diff --git a/br-ext-chip-hisilicon/board/hi3536cv100/hi3536cv100.config b/br-ext-chip-hisilicon/board/hi3536cv100/hi3536cv100.config new file mode 100644 index 00000000..4e797567 --- /dev/null +++ b/br-ext-chip-hisilicon/board/hi3536cv100/hi3536cv100.config @@ -0,0 +1,2 @@ +MEM_START_ADDR=0x80000000 +KERNEL_UPLOAD_ADDR=0x82000000 diff --git a/br-ext-chip-hisilicon/board/hi3536cv100/kernel/hi3536cv100.generic.config b/br-ext-chip-hisilicon/board/hi3536cv100/kernel/hi3536cv100.generic.config new file mode 100644 index 00000000..6a51d405 --- /dev/null +++ b/br-ext-chip-hisilicon/board/hi3536cv100/kernel/hi3536cv100.generic.config @@ -0,0 +1,2523 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.18.20 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_MIGHT_HAVE_PCI=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y + +# +# General setup +# +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +# CONFIG_COMPILE_TEST is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +# CONFIG_KERNEL_LZ4 is not set +CONFIG_DEFAULT_HOSTNAME="openipc" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_CROSS_MEMORY_ATTACH=y +# CONFIG_FHANDLE is not set +CONFIG_USELIB=y +# CONFIG_AUDIT is not set + +# +# IRQ subsystem +# +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_IRQ_DOMAIN=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_SPARSE_IRQ=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_ARCH_HAS_TICK_BROADCAST=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y + +# +# Timers subsystem +# +CONFIG_HZ_PERIODIC=y +# CONFIG_NO_HZ_IDLE is not set +# CONFIG_NO_HZ_FULL is not set +# CONFIG_NO_HZ is not set +# CONFIG_HIGH_RES_TIMERS is not set + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set +# CONFIG_IRQ_TIME_ACCOUNTING is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TASKS_RCU is not set +CONFIG_RCU_STALL_COMMON=y +# CONFIG_RCU_USER_QS is not set +CONFIG_RCU_FANOUT=32 +CONFIG_RCU_FANOUT_LEAF=16 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_NOCB_CPU is not set +# CONFIG_BUILD_BIN2C is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_FREEZER is not set +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +# CONFIG_CGROUP_CPUACCT is not set +# CONFIG_RESOURCE_COUNTERS is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_CFS_BANDWIDTH is not set +# CONFIG_RT_GROUP_SCHED is not set +# CONFIG_BLK_CGROUP is not set +# CONFIG_CHECKPOINT_RESTORE is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +# CONFIG_USER_NS is not set +CONFIG_PID_NS=y +CONFIG_NET_NS=y +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_RD_XZ=y +CONFIG_RD_LZO=y +CONFIG_RD_LZ4=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_HAVE_UID16=y +CONFIG_BPF=y +# CONFIG_EXPERT is not set +CONFIG_UID16=y +# CONFIG_SGETMASK_SYSCALL is not set +CONFIG_SYSFS_SYSCALL=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_BPF_SYSCALL is not set +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_ADVISE_SYSCALLS=y +# CONFIG_EMBEDDED is not set +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_COMPAT_BRK is not set +# CONFIG_SLAB is not set +CONFIG_SLUB=y +CONFIG_SLUB_CPU_PARTIAL=y +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +# CONFIG_JUMP_LABEL is not set +# CONFIG_UPROBES is not set +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_PERF_REGS=y +CONFIG_HAVE_PERF_USER_STACK_DUMP=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_HAVE_CC_STACKPROTECTOR=y +# CONFIG_CC_STACKPROTECTOR is not set +CONFIG_CC_STACKPROTECTOR_NONE=y +# CONFIG_CC_STACKPROTECTOR_REGULAR is not set +# CONFIG_CC_STACKPROTECTOR_STRONG is not set +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OLD_SIGACTION=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_MODULE_SIG is not set +# CONFIG_MODULE_COMPRESS is not set +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set +CONFIG_BLK_CMDLINE_PARSER=y + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_AIX_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +CONFIG_EFI_PARTITION=y +# CONFIG_SYSV68_PARTITION is not set +CONFIG_CMDLINE_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="deadline" +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +CONFIG_INLINE_READ_UNLOCK=y +CONFIG_INLINE_READ_UNLOCK_IRQ=y +CONFIG_INLINE_WRITE_UNLOCK=y +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +CONFIG_ARCH_MULTIPLATFORM=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE_LEGACY is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP1 is not set + +# +# Multiple platform selection +# + +# +# CPU Core family selection +# +# CONFIG_ARCH_MULTI_V6 is not set +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=y +# CONFIG_ARCH_MULTI_CPU_AUTO is not set +# CONFIG_ARCH_VIRT is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_BCM is not set +# CONFIG_ARCH_BERLIN is not set +# CONFIG_ARCH_HIGHBANK is not set +CONFIG_ARCH_HISI=y + +# +# Hisilicon platform type +# +# CONFIG_ARCH_HI3xxx is not set +# CONFIG_ARCH_HIP04 is not set +# CONFIG_ARCH_HIX5HD2 is not set +# CONFIG_ARCH_HI3519 is not set +# CONFIG_ARCH_HI3519V101 is not set +# CONFIG_ARCH_HI3516AV200 is not set +# CONFIG_ARCH_HI3559 is not set +# CONFIG_ARCH_HI3556 is not set +CONFIG_ARCH_HI3536C=y +# CONFIG_ARCH_HI3531D is not set +# CONFIG_ARCH_HI3521D is not set +# CONFIG_ARCH_KEYSTONE is not set +# CONFIG_ARCH_MESON is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MEDIATEK is not set + +# +# TI OMAP/AM/DM/DRA Family +# +# CONFIG_ARCH_OMAP3 is not set +# CONFIG_ARCH_OMAP4 is not set +# CONFIG_SOC_OMAP5 is not set +# CONFIG_SOC_AM33XX is not set +# CONFIG_SOC_AM43XX is not set +# CONFIG_SOC_DRA7XX is not set +# CONFIG_ARCH_QCOM is not set +# CONFIG_ARCH_ROCKCHIP is not set +# CONFIG_ARCH_SOCFPGA is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_STI is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_SHMOBILE_MULTI is not set +# CONFIG_ARCH_SUNXI is not set +# CONFIG_ARCH_SIRF is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_WM8850 is not set +# CONFIG_ARCH_ZYNQ is not set +CONFIG_ARM_TIMER_SP804=y + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +CONFIG_ARM_VIRT_EXT=y +CONFIG_SWP_EMULATE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_KUSER_HELPERS=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_643719 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_ARM_ERRATA_775420 is not set +# CONFIG_ARM_ERRATA_798181 is not set +# CONFIG_ARM_ERRATA_773022 is not set + +# +# Bus support +# +CONFIG_ARM_AMBA=y +# CONFIG_PCI is not set +# CONFIG_PCI_SYSCALL is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_SCHED_MC is not set +# CONFIG_SCHED_SMT is not set +# CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE is not set +CONFIG_HAVE_ARM_SCU=y +CONFIG_HAVE_ARM_ARCH_TIMER=y +# CONFIG_MCPM is not set +# CONFIG_BIG_LITTLE is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=4 +CONFIG_HOTPLUG_CPU=y +# CONFIG_ARM_PSCI is not set +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ_FIXED=0 +CONFIG_HZ_100=y +# CONFIG_HZ_200 is not set +# CONFIG_HZ_250 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_500 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=100 +# CONFIG_SCHED_HRTICK is not set +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_NO_BOOTMEM=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_BOUNCE=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA is not set +# CONFIG_ZPOOL is not set +# CONFIG_ZBUD is not set +# CONFIG_ZSMALLOC is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +CONFIG_SWIOTLB=y +CONFIG_IOMMU_HELPER=y +# CONFIG_XEN is not set +# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set + +# +# Boot options +# +CONFIG_USE_OF=y +CONFIG_ATAGS=y +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set +CONFIG_CMDLINE="" +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_AUTO_ZRELADDR=y + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# CPU Idle +# +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_FPE_NWFPE is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_SCRIPT=y +# CONFIG_HAVE_AOUT is not set +# CONFIG_BINFMT_MISC is not set +CONFIG_COREDUMP=y + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_WAKELOCK=y +# CONFIG_HISI_SNAPSHOT_BOOT is not set +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_CLK=y +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +CONFIG_NET_IP_TUNNEL=y +# CONFIG_IP_MROUTE is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_NET_UDP_TUNNEL=y +CONFIG_NET_FOU=y +# CONFIG_GENEVE is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=m +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_INET_UDP_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=m +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET6_XFRM_MODE_TUNNEL is not set +# CONFIG_INET6_XFRM_MODE_BEET is not set +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=m +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_GRE is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_ANDROID_PARANOID_NETWORK is not set +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NET_PTP_CLASSIFY is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +CONFIG_HAVE_NET_DSA=y +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_PHONET is not set +# CONFIG_6LOWPAN is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_VSOCKETS is not set +# CONFIG_NETLINK_MMAP is not set +# CONFIG_NETLINK_DIAG is not set +# CONFIG_NET_MPLS_GSO is not set +# CONFIG_HSR is not set +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +# CONFIG_CGROUP_NET_PRIO is not set +# CONFIG_CGROUP_NET_CLASSID is not set +CONFIG_NET_RX_BUSY_POLL=y +CONFIG_BQL=y +# CONFIG_BPF_JIT is not set +CONFIG_NET_FLOW_LIMIT=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=m +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +# CONFIG_CFG80211_WEXT is not set +# CONFIG_LIB80211 is not set +CONFIG_MAC80211=m +CONFIG_MAC80211_HAS_RC=y +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_MESH is not set +# CONFIG_MAC80211_MESSAGE_TRACING is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_RFKILL_REGULATOR is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set +CONFIG_HAVE_BPF_JIT=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_UEVENT_HELPER is not set +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set +CONFIG_ALLOW_DEV_COREDUMP=y +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +# CONFIG_HAVE_CPU_AUTOPROBE is not set +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +# CONFIG_DMA_SHARED_BUFFER is not set + +# +# Bus devices +# +# CONFIG_BRCMSTB_GISB_ARB is not set +# CONFIG_ARM_CCI is not set +# CONFIG_VEXPRESS_CONFIG is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +CONFIG_MTD_OF_PARTS=y +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set +CONFIG_HIFMC=y +CONFIG_HIFMC_SPI_NAND=y +# CONFIG_HIFMC_NAND is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +CONFIG_MTD_BLOCK2MTD=y + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_NAND_IDS=y +CONFIG_MTD_NAND_ECC=y +# CONFIG_MTD_NAND_ECC_SMC is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_ECC_BCH is not set +# CONFIG_MTD_SM_COMMON is not set +# CONFIG_MTD_NAND_DENALI is not set +# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_DOCG4 is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set +# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set +# CONFIG_MTD_NAND_HINFC610 is not set +# CONFIG_HIFMC100_NAND is not set +CONFIG_HIFMC100_SPI_NAND=y +CONFIG_SPI_NAND_MAX_CHIP_NUM=1 +# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set +CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y +# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR & LPDDR2 PCM memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_LPDDR2_NVM is not set +CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +CONFIG_SPI_HISI_SFC=y +CONFIG_CLOSE_SPI_8PIN_4IO=y +CONFIG_HISI_SPI_BLOCK_PROTECT=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_LIMIT=20 +# CONFIG_MTD_UBI_FASTMAP is not set +# CONFIG_MTD_UBI_GLUEBI is not set +# CONFIG_MTD_UBI_BLOCK is not set +CONFIG_DTC=y +CONFIG_OF=y + +# +# Device Tree and Open Firmware support +# +# CONFIG_OF_SELFTEST is not set +CONFIG_OF_FLATTREE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_IRQ=y +CONFIG_OF_NET=y +CONFIG_OF_MDIO=y +CONFIG_OF_MTD=y +CONFIG_OF_RESERVED_MEM=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_NULL_BLK is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_DRBD is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=65536 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_DUMMY_IRQ is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085_I2C is not set +# CONFIG_BMP085_SPI is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_LATTICE_ECP3_CONFIG is not set +# CONFIG_SRAM is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +CONFIG_EEPROM_93CX6=m +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set + +# +# Intel MIC Bus Driver +# + +# +# Intel MIC Host Driver +# + +# +# Intel MIC Card Driver +# +# CONFIG_ECHO is not set +# CONFIG_CXL_BASE is not set + +# +# hisi 'himm/himd.l/himc'support +# +# CONFIG_HISI_REG is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_MQ_DEFAULT is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +CONFIG_HI_SATA=y +CONFIG_HI_SATA_IOBASE=0x11010000 +CONFIG_HI_SATA_FBS=1 +CONFIG_HI_SATA_NCQ=1 +CONFIG_ATA=y +# CONFIG_ATA_NONSTANDARD is not set +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_SATA_PMP=y + +# +# Controllers with non-SFF native interface +# +CONFIG_SATA_AHCI_PLATFORM=y +# CONFIG_ATA_SFF is not set +# CONFIG_MD is not set +# CONFIG_TARGET_CORE is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_VXLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_TUN=m +# CONFIG_VETH is not set +# CONFIG_NLMON is not set + +# +# CAIF transport drivers +# + +# +# Distributed Switch Architecture drivers +# +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6060 is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +# CONFIG_NET_DSA_MV88E6131 is not set +# CONFIG_NET_DSA_MV88E6123_61_65 is not set +# CONFIG_NET_DSA_MV88E6171 is not set +# CONFIG_NET_DSA_BCM_SF2 is not set +CONFIG_ETHERNET=y +# CONFIG_ALTERA_TSE is not set +# CONFIG_NET_XGENE is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_CADENCE is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_HISILICON=y +# CONFIG_HIX5HD2_GMAC is not set +# CONFIG_HISI_FEMAC is not set +CONFIG_HIETH_GMAC=y +CONFIG_HIGMAC_DESC_4WORD=y +CONFIG_HIGMAC_RXCSUM=y +CONFIG_RX_FLOW_CTRL_SUPPORT=y +CONFIG_TX_FLOW_CTRL_SUPPORT=y +CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF +CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF +CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 +CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 +# CONFIG_HIETH_SWITCH_FABRIC is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_AT803X_PHY is not set +# CONFIG_AMD_PHY is not set +# CONFIG_AMD_XGBE_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_BCM7XXX_PHY is not set +# CONFIG_BCM87XX_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +# CONFIG_MDIO_BUS_MUX_MMIOREG is not set +# CONFIG_MDIO_BCM_UNIMAC is not set +# CONFIG_MDIO_HISI_FEMAC is not set +CONFIG_MDIO_HISI_GEMAC=y +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +CONFIG_USB_NET_DRIVERS=m +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_RTL8152 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_IPHETH is not set +CONFIG_WLAN=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +CONFIG_RTL8187=m +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +# CONFIG_ATH_CARDS is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BRCMSMAC is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_P54_COMMON is not set +CONFIG_RT2X00=m +CONFIG_RT2500USB=m +CONFIG_RT73USB=m +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT33XX=y +CONFIG_RT2800USB_RT35XX=y +# CONFIG_RT2800USB_RT3573 is not set +# CONFIG_RT2800USB_RT53XX is not set +# CONFIG_RT2800USB_RT55XX is not set +# CONFIG_RT2800USB_UNKNOWN is not set +CONFIG_RT2800_LIB=m +CONFIG_RT2X00_LIB_USB=m +CONFIG_RT2X00_LIB=m +CONFIG_RT2X00_LIB_FIRMWARE=y +CONFIG_RT2X00_LIB_CRYPTO=y +# CONFIG_RT2X00_DEBUG is not set +CONFIG_RTL_CARDS=m +CONFIG_RTL8192CU=m +CONFIG_RTLWIFI=m +CONFIG_RTLWIFI_USB=m +CONFIG_RTLWIFI_DEBUG=y +CONFIG_RTL8192C_COMMON=m +# CONFIG_WL_TI is not set +# CONFIG_ZD1211RW is not set +# CONFIG_MWIFIEX is not set +# CONFIG_CW1200 is not set +# CONFIG_RSI_91X is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set +# CONFIG_INPUT_MATRIXKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=m +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=m +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set +# CONFIG_INPUT_KEYCOMBO is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +CONFIG_KEYBOARD_ATKBD=m +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_CAP1106 is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=m +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_CYPRESS=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_CYAPA is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +# CONFIG_MOUSE_SYNAPTICS_USB is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_AMBAKMI is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_SERIO_APBPS2 is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_TTY=y +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_CONSOLE_SLEEP=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +CONFIG_SERIAL_EARLYCON=y +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_AMBA_PL010 is not set +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_SC16IS7XX is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_SERIAL_FSL_LPUART is not set +# CONFIG_SERIAL_ST_ASC is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_XILLYBUS is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y + +# +# Multiplexer I2C Chip support +# +# CONFIG_I2C_MUX_PCA9541 is not set +# CONFIG_I2C_MUX_PINCTRL is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_HIBVT is not set +# CONFIG_I2C_HISI_V110 is not set +# CONFIG_I2C_NOMADIK is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_RK3X is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_ROBOTFUZZ_OSIF is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +CONFIG_HI_I2C=y +CONFIG_HI_I2C0_IO_BASE=0x120c0000 +CONFIG_HI_I2C0_IO_SIZE=0x1000 +CONFIG_HI_I2C_RETRIES=0x1 +CONFIG_HI_I2C_TX_FIFO=0x8 +CONFIG_HI_I2C_RX_FIFO=0x8 +CONFIG_HI_I2C0_CLK_LIMIT=100000 +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_CADENCE is not set +# CONFIG_SPI_FSL_SPI is not set +CONFIG_SPI_PL022=y +# CONFIG_SPI_PXA2XX_PCI is not set +# CONFIG_SPI_ROCKCHIP is not set +# CONFIG_SPI_SC18IS602 is not set +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +CONFIG_SPI_SPIDEV=y +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_SPMI is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# + +# +# PTP clock support +# +# CONFIG_PTP_1588_CLOCK is not set + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +CONFIG_PINCTRL=y + +# +# Pin controllers +# +CONFIG_PINMUX=y +CONFIG_PINCONF=y +CONFIG_GENERIC_PINCONF=y +CONFIG_PINCTRL_SINGLE=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y +# CONFIG_GPIOLIB is not set +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_SBS is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_MANAGER is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_SMB347 is not set +CONFIG_POWER_RESET=y +# CONFIG_POWER_RESET_BRCMSTB is not set +CONFIG_POWER_RESET_HISI=y +# CONFIG_POWER_RESET_RESTART is not set +# CONFIG_POWER_RESET_VERSATILE is not set +# CONFIG_POWER_RESET_SYSCON is not set +# CONFIG_POWER_AVS is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +CONFIG_MFD_CORE=y +# CONFIG_MFD_AS3711 is not set +# CONFIG_MFD_AS3722 is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_BCM590XX is not set +# CONFIG_MFD_AXP20X is not set +# CONFIG_MFD_CROS_EC is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9055 is not set +# CONFIG_MFD_DA9063 is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_MFD_HI6421_PMIC is not set +CONFIG_MFD_HISI_FMC=y +# CONFIG_HTC_PASIC3 is not set +# CONFIG_INTEL_SOC_PMIC is not set +# CONFIG_MFD_KEMPLD is not set +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_MAX14577 is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_MENF21BMC is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_PM8921_CORE is not set +# CONFIG_MFD_RTSX_USB is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_RK808 is not set +# CONFIG_MFD_RN5T618 is not set +# CONFIG_MFD_SEC_CORE is not set +# CONFIG_MFD_SI476X_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_SMSC is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_STMPE is not set +CONFIG_MFD_SYSCON=y +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_LP3943 is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS65218 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_ACT8865 is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_ANATOP is not set +# CONFIG_REGULATOR_DA9210 is not set +# CONFIG_REGULATOR_DA9211 is not set +# CONFIG_REGULATOR_FAN53555 is not set +# CONFIG_REGULATOR_ISL9305 is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_LP872X is not set +# CONFIG_REGULATOR_LP8755 is not set +# CONFIG_REGULATOR_LTC3589 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_MAX8973 is not set +# CONFIG_REGULATOR_PFUZE100 is not set +# CONFIG_REGULATOR_TPS51632 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_TPS6524X is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_IMX_IPUV3_CORE is not set + +# +# Direct Rendering Manager +# +# CONFIG_DRM is not set + +# +# Frame buffer Devices +# +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +CONFIG_FB_CMDLINE=y +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_ARMCLCD is not set +# CONFIG_FB_OPENCORES is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_AUO_K190X is not set +# CONFIG_FB_SIMPLE is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_VGASTATE is not set + +# +# Console display driver support +# +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HIDRAW is not set +# CONFIG_UHID is not set +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +# CONFIG_HID_ACRUX is not set +CONFIG_HID_APPLE=y +# CONFIG_HID_APPLEIR is not set +# CONFIG_HID_AUREAL is not set +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_ELO is not set +CONFIG_HID_EZKEY=y +# CONFIG_HID_HOLTEK is not set +# CONFIG_HID_HUION is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_ICADE is not set +# CONFIG_HID_TWINHAN is not set +CONFIG_HID_KENSINGTON=y +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LENOVO is not set +CONFIG_HID_LOGITECH=y +# CONFIG_HID_LOGITECH_HIDPP is not set +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +# CONFIG_LOGIG940_FF is not set +# CONFIG_LOGIWHEELS_FF is not set +# CONFIG_HID_MAGICMOUSE is not set +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PENMOUNT is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_STEELSERIES is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_RMI is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_WACOM is not set +# CONFIG_HID_XINMO is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HID_SENSOR_HUB is not set + +# +# USB HID support +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# I2C HID support +# +# CONFIG_I2C_HID is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_FSM is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_XHCI_HCD is not set +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_FUSBH200_HCD is not set +# CONFIG_USB_FOTG210_HCD is not set +# CONFIG_USB_MAX3421_HCD is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HCD_TEST_MODE is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_REALTEK is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_STORAGE_ENE_UB6250 is not set +# CONFIG_USB_UAS is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USBIP_CORE is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_DWC2 is not set +# CONFIG_USB_CHIPIDEA is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_EHSET_TEST_FIXTURE is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_EZUSB_FX2 is not set +# CONFIG_USB_HSIC_USB3503 is not set +# CONFIG_USB_LINK_LAYER_TEST is not set + +# +# USB Physical Layer drivers +# +# CONFIG_USB_PHY is not set +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_AM335X_PHY_USB is not set +# CONFIG_USB_ISP1301 is not set +# CONFIG_USB_ULPI is not set +# CONFIG_USB_GADGET is not set +# CONFIG_UWB is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_EDAC is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_HYM8563 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_ISL12057 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF2127 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF85063 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1343 is not set +# CONFIG_RTC_DRV_DS1347 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set +# CONFIG_RTC_DRV_RX4581 is not set +# CONFIG_RTC_DRV_MCP795 is not set + +# +# Platform RTC drivers +# +CONFIG_RTC_DRV_HIBVT=y +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_DS2404 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_PL030 is not set +# CONFIG_RTC_DRV_PL031 is not set +# CONFIG_RTC_DRV_SNVS is not set +# CONFIG_RTC_DRV_XGENE is not set + +# +# HID Sensor RTC drivers +# +# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +# CONFIG_VIRT_DRIVERS is not set + +# +# Virtio drivers +# +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +CONFIG_STAGING=y +# CONFIG_PRISM2_USB is not set +# CONFIG_COMEDI is not set +# CONFIG_RTLLIB is not set +CONFIG_R8712U=m +CONFIG_R8188EU=m +CONFIG_88EU_AP_MODE=y +# CONFIG_VT6656 is not set +# CONFIG_BCM_WIMAX is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_SPEAKUP is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +# CONFIG_STAGING_MEDIA is not set + +# +# Android +# +# CONFIG_ANDROID is not set +# CONFIG_USB_WPAN_HCD is not set +# CONFIG_WIMAX_GDM72XX is not set +# CONFIG_LTE_GDM724X is not set +# CONFIG_MTD_SPINAND_MT29F is not set +# CONFIG_LUSTRE_FS is not set +# CONFIG_DGAP is not set +# CONFIG_GS_FPGABOOT is not set + +# +# SOC (System On Chip) specific Drivers +# +# CONFIG_SOC_TI is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y + +# +# Common Clock Framework +# +# CONFIG_COMMON_CLK_SI5351 is not set +# CONFIG_COMMON_CLK_SI570 is not set +# CONFIG_COMMON_CLK_PXA is not set +# CONFIG_COMMON_CLK_QCOM is not set + +# +# Hardware Spinlock drivers +# + +# +# Clock Source drivers +# +CONFIG_CLKSRC_OF=y +CONFIG_CLKSRC_MMIO=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set +# CONFIG_ATMEL_PIT is not set +# CONFIG_SH_TIMER_CMT is not set +# CONFIG_SH_TIMER_MTU2 is not set +# CONFIG_SH_TIMER_TMU is not set +# CONFIG_EM_TIMER_STI is not set +# CONFIG_CLKSRC_VERSATILE is not set +# CONFIG_MAILBOX is not set +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers +# +# CONFIG_STE_MODEM_RPROC is not set + +# +# Rpmsg drivers +# + +# +# SOC (System On Chip) specific Drivers +# +# CONFIG_PM_DEVFREQ is not set +# CONFIG_EXTCON is not set +# CONFIG_MEMORY is not set +# CONFIG_IIO is not set +# CONFIG_PWM is not set +CONFIG_IRQCHIP=y +CONFIG_ARM_GIC=y +# CONFIG_IPACK_BUS is not set +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_RESET_CONTROLLER=y +# CONFIG_FMC is not set + +# +# PHY Subsystem +# +CONFIG_GENERIC_PHY=y +# CONFIG_BCM_KONA_USB2_PHY is not set +CONFIG_PHY_HISI_INNO_USB2=y +CONFIG_HI_NANO_PHY_SATA=y +CONFIG_HI_SATA_PORTS=2 +CONFIG_HI_SATA_MODE=1 +# CONFIG_POWERCAP is not set +# CONFIG_MCB is not set +CONFIG_HI_DMAC=y +CONFIG_HI_DMAC_CHANNEL_NUM=4 + +# +# Hisilicon driver support +# + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_EXT2_FS=m +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=m +CONFIG_EXT3_DEFAULTS_TO_ORDERED=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_EXT4_FS=m +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD=m +CONFIG_JBD2=m +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=m +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set +CONFIG_OVERLAY_FS=y + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_KERNFS=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=m +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_YAFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_UBIFS_FS is not set +# CONFIG_LOGFS is not set +CONFIG_CRAMFS=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_FILE_CACHE=y +# CONFIG_SQUASHFS_FILE_DIRECT is not set +CONFIG_SQUASHFS_DECOMP_SINGLE=y +# CONFIG_SQUASHFS_DECOMP_MULTI is not set +# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_F2FS_FS=m +CONFIG_F2FS_FS_XATTR=y +CONFIG_F2FS_FS_POSIX_ACL=y +# CONFIG_F2FS_FS_SECURITY is not set +# CONFIG_F2FS_CHECK_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V2=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_SWAP is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_GRACE_PERIOD=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +CONFIG_NLS_CODEPAGE_866=m +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +CONFIG_NLS_UTF8=y +# CONFIG_DLM is not set + +# +# Kernel hacking +# + +# +# printk and dmesg options +# +# CONFIG_PRINTK_TIME is not set +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 + +# +# Compile-time checks and compiler options +# +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_FRAME_WARN=1024 +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_KERNEL is not set + +# +# Memory Debugging +# +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_MEMORY_INIT=y + +# +# Debug Lockups and Hangs +# +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 + +# +# Lock Debugging (spinlocks, mutexes, etc...) +# +CONFIG_STACKTRACE=y +CONFIG_DEBUG_BUGVERBOSE=y + +# +# RCU Debugging +# +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set + +# +# Runtime Testing +# +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_TEST_RHASHTABLE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_TEST_LKM is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_TEST_BPF is not set +# CONFIG_TEST_FIRMWARE is not set +# CONFIG_TEST_UDELAY is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_STRICT_DEVMEM=y +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +# CONFIG_DEBUG_UART_PL01X is not set +# CONFIG_DEBUG_UART_8250 is not set +# CONFIG_DEBUG_UART_BCM63XX is not set +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +# CONFIG_OC_ETM is not set +# CONFIG_PID_IN_CONTEXTIDR is not set +# CONFIG_DEBUG_SET_MODULE_RONX is not set +# CONFIG_CORESIGHT is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=m +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=m +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=m +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_PCRYPT is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_MCRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +CONFIG_CRYPTO_CCM=m +# CONFIG_CRYPTO_GCM is not set +CONFIG_CRYPTO_SEQIV=m + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +CONFIG_CRYPTO_CTR=m +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_CMAC is not set +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_CRCT10DIF is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA1_ARM is not set +# CONFIG_CRYPTO_SHA1_ARM_NEON is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_SHA512_ARM_NEON is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_ARM is not set +# CONFIG_CRYPTO_AES_ARM_BS is not set +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=m +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_DRBG_MENU is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_NET_UTILS=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y +CONFIG_CRC_CCITT=m +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +CONFIG_CRC_ITU_T=m +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set +# CONFIG_RANDOM32_SELFTEST is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_DECOMPRESS=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_XZ=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_DECOMPRESS_LZ4=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_GLOB=y +# CONFIG_GLOB_SELFTEST is not set +CONFIG_NLATTR=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set +CONFIG_LIBFDT=y +CONFIG_ARCH_HAS_SG_CHAIN=y +# CONFIG_VIRTUALIZATION is not set diff --git a/br-ext-chip-hisilicon/board/hi3536cv100/kernel/hi3536cv100.generic.config.original b/br-ext-chip-hisilicon/board/hi3536cv100/kernel/hi3536cv100.generic.config.original new file mode 100644 index 00000000..9f8b73a6 --- /dev/null +++ b/br-ext-chip-hisilicon/board/hi3536cv100/kernel/hi3536cv100.generic.config.original @@ -0,0 +1,2522 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.18.20 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_MIGHT_HAVE_PCI=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y + +# +# General setup +# +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +# CONFIG_COMPILE_TEST is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +# CONFIG_KERNEL_LZ4 is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_CROSS_MEMORY_ATTACH=y +# CONFIG_FHANDLE is not set +CONFIG_USELIB=y +# CONFIG_AUDIT is not set + +# +# IRQ subsystem +# +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_IRQ_DOMAIN=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_SPARSE_IRQ=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_ARCH_HAS_TICK_BROADCAST=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y + +# +# Timers subsystem +# +CONFIG_HZ_PERIODIC=y +# CONFIG_NO_HZ_IDLE is not set +# CONFIG_NO_HZ_FULL is not set +# CONFIG_NO_HZ is not set +# CONFIG_HIGH_RES_TIMERS is not set + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set +# CONFIG_IRQ_TIME_ACCOUNTING is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TASKS_RCU is not set +CONFIG_RCU_STALL_COMMON=y +# CONFIG_RCU_USER_QS is not set +CONFIG_RCU_FANOUT=32 +CONFIG_RCU_FANOUT_LEAF=16 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_NOCB_CPU is not set +# CONFIG_BUILD_BIN2C is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_FREEZER is not set +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +# CONFIG_CGROUP_CPUACCT is not set +# CONFIG_RESOURCE_COUNTERS is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_CFS_BANDWIDTH is not set +# CONFIG_RT_GROUP_SCHED is not set +# CONFIG_BLK_CGROUP is not set +# CONFIG_CHECKPOINT_RESTORE is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +# CONFIG_USER_NS is not set +CONFIG_PID_NS=y +CONFIG_NET_NS=y +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_RD_XZ=y +CONFIG_RD_LZO=y +CONFIG_RD_LZ4=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_HAVE_UID16=y +CONFIG_BPF=y +# CONFIG_EXPERT is not set +CONFIG_UID16=y +# CONFIG_SGETMASK_SYSCALL is not set +CONFIG_SYSFS_SYSCALL=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_BPF_SYSCALL is not set +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_ADVISE_SYSCALLS=y +# CONFIG_EMBEDDED is not set +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_COMPAT_BRK is not set +# CONFIG_SLAB is not set +CONFIG_SLUB=y +CONFIG_SLUB_CPU_PARTIAL=y +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +# CONFIG_JUMP_LABEL is not set +# CONFIG_UPROBES is not set +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_PERF_REGS=y +CONFIG_HAVE_PERF_USER_STACK_DUMP=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_HAVE_CC_STACKPROTECTOR=y +# CONFIG_CC_STACKPROTECTOR is not set +CONFIG_CC_STACKPROTECTOR_NONE=y +# CONFIG_CC_STACKPROTECTOR_REGULAR is not set +# CONFIG_CC_STACKPROTECTOR_STRONG is not set +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OLD_SIGACTION=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_MODULE_SIG is not set +# CONFIG_MODULE_COMPRESS is not set +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set +CONFIG_BLK_CMDLINE_PARSER=y + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_AIX_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +CONFIG_EFI_PARTITION=y +# CONFIG_SYSV68_PARTITION is not set +CONFIG_CMDLINE_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="deadline" +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +CONFIG_INLINE_READ_UNLOCK=y +CONFIG_INLINE_READ_UNLOCK_IRQ=y +CONFIG_INLINE_WRITE_UNLOCK=y +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +CONFIG_ARCH_MULTIPLATFORM=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE_LEGACY is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP1 is not set + +# +# Multiple platform selection +# + +# +# CPU Core family selection +# +# CONFIG_ARCH_MULTI_V6 is not set +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=y +# CONFIG_ARCH_MULTI_CPU_AUTO is not set +# CONFIG_ARCH_VIRT is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_BCM is not set +# CONFIG_ARCH_BERLIN is not set +# CONFIG_ARCH_HIGHBANK is not set +CONFIG_ARCH_HISI=y + +# +# Hisilicon platform type +# +# CONFIG_ARCH_HI3xxx is not set +# CONFIG_ARCH_HIP04 is not set +# CONFIG_ARCH_HIX5HD2 is not set +# CONFIG_ARCH_HI3519 is not set +# CONFIG_ARCH_HI3519V101 is not set +# CONFIG_ARCH_HI3516AV200 is not set +# CONFIG_ARCH_HI3559 is not set +# CONFIG_ARCH_HI3556 is not set +CONFIG_ARCH_HI3536C=y +# CONFIG_ARCH_HI3531D is not set +# CONFIG_ARCH_HI3521D is not set +# CONFIG_ARCH_KEYSTONE is not set +# CONFIG_ARCH_MESON is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MEDIATEK is not set + +# +# TI OMAP/AM/DM/DRA Family +# +# CONFIG_ARCH_OMAP3 is not set +# CONFIG_ARCH_OMAP4 is not set +# CONFIG_SOC_OMAP5 is not set +# CONFIG_SOC_AM33XX is not set +# CONFIG_SOC_AM43XX is not set +# CONFIG_SOC_DRA7XX is not set +# CONFIG_ARCH_QCOM is not set +# CONFIG_ARCH_ROCKCHIP is not set +# CONFIG_ARCH_SOCFPGA is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_STI is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_SHMOBILE_MULTI is not set +# CONFIG_ARCH_SUNXI is not set +# CONFIG_ARCH_SIRF is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_WM8850 is not set +# CONFIG_ARCH_ZYNQ is not set +CONFIG_ARM_TIMER_SP804=y + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +CONFIG_ARM_VIRT_EXT=y +CONFIG_SWP_EMULATE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_KUSER_HELPERS=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_643719 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_ARM_ERRATA_775420 is not set +# CONFIG_ARM_ERRATA_798181 is not set +# CONFIG_ARM_ERRATA_773022 is not set + +# +# Bus support +# +CONFIG_ARM_AMBA=y +# CONFIG_PCI is not set +# CONFIG_PCI_SYSCALL is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_SCHED_MC is not set +# CONFIG_SCHED_SMT is not set +# CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE is not set +CONFIG_HAVE_ARM_SCU=y +CONFIG_HAVE_ARM_ARCH_TIMER=y +# CONFIG_MCPM is not set +# CONFIG_BIG_LITTLE is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=4 +CONFIG_HOTPLUG_CPU=y +# CONFIG_ARM_PSCI is not set +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ_FIXED=0 +CONFIG_HZ_100=y +# CONFIG_HZ_200 is not set +# CONFIG_HZ_250 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_500 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=100 +# CONFIG_SCHED_HRTICK is not set +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_NO_BOOTMEM=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_BOUNCE=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA is not set +# CONFIG_ZPOOL is not set +# CONFIG_ZBUD is not set +# CONFIG_ZSMALLOC is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +CONFIG_SWIOTLB=y +CONFIG_IOMMU_HELPER=y +# CONFIG_XEN is not set +# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set + +# +# Boot options +# +CONFIG_USE_OF=y +CONFIG_ATAGS=y +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set +CONFIG_CMDLINE="" +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_AUTO_ZRELADDR=y + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +CONFIG_CPUFREQ_DT=y + +# +# ARM CPU frequency scaling drivers +# +# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set + +# +# CPU Idle +# +# CONFIG_CPU_IDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_FPE_NWFPE is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_SCRIPT=y +# CONFIG_HAVE_AOUT is not set +# CONFIG_BINFMT_MISC is not set +CONFIG_COREDUMP=y + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_WAKELOCK=y +# CONFIG_HISI_SNAPSHOT_BOOT is not set +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_OPP=y +CONFIG_PM_CLK=y +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +CONFIG_NET_IP_TUNNEL=m +# CONFIG_IP_MROUTE is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_NET_UDP_TUNNEL is not set +# CONFIG_NET_FOU is not set +# CONFIG_GENEVE is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=m +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_INET_UDP_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_MODE_BEET=m +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +# CONFIG_IPV6_VTI is not set +CONFIG_IPV6_SIT=m +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_GRE is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_ANDROID_PARANOID_NETWORK is not set +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NET_PTP_CLASSIFY is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NF_TABLES is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_IP_SET is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_NF_LOG_ARP is not set +# CONFIG_NF_LOG_IPV4 is not set +# CONFIG_NF_REJECT_IPV4 is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set + +# +# IPv6: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV6 is not set +# CONFIG_NF_REJECT_IPV6 is not set +# CONFIG_NF_LOG_IPV6 is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +CONFIG_HAVE_NET_DSA=y +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_PHONET is not set +# CONFIG_6LOWPAN is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_VSOCKETS is not set +# CONFIG_NETLINK_MMAP is not set +# CONFIG_NETLINK_DIAG is not set +# CONFIG_NET_MPLS_GSO is not set +# CONFIG_HSR is not set +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +# CONFIG_CGROUP_NET_PRIO is not set +# CONFIG_CGROUP_NET_CLASSID is not set +CONFIG_NET_RX_BUSY_POLL=y +CONFIG_BQL=y +# CONFIG_BPF_JIT is not set +CONFIG_NET_FLOW_LIMIT=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +# CONFIG_WIRELESS is not set +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_RFKILL_REGULATOR is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set +CONFIG_HAVE_BPF_JIT=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_UEVENT_HELPER is not set +CONFIG_DEVTMPFS=y +# CONFIG_DEVTMPFS_MOUNT is not set +CONFIG_STANDALONE=y +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set +CONFIG_ALLOW_DEV_COREDUMP=y +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +# CONFIG_HAVE_CPU_AUTOPROBE is not set +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +# CONFIG_DMA_SHARED_BUFFER is not set + +# +# Bus devices +# +# CONFIG_BRCMSTB_GISB_ARB is not set +# CONFIG_ARM_CCI is not set +# CONFIG_VEXPRESS_CONFIG is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +CONFIG_MTD_OF_PARTS=y +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set +CONFIG_HIFMC=y +CONFIG_HIFMC_SPI_NAND=y +# CONFIG_HIFMC_NAND is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +CONFIG_MTD_BLOCK2MTD=y + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_NAND_IDS=y +CONFIG_MTD_NAND_ECC=y +# CONFIG_MTD_NAND_ECC_SMC is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_ECC_BCH is not set +# CONFIG_MTD_SM_COMMON is not set +# CONFIG_MTD_NAND_DENALI is not set +# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_DOCG4 is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set +# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set +# CONFIG_MTD_NAND_HINFC610 is not set +# CONFIG_HIFMC100_NAND is not set +CONFIG_HIFMC100_SPI_NAND=y +CONFIG_SPI_NAND_MAX_CHIP_NUM=1 +# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set +CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y +# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR & LPDDR2 PCM memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_LPDDR2_NVM is not set +CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +CONFIG_SPI_HISI_SFC=y +CONFIG_CLOSE_SPI_8PIN_4IO=y +CONFIG_HISI_SPI_BLOCK_PROTECT=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_LIMIT=20 +# CONFIG_MTD_UBI_FASTMAP is not set +# CONFIG_MTD_UBI_GLUEBI is not set +# CONFIG_MTD_UBI_BLOCK is not set +CONFIG_DTC=y +CONFIG_OF=y + +# +# Device Tree and Open Firmware support +# +# CONFIG_OF_SELFTEST is not set +CONFIG_OF_FLATTREE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_IRQ=y +CONFIG_OF_NET=y +CONFIG_OF_MDIO=y +CONFIG_OF_MTD=y +CONFIG_OF_RESERVED_MEM=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_NULL_BLK is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_DRBD is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=65536 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_DUMMY_IRQ is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085_I2C is not set +# CONFIG_BMP085_SPI is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_LATTICE_ECP3_CONFIG is not set +# CONFIG_SRAM is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set + +# +# Intel MIC Bus Driver +# + +# +# Intel MIC Host Driver +# + +# +# Intel MIC Card Driver +# +# CONFIG_ECHO is not set +# CONFIG_CXL_BASE is not set + +# +# hisi 'himm/himd.l/himc'support +# +# CONFIG_HISI_REG is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_MQ_DEFAULT is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +CONFIG_HI_SATA=y +CONFIG_HI_SATA_IOBASE=0x11010000 +CONFIG_HI_SATA_FBS=1 +CONFIG_HI_SATA_NCQ=1 +CONFIG_ATA=y +# CONFIG_ATA_NONSTANDARD is not set +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_SATA_PMP=y + +# +# Controllers with non-SFF native interface +# +CONFIG_SATA_AHCI_PLATFORM=y +# CONFIG_ATA_SFF is not set +# CONFIG_MD is not set +# CONFIG_TARGET_CORE is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_VXLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_NLMON is not set + +# +# CAIF transport drivers +# + +# +# Distributed Switch Architecture drivers +# +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6060 is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +# CONFIG_NET_DSA_MV88E6131 is not set +# CONFIG_NET_DSA_MV88E6123_61_65 is not set +# CONFIG_NET_DSA_MV88E6171 is not set +# CONFIG_NET_DSA_BCM_SF2 is not set +CONFIG_ETHERNET=y +# CONFIG_ALTERA_TSE is not set +# CONFIG_NET_XGENE is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_CADENCE is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_HISILICON=y +# CONFIG_HIX5HD2_GMAC is not set +# CONFIG_HISI_FEMAC is not set +CONFIG_HIETH_GMAC=y +CONFIG_HIGMAC_DESC_4WORD=y +CONFIG_HIGMAC_RXCSUM=y +CONFIG_RX_FLOW_CTRL_SUPPORT=y +CONFIG_TX_FLOW_CTRL_SUPPORT=y +CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF +CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF +CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 +CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 +# CONFIG_HIETH_SWITCH_FABRIC is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_AT803X_PHY is not set +# CONFIG_AMD_PHY is not set +# CONFIG_AMD_XGBE_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_BCM7XXX_PHY is not set +# CONFIG_BCM87XX_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +# CONFIG_MDIO_BUS_MUX_MMIOREG is not set +# CONFIG_MDIO_BCM_UNIMAC is not set +# CONFIG_MDIO_HISI_FEMAC is not set +CONFIG_MDIO_HISI_GEMAC=y +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +CONFIG_USB_NET_DRIVERS=y +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_RTL8152 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_WLAN is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set +# CONFIG_INPUT_MATRIXKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +CONFIG_INPUT_JOYDEV=y +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set +# CONFIG_INPUT_KEYCOMBO is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_CAP1106 is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_CYPRESS=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_CYAPA is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +# CONFIG_MOUSE_SYNAPTICS_USB is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_AMBAKMI is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_SERIO_APBPS2 is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_TTY=y +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_CONSOLE_SLEEP=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +CONFIG_SERIAL_EARLYCON=y +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_AMBA_PL010 is not set +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_SC16IS7XX is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_SERIAL_FSL_LPUART is not set +# CONFIG_SERIAL_ST_ASC is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_XILLYBUS is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y + +# +# Multiplexer I2C Chip support +# +# CONFIG_I2C_MUX_PCA9541 is not set +# CONFIG_I2C_MUX_PINCTRL is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_HIBVT is not set +# CONFIG_I2C_HISI_V110 is not set +# CONFIG_I2C_NOMADIK is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_RK3X is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_ROBOTFUZZ_OSIF is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +CONFIG_HI_I2C=y +CONFIG_HI_I2C0_IO_BASE=0x120c0000 +CONFIG_HI_I2C0_IO_SIZE=0x1000 +CONFIG_HI_I2C_RETRIES=0x1 +CONFIG_HI_I2C_TX_FIFO=0x8 +CONFIG_HI_I2C_RX_FIFO=0x8 +CONFIG_HI_I2C0_CLK_LIMIT=100000 +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_CADENCE is not set +# CONFIG_SPI_FSL_SPI is not set +CONFIG_SPI_PL022=y +# CONFIG_SPI_PXA2XX_PCI is not set +# CONFIG_SPI_ROCKCHIP is not set +# CONFIG_SPI_SC18IS602 is not set +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +CONFIG_SPI_SPIDEV=y +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_SPMI is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# + +# +# PTP clock support +# +# CONFIG_PTP_1588_CLOCK is not set + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +CONFIG_PINCTRL=y + +# +# Pin controllers +# +CONFIG_PINMUX=y +CONFIG_PINCONF=y +CONFIG_GENERIC_PINCONF=y +# CONFIG_DEBUG_PINCTRL is not set +CONFIG_PINCTRL_SINGLE=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y +# CONFIG_GPIOLIB is not set +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_SBS is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_MANAGER is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_SMB347 is not set +CONFIG_POWER_RESET=y +# CONFIG_POWER_RESET_BRCMSTB is not set +CONFIG_POWER_RESET_HISI=y +# CONFIG_POWER_RESET_RESTART is not set +# CONFIG_POWER_RESET_VERSATILE is not set +# CONFIG_POWER_RESET_SYSCON is not set +# CONFIG_POWER_AVS is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +CONFIG_MFD_CORE=y +# CONFIG_MFD_AS3711 is not set +# CONFIG_MFD_AS3722 is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_BCM590XX is not set +# CONFIG_MFD_AXP20X is not set +# CONFIG_MFD_CROS_EC is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9055 is not set +# CONFIG_MFD_DA9063 is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_MFD_HI6421_PMIC is not set +CONFIG_MFD_HISI_FMC=y +# CONFIG_HTC_PASIC3 is not set +# CONFIG_INTEL_SOC_PMIC is not set +# CONFIG_MFD_KEMPLD is not set +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_MAX14577 is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_MENF21BMC is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_PM8921_CORE is not set +# CONFIG_MFD_RTSX_USB is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_RK808 is not set +# CONFIG_MFD_RN5T618 is not set +# CONFIG_MFD_SEC_CORE is not set +# CONFIG_MFD_SI476X_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_SMSC is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_STMPE is not set +CONFIG_MFD_SYSCON=y +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_LP3943 is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS65218 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_ACT8865 is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_ANATOP is not set +# CONFIG_REGULATOR_DA9210 is not set +# CONFIG_REGULATOR_DA9211 is not set +# CONFIG_REGULATOR_FAN53555 is not set +# CONFIG_REGULATOR_ISL9305 is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_LP872X is not set +# CONFIG_REGULATOR_LP8755 is not set +# CONFIG_REGULATOR_LTC3589 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_MAX8973 is not set +# CONFIG_REGULATOR_PFUZE100 is not set +# CONFIG_REGULATOR_TPS51632 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_TPS6524X is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_IMX_IPUV3_CORE is not set + +# +# Direct Rendering Manager +# +# CONFIG_DRM is not set + +# +# Frame buffer Devices +# +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +CONFIG_FB_CMDLINE=y +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_ARMCLCD is not set +# CONFIG_FB_OPENCORES is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_AUO_K190X is not set +# CONFIG_FB_SIMPLE is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_VGASTATE is not set + +# +# Console display driver support +# +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HIDRAW is not set +# CONFIG_UHID is not set +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +# CONFIG_HID_ACRUX is not set +CONFIG_HID_APPLE=y +# CONFIG_HID_APPLEIR is not set +# CONFIG_HID_AUREAL is not set +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_ELO is not set +CONFIG_HID_EZKEY=y +# CONFIG_HID_HOLTEK is not set +# CONFIG_HID_HUION is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_ICADE is not set +# CONFIG_HID_TWINHAN is not set +CONFIG_HID_KENSINGTON=y +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LENOVO is not set +CONFIG_HID_LOGITECH=y +# CONFIG_HID_LOGITECH_HIDPP is not set +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +# CONFIG_LOGIG940_FF is not set +# CONFIG_LOGIWHEELS_FF is not set +# CONFIG_HID_MAGICMOUSE is not set +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PENMOUNT is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_STEELSERIES is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_RMI is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_WACOM is not set +# CONFIG_HID_XINMO is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HID_SENSOR_HUB is not set + +# +# USB HID support +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# I2C HID support +# +# CONFIG_I2C_HID is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_FSM is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_XHCI_HCD is not set +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_FUSBH200_HCD is not set +# CONFIG_USB_FOTG210_HCD is not set +# CONFIG_USB_MAX3421_HCD is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HCD_TEST_MODE is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_REALTEK is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_STORAGE_ENE_UB6250 is not set +# CONFIG_USB_UAS is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USBIP_CORE is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_DWC2 is not set +# CONFIG_USB_CHIPIDEA is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_EHSET_TEST_FIXTURE is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_EZUSB_FX2 is not set +# CONFIG_USB_HSIC_USB3503 is not set +# CONFIG_USB_LINK_LAYER_TEST is not set + +# +# USB Physical Layer drivers +# +# CONFIG_USB_PHY is not set +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_AM335X_PHY_USB is not set +# CONFIG_USB_ISP1301 is not set +# CONFIG_USB_ULPI is not set +# CONFIG_USB_GADGET is not set +# CONFIG_UWB is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_EDAC is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_HYM8563 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_ISL12057 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF2127 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF85063 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1343 is not set +# CONFIG_RTC_DRV_DS1347 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set +# CONFIG_RTC_DRV_RX4581 is not set +# CONFIG_RTC_DRV_MCP795 is not set + +# +# Platform RTC drivers +# +CONFIG_RTC_DRV_HIBVT=y +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_DS2404 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_PL030 is not set +# CONFIG_RTC_DRV_PL031 is not set +# CONFIG_RTC_DRV_SNVS is not set +# CONFIG_RTC_DRV_XGENE is not set + +# +# HID Sensor RTC drivers +# +# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +# CONFIG_VIRT_DRIVERS is not set + +# +# Virtio drivers +# +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set + +# +# SOC (System On Chip) specific Drivers +# +# CONFIG_SOC_TI is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y + +# +# Common Clock Framework +# +# CONFIG_COMMON_CLK_SI5351 is not set +# CONFIG_COMMON_CLK_SI570 is not set +# CONFIG_COMMON_CLK_PXA is not set +# CONFIG_COMMON_CLK_QCOM is not set + +# +# Hardware Spinlock drivers +# + +# +# Clock Source drivers +# +CONFIG_CLKSRC_OF=y +CONFIG_CLKSRC_MMIO=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set +# CONFIG_ATMEL_PIT is not set +# CONFIG_SH_TIMER_CMT is not set +# CONFIG_SH_TIMER_MTU2 is not set +# CONFIG_SH_TIMER_TMU is not set +# CONFIG_EM_TIMER_STI is not set +# CONFIG_CLKSRC_VERSATILE is not set +# CONFIG_MAILBOX is not set +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers +# +# CONFIG_STE_MODEM_RPROC is not set + +# +# Rpmsg drivers +# + +# +# SOC (System On Chip) specific Drivers +# +# CONFIG_PM_DEVFREQ is not set +# CONFIG_EXTCON is not set +# CONFIG_MEMORY is not set +# CONFIG_IIO is not set +# CONFIG_PWM is not set +CONFIG_IRQCHIP=y +CONFIG_ARM_GIC=y +# CONFIG_IPACK_BUS is not set +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_RESET_CONTROLLER=y +# CONFIG_FMC is not set + +# +# PHY Subsystem +# +CONFIG_GENERIC_PHY=y +# CONFIG_BCM_KONA_USB2_PHY is not set +CONFIG_PHY_HISI_INNO_USB2=y +CONFIG_HI_NANO_PHY_SATA=y +CONFIG_HI_SATA_PORTS=2 +CONFIG_HI_SATA_MODE=1 +# CONFIG_POWERCAP is not set +# CONFIG_MCB is not set +CONFIG_HI_DMAC=y +CONFIG_HI_DMAC_CHANNEL_NUM=4 + +# +# Hisilicon driver support +# + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set +# CONFIG_OVERLAY_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_KERNFS=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=m +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set +CONFIG_YAFFS_XATTR=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_LOGFS is not set +CONFIG_CRAMFS=y +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_F2FS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V2=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_SWAP is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +CONFIG_GRACE_PERIOD=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +CONFIG_NLS_UTF8=y +# CONFIG_DLM is not set + +# +# Kernel hacking +# + +# +# printk and dmesg options +# +# CONFIG_PRINTK_TIME is not set +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 +# CONFIG_BOOT_PRINTK_DELAY is not set + +# +# Compile-time checks and compiler options +# +# CONFIG_DEBUG_INFO is not set +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_FRAME_WARN=1024 +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_MAGIC_SYSRQ is not set +CONFIG_DEBUG_KERNEL=y + +# +# Memory Debugging +# +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_VM is not set +CONFIG_DEBUG_MEMORY_INIT=y +# CONFIG_DEBUG_PER_CPU_MAPS is not set +# CONFIG_DEBUG_HIGHMEM is not set +# CONFIG_DEBUG_SHIRQ is not set + +# +# Debug Lockups and Hangs +# +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_SCHED_STACK_END_CHECK is not set +# CONFIG_TIMER_STATS is not set + +# +# Lock Debugging (spinlocks, mutexes, etc...) +# +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_LOCK_TORTURE_TEST is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_PI_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set + +# +# RCU Debugging +# +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_TORTURE_TEST is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +# CONFIG_RCU_CPU_STALL_INFO is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set + +# +# Runtime Testing +# +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_INTERVAL_TREE_TEST is not set +# CONFIG_PERCPU_TEST is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_TEST_RHASHTABLE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_TEST_LKM is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_TEST_BPF is not set +# CONFIG_TEST_FIRMWARE is not set +# CONFIG_TEST_UDELAY is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_ARM_PTDUMP is not set +CONFIG_STRICT_DEVMEM=y +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_LL is not set +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +# CONFIG_DEBUG_UART_PL01X is not set +# CONFIG_DEBUG_UART_8250 is not set +# CONFIG_DEBUG_UART_BCM63XX is not set +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +# CONFIG_OC_ETM is not set +# CONFIG_PID_IN_CONTEXTIDR is not set +# CONFIG_DEBUG_SET_MODULE_RONX is not set +# CONFIG_CORESIGHT is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_PCRYPT is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_MCRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_CMAC is not set +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_CRCT10DIF is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA1_ARM is not set +# CONFIG_CRYPTO_SHA1_ARM_NEON is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_SHA512_ARM_NEON is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_ARM is not set +# CONFIG_CRYPTO_AES_ARM_BS is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_DRBG_MENU is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_NET_UTILS=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y +# CONFIG_CRC_CCITT is not set +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set +# CONFIG_RANDOM32_SELFTEST is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_DECOMPRESS=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_XZ=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_DECOMPRESS_LZ4=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_GLOB=y +# CONFIG_GLOB_SELFTEST is not set +CONFIG_NLATTR=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +# CONFIG_AVERAGE is not set +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set +CONFIG_LIBFDT=y +CONFIG_ARCH_HAS_SG_CHAIN=y +# CONFIG_VIRTUALIZATION is not set diff --git a/br-ext-chip-hisilicon/board/hi3536cv100/kernel/overlay/include/linux/compiler-gcc7.h b/br-ext-chip-hisilicon/board/hi3536cv100/kernel/overlay/include/linux/compiler-gcc7.h new file mode 100644 index 00000000..613f9936 --- /dev/null +++ b/br-ext-chip-hisilicon/board/hi3536cv100/kernel/overlay/include/linux/compiler-gcc7.h @@ -0,0 +1,65 @@ +#ifndef __LINUX_COMPILER_H +#error "Please don't include <linux/compiler-gcc7.h> directly, include <linux/compiler.h> instead." +#endif + +#define __used __attribute__((__used__)) +#define __must_check __attribute__((warn_unused_result)) +#define __compiler_offsetof(a, b) __builtin_offsetof(a, b) + +/* Mark functions as cold. gcc will assume any path leading to a call + to them will be unlikely. This means a lot of manual unlikely()s + are unnecessary now for any paths leading to the usual suspects + like BUG(), printk(), panic() etc. [but let's keep them for now for + older compilers] + + Early snapshots of gcc 4.3 don't support this and we can't detect this + in the preprocessor, but we can live with this because they're unreleased. + Maketime probing would be overkill here. + + gcc also has a __attribute__((__hot__)) to move hot functions into + a special section, but I don't see any sense in this right now in + the kernel context */ +#define __cold __attribute__((__cold__)) + +#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) + +#ifndef __CHECKER__ +# define __compiletime_warning(message) __attribute__((warning(message))) +# define __compiletime_error(message) __attribute__((error(message))) +#endif /* __CHECKER__ */ + +/* + * Mark a position in code as unreachable. This can be used to + * suppress control flow warnings after asm blocks that transfer + * control elsewhere. + * + * Early snapshots of gcc 4.5 don't support this and we can't detect + * this in the preprocessor, but we can live with this because they're + * unreleased. Really, we need to have autoconf for the kernel. + */ +#define unreachable() __builtin_unreachable() + +/* Mark a function definition as prohibited from being cloned. */ +#define __noclone __attribute__((__noclone__)) + +/* + * Tell the optimizer that something else uses this function or variable. + */ +#define __visible __attribute__((externally_visible)) + +/* + * GCC 'asm goto' miscompiles certain code sequences: + * + * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58670 + * + * Work it around via a compiler barrier quirk suggested by Jakub Jelinek. + * + * (asm goto is automatically volatile - the naming reflects this.) + */ +#define asm_volatile_goto(x...) do { asm goto(x); asm (""); } while (0) + +#ifdef CONFIG_ARCH_USE_BUILTIN_BSWAP +#define __HAVE_BUILTIN_BSWAP32__ +#define __HAVE_BUILTIN_BSWAP64__ +#define __HAVE_BUILTIN_BSWAP16__ +#endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP */ diff --git a/br-ext-chip-hisilicon/board/hi3536cv100/kernel/patches/00_hi3536c_for_linux_v3.18.y.patch b/br-ext-chip-hisilicon/board/hi3536cv100/kernel/patches/00_hi3536c_for_linux_v3.18.y.patch new file mode 100755 index 00000000..686771ff --- /dev/null +++ b/br-ext-chip-hisilicon/board/hi3536cv100/kernel/patches/00_hi3536c_for_linux_v3.18.y.patch @@ -0,0 +1,326083 @@ +diff --git a/Documentation/ABI/testing/configfs-usb-gadget-midi b/Documentation/ABI/testing/configfs-usb-gadget-midi +new file mode 100644 +index 0000000..6b341df +--- /dev/null ++++ b/Documentation/ABI/testing/configfs-usb-gadget-midi +@@ -0,0 +1,12 @@ ++What: /config/usb-gadget/gadget/functions/midi.name ++Date: Nov 2014 ++KernelVersion: 3.19 ++Description: ++ The attributes: ++ ++ index - index value for the USB MIDI adapter ++ id - ID string for the USB MIDI adapter ++ buflen - MIDI buffer length ++ qlen - USB read request queue length ++ in_ports - number of MIDI input ports ++ out_ports - number of MIDI output ports +diff --git a/Documentation/ABI/testing/sysfs-class-dual-role-usb b/Documentation/ABI/testing/sysfs-class-dual-role-usb +new file mode 100644 +index 0000000..a900fd7 +--- /dev/null ++++ b/Documentation/ABI/testing/sysfs-class-dual-role-usb +@@ -0,0 +1,71 @@ ++What: /sys/class/dual_role_usb/.../ ++Date: June 2015 ++Contact: Badhri Jagan Sridharan<badhri@google.com> ++Description: ++ Provide a generic interface to monitor and change ++ the state of dual role usb ports. The name here ++ refers to the name mentioned in the ++ dual_role_phy_desc that is passed while registering ++ the dual_role_phy_intstance through ++ devm_dual_role_instance_register. ++ ++What: /sys/class/dual_role_usb/.../supported_modes ++Date: June 2015 ++Contact: Badhri Jagan Sridharan<badhri@google.com> ++Description: ++ This is a static node, once initialized this ++ is not expected to change during runtime. "dfp" ++ refers to "downstream facing port" i.e. port can ++ only act as host. "ufp" refers to "upstream ++ facing port" i.e. port can only act as device. ++ "dfp ufp" refers to "dual role port" i.e. the port ++ can either be a host port or a device port. ++ ++What: /sys/class/dual_role_usb/.../mode ++Date: June 2015 ++Contact: Badhri Jagan Sridharan<badhri@google.com> ++Description: ++ The mode node refers to the current mode in which the ++ port is operating. "dfp" for host ports. "ufp" for device ++ ports and "none" when cable is not connected. ++ ++ On devices where the USB mode is software-controllable, ++ userspace can change the mode by writing "dfp" or "ufp". ++ On devices where the USB mode is fixed in hardware, ++ this attribute is read-only. ++ ++What: /sys/class/dual_role_usb/.../power_role ++Date: June 2015 ++Contact: Badhri Jagan Sridharan<badhri@google.com> ++Description: ++ The power_role node mentions whether the port ++ is "sink"ing or "source"ing power. "none" if ++ they are not connected. ++ ++ On devices implementing USB Power Delivery, ++ userspace can control the power role by writing "sink" or ++ "source". On devices without USB-PD, this attribute is ++ read-only. ++ ++What: /sys/class/dual_role_usb/.../data_role ++Date: June 2015 ++Contact: Badhri Jagan Sridharan<badhri@google.com> ++Description: ++ The data_role node mentions whether the port ++ is acting as "host" or "device" for USB data connection. ++ "none" if there is no active data link. ++ ++ On devices implementing USB Power Delivery, userspace ++ can control the data role by writing "host" or "device". ++ On devices without USB-PD, this attribute is read-only ++ ++What: /sys/class/dual_role_usb/.../powers_vconn ++Date: June 2015 ++Contact: Badhri Jagan Sridharan<badhri@google.com> ++Description: ++ The powers_vconn node mentions whether the port ++ is supplying power for VCONN pin. ++ ++ On devices with software control of VCONN, ++ userspace can disable the power supply to VCONN by writing "n", ++ or enable the power supply by writing "y". +diff --git a/Documentation/ABI/testing/sysfs-kernel-wakeup_reasons b/Documentation/ABI/testing/sysfs-kernel-wakeup_reasons +new file mode 100644 +index 0000000..acb19b9 +--- /dev/null ++++ b/Documentation/ABI/testing/sysfs-kernel-wakeup_reasons +@@ -0,0 +1,16 @@ ++What: /sys/kernel/wakeup_reasons/last_resume_reason ++Date: February 2014 ++Contact: Ruchi Kandoi <kandoiruchi@google.com> ++Description: ++ The /sys/kernel/wakeup_reasons/last_resume_reason is ++ used to report wakeup reasons after system exited suspend. ++ ++What: /sys/kernel/wakeup_reasons/last_suspend_time ++Date: March 2015 ++Contact: jinqian <jinqian@google.com> ++Description: ++ The /sys/kernel/wakeup_reasons/last_suspend_time is ++ used to report time spent in last suspend cycle. It contains ++ two numbers (in seconds) separated by space. First number is ++ the time spent in suspend and resume processes. Second number ++ is the time spent in sleep state. +\ No newline at end of file +diff --git a/Documentation/android.txt b/Documentation/android.txt +new file mode 100644 +index 0000000..0f40a78 +--- /dev/null ++++ b/Documentation/android.txt +@@ -0,0 +1,121 @@ ++ ============= ++ A N D R O I D ++ ============= ++ ++Copyright (C) 2009 Google, Inc. ++Written by Mike Chan <mike@android.com> ++ ++CONTENTS: ++--------- ++ ++1. Android ++ 1.1 Required enabled config options ++ 1.2 Required disabled config options ++ 1.3 Recommended enabled config options ++2. Contact ++ ++ ++1. Android ++========== ++ ++Android (www.android.com) is an open source operating system for mobile devices. ++This document describes configurations needed to run the Android framework on ++top of the Linux kernel. ++ ++To see a working defconfig look at msm_defconfig or goldfish_defconfig ++which can be found at http://android.git.kernel.org in kernel/common.git ++and kernel/msm.git ++ ++ ++1.1 Required enabled config options ++----------------------------------- ++After building a standard defconfig, ensure that these options are enabled in ++your .config or defconfig if they are not already. Based off the msm_defconfig. ++You should keep the rest of the default options enabled in the defconfig ++unless you know what you are doing. ++ ++ANDROID_PARANOID_NETWORK ++ASHMEM ++CONFIG_FB_MODE_HELPERS ++CONFIG_FONT_8x16 ++CONFIG_FONT_8x8 ++CONFIG_YAFFS_SHORT_NAMES_IN_RAM ++DAB ++EARLYSUSPEND ++FB ++FB_CFB_COPYAREA ++FB_CFB_FILLRECT ++FB_CFB_IMAGEBLIT ++FB_DEFERRED_IO ++FB_TILEBLITTING ++HIGH_RES_TIMERS ++INOTIFY ++INOTIFY_USER ++INPUT_EVDEV ++INPUT_GPIO ++INPUT_MISC ++LEDS_CLASS ++LEDS_GPIO ++LOCK_KERNEL ++LkOGGER ++LOW_MEMORY_KILLER ++MISC_DEVICES ++NEW_LEDS ++NO_HZ ++POWER_SUPPLY ++PREEMPT ++RAMFS ++RTC_CLASS ++RTC_LIB ++SWITCH ++SWITCH_GPIO ++TMPFS ++UID_STAT ++UID16 ++USB_FUNCTION ++USB_FUNCTION_ADB ++USER_WAKELOCK ++VIDEO_OUTPUT_CONTROL ++WAKELOCK ++YAFFS_AUTO_YAFFS2 ++YAFFS_FS ++YAFFS_YAFFS1 ++YAFFS_YAFFS2 ++ ++ ++1.2 Required disabled config options ++------------------------------------ ++CONFIG_YAFFS_DISABLE_LAZY_LOAD ++DNOTIFY ++ ++ ++1.3 Recommended enabled config options ++------------------------------ ++ANDROID_PMEM ++PSTORE_CONSOLE ++PSTORE_RAM ++SCHEDSTATS ++DEBUG_PREEMPT ++DEBUG_MUTEXES ++DEBUG_SPINLOCK_SLEEP ++DEBUG_INFO ++FRAME_POINTER ++CPU_FREQ ++CPU_FREQ_TABLE ++CPU_FREQ_DEFAULT_GOV_ONDEMAND ++CPU_FREQ_GOV_ONDEMAND ++CRC_CCITT ++EMBEDDED ++INPUT_TOUCHSCREEN ++I2C ++I2C_BOARDINFO ++LOG_BUF_SHIFT=17 ++SERIAL_CORE ++SERIAL_CORE_CONSOLE ++ ++ ++2. Contact ++========== ++website: http://android.git.kernel.org ++ ++mailing-lists: android-kernel@googlegroups.com +diff --git a/Documentation/arm/small_task_packing.txt b/Documentation/arm/small_task_packing.txt +new file mode 100644 +index 0000000..613f2aa +--- /dev/null ++++ b/Documentation/arm/small_task_packing.txt +@@ -0,0 +1,135 @@ ++Small Task Packing in the big.LITTLE MP Reference Patch Set ++ ++What is small task packing? ++---- ++Simply that the scheduler will fit as many small tasks on a single CPU ++as possible before using other CPUs. A small task is defined as one ++whose tracked load is less than 90% of a NICE_0 task. This is a change ++from the usual behavior since the scheduler will normally use an idle ++CPU for a waking task unless that task is considered cache hot. ++ ++ ++How is it implemented? ++---- ++Since all small tasks must wake up relatively frequently, the main ++requirement for packing small tasks is to select a partly-busy CPU when ++waking rather than looking for an idle CPU. We use the tracked load of ++the CPU runqueue to determine how heavily loaded each CPU is and the ++tracked load of the task to determine if it will fit on the CPU. We ++always start with the lowest-numbered CPU in a sched domain and stop ++looking when we find a CPU with enough space for the task. ++ ++Some further tweaks are necessary to suppress load balancing when the ++CPU is not fully loaded, otherwise the scheduler attempts to spread ++tasks evenly across the domain. ++ ++ ++How does it interact with the HMP patches? ++---- ++Firstly, we only enable packing on the little domain. The intent is that ++the big domain is intended to spread tasks amongst the available CPUs ++one-task-per-CPU. The little domain however is attempting to use as ++little power as possible while servicing its tasks. ++ ++Secondly, since we offload big tasks onto little CPUs in order to try ++to devote one CPU to each task, we have a threshold above which we do ++not try to pack a task and instead will select an idle CPU if possible. ++This maintains maximum forward progress for busy tasks temporarily ++demoted from big CPUs. ++ ++ ++Can the behaviour be tuned? ++---- ++Yes, the load level of a 'full' CPU can be easily modified in the source ++and is exposed through sysfs as /sys/kernel/hmp/packing_limit to be ++changed at runtime. The presence of the packing behaviour is controlled ++by CONFIG_SCHED_HMP_LITTLE_PACKING and can be disabled at run-time ++using /sys/kernel/hmp/packing_enable. ++The definition of a small task is hard coded as 90% of NICE_0_LOAD ++and cannot be modified at run time. ++ ++ ++Why do I need to tune it? ++---- ++The optimal configuration is likely to be different depending upon the ++design and manufacturing of your SoC. ++ ++In the main, there are two system effects from enabling small task ++packing. ++ ++1. CPU operating point may increase ++2. wakeup latency of tasks may be increased ++ ++There are also likely to be secondary effects from loading one CPU ++rather than spreading tasks. ++ ++Note that all of these system effects are dependent upon the workload ++under consideration. ++ ++ ++CPU Operating Point ++---- ++The primary impact of loading one CPU with a number of light tasks is to ++increase the compute requirement of that CPU since it is no longer idle ++as often. Increased compute requirement causes an increase in the ++frequency of the CPU through CPUfreq. ++ ++Consider this example: ++We have a system with 3 CPUs which can operate at any frequency between ++350MHz and 1GHz. The system has 6 tasks which would each produce 10% ++load at 1GHz. The scheduler has frequency-invariant load scaling ++enabled. Our DVFS governor aims for 80% utilization at the chosen ++frequency. ++ ++Without task packing, these tasks will be spread out amongst all CPUs ++such that each has 2. This will produce roughly 20% system load, and ++the frequency of the package will remain at 350MHz. ++ ++With task packing set to the default packing_limit, all of these tasks ++will sit on one CPU and require a package frequency of ~750MHz to reach ++80% utilization. (0.75 = 0.6 * 0.8). ++ ++When a package operates on a single frequency domain, all CPUs in that ++package share frequency and voltage. ++ ++Depending upon the SoC implementation there can be a significant amount ++of energy lost to leakage from idle CPUs. The decision about how ++loaded a CPU must be to be considered 'full' is therefore controllable ++through sysfs (sys/kernel/hmp/packing_limit) and directly in the code. ++ ++Continuing the example, lets set packing_limit to 450 which means we ++will pack tasks until the total load of all running tasks >= 450. In ++practise, this is very similar to a 55% idle 1Ghz CPU. ++ ++Now we are only able to place 4 tasks on CPU0, and two will overflow ++onto CPU1. CPU0 will have a load of 40% and CPU1 will have a load of ++20%. In order to still hit 80% utilization, CPU0 now only needs to ++operate at (0.4*0.8=0.32) 320MHz, which means that the lowest operating ++point will be selected, the same as in the non-packing case, except that ++now CPU2 is no longer needed and can be power-gated. ++ ++In order to use less energy, the saving from power-gating CPU2 must be ++more than the energy spent running CPU0 for the extra cycles. This ++depends upon the SoC implementation. ++ ++This is obviously a contrived example requiring all the tasks to ++be runnable at the same time, but it illustrates the point. ++ ++ ++Wakeup Latency ++---- ++This is an unavoidable consequence of trying to pack tasks together ++rather than giving them a CPU each. If you cannot find an acceptable ++level of wakeup latency, you should turn packing off. ++ ++Cyclictest is a good test application for determining the added latency ++when configuring packing. ++ ++ ++Why is it turned off for the VersatileExpress V2P_CA15A7 CoreTile? ++---- ++Simply, this core tile only has power gating for the whole A7 package. ++When small task packing is enabled, all our low-energy use cases ++normally fit onto one A7 CPU. We therefore end up with 2 mostly-idle ++CPUs and one mostly-busy CPU. This decreases the amount of time ++available where the whole package is idle and can be turned off. +diff --git a/Documentation/arm64/legacy_instructions.txt b/Documentation/arm64/legacy_instructions.txt +new file mode 100644 +index 0000000..01bf3d9 +--- /dev/null ++++ b/Documentation/arm64/legacy_instructions.txt +@@ -0,0 +1,57 @@ ++The arm64 port of the Linux kernel provides infrastructure to support ++emulation of instructions which have been deprecated, or obsoleted in ++the architecture. The infrastructure code uses undefined instruction ++hooks to support emulation. Where available it also allows turning on ++the instruction execution in hardware. ++ ++The emulation mode can be controlled by writing to sysctl nodes ++(/proc/sys/abi). The following explains the different execution ++behaviours and the corresponding values of the sysctl nodes - ++ ++* Undef ++ Value: 0 ++ Generates undefined instruction abort. Default for instructions that ++ have been obsoleted in the architecture, e.g., SWP ++ ++* Emulate ++ Value: 1 ++ Uses software emulation. To aid migration of software, in this mode ++ usage of emulated instruction is traced as well as rate limited ++ warnings are issued. This is the default for deprecated ++ instructions, .e.g., CP15 barriers ++ ++* Hardware Execution ++ Value: 2 ++ Although marked as deprecated, some implementations may support the ++ enabling/disabling of hardware support for the execution of these ++ instructions. Using hardware execution generally provides better ++ performance, but at the loss of ability to gather runtime statistics ++ about the use of the deprecated instructions. ++ ++The default mode depends on the status of the instruction in the ++architecture. Deprecated instructions should default to emulation ++while obsolete instructions must be undefined by default. ++ ++Note: Instruction emulation may not be possible in all cases. See ++individual instruction notes for further information. ++ ++Supported legacy instructions ++----------------------------- ++* SWP{B} ++Node: /proc/sys/abi/swp ++Status: Obsolete ++Default: Undef (0) ++ ++* CP15 Barriers ++Node: /proc/sys/abi/cp15_barrier ++Status: Deprecated ++Default: Emulate (1) ++ ++* SETEND ++Node: /proc/sys/abi/setend ++Status: Deprecated ++Default: Emulate (1)* ++Note: All the cpus on the system must have mixed endian support at EL0 ++for this feature to be enabled. If a new CPU - which doesn't support mixed ++endian - is hotplugged in after this feature has been enabled, there could ++be unexpected results in the application. +diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt +index 10c949b..925b820 100644 +--- a/Documentation/cgroups/cgroups.txt ++++ b/Documentation/cgroups/cgroups.txt +@@ -578,6 +578,15 @@ is completely unused; @cgrp->parent is still valid. (Note - can also + be called for a newly-created cgroup if an error occurs after this + subsystem's create() method has been called for the new cgroup). + ++int allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) ++(cgroup_mutex held by caller) ++ ++Called prior to moving a task into a cgroup; if the subsystem ++returns an error, this will abort the attach operation. Used ++to extend the permission checks - if all subsystems in a cgroup ++return 0, the attach will be allowed to proceed, even if the ++default permission check (root or same user) fails. ++ + int can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) + (cgroup_mutex held by caller) + +diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt +index 77ec215..2ed766e 100644 +--- a/Documentation/cpu-freq/governors.txt ++++ b/Documentation/cpu-freq/governors.txt +@@ -28,6 +28,7 @@ Contents: + 2.3 Userspace + 2.4 Ondemand + 2.5 Conservative ++2.6 Interactive + + 3. The Governor Interface in the CPUfreq Core + +@@ -218,6 +219,90 @@ a decision on when to decrease the frequency while running in any + speed. Load for frequency increase is still evaluated every + sampling rate. + ++2.6 Interactive ++--------------- ++ ++The CPUfreq governor "interactive" is designed for latency-sensitive, ++interactive workloads. This governor sets the CPU speed depending on ++usage, similar to "ondemand" and "conservative" governors, but with a ++different set of configurable behaviors. ++ ++The tuneable values for this governor are: ++ ++target_loads: CPU load values used to adjust speed to influence the ++current CPU load toward that value. In general, the lower the target ++load, the more often the governor will raise CPU speeds to bring load ++below the target. The format is a single target load, optionally ++followed by pairs of CPU speeds and CPU loads to target at or above ++those speeds. Colons can be used between the speeds and associated ++target loads for readability. For example: ++ ++ 85 1000000:90 1700000:99 ++ ++targets CPU load 85% below speed 1GHz, 90% at or above 1GHz, until ++1.7GHz and above, at which load 99% is targeted. If speeds are ++specified these must appear in ascending order. Higher target load ++values are typically specified for higher speeds, that is, target load ++values also usually appear in an ascending order. The default is ++target load 90% for all speeds. ++ ++min_sample_time: The minimum amount of time to spend at the current ++frequency before ramping down. Default is 80000 uS. ++ ++hispeed_freq: An intermediate "hi speed" at which to initially ramp ++when CPU load hits the value specified in go_hispeed_load. If load ++stays high for the amount of time specified in above_hispeed_delay, ++then speed may be bumped higher. Default is the maximum speed ++allowed by the policy at governor initialization time. ++ ++go_hispeed_load: The CPU load at which to ramp to hispeed_freq. ++Default is 99%. ++ ++above_hispeed_delay: When speed is at or above hispeed_freq, wait for ++this long before raising speed in response to continued high load. ++The format is a single delay value, optionally followed by pairs of ++CPU speeds and the delay to use at or above those speeds. Colons can ++be used between the speeds and associated delays for readability. For ++example: ++ ++ 80000 1300000:200000 1500000:40000 ++ ++uses delay 80000 uS until CPU speed 1.3 GHz, at which speed delay ++200000 uS is used until speed 1.5 GHz, at which speed (and above) ++delay 40000 uS is used. If speeds are specified these must appear in ++ascending order. Default is 20000 uS. ++ ++timer_rate: Sample rate for reevaluating CPU load when the CPU is not ++idle. A deferrable timer is used, such that the CPU will not be woken ++from idle to service this timer until something else needs to run. ++(The maximum time to allow deferring this timer when not running at ++minimum speed is configurable via timer_slack.) Default is 20000 uS. ++ ++timer_slack: Maximum additional time to defer handling the governor ++sampling timer beyond timer_rate when running at speeds above the ++minimum. For platforms that consume additional power at idle when ++CPUs are running at speeds greater than minimum, this places an upper ++bound on how long the timer will be deferred prior to re-evaluating ++load and dropping speed. For example, if timer_rate is 20000uS and ++timer_slack is 10000uS then timers will be deferred for up to 30msec ++when not at lowest speed. A value of -1 means defer timers ++indefinitely at all speeds. Default is 80000 uS. ++ ++boost: If non-zero, immediately boost speed of all CPUs to at least ++hispeed_freq until zero is written to this attribute. If zero, allow ++CPU speeds to drop below hispeed_freq according to load as usual. ++Default is zero. ++ ++boostpulse: On each write, immediately boost speed of all CPUs to ++hispeed_freq for at least the period of time specified by ++boostpulse_duration, after which speeds are allowed to drop below ++hispeed_freq according to load as usual. ++ ++boostpulse_duration: Length of time to hold CPU speed at hispeed_freq ++on a write to boostpulse, before allowing speed to drop according to ++load as usual. Default is 80000 uS. ++ ++ + 3. The Governor Interface in the CPUfreq Core + ============================================= + +diff --git a/Documentation/device-mapper/dm-crypt.txt b/Documentation/device-mapper/dm-crypt.txt +index c81839b..692171f 100644 +--- a/Documentation/device-mapper/dm-crypt.txt ++++ b/Documentation/device-mapper/dm-crypt.txt +@@ -5,7 +5,7 @@ Device-Mapper's "crypt" target provides transparent encryption of block devices + using the kernel crypto API. + + For a more detailed description of supported parameters see: +-http://code.google.com/p/cryptsetup/wiki/DMCrypt ++https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt + + Parameters: <cipher> <key> <iv_offset> <device path> \ + <offset> [<#opt_params> <opt_params>] +@@ -51,7 +51,7 @@ Parameters: <cipher> <key> <iv_offset> <device path> \ + Otherwise #opt_params is the number of following arguments. + + Example of optional parameters section: +- 1 allow_discards ++ 3 allow_discards same_cpu_crypt submit_from_crypt_cpus + + allow_discards + Block discard requests (a.k.a. TRIM) are passed through the crypt device. +@@ -63,11 +63,24 @@ allow_discards + used space etc.) if the discarded blocks can be located easily on the + device later. + ++same_cpu_crypt ++ Perform encryption using the same cpu that IO was submitted on. ++ The default is to use an unbound workqueue so that encryption work ++ is automatically balanced between available CPUs. ++ ++submit_from_crypt_cpus ++ Disable offloading writes to a separate thread after encryption. ++ There are some situations where offloading write bios from the ++ encryption threads to a single thread degrades performance ++ significantly. The default is to offload write bios to the same ++ thread because it benefits CFQ to have writes submitted using the ++ same context. ++ + Example scripts + =============== + LUKS (Linux Unified Key Setup) is now the preferred way to set up disk + encryption with dm-crypt using the 'cryptsetup' utility, see +-http://code.google.com/p/cryptsetup/ ++https://gitlab.com/cryptsetup/cryptsetup + + [[ + #!/bin/sh +diff --git a/Documentation/device-mapper/verity.txt b/Documentation/device-mapper/verity.txt +index 9884681..0075f70 100644 +--- a/Documentation/device-mapper/verity.txt ++++ b/Documentation/device-mapper/verity.txt +@@ -125,7 +125,7 @@ block boundary) are the hash blocks which are stored a depth at a time + + The full specification of kernel parameters and on-disk metadata format + is available at the cryptsetup project's wiki page +- http://code.google.com/p/cryptsetup/wiki/DMVerity ++ https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity + + Status + ====== +@@ -142,7 +142,7 @@ Set up a device: + + A command line tool veritysetup is available to compute or verify + the hash tree or activate the kernel device. This is available from +-the cryptsetup upstream repository http://code.google.com/p/cryptsetup/ ++the cryptsetup upstream repository https://gitlab.com/cryptsetup/cryptsetup/ + (as a libcryptsetup extension). + + Create hash on the device: +diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt +new file mode 100644 +index 0000000..88602b7 +--- /dev/null ++++ b/Documentation/devicetree/bindings/arm/coresight.txt +@@ -0,0 +1,199 @@ ++* CoreSight Components: ++ ++CoreSight components are compliant with the ARM CoreSight architecture ++specification and can be connected in various topologies to suit a particular ++SoCs tracing needs. These trace components can generally be classified as ++sinks, links and sources. Trace data produced by one or more sources flows ++through the intermediate links connecting the source to the currently selected ++sink. Each CoreSight component device should use these properties to describe ++its hardware characteristcs. ++ ++* Required properties for all components *except* non-configurable replicators: ++ ++ * compatible: These have to be supplemented with "arm,primecell" as ++ drivers are using the AMBA bus interface. Possible values include: ++ - "arm,coresight-etb10", "arm,primecell"; ++ - "arm,coresight-tpiu", "arm,primecell"; ++ - "arm,coresight-tmc", "arm,primecell"; ++ - "arm,coresight-funnel", "arm,primecell"; ++ - "arm,coresight-etm3x", "arm,primecell"; ++ ++ * reg: physical base address and length of the register ++ set(s) of the component. ++ ++ * clocks: the clock associated to this component. ++ ++ * clock-names: the name of the clock as referenced by the code. ++ Since we are using the AMBA framework, the name should be ++ "apb_pclk". ++ ++ * port or ports: The representation of the component's port ++ layout using the generic DT graph presentation found in ++ "bindings/graph.txt". ++ ++* Required properties for devices that don't show up on the AMBA bus, such as ++ non-configurable replicators: ++ ++ * compatible: Currently supported value is (note the absence of the ++ AMBA markee): ++ - "arm,coresight-replicator" ++ ++ * port or ports: same as above. ++ ++* Optional properties for ETM/PTMs: ++ ++ * arm,cp14: must be present if the system accesses ETM/PTM management ++ registers via co-processor 14. ++ ++ * cpu: the cpu phandle this ETM/PTM is affined to. When omitted the ++ source is considered to belong to CPU0. ++ ++* Optional property for TMC: ++ ++ * arm,buffer-size: size of contiguous buffer space for TMC ETR ++ (embedded trace router) ++ ++ ++Example: ++ ++1. Sinks ++ etb@20010000 { ++ compatible = "arm,coresight-etb10", "arm,primecell"; ++ reg = <0 0x20010000 0 0x1000>; ++ ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ port { ++ etb_in_port: endpoint@0 { ++ slave-mode; ++ remote-endpoint = <&replicator_out_port0>; ++ }; ++ }; ++ }; ++ ++ tpiu@20030000 { ++ compatible = "arm,coresight-tpiu", "arm,primecell"; ++ reg = <0 0x20030000 0 0x1000>; ++ ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ port { ++ tpiu_in_port: endpoint@0 { ++ slave-mode; ++ remote-endpoint = <&replicator_out_port1>; ++ }; ++ }; ++ }; ++ ++2. Links ++ replicator { ++ /* non-configurable replicators don't show up on the ++ * AMBA bus. As such no need to add "arm,primecell". ++ */ ++ compatible = "arm,coresight-replicator"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* replicator output ports */ ++ port@0 { ++ reg = <0>; ++ replicator_out_port0: endpoint { ++ remote-endpoint = <&etb_in_port>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ replicator_out_port1: endpoint { ++ remote-endpoint = <&tpiu_in_port>; ++ }; ++ }; ++ ++ /* replicator input port */ ++ port@2 { ++ reg = <0>; ++ replicator_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&funnel_out_port0>; ++ }; ++ }; ++ }; ++ }; ++ ++ funnel@20040000 { ++ compatible = "arm,coresight-funnel", "arm,primecell"; ++ reg = <0 0x20040000 0 0x1000>; ++ ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* funnel output port */ ++ port@0 { ++ reg = <0>; ++ funnel_out_port0: endpoint { ++ remote-endpoint = ++ <&replicator_in_port0>; ++ }; ++ }; ++ ++ /* funnel input ports */ ++ port@1 { ++ reg = <0>; ++ funnel_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm0_out_port>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <1>; ++ funnel_in_port1: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm1_out_port>; ++ }; ++ }; ++ ++ port@3 { ++ reg = <2>; ++ funnel_in_port2: endpoint { ++ slave-mode; ++ remote-endpoint = <&etm0_out_port>; ++ }; ++ }; ++ ++ }; ++ }; ++ ++3. Sources ++ ptm@2201c000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0x2201c000 0 0x1000>; ++ ++ cpu = <&cpu0>; ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ port { ++ ptm0_out_port: endpoint { ++ remote-endpoint = <&funnel_in_port0>; ++ }; ++ }; ++ }; ++ ++ ptm@2201d000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0x2201d000 0 0x1000>; ++ ++ cpu = <&cpu1>; ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ port { ++ ptm1_out_port: endpoint { ++ remote-endpoint = <&funnel_in_port1>; ++ }; ++ }; ++ }; +diff --git a/Documentation/devicetree/bindings/clock/hi3519-clock.txt b/Documentation/devicetree/bindings/clock/hi3519-clock.txt +new file mode 100644 +index 0000000..9fea878 +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/hi3519-clock.txt +@@ -0,0 +1,46 @@ ++* Hisilicon Hi3519 Clock and Reset Generator(CRG) ++ ++The Hi3519 CRG module provides clock and reset signals to various ++controllers within the SoC. ++ ++This binding uses the following bindings: ++ Documentation/devicetree/bindings/clock/clock-bindings.txt ++ Documentation/devicetree/bindings/reset/reset.txt ++ ++Required Properties: ++ ++- compatible: should be one of the following. ++ - "hisilicon,hi3519-clock" - controller compatible with Hi3519 SoC. ++ ++- reg: physical base address of the controller and length of memory mapped ++ region. ++ ++- #clock-cells: should be 1. ++ ++Each clock is assigned an identifier and client nodes use this identifier ++to specify the clock which they consume. ++ ++All these identifier could be found in <dt-bindings/clock/hi3519-clock.h>. ++ ++- #reset-cells: should be 2. ++ ++A reset signal can be controlled by writing a bit register in the CRG module. ++The reset specifier consists of two cells. The first cell represents the ++register offset relative to the base address. The second cell represents the ++bit index in the register. ++ ++Example: CRG nodes ++CRG: clock-reset-controller { ++ compatible = "hisilicon,hi3519-clock"; ++ reg = <0x12010000 0x10000>; ++ #clock-cells = <1>; ++ #reset-cells = <2>; ++}; ++ ++Example: consumer nodes ++i2c0: i2c@0x12110000 { ++ compatible = "hisilicon,hi3519-i2c"; ++ reg = <0x12110000 0x1000>; ++ clocks = <&CRG HI3519_I2C0_RST>;*/ ++ resets = <&CRG 0xE4 0>; ++}; +diff --git a/Documentation/devicetree/bindings/i2c/i2c-hibvt.txt b/Documentation/devicetree/bindings/i2c/i2c-hibvt.txt +new file mode 100644 +index 0000000..db3d2e2 +--- /dev/null ++++ b/Documentation/devicetree/bindings/i2c/i2c-hibvt.txt +@@ -0,0 +1,24 @@ ++Hisilicon BVT I2C master controller ++ ++Required properties: ++- compatible: should be "hisilicon,hibvt-i2c" and one of the following: ++ "hisilicon,hi3516cv300-i2c" ++- reg: physical base address of the controller and length of memory mapped. ++ region. ++- interrupts: interrupt number to the cpu. ++- clocks: phandles to input clocks. ++ ++Optional properties: ++- clock-frequency: Desired I2C bus frequency in Hz, otherwise defaults to 100000. ++ ++Other properties: ++see Documentation/devicetree/bindings/i2c/i2c.txt. ++ ++Examples: ++i2c_bus0: i2c@12110000 { ++ compatible = "hisilicon,hi3516cv300-i2c", "hisilicon,hibvt-i2c"; ++ reg = <0x12110000 0x100>; ++ interrupts = <20>; ++ clocks = <&crg_ctrl HI3516CV300_APB_CLK>; ++ clock-frequency = <100000>; ++}; +diff --git a/Documentation/devicetree/bindings/net/hisilicon-femac-mdio.txt b/Documentation/devicetree/bindings/net/hisilicon-femac-mdio.txt +new file mode 100644 +index 0000000..23a39a3 +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/hisilicon-femac-mdio.txt +@@ -0,0 +1,22 @@ ++Hisilicon Fast Ethernet MDIO Controller interface ++ ++Required properties: ++- compatible: should be "hisilicon,hisi-femac-mdio". ++- reg: address and length of the register set for the device. ++- clocks: A phandle to the reference clock for this device. ++ ++- PHY subnode: inherits from phy binding [1] ++[1] Documentation/devicetree/bindings/net/phy.txt ++ ++Example: ++mdio: mdio@10091100 { ++ compatible = "hisilicon,hisi-femac-mdio"; ++ reg = <0x10091100 0x10>; ++ clocks = <&crg HI3516CV300_MDIO_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ phy0: phy@1 { ++ reg = <1>; ++ }; ++}; +diff --git a/Documentation/devicetree/bindings/net/hisilicon-femac.txt b/Documentation/devicetree/bindings/net/hisilicon-femac.txt +new file mode 100644 +index 0000000..d11af5e +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/hisilicon-femac.txt +@@ -0,0 +1,39 @@ ++Hisilicon Fast Ethernet MAC controller ++ ++Required properties: ++- compatible: should contain one of the following version strings: ++ * "hisilicon,hisi-femac-v1" ++ * "hisilicon,hisi-femac-v2" ++ and the soc string "hisilicon,hi3516cv300-femac". ++- reg: specifies base physical address(s) and size of the device registers. ++ The first region is the MAC core register base and size. ++ The second region is the global MAC control register. ++- interrupts: should contain the MAC interrupt. ++- clocks: A phandle to the MAC main clock. ++- resets: should contain the phandle to the MAC reset signal(required) and ++ the PHY reset signal(optional). ++- reset-names: should contain the reset signal name "mac"(required) ++ and "phy"(optional). ++- mac-address: see ethernet.txt [1]. ++- phy-mode: see ethernet.txt [1]. ++- phy-handle: see ethernet.txt [1]. ++- hisilicon,phy-reset-delays-us: triplet of delays if PHY reset signal given. ++ The 1st cell is reset pre-delay in micro seconds. ++ The 2nd cell is reset pulse in micro seconds. ++ The 3rd cell is reset post-delay in micro seconds. ++ ++[1] Documentation/devicetree/bindings/net/ethernet.txt ++ ++Example: ++ hisi_femac: ethernet@10090000 { ++ compatible = "hisilicon,hi3516cv300-femac","hisilicon,hisi-femac-v2"; ++ reg = <0x10090000 0x1000>,<0x10091300 0x200>; ++ interrupts = <12>; ++ clocks = <&crg HI3518EV200_ETH_CLK>; ++ resets = <&crg 0xec 0>,<&crg 0xec 3>; ++ reset-names = "mac","phy"; ++ mac-address = [00 00 00 00 00 00]; ++ phy-mode = "mii"; ++ phy-handle = <&phy0>; ++ hisilicon,phy-reset-delays-us = <10000 20000 20000>; ++ }; +diff --git a/Documentation/devicetree/bindings/net/hisilicon-gemac-mdio.txt b/Documentation/devicetree/bindings/net/hisilicon-gemac-mdio.txt +new file mode 100644 +index 0000000..c6f8202 +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/hisilicon-gemac-mdio.txt +@@ -0,0 +1,22 @@ ++Hisilicon Gigabit Ethernet MDIO Controller interface ++ ++Required properties: ++- compatible: should be "hisilicon,hisi-gemac-mdio". ++- reg: address and length of the register set for the device. ++- clocks: A phandle to the reference clock for this device. ++ ++- PHY subnode: inherits from phy binding [1] ++[1] Documentation/devicetree/bindings/net/phy.txt ++ ++Example: ++mdio: mdio@100503c0 { ++ compatible = "hisilicon,hisi-gemac-mdio"; ++ reg = <0x100503c0 0x20>; ++ clocks = <&crg HI3519V100_MDIO_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ phy0: phy@1 { ++ reg = <1>; ++ }; ++}; +diff --git a/Documentation/devicetree/bindings/net/hisilicon-hieth.txt b/Documentation/devicetree/bindings/net/hisilicon-hieth.txt +new file mode 100644 +index 0000000..21f8744 +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/hisilicon-hieth.txt +@@ -0,0 +1,45 @@ ++Hisilicon hieth controller ++ ++Required properties: ++- compatible: should be "hisilicon,hieth". ++- reg: specifies base physical address(s) and size of the device registers. ++ The region is the MAC register base and size. ++- interrupts: should contain the MAC interrupt. ++- #address-cells: must be <1>. ++- #size-cells: must be <0>. ++- phy-mode: see ethernet.txt [1]. ++- phy-handle: see ethernet.txt [1]. ++- mac-address: see ethernet.txt [1]. ++- clocks: clock phandle and specifier pair. ++- resets: reset controller phandle and specifier pair. ++ ++- PHY subnode: inherits from phy binding [2] ++ ++[1] Documentation/devicetree/bindings/net/ethernet.txt ++[2] Documentation/devicetree/bindings/net/phy.txt ++ ++Example: ++ hieth: ethernet@10050000 { ++ compatible = "hisilicon,hieth"; ++ reg = <0x10050000 0x4000>; ++ interrupts = <12>; ++ ++ clocks = <&clock HI3516CV300_ETH_CLK>; ++ clock-names = "clk"; ++ ++ resets = <&clock 0xcc 0>, ++ <&clock 0xcc 3>; ++ reset-names = "mac_reset", ++ "phy0_reset"; ++ ++ phy-handle = <&hieth_phy0>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hieth_phy0: hieth_phy@1 { ++ reg = <1>; ++ mac-address = [00 00 00 00 00 00]; ++ phy-mode = "mii"; ++ }; ++ }; +diff --git a/Documentation/devicetree/bindings/net/hisilicon-higmac.txt b/Documentation/devicetree/bindings/net/hisilicon-higmac.txt +new file mode 100644 +index 0000000..ea096d2 +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/hisilicon-higmac.txt +@@ -0,0 +1,52 @@ ++Hisilicon higmac controller ++ ++Required properties: ++- compatible: should be "hisilicon,higmac" and one of the following: ++ - "hisilicon,higmac-v1" ++ - "hisilicon,higmac-v2" ++ - "hisilicon,higmac-v3" ++ - "hisilicon,higmac-v4" ++ - "hisilicon,higmac-v5" ++- reg: specifies base physical address(s) and size of the device registers. ++ The first region is the MAC register base and size. ++ The second region is external interface control register. ++- interrupts: should contain the MAC interrupt. ++- #address-cells: must be <1>. ++- #size-cells: must be <0>. ++- phy-mode: see ethernet.txt [1]. ++- phy-handle: see ethernet.txt [1]. ++- mac-address: see ethernet.txt [1]. ++- clocks: clock phandle and specifier pair. ++- resets: reset controller phandle and specifier pair. ++ ++- PHY subnode: inherits from phy binding [2] ++ ++[1] Documentation/devicetree/bindings/net/ethernet.txt ++[2] Documentation/devicetree/bindings/net/phy.txt ++ ++Example: ++ higmac: ethernet@10050000 { ++ compatible = "hisilicon,higmac"; ++ reg = <0x10050000 0x1000>,<0x120100ec 0x4>; ++ interrupts = <0 25 4>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ phy-mode = "rgmii"; ++ phy-handle = <ð_phy>; ++ mac-address = [00 00 00 00 00 00]; ++ clocks = <&clock HI3519_ETH_CLK>, ++ <&clock HI3519_ETH_MACIF_CLK>; ++ clock-names = "higmac_clk", ++ "macif_clk"; ++ ++ resets = <&clock 0xcc 0>, ++ <&clock 0xcc 2>, ++ <&clock 0xcc 7>; ++ reset-names = "port_reset", ++ "macif_reset", ++ "phy_reset"; ++ ++ eth_phy: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ }; +diff --git a/Documentation/devicetree/bindings/phy/hisi-sata-nano-phy.txt b/Documentation/devicetree/bindings/phy/hisi-sata-nano-phy.txt +new file mode 100644 +index 0000000..f06efeb +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/hisi-sata-nano-phy.txt +@@ -0,0 +1,22 @@ ++Hisilicon SATA NANO PHY ++----------------------- ++ ++Required properties: ++- compatible: should be "hisilicon,hisi-sata-nano-phy" ++- reg: offset and length of the PHY registers ++- #phy-cells: must be 0 ++Refer to phy/phy-bindings.txt for the generic PHY binding properties ++ ++Optional Properties: ++- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral. ++- hisilicon,power-reg: offset and bit number within peripheral-syscon, ++ register of controlling sata power supply. ++ ++Example: ++ sata_phy: phy@f9900000 { ++ compatible = "hisilicon,hisi-sata-nano-phy"; ++ reg = <0xf9900000 0x10000>; ++ #phy-cells = <0>; ++ hisilicon,peripheral-syscon = <&peripheral_ctrl>; ++ hisilicon,power-reg = <0x8 10>; ++ }; +diff --git a/Documentation/devicetree/bindings/phy/hisi-usb-phy.txt b/Documentation/devicetree/bindings/phy/hisi-usb-phy.txt +new file mode 100644 +index 0000000..5d53065 +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/hisi-usb-phy.txt +@@ -0,0 +1,15 @@ ++Hisilicon hi3519 USB2 PHY ++----------------------- ++ ++Required properties: ++- compatible: should be "hisilicon,hisi-usb-phy" ++- reg: offset and length of the PHY registers ++- #phy-cells: must be 0 ++Refer to phy/phy-bindings.txt for the generic PHY binding properties ++ ++Example: ++ usb_phy: phy { ++ compatible = "hisilicon,hisi-usb-phy"; ++ reg = <0x12030000 0x10000>, <0x12010000 0x10000>; ++ #phy-cells = <0>; ++ }; +diff --git a/Documentation/devicetree/bindings/phy/hisi-usb3-phy.txt b/Documentation/devicetree/bindings/phy/hisi-usb3-phy.txt +new file mode 100644 +index 0000000..9781fc1 +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/hisi-usb3-phy.txt +@@ -0,0 +1,15 @@ ++Hisilicon hi3519 USB3 PHY ++----------------------- ++ ++Required properties: ++- compatible: should be "hisilicon,hisi-usb3-phy" ++- reg: offset and length of the PHY registers ++- #phy-cells: must be 0 ++Refer to phy/phy-bindings.txt for the generic PHY binding properties ++ ++Example: ++ usb3_phy: phy { ++ compatible = "hisilicon,hisi-usb3-phy"; ++ reg = <0x10180000 0x10000>, <0x12010000 0x10000>; ++ #phy-cells = <0>; ++ }; +diff --git a/Documentation/devicetree/bindings/phy/phy-hisi-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-hisi-inno-usb2.txt +new file mode 100644 +index 0000000..59eaf73 +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/phy-hisi-inno-usb2.txt +@@ -0,0 +1,48 @@ ++HiSilicon INNO USB2 PHY ++----------------------- ++Required properties: ++- compatible: Should be "hisilicon,inno_usb2_phy" ++- #phy-cells: Must be 0 ++- hisilicon,peripheral-syscon: Phandle of syscon used to control phy. ++- hisilicon,reg-num: Number of phy registers which should be configured ++at phy intialization stage ++- hisilicon,reg-seq: Sequence of triplets of (address, value, delay-us). ++The number of triplets is equal to "hisilicon,reg-num". Each triplet is ++used to write one phy register. The delay-us cell represents the delay ++time in microseconds to be applied after each write. ++- clocks: Phandle and clock specifier pair for reference clock utmi_refclk. ++- resets: List of phandle and reset specifier pairs for each reset signal in ++reset-names. ++- reset-names: Should be "por_rst" and "test_rst". The test_rst only ++exists in some of SOCs, so it is optional. ++ ++Phy node can includes up to four subnodes. Each subnode represents one port. ++The required properties of port node are as follows: ++- clocks: Phandle and clock specifier pair for utmi_clock. ++- resets: List of phandle and reset specifier pairs for port reset and utmi reset. ++- reset-names: List of reset signal names. Should be "port_rst" and "utmi_rst" ++ ++Refer to phy/phy-bindings.txt for the generic PHY binding properties ++ ++Example: ++usb_phy: phy { ++ compatible = "hisilicon,inno_usb2_phy"; ++ #phy-cells = <0>; ++ hisilicon,peripheral-syscon = <&peri_ctrl>; ++ hisilicon,reg-num = <7>; ++ hisilicon,reg-seq = <0x80 0x800000 20>, ++ <0x80 0xa0060c 200>, ++ <0x80 0x80001c 20>, ++ <0x80 0xa0001c 20>, ++ <0x80 0x80060f 20>, ++ <0x80 0xa0060f 20>, ++ <0x80 0x800a4b 20>; ++ clocks = <&crg USB2_REF_CLK>; ++ resets = <&crg 0xb4 2>; ++ reset-names = "por_rst"; ++ port0 { ++ clocks = <&crg USB2_UTMI0_CLK>; ++ resets = <&crg 0xb4 5>, <&crg 0xb4 1>; ++ reset-names = "port_rst", "utmi_rst"; ++ }; ++ }; +diff --git a/Documentation/devicetree/bindings/pwm/pwm-hibvt.txt b/Documentation/devicetree/bindings/pwm/pwm-hibvt.txt +new file mode 100644 +index 0000000..2ecf4e4 +--- /dev/null ++++ b/Documentation/devicetree/bindings/pwm/pwm-hibvt.txt +@@ -0,0 +1,20 @@ ++Hisilicon PWM controller ++ ++Required properties: ++-compatible: should contain one SoC specific compatible string and one generic compatible ++string "hisilicon, hibvt-pwm". The SoC specific strings supported including: ++ "hisilicon,hi3516cv300-pwm" ++ "hisilicon,hi3519v100-pwm" ++- reg: physical base address and length of the controller's registers. ++- clocks: phandle and clock specifier of the PWM reference clock. ++- resets: phandle and reset specifier for the PWM controller reset. ++ ++Example: ++ pwm: pwm@12130000 { ++ ++ compatible = "hisilicon,hi3516cv300-pwm", "hisilicon,hibvt-pwm"; ++ compatible = "hisilicon,hi3519v100-pwm", "hisilicon,hibvt-pwm"; ++ reg = <0x12130000 0x10000>; ++ clocks = <&crg_ctrl HI3516CV300_PWM_CLK>; ++ resets = <&crg_ctrl 0x38 0>; ++ }; +diff --git a/Documentation/devicetree/bindings/rtc/rtc-hibvt.txt b/Documentation/devicetree/bindings/rtc/rtc-hibvt.txt +new file mode 100644 +index 0000000..1543ee1 +--- /dev/null ++++ b/Documentation/devicetree/bindings/rtc/rtc-hibvt.txt +@@ -0,0 +1,16 @@ ++Hisilicon RTC controller ++ ++Required properties: ++-compatible: should contain one SoC specific compatible string ++The SoC specific strings supported including: ++- compatible: "hisilicon,hi35xvr-rtc" ++- reg: physical base address and length of the controller's registers. ++- interrupts: IRQ line for the RTC. ++ ++Example: ++rtc: rtc@120b0000 { ++ compatible = "hisilicon,hi35xvr-rtc"; ++ reg = <0x120b0000 0x10000>; ++ interrupts = <0 5 4>; ++ }; ++ +diff --git a/Documentation/devicetree/bindings/thermal/thermal.txt b/Documentation/devicetree/bindings/thermal/thermal.txt +index f5db6b7..99d6608 100644 +--- a/Documentation/devicetree/bindings/thermal/thermal.txt ++++ b/Documentation/devicetree/bindings/thermal/thermal.txt +@@ -167,6 +167,13 @@ Optional property: + by means of sensor ID. Additional coefficients are + interpreted as constant offset. + ++- sustainable-power: An estimate of the sustainable power (in mW) that the ++ Type: unsigned thermal zone can dissipate at the desired ++ Size: one cell control temperature. For reference, the ++ sustainable power of a 4'' phone is typically ++ 2000mW, while on a 10'' tablet is around ++ 4500mW. ++ + Note: The delay properties are bound to the maximum dT/dt (temperature + derivative over time) in two situations for a thermal zone: + (i) - when passive cooling is activated (polling-delay-passive); and +@@ -546,6 +553,8 @@ thermal-zones { + */ + coefficients = <1200 -345 890>; + ++ sustainable-power = <2500>; ++ + trips { + /* Trips are based on resulting linear equation */ + cpu-trip: cpu-trip { +diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt +index eb8a10e..e2def60 100644 +--- a/Documentation/filesystems/proc.txt ++++ b/Documentation/filesystems/proc.txt +@@ -369,6 +369,8 @@ is not associated with a file: + [stack:1001] = the stack of the thread with tid 1001 + [vdso] = the "virtual dynamic shared object", + the kernel system call handler ++ [anon:<name>] = an anonymous mapping that has been ++ named by userspace + + or if empty, the mapping is anonymous. + +@@ -419,6 +421,7 @@ KernelPageSize: 4 kB + MMUPageSize: 4 kB + Locked: 374 kB + VmFlags: rd ex mr mw me de ++Name: name from userspace + + the first of these lines shows the same information as is displayed for the + mapping in /proc/PID/maps. The remaining lines show the size of the mapping +@@ -470,6 +473,9 @@ Note that there is no guarantee that every flag and associated mnemonic will + be present in all further kernel releases. Things get changed, the flags may + be vanished or the reverse -- new added. + ++The "Name" field will only be present on a mapping that has been named by ++userspace, and will show the name passed in by userspace. ++ + This file is only present if the CONFIG_MMU kernel configuration option is + enabled. + +@@ -488,6 +494,10 @@ To clear the bits for the file mapped pages associated with the process + To clear the soft-dirty bit + > echo 4 > /proc/PID/clear_refs + ++To reset the peak resident set size ("high water mark") to the process's ++current value: ++ > echo 5 > /proc/PID/clear_refs ++ + Any other value written to /proc/PID/clear_refs will have no effect. + + The /proc/pid/pagemap gives the PFN, which can be used to find the pageflags +diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt +index a476b08..64a0fc5 100644 +--- a/Documentation/networking/ip-sysctl.txt ++++ b/Documentation/networking/ip-sysctl.txt +@@ -60,7 +60,9 @@ fwmark_reflect - BOOLEAN + Controls the fwmark of kernel-generated IPv4 reply packets that are not + associated with a socket for example, TCP RSTs or ICMP echo replies). + If unset, these packets have a fwmark of zero. If set, they have the +- fwmark of the packet they are replying to. ++ fwmark of the packet they are replying to. Similarly affects the fwmark ++ used by internal routing lookups triggered by incoming packets, such as ++ the ones used for Path MTU Discovery. + Default: 0 + + route/max_size - INTEGER +@@ -516,6 +518,16 @@ tcp_fastopen - INTEGER + + See include/net/tcp.h and the code for more details. + ++tcp_fwmark_accept - BOOLEAN ++ If set, incoming connections to listening sockets that do not have a ++ socket mark will set the mark of the accepting socket to the fwmark of ++ the incoming SYN packet. This will cause all packets on that connection ++ (starting from the first SYNACK) to be sent with that fwmark. The ++ listening socket's mark is unchanged. Listening sockets that already ++ have a fwmark set via setsockopt(SOL_SOCKET, SO_MARK, ...) are ++ unaffected. ++ Default: 0 ++ + tcp_syn_retries - INTEGER + Number of times initial SYNs for an active TCP connection attempt + will be retransmitted. Should not be higher than 255. Default value +@@ -1212,7 +1224,9 @@ fwmark_reflect - BOOLEAN + Controls the fwmark of kernel-generated IPv6 reply packets that are not + associated with a socket for example, TCP RSTs or ICMPv6 echo replies). + If unset, these packets have a fwmark of zero. If set, they have the +- fwmark of the packet they are replying to. ++ fwmark of the packet they are replying to. Similarly affects the fwmark ++ used by internal routing lookups triggered by incoming packets, such as ++ the ones used for Path MTU Discovery. + Default: 0 + + conf/interface/*: +@@ -1466,6 +1480,19 @@ suppress_frag_ndisc - INTEGER + 1 - (default) discard fragmented neighbor discovery packets + 0 - allow fragmented neighbor discovery packets + ++optimistic_dad - BOOLEAN ++ Whether to perform Optimistic Duplicate Address Detection (RFC 4429). ++ 0: disabled (default) ++ 1: enabled ++ ++use_optimistic - BOOLEAN ++ If enabled, do not classify optimistic addresses as deprecated during ++ source address selection. Preferred addresses will still be chosen ++ before optimistic addresses, subject to other ranking in the source ++ address selection algorithm. ++ 0: disabled (default) ++ 1: enabled ++ + icmp/*: + ratelimit - INTEGER + Limit the maximal rates for sending ICMPv6 packets. +diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt +index e839e7e..e9cb02a 100644 +--- a/Documentation/networking/phy.txt ++++ b/Documentation/networking/phy.txt +@@ -337,3 +337,12 @@ Board Fixups + The stubs set one of the two matching criteria, and set the other one to + match anything. + ++ When phy_register_fixup() or *_for_uid()/*_for_id() is called at module, ++ unregister fixup and free allocate memory are required. ++ ++ Call one of following function before unloading module. ++ ++ int phy_unregister_fixup(const char *phy_id, u32 phy_uid, u32 phy_uid_mask); ++ int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask); ++ int phy_register_fixup_for_id(const char *phy_id); ++ +diff --git a/Documentation/sync.txt b/Documentation/sync.txt +new file mode 100644 +index 0000000..a2d05e7 +--- /dev/null ++++ b/Documentation/sync.txt +@@ -0,0 +1,75 @@ ++Motivation: ++ ++In complicated DMA pipelines such as graphics (multimedia, camera, gpu, display) ++a consumer of a buffer needs to know when the producer has finished producing ++it. Likewise the producer needs to know when the consumer is finished with the ++buffer so it can reuse it. A particular buffer may be consumed by multiple ++consumers which will retain the buffer for different amounts of time. In ++addition, a consumer may consume multiple buffers atomically. ++The sync framework adds an API which allows synchronization between the ++producers and consumers in a generic way while also allowing platforms which ++have shared hardware synchronization primitives to exploit them. ++ ++Goals: ++ * provide a generic API for expressing synchronization dependencies ++ * allow drivers to exploit hardware synchronization between hardware ++ blocks ++ * provide a userspace API that allows a compositor to manage ++ dependencies. ++ * provide rich telemetry data to allow debugging slowdowns and stalls of ++ the graphics pipeline. ++ ++Objects: ++ * sync_timeline ++ * sync_pt ++ * sync_fence ++ ++sync_timeline: ++ ++A sync_timeline is an abstract monotonically increasing counter. In general, ++each driver/hardware block context will have one of these. They can be backed ++by the appropriate hardware or rely on the generic sw_sync implementation. ++Timelines are only ever created through their specific implementations ++(i.e. sw_sync.) ++ ++sync_pt: ++ ++A sync_pt is an abstract value which marks a point on a sync_timeline. Sync_pts ++have a single timeline parent. They have 3 states: active, signaled, and error. ++They start in active state and transition, once, to either signaled (when the ++timeline counter advances beyond the sync_pt’s value) or error state. ++ ++sync_fence: ++ ++Sync_fences are the primary primitives used by drivers to coordinate ++synchronization of their buffers. They are a collection of sync_pts which may ++or may not have the same timeline parent. A sync_pt can only exist in one fence ++and the fence's list of sync_pts is immutable once created. Fences can be ++waited on synchronously or asynchronously. Two fences can also be merged to ++create a third fence containing a copy of the two fences’ sync_pts. Fences are ++backed by file descriptors to allow userspace to coordinate the display pipeline ++dependencies. ++ ++Use: ++ ++A driver implementing sync support should have a work submission function which: ++ * takes a fence argument specifying when to begin work ++ * asynchronously queues that work to kick off when the fence is signaled ++ * returns a fence to indicate when its work will be done. ++ * signals the returned fence once the work is completed. ++ ++Consider an imaginary display driver that has the following API: ++/* ++ * assumes buf is ready to be displayed. ++ * blocks until the buffer is on screen. ++ */ ++ void display_buffer(struct dma_buf *buf); ++ ++The new API will become: ++/* ++ * will display buf when fence is signaled. ++ * returns immediately with a fence that will signal when buf ++ * is no longer displayed. ++ */ ++struct sync_fence* display_buffer(struct dma_buf *buf, ++ struct sync_fence *fence); +diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt +index 88152f2..302b5ed 100644 +--- a/Documentation/sysctl/fs.txt ++++ b/Documentation/sysctl/fs.txt +@@ -32,6 +32,8 @@ Currently, these files are in /proc/sys/fs: + - nr_open + - overflowuid + - overflowgid ++- pipe-user-pages-hard ++- pipe-user-pages-soft + - protected_hardlinks + - protected_symlinks + - suid_dumpable +@@ -159,6 +161,27 @@ The default is 65534. + + ============================================================== + ++pipe-user-pages-hard: ++ ++Maximum total number of pages a non-privileged user may allocate for pipes. ++Once this limit is reached, no new pipes may be allocated until usage goes ++below the limit again. When set to 0, no limit is applied, which is the default ++setting. ++ ++============================================================== ++ ++pipe-user-pages-soft: ++ ++Maximum total number of pages a non-privileged user may allocate for pipes ++before the pipe size gets limited to a single page. Once this limit is reached, ++new pipes will be limited to a single page in size for this user in order to ++limit total memory usage, and trying to increase them using fcntl() will be ++denied until usage goes below the limit again. The default value allows to ++allocate up to 1024 pipes at their default size. When set to 0, no limit is ++applied. ++ ++============================================================== ++ + protected_hardlinks: + + A long-standing class of security issues is the hardlink-based +diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt +index 4415aa9..9aecaa5 100644 +--- a/Documentation/sysctl/vm.txt ++++ b/Documentation/sysctl/vm.txt +@@ -29,6 +29,7 @@ Currently, these files are in /proc/sys/vm: + - dirty_writeback_centisecs + - drop_caches + - extfrag_threshold ++- extra_free_kbytes + - hugepages_treat_as_movable + - hugetlb_shm_group + - laptop_mode +@@ -225,6 +226,21 @@ fragmentation index is <= extfrag_threshold. The default value is 500. + + ============================================================== + ++extra_free_kbytes ++ ++This parameter tells the VM to keep extra free memory between the threshold ++where background reclaim (kswapd) kicks in, and the threshold where direct ++reclaim (by allocating processes) kicks in. ++ ++This is useful for workloads that require low latency memory allocations ++and have a bounded burstiness in memory allocations, for example a ++realtime application that receives and transmits network traffic ++(causing in-kernel memory allocations) with a maximum total message burst ++size of 200MB may need 200MB of extra free memory to avoid direct reclaim ++related latencies. ++ ++============================================================== ++ + hugepages_treat_as_movable + + This parameter controls whether we can allocate hugepages from ZONE_MOVABLE +diff --git a/Documentation/thermal/cpu-cooling-api.txt b/Documentation/thermal/cpu-cooling-api.txt +index fca24c9..7165358 100644 +--- a/Documentation/thermal/cpu-cooling-api.txt ++++ b/Documentation/thermal/cpu-cooling-api.txt +@@ -3,7 +3,7 @@ CPU cooling APIs How To + + Written by Amit Daniel Kachhap <amit.kachhap@linaro.org> + +-Updated: 12 May 2012 ++Updated: 6 Jan 2015 + + Copyright (c) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) + +@@ -25,8 +25,173 @@ the user. The registration APIs returns the cooling device pointer. + + clip_cpus: cpumask of cpus where the frequency constraints will happen. + +-1.1.2 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) ++1.1.2 struct thermal_cooling_device *of_cpufreq_cooling_register( ++ struct device_node *np, const struct cpumask *clip_cpus) ++ ++ This interface function registers the cpufreq cooling device with ++ the name "thermal-cpufreq-%x" linking it with a device tree node, in ++ order to bind it via the thermal DT code. This api can support multiple ++ instances of cpufreq cooling devices. ++ ++ np: pointer to the cooling device device tree node ++ clip_cpus: cpumask of cpus where the frequency constraints will happen. ++ ++1.1.3 struct thermal_cooling_device *cpufreq_power_cooling_register( ++ const struct cpumask *clip_cpus, u32 capacitance, ++ get_static_t plat_static_func) ++ ++Similar to cpufreq_cooling_register, this function registers a cpufreq ++cooling device. Using this function, the cooling device will ++implement the power extensions by using a simple cpu power model. The ++cpus must have registered their OPPs using the OPP library. ++ ++The additional parameters are needed for the power model (See 2. Power ++models). "capacitance" is the dynamic power coefficient (See 2.1 ++Dynamic power). "plat_static_func" is a function to calculate the ++static power consumed by these cpus (See 2.2 Static power). ++ ++1.1.4 struct thermal_cooling_device *of_cpufreq_power_cooling_register( ++ struct device_node *np, const struct cpumask *clip_cpus, u32 capacitance, ++ get_static_t plat_static_func) ++ ++Similar to cpufreq_power_cooling_register, this function register a ++cpufreq cooling device with power extensions using the device tree ++information supplied by the np parameter. ++ ++1.1.5 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) + + This interface function unregisters the "thermal-cpufreq-%x" cooling device. + + cdev: Cooling device pointer which has to be unregistered. ++ ++2. Power models ++ ++The power API registration functions provide a simple power model for ++CPUs. The current power is calculated as dynamic + (optionally) ++static power. This power model requires that the operating-points of ++the CPUs are registered using the kernel's opp library and the ++`cpufreq_frequency_table` is assigned to the `struct device` of the ++cpu. If you are using CONFIG_CPUFREQ_DT then the ++`cpufreq_frequency_table` should already be assigned to the cpu ++device. ++ ++The `plat_static_func` parameter of `cpufreq_power_cooling_register()` ++and `of_cpufreq_power_cooling_register()` is optional. If you don't ++provide it, only dynamic power will be considered. ++ ++2.1 Dynamic power ++ ++The dynamic power consumption of a processor depends on many factors. ++For a given processor implementation the primary factors are: ++ ++- The time the processor spends running, consuming dynamic power, as ++ compared to the time in idle states where dynamic consumption is ++ negligible. Herein we refer to this as 'utilisation'. ++- The voltage and frequency levels as a result of DVFS. The DVFS ++ level is a dominant factor governing power consumption. ++- In running time the 'execution' behaviour (instruction types, memory ++ access patterns and so forth) causes, in most cases, a second order ++ variation. In pathological cases this variation can be significant, ++ but typically it is of a much lesser impact than the factors above. ++ ++A high level dynamic power consumption model may then be represented as: ++ ++Pdyn = f(run) * Voltage^2 * Frequency * Utilisation ++ ++f(run) here represents the described execution behaviour and its ++result has a units of Watts/Hz/Volt^2 (this often expressed in ++mW/MHz/uVolt^2) ++ ++The detailed behaviour for f(run) could be modelled on-line. However, ++in practice, such an on-line model has dependencies on a number of ++implementation specific processor support and characterisation ++factors. Therefore, in initial implementation that contribution is ++represented as a constant coefficient. This is a simplification ++consistent with the relative contribution to overall power variation. ++ ++In this simplified representation our model becomes: ++ ++Pdyn = Capacitance * Voltage^2 * Frequency * Utilisation ++ ++Where `capacitance` is a constant that represents an indicative ++running time dynamic power coefficient in fundamental units of ++mW/MHz/uVolt^2. Typical values for mobile CPUs might lie in range ++from 100 to 500. For reference, the approximate values for the SoC in ++ARM's Juno Development Platform are 530 for the Cortex-A57 cluster and ++140 for the Cortex-A53 cluster. ++ ++ ++2.2 Static power ++ ++Static leakage power consumption depends on a number of factors. For a ++given circuit implementation the primary factors are: ++ ++- Time the circuit spends in each 'power state' ++- Temperature ++- Operating voltage ++- Process grade ++ ++The time the circuit spends in each 'power state' for a given ++evaluation period at first order means OFF or ON. However, ++'retention' states can also be supported that reduce power during ++inactive periods without loss of context. ++ ++Note: The visibility of state entries to the OS can vary, according to ++platform specifics, and this can then impact the accuracy of a model ++based on OS state information alone. It might be possible in some ++cases to extract more accurate information from system resources. ++ ++The temperature, operating voltage and process 'grade' (slow to fast) ++of the circuit are all significant factors in static leakage power ++consumption. All of these have complex relationships to static power. ++ ++Circuit implementation specific factors include the chosen silicon ++process as well as the type, number and size of transistors in both ++the logic gates and any RAM elements included. ++ ++The static power consumption modelling must take into account the ++power managed regions that are implemented. Taking the example of an ++ARM processor cluster, the modelling would take into account whether ++each CPU can be powered OFF separately or if only a single power ++region is implemented for the complete cluster. ++ ++In one view, there are others, a static power consumption model can ++then start from a set of reference values for each power managed ++region (e.g. CPU, Cluster/L2) in each state (e.g. ON, OFF) at an ++arbitrary process grade, voltage and temperature point. These values ++are then scaled for all of the following: the time in each state, the ++process grade, the current temperature and the operating voltage. ++However, since both implementation specific and complex relationships ++dominate the estimate, the appropriate interface to the model from the ++cpu cooling device is to provide a function callback that calculates ++the static power in this platform. When registering the cpu cooling ++device pass a function pointer that follows the `get_static_t` ++prototype: ++ ++ int plat_get_static(cpumask_t *cpumask, int interval, ++ unsigned long voltage, u32 &power); ++ ++`cpumask` is the cpumask of the cpus involved in the calculation. ++`voltage` is the voltage at which they are operating. The function ++should calculate the average static power for the last `interval` ++milliseconds. It returns 0 on success, -E* on error. If it ++succeeds, it should store the static power in `power`. Reading the ++temperature of the cpus described by `cpumask` is left for ++plat_get_static() to do as the platform knows best which thermal ++sensor is closest to the cpu. ++ ++If `plat_static_func` is NULL, static power is considered to be ++negligible for this platform and only dynamic power is considered. ++ ++The platform specific callback can then use any combination of tables ++and/or equations to permute the estimated value. Process grade ++information is not passed to the model since access to such data, from ++on-chip measurement capability or manufacture time data, is platform ++specific. ++ ++Note: the significance of static power for CPUs in comparison to ++dynamic power is highly dependent on implementation. Given the ++potential complexity in implementation, the importance and accuracy of ++its inclusion when using cpu cooling devices should be assessed on a ++case by case basis. ++ +diff --git a/Documentation/thermal/power_allocator.txt b/Documentation/thermal/power_allocator.txt +new file mode 100644 +index 0000000..c3797b5 +--- /dev/null ++++ b/Documentation/thermal/power_allocator.txt +@@ -0,0 +1,247 @@ ++Power allocator governor tunables ++================================= ++ ++Trip points ++----------- ++ ++The governor requires the following two passive trip points: ++ ++1. "switch on" trip point: temperature above which the governor ++ control loop starts operating. This is the first passive trip ++ point of the thermal zone. ++ ++2. "desired temperature" trip point: it should be higher than the ++ "switch on" trip point. This the target temperature the governor ++ is controlling for. This is the last passive trip point of the ++ thermal zone. ++ ++PID Controller ++-------------- ++ ++The power allocator governor implements a ++Proportional-Integral-Derivative controller (PID controller) with ++temperature as the control input and power as the controlled output: ++ ++ P_max = k_p * e + k_i * err_integral + k_d * diff_err + sustainable_power ++ ++where ++ e = desired_temperature - current_temperature ++ err_integral is the sum of previous errors ++ diff_err = e - previous_error ++ ++It is similar to the one depicted below: ++ ++ k_d ++ | ++current_temp | ++ | v ++ | +----------+ +---+ ++ | +----->| diff_err |-->| X |------+ ++ | | +----------+ +---+ | ++ | | | tdp actor ++ | | k_i | | get_requested_power() ++ | | | | | | | ++ | | | | | | | ... ++ v | v v v v v ++ +---+ | +-------+ +---+ +---+ +---+ +----------+ ++ | S |-------+----->| sum e |----->| X |--->| S |-->| S |-->|power | ++ +---+ | +-------+ +---+ +---+ +---+ |allocation| ++ ^ | ^ +----------+ ++ | | | | | ++ | | +---+ | | | ++ | +------->| X |-------------------+ v v ++ | +---+ granted performance ++desired_temperature ^ ++ | ++ | ++ k_po/k_pu ++ ++Sustainable power ++----------------- ++ ++An estimate of the sustainable dissipatable power (in mW) should be ++provided while registering the thermal zone. This estimates the ++sustained power that can be dissipated at the desired control ++temperature. This is the maximum sustained power for allocation at ++the desired maximum temperature. The actual sustained power can vary ++for a number of reasons. The closed loop controller will take care of ++variations such as environmental conditions, and some factors related ++to the speed-grade of the silicon. `sustainable_power` is therefore ++simply an estimate, and may be tuned to affect the aggressiveness of ++the thermal ramp. For reference, the sustainable power of a 4" phone ++is typically 2000mW, while on a 10" tablet is around 4500mW (may vary ++depending on screen size). ++ ++If you are using device tree, do add it as a property of the ++thermal-zone. For example: ++ ++ thermal-zones { ++ soc_thermal { ++ polling-delay = <1000>; ++ polling-delay-passive = <100>; ++ sustainable-power = <2500>; ++ ... ++ ++Instead, if the thermal zone is registered from the platform code, pass a ++`thermal_zone_params` that has a `sustainable_power`. If no ++`thermal_zone_params` were being passed, then something like below ++will suffice: ++ ++ static const struct thermal_zone_params tz_params = { ++ .sustainable_power = 3500, ++ }; ++ ++and then pass `tz_params` as the 5th parameter to ++`thermal_zone_device_register()` ++ ++k_po and k_pu ++------------- ++ ++The implementation of the PID controller in the power allocator ++thermal governor allows the configuration of two proportional term ++constants: `k_po` and `k_pu`. `k_po` is the proportional term ++constant during temperature overshoot periods (current temperature is ++above "desired temperature" trip point). Conversely, `k_pu` is the ++proportional term constant during temperature undershoot periods ++(current temperature below "desired temperature" trip point). ++ ++These controls are intended as the primary mechanism for configuring ++the permitted thermal "ramp" of the system. For instance, a lower ++`k_pu` value will provide a slower ramp, at the cost of capping ++available capacity at a low temperature. On the other hand, a high ++value of `k_pu` will result in the governor granting very high power ++whilst temperature is low, and may lead to temperature overshooting. ++ ++The default value for `k_pu` is: ++ ++ 2 * sustainable_power / (desired_temperature - switch_on_temp) ++ ++This means that at `switch_on_temp` the output of the controller's ++proportional term will be 2 * `sustainable_power`. The default value ++for `k_po` is: ++ ++ sustainable_power / (desired_temperature - switch_on_temp) ++ ++Focusing on the proportional and feed forward values of the PID ++controller equation we have: ++ ++ P_max = k_p * e + sustainable_power ++ ++The proportional term is proportional to the difference between the ++desired temperature and the current one. When the current temperature ++is the desired one, then the proportional component is zero and ++`P_max` = `sustainable_power`. That is, the system should operate in ++thermal equilibrium under constant load. `sustainable_power` is only ++an estimate, which is the reason for closed-loop control such as this. ++ ++Expanding `k_pu` we get: ++ P_max = 2 * sustainable_power * (T_set - T) / (T_set - T_on) + ++ sustainable_power ++ ++where ++ T_set is the desired temperature ++ T is the current temperature ++ T_on is the switch on temperature ++ ++When the current temperature is the switch_on temperature, the above ++formula becomes: ++ ++ P_max = 2 * sustainable_power * (T_set - T_on) / (T_set - T_on) + ++ sustainable_power = 2 * sustainable_power + sustainable_power = ++ 3 * sustainable_power ++ ++Therefore, the proportional term alone linearly decreases power from ++3 * `sustainable_power` to `sustainable_power` as the temperature ++rises from the switch on temperature to the desired temperature. ++ ++k_i and integral_cutoff ++----------------------- ++ ++`k_i` configures the PID loop's integral term constant. This term ++allows the PID controller to compensate for long term drift and for ++the quantized nature of the output control: cooling devices can't set ++the exact power that the governor requests. When the temperature ++error is below `integral_cutoff`, errors are accumulated in the ++integral term. This term is then multiplied by `k_i` and the result ++added to the output of the controller. Typically `k_i` is set low (1 ++or 2) and `integral_cutoff` is 0. ++ ++k_d ++--- ++ ++`k_d` configures the PID loop's derivative term constant. It's ++recommended to leave it as the default: 0. ++ ++Cooling device power API ++======================== ++ ++Cooling devices controlled by this governor must supply the additional ++"power" API in their `cooling_device_ops`. It consists on three ops: ++ ++1. int get_requested_power(struct thermal_cooling_device *cdev, ++ struct thermal_zone_device *tz, u32 *power); ++@cdev: The `struct thermal_cooling_device` pointer ++@tz: thermal zone in which we are currently operating ++@power: pointer in which to store the calculated power ++ ++`get_requested_power()` calculates the power requested by the device ++in milliwatts and stores it in @power . It should return 0 on ++success, -E* on failure. This is currently used by the power ++allocator governor to calculate how much power to give to each cooling ++device. ++ ++2. int state2power(struct thermal_cooling_device *cdev, struct ++ thermal_zone_device *tz, unsigned long state, u32 *power); ++@cdev: The `struct thermal_cooling_device` pointer ++@tz: thermal zone in which we are currently operating ++@state: A cooling device state ++@power: pointer in which to store the equivalent power ++ ++Convert cooling device state @state into power consumption in ++milliwatts and store it in @power. It should return 0 on success, -E* ++on failure. This is currently used by thermal core to calculate the ++maximum power that an actor can consume. ++ ++3. int power2state(struct thermal_cooling_device *cdev, u32 power, ++ unsigned long *state); ++@cdev: The `struct thermal_cooling_device` pointer ++@power: power in milliwatts ++@state: pointer in which to store the resulting state ++ ++Calculate a cooling device state that would make the device consume at ++most @power mW and store it in @state. It should return 0 on success, ++-E* on failure. This is currently used by the thermal core to convert ++a given power set by the power allocator governor to a state that the ++cooling device can set. It is a function because this conversion may ++depend on external factors that may change so this function should the ++best conversion given "current circumstances". ++ ++Cooling device weights ++---------------------- ++ ++Weights are a mechanism to bias the allocation among cooling ++devices. They express the relative power efficiency of different ++cooling devices. Higher weight can be used to express higher power ++efficiency. Weighting is relative such that if each cooling device ++has a weight of one they are considered equal. This is particularly ++useful in heterogeneous systems where two cooling devices may perform ++the same kind of compute, but with different efficiency. For example, ++a system with two different types of processors. ++ ++If the thermal zone is registered using ++`thermal_zone_device_register()` (i.e., platform code), then weights ++are passed as part of the thermal zone's `thermal_bind_parameters`. ++If the platform is registered using device tree, then they are passed ++as the `contribution` property of each map in the `cooling-maps` node. ++ ++Limitations of the power allocator governor ++=========================================== ++ ++The power allocator governor's PID controller works best if there is a ++periodic tick. If you have a driver that calls ++`thermal_zone_device_update()` (or anything that ends up calling the ++governor's `throttle()` function) repetitively, the governor response ++won't be very good. Note that this is not particular to this ++governor, step-wise will also misbehave if you call its throttle() ++faster than the normal thermal framework tick (due to interrupts for ++example) as it will overreact. +diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt +index 87519cb..c1f6864 100644 +--- a/Documentation/thermal/sysfs-api.txt ++++ b/Documentation/thermal/sysfs-api.txt +@@ -95,7 +95,7 @@ temperature) and throttle appropriate devices. + 1.3 interface for binding a thermal zone device with a thermal cooling device + 1.3.1 int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, + int trip, struct thermal_cooling_device *cdev, +- unsigned long upper, unsigned long lower); ++ unsigned long upper, unsigned long lower, unsigned int weight); + + This interface function bind a thermal cooling device to the certain trip + point of a thermal zone device. +@@ -110,6 +110,8 @@ temperature) and throttle appropriate devices. + lower:the Minimum cooling state can be used for this trip point. + THERMAL_NO_LIMIT means no lower limit, + and the cooling device can be in cooling state 0. ++ weight: the influence of this cooling device in this thermal ++ zone. See 1.4.1 below for more information. + + 1.3.2 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, + int trip, struct thermal_cooling_device *cdev); +@@ -127,9 +129,15 @@ temperature) and throttle appropriate devices. + This structure defines the following parameters that are used to bind + a zone with a cooling device for a particular trip point. + .cdev: The cooling device pointer +- .weight: The 'influence' of a particular cooling device on this zone. +- This is on a percentage scale. The sum of all these weights +- (for a particular zone) cannot exceed 100. ++ .weight: The 'influence' of a particular cooling device on this ++ zone. This is relative to the rest of the cooling ++ devices. For example, if all cooling devices have a ++ weight of 1, then they all contribute the same. You can ++ use percentages if you want, but it's not mandatory. A ++ weight of 0 means that this cooling device doesn't ++ contribute to the cooling of this zone unless all cooling ++ devices have a weight of 0. If all weights are 0, then ++ they all contribute the same. + .trip_mask:This is a bit mask that gives the binding relation between + this thermal zone and cdev, for a particular trip point. + If nth bit is set, then the cdev and thermal zone are bound +@@ -176,6 +184,14 @@ Thermal zone device sys I/F, created once it's registered: + |---trip_point_[0-*]_type: Trip point type + |---trip_point_[0-*]_hyst: Hysteresis value for this trip point + |---emul_temp: Emulated temperature set node ++ |---sustainable_power: Sustainable dissipatable power ++ |---k_po: Proportional term during temperature overshoot ++ |---k_pu: Proportional term during temperature undershoot ++ |---k_i: PID's integral term in the power allocator gov ++ |---k_d: PID's derivative term in the power allocator ++ |---integral_cutoff: Offset above which errors are accumulated ++ |---slope: Slope constant applied as linear extrapolation ++ |---offset: Offset constant applied as linear extrapolation + + Thermal cooling device sys I/F, created once it's registered: + /sys/class/thermal/cooling_device[0-*]: +@@ -192,6 +208,8 @@ thermal_zone_bind_cooling_device/thermal_zone_unbind_cooling_device. + /sys/class/thermal/thermal_zone[0-*]: + |---cdev[0-*]: [0-*]th cooling device in current thermal zone + |---cdev[0-*]_trip_point: Trip point that cdev[0-*] is associated with ++ |---cdev[0-*]_weight: Influence of the cooling device in ++ this thermal zone + + Besides the thermal zone device sysfs I/F and cooling device sysfs I/F, + the generic thermal driver also creates a hwmon sysfs I/F for each _type_ +@@ -265,6 +283,14 @@ cdev[0-*]_trip_point + point. + RO, Optional + ++cdev[0-*]_weight ++ The influence of cdev[0-*] in this thermal zone. This value ++ is relative to the rest of cooling devices in the thermal ++ zone. For example, if a cooling device has a weight double ++ than that of other, it's twice as effective in cooling the ++ thermal zone. ++ RW, Optional ++ + passive + Attribute is only present for zones in which the passive cooling + policy is not supported by native thermal driver. Default is zero +@@ -289,6 +315,66 @@ emul_temp + because userland can easily disable the thermal policy by simply + flooding this sysfs node with low temperature values. + ++sustainable_power ++ An estimate of the sustained power that can be dissipated by ++ the thermal zone. Used by the power allocator governor. For ++ more information see Documentation/thermal/power_allocator.txt ++ Unit: milliwatts ++ RW, Optional ++ ++k_po ++ The proportional term of the power allocator governor's PID ++ controller during temperature overshoot. Temperature overshoot ++ is when the current temperature is above the "desired ++ temperature" trip point. For more information see ++ Documentation/thermal/power_allocator.txt ++ RW, Optional ++ ++k_pu ++ The proportional term of the power allocator governor's PID ++ controller during temperature undershoot. Temperature undershoot ++ is when the current temperature is below the "desired ++ temperature" trip point. For more information see ++ Documentation/thermal/power_allocator.txt ++ RW, Optional ++ ++k_i ++ The integral term of the power allocator governor's PID ++ controller. This term allows the PID controller to compensate ++ for long term drift. For more information see ++ Documentation/thermal/power_allocator.txt ++ RW, Optional ++ ++k_d ++ The derivative term of the power allocator governor's PID ++ controller. For more information see ++ Documentation/thermal/power_allocator.txt ++ RW, Optional ++ ++integral_cutoff ++ Temperature offset from the desired temperature trip point ++ above which the integral term of the power allocator ++ governor's PID controller starts accumulating errors. For ++ example, if integral_cutoff is 0, then the integral term only ++ accumulates error when temperature is above the desired ++ temperature trip point. For more information see ++ Documentation/thermal/power_allocator.txt ++ RW, Optional ++ ++slope ++ The slope constant used in a linear extrapolation model ++ to determine a hotspot temperature based off the sensor's ++ raw readings. It is up to the device driver to determine ++ the usage of these values. ++ RW, Optional ++ ++offset ++ The offset constant used in a linear extrapolation model ++ to determine a hotspot temperature based off the sensor's ++ raw readings. It is up to the device driver to determine ++ the usage of these values. ++ RW, Optional ++ + ***************************** + * Cooling device attributes * + ***************************** +@@ -318,7 +404,8 @@ passive, active. If an ACPI thermal zone supports critical, passive, + active[0] and active[1] at the same time, it may register itself as a + thermal_zone_device (thermal_zone1) with 4 trip points in all. + It has one processor and one fan, which are both registered as +-thermal_cooling_device. ++thermal_cooling_device. Both are considered to have the same ++effectiveness in cooling the thermal zone. + + If the processor is listed in _PSL method, and the fan is listed in _AL0 + method, the sys I/F structure will be built like this: +@@ -340,8 +427,10 @@ method, the sys I/F structure will be built like this: + |---trip_point_3_type: active1 + |---cdev0: --->/sys/class/thermal/cooling_device0 + |---cdev0_trip_point: 1 /* cdev0 can be used for passive */ ++ |---cdev0_weight: 1024 + |---cdev1: --->/sys/class/thermal/cooling_device3 + |---cdev1_trip_point: 2 /* cdev1 can be used for active[0]*/ ++ |---cdev1_weight: 1024 + + |cooling_device0: + |---type: Processor +diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt +new file mode 100644 +index 0000000..77d14d5 +--- /dev/null ++++ b/Documentation/trace/coresight.txt +@@ -0,0 +1,299 @@ ++ Coresight - HW Assisted Tracing on ARM ++ ====================================== ++ ++ Author: Mathieu Poirier <mathieu.poirier@linaro.org> ++ Date: September 11th, 2014 ++ ++Introduction ++------------ ++ ++Coresight is an umbrella of technologies allowing for the debugging of ARM ++based SoC. It includes solutions for JTAG and HW assisted tracing. This ++document is concerned with the latter. ++ ++HW assisted tracing is becoming increasingly useful when dealing with systems ++that have many SoCs and other components like GPU and DMA engines. ARM has ++developed a HW assisted tracing solution by means of different components, each ++being added to a design at synthesis time to cater to specific tracing needs. ++Compoments are generally categorised as source, link and sinks and are ++(usually) discovered using the AMBA bus. ++ ++"Sources" generate a compressed stream representing the processor instruction ++path based on tracing scenarios as configured by users. From there the stream ++flows through the coresight system (via ATB bus) using links that are connecting ++the emanating source to a sink(s). Sinks serve as endpoints to the coresight ++implementation, either storing the compressed stream in a memory buffer or ++creating an interface to the outside world where data can be transferred to a ++host without fear of filling up the onboard coresight memory buffer. ++ ++At typical coresight system would look like this: ++ ++ ***************************************************************** ++ **************************** AMBA AXI ****************************===|| ++ ***************************************************************** || ++ ^ ^ | || ++ | | * ** ++ 0000000 ::::: 0000000 ::::: ::::: @@@@@@@ |||||||||||| ++ 0 CPU 0<-->: C : 0 CPU 0<-->: C : : C : @ STM @ || System || ++ |->0000000 : T : |->0000000 : T : : T :<--->@@@@@ || Memory || ++ | #######<-->: I : | #######<-->: I : : I : @@@<-| |||||||||||| ++ | # ETM # ::::: | # PTM # ::::: ::::: @ | ++ | ##### ^ ^ | ##### ^ ! ^ ! . | ||||||||| ++ | |->### | ! | |->### | ! | ! . | || DAP || ++ | | # | ! | | # | ! | ! . | ||||||||| ++ | | . | ! | | . | ! | ! . | | | ++ | | . | ! | | . | ! | ! . | | * ++ | | . | ! | | . | ! | ! . | | SWD/ ++ | | . | ! | | . | ! | ! . | | JTAG ++ *****************************************************************<-| ++ *************************** AMBA Debug APB ************************ ++ ***************************************************************** ++ | . ! . ! ! . | ++ | . * . * * . | ++ ***************************************************************** ++ ******************** Cross Trigger Matrix (CTM) ******************* ++ ***************************************************************** ++ | . ^ . . | ++ | * ! * * | ++ ***************************************************************** ++ ****************** AMBA Advanced Trace Bus (ATB) ****************** ++ ***************************************************************** ++ | ! =============== | ++ | * ===== F =====<---------| ++ | ::::::::: ==== U ==== ++ |-->:: CTI ::<!! === N === ++ | ::::::::: ! == N == ++ | ^ * == E == ++ | ! &&&&&&&&& IIIIIII == L == ++ |------>&& ETB &&<......II I ======= ++ | ! &&&&&&&&& II I . ++ | ! I I . ++ | ! I REP I<.......... ++ | ! I I ++ | !!>&&&&&&&&& II I *Source: ARM ltd. ++ |------>& TPIU &<......II I DAP = Debug Access Port ++ &&&&&&&&& IIIIIII ETM = Embedded Trace Macrocell ++ ; PTM = Program Trace Macrocell ++ ; CTI = Cross Trigger Interface ++ * ETB = Embedded Trace Buffer ++ To trace port TPIU= Trace Port Interface Unit ++ SWD = Serial Wire Debug ++ ++While on target configuration of the components is done via the APB bus, ++all trace data are carried out-of-band on the ATB bus. The CTM provides ++a way to aggregate and distribute signals between CoreSight components. ++ ++The coresight framework provides a central point to represent, configure and ++manage coresight devices on a platform. This first implementation centers on ++the basic tracing functionality, enabling components such ETM/PTM, funnel, ++replicator, TMC, TPIU and ETB. Future work will enable more ++intricate IP blocks such as STM and CTI. ++ ++ ++Acronyms and Classification ++--------------------------- ++ ++Acronyms: ++ ++PTM: Program Trace Macrocell ++ETM: Embedded Trace Macrocell ++STM: System trace Macrocell ++ETB: Embedded Trace Buffer ++ITM: Instrumentation Trace Macrocell ++TPIU: Trace Port Interface Unit ++TMC-ETR: Trace Memory Controller, configured as Embedded Trace Router ++TMC-ETF: Trace Memory Controller, configured as Embedded Trace FIFO ++CTI: Cross Trigger Interface ++ ++Classification: ++ ++Source: ++ ETMv3.x ETMv4, PTMv1.0, PTMv1.1, STM, STM500, ITM ++Link: ++ Funnel, replicator (intelligent or not), TMC-ETR ++Sinks: ++ ETBv1.0, ETB1.1, TPIU, TMC-ETF ++Misc: ++ CTI ++ ++ ++Device Tree Bindings ++---------------------- ++ ++See Documentation/devicetree/bindings/arm/coresight.txt for details. ++ ++As of this writing drivers for ITM, STMs and CTIs are not provided but are ++expected to be added as the solution matures. ++ ++ ++Framework and implementation ++---------------------------- ++ ++The coresight framework provides a central point to represent, configure and ++manage coresight devices on a platform. Any coresight compliant device can ++register with the framework for as long as they use the right APIs: ++ ++struct coresight_device *coresight_register(struct coresight_desc *desc); ++void coresight_unregister(struct coresight_device *csdev); ++ ++The registering function is taking a "struct coresight_device *csdev" and ++register the device with the core framework. The unregister function takes ++a reference to a "strut coresight_device", obtained at registration time. ++ ++If everything goes well during the registration process the new devices will ++show up under /sys/bus/coresight/devices, as showns here for a TC2 platform: ++ ++root:~# ls /sys/bus/coresight/devices/ ++replicator 20030000.tpiu 2201c000.ptm 2203c000.etm 2203e000.etm ++20010000.etb 20040000.funnel 2201d000.ptm 2203d000.etm ++root:~# ++ ++The functions take a "struct coresight_device", which looks like this: ++ ++struct coresight_desc { ++ enum coresight_dev_type type; ++ struct coresight_dev_subtype subtype; ++ const struct coresight_ops *ops; ++ struct coresight_platform_data *pdata; ++ struct device *dev; ++ const struct attribute_group **groups; ++}; ++ ++ ++The "coresight_dev_type" identifies what the device is, i.e, source link or ++sink while the "coresight_dev_subtype" will characterise that type further. ++ ++The "struct coresight_ops" is mandatory and will tell the framework how to ++perform base operations related to the components, each component having ++a different set of requirement. For that "struct coresight_ops_sink", ++"struct coresight_ops_link" and "struct coresight_ops_source" have been ++provided. ++ ++The next field, "struct coresight_platform_data *pdata" is acquired by calling ++"of_get_coresight_platform_data()", as part of the driver's _probe routine and ++"struct device *dev" gets the device reference embedded in the "amba_device": ++ ++static int etm_probe(struct amba_device *adev, const struct amba_id *id) ++{ ++ ... ++ ... ++ drvdata->dev = &adev->dev; ++ ... ++} ++ ++Specific class of device (source, link, or sink) have generic operations ++that can be performed on them (see "struct coresight_ops"). The ++"**groups" is a list of sysfs entries pertaining to operations ++specific to that component only. "Implementation defined" customisations are ++expected to be accessed and controlled using those entries. ++ ++Last but not least, "struct module *owner" is expected to be set to reflect ++the information carried in "THIS_MODULE". ++ ++How to use ++---------- ++ ++Before trace collection can start, a coresight sink needs to be identify. ++There is no limit on the amount of sinks (nor sources) that can be enabled at ++any given moment. As a generic operation, all device pertaining to the sink ++class will have an "active" entry in sysfs: ++ ++root:/sys/bus/coresight/devices# ls ++replicator 20030000.tpiu 2201c000.ptm 2203c000.etm 2203e000.etm ++20010000.etb 20040000.funnel 2201d000.ptm 2203d000.etm ++root:/sys/bus/coresight/devices# ls 20010000.etb ++enable_sink status trigger_cntr ++root:/sys/bus/coresight/devices# echo 1 > 20010000.etb/enable_sink ++root:/sys/bus/coresight/devices# cat 20010000.etb/enable_sink ++1 ++root:/sys/bus/coresight/devices# ++ ++At boot time the current etm3x driver will configure the first address ++comparator with "_stext" and "_etext", essentially tracing any instruction ++that falls within that range. As such "enabling" a source will immediately ++trigger a trace capture: ++ ++root:/sys/bus/coresight/devices# echo 1 > 2201c000.ptm/enable_source ++root:/sys/bus/coresight/devices# cat 2201c000.ptm/enable_source ++1 ++root:/sys/bus/coresight/devices# cat 20010000.etb/status ++Depth: 0x2000 ++Status: 0x1 ++RAM read ptr: 0x0 ++RAM wrt ptr: 0x19d3 <----- The write pointer is moving ++Trigger cnt: 0x0 ++Control: 0x1 ++Flush status: 0x0 ++Flush ctrl: 0x2001 ++root:/sys/bus/coresight/devices# ++ ++Trace collection is stopped the same way: ++ ++root:/sys/bus/coresight/devices# echo 0 > 2201c000.ptm/enable_source ++root:/sys/bus/coresight/devices# ++ ++The content of the ETB buffer can be harvested directly from /dev: ++ ++root:/sys/bus/coresight/devices# dd if=/dev/20010000.etb \ ++of=~/cstrace.bin ++ ++64+0 records in ++64+0 records out ++32768 bytes (33 kB) copied, 0.00125258 s, 26.2 MB/s ++root:/sys/bus/coresight/devices# ++ ++The file cstrace.bin can be decompressed using "ptm2human", DS-5 or Trace32. ++ ++Following is a DS-5 output of an experimental loop that increments a variable up ++to a certain value. The example is simple and yet provides a glimpse of the ++wealth of possibilities that coresight provides. ++ ++Info Tracing enabled ++Instruction 106378866 0x8026B53C E52DE004 false PUSH {lr} ++Instruction 0 0x8026B540 E24DD00C false SUB sp,sp,#0xc ++Instruction 0 0x8026B544 E3A03000 false MOV r3,#0 ++Instruction 0 0x8026B548 E58D3004 false STR r3,[sp,#4] ++Instruction 0 0x8026B54C E59D3004 false LDR r3,[sp,#4] ++Instruction 0 0x8026B550 E3530004 false CMP r3,#4 ++Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1 ++Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4] ++Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c ++Timestamp Timestamp: 17106715833 ++Instruction 319 0x8026B54C E59D3004 false LDR r3,[sp,#4] ++Instruction 0 0x8026B550 E3530004 false CMP r3,#4 ++Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1 ++Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4] ++Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c ++Instruction 9 0x8026B54C E59D3004 false LDR r3,[sp,#4] ++Instruction 0 0x8026B550 E3530004 false CMP r3,#4 ++Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1 ++Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4] ++Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c ++Instruction 7 0x8026B54C E59D3004 false LDR r3,[sp,#4] ++Instruction 0 0x8026B550 E3530004 false CMP r3,#4 ++Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1 ++Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4] ++Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c ++Instruction 7 0x8026B54C E59D3004 false LDR r3,[sp,#4] ++Instruction 0 0x8026B550 E3530004 false CMP r3,#4 ++Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1 ++Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4] ++Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c ++Instruction 10 0x8026B54C E59D3004 false LDR r3,[sp,#4] ++Instruction 0 0x8026B550 E3530004 false CMP r3,#4 ++Instruction 0 0x8026B554 E2833001 false ADD r3,r3,#1 ++Instruction 0 0x8026B558 E58D3004 false STR r3,[sp,#4] ++Instruction 0 0x8026B55C DAFFFFFA true BLE {pc}-0x10 ; 0x8026b54c ++Instruction 6 0x8026B560 EE1D3F30 false MRC p15,#0x0,r3,c13,c0,#1 ++Instruction 0 0x8026B564 E1A0100D false MOV r1,sp ++Instruction 0 0x8026B568 E3C12D7F false BIC r2,r1,#0x1fc0 ++Instruction 0 0x8026B56C E3C2203F false BIC r2,r2,#0x3f ++Instruction 0 0x8026B570 E59D1004 false LDR r1,[sp,#4] ++Instruction 0 0x8026B574 E59F0010 false LDR r0,[pc,#16] ; [0x8026B58C] = 0x80550368 ++Instruction 0 0x8026B578 E592200C false LDR r2,[r2,#0xc] ++Instruction 0 0x8026B57C E59221D0 false LDR r2,[r2,#0x1d0] ++Instruction 0 0x8026B580 EB07A4CF true BL {pc}+0x1e9344 ; 0x804548c4 ++Info Tracing enabled ++Instruction 13570831 0x8026B584 E28DD00C false ADD sp,sp,#0xc ++Instruction 0 0x8026B588 E8BD8000 true LDM sp!,{pc} ++Timestamp Timestamp: 17107041535 +diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt +index 4da4261..0344f76 100644 +--- a/Documentation/trace/ftrace.txt ++++ b/Documentation/trace/ftrace.txt +@@ -2043,6 +2043,35 @@ will produce: + 1) 1.449 us | } + + ++You can disable the hierarchical function call formatting and instead print a ++flat list of function entry and return events. This uses the format described ++in the Output Formatting section and respects all the trace options that ++control that formatting. Hierarchical formatting is the default. ++ ++ hierachical: echo nofuncgraph-flat > trace_options ++ flat: echo funcgraph-flat > trace_options ++ ++ ie: ++ ++ # tracer: function_graph ++ # ++ # entries-in-buffer/entries-written: 68355/68355 #P:2 ++ # ++ # _-----=> irqs-off ++ # / _----=> need-resched ++ # | / _---=> hardirq/softirq ++ # || / _--=> preempt-depth ++ # ||| / delay ++ # TASK-PID CPU# |||| TIMESTAMP FUNCTION ++ # | | | |||| | | ++ sh-1806 [001] d... 198.843443: graph_ent: func=_raw_spin_lock ++ sh-1806 [001] d... 198.843445: graph_ent: func=__raw_spin_lock ++ sh-1806 [001] d..1 198.843447: graph_ret: func=__raw_spin_lock ++ sh-1806 [001] d..1 198.843449: graph_ret: func=_raw_spin_lock ++ sh-1806 [001] d..1 198.843451: graph_ent: func=_raw_spin_unlock_irqrestore ++ sh-1806 [001] d... 198.843453: graph_ret: func=_raw_spin_unlock_irqrestore ++ ++ + You might find other useful features for this tracer in the + following "dynamic ftrace" section such as tracing only specific + functions or tasks. +diff --git a/MAINTAINERS b/MAINTAINERS +index c721042..0c921c8 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -918,6 +918,14 @@ M: Hubert Feurstein <hubert.feurstein@contec.at> + S: Maintained + F: arch/arm/mach-ep93xx/micro9.c + ++ARM/CORESIGHT FRAMEWORK AND DRIVERS ++M: Mathieu Poirier <mathieu.poirier@linaro.org> ++L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) ++S: Maintained ++F: drivers/hwtracing/coresight/* ++F: Documentation/trace/coresight.txt ++F: Documentation/devicetree/bindings/arm/coresight.txt ++ + ARM/CORGI MACHINE SUPPORT + M: Richard Purdie <rpurdie@rpsys.net> + S: Maintained +@@ -9297,6 +9305,7 @@ Q: https://patchwork.kernel.org/project/linux-pm/list/ + S: Supported + F: drivers/thermal/ + F: include/linux/thermal.h ++F: include/uapi/linux/thermal.h + F: include/linux/cpu_cooling.h + F: Documentation/devicetree/bindings/thermal/ + +diff --git a/Makefile b/Makefile +index 3ef5895..56992aa 100644 +--- a/Makefile ++++ b/Makefile +@@ -2,7 +2,7 @@ VERSION = 3 + PATCHLEVEL = 18 + SUBLEVEL = 20 + EXTRAVERSION = +-NAME = Diseased Newt ++NAME = Shuffling Zombie Juror + + # *DOCUMENTATION* + # To see a list of typical targets execute "make help" +diff --git a/android/configs/README b/android/configs/README +new file mode 100644 +index 0000000..8798731 +--- /dev/null ++++ b/android/configs/README +@@ -0,0 +1,15 @@ ++The files in this directory are meant to be used as a base for an Android ++kernel config. All devices should have the options in android-base.cfg enabled. ++While not mandatory, the options in android-recommended.cfg enable advanced ++Android features. ++ ++Assuming you already have a minimalist defconfig for your device, a possible ++way to enable these options would be: ++ ++ ARCH=<arch> scripts/kconfig/merge_config.sh <path_to>/<device>_defconfig android/configs/android-base.cfg android/configs/android-recommended.cfg ++ ++This will generate a .config that can then be used to save a new defconfig or ++compile a new kernel with Android features enabled. ++ ++Because there is no tool to consistently generate these config fragments, ++lets keep them alphabetically sorted instead of random. +diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg +new file mode 100644 +index 0000000..154bf16 +--- /dev/null ++++ b/android/configs/android-base.cfg +@@ -0,0 +1,160 @@ ++# KEEP ALPHABETICALLY SORTED ++# CONFIG_DEVKMEM is not set ++# CONFIG_DEVMEM is not set ++# CONFIG_INET_LRO is not set ++# CONFIG_MODULES is not set ++# CONFIG_OABI_COMPAT is not set ++CONFIG_ANDROID=y ++CONFIG_ANDROID_BINDER_IPC=y ++CONFIG_ANDROID_LOW_MEMORY_KILLER=y ++CONFIG_ARMV8_DEPRECATED=y ++CONFIG_ASHMEM=y ++CONFIG_AUDIT=y ++CONFIG_BLK_DEV_DM=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_CGROUPS=y ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_CGROUP_DEBUG=y ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CGROUP_SCHED=y ++CONFIG_CP15_BARRIER_EMULATION=y ++CONFIG_DM_CRYPT=y ++CONFIG_DM_VERITY=y ++CONFIG_EMBEDDED=y ++CONFIG_FB=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_INET6_AH=y ++CONFIG_INET6_ESP=y ++CONFIG_INET6_IPCOMP=y ++CONFIG_INET=y ++CONFIG_INET_ESP=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_IP6_NF_FILTER=y ++CONFIG_IP6_NF_IPTABLES=y ++CONFIG_IP6_NF_MANGLE=y ++CONFIG_IP6_NF_RAW=y ++CONFIG_IP6_NF_TARGET_REJECT=y ++CONFIG_IP6_NF_TARGET_REJECT_SKERR=y ++CONFIG_IPV6=y ++CONFIG_IPV6_MIP6=y ++CONFIG_IPV6_MULTIPLE_TABLES=y ++CONFIG_IPV6_OPTIMISTIC_DAD=y ++CONFIG_IPV6_PRIVACY=y ++CONFIG_IPV6_ROUTER_PREF=y ++CONFIG_IPV6_ROUTE_INFO=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_NF_ARPFILTER=y ++CONFIG_IP_NF_ARPTABLES=y ++CONFIG_IP_NF_ARP_MANGLE=y ++CONFIG_IP_NF_FILTER=y ++CONFIG_IP_NF_IPTABLES=y ++CONFIG_IP_NF_MANGLE=y ++CONFIG_IP_NF_MATCH_AH=y ++CONFIG_IP_NF_MATCH_ECN=y ++CONFIG_IP_NF_MATCH_TTL=y ++CONFIG_IP_NF_RAW=y ++CONFIG_IP_NF_SECURITY=y ++CONFIG_IP_NF_TARGET_MASQUERADE=y ++CONFIG_IP_NF_TARGET_NETMAP=y ++CONFIG_IP_NF_TARGET_REDIRECT=y ++CONFIG_IP_NF_TARGET_REJECT=y ++CONFIG_IP_NF_TARGET_REJECT_SKERR=y ++CONFIG_NET=y ++CONFIG_NETDEVICES=y ++CONFIG_NETFILTER=y ++CONFIG_NETFILTER_TPROXY=y ++CONFIG_NETFILTER_XT_MATCH_COMMENT=y ++CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y ++CONFIG_NETFILTER_XT_MATCH_CONNMARK=y ++CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y ++CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y ++CONFIG_NETFILTER_XT_MATCH_HELPER=y ++CONFIG_NETFILTER_XT_MATCH_IPRANGE=y ++CONFIG_NETFILTER_XT_MATCH_LENGTH=y ++CONFIG_NETFILTER_XT_MATCH_LIMIT=y ++CONFIG_NETFILTER_XT_MATCH_MAC=y ++CONFIG_NETFILTER_XT_MATCH_MARK=y ++CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y ++CONFIG_NETFILTER_XT_MATCH_POLICY=y ++CONFIG_NETFILTER_XT_MATCH_QTAGUID=y ++CONFIG_NETFILTER_XT_MATCH_QUOTA2=y ++CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y ++CONFIG_NETFILTER_XT_MATCH_QUOTA=y ++CONFIG_NETFILTER_XT_MATCH_SOCKET=y ++CONFIG_NETFILTER_XT_MATCH_STATE=y ++CONFIG_NETFILTER_XT_MATCH_STATISTIC=y ++CONFIG_NETFILTER_XT_MATCH_STRING=y ++CONFIG_NETFILTER_XT_MATCH_TIME=y ++CONFIG_NETFILTER_XT_MATCH_U32=y ++CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y ++CONFIG_NETFILTER_XT_TARGET_CONNMARK=y ++CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y ++CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y ++CONFIG_NETFILTER_XT_TARGET_MARK=y ++CONFIG_NETFILTER_XT_TARGET_NFLOG=y ++CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y ++CONFIG_NETFILTER_XT_TARGET_SECMARK=y ++CONFIG_NETFILTER_XT_TARGET_TCPMSS=y ++CONFIG_NETFILTER_XT_TARGET_TPROXY=y ++CONFIG_NETFILTER_XT_TARGET_TRACE=y ++CONFIG_NET_CLS_ACT=y ++CONFIG_NET_CLS_U32=y ++CONFIG_NET_EMATCH=y ++CONFIG_NET_EMATCH_U32=y ++CONFIG_NET_KEY=y ++CONFIG_NET_SCHED=y ++CONFIG_NET_SCH_HTB=y ++CONFIG_NF_CONNTRACK=y ++CONFIG_NF_CONNTRACK_AMANDA=y ++CONFIG_NF_CONNTRACK_EVENTS=y ++CONFIG_NF_CONNTRACK_FTP=y ++CONFIG_NF_CONNTRACK_H323=y ++CONFIG_NF_CONNTRACK_IPV4=y ++CONFIG_NF_CONNTRACK_IPV6=y ++CONFIG_NF_CONNTRACK_IRC=y ++CONFIG_NF_CONNTRACK_NETBIOS_NS=y ++CONFIG_NF_CONNTRACK_PPTP=y ++CONFIG_NF_CONNTRACK_SANE=y ++CONFIG_NF_CONNTRACK_SECMARK=y ++CONFIG_NF_CONNTRACK_TFTP=y ++CONFIG_NF_CT_NETLINK=y ++CONFIG_NF_CT_PROTO_DCCP=y ++CONFIG_NF_CT_PROTO_SCTP=y ++CONFIG_NF_CT_PROTO_UDPLITE=y ++CONFIG_NF_NAT=y ++CONFIG_NO_HZ=y ++CONFIG_PACKET=y ++CONFIG_PM_AUTOSLEEP=y ++CONFIG_PM_WAKELOCKS=y ++CONFIG_PPP=y ++CONFIG_PPPOLAC=y ++CONFIG_PPPOPNS=y ++CONFIG_PPP_BSDCOMP=y ++CONFIG_PPP_DEFLATE=y ++CONFIG_PPP_MPPE=y ++CONFIG_PREEMPT=y ++CONFIG_RESOURCE_COUNTERS=y ++CONFIG_RTC_CLASS=y ++CONFIG_RT_GROUP_SCHED=y ++CONFIG_SECURITY=y ++CONFIG_SECURITY_NETWORK=y ++CONFIG_SECURITY_SELINUX=y ++CONFIG_SETEND_EMULATION=y ++CONFIG_STAGING=y ++CONFIG_SWITCH=y ++CONFIG_SWP_EMULATION=y ++CONFIG_SYNC=y ++CONFIG_SYSVIPC=y ++CONFIG_TUN=y ++CONFIG_UNIX=y ++CONFIG_USB_GADGET=y ++CONFIG_USB_CONFIGFS=y ++CONFIG_USB_CONFIGFS_F_FS=y ++CONFIG_USB_CONFIGFS_F_MTP=y ++CONFIG_USB_CONFIGFS_F_PTP=y ++CONFIG_USB_CONFIGFS_F_ACC=y ++CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y ++CONFIG_USB_CONFIGFS_UEVENT=y ++CONFIG_USB_OTG_WAKELOCK=y ++CONFIG_XFRM_USER=y +diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg +new file mode 100644 +index 0000000..960b9de +--- /dev/null ++++ b/android/configs/android-recommended.cfg +@@ -0,0 +1,121 @@ ++# KEEP ALPHABETICALLY SORTED ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_NF_CONNTRACK_SIP is not set ++# CONFIG_PM_WAKELOCKS_GC is not set ++# CONFIG_VT is not set ++CONFIG_ANDROID_TIMED_GPIO=y ++CONFIG_BACKLIGHT_LCD_SUPPORT=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_SIZE=8192 ++CONFIG_COMPACTION=y ++CONFIG_DM_UEVENT=y ++CONFIG_DRAGONRISE_FF=y ++CONFIG_ENABLE_DEFAULT_TRACERS=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_FUSE_FS=y ++CONFIG_GREENASIA_FF=y ++CONFIG_HIDRAW=y ++CONFIG_HID_A4TECH=y ++CONFIG_HID_ACRUX=y ++CONFIG_HID_ACRUX_FF=y ++CONFIG_HID_APPLE=y ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++CONFIG_HID_CYPRESS=y ++CONFIG_HID_DRAGONRISE=y ++CONFIG_HID_ELECOM=y ++CONFIG_HID_EMS_FF=y ++CONFIG_HID_EZKEY=y ++CONFIG_HID_GREENASIA=y ++CONFIG_HID_GYRATION=y ++CONFIG_HID_HOLTEK=y ++CONFIG_HID_KENSINGTON=y ++CONFIG_HID_KEYTOUCH=y ++CONFIG_HID_KYE=y ++CONFIG_HID_LCPOWER=y ++CONFIG_HID_LOGITECH=y ++CONFIG_HID_LOGITECH_DJ=y ++CONFIG_HID_MAGICMOUSE=y ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++CONFIG_HID_MULTITOUCH=y ++CONFIG_HID_NTRIG=y ++CONFIG_HID_ORTEK=y ++CONFIG_HID_PANTHERLORD=y ++CONFIG_HID_PETALYNX=y ++CONFIG_HID_PICOLCD=y ++CONFIG_HID_PRIMAX=y ++CONFIG_HID_PRODIKEYS=y ++CONFIG_HID_ROCCAT=y ++CONFIG_HID_SAITEK=y ++CONFIG_HID_SAMSUNG=y ++CONFIG_HID_SMARTJOYPLUS=y ++CONFIG_HID_SONY=y ++CONFIG_HID_SPEEDLINK=y ++CONFIG_HID_SUNPLUS=y ++CONFIG_HID_THRUSTMASTER=y ++CONFIG_HID_TIVO=y ++CONFIG_HID_TOPSEED=y ++CONFIG_HID_TWINHAN=y ++CONFIG_HID_UCLOGIC=y ++CONFIG_HID_WACOM=y ++CONFIG_HID_WALTOP=y ++CONFIG_HID_WIIMOTE=y ++CONFIG_HID_ZEROPLUS=y ++CONFIG_HID_ZYDACRON=y ++CONFIG_INPUT_EVDEV=y ++CONFIG_INPUT_GPIO=y ++CONFIG_INPUT_JOYSTICK=y ++CONFIG_INPUT_KEYCHORD=y ++CONFIG_INPUT_KEYRESET=y ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_TABLET=y ++CONFIG_INPUT_UINPUT=y ++CONFIG_ION=y ++CONFIG_JOYSTICK_XPAD=y ++CONFIG_JOYSTICK_XPAD_FF=y ++CONFIG_JOYSTICK_XPAD_LEDS=y ++CONFIG_KALLSYMS_ALL=y ++CONFIG_KSM=y ++CONFIG_LOGIG940_FF=y ++CONFIG_LOGIRUMBLEPAD2_FF=y ++CONFIG_LOGITECH_FF=y ++CONFIG_MD=y ++CONFIG_MEDIA_SUPPORT=y ++CONFIG_MSDOS_FS=y ++CONFIG_PANIC_TIMEOUT=5 ++CONFIG_PANTHERLORD_FF=y ++CONFIG_PERF_EVENTS=y ++CONFIG_PM_DEBUG=y ++CONFIG_PM_RUNTIME=y ++CONFIG_PM_WAKELOCKS_LIMIT=0 ++CONFIG_POWER_SUPPLY=y ++CONFIG_PSTORE=y ++CONFIG_PSTORE_CONSOLE=y ++CONFIG_PSTORE_RAM=y ++CONFIG_SCHEDSTATS=y ++CONFIG_SMARTJOYPLUS_FF=y ++CONFIG_SND=y ++CONFIG_SOUND=y ++CONFIG_SUSPEND_TIME=y ++CONFIG_TABLET_USB_ACECAD=y ++CONFIG_TABLET_USB_AIPTEK=y ++CONFIG_TABLET_USB_GTCO=y ++CONFIG_TABLET_USB_HANWANG=y ++CONFIG_TABLET_USB_KBTAB=y ++CONFIG_TABLET_USB_WACOM=y ++CONFIG_TIMER_STATS=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_UHID=y ++CONFIG_UID_STAT=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_HIDDEV=y ++CONFIG_USB_USBNET=y ++CONFIG_VFAT_FS=y +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 89c4b5c..20c9e39 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -311,7 +311,7 @@ config ARCH_MULTIPLATFORM + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARM_HAS_SG_CHAIN + select ARM_PATCH_PHYS_VIRT +- select AUTO_ZRELADDR ++ #select AUTO_ZRELADDR + select CLKSRC_OF + select COMMON_CLK + select GENERIC_CLOCKEVENTS +@@ -1311,6 +1311,7 @@ source "drivers/pci/Kconfig" + source "drivers/pci/pcie/Kconfig" + + source "drivers/pcmcia/Kconfig" ++source "drivers/pci/hipcie/Kconfig" + + endmenu + +@@ -1385,6 +1386,107 @@ config SCHED_SMT + MultiThreading at a cost of slightly increased overhead in some + places. If unsure say N here. + ++config DISABLE_CPU_SCHED_DOMAIN_BALANCE ++ bool "(EXPERIMENTAL) Disable CPU level scheduler load-balancing" ++ help ++ Disables scheduler load-balancing at CPU sched domain level. ++ ++config SCHED_HMP ++ bool "(EXPERIMENTAL) Heterogenous multiprocessor scheduling" ++ depends on DISABLE_CPU_SCHED_DOMAIN_BALANCE && SCHED_MC && FAIR_GROUP_SCHED && !SCHED_AUTOGROUP ++ help ++ Experimental scheduler optimizations for heterogeneous platforms. ++ Attempts to introspectively select task affinity to optimize power ++ and performance. Basic support for multiple (>2) cpu types is in place, ++ but it has only been tested with two types of cpus. ++ There is currently no support for migration of task groups, hence ++ !SCHED_AUTOGROUP. Furthermore, normal load-balancing must be disabled ++ between cpus of different type (DISABLE_CPU_SCHED_DOMAIN_BALANCE). ++ When turned on, this option adds sys/kernel/hmp directory which ++ contains the following files: ++ up_threshold - the load average threshold used for up migration ++ (0 - 1023) ++ down_threshold - the load average threshold used for down migration ++ (0 - 1023) ++ hmp_domains - a list of cpumasks for the present HMP domains, ++ starting with the 'biggest' and ending with the ++ 'smallest'. ++ Note that both the threshold files can be written at runtime to ++ control scheduler behaviour. ++ ++config SCHED_HMP_PRIO_FILTER ++ bool "(EXPERIMENTAL) Filter HMP migrations by task priority" ++ depends on SCHED_HMP ++ help ++ Enables task priority based HMP migration filter. Any task with ++ a NICE value above the threshold will always be on low-power cpus ++ with less compute capacity. ++ ++config SCHED_HMP_PRIO_FILTER_VAL ++ int "NICE priority threshold" ++ default 5 ++ depends on SCHED_HMP_PRIO_FILTER ++ ++config HMP_FAST_CPU_MASK ++ string "HMP scheduler fast CPU mask" ++ depends on SCHED_HMP ++ help ++ Specify the cpuids of the fast CPUs in the system as a list string, ++ e.g. cpuid 0+1 should be specified as 0-1. ++ ++config HMP_SLOW_CPU_MASK ++ string "HMP scheduler slow CPU mask" ++ depends on SCHED_HMP ++ help ++ Specify the cpuids of the slow CPUs in the system as a list string, ++ e.g. cpuid 0+1 should be specified as 0-1. ++ ++config HMP_VARIABLE_SCALE ++ bool "Allows changing the load tracking scale through sysfs" ++ depends on SCHED_HMP ++ help ++ When turned on, this option exports the load average period value ++ for the load tracking patches through sysfs. ++ The values can be modified to change the rate of load accumulation ++ used for HMP migration. 'load_avg_period_ms' is the time in ms to ++ reach a load average of 0.5 for an idle task of 0 load average ++ ratio which becomes 100% busy. ++ For example, with load_avg_period_ms = 128 and up_threshold = 512, ++ a running task with a load of 0 will be migrated to a bigger CPU after ++ 128ms, because after 128ms its load_avg_ratio is 0.5 and the real ++ up_threshold is 0.5. ++ This patch has the same behavior as changing the Y of the load ++ average computation to ++ (1002/1024)^(LOAD_AVG_PERIOD/load_avg_period_ms) ++ but removes intermediate overflows in computation. ++ ++config HMP_FREQUENCY_INVARIANT_SCALE ++ bool "(EXPERIMENTAL) Frequency-Invariant Tracked Load for HMP" ++ depends on SCHED_HMP && CPU_FREQ ++ help ++ Scales the current load contribution in line with the frequency ++ of the CPU that the task was executed on. ++ In this version, we use a simple linear scale derived from the ++ maximum frequency reported by CPUFreq. ++ Restricting tracked load to be scaled by the CPU's frequency ++ represents the consumption of possible compute capacity ++ (rather than consumption of actual instantaneous capacity as ++ normal) and allows the HMP migration's simple threshold ++ migration strategy to interact more predictably with CPUFreq's ++ asynchronous compute capacity changes. ++ ++config SCHED_HMP_LITTLE_PACKING ++ bool "Small task packing for HMP" ++ depends on SCHED_HMP ++ default n ++ help ++ Allows the HMP Scheduler to pack small tasks into CPUs in the ++ smallest HMP domain. ++ Controlled by two sysfs files in sys/kernel/hmp. ++ packing_enable: 1 to enable, 0 to disable packing. Default 1. ++ packing_limit: runqueue load ratio where a RQ is considered ++ to be full. Default is NICE_0_LOAD * 9/8. ++ + config HAVE_ARM_SCU + bool + help +@@ -1790,6 +1892,15 @@ config XEN + help + Say Y if you want to run Linux in a Virtual Machine on Xen on ARM. + ++config ARM_FLUSH_CONSOLE_ON_RESTART ++ bool "Force flush the console on restart" ++ help ++ If the console is locked while the system is rebooted, the messages ++ in the temporary logbuffer would not have propogated to all the ++ console drivers. This option forces the console lock to be ++ released if it failed to be acquired, which will cause all the ++ pending messages to be flushed. ++ + endmenu + + menu "Boot options" +@@ -1820,6 +1931,21 @@ config DEPRECATED_PARAM_STRUCT + This was deprecated in 2001 and announced to live on for 5 years. + Some old boot loaders still use this way. + ++config BUILD_ARM_APPENDED_DTB_IMAGE ++ bool "Build a concatenated zImage/dtb by default" ++ depends on OF ++ help ++ Enabling this option will cause a concatenated zImage and list of ++ DTBs to be built by default (instead of a standalone zImage.) ++ The image will built in arch/arm/boot/zImage-dtb ++ ++config BUILD_ARM_APPENDED_DTB_IMAGE_NAMES ++ string "Default dtb names" ++ depends on BUILD_ARM_APPENDED_DTB_IMAGE ++ help ++ Space separated list of names of dtbs to append when ++ building a concatenated zImage-dtb. ++ + # Compressed boot loader in ROM. Yes, we really want to ask about + # TEXT and BSS so we preserve their values in the config files. + config ZBOOT_ROM_TEXT +diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug +index d8f6a2e..74debb3 100644 +--- a/arch/arm/Kconfig.debug ++++ b/arch/arm/Kconfig.debug +@@ -232,6 +232,77 @@ choice + Say Y here if you want kernel low-level debugging support + on HI3716 UART. + ++ config DEBUG_HI3516CV300_UART ++ bool "Hisilicon Hi3516cv300 Debug UART" ++ depends on ARCH_HI3516CV300 ++ select DEBUG_UART_PL01X ++ help ++ Say Y here if you want kernel low-level debugging support ++ on HI3516CV300 UART. ++ ++ config DEBUG_HI3519_UART ++ bool "Hisilicon Hi3519 Debug UART" ++ depends on ARCH_HI3519 ++ select DEBUG_UART_PL01X ++ help ++ Say Y here if you want kernel low-level debugging support ++ on HI3519 UART. ++ ++ config DEBUG_HI3519V101_UART ++ bool "Hisilicon Hi3519V101 Debug UART" ++ depends on ARCH_HI3519V101 ++ select DEBUG_UART_PL01X ++ help ++ Say Y here if you want kernel low-level debugging support ++ on HI3519V101 UART. ++ ++ config DEBUG_HI3559_UART ++ bool "Hisilicon Hi3559 Debug UART" ++ depends on ARCH_HI3559 ++ select DEBUG_UART_PL01X ++ help ++ Say Y here if you want kernel low-level debugging support ++ on HI3559 UART. ++ ++ config DEBUG_HI3516AV200_UART ++ bool "Hisilicon Hi3516AV200 Debug UART" ++ depends on ARCH_HI3516AV200 ++ select DEBUG_UART_PL01X ++ help ++ Say Y here if you want kernel low-level debugging support ++ on HI3516AV200 UART. ++ ++ config DEBUG_HI3556_UART ++ bool "Hisilicon Hi3556 Debug UART" ++ depends on ARCH_HI3556 ++ select DEBUG_UART_PL01X ++ help ++ Say Y here if you want kernel low-level debugging support ++ on HI3556 UART. ++ config DEBUG_HI3536C_UART ++ bool "Hisilicon Hi3536C Debug UART" ++ depends on ARCH_HI3536C ++ select DEBUG_UART_PL01X ++ help ++ Say Y here if you want kernel low-level debugging support ++ on HI3536C UART. ++ ++ config DEBUG_HI3531D_UART ++ bool "Hisilicon Hi3531D Debug UART" ++ depends on ARCH_HI3531D ++ select DEBUG_UART_PL01X ++ help ++ Say Y here if you want kernel low-level debugging support ++ on HI3531D UART. ++ ++ config DEBUG_HI3521D_UART ++ bool "Hisilicon Hi3521D Debug UART" ++ depends on ARCH_HI3521D ++ select DEBUG_UART_PL01X ++ help ++ Say Y here if you want kernel low-level debugging support ++ on HI3521D UART. ++ + config DEBUG_HIGHBANK_UART + bool "Kernel low-level debugging messages via Highbank UART" + depends on ARCH_HIGHBANK +@@ -1160,6 +1231,15 @@ config DEBUG_UART_PHYS + default 0xf8b00000 if DEBUG_HIX5HD2_UART + default 0xf991e000 if DEBUG_QCOM_UARTDM + default 0xfcb00000 if DEBUG_HI3620_UART ++ default 0x12100000 if DEBUG_HI3516CV300_UART ++ default 0x12100000 if DEBUG_HI3519_UART ++ default 0x12100000 if DEBUG_HI3519V101_UART ++ default 0x12100000 if DEBUG_HI3516AV200_UART ++ default 0x12100000 if DEBUG_HI3559_UART ++ default 0x12100000 if DEBUG_HI3556_UART ++ default 0x12080000 if DEBUG_HI3536C_UART ++ default 0x12080000 if DEBUG_HI3531D_UART ++ default 0x12080000 if DEBUG_HI3521D_UART + default 0xfe800000 if ARCH_IOP32X + default 0xff690000 if DEBUG_RK32_UART2 + default 0xffc02000 if DEBUG_SOCFPGA_UART +@@ -1218,6 +1298,15 @@ config DEBUG_UART_VIRT + default 0xfe300000 if DEBUG_BCM_KONA_UART + default 0xfe800000 if ARCH_IOP32X + default 0xfeb00000 if DEBUG_HI3620_UART || DEBUG_HIX5HD2_UART ++ default 0xfef00000 if DEBUG_HI3516CV300_UART ++ default 0xfef00000 if DEBUG_HI3519_UART ++ default 0xfef00000 if DEBUG_HI3519V101_UART ++ default 0xfef00000 if DEBUG_HI3516AV200_UART ++ default 0xfef00000 if DEBUG_HI3559_UART ++ default 0xfef00000 if DEBUG_HI3556_UART ++ default 0xfe480000 if DEBUG_HI3536C_UART ++ default 0xfe880000 if DEBUG_HI3531D_UART ++ default 0xfe480000 if DEBUG_HI3521D_UART + default 0xfeb24000 if DEBUG_RK3X_UART0 + default 0xfeb26000 if DEBUG_RK3X_UART1 + default 0xfeb30c00 if DEBUG_KEYSTONE_UART0 +@@ -1299,7 +1388,7 @@ config EARLY_PRINTK + + config OC_ETM + bool "On-chip ETM and ETB" +- depends on ARM_AMBA ++ depends on ARM_AMBA && !CORESIGHT + help + Enables the on-chip embedded trace macrocell and embedded trace + buffer driver that will allow you to collect traces of the +@@ -1331,4 +1420,6 @@ config DEBUG_SET_MODULE_RONX + against certain classes of kernel exploits. + If in doubt, say "N". + ++source "drivers/hwtracing/coresight/Kconfig" ++ + endmenu +diff --git a/arch/arm/Makefile b/arch/arm/Makefile +index 034a949..d2b2bfb 100644 +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -50,6 +50,14 @@ AS += -EL + LD += -EL + endif + ++# ++# The Scalar Replacement of Aggregates (SRA) optimization pass in GCC 4.9 and ++# later may result in code being generated that handles signed short and signed ++# char struct members incorrectly. So disable it. ++# (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65932) ++# ++KBUILD_CFLAGS += $(call cc-option,-fno-ipa-sra) ++ + # This selects which instruction set is used. + # Note that GCC does not numerically define an architecture version + # macro, but instead defines a whole series of macros which makes +@@ -278,6 +286,8 @@ libs-y := arch/arm/lib/ $(libs-y) + # Default target when executing plain make + ifeq ($(CONFIG_XIP_KERNEL),y) + KBUILD_IMAGE := xipImage ++else ifeq ($(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE),y) ++KBUILD_IMAGE := zImage-dtb + else + KBUILD_IMAGE := zImage + endif +@@ -312,8 +322,15 @@ $(INSTALL_TARGETS): + $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) $(boot)/dts/$@ + + PHONY += dtbs dtbs_install +-dtbs dtbs_install: prepare scripts +- $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) $@ ++ ++dtbs: prepare scripts ++ $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) ++ ++dtbs_install: ++ $(Q)$(MAKE) $(dtbinst)=$(boot)/dts MACHINE=$(MACHINE) ++ ++zImage-dtb: vmlinux scripts dtbs ++ $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ + + # We use MRPROPER_FILES and CLEAN_FILES now + archclean: +diff --git a/arch/arm/boot/.gitignore b/arch/arm/boot/.gitignore +index 3c79f85..ad7a025 100644 +--- a/arch/arm/boot/.gitignore ++++ b/arch/arm/boot/.gitignore +@@ -4,3 +4,4 @@ xipImage + bootpImage + uImage + *.dtb ++zImage-dtb +\ No newline at end of file +diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile +index ec2f806..afe52d3 100644 +--- a/arch/arm/boot/Makefile ++++ b/arch/arm/boot/Makefile +@@ -14,6 +14,8 @@ + ifneq ($(MACHINE),) + include $(srctree)/$(MACHINE)/Makefile.boot + endif ++include $(srctree)/arch/arm/mach-hisi/Makefile.boot ++include $(srctree)/arch/arm/boot/dts/Makefile + + # Note: the following conditions must always be true: + # ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET) +@@ -25,7 +27,15 @@ INITRD_PHYS := $(initrd_phys-y) + + export ZRELADDR INITRD_PHYS PARAMS_PHYS + +-targets := Image zImage xipImage bootpImage uImage ++targets := Image zImage xipImage bootpImage uImage zImage-dtb ++ ++DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES)) ++ifneq ($(DTB_NAMES),) ++DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES)) ++else ++DTB_LIST := $(dtb-y) ++endif ++DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST)) + + ifeq ($(CONFIG_XIP_KERNEL),y) + +@@ -55,6 +65,10 @@ $(obj)/zImage: $(obj)/compressed/vmlinux FORCE + $(call if_changed,objcopy) + @$(kecho) ' Kernel: $@ is ready' + ++$(obj)/zImage-dtb: $(obj)/zImage $(DTB_OBJS) FORCE ++ $(call if_changed,cat) ++ @echo ' Kernel: $@ is ready' ++ + endif + + ifneq ($(LOADADDR),) +@@ -75,7 +89,7 @@ if [ $(words $(UIMAGE_LOADADDR)) -ne 1 ]; then \ + false; \ + fi + +-$(obj)/uImage: $(obj)/zImage FORCE ++$(obj)/uImage: $(obj)/zImage-dtb FORCE + @$(check_for_multiple_loadaddr) + $(call if_changed,uimage) + @$(kecho) ' Image $@ is ready' +diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile +index 3ea230a..57bae7b 100644 +--- a/arch/arm/boot/compressed/Makefile ++++ b/arch/arm/boot/compressed/Makefile +@@ -23,7 +23,11 @@ endif + + AFLAGS_head.o += -DTEXT_OFFSET=$(TEXT_OFFSET) + HEAD = head.o ++ifeq ($(CONFIG_HW_DECOMPRESS),y) ++OBJS += misc.o hw_decompress.o ++else + OBJS += misc.o decompress.o ++endif + ifeq ($(CONFIG_DEBUG_UNCOMPRESS),y) + OBJS += debug.o + endif +diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S +index 68be901..aaf54a8 100644 +--- a/arch/arm/boot/compressed/head.S ++++ b/arch/arm/boot/compressed/head.S +@@ -176,6 +176,16 @@ not_angel: + ldr r4, =zreladdr + #endif + ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101\ ++ || defined CONFIG_ARCH_HI3559 || defined CONFIG_ARCH_HI3556 || defined CONFIG_ARCH_HI3516AV200) ++ /* ++ * set SMP bit ACTLR register to enable I cache and D cache ++ */ ++ mrc p15, 0, r0, c1, c0, 1 ++ orr r0, #(1 << 6) ++ mcr p15, 0, r0, c1, c0, 1 ++#endif ++ + /* + * Set up a page table only if it won't overwrite ourself. + * That means r4 < pc && r4 - 16k page directory > &_end. +@@ -730,6 +740,8 @@ __armv7_mmu_cache_on: + bic r6, r6, #1 << 31 @ 32-bit translation system + bic r6, r6, #3 << 0 @ use only ttbr0 + mcrne p15, 0, r3, c2, c0, 0 @ load page table pointer ++ mcrne p15, 0, r0, c8, c7, 0 @ flush I,D TLBs ++ mcr p15, 0, r0, c7, c5, 4 @ ISB + mcrne p15, 0, r1, c3, c0, 0 @ load domain access control + mcrne p15, 0, r6, c2, c0, 2 @ load ttb control + #endif +diff --git a/arch/arm/boot/compressed/hw_decompress.c b/arch/arm/boot/compressed/hw_decompress.c +new file mode 100644 +index 0000000..20cd896 +--- /dev/null ++++ b/arch/arm/boot/compressed/hw_decompress.c +@@ -0,0 +1,266 @@ ++#include"hw_decompress.h" ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++#include"hw_decompress_hi3559.h" ++#endif ++ ++static inline void udelay(unsigned long loops) ++{ ++ __asm__ volatile ("1:\n" ++ "subs %0, %1, #1\n" ++ "bne 1b" : "=r" (loops) : "0" (loops)); ++} ++ ++unsigned int hw_dec_type = 0; ++unsigned int hw_dec_sop = 0; ++unsigned int hw_dec_eop = 0; ++unsigned int hw_dec_cur_blk = 0; ++unsigned int hw_blk_total_num = 0; ++ ++void hw_dec_sop_eop_first_set(int block_num) ++{ ++ if (block_num == 1) { ++ hw_dec_sop = 1; ++ hw_dec_eop = 1; ++ } else { ++ hw_dec_sop = 1; ++ hw_dec_eop = 0; ++ } ++ ++ hw_dec_type = 0; ++ hw_dec_cur_blk = 0; ++ hw_blk_total_num = block_num; ++} ++ ++static inline void hw_dec_work_en_set(unsigned int work_en_flg) ++{ ++ /* Enable the emar*/ ++ writel(work_en_flg, (void *)(HW_DEC_REG_BASE_ADDR + EAMR_WORK_EN_REG_OFST)); ++} ++ ++static inline void hw_dec_rtn_baddr_set(unsigned int addr) ++{ ++ writel(addr, (void *)(HW_DEC_REG_BASE_ADDR + DPRS_DATA_RTN_BADDR)); ++} ++ ++static inline void hw_dec_dprs_data_baddr_set(unsigned int addr) ++{ ++ writel(addr, (void *)(HW_DEC_REG_BASE_ADDR + DPRS_DATA_INFO_BADDR)); ++} ++ ++static inline void hw_dec_data_rtn_len_set(unsigned int len) ++{ ++ writel(len, (void *)(HW_DEC_REG_BASE_ADDR + DPRS_DATA_RTN_LEN)); ++} ++ ++static inline void hw_dec_dprs_data_len_set(unsigned int len) ++{ ++ writel(len, (void *)(HW_DEC_REG_BASE_ADDR + DPRS_DATA_INFO_LEN)); ++} ++ ++static inline void hw_dec_crc_check_en(unsigned int crc_en) ++{ ++ writel(crc_en, (void *)(HW_DEC_REG_BASE_ADDR + CRC_CHECK_EN)); ++} ++ ++static inline void hw_dec_data_crc32_set(unsigned int crc32) ++{ ++ writel(crc32, (void *)(HW_DEC_REG_BASE_ADDR + DPRS_DATA_CRC32)); ++} ++ ++static inline unsigned int hw_dec_buf_status_get(void) ++{ ++ return readl((void *)(HW_DEC_REG_BASE_ADDR + BUF_INFO)); ++} ++ ++static inline unsigned int hw_dec_dprs_rtn_status_get(void) ++{ ++ return readl((void *)(HW_DEC_REG_BASE_ADDR + DPRS_RTN_INFO)); ++} ++ ++static inline void hw_dec_buf_status_clr(void) ++{ ++ writel(0x1, (void *)(HW_DEC_REG_BASE_ADDR + BUF_INFO_CLR)); ++} ++ ++static inline void hw_dec_dprs_rtn_status_clr(void) ++{ ++ writel(0x1, (void *)(HW_DEC_REG_BASE_ADDR + RLT_INFO_CLR)); ++} ++ ++static inline void hw_dec_intr_en_set(int blk_intr_en, int task_intr_en) ++{ ++ U_INTR_EN intr_en; ++ intr_en.bits.task_intrpt_en = task_intr_en; ++ intr_en.bits.block_intrpt_en = blk_intr_en; ++ writel(intr_en.u32, (void *)(HW_DEC_REG_BASE_ADDR + INT_EN_REG_ADDR)); ++} ++ ++static inline unsigned int hw_dec_intr_status_get(void) ++{ ++ return readl((void *)(HW_DEC_REG_BASE_ADDR + INT_STATUS_REG_ADDR)); ++} ++ ++static inline void hw_dec_block_intr_status_clr(void) ++{ ++ U_INTR_CLR intr_clr; ++ ++ intr_clr.u32 = readl((void *)(HW_DEC_REG_BASE_ADDR + INT_CLEAR_REG_ADDR)); ++ intr_clr.bits.block_intrpt_clr = 0x1; ++ writel(intr_clr.u32, (void *)(HW_DEC_REG_BASE_ADDR + INT_CLEAR_REG_ADDR)); ++} ++ ++static inline void hw_dec_task_intr_status_clr(void) ++{ ++ U_INTR_CLR intr_clr; ++ ++ intr_clr.u32 = readl((void *)(HW_DEC_REG_BASE_ADDR + INT_CLEAR_REG_ADDR)); ++ intr_clr.bits.task_intrpt_clr = 0x1; ++ writel(intr_clr.u32, (void *)(HW_DEC_REG_BASE_ADDR + INT_CLEAR_REG_ADDR)); ++} ++ ++int hw_dec_intr_proc(int irq, void *para) ++{ ++ U_BUF_STATUS buf_status; ++ U_INTR_STATUS intr_status; ++ U_DPRS_RTN_STATUS dprs_status; ++ ++ intr_status.u32 = hw_dec_intr_status_get(); ++ if (intr_status.bits.block_intrpt) { ++ buf_status.u32 = hw_dec_buf_status_get(); ++ if (buf_status.bits.aval_flg) ++ hw_dec_buf_status_clr(); ++ ++ hw_dec_block_intr_status_clr(); ++ } ++ ++ if (intr_status.bits.task_intrpt) { ++ dprs_status.u32 = hw_dec_dprs_rtn_status_get(); ++ if (dprs_status.bits.aval_flg) { ++ if (dprs_status.bits.err_info) { ++ // printk("err = 0x%x, dec_data_len = 0x%x\n", ++ // dprs_status.bits.err_info, ++ // readl(HW_DEC_REG_BASE_ADDR ++ // + DPRS_RTN_LEN)); ++ } ++ hw_dec_dprs_rtn_status_clr(); ++ } ++ ++ hw_dec_task_intr_status_clr(); ++ return 0; ++ } ++ ++ return -1; ++} ++ ++void hw_dec_start(unsigned int src_baddr, ++ unsigned int dst_baddr, ++ unsigned int src_len, ++ unsigned int dst_len, ++ unsigned int crc_en, ++ unsigned int crc32, ++ unsigned int dec_type) ++{ ++ unsigned int val; ++ ++ if (hw_dec_sop) { ++ if (!dec_type) { ++ /* set the parameters of output buffer */ ++ hw_dec_rtn_baddr_set(dst_baddr); ++ hw_dec_data_rtn_len_set(dst_len); ++ } else { ++ /* set the parameter of output buffer */ ++ hw_dec_dprs_data_baddr_set(dst_baddr); ++ hw_dec_dprs_data_len_set(PAGE_NR(dst_len) * 4); ++ } ++ } ++ ++ /* set the parameter of input buffer */ ++ writel(src_baddr, (void *)(HW_DEC_REG_BASE_ADDR + DPRS_DATA_SRC_BADDR)); ++ ++ val = src_len | (hw_dec_sop << 28) ++ | (hw_dec_eop << 29) | (!dec_type << 31); ++ writel(val, (void *)(HW_DEC_REG_BASE_ADDR + DPRS_DATA_SRC_LEN)); ++ ++ hw_dec_crc_check_en(crc_en); ++} ++ ++int hw_dec_wait_finish(void) ++{ ++ int ret; ++ int times = 0; ++ ++ do { ++ ret = hw_dec_intr_proc(HW_DEC_INTR, NULL); ++ times++; ++ if (times > 100000) { ++ // printk("hardware decompress overtime!" ++ // " func:%s, line:%d\n", ++ // __func__, __LINE__); ++ times = 0; ++ break; ++ } ++ ++ udelay(1000); ++ } while (-1 == ret); ++ ++ return ret; ++} ++ ++void hw_gzip_init(void) ++{ ++ enable_decompress_clock(); ++ /* Init the emar interface */ ++ writel(0, (void *)(HW_DEC_REG_BASE_ADDR + EAMR_RID_REG_OFST)); ++ writel(0x3, (void *)(HW_DEC_REG_BASE_ADDR + EAMR_ROSD_REG_OFST)); ++ writel(0, (void *)(HW_DEC_REG_BASE_ADDR + EAMR_WID_REG_OFST)); ++ writel(0x3, (void *)(HW_DEC_REG_BASE_ADDR + EAMR_WOSD_REG_OFST)); ++ ++ /*Enable interrupt*/ ++ hw_dec_intr_en_set(0x1, 0x1); ++ ++ /* Enable emar*/ ++ hw_dec_work_en_set(0x1); ++} ++ ++int hw_gzip_dec(unsigned int dst, int *dstlen, unsigned int src, int srclen) ++{ ++ int ret; ++ ++ hw_dec_sop_eop_first_set(1); ++ ++ hw_dec_start(src, dst, srclen, *dstlen, 0, 0, hw_dec_type); ++ ++ ret = hw_dec_wait_finish(); ++ if (ret) ++ return -1; ++ ++ //*dstlen = readl(HW_DEC_REG_BASE_ADDR + DPRS_RTN_LEN); ++ ++ return 0; ++} ++ ++void hw_gzip_exit(void) ++{ ++ /*Disable emar*/ ++ hw_dec_work_en_set(0x0); ++ ++ /*Disable interrupt*/ ++ hw_dec_intr_en_set(0x0, 0x0); ++ disable_decompress_clock(); ++} ++ ++ ++int do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x)) ++{ ++ unsigned int src_pa = (unsigned long)input; ++ unsigned int dst_pa = (unsigned long)output; ++ int dstlen = 0x800000; ++ ++ hw_gzip_init(); ++ ++ hw_gzip_dec(dst_pa, &dstlen, src_pa, len); ++ ++ hw_gzip_exit(); ++ ++ return 0; ++} +diff --git a/arch/arm/boot/compressed/hw_decompress.h b/arch/arm/boot/compressed/hw_decompress.h +new file mode 100644 +index 0000000..053c419 +--- /dev/null ++++ b/arch/arm/boot/compressed/hw_decompress.h +@@ -0,0 +1,130 @@ ++#include <linux/io.h> ++ ++/* for fast boot */ ++#define HW_DEC_INTR (73) ++ ++/* These are same definition as in linux 2.6.11 kernel. */ ++//#define PAGE_SIZE 4096 ++#define PAGE_NR(x) (((x)+PAGE_SIZE-1)/PAGE_SIZE) ++#define __ALIGN_UP(x,y) (((x)+(y)-1) & (~((y)-1))) ++ ++/* The base address for emar*/ ++#define HW_DEC_REG_BASE_ADDR (0x110c0000) ++ ++/* The global init registers for emar interface */ ++#define EAMR_RID_REG_OFST (0x0108) ++#define EAMR_ROSD_REG_OFST (0x010C) ++#define EAMR_WID_REG_OFST (0x0110) ++#define EAMR_WOSD_REG_OFST (0x0114) ++ ++/* The enable register */ ++#define EAMR_WORK_EN_REG_OFST (0x0100) ++ ++#define DPRS_DATA_SRC_BADDR (0x2040) ++#define DPRS_DATA_SRC_LEN (0x2044) ++ ++/* Decompress parameter reigsters for page address */ ++#define DPRS_DATA_RTN_BADDR (0x2020) ++#define DPRS_DATA_RTN_LEN (0x2024) ++ ++/* Decompress parameter registers for page data */ ++#define DPRS_DATA_INFO_BADDR (0x2028) ++#define DPRS_DATA_INFO_LEN (0x202C) ++ ++#define DPRS_DATA_CRC32 (0x2030) ++ ++#define CRC_CHECK_EN (0x4000) ++ ++/* The status registers*/ ++#define BUF_INFO (0x2080) ++#define DPRS_RTN_INFO (0x2084) ++#define DPRS_RTN_LEN (0x2088) ++#define BUF_INFO_CLR (0x2090) ++#define RLT_INFO_CLR (0x2094) ++ ++/* The intr registers*/ ++#define INT_EN_REG_ADDR (0x0128) ++#define INT_STATUS_REG_ADDR (0x0124) ++#define INT_CLEAR_REG_ADDR (0x0130) ++ ++/* Define the union U_DPRS_DATA_BUF_INFO */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int buf_len:24; /* [23..0] */ ++ unsigned int buf_id:2; /* [25..24] */ ++ unsigned int reserved_1:2; /* [27..26] */ ++ unsigned int eop:1; /* [28] */ ++ unsigned int sop:1; /* [29] */ ++ unsigned int reserved_0:1; /* [30] */ ++ unsigned int mode:1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++ ++} U_DPRS_DATA_BUF_INFO; ++ ++typedef union { ++ struct { ++ unsigned int buf_id:2; /* [1:0] */ ++ unsigned int rsv:29; /* [30:2] */ ++ unsigned int aval_flg:1; /* [31] */ ++ } bits; ++ unsigned int u32; ++} U_BUF_STATUS; ++ ++typedef union { ++ struct { ++ unsigned int err_info:8; /* [7:0] */ ++ unsigned int rsv:23; /* [30:8] */ ++ unsigned int aval_flg:1; /* [31] */ ++ } bits; ++ ++ unsigned int u32; ++ ++} U_DPRS_RTN_STATUS; ++ ++/* Define the union U_INT_EN */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int task_intrpt_en:1; /* [0] */ ++ unsigned int block_intrpt_en:1; /* [1] */ ++ unsigned int reserved_0:30; /* [31..2] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++ ++} U_INTR_EN; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int task_intrpt:1; /* [0] */ ++ unsigned int block_intrpt:1; /* [1] */ ++ unsigned int reserved_0:30; /* [31..2] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++ ++} U_INTR_STATUS; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int task_intrpt_clr:1; /* [0] */ ++ unsigned int block_intrpt_clr:1; /* [1] */ ++ unsigned int reserved_0:30; /* [31..2] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} U_INTR_CLR; ++ ++ ++void hw_gzip_exit(void); ++int hw_gzip_dec(unsigned int dst, int *dstlen, unsigned int src, int srclen); ++void hw_gzip_init(void); +diff --git a/arch/arm/boot/compressed/hw_decompress_hi3559.h b/arch/arm/boot/compressed/hw_decompress_hi3559.h +new file mode 100644 +index 0000000..32f4dde +--- /dev/null ++++ b/arch/arm/boot/compressed/hw_decompress_hi3559.h +@@ -0,0 +1,21 @@ ++#define CRG_REG_BASE 0x12010000 ++#define PERI_CRG33 0x84 ++#define GZIP_CLKEN (1<<1) ++ ++static void disable_decompress_clock(void) ++{ ++ unsigned int regval; ++ ++ regval = readl((void *)(CRG_REG_BASE + PERI_CRG33)); ++ regval &= ~GZIP_CLKEN; ++ writel(regval, (void *)(CRG_REG_BASE + PERI_CRG33)); ++} ++ ++static void enable_decompress_clock(void) ++{ ++ unsigned int regval; ++ ++ regval = readl((void *)(CRG_REG_BASE + PERI_CRG33)); ++ regval |= GZIP_CLKEN; ++ writel(regval, (void *)(CRG_REG_BASE + PERI_CRG33)); ++} +diff --git a/arch/arm/boot/compressed/vmlinux.lds.S b/arch/arm/boot/compressed/vmlinux.lds.S +index 2b60b84..48ab3fb 100644 +--- a/arch/arm/boot/compressed/vmlinux.lds.S ++++ b/arch/arm/boot/compressed/vmlinux.lds.S +@@ -48,6 +48,7 @@ SECTIONS + *(.rodata) + *(.rodata.*) + } ++ . = ALIGN(16); + .piggydata : { + *(.piggydata) + } +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index 38c89ca..3f541e2 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -91,6 +91,22 @@ dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \ + exynos5800-peach-pi.dtb + dtb-$(CONFIG_ARCH_HI3xxx) += hi3620-hi4511.dtb + dtb-$(CONFIG_ARCH_HIX5HD2) += hisi-x5hd2-dkb.dtb ++dtb-$(CONFIG_ARCH_HI3516CV300) += hi3516cv300-demb.dtb ++dtb-$(CONFIG_ARCH_HI3536C) += hi3536c-demb.dtb ++dtb-$(CONFIG_ARCH_HI3521D) += hi3521d-demb.dtb ++dtb-$(CONFIG_ARCH_HI3531D) += hi3531d-demb.dtb ++ifeq ($(CONFIG_SMP),y) ++dtb-$(CONFIG_ARCH_HI3519) += hisi-hi3519-hmp-demb.dtb ++dtb-$(CONFIG_ARCH_HI3519V101) += hisi-hi3519v101-hmp-demb.dtb ++dtb-$(CONFIG_ARCH_HI3516AV200) += hisi-hi3516av200-hmp-demb.dtb ++dtb-$(CONFIG_ARCH_HI3559) += hisi-hi3559-hmp-demb.dtb ++else ++dtb-$(CONFIG_ARCH_HI3519) += hisi-hi3519-demb.dtb ++dtb-$(CONFIG_ARCH_HI3519V101) += hisi-hi3519v101-demb.dtb ++dtb-$(CONFIG_ARCH_HI3516AV200) += hisi-hi3516av200-demb.dtb ++dtb-$(CONFIG_ARCH_HI3559) += hisi-hi3559-demb.dtb ++dtb-$(CONFIG_ARCH_HI3556) += hisi-hi3556-demb.dtb ++endif + dtb-$(CONFIG_ARCH_HIGHBANK) += highbank.dtb \ + ecx-2000.dtb + dtb-$(CONFIG_ARCH_HIP04) += hip04-d01.dtb +@@ -517,15 +533,14 @@ dtb-$(CONFIG_MACH_DOVE) += dove-cm-a510.dtb \ + dove-dove-db.dtb + dtb-$(CONFIG_ARCH_MEDIATEK) += mt6589-aquaris5.dtb + +-targets += dtbs dtbs_install +-targets += $(dtb-y) ++DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES)) ++ifneq ($(DTB_NAMES),) ++DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES)) ++else ++DTB_LIST := $(dtb-y) + endif + +-# *.dtb used to be generated in the directory above. Clean out the +-# old build results so people don't accidentally use them. +-dtbs: $(addprefix $(obj)/, $(dtb-y)) +- $(Q)rm -f $(obj)/../*.dtb +- +-clean-files := *.dtb ++endif + +-dtbs_install: $(addsuffix _dtbinst_, $(dtb-y)) ++always := $(DTB_LIST) ++clean-files := *.dtb +diff --git a/arch/arm/boot/dts/hi3516cv300-demb.dts b/arch/arm/boot/dts/hi3516cv300-demb.dts +new file mode 100644 +index 0000000..1584a45 +--- /dev/null ++++ b/arch/arm/boot/dts/hi3516cv300-demb.dts +@@ -0,0 +1,636 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++ ++/dts-v1/; ++#include "hi3516cv300.dtsi" ++ ++/ { ++ model = "Hisilicon Hi3516CV300 DEMO Board"; ++ compatible = "hisilicon,hi3516cv300"; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x80000000 0x10000000>; ++ }; ++}; ++ ++&dual_timer0 { ++ status = "okay"; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&i2c_bus1 { ++ status = "okay"; ++ clock-frequency = <100000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c1_pmux &i2c1_pconf>; ++}; ++ ++&spi_bus1{ ++ status = "disabled"; ++ num-cs = <2>; ++ cs-gpios = <&gpio_chip5 3 0>, <&gpio_chip5 4 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi1_pmux &spi1_pconf1 &spi1_pconf2 &spi1_pconf3>; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <50000000>; ++ }; ++ ++ spidev@1 { ++ compatible = "rohm,dh2228fv"; ++ reg = <1>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <50000000>; ++ }; ++}; ++ ++&pwm { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_pmux>; ++}; ++ ++&hisfc { ++ assigned-clocks = <&crg_ctrl HI3516CV300_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ ++ hi_sfc { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ m25p,fast-read; ++ }; ++}; ++ ++&hisnfc { ++ assigned-clocks = <&crg_ctrl HI3516CV300_FMC_CLK>; ++ assigned-clock-rates = <83300000>; ++ ++ hinand { ++ compatible = "jedec,spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ }; ++}; ++ ++&mmc0 { ++ status = "okay"; ++}; ++ ++&mmc1 { ++ status = "okay"; ++}; ++ ++&mmc2 { ++ status = "okay"; ++}; ++ ++&mdio { ++ phy0: phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&hisi_femac { ++ mac-address = [00 00 00 00 00 00]; ++ phy-mode = "rmii"; ++ phy-handle = <&phy0>; ++ hisilicon,phy-reset-delays-us = <10000 10000 150000>; ++}; ++ ++&usb_phy { ++ status = "okay"; ++}; ++ ++&ehci { ++ status = "okay"; ++}; ++ ++&ohci { ++ status = "okay"; ++}; ++ ++&hiudc { ++ status = "okay"; ++}; ++ ++&hidmac { ++ status = "okay"; ++}; ++ ++&dmac { ++ status = "disabled"; ++}; ++ ++&gpio_chip0 { ++ status = "okay"; ++}; ++ ++&gpio_chip1 { ++ status = "okay"; ++}; ++ ++&gpio_chip2 { ++ status = "okay"; ++}; ++ ++&gpio_chip3 { ++ status = "okay"; ++}; ++ ++&gpio_chip4 { ++ status = "okay"; ++}; ++ ++&gpio_chip5 { ++ status = "okay"; ++}; ++ ++&gpio_chip6 { ++ status = "okay"; ++}; ++ ++&gpio_chip7 { ++ status = "okay"; ++}; ++ ++&gpio_chip8 { ++ status = "okay"; ++}; ++ ++&pmux { ++ i2c0_pmux: i2c0_pmux { ++ pinctrl-single,pins = < ++ 0x2c 0x3 /*I2C0_SDA*/ ++ 0x30 0x3 /*I2C0_SCL*/ ++ >; ++ }; ++ ++ i2c1_pmux: i2c1_pmux { ++ pinctrl-single,pins = < ++ 0x20 0x1 /*I2C1_SDA*/ ++ 0x24 0x1 /*I2C1_SCL*/ ++ >; ++ }; ++ ++ spi0_pmux: spi0_pmux { ++ pinctrl-single,pins = < ++ 0x28 0x1 /*SPI0_SDI*/ ++ 0x2c 0x1 /*SPI0_SDO*/ ++ 0x30 0x1 /*SPI0_SCLK*/ ++ 0x34 0x1 /*SPI0_CSN*/ ++ >; ++ }; ++ ++ spi1_pmux: spi1_pmux { ++ pinctrl-single,pins = < ++ 0xc4 0x1 /*SPI1_CSN1*/ ++ 0xc8 0x1 /*SPI1_CSN0*/ ++ 0xcc 0x1 /*SPI1_SDO*/ ++ 0xd0 0x1 /*SPI1_SDI*/ ++ 0xd4 0x1 /*SPI1_SCLK*/ ++ >; ++ }; ++ ++ sensor_pmux: sensor_pmux { ++ pinctrl-single,pins = < ++ 0x38 0x1 /*SENSOR_RSTN*/ ++ 0x3c 0x1 /*SENSOR_CLK*/ ++ >; ++ }; ++ ++ spi_3wire_pmux: spi_3wire_pmux{ ++ pinctrl-single,pins = < ++ 0x2c 0x2 /*SPI_3LINE_SDATA*/ ++ 0x30 0x2 /*SPI_3LINE_SCLK*/ ++ 0x34 0x2 /*SPI_3LINE_CSN*/ ++ >; ++ }; ++ ++ vi_flash_trig_pmux: vi_flash_trig_pmux { ++ pinctrl-single,pins = < ++ 0x1c 0x1 /*FLASH_TRIG*/ ++ >; ++ }; ++ ++ vi_shutter_trig_pmux: vi_shutter_trig_pmux { ++ pinctrl-single,pins = < ++ 0x18 0x1 /*SHUTTER_TRIG*/ ++ >; ++ }; ++ ++ vi_bt1120_pmux: vi_bt1120_pmux{ ++ pinctrl-single,pins = < ++ 0x40 0x1 /*VI_DATA13*/ ++ 0x44 0x1 /*VI_DATA10*/ ++ 0x48 0x1 /*VI_DATA12*/ ++ 0x4c 0x1 /*VI_DATA11*/ ++ 0x50 0x1 /*VI_DATA9*/ ++ 0x54 0x1 /*VI_DATA14*/ ++ 0x58 0x1 /*VI_DATA15*/ ++ 0x5c 0x1 /*VI_VS*/ ++ 0x60 0x1 /*VI_HS*/ ++ >; ++ }; ++ ++ sensor_dc_pmux: sensor_dc_pmux{ ++ pinctrl-single,pins = < ++ 0x40 0x1 /*VI_DATA13*/ ++ 0x44 0x1 /*VI_DATA10*/ ++ 0x48 0x1 /*VI_DATA12*/ ++ 0x4c 0x1 /*VI_DATA11*/ ++ 0x50 0x1 /*VI_DATA9*/ ++ 0x54 0x1 /*VI_DATA14*/ ++ 0x58 0x1 /*VI_DATA15*/ ++ 0x5c 0x1 /*VI_VS*/ ++ 0x60 0x1 /*VI_HS*/ ++ >; ++ }; ++ ++ vo_bt656_pmux: vo_bt656_pmux{ ++ pinctrl-single,pins = < ++ 0x40 0x2 /*VOU656_CLK*/ ++ 0x44 0x2 /*VOU656_DATA3*/ ++ 0x48 0x2 /*VOU656_DATA7*/ ++ 0x4c 0x2 /*VOU656_DATA2*/ ++ 0x50 0x2 /*VOU656_DATA6*/ ++ 0x54 0x2 /*VOU656_DATA5*/ ++ 0x58 0x2 /*VOU656_DATA4*/ ++ 0x5c 0x2 /*VOU656_DATA1*/ ++ 0x60 0x2 /*VOU656_DATA0*/ ++ >; ++ }; ++ ++ vo_lcd_pmux: vo_lcd_pmux{ ++ pinctrl-single,pins = < ++ 0x40 0x3 /*LCD_CLK*/ ++ 0x44 0x3 /*LCD_DATA1*/ ++ 0x48 0x3 /*LCD_DATA3*/ ++ 0x4c 0x3 /*LCD_DATA2*/ ++ 0x50 0x3 /*LCD_DATA4*/ ++ 0x54 0x3 /*LCD_DATA5*/ ++ 0x58 0x3 /*LCD_DATA0*/ ++ 0x5c 0x3 /*LCD_HSYNC*/ ++ 0x60 0x3 /*LCD_VSYNC*/ ++ 0x64 0x3 /*LCD_DE*/ ++ >; ++ }; ++ ++ i2s_with_jtag_pmux: i2s_with_jtag_pmux{ ++ pinctrl-single,pins = < ++ 0xc4 0x4 /*I2S_SD_TX*/ ++ 0xc8 0x4 /*I2S_WS_TX*/ ++ 0xcc 0x4 /*I2S_BCLK_TX*/ ++ 0xd0 0x4 /*I2S_MCLK*/ ++ 0xd4 0x4 /*I2S_SD_RX*/ ++ >; ++ }; ++ ++ i2s_with_vi_pmux: i2s_with_vi_pmux{ ++ pinctrl-single,pins = < ++ 0x40 0x4 /*I2S_MCLK*/ ++ 0x44 0x4 /*I2S_SD_TX*/ ++ 0x48 0x4 /*I2S_BCLK_TX*/ ++ 0x4c 0x4 /*I2S_WS_TX*/ ++ 0x58 0x4 /*I2S_SD_RX*/ ++ >; ++ }; ++ ++ pwm_pmux: pwm_pmux{ ++ pinctrl-single,pins = < ++ 0x04 0x1 ++ 0x08 0x1 ++ 0x0c 0x1 ++ >; ++ }; ++}; ++ ++&pconf { ++ i2c0_pconf: i2c0_pconf { ++ pinctrl-single,pins = < ++ 0x2c 0 ++ 0x30 0 ++ >; ++ pinctrl-single,drive-strength = <0x70 0xff>; ++ }; ++ ++ i2c1_pconf: i2c1_pconf { ++ pinctrl-single,pins = < ++ 0x20 0 ++ 0x24 0 ++ >; ++ pinctrl-single,drive-strength = <0x30 0xff>; ++ }; ++ ++ /*spi0 drive strength conf 1~3*/ ++ spi0_pconf1: spi0_pconf1 { ++ pinctrl-single,pins = < ++ 0x28 0 ++ 0x2c 0 ++ >; ++ pinctrl-single,drive-strength = <0x20 0xff>; ++ }; ++ ++ spi0_pconf2: spi0_pconf2 { ++ pinctrl-single,pins = < ++ 0x30 0 ++ >; ++ pinctrl-single,drive-strength = <0x50 0xff>; ++ }; ++ ++ spi0_pconf3: spi0_pconf3 { ++ pinctrl-single,pins = < ++ 0x34 0 ++ >; ++ pinctrl-single,drive-strength = <0x30 0xff>; ++ }; ++ ++ /*spi1 drive strength conf 1~3*/ ++ spi1_pconf1: spi1_pconf1 { ++ pinctrl-single,pins = < ++ 0xe4 0 ++ 0xe8 0 ++ >; ++ pinctrl-single,drive-strength = <0x30 0xff>; ++ }; ++ ++ spi1_pconf2: spi1_pconf2 { ++ pinctrl-single,pins = < ++ 0xec 0 ++ 0xf0 0 ++ >; ++ pinctrl-single,drive-strength = <0x20 0xff>; ++ }; ++ ++ spi1_pconf3: spi1_pconf3 { ++ pinctrl-single,pins = < ++ 0xf4 0 ++ >; ++ pinctrl-single,drive-strength = <0x0 0xff>; ++ }; ++ ++ spi_3wire_pconf: spi_3wire_pconf{ ++ pinctrl-single,pins = < ++ 0x2c 0 ++ 0x30 0 ++ 0x34 0 ++ >; ++ pinctrl-single,drive-strength = <0x70 0xff>; ++ }; ++ ++ sensor_pconf1: sensor_pconf1 { ++ pinctrl-single,pins = < ++ 0x38 0 ++ >; ++ pinctrl-single,drive-strength = <0x31 0xff>; ++ }; ++ ++ sensor_pconf2: sensor_pconf2 { ++ pinctrl-single,pins = < ++ 0x3c 0 ++ >; ++ pinctrl-single,drive-strength = <0x20 0xff>; ++ }; ++ ++ vi_bt1120_pconf: vi_bt1120_pconf { ++ pinctrl-single,pins = < ++ 0x40 0 ++ 0x44 0 ++ 0x48 0 ++ 0x4c 0 ++ 0x50 0 ++ 0x54 0 ++ 0x58 0 ++ 0x5c 0 ++ 0x60 0 ++ >; ++ pinctrl-single,drive-strength = <0x70 0xff>; ++ }; ++ ++ sensor_dc_pconf: sensor_dc_pconf { ++ pinctrl-single,pins = < ++ 0x40 0 ++ 0x44 0 ++ 0x48 0 ++ 0x4c 0 ++ 0x50 0 ++ 0x54 0 ++ 0x58 0 ++ 0x5c 0 ++ 0x60 0 ++ >; ++ pinctrl-single,drive-strength = <0x70 0xff>; ++ }; ++ ++ vo_bt656_pconf1: vo_bt656_pconf1 { ++ pinctrl-single,pins = < ++ 0x40 0 ++ >; ++ pinctrl-single,drive-strength = <0x40 0xff>; ++ }; ++ ++ vo_bt656_pconf2: vo_bt656_pconf2 { ++ pinctrl-single,pins = < ++ 0x44 0 ++ 0x48 0 ++ 0x4c 0 ++ 0x50 0 ++ 0x54 0 ++ 0x58 0 ++ 0x5c 0 ++ 0x60 0 ++ >; ++ pinctrl-single,drive-strength = <0x20 0xff>; ++ }; ++ ++ vo_lcd_pconf1: vo_lcd_pconf1 { ++ pinctrl-single,pins = < ++ 0x40 0 ++ >; ++ pinctrl-single,drive-strength = <0x40 0xff>; ++ }; ++ ++ vo_lcd_pconf2: vo_lcd_pconf2 { ++ pinctrl-single,pins = < ++ 0x44 0 ++ 0x48 0 ++ 0x4c 0 ++ 0x50 0 ++ 0x54 0 ++ 0x58 0 ++ 0x5c 0 ++ 0x60 0 ++ 0x64 0 ++ >; ++ pinctrl-single,drive-strength = <0x10 0xff>; ++ }; ++ ++ i2s_with_jtag_pconf: i2s_with_jtag_pconf{ ++ pinctrl-single,pins = < ++ 0xe4 0 ++ 0xe8 0 ++ 0xec 0 ++ 0xf0 0 ++ 0xf4 0 ++ >; ++ pinctrl-single,drive-strength = <0x30 0xff>; ++ }; ++ ++ i2s_with_vi_pconf: i2s_with_vi_pconf{ ++ pinctrl-single,pins = < ++ 0x40 0 ++ 0x44 0 ++ 0x48 0 ++ 0x4c 0 ++ 0x58 0 ++ >; ++ pinctrl-single,drive-strength = <0x70 0xff>; ++ }; ++}; ++ ++&i2c_bus0 { ++ status = "okay"; ++ clock-frequency = <100000>; ++}; ++ ++&spi_bus0{ ++ status = "okay"; ++ num-cs = <1>; ++ cs-gpios = <&gpio_chip0 6 0>; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <50000000>; ++ }; ++}; ++ ++&sensor_device0 { ++ pinctrl-names = "i2c_mipi", "ssp_mipi", "i2c_dc", "ssp_dc", "sleep"; ++ pinctrl-0 = <&sensor_pmux &sensor_pconf1 &sensor_pconf2 &i2c0_pmux &i2c0_pconf>; ++ pinctrl-1 = <&sensor_pmux &sensor_pconf1 &sensor_pconf2 &spi0_pmux &spi0_pconf1 &spi0_pconf2 &spi0_pconf3>; ++ pinctrl-2 = <&sensor_pmux &sensor_pconf1 &sensor_pconf2 &i2c0_pmux &i2c0_pconf &sensor_dc_pmux &sensor_dc_pconf>; ++ pinctrl-3 = <&sensor_pmux &sensor_pconf1 &sensor_pconf2 &spi0_pmux &spi0_pconf1 &spi0_pconf2 &spi0_pconf3 &sensor_dc_pmux &sensor_dc_pconf>; ++ pinctrl-4 = <&sensor_pmux &sensor_pconf1 &sensor_pconf2>; ++}; ++ ++&viu { ++ pinctrl-names = "default", "bt1120", "sleep"; ++ pinctrl-0 = <&vi_flash_trig_pmux &vi_shutter_trig_pmux>; ++ pinctrl-1 = <&vi_flash_trig_pmux &vi_shutter_trig_pmux &vi_bt1120_pmux &vi_bt1120_pconf>; ++ pinctrl-2 = <&vi_flash_trig_pmux &vi_shutter_trig_pmux>; ++}; ++ ++&vou { ++ pinctrl-names = "bt656", "lcd", "sleep", "default"; ++ pinctrl-0 = <&vo_bt656_pmux &vo_bt656_pconf1 &vo_bt656_pconf2>; ++ pinctrl-1 = <&vo_lcd_pmux &vo_lcd_pconf1 &vo_lcd_pconf2>; ++ pinctrl-2 = <>; ++ pinctrl-3 = <>; ++}; ++ ++/* ++&audio { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_with_jtag_pmux &i2s_with_jtag_pconf>; ++}; ++*/ ++ ++&pin_ctrl_online { ++ pinctrl-single,pins = < ++ 0x12030000 0x00000080 ++ 0x12030044 0x66664111 ++ 0x12030048 0x66666013 ++ 0x1203004c 0x65266666 ++ 0x12030050 0x00000011 ++ 0x12030054 0x00000110 ++ 0x12030058 0x00000000 ++ 0x12060204 0x1f>; ++}; ++ ++&pin_ctrl_offline { ++ pinctrl-single,pins = < ++ 0x12030000 0x00000000 ++ 0x12030044 0x66666111 ++ 0x12030048 0x66666023 ++ 0x1203004c 0x65266666 ++ 0x12030050 0x00000011 ++ 0x12030054 0x00000100 ++ 0x12030058 0x00000000 ++ 0x12060204 0x2>; ++}; ++ ++&pin_ctrl_ddr { ++ pinctrl-single,pins = < ++ 0x120600c0 0x76543210 ++ 0x120600c4 0x76543210 ++ 0x120600c8 0x76543210 ++ 0x120600cc 0x76543210 ++ 0x120600d0 0x76543210 ++ 0x120600d4 0x76543210 ++ 0x12060100 0x76543210 ++ 0x12060104 0x76543210 ++ 0x12060108 0x76543210 ++ 0x1206010c 0x76543210 ++ 0x12060110 0x76543210 ++ 0x12060114 0x76543210 ++ 0x12060140 0x00000000 ++ 0x12060144 0x00000000 ++ 0x12060148 0x00000000 ++ 0x1206014c 0x00000000 ++ 0x12060150 0x00000000 ++ 0x12060154 0x00000000 ++ 0x12060180 0x00004000 ++ 0x12060184 0x00004000 ++ 0x12060188 0x00000000 ++ 0x1206018c 0x00000000 ++ 0x12060190 0x00000100 ++ 0x12060194 0x00000000 ++ 0x12060200 0x1f ++ 0x12060208 0x2 ++ 0x1206020c 0x2 ++ 0x12060214 0x3 ++ 0x12060240 0xb ++ 0x12060244 0x0 ++ 0x12060040 0x81001000 ++ 0x12060044 0x81001000 ++ 0x12060048 0x81001000 ++ 0x1206004c 0x81001000 ++ 0x12060050 0x81001000 ++ 0x12060054 0x81001000 ++ 0x120614bc 0x101 ++ 0x113200E0 0xd ++ >; ++}; ++ ++&user_pinmux { ++ pinctrl-single,pins = < ++ >; ++}; +diff --git a/arch/arm/boot/dts/hi3516cv300.dtsi b/arch/arm/boot/dts/hi3516cv300.dtsi +new file mode 100644 +index 0000000..2832476 +--- /dev/null ++++ b/arch/arm/boot/dts/hi3516cv300.dtsi +@@ -0,0 +1,675 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/hi3516cv300-clock.h> ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ aliases { ++ serial0 = &uart0; ++ serial1 = &uart1; ++ serial2 = &uart2; ++ i2c0 = &i2c_bus0; ++ i2c1 = &i2c_bus1; ++ spi0 = &spi_bus0; ++ spi1 = &spi_bus1; ++ gpio0 = &gpio_chip0; ++ gpio1 = &gpio_chip1; ++ gpio2 = &gpio_chip2; ++ gpio3 = &gpio_chip3; ++ gpio4 = &gpio_chip4; ++ gpio5 = &gpio_chip5; ++ gpio6 = &gpio_chip6; ++ gpio7 = &gpio_chip7; ++ gpio8 = &gpio_chip8; ++ sensor0 = &sensor_device0; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,arm926ej-s"; ++ reg = <0>; ++ }; ++ }; ++ ++ vic: interrupt-controller@10040000 { ++ compatible = "arm,pl190-vic"; ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ reg = <0x10040000 0x1000>; ++ }; ++ ++ soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&vic>; ++ ranges; ++ ++ crg_ctrl: crg_ctrl@12010000 { ++ compatible = "hisilicon,hi3516cv300-crg"; ++ reg = <0x12010000 0x1000>; ++ #clock-cells = <1>; ++ #reset-cells = <2>; ++ }; ++ ++ sys_ctrl: system-controller@12020000 { ++ compatible = "hisilicon,hi3516cv300-sys", "syscon"; ++ reg = <0x12020000 0x1000>; ++ #clock-cells = <1>; ++ }; ++ ++ reboot { ++ compatible = "syscon-reboot"; ++ regmap = <&sys_ctrl>; ++ offset = <0x4>; ++ mask = <0xdeadbeef>; ++ }; ++ ++ pm { ++ compatible = "hisilicon,hibvt-pm"; ++ reg = <0x12020000 0x1000>, <0x12000000 0x1000>; ++ }; ++ ++ pm_hibernate { ++ compatible = "hisilicon,hibvt-pm-hibernate"; ++ reg = <0x12020000 0x1000>; ++ }; ++ ++ dual_timer0: dual_timer@12000000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ reg = <0x12000000 0x1000>; ++ interrupts = <3>; ++ clocks = <&sys_ctrl HI3516CV300_TIME00_CLK>, ++ <&sys_ctrl HI3516CV300_TIME01_CLK>, ++ <&crg_ctrl HI3516CV300_APB_CLK>; ++ clock-names = "timer0", "timer1", "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer1: dual_timer@12001000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ reg = <0x12001000 0x1000>; ++ interrupts = <4>; ++ clocks = <&sys_ctrl HI3516CV300_TIME10_CLK>, ++ <&sys_ctrl HI3516CV300_TIME11_CLK>, ++ <&crg_ctrl HI3516CV300_APB_CLK>; ++ clock-names = "timer0", "timer1", "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart0: uart@12100000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12100000 0x1000>; ++ interrupts = <5>; ++ clocks = <&crg_ctrl HI3516CV300_UART0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart1: uart@12101000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12101000 0x1000>; ++ interrupts = <30>; ++ clocks = <&crg_ctrl HI3516CV300_UART1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart2: uart@12102000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12102000 0x1000>; ++ interrupts = <25>; ++ clocks = <&crg_ctrl HI3516CV300_UART2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ i2c_bus0: i2c@12110000 { ++ compatible = "hisilicon,hi3516cv300-i2c", ++ "hisilicon,hibvt-i2c"; ++ reg = <0x12110000 0x1000>; ++ clocks = <&crg_ctrl HI3516CV300_APB_CLK>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus1: i2c@12112000 { ++ compatible = "hisilicon,hi3516cv300-i2c", ++ "hisilicon,hibvt-i2c"; ++ reg = <0x12112000 0x1000>; ++ clocks = <&crg_ctrl HI3516CV300_APB_CLK>; ++ status = "disabled"; ++ }; ++ ++ spi_bus0: spi@12120000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12120000 0x1000>; ++ interrupts = <6>; ++ clocks = <&crg_ctrl HI3516CV300_SPI0_CLK>; ++ clock-names = "apb_pclk"; ++ /* dmas = <&dmac 12 1>, <&dmac 13 2>; */ ++ /* dma-names = "rx", "tx"; */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ spi_bus1: spi@12121000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12121000 0x1000>, <0x12030000 0x4>; ++ interrupts = <7>; ++ clocks = <&crg_ctrl HI3516CV300_SPI1_CLK>; ++ clock-names = "apb_pclk"; ++ /* dmas = <&dmac 14 1>, <&dmac 15 2>; */ ++ /* dma-names = "rx", "tx"; */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ hisi,spi_cs_sb = <4>; ++ hisi,spi_cs_mask_bit = <0x10>; ++ status = "disabled"; ++ }; ++ ++ fmc: spi-nor-controller@10000000 { ++ compatible = "hisilicon,hisi-fmc"; ++ reg = <0x10000000 0x1000>, <0x14000000 0x1000000>; ++ reg-names = "control", "memory"; ++ clocks = <&crg_ctrl HI3516CV300_FMC_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hisfc:spi-nor@0 { ++ compatible = "hisilicon,hisi-sfc"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hisnfc:spi-nand@0 { ++ compatible = "hisilicon,hisi-spi-nand"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ ++ mmc2: himciv200.MMC@0x100e0000 { ++ compatible = "hisilicon,hi3516cv300-himciv200"; ++ reg = <0x100e0000 0x1000>; ++ interrupts = <11>; ++ clocks = <&crg_ctrl HI3516CV300_MMC2_CLK>; ++ clock-names = "mmc_clk"; ++ max-frequency = <99000000>; ++ bus-width = <4>; ++ cap-mmc-highspeed; ++ cap-mmc-hw-reset; ++ mmc-hs200-1_8v; ++ full-pwr-cycle; ++ devid = <2>; ++ regmap = <&sys_ctrl>; ++ status = "disabled"; ++ }; ++ ++ mmc0: himciv200.SD@0x100c0000 { ++ compatible = "hisilicon,hi3516cv300-himciv200"; ++ reg = <0x100c0000 0x1000>; ++ interrupts = <18>; ++ clocks = <&crg_ctrl HI3516CV300_MMC0_CLK>; ++ clock-names = "mmc_clk"; ++ max-frequency = <49500000>; ++ bus-width = <4>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ devid = <0>; ++ status = "disabled"; ++ }; ++ ++ mmc1: himciv200.SD@0x100d0000 { ++ compatible = "hisilicon,hi3516cv300-himciv200"; ++ reg = <0x100d0000 0x1000>; ++ interrupts = <27>; ++ clocks = <&crg_ctrl HI3516CV300_MMC1_CLK>; ++ clock-names = "mmc_clk"; ++ max-frequency = <49500000>; ++ bus-width = <4>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ devid = <1>; ++ status = "disabled"; ++ }; ++ ++ mmc3: himciv200.SD@0x100f0000 { ++ compatible = "hisilicon,hi3516cv300-himciv200"; ++ reg = <0x100f0000 0x1000>; ++ interrupts = <27>; ++ clocks = <&crg_ctrl HI3516CV300_MMC3_CLK>; ++ clock-names = "mmc_clk"; ++ max-frequency = <49500000>; ++ bus-width = <4>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ devid = <3>; ++ status = "disabled"; ++ }; ++ ++ mdio: mdio@10051100 { ++ compatible = "hisilicon,hisi-femac-mdio"; ++ reg = <0x10051100 0x10>; ++ clocks = <&crg_ctrl HI3516CV300_ETH_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hisi_femac: ethernet@10090000 { ++ compatible = "hisilicon,hi3516cv300-femac", ++ "hisilicon,hisi-femac-v2"; ++ reg = <0x10050000 0x1000>,<0x10051300 0x200>; ++ interrupts = <12>; ++ clocks = <&crg_ctrl HI3516CV300_ETH_CLK>; ++ resets = <&crg_ctrl 0xec 0>, <&crg_ctrl 0xec 3>; ++ reset-names = "mac","phy"; ++ }; ++ ++ usb_phy: usbphy { ++ compatible = "hisilicon,inno_usb2_phy"; ++ reg = <0x12030000 0x1000>, <0x120d0000 0x1000>, ++ <0x12010000 0x1000>, <0x12020000 0x1000>; ++ clocks = <&crg_ctrl HI3516CV300_USB2_BUS_CLK>; ++ clock-names = "ref_clk"; ++ #phy-cells = <0>; ++ resets = <&crg_ctrl 0xb8 13>, <&crg_ctrl 0xb8 17>; ++ reset-names = "por_rst", "test_rst"; ++ status = "disabled"; ++ port0: port0 { ++ clocks = <&crg_ctrl HI3516CV300_UTMI0_CLK>; ++ resets = <&crg_ctrl 0xb8 14>, ++ <&crg_ctrl 0xb8 9>; ++ reset-names = "port_rst", "utmi_rst"; ++ }; ++ }; ++ ++ ehci: ehci@0x10120000 { ++ compatible = "generic-ehci"; ++ reg = <0x10120000 0x10000>; ++ interrupts = <15>; ++ clocks = <&crg_ctrl HI3516CV300_USB2_CLK>; ++ clock-names = "clk"; ++ status = "disabled"; ++ }; ++ ++ ohci: ohci@0x10110000 { ++ compatible = "generic-ohci"; ++ reg = <0x10110000 0x10000>; ++ interrupts = <16>; ++ clocks = <&crg_ctrl HI3516CV300_USB2_CLK>; ++ clock-names = "clk"; ++ status = "disabled"; ++ }; ++ ++ hiudc: hiudc@0x10130000 { ++ compatible = "hiudc"; ++ reg = <0x10130000 0x40000>; ++ interrupts = <10>; ++ clocks = <&crg_ctrl HI3516CV300_USB2_CLK>; ++ clock-names = "clk"; ++ status = "disabled"; ++ }; ++ ++ hidmac: hidma-controller@10030000 { ++ compatible = "hisilicon,hisi-dmac"; ++ reg = <0x10030000 0x1000>; ++ interrupts = <14>; ++ clocks = <&crg_ctrl HI3516CV300_DMAC_CLK>; ++ clock-names = "apb_pclk"; ++ resets = <&crg_ctrl 0xd8 4>; ++ reset-names = "dma-reset"; ++ #dma-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ dmac: dma-controller@10030000 { ++ compatible = "arm,pl080", "arm,primecell"; ++ reg = <0x10030000 0x1000>; ++ interrupts = <14>; ++ clocks = <&crg_ctrl HI3516CV300_DMAC_CLK>; ++ clock-names = "apb_pclk"; ++ lli-bus-interface-ahb1; ++ lli-bus-interface-ahb2; ++ mem-bus-interface-ahb1; ++ mem-bus-interface-ahb2; ++ memcpy-burst-size = <256>; ++ memcpy-bus-width = <32>; ++ #dma-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip0: gpio@12140000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12140000 0x1000>; ++ interrupts = <31>; ++ clocks = <&crg_ctrl HI3516CV300_APB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ gpio-ranges = <&pmux 0 61 2>, ++ <&pmux 4 11 1>, ++ <&pmux 5 10 1>, ++ <&pmux 6 13 2>; ++ ++ status = "disabled"; ++ }; ++ ++ gpio_chip1: gpio@12141000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12141000 0x1000>; ++ interrupts = <31>; ++ clocks = <&crg_ctrl HI3516CV300_APB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ gpio-ranges = <&pmux 0 16 7>, ++ <&pmux 7 0 1>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip2: gpio@12142000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12142000 0x1000>; ++ interrupts = <31>; ++ clocks = <&crg_ctrl HI3516CV300_APB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ gpio-ranges = <&pmux 0 46 1>, ++ <&pmux 1 45 1>, ++ <&pmux 2 44 1>, ++ <&pmux 3 43 1>, ++ <&pmux 4 39 1>, ++ <&pmux 5 38 1>, ++ <&pmux 6 40 1>, ++ <&pmux 7 48 1>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip3: gpio@12143000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12143000 0x1000>; ++ interrupts = <31>; ++ clocks = <&crg_ctrl HI3516CV300_APB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ gpio-ranges = <&pmux 0 37 1>, ++ <&pmux 1 36 1>, ++ <&pmux 2 35 1>, ++ <&pmux 3 34 1>, ++ <&pmux 4 23 2>, ++ <&pmux 6 8 2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip4: gpio@12144000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12144000 0x1000>; ++ interrupts = <31>; ++ clocks = <&crg_ctrl HI3516CV300_APB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ gpio-ranges = <&pmux 0 27 1>, ++ <&pmux 1 26 1>, ++ <&pmux 2 31 1>, ++ <&pmux 3 30 1>, ++ <&pmux 4 28 2>, ++ <&pmux 6 33 1>, ++ <&pmux 7 32 1>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip5: gpio@12145000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12145000 0x1000>; ++ interrupts = <31>; ++ clocks = <&crg_ctrl HI3516CV300_APB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ gpio-ranges = <&pmux 0 53 1>, ++ <&pmux 1 51 2>, ++ <&pmux 3 50 1>, ++ <&pmux 4 49 1>, ++ <&pmux 5 47 1>, ++ <&pmux 6 40 2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip6: gpio@12146000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12146000 0x1000>; ++ interrupts = <31>; ++ clocks = <&crg_ctrl HI3516CV300_APB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ gpio-ranges = <&pmux 0 7 1>, ++ <&pmux 1 6 1>, ++ <&pmux 2 4 1>, ++ <&pmux 3 5 1>, ++ <&pmux 4 15 1>, ++ <&pmux 5 1 3>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip7: gpio@12147000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12147000 0x1000>; ++ interrupts = <31>; ++ clocks = <&crg_ctrl HI3516CV300_APB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ gpio-ranges = <&pmux 1 55 6>, ++ <&pmux 7 25 1>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip8: gpio@12148000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12148000 0x1000>; ++ interrupts = <31>; ++ clocks = <&crg_ctrl HI3516CV300_APB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ gpio-ranges = <&pmux 0 63 3>, ++ <&pmux 3 12 1>; ++ status = "disabled"; ++ }; ++ ++ pmux: pinmux@12040000 { ++ compatible = "pinctrl-single"; ++ reg = <0x12040000 0x108>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ #gpio-range-cells = <3>; ++ ranges; ++ ++ pinctrl-single,register-width = <32>; ++ pinctrl-single,function-mask = <7>; ++ /* pin base, nr pins & gpio function */ ++ pinctrl-single,gpio-range = <&range 0 54 0 ++ &range 55 6 1 &range 61 5 0>; ++ ++ range: gpio-range { ++ #pinctrl-single,gpio-range-cells = <3>; ++ }; ++ }; ++ ++ pconf: pinconf@12040800 { ++ compatible = "pinconf-single"; ++ reg = <0x12040800 0x130>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ pinctrl-single,register-width = <32>; ++ }; ++ }; ++ ++ media { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&vic>; ++ ranges; ++ ++ sys: sys@12010000 { ++ compatible = "hisilicon,hi35xx_sys"; ++ reg = <0x12010000 0x10000>, <0x12020000 0x10000>, ++ <0x12060000 0x10000>, <0X12030000 0x10000>; ++ reg-names = "crg", "sys", "ddr", "misc"; ++ }; ++ ++ audio: audio@11310000 { ++ compatible = "hisilicon,hi35xx_aiao"; ++ interrupts = <9>; ++ reg = <0x11310000 0x10000>, <0x11320000 0x2000>; ++ reg-names = "aiao", "acodec"; ++ clocks = <&crg_ctrl HI3516CV300_AIAO_CLK>; ++ }; ++ ++ ive: ive@11230000 { ++ compatible = "hisilicon,hi35xx_ive"; ++ interrupts = <21>; ++ reg = <0x11230000 0x10000>; ++ clocks = <&crg_ctrl HI3516CV300_IVE_CLK>; ++ }; ++ ++ sensor_device0: sensor_device0 { ++ compatible = "hisilicon,hi35xx_sensor"; ++ clocks = <&crg_ctrl HI3516CV300_SENSOR_CLK>; ++ }; ++ ++ mipi: mipi@11300000 { ++ compatible = "hisilicon,hi35xx_mipi"; ++ interrupts = <28>; ++ reg = <0x11300000 0x10000>; ++ clocks = <&crg_ctrl HI3516CV300_MIPI_CLK>; ++ }; ++ ++ isp: isp@11380000 { ++ compatible = "hisilicon,hi35xx_isp"; ++ interrupts = <22>; ++ reg = <0x11380000 0x10000>, <0x11392200 0x40000>; ++ reg-names = "reg_vicap_base_va", "reg_isp_base_va"; ++ }; ++ ++ viu: viu@11380000 { ++ compatible = "hisilicon,hi35xx_viu"; ++ interrupts = <22>; ++ interrupt-names = "viu0"; ++ reg = <0x11380000 0x70000>; ++ reg-names = "viu0"; ++ clocks = <&crg_ctrl HI3516CV300_VIU_CLK>, <&crg_ctrl HI3516CV300_ISP_CLK>; ++ clock-names = "viu0", "isp0"; ++ }; ++ ++ vou: vou@11400000 { ++ compatible = "hisilicon,hi35xx_vou"; ++ interrupts = <23>; ++ reg = <0x11400000 0x10000>; ++ }; ++ ++ vgs: vgs@11240000 { ++ compatible = "hisilicon,hi35xx_vgs"; ++ interrupts = <29>; ++ reg = <0x11240000 0x10000>; ++ clocks = <&crg_ctrl HI3516CV300_VGS_CLK>; ++ }; ++ ++ vpss: vpss@11250000 { ++ compatible = "hisilicon,hi35xx_vpss"; ++ interrupts = <17>, <22>; ++ interrupt-names = "vpss", "vi"; ++ reg = <0x11250000 0x10000>; ++ clocks = <&crg_ctrl HI3516CV300_VPSS_CLK>; ++ }; ++ ++ vedu: vedu@11260000 { ++ compatible = "hisilicon,hi35xx_vedu"; ++ interrupts = <24>; ++ reg = <0x11260000 0x10000>; ++ clocks = <&crg_ctrl HI3516CV300_VEDU_CLK>; ++ }; ++ ++ jpege: jpege@11220000 { ++ compatible = "hisilicon,hi35xx_jpege"; ++ interrupts = <26>; ++ reg = <0x11220000 0x10000>; ++ clocks = <&crg_ctrl HI3516CV300_JPGE_CLK>; ++ }; ++ ++ pwm: pwm@12130000 { ++ compatible = "hisilicon,hi3516cv300-pwm"; ++ reg = <0x12130000 0x10000>; ++ clocks = <&crg_ctrl HI3516CV300_PWM_CLK>; ++ resets = <&crg_ctrl 0x38 0>; ++ #pwm-cells = <3>; ++ status = "disabled"; ++ }; ++ ++ piris: piris@12140000 { ++ compatible = "hisilicon,piris"; ++ reg = <0x12140000 0x10000>; ++ }; ++ ++ wtdg: wtdg@12080000 { ++ compatible = "hisilicon,hi_wdg"; ++ reg = <0x12080000 0x10000>; ++ reg-names = "wtdg"; ++ }; ++ ++ rtc: rtc@12090000 { ++ compatible = "hisilicon,hi_rtc"; ++ interrupts = <2>; ++ reg = <0x12090000 0x10000>; ++ }; ++ ++ ir: ir@120f0000{ ++ compatible = "hisilicon,hi_ir"; ++ interrupts = <19>; ++ reg = <0x120f0000 0x10000>; ++ }; ++ ++ online_flag: online_flag { ++ compatible = "hisilicon,vi-vpss-online"; ++ }; ++ ++ pin_ctrl_ddr: pin_ctrl_ddr { ++ compatible = "hisilicon,pinctrl-ddr"; ++ }; ++ ++ pin_ctrl_online: pin_ctrl_online { ++ compatible = "hisilicon,pinctrl-online"; ++ }; ++ ++ pin_ctrl_offline: pin_ctrl_offline { ++ compatible = "hisilicon,pinctrl-offline"; ++ }; ++ ++ user_pinmux: user_pinmux { ++ compatible = "hisilicon,user_define_pinmux"; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/hi3521d-demb.dts b/arch/arm/boot/dts/hi3521d-demb.dts +new file mode 100644 +index 0000000..548ed81 +--- /dev/null ++++ b/arch/arm/boot/dts/hi3521d-demb.dts +@@ -0,0 +1,275 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++/dts-v1/; ++#include "hi3521d.dtsi" ++ ++/ { ++ model = "Hisilicon HI3521D DEMO Board"; ++ compatible = "hisilicon,hi3521d"; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ enable-method = "hisilicon,hi3521d-smp"; ++ ++ cpu@0 { ++ compatible = "arm,cortex-a7"; ++ device_type = "cpu"; ++ clock-frequency = <HI3521D_FIXED_1500M>; ++ reg = <0>; ++ }; ++ ++ cpu@1 { ++ compatible = "arm,cortex-a7"; ++ device_type = "cpu"; ++ clock-frequency = <HI3521D_FIXED_1500M>; ++ reg = <1>; ++ }; ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x80000000 0x40000000>; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&spi_bus0{ ++ status = "okay"; ++ num-cs = <2>; ++ ++ spidev@0 { ++ compatible = "spidev"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <31250000>; ++ }; ++ ++ spidev@1 { ++ compatible = "spidev"; ++ reg = <1>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <31250000>; ++ }; ++}; ++ ++&hisfc { ++ hi_sfc { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ m25p,fast-read; ++ }; ++}; ++ ++&hisnfc { ++ hinand { ++ compatible = "jedec,spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ }; ++}; ++ ++&hidmac { ++ status = "okay"; ++}; ++ ++&mdio { ++ ethphy: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&higmac { ++ phy-handle = <ðphy>; ++ phy-mode = "rgmii"; ++}; ++ ++&pmux { ++ ++ viadclk_demob_pmux: viadclk_demob_pmux{ ++ pinctrl-single,pins = < ++ 0x0000 0x2 ++ 0x0024 0x2 ++ 0x0048 0x2 ++ 0x0070 0x2 ++ 0x0094 0x2 ++ >; ++ }; ++ ++ viadclk_sck_pmux: viadclk_sck_pmux{ ++ pinctrl-single,pins = < ++ 0x0000 0x2 ++ 0x004c 0x2 ++ 0x0024 0x2 ++ 0x0048 0x2 ++ 0x0070 0x2 ++ 0x0094 0x2 ++ >; ++ }; ++ ++ vicap_demob_pmux: vicap_demob_pmux{ ++ pinctrl-single,pins = < ++ 0x0004 0x1 ++ 0x0008 0x1 ++ 0x000c 0x1 ++ 0x0010 0x1 ++ 0x0014 0x1 ++ 0x0018 0x1 ++ 0x001c 0x1 ++ 0x0020 0x1 ++ 0x0028 0x1 ++ 0x002c 0x1 ++ 0x0030 0x1 ++ 0x0034 0x1 ++ 0x0038 0x1 ++ 0x003c 0x1 ++ 0x0040 0x1 ++ 0x0044 0x1 ++ 0x0050 0x1 ++ 0x0054 0x1 ++ 0x0058 0x1 ++ 0x005c 0x1 ++ 0x0060 0x1 ++ 0x0064 0x1 ++ 0x0068 0x1 ++ 0x006c 0x1 ++ 0x0074 0x1 ++ 0x0078 0x1 ++ 0x007c 0x1 ++ 0x0080 0x1 ++ 0x0084 0x1 ++ 0x0088 0x1 ++ 0x008c 0x1 ++ 0x0090 0x1 ++ >; ++ }; ++ ++ vo_vga_pmux: vo_vga_pmux{ ++ pinctrl-single,pins = < ++ 0x0174 0x1 ++ 0x017c 0x1 ++ 0x0180 0x1 ++ >; ++ }; ++ ++ vo_hdmi_pmux: vo_hdmi_pmux{ ++ pinctrl-single,pins = < ++ 0x0098 0x1 ++ 0x009c 0X1 ++ >; ++ }; ++ ++ i2s0_pmux: i2s0_pmux { ++ pinctrl-single,pins = < ++ 0x00a0 0x1 ++ 0x00a4 0x1 ++ 0x00a8 0x1 ++ >; ++ }; ++ ++ i2s1_pmux: i2s1_pmux { ++ pinctrl-single,pins = < ++ 0x00b0 0x1 ++ 0x00b4 0x1 ++ >; ++ }; ++ ++ i2s2_demob_pmux: i2s2_demob_pmux { ++ pinctrl-single,pins = < ++ 0x00b8 0x1 ++ 0x00bc 0x1 ++ 0x00c0 0x1 ++ >; ++ }; ++ ++ i2s2_sck_pmux: i2s2_sck_pmux { ++ pinctrl-single,pins = < ++ 0x00b8 0x1 ++ 0x00bc 0x1 ++ 0x00c0 0x1 ++ 0x00ac 0x2 ++ >; ++ }; ++}; ++ ++&sys_config_ctrl { ++ padctrl-ability,demo = < ++ 0x120f08a0 0x120 ++ 0x120f08a4 0x130 ++ 0x120f08a8 0x130 ++ 0x120f08ac 0x120 ++ 0x120f08b0 0x130 ++ 0x120f08b4 0x130 ++ 0x120f08b8 0x120 ++ 0x120f08bc 0x130 ++ 0x120f08c0 0x130 ++ 0x120f0800 0x140 ++ 0x120f084c 0x140 ++ 0x120f0898 0x40 ++ 0x120f089c 0x40 ++ >; ++ ++ sysctrl-ddr,pins = < ++ 0x12040098 0x00ffff1f ++ 0x1204009c 0x8888 ++ 0x12040168 0x00000f55 ++ 0x1212007c 0x35533201 ++ 0x12120080 0x65335526 ++ 0x12120084 0x66666666 ++ 0x12120094 0x65 /*VIVO axi max: 7*/ ++ >; ++ dllctrl-ddr,pins = < ++ 0x120401e0 0x0 /*DLL rst, disable*/ ++ 0x120401ec 0x0 ++ 0x120401f8 0x0 ++ 0x12040204 0x0 ++ 0x120401e0 0x4 /*DLL rst, disable*/ ++ 0x120401ec 0x4 ++ 0x120401f8 0x4 ++ 0x12040204 0x4 ++ 0x120401e0 0x6 /*DLL 150M and DLL rst, disable*/ ++ 0x120401ec 0x6 ++ 0x120401f8 0x6 ++ 0x12040204 0x6 ++ 0x120401e0 0x7 /*DLL 150M and DLL rst, enable*/ ++ 0x120401ec 0x7 ++ 0x120401f8 0x7 ++ 0x12040204 0x7 ++ 0x120401e0 0x3 /*DLL 150M and DLL unrst, enable*/ ++ 0x120401ec 0x3 ++ 0x120401f8 0x3 ++ 0x12040204 0x3 ++ 0x120401e4 0x10 /*dll_slave_en:1,dll_stop:0*/ ++ 0x120401F0 0x10 ++ 0x120401fC 0x10 ++ 0x12040208 0x10 ++ >; ++ ++ pinctrl-names = "demo", "sck", "default"; ++ pinctrl-0 = <&viadclk_demob_pmux &vicap_demob_pmux &i2s0_pmux &i2s2_demob_pmux &vo_hdmi_pmux &vo_vga_pmux>; ++ pinctrl-1 = <&viadclk_sck_pmux &vicap_demob_pmux &i2s0_pmux &i2s1_pmux &i2s2_sck_pmux &vo_hdmi_pmux &vo_vga_pmux >; ++ pinctrl-2 = <>; ++}; +diff --git a/arch/arm/boot/dts/hi3521d.dtsi b/arch/arm/boot/dts/hi3521d.dtsi +new file mode 100644 +index 0000000..5a2a2da +--- /dev/null ++++ b/arch/arm/boot/dts/hi3521d.dtsi +@@ -0,0 +1,392 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/hi3521d-clock.h> ++/ { ++ aliases { ++ fmc = &fmc; ++ serial0 = &uart0; ++ spi0 = &spi_bus0; ++ }; ++ ++ clock: clock@12040000 { ++ compatible = "hisilicon,hi3521d-clock"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ #clock-cells = <1>; ++ #reset-cells = <2>; ++ reg = <0x12040000 0x1000>; ++ }; ++ ++ gic: interrupt-controller@10300000 { ++ compatible = "arm,cortex-a7-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ /* gic dist base, gic cpu base , no virtual support */ ++ reg = <0x10301000 0x1000>, <0x10302000 0x100>; ++ }; ++ ++ soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&gic>; ++ ranges; ++ ++ sysctrl: system-controller@12050000 { ++ compatible = "hisilicon,sysctrl"; ++ reg = <0x12050000 0x1000>; ++ reboot-offset = <0x4>; ++ #clock-cells = <1>; ++ }; ++ ++ pmu { ++ compatible = "arm,cortex-a7-pmu"; ++ interrupts = <0 54 4>, ++ <0 48 4>; ++ }; ++ ++ amba { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "arm,amba-bus"; ++ ranges; ++ ++ uart0: uart@12080000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12080000 0x1000>; ++ interrupts = <0 6 4>; ++ clocks = <&clock HI3521D_UART0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart1: uart@121090000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12090000 0x1000>; ++ interrupts = <0 7 4>; ++ clocks = <&clock HI3521D_UART1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart2: uart@120a0000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x120a0000 0x1000>; ++ interrupts = <0 8 4>; ++ clocks = <&clock HI3521D_UART2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ timer@0x12000000 { ++ compatible = "hisilicon,timer"; ++ /* timer0 & timer1 & timer2*/ ++ reg = <0x12000000 0x20>, /* clocksource */ ++ <0x12000020 0x20>, /* local timer for each cpu */ ++ <0x12010000 0x20>; ++ interrupts = <0 1 4>, <0 2 4>; /* irq of local timer */ ++ clocks = <&sysctrl HI3521D_TIME0_0_CLK>; ++ clock-names = "timer00"; ++ }; ++ }; ++ ++ /* dual timer 0 and 1 have used for clocksource and local timer */ ++ dual_timer2: dual_timer@12020000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ /* timer4 & timer5 */ ++ interrupts = <0 3 4>; ++ reg = <0x12020000 0x1000>; ++ clocks = <&sysctrl HI3521D_TIME2_4_CLK>, ++ <&sysctrl HI3521D_TIME2_5_CLK>, ++ <&clock HI3521D_SYSAXI_CLK>; ++ clock-names = "timer4", "timer5", "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer3: dual_timer@12030000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ /* timer6 & timer7 */ ++ interrupts = <0 4 4>; ++ reg = <0x12030000 0x1000>; ++ clocks = <&sysctrl HI3521D_TIME3_6_CLK>, ++ <&sysctrl HI3521D_TIME3_7_CLK>, ++ <&clock HI3521D_SYSAXI_CLK>; ++ clock-names = "timer6", "timer7", "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ spi_bus0: spi@120d0000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x120d0000 0x1000>, <0x12120014 0x4>; ++ interrupts = <0 14 4>; ++ clocks = <&clock HI3521D_SPI0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ hisi,spi_cs_sb = <0>; ++ hisi,spi_cs_mask_bit = <0x00000003>; ++ }; ++ ++ sata_phy: phy@11010000 { ++ compatible = "hisilicon,hisi-sata-nano-phy"; ++ reg = <0x11010000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ++ ahci: sata@11010000 { ++ compatible = "hisilicon,hisi-ahci"; ++ reg = <0x11010000 0x1000>; ++ interrupts = <0 17 4>; ++ phys = <&sata_phy>; ++ phy-names = "sata-phy"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ usb_phy: usbphy { ++ compatible = "hisilicon,hi3521d-usb2-phy"; ++ reg = <0x12040000 0x1000>, <0x12120000 0x1000>; ++ }; ++ ++ ehci@0x10120000 { ++ compatible = "generic-ehci"; ++ reg = <0x10040000 0x10000>; ++ interrupts = <0 19 4>; ++ }; ++ ++ ohci@0x10110000 { ++ compatible = "generic-ohci"; ++ reg = <0x10030000 0x10000>; ++ interrupts = <0 18 4>; ++ }; ++ ++ mdio: mdio@100a03c0 { ++ compatible = "hisilicon,hisi-gemac-mdio"; ++ reg = <0x100a03c0 0x20>; ++ clocks = <&clock HI3521D_ETH_CLK>; ++ resets = <&clock 0x14c 5>; ++ reset-names = "phy_reset"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ higmac: ethernet@100a0000 { ++ compatible = "hisilicon,higmac-v5"; ++ reg = <0x100a0000 0x1000>,<0x100a300c 0x4>; ++ interrupts = <0 16 4>, <0 61 4>, <0 62 4>, <0 63 4>; ++ ++ clocks = <&clock HI3521D_ETH_CLK>, ++ <&clock HI3521D_ETH_MACIF_CLK>; ++ clock-names = "higmac_clk", ++ "macif_clk"; ++ ++ resets = <&clock 0x14c 0>, ++ <&clock 0x14c 2>, ++ <&clock 0x14c 5>; ++ reset-names = "port_reset", ++ "macif_reset", ++ "phy_reset"; ++ ++ mac-address = [00 00 00 00 00 00]; ++ }; ++ ++ fmc: flash-memory-controller@10000000 { ++ compatible = "hisilicon,hisi-fmc"; ++ reg = <0x10000000 0x1000>, <0x14000000 0x10000>; ++ reg-names = "control", "memory"; ++ clocks = <&clock HI3521D_FMC_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hisfc:spi_nor_controller { ++ compatible = "hisilicon,hisi-sfc"; ++ assigned-clocks = <&clock HI3521D_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hisnfc:spi_nand_controller { ++ compatible = "hisilicon,hisi-spi-nand"; ++ assigned-clocks = <&clock HI3521D_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ ++ hidmac: hidma-controller@10060000 { ++ compatible = "hisilicon,hisi-dmac"; ++ reg = <0x10060000 0x1000>; ++ interrupts = <0 10 4>; ++ clocks = <&clock HI3521D_DMAC_CLK>; ++ clock-names = "dmac_clk"; ++ resets = <&clock 0x144 0>; ++ reset-names = "dma-reset"; ++ #dma-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ pmux: pinmux@120F0000 { ++ compatible = "pinctrl-single"; ++ reg = <0x120F0000 0x3A8>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ #gpio-range-cells = <3>; ++ ranges; ++ ++ pinctrl-single,register-width = <32>; ++ pinctrl-single,function-mask = <7>; ++ /* pin base, nr pins & gpio function */ ++ pinctrl-single,gpio-range = <&range 0 54 0 ++ &range 55 6 1 &range 61 5 0>; ++ ++ range: gpio-range { ++ #pinctrl-single,gpio-range-cells = <3>; ++ }; ++ }; ++ }; ++ ++ media { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&gic>; ++ ranges; ++ ++ osal: osal { ++ compatible = "hisilicon,osal"; ++ }; ++ ++ sys: sys@12040000 { ++ compatible = "hisilicon,hi35xx_sys"; ++ reg = <0x12040000 0x10000>, <0x12050000 0x10000>, ++ <0x12110000 0x10000>, <0x12120000 0x10000>; ++ reg-names = "crg", "sys", "ddr", "misc"; ++ }; ++ ++ viu: viu@130C0000 { ++ compatible = "hisilicon,hi35xx_viu"; ++ interrupts = <0 28 4>; ++ reg = <0x130C0000 0x40000>; ++ clocks = <&clock HI3521D_VIU_CLK>; ++ clock-names = "viu"; ++ }; ++ ++ vou: vou@13020000 { ++ compatible = "hisilicon,hi35xx_vou"; ++ interrupts = <0 30 4>; ++ reg = <0x13020000 0x10000>; ++ }; ++ ++ vda: vda@13150000 { ++ compatible = "hisilicon,hi35xx_vda"; ++ interrupts = <0 43 4>; ++ reg = <0x13150000 0x10000>; ++ }; ++ ++ ive: ive@13060000 { ++ compatible = "hisilicon,hi35xx_ive"; ++ interrupts = <0 33 4>; ++ reg = <0x13060000 0x10000>; ++ }; ++ ++ vgs: vgs@13080000 { ++ compatible = "hisilicon,hi35xx_vgs"; ++ interrupts = <0 35 4>; ++ reg = <0x13080000 0x10000>; ++ }; ++ ++ vpss: vpss@13110000 { ++ compatible = "hisilicon,hi35xx_vpss"; ++ interrupts = <0 37 4>; ++ interrupt-names = "vpss0"; ++ reg = <0x13110000 0x10000>; ++ reg-names = "vpss0"; ++ }; ++ ++ audio: audio@13140000 { ++ compatible = "hisilicon,hi35xx_aiao"; ++ interrupts = <0 40 4>; ++ reg = <0x13140000 0x10000>, <0x13120000 0x10000>; ++ reg-names = "aiao", "voie"; ++ }; ++ ++ vdec: vdec@13100000 { ++ compatible = "hisilicon,hi35xx_vdec"; ++ interrupts =<0 21 4> , <0 23 4>; ++ interrupt-names ="vdm", "scd"; ++ reg = <0x13100000 0xc000>, <0x1310c000 0x4000>; ++ reg-names = "vdm", "scd"; ++ }; ++ ++ tde: tde@13050000 { ++ compatible = "hisilicon,hi35xx_tde"; ++ interrupts = <0 32 4>; ++ reg = <0x13050000 0x1000>; ++ }; ++ ++ jpgd: jpgd@13070000 { ++ compatible = "hisilicon,hi35xx_jpgd"; ++ interrupts = <0 34 4>; ++ interrupt-names = "jpgd"; ++ reg = <0x13070000 0x10000>; ++ reg-names = "jpgd"; ++ }; ++ ++ vedu: vedu@13040000 { ++ compatible = "hisilicon,hi35xx_vedu"; ++ interrupts = <0 31 4>; ++ interrupt-names = "vedu0"; ++ reg = <0x13040000 0x10000>; ++ reg-names = "vedu0"; ++ }; ++ ++ jpege: jpege@13130000 { ++ compatible = "hisilicon,hi35xx_jpege"; ++ interrupts = <0 39 4>; ++ reg = <0x13130000 0x10000>; ++ }; ++ ++ online_flag: online_flag { ++ compatible = "hisilicon,vi-vpss-online"; ++ }; ++ ++ sys_config_ctrl: sys_config_ctrl { ++ compatible = "hisilicon,sys_config_ctrl"; ++ }; ++ ++ pin_ctrl_ddr: pin_ctrl_ddr { ++ compatible = "hisilicon,pinctrl-ddrpinctrl-ddr"; ++ }; ++ ++ pin_ctrl_online: pin_ctrl_online { ++ compatible = "hisilicon,pinctrl-online"; ++ }; ++ ++ pin_ctrl_offline: pin_ctrl_offline { ++ compatible = "hisilicon,pinctrl-offline"; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/hi3531d-demb.dts b/arch/arm/boot/dts/hi3531d-demb.dts +new file mode 100644 +index 0000000..901076a +--- /dev/null ++++ b/arch/arm/boot/dts/hi3531d-demb.dts +@@ -0,0 +1,558 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++/dts-v1/; ++#include "hi3531d.dtsi" ++ ++/ { ++ model = "Hisilicon HI3531D DEMO Board"; ++ compatible = "hisilicon,hi3531d"; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ enable-method = "hisilicon,hi3531d-smp"; ++ ++ cpu@0 { ++ compatible = "arm,cortex-a9"; ++ device_type = "cpu"; ++ reg = <0>; ++ next-level-cache = <&L2>; ++ }; ++ ++ cpu@1 { ++ compatible = "arm,cortex-a9"; ++ device_type = "cpu"; ++ reg = <1>; ++ next-level-cache = <&L2>; ++ }; ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x40000000 0xc0000000>; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&spi_bus0{ ++ status = "okay"; ++ num-cs = <4>; ++ ++ spidev@0 { ++ compatible = "spidev"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <40500000>; ++ }; ++ ++ spidev@1 { ++ compatible = "spidev"; ++ reg = <1>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <40500000>; ++ }; ++ ++ spidev@2 { ++ compatible = "spidev"; ++ reg = <2>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <40500000>; ++ }; ++ ++ spidev@3 { ++ compatible = "spidev"; ++ reg = <3>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <40500000>; ++ }; ++}; ++ ++&hisfc { ++ hi_sfc { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ m25p,fast-read; ++ }; ++}; ++ ++&hisnfc { ++ hinand { ++ compatible = "jedec,spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ }; ++}; ++ ++&hinfc610 { ++ assigned-clocks = <&clock HI3531D_NFC_CLK>; ++ assigned-clock-rates = <200000000>; ++ ++ hinand { ++ compatible = "jedec,nand"; ++ reg = <0>; ++ nand-max-frequency = <200000000>; ++ }; ++}; ++ ++&hidmac { ++ status = "okay"; ++}; ++ ++&mdio { ++ ethphy: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&higmac { ++ phy-handle = <ðphy>; ++ phy-mode = "rgmii"; ++}; ++ ++&pmux { ++ i2c0_pmux: i2c0_pmux { ++ pinctrl-single,pins = < ++ 0x01CC 0x1 /*I2C0_SDA*/ ++ 0x01D0 0x1 /*I2C0_SCL*/ ++ >; ++ }; ++ ++ i2c1_pmux: i2c1_pmux { ++ pinctrl-single,pins = < ++ 0x0148 0x2 /*I2C1_SDA*/ ++ 0x014C 0x2 /*I2C1_SCL*/ ++ >; ++ }; ++ ++ vicap_demob_pmux: vicap_demob_pmux{ ++ pinctrl-single,pins = < ++ 0x0000 0x0 /*VI0_CLK*/ ++ 0x0024 0x0 /*VI1_CLK*/ ++ 0x0048 0x1 /*VI_ADC_REFCLK0*/ ++ 0x004c 0x0 /*VI2_CLK*/ ++ 0x0070 0x0 /*VI3_CLK*/ ++ 0x0094 0x1 /*VI_ADC_REFCLK1*/ ++ 0x0098 0x0 /*VI4_CLK*/ ++ 0x00bc 0x0 /*VI5_CLK*/ ++ 0x00e4 0x0 /*VI6_CLK*/ ++ 0x0108 0x0 /*VI7_CLK*/ ++ >; ++ }; ++ vicap_demopro_pmux: vicap_demopro_pmux{ ++ pinctrl-single,pins = < ++ 0x0000 0x2 /*VI_ADC_REFCLK0*/ ++ 0x0024 0x2 /*VI0_CLK*/ ++ 0x0048 0x2 /*VI1_CLK*/ ++ 0x004c 0x2 /*VI_ADC_REFCLK1*/ ++ 0x0070 0x2 /*VI2_CLK*/ ++ 0x0094 0x2 /*VI3_CLK*/ ++ 0x00bc 0x2 /*VI_ADC_REFCLK2*/ ++ 0x00E0 0x2 /*VI4_CLK */ ++ 0x0108 0x2 /*VI_ADC_REFCLK3*/ ++ 0x012C 0x2 /*VI6_CLK */ ++ >; ++ }; ++ vicap_cascade_pmux: vicap_cascade_pmux{ ++ pinctrl-single,pins = < ++ 0x0290 0x1 /*VI8_DAT14*/ ++ 0x0294 0x1 /*VI8_DAT13*/ ++ 0x0298 0x1 /*VI8_DAT12*/ ++ 0x029C 0x1 /*VI8_DAT11*/ ++ 0x02A0 0x1 /*VI8_DAT10*/ ++ 0x02A4 0x1 /*VI8_DAT9 */ ++ 0x02A8 0x1 /*VI8_DAT8 */ ++ 0x02AC 0x1 /*VI8_DAT7 */ ++ 0x02B0 0x1 /*VI8_DAT1 */ ++ 0x02B4 0x1 /*VI8_CLK */ ++ 0x02B8 0x1 /*VI8_DAT3 */ ++ 0x02BC 0x1 /*VI8_DAT4 */ ++ 0x02C0 0x1 /*VI8_DAT5 */ ++ 0x02C4 0x1 /*VI8_DAT6 */ ++ 0x02C8 0x1 /*VI8_DAT2 */ ++ 0x02CC 0x1 /*VI8_DAT0 */ ++ 0x02D0 0x1 /*VI8_DAT15*/ ++ >; ++ }; ++ ++ vicap_sck_pmux: vicap_sck_pmux{ ++ pinctrl-single,pins = < ++ 0x0000 0x2 /* 0: VI0_CLK 1: GPIO21_0 2: VI_ADC_REFCLK0 */ ++ 0x0024 0x2 /* 0: VI1_CLK 1: GPIO21_1 2: VI0_CLK */ ++ 0x0048 0x2 /* 0: GPIO21_2 1: VI_ADC_REFCLK0 2: VI1_CLK */ ++ 0x004c 0x2 /* 0: VI2_CLK 1: GPIO21_3 2: VI_ADC_REFCLK1 */ ++ 0x0070 0x2 /* 0: VI3_CLK 1: GPIO21_4 2: VI2_CLK */ ++ 0x0094 0x2 /* 0: GPIO21_5 1: VI_ADC_REFCLK1 2: VI3_CLK */ ++ 0x0098 0x2 /* 0: VI4_CLK 1: GPIO21_6 2: VI_ADC_REFCLK2 */ ++ 0x00bc 0x2 /* 0: VI5_CLK 1: GPIO21_7 2: VI4_CLK */ ++ 0x00E0 0x2 /* 0: GPIO12_1 1: VI_ADC_REFCLK2 2: VI5_CLK */ ++ 0x00e4 0x2 /* 0: VI6_CLK 1: GPIO12_2 2: VI_ADC_REFCLK3 */ ++ 0x0108 0x2 /* 0: VI7_CLK 1: GPIO15_7 2: VI6_CLK */ ++ 0x012C 0x2 /* 0: GPIO20_6 1: VI_ADC_REFCLK3 2: VI7_CLK */ ++ >; ++ }; ++ ++ vo_bt1120_pmux_demo: vo_bt1120_pmux_demo{ ++ pinctrl-single,pins = < ++ 0x0154 0x1 /*VOU_SLV_DAT15*/ ++ 0x0158 0x1 /*VOU_SLV_DAT1*/ ++ 0x015C 0x1 /*VOU1120_DAT1*/ ++ 0x0160 0x1 /*VOU1120_DAT2*/ ++ 0x0164 0x1 /*VOU1120_DAT3*/ ++ 0x0168 0x1 /*VOU1120_DAT4*/ ++ 0x016C 0x1 /*VOU1120_DAT5*/ ++ 0x0170 0x1 /*VOU1120_DAT6*/ ++ 0x0174 0x1 /*VOU1120_DAT7*/ ++ 0x0178 0x1 /*VOU1120_DAT8*/ ++ 0x017C 0x1 /*VOU1120_DAT9*/ ++ 0x0180 0x1 /*VOU1120_DAT10*/ ++ 0x0184 0x1 /*VOU1120_DAT11*/ ++ 0x0188 0x1 /*VOU1120_DAT12*/ ++ 0x018C 0x1 /*VOU1120_DAT13*/ ++ 0x0190 0x1 /*VOU1120_DAT14*/ ++ 0x0194 0x1 /*VOU1120_DAT15*/ ++ >; ++ }; ++ ++ vo_bt1120_pmux_slave1: vo_bt1120_pmux_slave1{ ++ pinctrl-single,pins = < ++ 0x01D4 0x1 /*VOU_SLV_DAT15*/ ++ 0x01D8 0x1 /*VOU_SLV_DAT1*/ ++ 0x01DC 0x1 /*VOU_SLV_DAT0*/ ++ 0x01E0 0x1 /*VOU_SLV_DAT9*/ ++ 0x01E4 0x1 /*VOU_SLV_DAT14*/ ++ 0x01E8 0x1 /*VOU_SLV_DAT8*/ ++ 0x01EC 0x1 /*VOU_SLV_CLK*/ ++ 0x01F0 0x1 /*VOU_SLV_DAT2*/ ++ 0x01F4 0x1 /*VOU_SLV_DAT3*/ ++ 0x01F8 0x1 /*VOU_SLV_DAT5 */ ++ 0x01FC 0x1 /*VOU_SLV_DAT4 */ ++ 0x0200 0x1 /*VOU_SLV_DAT11*/ ++ 0x0204 0x1 /*VOU_SLV_DAT10*/ ++ 0x0208 0x1 /*VOU_SLV_DAT7 */ ++ 0x020C 0x1 /*VOU_SLV_DAT6 */ ++ 0x0210 0x1 /*VOU_SLV_DAT12*/ ++ 0x0214 0x1 /*VOU_SLV_DAT13*/ ++ >; ++ }; ++ ++ vo_bt1120_pmux_slave2: vo_bt1120_pmux_slave2{ ++ pinctrl-single,pins = < ++ 0x01D4 0x1 /*VOU_SLV_DAT15*/ ++ 0x01D8 0x1 /*VOU_SLV_DAT1*/ ++ 0x01DC 0x1 /*VOU_SLV_DAT0*/ ++ 0x01E0 0x1 /*VOU_SLV_DAT9*/ ++ 0x01E4 0x1 /*VOU_SLV_DAT14*/ ++ 0x01E8 0x1 /*VOU_SLV_DAT8*/ ++ 0x01EC 0x1 /*VOU_SLV_CLK*/ ++ 0x01F0 0x1 /*VOU_SLV_DAT2*/ ++ 0x01F4 0x1 /*VOU_SLV_DAT3*/ ++ 0x01F8 0x1 /*VOU_SLV_DAT5 */ ++ 0x01FC 0x1 /*VOU_SLV_DAT4 */ ++ 0x0200 0x1 /*VOU_SLV_DAT11*/ ++ 0x0204 0x1 /*VOU_SLV_DAT10*/ ++ 0x0208 0x1 /*VOU_SLV_DAT7 */ ++ 0x020C 0x1 /*VOU_SLV_DAT6 */ ++ 0x0210 0x1 /*VOU_SLV_DAT12*/ ++ 0x0214 0x1 /*VOU_SLV_DAT13*/ ++ >; ++ }; ++ ++ ++ vo_vga_pmux: vo_vga_pmux{ ++ pinctrl-single,pins = < ++ 0x01A4 0x1 /*VGA_HS*/ ++ 0x01A8 0X1 /*VGA_VS*/ ++ >; ++ }; ++ ++ i2s0_pmux: i2s0_pmux { ++ pinctrl-single,pins = < ++ 0x0130 0x1 /* 0: GPIO11_0 1: I2S0_BCLK_RX */ ++ 0x0134 0x1 /* 0: GPIO11_1 1: I2S0_WS_RX */ ++ 0x0138 0x1 /* 0: GPIO11_2 1: I2S0_SD_RX */ ++ 0x01B4 0x1 /* 0: GPIO12_0 1: I2S2_SD_RX */ ++ 0x01BC 0x1 /* 0: GPIO12_4 1: I2S2_MCLK */ ++ >; ++ }; ++ ++ i2s1_pmux: i2s1_pmux { ++ pinctrl-single,pins = < ++ 0x013c 0x1 /*I2S1_BCLK_RX*/ ++ 0x0140 0x1 /*I2S1_WS_RX*/ ++ 0x0144 0x1 /*I2S1_SD_RX*/ ++ >; ++ }; ++ ++ i2s2_pmux: i2s2_pmux { ++ pinctrl-single,pins = < ++ 0x01AC 0x1 /*I2S2_BCLK_RX*/ ++ 0x01B0 0x1 /*I2S2_WS_RX*/ ++ 0x01B8 0x1 /*I2S2_SD_RX*/ ++ >; ++ }; ++ ++ i2s3_pmux: i2s3_pmux { ++ pinctrl-single,pins = < ++ 0x0198 0x1 /*I2S3_BCLK_RX*/ ++ 0x019C 0x1 /*I2S3_WS_RX*/ ++ 0x01A0 0x1 /*I2S3_SD_RX*/ ++ >; ++ }; ++ ++ uart0_pmux: uart0_pmux { ++ pinctrl-single,pins = < ++ 0x0158 0x2 /*0: GPIO9_0 1: VOU1120_DAT0 2: UART0_RXD*/ ++ 0x015c 0x2 /*0: GPIO9_1 1: VOU1120_DAT1 2: UART0_TXD*/ ++ >; ++ }; ++}; ++ ++&sys_config_ctrl { ++ ++ padctrl-ability,demo = < ++ 0x120f093c 0x130 ++ 0x120f0940 0x130 ++ 0x120f0944 0x130 ++ 0x120f09bc 0x130 ++ 0x120f09c0 0x130 ++ 0x120f09c8 0x530 ++ 0x120f09a8 0x130 ++ 0x120f09ac 0x130 ++ 0x120f09b0 0x530 ++ 0x120f0b1c 0x130 ++ 0x120f0b24 0x130 ++ 0x120f0b28 0x130 ++ 0x120f0964 0x40 ++ 0x120f0968 0x210 ++ 0x120f096c 0x10 ++ 0x120f0970 0x210 ++ 0x120f0974 0x10 ++ 0x120f0978 0x10 ++ 0x120f097c 0x10 ++ 0x120f0980 0x210 ++ 0x120f0984 0x10 ++ 0x120f0988 0x410 ++ 0x120f098c 0x410 ++ 0x120f0990 0x410 ++ 0x120f0994 0x10 ++ 0x120f0998 0x10 ++ 0x120f099c 0x10 ++ 0x120f09a0 0x10 ++ 0x120f09a4 0x10 ++ 0x120f09b4 0x40 ++ 0x120f09b8 0x40 ++ >; ++ ++ padctrl-ability,sck = < ++ 0x120f093c 0x130 ++ 0x120f0940 0x130 ++ 0x120f0944 0x130 ++ 0x120f09bc 0x130 ++ 0x120f09c0 0x130 ++ 0x120f09c8 0x530 ++ 0x120f09a8 0x130 ++ 0x120f09ac 0x130 ++ 0x120f09b0 0x530 ++ 0x120f0b1c 0x130 ++ 0x120f0b24 0x130 ++ 0x120f0b28 0x130 ++ 0x120f0964 0x40 ++ 0x120f0968 0x210 ++ 0x120f096c 0x10 ++ 0x120f0970 0x210 ++ 0x120f0974 0x10 ++ 0x120f0978 0x10 ++ 0x120f097c 0x10 ++ 0x120f0980 0x210 ++ 0x120f0984 0x10 ++ 0x120f0988 0x410 ++ 0x120f098c 0x410 ++ 0x120f0990 0x410 ++ 0x120f0994 0x10 ++ 0x120f0998 0x10 ++ 0x120f099c 0x10 ++ 0x120f09a0 0x10 ++ 0x120f09a4 0x10 ++ 0x120f09b4 0x40 ++ 0x120f09b8 0x40 ++ 0x120f0930 0x130/* I2S0_BCLK_RX------I2S0_BCLK_RX */ ++ 0x120f0934 0x130/* I2S0_WS_RX------I2S0_WS_RX */ ++ 0x120f0938 0x130/* I2S0_SD_RX------I2S0_SD_RX */ ++ 0x120f09c4 0x130/* I2S2_SD_RX------I2S2_SD_RX */ ++ 0x120f09cc 0x530/* I2S2_MCLK------I2S2_MCLK */ ++ >; ++ ++ padctrl-ability,slave1 = < ++ 0x120f093c 0x130 ++ 0x120f0940 0x130 ++ 0x120f0944 0x130 ++ 0x120f09bc 0x130 ++ 0x120f09c0 0x130 ++ 0x120f09c8 0x530 ++ 0x120f0b1c 0x130 ++ 0x120f0b24 0x130 ++ 0x120f0b28 0x130 ++ 0x120f0ac4 0x330 ++ 0x120f0adc 0x130 ++ 0x120f0ac0 0x330 ++ 0x120f0ad8 0x130 ++ 0x120f0ac8 0x130 ++ 0x120f0acc 0x130 ++ 0x120f0ad0 0x130 ++ 0x120f0ad4 0x130 ++ 0x120f0abc 0x130 ++ 0x120f0ab8 0x130 ++ 0x120f0ab4 0x130 ++ 0x120f0ab0 0x130 ++ 0x120f0aac 0x130 ++ 0x120f0aa8 0x130 ++ 0x120f0aa4 0x130 ++ 0x120f0aa0 0x130 ++ 0x120f0ae0 0x130 ++ 0x120f09b4 0x40 ++ 0x120f09b8 0x40 ++ 0x120f09fc 0xD0 ++ 0x120f09ec 0x20 ++ 0x120f09e8 0x20 ++ 0x120f0a00 0x20 ++ 0x120f0a04 0x20 ++ 0x120f0a0c 0x20 ++ 0x120f0a08 0x220 ++ 0x120f0a1c 0x20 ++ 0x120f0a18 0x220 ++ 0x120f09f8 0x20 ++ 0x120f09f0 0x20 ++ 0x120f0a14 0x20 ++ 0x120f0a10 0x20 ++ 0x120f0a20 0x220 ++ 0x120f0a24 0x20 ++ 0x120f09f4 0x20 ++ 0x120f09e4 0x20 ++ 0x120f09fc 0xD0 ++ 0x12040178 0x40 ++ >; ++ ++padctrl-ability,slave2 = < ++ 0x120f093c 0x130 ++ 0x120f0940 0x130 ++ 0x120f0944 0x130 ++ 0x120f09bc 0x130 ++ 0x120f09c0 0x130 ++ 0x120f09c8 0x530 ++ 0x120f09fc 0xD0 ++ 0x120f09ec 0x20 ++ 0x120f09e8 0x20 ++ 0x120f0a00 0x20 ++ 0x120f0a04 0x20 ++ 0x120f0a0c 0x20 ++ 0x120f0a08 0x220 ++ 0x120f0a1c 0x20 ++ 0x120f0a18 0x220 ++ 0x120f09f8 0x20 ++ 0x120f09f0 0x20 ++ 0x120f0a14 0x20 ++ 0x120f0a10 0x20 ++ 0x120f0a20 0x220 ++ 0x120f0a24 0x20 ++ 0x120f09f4 0x20 ++ 0x120f09e4 0x20 ++ 0x12040178 0x40 ++ >; ++ ++ sysctrl-ddr,pins = < ++ 0x1212007c 0x35553301 ++ 0x12120080 0x33355633 ++ 0x12120084 0x66266623 ++ 0x12120088 0x66666666 ++ 0x12120054 0x80018001 ++ 0x12040094 0x0000FFFF /*PPC*/ ++ 0x12040098 0x01ffff1f ++ 0x1204009c 0x8888 /*PT clk default 9999*/ ++ 0x120400a0 0x88888 ++ 0x12040168 0x00000f55 /*ADC*/ ++ >; ++ ++ dllctrl-ddr,pins = < ++ 0x120401e0 0x0 /*DLL rst, disable*/ ++ 0x120401ec 0x0 ++ 0x120401f8 0x0 ++ 0x12040204 0x0 ++ 0x12040210 0x0 ++ 0x1204021c 0x0 ++ 0x12040228 0x0 ++ 0x12040234 0x0 ++ 0x120401e0 0x4 /*DLL rst, disable*/ ++ 0x120401ec 0x4 ++ 0x120401f8 0x4 ++ 0x12040204 0x4 ++ 0x12040210 0x4 ++ 0x1204021c 0x4 ++ 0x12040228 0x4 ++ 0x12040234 0x4 ++ 0x120401e0 0x6 /*DLL 150M and DLL rst, disable*/ ++ 0x120401ec 0x6 ++ 0x120401f8 0x6 ++ 0x12040204 0x6 ++ 0x12040210 0x6 ++ 0x1204021c 0x6 ++ 0x12040228 0x6 ++ 0x12040234 0x6 ++ 0x120401e0 0x7 /*DLL 150M and DLL rst, enable*/ ++ 0x120401ec 0x7 ++ 0x120401f8 0x7 ++ 0x12040204 0x7 ++ 0x12040210 0x7 ++ 0x1204021c 0x7 ++ 0x12040228 0x7 ++ 0x12040234 0x7 ++ 0x120401e0 0x3 /*DLL 150M and DLL unrst, enable*/ ++ 0x120401ec 0x3 ++ 0x120401f8 0x3 ++ 0x12040204 0x3 ++ 0x12040210 0x3 ++ 0x1204021c 0x3 ++ 0x12040228 0x3 ++ 0x12040234 0x3 ++ 0x120401e4 0x10 /*dll_slave_en:1,dll_stop:0*/ ++ 0x120401F0 0x10 ++ 0x120401fC 0x10 ++ 0x12040208 0x10 ++ 0x12040214 0x10 ++ 0x12040220 0x10 ++ 0x1204022c 0x10 ++ 0x12040238 0x10 ++ >; ++ ++ pinctrl-names = "demo", "demopro", "slave1", "slave2", "sck", "default"; ++ pinctrl-0 = <&vo_bt1120_pmux_demo &vo_vga_pmux &vicap_demob_pmux &i2c0_pmux &i2c1_pmux &i2s1_pmux &i2s2_pmux &i2s3_pmux>; ++ pinctrl-1 = <&vo_bt1120_pmux_demo &vo_vga_pmux &vicap_demopro_pmux &i2c0_pmux &i2c1_pmux &i2s1_pmux &i2s2_pmux &i2s3_pmux>; ++ pinctrl-2 = <&vo_bt1120_pmux_slave1 &vo_vga_pmux &vicap_demob_pmux &i2c0_pmux &i2c1_pmux &i2s1_pmux &i2s2_pmux &i2s3_pmux &vicap_cascade_pmux>; ++ pinctrl-3 = <&vo_bt1120_pmux_slave2 &vo_vga_pmux &vicap_demob_pmux &i2c0_pmux &i2c1_pmux &i2s1_pmux &i2s2_pmux &i2s3_pmux &uart0_pmux>; ++ pinctrl-4 = <&vo_bt1120_pmux_demo &vo_vga_pmux &vicap_sck_pmux &i2c0_pmux &i2c1_pmux &i2s1_pmux &i2s2_pmux &i2s3_pmux &i2s0_pmux>; ++ pinctrl-5 = <>; ++}; +diff --git a/arch/arm/boot/dts/hi3531d.dtsi b/arch/arm/boot/dts/hi3531d.dtsi +new file mode 100644 +index 0000000..aac76f7 +--- /dev/null ++++ b/arch/arm/boot/dts/hi3531d.dtsi +@@ -0,0 +1,417 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/hi3531d-clock.h> ++/ { ++ aliases { ++ fmc = &fmc; ++ serial0 = &uart0; ++ spi0 = &spi_bus0; ++ }; ++ ++ clock: clock@12040000 { ++ compatible = "hisilicon,hi3531d-clock"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ #clock-cells = <1>; ++ #reset-cells = <2>; ++ reg = <0x12040000 0x1000>; ++ }; ++ ++ gic: interrupt-controller@10300000 { ++ compatible = "arm,cortex-a9-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ /* gic dist base, gic cpu base , no virtual support */ ++ reg = <0x10301000 0x1000>, <0x10300100 0x100>; ++ }; ++ ++ soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&gic>; ++ ranges; ++ ++ sysctrl: system-controller@12050000 { ++ compatible = "hisilicon,sysctrl"; ++ reg = <0x12050000 0x1000>; ++ reboot-offset = <0x4>; ++ #clock-cells = <1>; ++ }; ++ ++ pmu { ++ compatible = "arm,cortex-a9-pmu"; ++ interrupts = <0 25 4>, ++ <0 54 4>; ++ }; ++ ++ amba { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "arm,amba-bus"; ++ ranges; ++ ++ L2: l2-cache@10700000 { ++ compatible = "arm,pl310-cache"; ++ reg = <0x10700000 0x10000>; ++ interrupts = <0 55 4>; ++ cache-unified; ++ cache-level = <2>; ++ }; ++ ++ uart0: uart@12080000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12080000 0x1000>; ++ interrupts = <0 6 4>; ++ clocks = <&clock HI3531D_UART0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart1: uart@121090000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12090000 0x1000>; ++ interrupts = <0 7 4>; ++ clocks = <&clock HI3531D_UART1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart2: uart@120a0000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x120a0000 0x1000>; ++ interrupts = <0 8 4>; ++ clocks = <&clock HI3531D_UART2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart3: uart@12130000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12130000 0x1000>; ++ interrupts = <0 20 4>; ++ clocks = <&clock HI3531D_UART3_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ timer@0x12000000 { ++ compatible = "hisilicon,timer"; ++ /* timer0 & timer1 & timer2*/ ++ reg = <0x12000000 0x20>, /* clocksource */ ++ <0x12000020 0x20>, /* local timer for each cpu */ ++ <0x12010000 0x20>; ++ interrupts = <0 1 4>, <0 2 4>; /* irq of local timer */ ++ clocks = <&sysctrl HI3531D_TIME0_0_CLK>; ++ clock-names = "timer00"; ++ }; ++ }; ++ ++ /* dual timer 0 and 1 have used for clocksource and local timer */ ++ dual_timer2: dual_timer@12020000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ /* timer4 & timer5 */ ++ interrupts = <0 3 4>; ++ reg = <0x12020000 0x1000>; ++ clocks = <&sysctrl HI3531D_TIME2_4_CLK>, ++ <&sysctrl HI3531D_TIME2_5_CLK>, ++ <&clock HI3531D_PERIAXI_CLK>; ++ clock-names = "timer4", "timer5", "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer3: dual_timer@12030000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ /* timer4 & timer5 */ ++ interrupts = <0 4 4>; ++ reg = <0x12030000 0x1000>; ++ clocks = <&sysctrl HI3531D_TIME3_6_CLK>, ++ <&sysctrl HI3531D_TIME3_7_CLK>, ++ <&clock HI3531D_PERIAXI_CLK>; ++ clock-names = "timer6", "timer7", "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ spi_bus0: spi@120d0000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x120d0000 0x1000>, <0x12120014 0x4>; ++ interrupts = <0 14 4>; ++ clocks = <&clock HI3531D_SPI0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ hisi,spi_cs_sb = <0>; ++ hisi,spi_cs_mask_bit = <0x00000007>; ++ }; ++ ++ sata_phy: phy@11010000 { ++ compatible = "hisilicon,hisi-sata-nano-phy"; ++ reg = <0x11010000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ++ ahci: sata@11010000 { ++ compatible = "hisilicon,hisi-ahci"; ++ reg = <0x11010000 0x1000>; ++ interrupts = <0 17 4>; ++ phys = <&sata_phy>; ++ phy-names = "sata-phy"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ usb_phy: usbphy { ++ compatible = "hisilicon,hi3531d-usb2-phy"; ++ reg = <0x12040000 0x1000>, <0x12120000 0x1000>, ++ <0x12010000 0x1000>; ++ }; ++ ++ usb3_phy: phy3 { ++ compatible = "hisilicon,hi3531d-usb3-phy"; ++ reg = <0x11000000 0x10000>, <0x12040000 0x10000>, ++ <0x12120000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ++ ehci@0x10120000 { ++ compatible = "generic-ehci"; ++ reg = <0x100c0000 0x10000>; ++ interrupts = <0 19 4>; ++ }; ++ ++ ohci@0x10110000 { ++ compatible = "generic-ohci"; ++ reg = <0x100b0000 0x10000>; ++ interrupts = <0 18 4>; ++ }; ++ ++ xhci@0x10180000 { ++ compatible = "hisilicon,hi3519-xhci", "generic-xhci"; ++ reg = <0x11000000 0x10000>; ++ interrupts = <0 22 4>; ++ }; ++ ++ mdio: mdio@100a03c0 { ++ compatible = "hisilicon,hisi-gemac-mdio"; ++ reg = <0x100a03c0 0x20>; ++ clocks = <&clock HI3531D_ETH_CLK>; ++ resets = <&clock 0x14c 5>; ++ reset-names = "phy_reset"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ higmac: ethernet@100a0000 { ++ compatible = "hisilicon,higmac"; ++ reg = <0x100a0000 0x1000>,<0x1204015c 0x4>; ++ interrupts = <0 16 4>; ++ ++ clocks = <&clock HI3531D_ETH_CLK>, ++ <&clock HI3531D_ETH_MACIF_CLK>; ++ clock-names = "higmac_clk", ++ "macif_clk"; ++ ++ resets = <&clock 0x14c 0>, ++ <&clock 0x14c 2>, ++ <&clock 0x14c 5>; ++ reset-names = "port_reset", ++ "macif_reset", ++ "phy_reset"; ++ ++ mac-address = [00 00 00 00 00 00]; ++ }; ++ ++ fmc: flash-memory-controller@10000000 { ++ compatible = "hisilicon,hisi-fmc"; ++ reg = <0x10000000 0x1000>, <0x14000000 0x10000>; ++ reg-names = "control", "memory"; ++ clocks = <&clock HI3531D_FMC_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hisfc:spi_nor_controller { ++ compatible = "hisilicon,hisi-sfc"; ++ assigned-clocks = <&clock HI3531D_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hisnfc:spi_nand_controller { ++ compatible = "hisilicon,hisi-spi-nand"; ++ assigned-clocks = <&clock HI3531D_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ ++ hidmac: hidma-controller@10060000 { ++ compatible = "hisilicon,hisi-dmac"; ++ reg = <0x10060000 0x1000>; ++ interrupts = <0 10 4>; ++ clocks = <&clock HI3531D_DMAC_CLK>; ++ clock-names = "dmac_clk"; ++ resets = <&clock 0x144 0>; ++ reset-names = "dma-reset"; ++ #dma-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ pmux: pinmux@120F0000 { ++ compatible = "pinctrl-single"; ++ reg = <0x120F0000 0x3A8>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ #gpio-range-cells = <3>; ++ ranges; ++ ++ pinctrl-single,register-width = <32>; ++ pinctrl-single,function-mask = <7>; ++ /* pin base, nr pins & gpio function */ ++ pinctrl-single,gpio-range = <&range 0 54 0 ++ &range 55 6 1 &range 61 5 0>; ++ ++ range: gpio-range { ++ #pinctrl-single,gpio-range-cells = <3>; ++ }; ++ }; ++ ++ hinfc610: parallel-nand-controller@10010000 { ++ compatible = "hisilicon,hinfc610-nand"; ++ reg = <0x10010000 0x1000>, <0x15000000 0x10000>; ++ reg-names = "control", "memory"; ++ clocks = <&clock HI3531D_NFC_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ ++ media { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&gic>; ++ ranges; ++ ++ osal: osal { ++ compatible = "hisilicon,osal"; ++ }; ++ ++ sys: sys@12040000 { ++ compatible = "hisilicon,hi35xx_sys"; ++ reg = <0x12040000 0x10000>, <0x12050000 0x10000>, ++ <0x12110000 0x10000>, <0x12120000 0x10000>; ++ reg-names = "crg", "sys", "ddr", "misc"; ++ }; ++ ++ viu: viu@130C0000 { ++ compatible = "hisilicon,hi35xx_viu"; ++ interrupts = <0 28 4>; ++ reg = <0x130C0000 0x40000>; ++ reg-names = "viu"; ++ clocks = <&clock HI3531D_VIU_CLK>; ++ clock-names = "viu"; ++ }; ++ ++ vou: vou@13020000 { ++ compatible = "hisilicon,hi35xx_vou"; ++ interrupts = <0 30 4>; ++ reg = <0x13020000 0x10000>; ++ }; ++ ++ vda: vda@13090000 { ++ compatible = "hisilicon,hi35xx_vda"; ++ interrupts = <0 43 4>; ++ reg = <0x13090000 0x10000>; ++ }; ++ ++ ive: ive@13060000 { ++ compatible = "hisilicon,hi35xx_ive"; ++ interrupts = <0 33 4>; ++ reg = <0x13060000 0x10000>; ++ }; ++ ++ vgs: vgs@13150000 { ++ compatible = "hisilicon,hi35xx_vgs"; ++ interrupts = <0 35 4>; ++ reg = <0x13150000 0x10000>; ++ }; ++ ++ vpss: vpss@13080000 { ++ compatible = "hisilicon,hi35xx_vpss"; ++ interrupts = <0 37 4>, <0 46 4>, <0 47 4>; ++ interrupt-names = "vpss0", "vpss1", "vpss2"; ++ reg = <0x13080000 0x10000>, <0x13110000 0x10000>, <0x13180000 0x10000>; ++ reg-names = "vpss0", "vpss1", "vpss2"; ++ }; ++ ++ audio: audio@13140000 { ++ compatible = "hisilicon,hi35xx_aiao"; ++ interrupts = <0 40 4>; ++ reg = <0x13140000 0x10000>, <0x13120000 0x10000>; ++ reg-names = "aiao", "voie"; ++ }; ++ ++ vdec: vdec@13170000 { ++ compatible = "hisilicon,hi35xx_vdec"; ++ interrupts = <0 81 4>, <0 83 4>; ++ interrupt-names = "vdm", "scd"; ++ reg = <0x13170000 0xc000>, <0x1317c000 0x4000>; ++ reg-names = "vdm", "scd"; ++ }; ++ ++ tde: tde@13050000 { ++ compatible = "hisilicon,hi35xx_tde"; ++ interrupts = <0 32 4>; ++ reg = <0x13050000 0x1000>; ++ }; ++ ++ jpgd: jpgd@13070000 { ++ compatible = "hisilicon,hi35xx_jpgd"; ++ interrupts = <0 34 4>; ++ interrupt-names = "jpgd"; ++ reg = <0x13070000 0x10000>; ++ reg-names = "jpgd"; ++ }; ++ ++ vedu: vedu@13040000 { ++ compatible = "hisilicon,hi35xx_vedu"; ++ interrupts = <0 31 4>, <0 36 4>; ++ interrupt-names = "vedu0", "vedu1"; ++ reg = <0x13040000 0x10000>, <0x13100000 0x10000>; ++ reg-names = "vedu0", "vedu1"; ++ }; ++ ++ jpege: jpege@13130000 { ++ compatible = "hisilicon,hi35xx_jpege"; ++ interrupts = <0 39 4>; ++ reg = <0x13130000 0x10000>; ++ }; ++ ++ sys_config_ctrl: sys_config_ctrl { ++ compatible = "hisilicon,sys_config_ctrl"; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/hi3536c-demb.dts b/arch/arm/boot/dts/hi3536c-demb.dts +new file mode 100644 +index 0000000..afdbced +--- /dev/null ++++ b/arch/arm/boot/dts/hi3536c-demb.dts +@@ -0,0 +1,199 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++/dts-v1/; ++#include "hi3536c.dtsi" ++ ++/ { ++ model = "Hisilicon HI3536C DEMO Board"; ++ compatible = "hisilicon,hi3536c"; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ enable-method = "hisilicon,hi3536c-smp"; ++ ++ cpu@0 { ++ compatible = "arm,cortex-a7"; ++ device_type = "cpu"; ++ clock-frequency = <HI3536C_FIXED_1500M>; ++ reg = <0>; ++ }; ++ ++ cpu@1 { ++ compatible = "arm,cortex-a7"; ++ device_type = "cpu"; ++ clock-frequency = <HI3536C_FIXED_1500M>; ++ reg = <1>; ++ }; ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x80000000 0x20000000>; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&rtc { ++ status = "okay"; ++}; ++ ++&spi_bus0{ ++ status = "okay"; ++ num-cs = <2>; ++ ++ spidev@0 { ++ compatible = "spidev"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <37500000>; ++ }; ++ ++ spidev@1 { ++ compatible = "spidev"; ++ reg = <1>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <37500000>; ++ }; ++}; ++ ++&hisfc { ++ hi_sfc { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ m25p,fast-read; ++ }; ++}; ++ ++&hisnfc { ++ hinand { ++ compatible = "jedec,spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ }; ++}; ++ ++&hidmac { ++ status = "okay"; ++}; ++ ++&mdio { ++ ethphy: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++&mdio1 { ++ ethphy1: ethernet-phy@2 { ++ reg = <2>; ++ }; ++}; ++ ++&higmac { ++ phy-handle = <ðphy>; ++ phy-mode = "rgmii"; ++}; ++&higmac1 { ++ phy-handle = <ðphy1>; ++ phy-mode = "rgmii"; ++}; ++ ++ ++&pmux { ++ ++ vo_vga_pmux: vo_vga_pmux{ ++ pinctrl-single,pins = < ++ 0x0174 0x1 ++ 0x017c 0x1 ++ 0x0180 0x1 ++ >; ++ }; ++ ++ vo_hdmi_pmux: vo_hdmi_pmux{ ++ pinctrl-single,pins = < ++ 0x0098 0x1 ++ 0x009c 0X1 ++ >; ++ }; ++ ++ i2c0_pmux: i2c0_pmux { ++ pinctrl-single,pins = < ++ 0x00e0 0x1 ++ 0x00e4 0x1 ++ >; ++ }; ++ ++ i2s0_pmux: i2s0_pmux { ++ pinctrl-single,pins = < ++ 0x00a0 0x1 ++ 0x00a4 0x1 ++ 0x00a8 0x1 ++ >; ++ }; ++ ++ i2s1_pmux: i2s1_pmux { ++ pinctrl-single,pins = < ++ 0x00b0 0x1 ++ 0x00b4 0x1 ++ >; ++ }; ++ ++ i2s2_sck_pmux: i2s2_sck_pmux { ++ pinctrl-single,pins = < ++ 0x00b8 0x1 ++ 0x00bc 0x1 ++ 0x00c0 0x1 ++ 0x00ac 0x2 ++ >; ++ }; ++}; ++ ++&sys_config_ctrl { ++ padctrl-ability,demo = < ++ 0x120f08a0 0x120 ++ 0x120f08a4 0x130 ++ 0x120f08a8 0x130 ++ 0x120f08ac 0x120 ++ 0x120f08b0 0x130 ++ 0x120f08b4 0x130 ++ 0x120f08b8 0x120 ++ 0x120f08bc 0x130 ++ 0x120f08c0 0x130 ++ 0x120f0898 0x40 ++ 0x120f089c 0x40 ++ >; ++ ++ sysctrl-ddr,pins = < ++ 0x1212007c 0x35533201 ++ 0x12120080 0x25335526 ++ 0x12120084 0x66666666 ++ 0x12120094 0x65 ++ >; ++ ++ pinctrl-names = "demo", "sck", "default"; ++ pinctrl-0 = <&i2c0_pmux &i2s1_pmux &i2s2_sck_pmux &vo_hdmi_pmux &vo_vga_pmux>; ++ pinctrl-1 = <&i2s0_pmux &i2s1_pmux &i2s2_sck_pmux &vo_hdmi_pmux &vo_vga_pmux>; ++ pinctrl-2 = <>; ++}; +diff --git a/arch/arm/boot/dts/hi3536c.dtsi b/arch/arm/boot/dts/hi3536c.dtsi +new file mode 100644 +index 0000000..8605c9d +--- /dev/null ++++ b/arch/arm/boot/dts/hi3536c.dtsi +@@ -0,0 +1,408 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/hi3536c-clock.h> ++/ { ++ aliases { ++ fmc = &fmc; ++ serial0 = &uart0; ++ spi0 = &spi_bus0; ++ }; ++ ++ clock: clock@12040000 { ++ compatible = "hisilicon,hi3536c-clock"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ #clock-cells = <1>; ++ #reset-cells = <2>; ++ reg = <0x12040000 0x1000>; ++ }; ++ ++ gic: interrupt-controller@10300000 { ++ compatible = "arm,cortex-a7-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ /* gic dist base, gic cpu base , no virtual support */ ++ reg = <0x10301000 0x1000>, <0x10302000 0x100>; ++ }; ++ ++ soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&gic>; ++ ranges; ++ ++ rtc: rtc@120b0000 { ++ compatible = "hisilicon,hi35xx-rtc"; ++ interrupts = <0 5 4>; ++ reg = <0x120b0000 0x10000>; ++ status = "disabled"; ++ }; ++ sysctrl: system-controller@12050000 { ++ compatible = "hisilicon,sysctrl"; ++ reg = <0x12050000 0x1000>; ++ reboot-offset = <0x4>; ++ #clock-cells = <1>; ++ }; ++ ++ pmu { ++ compatible = "arm,cortex-a7-pmu"; ++ interrupts = <0 54 4>, ++ <0 48 4>; ++ }; ++ ++ amba { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "arm,amba-bus"; ++ ranges; ++ ++ uart0: uart@12080000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12080000 0x1000>; ++ interrupts = <0 6 4>; ++ clocks = <&clock HI3536C_UART0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart1: uart@121090000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12090000 0x1000>; ++ interrupts = <0 7 4>; ++ clocks = <&clock HI3536C_UART1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart2: uart@120a0000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x120a0000 0x1000>; ++ interrupts = <0 8 4>; ++ clocks = <&clock HI3536C_UART2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ timer@0x12000000 { ++ compatible = "hisilicon,timer"; ++ /* timer0 & timer1 & timer2*/ ++ reg = <0x12000000 0x20>, /* clocksource */ ++ <0x12000020 0x20>, /* local timer for each cpu */ ++ <0x12010000 0x20>; ++ interrupts = <0 1 4>, <0 2 4>; /* irq of local timer */ ++ clocks = <&sysctrl HI3536C_TIME0_0_CLK>; ++ clock-names = "timer00"; ++ }; ++ }; ++ ++ /* dual timer 0 and 1 have used for clocksource and local timer */ ++ dual_timer2: dual_timer@12020000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ /* timer4 & timer5 */ ++ interrupts = <0 3 4>; ++ reg = <0x12020000 0x1000>; ++ clocks = <&sysctrl HI3536C_TIME2_4_CLK>, ++ <&sysctrl HI3536C_TIME2_5_CLK>, ++ <&clock HI3536C_SYSAXI_CLK>; ++ clock-names = "timer4", "timer5", "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer3: dual_timer@12030000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ /* timer6 & timer7 */ ++ interrupts = <0 4 4>; ++ reg = <0x12030000 0x1000>; ++ clocks = <&sysctrl HI3536C_TIME3_6_CLK>, ++ <&sysctrl HI3536C_TIME3_7_CLK>, ++ <&clock HI3536C_SYSAXI_CLK>; ++ clock-names = "timer6", "timer7", "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ spi_bus0: spi@120d0000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x120d0000 0x1000>, <0x12120014 0x4>; ++ interrupts = <0 14 4>; ++ clocks = <&clock HI3536C_SPI0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ hisi,spi_cs_sb = <0>; ++ hisi,spi_cs_mask_bit = <0x00000003>; ++ }; ++ ++ sata_phy: phy@11010000 { ++ compatible = "hisilicon,hisi-sata-nano-phy"; ++ reg = <0x11010000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ++ ahci: sata@11010000 { ++ compatible = "hisilicon,hisi-ahci"; ++ reg = <0x11010000 0x1000>; ++ interrupts = <0 17 4>; ++ phys = <&sata_phy>; ++ phy-names = "sata-phy"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ usb_phy: usbphy { ++ compatible = "hisilicon,hisi-usb-phy"; ++ reg = <0x12120000 0x1000>, <0x12040000 0x1000>; ++ }; ++ ++ ehci@0x10120000 { ++ compatible = "generic-ehci"; ++ reg = <0x10040000 0x10000>; ++ interrupts = <0 19 4>; ++ }; ++ ++ ohci@0x10110000 { ++ compatible = "generic-ohci"; ++ reg = <0x10030000 0x10000>; ++ interrupts = <0 18 4>; ++ }; ++ ++ mdio: mdio@100a03c0 { ++ compatible = "hisilicon,hisi-gemac-mdio"; ++ reg = <0x100a03c0 0x20>; ++ clocks = <&clock HI3536C_ETH_CLK>; ++ resets = <&clock 0x14c 5>; ++ reset-names = "phy_reset"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ mdio1: mdio@100a13c0 { ++ compatible = "hisilicon,hisi-gemac-mdio"; ++ reg = <0x100a13c0 0x20>; ++ clocks = <&clock HI3536C_ETH1_CLK>; ++ resets = <&clock 0x14c 13>; ++ reset-names = "phy_reset"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ higmac: ethernet@100a0000 { ++ compatible = "hisilicon,higmac-v5"; ++ reg = <0x100a0000 0x1000>,<0x100a300c 0x4>; ++ interrupts = <0 16 4>, <0 61 4>, <0 62 4>, <0 63 4>; ++ ++ clocks = <&clock HI3536C_ETH_CLK>, ++ <&clock HI3536C_ETH_MACIF_CLK>; ++ clock-names = "higmac_clk", ++ "macif_clk"; ++ ++ resets = <&clock 0x14c 0>, ++ <&clock 0x14c 2>, ++ <&clock 0x14c 5>; ++ reset-names = "port_reset", ++ "macif_reset", ++ "phy_reset"; ++ ++ mac-address = [00 00 00 00 00 00]; ++ }; ++ higmac1: ethernet@100a1000 { ++ compatible = "hisilicon,higmac-v5"; ++ reg = <0x100a1000 0x1000>,<0x100a3010 0x4>; ++ interrupts = <0 16 4>, <0 61 4>, <0 62 4>, <0 63 4>; ++ ++ clocks = <&clock HI3536C_ETH1_CLK>, ++ <&clock HI3536C_ETH_MACIF1_CLK>; ++ clock-names = "higmac_clk", ++ "macif_clk"; ++ ++ resets = <&clock 0x14c 8>, ++ <&clock 0x14c 10>, ++ <&clock 0x14c 13>; ++ reset-names = "port_reset", ++ "macif_reset", ++ "phy_reset"; ++ ++ mac-address = [00 00 00 00 00 00]; ++ }; ++ ++ ++ fmc: flash-memory-controller@10000000 { ++ compatible = "hisilicon,hisi-fmc"; ++ reg = <0x10000000 0x1000>, <0x14000000 0x10000>; ++ reg-names = "control", "memory"; ++ clocks = <&clock HI3536C_FMC_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hisfc:spi_nor_controller { ++ compatible = "hisilicon,hisi-sfc"; ++ assigned-clocks = <&clock HI3536C_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hisnfc:spi_nand_controller { ++ compatible = "hisilicon,hisi-spi-nand"; ++ assigned-clocks = <&clock HI3536C_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ ++ hidmac: hidma-controller@10060000 { ++ compatible = "hisilicon,hisi-dmac"; ++ reg = <0x10060000 0x1000>; ++ interrupts = <0 10 4>; ++ clocks = <&clock HI3536C_DMAC_CLK>; ++ clock-names = "dmac_clk"; ++ resets = <&clock 0x144 0>; ++ reset-names = "dma-reset"; ++ #dma-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ pmux: pinmux@120F0000 { ++ compatible = "pinctrl-single"; ++ reg = <0x120F0000 0x3A8>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ #gpio-range-cells = <3>; ++ ranges; ++ ++ pinctrl-single,register-width = <32>; ++ pinctrl-single,function-mask = <7>; ++ /* pin base, nr pins & gpio function */ ++ pinctrl-single,gpio-range = <&range 0 54 0 ++ &range 55 6 1 &range 61 5 0>; ++ ++ range: gpio-range { ++ #pinctrl-single,gpio-range-cells = <3>; ++ }; ++ }; ++ }; ++ ++ ++ media { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&gic>; ++ ranges; ++ ++ osal: osal { ++ compatible = "hisilicon,osal"; ++ }; ++ ++ sys: sys@12040000 { ++ compatible = "hisilicon,hi35xx_sys"; ++ reg = <0x12040000 0x10000>, <0x12050000 0x10000>, ++ <0x12110000 0x10000>, <0x12120000 0x10000>; ++ reg-names = "crg", "sys", "ddr", "misc"; ++ }; ++ ++ vou: vou@13020000 { ++ compatible = "hisilicon,hi35xx_vou"; ++ interrupts = <0 30 4>; ++ reg = <0x13020000 0x10000>; ++ }; ++ ++ vda: vda@13150000 { ++ compatible = "hisilicon,hi35xx_vda"; ++ interrupts = <0 43 4>; ++ reg = <0x13150000 0x10000>; ++ }; ++ ++ ive: ive@13060000 { ++ compatible = "hisilicon,hi35xx_ive"; ++ interrupts = <0 33 4>; ++ reg = <0x13060000 0x10000>; ++ }; ++ ++ vgs: vgs@13080000 { ++ compatible = "hisilicon,hi35xx_vgs"; ++ interrupts = <0 35 4>; ++ reg = <0x13080000 0x10000>; ++ }; ++ ++ vpss: vpss@13110000 { ++ compatible = "hisilicon,hi35xx_vpss"; ++ interrupts = <0 37 4>; ++ interrupt-names = "vpss0"; ++ reg = <0x13110000 0x10000>; ++ reg-names = "vpss0"; ++ }; ++ ++ audio: audio@13140000 { ++ compatible = "hisilicon,hi35xx_aiao"; ++ interrupts = <0 40 4>; ++ reg = <0x13140000 0x10000>, <0x13120000 0x10000>; ++ reg-names = "aiao", "voie"; ++ }; ++ ++ vdec: vdec@13100000 { ++ compatible = "hisilicon,hi35xx_vdec"; ++ interrupts =<0 21 4> , <0 23 4>; ++ interrupt-names ="vdm", "scd"; ++ reg = <0x13100000 0xc000>, <0x1310c000 0x4000>; ++ reg-names = "vdm", "scd"; ++ }; ++ ++ tde: tde@13050000 { ++ compatible = "hisilicon,hi35xx_tde"; ++ interrupts = <0 32 4>; ++ reg = <0x13050000 0x1000>; ++ }; ++ ++ jpgd: jpgd@13070000 { ++ compatible = "hisilicon,hi35xx_jpgd"; ++ interrupts = <0 34 4>; ++ interrupt-names = "jpgd"; ++ reg = <0x13070000 0x10000>; ++ reg-names = "jpgd"; ++ }; ++ ++ vedu: vedu@13040000 { ++ compatible = "hisilicon,hi35xx_vedu"; ++ interrupts = <0 31 4>; ++ interrupt-names = "vedu0"; ++ reg = <0x13040000 0x10000>; ++ reg-names = "vedu0"; ++ }; ++ ++ jpege: jpege@13130000 { ++ compatible = "hisilicon,hi35xx_jpege"; ++ interrupts = <0 39 4>; ++ reg = <0x13130000 0x10000>; ++ }; ++ ++ sys_config_ctrl: sys_config_ctrl { ++ compatible = "hisilicon,sys_config_ctrl"; ++ }; ++ ++ pin_ctrl_ddr: pin_ctrl_ddr { ++ compatible = "hisilicon,pinctrl-ddrpinctrl-ddr"; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/hip04.dtsi b/arch/arm/boot/dts/hip04.dtsi +index 93b6c90..44044f2 100644 +--- a/arch/arm/boot/dts/hip04.dtsi ++++ b/arch/arm/boot/dts/hip04.dtsi +@@ -190,6 +190,12 @@ + clock-frequency = <168000000>; + }; + ++ clk_375m: clk_375m { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <375000000>; ++ }; ++ + soc { + /* It's a 32-bit SoC. */ + #address-cells = <1>; +@@ -264,4 +270,714 @@ + }; + + }; ++ ++ etb@0,e3c42000 { ++ compatible = "arm,coresight-etb10", "arm,primecell"; ++ reg = <0 0xe3c42000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ port { ++ etb0_in_port: endpoint@0 { ++ slave-mode; ++ remote-endpoint = <&replicator0_out_port0>; ++ }; ++ }; ++ }; ++ ++ etb@0,e3c82000 { ++ compatible = "arm,coresight-etb10", "arm,primecell"; ++ reg = <0 0xe3c82000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ port { ++ etb1_in_port: endpoint@0 { ++ slave-mode; ++ remote-endpoint = <&replicator1_out_port0>; ++ }; ++ }; ++ }; ++ ++ etb@0,e3cc2000 { ++ compatible = "arm,coresight-etb10", "arm,primecell"; ++ reg = <0 0xe3cc2000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ port { ++ etb2_in_port: endpoint@0 { ++ slave-mode; ++ remote-endpoint = <&replicator2_out_port0>; ++ }; ++ }; ++ }; ++ ++ etb@0,e3d02000 { ++ compatible = "arm,coresight-etb10", "arm,primecell"; ++ reg = <0 0xe3d02000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ port { ++ etb3_in_port: endpoint@0 { ++ slave-mode; ++ remote-endpoint = <&replicator3_out_port0>; ++ }; ++ }; ++ }; ++ ++ tpiu@0,e3c05000 { ++ compatible = "arm,coresight-tpiu", "arm,primecell"; ++ reg = <0 0xe3c05000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ port { ++ tpiu_in_port: endpoint@0 { ++ slave-mode; ++ remote-endpoint = <&funnel4_out_port0>; ++ }; ++ }; ++ }; ++ ++ replicator0 { ++ /* non-configurable replicators don't show up on the ++ * AMBA bus. As such no need to add "arm,primecell". ++ */ ++ compatible = "arm,coresight-replicator"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* replicator output ports */ ++ port@0 { ++ reg = <0>; ++ replicator0_out_port0: endpoint { ++ remote-endpoint = <&etb0_in_port>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ replicator0_out_port1: endpoint { ++ remote-endpoint = <&funnel4_in_port0>; ++ }; ++ }; ++ ++ /* replicator input port */ ++ port@2 { ++ reg = <0>; ++ replicator0_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&funnel0_out_port0>; ++ }; ++ }; ++ }; ++ }; ++ ++ replicator1 { ++ /* non-configurable replicators don't show up on the ++ * AMBA bus. As such no need to add "arm,primecell". ++ */ ++ compatible = "arm,coresight-replicator"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* replicator output ports */ ++ port@0 { ++ reg = <0>; ++ replicator1_out_port0: endpoint { ++ remote-endpoint = <&etb1_in_port>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ replicator1_out_port1: endpoint { ++ remote-endpoint = <&funnel4_in_port1>; ++ }; ++ }; ++ ++ /* replicator input port */ ++ port@2 { ++ reg = <0>; ++ replicator1_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&funnel1_out_port0>; ++ }; ++ }; ++ }; ++ }; ++ ++ replicator2 { ++ /* non-configurable replicators don't show up on the ++ * AMBA bus. As such no need to add "arm,primecell". ++ */ ++ compatible = "arm,coresight-replicator"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* replicator output ports */ ++ port@0 { ++ reg = <0>; ++ replicator2_out_port0: endpoint { ++ remote-endpoint = <&etb2_in_port>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ replicator2_out_port1: endpoint { ++ remote-endpoint = <&funnel4_in_port2>; ++ }; ++ }; ++ ++ /* replicator input port */ ++ port@2 { ++ reg = <0>; ++ replicator2_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&funnel2_out_port0>; ++ }; ++ }; ++ }; ++ }; ++ ++ replicator3 { ++ /* non-configurable replicators don't show up on the ++ * AMBA bus. As such no need to add "arm,primecell". ++ */ ++ compatible = "arm,coresight-replicator"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* replicator output ports */ ++ port@0 { ++ reg = <0>; ++ replicator3_out_port0: endpoint { ++ remote-endpoint = <&etb3_in_port>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ replicator3_out_port1: endpoint { ++ remote-endpoint = <&funnel4_in_port3>; ++ }; ++ }; ++ ++ /* replicator input port */ ++ port@2 { ++ reg = <0>; ++ replicator3_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&funnel3_out_port0>; ++ }; ++ }; ++ }; ++ }; ++ ++ funnel@0,e3c41000 { ++ compatible = "arm,coresight-funnel", "arm,primecell"; ++ reg = <0 0xe3c41000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* funnel output port */ ++ port@0 { ++ reg = <0>; ++ funnel0_out_port0: endpoint { ++ remote-endpoint = ++ <&replicator0_in_port0>; ++ }; ++ }; ++ ++ /* funnel input ports */ ++ port@1 { ++ reg = <0>; ++ funnel0_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm0_out_port>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <1>; ++ funnel0_in_port1: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm1_out_port>; ++ }; ++ }; ++ ++ port@3 { ++ reg = <2>; ++ funnel0_in_port2: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm2_out_port>; ++ }; ++ }; ++ ++ port@4 { ++ reg = <3>; ++ funnel0_in_port3: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm3_out_port>; ++ }; ++ }; ++ }; ++ }; ++ ++ funnel@0,e3c81000 { ++ compatible = "arm,coresight-funnel", "arm,primecell"; ++ reg = <0 0xe3c81000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* funnel output port */ ++ port@0 { ++ reg = <0>; ++ funnel1_out_port0: endpoint { ++ remote-endpoint = ++ <&replicator1_in_port0>; ++ }; ++ }; ++ ++ /* funnel input ports */ ++ port@1 { ++ reg = <0>; ++ funnel1_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm4_out_port>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <1>; ++ funnel1_in_port1: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm5_out_port>; ++ }; ++ }; ++ ++ port@3 { ++ reg = <2>; ++ funnel1_in_port2: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm6_out_port>; ++ }; ++ }; ++ ++ port@4 { ++ reg = <3>; ++ funnel1_in_port3: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm7_out_port>; ++ }; ++ }; ++ }; ++ }; ++ ++ funnel@0,e3cc1000 { ++ compatible = "arm,coresight-funnel", "arm,primecell"; ++ reg = <0 0xe3cc1000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* funnel output port */ ++ port@0 { ++ reg = <0>; ++ funnel2_out_port0: endpoint { ++ remote-endpoint = ++ <&replicator2_in_port0>; ++ }; ++ }; ++ ++ /* funnel input ports */ ++ port@1 { ++ reg = <0>; ++ funnel2_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm8_out_port>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <1>; ++ funnel2_in_port1: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm9_out_port>; ++ }; ++ }; ++ ++ port@3 { ++ reg = <2>; ++ funnel2_in_port2: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm10_out_port>; ++ }; ++ }; ++ ++ port@4 { ++ reg = <3>; ++ funnel2_in_port3: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm11_out_port>; ++ }; ++ }; ++ }; ++ }; ++ ++ funnel@0,e3d01000 { ++ compatible = "arm,coresight-funnel", "arm,primecell"; ++ reg = <0 0xe3d01000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* funnel output port */ ++ port@0 { ++ reg = <0>; ++ funnel3_out_port0: endpoint { ++ remote-endpoint = ++ <&replicator3_in_port0>; ++ }; ++ }; ++ ++ /* funnel input ports */ ++ port@1 { ++ reg = <0>; ++ funnel3_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm12_out_port>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <1>; ++ funnel3_in_port1: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm13_out_port>; ++ }; ++ }; ++ ++ port@3 { ++ reg = <2>; ++ funnel3_in_port2: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm14_out_port>; ++ }; ++ }; ++ ++ port@4 { ++ reg = <3>; ++ funnel3_in_port3: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm15_out_port>; ++ }; ++ }; ++ }; ++ }; ++ ++ funnel@0,e3c04000 { ++ compatible = "arm,coresight-funnel", "arm,primecell"; ++ reg = <0 0xe3c04000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* funnel output port */ ++ port@0 { ++ reg = <0>; ++ funnel4_out_port0: endpoint { ++ remote-endpoint = <&tpiu_in_port>; ++ }; ++ }; ++ ++ /* funnel input ports */ ++ port@1 { ++ reg = <0>; ++ funnel4_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = ++ <&replicator0_out_port1>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <1>; ++ funnel4_in_port1: endpoint { ++ slave-mode; ++ remote-endpoint = ++ <&replicator1_out_port1>; ++ }; ++ }; ++ ++ port@3 { ++ reg = <2>; ++ funnel4_in_port2: endpoint { ++ slave-mode; ++ remote-endpoint = ++ <&replicator2_out_port1>; ++ }; ++ }; ++ ++ port@4 { ++ reg = <3>; ++ funnel4_in_port3: endpoint { ++ slave-mode; ++ remote-endpoint = ++ <&replicator3_out_port1>; ++ }; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3c7c000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3c7c000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU0>; ++ port { ++ ptm0_out_port: endpoint { ++ remote-endpoint = <&funnel0_in_port0>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3c7d000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3c7d000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU1>; ++ port { ++ ptm1_out_port: endpoint { ++ remote-endpoint = <&funnel0_in_port1>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3c7e000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3c7e000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU2>; ++ port { ++ ptm2_out_port: endpoint { ++ remote-endpoint = <&funnel0_in_port2>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3c7f000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3c7f000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU3>; ++ port { ++ ptm3_out_port: endpoint { ++ remote-endpoint = <&funnel0_in_port3>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3cbc000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3cbc000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU4>; ++ port { ++ ptm4_out_port: endpoint { ++ remote-endpoint = <&funnel1_in_port0>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3cbd000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3cbd000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU5>; ++ port { ++ ptm5_out_port: endpoint { ++ remote-endpoint = <&funnel1_in_port1>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3cbe000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3cbe000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU6>; ++ port { ++ ptm6_out_port: endpoint { ++ remote-endpoint = <&funnel1_in_port2>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3cbf000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3cbf000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU7>; ++ port { ++ ptm7_out_port: endpoint { ++ remote-endpoint = <&funnel1_in_port3>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3cfc000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3cfc000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU8>; ++ port { ++ ptm8_out_port: endpoint { ++ remote-endpoint = <&funnel2_in_port0>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3cfd000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3cfd000 0 0x1000>; ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU9>; ++ port { ++ ptm9_out_port: endpoint { ++ remote-endpoint = <&funnel2_in_port1>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3cfe000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3cfe000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU10>; ++ port { ++ ptm10_out_port: endpoint { ++ remote-endpoint = <&funnel2_in_port2>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3cff000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3cff000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU11>; ++ port { ++ ptm11_out_port: endpoint { ++ remote-endpoint = <&funnel2_in_port3>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3d3c000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3d3c000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU12>; ++ port { ++ ptm12_out_port: endpoint { ++ remote-endpoint = <&funnel3_in_port0>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3d3d000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3d3d000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU13>; ++ port { ++ ptm13_out_port: endpoint { ++ remote-endpoint = <&funnel3_in_port1>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3d3e000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3d3e000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU14>; ++ port { ++ ptm14_out_port: endpoint { ++ remote-endpoint = <&funnel3_in_port2>; ++ }; ++ }; ++ }; ++ ++ ptm@0,e3d3f000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0xe3d3f000 0 0x1000>; ++ ++ clocks = <&clk_375m>; ++ clock-names = "apb_pclk"; ++ cpu = <&CPU15>; ++ port { ++ ptm15_out_port: endpoint { ++ remote-endpoint = <&funnel3_in_port3>; ++ }; ++ }; ++ }; + }; +diff --git a/arch/arm/boot/dts/hisi-hi3516av200-demb.dts b/arch/arm/boot/dts/hisi-hi3516av200-demb.dts +new file mode 100644 +index 0000000..6cf8ba4 +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3516av200-demb.dts +@@ -0,0 +1,247 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++/dts-v1/; ++#include "hisi-hi3516av200.dtsi" ++ ++/ { ++ model = "Hisilicon HI3516AV200 DEMO Board"; ++ compatible = "hisilicon,hi3516av200"; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ enable-method = "hisilicon,hi3516av200-smp"; ++ ++ cpu@0 { ++ compatible = "arm,cortex-a7"; ++ device_type = "cpu"; ++ clock-frequency = <HI3516AV200_FIXED_792M>; ++ reg = <0>; ++ cci-control-port = <&cci_control0>; ++ }; ++ ++ /*cpu@100 { ++ compatible = "arm,cortex-a17"; ++ device_type = "cpu"; ++ clock-frequency = <HI3516AV200_FIXED_1000M>; ++ reg = <0x100>; ++ cci-control-port = <&cci_control1>; ++ };*/ ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x80000000 0x40000000>; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&dual_timer0 { ++ status = "okay"; ++}; ++ ++&i2c_bus0 { ++ status = "okay"; ++}; ++ ++&i2c_bus1 { ++ status = "okay"; ++}; ++ ++&i2c_bus2 { ++ status = "okay"; ++}; ++ ++&i2c_bus3 { ++ status = "okay"; ++}; ++ ++&spi_bus0 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++ ++}; ++ ++&spi_bus1 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++}; ++ ++&spi_bus2 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++ ++ spidev@1 { ++ compatible = "rohm,dh2228fv"; ++ reg = <1>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++}; ++ ++&spi_bus3 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++}; ++ ++&hisfc { ++ hi_sfc { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ m25p,fast-read; ++ }; ++ ++}; ++ ++&hisnfc { ++ hinand { ++ compatible = "jedec,spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ }; ++}; ++ ++&hinfc { ++ hinand { ++ compatible = "jedec,nand"; ++ reg = <0>; ++ nand-max-frequency = <200000000>; ++ }; ++}; ++ ++&mmc0 { ++ status = "okay"; ++}; ++ ++&mmc1 { ++ status = "okay"; ++}; ++ ++&mmc2 { ++ status = "okay"; ++}; ++ ++&mdio { ++ ethphy: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&higmac { ++ phy-handle = <ðphy>; ++ phy-mode = "rgmii"; ++}; ++ ++&gpio_chip0 { ++ status = "okay"; ++}; ++ ++&gpio_chip1 { ++ status = "okay"; ++}; ++ ++&gpio_chip2 { ++ status = "okay"; ++}; ++ ++&gpio_chip3 { ++ status = "okay"; ++}; ++ ++&gpio_chip4 { ++ status = "okay"; ++}; ++ ++&gpio_chip5 { ++ status = "okay"; ++}; ++ ++&gpio_chip6 { ++ status = "okay"; ++}; ++ ++&gpio_chip7 { ++ status = "okay"; ++}; ++ ++&gpio_chip8 { ++ status = "okay"; ++}; ++ ++&gpio_chip9 { ++ status = "okay"; ++}; ++ ++&gpio_chip10 { ++ status = "okay"; ++}; ++ ++&gpio_chip11 { ++ status = "okay"; ++}; ++ ++&gpio_chip12 { ++ status = "okay"; ++}; ++ ++&gpio_chip13 { ++ status = "okay"; ++}; ++ ++&gpio_chip14 { ++ status = "okay"; ++}; ++ ++&gpio_chip16 { ++ status = "okay"; ++}; +diff --git a/arch/arm/boot/dts/hisi-hi3516av200-hmp-demb.dts b/arch/arm/boot/dts/hisi-hi3516av200-hmp-demb.dts +new file mode 100644 +index 0000000..3c6f10c +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3516av200-hmp-demb.dts +@@ -0,0 +1,310 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++/dts-v1/; ++#include "hisi-hi3516av200.dtsi" ++ ++/ { ++ model = "Hisilicon HI3516AV200 DEMO Board"; ++ compatible = "hisilicon,hi3516av200"; ++ ++ chosen { ++ bootargs = "console=ttyAMA0,115200 early_printk ++root=/dev/mtdblock2 rootfstype=jffs2 mtdparts=hi_sfc:1M(boot), ++4M(kernel),11M(rootfs)"; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ enable-method = "hisilicon,hi3516av200-smp"; ++ ++ cpu@0 { ++ compatible = "arm,cortex-a7"; ++ device_type = "cpu"; ++ clock-frequency = <HI3516AV200_FIXED_792M>; ++ reg = <0>; ++ cci-control-port = <&cci_control0>; ++ }; ++ ++ cpu@100 { ++ compatible = "arm,cortex-a17"; ++ device_type = "cpu"; ++ reg = <0x100>; ++ cci-control-port = <&cci_control1>; ++ operating-points = < ++ /* KHz uV */ ++ 1250000 1060000 ++ 1150000 1060000 ++ 1000000 1000000 ++ 930000 1000000 ++ 792000 940000 ++ 594000 940000 ++ >; ++ clocks = <&clock HI3516AV200_A17_MUX>, ++ <&clock HI3516AV200_FIXED_400M>, ++ <&clock HI3516AV200_FIXED_500M>, ++ <&clock HI3516AV200_FIXED_594M>, ++ <&clock HI3516AV200_FIXED_792M>, ++ <&clock HI3516AV200_APLL_CLK>; ++ clock-names = "a17_mux","400m", "500m", ++ "594m", "792m", "apll"; ++ vcc-supply = <&a17_regulator>; ++ }; ++ }; ++ ++ avs { ++ compatible = "hi3516av200,avs"; ++ avs-num = <2>; ++ avs-name-array = "cpu-avs","media-avs"; ++ cpu_avs: cpu_avs{ ++ avs-name = "cpu-avs"; ++ opp-num = <6>; ++ opp-freq = <1250000 1150000 1000000 930000 792000 594000 >; ++ opp-volt-min = <870000 870000 800000 800000 740000 740000>; ++ opp-hpm = <310 310 280 280 250 250>; ++ opp-div = <24 22 19 18 15 11>; ++ opp-volt-max = <1060000>; ++ }; ++ ++ media_avs: media_avs{ ++ avs-name = "media-avs"; ++ opp-num = <4>; ++ opp-prof-num = <2>; ++ opp-temp-num = <2>; ++ opp-temp = <50 200>; ++ opp-freq = <1 2 3 4>; ++ opp-volt-min = < ++ /* profile2 profile3*/ ++ 770000 770000 ++ 770000 770000 ++ >; ++ opp-hpm = < ++ /* profile2 profile3*/ ++ 210 215 ++ 190 215 ++ >; ++ opp-div = <3 3 3 3>; ++ opp-volt-max = < ++ /* profile2 profile3*/ ++ 977000 977000 ++ 977000 977000 ++ >; ++ }; ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x80000000 0x40000000>; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&dual_timer0 { ++ status = "okay"; ++}; ++ ++&i2c_bus0 { ++ status = "okay"; ++}; ++ ++&i2c_bus1 { ++ status = "okay"; ++}; ++ ++&i2c_bus2 { ++ status = "okay"; ++}; ++ ++&i2c_bus3 { ++ status = "okay"; ++}; ++ ++&spi_bus0 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24000000>; ++ }; ++ ++}; ++ ++&spi_bus1 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24000000>; ++ }; ++ ++ spidev@1 { ++ compatible = "rohm,dh2228fv"; ++ reg = <1>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24000000>; ++ }; ++}; ++ ++&spi_bus2 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24000000>; ++ }; ++}; ++ ++&spi_bus3 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24000000>; ++ }; ++}; ++ ++&hisfc { ++ hi_sfc { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ m25p,fast-read; ++ }; ++}; ++ ++&hisnfc { ++ hinand { ++ compatible = "jedec,spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ }; ++}; ++ ++&hinfc { ++ hinand { ++ compatible = "jedec,nand"; ++ reg = <0>; ++ nand-max-frequency = <200000000>; ++ }; ++}; ++ ++&mmc0 { ++ status = "okay"; ++}; ++ ++&mmc1 { ++ status = "okay"; ++}; ++ ++&mmc2 { ++ status = "okay"; ++}; ++ ++&mdio { ++ ethphy: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&higmac { ++ compatible = "hisilicon,higmac-v3", "hisilicon,higmac"; ++ phy-handle = <ðphy>; ++ phy-mode = "rgmii"; ++}; ++ ++&gpio_chip0 { ++ status = "okay"; ++}; ++ ++&gpio_chip1 { ++ status = "okay"; ++}; ++ ++&gpio_chip2 { ++ status = "okay"; ++}; ++ ++&gpio_chip3 { ++ status = "okay"; ++}; ++ ++&gpio_chip4 { ++ status = "okay"; ++}; ++ ++&gpio_chip5 { ++ status = "okay"; ++}; ++ ++&gpio_chip6 { ++ status = "okay"; ++}; ++ ++&gpio_chip7 { ++ status = "okay"; ++}; ++ ++&gpio_chip8 { ++ status = "okay"; ++}; ++ ++&gpio_chip9 { ++ status = "okay"; ++}; ++ ++&gpio_chip10 { ++ status = "okay"; ++}; ++ ++&gpio_chip11 { ++ status = "okay"; ++}; ++ ++&gpio_chip12 { ++ status = "okay"; ++}; ++ ++&gpio_chip13 { ++ status = "okay"; ++}; ++ ++&gpio_chip14 { ++ status = "okay"; ++}; ++ ++&gpio_chip16 { ++ status = "okay"; ++}; +diff --git a/arch/arm/boot/dts/hisi-hi3516av200.dtsi b/arch/arm/boot/dts/hisi-hi3516av200.dtsi +new file mode 100644 +index 0000000..f3f087a +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3516av200.dtsi +@@ -0,0 +1,777 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/hi3516av200-clock.h> ++/ { ++ aliases { ++ serial0 = &uart0; ++ i2c0 = &i2c_bus0; ++ i2c1 = &i2c_bus1; ++ i2c2 = &i2c_bus2; ++ i2c3 = &i2c_bus3; ++ spi0 = &spi_bus0; ++ spi1 = &spi_bus1; ++ spi2 = &spi_bus2; ++ spi3 = &spi_bus3; ++ gpio0 = &gpio_chip0; ++ gpio1 = &gpio_chip1; ++ gpio2 = &gpio_chip2; ++ gpio3 = &gpio_chip3; ++ gpio4 = &gpio_chip4; ++ gpio5 = &gpio_chip5; ++ gpio6 = &gpio_chip6; ++ gpio7 = &gpio_chip7; ++ gpio8 = &gpio_chip8; ++ gpio9 = &gpio_chip9; ++ gpio10 = &gpio_chip10; ++ gpio11 = &gpio_chip11; ++ gpio12 = &gpio_chip12; ++ gpio13 = &gpio_chip13; ++ gpio14 = &gpio_chip14; ++ gpio16 = &gpio_chip16; ++ }; ++ ++ gic: interrupt-controller@10300000 { ++ compatible = "arm,cortex-a7-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ /* gic dist base, gic cpu base , no virtual support */ ++ reg = <0x10301000 0x1000>, <0x10302000 0x1000>; ++ }; ++ ++ clock: clock0 { ++ compatible = "hisilicon,hi3516av200-clock"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ #clock-cells = <1>; ++ #reset-cells = <2>; ++ reg = <0x12010000 0x10000>; ++ }; ++ ++ syscounter { ++ compatible = "arm,armv7-timer"; ++ interrupt-parent = <&gic>; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>; ++ clock-frequency = <24000000>; ++ }; ++ ++ soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&gic>; ++ ranges; ++ ++ amba { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "arm,amba-bus"; ++ ranges; ++ ++ uart0: uart@12100000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12100000 0x1000>; ++ interrupts = <0 4 4>; ++ clocks = <&clock HI3516AV200_UART0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart1: uart@12101000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12101000 0x1000>; ++ interrupts = <0 5 4>; ++ clocks = <&clock HI3516AV200_UART1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart2: uart@12102000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12102000 0x1000>; ++ interrupts = <0 6 4>; ++ clocks = <&clock HI3516AV200_UART2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart3: uart@12103000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12103000 0x1000>; ++ interrupts = <0 7 4>; ++ clocks = <&clock HI3516AV200_UART3_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart4: uart@12104000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12104000 0x1000>; ++ interrupts = <0 8 4>; ++ clocks = <&clock HI3516AV200_UART4_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ sys: sys@12010000 { ++ compatible = "hisilicon,hi35xx_sys"; ++ reg = <0x12020000 0x10000>, <0x12060000 0x10000>, <0X12030000 0x10000>; ++ reg-names = "sys", "ddr", "misc"; ++ }; ++ ++ mipi: mipi@11300000 { ++ compatible = "hisilicon,hi35xx_mipi"; ++ interrupts = <0 28 4>, <0 29 4>; ++ interrupt-names = "mipi0", "mipi1"; ++ reg = <0x11300000 0x10000>; ++ }; ++ ++ isp: isp@11380000 { ++ compatible = "hisilicon,hi35xx_isp"; ++ interrupts = <0 30 4>, <0 31 4>; ++ interrupt-names = "isp0", "isp1"; ++ }; ++ ++ viu: viu@11380000 { ++ compatible = "hisilicon,hi35xx_viu"; ++ interrupts = <0 30 4>, <0 31 4>; ++ interrupt-names = "viu0", "viu1"; ++ reg = <0x11380000 0x70000>, <0x11480000 0x70000>; ++ reg-names = "viu0", "viu1"; ++ }; ++ ++ vou: vou@11000000 { ++ compatible = "hisilicon,hi35xx_vou"; ++ interrupts = <0 27 4>; ++ reg = <0x11000000 0x20000>; ++ }; ++ ++ tde: tde@11100000 { ++ compatible = "hisilicon,hi35xx_tde"; ++ interrupts = <0 34 4>; ++ reg = <0x11100000 0x10000>; ++ }; ++ ++ fisheye: fisheye@11110000 { ++ compatible = "hisilicon,hi35xx_fisheye"; ++ interrupts = <0 48 4>; ++ reg = <0x11110000 0x10000>; ++ }; ++ ++ vgs: vgs@11120000 { ++ compatible = "hisilicon,hi35xx_vgs"; ++ interrupts = <0 35 4>; ++ reg = <0x11120000 0x10000>; ++ }; ++ ++ vpss: vpss@11180000 { ++ compatible = "hisilicon,hi35xx_vpss"; ++ interrupts = <0 32 4>; ++ reg = <0x11180000 0x10000>; ++ }; ++ ++ vedu: vedu@11280000 { ++ compatible = "hisilicon,hi35xx_vedu"; ++ interrupts = <0 37 4>; ++ reg = <0x11280000 0x10000>; ++ }; ++ ++ jpege: jpege@11200000 { ++ compatible = "hisilicon,hi35xx_jpege"; ++ interrupts = <0 38 4>; ++ reg = <0x11200000 0x10000>; ++ }; ++ ++ aiao: aiao@11080000 { ++ compatible = "hisilicon,hi35xx_aiao"; ++ interrupts = <0 36 4>; ++ reg = <0x11080000 0x3000>; ++ }; ++ ++ ive: ive@11040000 { ++ compatible = "hisilicon,hi35xx_ive"; ++ interrupts = <0 39 4>; ++ reg = <0x11040000 0x10000>; ++ }; ++ ++ fd: fd@11060000 { ++ compatible = "hisilicon,hi35xx_fd"; ++ interrupts = <0 40 4>; ++ reg = <0x11060000 0x10000>; ++ }; ++ ++ pwm: pwm@12130000 { ++ compatible = "hisilicon,pwm"; ++ reg = <0x12130000 0x10000>; ++ }; ++ ++ piris: piris@12145000 { ++ compatible = "hisilicon,piris"; ++ reg = <0x12145000 0x1000>; ++ }; ++ ++ wtdg: wtdg@12080000 { ++ compatible = "hisilicon,hi_wdg"; ++ reg = <0x12080000 0x10000>; ++ reg-names = "wtdg"; ++ }; ++ ++ rtc: rtc@12090000 { ++ compatible = "hisilicon,hi_rtc"; ++ interrupts = <0 1 4>; ++ reg = <0x12090000 0x10000>; ++ }; ++ ++ ir: ir@120f0000{ ++ compatible = "hisilicon,hi_ir"; ++ interrupts = <0 15 4>; ++ reg = <0x120f0000 0x10000>; ++ }; ++ ++ pm { ++ compatible = "hisilicon,hibvt-pm"; ++ reg = <0x12020000 0x1000>, <0x12000000 0x1000>; ++ }; ++ ++ pm_hibernate { ++ compatible = "hisilicon,hibvt-pm-hibernate"; ++ reg = <0x12020000 0x1000>; ++ }; ++ ++ dual_timer0: dual_timer@12000000 { ++ compatible = "arm,sp804"; ++ /* timer0 & timer1 */ ++ interrupts = <0 64 4>, <0 65 4>; ++ reg = <0x12000000 0x1000>; ++ clocks = <&clock HI3516AV200_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer1: dual_timer@12001000 { ++ compatible = "arm,sp804"; ++ /* timer2 & timer3 */ ++ interrupts = <0 66 4>, <0 67 4>; ++ reg = <0x12001000 0x1000>; ++ clocks = <&clock HI3516AV200_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer2: dual_timer@12002000 { ++ compatible = "arm,sp804"; ++ /* timer4 & timer5 */ ++ interrupts = <0 68 4>, <0 69 4>; ++ reg = <0x12002000 0x1000>; ++ clocks = <&clock HI3516AV200_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ i2c_bus0: i2c@12110000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12110000 0x100>; ++ clocks = <&clock HI3516AV200_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus1: i2c@12111000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12111000 0x100>; ++ clocks = <&clock HI3516AV200_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus2: i2c@12112000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12112000 0x100>; ++ clocks = <&clock HI3516AV200_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus3: i2c@12113000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12113000 0x100>; ++ clocks = <&clock HI3516AV200_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ spi_bus0: spi@12120000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12120000 0x1000>; ++ interrupts = <0 9 4>; ++ clocks = <&clock HI3516AV200_SPI0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi_bus1: spi@12121000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12121000 0x1000>; ++ interrupts = <0 10 4>; ++ clocks = <&clock HI3516AV200_SPI1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi_bus2: spi@12122000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12122000 0x1000>, <0x12030004 0x4>; ++ interrupts = <0 11 4>; ++ clocks = <&clock HI3516AV200_SPI2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <2>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ hisi,spi_cs_sb = <26>; ++ hisi,spi_cs_mask_bit = <0x0c000000>; ++ }; ++ ++ spi_bus3: spi@12123000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12123000 0x1000>; ++ interrupts = <0 12 4>; ++ clocks = <&clock HI3516AV200_SPI3_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ gpio_chip0: gpio_chip@12140000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12140000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip1: gpio_chip@12141000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12141000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip2: gpio_chip@12142000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12142000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip3: gpio_chip@12143000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12143000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip4: gpio_chip@12144000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12144000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip5: gpio_chip@12145000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12145000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip6: gpio_chip@12146000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12146000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip7: gpio_chip@12147000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12147000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip8: gpio_chip@12148000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12148000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip9: gpio_chip@12149000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12149000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip10: gpio_chip@1214a000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214a000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip11: gpio_chip@1214b000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214b000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip12: gpio_chip@1214c000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214c000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip13: gpio_chip@1214d000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214d000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip14: gpio_chip@1214e000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214e000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip16: gpio_chip@12150000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12150000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3516AV200_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ }; ++ ++ pmc@0x120a0000 { ++ compatible = "hisilicon,pmc"; ++ reg = <0x120a0000 0x1000>; ++ }; ++ ++ sysctrl: system-controller@00000000 { ++ compatible = "hisilicon,sysctrl"; ++ reg = <0x12020000 0x1000>; ++ reboot-offset = <0x4>; ++ }; ++ ++ usb_phy: phy { ++ compatible = "hisilicon,hisi-usb-phy"; ++ reg = <0x12030000 0x10000>, <0x12010000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ++ usb3_phy: phy3 { ++ compatible = "hisilicon,hisi-usb3-phy"; ++ reg = <0x10180000 0x10000>, <0x12010000 0x10000>, ++ <0x12030000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ehci@0x10120000 { ++ compatible = "generic-ehci"; ++ reg = <0x10120000 0x10000>; ++ interrupts = <0 19 4>; ++ ++ clocks = <&clock HI3516AV200_USB2_CTRL_UTMI0_REQ>, ++ <&clock HI3516AV200_USB2_HRST_REQ>; ++ clock-names = "usb2_cttl_utmi0_req", "usb2_hrst_req"; ++ }; ++ ++ ohci@0x10110000 { ++ compatible = "generic-ohci"; ++ reg = <0x10110000 0x10000>; ++ interrupts = <0 20 4>; ++ ++ clocks = <&clock HI3516AV200_USB2_CTRL_UTMI0_REQ>, ++ <&clock HI3516AV200_USB2_HRST_REQ>; ++ clock-names = "usb2_cttl_utmi0_req", "usb2_hrst_req"; ++ }; ++ ++ xhci@0x10180000 { ++ compatible = "hisilicon,hi3516av200-xhci", "generic-xhci"; ++ reg = <0x10180000 0x10000>; ++ interrupts = <0 22 4>; ++ ++ clocks = <&clock HI3516AV200_USB3_CLK>; ++ clock-names = "clk"; ++ }; ++ ++ hiudc@0x10130000 { ++ compatible = "hiudc"; ++ reg = <0x10130000 0x40000>; ++ interrupts = <0 21 4>; ++ ++ clocks = <&clock HI3516AV200_USB2_HRST_REQ>; ++ clock-names = "clk"; ++ }; ++ ++ hiudc3@0x10180000 { ++ compatible = "dwc_usb3"; ++ reg = <0x10180000 0x40000>; ++ interrupts = <0 22 4>; ++ ++ clocks = <&clock HI3516AV200_USB3_CLK>; ++ clock-names = "clk"; ++ }; ++ ++ cci: cci@1ff00000 { ++ compatible = "arm,cci-400"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0x1ff00000 0x1000>; ++ ranges = <0x0 0x1ff00000 0x6000>; ++ ++ cci_control0: slave-if@4000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x2000 0x1000>; ++ }; ++ ++ cci_control1: slave-if@5000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x3000 0x1000>; ++ }; ++ ++ }; ++ ++ regulators@120a0000 { ++ compatible = "hi35xx,regulators"; ++ reg = <0x120a0000 0x1000>; ++ regulator-num = <2>; ++ regulator-name-array = "regulator-a17","regulator-media"; ++ ++ a17_regulator: a17_regulator{ ++ regulator-name = "regulator-a17"; ++ regulator-min-microvolt = <651216>; ++ regulator-max-microvolt = <1083359>; ++ regulator-always-on; ++ reg_offset = <0x4>; ++ }; ++ ++ media_regulator: media_regulator{ ++ regulator-name = "regulator-media"; ++ regulator-min-microvolt = <649609>; ++ regulator-max-microvolt = <977031>; ++ regulator-always-on; ++ reg_offset = <0xC>; ++ }; ++ }; ++ ++ pmu { ++ compatible = "arm,cortex-a7-pmu", "arm,cortex-a17-pmu"; ++ interrupts = <0 45 4>, ++ <0 46 4>; ++ }; ++ ++ mdio: mdio@100503c0 { ++ compatible = "hisilicon,hisi-gemac-mdio"; ++ reg = <0x100503c0 0x20>; ++ clocks = <&clock HI3516AV200_ETH_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ higmac: ethernet@10050000 { ++ compatible = "hisilicon,higmac"; ++ reg = <0x10050000 0x1000>,<0x120100ec 0x4>; ++ interrupts = <0 25 4>; ++ ++ clocks = <&clock HI3516AV200_ETH_CLK>, ++ <&clock HI3516AV200_ETH_MACIF_CLK>; ++ clock-names = "higmac_clk", ++ "macif_clk"; ++ ++ resets = <&clock 0xcc 0>, ++ <&clock 0xcc 2>, ++ <&clock 0xcc 7>; ++ reset-names = "port_reset", ++ "macif_reset", ++ "phy_reset"; ++ ++ mac-address = [00 00 00 00 00 00]; ++ }; ++ ++ mmc2: himciv200.MMC@0x100e0000 { ++ compatible = "hisilicon,hi3516av200-himciv200"; ++ reg = <0x100e0000 0x10000>; ++ interrupts = <0 13 4>; ++ ++ clocks = <&clock HI3516AV200_MMC2_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <8>; ++ max-frequency = <148500000>; ++ cap-mmc-highspeed; ++ cap-mmc-hw-reset; ++ full-pwr-cycle; ++ mmc-hs400-1_8v; ++ devid = <2>; ++ status = "disabled"; ++ }; ++ ++ mmc0: himciv200.SD@0x100c0000 { ++ compatible = "hisilicon,hi3516av200-himciv200"; ++ reg = <0x100c0000 0x10000>; ++ interrupts = <0 23 4>; ++ ++ clocks = <&clock HI3516AV200_MMC0_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <4>; ++ max-frequency = <148500000>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ devid = <0>; ++ status = "disabled"; ++ }; ++ ++ mmc1: himciv200.SD@0x100d0000 { ++ compatible = "hisilicon,hi3516av200-himciv200"; ++ reg = <0x100d0000 0x10000>; ++ interrupts = <0 24 4>; ++ ++ clocks = <&clock HI3516AV200_MMC1_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <4>; ++ max-frequency = <148500000>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ devid = <1>; ++ status = "disabled"; ++ }; ++ ++ fmc: spi-nor-controller@10000000 { ++ compatible = "hisilicon,hisi-fmc"; ++ reg = <0x10000000 0x1000>, <0x14000000 0x1000000>; ++ reg-names = "control", "memory"; ++ clocks = <&clock HI3516AV200_FMC_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hisfc:spi-nor@0 { ++ compatible = "hisilicon,hisi-sfc"; ++ assigned-clocks = <&clock HI3516AV200_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hisnfc:spi-nand@0 { ++ compatible = "hisilicon,hisi-spi-nand"; ++ assigned-clocks = <&clock HI3516AV200_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hinfc:nand@0 { ++ compatible = "hisilicon,hisi-nand"; ++ assigned-clocks = <&clock HI3516AV200_FMC_CLK>; ++ assigned-clock-rates = <200000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ }; ++ ++}; +diff --git a/arch/arm/boot/dts/hisi-hi3519-demb.dts b/arch/arm/boot/dts/hisi-hi3519-demb.dts +new file mode 100644 +index 0000000..9b56684 +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3519-demb.dts +@@ -0,0 +1,109 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++/dts-v1/; ++#include "hisi-hi3519.dtsi" ++ ++/ { ++ model = "Hisilicon HI3519 DEMO Board"; ++ compatible = "hisilicon,hi3519"; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ enable-method = "hisilicon,hi3519-smp"; ++ ++ cpu@0 { ++ compatible = "arm,cortex-a7"; ++ device_type = "cpu"; ++ clock-frequency = <HI3519_FIXED_792M>; ++ reg = <0>; ++ cci-control-port = <&cci_control0>; ++ }; ++ ++ /*cpu@100 { ++ compatible = "arm,cortex-a17"; ++ device_type = "cpu"; ++ clock-frequency = <HI3519_FIXED_1000M>; ++ reg = <0x100>; ++ cci-control-port = <&cci_control1>; ++ };*/ ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x80000000 0x40000000>; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&dual_timer0 { ++ status = "okay"; ++}; ++ ++&hisfc { ++ hi_sfc { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ m25p,fast-read; ++ }; ++}; ++ ++&hisnfc { ++ hinand { ++ compatible = "jedec,spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ }; ++}; ++ ++&hinfc { ++ hinand { ++ compatible = "jedec,nand"; ++ reg = <0>; ++ nand-max-frequency = <200000000>; ++ }; ++}; ++ ++&mmc0 { ++ status = "okay"; ++}; ++ ++&mmc1 { ++ status = "okay"; ++}; ++ ++&mmc2 { ++ status = "okay"; ++}; ++ ++&mdio { ++ ethphy: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&higmac { ++ phy-handle = <ðphy>; ++ phy-mode = "rgmii"; ++}; +diff --git a/arch/arm/boot/dts/hisi-hi3519-hmp-demb.dts b/arch/arm/boot/dts/hisi-hi3519-hmp-demb.dts +new file mode 100644 +index 0000000..c43b941 +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3519-hmp-demb.dts +@@ -0,0 +1,159 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++/dts-v1/; ++#include "hisi-hi3519.dtsi" ++ ++/ { ++ model = "Hisilicon HI3519 DEMO Board"; ++ compatible = "hisilicon,hi3519"; ++ ++ chosen { ++ bootargs = "console=ttyAMA0,115200 early_printk ++root=/dev/mtdblock2 rootfstype=jffs2 mtdparts=hi_sfc:1M(boot), ++4M(kernel),11M(rootfs)"; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ enable-method = "hisilicon,hi3519-smp"; ++ ++ cpu@0 { ++ compatible = "arm,cortex-a7"; ++ device_type = "cpu"; ++ clock-frequency = <HI3519_FIXED_792M>; ++ reg = <0>; ++ cci-control-port = <&cci_control0>; ++ }; ++ ++ cpu@100 { ++ compatible = "arm,cortex-a17"; ++ device_type = "cpu"; ++ reg = <0x100>; ++ cci-control-port = <&cci_control1>; ++#if 1 ++ operating-points = < ++ /* KHz uV */ ++ 1150000 1000000 ++ 1000000 960000 ++ 880000 920000 ++ 792000 890000 ++ 594000 830000 ++ >; ++ clocks = <&clock HI3519_A17_MUX>, ++ <&clock HI3519_FIXED_400M>, ++ <&clock HI3519_FIXED_500M>, ++ <&clock HI3519_FIXED_594M>, ++ <&clock HI3519_FIXED_792M>, ++ <&clock HI3519_FIXED_1000M>, ++ <&clock HI3519_APLL_CLK>; ++ clock-names = "a17_mux","400m", "500m", ++ "594m", "792m","1000m", "apll"; ++ vcc-supply = <&a17_regulator>; ++#endif ++ }; ++ }; ++ ++ avs { ++ compatible = "hi3519,avs"; ++ avs-num = <2>; ++ avs-name-array = "cpu-avs","media-avs"; ++ cpu_avs: cpu_avs{ ++ avs-name = "cpu-avs"; ++ opp-num = <5>; ++ opp-freq = <1150000 1000000 880000 792000 594000 >; ++ opp-volt-min = <800000 750000 720000 700000 670000 >; ++ opp-hpm = <305 290 270 240 220 >; ++ opp-div = <22 19 17 15 11 >; ++ opp-volt-max = <1000000>; ++ }; ++ ++ media_avs: media_avs{ ++ avs-name = "media-avs"; ++ opp-num = <1>; ++ opp-freq = <1>; ++ opp-volt-min = <750000>; ++ opp-hpm = <260>; ++ opp-div = <3>; ++ opp-volt-max = <950000>; ++ }; ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x80000000 0x40000000>; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&dual_timer0 { ++ status = "okay"; ++}; ++ ++&hisfc { ++ hi_sfc { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ m25p,fast-read; ++ }; ++}; ++ ++&hisnfc { ++ hinand { ++ compatible = "jedec,spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ }; ++}; ++ ++&hinfc { ++ hinand { ++ compatible = "jedec,nand"; ++ reg = <0>; ++ nand-max-frequency = <200000000>; ++ }; ++}; ++ ++&mmc0 { ++ status = "okay"; ++}; ++ ++&mmc1 { ++ status = "okay"; ++}; ++ ++&mmc2 { ++ status = "okay"; ++}; ++ ++&mdio { ++ ethphy: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&higmac { ++ phy-handle = <ðphy>; ++ phy-mode = "rgmii"; ++}; +diff --git a/arch/arm/boot/dts/hisi-hi3519.dtsi b/arch/arm/boot/dts/hisi-hi3519.dtsi +new file mode 100644 +index 0000000..8fb6d56 +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3519.dtsi +@@ -0,0 +1,470 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/hi3519-clock.h> ++/ { ++ aliases { ++ serial0 = &uart0; ++ spi0 = &spi_bus0; ++ spi1 = &spi_bus1; ++ spi2 = &spi_bus2; ++ }; ++ ++ gic: interrupt-controller@10300000 { ++ compatible = "arm,cortex-a7-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ /* gic dist base, gic cpu base , no virtual support */ ++ reg = <0x10301000 0x1000>, <0x10302000 0x1000>; ++ }; ++ ++ clock: clock0 { ++ compatible = "hisilicon,hi3519-clock"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ #clock-cells = <1>; ++ #reset-cells = <2>; ++ reg = <0x12010000 0x10000>; ++ }; ++ ++ syscounter { ++ compatible = "arm,armv7-timer"; ++ interrupt-parent = <&gic>; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>; ++ clock-frequency = <24000000>; ++ }; ++ ++ soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&gic>; ++ ranges; ++ ++ amba { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "arm,amba-bus"; ++ ranges; ++ ++ uart0: uart@12100000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12100000 0x1000>; ++ interrupts = <0 4 4>; ++ clocks = <&clock HI3519_UART0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart1: uart@12101000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12101000 0x1000>; ++ interrupts = <0 5 4>; ++ clocks = <&clock HI3519_UART1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart2: uart@12102000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12102000 0x1000>; ++ interrupts = <0 6 4>; ++ clocks = <&clock HI3519_UART2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart3: uart@12103000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12103000 0x1000>; ++ interrupts = <0 7 4>; ++ clocks = <&clock HI3519_UART3_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart4: uart@12104000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12104000 0x1000>; ++ interrupts = <0 8 4>; ++ clocks = <&clock HI3519_UART4_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ ++ pm { ++ compatible = "hisilicon,hibvt-pm"; ++ reg = <0x12020000 0x1000>, <0x12000000 0x1000>; ++ }; ++ ++ pm_hibernate { ++ compatible = "hisilicon,hibvt-pm-hibernate"; ++ reg = <0x12020000 0x1000>; ++ }; ++ ++ dual_timer0: dual_timer@12000000 { ++ compatible = "arm,sp804"; ++ /* timer0 & timer1 */ ++ interrupts = <0 64 4>, <0 65 4>; ++ reg = <0x12000000 0x1000>; ++ clocks = <&clock HI3519_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer1: dual_timer@12001000 { ++ compatible = "arm,sp804"; ++ /* timer2 & timer3 */ ++ interrupts = <0 66 4>, <0 67 4>; ++ reg = <0x12001000 0x1000>; ++ clocks = <&clock HI3519_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer2: dual_timer@12002000 { ++ compatible = "arm,sp804"; ++ /* timer4 & timer5 */ ++ interrupts = <0 68 4>, <0 69 4>; ++ reg = <0x12002000 0x1000>; ++ clocks = <&clock HI3519_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ spi_bus0: spi@12120000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12120000 0x1000>; ++ interrupts = <0 9 4>; ++ clocks = <&clock HI3519_SPI0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "okay"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ spidev@0 { ++ compatible = "spidev"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++ ++ }; ++ ++ spi_bus1: spi@12121000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12121000 0x1000>, <0x12030004 0x4>; ++ interrupts = <0 10 4>; ++ clocks = <&clock HI3519_SPI1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "okay"; ++ num-cs = <2>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ hisi,spi_cs_sb = <26>; ++ hisi,spi_cs_mask_bit = <0x0c000000>; ++ ++ spidev@0 { ++ compatible = "spidev"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++ ++ spidev@1 { ++ compatible = "spidev"; ++ reg = <1>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++ }; ++ ++ spi_bus2: spi@12122000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12122000 0x1000>; ++ interrupts = <0 11 4>; ++ clocks = <&clock HI3519_SPI2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "okay"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ spidev@0 { ++ compatible = "spidev"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++ }; ++ ++ }; ++ ++ pmc@0x120a0000 { ++ compatible = "hisilicon,pmc"; ++ reg = <0x120a0000 0x1000>; ++ }; ++ ++ sysctrl: system-controller@00000000 { ++ compatible = "hisilicon,sysctrl"; ++ reg = <0x12020000 0x1000>; ++ reboot-offset = <0x4>; ++ }; ++ ++ usb_phy: phy { ++ compatible = "hisilicon,hisi-usb-phy"; ++ reg = <0x12030000 0x10000>, <0x12010000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ++ usb3_phy: phy3 { ++ compatible = "hisilicon,hisi-usb3-phy"; ++ reg = <0x10180000 0x10000>, <0x12010000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ehci@0x10120000 { ++ compatible = "generic-ehci"; ++ reg = <0x10120000 0x10000>; ++ interrupts = <0 19 4>; ++ ++ clocks = <&clock HI3519_USB2_CTRL_UTMI0_REQ>, ++ <&clock HI3519_USB2_HRST_REQ>; ++ clock-names = "usb2_cttl_utmi0_req", "usb2_hrst_req"; ++ }; ++ ++ ohci@0x10110000 { ++ compatible = "generic-ohci"; ++ reg = <0x10110000 0x10000>; ++ interrupts = <0 20 4>; ++ ++ clocks = <&clock HI3519_USB2_CTRL_UTMI0_REQ>, ++ <&clock HI3519_USB2_HRST_REQ>; ++ clock-names = "usb2_cttl_utmi0_req", "usb2_hrst_req"; ++ }; ++ ++ xhci@0x10180000 { ++ compatible = "hisilicon,hi3519-xhci", "generic-xhci"; ++ reg = <0x10180000 0x10000>; ++ interrupts = <0 22 4>; ++ ++ clocks = <&clock HI3519_USB3_CLK>; ++ clock-names = "clk"; ++ }; ++ ++ hiudc@0x10130000 { ++ compatible = "hiudc"; ++ reg = <0x10130000 0x40000>; ++ interrupts = <0 21 4>; ++ ++ clocks = <&clock HI3519_USB2_HRST_REQ>; ++ clock-names = "clk"; ++ }; ++ ++ hiudc3@0x10180000 { ++ compatible = "dwc_usb3"; ++ reg = <0x10180000 0x40000>; ++ interrupts = <0 22 4>; ++ ++ clocks = <&clock HI3519_USB3_CLK>; ++ clock-names = "clk"; ++ }; ++ ++ cci: cci@1ff00000 { ++ compatible = "arm,cci-400"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0x1ff00000 0x1000>; ++ ranges = <0x0 0x1ff00000 0x6000>; ++ ++ cci_control0: slave-if@4000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x1000 0x1000>; ++ }; ++ ++ cci_control1: slave-if@5000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x2000 0x1000>; ++ }; ++ ++ }; ++ ++ regulators@120a0000 { ++ compatible = "hi3519,regulators"; ++ reg = <0x120a0000 0x1000>; ++ regulator-num = <2>; ++ regulator-name-array = "regulator-a17","regulator-media"; ++ ++ a17_regulator: a17_regulator{ ++ regulator-name = "regulator-a17"; ++ regulator-min-microvolt = <648633>; ++ regulator-max-microvolt = <1200525>; ++ regulator-always-on; ++ reg_offset = <0x4>; ++ }; ++ ++ media_regulator: media_regulator{ ++ regulator-name = "regulator-media"; ++ regulator-min-microvolt = <649740>; ++ regulator-max-microvolt = <1204331>; ++ regulator-always-on; ++ reg_offset = <0xC>; ++ }; ++ }; ++ ++ pmu { ++ compatible = "arm,cortex-a7-pmu", "arm,cortex-a17-pmu"; ++ interrupts = <0 45 4>, ++ <0 46 4>; ++ }; ++ ++ mdio: mdio@100503c0 { ++ compatible = "hisilicon,hisi-gemac-mdio"; ++ reg = <0x100503c0 0x20>; ++ clocks = <&clock HI3519_ETH_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ higmac: ethernet@10050000 { ++ compatible = "hisilicon,higmac"; ++ reg = <0x10050000 0x1000>,<0x120100ec 0x4>; ++ interrupts = <0 25 4>; ++ ++ clocks = <&clock HI3519_ETH_CLK>, ++ <&clock HI3519_ETH_MACIF_CLK>; ++ clock-names = "higmac_clk", ++ "macif_clk"; ++ ++ resets = <&clock 0xcc 0>, ++ <&clock 0xcc 2>, ++ <&clock 0xcc 7>; ++ reset-names = "port_reset", ++ "macif_reset", ++ "phy_reset"; ++ ++ mac-address = [00 00 00 00 00 00]; ++ }; ++ ++ mmc2: himciv200.MMC@0x100e0000 { ++ compatible = "hisilicon,hi3519-himciv200"; ++ reg = <0x100e0000 0x10000>; ++ interrupts = <0 13 4>; ++ ++ clocks = <&clock HI3519_MMC2_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <8>; ++ max-frequency = <99000000>; ++ cap-mmc-highspeed; ++ cap-mmc-hw-reset; ++ full-pwr-cycle; ++ mmc-hs400-1_8v; ++ devid = <2>; ++ status = "disabled"; ++ }; ++ ++ mmc0: himciv200.SD@0x100c0000 { ++ compatible = "hisilicon,hi3519-himciv200"; ++ reg = <0x100c0000 0x10000>; ++ interrupts = <0 23 4>; ++ ++ clocks = <&clock HI3519_MMC0_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <4>; ++ max-frequency = <99000000>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ devid = <0>; ++ status = "disabled"; ++ }; ++ ++ mmc1: himciv200.SD@0x100d0000 { ++ compatible = "hisilicon,hi3519-himciv200"; ++ reg = <0x100d0000 0x10000>; ++ interrupts = <0 24 4>; ++ ++ clocks = <&clock HI3519_MMC1_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <4>; ++ max-frequency = <99000000>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ devid = <1>; ++ status = "disabled"; ++ }; ++ ++ fmc: spi-nor-controller@10000000 { ++ compatible = "hisilicon,hisi-fmc"; ++ reg = <0x10000000 0x1000>, <0x14000000 0x1000000>; ++ reg-names = "control", "memory"; ++ clocks = <&clock HI3519_FMC_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hisfc:spi-nor@0 { ++ compatible = "hisilicon,hisi-sfc"; ++ assigned-clocks = <&clock HI3519_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hisnfc:spi-nand@0 { ++ compatible = "hisilicon,hisi-spi-nand"; ++ assigned-clocks = <&clock HI3519_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hinfc:nand@0 { ++ compatible = "hisilicon,hisi-nand"; ++ assigned-clocks = <&clock HI3519_FMC_CLK>; ++ assigned-clock-rates = <200000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ }; ++ ++}; +diff --git a/arch/arm/boot/dts/hisi-hi3519v101-demb.dts b/arch/arm/boot/dts/hisi-hi3519v101-demb.dts +new file mode 100644 +index 0000000..7478525 +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3519v101-demb.dts +@@ -0,0 +1,247 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++/dts-v1/; ++#include "hisi-hi3519v101.dtsi" ++ ++/ { ++ model = "Hisilicon HI3519V101 DEMO Board"; ++ compatible = "hisilicon,hi3519v101"; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ enable-method = "hisilicon,hi3519-smp"; ++ ++ cpu@0 { ++ compatible = "arm,cortex-a7"; ++ device_type = "cpu"; ++ clock-frequency = <HI3519_FIXED_792M>; ++ reg = <0>; ++ cci-control-port = <&cci_control0>; ++ }; ++ ++ /*cpu@100 { ++ compatible = "arm,cortex-a17"; ++ device_type = "cpu"; ++ clock-frequency = <HI3519_FIXED_1000M>; ++ reg = <0x100>; ++ cci-control-port = <&cci_control1>; ++ };*/ ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x80000000 0x40000000>; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&dual_timer0 { ++ status = "okay"; ++}; ++ ++&i2c_bus0 { ++ status = "okay"; ++}; ++ ++&i2c_bus1 { ++ status = "okay"; ++}; ++ ++&i2c_bus2 { ++ status = "okay"; ++}; ++ ++&i2c_bus3 { ++ status = "okay"; ++}; ++ ++&spi_bus0 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++ ++}; ++ ++&spi_bus1 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++}; ++ ++&spi_bus2 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++ ++ spidev@1 { ++ compatible = "rohm,dh2228fv"; ++ reg = <1>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++}; ++ ++&spi_bus3 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++}; ++ ++&hisfc { ++ hi_sfc { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ m25p,fast-read; ++ }; ++ ++}; ++ ++&hisnfc { ++ hinand { ++ compatible = "jedec,spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ }; ++}; ++ ++&hinfc { ++ hinand { ++ compatible = "jedec,nand"; ++ reg = <0>; ++ nand-max-frequency = <200000000>; ++ }; ++}; ++ ++&mmc0 { ++ status = "okay"; ++}; ++ ++&mmc1 { ++ status = "okay"; ++}; ++ ++&mmc2 { ++ status = "okay"; ++}; ++ ++&mdio { ++ ethphy: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&higmac { ++ phy-handle = <ðphy>; ++ phy-mode = "rgmii"; ++}; ++ ++&gpio_chip0 { ++ status = "okay"; ++}; ++ ++&gpio_chip1 { ++ status = "okay"; ++}; ++ ++&gpio_chip2 { ++ status = "okay"; ++}; ++ ++&gpio_chip3 { ++ status = "okay"; ++}; ++ ++&gpio_chip4 { ++ status = "okay"; ++}; ++ ++&gpio_chip5 { ++ status = "okay"; ++}; ++ ++&gpio_chip6 { ++ status = "okay"; ++}; ++ ++&gpio_chip7 { ++ status = "okay"; ++}; ++ ++&gpio_chip8 { ++ status = "okay"; ++}; ++ ++&gpio_chip9 { ++ status = "okay"; ++}; ++ ++&gpio_chip10 { ++ status = "okay"; ++}; ++ ++&gpio_chip11 { ++ status = "okay"; ++}; ++ ++&gpio_chip12 { ++ status = "okay"; ++}; ++ ++&gpio_chip13 { ++ status = "okay"; ++}; ++ ++&gpio_chip14 { ++ status = "okay"; ++}; ++ ++&gpio_chip16 { ++ status = "okay"; ++}; +diff --git a/arch/arm/boot/dts/hisi-hi3519v101-hmp-demb.dts b/arch/arm/boot/dts/hisi-hi3519v101-hmp-demb.dts +new file mode 100644 +index 0000000..dd2412f +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3519v101-hmp-demb.dts +@@ -0,0 +1,310 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++/dts-v1/; ++#include "hisi-hi3519v101.dtsi" ++ ++/ { ++ model = "Hisilicon HI3519V101 DEMO Board"; ++ compatible = "hisilicon,hi3519v101"; ++ ++ chosen { ++ bootargs = "console=ttyAMA0,115200 early_printk ++root=/dev/mtdblock2 rootfstype=jffs2 mtdparts=hi_sfc:1M(boot), ++4M(kernel),11M(rootfs)"; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ enable-method = "hisilicon,hi3519-smp"; ++ ++ cpu@0 { ++ compatible = "arm,cortex-a7"; ++ device_type = "cpu"; ++ clock-frequency = <HI3519_FIXED_792M>; ++ reg = <0>; ++ cci-control-port = <&cci_control0>; ++ }; ++ ++ cpu@100 { ++ compatible = "arm,cortex-a17"; ++ device_type = "cpu"; ++ reg = <0x100>; ++ cci-control-port = <&cci_control1>; ++ operating-points = < ++ /* KHz uV */ ++ 1250000 1060000 ++ 1150000 1060000 ++ 1000000 1000000 ++ 930000 1000000 ++ 792000 940000 ++ 594000 940000 ++ >; ++ clocks = <&clock HI3519_A17_MUX>, ++ <&clock HI3519_FIXED_400M>, ++ <&clock HI3519_FIXED_500M>, ++ <&clock HI3519_FIXED_594M>, ++ <&clock HI3519_FIXED_792M>, ++ <&clock HI3519_APLL_CLK>; ++ clock-names = "a17_mux","400m", "500m", ++ "594m", "792m", "apll"; ++ vcc-supply = <&a17_regulator>; ++ }; ++ }; ++ ++ avs { ++ compatible = "hi3519,avs"; ++ avs-num = <2>; ++ avs-name-array = "cpu-avs","media-avs"; ++ cpu_avs: cpu_avs{ ++ avs-name = "cpu-avs"; ++ opp-num = <6>; ++ opp-freq = <1250000 1150000 1000000 930000 792000 594000 >; ++ opp-volt-min = <870000 870000 800000 800000 740000 740000>; ++ opp-hpm = <310 310 280 280 250 250>; ++ opp-div = <24 22 19 18 15 11>; ++ opp-volt-max = <1060000>; ++ }; ++ ++ media_avs: media_avs{ ++ avs-name = "media-avs"; ++ opp-num = <4>; ++ opp-prof-num = <2>; ++ opp-temp-num = <2>; ++ opp-temp = <50 200>; ++ opp-freq = <1 2 3 4>; ++ opp-volt-min = < ++ /* profile2 profile3*/ ++ 770000 770000 ++ 770000 770000 ++ >; ++ opp-hpm = < ++ /* profile2 profile3*/ ++ 210 215 ++ 190 215 ++ >; ++ opp-div = <3 3 3 3>; ++ opp-volt-max = < ++ /* profile2 profile3*/ ++ 977000 977000 ++ 977000 977000 ++ >; ++ }; ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x80000000 0x40000000>; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&dual_timer0 { ++ status = "okay"; ++}; ++ ++&i2c_bus0 { ++ status = "okay"; ++}; ++ ++&i2c_bus1 { ++ status = "okay"; ++}; ++ ++&i2c_bus2 { ++ status = "okay"; ++}; ++ ++&i2c_bus3 { ++ status = "okay"; ++}; ++ ++&spi_bus0 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24000000>; ++ }; ++ ++}; ++ ++&spi_bus1 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24000000>; ++ }; ++ ++ spidev@1 { ++ compatible = "rohm,dh2228fv"; ++ reg = <1>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24000000>; ++ }; ++}; ++ ++&spi_bus2 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24000000>; ++ }; ++}; ++ ++&spi_bus3 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24000000>; ++ }; ++}; ++ ++&hisfc { ++ hi_sfc { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ m25p,fast-read; ++ }; ++}; ++ ++&hisnfc { ++ hinand { ++ compatible = "jedec,spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ }; ++}; ++ ++&hinfc { ++ hinand { ++ compatible = "jedec,nand"; ++ reg = <0>; ++ nand-max-frequency = <200000000>; ++ }; ++}; ++ ++&mmc0 { ++ status = "okay"; ++}; ++ ++&mmc1 { ++ status = "okay"; ++}; ++ ++&mmc2 { ++ status = "okay"; ++}; ++ ++&mdio { ++ ethphy: ethernet-phy@1 { ++ reg = <1>; ++ }; ++}; ++ ++&higmac { ++ compatible = "hisilicon,higmac-v3", "hisilicon,higmac"; ++ phy-handle = <ðphy>; ++ phy-mode = "rgmii"; ++}; ++ ++&gpio_chip0 { ++ status = "okay"; ++}; ++ ++&gpio_chip1 { ++ status = "okay"; ++}; ++ ++&gpio_chip2 { ++ status = "okay"; ++}; ++ ++&gpio_chip3 { ++ status = "okay"; ++}; ++ ++&gpio_chip4 { ++ status = "okay"; ++}; ++ ++&gpio_chip5 { ++ status = "okay"; ++}; ++ ++&gpio_chip6 { ++ status = "okay"; ++}; ++ ++&gpio_chip7 { ++ status = "okay"; ++}; ++ ++&gpio_chip8 { ++ status = "okay"; ++}; ++ ++&gpio_chip9 { ++ status = "okay"; ++}; ++ ++&gpio_chip10 { ++ status = "okay"; ++}; ++ ++&gpio_chip11 { ++ status = "okay"; ++}; ++ ++&gpio_chip12 { ++ status = "okay"; ++}; ++ ++&gpio_chip13 { ++ status = "okay"; ++}; ++ ++&gpio_chip14 { ++ status = "okay"; ++}; ++ ++&gpio_chip16 { ++ status = "okay"; ++}; +diff --git a/arch/arm/boot/dts/hisi-hi3519v101.dtsi b/arch/arm/boot/dts/hisi-hi3519v101.dtsi +new file mode 100644 +index 0000000..df48673 +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3519v101.dtsi +@@ -0,0 +1,777 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/hi3519-clock.h> ++/ { ++ aliases { ++ serial0 = &uart0; ++ i2c0 = &i2c_bus0; ++ i2c1 = &i2c_bus1; ++ i2c2 = &i2c_bus2; ++ i2c3 = &i2c_bus3; ++ spi0 = &spi_bus0; ++ spi1 = &spi_bus1; ++ spi2 = &spi_bus2; ++ spi3 = &spi_bus3; ++ gpio0 = &gpio_chip0; ++ gpio1 = &gpio_chip1; ++ gpio2 = &gpio_chip2; ++ gpio3 = &gpio_chip3; ++ gpio4 = &gpio_chip4; ++ gpio5 = &gpio_chip5; ++ gpio6 = &gpio_chip6; ++ gpio7 = &gpio_chip7; ++ gpio8 = &gpio_chip8; ++ gpio9 = &gpio_chip9; ++ gpio10 = &gpio_chip10; ++ gpio11 = &gpio_chip11; ++ gpio12 = &gpio_chip12; ++ gpio13 = &gpio_chip13; ++ gpio14 = &gpio_chip14; ++ gpio16 = &gpio_chip16; ++ }; ++ ++ gic: interrupt-controller@10300000 { ++ compatible = "arm,cortex-a7-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ /* gic dist base, gic cpu base , no virtual support */ ++ reg = <0x10301000 0x1000>, <0x10302000 0x1000>; ++ }; ++ ++ clock: clock0 { ++ compatible = "hisilicon,hi3519v101-clock"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ #clock-cells = <1>; ++ #reset-cells = <2>; ++ reg = <0x12010000 0x10000>; ++ }; ++ ++ syscounter { ++ compatible = "arm,armv7-timer"; ++ interrupt-parent = <&gic>; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>; ++ clock-frequency = <24000000>; ++ }; ++ ++ soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&gic>; ++ ranges; ++ ++ amba { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "arm,amba-bus"; ++ ranges; ++ ++ uart0: uart@12100000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12100000 0x1000>; ++ interrupts = <0 4 4>; ++ clocks = <&clock HI3519_UART0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart1: uart@12101000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12101000 0x1000>; ++ interrupts = <0 5 4>; ++ clocks = <&clock HI3519_UART1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart2: uart@12102000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12102000 0x1000>; ++ interrupts = <0 6 4>; ++ clocks = <&clock HI3519_UART2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart3: uart@12103000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12103000 0x1000>; ++ interrupts = <0 7 4>; ++ clocks = <&clock HI3519_UART3_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart4: uart@12104000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12104000 0x1000>; ++ interrupts = <0 8 4>; ++ clocks = <&clock HI3519_UART4_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ sys: sys@12010000 { ++ compatible = "hisilicon,hi35xx_sys"; ++ reg = <0x12020000 0x10000>, <0x12060000 0x10000>, <0X12030000 0x10000>; ++ reg-names = "sys", "ddr", "misc"; ++ }; ++ ++ mipi: mipi@11300000 { ++ compatible = "hisilicon,hi35xx_mipi"; ++ interrupts = <0 28 4>, <0 29 4>; ++ interrupt-names = "mipi0", "mipi1"; ++ reg = <0x11300000 0x10000>; ++ }; ++ ++ isp: isp@11380000 { ++ compatible = "hisilicon,hi35xx_isp"; ++ interrupts = <0 30 4>, <0 31 4>; ++ interrupt-names = "isp0", "isp1"; ++ }; ++ ++ viu: viu@11380000 { ++ compatible = "hisilicon,hi35xx_viu"; ++ interrupts = <0 30 4>, <0 31 4>; ++ interrupt-names = "viu0", "viu1"; ++ reg = <0x11380000 0x70000>, <0x11480000 0x70000>; ++ reg-names = "viu0", "viu1"; ++ }; ++ ++ vou: vou@11000000 { ++ compatible = "hisilicon,hi35xx_vou"; ++ interrupts = <0 27 4>; ++ reg = <0x11000000 0x20000>; ++ }; ++ ++ tde: tde@11100000 { ++ compatible = "hisilicon,hi35xx_tde"; ++ interrupts = <0 34 4>; ++ reg = <0x11100000 0x10000>; ++ }; ++ ++ fisheye: fisheye@11110000 { ++ compatible = "hisilicon,hi35xx_fisheye"; ++ interrupts = <0 48 4>; ++ reg = <0x11110000 0x10000>; ++ }; ++ ++ vgs: vgs@11120000 { ++ compatible = "hisilicon,hi35xx_vgs"; ++ interrupts = <0 35 4>; ++ reg = <0x11120000 0x10000>; ++ }; ++ ++ vpss: vpss@11180000 { ++ compatible = "hisilicon,hi35xx_vpss"; ++ interrupts = <0 30 4>, <0 32 4>; ++ interrupt-names = "viu0", "vpss"; ++ reg = <0x11180000 0x10000>; ++ }; ++ ++ vedu: vedu@11280000 { ++ compatible = "hisilicon,hi35xx_vedu"; ++ interrupts = <0 37 4>; ++ reg = <0x11280000 0x10000>; ++ }; ++ ++ jpege: jpege@11200000 { ++ compatible = "hisilicon,hi35xx_jpege"; ++ interrupts = <0 38 4>; ++ reg = <0x11200000 0x10000>; ++ }; ++ ++ aiao: aiao@11080000 { ++ compatible = "hisilicon,hi35xx_aiao"; ++ interrupts = <0 36 4>; ++ reg = <0x11080000 0x3000>; ++ }; ++ ++ ive: ive@11040000 { ++ compatible = "hisilicon,hi35xx_ive"; ++ interrupts = <0 39 4>; ++ reg = <0x11040000 0x10000>; ++ }; ++ ++ fd: fd@11060000 { ++ compatible = "hisilicon,hi35xx_fd"; ++ interrupts = <0 40 4>; ++ reg = <0x11060000 0x10000>; ++ }; ++ ++ pwm: pwm@12130000 { ++ compatible = "hisilicon,pwm"; ++ reg = <0x12130000 0x10000>; ++ }; ++ ++ piris: piris@12145000 { ++ compatible = "hisilicon,piris"; ++ reg = <0x12145000 0x1000>; ++ }; ++ ++ wtdg: wtdg@12080000 { ++ compatible = "hisilicon,hi_wdg"; ++ reg = <0x12080000 0x10000>; ++ reg-names = "wtdg"; ++ }; ++ ++ rtc: rtc@12090000 { ++ compatible = "hisilicon,hi_rtc"; ++ interrupts = <0 1 4>; ++ reg = <0x12090000 0x10000>; ++ }; ++ ++ ir: ir@120f0000{ ++ compatible = "hisilicon,hi_ir"; ++ interrupts = <0 15 4>; ++ reg = <0x120f0000 0x10000>; ++ }; ++ ++ pm { ++ compatible = "hisilicon,hibvt-pm"; ++ reg = <0x12020000 0x1000>, <0x12000000 0x1000>; ++ }; ++ ++ pm_hibernate { ++ compatible = "hisilicon,hibvt-pm-hibernate"; ++ reg = <0x12020000 0x1000>; ++ }; ++ ++ dual_timer0: dual_timer@12000000 { ++ compatible = "arm,sp804"; ++ /* timer0 & timer1 */ ++ interrupts = <0 64 4>, <0 65 4>; ++ reg = <0x12000000 0x1000>; ++ clocks = <&clock HI3519_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer1: dual_timer@12001000 { ++ compatible = "arm,sp804"; ++ /* timer2 & timer3 */ ++ interrupts = <0 66 4>, <0 67 4>; ++ reg = <0x12001000 0x1000>; ++ clocks = <&clock HI3519_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer2: dual_timer@12002000 { ++ compatible = "arm,sp804"; ++ /* timer4 & timer5 */ ++ interrupts = <0 68 4>, <0 69 4>; ++ reg = <0x12002000 0x1000>; ++ clocks = <&clock HI3519_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ i2c_bus0: i2c@12110000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12110000 0x100>; ++ clocks = <&clock HI3519_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus1: i2c@12111000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12111000 0x100>; ++ clocks = <&clock HI3519_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus2: i2c@12112000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12112000 0x100>; ++ clocks = <&clock HI3519_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus3: i2c@12113000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12113000 0x100>; ++ clocks = <&clock HI3519_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ spi_bus0: spi@12120000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12120000 0x1000>; ++ interrupts = <0 9 4>; ++ clocks = <&clock HI3519_SPI0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi_bus1: spi@12121000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12121000 0x1000>; ++ interrupts = <0 10 4>; ++ clocks = <&clock HI3519_SPI1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi_bus2: spi@12122000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12122000 0x1000>, <0x12030004 0x4>; ++ interrupts = <0 11 4>; ++ clocks = <&clock HI3519_SPI2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <2>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ hisi,spi_cs_sb = <26>; ++ hisi,spi_cs_mask_bit = <0x0c000000>; ++ }; ++ ++ spi_bus3: spi@12123000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12123000 0x1000>; ++ interrupts = <0 12 4>; ++ clocks = <&clock HI3519_SPI3_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ gpio_chip0: gpio_chip@12140000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12140000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip1: gpio_chip@12141000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12141000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip2: gpio_chip@12142000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12142000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip3: gpio_chip@12143000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12143000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip4: gpio_chip@12144000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12144000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip5: gpio_chip@12145000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12145000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip6: gpio_chip@12146000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12146000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip7: gpio_chip@12147000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12147000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip8: gpio_chip@12148000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12148000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip9: gpio_chip@12149000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12149000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip10: gpio_chip@1214a000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214a000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip11: gpio_chip@1214b000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214b000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip12: gpio_chip@1214c000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214c000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip13: gpio_chip@1214d000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214d000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip14: gpio_chip@1214e000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214e000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip16: gpio_chip@12150000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12150000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3519_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ }; ++ ++ pmc@0x120a0000 { ++ compatible = "hisilicon,pmc"; ++ reg = <0x120a0000 0x1000>; ++ }; ++ ++ sysctrl: system-controller@00000000 { ++ compatible = "hisilicon,sysctrl"; ++ reg = <0x12020000 0x1000>; ++ reboot-offset = <0x4>; ++ }; ++ ++ usb_phy: phy { ++ compatible = "hisilicon,hisi-usb-phy"; ++ reg = <0x12030000 0x10000>, <0x12010000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ++ usb3_phy: phy3 { ++ compatible = "hisilicon,hisi-usb3-phy"; ++ reg = <0x10180000 0x10000>, <0x12010000 0x10000>, ++ <0x12030000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ehci@0x10120000 { ++ compatible = "generic-ehci"; ++ reg = <0x10120000 0x10000>; ++ interrupts = <0 19 4>; ++ ++ clocks = <&clock HI3519_USB2_CTRL_UTMI0_REQ>, ++ <&clock HI3519_USB2_HRST_REQ>; ++ clock-names = "usb2_cttl_utmi0_req", "usb2_hrst_req"; ++ }; ++ ++ ohci@0x10110000 { ++ compatible = "generic-ohci"; ++ reg = <0x10110000 0x10000>; ++ interrupts = <0 20 4>; ++ clocks = <&clock HI3519_USB2_CTRL_UTMI0_REQ>, ++ <&clock HI3519_USB2_HRST_REQ>; ++ clock-names = "usb2_cttl_utmi0_req", "usb2_hrst_req"; ++ }; ++ ++ xhci@0x10180000 { ++ compatible = "hisilicon,hi3519-xhci", "generic-xhci"; ++ reg = <0x10180000 0x10000>; ++ interrupts = <0 22 4>; ++ ++ clocks = <&clock HI3519_USB3_CLK>; ++ clock-names = "clk"; ++ }; ++ ++ hiudc@0x10130000 { ++ compatible = "hiudc"; ++ reg = <0x10130000 0x40000>; ++ interrupts = <0 21 4>; ++ ++ clocks = <&clock HI3519_USB2_HRST_REQ>; ++ clock-names = "clk"; ++ }; ++ ++ hiudc3@0x10180000 { ++ compatible = "dwc_usb3"; ++ reg = <0x10180000 0x40000>; ++ interrupts = <0 22 4>; ++ ++ clocks = <&clock HI3519_USB3_CLK>; ++ clock-names = "clk"; ++ }; ++ ++ cci: cci@1ff00000 { ++ compatible = "arm,cci-400"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0x1ff00000 0x1000>; ++ ranges = <0x0 0x1ff00000 0x6000>; ++ ++ cci_control0: slave-if@4000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x2000 0x1000>; ++ }; ++ ++ cci_control1: slave-if@5000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x3000 0x1000>; ++ }; ++ ++ }; ++ ++ regulators@120a0000 { ++ compatible = "hi35xx,regulators"; ++ reg = <0x120a0000 0x1000>; ++ regulator-num = <2>; ++ regulator-name-array = "regulator-a17","regulator-media"; ++ ++ a17_regulator: a17_regulator{ ++ regulator-name = "regulator-a17"; ++ regulator-min-microvolt = <651216>; ++ regulator-max-microvolt = <1083359>; ++ regulator-always-on; ++ reg_offset = <0x4>; ++ }; ++ ++ media_regulator: media_regulator{ ++ regulator-name = "regulator-media"; ++ regulator-min-microvolt = <649609>; ++ regulator-max-microvolt = <977031>; ++ regulator-always-on; ++ reg_offset = <0xC>; ++ }; ++ }; ++ ++ pmu { ++ compatible = "arm,cortex-a7-pmu", "arm,cortex-a17-pmu"; ++ interrupts = <0 45 4>, ++ <0 46 4>; ++ }; ++ ++ mdio: mdio@100503c0 { ++ compatible = "hisilicon,hisi-gemac-mdio"; ++ reg = <0x100503c0 0x20>; ++ clocks = <&clock HI3519_ETH_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ higmac: ethernet@10050000 { ++ compatible = "hisilicon,higmac"; ++ reg = <0x10050000 0x1000>,<0x120100ec 0x4>; ++ interrupts = <0 25 4>; ++ ++ clocks = <&clock HI3519_ETH_CLK>, ++ <&clock HI3519_ETH_MACIF_CLK>; ++ clock-names = "higmac_clk", ++ "macif_clk"; ++ ++ resets = <&clock 0xcc 0>, ++ <&clock 0xcc 2>, ++ <&clock 0xcc 7>; ++ reset-names = "port_reset", ++ "macif_reset", ++ "phy_reset"; ++ ++ mac-address = [00 00 00 00 00 00]; ++ }; ++ ++ mmc2: himciv200.MMC@0x100e0000 { ++ compatible = "hisilicon,hi3519-himciv200"; ++ reg = <0x100e0000 0x10000>; ++ interrupts = <0 13 4>; ++ ++ clocks = <&clock HI3519_MMC2_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <8>; ++ max-frequency = <148500000>; ++ cap-mmc-highspeed; ++ cap-mmc-hw-reset; ++ full-pwr-cycle; ++ mmc-hs400-1_8v; ++ devid = <2>; ++ status = "disabled"; ++ }; ++ ++ mmc0: himciv200.SD@0x100c0000 { ++ compatible = "hisilicon,hi3519-himciv200"; ++ reg = <0x100c0000 0x10000>; ++ interrupts = <0 23 4>; ++ ++ clocks = <&clock HI3519_MMC0_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <4>; ++ max-frequency = <148500000>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ devid = <0>; ++ status = "disabled"; ++ }; ++ ++ mmc1: himciv200.SD@0x100d0000 { ++ compatible = "hisilicon,hi3519-himciv200"; ++ reg = <0x100d0000 0x10000>; ++ interrupts = <0 24 4>; ++ ++ clocks = <&clock HI3519_MMC1_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <4>; ++ max-frequency = <148500000>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ devid = <1>; ++ status = "disabled"; ++ }; ++ ++ fmc: spi-nor-controller@10000000 { ++ compatible = "hisilicon,hisi-fmc"; ++ reg = <0x10000000 0x1000>, <0x14000000 0x1000000>; ++ reg-names = "control", "memory"; ++ clocks = <&clock HI3519_FMC_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hisfc:spi-nor@0 { ++ compatible = "hisilicon,hisi-sfc"; ++ assigned-clocks = <&clock HI3519_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hisnfc:spi-nand@0 { ++ compatible = "hisilicon,hisi-spi-nand"; ++ assigned-clocks = <&clock HI3519_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hinfc:nand@0 { ++ compatible = "hisilicon,hisi-nand"; ++ assigned-clocks = <&clock HI3519_FMC_CLK>; ++ assigned-clock-rates = <200000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ }; ++ ++}; +diff --git a/arch/arm/boot/dts/hisi-hi3556-demb.dts b/arch/arm/boot/dts/hisi-hi3556-demb.dts +new file mode 100644 +index 0000000..1e99e3e +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3556-demb.dts +@@ -0,0 +1,236 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++/dts-v1/; ++#include "hisi-hi3556.dtsi" ++ ++/ { ++ model = "Hisilicon HI3556 DEMO Board"; ++ compatible = "hisilicon,hi3556"; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ enable-method = "hisilicon,hi3556-smp"; ++ ++ cpu@0 { ++ compatible = "arm,cortex-a7"; ++ device_type = "cpu"; ++ clock-frequency = <HI3556_FIXED_792M>; ++ reg = <0>; ++ cci-control-port = <&cci_control0>; ++ }; ++ ++ /*cpu@100 { ++ compatible = "arm,cortex-a17"; ++ device_type = "cpu"; ++ clock-frequency = <HI3556_FIXED_1000M>; ++ reg = <0x100>; ++ cci-control-port = <&cci_control1>; ++ };*/ ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x80000000 0x40000000>; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&dual_timer0 { ++ status = "okay"; ++}; ++ ++&i2c_bus0 { ++ status = "disabled"; ++}; ++ ++&i2c_bus1 { ++ status = "disabled"; ++}; ++ ++&i2c_bus2 { ++ status = "disabled"; ++}; ++ ++&i2c_bus3 { ++ status = "okay"; ++}; ++ ++&spi_bus0 { ++ status = "disabled"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++ ++}; ++ ++&spi_bus1 { ++ status = "disabled"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++}; ++ ++&spi_bus2 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++ ++ spidev@1 { ++ compatible = "rohm,dh2228fv"; ++ reg = <1>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++}; ++ ++&spi_bus3 { ++ status = "disabled"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++}; ++ ++&hisfc { ++ hi_sfc { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ m25p,fast-read; ++ }; ++ ++}; ++ ++&hisnfc { ++ hinand { ++ compatible = "jedec,spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ }; ++}; ++ ++&hinfc { ++ hinand { ++ compatible = "jedec,nand"; ++ reg = <0>; ++ nand-max-frequency = <200000000>; ++ }; ++}; ++ ++&mmc0 { ++ status = "okay"; ++}; ++ ++&mmc1 { ++ status = "okay"; ++}; ++ ++&mmc2 { ++ status = "disabled"; ++}; ++ ++&gpio_chip0 { ++ status = "disabled"; ++}; ++ ++&gpio_chip1 { ++ status = "disabled"; ++}; ++ ++&gpio_chip2 { ++ status = "disabled"; ++}; ++ ++&gpio_chip3 { ++ status = "disabled"; ++}; ++ ++&gpio_chip4 { ++ status = "disabled"; ++}; ++ ++&gpio_chip5 { ++ status = "disabled"; ++}; ++ ++&gpio_chip6 { ++ status = "disabled"; ++}; ++ ++&gpio_chip7 { ++ status = "disabled"; ++}; ++ ++&gpio_chip8 { ++ status = "disabled"; ++}; ++ ++&gpio_chip9 { ++ status = "disabled"; ++}; ++ ++&gpio_chip10 { ++ status = "disabled"; ++}; ++ ++&gpio_chip11 { ++ status = "disabled"; ++}; ++ ++&gpio_chip12 { ++ status = "disabled"; ++}; ++ ++&gpio_chip13 { ++ status = "disabled"; ++}; ++ ++&gpio_chip14 { ++ status = "disabled"; ++}; ++ ++&gpio_chip16 { ++ status = "disabled"; ++}; +diff --git a/arch/arm/boot/dts/hisi-hi3556.dtsi b/arch/arm/boot/dts/hisi-hi3556.dtsi +new file mode 100644 +index 0000000..4a92aa6 +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3556.dtsi +@@ -0,0 +1,755 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/hi3556-clock.h> ++/ { ++ aliases { ++ serial0 = &uart0; ++ i2c0 = &i2c_bus0; ++ i2c1 = &i2c_bus1; ++ i2c2 = &i2c_bus2; ++ i2c3 = &i2c_bus3; ++ spi0 = &spi_bus0; ++ spi1 = &spi_bus1; ++ spi2 = &spi_bus2; ++ spi3 = &spi_bus3; ++ gpio0 = &gpio_chip0; ++ gpio1 = &gpio_chip1; ++ gpio2 = &gpio_chip2; ++ gpio3 = &gpio_chip3; ++ gpio4 = &gpio_chip4; ++ gpio5 = &gpio_chip5; ++ gpio6 = &gpio_chip6; ++ gpio7 = &gpio_chip7; ++ gpio8 = &gpio_chip8; ++ gpio9 = &gpio_chip9; ++ gpio10 = &gpio_chip10; ++ gpio11 = &gpio_chip11; ++ gpio12 = &gpio_chip12; ++ gpio13 = &gpio_chip13; ++ gpio14 = &gpio_chip14; ++ gpio16 = &gpio_chip16; ++ }; ++ ++ gic: interrupt-controller@10300000 { ++ compatible = "arm,cortex-a7-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ /* gic dist base, gic cpu base , no virtual support */ ++ reg = <0x10301000 0x1000>, <0x10302000 0x1000>; ++ }; ++ ++ clock: clock0 { ++ compatible = "hisilicon,hi3556-clock"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ #clock-cells = <1>; ++ #reset-cells = <2>; ++ reg = <0x12010000 0x10000>; ++ }; ++ ++ syscounter { ++ compatible = "arm,armv7-timer"; ++ interrupt-parent = <&gic>; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>; ++ clock-frequency = <24000000>; ++ }; ++ ++ soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&gic>; ++ ranges; ++ ++ amba { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "arm,amba-bus"; ++ ranges; ++ ++ uart0: uart@12100000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12100000 0x1000>; ++ interrupts = <0 4 4>; ++ clocks = <&clock HI3556_UART0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart1: uart@12101000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12101000 0x1000>; ++ interrupts = <0 5 4>; ++ clocks = <&clock HI3556_UART1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart2: uart@12102000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12102000 0x1000>; ++ interrupts = <0 6 4>; ++ clocks = <&clock HI3556_UART2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart3: uart@12103000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12103000 0x1000>; ++ interrupts = <0 7 4>; ++ clocks = <&clock HI3556_UART3_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart4: uart@12104000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12104000 0x1000>; ++ interrupts = <0 8 4>; ++ clocks = <&clock HI3556_UART4_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ sys: sys@12010000 { ++ compatible = "hisilicon,hi35xx_sys"; ++ reg = <0x12020000 0x10000>, <0x12060000 0x10000>, <0X12030000 0x10000>; ++ reg-names = "sys", "ddr", "misc"; ++ }; ++ ++ mipi: mipi@11300000 { ++ compatible = "hisilicon,hi35xx_mipi"; ++ interrupts = <0 28 4>, <0 29 4>; ++ interrupt-names = "mipi0", "mipi1"; ++ reg = <0x11300000 0x10000>; ++ }; ++ ++ isp: isp@11380000 { ++ compatible = "hisilicon,hi35xx_isp"; ++ interrupts = <0 30 4>, <0 31 4>; ++ interrupt-names = "isp0", "isp1"; ++ }; ++ ++ viu: viu@11380000 { ++ compatible = "hisilicon,hi35xx_viu"; ++ interrupts = <0 30 4>, <0 31 4>; ++ interrupt-names = "viu0", "viu1"; ++ reg = <0x11380000 0x70000>, <0x11480000 0x70000>; ++ reg-names = "viu0", "viu1"; ++ }; ++ ++ vou: vou@11000000 { ++ compatible = "hisilicon,hi35xx_vou"; ++ interrupts = <0 27 4>; ++ reg = <0x11000000 0x20000>; ++ }; ++ ++ tde: tde@11100000 { ++ compatible = "hisilicon,hi35xx_tde"; ++ interrupts = <0 34 4>; ++ reg = <0x11100000 0x10000>; ++ }; ++ ++ fisheye: fisheye@11110000 { ++ compatible = "hisilicon,hi35xx_fisheye"; ++ interrupts = <0 48 4>; ++ reg = <0x11110000 0x10000>; ++ }; ++ ++ vgs: vgs@11120000 { ++ compatible = "hisilicon,hi35xx_vgs"; ++ interrupts = <0 35 4>; ++ reg = <0x11120000 0x10000>; ++ }; ++ ++ vpss: vpss@11180000 { ++ compatible = "hisilicon,hi35xx_vpss"; ++ interrupts = <0 32 4>; ++ reg = <0x11180000 0x10000>; ++ }; ++ ++ vedu: vedu@11280000 { ++ compatible = "hisilicon,hi35xx_vedu"; ++ interrupts = <0 37 4>; ++ reg = <0x11280000 0x10000>; ++ }; ++ ++ jpege: jpege@11200000 { ++ compatible = "hisilicon,hi35xx_jpege"; ++ interrupts = <0 38 4>; ++ reg = <0x11200000 0x10000>; ++ }; ++ ++ aiao: aiao@11080000 { ++ compatible = "hisilicon,hi35xx_aiao"; ++ interrupts = <0 36 4>; ++ reg = <0x11080000 0x3000>; ++ }; ++ ++ ive: ive@11040000 { ++ compatible = "hisilicon,hi35xx_ive"; ++ interrupts = <0 39 4>; ++ reg = <0x11040000 0x10000>; ++ }; ++ ++ fd: fd@11060000 { ++ compatible = "hisilicon,hi35xx_fd"; ++ interrupts = <0 40 4>; ++ reg = <0x11060000 0x10000>; ++ }; ++ ++ pwm: pwm@12130000 { ++ compatible = "hisilicon,pwm"; ++ reg = <0x12130000 0x10000>; ++ }; ++ ++ piris: piris@12145000 { ++ compatible = "hisilicon,piris"; ++ reg = <0x12145000 0x1000>; ++ }; ++ ++ wtdg: wtdg@12080000 { ++ compatible = "hisilicon,hi_wdg"; ++ reg = <0x12080000 0x10000>; ++ reg-names = "wtdg"; ++ }; ++ ++ rtc: rtc@12090000 { ++ compatible = "hisilicon,hi_rtc"; ++ interrupts = <0 1 4>; ++ reg = <0x12090000 0x8000>; ++ }; ++ ++ pwr: wtdg@12098000 { ++ compatible = "hisilicon,hi_pwr"; ++ reg = <0x12098000 0x8000>; ++ reg-names = "pwr"; ++ }; ++ ++ ir: ir@120f0000{ ++ compatible = "hisilicon,hi_ir"; ++ interrupts = <0 15 4>; ++ reg = <0x120f0000 0x10000>; ++ }; ++ ++ pm { ++ compatible = "hisilicon,hibvt-pm"; ++ reg = <0x12020000 0x1000>, <0x12000000 0x1000>; ++ }; ++ ++ pm_hibernate { ++ compatible = "hisilicon,hibvt-pm-hibernate"; ++ reg = <0x12020000 0x1000>; ++ }; ++ ++ dual_timer0: dual_timer@12000000 { ++ compatible = "arm,sp804"; ++ /* timer0 & timer1 */ ++ interrupts = <0 64 4>, <0 65 4>; ++ reg = <0x12000000 0x1000>; ++ clocks = <&clock HI3556_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer1: dual_timer@12001000 { ++ compatible = "arm,sp804"; ++ /* timer2 & timer3 */ ++ interrupts = <0 66 4>, <0 67 4>; ++ reg = <0x12001000 0x1000>; ++ clocks = <&clock HI3556_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer2: dual_timer@12002000 { ++ compatible = "arm,sp804"; ++ /* timer4 & timer5 */ ++ interrupts = <0 68 4>, <0 69 4>; ++ reg = <0x12002000 0x1000>; ++ clocks = <&clock HI3556_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ i2c_bus0: i2c@12110000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12110000 0x100>; ++ clocks = <&clock HI3556_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus1: i2c@12111000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12111000 0x100>; ++ clocks = <&clock HI3556_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus2: i2c@12112000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12112000 0x100>; ++ clocks = <&clock HI3556_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus3: i2c@12113000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12113000 0x100>; ++ clocks = <&clock HI3556_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ spi_bus0: spi@12120000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12120000 0x1000>; ++ interrupts = <0 9 4>; ++ clocks = <&clock HI3556_SPI0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi_bus1: spi@12121000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12121000 0x1000>; ++ interrupts = <0 10 4>; ++ clocks = <&clock HI3556_SPI1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi_bus2: spi@12122000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12122000 0x1000>, <0x12030004 0x4>; ++ interrupts = <0 11 4>; ++ clocks = <&clock HI3556_SPI2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <2>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ hisi,spi_cs_sb = <26>; ++ hisi,spi_cs_mask_bit = <0x0c000000>; ++ }; ++ ++ spi_bus3: spi@12123000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12123000 0x1000>; ++ interrupts = <0 12 4>; ++ clocks = <&clock HI3556_SPI3_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ gpio_chip0: gpio_chip@12140000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12140000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip1: gpio_chip@12141000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12141000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip2: gpio_chip@12142000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12142000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip3: gpio_chip@12143000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12143000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip4: gpio_chip@12144000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12144000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip5: gpio_chip@12145000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12145000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip6: gpio_chip@12146000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12146000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip7: gpio_chip@12147000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12147000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip8: gpio_chip@12148000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12148000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip9: gpio_chip@12149000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12149000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip10: gpio_chip@1214a000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214a000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip11: gpio_chip@1214b000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214b000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip12: gpio_chip@1214c000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214c000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip13: gpio_chip@1214d000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214d000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip14: gpio_chip@1214e000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214e000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip16: gpio_chip@12150000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12150000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3556_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ }; ++ ++ pmc@0x120a0000 { ++ compatible = "hisilicon,pmc"; ++ reg = <0x120a0000 0x1000>; ++ }; ++ ++ sysctrl: system-controller@00000000 { ++ compatible = "hisilicon,sysctrl"; ++ reg = <0x12020000 0x1000>; ++ reboot-offset = <0x4>; ++ }; ++ ++ usb_phy: phy { ++ compatible = "hisilicon,hisi-usb-phy"; ++ reg = <0x12030000 0x10000>, <0x12010000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ++ usb3_phy: phy3 { ++ compatible = "hisilicon,hisi-usb3-phy"; ++ reg = <0x10180000 0x10000>, <0x12010000 0x10000>, ++ <0x12030000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ehci@0x10120000 { ++ compatible = "generic-ehci"; ++ reg = <0x10120000 0x10000>; ++ interrupts = <0 19 4>; ++ ++ clocks = <&clock HI3556_USB2_CTRL_UTMI0_REQ>, ++ <&clock HI3556_USB2_HRST_REQ>; ++ clock-names = "usb2_cttl_utmi0_req", "usb2_hrst_req"; ++ }; ++ ++ ohci@0x10110000 { ++ compatible = "generic-ohci"; ++ reg = <0x10110000 0x10000>; ++ interrupts = <0 20 4>; ++ ++ clocks = <&clock HI3556_USB2_CTRL_UTMI0_REQ>, ++ <&clock HI3556_USB2_HRST_REQ>; ++ clock-names = "usb2_cttl_utmi0_req", "usb2_hrst_req"; ++ }; ++ ++ xhci@0x10180000 { ++ compatible = "hisilicon,hi3556-xhci", "generic-xhci"; ++ reg = <0x10180000 0x10000>; ++ interrupts = <0 22 4>; ++ ++ clocks = <&clock HI3556_USB3_CLK>; ++ clock-names = "clk"; ++ }; ++ ++ hiudc@0x10130000 { ++ compatible = "hiudc"; ++ reg = <0x10130000 0x40000>; ++ interrupts = <0 21 4>; ++ ++ clocks = <&clock HI3556_USB2_HRST_REQ>; ++ clock-names = "clk"; ++ }; ++ ++ hiudc3@0x10180000 { ++ compatible = "dwc_usb3"; ++ reg = <0x10180000 0x40000>; ++ interrupts = <0 22 4>; ++ ++ clocks = <&clock HI3556_USB3_CLK>; ++ clock-names = "clk"; ++ }; ++ ++ cci: cci@1ff00000 { ++ compatible = "arm,cci-400"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0x1ff00000 0x1000>; ++ ranges = <0x0 0x1ff00000 0x6000>; ++ ++ cci_control0: slave-if@4000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x2000 0x1000>; ++ }; ++ ++ cci_control1: slave-if@5000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x3000 0x1000>; ++ }; ++ ++ }; ++ ++ regulators@120a0000 { ++ compatible = "hi3556,regulators"; ++ reg = <0x120a0000 0x1000>; ++ regulator-num = <2>; ++ regulator-name-array = "regulator-a17","regulator-media"; ++ ++ a17_regulator: a17_regulator{ ++ regulator-name = "regulator-a17"; ++ regulator-min-microvolt = <648633>; ++ regulator-max-microvolt = <1200525>; ++ regulator-always-on; ++ reg_offset = <0x4>; ++ }; ++ ++ media_regulator: media_regulator{ ++ regulator-name = "regulator-media"; ++ regulator-min-microvolt = <649740>; ++ regulator-max-microvolt = <1204331>; ++ regulator-always-on; ++ reg_offset = <0xC>; ++ }; ++ }; ++ ++ pmu { ++ compatible = "arm,cortex-a7-pmu", "arm,cortex-a17-pmu"; ++ interrupts = <0 45 4>, ++ <0 46 4>; ++ }; ++ ++ mmc2: himciv200.MMC@0x100e0000 { ++ compatible = "hisilicon,hi3556-himciv200"; ++ reg = <0x100e0000 0x10000>; ++ interrupts = <0 13 4>; ++ ++ clocks = <&clock HI3556_MMC2_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <8>; ++ max-frequency = <148500000>; ++ cap-mmc-highspeed; ++ cap-mmc-hw-reset; ++ full-pwr-cycle; ++ mmc-hs400-1_8v; ++ devid = <2>; ++ status = "disabled"; ++ }; ++ ++ mmc0: himciv200.SD@0x100c0000 { ++ compatible = "hisilicon,hi3556-himciv200"; ++ reg = <0x100c0000 0x10000>; ++ interrupts = <0 23 4>; ++ ++ clocks = <&clock HI3556_MMC0_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <4>; ++ max-frequency = <148500000>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ devid = <0>; ++ status = "disabled"; ++ }; ++ ++ mmc1: himciv200.SD@0x100d0000 { ++ compatible = "hisilicon,hi3556-himciv200"; ++ reg = <0x100d0000 0x10000>; ++ interrupts = <0 24 4>; ++ ++ clocks = <&clock HI3556_MMC1_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <4>; ++ max-frequency = <148500000>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ devid = <1>; ++ status = "disabled"; ++ }; ++ ++ fmc: spi-nor-controller@10000000 { ++ compatible = "hisilicon,hisi-fmc"; ++ reg = <0x10000000 0x1000>, <0x14000000 0x1000000>; ++ reg-names = "control", "memory"; ++ clocks = <&clock HI3556_FMC_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hisfc:spi-nor@0 { ++ compatible = "hisilicon,hisi-sfc"; ++ assigned-clocks = <&clock HI3556_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hisnfc:spi-nand@0 { ++ compatible = "hisilicon,hisi-spi-nand"; ++ assigned-clocks = <&clock HI3556_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hinfc:nand@0 { ++ compatible = "hisilicon,hisi-nand"; ++ assigned-clocks = <&clock HI3556_FMC_CLK>; ++ assigned-clock-rates = <200000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ }; ++ ++}; +diff --git a/arch/arm/boot/dts/hisi-hi3559-demb.dts b/arch/arm/boot/dts/hisi-hi3559-demb.dts +new file mode 100644 +index 0000000..0fa7209 +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3559-demb.dts +@@ -0,0 +1,236 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++/dts-v1/; ++#include "hisi-hi3559.dtsi" ++ ++/ { ++ model = "Hisilicon HI3559 DEMO Board"; ++ compatible = "hisilicon,hi3559"; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ enable-method = "hisilicon,hi3559-smp"; ++ ++ cpu@0 { ++ compatible = "arm,cortex-a7"; ++ device_type = "cpu"; ++ clock-frequency = <HI3559_FIXED_792M>; ++ reg = <0>; ++ cci-control-port = <&cci_control0>; ++ }; ++ ++ /*cpu@100 { ++ compatible = "arm,cortex-a17"; ++ device_type = "cpu"; ++ clock-frequency = <HI3559_FIXED_1000M>; ++ reg = <0x100>; ++ cci-control-port = <&cci_control1>; ++ };*/ ++ }; ++ ++ memory { ++ device_type = "memory"; ++ reg = <0x80000000 0x40000000>; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&dual_timer0 { ++ status = "okay"; ++}; ++ ++&i2c_bus0 { ++ status = "disabled"; ++}; ++ ++&i2c_bus1 { ++ status = "disabled"; ++}; ++ ++&i2c_bus2 { ++ status = "disabled"; ++}; ++ ++&i2c_bus3 { ++ status = "okay"; ++}; ++ ++&spi_bus0 { ++ status = "disabled"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++ ++}; ++ ++&spi_bus1 { ++ status = "disabled"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++}; ++ ++&spi_bus2 { ++ status = "okay"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++ ++ spidev@1 { ++ compatible = "rohm,dh2228fv"; ++ reg = <1>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++}; ++ ++&spi_bus3 { ++ status = "disabled"; ++ ++ spidev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,interface = <0>; ++ pl022,com-mode = <0>; ++ spi-max-frequency = <24750000>; ++ }; ++}; ++ ++&hisfc { ++ hi_sfc { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ m25p,fast-read; ++ }; ++ ++}; ++ ++&hisnfc { ++ hinand { ++ compatible = "jedec,spi-nand"; ++ reg = <0>; ++ spi-max-frequency = <160000000>; ++ }; ++}; ++ ++&hinfc { ++ hinand { ++ compatible = "jedec,nand"; ++ reg = <0>; ++ nand-max-frequency = <200000000>; ++ }; ++}; ++ ++&mmc0 { ++ status = "okay"; ++}; ++ ++&mmc1 { ++ status = "okay"; ++}; ++ ++&mmc2 { ++ status = "disabled"; ++}; ++ ++&gpio_chip0 { ++ status = "disabled"; ++}; ++ ++&gpio_chip1 { ++ status = "disabled"; ++}; ++ ++&gpio_chip2 { ++ status = "disabled"; ++}; ++ ++&gpio_chip3 { ++ status = "disabled"; ++}; ++ ++&gpio_chip4 { ++ status = "disabled"; ++}; ++ ++&gpio_chip5 { ++ status = "disabled"; ++}; ++ ++&gpio_chip6 { ++ status = "disabled"; ++}; ++ ++&gpio_chip7 { ++ status = "disabled"; ++}; ++ ++&gpio_chip8 { ++ status = "disabled"; ++}; ++ ++&gpio_chip9 { ++ status = "disabled"; ++}; ++ ++&gpio_chip10 { ++ status = "disabled"; ++}; ++ ++&gpio_chip11 { ++ status = "disabled"; ++}; ++ ++&gpio_chip12 { ++ status = "disabled"; ++}; ++ ++&gpio_chip13 { ++ status = "disabled"; ++}; ++ ++&gpio_chip14 { ++ status = "disabled"; ++}; ++ ++&gpio_chip16 { ++ status = "disabled"; ++}; +diff --git a/arch/arm/boot/dts/hisi-hi3559.dtsi b/arch/arm/boot/dts/hisi-hi3559.dtsi +new file mode 100644 +index 0000000..58b6d8c +--- /dev/null ++++ b/arch/arm/boot/dts/hisi-hi3559.dtsi +@@ -0,0 +1,755 @@ ++/* ++ * Copyright (c) 2013-2014 Linaro Ltd. ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "skeleton.dtsi" ++#include <dt-bindings/clock/hi3559-clock.h> ++/ { ++ aliases { ++ serial0 = &uart0; ++ i2c0 = &i2c_bus0; ++ i2c1 = &i2c_bus1; ++ i2c2 = &i2c_bus2; ++ i2c3 = &i2c_bus3; ++ spi0 = &spi_bus0; ++ spi1 = &spi_bus1; ++ spi2 = &spi_bus2; ++ spi3 = &spi_bus3; ++ gpio0 = &gpio_chip0; ++ gpio1 = &gpio_chip1; ++ gpio2 = &gpio_chip2; ++ gpio3 = &gpio_chip3; ++ gpio4 = &gpio_chip4; ++ gpio5 = &gpio_chip5; ++ gpio6 = &gpio_chip6; ++ gpio7 = &gpio_chip7; ++ gpio8 = &gpio_chip8; ++ gpio9 = &gpio_chip9; ++ gpio10 = &gpio_chip10; ++ gpio11 = &gpio_chip11; ++ gpio12 = &gpio_chip12; ++ gpio13 = &gpio_chip13; ++ gpio14 = &gpio_chip14; ++ gpio16 = &gpio_chip16; ++ }; ++ ++ gic: interrupt-controller@10300000 { ++ compatible = "arm,cortex-a7-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ /* gic dist base, gic cpu base , no virtual support */ ++ reg = <0x10301000 0x1000>, <0x10302000 0x1000>; ++ }; ++ ++ clock: clock0 { ++ compatible = "hisilicon,hi3559-clock"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ #clock-cells = <1>; ++ #reset-cells = <2>; ++ reg = <0x12010000 0x10000>; ++ }; ++ ++ syscounter { ++ compatible = "arm,armv7-timer"; ++ interrupt-parent = <&gic>; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>; ++ clock-frequency = <24000000>; ++ }; ++ ++ soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "simple-bus"; ++ interrupt-parent = <&gic>; ++ ranges; ++ ++ amba { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "arm,amba-bus"; ++ ranges; ++ ++ uart0: uart@12100000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12100000 0x1000>; ++ interrupts = <0 4 4>; ++ clocks = <&clock HI3559_UART0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart1: uart@12101000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12101000 0x1000>; ++ interrupts = <0 5 4>; ++ clocks = <&clock HI3559_UART1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart2: uart@12102000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12102000 0x1000>; ++ interrupts = <0 6 4>; ++ clocks = <&clock HI3559_UART2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart3: uart@12103000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12103000 0x1000>; ++ interrupts = <0 7 4>; ++ clocks = <&clock HI3559_UART3_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ uart4: uart@12104000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x12104000 0x1000>; ++ interrupts = <0 8 4>; ++ clocks = <&clock HI3559_UART4_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ sys: sys@12010000 { ++ compatible = "hisilicon,hi35xx_sys"; ++ reg = <0x12020000 0x10000>, <0x12060000 0x10000>, <0X12030000 0x10000>; ++ reg-names = "sys", "ddr", "misc"; ++ }; ++ ++ mipi: mipi@11300000 { ++ compatible = "hisilicon,hi35xx_mipi"; ++ interrupts = <0 28 4>, <0 29 4>; ++ interrupt-names = "mipi0", "mipi1"; ++ reg = <0x11300000 0x10000>; ++ }; ++ ++ isp: isp@11380000 { ++ compatible = "hisilicon,hi35xx_isp"; ++ interrupts = <0 30 4>, <0 31 4>; ++ interrupt-names = "isp0", "isp1"; ++ }; ++ ++ viu: viu@11380000 { ++ compatible = "hisilicon,hi35xx_viu"; ++ interrupts = <0 30 4>, <0 31 4>; ++ interrupt-names = "viu0", "viu1"; ++ reg = <0x11380000 0x70000>, <0x11480000 0x70000>; ++ reg-names = "viu0", "viu1"; ++ }; ++ ++ vou: vou@11000000 { ++ compatible = "hisilicon,hi35xx_vou"; ++ interrupts = <0 27 4>; ++ reg = <0x11000000 0x20000>; ++ }; ++ ++ tde: tde@11100000 { ++ compatible = "hisilicon,hi35xx_tde"; ++ interrupts = <0 34 4>; ++ reg = <0x11100000 0x10000>; ++ }; ++ ++ fisheye: fisheye@11110000 { ++ compatible = "hisilicon,hi35xx_fisheye"; ++ interrupts = <0 48 4>; ++ reg = <0x11110000 0x10000>; ++ }; ++ ++ vgs: vgs@11120000 { ++ compatible = "hisilicon,hi35xx_vgs"; ++ interrupts = <0 35 4>; ++ reg = <0x11120000 0x10000>; ++ }; ++ ++ vpss: vpss@11180000 { ++ compatible = "hisilicon,hi35xx_vpss"; ++ interrupts = <0 32 4>; ++ reg = <0x11180000 0x10000>; ++ }; ++ ++ vedu: vedu@11280000 { ++ compatible = "hisilicon,hi35xx_vedu"; ++ interrupts = <0 37 4>; ++ reg = <0x11280000 0x10000>; ++ }; ++ ++ jpege: jpege@11200000 { ++ compatible = "hisilicon,hi35xx_jpege"; ++ interrupts = <0 38 4>; ++ reg = <0x11200000 0x10000>; ++ }; ++ ++ aiao: aiao@11080000 { ++ compatible = "hisilicon,hi35xx_aiao"; ++ interrupts = <0 36 4>; ++ reg = <0x11080000 0x3000>; ++ }; ++ ++ ive: ive@11040000 { ++ compatible = "hisilicon,hi35xx_ive"; ++ interrupts = <0 39 4>; ++ reg = <0x11040000 0x10000>; ++ }; ++ ++ fd: fd@11060000 { ++ compatible = "hisilicon,hi35xx_fd"; ++ interrupts = <0 40 4>; ++ reg = <0x11060000 0x10000>; ++ }; ++ ++ pwm: pwm@12130000 { ++ compatible = "hisilicon,pwm"; ++ reg = <0x12130000 0x10000>; ++ }; ++ ++ piris: piris@12145000 { ++ compatible = "hisilicon,piris"; ++ reg = <0x12145000 0x1000>; ++ }; ++ ++ wtdg: wtdg@12080000 { ++ compatible = "hisilicon,hi_wdg"; ++ reg = <0x12080000 0x10000>; ++ reg-names = "wtdg"; ++ }; ++ ++ rtc: rtc@12090000 { ++ compatible = "hisilicon,hi_rtc"; ++ interrupts = <0 1 4>; ++ reg = <0x12090000 0x8000>; ++ }; ++ ++ pwr: wtdg@12098000 { ++ compatible = "hisilicon,hi_pwr"; ++ reg = <0x12098000 0x8000>; ++ reg-names = "pwr"; ++ }; ++ ++ ir: ir@120f0000{ ++ compatible = "hisilicon,hi_ir"; ++ interrupts = <0 15 4>; ++ reg = <0x120f0000 0x10000>; ++ }; ++ ++ pm { ++ compatible = "hisilicon,hibvt-pm"; ++ reg = <0x12020000 0x1000>, <0x12000000 0x1000>; ++ }; ++ ++ pm_hibernate { ++ compatible = "hisilicon,hibvt-pm-hibernate"; ++ reg = <0x12020000 0x1000>; ++ }; ++ ++ dual_timer0: dual_timer@12000000 { ++ compatible = "arm,sp804"; ++ /* timer0 & timer1 */ ++ interrupts = <0 64 4>, <0 65 4>; ++ reg = <0x12000000 0x1000>; ++ clocks = <&clock HI3559_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer1: dual_timer@12001000 { ++ compatible = "arm,sp804"; ++ /* timer2 & timer3 */ ++ interrupts = <0 66 4>, <0 67 4>; ++ reg = <0x12001000 0x1000>; ++ clocks = <&clock HI3559_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ dual_timer2: dual_timer@12002000 { ++ compatible = "arm,sp804"; ++ /* timer4 & timer5 */ ++ interrupts = <0 68 4>, <0 69 4>; ++ reg = <0x12002000 0x1000>; ++ clocks = <&clock HI3559_FIXED_3M>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ }; ++ ++ i2c_bus0: i2c@12110000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12110000 0x100>; ++ clocks = <&clock HI3559_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus1: i2c@12111000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12111000 0x100>; ++ clocks = <&clock HI3559_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus2: i2c@12112000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12112000 0x100>; ++ clocks = <&clock HI3559_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ i2c_bus3: i2c@12113000 { ++ compatible = "hisilicon,hisi-i2c-v110"; ++ reg = <0x12113000 0x100>; ++ clocks = <&clock HI3559_I2C_MUX>; ++ clock-frequency = <100000>; ++ status = "disabled"; ++ }; ++ ++ spi_bus0: spi@12120000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12120000 0x1000>; ++ interrupts = <0 9 4>; ++ clocks = <&clock HI3559_SPI0_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi_bus1: spi@12121000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12121000 0x1000>; ++ interrupts = <0 10 4>; ++ clocks = <&clock HI3559_SPI1_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ spi_bus2: spi@12122000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12122000 0x1000>, <0x12030004 0x4>; ++ interrupts = <0 11 4>; ++ clocks = <&clock HI3559_SPI2_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <2>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ hisi,spi_cs_sb = <26>; ++ hisi,spi_cs_mask_bit = <0x0c000000>; ++ }; ++ ++ spi_bus3: spi@12123000 { ++ compatible = "arm,pl022", "arm,primecell"; ++ arm,primecell-periphid = <0x00800022>; ++ reg = <0x12123000 0x1000>; ++ interrupts = <0 12 4>; ++ clocks = <&clock HI3559_SPI3_CLK>; ++ clock-names = "apb_pclk"; ++ status = "disabled"; ++ num-cs = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ gpio_chip0: gpio_chip@12140000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12140000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip1: gpio_chip@12141000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12141000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip2: gpio_chip@12142000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12142000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip3: gpio_chip@12143000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12143000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip4: gpio_chip@12144000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12144000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip5: gpio_chip@12145000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12145000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip6: gpio_chip@12146000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12146000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip7: gpio_chip@12147000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12147000 0x1000>; ++ interrupts = <0 43 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip8: gpio_chip@12148000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12148000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip9: gpio_chip@12149000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12149000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip10: gpio_chip@1214a000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214a000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip11: gpio_chip@1214b000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214b000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip12: gpio_chip@1214c000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214c000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip13: gpio_chip@1214d000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214d000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip14: gpio_chip@1214e000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x1214e000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ ++ gpio_chip16: gpio_chip@12150000 { ++ compatible = "arm,pl061", "arm,primecell"; ++ reg = <0x12150000 0x1000>; ++ interrupts = <0 44 4>; ++ clocks = <&clock HI3559_SYSAPB_CLK>; ++ clock-names = "apb_pclk"; ++ #gpio-cells = <2>; ++ status = "disabled"; ++ }; ++ }; ++ ++ pmc@0x120a0000 { ++ compatible = "hisilicon,pmc"; ++ reg = <0x120a0000 0x1000>; ++ }; ++ ++ sysctrl: system-controller@00000000 { ++ compatible = "hisilicon,sysctrl"; ++ reg = <0x12020000 0x1000>; ++ reboot-offset = <0x4>; ++ }; ++ ++ usb_phy: phy { ++ compatible = "hisilicon,hisi-usb-phy"; ++ reg = <0x12030000 0x10000>, <0x12010000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ++ usb3_phy: phy3 { ++ compatible = "hisilicon,hisi-usb3-phy"; ++ reg = <0x10180000 0x10000>, <0x12010000 0x10000>, ++ <0x12030000 0x10000>; ++ #phy-cells = <0>; ++ }; ++ ehci@0x10120000 { ++ compatible = "generic-ehci"; ++ reg = <0x10120000 0x10000>; ++ interrupts = <0 19 4>; ++ ++ clocks = <&clock HI3559_USB2_CTRL_UTMI0_REQ>, ++ <&clock HI3559_USB2_HRST_REQ>; ++ clock-names = "usb2_cttl_utmi0_req", "usb2_hrst_req"; ++ }; ++ ++ ohci@0x10110000 { ++ compatible = "generic-ohci"; ++ reg = <0x10110000 0x10000>; ++ interrupts = <0 20 4>; ++ ++ clocks = <&clock HI3559_USB2_CTRL_UTMI0_REQ>, ++ <&clock HI3559_USB2_HRST_REQ>; ++ clock-names = "usb2_cttl_utmi0_req", "usb2_hrst_req"; ++ }; ++ ++ xhci@0x10180000 { ++ compatible = "hisilicon,hi3559-xhci", "generic-xhci"; ++ reg = <0x10180000 0x10000>; ++ interrupts = <0 22 4>; ++ ++ clocks = <&clock HI3559_USB3_CLK>; ++ clock-names = "clk"; ++ }; ++ ++ hiudc@0x10130000 { ++ compatible = "hiudc"; ++ reg = <0x10130000 0x40000>; ++ interrupts = <0 21 4>; ++ ++ clocks = <&clock HI3559_USB2_HRST_REQ>; ++ clock-names = "clk"; ++ }; ++ ++ hiudc3@0x10180000 { ++ compatible = "dwc_usb3"; ++ reg = <0x10180000 0x40000>; ++ interrupts = <0 22 4>; ++ ++ clocks = <&clock HI3559_USB3_CLK>; ++ clock-names = "clk"; ++ }; ++ ++ cci: cci@1ff00000 { ++ compatible = "arm,cci-400"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ reg = <0x1ff00000 0x1000>; ++ ranges = <0x0 0x1ff00000 0x6000>; ++ ++ cci_control0: slave-if@4000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x2000 0x1000>; ++ }; ++ ++ cci_control1: slave-if@5000 { ++ compatible = "arm,cci-400-ctrl-if"; ++ interface-type = "ace"; ++ reg = <0x3000 0x1000>; ++ }; ++ ++ }; ++ ++ regulators@120a0000 { ++ compatible = "hi3559,regulators"; ++ reg = <0x120a0000 0x1000>; ++ regulator-num = <2>; ++ regulator-name-array = "regulator-a17","regulator-media"; ++ ++ a17_regulator: a17_regulator{ ++ regulator-name = "regulator-a17"; ++ regulator-min-microvolt = <648633>; ++ regulator-max-microvolt = <1200525>; ++ regulator-always-on; ++ reg_offset = <0x4>; ++ }; ++ ++ media_regulator: media_regulator{ ++ regulator-name = "regulator-media"; ++ regulator-min-microvolt = <649740>; ++ regulator-max-microvolt = <1204331>; ++ regulator-always-on; ++ reg_offset = <0xC>; ++ }; ++ }; ++ ++ pmu { ++ compatible = "arm,cortex-a7-pmu", "arm,cortex-a17-pmu"; ++ interrupts = <0 45 4>, ++ <0 46 4>; ++ }; ++ ++ mmc2: himciv200.MMC@0x100e0000 { ++ compatible = "hisilicon,hi3559-himciv200"; ++ reg = <0x100e0000 0x10000>; ++ interrupts = <0 13 4>; ++ ++ clocks = <&clock HI3559_MMC2_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <8>; ++ max-frequency = <148500000>; ++ cap-mmc-highspeed; ++ cap-mmc-hw-reset; ++ full-pwr-cycle; ++ mmc-hs400-1_8v; ++ devid = <2>; ++ status = "disabled"; ++ }; ++ ++ mmc0: himciv200.SD@0x100c0000 { ++ compatible = "hisilicon,hi3559-himciv200"; ++ reg = <0x100c0000 0x10000>; ++ interrupts = <0 23 4>; ++ ++ clocks = <&clock HI3559_MMC0_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <4>; ++ max-frequency = <148500000>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ devid = <0>; ++ status = "disabled"; ++ }; ++ ++ mmc1: himciv200.SD@0x100d0000 { ++ compatible = "hisilicon,hi3559-himciv200"; ++ reg = <0x100d0000 0x10000>; ++ interrupts = <0 24 4>; ++ ++ clocks = <&clock HI3559_MMC1_CLK>; ++ clock-names = "mmc_clk"; ++ ++ bus-width = <4>; ++ max-frequency = <148500000>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ devid = <1>; ++ status = "disabled"; ++ }; ++ ++ fmc: spi-nor-controller@10000000 { ++ compatible = "hisilicon,hisi-fmc"; ++ reg = <0x10000000 0x1000>, <0x14000000 0x1000000>; ++ reg-names = "control", "memory"; ++ clocks = <&clock HI3559_FMC_CLK>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hisfc:spi-nor@0 { ++ compatible = "hisilicon,hisi-sfc"; ++ assigned-clocks = <&clock HI3559_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hisnfc:spi-nand@0 { ++ compatible = "hisilicon,hisi-spi-nand"; ++ assigned-clocks = <&clock HI3559_FMC_CLK>; ++ assigned-clock-rates = <24000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ ++ hinfc:nand@0 { ++ compatible = "hisilicon,hisi-nand"; ++ assigned-clocks = <&clock HI3559_FMC_CLK>; ++ assigned-clock-rates = <200000000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ }; ++ }; ++ ++}; +diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +index 322fd15..7a2aeac 100644 +--- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts ++++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +@@ -358,6 +358,204 @@ + }; + }; + ++ etb@0,20010000 { ++ compatible = "arm,coresight-etb10", "arm,primecell"; ++ reg = <0 0x20010000 0 0x1000>; ++ ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ port { ++ etb_in_port: endpoint@0 { ++ slave-mode; ++ remote-endpoint = <&replicator_out_port0>; ++ }; ++ }; ++ }; ++ ++ tpiu@0,20030000 { ++ compatible = "arm,coresight-tpiu", "arm,primecell"; ++ reg = <0 0x20030000 0 0x1000>; ++ ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ port { ++ tpiu_in_port: endpoint@0 { ++ slave-mode; ++ remote-endpoint = <&replicator_out_port1>; ++ }; ++ }; ++ }; ++ ++ replicator { ++ /* non-configurable replicators don't show up on the ++ * AMBA bus. As such no need to add "arm,primecell". ++ */ ++ compatible = "arm,coresight-replicator"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* replicator output ports */ ++ port@0 { ++ reg = <0>; ++ replicator_out_port0: endpoint { ++ remote-endpoint = <&etb_in_port>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ replicator_out_port1: endpoint { ++ remote-endpoint = <&tpiu_in_port>; ++ }; ++ }; ++ ++ /* replicator input port */ ++ port@2 { ++ reg = <0>; ++ replicator_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&funnel_out_port0>; ++ }; ++ }; ++ }; ++ }; ++ ++ funnel@0,20040000 { ++ compatible = "arm,coresight-funnel", "arm,primecell"; ++ reg = <0 0x20040000 0 0x1000>; ++ ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* funnel output port */ ++ port@0 { ++ reg = <0>; ++ funnel_out_port0: endpoint { ++ remote-endpoint = ++ <&replicator_in_port0>; ++ }; ++ }; ++ ++ /* funnel input ports */ ++ port@1 { ++ reg = <0>; ++ funnel_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm0_out_port>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <1>; ++ funnel_in_port1: endpoint { ++ slave-mode; ++ remote-endpoint = <&ptm1_out_port>; ++ }; ++ }; ++ ++ port@3 { ++ reg = <2>; ++ funnel_in_port2: endpoint { ++ slave-mode; ++ remote-endpoint = <&etm0_out_port>; ++ }; ++ }; ++ ++ /* Input port #3 is for ITM, not supported here */ ++ ++ port@4 { ++ reg = <4>; ++ funnel_in_port4: endpoint { ++ slave-mode; ++ remote-endpoint = <&etm1_out_port>; ++ }; ++ }; ++ ++ port@5 { ++ reg = <5>; ++ funnel_in_port5: endpoint { ++ slave-mode; ++ remote-endpoint = <&etm2_out_port>; ++ }; ++ }; ++ }; ++ }; ++ ++ ptm@0,2201c000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0x2201c000 0 0x1000>; ++ ++ cpu = <&cpu0>; ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ port { ++ ptm0_out_port: endpoint { ++ remote-endpoint = <&funnel_in_port0>; ++ }; ++ }; ++ }; ++ ++ ptm@0,2201d000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0x2201d000 0 0x1000>; ++ ++ cpu = <&cpu1>; ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ port { ++ ptm1_out_port: endpoint { ++ remote-endpoint = <&funnel_in_port1>; ++ }; ++ }; ++ }; ++ ++ etm@0,2203c000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0x2203c000 0 0x1000>; ++ ++ cpu = <&cpu2>; ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ port { ++ etm0_out_port: endpoint { ++ remote-endpoint = <&funnel_in_port2>; ++ }; ++ }; ++ }; ++ ++ etm@0,2203d000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0x2203d000 0 0x1000>; ++ ++ cpu = <&cpu3>; ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ port { ++ etm1_out_port: endpoint { ++ remote-endpoint = <&funnel_in_port4>; ++ }; ++ }; ++ }; ++ ++ etm@0,2203e000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0 0x2203e000 0 0x1000>; ++ ++ cpu = <&cpu4>; ++ clocks = <&oscclk6a>; ++ clock-names = "apb_pclk"; ++ port { ++ etm2_out_port: endpoint { ++ remote-endpoint = <&funnel_in_port5>; ++ }; ++ }; ++ }; ++ + smb { + compatible = "simple-bus"; + +diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig +index c3a4e9c..f4a38a7 100644 +--- a/arch/arm/common/Kconfig ++++ b/arch/arm/common/Kconfig +@@ -20,3 +20,7 @@ config SHARP_SCOOP + + config TI_PRIV_EDMA + bool ++ ++config FIQ_GLUE ++ bool ++ select FIQ +diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile +index 70b1eff..5748afd 100644 +--- a/arch/arm/common/Makefile ++++ b/arch/arm/common/Makefile +@@ -4,6 +4,7 @@ + + obj-y += firmware.o + ++obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o + obj-$(CONFIG_ICST) += icst.o + obj-$(CONFIG_SA1111) += sa1111.o + obj-$(CONFIG_DMABOUNCE) += dmabounce.o +diff --git a/arch/arm/common/fiq_glue.S b/arch/arm/common/fiq_glue.S +new file mode 100644 +index 0000000..24b42ce +--- /dev/null ++++ b/arch/arm/common/fiq_glue.S +@@ -0,0 +1,118 @@ ++/* ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/linkage.h> ++#include <asm/assembler.h> ++ ++ .text ++ ++ .global fiq_glue_end ++ ++ /* fiq stack: r0-r15,cpsr,spsr of interrupted mode */ ++ ++ENTRY(fiq_glue) ++ /* store pc, cpsr from previous mode, reserve space for spsr */ ++ mrs r12, spsr ++ sub lr, lr, #4 ++ subs r10, #1 ++ bne nested_fiq ++ ++ str r12, [sp, #-8]! ++ str lr, [sp, #-4]! ++ ++ /* store r8-r14 from previous mode */ ++ sub sp, sp, #(7 * 4) ++ stmia sp, {r8-r14}^ ++ nop ++ ++ /* store r0-r7 from previous mode */ ++ stmfd sp!, {r0-r7} ++ ++ /* setup func(data,regs) arguments */ ++ mov r0, r9 ++ mov r1, sp ++ mov r3, r8 ++ ++ mov r7, sp ++ ++ /* Get sp and lr from non-user modes */ ++ and r4, r12, #MODE_MASK ++ cmp r4, #USR_MODE ++ beq fiq_from_usr_mode ++ ++ mov r7, sp ++ orr r4, r4, #(PSR_I_BIT | PSR_F_BIT) ++ msr cpsr_c, r4 ++ str sp, [r7, #(4 * 13)] ++ str lr, [r7, #(4 * 14)] ++ mrs r5, spsr ++ str r5, [r7, #(4 * 17)] ++ ++ cmp r4, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT) ++ /* use fiq stack if we reenter this mode */ ++ subne sp, r7, #(4 * 3) ++ ++fiq_from_usr_mode: ++ msr cpsr_c, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT) ++ mov r2, sp ++ sub sp, r7, #12 ++ stmfd sp!, {r2, ip, lr} ++ /* call func(data,regs) */ ++ blx r3 ++ ldmfd sp, {r2, ip, lr} ++ mov sp, r2 ++ ++ /* restore/discard saved state */ ++ cmp r4, #USR_MODE ++ beq fiq_from_usr_mode_exit ++ ++ msr cpsr_c, r4 ++ ldr sp, [r7, #(4 * 13)] ++ ldr lr, [r7, #(4 * 14)] ++ msr spsr_cxsf, r5 ++ ++fiq_from_usr_mode_exit: ++ msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) ++ ++ ldmfd sp!, {r0-r7} ++ ldr lr, [sp, #(4 * 7)] ++ ldr r12, [sp, #(4 * 8)] ++ add sp, sp, #(10 * 4) ++exit_fiq: ++ msr spsr_cxsf, r12 ++ add r10, #1 ++ cmp r11, #0 ++ moveqs pc, lr ++ bx r11 /* jump to custom fiq return function */ ++ ++nested_fiq: ++ orr r12, r12, #(PSR_F_BIT) ++ b exit_fiq ++ ++fiq_glue_end: ++ ++ENTRY(fiq_glue_setup) /* func, data, sp, smc call number */ ++ stmfd sp!, {r4} ++ mrs r4, cpsr ++ msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) ++ movs r8, r0 ++ mov r9, r1 ++ mov sp, r2 ++ mov r11, r3 ++ moveq r10, #0 ++ movne r10, #1 ++ msr cpsr_c, r4 ++ ldmfd sp!, {r4} ++ bx lr ++ +diff --git a/arch/arm/common/fiq_glue_setup.c b/arch/arm/common/fiq_glue_setup.c +new file mode 100644 +index 0000000..8cb1b61 +--- /dev/null ++++ b/arch/arm/common/fiq_glue_setup.c +@@ -0,0 +1,147 @@ ++/* ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/percpu.h> ++#include <linux/slab.h> ++#include <asm/fiq.h> ++#include <asm/fiq_glue.h> ++ ++extern unsigned char fiq_glue, fiq_glue_end; ++extern void fiq_glue_setup(void *func, void *data, void *sp, ++ fiq_return_handler_t fiq_return_handler); ++ ++static struct fiq_handler fiq_debbuger_fiq_handler = { ++ .name = "fiq_glue", ++}; ++DEFINE_PER_CPU(void *, fiq_stack); ++static struct fiq_glue_handler *current_handler; ++static fiq_return_handler_t fiq_return_handler; ++static DEFINE_MUTEX(fiq_glue_lock); ++ ++static void fiq_glue_setup_helper(void *info) ++{ ++ struct fiq_glue_handler *handler = info; ++ fiq_glue_setup(handler->fiq, handler, ++ __get_cpu_var(fiq_stack) + THREAD_START_SP, ++ fiq_return_handler); ++} ++ ++int fiq_glue_register_handler(struct fiq_glue_handler *handler) ++{ ++ int ret; ++ int cpu; ++ ++ if (!handler || !handler->fiq) ++ return -EINVAL; ++ ++ mutex_lock(&fiq_glue_lock); ++ if (fiq_stack) { ++ ret = -EBUSY; ++ goto err_busy; ++ } ++ ++ for_each_possible_cpu(cpu) { ++ void *stack; ++ stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); ++ if (WARN_ON(!stack)) { ++ ret = -ENOMEM; ++ goto err_alloc_fiq_stack; ++ } ++ per_cpu(fiq_stack, cpu) = stack; ++ } ++ ++ ret = claim_fiq(&fiq_debbuger_fiq_handler); ++ if (WARN_ON(ret)) ++ goto err_claim_fiq; ++ ++ current_handler = handler; ++ on_each_cpu(fiq_glue_setup_helper, handler, true); ++ set_fiq_handler(&fiq_glue, &fiq_glue_end - &fiq_glue); ++ ++ mutex_unlock(&fiq_glue_lock); ++ return 0; ++ ++err_claim_fiq: ++err_alloc_fiq_stack: ++ for_each_possible_cpu(cpu) { ++ __free_pages(per_cpu(fiq_stack, cpu), THREAD_SIZE_ORDER); ++ per_cpu(fiq_stack, cpu) = NULL; ++ } ++err_busy: ++ mutex_unlock(&fiq_glue_lock); ++ return ret; ++} ++ ++static void fiq_glue_update_return_handler(void (*fiq_return)(void)) ++{ ++ fiq_return_handler = fiq_return; ++ if (current_handler) ++ on_each_cpu(fiq_glue_setup_helper, current_handler, true); ++} ++ ++int fiq_glue_set_return_handler(void (*fiq_return)(void)) ++{ ++ int ret; ++ ++ mutex_lock(&fiq_glue_lock); ++ if (fiq_return_handler) { ++ ret = -EBUSY; ++ goto err_busy; ++ } ++ fiq_glue_update_return_handler(fiq_return); ++ ret = 0; ++err_busy: ++ mutex_unlock(&fiq_glue_lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL(fiq_glue_set_return_handler); ++ ++int fiq_glue_clear_return_handler(void (*fiq_return)(void)) ++{ ++ int ret; ++ ++ mutex_lock(&fiq_glue_lock); ++ if (WARN_ON(fiq_return_handler != fiq_return)) { ++ ret = -EINVAL; ++ goto err_inval; ++ } ++ fiq_glue_update_return_handler(NULL); ++ ret = 0; ++err_inval: ++ mutex_unlock(&fiq_glue_lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL(fiq_glue_clear_return_handler); ++ ++/** ++ * fiq_glue_resume - Restore fiqs after suspend or low power idle states ++ * ++ * This must be called before calling local_fiq_enable after returning from a ++ * power state where the fiq mode registers were lost. If a driver provided ++ * a resume hook when it registered the handler it will be called. ++ */ ++ ++void fiq_glue_resume(void) ++{ ++ if (!current_handler) ++ return; ++ fiq_glue_setup(current_handler->fiq, current_handler, ++ __get_cpu_var(fiq_stack) + THREAD_START_SP, ++ fiq_return_handler); ++ if (current_handler->resume) ++ current_handler->resume(current_handler); ++} ++ +diff --git a/arch/arm/common/timer-sp.c b/arch/arm/common/timer-sp.c +index 1921132..687bafe 100644 +--- a/arch/arm/common/timer-sp.c ++++ b/arch/arm/common/timer-sp.c +@@ -226,6 +226,10 @@ static void __init sp804_of_init(struct device_node *np) + writel(0, base + TIMER_CTRL); + writel(0, base + TIMER_2_BASE + TIMER_CTRL); + ++ /* Ensure timer interrupts are clear */ ++ writel(1, base + TIMER_INTCLR); ++ writel(1, base + TIMER_2_BASE + TIMER_INTCLR); ++ + if (initialized || !of_device_is_available(np)) + goto err; + +diff --git a/arch/arm/configs/hi3516av200_big_little_defconfig b/arch/arm/configs/hi3516av200_big_little_defconfig +new file mode 100644 +index 0000000..afed80c +--- /dev/null ++++ b/arch/arm/configs/hi3516av200_big_little_defconfig +@@ -0,0 +1,2599 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++# CONFIG_NO_HZ_FULL is not set ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_RCU_FAST_NO_HZ is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_NOCB_CPU is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++CONFIG_ARCH_HI3516AV200=y ++# CONFIG_ARCH_HI3559 is not set ++# CONFIG_ARCH_HI3556 is not set ++CONFIG_PMC=y ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_643719 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_754327 is not set ++# CONFIG_ARM_ERRATA_764369 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_798181 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++CONFIG_SCHED_MC=y ++# CONFIG_SCHED_SMT is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_SCHED_HMP=y ++# CONFIG_SCHED_HMP_PRIO_FILTER is not set ++CONFIG_HMP_FAST_CPU_MASK="" ++CONFIG_HMP_SLOW_CPU_MASK="" ++CONFIG_HMP_VARIABLE_SCALE=y ++# CONFIG_HMP_FREQUENCY_INVARIANT_SCALE is not set ++# CONFIG_SCHED_HMP_LITTLE_PACKING is not set ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++# CONFIG_MCPM is not set ++# CONFIG_BIG_LITTLE is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPUFREQ_DT=y ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++CONFIG_CPU_IDLE_GOV_MENU=y ++ ++# ++# ARM CPU Idle Drivers ++# ++CONFIG_ARM_HI3516AV200_CPUIDLE=y ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++# CONFIG_DMA_SHARED_BUFFER is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_GPIO is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++CONFIG_CLOSE_SPI_8PIN_4IO=y ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++# CONFIG_MTD_UBI is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++CONFIG_HISI_REG=y ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++CONFIG_HIETH_GMAC=y ++CONFIG_HIGMAC_DESC_4WORD=y ++CONFIG_HIGMAC_RXCSUM=y ++CONFIG_RX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF ++CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF ++CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 ++CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++CONFIG_MDIO_HISI_GEMAC=y ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++# CONFIG_I2C_MUX is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIO_DEVRES=y ++CONFIG_OF_GPIO=y ++CONFIG_GPIOLIB_IRQCHIP=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_EM is not set ++# CONFIG_GPIO_ZEVIO is not set ++CONFIG_GPIO_PL061=y ++# CONFIG_GPIO_SCH311X is not set ++# CONFIG_GPIO_SYSCON is not set ++# CONFIG_GPIO_GRGPIO is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# LPC GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++ ++# ++# USB GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_GPIO is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++# CONFIG_HID_CP2112 is not set ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_PLATFORM=y ++CONFIG_USB_XHCI_HISILICON=y ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_SERIAL=m ++CONFIG_USB_F_OBEX=m ++CONFIG_USB_F_ECM=m ++CONFIG_USB_F_SUBSET=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_ETH=m ++CONFIG_USB_ETH_RNDIS=y ++# CONFIG_USB_ETH_EEM is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++CONFIG_USB_G_SERIAL=m ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++CONFIG_PHY_HISI_USB3=y ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_IO_BASE=0x10030000 ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3516av200_big_little_nand_defconfig b/arch/arm/configs/hi3516av200_big_little_nand_defconfig +new file mode 100644 +index 0000000..3990e3f +--- /dev/null ++++ b/arch/arm/configs/hi3516av200_big_little_nand_defconfig +@@ -0,0 +1,2597 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++# CONFIG_NO_HZ_FULL is not set ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_RCU_FAST_NO_HZ is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_NOCB_CPU is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++CONFIG_ARCH_HI3516AV200=y ++# CONFIG_ARCH_HI3559 is not set ++# CONFIG_ARCH_HI3556 is not set ++CONFIG_PMC=y ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_643719 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_754327 is not set ++# CONFIG_ARM_ERRATA_764369 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_798181 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++CONFIG_SCHED_MC=y ++# CONFIG_SCHED_SMT is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_SCHED_HMP=y ++# CONFIG_SCHED_HMP_PRIO_FILTER is not set ++CONFIG_HMP_FAST_CPU_MASK="" ++CONFIG_HMP_SLOW_CPU_MASK="" ++CONFIG_HMP_VARIABLE_SCALE=y ++# CONFIG_HMP_FREQUENCY_INVARIANT_SCALE is not set ++# CONFIG_SCHED_HMP_LITTLE_PACKING is not set ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++# CONFIG_MCPM is not set ++# CONFIG_BIG_LITTLE is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPUFREQ_DT=y ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++CONFIG_CPU_IDLE_GOV_MENU=y ++ ++# ++# ARM CPU Idle Drivers ++# ++CONFIG_ARM_HI3516AV200_CPUIDLE=y ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++# CONFIG_DMA_SHARED_BUFFER is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++# CONFIG_HIFMC_SPI_NAND is not set ++CONFIG_HIFMC_NAND=y ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_GPIO is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++CONFIG_HIFMC100_NAND=y ++CONFIG_HIFMC100_MAX_NAND_CHIP=1 ++# CONFIG_HIFMC100_NAND_EDO_MODE is not set ++CONFIG_RW_H_WIDTH=10 ++CONFIG_R_L_WIDTH=10 ++CONFIG_W_L_WIDTH=10 ++# CONFIG_HIFMC100_NAND_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_NAND_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_NAND_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++# CONFIG_MTD_SPI_NOR is not set ++# CONFIG_MTD_UBI is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++CONFIG_HISI_REG=y ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++CONFIG_HIETH_GMAC=y ++CONFIG_HIGMAC_DESC_4WORD=y ++CONFIG_HIGMAC_RXCSUM=y ++CONFIG_RX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF ++CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF ++CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 ++CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++CONFIG_MDIO_HISI_GEMAC=y ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++# CONFIG_I2C_MUX is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIO_DEVRES=y ++CONFIG_OF_GPIO=y ++CONFIG_GPIOLIB_IRQCHIP=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_EM is not set ++# CONFIG_GPIO_ZEVIO is not set ++CONFIG_GPIO_PL061=y ++# CONFIG_GPIO_SCH311X is not set ++# CONFIG_GPIO_SYSCON is not set ++# CONFIG_GPIO_GRGPIO is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# LPC GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++ ++# ++# USB GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_GPIO is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++# CONFIG_HID_CP2112 is not set ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_PLATFORM=y ++CONFIG_USB_XHCI_HISILICON=y ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_SERIAL=m ++CONFIG_USB_F_OBEX=m ++CONFIG_USB_F_ECM=m ++CONFIG_USB_F_SUBSET=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_ETH=m ++CONFIG_USB_ETH_RNDIS=y ++# CONFIG_USB_ETH_EEM is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++CONFIG_USB_G_SERIAL=m ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++CONFIG_PHY_HISI_USB3=y ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_IO_BASE=0x10030000 ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3516cv300_full_defconfig b/arch/arm/configs/hi3516cv300_full_defconfig +new file mode 100644 +index 0000000..70f4635 +--- /dev/null ++++ b/arch/arm/configs/hi3516cv300_full_defconfig +@@ -0,0 +1,2529 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_VIRT_CPU_ACCOUNTING=y ++# CONFIG_TICK_CPU_ACCOUNTING is not set ++CONFIG_VIRT_CPU_ACCOUNTING_GEN=y ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++CONFIG_CONTEXT_TRACKING=y ++# CONFIG_CONTEXT_TRACKING_FORCE is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V4 is not set ++# CONFIG_ARCH_MULTI_V4T is not set ++CONFIG_ARCH_MULTI_V5=y ++CONFIG_ARCH_MULTI_V4_V5=y ++# CONFIG_ARCH_MULTI_V6 is not set ++# CONFIG_ARCH_MULTI_V7 is not set ++CONFIG_ARCH_MULTI_CPU_AUTO=y ++# CONFIG_ARCH_MVEBU is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++CONFIG_ARCH_HI3516CV300=y ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MXS is not set ++# CONFIG_ARCH_NOMADIK is not set ++# CONFIG_ARCH_NSPIRE is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_U300 is not set ++# CONFIG_ARCH_WM8505 is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_ARM926T=y ++CONFIG_CPU_32v5=y ++CONFIG_CPU_ABRT_EV5TJ=y ++CONFIG_CPU_PABRT_LEGACY=y ++CONFIG_CPU_CACHE_VIVT=y ++CONFIG_CPU_COPY_V4WB=y ++CONFIG_CPU_TLB_V4WBI=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++CONFIG_CPU_USE_DOMAINS=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set ++# CONFIG_CPU_CACHE_ROUND_ROBIN is not set ++CONFIG_NEED_KUSER_HELPERS=y ++CONFIG_KUSER_HELPERS=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT=5 ++CONFIG_MULTI_IRQ_HANDLER=y ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=999999 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++# CONFIG_CMA is not set ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++# CONFIG_CPU_FREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_GPIO is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++CONFIG_CLOSE_SPI_8PIN_4IO=y ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++CONFIG_HISI_FEMAC=y ++# CONFIG_HIETH_GMAC is not set ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++CONFIG_NET_VENDOR_MICROCHIP=y ++# CONFIG_ENC28J60 is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++CONFIG_MDIO_HISI_FEMAC=y ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=m ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set ++# CONFIG_I2C_MUX_GPIO is not set ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PCA954x is not set ++# CONFIG_I2C_MUX_PINCTRL is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++CONFIG_I2C_HIBVT=y ++# CONFIG_I2C_HISI_V110 is not set ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_PINCTRL=y ++ ++# ++# Pin controllers ++# ++CONFIG_PINMUX=y ++CONFIG_PINCONF=y ++CONFIG_GENERIC_PINCONF=y ++# CONFIG_DEBUG_PINCTRL is not set ++CONFIG_PINCTRL_SINGLE=y ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIO_DEVRES=y ++CONFIG_OF_GPIO=y ++CONFIG_GPIOLIB_IRQCHIP=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_EM is not set ++# CONFIG_GPIO_ZEVIO is not set ++CONFIG_GPIO_PL061=y ++# CONFIG_GPIO_SCH311X is not set ++# CONFIG_GPIO_SYSCON is not set ++# CONFIG_GPIO_GRGPIO is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# LPC GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++ ++# ++# USB GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++CONFIG_POWER_RESET_SYSCON=y ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++# CONFIG_REGULATOR is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++# CONFIG_HID_CP2112 is not set ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_VIC=y ++CONFIG_ARM_VIC_NR=2 ++# CONFIG_IPACK_BUS is not set ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++ ++# ++# File systems ++# ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++# CONFIG_NLS_UTF8 is not set ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_GENERIC_ATOMIC64=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3516ev100_full_defconfig b/arch/arm/configs/hi3516ev100_full_defconfig +new file mode 100644 +index 0000000..70f4635 +--- /dev/null ++++ b/arch/arm/configs/hi3516ev100_full_defconfig +@@ -0,0 +1,2529 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_VIRT_CPU_ACCOUNTING=y ++# CONFIG_TICK_CPU_ACCOUNTING is not set ++CONFIG_VIRT_CPU_ACCOUNTING_GEN=y ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++CONFIG_CONTEXT_TRACKING=y ++# CONFIG_CONTEXT_TRACKING_FORCE is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V4 is not set ++# CONFIG_ARCH_MULTI_V4T is not set ++CONFIG_ARCH_MULTI_V5=y ++CONFIG_ARCH_MULTI_V4_V5=y ++# CONFIG_ARCH_MULTI_V6 is not set ++# CONFIG_ARCH_MULTI_V7 is not set ++CONFIG_ARCH_MULTI_CPU_AUTO=y ++# CONFIG_ARCH_MVEBU is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++CONFIG_ARCH_HI3516CV300=y ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MXS is not set ++# CONFIG_ARCH_NOMADIK is not set ++# CONFIG_ARCH_NSPIRE is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_U300 is not set ++# CONFIG_ARCH_WM8505 is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_ARM926T=y ++CONFIG_CPU_32v5=y ++CONFIG_CPU_ABRT_EV5TJ=y ++CONFIG_CPU_PABRT_LEGACY=y ++CONFIG_CPU_CACHE_VIVT=y ++CONFIG_CPU_COPY_V4WB=y ++CONFIG_CPU_TLB_V4WBI=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++CONFIG_CPU_USE_DOMAINS=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set ++# CONFIG_CPU_CACHE_ROUND_ROBIN is not set ++CONFIG_NEED_KUSER_HELPERS=y ++CONFIG_KUSER_HELPERS=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT=5 ++CONFIG_MULTI_IRQ_HANDLER=y ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=999999 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++# CONFIG_CMA is not set ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++# CONFIG_CPU_FREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_GPIO is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++CONFIG_CLOSE_SPI_8PIN_4IO=y ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++CONFIG_HISI_FEMAC=y ++# CONFIG_HIETH_GMAC is not set ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++CONFIG_NET_VENDOR_MICROCHIP=y ++# CONFIG_ENC28J60 is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++CONFIG_MDIO_HISI_FEMAC=y ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=m ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set ++# CONFIG_I2C_MUX_GPIO is not set ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PCA954x is not set ++# CONFIG_I2C_MUX_PINCTRL is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++CONFIG_I2C_HIBVT=y ++# CONFIG_I2C_HISI_V110 is not set ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_PINCTRL=y ++ ++# ++# Pin controllers ++# ++CONFIG_PINMUX=y ++CONFIG_PINCONF=y ++CONFIG_GENERIC_PINCONF=y ++# CONFIG_DEBUG_PINCTRL is not set ++CONFIG_PINCTRL_SINGLE=y ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIO_DEVRES=y ++CONFIG_OF_GPIO=y ++CONFIG_GPIOLIB_IRQCHIP=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_EM is not set ++# CONFIG_GPIO_ZEVIO is not set ++CONFIG_GPIO_PL061=y ++# CONFIG_GPIO_SCH311X is not set ++# CONFIG_GPIO_SYSCON is not set ++# CONFIG_GPIO_GRGPIO is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# LPC GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++ ++# ++# USB GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++CONFIG_POWER_RESET_SYSCON=y ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++# CONFIG_REGULATOR is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++# CONFIG_HID_CP2112 is not set ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_VIC=y ++CONFIG_ARM_VIC_NR=2 ++# CONFIG_IPACK_BUS is not set ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++ ++# ++# File systems ++# ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++# CONFIG_NLS_UTF8 is not set ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_GENERIC_ATOMIC64=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3516ev100_mini_defconfig b/arch/arm/configs/hi3516ev100_mini_defconfig +new file mode 100644 +index 0000000..116773a +--- /dev/null ++++ b/arch/arm/configs/hi3516ev100_mini_defconfig +@@ -0,0 +1,2188 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_VIRT_CPU_ACCOUNTING=y ++# CONFIG_TICK_CPU_ACCOUNTING is not set ++CONFIG_VIRT_CPU_ACCOUNTING_GEN=y ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++CONFIG_CONTEXT_TRACKING=y ++# CONFIG_CONTEXT_TRACKING_FORCE is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V4 is not set ++# CONFIG_ARCH_MULTI_V4T is not set ++CONFIG_ARCH_MULTI_V5=y ++CONFIG_ARCH_MULTI_V4_V5=y ++# CONFIG_ARCH_MULTI_V6 is not set ++# CONFIG_ARCH_MULTI_V7 is not set ++CONFIG_ARCH_MULTI_CPU_AUTO=y ++# CONFIG_ARCH_MVEBU is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++CONFIG_ARCH_HI3516CV300=y ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MXS is not set ++# CONFIG_ARCH_NOMADIK is not set ++# CONFIG_ARCH_NSPIRE is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_U300 is not set ++# CONFIG_ARCH_WM8505 is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_ARM926T=y ++CONFIG_CPU_32v5=y ++CONFIG_CPU_ABRT_EV5TJ=y ++CONFIG_CPU_PABRT_LEGACY=y ++CONFIG_CPU_CACHE_VIVT=y ++CONFIG_CPU_COPY_V4WB=y ++CONFIG_CPU_TLB_V4WBI=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++CONFIG_CPU_USE_DOMAINS=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++# CONFIG_ARM_THUMB is not set ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set ++# CONFIG_CPU_CACHE_ROUND_ROBIN is not set ++CONFIG_NEED_KUSER_HELPERS=y ++CONFIG_KUSER_HELPERS=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT=5 ++CONFIG_MULTI_IRQ_HANDLER=y ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=999999 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++# CONFIG_CMA is not set ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++# CONFIG_CPU_FREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++# CONFIG_WIRELESS is not set ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++# CONFIG_DMA_SHARED_BUFFER is not set ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++# CONFIG_HIFMC_SPI_NAND is not set ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_NAND is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++CONFIG_CLOSE_SPI_8PIN_4IO=y ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++CONFIG_HISI_FEMAC=y ++# CONFIG_HIETH_GMAC is not set ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++CONFIG_NET_VENDOR_MICROCHIP=y ++# CONFIG_ENC28J60 is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++CONFIG_MDIO_HISI_FEMAC=y ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++ ++# ++# Host-side USB support is needed for USB Network Adapter support ++# ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++# CONFIG_I2C_MUX is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++CONFIG_I2C_HIBVT=y ++# CONFIG_I2C_HISI_V110 is not set ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_TAOS_EVM is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_PINCTRL=y ++ ++# ++# Pin controllers ++# ++CONFIG_PINMUX=y ++CONFIG_PINCONF=y ++CONFIG_GENERIC_PINCONF=y ++# CONFIG_DEBUG_PINCTRL is not set ++CONFIG_PINCTRL_SINGLE=y ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIO_DEVRES=y ++CONFIG_OF_GPIO=y ++CONFIG_GPIOLIB_IRQCHIP=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_EM is not set ++# CONFIG_GPIO_ZEVIO is not set ++CONFIG_GPIO_PL061=y ++# CONFIG_GPIO_SCH311X is not set ++# CONFIG_GPIO_SYSCON is not set ++# CONFIG_GPIO_GRGPIO is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# LPC GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++ ++# ++# USB GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++CONFIG_POWER_RESET_SYSCON=y ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++# CONFIG_REGULATOR is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++# CONFIG_USB_SUPPORT is not set ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_VIC=y ++CONFIG_ARM_VIC_NR=2 ++# CONFIG_IPACK_BUS is not set ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++ ++# ++# File systems ++# ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_YAFFS_FS is not set ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++# CONFIG_NLS_UTF8 is not set ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_GENERIC_ATOMIC64=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3519_big_little_defconfig b/arch/arm/configs/hi3519_big_little_defconfig +new file mode 100644 +index 0000000..f09a9bf +--- /dev/null ++++ b/arch/arm/configs/hi3519_big_little_defconfig +@@ -0,0 +1,2515 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++# CONFIG_NO_HZ_FULL is not set ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_RCU_FAST_NO_HZ is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_NOCB_CPU is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++CONFIG_ARCH_HI3519=y ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++# CONFIG_ARCH_HI3556 is not set ++CONFIG_PMC=y ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_643719 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_754327 is not set ++# CONFIG_ARM_ERRATA_764369 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_798181 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++CONFIG_SCHED_MC=y ++# CONFIG_SCHED_SMT is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_SCHED_HMP=y ++# CONFIG_SCHED_HMP_PRIO_FILTER is not set ++CONFIG_HMP_FAST_CPU_MASK="" ++CONFIG_HMP_SLOW_CPU_MASK="" ++CONFIG_HMP_VARIABLE_SCALE=y ++# CONFIG_HMP_FREQUENCY_INVARIANT_SCALE is not set ++# CONFIG_SCHED_HMP_LITTLE_PACKING is not set ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++# CONFIG_MCPM is not set ++# CONFIG_BIG_LITTLE is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPUFREQ_DT=y ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++# CONFIG_DMA_SHARED_BUFFER is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++CONFIG_CLOSE_SPI_8PIN_4IO=y ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++# CONFIG_MTD_UBI is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++CONFIG_HISI_REG=y ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++CONFIG_HIETH_GMAC=y ++CONFIG_HIGMAC_DESC_4WORD=y ++CONFIG_HIGMAC_RXCSUM=y ++CONFIG_RX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF ++CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF ++CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 ++CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++CONFIG_MDIO_HISI_GEMAC=y ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++CONFIG_I2C_HELPER_AUTO=y ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_HIBVT is not set ++# CONFIG_I2C_HISI_V110 is not set ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++CONFIG_HI_I2C=y ++CONFIG_HI_I2C0_IO_BASE=0x12110000 ++CONFIG_HI_I2C0_IO_SIZE=0x1000 ++CONFIG_HI_I2C1_IO_BASE=0x12111000 ++CONFIG_HI_I2C1_IO_SIZE=0x1000 ++CONFIG_HI_I2C2_IO_BASE=0x12112000 ++CONFIG_HI_I2C2_IO_SIZE=0x1000 ++CONFIG_HI_I2C3_IO_BASE=0x12113000 ++CONFIG_HI_I2C3_IO_SIZE=0x1000 ++CONFIG_HI_I2C_RETRIES=0x1 ++CONFIG_HI_I2C_TX_FIFO=0x8 ++CONFIG_HI_I2C_RX_FIFO=0x8 ++CONFIG_HI_I2C0_CLK_LIMIT=100000 ++CONFIG_HI_I2C1_CLK_LIMIT=100000 ++CONFIG_HI_I2C2_CLK_LIMIT=100000 ++CONFIG_HI_I2C3_CLK_LIMIT=100000 ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_PLATFORM=y ++CONFIG_USB_XHCI_HISILICON=y ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_SERIAL=m ++CONFIG_USB_F_OBEX=m ++CONFIG_USB_F_ECM=m ++CONFIG_USB_F_SUBSET=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_ETH=m ++CONFIG_USB_ETH_RNDIS=y ++# CONFIG_USB_ETH_EEM is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++CONFIG_USB_G_SERIAL=m ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++CONFIG_PHY_HISI_USB3=y ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_HI_DMAC is not set ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=21 ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++# CONFIG_STRICT_DEVMEM is not set ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3519_big_little_nand_defconfig b/arch/arm/configs/hi3519_big_little_nand_defconfig +new file mode 100644 +index 0000000..9eed93e +--- /dev/null ++++ b/arch/arm/configs/hi3519_big_little_nand_defconfig +@@ -0,0 +1,2513 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++# CONFIG_NO_HZ_FULL is not set ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_RCU_FAST_NO_HZ is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_NOCB_CPU is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++CONFIG_ARCH_HI3519=y ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++# CONFIG_ARCH_HI3556 is not set ++CONFIG_PMC=y ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_643719 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_754327 is not set ++# CONFIG_ARM_ERRATA_764369 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_798181 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++CONFIG_SCHED_MC=y ++# CONFIG_SCHED_SMT is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_SCHED_HMP=y ++# CONFIG_SCHED_HMP_PRIO_FILTER is not set ++CONFIG_HMP_FAST_CPU_MASK="" ++CONFIG_HMP_SLOW_CPU_MASK="" ++CONFIG_HMP_VARIABLE_SCALE=y ++# CONFIG_HMP_FREQUENCY_INVARIANT_SCALE is not set ++# CONFIG_SCHED_HMP_LITTLE_PACKING is not set ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++# CONFIG_MCPM is not set ++# CONFIG_BIG_LITTLE is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPUFREQ_DT=y ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++# CONFIG_DMA_SHARED_BUFFER is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++# CONFIG_HIFMC_SPI_NAND is not set ++CONFIG_HIFMC_NAND=y ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++CONFIG_HIFMC100_NAND=y ++CONFIG_HIFMC100_MAX_NAND_CHIP=1 ++# CONFIG_HIFMC100_NAND_EDO_MODE is not set ++CONFIG_RW_H_WIDTH=10 ++CONFIG_R_L_WIDTH=10 ++CONFIG_W_L_WIDTH=10 ++# CONFIG_HIFMC100_NAND_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_NAND_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_NAND_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++# CONFIG_MTD_SPI_NOR is not set ++# CONFIG_MTD_UBI is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++CONFIG_HISI_REG=y ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++CONFIG_HIETH_GMAC=y ++CONFIG_HIGMAC_DESC_4WORD=y ++CONFIG_HIGMAC_RXCSUM=y ++CONFIG_RX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF ++CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF ++CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 ++CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++CONFIG_MDIO_HISI_GEMAC=y ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++CONFIG_I2C_HELPER_AUTO=y ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_HIBVT is not set ++# CONFIG_I2C_HISI_V110 is not set ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++CONFIG_HI_I2C=y ++CONFIG_HI_I2C0_IO_BASE=0x12110000 ++CONFIG_HI_I2C0_IO_SIZE=0x1000 ++CONFIG_HI_I2C1_IO_BASE=0x12111000 ++CONFIG_HI_I2C1_IO_SIZE=0x1000 ++CONFIG_HI_I2C2_IO_BASE=0x12112000 ++CONFIG_HI_I2C2_IO_SIZE=0x1000 ++CONFIG_HI_I2C3_IO_BASE=0x12113000 ++CONFIG_HI_I2C3_IO_SIZE=0x1000 ++CONFIG_HI_I2C_RETRIES=0x1 ++CONFIG_HI_I2C_TX_FIFO=0x8 ++CONFIG_HI_I2C_RX_FIFO=0x8 ++CONFIG_HI_I2C0_CLK_LIMIT=100000 ++CONFIG_HI_I2C1_CLK_LIMIT=100000 ++CONFIG_HI_I2C2_CLK_LIMIT=100000 ++CONFIG_HI_I2C3_CLK_LIMIT=100000 ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_PLATFORM=y ++CONFIG_USB_XHCI_HISILICON=y ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_SERIAL=m ++CONFIG_USB_F_OBEX=m ++CONFIG_USB_F_ECM=m ++CONFIG_USB_F_SUBSET=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_ETH=m ++CONFIG_USB_ETH_RNDIS=y ++# CONFIG_USB_ETH_EEM is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++CONFIG_USB_G_SERIAL=m ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++CONFIG_PHY_HISI_USB3=y ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_HI_DMAC is not set ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=21 ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++# CONFIG_STRICT_DEVMEM is not set ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3519v101_big_little_defconfig b/arch/arm/configs/hi3519v101_big_little_defconfig +new file mode 100644 +index 0000000..509ac58 +--- /dev/null ++++ b/arch/arm/configs/hi3519v101_big_little_defconfig +@@ -0,0 +1,2701 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++# CONFIG_NO_HZ_FULL is not set ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_RCU_FAST_NO_HZ is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_NOCB_CPU is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++CONFIG_ARCH_HI3519V101=y ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++# CONFIG_ARCH_HI3556 is not set ++CONFIG_PMC=y ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_643719 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_754327 is not set ++# CONFIG_ARM_ERRATA_764369 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_798181 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++CONFIG_SCHED_MC=y ++# CONFIG_SCHED_SMT is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_SCHED_HMP=y ++# CONFIG_SCHED_HMP_PRIO_FILTER is not set ++CONFIG_HMP_FAST_CPU_MASK="" ++CONFIG_HMP_SLOW_CPU_MASK="" ++CONFIG_HMP_VARIABLE_SCALE=y ++# CONFIG_HMP_FREQUENCY_INVARIANT_SCALE is not set ++# CONFIG_SCHED_HMP_LITTLE_PACKING is not set ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++# CONFIG_MCPM is not set ++# CONFIG_BIG_LITTLE is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPUFREQ_DT=y ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++CONFIG_CPU_IDLE_GOV_MENU=y ++ ++# ++# ARM CPU Idle Drivers ++# ++CONFIG_ARM_HI3519V101_CPUIDLE=y ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_GPIO is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++CONFIG_CLOSE_SPI_8PIN_4IO=y ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++# CONFIG_MTD_UBI is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++CONFIG_HISI_REG=y ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++CONFIG_HIETH_GMAC=y ++CONFIG_HIGMAC_DESC_4WORD=y ++CONFIG_HIGMAC_RXCSUM=y ++CONFIG_RX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF ++CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF ++CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 ++CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++CONFIG_MDIO_HISI_GEMAC=y ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set ++# CONFIG_I2C_MUX_GPIO is not set ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PCA954x is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIO_DEVRES=y ++CONFIG_OF_GPIO=y ++CONFIG_GPIOLIB_IRQCHIP=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_EM is not set ++# CONFIG_GPIO_ZEVIO is not set ++CONFIG_GPIO_PL061=y ++# CONFIG_GPIO_SCH311X is not set ++# CONFIG_GPIO_SYSCON is not set ++# CONFIG_GPIO_GRGPIO is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# LPC GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++ ++# ++# USB GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_GPIO is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++# CONFIG_HID_CP2112 is not set ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_PLATFORM=y ++CONFIG_USB_XHCI_HISILICON=y ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_SERIAL=m ++CONFIG_USB_F_OBEX=m ++CONFIG_USB_F_ECM=m ++CONFIG_USB_F_SUBSET=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++CONFIG_USB_ETH=m ++CONFIG_USB_ETH_RNDIS=y ++# CONFIG_USB_ETH_EEM is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++CONFIG_USB_G_SERIAL=m ++# CONFIG_USB_MIDI_GADGET is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++CONFIG_PHY_HISI_USB3=y ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_IO_BASE=0x10030000 ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3519v101_big_little_nand_defconfig b/arch/arm/configs/hi3519v101_big_little_nand_defconfig +new file mode 100644 +index 0000000..7d44600 +--- /dev/null ++++ b/arch/arm/configs/hi3519v101_big_little_nand_defconfig +@@ -0,0 +1,2699 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++# CONFIG_NO_HZ_FULL is not set ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_RCU_FAST_NO_HZ is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_NOCB_CPU is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++CONFIG_COMPAT_BRK=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++CONFIG_ARCH_HI3519V101=y ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++# CONFIG_ARCH_HI3556 is not set ++CONFIG_PMC=y ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_643719 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_754327 is not set ++# CONFIG_ARM_ERRATA_764369 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_798181 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++CONFIG_SCHED_MC=y ++# CONFIG_SCHED_SMT is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_SCHED_HMP=y ++# CONFIG_SCHED_HMP_PRIO_FILTER is not set ++CONFIG_HMP_FAST_CPU_MASK="" ++CONFIG_HMP_SLOW_CPU_MASK="" ++CONFIG_HMP_VARIABLE_SCALE=y ++# CONFIG_HMP_FREQUENCY_INVARIANT_SCALE is not set ++# CONFIG_SCHED_HMP_LITTLE_PACKING is not set ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++# CONFIG_MCPM is not set ++# CONFIG_BIG_LITTLE is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPUFREQ_DT=y ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++CONFIG_CPU_IDLE_GOV_MENU=y ++ ++# ++# ARM CPU Idle Drivers ++# ++CONFIG_ARM_HI3519V101_CPUIDLE=y ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++# CONFIG_HIFMC_SPI_NAND is not set ++CONFIG_HIFMC_NAND=y ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_GPIO is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++CONFIG_HIFMC100_NAND=y ++CONFIG_HIFMC100_MAX_NAND_CHIP=1 ++# CONFIG_HIFMC100_NAND_EDO_MODE is not set ++CONFIG_RW_H_WIDTH=10 ++CONFIG_R_L_WIDTH=10 ++CONFIG_W_L_WIDTH=10 ++# CONFIG_HIFMC100_NAND_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_NAND_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_NAND_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++# CONFIG_MTD_SPI_NOR is not set ++# CONFIG_MTD_UBI is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++CONFIG_HISI_REG=y ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++CONFIG_HIETH_GMAC=y ++CONFIG_HIGMAC_DESC_4WORD=y ++CONFIG_HIGMAC_RXCSUM=y ++CONFIG_RX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF ++CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF ++CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 ++CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++CONFIG_MDIO_HISI_GEMAC=y ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set ++# CONFIG_I2C_MUX_GPIO is not set ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PCA954x is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIO_DEVRES=y ++CONFIG_OF_GPIO=y ++CONFIG_GPIOLIB_IRQCHIP=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_EM is not set ++# CONFIG_GPIO_ZEVIO is not set ++CONFIG_GPIO_PL061=y ++# CONFIG_GPIO_SCH311X is not set ++# CONFIG_GPIO_SYSCON is not set ++# CONFIG_GPIO_GRGPIO is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# LPC GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++ ++# ++# USB GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_GPIO is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++# CONFIG_HID_CP2112 is not set ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_PLATFORM=y ++CONFIG_USB_XHCI_HISILICON=y ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_SERIAL=m ++CONFIG_USB_F_OBEX=m ++CONFIG_USB_F_ECM=m ++CONFIG_USB_F_SUBSET=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++CONFIG_USB_ETH=m ++CONFIG_USB_ETH_RNDIS=y ++# CONFIG_USB_ETH_EEM is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++CONFIG_USB_G_SERIAL=m ++# CONFIG_USB_MIDI_GADGET is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++# CONFIG_USB_G_MULTI is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++CONFIG_PHY_HISI_USB3=y ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_IO_BASE=0x10030000 ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3521d_full_defconfig b/arch/arm/configs/hi3521d_full_defconfig +new file mode 100644 +index 0000000..0798397 +--- /dev/null ++++ b/arch/arm/configs/hi3521d_full_defconfig +@@ -0,0 +1,2433 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_HZ_PERIODIC=y ++# CONFIG_NO_HZ_IDLE is not set ++# CONFIG_NO_HZ_FULL is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_NOCB_CPU is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++# CONFIG_ARCH_HI3556 is not set ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_HI3531D is not set ++CONFIG_ARCH_HI3521D=y ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_643719 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_754327 is not set ++# CONFIG_ARM_ERRATA_764369 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_798181 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++# CONFIG_SCHED_MC is not set ++# CONFIG_SCHED_SMT is not set ++# CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE is not set ++CONFIG_HAVE_ARM_SCU=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++# CONFIG_MCPM is not set ++# CONFIG_BIG_LITTLE is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++# CONFIG_CLEANCACHE is not set ++# CONFIG_CMA is not set ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_INTERACTIVE is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++CONFIG_CPUFREQ_DT=y ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++# CONFIG_FPE_NWFPE is not set ++# CONFIG_FPE_FASTFPE is not set ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++# CONFIG_HISI_SNAPSHOT_BOOT is not set ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_IP_MROUTE is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++# CONFIG_WIRELESS is not set ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++# CONFIG_DEVTMPFS_MOUNT is not set ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++# CONFIG_DMA_SHARED_BUFFER is not set ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++# CONFIG_ARM_CCI is not set ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++CONFIG_MTD_BLOCK2MTD=y ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_MTD_NAND_HINFC610 is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++CONFIG_CLOSE_SPI_8PIN_4IO=y ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++CONFIG_BLK_DEV_SR=y ++# CONFIG_BLK_DEV_SR_VENDOR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++CONFIG_HI_SATA=y ++CONFIG_HI_SATA_IOBASE=0x11010000 ++CONFIG_HI_SATA_FBS=1 ++CONFIG_HI_SATA_NCQ=1 ++CONFIG_ATA=y ++# CONFIG_ATA_NONSTANDARD is not set ++CONFIG_ATA_VERBOSE_ERROR=y ++CONFIG_SATA_PMP=y ++ ++# ++# Controllers with non-SFF native interface ++# ++CONFIG_SATA_AHCI_PLATFORM=y ++# CONFIG_ATA_SFF is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++CONFIG_HIETH_GMAC=y ++CONFIG_HIGMAC_DESC_4WORD=y ++CONFIG_HIGMAC_RXCSUM=y ++CONFIG_RX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF ++CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF ++CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 ++CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++CONFIG_MDIO_HISI_GEMAC=y ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++CONFIG_INPUT_JOYDEV=y ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++# CONFIG_DEVKMEM is not set ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PINCTRL is not set ++CONFIG_I2C_HELPER_AUTO=y ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_HIBVT is not set ++# CONFIG_I2C_HISI_V110 is not set ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++CONFIG_HI_I2C=y ++CONFIG_HI_I2C0_IO_BASE=0x120c0000 ++CONFIG_HI_I2C0_IO_SIZE=0x1000 ++CONFIG_HI_I2C_RETRIES=0x1 ++CONFIG_HI_I2C_TX_FIFO=0x8 ++CONFIG_HI_I2C_RX_FIFO=0x8 ++CONFIG_HI_I2C0_CLK_LIMIT=100000 ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_PINCTRL=y ++ ++# ++# Pin controllers ++# ++CONFIG_PINMUX=y ++CONFIG_PINCONF=y ++CONFIG_GENERIC_PINCONF=y ++# CONFIG_DEBUG_PINCTRL is not set ++CONFIG_PINCTRL_SINGLE=y ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++# CONFIG_USB_GADGET is not set ++# CONFIG_UWB is not set ++# CONFIG_MMC is not set ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++# CONFIG_PHY_HISI_INNO_USB2 is not set ++CONFIG_PHY_HI35x1D_INNO_USB2=y ++CONFIG_HI_NANO_PHY_SATA=y ++CONFIG_HI_SATA_PORTS=2 ++CONFIG_HI_SATA_MODE=1 ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++CONFIG_ISO9660_FS=y ++# CONFIG_JOLIET is not set ++# CONFIG_ZISOFS is not set ++CONFIG_UDF_FS=y ++CONFIG_UDF_NLS=y ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++CONFIG_CRC_ITU_T=y ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_GLOB=y ++# CONFIG_GLOB_SELFTEST is not set ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3531d_full_defconfig b/arch/arm/configs/hi3531d_full_defconfig +new file mode 100644 +index 0000000..309c44f +--- /dev/null ++++ b/arch/arm/configs/hi3531d_full_defconfig +@@ -0,0 +1,2611 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_NEED_MACH_IO_H=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_HZ_PERIODIC=y ++# CONFIG_NO_HZ_IDLE is not set ++# CONFIG_NO_HZ_FULL is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_NOCB_CPU is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++CONFIG_PCI_QUIRKS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++# CONFIG_ARCH_HI3556 is not set ++# CONFIG_ARCH_HI3536C is not set ++CONFIG_ARCH_HI3531D=y ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_OUTER_CACHE=y ++CONFIG_OUTER_CACHE_SYNC=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++CONFIG_CACHE_L2X0=y ++CONFIG_CACHE_PL310=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_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_643719 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_754327 is not set ++# CONFIG_ARM_ERRATA_764369 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_798181 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++CONFIG_PCI=y ++CONFIG_PCI_SYSCALL=y ++# CONFIG_PCI_MSI is not set ++# CONFIG_PCI_DEBUG is not set ++# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set ++# CONFIG_PCI_STUB is not set ++# CONFIG_PCI_IOV is not set ++# CONFIG_PCI_PRI is not set ++# CONFIG_PCI_PASID is not set ++ ++# ++# PCI host controller drivers ++# ++# CONFIG_PCI_HOST_GENERIC is not set ++CONFIG_PCIEPORTBUS=y ++CONFIG_PCIEAER=y ++# CONFIG_PCIE_ECRC is not set ++# CONFIG_PCIEAER_INJECT is not set ++CONFIG_PCIEASPM=y ++# CONFIG_PCIEASPM_DEBUG is not set ++CONFIG_PCIEASPM_DEFAULT=y ++# CONFIG_PCIEASPM_POWERSAVE is not set ++# CONFIG_PCIEASPM_PERFORMANCE is not set ++# CONFIG_PCCARD is not set ++CONFIG_HIPCIE=y ++ ++# ++# PCI Express configs ++# ++CONFIG_PCIE0_SEL=1 ++CONFIG_PCIE0_DEVICES_MEM_SIZE=0x8000000 ++CONFIG_PCIE0_DEVICES_CONFIG_SIZE=0x8000000 ++CONFIG_LIMIT_MAX_RD_REQ_SIZE=y ++CONFIG_PCIE1_SEL=1 ++CONFIG_PCIE1_DEVICES_MEM_SIZE=0x7800000 ++CONFIG_PCIE1_DEVICES_CONFIG_SIZE=0x800000 ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++# CONFIG_SCHED_MC is not set ++# CONFIG_SCHED_SMT is not set ++# CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE is not set ++CONFIG_HAVE_ARM_SCU=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++# CONFIG_MCPM is not set ++# CONFIG_BIG_LITTLE is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++# CONFIG_CLEANCACHE is not set ++# CONFIG_CMA is not set ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_INTERACTIVE is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++CONFIG_CPUFREQ_DT=y ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++# CONFIG_FPE_NWFPE is not set ++# CONFIG_FPE_FASTFPE is not set ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++# CONFIG_HISI_SNAPSHOT_BOOT is not set ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_IP_MROUTE is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++# CONFIG_WIRELESS is not set ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++# CONFIG_DEVTMPFS_MOUNT is not set ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++# CONFIG_DMA_SHARED_BUFFER is not set ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++# CONFIG_ARM_CCI is not set ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_INTEL_VR_NOR is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_PMC551 is not set ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_RICOH is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_CAFE is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_MTD_NAND_HINFC610 is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++# CONFIG_CLOSE_SPI_8PIN_4IO is not set ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++# CONFIG_MTD_UBI is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_ADDRESS_PCI=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_PCI=y ++CONFIG_OF_PCI_IRQ=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set ++# CONFIG_BLK_CPQ_CISS_DA is not set ++# CONFIG_BLK_DEV_DAC960 is not set ++# CONFIG_BLK_DEV_UMEM is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_NVME is not set ++# CONFIG_BLK_DEV_SX8 is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++# CONFIG_BLK_DEV_RSXX is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_PHANTOM is not set ++# CONFIG_SGI_IOC4 is not set ++# CONFIG_TIFM_CORE is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_HP_ILO is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++# CONFIG_CB710_CORE is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++CONFIG_HAVE_IDE=y ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++CONFIG_BLK_DEV_SR=y ++# CONFIG_BLK_DEV_SR_VENDOR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++CONFIG_HI_SATA=y ++CONFIG_HI_SATA_IOBASE=0x11010000 ++CONFIG_HI_SATA_FBS=1 ++CONFIG_HI_SATA_NCQ=1 ++CONFIG_ATA=y ++# CONFIG_ATA_NONSTANDARD is not set ++CONFIG_ATA_VERBOSE_ERROR=y ++CONFIG_SATA_PMP=y ++ ++# ++# Controllers with non-SFF native interface ++# ++# CONFIG_SATA_AHCI is not set ++CONFIG_SATA_AHCI_PLATFORM=y ++# CONFIG_SATA_INIC162X is not set ++# CONFIG_SATA_ACARD_AHCI is not set ++# CONFIG_SATA_SIL24 is not set ++# CONFIG_ATA_SFF is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++# CONFIG_FUSION is not set ++ ++# ++# IEEE 1394 (FireWire) support ++# ++# CONFIG_FIREWIRE is not set ++# CONFIG_FIREWIRE_NOSY is not set ++# CONFIG_I2O is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_FC is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++# CONFIG_ARCNET is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_NET_VENDOR_3COM is not set ++# CONFIG_NET_VENDOR_ADAPTEC is not set ++# CONFIG_NET_VENDOR_AGERE is not set ++# CONFIG_NET_VENDOR_ALTEON is not set ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_VENDOR_AMD is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_VENDOR_ATHEROS is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_BROCADE is not set ++# CONFIG_NET_VENDOR_CHELSIO is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_NET_VENDOR_CISCO is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_DEC is not set ++# CONFIG_NET_VENDOR_DLINK is not set ++# CONFIG_NET_VENDOR_EMULEX is not set ++# CONFIG_NET_VENDOR_EXAR is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++CONFIG_HIETH_GMAC=y ++CONFIG_HIGMAC_DESC_4WORD=y ++CONFIG_HIGMAC_RXCSUM=y ++CONFIG_RX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF ++CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF ++CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 ++CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_HP is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_IP1000 is not set ++# CONFIG_JME is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MELLANOX is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_MYRI is not set ++# CONFIG_FEALNX is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_NET_VENDOR_NVIDIA is not set ++# CONFIG_NET_VENDOR_OKI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_PACKET_ENGINE is not set ++# CONFIG_NET_VENDOR_QLOGIC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_REALTEK is not set ++# CONFIG_NET_VENDOR_RDC is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SILAN is not set ++# CONFIG_NET_VENDOR_SIS is not set ++# CONFIG_SFC is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_SUN is not set ++# CONFIG_NET_VENDOR_TEHUTI is not set ++# CONFIG_NET_VENDOR_TI is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++# CONFIG_FDDI is not set ++# CONFIG_HIPPI is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++CONFIG_MDIO_HISI_GEMAC=y ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_VMXNET3 is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++CONFIG_INPUT_JOYDEV=y ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++# CONFIG_SERIO_PCIPS2 is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_NOZOMI is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++# CONFIG_SERIAL_MFD_HSU is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_JSM is not set ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_RP2 is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_APPLICOM is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++CONFIG_DEVPORT=y ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PINCTRL is not set ++CONFIG_I2C_HELPER_AUTO=y ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# PC SMBus host controller drivers ++# ++# CONFIG_I2C_ALI1535 is not set ++# CONFIG_I2C_ALI1563 is not set ++# CONFIG_I2C_ALI15X3 is not set ++# CONFIG_I2C_AMD756 is not set ++# CONFIG_I2C_AMD8111 is not set ++# CONFIG_I2C_I801 is not set ++# CONFIG_I2C_ISCH is not set ++# CONFIG_I2C_PIIX4 is not set ++# CONFIG_I2C_NFORCE2 is not set ++# CONFIG_I2C_SIS5595 is not set ++# CONFIG_I2C_SIS630 is not set ++# CONFIG_I2C_SIS96X is not set ++# CONFIG_I2C_VIA is not set ++# CONFIG_I2C_VIAPRO is not set ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_DESIGNWARE_PCI is not set ++# CONFIG_I2C_HIBVT is not set ++# CONFIG_I2C_HISI_V110 is not set ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++CONFIG_HI_I2C=y ++CONFIG_HI_I2C0_IO_BASE=0x120c0000 ++CONFIG_HI_I2C0_IO_SIZE=0x1000 ++CONFIG_HI_I2C1_IO_BASE=0x122e0000 ++CONFIG_HI_I2C1_IO_SIZE=0x1000 ++CONFIG_HI_I2C_RETRIES=0x1 ++CONFIG_HI_I2C_TX_FIFO=0x8 ++CONFIG_HI_I2C_RX_FIFO=0x8 ++CONFIG_HI_I2C0_CLK_LIMIT=100000 ++CONFIG_HI_I2C1_CLK_LIMIT=100000 ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX is not set ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_PINCTRL=y ++ ++# ++# Pin controllers ++# ++CONFIG_PINMUX=y ++CONFIG_PINCONF=y ++CONFIG_GENERIC_PINCONF=y ++# CONFIG_DEBUG_PINCTRL is not set ++CONFIG_PINCTRL_SINGLE=y ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_LPC_ICH is not set ++# CONFIG_LPC_SCH is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_JANZ_CMODIO is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RDC321X is not set ++# CONFIG_MFD_RTSX_PCI is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_VX855 is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++CONFIG_VGA_ARB=y ++CONFIG_VGA_ARB_MAX_GPUS=16 ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_CIRRUS is not set ++# CONFIG_FB_PM2 is not set ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_CYBER2000 is not set ++# CONFIG_FB_ASILIANT is not set ++# CONFIG_FB_IMSTT is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_NVIDIA is not set ++# CONFIG_FB_RIVA is not set ++# CONFIG_FB_I740 is not set ++# CONFIG_FB_MATROX is not set ++# CONFIG_FB_RADEON is not set ++# CONFIG_FB_ATY128 is not set ++# CONFIG_FB_ATY is not set ++# CONFIG_FB_S3 is not set ++# CONFIG_FB_SAVAGE is not set ++# CONFIG_FB_SIS is not set ++# CONFIG_FB_NEOMAGIC is not set ++# CONFIG_FB_KYRO is not set ++# CONFIG_FB_3DFX is not set ++# CONFIG_FB_VOODOO1 is not set ++# CONFIG_FB_VT8623 is not set ++# CONFIG_FB_TRIDENT is not set ++# CONFIG_FB_ARK is not set ++# CONFIG_FB_PM3 is not set ++# CONFIG_FB_CARMINE is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_MB862XX is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_PCI=y ++CONFIG_USB_XHCI_PLATFORM=y ++CONFIG_USB_XHCI_HISILICON=y ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_PCI=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PCI=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_UHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++# CONFIG_USB_GADGET is not set ++# CONFIG_UWB is not set ++# CONFIG_MMC is not set ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_INFINIBAND is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_PCI is not set ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_VME_BUS is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++# CONFIG_PHY_HISI_INNO_USB2 is not set ++CONFIG_PHY_HI35x1D_INNO_USB2=y ++CONFIG_PHY_HI3531D_USB3=y ++CONFIG_HI_NANO_PHY_SATA=y ++CONFIG_HI_SATA_PORTS=4 ++CONFIG_HI_SATA_MODE=1 ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_RAS=y ++# CONFIG_THUNDERBOLT is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++CONFIG_ISO9660_FS=y ++# CONFIG_JOLIET is not set ++# CONFIG_ZISOFS is not set ++CONFIG_UDF_FS=y ++CONFIG_UDF_NLS=y ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_CRYPTO_DEV_HIFN_795X is not set ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++CONFIG_CRC_ITU_T=y ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_GLOB=y ++# CONFIG_GLOB_SELFTEST is not set ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3531d_full_slave_defconfig b/arch/arm/configs/hi3531d_full_slave_defconfig +new file mode 100644 +index 0000000..30e6c6f +--- /dev/null ++++ b/arch/arm/configs/hi3531d_full_slave_defconfig +@@ -0,0 +1,2437 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_HZ_PERIODIC=y ++# CONFIG_NO_HZ_IDLE is not set ++# CONFIG_NO_HZ_FULL is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_NOCB_CPU is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++# CONFIG_ARCH_HI3556 is not set ++# CONFIG_ARCH_HI3536C is not set ++CONFIG_ARCH_HI3531D=y ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_OUTER_CACHE=y ++CONFIG_OUTER_CACHE_SYNC=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++CONFIG_CACHE_L2X0=y ++CONFIG_CACHE_PL310=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_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_643719 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_754327 is not set ++# CONFIG_ARM_ERRATA_764369 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_798181 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++# CONFIG_SCHED_MC is not set ++# CONFIG_SCHED_SMT is not set ++# CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE is not set ++CONFIG_HAVE_ARM_SCU=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++# CONFIG_MCPM is not set ++# CONFIG_BIG_LITTLE is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++# CONFIG_CLEANCACHE is not set ++# CONFIG_CMA is not set ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_INTERACTIVE is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++CONFIG_CPUFREQ_DT=y ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++# CONFIG_FPE_NWFPE is not set ++# CONFIG_FPE_FASTFPE is not set ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++# CONFIG_HISI_SNAPSHOT_BOOT is not set ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_IP_MROUTE is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++# CONFIG_WIRELESS is not set ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++# CONFIG_DEVTMPFS_MOUNT is not set ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++# CONFIG_DMA_SHARED_BUFFER is not set ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++# CONFIG_ARM_CCI is not set ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_MTD_NAND_HINFC610 is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++# CONFIG_CLOSE_SPI_8PIN_4IO is not set ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++# CONFIG_MTD_UBI is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++CONFIG_BLK_DEV_SR=y ++# CONFIG_BLK_DEV_SR_VENDOR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++CONFIG_HI_SATA=y ++CONFIG_HI_SATA_IOBASE=0x11010000 ++CONFIG_HI_SATA_FBS=1 ++CONFIG_HI_SATA_NCQ=1 ++CONFIG_ATA=y ++# CONFIG_ATA_NONSTANDARD is not set ++CONFIG_ATA_VERBOSE_ERROR=y ++CONFIG_SATA_PMP=y ++ ++# ++# Controllers with non-SFF native interface ++# ++CONFIG_SATA_AHCI_PLATFORM=y ++# CONFIG_ATA_SFF is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++CONFIG_HIETH_GMAC=y ++CONFIG_HIGMAC_DESC_4WORD=y ++CONFIG_HIGMAC_RXCSUM=y ++CONFIG_RX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF ++CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF ++CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 ++CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++CONFIG_MDIO_HISI_GEMAC=y ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++CONFIG_INPUT_JOYDEV=y ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PINCTRL is not set ++CONFIG_I2C_HELPER_AUTO=y ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_HIBVT is not set ++# CONFIG_I2C_HISI_V110 is not set ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++CONFIG_HI_I2C=y ++CONFIG_HI_I2C0_IO_BASE=0x120c0000 ++CONFIG_HI_I2C0_IO_SIZE=0x1000 ++CONFIG_HI_I2C1_IO_BASE=0x122e0000 ++CONFIG_HI_I2C1_IO_SIZE=0x1000 ++CONFIG_HI_I2C_RETRIES=0x1 ++CONFIG_HI_I2C_TX_FIFO=0x8 ++CONFIG_HI_I2C_RX_FIFO=0x8 ++CONFIG_HI_I2C0_CLK_LIMIT=100000 ++CONFIG_HI_I2C1_CLK_LIMIT=100000 ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_PINCTRL=y ++ ++# ++# Pin controllers ++# ++CONFIG_PINMUX=y ++CONFIG_PINCONF=y ++CONFIG_GENERIC_PINCONF=y ++# CONFIG_DEBUG_PINCTRL is not set ++CONFIG_PINCTRL_SINGLE=y ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_PLATFORM=y ++CONFIG_USB_XHCI_HISILICON=y ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++# CONFIG_USB_GADGET is not set ++# CONFIG_UWB is not set ++# CONFIG_MMC is not set ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++# CONFIG_PHY_HISI_INNO_USB2 is not set ++CONFIG_PHY_HI35x1D_INNO_USB2=y ++CONFIG_PHY_HI3531D_USB3=y ++CONFIG_HI_NANO_PHY_SATA=y ++CONFIG_HI_SATA_PORTS=4 ++CONFIG_HI_SATA_MODE=1 ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++CONFIG_ISO9660_FS=y ++# CONFIG_JOLIET is not set ++# CONFIG_ZISOFS is not set ++CONFIG_UDF_FS=y ++CONFIG_UDF_NLS=y ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++CONFIG_CRC_ITU_T=y ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_GLOB=y ++# CONFIG_GLOB_SELFTEST is not set ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3531d_nand_defconfig b/arch/arm/configs/hi3531d_nand_defconfig +new file mode 100644 +index 0000000..a88a13d +--- /dev/null ++++ b/arch/arm/configs/hi3531d_nand_defconfig +@@ -0,0 +1,2603 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_NEED_MACH_IO_H=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_HZ_PERIODIC=y ++# CONFIG_NO_HZ_IDLE is not set ++# CONFIG_NO_HZ_FULL is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_NOCB_CPU is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++CONFIG_PCI_QUIRKS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++# CONFIG_ARCH_HI3556 is not set ++# CONFIG_ARCH_HI3536C is not set ++CONFIG_ARCH_HI3531D=y ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_OUTER_CACHE=y ++CONFIG_OUTER_CACHE_SYNC=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++CONFIG_CACHE_L2X0=y ++CONFIG_CACHE_PL310=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_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_643719 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_754327 is not set ++# CONFIG_ARM_ERRATA_764369 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_798181 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++CONFIG_PCI=y ++CONFIG_PCI_SYSCALL=y ++# CONFIG_PCI_MSI is not set ++# CONFIG_PCI_DEBUG is not set ++# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set ++# CONFIG_PCI_STUB is not set ++# CONFIG_PCI_IOV is not set ++# CONFIG_PCI_PRI is not set ++# CONFIG_PCI_PASID is not set ++ ++# ++# PCI host controller drivers ++# ++# CONFIG_PCI_HOST_GENERIC is not set ++CONFIG_PCIEPORTBUS=y ++CONFIG_PCIEAER=y ++# CONFIG_PCIE_ECRC is not set ++# CONFIG_PCIEAER_INJECT is not set ++CONFIG_PCIEASPM=y ++# CONFIG_PCIEASPM_DEBUG is not set ++CONFIG_PCIEASPM_DEFAULT=y ++# CONFIG_PCIEASPM_POWERSAVE is not set ++# CONFIG_PCIEASPM_PERFORMANCE is not set ++# CONFIG_PCCARD is not set ++CONFIG_HIPCIE=y ++ ++# ++# PCI Express configs ++# ++CONFIG_PCIE0_SEL=1 ++CONFIG_PCIE0_DEVICES_MEM_SIZE=0x8000000 ++CONFIG_PCIE0_DEVICES_CONFIG_SIZE=0x8000000 ++CONFIG_LIMIT_MAX_RD_REQ_SIZE=y ++CONFIG_PCIE1_SEL=1 ++CONFIG_PCIE1_DEVICES_MEM_SIZE=0x8000000 ++CONFIG_PCIE1_DEVICES_CONFIG_SIZE=0x8000000 ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++# CONFIG_SCHED_MC is not set ++# CONFIG_SCHED_SMT is not set ++# CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE is not set ++CONFIG_HAVE_ARM_SCU=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++# CONFIG_MCPM is not set ++# CONFIG_BIG_LITTLE is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++# CONFIG_CLEANCACHE is not set ++# CONFIG_CMA is not set ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_INTERACTIVE is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++CONFIG_CPUFREQ_DT=y ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++# CONFIG_FPE_NWFPE is not set ++# CONFIG_FPE_FASTFPE is not set ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++# CONFIG_HISI_SNAPSHOT_BOOT is not set ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_IP_MROUTE is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++# CONFIG_WIRELESS is not set ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++# CONFIG_DEVTMPFS_MOUNT is not set ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++# CONFIG_DMA_SHARED_BUFFER is not set ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++# CONFIG_ARM_CCI is not set ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_HIFMC is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_INTEL_VR_NOR is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_PMC551 is not set ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_RICOH is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_CAFE is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++CONFIG_MTD_NAND_HINFC610=y ++# CONFIG_MTD_PARTITION_FROM_DTS is not set ++CONFIG_HINFC610_MAX_CHIP=1 ++# CONFIG_HINFC610_DBG_NAND_DEBUG is not set ++CONFIG_HINFC610_AUTO_PAGESIZE_ECC=y ++# CONFIG_HINFC610_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++# CONFIG_MTD_SPI_NOR is not set ++# CONFIG_MTD_UBI is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_ADDRESS_PCI=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_PCI=y ++CONFIG_OF_PCI_IRQ=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set ++# CONFIG_BLK_CPQ_CISS_DA is not set ++# CONFIG_BLK_DEV_DAC960 is not set ++# CONFIG_BLK_DEV_UMEM is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_NVME is not set ++# CONFIG_BLK_DEV_SX8 is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++# CONFIG_BLK_DEV_RSXX is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_PHANTOM is not set ++# CONFIG_SGI_IOC4 is not set ++# CONFIG_TIFM_CORE is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_HP_ILO is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++# CONFIG_CB710_CORE is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++CONFIG_HAVE_IDE=y ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++CONFIG_BLK_DEV_SR=y ++# CONFIG_BLK_DEV_SR_VENDOR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++CONFIG_HI_SATA=y ++CONFIG_HI_SATA_IOBASE=0x11010000 ++CONFIG_HI_SATA_FBS=1 ++CONFIG_HI_SATA_NCQ=1 ++CONFIG_ATA=y ++# CONFIG_ATA_NONSTANDARD is not set ++CONFIG_ATA_VERBOSE_ERROR=y ++CONFIG_SATA_PMP=y ++ ++# ++# Controllers with non-SFF native interface ++# ++# CONFIG_SATA_AHCI is not set ++CONFIG_SATA_AHCI_PLATFORM=y ++# CONFIG_SATA_INIC162X is not set ++# CONFIG_SATA_ACARD_AHCI is not set ++# CONFIG_SATA_SIL24 is not set ++# CONFIG_ATA_SFF is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++# CONFIG_FUSION is not set ++ ++# ++# IEEE 1394 (FireWire) support ++# ++# CONFIG_FIREWIRE is not set ++# CONFIG_FIREWIRE_NOSY is not set ++# CONFIG_I2O is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_FC is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++# CONFIG_ARCNET is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_NET_VENDOR_3COM is not set ++# CONFIG_NET_VENDOR_ADAPTEC is not set ++# CONFIG_NET_VENDOR_AGERE is not set ++# CONFIG_NET_VENDOR_ALTEON is not set ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_VENDOR_AMD is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_VENDOR_ATHEROS is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_BROCADE is not set ++# CONFIG_NET_VENDOR_CHELSIO is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_NET_VENDOR_CISCO is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_DEC is not set ++# CONFIG_NET_VENDOR_DLINK is not set ++# CONFIG_NET_VENDOR_EMULEX is not set ++# CONFIG_NET_VENDOR_EXAR is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++CONFIG_HIETH_GMAC=y ++CONFIG_HIGMAC_DESC_4WORD=y ++CONFIG_HIGMAC_RXCSUM=y ++CONFIG_RX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF ++CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF ++CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 ++CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_HP is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_IP1000 is not set ++# CONFIG_JME is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MELLANOX is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_MYRI is not set ++# CONFIG_FEALNX is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_NET_VENDOR_NVIDIA is not set ++# CONFIG_NET_VENDOR_OKI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_PACKET_ENGINE is not set ++# CONFIG_NET_VENDOR_QLOGIC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_REALTEK is not set ++# CONFIG_NET_VENDOR_RDC is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SILAN is not set ++# CONFIG_NET_VENDOR_SIS is not set ++# CONFIG_SFC is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_SUN is not set ++# CONFIG_NET_VENDOR_TEHUTI is not set ++# CONFIG_NET_VENDOR_TI is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++# CONFIG_FDDI is not set ++# CONFIG_HIPPI is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++CONFIG_MDIO_HISI_GEMAC=y ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_VMXNET3 is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++CONFIG_INPUT_JOYDEV=y ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++# CONFIG_SERIO_PCIPS2 is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_NOZOMI is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++# CONFIG_SERIAL_MFD_HSU is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_JSM is not set ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_RP2 is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_APPLICOM is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++CONFIG_DEVPORT=y ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PINCTRL is not set ++CONFIG_I2C_HELPER_AUTO=y ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# PC SMBus host controller drivers ++# ++# CONFIG_I2C_ALI1535 is not set ++# CONFIG_I2C_ALI1563 is not set ++# CONFIG_I2C_ALI15X3 is not set ++# CONFIG_I2C_AMD756 is not set ++# CONFIG_I2C_AMD8111 is not set ++# CONFIG_I2C_I801 is not set ++# CONFIG_I2C_ISCH is not set ++# CONFIG_I2C_PIIX4 is not set ++# CONFIG_I2C_NFORCE2 is not set ++# CONFIG_I2C_SIS5595 is not set ++# CONFIG_I2C_SIS630 is not set ++# CONFIG_I2C_SIS96X is not set ++# CONFIG_I2C_VIA is not set ++# CONFIG_I2C_VIAPRO is not set ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_DESIGNWARE_PCI is not set ++# CONFIG_I2C_HIBVT is not set ++# CONFIG_I2C_HISI_V110 is not set ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++CONFIG_HI_I2C=y ++CONFIG_HI_I2C0_IO_BASE=0x120c0000 ++CONFIG_HI_I2C0_IO_SIZE=0x1000 ++CONFIG_HI_I2C1_IO_BASE=0x122e0000 ++CONFIG_HI_I2C1_IO_SIZE=0x1000 ++CONFIG_HI_I2C_RETRIES=0x1 ++CONFIG_HI_I2C_TX_FIFO=0x8 ++CONFIG_HI_I2C_RX_FIFO=0x8 ++CONFIG_HI_I2C0_CLK_LIMIT=100000 ++CONFIG_HI_I2C1_CLK_LIMIT=100000 ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX is not set ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_PINCTRL=y ++ ++# ++# Pin controllers ++# ++CONFIG_PINMUX=y ++CONFIG_PINCONF=y ++CONFIG_GENERIC_PINCONF=y ++# CONFIG_DEBUG_PINCTRL is not set ++CONFIG_PINCTRL_SINGLE=y ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_LPC_ICH is not set ++# CONFIG_LPC_SCH is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_JANZ_CMODIO is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RDC321X is not set ++# CONFIG_MFD_RTSX_PCI is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_VX855 is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++CONFIG_VGA_ARB=y ++CONFIG_VGA_ARB_MAX_GPUS=16 ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_CIRRUS is not set ++# CONFIG_FB_PM2 is not set ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_CYBER2000 is not set ++# CONFIG_FB_ASILIANT is not set ++# CONFIG_FB_IMSTT is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_NVIDIA is not set ++# CONFIG_FB_RIVA is not set ++# CONFIG_FB_I740 is not set ++# CONFIG_FB_MATROX is not set ++# CONFIG_FB_RADEON is not set ++# CONFIG_FB_ATY128 is not set ++# CONFIG_FB_ATY is not set ++# CONFIG_FB_S3 is not set ++# CONFIG_FB_SAVAGE is not set ++# CONFIG_FB_SIS is not set ++# CONFIG_FB_NEOMAGIC is not set ++# CONFIG_FB_KYRO is not set ++# CONFIG_FB_3DFX is not set ++# CONFIG_FB_VOODOO1 is not set ++# CONFIG_FB_VT8623 is not set ++# CONFIG_FB_TRIDENT is not set ++# CONFIG_FB_ARK is not set ++# CONFIG_FB_PM3 is not set ++# CONFIG_FB_CARMINE is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_MB862XX is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_PCI=y ++CONFIG_USB_XHCI_PLATFORM=y ++CONFIG_USB_XHCI_HISILICON=y ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_PCI=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PCI=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_UHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++# CONFIG_USB_GADGET is not set ++# CONFIG_UWB is not set ++# CONFIG_MMC is not set ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_INFINIBAND is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_PCI is not set ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_VME_BUS is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++# CONFIG_PHY_HISI_INNO_USB2 is not set ++CONFIG_PHY_HI35x1D_INNO_USB2=y ++CONFIG_PHY_HI3531D_USB3=y ++CONFIG_HI_NANO_PHY_SATA=y ++CONFIG_HI_SATA_PORTS=4 ++CONFIG_HI_SATA_MODE=1 ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_RAS=y ++# CONFIG_THUNDERBOLT is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++CONFIG_ISO9660_FS=y ++# CONFIG_JOLIET is not set ++# CONFIG_ZISOFS is not set ++CONFIG_UDF_FS=y ++CONFIG_UDF_NLS=y ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_CRYPTO_DEV_HIFN_795X is not set ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++CONFIG_CRC_ITU_T=y ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_GLOB=y ++# CONFIG_GLOB_SELFTEST is not set ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3531d_nand_slave_defconfig b/arch/arm/configs/hi3531d_nand_slave_defconfig +new file mode 100644 +index 0000000..aa7138a +--- /dev/null ++++ b/arch/arm/configs/hi3531d_nand_slave_defconfig +@@ -0,0 +1,2429 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_HZ_PERIODIC=y ++# CONFIG_NO_HZ_IDLE is not set ++# CONFIG_NO_HZ_FULL is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_NOCB_CPU is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++# CONFIG_ARCH_HI3556 is not set ++# CONFIG_ARCH_HI3536C is not set ++CONFIG_ARCH_HI3531D=y ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_OUTER_CACHE=y ++CONFIG_OUTER_CACHE_SYNC=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++CONFIG_CACHE_L2X0=y ++CONFIG_CACHE_PL310=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_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_643719 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_754327 is not set ++# CONFIG_ARM_ERRATA_764369 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_798181 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++# CONFIG_SCHED_MC is not set ++# CONFIG_SCHED_SMT is not set ++# CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE is not set ++CONFIG_HAVE_ARM_SCU=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++# CONFIG_MCPM is not set ++# CONFIG_BIG_LITTLE is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++# CONFIG_CLEANCACHE is not set ++# CONFIG_CMA is not set ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_INTERACTIVE is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++CONFIG_CPUFREQ_DT=y ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++# CONFIG_FPE_NWFPE is not set ++# CONFIG_FPE_FASTFPE is not set ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++# CONFIG_HISI_SNAPSHOT_BOOT is not set ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_IP_MROUTE is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++# CONFIG_WIRELESS is not set ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++# CONFIG_DEVTMPFS_MOUNT is not set ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++# CONFIG_DMA_SHARED_BUFFER is not set ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++# CONFIG_ARM_CCI is not set ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_HIFMC is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++CONFIG_MTD_NAND_HINFC610=y ++# CONFIG_MTD_PARTITION_FROM_DTS is not set ++CONFIG_HINFC610_MAX_CHIP=1 ++# CONFIG_HINFC610_DBG_NAND_DEBUG is not set ++CONFIG_HINFC610_AUTO_PAGESIZE_ECC=y ++# CONFIG_HINFC610_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++# CONFIG_MTD_SPI_NOR is not set ++# CONFIG_MTD_UBI is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++CONFIG_BLK_DEV_SR=y ++# CONFIG_BLK_DEV_SR_VENDOR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++CONFIG_HI_SATA=y ++CONFIG_HI_SATA_IOBASE=0x11010000 ++CONFIG_HI_SATA_FBS=1 ++CONFIG_HI_SATA_NCQ=1 ++CONFIG_ATA=y ++# CONFIG_ATA_NONSTANDARD is not set ++CONFIG_ATA_VERBOSE_ERROR=y ++CONFIG_SATA_PMP=y ++ ++# ++# Controllers with non-SFF native interface ++# ++CONFIG_SATA_AHCI_PLATFORM=y ++# CONFIG_ATA_SFF is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++CONFIG_HIETH_GMAC=y ++CONFIG_HIGMAC_DESC_4WORD=y ++CONFIG_HIGMAC_RXCSUM=y ++CONFIG_RX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF ++CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF ++CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 ++CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++CONFIG_MDIO_HISI_GEMAC=y ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++CONFIG_INPUT_JOYDEV=y ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PINCTRL is not set ++CONFIG_I2C_HELPER_AUTO=y ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_HIBVT is not set ++# CONFIG_I2C_HISI_V110 is not set ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++CONFIG_HI_I2C=y ++CONFIG_HI_I2C0_IO_BASE=0x120c0000 ++CONFIG_HI_I2C0_IO_SIZE=0x1000 ++CONFIG_HI_I2C1_IO_BASE=0x122e0000 ++CONFIG_HI_I2C1_IO_SIZE=0x1000 ++CONFIG_HI_I2C_RETRIES=0x1 ++CONFIG_HI_I2C_TX_FIFO=0x8 ++CONFIG_HI_I2C_RX_FIFO=0x8 ++CONFIG_HI_I2C0_CLK_LIMIT=100000 ++CONFIG_HI_I2C1_CLK_LIMIT=100000 ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_PINCTRL=y ++ ++# ++# Pin controllers ++# ++CONFIG_PINMUX=y ++CONFIG_PINCONF=y ++CONFIG_GENERIC_PINCONF=y ++# CONFIG_DEBUG_PINCTRL is not set ++CONFIG_PINCTRL_SINGLE=y ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_XHCI_PLATFORM=y ++CONFIG_USB_XHCI_HISILICON=y ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++# CONFIG_USB_GADGET is not set ++# CONFIG_UWB is not set ++# CONFIG_MMC is not set ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++# CONFIG_PHY_HISI_INNO_USB2 is not set ++CONFIG_PHY_HI35x1D_INNO_USB2=y ++CONFIG_PHY_HI3531D_USB3=y ++CONFIG_HI_NANO_PHY_SATA=y ++CONFIG_HI_SATA_PORTS=4 ++CONFIG_HI_SATA_MODE=1 ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++CONFIG_ISO9660_FS=y ++# CONFIG_JOLIET is not set ++# CONFIG_ZISOFS is not set ++CONFIG_UDF_FS=y ++CONFIG_UDF_NLS=y ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++# CONFIG_CRYPTO_DEFLATE is not set ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++CONFIG_CRC_ITU_T=y ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_GLOB=y ++# CONFIG_GLOB_SELFTEST is not set ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3536c_full_defconfig b/arch/arm/configs/hi3536c_full_defconfig +new file mode 100644 +index 0000000..9f8b73a +--- /dev/null ++++ b/arch/arm/configs/hi3536c_full_defconfig +@@ -0,0 +1,2522 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_ARCH_HAS_TICK_BROADCAST=y ++CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y ++ ++# ++# Timers subsystem ++# ++CONFIG_HZ_PERIODIC=y ++# CONFIG_NO_HZ_IDLE is not set ++# CONFIG_NO_HZ_FULL is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TREE_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++CONFIG_RCU_STALL_COMMON=y ++# CONFIG_RCU_USER_QS is not set ++CONFIG_RCU_FANOUT=32 ++CONFIG_RCU_FANOUT_LEAF=16 ++# CONFIG_RCU_FANOUT_EXACT is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_RCU_NOCB_CPU is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++CONFIG_SLUB_CPU_PARTIAL=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_STOP_MACHINE=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_MUTEX_SPIN_ON_OWNER=y ++CONFIG_RWSEM_SPIN_ON_OWNER=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++# CONFIG_ARCH_HI3556 is not set ++CONFIG_ARCH_HI3536C=y ++# CONFIG_ARCH_HI3531D is not set ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_643719 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_754327 is not set ++# CONFIG_ARM_ERRATA_764369 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_798181 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++CONFIG_SMP=y ++CONFIG_SMP_ON_UP=y ++CONFIG_ARM_CPU_TOPOLOGY=y ++# CONFIG_SCHED_MC is not set ++# CONFIG_SCHED_SMT is not set ++# CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE is not set ++CONFIG_HAVE_ARM_SCU=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++# CONFIG_MCPM is not set ++# CONFIG_BIG_LITTLE is not set ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_NR_CPUS=4 ++CONFIG_HOTPLUG_CPU=y ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++CONFIG_HZ_100=y ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++# CONFIG_HZ_1000 is not set ++CONFIG_HZ=100 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++# CONFIG_CLEANCACHE is not set ++# CONFIG_CMA is not set ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++CONFIG_AUTO_ZRELADDR=y ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_GOV_USERSPACE is not set ++# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set ++# CONFIG_CPU_FREQ_GOV_INTERACTIVE is not set ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++CONFIG_CPUFREQ_DT=y ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++# CONFIG_FPE_NWFPE is not set ++# CONFIG_FPE_FASTFPE is not set ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++# CONFIG_HISI_SNAPSHOT_BOOT is not set ++CONFIG_PM_SLEEP=y ++CONFIG_PM_SLEEP_SMP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_IP_MROUTE is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++CONFIG_RPS=y ++CONFIG_RFS_ACCEL=y ++CONFIG_XPS=y ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++CONFIG_NET_FLOW_LIMIT=y ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++# CONFIG_WIRELESS is not set ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++# CONFIG_DEVTMPFS_MOUNT is not set ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++# CONFIG_DMA_SHARED_BUFFER is not set ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++# CONFIG_ARM_CCI is not set ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++CONFIG_MTD_BLOCK2MTD=y ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_MTD_NAND_HINFC610 is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++CONFIG_CLOSE_SPI_8PIN_4IO=y ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++CONFIG_BLK_DEV_SR=y ++# CONFIG_BLK_DEV_SR_VENDOR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++CONFIG_HI_SATA=y ++CONFIG_HI_SATA_IOBASE=0x11010000 ++CONFIG_HI_SATA_FBS=1 ++CONFIG_HI_SATA_NCQ=1 ++CONFIG_ATA=y ++# CONFIG_ATA_NONSTANDARD is not set ++CONFIG_ATA_VERBOSE_ERROR=y ++CONFIG_SATA_PMP=y ++ ++# ++# Controllers with non-SFF native interface ++# ++CONFIG_SATA_AHCI_PLATFORM=y ++# CONFIG_ATA_SFF is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++CONFIG_ETHERNET=y ++# CONFIG_ALTERA_TSE is not set ++# CONFIG_NET_XGENE is not set ++# CONFIG_NET_VENDOR_ARC is not set ++# CONFIG_NET_CADENCE is not set ++# CONFIG_NET_VENDOR_BROADCOM is not set ++# CONFIG_NET_VENDOR_CIRRUS is not set ++# CONFIG_DM9000 is not set ++# CONFIG_DNET is not set ++# CONFIG_NET_VENDOR_FARADAY is not set ++CONFIG_NET_VENDOR_HISILICON=y ++# CONFIG_HIX5HD2_GMAC is not set ++# CONFIG_HISI_FEMAC is not set ++CONFIG_HIETH_GMAC=y ++CONFIG_HIGMAC_DESC_4WORD=y ++CONFIG_HIGMAC_RXCSUM=y ++CONFIG_RX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_SUPPORT=y ++CONFIG_TX_FLOW_CTRL_PAUSE_TIME=0xFFFF ++CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL=0xFFFF ++CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD=16 ++CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD=32 ++# CONFIG_HIETH_SWITCH_FABRIC is not set ++# CONFIG_NET_VENDOR_INTEL is not set ++# CONFIG_NET_VENDOR_MARVELL is not set ++# CONFIG_NET_VENDOR_MICREL is not set ++# CONFIG_NET_VENDOR_MICROCHIP is not set ++# CONFIG_NET_VENDOR_NATSEMI is not set ++# CONFIG_ETHOC is not set ++# CONFIG_NET_VENDOR_QUALCOMM is not set ++# CONFIG_NET_VENDOR_SAMSUNG is not set ++# CONFIG_NET_VENDOR_SEEQ is not set ++# CONFIG_NET_VENDOR_SMSC is not set ++# CONFIG_NET_VENDOR_STMICRO is not set ++# CONFIG_NET_VENDOR_VIA is not set ++# CONFIG_NET_VENDOR_WIZNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++CONFIG_MDIO_HISI_GEMAC=y ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++CONFIG_INPUT_JOYDEV=y ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++# CONFIG_DEVKMEM is not set ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++CONFIG_I2C_COMPAT=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=y ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PINCTRL is not set ++CONFIG_I2C_HELPER_AUTO=y ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_HIBVT is not set ++# CONFIG_I2C_HISI_V110 is not set ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++CONFIG_HI_I2C=y ++CONFIG_HI_I2C0_IO_BASE=0x120c0000 ++CONFIG_HI_I2C0_IO_SIZE=0x1000 ++CONFIG_HI_I2C_RETRIES=0x1 ++CONFIG_HI_I2C_TX_FIFO=0x8 ++CONFIG_HI_I2C_RX_FIFO=0x8 ++CONFIG_HI_I2C0_CLK_LIMIT=100000 ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_PINCTRL=y ++ ++# ++# Pin controllers ++# ++CONFIG_PINMUX=y ++CONFIG_PINCONF=y ++CONFIG_GENERIC_PINCONF=y ++# CONFIG_DEBUG_PINCTRL is not set ++CONFIG_PINCTRL_SINGLE=y ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_MANAGER is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++CONFIG_USB_DEFAULT_PERSIST=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++CONFIG_USB_EHCI_HCD=y ++# CONFIG_USB_EHCI_ROOT_HUB_TT is not set ++CONFIG_USB_EHCI_TT_NEWSCHED=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_SISUSBVGA is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++# CONFIG_USB_GADGET is not set ++# CONFIG_UWB is not set ++# CONFIG_MMC is not set ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_HCTOSYS=y ++CONFIG_RTC_SYSTOHC=y ++CONFIG_RTC_HCTOSYS_DEVICE="rtc0" ++# CONFIG_RTC_DEBUG is not set ++ ++# ++# RTC interfaces ++# ++CONFIG_RTC_INTF_SYSFS=y ++CONFIG_RTC_INTF_PROC=y ++CONFIG_RTC_INTF_DEV=y ++# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set ++# CONFIG_RTC_DRV_TEST is not set ++ ++# ++# I2C RTC drivers ++# ++# CONFIG_RTC_DRV_DS1307 is not set ++# CONFIG_RTC_DRV_DS1374 is not set ++# CONFIG_RTC_DRV_DS1672 is not set ++# CONFIG_RTC_DRV_DS3232 is not set ++# CONFIG_RTC_DRV_HYM8563 is not set ++# CONFIG_RTC_DRV_MAX6900 is not set ++# CONFIG_RTC_DRV_RS5C372 is not set ++# CONFIG_RTC_DRV_ISL1208 is not set ++# CONFIG_RTC_DRV_ISL12022 is not set ++# CONFIG_RTC_DRV_ISL12057 is not set ++# CONFIG_RTC_DRV_X1205 is not set ++# CONFIG_RTC_DRV_PCF2127 is not set ++# CONFIG_RTC_DRV_PCF8523 is not set ++# CONFIG_RTC_DRV_PCF8563 is not set ++# CONFIG_RTC_DRV_PCF85063 is not set ++# CONFIG_RTC_DRV_PCF8583 is not set ++# CONFIG_RTC_DRV_M41T80 is not set ++# CONFIG_RTC_DRV_BQ32K is not set ++# CONFIG_RTC_DRV_S35390A is not set ++# CONFIG_RTC_DRV_FM3130 is not set ++# CONFIG_RTC_DRV_RX8581 is not set ++# CONFIG_RTC_DRV_RX8025 is not set ++# CONFIG_RTC_DRV_EM3027 is not set ++# CONFIG_RTC_DRV_RV3029C2 is not set ++ ++# ++# SPI RTC drivers ++# ++# CONFIG_RTC_DRV_M41T93 is not set ++# CONFIG_RTC_DRV_M41T94 is not set ++# CONFIG_RTC_DRV_DS1305 is not set ++# CONFIG_RTC_DRV_DS1343 is not set ++# CONFIG_RTC_DRV_DS1347 is not set ++# CONFIG_RTC_DRV_DS1390 is not set ++# CONFIG_RTC_DRV_MAX6902 is not set ++# CONFIG_RTC_DRV_R9701 is not set ++# CONFIG_RTC_DRV_RS5C348 is not set ++# CONFIG_RTC_DRV_DS3234 is not set ++# CONFIG_RTC_DRV_PCF2123 is not set ++# CONFIG_RTC_DRV_RX4581 is not set ++# CONFIG_RTC_DRV_MCP795 is not set ++ ++# ++# Platform RTC drivers ++# ++CONFIG_RTC_DRV_HIBVT=y ++# CONFIG_RTC_DRV_CMOS is not set ++# CONFIG_RTC_DRV_DS1286 is not set ++# CONFIG_RTC_DRV_DS1511 is not set ++# CONFIG_RTC_DRV_DS1553 is not set ++# CONFIG_RTC_DRV_DS1742 is not set ++# CONFIG_RTC_DRV_DS2404 is not set ++# CONFIG_RTC_DRV_STK17TA8 is not set ++# CONFIG_RTC_DRV_M48T86 is not set ++# CONFIG_RTC_DRV_M48T35 is not set ++# CONFIG_RTC_DRV_M48T59 is not set ++# CONFIG_RTC_DRV_MSM6242 is not set ++# CONFIG_RTC_DRV_BQ4802 is not set ++# CONFIG_RTC_DRV_RP5C01 is not set ++# CONFIG_RTC_DRV_V3020 is not set ++ ++# ++# on-CPU RTC drivers ++# ++# CONFIG_RTC_DRV_PL030 is not set ++# CONFIG_RTC_DRV_PL031 is not set ++# CONFIG_RTC_DRV_SNVS is not set ++# CONFIG_RTC_DRV_XGENE is not set ++ ++# ++# HID Sensor RTC drivers ++# ++# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++CONFIG_HI_NANO_PHY_SATA=y ++CONFIG_HI_SATA_PORTS=2 ++CONFIG_HI_SATA_MODE=1 ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++CONFIG_HI_DMAC=y ++CONFIG_HI_DMAC_CHANNEL_NUM=4 ++ ++# ++# Hisilicon driver support ++# ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++CONFIG_ISO9660_FS=y ++# CONFIG_JOLIET is not set ++# CONFIG_ZISOFS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_PER_CPU_MAPS is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_PANIC_ON_OOPS is not set ++CONFIG_PANIC_ON_OOPS_VALUE=0 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_CPU_STALL_INFO is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++# CONFIG_CRYPTO_MANAGER is not set ++# CONFIG_CRYPTO_MANAGER2 is not set ++# CONFIG_CRYPTO_USER is not set ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++# CONFIG_CRYPTO_PCRYPT is not set ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++# CONFIG_CRYPTO_SEQIV is not set ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++# CONFIG_CRYPTO_ARC4 is not set ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++# CONFIG_LIBCRC32C is not set ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_CPU_RMAP=y ++CONFIG_DQL=y ++CONFIG_GLOB=y ++# CONFIG_GLOB_SELFTEST is not set ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3556_single_defconfig b/arch/arm/configs/hi3556_single_defconfig +new file mode 100644 +index 0000000..1260690 +--- /dev/null ++++ b/arch/arm/configs/hi3556_single_defconfig +@@ -0,0 +1,2617 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++# CONFIG_HW_DECOMPRESS is not set ++CONFIG_SOFT_DECOMPRESS=y ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++CONFIG_ARCH_HI3556=y ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_HI3531D is not set ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++# CONFIG_SMP is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++# CONFIG_HZ_100 is not set ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++CONFIG_HZ_1000=y ++CONFIG_HZ=1000 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++# CONFIG_AUTO_ZRELADDR is not set ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++# CONFIG_CPUFREQ_DT is not set ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_GPIO is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_MTD_NAND_HINFC610 is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++CONFIG_CLOSE_SPI_8PIN_4IO=y ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++# CONFIG_ETHERNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_WL_TI is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=m ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set ++# CONFIG_I2C_MUX_GPIO is not set ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PCA954x is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIO_DEVRES=y ++CONFIG_OF_GPIO=y ++CONFIG_GPIOLIB_IRQCHIP=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_EM is not set ++# CONFIG_GPIO_ZEVIO is not set ++CONFIG_GPIO_PL061=y ++# CONFIG_GPIO_SCH311X is not set ++# CONFIG_GPIO_SYSCON is not set ++# CONFIG_GPIO_GRGPIO is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# LPC GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++ ++# ++# USB GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_GPIO is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++# CONFIG_HID_CP2112 is not set ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEFAULT_PERSIST is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++# CONFIG_USB_STORAGE is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++CONFIG_USB_G_MULTI=m ++CONFIG_USB_G_MULTI_RNDIS=y ++# CONFIG_USB_G_MULTI_CDC is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_HI_DMAC is not set ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=y ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_BLKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_PCOMP2=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++# CONFIG_CRYPTO_USER is not set ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++CONFIG_CRYPTO_WORKQUEUE=y ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++CONFIG_CRYPTO_CCM=y ++# CONFIG_CRYPTO_GCM is not set ++CONFIG_CRYPTO_SEQIV=y ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++CONFIG_CRYPTO_CTR=y ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++CONFIG_LIBCRC32C=y ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3556_single_nand_defconfig b/arch/arm/configs/hi3556_single_nand_defconfig +new file mode 100644 +index 0000000..03eae71 +--- /dev/null ++++ b/arch/arm/configs/hi3556_single_nand_defconfig +@@ -0,0 +1,2614 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++# CONFIG_HW_DECOMPRESS is not set ++CONFIG_SOFT_DECOMPRESS=y ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++CONFIG_ARCH_HI3556=y ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_HI3531D is not set ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++# CONFIG_SMP is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++# CONFIG_HZ_100 is not set ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++CONFIG_HZ_1000=y ++CONFIG_HZ=1000 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++# CONFIG_AUTO_ZRELADDR is not set ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++# CONFIG_CPUFREQ_DT is not set ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++# CONFIG_HIFMC_SPI_NAND is not set ++CONFIG_HIFMC_NAND=y ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_GPIO is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_MTD_NAND_HINFC610 is not set ++CONFIG_HIFMC100_NAND=y ++CONFIG_HIFMC100_MAX_NAND_CHIP=1 ++# CONFIG_HIFMC100_NAND_EDO_MODE is not set ++CONFIG_RW_H_WIDTH=10 ++CONFIG_R_L_WIDTH=10 ++CONFIG_W_L_WIDTH=10 ++# CONFIG_HIFMC100_NAND_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_NAND_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_NAND_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++# CONFIG_MTD_SPI_NOR is not set ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++# CONFIG_ETHERNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_WL_TI is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=m ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set ++# CONFIG_I2C_MUX_GPIO is not set ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PCA954x is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIO_DEVRES=y ++CONFIG_OF_GPIO=y ++CONFIG_GPIOLIB_IRQCHIP=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_EM is not set ++# CONFIG_GPIO_ZEVIO is not set ++CONFIG_GPIO_PL061=y ++# CONFIG_GPIO_SCH311X is not set ++# CONFIG_GPIO_SYSCON is not set ++# CONFIG_GPIO_GRGPIO is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# LPC GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++ ++# ++# USB GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_GPIO is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++# CONFIG_HID_CP2112 is not set ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEFAULT_PERSIST is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++# CONFIG_USB_STORAGE is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++CONFIG_USB_G_MULTI=m ++CONFIG_USB_G_MULTI_RNDIS=y ++# CONFIG_USB_G_MULTI_CDC is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_HI_DMAC is not set ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=y ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_BLKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_PCOMP2=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++# CONFIG_CRYPTO_USER is not set ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++CONFIG_CRYPTO_WORKQUEUE=y ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++CONFIG_CRYPTO_CCM=y ++# CONFIG_CRYPTO_GCM is not set ++CONFIG_CRYPTO_SEQIV=y ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++CONFIG_CRYPTO_CTR=y ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++CONFIG_LIBCRC32C=y ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3556_single_nand_mini_defconfig b/arch/arm/configs/hi3556_single_nand_mini_defconfig +new file mode 100644 +index 0000000..b56c7ef +--- /dev/null ++++ b/arch/arm/configs/hi3556_single_nand_mini_defconfig +@@ -0,0 +1,2396 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_HW_DECOMPRESS=y ++# CONFIG_SOFT_DECOMPRESS is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_NAMESPACES is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++CONFIG_EXPERT=y ++# CONFIG_UID16 is not set ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++# CONFIG_VM_EVENT_COUNTERS is not set ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++CONFIG_ARCH_HI3556=y ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_HI3531D is not set ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++# CONFIG_SMP is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++# CONFIG_HZ_100 is not set ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++CONFIG_HZ_1000=y ++CONFIG_HZ=1000 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++# CONFIG_AUTO_ZRELADDR is not set ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++# CONFIG_CPUFREQ_DT is not set ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++# CONFIG_WAKELOCK is not set ++# CONFIG_HISI_SNAPSHOT_BOOT is not set ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_NET_IP_TUNNEL is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++CONFIG_HIFMC=y ++# CONFIG_HIFMC_SPI_NAND is not set ++CONFIG_HIFMC_NAND=y ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_MTD_NAND_HINFC610 is not set ++CONFIG_HIFMC100_NAND=y ++CONFIG_HIFMC100_MAX_NAND_CHIP=1 ++CONFIG_HIFMC100_NAND_EDO_MODE=y ++CONFIG_RW_H_WIDTH=3 ++CONFIG_R_L_WIDTH=2 ++CONFIG_W_L_WIDTH=2 ++# CONFIG_HIFMC100_NAND_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_NAND_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_NAND_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++# CONFIG_MTD_SPI_NOR is not set ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++# CONFIG_ETHERNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++ ++# ++# Host-side USB support is needed for USB Network Adapter support ++# ++CONFIG_USB_NET_DRIVERS=m ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_WL_TI is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_TTY_PRINTK is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=m ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++# CONFIG_HID_A4TECH is not set ++# CONFIG_HID_ACRUX is not set ++# CONFIG_HID_APPLE is not set ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++# CONFIG_HID_BELKIN is not set ++# CONFIG_HID_CHERRY is not set ++# CONFIG_HID_CHICONY is not set ++# CONFIG_HID_CYPRESS is not set ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++# CONFIG_HID_EZKEY is not set ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++# CONFIG_HID_KENSINGTON is not set ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++# CONFIG_HID_LOGITECH is not set ++# CONFIG_HID_MAGICMOUSE is not set ++# CONFIG_HID_MICROSOFT is not set ++# CONFIG_HID_MONTEREY is not set ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=m ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# USB HID Boot Protocol drivers ++# ++# CONFIG_USB_KBD is not set ++# CONFIG_USB_MOUSE is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=m ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEFAULT_PERSIST is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++# CONFIG_USB_STORAGE is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++CONFIG_USB_G_MULTI=m ++CONFIG_USB_G_MULTI_RNDIS=y ++# CONFIG_USB_G_MULTI_CDC is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=m ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=m ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=m ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=m ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_HI_DMAC is not set ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_YAFFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++# CONFIG_DEBUG_MEMORY_INIT is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=y ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_BLKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_PCOMP2=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++# CONFIG_CRYPTO_USER is not set ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++CONFIG_CRYPTO_WORKQUEUE=y ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++CONFIG_CRYPTO_CCM=y ++# CONFIG_CRYPTO_GCM is not set ++CONFIG_CRYPTO_SEQIV=y ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++CONFIG_CRYPTO_CTR=y ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++CONFIG_LIBCRC32C=y ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3556_single_spinand_mini_defconfig b/arch/arm/configs/hi3556_single_spinand_mini_defconfig +new file mode 100644 +index 0000000..6cf319c +--- /dev/null ++++ b/arch/arm/configs/hi3556_single_spinand_mini_defconfig +@@ -0,0 +1,2398 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_HW_DECOMPRESS=y ++# CONFIG_SOFT_DECOMPRESS is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_NAMESPACES is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++CONFIG_EXPERT=y ++# CONFIG_UID16 is not set ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++# CONFIG_VM_EVENT_COUNTERS is not set ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++CONFIG_ARCH_HI3556=y ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_HI3531D is not set ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++# CONFIG_SMP is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++# CONFIG_HZ_100 is not set ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++CONFIG_HZ_1000=y ++CONFIG_HZ=1000 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++# CONFIG_AUTO_ZRELADDR is not set ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++# CONFIG_CPUFREQ_DT is not set ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++# CONFIG_WAKELOCK is not set ++# CONFIG_HISI_SNAPSHOT_BOOT is not set ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_NET_IP_TUNNEL is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_MTD_NAND_HINFC610 is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++# CONFIG_MTD_SPI_NOR is not set ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++# CONFIG_ETHERNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++ ++# ++# Host-side USB support is needed for USB Network Adapter support ++# ++CONFIG_USB_NET_DRIVERS=m ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_WL_TI is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_TTY_PRINTK is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=m ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=m ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# USB HID Boot Protocol drivers ++# ++# CONFIG_USB_KBD is not set ++# CONFIG_USB_MOUSE is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=m ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEFAULT_PERSIST is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++# CONFIG_USB_STORAGE is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++CONFIG_USB_G_MULTI=m ++CONFIG_USB_G_MULTI_RNDIS=y ++# CONFIG_USB_G_MULTI_CDC is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=m ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=m ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=m ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=m ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_HI_DMAC is not set ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_YAFFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=y ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_BLKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_PCOMP2=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++# CONFIG_CRYPTO_USER is not set ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++CONFIG_CRYPTO_WORKQUEUE=y ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++CONFIG_CRYPTO_CCM=y ++# CONFIG_CRYPTO_GCM is not set ++CONFIG_CRYPTO_SEQIV=y ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++CONFIG_CRYPTO_CTR=y ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++CONFIG_LIBCRC32C=y ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3556_single_spinor_mini_defconfig b/arch/arm/configs/hi3556_single_spinor_mini_defconfig +new file mode 100644 +index 0000000..31cd1bb +--- /dev/null ++++ b/arch/arm/configs/hi3556_single_spinor_mini_defconfig +@@ -0,0 +1,2399 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_HW_DECOMPRESS=y ++# CONFIG_SOFT_DECOMPRESS is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_NAMESPACES is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++CONFIG_EXPERT=y ++# CONFIG_UID16 is not set ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++# CONFIG_VM_EVENT_COUNTERS is not set ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++# CONFIG_ARCH_HI3559 is not set ++CONFIG_ARCH_HI3556=y ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_HI3531D is not set ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++# CONFIG_SMP is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++# CONFIG_HZ_100 is not set ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++CONFIG_HZ_1000=y ++CONFIG_HZ=1000 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++# CONFIG_AUTO_ZRELADDR is not set ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++# CONFIG_CPUFREQ_DT is not set ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++# CONFIG_WAKELOCK is not set ++# CONFIG_HISI_SNAPSHOT_BOOT is not set ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_NET_IP_TUNNEL is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_HIFMC is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++# CONFIG_MTD_NAND_IDS is not set ++# CONFIG_MTD_NAND is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++CONFIG_CLOSE_SPI_8PIN_4IO=y ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++CONFIG_MTD_UBI_FASTMAP=y ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++# CONFIG_ETHERNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++ ++# ++# Host-side USB support is needed for USB Network Adapter support ++# ++CONFIG_USB_NET_DRIVERS=m ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_WL_TI is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_TTY_PRINTK is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=m ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++# CONFIG_HID_A4TECH is not set ++# CONFIG_HID_ACRUX is not set ++# CONFIG_HID_APPLE is not set ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++# CONFIG_HID_BELKIN is not set ++# CONFIG_HID_CHERRY is not set ++# CONFIG_HID_CHICONY is not set ++# CONFIG_HID_CYPRESS is not set ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++# CONFIG_HID_EZKEY is not set ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++# CONFIG_HID_KENSINGTON is not set ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++# CONFIG_HID_LOGITECH is not set ++# CONFIG_HID_MAGICMOUSE is not set ++# CONFIG_HID_MICROSOFT is not set ++# CONFIG_HID_MONTEREY is not set ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=m ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# USB HID Boot Protocol drivers ++# ++# CONFIG_USB_KBD is not set ++# CONFIG_USB_MOUSE is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=m ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEFAULT_PERSIST is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++# CONFIG_USB_STORAGE is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++CONFIG_USB_G_MULTI=m ++CONFIG_USB_G_MULTI_RNDIS=y ++# CONFIG_USB_G_MULTI_CDC is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=m ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=m ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=m ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=m ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_HI_DMAC is not set ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_YAFFS_FS is not set ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++# CONFIG_CRAMFS is not set ++CONFIG_SQUASHFS=y ++CONFIG_SQUASHFS_FILE_CACHE=y ++# CONFIG_SQUASHFS_FILE_DIRECT is not set ++CONFIG_SQUASHFS_DECOMP_SINGLE=y ++# CONFIG_SQUASHFS_DECOMP_MULTI is not set ++# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set ++# CONFIG_SQUASHFS_XATTR is not set ++CONFIG_SQUASHFS_ZLIB=y ++CONFIG_SQUASHFS_LZO=y ++# CONFIG_SQUASHFS_XZ is not set ++# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set ++# CONFIG_SQUASHFS_EMBEDDED is not set ++CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=y ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_BLKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_PCOMP2=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++# CONFIG_CRYPTO_USER is not set ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++CONFIG_CRYPTO_WORKQUEUE=y ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++CONFIG_CRYPTO_CCM=y ++# CONFIG_CRYPTO_GCM is not set ++CONFIG_CRYPTO_SEQIV=y ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++CONFIG_CRYPTO_CTR=y ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++CONFIG_LIBCRC32C=y ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3559_single_defconfig b/arch/arm/configs/hi3559_single_defconfig +new file mode 100644 +index 0000000..0b5f028 +--- /dev/null ++++ b/arch/arm/configs/hi3559_single_defconfig +@@ -0,0 +1,2617 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++# CONFIG_HW_DECOMPRESS is not set ++CONFIG_SOFT_DECOMPRESS=y ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++CONFIG_ARCH_HI3559=y ++# CONFIG_ARCH_HI3556 is not set ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_HI3531D is not set ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++# CONFIG_SMP is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++# CONFIG_HZ_100 is not set ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++CONFIG_HZ_1000=y ++CONFIG_HZ=1000 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++# CONFIG_AUTO_ZRELADDR is not set ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++# CONFIG_CPUFREQ_DT is not set ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_GPIO is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_MTD_NAND_HINFC610 is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++CONFIG_CLOSE_SPI_8PIN_4IO=y ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++# CONFIG_ETHERNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_WL_TI is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=m ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set ++# CONFIG_I2C_MUX_GPIO is not set ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PCA954x is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIO_DEVRES=y ++CONFIG_OF_GPIO=y ++CONFIG_GPIOLIB_IRQCHIP=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_EM is not set ++# CONFIG_GPIO_ZEVIO is not set ++CONFIG_GPIO_PL061=y ++# CONFIG_GPIO_SCH311X is not set ++# CONFIG_GPIO_SYSCON is not set ++# CONFIG_GPIO_GRGPIO is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# LPC GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++ ++# ++# USB GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_GPIO is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++# CONFIG_HID_CP2112 is not set ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEFAULT_PERSIST is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++# CONFIG_USB_STORAGE is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++CONFIG_USB_G_MULTI=m ++CONFIG_USB_G_MULTI_RNDIS=y ++# CONFIG_USB_G_MULTI_CDC is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_HI_DMAC is not set ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=y ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_BLKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_PCOMP2=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++# CONFIG_CRYPTO_USER is not set ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++CONFIG_CRYPTO_WORKQUEUE=y ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++CONFIG_CRYPTO_CCM=y ++# CONFIG_CRYPTO_GCM is not set ++CONFIG_CRYPTO_SEQIV=y ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++CONFIG_CRYPTO_CTR=y ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++CONFIG_LIBCRC32C=y ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3559_single_nand_defconfig b/arch/arm/configs/hi3559_single_nand_defconfig +new file mode 100644 +index 0000000..02fae8f +--- /dev/null ++++ b/arch/arm/configs/hi3559_single_nand_defconfig +@@ -0,0 +1,2615 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++# CONFIG_HW_DECOMPRESS is not set ++CONFIG_SOFT_DECOMPRESS=y ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++CONFIG_NAMESPACES=y ++CONFIG_UTS_NS=y ++CONFIG_IPC_NS=y ++# CONFIG_USER_NS is not set ++CONFIG_PID_NS=y ++CONFIG_NET_NS=y ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="" ++CONFIG_RD_GZIP=y ++CONFIG_RD_BZIP2=y ++CONFIG_RD_LZMA=y ++CONFIG_RD_XZ=y ++CONFIG_RD_LZO=y ++CONFIG_RD_LZ4=y ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++# CONFIG_EXPERT is not set ++CONFIG_UID16=y ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++CONFIG_VM_EVENT_COUNTERS=y ++CONFIG_SLUB_DEBUG=y ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++CONFIG_ARCH_HI3559=y ++# CONFIG_ARCH_HI3556 is not set ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_HI3531D is not set ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++# CONFIG_SMP is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++# CONFIG_HZ_100 is not set ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++CONFIG_HZ_1000=y ++CONFIG_HZ=1000 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++# CONFIG_FRONTSWAP is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++# CONFIG_AUTO_ZRELADDR is not set ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++# CONFIG_CPUFREQ_DT is not set ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_WAKELOCK=y ++CONFIG_HIBERNATE_CALLBACKS=y ++CONFIG_HISI_SNAPSHOT_BOOT=y ++# CONFIG_DEFAULT_MTD is not set ++CONFIG_DEFAULT_DDR=y ++CONFIG_SNAPSHOT_BUF_START=0x8a000000 ++CONFIG_SNAPSHOT_BUF_SIZE=0x4000000 ++CONFIG_HIBERNATION=y ++CONFIG_GZIP_COMPRESS=y ++CONFIG_PM_STD_PARTITION="" ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++CONFIG_NET_IP_TUNNEL=m ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++CONFIG_INET_TUNNEL=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++CONFIG_IPV6=y ++# CONFIG_IPV6_ROUTER_PREF is not set ++# CONFIG_IPV6_OPTIMISTIC_DAD is not set ++# CONFIG_INET6_AH is not set ++# CONFIG_INET6_ESP is not set ++# CONFIG_INET6_IPCOMP is not set ++# CONFIG_IPV6_MIP6 is not set ++# CONFIG_INET6_XFRM_TUNNEL is not set ++# CONFIG_INET6_TUNNEL is not set ++CONFIG_INET6_XFRM_MODE_TRANSPORT=m ++CONFIG_INET6_XFRM_MODE_TUNNEL=m ++CONFIG_INET6_XFRM_MODE_BEET=m ++# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set ++# CONFIG_IPV6_VTI is not set ++CONFIG_IPV6_SIT=m ++# CONFIG_IPV6_SIT_6RD is not set ++CONFIG_IPV6_NDISC_NODETYPE=y ++# CONFIG_IPV6_TUNNEL is not set ++# CONFIG_IPV6_GRE is not set ++# CONFIG_IPV6_MULTIPLE_TABLES is not set ++# CONFIG_IPV6_MROUTE is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++ ++# ++# IPv6: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV6 is not set ++# CONFIG_NF_REJECT_IPV6 is not set ++# CONFIG_NF_LOG_IPV6 is not set ++# CONFIG_IP6_NF_IPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_6LOWPAN is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_MTD_SWAP is not set ++CONFIG_HIFMC=y ++# CONFIG_HIFMC_SPI_NAND is not set ++CONFIG_HIFMC_NAND=y ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_GPIO is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_MTD_NAND_HINFC610 is not set ++CONFIG_HIFMC100_NAND=y ++CONFIG_HIFMC100_MAX_NAND_CHIP=1 ++# CONFIG_HIFMC100_NAND_EDO_MODE is not set ++CONFIG_RW_H_WIDTH=10 ++CONFIG_R_L_WIDTH=10 ++CONFIG_W_L_WIDTH=10 ++# CONFIG_HIFMC100_NAND_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_NAND_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_NAND_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++# CONFIG_MTD_SPI_NOR is not set ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++# CONFIG_ETHERNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_GPIO is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++CONFIG_USB_NET_DRIVERS=y ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_WL_TI is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_MOUSEDEV_PSAUX=y ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++CONFIG_INPUT_KEYBOARD=y ++# CONFIG_KEYBOARD_ADP5588 is not set ++# CONFIG_KEYBOARD_ADP5589 is not set ++CONFIG_KEYBOARD_ATKBD=y ++# CONFIG_KEYBOARD_QT1070 is not set ++# CONFIG_KEYBOARD_QT2160 is not set ++# CONFIG_KEYBOARD_LKKBD is not set ++# CONFIG_KEYBOARD_GPIO is not set ++# CONFIG_KEYBOARD_GPIO_POLLED is not set ++# CONFIG_KEYBOARD_TCA6416 is not set ++# CONFIG_KEYBOARD_TCA8418 is not set ++# CONFIG_KEYBOARD_MATRIX is not set ++# CONFIG_KEYBOARD_LM8333 is not set ++# CONFIG_KEYBOARD_MAX7359 is not set ++# CONFIG_KEYBOARD_MCS is not set ++# CONFIG_KEYBOARD_MPR121 is not set ++# CONFIG_KEYBOARD_NEWTON is not set ++# CONFIG_KEYBOARD_OPENCORES is not set ++# CONFIG_KEYBOARD_SAMSUNG is not set ++# CONFIG_KEYBOARD_STOWAWAY is not set ++# CONFIG_KEYBOARD_SUNKBD is not set ++# CONFIG_KEYBOARD_OMAP4 is not set ++# CONFIG_KEYBOARD_XTKBD is not set ++# CONFIG_KEYBOARD_CAP1106 is not set ++CONFIG_INPUT_MOUSE=y ++CONFIG_MOUSE_PS2=y ++CONFIG_MOUSE_PS2_ALPS=y ++CONFIG_MOUSE_PS2_LOGIPS2PP=y ++CONFIG_MOUSE_PS2_SYNAPTICS=y ++CONFIG_MOUSE_PS2_CYPRESS=y ++CONFIG_MOUSE_PS2_TRACKPOINT=y ++# CONFIG_MOUSE_PS2_ELANTECH is not set ++# CONFIG_MOUSE_PS2_SENTELIC is not set ++# CONFIG_MOUSE_PS2_TOUCHKIT is not set ++# CONFIG_MOUSE_SERIAL is not set ++# CONFIG_MOUSE_APPLETOUCH is not set ++# CONFIG_MOUSE_BCM5974 is not set ++# CONFIG_MOUSE_CYAPA is not set ++# CONFIG_MOUSE_VSXXXAA is not set ++# CONFIG_MOUSE_GPIO is not set ++# CONFIG_MOUSE_SYNAPTICS_I2C is not set ++# CONFIG_MOUSE_SYNAPTICS_USB is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=y ++CONFIG_SERIO_SERPORT=y ++# CONFIG_SERIO_AMBAKMI is not set ++CONFIG_SERIO_LIBPS2=y ++# CONFIG_SERIO_RAW is not set ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++# CONFIG_SERIO_ARC_PS2 is not set ++# CONFIG_SERIO_APBPS2 is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_IFX6X60 is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=m ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set ++# CONFIG_I2C_MUX_GPIO is not set ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_MUX_PCA954x is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_CBUS_GPIO is not set ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_GPIO is not set ++# CONFIG_SPI_FSL_SPI is not set ++# CONFIG_SPI_OC_TINY is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++CONFIG_GPIOLIB=y ++CONFIG_GPIO_DEVRES=y ++CONFIG_OF_GPIO=y ++CONFIG_GPIOLIB_IRQCHIP=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_DWAPB is not set ++# CONFIG_GPIO_EM is not set ++# CONFIG_GPIO_ZEVIO is not set ++CONFIG_GPIO_PL061=y ++# CONFIG_GPIO_SCH311X is not set ++# CONFIG_GPIO_SYSCON is not set ++# CONFIG_GPIO_GRGPIO is not set ++ ++# ++# I2C GPIO expanders: ++# ++# CONFIG_GPIO_MAX7300 is not set ++# CONFIG_GPIO_MAX732X is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_GPIO_SX150X is not set ++# CONFIG_GPIO_ADP5588 is not set ++# CONFIG_GPIO_ADNP is not set ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++# CONFIG_GPIO_MAX7301 is not set ++# CONFIG_GPIO_MCP23S08 is not set ++# CONFIG_GPIO_MC33880 is not set ++# CONFIG_GPIO_74X164 is not set ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# LPC GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++ ++# ++# USB GPIO expanders: ++# ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_GPIO is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_BQ24190 is not set ++# CONFIG_CHARGER_BQ24735 is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++# CONFIG_POWER_RESET_GPIO is not set ++# CONFIG_POWER_RESET_GPIO_RESTART is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_LTC2952 is not set ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_AAT2870_CORE is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_MFD_ASIC3 is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_EGPIO is not set ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_HTC_I2CPLD is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS65010 is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS65910 is not set ++# CONFIG_MFD_TPS65912 is not set ++# CONFIG_MFD_TPS65912_I2C is not set ++# CONFIG_MFD_TPS65912_SPI is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_GPIO is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_FB_SSD1307 is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++# CONFIG_HID_CP2112 is not set ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=y ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=y ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEFAULT_PERSIST is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++# CONFIG_USB_STORAGE is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_USB_OTG_WAKELOCK is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++# CONFIG_USB_DUMMY_HCD is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++CONFIG_USB_G_MULTI=m ++CONFIG_USB_G_MULTI_RNDIS=y ++# CONFIG_USB_G_MULTI_CDC is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=y ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=y ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_HI_DMAC is not set ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_USE_FOR_EXT23=y ++# CONFIG_EXT4_FS_POSIX_ACL is not set ++# CONFIG_EXT4_FS_SECURITY is not set ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_JBD2=y ++# CONFIG_JBD2_DEBUG is not set ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++CONFIG_YAFFS_FS=y ++CONFIG_YAFFS_YAFFS1=y ++# CONFIG_YAFFS_9BYTE_TAGS is not set ++# CONFIG_YAFFS_DOES_ECC is not set ++CONFIG_YAFFS_YAFFS2=y ++CONFIG_YAFFS_AUTO_YAFFS2=y ++# CONFIG_YAFFS_DISABLE_TAGS_ECC is not set ++# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set ++# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set ++# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set ++# CONFIG_YAFFS_DISABLE_BACKGROUND is not set ++# CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING is not set ++CONFIG_YAFFS_XATTR=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++CONFIG_CRAMFS=y ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_DEBUG_ON is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=y ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_BLKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_PCOMP2=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++# CONFIG_CRYPTO_USER is not set ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++CONFIG_CRYPTO_WORKQUEUE=y ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++CONFIG_CRYPTO_CCM=y ++# CONFIG_CRYPTO_GCM is not set ++CONFIG_CRYPTO_SEQIV=y ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++CONFIG_CRYPTO_CTR=y ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++CONFIG_LIBCRC32C=y ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_LZ4_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_DECOMPRESS_GZIP=y ++CONFIG_DECOMPRESS_BZIP2=y ++CONFIG_DECOMPRESS_LZMA=y ++CONFIG_DECOMPRESS_XZ=y ++CONFIG_DECOMPRESS_LZO=y ++CONFIG_DECOMPRESS_LZ4=y ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3559_single_nand_mini_defconfig b/arch/arm/configs/hi3559_single_nand_mini_defconfig +new file mode 100644 +index 0000000..d1101a1 +--- /dev/null ++++ b/arch/arm/configs/hi3559_single_nand_mini_defconfig +@@ -0,0 +1,2396 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_HW_DECOMPRESS=y ++# CONFIG_SOFT_DECOMPRESS is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_NAMESPACES is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++CONFIG_EXPERT=y ++# CONFIG_UID16 is not set ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++# CONFIG_VM_EVENT_COUNTERS is not set ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++CONFIG_ARCH_HI3559=y ++# CONFIG_ARCH_HI3556 is not set ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_HI3531D is not set ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++# CONFIG_SMP is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++# CONFIG_HZ_100 is not set ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++CONFIG_HZ_1000=y ++CONFIG_HZ=1000 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++# CONFIG_AUTO_ZRELADDR is not set ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++# CONFIG_CPUFREQ_DT is not set ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++# CONFIG_WAKELOCK is not set ++# CONFIG_HISI_SNAPSHOT_BOOT is not set ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_NET_IP_TUNNEL is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++CONFIG_HIFMC=y ++# CONFIG_HIFMC_SPI_NAND is not set ++CONFIG_HIFMC_NAND=y ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_MTD_NAND_HINFC610 is not set ++CONFIG_HIFMC100_NAND=y ++CONFIG_HIFMC100_MAX_NAND_CHIP=1 ++CONFIG_HIFMC100_NAND_EDO_MODE=y ++CONFIG_RW_H_WIDTH=3 ++CONFIG_R_L_WIDTH=2 ++CONFIG_W_L_WIDTH=2 ++# CONFIG_HIFMC100_NAND_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_NAND_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_NAND_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++# CONFIG_MTD_SPI_NOR is not set ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++# CONFIG_ETHERNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++ ++# ++# Host-side USB support is needed for USB Network Adapter support ++# ++CONFIG_USB_NET_DRIVERS=m ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_WL_TI is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_TTY_PRINTK is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=m ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++# CONFIG_HID_A4TECH is not set ++# CONFIG_HID_ACRUX is not set ++# CONFIG_HID_APPLE is not set ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++# CONFIG_HID_BELKIN is not set ++# CONFIG_HID_CHERRY is not set ++# CONFIG_HID_CHICONY is not set ++# CONFIG_HID_CYPRESS is not set ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++# CONFIG_HID_EZKEY is not set ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++# CONFIG_HID_KENSINGTON is not set ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++# CONFIG_HID_LOGITECH is not set ++# CONFIG_HID_MAGICMOUSE is not set ++# CONFIG_HID_MICROSOFT is not set ++# CONFIG_HID_MONTEREY is not set ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=m ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# USB HID Boot Protocol drivers ++# ++# CONFIG_USB_KBD is not set ++# CONFIG_USB_MOUSE is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=m ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEFAULT_PERSIST is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++# CONFIG_USB_STORAGE is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++CONFIG_USB_G_MULTI=m ++CONFIG_USB_G_MULTI_RNDIS=y ++# CONFIG_USB_G_MULTI_CDC is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=m ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=m ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=m ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=m ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_HI_DMAC is not set ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_YAFFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++# CONFIG_DEBUG_MEMORY_INIT is not set ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=y ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_BLKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_PCOMP2=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++# CONFIG_CRYPTO_USER is not set ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++CONFIG_CRYPTO_WORKQUEUE=y ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++CONFIG_CRYPTO_CCM=y ++# CONFIG_CRYPTO_GCM is not set ++CONFIG_CRYPTO_SEQIV=y ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++CONFIG_CRYPTO_CTR=y ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++CONFIG_LIBCRC32C=y ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3559_single_spinand_mini_defconfig b/arch/arm/configs/hi3559_single_spinand_mini_defconfig +new file mode 100644 +index 0000000..36d3e93 +--- /dev/null ++++ b/arch/arm/configs/hi3559_single_spinand_mini_defconfig +@@ -0,0 +1,2398 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_HW_DECOMPRESS=y ++# CONFIG_SOFT_DECOMPRESS is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_NAMESPACES is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++CONFIG_EXPERT=y ++# CONFIG_UID16 is not set ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++# CONFIG_VM_EVENT_COUNTERS is not set ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++CONFIG_ARCH_HI3559=y ++# CONFIG_ARCH_HI3556 is not set ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_HI3531D is not set ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++# CONFIG_SMP is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++# CONFIG_HZ_100 is not set ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++CONFIG_HZ_1000=y ++CONFIG_HZ=1000 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++# CONFIG_AUTO_ZRELADDR is not set ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++# CONFIG_CPUFREQ_DT is not set ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++# CONFIG_WAKELOCK is not set ++# CONFIG_HISI_SNAPSHOT_BOOT is not set ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_NET_IP_TUNNEL is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++CONFIG_HIFMC=y ++CONFIG_HIFMC_SPI_NAND=y ++# CONFIG_HIFMC_NAND is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++CONFIG_MTD_NAND_IDS=y ++CONFIG_MTD_NAND_ECC=y ++# CONFIG_MTD_NAND_ECC_SMC is not set ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_ECC_BCH is not set ++# CONFIG_MTD_SM_COMMON is not set ++# CONFIG_MTD_NAND_DENALI is not set ++# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set ++# CONFIG_MTD_NAND_DISKONCHIP is not set ++# CONFIG_MTD_NAND_DOCG4 is not set ++# CONFIG_MTD_NAND_NANDSIM is not set ++# CONFIG_MTD_NAND_PLATFORM is not set ++# CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 is not set ++# CONFIG_HISI_NAND_ECC_STATUS_REPORT is not set ++# CONFIG_MTD_NAND_HINFC610 is not set ++# CONFIG_HIFMC100_NAND is not set ++CONFIG_HIFMC100_SPI_NAND=y ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_HIFMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++# CONFIG_MTD_SPI_NOR is not set ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++# CONFIG_MTD_UBI_FASTMAP is not set ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++# CONFIG_ETHERNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++ ++# ++# Host-side USB support is needed for USB Network Adapter support ++# ++CONFIG_USB_NET_DRIVERS=m ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_WL_TI is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_TTY_PRINTK is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=m ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=y ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=y ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++CONFIG_HID_BELKIN=y ++CONFIG_HID_CHERRY=y ++CONFIG_HID_CHICONY=y ++CONFIG_HID_CYPRESS=y ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++CONFIG_HID_EZKEY=y ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++CONFIG_HID_KENSINGTON=y ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++CONFIG_HID_LOGITECH=y ++# CONFIG_HID_LOGITECH_HIDPP is not set ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWHEELS_FF is not set ++# CONFIG_HID_MAGICMOUSE is not set ++CONFIG_HID_MICROSOFT=y ++CONFIG_HID_MONTEREY=y ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=m ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# USB HID Boot Protocol drivers ++# ++# CONFIG_USB_KBD is not set ++# CONFIG_USB_MOUSE is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=m ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEFAULT_PERSIST is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++# CONFIG_USB_STORAGE is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++CONFIG_USB_G_MULTI=m ++CONFIG_USB_G_MULTI_RNDIS=y ++# CONFIG_USB_G_MULTI_CDC is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=m ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=m ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=m ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=m ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_HI_DMAC is not set ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_YAFFS_FS is not set ++# CONFIG_JFFS2_FS is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=y ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_BLKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_PCOMP2=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++# CONFIG_CRYPTO_USER is not set ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++CONFIG_CRYPTO_WORKQUEUE=y ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++CONFIG_CRYPTO_CCM=y ++# CONFIG_CRYPTO_GCM is not set ++CONFIG_CRYPTO_SEQIV=y ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++CONFIG_CRYPTO_CTR=y ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++CONFIG_LIBCRC32C=y ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hi3559_single_spinor_mini_defconfig b/arch/arm/configs/hi3559_single_spinor_mini_defconfig +new file mode 100644 +index 0000000..5d582ae +--- /dev/null ++++ b/arch/arm/configs/hi3559_single_spinor_mini_defconfig +@@ -0,0 +1,2399 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.18.20 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_ARM_HAS_SG_CHAIN=y ++CONFIG_MIGHT_HAVE_PCI=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_ARCH_SUPPORTS_UPROBES=y ++CONFIG_VECTORS_BASE=0xffff0000 ++CONFIG_ARM_PATCH_PHYS_VIRT=y ++CONFIG_GENERIC_BUG=y ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_IRQ_WORK=y ++CONFIG_BUILDTIME_EXTABLE_SORT=y ++ ++# ++# General setup ++# ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_XZ=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_HAVE_KERNEL_LZ4=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_XZ is not set ++# CONFIG_KERNEL_LZO is not set ++# CONFIG_KERNEL_LZ4 is not set ++CONFIG_HW_DECOMPRESS=y ++# CONFIG_SOFT_DECOMPRESS is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++# CONFIG_SWAP is not set ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++# CONFIG_POSIX_MQUEUE is not set ++CONFIG_CROSS_MEMORY_ATTACH=y ++# CONFIG_FHANDLE is not set ++CONFIG_USELIB=y ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_ARCH_AUDITSYSCALL=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_GENERIC_IRQ_SHOW=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_IRQ_DOMAIN=y ++CONFIG_HANDLE_DOMAIN_IRQ=y ++CONFIG_IRQ_FORCED_THREADING=y ++CONFIG_SPARSE_IRQ=y ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++ ++# ++# Timers subsystem ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ_COMMON=y ++# CONFIG_HZ_PERIODIC is not set ++CONFIG_NO_HZ_IDLE=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++ ++# ++# CPU/Task time and stats accounting ++# ++CONFIG_TICK_CPU_ACCOUNTING=y ++# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set ++# CONFIG_IRQ_TIME_ACCOUNTING is not set ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_TASKSTATS is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_TASKS_RCU is not set ++# CONFIG_RCU_STALL_COMMON is not set ++# CONFIG_TREE_RCU_TRACE is not set ++# CONFIG_BUILD_BIN2C is not set ++# CONFIG_IKCONFIG is not set ++CONFIG_LOG_BUF_SHIFT=17 ++CONFIG_GENERIC_SCHED_CLOCK=y ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CPUSETS is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_RESOURCE_COUNTERS is not set ++CONFIG_CGROUP_SCHED=y ++CONFIG_FAIR_GROUP_SCHED=y ++# CONFIG_CFS_BANDWIDTH is not set ++# CONFIG_RT_GROUP_SCHED is not set ++# CONFIG_BLK_CGROUP is not set ++# CONFIG_CHECKPOINT_RESTORE is not set ++# CONFIG_NAMESPACES is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_HAVE_UID16=y ++CONFIG_BPF=y ++CONFIG_EXPERT=y ++# CONFIG_UID16 is not set ++# CONFIG_SGETMASK_SYSCALL is not set ++CONFIG_SYSFS_SYSCALL=y ++# CONFIG_SYSCTL_SYSCALL is not set ++CONFIG_KALLSYMS=y ++# CONFIG_KALLSYMS_ALL is not set ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++CONFIG_ELF_CORE=y ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++# CONFIG_BPF_SYSCALL is not set ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_ADVISE_SYSCALLS=y ++# CONFIG_EMBEDDED is not set ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++# CONFIG_VM_EVENT_COUNTERS is not set ++# CONFIG_SLUB_DEBUG is not set ++# CONFIG_COMPAT_BRK is not set ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++# CONFIG_JUMP_LABEL is not set ++# CONFIG_UPROBES is not set ++# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set ++CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y ++CONFIG_ARCH_USE_BUILTIN_BSWAP=y ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_ARCH_TRACEHOOK=y ++CONFIG_HAVE_DMA_ATTRS=y ++CONFIG_HAVE_DMA_CONTIGUOUS=y ++CONFIG_GENERIC_SMP_IDLE_THREAD=y ++CONFIG_GENERIC_IDLE_POLL_SETUP=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++CONFIG_HAVE_PERF_REGS=y ++CONFIG_HAVE_PERF_USER_STACK_DUMP=y ++CONFIG_HAVE_ARCH_JUMP_LABEL=y ++CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y ++CONFIG_HAVE_ARCH_SECCOMP_FILTER=y ++CONFIG_HAVE_CC_STACKPROTECTOR=y ++# CONFIG_CC_STACKPROTECTOR is not set ++CONFIG_CC_STACKPROTECTOR_NONE=y ++# CONFIG_CC_STACKPROTECTOR_REGULAR is not set ++# CONFIG_CC_STACKPROTECTOR_STRONG is not set ++CONFIG_HAVE_CONTEXT_TRACKING=y ++CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y ++CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y ++CONFIG_HAVE_MOD_ARCH_SPECIFIC=y ++CONFIG_MODULES_USE_ELF_REL=y ++CONFIG_CLONE_BACKWARDS=y ++CONFIG_OLD_SIGSUSPEND3=y ++CONFIG_OLD_SIGACTION=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++# CONFIG_MODVERSIONS is not set ++# CONFIG_MODULE_SRCVERSION_ALL is not set ++# CONFIG_MODULE_SIG is not set ++# CONFIG_MODULE_COMPRESS is not set ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++CONFIG_BLK_DEV_BSG=y ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++CONFIG_BLK_CMDLINE_PARSER=y ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_AIX_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++# CONFIG_MAC_PARTITION is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_CMDLINE_PARTITION=y ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++CONFIG_DEFAULT_DEADLINE=y ++# CONFIG_DEFAULT_CFQ is not set ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="deadline" ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++CONFIG_INLINE_READ_UNLOCK=y ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++CONFIG_INLINE_WRITE_UNLOCK=y ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++CONFIG_ARCH_MULTIPLATFORM=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE_LEGACY is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C24XX is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP1 is not set ++ ++# ++# Multiple platform selection ++# ++ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_MULTI_V6 is not set ++CONFIG_ARCH_MULTI_V7=y ++CONFIG_ARCH_MULTI_V6_V7=y ++# CONFIG_ARCH_MULTI_CPU_AUTO is not set ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_HIGHBANK is not set ++CONFIG_ARCH_HISI=y ++ ++# ++# Hisilicon platform type ++# ++# CONFIG_ARCH_HI3xxx is not set ++# CONFIG_ARCH_HIP04 is not set ++# CONFIG_ARCH_HIX5HD2 is not set ++# CONFIG_ARCH_HI3519 is not set ++# CONFIG_ARCH_HI3519V101 is not set ++# CONFIG_ARCH_HI3516AV200 is not set ++CONFIG_ARCH_HI3559=y ++# CONFIG_ARCH_HI3556 is not set ++# CONFIG_ARCH_HI3536C is not set ++# CONFIG_ARCH_HI3531D is not set ++# CONFIG_ARCH_HI3521D is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_SHMOBILE_MULTI is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZYNQ is not set ++CONFIG_ARM_TIMER_SP804=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V7=y ++CONFIG_CPU_32v6K=y ++CONFIG_CPU_32v7=y ++CONFIG_CPU_ABRT_EV7=y ++CONFIG_CPU_PABRT_V7=y ++CONFIG_CPU_CACHE_V7=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V7=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++ ++# ++# Processor Features ++# ++# CONFIG_ARM_LPAE is not set ++# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set ++CONFIG_ARM_THUMB=y ++# CONFIG_ARM_THUMBEE is not set ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_SWP_EMULATE=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_KUSER_HELPERS=y ++CONFIG_MIGHT_HAVE_CACHE_L2X0=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_ARM_L1_CACHE_SHIFT_6=y ++CONFIG_ARM_L1_CACHE_SHIFT=6 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_MULTI_IRQ_HANDLER=y ++# CONFIG_ARM_ERRATA_430973 is not set ++# CONFIG_ARM_ERRATA_720789 is not set ++# CONFIG_ARM_ERRATA_754322 is not set ++# CONFIG_ARM_ERRATA_775420 is not set ++# CONFIG_ARM_ERRATA_773022 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI is not set ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_HAVE_SMP=y ++# CONFIG_SMP is not set ++CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++# CONFIG_ARM_PSCI is not set ++CONFIG_ARCH_NR_GPIO=0 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ_FIXED=0 ++# CONFIG_HZ_100 is not set ++# CONFIG_HZ_200 is not set ++# CONFIG_HZ_250 is not set ++# CONFIG_HZ_300 is not set ++# CONFIG_HZ_500 is not set ++CONFIG_HZ_1000=y ++CONFIG_HZ=1000 ++# CONFIG_SCHED_HRTICK is not set ++# CONFIG_THUMB2_KERNEL is not set ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++CONFIG_HIGHMEM=y ++# CONFIG_HIGHPTE is not set ++CONFIG_ARCH_WANT_GENERAL_HUGETLB=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_NO_BOOTMEM=y ++CONFIG_MEMORY_ISOLATION=y ++# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++CONFIG_COMPACTION=y ++CONFIG_MIGRATION=y ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_BOUNCE=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_CMA=y ++# CONFIG_CMA_DEBUG is not set ++CONFIG_CMA_AREAS=7 ++# CONFIG_ZPOOL is not set ++# CONFIG_ZBUD is not set ++# CONFIG_ZSMALLOC is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++CONFIG_SWIOTLB=y ++CONFIG_IOMMU_HELPER=y ++# CONFIG_XEN is not set ++# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set ++ ++# ++# Boot options ++# ++CONFIG_USE_OF=y ++CONFIG_ATAGS=y ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set ++CONFIG_CMDLINE="" ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++# CONFIG_AUTO_ZRELADDR is not set ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_GOV_COMMON=y ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set ++# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_INTERACTIVE=y ++# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set ++# CONFIG_CPUFREQ_DT is not set ++ ++# ++# ARM CPU frequency scaling drivers ++# ++# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++CONFIG_VFP=y ++CONFIG_VFPv3=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y ++CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y ++CONFIG_BINFMT_SCRIPT=y ++# CONFIG_HAVE_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++CONFIG_COREDUMP=y ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++# CONFIG_WAKELOCK is not set ++# CONFIG_HISI_SNAPSHOT_BOOT is not set ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_AUTOSLEEP is not set ++# CONFIG_PM_WAKELOCKS is not set ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_OPP=y ++CONFIG_PM_CLK=y ++# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set ++CONFIG_CPU_PM=y ++# CONFIG_SUSPEND_TIME is not set ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_ARM_CPU_SUSPEND=y ++CONFIG_ARCH_HIBERNATION_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_DIAG is not set ++CONFIG_UNIX=y ++# CONFIG_UNIX_DIAG is not set ++CONFIG_XFRM=y ++# CONFIG_XFRM_USER is not set ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++# CONFIG_NET_KEY is not set ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_NET_IP_TUNNEL is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_NET_IPVTI is not set ++# CONFIG_NET_UDP_TUNNEL is not set ++# CONFIG_NET_FOU is not set ++# CONFIG_GENEVE is not set ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++CONFIG_INET_XFRM_MODE_TRANSPORT=y ++CONFIG_INET_XFRM_MODE_TUNNEL=y ++CONFIG_INET_XFRM_MODE_BEET=y ++CONFIG_INET_LRO=y ++CONFIG_INET_DIAG=y ++CONFIG_INET_TCP_DIAG=y ++# CONFIG_INET_UDP_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_ANDROID_PARANOID_NETWORK is not set ++CONFIG_NET_ACTIVITY_STATS=y ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NET_PTP_CLASSIFY is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++CONFIG_NETFILTER=y ++# CONFIG_NETFILTER_DEBUG is not set ++CONFIG_NETFILTER_ADVANCED=y ++ ++# ++# Core Netfilter Configuration ++# ++# CONFIG_NETFILTER_NETLINK_ACCT is not set ++# CONFIG_NETFILTER_NETLINK_QUEUE is not set ++# CONFIG_NETFILTER_NETLINK_LOG is not set ++# CONFIG_NF_CONNTRACK is not set ++# CONFIG_NF_TABLES is not set ++# CONFIG_NETFILTER_XTABLES is not set ++# CONFIG_IP_SET is not set ++# CONFIG_IP_VS is not set ++ ++# ++# IP: Netfilter Configuration ++# ++# CONFIG_NF_DEFRAG_IPV4 is not set ++# CONFIG_NF_LOG_ARP is not set ++# CONFIG_NF_LOG_IPV4 is not set ++# CONFIG_NF_REJECT_IPV4 is not set ++# CONFIG_IP_NF_IPTABLES is not set ++# CONFIG_IP_NF_ARPTABLES is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++CONFIG_HAVE_NET_DSA=y ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_PHONET is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++# CONFIG_BATMAN_ADV is not set ++# CONFIG_OPENVSWITCH is not set ++# CONFIG_VSOCKETS is not set ++# CONFIG_NETLINK_MMAP is not set ++# CONFIG_NETLINK_DIAG is not set ++# CONFIG_NET_MPLS_GSO is not set ++# CONFIG_HSR is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++CONFIG_NET_RX_BUSY_POLL=y ++CONFIG_BQL=y ++# CONFIG_BPF_JIT is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++# CONFIG_CFG80211 is not set ++# CONFIG_LIB80211 is not set ++ ++# ++# CFG80211 needs to be enabled for MAC80211 ++# ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_RFKILL_REGULATOR is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++CONFIG_HAVE_BPF_JIT=y ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++# CONFIG_UEVENT_HELPER is not set ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_STANDALONE=y ++# CONFIG_PREVENT_FIRMWARE_BUILD is not set ++CONFIG_FW_LOADER=y ++# CONFIG_FIRMWARE_IN_KERNEL is not set ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set ++CONFIG_ALLOW_DEV_COREDUMP=y ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_GENERIC_CPU_DEVICES is not set ++# CONFIG_HAVE_CPU_AUTOPROBE is not set ++CONFIG_REGMAP=y ++CONFIG_REGMAP_MMIO=y ++CONFIG_DMA_SHARED_BUFFER=y ++# CONFIG_FENCE_TRACE is not set ++CONFIG_DMA_CMA=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=16 ++CONFIG_CMA_SIZE_SEL_MBYTES=y ++# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set ++# CONFIG_CMA_SIZE_SEL_MIN is not set ++# CONFIG_CMA_SIZE_SEL_MAX is not set ++CONFIG_CMA_ALIGNMENT=8 ++ ++# ++# Bus devices ++# ++# CONFIG_BRCMSTB_GISB_ARB is not set ++CONFIG_ARM_CCI=y ++# CONFIG_VEXPRESS_CONFIG is not set ++# CONFIG_CONNECTOR is not set ++CONFIG_MTD=y ++# CONFIG_MTD_TESTS is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++CONFIG_MTD_CMDLINE_PARTS=y ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_OF_PARTS=y ++# CONFIG_MTD_AR7_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_BLKDEVS=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++# CONFIG_INFTL is not set ++# CONFIG_RFD_FTL is not set ++# CONFIG_SSFDC is not set ++# CONFIG_SM_FTL is not set ++# CONFIG_MTD_OOPS is not set ++# CONFIG_HIFMC is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++CONFIG_MTD_MAP_BANK_WIDTH_1=y ++CONFIG_MTD_MAP_BANK_WIDTH_2=y ++CONFIG_MTD_MAP_BANK_WIDTH_4=y ++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set ++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set ++CONFIG_MTD_CFI_I1=y ++CONFIG_MTD_CFI_I2=y ++# CONFIG_MTD_CFI_I4 is not set ++# CONFIG_MTD_CFI_I8 is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_COMPLEX_MAPPINGS is not set ++# CONFIG_MTD_PLATRAM is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_DATAFLASH is not set ++# CONFIG_MTD_M25P80 is not set ++# CONFIG_MTD_SST25L is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_PHRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLOCK2MTD is not set ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOCG3 is not set ++# CONFIG_MTD_NAND_IDS is not set ++# CONFIG_MTD_NAND is not set ++# CONFIG_MTD_ONENAND is not set ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++# CONFIG_MTD_LPDDR is not set ++# CONFIG_MTD_LPDDR2_NVM is not set ++CONFIG_MTD_SPI_NOR=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_SPI_HISI_SFC=y ++CONFIG_CLOSE_SPI_8PIN_4IO=y ++CONFIG_HISI_SPI_BLOCK_PROTECT=y ++CONFIG_MTD_UBI=y ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_LIMIT=20 ++CONFIG_MTD_UBI_FASTMAP=y ++# CONFIG_MTD_UBI_GLUEBI is not set ++# CONFIG_MTD_UBI_BLOCK is not set ++CONFIG_DTC=y ++CONFIG_OF=y ++ ++# ++# Device Tree and Open Firmware support ++# ++# CONFIG_OF_SELFTEST is not set ++CONFIG_OF_FLATTREE=y ++CONFIG_OF_EARLY_FLATTREE=y ++CONFIG_OF_ADDRESS=y ++CONFIG_OF_IRQ=y ++CONFIG_OF_NET=y ++CONFIG_OF_MDIO=y ++CONFIG_OF_MTD=y ++CONFIG_OF_RESERVED_MEM=y ++CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_NULL_BLK is not set ++# CONFIG_BLK_DEV_COW_COMMON is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_DRBD is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_BLK_DEV_RBD is not set ++ ++# ++# Misc devices ++# ++# CONFIG_SENSORS_LIS3LV02D is not set ++# CONFIG_AD525X_DPOT is not set ++# CONFIG_DUMMY_IRQ is not set ++# CONFIG_ICS932S401 is not set ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_APDS9802ALS is not set ++# CONFIG_ISL29003 is not set ++# CONFIG_ISL29020 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++# CONFIG_SENSORS_BH1780 is not set ++# CONFIG_SENSORS_BH1770 is not set ++# CONFIG_SENSORS_APDS990X is not set ++# CONFIG_HMC6352 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_TI_DAC7512 is not set ++# CONFIG_UID_STAT is not set ++# CONFIG_BMP085_I2C is not set ++# CONFIG_BMP085_SPI is not set ++# CONFIG_USB_SWITCH_FSA9480 is not set ++# CONFIG_LATTICE_ECP3_CONFIG is not set ++# CONFIG_SRAM is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_AT24 is not set ++# CONFIG_EEPROM_AT25 is not set ++# CONFIG_EEPROM_LEGACY is not set ++# CONFIG_EEPROM_MAX6875 is not set ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_EEPROM_93XX46 is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_SENSORS_LIS3_SPI is not set ++# CONFIG_SENSORS_LIS3_I2C is not set ++ ++# ++# Altera FPGA firmware download module ++# ++# CONFIG_ALTERA_STAPL is not set ++ ++# ++# Intel MIC Bus Driver ++# ++ ++# ++# Intel MIC Host Driver ++# ++ ++# ++# Intel MIC Card Driver ++# ++# CONFIG_ECHO is not set ++# CONFIG_CXL_BASE is not set ++ ++# ++# hisi 'himm/himd.l/himc'support ++# ++# CONFIG_HISI_REG is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_MQ_DEFAULT is not set ++CONFIG_SCSI_PROC_FS=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=y ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++CONFIG_NET_CORE=y ++# CONFIG_BONDING is not set ++# CONFIG_DUMMY is not set ++# CONFIG_EQUALIZER is not set ++# CONFIG_NET_TEAM is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_VXLAN is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_TUN is not set ++# CONFIG_VETH is not set ++# CONFIG_NLMON is not set ++ ++# ++# CAIF transport drivers ++# ++ ++# ++# Distributed Switch Architecture drivers ++# ++# CONFIG_NET_DSA_MV88E6XXX is not set ++# CONFIG_NET_DSA_MV88E6060 is not set ++# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set ++# CONFIG_NET_DSA_MV88E6131 is not set ++# CONFIG_NET_DSA_MV88E6123_61_65 is not set ++# CONFIG_NET_DSA_MV88E6171 is not set ++# CONFIG_NET_DSA_BCM_SF2 is not set ++# CONFIG_ETHERNET is not set ++CONFIG_PHYLIB=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_AT803X_PHY is not set ++# CONFIG_AMD_PHY is not set ++# CONFIG_AMD_XGBE_PHY is not set ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_BCM7XXX_PHY is not set ++# CONFIG_BCM87XX_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++# CONFIG_FIXED_PHY is not set ++# CONFIG_MDIO_BITBANG is not set ++# CONFIG_MDIO_BUS_MUX_MMIOREG is not set ++# CONFIG_MDIO_BCM_UNIMAC is not set ++# CONFIG_MDIO_HISI_FEMAC is not set ++# CONFIG_MDIO_HISI_GEMAC is not set ++# CONFIG_MICREL_KS8995MA is not set ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++ ++# ++# Host-side USB support is needed for USB Network Adapter support ++# ++CONFIG_USB_NET_DRIVERS=m ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++# CONFIG_USB_RTL8152 is not set ++# CONFIG_USB_USBNET is not set ++# CONFIG_USB_IPHETH is not set ++CONFIG_WLAN=y ++# CONFIG_WIFI_CONTROL_FUNC is not set ++# CONFIG_HOSTAP is not set ++# CONFIG_WL_TI is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++# CONFIG_WAN is not set ++# CONFIG_ISDN is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++# CONFIG_INPUT_MATRIXKMAP is not set ++ ++# ++# Userland interfaces ++# ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=y ++# CONFIG_INPUT_EVBUG is not set ++# CONFIG_INPUT_KEYRESET is not set ++# CONFIG_INPUT_KEYCOMBO is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++# CONFIG_INPUT_MISC is not set ++ ++# ++# Hardware I/O ports ++# ++# CONFIG_SERIO is not set ++# CONFIG_GAMEPORT is not set ++ ++# ++# Character devices ++# ++CONFIG_TTY=y ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_VT_CONSOLE_SLEEP=y ++CONFIG_HW_CONSOLE=y ++# CONFIG_VT_HW_CONSOLE_BINDING is not set ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++CONFIG_DEVMEM=y ++CONFIG_DEVKMEM=y ++ ++# ++# Serial drivers ++# ++CONFIG_SERIAL_EARLYCON=y ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set ++# CONFIG_SERIAL_MAX3100 is not set ++# CONFIG_SERIAL_MAX310X is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_SCCNXP is not set ++# CONFIG_SERIAL_SC16IS7XX is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_ARC is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_TTY_PRINTK is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++CONFIG_HW_RANDOM=y ++# CONFIG_HW_RANDOM_TIMERIOMEM is not set ++# CONFIG_R3964 is not set ++# CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++# CONFIG_DCC_TTY is not set ++# CONFIG_XILLYBUS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_COMPAT is not set ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_MUX=m ++ ++# ++# Multiplexer I2C Chip support ++# ++# CONFIG_I2C_MUX_PCA9541 is not set ++# CONFIG_I2C_HELPER_AUTO is not set ++# CONFIG_I2C_SMBUS is not set ++ ++# ++# I2C Algorithms ++# ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set ++ ++# ++# I2C Hardware Bus support ++# ++ ++# ++# I2C system bus drivers (mostly embedded / system-on-chip) ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_HIBVT is not set ++CONFIG_I2C_HISI_V110=y ++# CONFIG_I2C_NOMADIK is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PCA_PLATFORM is not set ++# CONFIG_I2C_PXA_PCI is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# External I2C/SMBus adapter drivers ++# ++# CONFIG_I2C_DIOLAN_U2C is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_ROBOTFUZZ_OSIF is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_TINY_USB is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++CONFIG_SPI=y ++# CONFIG_SPI_DEBUG is not set ++CONFIG_SPI_MASTER=y ++ ++# ++# SPI Master Controller Drivers ++# ++# CONFIG_SPI_ALTERA is not set ++# CONFIG_SPI_BITBANG is not set ++# CONFIG_SPI_CADENCE is not set ++# CONFIG_SPI_FSL_SPI is not set ++CONFIG_SPI_PL022=y ++# CONFIG_SPI_PXA2XX_PCI is not set ++# CONFIG_SPI_ROCKCHIP is not set ++# CONFIG_SPI_SC18IS602 is not set ++# CONFIG_SPI_XCOMM is not set ++# CONFIG_SPI_XILINX is not set ++# CONFIG_SPI_DESIGNWARE is not set ++ ++# ++# SPI Protocol Masters ++# ++CONFIG_SPI_SPIDEV=y ++# CONFIG_SPI_TLE62X0 is not set ++# CONFIG_SPMI is not set ++# CONFIG_HSI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++# CONFIG_PTP_1588_CLOCK is not set ++ ++# ++# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. ++# ++CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y ++CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y ++# CONFIG_GPIOLIB is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++# CONFIG_TEST_POWER is not set ++# CONFIG_BATTERY_DS2780 is not set ++# CONFIG_BATTERY_DS2781 is not set ++# CONFIG_BATTERY_DS2782 is not set ++# CONFIG_BATTERY_SBS is not set ++# CONFIG_BATTERY_BQ27x00 is not set ++# CONFIG_BATTERY_MAX17040 is not set ++# CONFIG_BATTERY_MAX17042 is not set ++# CONFIG_CHARGER_MAX8903 is not set ++# CONFIG_CHARGER_LP8727 is not set ++# CONFIG_CHARGER_BQ2415X is not set ++# CONFIG_CHARGER_SMB347 is not set ++CONFIG_POWER_RESET=y ++# CONFIG_POWER_RESET_BRCMSTB is not set ++CONFIG_POWER_RESET_HISI=y ++# CONFIG_POWER_RESET_RESTART is not set ++# CONFIG_POWER_RESET_VERSATILE is not set ++# CONFIG_POWER_RESET_SYSCON is not set ++# CONFIG_POWER_AVS is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++ ++# ++# Multifunction device drivers ++# ++CONFIG_MFD_CORE=y ++# CONFIG_MFD_AS3711 is not set ++# CONFIG_MFD_AS3722 is not set ++# CONFIG_PMIC_ADP5520 is not set ++# CONFIG_MFD_BCM590XX is not set ++# CONFIG_MFD_AXP20X is not set ++# CONFIG_MFD_CROS_EC is not set ++# CONFIG_PMIC_DA903X is not set ++# CONFIG_MFD_DA9052_SPI is not set ++# CONFIG_MFD_DA9052_I2C is not set ++# CONFIG_MFD_DA9055 is not set ++# CONFIG_MFD_DA9063 is not set ++# CONFIG_MFD_MC13XXX_SPI is not set ++# CONFIG_MFD_MC13XXX_I2C is not set ++# CONFIG_MFD_HI6421_PMIC is not set ++CONFIG_MFD_HISI_FMC=y ++# CONFIG_HTC_PASIC3 is not set ++# CONFIG_INTEL_SOC_PMIC is not set ++# CONFIG_MFD_KEMPLD is not set ++# CONFIG_MFD_88PM800 is not set ++# CONFIG_MFD_88PM805 is not set ++# CONFIG_MFD_88PM860X is not set ++# CONFIG_MFD_MAX14577 is not set ++# CONFIG_MFD_MAX77686 is not set ++# CONFIG_MFD_MAX77693 is not set ++# CONFIG_MFD_MAX8907 is not set ++# CONFIG_MFD_MAX8925 is not set ++# CONFIG_MFD_MAX8997 is not set ++# CONFIG_MFD_MAX8998 is not set ++# CONFIG_MFD_MENF21BMC is not set ++# CONFIG_EZX_PCAP is not set ++# CONFIG_MFD_VIPERBOARD is not set ++# CONFIG_MFD_RETU is not set ++# CONFIG_MFD_PCF50633 is not set ++# CONFIG_MFD_PM8921_CORE is not set ++# CONFIG_MFD_RTSX_USB is not set ++# CONFIG_MFD_RC5T583 is not set ++# CONFIG_MFD_RK808 is not set ++# CONFIG_MFD_RN5T618 is not set ++# CONFIG_MFD_SEC_CORE is not set ++# CONFIG_MFD_SI476X_CORE is not set ++# CONFIG_MFD_SM501 is not set ++# CONFIG_MFD_SMSC is not set ++# CONFIG_ABX500_CORE is not set ++# CONFIG_MFD_STMPE is not set ++CONFIG_MFD_SYSCON=y ++# CONFIG_MFD_TI_AM335X_TSCADC is not set ++# CONFIG_MFD_LP3943 is not set ++# CONFIG_MFD_LP8788 is not set ++# CONFIG_MFD_PALMAS is not set ++# CONFIG_TPS6105X is not set ++# CONFIG_TPS6507X is not set ++# CONFIG_MFD_TPS65090 is not set ++# CONFIG_MFD_TPS65217 is not set ++# CONFIG_MFD_TPS65218 is not set ++# CONFIG_MFD_TPS6586X is not set ++# CONFIG_MFD_TPS80031 is not set ++# CONFIG_TWL4030_CORE is not set ++# CONFIG_TWL6040_CORE is not set ++# CONFIG_MFD_WL1273_CORE is not set ++# CONFIG_MFD_LM3533 is not set ++# CONFIG_MFD_TC3589X is not set ++# CONFIG_MFD_TMIO is not set ++# CONFIG_MFD_T7L66XB is not set ++# CONFIG_MFD_TC6387XB is not set ++# CONFIG_MFD_TC6393XB is not set ++# CONFIG_MFD_ARIZONA_I2C is not set ++# CONFIG_MFD_ARIZONA_SPI is not set ++# CONFIG_MFD_WM8400 is not set ++# CONFIG_MFD_WM831X_I2C is not set ++# CONFIG_MFD_WM831X_SPI is not set ++# CONFIG_MFD_WM8350_I2C is not set ++# CONFIG_MFD_WM8994 is not set ++CONFIG_REGULATOR=y ++# CONFIG_REGULATOR_DEBUG is not set ++# CONFIG_REGULATOR_FIXED_VOLTAGE is not set ++# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set ++# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set ++# CONFIG_REGULATOR_ACT8865 is not set ++# CONFIG_REGULATOR_AD5398 is not set ++# CONFIG_REGULATOR_ANATOP is not set ++# CONFIG_REGULATOR_DA9210 is not set ++# CONFIG_REGULATOR_DA9211 is not set ++# CONFIG_REGULATOR_FAN53555 is not set ++# CONFIG_REGULATOR_ISL9305 is not set ++# CONFIG_REGULATOR_ISL6271A is not set ++# CONFIG_REGULATOR_LP3971 is not set ++# CONFIG_REGULATOR_LP3972 is not set ++# CONFIG_REGULATOR_LP872X is not set ++# CONFIG_REGULATOR_LP8755 is not set ++# CONFIG_REGULATOR_LTC3589 is not set ++# CONFIG_REGULATOR_MAX1586 is not set ++# CONFIG_REGULATOR_MAX8649 is not set ++# CONFIG_REGULATOR_MAX8660 is not set ++# CONFIG_REGULATOR_MAX8952 is not set ++# CONFIG_REGULATOR_MAX8973 is not set ++# CONFIG_REGULATOR_PFUZE100 is not set ++# CONFIG_REGULATOR_TPS51632 is not set ++# CONFIG_REGULATOR_TPS62360 is not set ++# CONFIG_REGULATOR_TPS65023 is not set ++# CONFIG_REGULATOR_TPS6507X is not set ++# CONFIG_REGULATOR_TPS6524X is not set ++CONFIG_MEDIA_SUPPORT=m ++ ++# ++# Multimedia core support ++# ++CONFIG_MEDIA_CAMERA_SUPPORT=y ++# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set ++# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set ++# CONFIG_MEDIA_RADIO_SUPPORT is not set ++# CONFIG_MEDIA_SDR_SUPPORT is not set ++# CONFIG_MEDIA_RC_SUPPORT is not set ++# CONFIG_MEDIA_CONTROLLER is not set ++CONFIG_VIDEO_DEV=m ++CONFIG_VIDEO_V4L2=m ++# CONFIG_VIDEO_ADV_DEBUG is not set ++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set ++CONFIG_VIDEOBUF2_CORE=m ++CONFIG_VIDEOBUF2_MEMOPS=m ++CONFIG_VIDEOBUF2_VMALLOC=m ++# CONFIG_TTPCI_EEPROM is not set ++ ++# ++# Media drivers ++# ++# CONFIG_MEDIA_USB_SUPPORT is not set ++# CONFIG_V4L_PLATFORM_DRIVERS is not set ++# CONFIG_V4L_MEM2MEM_DRIVERS is not set ++# CONFIG_V4L_TEST_DRIVERS is not set ++ ++# ++# Supported MMC/SDIO adapters ++# ++# CONFIG_CYPRESS_FIRMWARE is not set ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, frontends) ++# ++CONFIG_MEDIA_SUBDRV_AUTOSELECT=y ++ ++# ++# Audio decoders, processors and mixers ++# ++ ++# ++# RDS decoders ++# ++ ++# ++# Video decoders ++# ++ ++# ++# Video and audio decoders ++# ++ ++# ++# Video encoders ++# ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Flash devices ++# ++ ++# ++# Video improvement chips ++# ++ ++# ++# Audio/Video compression chips ++# ++ ++# ++# Miscellaneous helper chips ++# ++ ++# ++# Sensors used on soc_camera driver ++# ++ ++# ++# Tools to develop new frontends ++# ++# CONFIG_DVB_DUMMY_FE is not set ++ ++# ++# Graphics support ++# ++# CONFIG_IMX_IPUV3_CORE is not set ++ ++# ++# Direct Rendering Manager ++# ++# CONFIG_DRM is not set ++ ++# ++# Frame buffer Devices ++# ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++CONFIG_FB_CMDLINE=y ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++# CONFIG_FB_CFB_FILLRECT is not set ++# CONFIG_FB_CFB_COPYAREA is not set ++# CONFIG_FB_CFB_IMAGEBLIT is not set ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_OPENCORES is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_SMSCUFX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_FB_AUO_K190X is not set ++# CONFIG_FB_SIMPLE is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++# CONFIG_VGASTATE is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE is not set ++# CONFIG_LOGO is not set ++# CONFIG_SOUND is not set ++ ++# ++# HID support ++# ++CONFIG_HID=y ++# CONFIG_HID_BATTERY_STRENGTH is not set ++# CONFIG_HIDRAW is not set ++# CONFIG_UHID is not set ++CONFIG_HID_GENERIC=y ++ ++# ++# Special HID drivers ++# ++# CONFIG_HID_A4TECH is not set ++# CONFIG_HID_ACRUX is not set ++# CONFIG_HID_APPLE is not set ++# CONFIG_HID_APPLEIR is not set ++# CONFIG_HID_AUREAL is not set ++# CONFIG_HID_BELKIN is not set ++# CONFIG_HID_CHERRY is not set ++# CONFIG_HID_CHICONY is not set ++# CONFIG_HID_CYPRESS is not set ++# CONFIG_HID_DRAGONRISE is not set ++# CONFIG_HID_EMS_FF is not set ++# CONFIG_HID_ELECOM is not set ++# CONFIG_HID_ELO is not set ++# CONFIG_HID_EZKEY is not set ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_HUION is not set ++# CONFIG_HID_KEYTOUCH is not set ++# CONFIG_HID_KYE is not set ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++# CONFIG_HID_GYRATION is not set ++# CONFIG_HID_ICADE is not set ++# CONFIG_HID_TWINHAN is not set ++# CONFIG_HID_KENSINGTON is not set ++# CONFIG_HID_LCPOWER is not set ++# CONFIG_HID_LENOVO is not set ++# CONFIG_HID_LOGITECH is not set ++# CONFIG_HID_MAGICMOUSE is not set ++# CONFIG_HID_MICROSOFT is not set ++# CONFIG_HID_MONTEREY is not set ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++# CONFIG_HID_ORTEK is not set ++# CONFIG_HID_PANTHERLORD is not set ++# CONFIG_HID_PENMOUNT is not set ++# CONFIG_HID_PETALYNX is not set ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_PRIMAX is not set ++# CONFIG_HID_ROCCAT is not set ++# CONFIG_HID_SAITEK is not set ++# CONFIG_HID_SAMSUNG is not set ++# CONFIG_HID_SPEEDLINK is not set ++# CONFIG_HID_STEELSERIES is not set ++# CONFIG_HID_SUNPLUS is not set ++# CONFIG_HID_RMI is not set ++# CONFIG_HID_GREENASIA is not set ++# CONFIG_HID_SMARTJOYPLUS is not set ++# CONFIG_HID_TIVO is not set ++# CONFIG_HID_TOPSEED is not set ++# CONFIG_HID_THRUSTMASTER is not set ++# CONFIG_HID_WACOM is not set ++# CONFIG_HID_XINMO is not set ++# CONFIG_HID_ZEROPLUS is not set ++# CONFIG_HID_ZYDACRON is not set ++# CONFIG_HID_SENSOR_HUB is not set ++ ++# ++# USB HID support ++# ++CONFIG_USB_HID=m ++# CONFIG_HID_PID is not set ++# CONFIG_USB_HIDDEV is not set ++ ++# ++# USB HID Boot Protocol drivers ++# ++# CONFIG_USB_KBD is not set ++# CONFIG_USB_MOUSE is not set ++ ++# ++# I2C HID support ++# ++# CONFIG_I2C_HID is not set ++CONFIG_USB_OHCI_LITTLE_ENDIAN=y ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_COMMON=y ++CONFIG_USB_ARCH_HAS_HCD=y ++CONFIG_USB=m ++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEFAULT_PERSIST is not set ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++# CONFIG_USB_OTG_FSM is not set ++# CONFIG_USB_MON is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_XHCI_HCD is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_FUSBH200_HCD is not set ++# CONFIG_USB_FOTG210_HCD is not set ++# CONFIG_USB_MAX3421_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HCD_TEST_MODE is not set ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++# CONFIG_USB_STORAGE is not set ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++# CONFIG_USBIP_CORE is not set ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CHIPIDEA is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_EHSET_TEST_FIXTURE is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_EZUSB_FX2 is not set ++# CONFIG_USB_HSIC_USB3503 is not set ++# CONFIG_USB_LINK_LAYER_TEST is not set ++ ++# ++# USB Physical Layer drivers ++# ++# CONFIG_USB_PHY is not set ++# CONFIG_NOP_USB_XCEIV is not set ++# CONFIG_AM335X_PHY_USB is not set ++# CONFIG_USB_ISP1301 is not set ++# CONFIG_USB_ULPI is not set ++CONFIG_USB_GADGET=y ++# CONFIG_USB_GADGET_DEBUG is not set ++# CONFIG_USB_GADGET_DEBUG_FILES is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 ++ ++# ++# USB Peripheral Controller ++# ++# CONFIG_USB_FUSB300 is not set ++# CONFIG_USB_FOTG210_UDC is not set ++# CONFIG_USB_GR_UDC is not set ++# CONFIG_USB_R8A66597 is not set ++# CONFIG_USB_PXA27X is not set ++# CONFIG_USB_MV_UDC is not set ++CONFIG_HIUSB_DEVICE2_0=y ++CONFIG_USB_HISI_UDC=m ++# CONFIG_USB_AUTO_SWITCH is not set ++# CONFIG_HIUSB_DEVICE3_0 is not set ++# CONFIG_USB_MV_U3D is not set ++# CONFIG_USB_M66592 is not set ++# CONFIG_USB_NET2272 is not set ++# CONFIG_USB_GADGET_XILINX is not set ++CONFIG_USB_LIBCOMPOSITE=m ++CONFIG_USB_F_ACM=m ++CONFIG_USB_U_SERIAL=m ++CONFIG_USB_U_ETHER=m ++CONFIG_USB_F_RNDIS=m ++CONFIG_USB_F_MASS_STORAGE=m ++CONFIG_USB_F_UAC1=m ++CONFIG_USB_F_UVC=m ++# CONFIG_USB_CONFIGFS is not set ++# CONFIG_USB_ZERO is not set ++CONFIG_USB_AUDIO=m ++CONFIG_GADGET_UAC1=y ++# CONFIG_USB_ETH is not set ++# CONFIG_USB_G_NCM is not set ++# CONFIG_USB_GADGETFS is not set ++# CONFIG_USB_FUNCTIONFS is not set ++CONFIG_USB_MASS_STORAGE=m ++# CONFIG_USB_G_SERIAL is not set ++# CONFIG_USB_G_PRINTER is not set ++# CONFIG_USB_CDC_COMPOSITE is not set ++# CONFIG_USB_G_ACM_MS is not set ++CONFIG_USB_G_MULTI=m ++CONFIG_USB_G_MULTI_RNDIS=y ++# CONFIG_USB_G_MULTI_CDC is not set ++# CONFIG_USB_G_HID is not set ++# CONFIG_USB_G_DBGP is not set ++# CONFIG_USB_G_WEBCAM is not set ++CONFIG_USB_G_WEBCAM_AUDIO=m ++# CONFIG_UWB is not set ++CONFIG_MMC=m ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_CLKGATE is not set ++CONFIG_MMC_EMBEDDED_SDIO=y ++# CONFIG_MMC_PARANOID_SD_INIT is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=m ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++# CONFIG_MMC_SDHCI is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MMC_USDHI6ROL0 is not set ++CONFIG_HIMCIV200=m ++CONFIG_SEND_AUTO_STOP=y ++CONFIG_DETECT_CARD_TIME=200 ++# CONFIG_MEMSTICK is not set ++# CONFIG_NEW_LEDS is not set ++# CONFIG_SWITCH is not set ++# CONFIG_ACCESSIBILITY is not set ++# CONFIG_EDAC is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# Microsoft Hyper-V guest support ++# ++# CONFIG_STAGING is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++CONFIG_CLKDEV_LOOKUP=y ++CONFIG_HAVE_CLK_PREPARE=y ++CONFIG_COMMON_CLK=y ++ ++# ++# Common Clock Framework ++# ++# CONFIG_COMMON_CLK_SI5351 is not set ++# CONFIG_COMMON_CLK_SI570 is not set ++# CONFIG_COMMON_CLK_PXA is not set ++# CONFIG_COMMON_CLK_QCOM is not set ++ ++# ++# Hardware Spinlock drivers ++# ++ ++# ++# Clock Source drivers ++# ++CONFIG_CLKSRC_OF=y ++CONFIG_CLKSRC_MMIO=y ++CONFIG_ARM_ARCH_TIMER=y ++CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y ++# CONFIG_ARM_ARCH_TIMER_VCT_ACCESS is not set ++# CONFIG_ATMEL_PIT is not set ++# CONFIG_SH_TIMER_CMT is not set ++# CONFIG_SH_TIMER_MTU2 is not set ++# CONFIG_SH_TIMER_TMU is not set ++# CONFIG_EM_TIMER_STI is not set ++# CONFIG_CLKSRC_VERSATILE is not set ++# CONFIG_MAILBOX is not set ++# CONFIG_IOMMU_SUPPORT is not set ++ ++# ++# Remoteproc drivers ++# ++# CONFIG_STE_MODEM_RPROC is not set ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_IIO is not set ++# CONFIG_PWM is not set ++CONFIG_IRQCHIP=y ++CONFIG_ARM_GIC=y ++# CONFIG_IPACK_BUS is not set ++CONFIG_ARCH_HAS_RESET_CONTROLLER=y ++CONFIG_RESET_CONTROLLER=y ++# CONFIG_FMC is not set ++ ++# ++# PHY Subsystem ++# ++CONFIG_GENERIC_PHY=y ++# CONFIG_BCM_KONA_USB2_PHY is not set ++CONFIG_PHY_HISI_INNO_USB2=m ++# CONFIG_POWERCAP is not set ++# CONFIG_MCB is not set ++# CONFIG_HI_DMAC is not set ++ ++# ++# Hisilicon driver support ++# ++# CONFIG_CMA_MEM_SHARED is not set ++# CONFIG_CMA_ADVANCE_SHARE is not set ++ ++# ++# File systems ++# ++CONFIG_DCACHE_WORD_ACCESS=y ++# CONFIG_EXT2_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_EXT4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_FUSE_FS is not set ++# CONFIG_OVERLAY_FS is not set ++ ++# ++# Caches ++# ++# CONFIG_FSCACHE is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++# CONFIG_ISO9660_FS is not set ++# CONFIG_UDF_FS is not set ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" ++# CONFIG_NTFS_FS is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_KERNFS=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=m ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_YAFFS_FS is not set ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++CONFIG_JFFS2_FS_WRITEBUFFER=y ++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set ++# CONFIG_JFFS2_SUMMARY is not set ++# CONFIG_JFFS2_FS_XATTR is not set ++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set ++CONFIG_JFFS2_ZLIB=y ++# CONFIG_JFFS2_LZO is not set ++CONFIG_JFFS2_RTIME=y ++# CONFIG_JFFS2_RUBIN is not set ++CONFIG_UBIFS_FS=y ++# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set ++CONFIG_UBIFS_FS_LZO=y ++CONFIG_UBIFS_FS_ZLIB=y ++# CONFIG_LOGFS is not set ++# CONFIG_CRAMFS is not set ++CONFIG_SQUASHFS=y ++CONFIG_SQUASHFS_FILE_CACHE=y ++# CONFIG_SQUASHFS_FILE_DIRECT is not set ++CONFIG_SQUASHFS_DECOMP_SINGLE=y ++# CONFIG_SQUASHFS_DECOMP_MULTI is not set ++# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set ++# CONFIG_SQUASHFS_XATTR is not set ++CONFIG_SQUASHFS_ZLIB=y ++CONFIG_SQUASHFS_LZO=y ++# CONFIG_SQUASHFS_XZ is not set ++# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set ++# CONFIG_SQUASHFS_EMBEDDED is not set ++CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX6FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_F2FS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V2=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++# CONFIG_NFS_V4 is not set ++# CONFIG_NFS_SWAP is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++CONFIG_GRACE_PERIOD=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++# CONFIG_SUNRPC_DEBUG is not set ++# CONFIG_CEPH_FS is not set ++# CONFIG_CIFS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="iso8859-1" ++CONFIG_NLS_CODEPAGE_437=y ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ASCII is not set ++CONFIG_NLS_ISO8859_1=y ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_MAC_ROMAN is not set ++# CONFIG_NLS_MAC_CELTIC is not set ++# CONFIG_NLS_MAC_CENTEURO is not set ++# CONFIG_NLS_MAC_CROATIAN is not set ++# CONFIG_NLS_MAC_CYRILLIC is not set ++# CONFIG_NLS_MAC_GAELIC is not set ++# CONFIG_NLS_MAC_GREEK is not set ++# CONFIG_NLS_MAC_ICELAND is not set ++# CONFIG_NLS_MAC_INUIT is not set ++# CONFIG_NLS_MAC_ROMANIAN is not set ++# CONFIG_NLS_MAC_TURKISH is not set ++CONFIG_NLS_UTF8=y ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 ++# CONFIG_BOOT_PRINTK_DELAY is not set ++ ++# ++# Compile-time checks and compiler options ++# ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++CONFIG_FRAME_WARN=1024 ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_READABLE_ASM is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_MAGIC_SYSRQ is not set ++CONFIG_DEBUG_KERNEL=y ++ ++# ++# Memory Debugging ++# ++# CONFIG_DEBUG_PAGEALLOC is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_SLUB_STATS is not set ++CONFIG_HAVE_DEBUG_KMEMLEAK=y ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_VM is not set ++CONFIG_DEBUG_MEMORY_INIT=y ++# CONFIG_DEBUG_HIGHMEM is not set ++# CONFIG_DEBUG_SHIRQ is not set ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++CONFIG_PANIC_ON_OOPS=y ++CONFIG_PANIC_ON_OOPS_VALUE=1 ++CONFIG_PANIC_TIMEOUT=0 ++CONFIG_SCHED_DEBUG=y ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_SCHED_STACK_END_CHECK is not set ++# CONFIG_TIMER_STATS is not set ++ ++# ++# Lock Debugging (spinlocks, mutexes, etc...) ++# ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_LOCK_TORTURE_TEST is not set ++CONFIG_STACKTRACE=y ++# CONFIG_DEBUG_KOBJECT is not set ++CONFIG_DEBUG_BUGVERBOSE=y ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_DEBUG_PI_LIST is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++ ++# ++# RCU Debugging ++# ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_TORTURE_TEST is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_NOTIFIER_ERROR_INJECTION is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_SYSCALL_TRACEPOINTS=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++ ++# ++# Runtime Testing ++# ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_RBTREE_TEST is not set ++# CONFIG_INTERVAL_TREE_TEST is not set ++# CONFIG_PERCPU_TEST is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_TEST_STRING_HELPERS is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_TEST_RHASHTABLE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_TEST_LKM is not set ++# CONFIG_TEST_USER_COPY is not set ++# CONFIG_TEST_BPF is not set ++# CONFIG_TEST_FIRMWARE is not set ++# CONFIG_TEST_UDELAY is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_ARM_PTDUMP is not set ++CONFIG_STRICT_DEVMEM=y ++CONFIG_ARM_UNWIND=y ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" ++# CONFIG_DEBUG_UART_PL01X is not set ++# CONFIG_DEBUG_UART_8250 is not set ++# CONFIG_DEBUG_UART_BCM63XX is not set ++CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" ++# CONFIG_OC_ETM is not set ++# CONFIG_PID_IN_CONTEXTIDR is not set ++# CONFIG_DEBUG_SET_MODULE_RONX is not set ++# CONFIG_CORESIGHT is not set ++ ++# ++# Security options ++# ++# CONFIG_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=y ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_BLKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=y ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_PCOMP2=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++# CONFIG_CRYPTO_USER is not set ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++CONFIG_CRYPTO_WORKQUEUE=y ++# CONFIG_CRYPTO_CRYPTD is not set ++# CONFIG_CRYPTO_MCRYPTD is not set ++# CONFIG_CRYPTO_AUTHENC is not set ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++CONFIG_CRYPTO_CCM=y ++# CONFIG_CRYPTO_GCM is not set ++CONFIG_CRYPTO_SEQIV=y ++ ++# ++# Block modes ++# ++# CONFIG_CRYPTO_CBC is not set ++CONFIG_CRYPTO_CTR=y ++# CONFIG_CRYPTO_CTS is not set ++# CONFIG_CRYPTO_ECB is not set ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++# CONFIG_CRYPTO_CMAC is not set ++# CONFIG_CRYPTO_HMAC is not set ++# CONFIG_CRYPTO_XCBC is not set ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_CRC32 is not set ++# CONFIG_CRYPTO_CRCT10DIF is not set ++# CONFIG_CRYPTO_GHASH is not set ++# CONFIG_CRYPTO_MD4 is not set ++# CONFIG_CRYPTO_MD5 is not set ++# CONFIG_CRYPTO_MICHAEL_MIC is not set ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++# CONFIG_CRYPTO_SHA1 is not set ++# CONFIG_CRYPTO_SHA1_ARM is not set ++# CONFIG_CRYPTO_SHA1_ARM_NEON is not set ++# CONFIG_CRYPTO_SHA256 is not set ++# CONFIG_CRYPTO_SHA512 is not set ++# CONFIG_CRYPTO_SHA512_ARM_NEON is not set ++# CONFIG_CRYPTO_TGR192 is not set ++# CONFIG_CRYPTO_WP512 is not set ++ ++# ++# Ciphers ++# ++CONFIG_CRYPTO_AES=y ++# CONFIG_CRYPTO_AES_ARM is not set ++# CONFIG_CRYPTO_AES_ARM_BS is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=y ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++# CONFIG_CRYPTO_CAST5 is not set ++# CONFIG_CRYPTO_CAST6 is not set ++# CONFIG_CRYPTO_DES is not set ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=y ++# CONFIG_CRYPTO_ZLIB is not set ++CONFIG_CRYPTO_LZO=y ++# CONFIG_CRYPTO_LZ4 is not set ++# CONFIG_CRYPTO_LZ4HC is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_DRBG_MENU is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++CONFIG_CRYPTO_HW=y ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_GENERIC_STRNCPY_FROM_USER=y ++CONFIG_GENERIC_STRNLEN_USER=y ++CONFIG_GENERIC_NET_UTILS=y ++CONFIG_GENERIC_PCI_IOMAP=y ++CONFIG_GENERIC_IO=y ++CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y ++# CONFIG_CRC_CCITT is not set ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++# CONFIG_CRC_ITU_T is not set ++CONFIG_CRC32=y ++# CONFIG_CRC32_SELFTEST is not set ++CONFIG_CRC32_SLICEBY8=y ++# CONFIG_CRC32_SLICEBY4 is not set ++# CONFIG_CRC32_SARWATE is not set ++# CONFIG_CRC32_BIT is not set ++# CONFIG_CRC7 is not set ++CONFIG_LIBCRC32C=y ++# CONFIG_CRC8 is not set ++# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set ++# CONFIG_RANDOM32_SELFTEST is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++CONFIG_LZO_COMPRESS=y ++CONFIG_LZO_DECOMPRESS=y ++CONFIG_XZ_DEC=y ++CONFIG_XZ_DEC_X86=y ++CONFIG_XZ_DEC_POWERPC=y ++CONFIG_XZ_DEC_IA64=y ++CONFIG_XZ_DEC_ARM=y ++CONFIG_XZ_DEC_ARMTHUMB=y ++CONFIG_XZ_DEC_SPARC=y ++CONFIG_XZ_DEC_BCJ=y ++# CONFIG_XZ_DEC_TEST is not set ++CONFIG_GENERIC_ALLOCATOR=y ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT_MAP=y ++CONFIG_HAS_DMA=y ++CONFIG_DQL=y ++CONFIG_NLATTR=y ++CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y ++CONFIG_AVERAGE=y ++# CONFIG_CORDIC is not set ++# CONFIG_DDR is not set ++CONFIG_LIBFDT=y ++CONFIG_ARCH_HAS_SG_CHAIN=y ++# CONFIG_VIRTUALIZATION is not set +diff --git a/arch/arm/configs/hisi_defconfig b/arch/arm/configs/hisi_defconfig +index 1772505..9e35ff8 100644 +--- a/arch/arm/configs/hisi_defconfig ++++ b/arch/arm/configs/hisi_defconfig +@@ -7,6 +7,7 @@ CONFIG_ARCH_HISI=y + CONFIG_ARCH_HI3xxx=y + CONFIG_ARCH_HIX5HD2=y + CONFIG_ARCH_HIP04=y ++CONFIG_ARCH_HI3519=y + CONFIG_SMP=y + CONFIG_NR_CPUS=16 + CONFIG_PREEMPT=y +diff --git a/arch/arm/include/asm/fiq_glue.h b/arch/arm/include/asm/fiq_glue.h +new file mode 100644 +index 0000000..a9e244f +--- /dev/null ++++ b/arch/arm/include/asm/fiq_glue.h +@@ -0,0 +1,33 @@ ++/* ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __ASM_FIQ_GLUE_H ++#define __ASM_FIQ_GLUE_H ++ ++struct fiq_glue_handler { ++ void (*fiq)(struct fiq_glue_handler *h, void *regs, void *svc_sp); ++ void (*resume)(struct fiq_glue_handler *h); ++}; ++typedef void (*fiq_return_handler_t)(void); ++ ++int fiq_glue_register_handler(struct fiq_glue_handler *handler); ++int fiq_glue_set_return_handler(fiq_return_handler_t fiq_return); ++int fiq_glue_clear_return_handler(fiq_return_handler_t fiq_return); ++ ++#ifdef CONFIG_FIQ_GLUE ++void fiq_glue_resume(void); ++#else ++static inline void fiq_glue_resume(void) {} ++#endif ++ ++#endif +diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h +index fe3ea77..5df33e3 100644 +--- a/arch/arm/include/asm/hardirq.h ++++ b/arch/arm/include/asm/hardirq.h +@@ -5,7 +5,7 @@ + #include <linux/threads.h> + #include <asm/irq.h> + +-#define NR_IPI 8 ++#define NR_IPI 9 + + typedef struct { + unsigned int __softirq_pending; +diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h +index ad774f3..b350765 100644 +--- a/arch/arm/include/asm/hardware/coresight.h ++++ b/arch/arm/include/asm/hardware/coresight.h +@@ -17,15 +17,23 @@ + #define TRACER_ACCESSED_BIT 0 + #define TRACER_RUNNING_BIT 1 + #define TRACER_CYCLE_ACC_BIT 2 ++#define TRACER_TRACE_DATA_BIT 3 ++#define TRACER_TIMESTAMP_BIT 4 ++#define TRACER_BRANCHOUTPUT_BIT 5 ++#define TRACER_RETURN_STACK_BIT 6 + #define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT) + #define TRACER_RUNNING BIT(TRACER_RUNNING_BIT) + #define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT) ++#define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT) ++#define TRACER_TIMESTAMP BIT(TRACER_TIMESTAMP_BIT) ++#define TRACER_BRANCHOUTPUT BIT(TRACER_BRANCHOUTPUT_BIT) ++#define TRACER_RETURN_STACK BIT(TRACER_RETURN_STACK_BIT) + + #define TRACER_TIMEOUT 10000 + +-#define etm_writel(t, v, x) \ +- (writel_relaxed((v), (t)->etm_regs + (x))) +-#define etm_readl(t, x) (readl_relaxed((t)->etm_regs + (x))) ++#define etm_writel(t, id, v, x) \ ++ (writel_relaxed((v), (t)->etm_regs[(id)] + (x))) ++#define etm_readl(t, id, x) (readl_relaxed((t)->etm_regs[(id)] + (x))) + + /* CoreSight Management Registers */ + #define CSMR_LOCKACCESS 0xfb0 +@@ -43,7 +51,7 @@ + #define ETMCTRL_POWERDOWN 1 + #define ETMCTRL_PROGRAM (1 << 10) + #define ETMCTRL_PORTSEL (1 << 11) +-#define ETMCTRL_DO_CONTEXTID (3 << 14) ++#define ETMCTRL_CONTEXTIDSIZE(x) (((x) & 3) << 14) + #define ETMCTRL_PORTMASK1 (7 << 4) + #define ETMCTRL_PORTMASK2 (1 << 21) + #define ETMCTRL_PORTMASK (ETMCTRL_PORTMASK1 | ETMCTRL_PORTMASK2) +@@ -55,9 +63,12 @@ + #define ETMCTRL_DATA_DO_BOTH (ETMCTRL_DATA_DO_DATA | ETMCTRL_DATA_DO_ADDR) + #define ETMCTRL_BRANCH_OUTPUT (1 << 8) + #define ETMCTRL_CYCLEACCURATE (1 << 12) ++#define ETMCTRL_TIMESTAMP_EN (1 << 28) ++#define ETMCTRL_RETURN_STACK_EN (1 << 29) + + /* ETM configuration code register */ + #define ETMR_CONFCODE (0x04) ++#define ETMCCR_ETMIDR_PRESENT BIT(31) + + /* ETM trace start/stop resource control register */ + #define ETMR_TRACESSCTRL (0x18) +@@ -113,10 +124,25 @@ + #define ETMR_TRACEENCTRL 0x24 + #define ETMTE_INCLEXCL BIT(24) + #define ETMR_TRACEENEVT 0x20 +-#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \ +- ETMCTRL_DATA_DO_ADDR | \ +- ETMCTRL_BRANCH_OUTPUT | \ +- ETMCTRL_DO_CONTEXTID) ++ ++#define ETMR_VIEWDATAEVT 0x30 ++#define ETMR_VIEWDATACTRL1 0x34 ++#define ETMR_VIEWDATACTRL2 0x38 ++#define ETMR_VIEWDATACTRL3 0x3c ++#define ETMVDC3_EXCLONLY BIT(16) ++ ++#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT) ++ ++#define ETMR_ID 0x1e4 ++#define ETMIDR_VERSION(x) (((x) >> 4) & 0xff) ++#define ETMIDR_VERSION_3_1 0x21 ++#define ETMIDR_VERSION_PFT_1_0 0x30 ++ ++#define ETMR_CCE 0x1e8 ++#define ETMCCER_RETURN_STACK_IMPLEMENTED BIT(23) ++#define ETMCCER_TIMESTAMPING_IMPLEMENTED BIT(22) ++ ++#define ETMR_TRACEIDR 0x200 + + /* ETM management registers, "ETM Architecture", 3.5.24 */ + #define ETMMR_OSLAR 0x300 +@@ -140,14 +166,16 @@ + #define ETBFF_TRIGIN BIT(8) + #define ETBFF_TRIGEVT BIT(9) + #define ETBFF_TRIGFL BIT(10) ++#define ETBFF_STOPFL BIT(12) + + #define etb_writel(t, v, x) \ + (writel_relaxed((v), (t)->etb_regs + (x))) + #define etb_readl(t, x) (readl_relaxed((t)->etb_regs + (x))) + +-#define etm_lock(t) do { etm_writel((t), 0, CSMR_LOCKACCESS); } while (0) +-#define etm_unlock(t) \ +- do { etm_writel((t), CS_LAR_KEY, CSMR_LOCKACCESS); } while (0) ++#define etm_lock(t, id) \ ++ do { etm_writel((t), (id), 0, CSMR_LOCKACCESS); } while (0) ++#define etm_unlock(t, id) \ ++ do { etm_writel((t), (id), CS_LAR_KEY, CSMR_LOCKACCESS); } while (0) + + #define etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0) + #define etb_unlock(t) \ +diff --git a/arch/arm/include/asm/hardware/cp14.h b/arch/arm/include/asm/hardware/cp14.h +new file mode 100644 +index 0000000..61576dc +--- /dev/null ++++ b/arch/arm/include/asm/hardware/cp14.h +@@ -0,0 +1,542 @@ ++/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __ASM_HARDWARE_CP14_H ++#define __ASM_HARDWARE_CP14_H ++ ++#include <linux/types.h> ++ ++/* Accessors for CP14 registers */ ++#define dbg_read(reg) RCP14_##reg() ++#define dbg_write(val, reg) WCP14_##reg(val) ++#define etm_read(reg) RCP14_##reg() ++#define etm_write(val, reg) WCP14_##reg(val) ++ ++/* MRC14 and MCR14 */ ++#define MRC14(op1, crn, crm, op2) \ ++({ \ ++u32 val; \ ++asm volatile("mrc p14, "#op1", %0, "#crn", "#crm", "#op2 : "=r" (val)); \ ++val; \ ++}) ++ ++#define MCR14(val, op1, crn, crm, op2) \ ++({ \ ++asm volatile("mcr p14, "#op1", %0, "#crn", "#crm", "#op2 : : "r" (val));\ ++}) ++ ++/* ++ * Debug Registers ++ * ++ * Available only in DBGv7 ++ * DBGECR, DBGDSCCR, DBGDSMCR, DBGDRCR ++ * ++ * Available only in DBGv7.1 ++ * DBGBXVRm, DBGOSDLR, DBGDEVID2, DBGDEVID1 ++ * ++ * Read only ++ * DBGDIDR, DBGDSCRint, DBGDTRRXint, DBGDRAR, DBGOSLSR, DBGOSSRR, DBGPRSR, ++ * DBGPRSR, DBGDSAR, DBGAUTHSTATUS, DBGDEVID2, DBGDEVID1, DBGDEVID ++ * ++ * Write only ++ * DBGDTRTXint, DBGOSLAR ++ */ ++#define RCP14_DBGDIDR() MRC14(0, c0, c0, 0) ++#define RCP14_DBGDSCRint() MRC14(0, c0, c1, 0) ++#define RCP14_DBGDTRRXint() MRC14(0, c0, c5, 0) ++#define RCP14_DBGWFAR() MRC14(0, c0, c6, 0) ++#define RCP14_DBGVCR() MRC14(0, c0, c7, 0) ++#define RCP14_DBGECR() MRC14(0, c0, c9, 0) ++#define RCP14_DBGDSCCR() MRC14(0, c0, c10, 0) ++#define RCP14_DBGDSMCR() MRC14(0, c0, c11, 0) ++#define RCP14_DBGDTRRXext() MRC14(0, c0, c0, 2) ++#define RCP14_DBGDSCRext() MRC14(0, c0, c2, 2) ++#define RCP14_DBGDTRTXext() MRC14(0, c0, c3, 2) ++#define RCP14_DBGDRCR() MRC14(0, c0, c4, 2) ++#define RCP14_DBGBVR0() MRC14(0, c0, c0, 4) ++#define RCP14_DBGBVR1() MRC14(0, c0, c1, 4) ++#define RCP14_DBGBVR2() MRC14(0, c0, c2, 4) ++#define RCP14_DBGBVR3() MRC14(0, c0, c3, 4) ++#define RCP14_DBGBVR4() MRC14(0, c0, c4, 4) ++#define RCP14_DBGBVR5() MRC14(0, c0, c5, 4) ++#define RCP14_DBGBVR6() MRC14(0, c0, c6, 4) ++#define RCP14_DBGBVR7() MRC14(0, c0, c7, 4) ++#define RCP14_DBGBVR8() MRC14(0, c0, c8, 4) ++#define RCP14_DBGBVR9() MRC14(0, c0, c9, 4) ++#define RCP14_DBGBVR10() MRC14(0, c0, c10, 4) ++#define RCP14_DBGBVR11() MRC14(0, c0, c11, 4) ++#define RCP14_DBGBVR12() MRC14(0, c0, c12, 4) ++#define RCP14_DBGBVR13() MRC14(0, c0, c13, 4) ++#define RCP14_DBGBVR14() MRC14(0, c0, c14, 4) ++#define RCP14_DBGBVR15() MRC14(0, c0, c15, 4) ++#define RCP14_DBGBCR0() MRC14(0, c0, c0, 5) ++#define RCP14_DBGBCR1() MRC14(0, c0, c1, 5) ++#define RCP14_DBGBCR2() MRC14(0, c0, c2, 5) ++#define RCP14_DBGBCR3() MRC14(0, c0, c3, 5) ++#define RCP14_DBGBCR4() MRC14(0, c0, c4, 5) ++#define RCP14_DBGBCR5() MRC14(0, c0, c5, 5) ++#define RCP14_DBGBCR6() MRC14(0, c0, c6, 5) ++#define RCP14_DBGBCR7() MRC14(0, c0, c7, 5) ++#define RCP14_DBGBCR8() MRC14(0, c0, c8, 5) ++#define RCP14_DBGBCR9() MRC14(0, c0, c9, 5) ++#define RCP14_DBGBCR10() MRC14(0, c0, c10, 5) ++#define RCP14_DBGBCR11() MRC14(0, c0, c11, 5) ++#define RCP14_DBGBCR12() MRC14(0, c0, c12, 5) ++#define RCP14_DBGBCR13() MRC14(0, c0, c13, 5) ++#define RCP14_DBGBCR14() MRC14(0, c0, c14, 5) ++#define RCP14_DBGBCR15() MRC14(0, c0, c15, 5) ++#define RCP14_DBGWVR0() MRC14(0, c0, c0, 6) ++#define RCP14_DBGWVR1() MRC14(0, c0, c1, 6) ++#define RCP14_DBGWVR2() MRC14(0, c0, c2, 6) ++#define RCP14_DBGWVR3() MRC14(0, c0, c3, 6) ++#define RCP14_DBGWVR4() MRC14(0, c0, c4, 6) ++#define RCP14_DBGWVR5() MRC14(0, c0, c5, 6) ++#define RCP14_DBGWVR6() MRC14(0, c0, c6, 6) ++#define RCP14_DBGWVR7() MRC14(0, c0, c7, 6) ++#define RCP14_DBGWVR8() MRC14(0, c0, c8, 6) ++#define RCP14_DBGWVR9() MRC14(0, c0, c9, 6) ++#define RCP14_DBGWVR10() MRC14(0, c0, c10, 6) ++#define RCP14_DBGWVR11() MRC14(0, c0, c11, 6) ++#define RCP14_DBGWVR12() MRC14(0, c0, c12, 6) ++#define RCP14_DBGWVR13() MRC14(0, c0, c13, 6) ++#define RCP14_DBGWVR14() MRC14(0, c0, c14, 6) ++#define RCP14_DBGWVR15() MRC14(0, c0, c15, 6) ++#define RCP14_DBGWCR0() MRC14(0, c0, c0, 7) ++#define RCP14_DBGWCR1() MRC14(0, c0, c1, 7) ++#define RCP14_DBGWCR2() MRC14(0, c0, c2, 7) ++#define RCP14_DBGWCR3() MRC14(0, c0, c3, 7) ++#define RCP14_DBGWCR4() MRC14(0, c0, c4, 7) ++#define RCP14_DBGWCR5() MRC14(0, c0, c5, 7) ++#define RCP14_DBGWCR6() MRC14(0, c0, c6, 7) ++#define RCP14_DBGWCR7() MRC14(0, c0, c7, 7) ++#define RCP14_DBGWCR8() MRC14(0, c0, c8, 7) ++#define RCP14_DBGWCR9() MRC14(0, c0, c9, 7) ++#define RCP14_DBGWCR10() MRC14(0, c0, c10, 7) ++#define RCP14_DBGWCR11() MRC14(0, c0, c11, 7) ++#define RCP14_DBGWCR12() MRC14(0, c0, c12, 7) ++#define RCP14_DBGWCR13() MRC14(0, c0, c13, 7) ++#define RCP14_DBGWCR14() MRC14(0, c0, c14, 7) ++#define RCP14_DBGWCR15() MRC14(0, c0, c15, 7) ++#define RCP14_DBGDRAR() MRC14(0, c1, c0, 0) ++#define RCP14_DBGBXVR0() MRC14(0, c1, c0, 1) ++#define RCP14_DBGBXVR1() MRC14(0, c1, c1, 1) ++#define RCP14_DBGBXVR2() MRC14(0, c1, c2, 1) ++#define RCP14_DBGBXVR3() MRC14(0, c1, c3, 1) ++#define RCP14_DBGBXVR4() MRC14(0, c1, c4, 1) ++#define RCP14_DBGBXVR5() MRC14(0, c1, c5, 1) ++#define RCP14_DBGBXVR6() MRC14(0, c1, c6, 1) ++#define RCP14_DBGBXVR7() MRC14(0, c1, c7, 1) ++#define RCP14_DBGBXVR8() MRC14(0, c1, c8, 1) ++#define RCP14_DBGBXVR9() MRC14(0, c1, c9, 1) ++#define RCP14_DBGBXVR10() MRC14(0, c1, c10, 1) ++#define RCP14_DBGBXVR11() MRC14(0, c1, c11, 1) ++#define RCP14_DBGBXVR12() MRC14(0, c1, c12, 1) ++#define RCP14_DBGBXVR13() MRC14(0, c1, c13, 1) ++#define RCP14_DBGBXVR14() MRC14(0, c1, c14, 1) ++#define RCP14_DBGBXVR15() MRC14(0, c1, c15, 1) ++#define RCP14_DBGOSLSR() MRC14(0, c1, c1, 4) ++#define RCP14_DBGOSSRR() MRC14(0, c1, c2, 4) ++#define RCP14_DBGOSDLR() MRC14(0, c1, c3, 4) ++#define RCP14_DBGPRCR() MRC14(0, c1, c4, 4) ++#define RCP14_DBGPRSR() MRC14(0, c1, c5, 4) ++#define RCP14_DBGDSAR() MRC14(0, c2, c0, 0) ++#define RCP14_DBGITCTRL() MRC14(0, c7, c0, 4) ++#define RCP14_DBGCLAIMSET() MRC14(0, c7, c8, 6) ++#define RCP14_DBGCLAIMCLR() MRC14(0, c7, c9, 6) ++#define RCP14_DBGAUTHSTATUS() MRC14(0, c7, c14, 6) ++#define RCP14_DBGDEVID2() MRC14(0, c7, c0, 7) ++#define RCP14_DBGDEVID1() MRC14(0, c7, c1, 7) ++#define RCP14_DBGDEVID() MRC14(0, c7, c2, 7) ++ ++#define WCP14_DBGDTRTXint(val) MCR14(val, 0, c0, c5, 0) ++#define WCP14_DBGWFAR(val) MCR14(val, 0, c0, c6, 0) ++#define WCP14_DBGVCR(val) MCR14(val, 0, c0, c7, 0) ++#define WCP14_DBGECR(val) MCR14(val, 0, c0, c9, 0) ++#define WCP14_DBGDSCCR(val) MCR14(val, 0, c0, c10, 0) ++#define WCP14_DBGDSMCR(val) MCR14(val, 0, c0, c11, 0) ++#define WCP14_DBGDTRRXext(val) MCR14(val, 0, c0, c0, 2) ++#define WCP14_DBGDSCRext(val) MCR14(val, 0, c0, c2, 2) ++#define WCP14_DBGDTRTXext(val) MCR14(val, 0, c0, c3, 2) ++#define WCP14_DBGDRCR(val) MCR14(val, 0, c0, c4, 2) ++#define WCP14_DBGBVR0(val) MCR14(val, 0, c0, c0, 4) ++#define WCP14_DBGBVR1(val) MCR14(val, 0, c0, c1, 4) ++#define WCP14_DBGBVR2(val) MCR14(val, 0, c0, c2, 4) ++#define WCP14_DBGBVR3(val) MCR14(val, 0, c0, c3, 4) ++#define WCP14_DBGBVR4(val) MCR14(val, 0, c0, c4, 4) ++#define WCP14_DBGBVR5(val) MCR14(val, 0, c0, c5, 4) ++#define WCP14_DBGBVR6(val) MCR14(val, 0, c0, c6, 4) ++#define WCP14_DBGBVR7(val) MCR14(val, 0, c0, c7, 4) ++#define WCP14_DBGBVR8(val) MCR14(val, 0, c0, c8, 4) ++#define WCP14_DBGBVR9(val) MCR14(val, 0, c0, c9, 4) ++#define WCP14_DBGBVR10(val) MCR14(val, 0, c0, c10, 4) ++#define WCP14_DBGBVR11(val) MCR14(val, 0, c0, c11, 4) ++#define WCP14_DBGBVR12(val) MCR14(val, 0, c0, c12, 4) ++#define WCP14_DBGBVR13(val) MCR14(val, 0, c0, c13, 4) ++#define WCP14_DBGBVR14(val) MCR14(val, 0, c0, c14, 4) ++#define WCP14_DBGBVR15(val) MCR14(val, 0, c0, c15, 4) ++#define WCP14_DBGBCR0(val) MCR14(val, 0, c0, c0, 5) ++#define WCP14_DBGBCR1(val) MCR14(val, 0, c0, c1, 5) ++#define WCP14_DBGBCR2(val) MCR14(val, 0, c0, c2, 5) ++#define WCP14_DBGBCR3(val) MCR14(val, 0, c0, c3, 5) ++#define WCP14_DBGBCR4(val) MCR14(val, 0, c0, c4, 5) ++#define WCP14_DBGBCR5(val) MCR14(val, 0, c0, c5, 5) ++#define WCP14_DBGBCR6(val) MCR14(val, 0, c0, c6, 5) ++#define WCP14_DBGBCR7(val) MCR14(val, 0, c0, c7, 5) ++#define WCP14_DBGBCR8(val) MCR14(val, 0, c0, c8, 5) ++#define WCP14_DBGBCR9(val) MCR14(val, 0, c0, c9, 5) ++#define WCP14_DBGBCR10(val) MCR14(val, 0, c0, c10, 5) ++#define WCP14_DBGBCR11(val) MCR14(val, 0, c0, c11, 5) ++#define WCP14_DBGBCR12(val) MCR14(val, 0, c0, c12, 5) ++#define WCP14_DBGBCR13(val) MCR14(val, 0, c0, c13, 5) ++#define WCP14_DBGBCR14(val) MCR14(val, 0, c0, c14, 5) ++#define WCP14_DBGBCR15(val) MCR14(val, 0, c0, c15, 5) ++#define WCP14_DBGWVR0(val) MCR14(val, 0, c0, c0, 6) ++#define WCP14_DBGWVR1(val) MCR14(val, 0, c0, c1, 6) ++#define WCP14_DBGWVR2(val) MCR14(val, 0, c0, c2, 6) ++#define WCP14_DBGWVR3(val) MCR14(val, 0, c0, c3, 6) ++#define WCP14_DBGWVR4(val) MCR14(val, 0, c0, c4, 6) ++#define WCP14_DBGWVR5(val) MCR14(val, 0, c0, c5, 6) ++#define WCP14_DBGWVR6(val) MCR14(val, 0, c0, c6, 6) ++#define WCP14_DBGWVR7(val) MCR14(val, 0, c0, c7, 6) ++#define WCP14_DBGWVR8(val) MCR14(val, 0, c0, c8, 6) ++#define WCP14_DBGWVR9(val) MCR14(val, 0, c0, c9, 6) ++#define WCP14_DBGWVR10(val) MCR14(val, 0, c0, c10, 6) ++#define WCP14_DBGWVR11(val) MCR14(val, 0, c0, c11, 6) ++#define WCP14_DBGWVR12(val) MCR14(val, 0, c0, c12, 6) ++#define WCP14_DBGWVR13(val) MCR14(val, 0, c0, c13, 6) ++#define WCP14_DBGWVR14(val) MCR14(val, 0, c0, c14, 6) ++#define WCP14_DBGWVR15(val) MCR14(val, 0, c0, c15, 6) ++#define WCP14_DBGWCR0(val) MCR14(val, 0, c0, c0, 7) ++#define WCP14_DBGWCR1(val) MCR14(val, 0, c0, c1, 7) ++#define WCP14_DBGWCR2(val) MCR14(val, 0, c0, c2, 7) ++#define WCP14_DBGWCR3(val) MCR14(val, 0, c0, c3, 7) ++#define WCP14_DBGWCR4(val) MCR14(val, 0, c0, c4, 7) ++#define WCP14_DBGWCR5(val) MCR14(val, 0, c0, c5, 7) ++#define WCP14_DBGWCR6(val) MCR14(val, 0, c0, c6, 7) ++#define WCP14_DBGWCR7(val) MCR14(val, 0, c0, c7, 7) ++#define WCP14_DBGWCR8(val) MCR14(val, 0, c0, c8, 7) ++#define WCP14_DBGWCR9(val) MCR14(val, 0, c0, c9, 7) ++#define WCP14_DBGWCR10(val) MCR14(val, 0, c0, c10, 7) ++#define WCP14_DBGWCR11(val) MCR14(val, 0, c0, c11, 7) ++#define WCP14_DBGWCR12(val) MCR14(val, 0, c0, c12, 7) ++#define WCP14_DBGWCR13(val) MCR14(val, 0, c0, c13, 7) ++#define WCP14_DBGWCR14(val) MCR14(val, 0, c0, c14, 7) ++#define WCP14_DBGWCR15(val) MCR14(val, 0, c0, c15, 7) ++#define WCP14_DBGBXVR0(val) MCR14(val, 0, c1, c0, 1) ++#define WCP14_DBGBXVR1(val) MCR14(val, 0, c1, c1, 1) ++#define WCP14_DBGBXVR2(val) MCR14(val, 0, c1, c2, 1) ++#define WCP14_DBGBXVR3(val) MCR14(val, 0, c1, c3, 1) ++#define WCP14_DBGBXVR4(val) MCR14(val, 0, c1, c4, 1) ++#define WCP14_DBGBXVR5(val) MCR14(val, 0, c1, c5, 1) ++#define WCP14_DBGBXVR6(val) MCR14(val, 0, c1, c6, 1) ++#define WCP14_DBGBXVR7(val) MCR14(val, 0, c1, c7, 1) ++#define WCP14_DBGBXVR8(val) MCR14(val, 0, c1, c8, 1) ++#define WCP14_DBGBXVR9(val) MCR14(val, 0, c1, c9, 1) ++#define WCP14_DBGBXVR10(val) MCR14(val, 0, c1, c10, 1) ++#define WCP14_DBGBXVR11(val) MCR14(val, 0, c1, c11, 1) ++#define WCP14_DBGBXVR12(val) MCR14(val, 0, c1, c12, 1) ++#define WCP14_DBGBXVR13(val) MCR14(val, 0, c1, c13, 1) ++#define WCP14_DBGBXVR14(val) MCR14(val, 0, c1, c14, 1) ++#define WCP14_DBGBXVR15(val) MCR14(val, 0, c1, c15, 1) ++#define WCP14_DBGOSLAR(val) MCR14(val, 0, c1, c0, 4) ++#define WCP14_DBGOSSRR(val) MCR14(val, 0, c1, c2, 4) ++#define WCP14_DBGOSDLR(val) MCR14(val, 0, c1, c3, 4) ++#define WCP14_DBGPRCR(val) MCR14(val, 0, c1, c4, 4) ++#define WCP14_DBGITCTRL(val) MCR14(val, 0, c7, c0, 4) ++#define WCP14_DBGCLAIMSET(val) MCR14(val, 0, c7, c8, 6) ++#define WCP14_DBGCLAIMCLR(val) MCR14(val, 0, c7, c9, 6) ++ ++/* ++ * ETM Registers ++ * ++ * Available only in ETMv3.3, 3.4, 3.5 ++ * ETMASICCR, ETMTECR2, ETMFFRR, ETMVDEVR, ETMVDCR1, ETMVDCR2, ETMVDCR3, ++ * ETMDCVRn, ETMDCMRn ++ * ++ * Available only in ETMv3.5 as read only ++ * ETMIDR2 ++ * ++ * Available only in ETMv3.5, PFTv1.0, 1.1 ++ * ETMTSEVR, ETMVMIDCVR, ETMPDCR ++ * ++ * Read only ++ * ETMCCR, ETMSCR, ETMIDR, ETMCCER, ETMOSLSR ++ * ETMLSR, ETMAUTHSTATUS, ETMDEVID, ETMDEVTYPE, ETMPIDR4, ETMPIDR5, ETMPIDR6, ++ * ETMPIDR7, ETMPIDR0, ETMPIDR1, ETMPIDR2, ETMPIDR2, ETMPIDR3, ETMCIDR0, ++ * ETMCIDR1, ETMCIDR2, ETMCIDR3 ++ * ++ * Write only ++ * ETMOSLAR, ETMLAR ++ * Note: ETMCCER[11] controls WO nature of certain regs. Refer ETM arch spec. ++ */ ++#define RCP14_ETMCR() MRC14(1, c0, c0, 0) ++#define RCP14_ETMCCR() MRC14(1, c0, c1, 0) ++#define RCP14_ETMTRIGGER() MRC14(1, c0, c2, 0) ++#define RCP14_ETMASICCR() MRC14(1, c0, c3, 0) ++#define RCP14_ETMSR() MRC14(1, c0, c4, 0) ++#define RCP14_ETMSCR() MRC14(1, c0, c5, 0) ++#define RCP14_ETMTSSCR() MRC14(1, c0, c6, 0) ++#define RCP14_ETMTECR2() MRC14(1, c0, c7, 0) ++#define RCP14_ETMTEEVR() MRC14(1, c0, c8, 0) ++#define RCP14_ETMTECR1() MRC14(1, c0, c9, 0) ++#define RCP14_ETMFFRR() MRC14(1, c0, c10, 0) ++#define RCP14_ETMFFLR() MRC14(1, c0, c11, 0) ++#define RCP14_ETMVDEVR() MRC14(1, c0, c12, 0) ++#define RCP14_ETMVDCR1() MRC14(1, c0, c13, 0) ++#define RCP14_ETMVDCR2() MRC14(1, c0, c14, 0) ++#define RCP14_ETMVDCR3() MRC14(1, c0, c15, 0) ++#define RCP14_ETMACVR0() MRC14(1, c0, c0, 1) ++#define RCP14_ETMACVR1() MRC14(1, c0, c1, 1) ++#define RCP14_ETMACVR2() MRC14(1, c0, c2, 1) ++#define RCP14_ETMACVR3() MRC14(1, c0, c3, 1) ++#define RCP14_ETMACVR4() MRC14(1, c0, c4, 1) ++#define RCP14_ETMACVR5() MRC14(1, c0, c5, 1) ++#define RCP14_ETMACVR6() MRC14(1, c0, c6, 1) ++#define RCP14_ETMACVR7() MRC14(1, c0, c7, 1) ++#define RCP14_ETMACVR8() MRC14(1, c0, c8, 1) ++#define RCP14_ETMACVR9() MRC14(1, c0, c9, 1) ++#define RCP14_ETMACVR10() MRC14(1, c0, c10, 1) ++#define RCP14_ETMACVR11() MRC14(1, c0, c11, 1) ++#define RCP14_ETMACVR12() MRC14(1, c0, c12, 1) ++#define RCP14_ETMACVR13() MRC14(1, c0, c13, 1) ++#define RCP14_ETMACVR14() MRC14(1, c0, c14, 1) ++#define RCP14_ETMACVR15() MRC14(1, c0, c15, 1) ++#define RCP14_ETMACTR0() MRC14(1, c0, c0, 2) ++#define RCP14_ETMACTR1() MRC14(1, c0, c1, 2) ++#define RCP14_ETMACTR2() MRC14(1, c0, c2, 2) ++#define RCP14_ETMACTR3() MRC14(1, c0, c3, 2) ++#define RCP14_ETMACTR4() MRC14(1, c0, c4, 2) ++#define RCP14_ETMACTR5() MRC14(1, c0, c5, 2) ++#define RCP14_ETMACTR6() MRC14(1, c0, c6, 2) ++#define RCP14_ETMACTR7() MRC14(1, c0, c7, 2) ++#define RCP14_ETMACTR8() MRC14(1, c0, c8, 2) ++#define RCP14_ETMACTR9() MRC14(1, c0, c9, 2) ++#define RCP14_ETMACTR10() MRC14(1, c0, c10, 2) ++#define RCP14_ETMACTR11() MRC14(1, c0, c11, 2) ++#define RCP14_ETMACTR12() MRC14(1, c0, c12, 2) ++#define RCP14_ETMACTR13() MRC14(1, c0, c13, 2) ++#define RCP14_ETMACTR14() MRC14(1, c0, c14, 2) ++#define RCP14_ETMACTR15() MRC14(1, c0, c15, 2) ++#define RCP14_ETMDCVR0() MRC14(1, c0, c0, 3) ++#define RCP14_ETMDCVR2() MRC14(1, c0, c2, 3) ++#define RCP14_ETMDCVR4() MRC14(1, c0, c4, 3) ++#define RCP14_ETMDCVR6() MRC14(1, c0, c6, 3) ++#define RCP14_ETMDCVR8() MRC14(1, c0, c8, 3) ++#define RCP14_ETMDCVR10() MRC14(1, c0, c10, 3) ++#define RCP14_ETMDCVR12() MRC14(1, c0, c12, 3) ++#define RCP14_ETMDCVR14() MRC14(1, c0, c14, 3) ++#define RCP14_ETMDCMR0() MRC14(1, c0, c0, 4) ++#define RCP14_ETMDCMR2() MRC14(1, c0, c2, 4) ++#define RCP14_ETMDCMR4() MRC14(1, c0, c4, 4) ++#define RCP14_ETMDCMR6() MRC14(1, c0, c6, 4) ++#define RCP14_ETMDCMR8() MRC14(1, c0, c8, 4) ++#define RCP14_ETMDCMR10() MRC14(1, c0, c10, 4) ++#define RCP14_ETMDCMR12() MRC14(1, c0, c12, 4) ++#define RCP14_ETMDCMR14() MRC14(1, c0, c14, 4) ++#define RCP14_ETMCNTRLDVR0() MRC14(1, c0, c0, 5) ++#define RCP14_ETMCNTRLDVR1() MRC14(1, c0, c1, 5) ++#define RCP14_ETMCNTRLDVR2() MRC14(1, c0, c2, 5) ++#define RCP14_ETMCNTRLDVR3() MRC14(1, c0, c3, 5) ++#define RCP14_ETMCNTENR0() MRC14(1, c0, c4, 5) ++#define RCP14_ETMCNTENR1() MRC14(1, c0, c5, 5) ++#define RCP14_ETMCNTENR2() MRC14(1, c0, c6, 5) ++#define RCP14_ETMCNTENR3() MRC14(1, c0, c7, 5) ++#define RCP14_ETMCNTRLDEVR0() MRC14(1, c0, c8, 5) ++#define RCP14_ETMCNTRLDEVR1() MRC14(1, c0, c9, 5) ++#define RCP14_ETMCNTRLDEVR2() MRC14(1, c0, c10, 5) ++#define RCP14_ETMCNTRLDEVR3() MRC14(1, c0, c11, 5) ++#define RCP14_ETMCNTVR0() MRC14(1, c0, c12, 5) ++#define RCP14_ETMCNTVR1() MRC14(1, c0, c13, 5) ++#define RCP14_ETMCNTVR2() MRC14(1, c0, c14, 5) ++#define RCP14_ETMCNTVR3() MRC14(1, c0, c15, 5) ++#define RCP14_ETMSQ12EVR() MRC14(1, c0, c0, 6) ++#define RCP14_ETMSQ21EVR() MRC14(1, c0, c1, 6) ++#define RCP14_ETMSQ23EVR() MRC14(1, c0, c2, 6) ++#define RCP14_ETMSQ31EVR() MRC14(1, c0, c3, 6) ++#define RCP14_ETMSQ32EVR() MRC14(1, c0, c4, 6) ++#define RCP14_ETMSQ13EVR() MRC14(1, c0, c5, 6) ++#define RCP14_ETMSQR() MRC14(1, c0, c7, 6) ++#define RCP14_ETMEXTOUTEVR0() MRC14(1, c0, c8, 6) ++#define RCP14_ETMEXTOUTEVR1() MRC14(1, c0, c9, 6) ++#define RCP14_ETMEXTOUTEVR2() MRC14(1, c0, c10, 6) ++#define RCP14_ETMEXTOUTEVR3() MRC14(1, c0, c11, 6) ++#define RCP14_ETMCIDCVR0() MRC14(1, c0, c12, 6) ++#define RCP14_ETMCIDCVR1() MRC14(1, c0, c13, 6) ++#define RCP14_ETMCIDCVR2() MRC14(1, c0, c14, 6) ++#define RCP14_ETMCIDCMR() MRC14(1, c0, c15, 6) ++#define RCP14_ETMIMPSPEC0() MRC14(1, c0, c0, 7) ++#define RCP14_ETMIMPSPEC1() MRC14(1, c0, c1, 7) ++#define RCP14_ETMIMPSPEC2() MRC14(1, c0, c2, 7) ++#define RCP14_ETMIMPSPEC3() MRC14(1, c0, c3, 7) ++#define RCP14_ETMIMPSPEC4() MRC14(1, c0, c4, 7) ++#define RCP14_ETMIMPSPEC5() MRC14(1, c0, c5, 7) ++#define RCP14_ETMIMPSPEC6() MRC14(1, c0, c6, 7) ++#define RCP14_ETMIMPSPEC7() MRC14(1, c0, c7, 7) ++#define RCP14_ETMSYNCFR() MRC14(1, c0, c8, 7) ++#define RCP14_ETMIDR() MRC14(1, c0, c9, 7) ++#define RCP14_ETMCCER() MRC14(1, c0, c10, 7) ++#define RCP14_ETMEXTINSELR() MRC14(1, c0, c11, 7) ++#define RCP14_ETMTESSEICR() MRC14(1, c0, c12, 7) ++#define RCP14_ETMEIBCR() MRC14(1, c0, c13, 7) ++#define RCP14_ETMTSEVR() MRC14(1, c0, c14, 7) ++#define RCP14_ETMAUXCR() MRC14(1, c0, c15, 7) ++#define RCP14_ETMTRACEIDR() MRC14(1, c1, c0, 0) ++#define RCP14_ETMIDR2() MRC14(1, c1, c2, 0) ++#define RCP14_ETMVMIDCVR() MRC14(1, c1, c0, 1) ++#define RCP14_ETMOSLSR() MRC14(1, c1, c1, 4) ++/* Not available in PFTv1.1 */ ++#define RCP14_ETMOSSRR() MRC14(1, c1, c2, 4) ++#define RCP14_ETMPDCR() MRC14(1, c1, c4, 4) ++#define RCP14_ETMPDSR() MRC14(1, c1, c5, 4) ++#define RCP14_ETMITCTRL() MRC14(1, c7, c0, 4) ++#define RCP14_ETMCLAIMSET() MRC14(1, c7, c8, 6) ++#define RCP14_ETMCLAIMCLR() MRC14(1, c7, c9, 6) ++#define RCP14_ETMLSR() MRC14(1, c7, c13, 6) ++#define RCP14_ETMAUTHSTATUS() MRC14(1, c7, c14, 6) ++#define RCP14_ETMDEVID() MRC14(1, c7, c2, 7) ++#define RCP14_ETMDEVTYPE() MRC14(1, c7, c3, 7) ++#define RCP14_ETMPIDR4() MRC14(1, c7, c4, 7) ++#define RCP14_ETMPIDR5() MRC14(1, c7, c5, 7) ++#define RCP14_ETMPIDR6() MRC14(1, c7, c6, 7) ++#define RCP14_ETMPIDR7() MRC14(1, c7, c7, 7) ++#define RCP14_ETMPIDR0() MRC14(1, c7, c8, 7) ++#define RCP14_ETMPIDR1() MRC14(1, c7, c9, 7) ++#define RCP14_ETMPIDR2() MRC14(1, c7, c10, 7) ++#define RCP14_ETMPIDR3() MRC14(1, c7, c11, 7) ++#define RCP14_ETMCIDR0() MRC14(1, c7, c12, 7) ++#define RCP14_ETMCIDR1() MRC14(1, c7, c13, 7) ++#define RCP14_ETMCIDR2() MRC14(1, c7, c14, 7) ++#define RCP14_ETMCIDR3() MRC14(1, c7, c15, 7) ++ ++#define WCP14_ETMCR(val) MCR14(val, 1, c0, c0, 0) ++#define WCP14_ETMTRIGGER(val) MCR14(val, 1, c0, c2, 0) ++#define WCP14_ETMASICCR(val) MCR14(val, 1, c0, c3, 0) ++#define WCP14_ETMSR(val) MCR14(val, 1, c0, c4, 0) ++#define WCP14_ETMTSSCR(val) MCR14(val, 1, c0, c6, 0) ++#define WCP14_ETMTECR2(val) MCR14(val, 1, c0, c7, 0) ++#define WCP14_ETMTEEVR(val) MCR14(val, 1, c0, c8, 0) ++#define WCP14_ETMTECR1(val) MCR14(val, 1, c0, c9, 0) ++#define WCP14_ETMFFRR(val) MCR14(val, 1, c0, c10, 0) ++#define WCP14_ETMFFLR(val) MCR14(val, 1, c0, c11, 0) ++#define WCP14_ETMVDEVR(val) MCR14(val, 1, c0, c12, 0) ++#define WCP14_ETMVDCR1(val) MCR14(val, 1, c0, c13, 0) ++#define WCP14_ETMVDCR2(val) MCR14(val, 1, c0, c14, 0) ++#define WCP14_ETMVDCR3(val) MCR14(val, 1, c0, c15, 0) ++#define WCP14_ETMACVR0(val) MCR14(val, 1, c0, c0, 1) ++#define WCP14_ETMACVR1(val) MCR14(val, 1, c0, c1, 1) ++#define WCP14_ETMACVR2(val) MCR14(val, 1, c0, c2, 1) ++#define WCP14_ETMACVR3(val) MCR14(val, 1, c0, c3, 1) ++#define WCP14_ETMACVR4(val) MCR14(val, 1, c0, c4, 1) ++#define WCP14_ETMACVR5(val) MCR14(val, 1, c0, c5, 1) ++#define WCP14_ETMACVR6(val) MCR14(val, 1, c0, c6, 1) ++#define WCP14_ETMACVR7(val) MCR14(val, 1, c0, c7, 1) ++#define WCP14_ETMACVR8(val) MCR14(val, 1, c0, c8, 1) ++#define WCP14_ETMACVR9(val) MCR14(val, 1, c0, c9, 1) ++#define WCP14_ETMACVR10(val) MCR14(val, 1, c0, c10, 1) ++#define WCP14_ETMACVR11(val) MCR14(val, 1, c0, c11, 1) ++#define WCP14_ETMACVR12(val) MCR14(val, 1, c0, c12, 1) ++#define WCP14_ETMACVR13(val) MCR14(val, 1, c0, c13, 1) ++#define WCP14_ETMACVR14(val) MCR14(val, 1, c0, c14, 1) ++#define WCP14_ETMACVR15(val) MCR14(val, 1, c0, c15, 1) ++#define WCP14_ETMACTR0(val) MCR14(val, 1, c0, c0, 2) ++#define WCP14_ETMACTR1(val) MCR14(val, 1, c0, c1, 2) ++#define WCP14_ETMACTR2(val) MCR14(val, 1, c0, c2, 2) ++#define WCP14_ETMACTR3(val) MCR14(val, 1, c0, c3, 2) ++#define WCP14_ETMACTR4(val) MCR14(val, 1, c0, c4, 2) ++#define WCP14_ETMACTR5(val) MCR14(val, 1, c0, c5, 2) ++#define WCP14_ETMACTR6(val) MCR14(val, 1, c0, c6, 2) ++#define WCP14_ETMACTR7(val) MCR14(val, 1, c0, c7, 2) ++#define WCP14_ETMACTR8(val) MCR14(val, 1, c0, c8, 2) ++#define WCP14_ETMACTR9(val) MCR14(val, 1, c0, c9, 2) ++#define WCP14_ETMACTR10(val) MCR14(val, 1, c0, c10, 2) ++#define WCP14_ETMACTR11(val) MCR14(val, 1, c0, c11, 2) ++#define WCP14_ETMACTR12(val) MCR14(val, 1, c0, c12, 2) ++#define WCP14_ETMACTR13(val) MCR14(val, 1, c0, c13, 2) ++#define WCP14_ETMACTR14(val) MCR14(val, 1, c0, c14, 2) ++#define WCP14_ETMACTR15(val) MCR14(val, 1, c0, c15, 2) ++#define WCP14_ETMDCVR0(val) MCR14(val, 1, c0, c0, 3) ++#define WCP14_ETMDCVR2(val) MCR14(val, 1, c0, c2, 3) ++#define WCP14_ETMDCVR4(val) MCR14(val, 1, c0, c4, 3) ++#define WCP14_ETMDCVR6(val) MCR14(val, 1, c0, c6, 3) ++#define WCP14_ETMDCVR8(val) MCR14(val, 1, c0, c8, 3) ++#define WCP14_ETMDCVR10(val) MCR14(val, 1, c0, c10, 3) ++#define WCP14_ETMDCVR12(val) MCR14(val, 1, c0, c12, 3) ++#define WCP14_ETMDCVR14(val) MCR14(val, 1, c0, c14, 3) ++#define WCP14_ETMDCMR0(val) MCR14(val, 1, c0, c0, 4) ++#define WCP14_ETMDCMR2(val) MCR14(val, 1, c0, c2, 4) ++#define WCP14_ETMDCMR4(val) MCR14(val, 1, c0, c4, 4) ++#define WCP14_ETMDCMR6(val) MCR14(val, 1, c0, c6, 4) ++#define WCP14_ETMDCMR8(val) MCR14(val, 1, c0, c8, 4) ++#define WCP14_ETMDCMR10(val) MCR14(val, 1, c0, c10, 4) ++#define WCP14_ETMDCMR12(val) MCR14(val, 1, c0, c12, 4) ++#define WCP14_ETMDCMR14(val) MCR14(val, 1, c0, c14, 4) ++#define WCP14_ETMCNTRLDVR0(val) MCR14(val, 1, c0, c0, 5) ++#define WCP14_ETMCNTRLDVR1(val) MCR14(val, 1, c0, c1, 5) ++#define WCP14_ETMCNTRLDVR2(val) MCR14(val, 1, c0, c2, 5) ++#define WCP14_ETMCNTRLDVR3(val) MCR14(val, 1, c0, c3, 5) ++#define WCP14_ETMCNTENR0(val) MCR14(val, 1, c0, c4, 5) ++#define WCP14_ETMCNTENR1(val) MCR14(val, 1, c0, c5, 5) ++#define WCP14_ETMCNTENR2(val) MCR14(val, 1, c0, c6, 5) ++#define WCP14_ETMCNTENR3(val) MCR14(val, 1, c0, c7, 5) ++#define WCP14_ETMCNTRLDEVR0(val) MCR14(val, 1, c0, c8, 5) ++#define WCP14_ETMCNTRLDEVR1(val) MCR14(val, 1, c0, c9, 5) ++#define WCP14_ETMCNTRLDEVR2(val) MCR14(val, 1, c0, c10, 5) ++#define WCP14_ETMCNTRLDEVR3(val) MCR14(val, 1, c0, c11, 5) ++#define WCP14_ETMCNTVR0(val) MCR14(val, 1, c0, c12, 5) ++#define WCP14_ETMCNTVR1(val) MCR14(val, 1, c0, c13, 5) ++#define WCP14_ETMCNTVR2(val) MCR14(val, 1, c0, c14, 5) ++#define WCP14_ETMCNTVR3(val) MCR14(val, 1, c0, c15, 5) ++#define WCP14_ETMSQ12EVR(val) MCR14(val, 1, c0, c0, 6) ++#define WCP14_ETMSQ21EVR(val) MCR14(val, 1, c0, c1, 6) ++#define WCP14_ETMSQ23EVR(val) MCR14(val, 1, c0, c2, 6) ++#define WCP14_ETMSQ31EVR(val) MCR14(val, 1, c0, c3, 6) ++#define WCP14_ETMSQ32EVR(val) MCR14(val, 1, c0, c4, 6) ++#define WCP14_ETMSQ13EVR(val) MCR14(val, 1, c0, c5, 6) ++#define WCP14_ETMSQR(val) MCR14(val, 1, c0, c7, 6) ++#define WCP14_ETMEXTOUTEVR0(val) MCR14(val, 1, c0, c8, 6) ++#define WCP14_ETMEXTOUTEVR1(val) MCR14(val, 1, c0, c9, 6) ++#define WCP14_ETMEXTOUTEVR2(val) MCR14(val, 1, c0, c10, 6) ++#define WCP14_ETMEXTOUTEVR3(val) MCR14(val, 1, c0, c11, 6) ++#define WCP14_ETMCIDCVR0(val) MCR14(val, 1, c0, c12, 6) ++#define WCP14_ETMCIDCVR1(val) MCR14(val, 1, c0, c13, 6) ++#define WCP14_ETMCIDCVR2(val) MCR14(val, 1, c0, c14, 6) ++#define WCP14_ETMCIDCMR(val) MCR14(val, 1, c0, c15, 6) ++#define WCP14_ETMIMPSPEC0(val) MCR14(val, 1, c0, c0, 7) ++#define WCP14_ETMIMPSPEC1(val) MCR14(val, 1, c0, c1, 7) ++#define WCP14_ETMIMPSPEC2(val) MCR14(val, 1, c0, c2, 7) ++#define WCP14_ETMIMPSPEC3(val) MCR14(val, 1, c0, c3, 7) ++#define WCP14_ETMIMPSPEC4(val) MCR14(val, 1, c0, c4, 7) ++#define WCP14_ETMIMPSPEC5(val) MCR14(val, 1, c0, c5, 7) ++#define WCP14_ETMIMPSPEC6(val) MCR14(val, 1, c0, c6, 7) ++#define WCP14_ETMIMPSPEC7(val) MCR14(val, 1, c0, c7, 7) ++/* Can be read only in ETMv3.4, ETMv3.5 */ ++#define WCP14_ETMSYNCFR(val) MCR14(val, 1, c0, c8, 7) ++#define WCP14_ETMEXTINSELR(val) MCR14(val, 1, c0, c11, 7) ++#define WCP14_ETMTESSEICR(val) MCR14(val, 1, c0, c12, 7) ++#define WCP14_ETMEIBCR(val) MCR14(val, 1, c0, c13, 7) ++#define WCP14_ETMTSEVR(val) MCR14(val, 1, c0, c14, 7) ++#define WCP14_ETMAUXCR(val) MCR14(val, 1, c0, c15, 7) ++#define WCP14_ETMTRACEIDR(val) MCR14(val, 1, c1, c0, 0) ++#define WCP14_ETMIDR2(val) MCR14(val, 1, c1, c2, 0) ++#define WCP14_ETMVMIDCVR(val) MCR14(val, 1, c1, c0, 1) ++#define WCP14_ETMOSLAR(val) MCR14(val, 1, c1, c0, 4) ++/* Not available in PFTv1.1 */ ++#define WCP14_ETMOSSRR(val) MCR14(val, 1, c1, c2, 4) ++#define WCP14_ETMPDCR(val) MCR14(val, 1, c1, c4, 4) ++#define WCP14_ETMPDSR(val) MCR14(val, 1, c1, c5, 4) ++#define WCP14_ETMITCTRL(val) MCR14(val, 1, c7, c0, 4) ++#define WCP14_ETMCLAIMSET(val) MCR14(val, 1, c7, c8, 6) ++#define WCP14_ETMCLAIMCLR(val) MCR14(val, 1, c7, c9, 6) ++/* Writes to this from CP14 interface are ignored */ ++#define WCP14_ETMLAR(val) MCR14(val, 1, c7, c12, 6) ++ ++#endif +diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h +index 1805674..630a208 100644 +--- a/arch/arm/include/asm/io.h ++++ b/arch/arm/include/asm/io.h +@@ -29,6 +29,7 @@ + #include <asm/memory.h> + #include <asm-generic/pci_iomap.h> + #include <xen/xen.h> ++#include <linux/printk.h> + + /* + * ISA I/O bus memory addresses are 1:1 with the physical address. +@@ -37,6 +38,10 @@ + #define isa_page_to_bus page_to_phys + #define isa_bus_to_virt phys_to_virt + ++#include <linux/spinlock_types.h> ++#include <linux/spinlock.h> ++extern raw_spinlock_t hisilcon_lock; ++ + /* + * Atomic MMIO-wide IO modify + */ +@@ -63,6 +68,10 @@ extern void __raw_readsl(const void __iomem *addr, void *data, int longlen); + */ + #define __raw_readw(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a)) + #define __raw_writew(v,a) ((void)(__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v))) ++ ++#define __hi_raw_readw(a) __raw_readw(a) ++#define __hi_raw_writew(v,a) __raw_writew(v,a) ++ + #else + /* + * When running under a hypervisor, we want to avoid I/O accesses with +@@ -76,6 +85,23 @@ static inline void __raw_writew(u16 val, volatile void __iomem *addr) + : "r" (val)); + } + ++static inline void __hi_raw_writew(u16 val, volatile void __iomem *addr) ++{ ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ unsigned long flags; ++ raw_spin_lock_irqsave(&hisilcon_lock, flags); ++ dsb(); ++ isb(); ++#endif ++ asm volatile("strh %1, %0" ++ : "+Q" (*(volatile u16 __force *)addr) ++ : "r" (val)); ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ dsb(); ++ isb(); ++ raw_spin_unlock_irqrestore(&hisilcon_lock, flags); ++#endif ++} + static inline u16 __raw_readw(const volatile void __iomem *addr) + { + u16 val; +@@ -84,6 +110,27 @@ static inline u16 __raw_readw(const volatile void __iomem *addr) + "=r" (val)); + return val; + } ++ ++static inline u16 __hi_raw_readw(const volatile void __iomem *addr) ++{ ++ u16 val; ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ unsigned long flags; ++ raw_spin_lock_irqsave(&hisilcon_lock, flags); ++ dsb(); ++ isb(); ++#endif ++ asm volatile("ldrh %1, %0" ++ : "+Q" (*(volatile u16 __force *)addr), ++ "=r" (val)); ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ dsb(); ++ isb(); ++ raw_spin_unlock_irqrestore(&hisilcon_lock, flags); ++#endif ++ return val; ++} ++ + #endif + + static inline void __raw_writeb(u8 val, volatile void __iomem *addr) +@@ -93,6 +140,24 @@ static inline void __raw_writeb(u8 val, volatile void __iomem *addr) + : "r" (val)); + } + ++static inline void __hi_raw_writeb(u8 val, volatile void __iomem *addr) ++{ ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ unsigned long flags; ++ raw_spin_lock_irqsave(&hisilcon_lock, flags); ++ dsb(); ++ isb(); ++#endif ++ asm volatile("strb %1, %0" ++ : "+Qo" (*(volatile u8 __force *)addr) ++ : "r" (val)); ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ dsb(); ++ isb(); ++ raw_spin_unlock_irqrestore(&hisilcon_lock, flags); ++#endif ++} ++ + static inline void __raw_writel(u32 val, volatile void __iomem *addr) + { + asm volatile("str %1, %0" +@@ -100,6 +165,24 @@ static inline void __raw_writel(u32 val, volatile void __iomem *addr) + : "r" (val)); + } + ++static inline void __hi_raw_writel(u32 val, volatile void __iomem *addr) ++{ ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ unsigned long flags; ++ raw_spin_lock_irqsave(&hisilcon_lock, flags); ++ dsb(); ++ isb(); ++#endif ++ asm volatile("str %1, %0" ++ : "+Qo" (*(volatile u32 __force *)addr) ++ : "r" (val)); ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ dsb(); ++ isb(); ++ raw_spin_unlock_irqrestore(&hisilcon_lock, flags); ++#endif ++} ++ + static inline u8 __raw_readb(const volatile void __iomem *addr) + { + u8 val; +@@ -109,6 +192,26 @@ static inline u8 __raw_readb(const volatile void __iomem *addr) + return val; + } + ++static inline u8 __hi_raw_readb(const volatile void __iomem *addr) ++{ ++ u8 val; ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ unsigned long flags; ++ raw_spin_lock_irqsave(&hisilcon_lock, flags); ++ dsb(); ++ isb(); ++#endif ++ asm volatile("ldrb %1, %0" ++ : "+Qo" (*(volatile u8 __force *)addr), ++ "=r" (val)); ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ dsb(); ++ isb(); ++ raw_spin_unlock_irqrestore(&hisilcon_lock, flags); ++#endif ++ return val; ++} ++ + static inline u32 __raw_readl(const volatile void __iomem *addr) + { + u32 val; +@@ -118,6 +221,26 @@ static inline u32 __raw_readl(const volatile void __iomem *addr) + return val; + } + ++static inline u32 __hi_raw_readl(const volatile void __iomem *addr) ++{ ++ u32 val; ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ unsigned long flags; ++ raw_spin_lock_irqsave(&hisilcon_lock, flags); ++ dsb(); ++ isb(); ++#endif ++ asm volatile("ldr %1, %0" ++ : "+Qo" (*(volatile u32 __force *)addr), ++ "=r" (val)); ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ dsb(); ++ isb(); ++ raw_spin_unlock_irqrestore(&hisilcon_lock, flags); ++#endif ++ return val; ++} ++ + /* + * Architecture ioremap implementation. + */ +@@ -307,18 +430,36 @@ extern void _memset_io(volatile void __iomem *, int, size_t); + #define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32) \ + __raw_readl(c)); __r; }) + ++#define hi_readb_relaxed(c) ({ u8 __r = __hi_raw_readb(c); __r; }) ++#define hi_readw_relaxed(c) ({ u16 __r = le16_to_cpu((__force __le16) \ ++ __hi_raw_readw(c)); __r; }) ++#define hi_readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32) \ ++ __hi_raw_readl(c)); __r; }) ++ + #define writeb_relaxed(v,c) __raw_writeb(v,c) + #define writew_relaxed(v,c) __raw_writew((__force u16) cpu_to_le16(v),c) + #define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c) + ++#define hi_writeb_relaxed(v,c) __hi_raw_writeb(v,c) ++#define hi_writew_relaxed(v,c) __hi_raw_writew((__force u16) cpu_to_le16(v),c) ++#define hi_writel_relaxed(v,c) __hi_raw_writel((__force u32) cpu_to_le32(v),c) ++ + #define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; }) + #define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; }) + #define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; }) + ++#define hi_readb(c) ({ u8 __v = hi_readb_relaxed(c); __iormb(); __v; }) ++#define hi_readw(c) ({ u16 __v = hi_readw_relaxed(c); __iormb(); __v; }) ++#define hi_readl(c) ({ u32 __v = hi_readl_relaxed(c); __iormb(); __v; }) ++ + #define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); }) + #define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); }) + #define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); }) + ++#define hi_writeb(v,c) ({ __iowmb(); hi_writeb_relaxed(v,c); }) ++#define hi_writew(v,c) ({ __iowmb(); hi_writew_relaxed(v,c); }) ++#define hi_writel(v,c) ({ __iowmb(); hi_writel_relaxed(v,c); }) ++ + #define readsb(p,d,l) __raw_readsb(p,d,l) + #define readsw(p,d,l) __raw_readsw(p,d,l) + #define readsl(p,d,l) __raw_readsl(p,d,l) +diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h +index 53c15de..809203a 100644 +--- a/arch/arm/include/asm/irq.h ++++ b/arch/arm/include/asm/irq.h +@@ -35,6 +35,9 @@ extern void (*handle_arch_irq)(struct pt_regs *); + extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); + #endif + ++void arch_trigger_all_cpu_backtrace(void); ++#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace ++ + #endif + + #endif +diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h +new file mode 100644 +index 0000000..bca864a +--- /dev/null ++++ b/arch/arm/include/asm/mach/mmc.h +@@ -0,0 +1,28 @@ ++/* ++ * arch/arm/include/asm/mach/mmc.h ++ */ ++#ifndef ASMARM_MACH_MMC_H ++#define ASMARM_MACH_MMC_H ++ ++#include <linux/mmc/host.h> ++#include <linux/mmc/card.h> ++#include <linux/mmc/sdio_func.h> ++ ++struct embedded_sdio_data { ++ struct sdio_cis cis; ++ struct sdio_cccr cccr; ++ struct sdio_embedded_func *funcs; ++ int num_funcs; ++}; ++ ++struct mmc_platform_data { ++ unsigned int ocr_mask; /* available voltages */ ++ int built_in; /* built-in device flag */ ++ int card_present; /* card detect state */ ++ u32 (*translate_vdd)(struct device *, unsigned int); ++ unsigned int (*status)(struct device *); ++ struct embedded_sdio_data *embedded_sdio; ++ int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); ++}; ++ ++#endif +diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h +index e731018..c1668d1 100644 +--- a/arch/arm/include/asm/memory.h ++++ b/arch/arm/include/asm/memory.h +@@ -289,6 +289,9 @@ static inline void *phys_to_virt(phys_addr_t x) + */ + #define __pa(x) __virt_to_phys((unsigned long)(x)) + #define __va(x) ((void *)__phys_to_virt((phys_addr_t)(x))) ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++#define __pa_symbol(x) __pa(RELOC_HIDE((unsigned long)(x), 0)) ++#endif + #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) + + extern phys_addr_t (*arch_virt_to_idmap)(unsigned long x); +diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h +index 18f5a55..7f31b4f 100644 +--- a/arch/arm/include/asm/smp.h ++++ b/arch/arm/include/asm/smp.h +@@ -81,6 +81,8 @@ extern void arch_send_wakeup_ipi_mask(const struct cpumask *mask); + + extern int register_ipi_completion(struct completion *completion, int cpu); + ++extern void smp_send_all_cpu_backtrace(void); ++ + struct smp_operations { + #ifdef CONFIG_SMP + /* +diff --git a/arch/arm/include/asm/topology.h b/arch/arm/include/asm/topology.h +index 2fe85ff..d71d002 100644 +--- a/arch/arm/include/asm/topology.h ++++ b/arch/arm/include/asm/topology.h +@@ -23,11 +23,45 @@ extern struct cputopo_arm cpu_topology[NR_CPUS]; + void init_cpu_topology(void); + void store_cpu_topology(unsigned int cpuid); + const struct cpumask *cpu_coregroup_mask(int cpu); ++int cluster_to_logical_mask(unsigned int socket_id, cpumask_t *cluster_mask); ++ ++#ifdef CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE ++/* Common values for CPUs */ ++#ifndef SD_CPU_INIT ++#define SD_CPU_INIT (struct sched_domain) { \ ++ .min_interval = 1, \ ++ .max_interval = 4, \ ++ .busy_factor = 64, \ ++ .imbalance_pct = 125, \ ++ .cache_nice_tries = 1, \ ++ .busy_idx = 2, \ ++ .idle_idx = 1, \ ++ .newidle_idx = 0, \ ++ .wake_idx = 0, \ ++ .forkexec_idx = 0, \ ++ \ ++ .flags = 0*SD_LOAD_BALANCE \ ++ | 1*SD_BALANCE_NEWIDLE \ ++ | 1*SD_BALANCE_EXEC \ ++ | 1*SD_BALANCE_FORK \ ++ | 0*SD_BALANCE_WAKE \ ++ | 1*SD_WAKE_AFFINE \ ++ | 0*SD_SHARE_CPUPOWER \ ++ | 0*SD_SHARE_PKG_RESOURCES \ ++ | 0*SD_SERIALIZE \ ++ , \ ++ .last_balance = jiffies, \ ++ .balance_interval = 1, \ ++} ++#endif ++#endif /* CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE */ + + #else + + static inline void init_cpu_topology(void) { } + static inline void store_cpu_topology(unsigned int cpuid) { } ++static inline int cluster_to_logical_mask(unsigned int socket_id, ++ cpumask_t *cluster_mask) { return -EINVAL; } + + #endif + +diff --git a/arch/arm/include/mach/hi3516av200_io.h b/arch/arm/include/mach/hi3516av200_io.h +new file mode 100644 +index 0000000..eb992fe +--- /dev/null ++++ b/arch/arm/include/mach/hi3516av200_io.h +@@ -0,0 +1,46 @@ ++#ifndef __HI3516AV200_IO_H ++#define __HI3516AV200_IO_H ++ ++#ifdef CONFIG_PCI ++#define IO_SPACE_LIMIT 0xffffffff ++#define __io(a) __typesafe_io(PCI_IO_VIRT_BASE + ((a) & IO_SPACE_LIMIT)) ++#endif ++ ++/* phys_addr virt_addr ++ * 0x1000_0000 <-----> 0xFE00_0000 ++ */ ++#define HI3516AV200_IOCH1_VIRT (0xFE000000) ++#define HI3516AV200_IOCH1_PHYS (0x10000000) ++#define HI3516AV200_IOCH1_SIZE (0x00310000) ++ ++/* phys_addr virt_addr ++ * 0x11000000 <-----> 0xFE400000 ++ */ ++#define HI3516AV200_IOCH2_VIRT (0xFE400000) ++#define HI3516AV200_IOCH2_PHYS (0x11000000) ++#define HI3516AV200_IOCH2_SIZE (0x004F0000) ++ ++/* phys_addr virt_addr ++ * 0x12000000 <-----> 0xFE900000 ++ */ ++#define HI3516AV200_IOCH3_VIRT (0xFE900000) ++#define HI3516AV200_IOCH3_PHYS (0x12000000) ++#define HI3516AV200_IOCH3_SIZE (0x00170000) ++ ++ ++#define IO_OFFSET_LOW (0xEC900000) ++#define IO_OFFSET_MID (0xED400000) ++#define IO_OFFSET_HIGH (0xEE000000) ++ ++#define IO_ADDRESS_LOW(x) ((x) + IO_OFFSET_LOW) ++#define IO_ADDRESS_MID(x) ((x) + IO_OFFSET_MID) ++#define IO_ADDRESS_HIGH(x) ((x) + IO_OFFSET_HIGH) ++ ++#define __IO_ADDRESS(x) ((x >= HI3516AV200_IOCH2_PHYS) ? IO_ADDRESS_MID(x)\ ++ : IO_ADDRESS_HIGH(x)) ++#define IO_ADDRESS(x) ((x) >= HI3516AV200_IOCH3_PHYS ? IO_ADDRESS_LOW(x)\ ++ : __IO_ADDRESS(x)) ++ ++#define __io_address(n) (IOMEM(IO_ADDRESS(n))) ++ ++#endif +diff --git a/arch/arm/include/mach/hi3516av200_platform.h b/arch/arm/include/mach/hi3516av200_platform.h +new file mode 100644 +index 0000000..a7aaa25 +--- /dev/null ++++ b/arch/arm/include/mach/hi3516av200_platform.h +@@ -0,0 +1,164 @@ ++#ifndef __HI3516AV200_CHIP_REGS_H__ ++#define __HI3516AV200_CHIP_REGS_H__ ++ ++/* SRAM Base Address Register */ ++#define SRAM_BASE_ADDRESS 0x4010000 ++ ++#define REG_BASE_TIMER01 0x12000000 ++#define REG_BASE_TIMER23 0x12001000 ++ ++/* -------------------------------------------------------------------- */ ++/* Clock and Reset Generator REG */ ++/* -------------------------------------------------------------------- */ ++#define CRG_REG_BASE 0x12010000 ++#define REG_BASE_CRG CRG_REG_BASE ++ ++#define REG_CRG48 0x00c0 ++ ++/* FMC CRG register offset */ ++#define REG_FMC_CRG REG_CRG48 ++#define FMC_CLK_SEL(_clk) (((_clk) & 0x7) << 2) ++#define FMC_CLK_SEL_MASK (0x7 << 2) ++#define GET_FMC_CLK_TYPE(_reg) (((_reg) >> 2) & 0x7) ++/* SDR/DDR clock */ ++#define FMC_CLK_24M 0 ++#define FMC_CLK_75M 1 ++#define FMC_CLK_125M 2 ++#define FMC_CLK_150M 3 ++#define FMC_CLK_200M 4 ++#define FMC_CLK_250M 5 ++/* Only DDR clock */ ++#define FMC_CLK_300M 6 ++#define FMC_CLK_400M 7 ++#define FMC_CLK_ENABLE (0x1 << 1) ++#define FMC_SOFT_RST_REQ (0x1 << 0) ++ ++#define REG_BASE_UART0 0x12100000 ++#define REG_BASE_UART1 0x12101000 ++#define REG_BASE_UART2 0x12102000 ++#define REG_BASE_UART3 0x12103000 ++#define REG_BASE_UART4 0x12104000 ++#define REG_BASE_CUR_UART REG_BASE_UART0 ++ ++#define PMC_BASE 0x120a0000 ++ ++#define CCI_BASE 0x1ff00000 ++#define CCI_PORT0_BASE 0x1ff01000 ++#define CCI_PORT1_BASE 0x1ff02000 ++ ++ ++#define REG_BASE_A7_PERI 0x10300000 ++ ++/*CORTTX-A7 MPCORE MEMORY REGION*/ ++#define REG_A7_PERI_SCU 0x0000 ++#define REG_A7_PERI_GIC_CPU 0x2000 ++#define REG_A7_PERI_GLOBAL_TIMER 0x0200 ++#define REG_A7_PERI_PRI_TIMER_WDT 0x0600 ++#define REG_A7_PERI_GIC_DIST 0x1000 ++/* -------------------------------------------------------------------- */ ++/* System Control REG */ ++/* -------------------------------------------------------------------- */ ++#define SYS_CTRL_BASE 0x12020000 ++ ++/* System Control register offset */ ++#define REG_SC_CTRL 0x0000 ++ ++/* System soft reset register offset */ ++#define REG_SC_SYSRES 0x0004 ++ ++/* System Status register offset */ ++#define REG_SC_STAT 0x008c ++ ++/* if bit[5:4]=00b: boot form SPI && bit[3]=0: SPI nor flash ++ * bit[7]=0: 3-Byte address mode; bit[7]=1: 4-Byte address mode */ ++#define GET_SPI_NOR_ADDR_MODE(_reg) (((_reg) >> 7) & 0x1) ++#define GET_SYS_BOOT_MODE(_reg) (((_reg) >> 4) & 0x3) ++#define BOOT_MODE_MASK ((0x3) << 4) ++#define BOOT_FROM_SPI 0 ++#define BOOT_FROM_NAND 1 ++#define BOOT_FROM_EMMC 2 ++#define BOOT_FROM_DDR 3 ++/* bit[3]=0; SPI nor flash; bit[3]=1: SPI nand flash */ ++#define GET_SPI_DEVICE_TYPE(_reg) (((_reg) >> 3) & 0x1) ++ ++#define REG_SC_XTALCTRL 0x0010 ++#define REG_SC_APLLCTRL 0x0014 ++#define REG_SC_APLLFREQCTRL0 0x0018 ++#define REG_SC_APLLFREQCTRL1 0x001C ++#define REG_SC_VPLL0FREQCTRL0 0x0020 ++#define REG_SC_VPLL0FREQCTRL1 0x0024 ++#define REG_SC_VPLL1FREQCTRL0 0x0028 ++#define REG_SC_VPLL1FREQCTRL1 0x002C ++#define REG_SC_EPLLFREQCTRL0 0x0030 ++#define REG_SC_EPLLFREQCTRL1 0x0034 ++#define REG_SC_QPLLFREQCTRL0 0x0038 ++#define REG_SC_QPLLFREQCTRL1 0x003C ++#define REG_SC_LOW_POWER_CTRL 0x0040 ++#define REG_SC_IO_REUSE_SEL 0x0044 ++#define REG_SC_SRST_REQ_CTRL 0x0048 ++#define REG_SC_CA_RST_CTRL 0x004C ++#define REG_SC_WDG_RST_CTRL 0x0050 ++#define REG_SC_DDRC_DFI_RST_CTRL 0x0054 ++#define REG_SC_PLLLOCK_STAT 0x0070 ++#define REG_SC_GEN0 0x0080 ++#define REG_SC_GEN1 0x0084 ++#define REG_SC_GEN2 0x0088 ++#define REG_SC_GEN3 0x008C ++#define REG_SC_GEN4 0x0090 ++#define REG_SC_GEN5 0x0094 ++#define REG_SC_GEN6 0x0098 ++#define REG_SC_GEN7 0x009C ++#define REG_SC_GEN8 0x00A0 ++#define REG_SC_GEN9 0x00A4 ++#define REG_SC_GEN10 0x00A8 ++#define REG_SC_GEN11 0x00AC ++#define REG_SC_GEN12 0x00B0 ++#define REG_SC_GEN13 0x00B4 ++#define REG_SC_GEN14 0x00B8 ++#define REG_SC_GEN15 0x00BC ++#define REG_SC_GEN16 0x00C0 ++#define REG_SC_GEN17 0x00C4 ++#define REG_SC_GEN18 0x00C8 ++#define REG_SC_GEN19 0x00CC ++#define REG_SC_GEN20 0x00D0 ++#define REG_SC_GEN21 0x00D4 ++#define REG_SC_GEN22 0x00D8 ++#define REG_SC_GEN23 0x00DC ++#define REG_SC_GEN24 0x00E0 ++#define REG_SC_GEN25 0x00E4 ++#define REG_SC_GEN26 0x00E8 ++#define REG_SC_GEN27 0x00EC ++#define REG_SC_GEN28 0x00F0 ++#define REG_SC_GEN29 0x00F4 ++#define REG_SC_GEN30 0x00F8 ++#define REG_SC_GEN31 0x00FC ++#define REG_SC_LOCKEN 0x020C ++#define REG_SC_SYSID0 0x0EE0 ++#define REG_SC_SYSID1 0x0EE4 ++#define REG_SC_SYSID2 0x0EE8 ++#define REG_SC_SYSID3 0x0EEC ++ ++/*#define REG_BASE_WDG0 0xF8A2C000 */ ++ ++#define REG_PERI_CRG94 0x178 ++#define REG_PERI_CRG10 0x0028 ++#define REG_PERI_CRG98 0x0188 ++ ++#define CFG_GIC_CPU_BASE (IO_ADDRESS(REG_BASE_A7_PERI)\ ++ + REG_A7_PERI_GIC_CPU) ++#define CFG_GIC_DIST_BASE (IO_ADDRESS(REG_BASE_A7_PERI)\ ++ + REG_A7_PERI_GIC_DIST) ++ ++ ++/*********************************************************************/ ++/* ++ * 0x1-> init item1 ++ * 0x2-> init item2 ++ * 0x3->init item1 & item2 ++ */ ++#define INIT_REG_ITEM1 1 ++#define INIT_REG_ITEM2 2 ++#define INIT_REG_ITEM1_ITEM2 (INIT_REG_ITEM1 | INIT_REG_ITEM2) ++ ++ ++#endif /* End of __HI_CHIP_REGS_H__ */ +diff --git a/arch/arm/include/mach/hi3519_io.h b/arch/arm/include/mach/hi3519_io.h +new file mode 100644 +index 0000000..108d401 +--- /dev/null ++++ b/arch/arm/include/mach/hi3519_io.h +@@ -0,0 +1,46 @@ ++#ifndef __HI3519_IO_H ++#define __HI3519_IO_H ++ ++#ifdef CONFIG_PCI ++#define IO_SPACE_LIMIT 0xffffffff ++#define __io(a) __typesafe_io(PCI_IO_VIRT_BASE + ((a) & IO_SPACE_LIMIT)) ++#endif ++ ++/* phys_addr virt_addr ++ * 0x1000_0000 <-----> 0xFE00_0000 ++ */ ++#define HI3519_IOCH1_VIRT (0xFE000000) ++#define HI3519_IOCH1_PHYS (0x10000000) ++#define HI3519_IOCH1_SIZE (0x00310000) ++ ++/* phys_addr virt_addr ++ * 0x11000000 <-----> 0xFE400000 ++ */ ++#define HI3519_IOCH2_VIRT (0xFE400000) ++#define HI3519_IOCH2_PHYS (0x11000000) ++#define HI3519_IOCH2_SIZE (0x003F0000) ++ ++/* phys_addr virt_addr ++ * 0x12000000 <-----> 0xFE800000 ++ */ ++#define HI3519_IOCH3_VIRT (0xFE800000) ++#define HI3519_IOCH3_PHYS (0x12000000) ++#define HI3519_IOCH3_SIZE (0x00170000) ++ ++ ++#define IO_OFFSET_LOW (0xEC800000) ++#define IO_OFFSET_MID (0xED400000) ++#define IO_OFFSET_HIGH (0xEE000000) ++ ++#define IO_ADDRESS_LOW(x) ((x) + IO_OFFSET_LOW) ++#define IO_ADDRESS_MID(x) ((x) + IO_OFFSET_MID) ++#define IO_ADDRESS_HIGH(x) ((x) + IO_OFFSET_HIGH) ++ ++#define __IO_ADDRESS(x) ((x >= HI3519_IOCH2_PHYS) ? IO_ADDRESS_MID(x)\ ++ : IO_ADDRESS_HIGH(x)) ++#define IO_ADDRESS(x) ((x) >= HI3519_IOCH3_PHYS ? IO_ADDRESS_LOW(x)\ ++ : __IO_ADDRESS(x)) ++ ++#define __io_address(n) (IOMEM(IO_ADDRESS(n))) ++ ++#endif +diff --git a/arch/arm/include/mach/hi3519_platform.h b/arch/arm/include/mach/hi3519_platform.h +new file mode 100644 +index 0000000..b845f33 +--- /dev/null ++++ b/arch/arm/include/mach/hi3519_platform.h +@@ -0,0 +1,164 @@ ++#ifndef __HI3519_CHIP_REGS_H__ ++#define __HI3519_CHIP_REGS_H__ ++ ++/* SRAM Base Address Register */ ++#define SRAM_BASE_ADDRESS 0x4010000 ++ ++#define REG_BASE_TIMER01 0x12000000 ++#define REG_BASE_TIMER23 0x12001000 ++ ++/* -------------------------------------------------------------------- */ ++/* Clock and Reset Generator REG */ ++/* -------------------------------------------------------------------- */ ++#define CRG_REG_BASE 0x12010000 ++#define REG_BASE_CRG CRG_REG_BASE ++ ++#define REG_CRG48 0x00c0 ++ ++/* FMC CRG register offset */ ++#define REG_FMC_CRG REG_CRG48 ++#define FMC_CLK_SEL(_clk) (((_clk) & 0x7) << 2) ++#define FMC_CLK_SEL_MASK (0x7 << 2) ++#define GET_FMC_CLK_TYPE(_reg) (((_reg) >> 2) & 0x7) ++/* SDR/DDR clock */ ++#define FMC_CLK_24M 0 ++#define FMC_CLK_75M 1 ++#define FMC_CLK_125M 2 ++#define FMC_CLK_150M 3 ++#define FMC_CLK_200M 4 ++#define FMC_CLK_250M 5 ++/* Only DDR clock */ ++#define FMC_CLK_300M 6 ++#define FMC_CLK_400M 7 ++#define FMC_CLK_ENABLE (0x1 << 1) ++#define FMC_SOFT_RST_REQ (0x1 << 0) ++ ++#define REG_BASE_UART0 0x12100000 ++#define REG_BASE_UART1 0x12101000 ++#define REG_BASE_UART2 0x12102000 ++#define REG_BASE_UART3 0x12103000 ++#define REG_BASE_UART4 0x12104000 ++#define REG_BASE_CUR_UART REG_BASE_UART0 ++ ++#define PMC_BASE 0x120a0000 ++ ++#define CCI_BASE 0x1ff00000 ++#define CCI_PORT0_BASE 0x1ff01000 ++#define CCI_PORT1_BASE 0x1ff02000 ++ ++ ++#define REG_BASE_A7_PERI 0x10300000 ++ ++/*CORTTX-A7 MPCORE MEMORY REGION*/ ++#define REG_A7_PERI_SCU 0x0000 ++#define REG_A7_PERI_GIC_CPU 0x2000 ++#define REG_A7_PERI_GLOBAL_TIMER 0x0200 ++#define REG_A7_PERI_PRI_TIMER_WDT 0x0600 ++#define REG_A7_PERI_GIC_DIST 0x1000 ++/* -------------------------------------------------------------------- */ ++/* System Control REG */ ++/* -------------------------------------------------------------------- */ ++#define SYS_CTRL_BASE 0x12020000 ++ ++/* System Control register offset */ ++#define REG_SC_CTRL 0x0000 ++ ++/* System soft reset register offset */ ++#define REG_SC_SYSRES 0x0004 ++ ++/* System Status register offset */ ++#define REG_SC_STAT 0x008c ++ ++/* if bit[5:4]=00b: boot form SPI && bit[3]=0: SPI nor flash ++ * bit[7]=0: 3-Byte address mode; bit[7]=1: 4-Byte address mode */ ++#define GET_SPI_NOR_ADDR_MODE(_reg) (((_reg) >> 7) & 0x1) ++#define GET_SYS_BOOT_MODE(_reg) (((_reg) >> 4) & 0x3) ++#define BOOT_MODE_MASK ((0x3) << 4) ++#define BOOT_FROM_SPI 0 ++#define BOOT_FROM_NAND 1 ++#define BOOT_FROM_EMMC 2 ++#define BOOT_FROM_DDR 3 ++/* bit[3]=0; SPI nor flash; bit[3]=1: SPI nand flash */ ++#define GET_SPI_DEVICE_TYPE(_reg) (((_reg) >> 3) & 0x1) ++ ++#define REG_SC_XTALCTRL 0x0010 ++#define REG_SC_APLLCTRL 0x0014 ++#define REG_SC_APLLFREQCTRL0 0x0018 ++#define REG_SC_APLLFREQCTRL1 0x001C ++#define REG_SC_VPLL0FREQCTRL0 0x0020 ++#define REG_SC_VPLL0FREQCTRL1 0x0024 ++#define REG_SC_VPLL1FREQCTRL0 0x0028 ++#define REG_SC_VPLL1FREQCTRL1 0x002C ++#define REG_SC_EPLLFREQCTRL0 0x0030 ++#define REG_SC_EPLLFREQCTRL1 0x0034 ++#define REG_SC_QPLLFREQCTRL0 0x0038 ++#define REG_SC_QPLLFREQCTRL1 0x003C ++#define REG_SC_LOW_POWER_CTRL 0x0040 ++#define REG_SC_IO_REUSE_SEL 0x0044 ++#define REG_SC_SRST_REQ_CTRL 0x0048 ++#define REG_SC_CA_RST_CTRL 0x004C ++#define REG_SC_WDG_RST_CTRL 0x0050 ++#define REG_SC_DDRC_DFI_RST_CTRL 0x0054 ++#define REG_SC_PLLLOCK_STAT 0x0070 ++#define REG_SC_GEN0 0x0080 ++#define REG_SC_GEN1 0x0084 ++#define REG_SC_GEN2 0x0088 ++#define REG_SC_GEN3 0x008C ++#define REG_SC_GEN4 0x0090 ++#define REG_SC_GEN5 0x0094 ++#define REG_SC_GEN6 0x0098 ++#define REG_SC_GEN7 0x009C ++#define REG_SC_GEN8 0x00A0 ++#define REG_SC_GEN9 0x00A4 ++#define REG_SC_GEN10 0x00A8 ++#define REG_SC_GEN11 0x00AC ++#define REG_SC_GEN12 0x00B0 ++#define REG_SC_GEN13 0x00B4 ++#define REG_SC_GEN14 0x00B8 ++#define REG_SC_GEN15 0x00BC ++#define REG_SC_GEN16 0x00C0 ++#define REG_SC_GEN17 0x00C4 ++#define REG_SC_GEN18 0x00C8 ++#define REG_SC_GEN19 0x00CC ++#define REG_SC_GEN20 0x00D0 ++#define REG_SC_GEN21 0x00D4 ++#define REG_SC_GEN22 0x00D8 ++#define REG_SC_GEN23 0x00DC ++#define REG_SC_GEN24 0x00E0 ++#define REG_SC_GEN25 0x00E4 ++#define REG_SC_GEN26 0x00E8 ++#define REG_SC_GEN27 0x00EC ++#define REG_SC_GEN28 0x00F0 ++#define REG_SC_GEN29 0x00F4 ++#define REG_SC_GEN30 0x00F8 ++#define REG_SC_GEN31 0x00FC ++#define REG_SC_LOCKEN 0x020C ++#define REG_SC_SYSID0 0x0EE0 ++#define REG_SC_SYSID1 0x0EE4 ++#define REG_SC_SYSID2 0x0EE8 ++#define REG_SC_SYSID3 0x0EEC ++ ++/*#define REG_BASE_WDG0 0xF8A2C000 */ ++ ++#define REG_PERI_CRG94 0x178 ++#define REG_PERI_CRG10 0x0028 ++#define REG_PERI_CRG98 0x0188 ++ ++#define CFG_GIC_CPU_BASE (IO_ADDRESS(REG_BASE_A7_PERI)\ ++ + REG_A7_PERI_GIC_CPU) ++#define CFG_GIC_DIST_BASE (IO_ADDRESS(REG_BASE_A7_PERI)\ ++ + REG_A7_PERI_GIC_DIST) ++ ++ ++/*********************************************************************/ ++/* ++ * 0x1-> init item1 ++ * 0x2-> init item2 ++ * 0x3->init item1 & item2 ++ */ ++#define INIT_REG_ITEM1 1 ++#define INIT_REG_ITEM2 2 ++#define INIT_REG_ITEM1_ITEM2 (INIT_REG_ITEM1 | INIT_REG_ITEM2) ++ ++ ++#endif /* End of __HI_CHIP_REGS_H__ */ +diff --git a/arch/arm/include/mach/hi3519v101_io.h b/arch/arm/include/mach/hi3519v101_io.h +new file mode 100644 +index 0000000..7083af5 +--- /dev/null ++++ b/arch/arm/include/mach/hi3519v101_io.h +@@ -0,0 +1,46 @@ ++#ifndef __HI3519_IO_H ++#define __HI3519_IO_H ++ ++#ifdef CONFIG_PCI ++#define IO_SPACE_LIMIT 0xffffffff ++#define __io(a) __typesafe_io(PCI_IO_VIRT_BASE + ((a) & IO_SPACE_LIMIT)) ++#endif ++ ++/* phys_addr virt_addr ++ * 0x1000_0000 <-----> 0xFE00_0000 ++ */ ++#define HI3519_IOCH1_VIRT (0xFE000000) ++#define HI3519_IOCH1_PHYS (0x10000000) ++#define HI3519_IOCH1_SIZE (0x00310000) ++ ++/* phys_addr virt_addr ++ * 0x11000000 <-----> 0xFE400000 ++ */ ++#define HI3519_IOCH2_VIRT (0xFE400000) ++#define HI3519_IOCH2_PHYS (0x11000000) ++#define HI3519_IOCH2_SIZE (0x004F0000) ++ ++/* phys_addr virt_addr ++ * 0x12000000 <-----> 0xFE900000 ++ */ ++#define HI3519_IOCH3_VIRT (0xFE900000) ++#define HI3519_IOCH3_PHYS (0x12000000) ++#define HI3519_IOCH3_SIZE (0x00170000) ++ ++ ++#define IO_OFFSET_LOW (0xEC900000) ++#define IO_OFFSET_MID (0xED400000) ++#define IO_OFFSET_HIGH (0xEE000000) ++ ++#define IO_ADDRESS_LOW(x) ((x) + IO_OFFSET_LOW) ++#define IO_ADDRESS_MID(x) ((x) + IO_OFFSET_MID) ++#define IO_ADDRESS_HIGH(x) ((x) + IO_OFFSET_HIGH) ++ ++#define __IO_ADDRESS(x) ((x >= HI3519_IOCH2_PHYS) ? IO_ADDRESS_MID(x)\ ++ : IO_ADDRESS_HIGH(x)) ++#define IO_ADDRESS(x) ((x) >= HI3519_IOCH3_PHYS ? IO_ADDRESS_LOW(x)\ ++ : __IO_ADDRESS(x)) ++ ++#define __io_address(n) (IOMEM(IO_ADDRESS(n))) ++ ++#endif +diff --git a/arch/arm/include/mach/hi3521d_io.h b/arch/arm/include/mach/hi3521d_io.h +new file mode 100644 +index 0000000..ed686f8 +--- /dev/null ++++ b/arch/arm/include/mach/hi3521d_io.h +@@ -0,0 +1,43 @@ ++#ifndef __HI3521D_IO_H ++#define __HI3521D_IO_H ++ ++/* phys_addr virt_addr ++ * 0x1000_0000 <-----> 0xFE00_0000 ++ * 0x1004_0000 <-----> 0xFE04_0000 ++ */ ++#define HI3521D_IOCH1_VIRT (0xFE000000) ++#define HI3521D_IOCH1_PHYS (0x10000000) ++#define HI3521D_IOCH1_SIZE (0x00400000) ++ ++/* phys_addr virt_addr ++ * 0x1200_0000 <-----> 0xFE4_00000 ++ * 0x1223_0000 <-----> 0xFE63_0000 ++ */ ++#define HI3521D_IOCH2_VIRT (0xFE400000) ++#define HI3521D_IOCH2_PHYS (0x12000000) ++#define HI3521D_IOCH2_SIZE (0x00230000) ++ ++/* phys_addr virt_addr ++ * 0x1300_0000 <-----> 0xFE70_0000 ++ * 0x1316_0000 <-----> 0xFE86_0000 ++ */ ++#define HI3521D_IOCH3_VIRT (0xFE700000) ++#define HI3521D_IOCH3_PHYS (0x13000000) ++#define HI3521D_IOCH3_SIZE (0x00160000) ++ ++#define IO_OFFSET_LOW (0xEB700000) ++#define IO_OFFSET_MID (0xEC400000) ++#define IO_OFFSET_HIGH (0xEE000000) ++ ++#define IO_ADDRESS_LOW(x) ((x) + IO_OFFSET_LOW) ++#define IO_ADDRESS_MID(x) ((x) + IO_OFFSET_MID) ++#define IO_ADDRESS_HIGH(x) ((x) + IO_OFFSET_HIGH) ++ ++#define __IO_ADDRESS_HIGH(x) ((x >= HI3521D_IOCH2_PHYS) ? IO_ADDRESS_MID(x) \ ++ : IO_ADDRESS_HIGH(x)) ++#define IO_ADDRESS(x) ((x) >= HI3521D_IOCH3_PHYS ? IO_ADDRESS_LOW(x) \ ++ : __IO_ADDRESS_HIGH(x)) ++ ++#define __io_address(x) (IOMEM(IO_ADDRESS(x))) ++ ++#endif +diff --git a/arch/arm/include/mach/hi3521d_platform.h b/arch/arm/include/mach/hi3521d_platform.h +new file mode 100644 +index 0000000..ac2d8b6 +--- /dev/null ++++ b/arch/arm/include/mach/hi3521d_platform.h +@@ -0,0 +1,88 @@ ++#ifndef __HI3521D_CHIP_REGS_H__ ++#define __HI3521D_CHIP_REGS_H__ ++ ++/* -------------------------------------------------------------------- */ ++/* SRAM Base Address Register */ ++#define SRAM_BASE_ADDRESS 0x04010000 ++ ++/* -------------------------------------------------------------------- */ ++/*CORTTX-A7 MPCORE MEMORY REGION*/ ++/* -------------------------------------------------------------------- */ ++#define REG_BASE_A7_PERI 0x10300000 ++#define A7_PERI_BASE REG_BASE_A7_PERI ++ ++#define REG_A7_PERI_SCU 0x0000 ++ ++/* -------------------------------------------------------------------- */ ++/* Clock and Reset Generator REG */ ++/* -------------------------------------------------------------------- */ ++#define REG_BASE_CRG 0x12040000 ++#define CRG_REG_BASE REG_BASE_CRG ++ ++#define REG_CRG32 0x0080 ++ ++/* A7 soft reset request register offset */ ++#define REG_A7_SRST_CRG REG_CRG32 ++#define DBG1_SRST_REQ BIT(4) ++#define CORE1_SRST_REQ BIT(2) ++ ++/* Ethernet CRG register offset */ ++#define REG_ETH_CRG 0x014C ++#define REG_ETH_MAC_IF 0x008C ++ ++#define UART_CKSEL_MASK (0x3 << 18) ++#define UART_CKSEL_24M (0x2 << 18) ++ ++#define REG_BASE_CUR_UART REG_BASE_UART0 ++ ++/*********************************************************************/ ++#define A7_AXI_SCALE_REG IO_ADDRESS(REG_BASE_CRG + 0x50) ++ ++/* -------------------------------------------------------------------- */ ++#define DDR_MEM_BASE 0x80000000 ++#define CFG_TIMER_PER (4) /* AXI:APB is 4:1 */ ++/* -------------------------------------------------------------------- */ ++#define get_bus_clk() ({ \ ++ unsigned long tmp_reg, busclk = 0;\ ++ tmp_reg = readl((void *)A7_AXI_SCALE_REG);\ ++ tmp_reg = (tmp_reg >> 2) & 0x3;\ ++ if (0x3 == tmp_reg) {\ ++ busclk = 300000000;\ ++ } else if (0x1 == tmp_reg) {\ ++ busclk = 200000000;\ ++ } \ ++ busclk;\ ++}) ++ ++/* hisilicon satav200 register */ ++#define HI_SATA_PORT_FIFOTH 0x44 ++#define HI_SATA_PORT_PHYCTL1 0x48 ++#define HI_SATA_PORT_PHYCTL 0x74 ++ ++#define HI_SATA_PHY_CTL0 0xA0 ++#define HI_SATA_PHY_CTL1 0xA4 ++#define HI_SATA_PHY_CTL2 0xB0 ++#define HI_SATA_PHY_RST_BACK_MASK 0xAC ++ ++#define HI_SATA_FIFOTH_VALUE 0xFEED9F24 ++ ++#define HI_SATA_BIGENDINE (1 << 3) ++ ++#define HI_SATA_PHY_MODE_1_5G 0 ++#define HI_SATA_PHY_MODE_3G 1 ++#define HI_SATA_PHY_MODE_6G 2 ++ ++#define HI_SATA_PHY_1_5G 0x0e180000 ++#define HI_SATA_PHY_3G 0x0e390000 ++#define HI_SATA_PHY_6G 0x0e5a0000 ++ ++#define HI_SATA_PHY_SG_1_5G 0x50438 ++#define HI_SATA_PHY_SG_3G 0x50438 ++#define HI_SATA_PHY_SG_6G 0x50438 ++ ++#define HI_SATA_MISC_CTRL IO_ADDRESS(0x12120000) ++#define HI_SATA_MISC_SATA_PHY0 (HI_SATA_MISC_CTRL + 0x58) ++#define HI_SATA_MISC_SATA_PHY1 (HI_SATA_MISC_CTRL + 0x64) ++ ++#endif /* End of __HI_CHIP_REGS_H__ */ ++ +diff --git a/arch/arm/include/mach/hi3531d_io.h b/arch/arm/include/mach/hi3531d_io.h +new file mode 100644 +index 0000000..6d2640e +--- /dev/null ++++ b/arch/arm/include/mach/hi3531d_io.h +@@ -0,0 +1,61 @@ ++#ifndef __HI3531D_IO_H ++#define __HI3531D_IO_H ++ ++#ifdef CONFIG_PCI ++#define IO_SPACE_LIMIT 0xffffffff ++#define __io(a) __typesafe_io(PCI_IO_VIRT_BASE + ((a) & IO_SPACE_LIMIT)) ++#endif ++ ++/* phys_addr virt_addr ++ * 0x1000_0000 <-----> 0xFE00_0000 ++ * 0x1071_0000 <-----> 0xFE71_0000 ++ */ ++#define HI3531D_IOCH1_VIRT (0xFE000000) ++#define HI3531D_IOCH1_PHYS (0x10000000) ++#define HI3531D_IOCH1_SIZE (0x00710000) ++#define IO_OFFSET_IOCH1 (0xEE000000) ++ ++/* phys_addr virt_addr ++ * 0x1100_0000 <-----> 0xFE78_0000 ++ * 0x1104_0000 <-----> 0xFE7C_0000 ++ */ ++#define HI3531D_IOCH2_VIRT (0xFE780000) ++#define HI3531D_IOCH2_PHYS (0x11000000) ++#define HI3531D_IOCH2_SIZE (0x00040000) ++#define IO_OFFSET_IOCH2 (0xED780000) ++ ++/* phys_addr virt_addr ++ * 0x1200_0000 <-----> 0xFE80_0000 ++ * 0x1230_0000 <-----> 0xFEB0_0000 ++ */ ++#define HI3531D_IOCH3_VIRT (0xFE800000) ++#define HI3531D_IOCH3_PHYS (0x12000000) ++#define HI3531D_IOCH3_SIZE (0x00300000) ++#define IO_OFFSET_IOCH3 (0xEC800000) ++ ++/* phys_addr virt_addr ++ * 0x1300_0000 <-----> 0xFEB0_0000 ++ * 0x131A_0000 <-----> 0xFECA_0000 ++ */ ++#define HI3531D_IOCH4_VIRT (0xFEB00000) ++#define HI3531D_IOCH4_PHYS (0x13000000) ++#define HI3531D_IOCH4_SIZE (0x001A0000) ++#define IO_OFFSET_IOCH4 (0xEBB00000) ++ ++#define IO_ADDR_HIGH_H(n) ((n) + IO_OFFSET_IOCH4) ++#define IO_ADDR_HIGH_L(n) ((n) + IO_OFFSET_IOCH3) ++#define IO_ADDR_LOW_H(n) ((n) + IO_OFFSET_IOCH2) ++#define IO_ADDR_LOW_L(n) ((n) + IO_OFFSET_IOCH1) ++ ++#define __IO_ADDR_HIGH(n) ((n >= HI3531D_IOCH4_PHYS) ? IO_ADDR_HIGH_H(n) \ ++ : IO_ADDR_HIGH_L(n)) ++ ++#define __IO_ADDR_LOW(n) ((n >= HI3531D_IOCH2_PHYS) ? IO_ADDR_LOW_H(n) \ ++ : IO_ADDR_LOW_L(n)) ++ ++#define IO_ADDRESS(n) ((n) >= HI3531D_IOCH3_PHYS ? __IO_ADDR_HIGH(n) \ ++ : __IO_ADDR_LOW(n)) ++ ++#define __io_address(n) (IOMEM(IO_ADDRESS(n))) ++ ++#endif +diff --git a/arch/arm/include/mach/hi3531d_platform.h b/arch/arm/include/mach/hi3531d_platform.h +new file mode 100644 +index 0000000..c2ff7c0 +--- /dev/null ++++ b/arch/arm/include/mach/hi3531d_platform.h +@@ -0,0 +1,279 @@ ++#ifndef __HI3531D_CHIP_REGS_H__ ++#define __HI3531D_CHIP_REGS_H__ ++ ++/* -------------------------------------------------------------------- */ ++/* SRAM Base Address Register */ ++#define SRAM_BASE_ADDRESS 0x04010000 ++ ++/* -------------------------------------------------------------------- */ ++#define FMC_REG_BASE 0x10000000 ++#define NFC_REG_BASE 0x10010000 ++ ++/* -------------------------------------------------------------------- */ ++/* CORTTX-A9 internal Register */ ++/* -------------------------------------------------------------------- */ ++#define A9_PERI_BASE 0x10300000 ++ ++#define REG_A9_PERI_SCU 0x0000 ++#define REG_A9_PERI_GIC_CPU 0x0100 ++#define REG_A9_PERI_GLOBAL_TIMER 0x0200 ++#define REG_A9_PERI_PRI_TIMER_WDT 0x0600 ++#define REG_A9_PERI_GIC_DIST 0x1000 ++ ++#define CFG_GIC_CPU_BASE (IO_ADDRESS(A9_PERI_BASE) \ ++ + REG_A9_PERI_GIC_CPU) ++#define CFG_GIC_DIST_BASE (IO_ADDRESS(A9_PERI_BASE) \ ++ + REG_A9_PERI_GIC_DIST) ++ ++ /* -------------------------------------------------------------------- */ ++#define GSF_REG_BASE 0x100a0000 ++ ++/* -------------------------------------------------------------------- */ ++#define REG_BASE_L2CACHE 0x10700000 ++ ++/* -------------------------------------------------------------------- */ ++#define TIMER0_REG_BASE 0x12000000 ++#define TIMER1_REG_BASE 0x12000020 ++#define TIMER2_REG_BASE 0x12010000 ++#define TIMER3_REG_BASE 0x12010020 ++#define TIMER4_REG_BASE 0x12020000 ++#define TIMER5_REG_BASE 0x12020020 ++#define TIMER6_REG_BASE 0x12030000 ++#define TIMER7_REG_BASE 0x12030020 ++ ++/* -------------------------------------------------------------------- */ ++/* Clock and Reset Generator REG */ ++/* -------------------------------------------------------------------- */ ++#define CRG_REG_BASE 0x12040000 ++#define REG_BASE_CRG CRG_REG_BASE ++ ++#define REG_CRG20 0x0050 ++#define REG_CRG32 0x0080 ++#define REG_CRG72 0x0120 ++#define REG_CRG75 0x012c ++#define REG_CRG76 0x0130 ++#define REG_CRG77 0x0134 ++#define REG_CRG79 0x013c ++#define REG_CRG81 0x0144 ++#define REG_CRG82 0x0148 ++#define REG_CRG83 0x014c ++#define REG_CRG85 0x0154 ++#define REG_CRG87 0x015c ++#define REG_CRG91 0x016c ++ ++/* SOC CRG register offset */ ++#define REG_SOC_CRG REG_CRG20 ++#define GET_SYS_BUS_CLK(_reg) (((_reg) >> 2) & 0x3) ++#define SYS_CLK_XTAL 0 ++#define SYS_CLK_324M 1 ++#define SYS_CLK_375M 2 ++#define GET_PERI_AXI_CLK(_reg) ((_reg) & 0x3) ++#define PERI_CLK_XTAL 0 ++#define PERI_CLK_200M 1 ++#define PERI_CLK_300M 2 ++ ++/* A9 soft reset request register offset */ ++#define REG_A9_SRST_CRG REG_CRG32 ++#define WDG1_SRST_REQ (0x1 << 6) ++#define DBG1_SRST_REQ (0x1 << 5) ++#define CPU1_SRST_REQ (0x1 << 4) ++ ++/* USB 3.0 CRG PHY register offset */ ++#define REG_USB3_PHY0 REG_CRG72 ++ ++/* USB 3.0 CRG Control register offset */ ++#define REG_USB3_CTRL REG_CRG75 ++ ++/* USB 2.0 CRG Control register offset */ ++#define REG_USB2_CTRL REG_CRG76 ++ ++/* USB 2.0 CRG PHY register offset */ ++#define REG_USB2_PHY0 REG_CRG77 ++#define REG_USB2_PHY1 REG_CRG91 ++ ++/* NFC CRG register offset */ ++#define REG_NFC_CRG REG_CRG79 ++#define NFC_CLK_SEL(_clk) (((_clk) & 0x3) << 2) ++#define NFC_CLK_24M 0 ++#define NFC_CLK_200M 1 ++#define NFC_CLK_ENABLE (1 << 1) ++ ++/* DMAC CRG register offset */ ++#define REG_DMAC_CRG REG_CRG81 ++#define DMAC_CLK_EN (0x1 << 1) ++#define DMAC_SRST_REQ (0x1 << 0) ++ ++/* FMC CRG register offset */ ++#define REG_FMC_CRG REG_CRG82 ++#define FMC_CLK_SEL(_clk) (((_clk) & 0x3) << 2) ++#define FMC_CLK_SEL_MASK (0x3 << 2) ++#define FMC_CLK_24M 0 ++#define FMC_CLK_83M 1 ++#define FMC_CLK_125M 2 ++#define FMC_CLK_150M 3 ++#define FMC_CLK_ENABLE (0x1 << 1) ++ ++/* Ethernet CRG register offset */ ++#define REG_ETH_CRG REG_CRG83 ++#define REG_ETH_MAC_IF REG_CRG87 ++ ++/* Uart CRG register offset */ ++#define REG_UART_CRG REG_CRG85 ++#define UART_CLK_SEL(_clk) (((_clk) & 0x3) << 19) ++#define UART_CLK_SEL_MASK (0x3 << 19) ++#define UART_CLK_APB 0 ++#define UART_CLK_24M 1 ++#define UART_CLK_2M 2 ++ ++/* SSP CRG register offset */ ++#define REG_SSP_CRG REG_CRG85 ++#define SSP_CLK_ENABLE (0x1 << 13) ++#define SSP_SOFT_RESET_REQ (0x1 << 5) ++ ++/* -------------------------------------------------------------------- */ ++/* System Control REG */ ++/* -------------------------------------------------------------------- */ ++#define SYS_CTRL_BASE 0x12050000 ++ ++/* System Control register offset */ ++#define REG_SC_CTRL 0x0000 ++#define SC_CTRL_TIMER7_CLK_SEL(_clk) (((_clk) & 0x1) << 31) ++#define SC_CTRL_TIMER6_CLK_SEL(_clk) (((_clk) & 0x1) << 29) ++#define SC_CTRL_TIMER5_CLK_SEL(_clk) (((_clk) & 0x1) << 27) ++#define SC_CTRL_TIMER4_CLK_SEL(_clk) (((_clk) & 0x1) << 25) ++#define SC_CTRL_TIMER3_CLK_SEL(_clk) (((_clk) & 0x1) << 22) ++#define SC_CTRL_TIMER2_CLK_SEL(_clk) (((_clk) & 0x1) << 20) ++#define SC_CTRL_TIMER1_CLK_SEL(_clk) (((_clk) & 0x1) << 18) ++#define SC_CTRL_TIMER0_CLK_SEL(_clk) (((_clk) & 0x1) << 16) ++#define TIMER_CLK_3M 0 ++#define TIMER_CLK_BUS 1 ++ ++/* System soft reset register offset */ ++#define REG_SC_SYSRES 0x0004 ++ ++#define REG_SC_SOFT_INT 0x001c ++#define REG_SC_SOFT_TYPE 0x0020 ++#define REG_SC_LOCK_EN 0x0044 ++ ++/* System Status register offset */ ++#define REG_SC_STAT 0x008c ++#define SYS_CTRL_SYSSTAT REG_SC_STAT ++ ++/* bit[8]=0; SPI nor flash; bit[8]=1: SPI nand flash */ ++#define GET_SPI_DEVICE_TYPE(_reg) (((_reg) >> 8) & 0x1) ++/* if bit[8]=0 SPI nor flash ++ * * bit[7]=0: 3-Byte address mode; bit[7]=1: 4-Byte address mode */ ++#define GET_SPI_NOR_ADDR_MODE(_reg) (((_reg) >> 7) & 0x1) ++ ++#define REG_SC_SYSID0 0x0EE0 ++#define REG_SC_SYSID1 0x0EE4 ++#define REG_SC_SYSID2 0x0EE8 ++#define REG_SC_SYSID3 0x0EEC ++ ++/* -------------------------------------------------------------------- */ ++/* UART Control REG */ ++/* -------------------------------------------------------------------- */ ++#define UART0_REG_BASE 0x12080000 ++#define UART1_REG_BASE 0x12090000 ++#define UART2_REG_BASE 0x120A0000 ++#define UART3_REG_BASE 0x12130000 ++ ++#define REG_UART_DATA 0x0000 ++#define REG_UART_FLAG 0x0018 ++#define REG_UART_CTRL 0x0030 ++#define REG_UART_DMA_CR 0x0048 ++ ++/* -------------------------------------------------------------------- */ ++/* I2C Control REG */ ++/* -------------------------------------------------------------------- */ ++#define I2C0_REG_BASE 0x120c0000 ++#define I2C1_REG_BASE 0x122e0000 ++ ++#define REG_I2C_DATA 0x0010 ++ ++/* -------------------------------------------------------------------- */ ++/* SSP Control REG */ ++/* -------------------------------------------------------------------- */ ++#define SSP_REG_BASE 0x120d0000 ++ ++#define REG_SSP_DATA 0x0008 ++ ++/* -------------------------------------------------------------------- */ ++/* Peripheral Control REG */ ++/* -------------------------------------------------------------------- */ ++#define MISC_REG_BASE 0x12120000 ++ ++#define MISC_CTRL5 0x0014 ++#define MISC_CTRL36 0x0090 ++#define MISC_CTRL37 0x0094 ++#define MISC_CTRL74 0x0128 ++#define MISC_CTRL75 0x012c ++#define MISC_CTRL78 0x0138 ++ ++/* SPI Chip Select register offset */ ++#define REG_SSP_CS MISC_CTRL5 ++#define SSP_CS_SEL(_cs) (((_cs) & 0x3) << 0) ++#define SSP_CS_SEL_MASK (0x3 << 0) ++ ++/* USB 2.0 MISC Control register offset */ ++#define REG_USB2_CTRL0 MISC_CTRL36 ++#define REG_USB2_CTRL1 MISC_CTRL37 ++ ++/* USB 3.0 MISC Control register offset */ ++#define REG_USB3_CTRL0 MISC_CTRL74 ++#define REG_USB3_CTRL1 MISC_CTRL75 ++ ++#define REG_COMB_PHY1 MISC_CTRL78 ++ ++/* hisilicon satav200 register */ ++#define HI_SATA_PORT_FIFOTH 0x44 ++#define HI_SATA_PORT_PHYCTL1 0x48 ++#define HI_SATA_PORT_PHYCTL 0x74 ++ ++#define HI_SATA_PHY_CTL0 0xA0 ++#define HI_SATA_PHY_CTL1 0xA4 ++#define HI_SATA_PHY_CTL2 0xB0 ++#define HI_SATA_PHY_RST_BACK_MASK 0xAC ++ ++#define HI_SATA_FIFOTH_VALUE 0xFEED9F24 ++ ++#define HI_SATA_BIGENDINE (1 << 3) ++ ++#define HI_SATA_PHY_MODE_1_5G 0 ++#define HI_SATA_PHY_MODE_3G 1 ++#define HI_SATA_PHY_MODE_6G 2 ++ ++#define HI_SATA_PHY_1_5G 0x0e180000 ++#define HI_SATA_PHY_3G 0x0e390000 ++#define HI_SATA_PHY_6G 0x0e5a0000 ++ ++#define HI_SATA_PHY_SG_1_5G 0x50438 ++#define HI_SATA_PHY_SG_3G 0x50438 ++#define HI_SATA_PHY_SG_6G 0x50438 ++ ++#define HI_SATA_MISC_CTRL IO_ADDRESS(0x12120000) ++#define HI_SATA_MISC_COMB_PHY0 (HI_SATA_MISC_CTRL + 0x134) ++#define HI_SATA_MISC_COMB_PHY1 (HI_SATA_MISC_CTRL + 0x138) ++#define HI_SATA_MISC_COMB_PHY2 (HI_SATA_MISC_CTRL + 0x140) ++#define HI_SATA_MISC_COMB_PHY3 (HI_SATA_MISC_CTRL + 0x144) ++ ++/* -------------------------------------------------------------------- */ ++#define FMC_MEM_BASE 0x14000000 ++#define NFC_MEM_BASE 0x15000000 ++#define DDR_MEM_BASE 0x40000000 ++ ++#define CFG_TIMER_PER (4) /* AXI:APB is 4:1 */ ++/* -------------------------------------------------------------------- */ ++#define get_bus_clk() ({ \ ++ unsigned int base, regval, busclk = 0; \ ++ base = IO_ADDRESS(CRG_REG_BASE + REG_SOC_CRG); \ ++ regval = readl((void *)base); \ ++ regval = GET_PERI_AXI_CLK(regval); \ ++ if (PERI_CLK_200M == regval) \ ++ busclk = 200000000; \ ++ else if (PERI_CLK_300M == regval) \ ++ busclk = 300000000; \ ++ busclk; \ ++ }) ++ ++#endif /* End of __HI3531D_CHIP_REGS_H__ */ +diff --git a/arch/arm/include/mach/hi3536c_io.h b/arch/arm/include/mach/hi3536c_io.h +new file mode 100644 +index 0000000..5e0f34d +--- /dev/null ++++ b/arch/arm/include/mach/hi3536c_io.h +@@ -0,0 +1,43 @@ ++#ifndef __HI3536C_IO_H ++#define __HI3536C_IO_H ++ ++/* phys_addr virt_addr ++ * 0x1000_0000 <-----> 0xFE00_0000 ++ * 0x1004_0000 <-----> 0xFE04_0000 ++ */ ++#define HI3536C_IOCH1_VIRT (0xFE000000) ++#define HI3536C_IOCH1_PHYS (0x10000000) ++#define HI3536C_IOCH1_SIZE (0x00400000) ++ ++/* phys_addr virt_addr ++ * 0x1200_0000 <-----> 0xFE4_00000 ++ * 0x1223_0000 <-----> 0xFE63_0000 ++ */ ++#define HI3536C_IOCH2_VIRT (0xFE400000) ++#define HI3536C_IOCH2_PHYS (0x12000000) ++#define HI3536C_IOCH2_SIZE (0x00230000) ++ ++/* phys_addr virt_addr ++ * 0x1300_0000 <-----> 0xFE70_0000 ++ * 0x1316_0000 <-----> 0xFE86_0000 ++ */ ++#define HI3536C_IOCH3_VIRT (0xFE700000) ++#define HI3536C_IOCH3_PHYS (0x13000000) ++#define HI3536C_IOCH3_SIZE (0x00160000) ++ ++#define IO_OFFSET_LOW (0xEB700000) ++#define IO_OFFSET_MID (0xEC400000) ++#define IO_OFFSET_HIGH (0xEE000000) ++ ++#define IO_ADDRESS_LOW(x) ((x) + IO_OFFSET_LOW) ++#define IO_ADDRESS_MID(x) ((x) + IO_OFFSET_MID) ++#define IO_ADDRESS_HIGH(x) ((x) + IO_OFFSET_HIGH) ++ ++#define __IO_ADDRESS_HIGH(x) ((x >= HI3536C_IOCH2_PHYS) ? IO_ADDRESS_MID(x) \ ++ : IO_ADDRESS_HIGH(x)) ++#define IO_ADDRESS(x) ((x) >= HI3536C_IOCH3_PHYS ? IO_ADDRESS_LOW(x) \ ++ : __IO_ADDRESS_HIGH(x)) ++ ++#define __io_address(x) (IOMEM(IO_ADDRESS(x))) ++ ++#endif +diff --git a/arch/arm/include/mach/hi3536c_platform.h b/arch/arm/include/mach/hi3536c_platform.h +new file mode 100644 +index 0000000..5191057 +--- /dev/null ++++ b/arch/arm/include/mach/hi3536c_platform.h +@@ -0,0 +1,88 @@ ++#ifndef __HI3536C_CHIP_REGS_H__ ++#define __HI3536C_CHIP_REGS_H__ ++ ++/* -------------------------------------------------------------------- */ ++/* SRAM Base Address Register */ ++#define SRAM_BASE_ADDRESS 0x04010000 ++ ++/* -------------------------------------------------------------------- */ ++/*CORTTX-A7 MPCORE MEMORY REGION*/ ++/* -------------------------------------------------------------------- */ ++#define REG_BASE_A7_PERI 0x10300000 ++#define A7_PERI_BASE REG_BASE_A7_PERI ++ ++#define REG_A7_PERI_SCU 0x0000 ++ ++/* -------------------------------------------------------------------- */ ++/* Clock and Reset Generator REG */ ++/* -------------------------------------------------------------------- */ ++#define REG_BASE_CRG 0x12040000 ++#define CRG_REG_BASE REG_BASE_CRG ++ ++#define REG_CRG32 0x0080 ++ ++/* A7 soft reset request register offset */ ++#define REG_A7_SRST_CRG REG_CRG32 ++#define DBG1_SRST_REQ BIT(4) ++#define CORE1_SRST_REQ BIT(2) ++ ++/* Ethernet CRG register offset */ ++#define REG_ETH_CRG 0x014C ++#define REG_ETH_MAC_IF 0x008C ++ ++#define UART_CKSEL_MASK (0x3 << 18) ++#define UART_CKSEL_24M (0x2 << 18) ++ ++#define REG_BASE_CUR_UART REG_BASE_UART0 ++ ++/*********************************************************************/ ++#define A7_AXI_SCALE_REG IO_ADDRESS(REG_BASE_CRG + 0x50) ++ ++/* -------------------------------------------------------------------- */ ++#define DDR_MEM_BASE 0x80000000 ++#define CFG_TIMER_PER (4) /* AXI:APB is 4:1 */ ++/* -------------------------------------------------------------------- */ ++#define get_bus_clk() ({ \ ++ unsigned long tmp_reg, busclk = 0;\ ++ tmp_reg = readl((void *)A7_AXI_SCALE_REG);\ ++ tmp_reg = (tmp_reg >> 2) & 0x3;\ ++ if (0x3 == tmp_reg) {\ ++ busclk = 300000000;\ ++ } else if (0x1 == tmp_reg) {\ ++ busclk = 200000000;\ ++ } \ ++ busclk;\ ++}) ++ ++/* hisilicon satav200 register */ ++#define HI_SATA_PORT_FIFOTH 0x44 ++#define HI_SATA_PORT_PHYCTL1 0x48 ++#define HI_SATA_PORT_PHYCTL 0x74 ++ ++#define HI_SATA_PHY_CTL0 0xA0 ++#define HI_SATA_PHY_CTL1 0xA4 ++#define HI_SATA_PHY_CTL2 0xB0 ++#define HI_SATA_PHY_RST_BACK_MASK 0xAC ++ ++#define HI_SATA_FIFOTH_VALUE 0xFEED9F24 ++ ++#define HI_SATA_BIGENDINE (1 << 3) ++ ++#define HI_SATA_PHY_MODE_1_5G 0 ++#define HI_SATA_PHY_MODE_3G 1 ++#define HI_SATA_PHY_MODE_6G 2 ++ ++#define HI_SATA_PHY_1_5G 0x0e180000 ++#define HI_SATA_PHY_3G 0x0e390000 ++#define HI_SATA_PHY_6G 0x0e5a0000 ++ ++#define HI_SATA_PHY_SG_1_5G 0x50438 ++#define HI_SATA_PHY_SG_3G 0x50438 ++#define HI_SATA_PHY_SG_6G 0x50438 ++ ++#define HI_SATA_MISC_CTRL IO_ADDRESS(0x12120000) ++#define HI_SATA_MISC_SATA_PHY0 (HI_SATA_MISC_CTRL + 0x58) ++#define HI_SATA_MISC_SATA_PHY1 (HI_SATA_MISC_CTRL + 0x64) ++ ++#endif /* End of __HI_CHIP_REGS_H__ */ ++ +diff --git a/arch/arm/include/mach/hi3559_io.h b/arch/arm/include/mach/hi3559_io.h +new file mode 100644 +index 0000000..0238699 +--- /dev/null ++++ b/arch/arm/include/mach/hi3559_io.h +@@ -0,0 +1,46 @@ ++#ifndef __HI3559_IO_H ++#define __HI3559_IO_H ++ ++#ifdef CONFIG_PCI ++#define IO_SPACE_LIMIT 0xffffffff ++#define __io(a) __typesafe_io(PCI_IO_VIRT_BASE + ((a) & IO_SPACE_LIMIT)) ++#endif ++ ++/* phys_addr virt_addr ++ * 0x1000_0000 <-----> 0xFE00_0000 ++ */ ++#define HI3559_IOCH1_VIRT (0xFE000000) ++#define HI3559_IOCH1_PHYS (0x10000000) ++#define HI3559_IOCH1_SIZE (0x00310000) ++ ++/* phys_addr virt_addr ++ * 0x11000000 <-----> 0xFE400000 ++ */ ++#define HI3559_IOCH2_VIRT (0xFE400000) ++#define HI3559_IOCH2_PHYS (0x11000000) ++#define HI3559_IOCH2_SIZE (0x004F0000) ++ ++/* phys_addr virt_addr ++ * 0x12000000 <-----> 0xFE900000 ++ */ ++#define HI3559_IOCH3_VIRT (0xFE900000) ++#define HI3559_IOCH3_PHYS (0x12000000) ++#define HI3559_IOCH3_SIZE (0x00170000) ++ ++ ++#define IO_OFFSET_LOW (0xEC900000) ++#define IO_OFFSET_MID (0xED400000) ++#define IO_OFFSET_HIGH (0xEE000000) ++ ++#define IO_ADDRESS_LOW(x) ((x) + IO_OFFSET_LOW) ++#define IO_ADDRESS_MID(x) ((x) + IO_OFFSET_MID) ++#define IO_ADDRESS_HIGH(x) ((x) + IO_OFFSET_HIGH) ++ ++#define __IO_ADDRESS(x) ((x >= HI3559_IOCH2_PHYS) ? IO_ADDRESS_MID(x)\ ++ : IO_ADDRESS_HIGH(x)) ++#define IO_ADDRESS(x) ((x) >= HI3559_IOCH3_PHYS ? IO_ADDRESS_LOW(x)\ ++ : __IO_ADDRESS(x)) ++ ++#define __io_address(n) (IOMEM(IO_ADDRESS(n))) ++ ++#endif +diff --git a/arch/arm/include/mach/hi3559_platform.h b/arch/arm/include/mach/hi3559_platform.h +new file mode 100644 +index 0000000..0e2f4d4 +--- /dev/null ++++ b/arch/arm/include/mach/hi3559_platform.h +@@ -0,0 +1,164 @@ ++#ifndef __HI3559_CHIP_REGS_H__ ++#define __HI3559_CHIP_REGS_H__ ++ ++/* SRAM Base Address Register */ ++#define SRAM_BASE_ADDRESS 0x4010000 ++ ++#define REG_BASE_TIMER01 0x12000000 ++#define REG_BASE_TIMER23 0x12001000 ++ ++/* -------------------------------------------------------------------- */ ++/* Clock and Reset Generator REG */ ++/* -------------------------------------------------------------------- */ ++#define CRG_REG_BASE 0x12010000 ++#define REG_BASE_CRG CRG_REG_BASE ++ ++#define REG_CRG48 0x00c0 ++ ++/* FMC CRG register offset */ ++#define REG_FMC_CRG REG_CRG48 ++#define FMC_CLK_SEL(_clk) (((_clk) & 0x7) << 2) ++#define FMC_CLK_SEL_MASK (0x7 << 2) ++#define GET_FMC_CLK_TYPE(_reg) (((_reg) >> 2) & 0x7) ++/* SDR/DDR clock */ ++#define FMC_CLK_24M 0 ++#define FMC_CLK_75M 1 ++#define FMC_CLK_125M 2 ++#define FMC_CLK_150M 3 ++#define FMC_CLK_200M 4 ++#define FMC_CLK_250M 5 ++/* Only DDR clock */ ++#define FMC_CLK_300M 6 ++#define FMC_CLK_400M 7 ++#define FMC_CLK_ENABLE (0x1 << 1) ++#define FMC_SOFT_RST_REQ (0x1 << 0) ++ ++#define REG_BASE_UART0 0x12100000 ++#define REG_BASE_UART1 0x12101000 ++#define REG_BASE_UART2 0x12102000 ++#define REG_BASE_UART3 0x12103000 ++#define REG_BASE_UART4 0x12104000 ++#define REG_BASE_CUR_UART REG_BASE_UART0 ++ ++#define PMC_BASE 0x120a0000 ++ ++#define CCI_BASE 0x1ff00000 ++#define CCI_PORT0_BASE 0x1ff01000 ++#define CCI_PORT1_BASE 0x1ff02000 ++ ++ ++#define REG_BASE_A7_PERI 0x10300000 ++ ++/*CORTTX-A7 MPCORE MEMORY REGION*/ ++#define REG_A7_PERI_SCU 0x0000 ++#define REG_A7_PERI_GIC_CPU 0x2000 ++#define REG_A7_PERI_GLOBAL_TIMER 0x0200 ++#define REG_A7_PERI_PRI_TIMER_WDT 0x0600 ++#define REG_A7_PERI_GIC_DIST 0x1000 ++/* -------------------------------------------------------------------- */ ++/* System Control REG */ ++/* -------------------------------------------------------------------- */ ++#define SYS_CTRL_BASE 0x12020000 ++ ++/* System Control register offset */ ++#define REG_SC_CTRL 0x0000 ++ ++/* System soft reset register offset */ ++#define REG_SC_SYSRES 0x0004 ++ ++/* System Status register offset */ ++#define REG_SC_STAT 0x008c ++ ++/* if bit[5:4]=00b: boot form SPI && bit[3]=0: SPI nor flash ++ * bit[7]=0: 3-Byte address mode; bit[7]=1: 4-Byte address mode */ ++#define GET_SPI_NOR_ADDR_MODE(_reg) (((_reg) >> 7) & 0x1) ++#define GET_SYS_BOOT_MODE(_reg) (((_reg) >> 4) & 0x3) ++#define BOOT_MODE_MASK ((0x3) << 4) ++#define BOOT_FROM_SPI 0 ++#define BOOT_FROM_NAND 1 ++#define BOOT_FROM_EMMC 2 ++#define BOOT_FROM_DDR 3 ++/* bit[3]=0; SPI nor flash; bit[3]=1: SPI nand flash */ ++#define GET_SPI_DEVICE_TYPE(_reg) (((_reg) >> 3) & 0x1) ++ ++#define REG_SC_XTALCTRL 0x0010 ++#define REG_SC_APLLCTRL 0x0014 ++#define REG_SC_APLLFREQCTRL0 0x0018 ++#define REG_SC_APLLFREQCTRL1 0x001C ++#define REG_SC_VPLL0FREQCTRL0 0x0020 ++#define REG_SC_VPLL0FREQCTRL1 0x0024 ++#define REG_SC_VPLL1FREQCTRL0 0x0028 ++#define REG_SC_VPLL1FREQCTRL1 0x002C ++#define REG_SC_EPLLFREQCTRL0 0x0030 ++#define REG_SC_EPLLFREQCTRL1 0x0034 ++#define REG_SC_QPLLFREQCTRL0 0x0038 ++#define REG_SC_QPLLFREQCTRL1 0x003C ++#define REG_SC_LOW_POWER_CTRL 0x0040 ++#define REG_SC_IO_REUSE_SEL 0x0044 ++#define REG_SC_SRST_REQ_CTRL 0x0048 ++#define REG_SC_CA_RST_CTRL 0x004C ++#define REG_SC_WDG_RST_CTRL 0x0050 ++#define REG_SC_DDRC_DFI_RST_CTRL 0x0054 ++#define REG_SC_PLLLOCK_STAT 0x0070 ++#define REG_SC_GEN0 0x0080 ++#define REG_SC_GEN1 0x0084 ++#define REG_SC_GEN2 0x0088 ++#define REG_SC_GEN3 0x008C ++#define REG_SC_GEN4 0x0090 ++#define REG_SC_GEN5 0x0094 ++#define REG_SC_GEN6 0x0098 ++#define REG_SC_GEN7 0x009C ++#define REG_SC_GEN8 0x00A0 ++#define REG_SC_GEN9 0x00A4 ++#define REG_SC_GEN10 0x00A8 ++#define REG_SC_GEN11 0x00AC ++#define REG_SC_GEN12 0x00B0 ++#define REG_SC_GEN13 0x00B4 ++#define REG_SC_GEN14 0x00B8 ++#define REG_SC_GEN15 0x00BC ++#define REG_SC_GEN16 0x00C0 ++#define REG_SC_GEN17 0x00C4 ++#define REG_SC_GEN18 0x00C8 ++#define REG_SC_GEN19 0x00CC ++#define REG_SC_GEN20 0x00D0 ++#define REG_SC_GEN21 0x00D4 ++#define REG_SC_GEN22 0x00D8 ++#define REG_SC_GEN23 0x00DC ++#define REG_SC_GEN24 0x00E0 ++#define REG_SC_GEN25 0x00E4 ++#define REG_SC_GEN26 0x00E8 ++#define REG_SC_GEN27 0x00EC ++#define REG_SC_GEN28 0x00F0 ++#define REG_SC_GEN29 0x00F4 ++#define REG_SC_GEN30 0x00F8 ++#define REG_SC_GEN31 0x00FC ++#define REG_SC_LOCKEN 0x020C ++#define REG_SC_SYSID0 0x0EE0 ++#define REG_SC_SYSID1 0x0EE4 ++#define REG_SC_SYSID2 0x0EE8 ++#define REG_SC_SYSID3 0x0EEC ++ ++/*#define REG_BASE_WDG0 0xF8A2C000 */ ++ ++#define REG_PERI_CRG94 0x178 ++#define REG_PERI_CRG10 0x0028 ++#define REG_PERI_CRG98 0x0188 ++ ++#define CFG_GIC_CPU_BASE (IO_ADDRESS(REG_BASE_A7_PERI)\ ++ + REG_A7_PERI_GIC_CPU) ++#define CFG_GIC_DIST_BASE (IO_ADDRESS(REG_BASE_A7_PERI)\ ++ + REG_A7_PERI_GIC_DIST) ++ ++ ++/*********************************************************************/ ++/* ++ * 0x1-> init item1 ++ * 0x2-> init item2 ++ * 0x3->init item1 & item2 ++ */ ++#define INIT_REG_ITEM1 1 ++#define INIT_REG_ITEM2 2 ++#define INIT_REG_ITEM1_ITEM2 (INIT_REG_ITEM1 | INIT_REG_ITEM2) ++ ++ ++#endif /* End of __HI_CHIP_REGS_H__ */ +diff --git a/arch/arm/include/mach/io.h b/arch/arm/include/mach/io.h +new file mode 100644 +index 0000000..ae92d98 +--- /dev/null ++++ b/arch/arm/include/mach/io.h +@@ -0,0 +1,32 @@ ++#ifndef __ASM_ARM_ARCH_IO_H ++#define __ASM_ARM_ARCH_IO_H ++ ++#ifdef CONFIG_ARCH_HI3519 ++#include <mach/hi3519_io.h> ++#endif ++ ++#ifdef CONFIG_ARCH_HI3519V101 ++#include <mach/hi3519v101_io.h> ++#endif ++ ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++#include <mach/hi3559_io.h> ++#endif ++ ++#ifdef CONFIG_ARCH_HI3516AV200 ++#include <mach/hi3516av200_io.h> ++#endif ++ ++#ifdef CONFIG_ARCH_HI3536C ++#include <mach/hi3536c_io.h> ++#endif ++ ++#ifdef CONFIG_ARCH_HI3531D ++#include <mach/hi3531d_io.h> ++#endif ++ ++#ifdef CONFIG_ARCH_HI3521D ++#include <mach/hi3521d_io.h> ++#endif ++ ++#endif +diff --git a/arch/arm/include/mach/platform.h b/arch/arm/include/mach/platform.h +new file mode 100644 +index 0000000..ef78a22 +--- /dev/null ++++ b/arch/arm/include/mach/platform.h +@@ -0,0 +1,28 @@ ++#ifndef __HI_CHIP_REGS_H__ ++#define __HI_CHIP_REGS_H__ ++ ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++#include <mach/hi3519_platform.h> ++#endif ++ ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++#include <mach/hi3559_platform.h> ++#endif ++ ++#ifdef CONFIG_ARCH_HI3516AV200 ++#include <mach/hi3516av200_platform.h> ++#endif ++ ++#ifdef CONFIG_ARCH_HI3536C ++#include <mach/hi3536c_platform.h> ++#endif ++ ++#ifdef CONFIG_ARCH_HI3531D ++#include <mach/hi3531d_platform.h> ++#endif ++ ++#ifdef CONFIG_ARCH_HI3521D ++#include <mach/hi3521d_platform.h> ++#endif ++ ++#endif /* End of __HI_CHIP_REGS_H__ */ +diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c +index a88671c..152a95a 100644 +--- a/arch/arm/kernel/armksyms.c ++++ b/arch/arm/kernel/armksyms.c +@@ -83,6 +83,7 @@ EXPORT_SYMBOL(__raw_writesl); + EXPORT_SYMBOL(strchr); + EXPORT_SYMBOL(strrchr); + EXPORT_SYMBOL(memset); ++ + EXPORT_SYMBOL(memcpy); + EXPORT_SYMBOL(memmove); + EXPORT_SYMBOL(memchr); +diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c +index 2d2d608..e4d88fa 100644 +--- a/arch/arm/kernel/asm-offsets.c ++++ b/arch/arm/kernel/asm-offsets.c +@@ -10,9 +10,11 @@ + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +-#include <linux/compiler.h> + #include <linux/sched.h> + #include <linux/mm.h> ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++#include <linux/suspend.h> ++#endif + #include <linux/dma-mapping.h> + #ifdef CONFIG_KVM_ARM_HOST + #include <linux/kvm_host.h> +@@ -40,19 +42,10 @@ + * GCC 3.2.x: miscompiles NEW_AUX_ENT in fs/binfmt_elf.c + * (http://gcc.gnu.org/PR8896) and incorrect structure + * initialisation in fs/jffs2/erase.c +- * GCC 4.8.0-4.8.2: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58854 +- * miscompiles find_get_entry(), and can result in EXT3 and EXT4 +- * filesystem corruption (possibly other FS too). + */ +-#ifdef __GNUC__ + #if (__GNUC__ == 3 && __GNUC_MINOR__ < 3) + #error Your compiler is too buggy; it is known to miscompile kernels. +-#error Known good compilers: 3.3, 4.x +-#endif +-#if GCC_VERSION >= 40800 && GCC_VERSION < 40803 +-#error Your compiler is too buggy; it is known to miscompile kernels +-#error and result in filesystem corruption and oopses. +-#endif ++#error Known good compilers: 3.3 + #endif + + int main(void) +@@ -165,6 +158,11 @@ int main(void) + DEFINE(DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL); + DEFINE(DMA_TO_DEVICE, DMA_TO_DEVICE); + DEFINE(DMA_FROM_DEVICE, DMA_FROM_DEVICE); ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ DEFINE(PBE_ADDRESS, offsetof(struct pbe, address)); ++ DEFINE(PBE_ORIG_ADDRESS, offsetof(struct pbe, orig_address)); ++ DEFINE(PBE_NEXT, offsetof(struct pbe, next)); ++#endif + BLANK(); + DEFINE(CACHE_WRITEBACK_ORDER, __CACHE_WRITEBACK_ORDER); + DEFINE(CACHE_WRITEBACK_GRANULE, __CACHE_WRITEBACK_GRANULE); +diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c +index 131a6ab..a634e0e 100644 +--- a/arch/arm/kernel/etm.c ++++ b/arch/arm/kernel/etm.c +@@ -15,6 +15,7 @@ + #include <linux/init.h> + #include <linux/types.h> + #include <linux/io.h> ++#include <linux/slab.h> + #include <linux/sysrq.h> + #include <linux/device.h> + #include <linux/clk.h> +@@ -37,26 +38,37 @@ MODULE_AUTHOR("Alexander Shishkin"); + struct tracectx { + unsigned int etb_bufsz; + void __iomem *etb_regs; +- void __iomem *etm_regs; ++ void __iomem **etm_regs; ++ int etm_regs_count; + unsigned long flags; + int ncmppairs; + int etm_portsz; ++ int etm_contextid_size; ++ u32 etb_fc; ++ unsigned long range_start; ++ unsigned long range_end; ++ unsigned long data_range_start; ++ unsigned long data_range_end; ++ bool dump_initial_etb; + struct device *dev; + struct clk *emu_clk; + struct mutex mutex; + }; + +-static struct tracectx tracer; ++static struct tracectx tracer = { ++ .range_start = (unsigned long)_stext, ++ .range_end = (unsigned long)_etext, ++}; + + static inline bool trace_isrunning(struct tracectx *t) + { + return !!(t->flags & TRACER_RUNNING); + } + +-static int etm_setup_address_range(struct tracectx *t, int n, ++static int etm_setup_address_range(struct tracectx *t, int id, int n, + unsigned long start, unsigned long end, int exclude, int data) + { +- u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \ ++ u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY | + ETMAAT_NOVALCMP; + + if (n < 1 || n > t->ncmppairs) +@@ -72,95 +84,185 @@ static int etm_setup_address_range(struct tracectx *t, int n, + flags |= ETMAAT_IEXEC; + + /* first comparator for the range */ +- etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2)); +- etm_writel(t, start, ETMR_COMP_VAL(n * 2)); ++ etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2)); ++ etm_writel(t, id, start, ETMR_COMP_VAL(n * 2)); + + /* second comparator is right next to it */ +- etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); +- etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1)); +- +- flags = exclude ? ETMTE_INCLEXCL : 0; +- etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL); ++ etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); ++ etm_writel(t, id, end, ETMR_COMP_VAL(n * 2 + 1)); ++ ++ if (data) { ++ flags = exclude ? ETMVDC3_EXCLONLY : 0; ++ if (exclude) ++ n += 8; ++ etm_writel(t, id, flags | BIT(n), ETMR_VIEWDATACTRL3); ++ } else { ++ flags = exclude ? ETMTE_INCLEXCL : 0; ++ etm_writel(t, id, flags | (1 << n), ETMR_TRACEENCTRL); ++ } + + return 0; + } + +-static int trace_start(struct tracectx *t) ++static int trace_start_etm(struct tracectx *t, int id) + { + u32 v; + unsigned long timeout = TRACER_TIMEOUT; + +- etb_unlock(t); +- +- etb_writel(t, 0, ETBR_FORMATTERCTRL); +- etb_writel(t, 1, ETBR_CTRL); +- +- etb_lock(t); +- +- /* configure etm */ + v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz); ++ v |= ETMCTRL_CONTEXTIDSIZE(t->etm_contextid_size); + + if (t->flags & TRACER_CYCLE_ACC) + v |= ETMCTRL_CYCLEACCURATE; + +- etm_unlock(t); ++ if (t->flags & TRACER_BRANCHOUTPUT) ++ v |= ETMCTRL_BRANCH_OUTPUT; ++ ++ if (t->flags & TRACER_TRACE_DATA) ++ v |= ETMCTRL_DATA_DO_ADDR; ++ ++ if (t->flags & TRACER_TIMESTAMP) ++ v |= ETMCTRL_TIMESTAMP_EN; ++ ++ if (t->flags & TRACER_RETURN_STACK) ++ v |= ETMCTRL_RETURN_STACK_EN; + +- etm_writel(t, v, ETMR_CTRL); ++ etm_unlock(t, id); + +- while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) ++ etm_writel(t, id, v, ETMR_CTRL); ++ ++ while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) + ; + if (!timeout) { + dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); +- etm_lock(t); ++ etm_lock(t, id); + return -EFAULT; + } + +- etm_setup_address_range(t, 1, (unsigned long)_stext, +- (unsigned long)_etext, 0, 0); +- etm_writel(t, 0, ETMR_TRACEENCTRL2); +- etm_writel(t, 0, ETMR_TRACESSCTRL); +- etm_writel(t, 0x6f, ETMR_TRACEENEVT); ++ if (t->range_start || t->range_end) ++ etm_setup_address_range(t, id, 1, ++ t->range_start, t->range_end, 0, 0); ++ else ++ etm_writel(t, id, ETMTE_INCLEXCL, ETMR_TRACEENCTRL); ++ ++ etm_writel(t, id, 0, ETMR_TRACEENCTRL2); ++ etm_writel(t, id, 0, ETMR_TRACESSCTRL); ++ etm_writel(t, id, 0x6f, ETMR_TRACEENEVT); ++ ++ etm_writel(t, id, 0, ETMR_VIEWDATACTRL1); ++ etm_writel(t, id, 0, ETMR_VIEWDATACTRL2); ++ ++ if (t->data_range_start || t->data_range_end) ++ etm_setup_address_range(t, id, 2, t->data_range_start, ++ t->data_range_end, 0, 1); ++ else ++ etm_writel(t, id, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3); ++ ++ etm_writel(t, id, 0x6f, ETMR_VIEWDATAEVT); + + v &= ~ETMCTRL_PROGRAM; + v |= ETMCTRL_PORTSEL; + +- etm_writel(t, v, ETMR_CTRL); ++ etm_writel(t, id, v, ETMR_CTRL); + + timeout = TRACER_TIMEOUT; +- while (etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) ++ while (etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) + ; + if (!timeout) { + dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n"); +- etm_lock(t); ++ etm_lock(t, id); + return -EFAULT; + } + +- etm_lock(t); ++ etm_lock(t, id); ++ return 0; ++} ++ ++static int trace_start(struct tracectx *t) ++{ ++ int ret; ++ int id; ++ u32 etb_fc = t->etb_fc; ++ ++ etb_unlock(t); ++ ++ t->dump_initial_etb = false; ++ etb_writel(t, 0, ETBR_WRITEADDR); ++ etb_writel(t, etb_fc, ETBR_FORMATTERCTRL); ++ etb_writel(t, 1, ETBR_CTRL); ++ ++ etb_lock(t); ++ ++ /* configure etm(s) */ ++ for (id = 0; id < t->etm_regs_count; id++) { ++ ret = trace_start_etm(t, id); ++ if (ret) ++ return ret; ++ } + + t->flags |= TRACER_RUNNING; + + return 0; + } + +-static int trace_stop(struct tracectx *t) ++static int trace_stop_etm(struct tracectx *t, int id) + { + unsigned long timeout = TRACER_TIMEOUT; + +- etm_unlock(t); ++ etm_unlock(t, id); + +- etm_writel(t, 0x440, ETMR_CTRL); +- while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) ++ etm_writel(t, id, 0x440, ETMR_CTRL); ++ while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) + ; + if (!timeout) { +- dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); +- etm_lock(t); ++ dev_err(t->dev, ++ "etm%d: Waiting for progbit to assert timed out\n", ++ id); ++ etm_lock(t, id); + return -EFAULT; + } + +- etm_lock(t); ++ etm_lock(t, id); ++ return 0; ++} ++ ++static int trace_power_down_etm(struct tracectx *t, int id) ++{ ++ unsigned long timeout = TRACER_TIMEOUT; ++ etm_unlock(t, id); ++ while (!(etm_readl(t, id, ETMR_STATUS) & ETMST_PROGBIT) && --timeout) ++ ; ++ if (!timeout) { ++ dev_err(t->dev, "etm%d: Waiting for status progbit to assert timed out\n", ++ id); ++ etm_lock(t, id); ++ return -EFAULT; ++ } ++ ++ etm_writel(t, id, 0x441, ETMR_CTRL); ++ ++ etm_lock(t, id); ++ return 0; ++} ++ ++static int trace_stop(struct tracectx *t) ++{ ++ int id; ++ unsigned long timeout = TRACER_TIMEOUT; ++ u32 etb_fc = t->etb_fc; ++ ++ for (id = 0; id < t->etm_regs_count; id++) ++ trace_stop_etm(t, id); ++ ++ for (id = 0; id < t->etm_regs_count; id++) ++ trace_power_down_etm(t, id); + + etb_unlock(t); +- etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); ++ if (etb_fc) { ++ etb_fc |= ETBFF_STOPFL; ++ etb_writel(t, t->etb_fc, ETBR_FORMATTERCTRL); ++ } ++ etb_writel(t, etb_fc | ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); + + timeout = TRACER_TIMEOUT; + while (etb_readl(t, ETBR_FORMATTERCTRL) & +@@ -185,24 +287,15 @@ static int trace_stop(struct tracectx *t) + static int etb_getdatalen(struct tracectx *t) + { + u32 v; +- int rp, wp; ++ int wp; + + v = etb_readl(t, ETBR_STATUS); + + if (v & 1) + return t->etb_bufsz; + +- rp = etb_readl(t, ETBR_READADDR); + wp = etb_readl(t, ETBR_WRITEADDR); +- +- if (rp > wp) { +- etb_writel(t, 0, ETBR_READADDR); +- etb_writel(t, 0, ETBR_WRITEADDR); +- +- return 0; +- } +- +- return wp - rp; ++ return wp; + } + + /* sysrq+v will always stop the running trace and leave it at that */ +@@ -235,21 +328,18 @@ static void etm_dump(void) + printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM))); + printk(KERN_INFO "\n--- ETB buffer end ---\n"); + +- /* deassert the overflow bit */ +- etb_writel(t, 1, ETBR_CTRL); +- etb_writel(t, 0, ETBR_CTRL); +- +- etb_writel(t, 0, ETBR_TRIGGERCOUNT); +- etb_writel(t, 0, ETBR_READADDR); +- etb_writel(t, 0, ETBR_WRITEADDR); +- + etb_lock(t); + } + + static void sysrq_etm_dump(int key) + { ++ if (!mutex_trylock(&tracer.mutex)) { ++ printk(KERN_INFO "Tracing hardware busy\n"); ++ return; ++ } + dev_dbg(tracer.dev, "Dumping ETB buffer\n"); + etm_dump(); ++ mutex_unlock(&tracer.mutex); + } + + static struct sysrq_key_op sysrq_etm_op = { +@@ -276,6 +366,10 @@ static ssize_t etb_read(struct file *file, char __user *data, + struct tracectx *t = file->private_data; + u32 first = 0; + u32 *buf; ++ int wpos; ++ int skip; ++ long wlength; ++ loff_t pos = *ppos; + + mutex_lock(&t->mutex); + +@@ -287,31 +381,39 @@ static ssize_t etb_read(struct file *file, char __user *data, + etb_unlock(t); + + total = etb_getdatalen(t); ++ if (total == 0 && t->dump_initial_etb) ++ total = t->etb_bufsz; + if (total == t->etb_bufsz) + first = etb_readl(t, ETBR_WRITEADDR); + ++ if (pos > total * 4) { ++ skip = 0; ++ wpos = total; ++ } else { ++ skip = (int)pos % 4; ++ wpos = (int)pos / 4; ++ } ++ total -= wpos; ++ first = (first + wpos) % t->etb_bufsz; ++ + etb_writel(t, first, ETBR_READADDR); + +- length = min(total * 4, (int)len); +- buf = vmalloc(length); ++ wlength = min(total, DIV_ROUND_UP(skip + (int)len, 4)); ++ length = min(total * 4 - skip, (int)len); ++ buf = vmalloc(wlength * 4); + +- dev_dbg(t->dev, "ETB buffer length: %d\n", total); ++ dev_dbg(t->dev, "ETB read %ld bytes to %lld from %ld words at %d\n", ++ length, pos, wlength, first); ++ dev_dbg(t->dev, "ETB buffer length: %d\n", total + wpos); + dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS)); +- for (i = 0; i < length / 4; i++) ++ for (i = 0; i < wlength; i++) + buf[i] = etb_readl(t, ETBR_READMEM); + +- /* the only way to deassert overflow bit in ETB status is this */ +- etb_writel(t, 1, ETBR_CTRL); +- etb_writel(t, 0, ETBR_CTRL); +- +- etb_writel(t, 0, ETBR_WRITEADDR); +- etb_writel(t, 0, ETBR_READADDR); +- etb_writel(t, 0, ETBR_TRIGGERCOUNT); +- + etb_lock(t); + +- length -= copy_to_user(data, buf, length); ++ length -= copy_to_user(data, (u8 *)buf + skip, length); + vfree(buf); ++ *ppos = pos + length; + + out: + mutex_unlock(&t->mutex); +@@ -348,28 +450,17 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id) + if (ret) + goto out; + ++ mutex_lock(&t->mutex); + t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); + if (!t->etb_regs) { + ret = -ENOMEM; + goto out_release; + } + ++ t->dev = &dev->dev; ++ t->dump_initial_etb = true; + amba_set_drvdata(dev, t); + +- etb_miscdev.parent = &dev->dev; +- +- ret = misc_register(&etb_miscdev); +- if (ret) +- goto out_unmap; +- +- t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); +- if (IS_ERR(t->emu_clk)) { +- dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); +- return -EFAULT; +- } +- +- clk_enable(t->emu_clk); +- + etb_unlock(t); + t->etb_bufsz = etb_readl(t, ETBR_DEPTH); + dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz); +@@ -378,6 +469,20 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id) + etb_writel(t, 0, ETBR_CTRL); + etb_writel(t, 0x1000, ETBR_FORMATTERCTRL); + etb_lock(t); ++ mutex_unlock(&t->mutex); ++ ++ etb_miscdev.parent = &dev->dev; ++ ++ ret = misc_register(&etb_miscdev); ++ if (ret) ++ goto out_unmap; ++ ++ /* Get optional clock. Currently used to select clock source on omap3 */ ++ t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); ++ if (IS_ERR(t->emu_clk)) ++ dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); ++ else ++ clk_enable(t->emu_clk); + + dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n"); + +@@ -385,9 +490,12 @@ out: + return ret; + + out_unmap: ++ mutex_lock(&t->mutex); + iounmap(t->etb_regs); ++ t->etb_regs = NULL; + + out_release: ++ mutex_unlock(&t->mutex); + amba_release_regions(dev); + + return ret; +@@ -400,8 +508,10 @@ static int etb_remove(struct amba_device *dev) + iounmap(t->etb_regs); + t->etb_regs = NULL; + +- clk_disable(t->emu_clk); +- clk_put(t->emu_clk); ++ if (!IS_ERR(t->emu_clk)) { ++ clk_disable(t->emu_clk); ++ clk_put(t->emu_clk); ++ } + + amba_release_regions(dev); + +@@ -445,7 +555,10 @@ static ssize_t trace_running_store(struct kobject *kobj, + return -EINVAL; + + mutex_lock(&tracer.mutex); +- ret = value ? trace_start(&tracer) : trace_stop(&tracer); ++ if (!tracer.etb_regs) ++ ret = -ENODEV; ++ else ++ ret = value ? trace_start(&tracer) : trace_stop(&tracer); + mutex_unlock(&tracer.mutex); + + return ret ? : n; +@@ -460,36 +573,50 @@ static ssize_t trace_info_show(struct kobject *kobj, + { + u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st; + int datalen; ++ int id; ++ int ret; + +- etb_unlock(&tracer); +- datalen = etb_getdatalen(&tracer); +- etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); +- etb_ra = etb_readl(&tracer, ETBR_READADDR); +- etb_st = etb_readl(&tracer, ETBR_STATUS); +- etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); +- etb_lock(&tracer); +- +- etm_unlock(&tracer); +- etm_ctrl = etm_readl(&tracer, ETMR_CTRL); +- etm_st = etm_readl(&tracer, ETMR_STATUS); +- etm_lock(&tracer); ++ mutex_lock(&tracer.mutex); ++ if (tracer.etb_regs) { ++ etb_unlock(&tracer); ++ datalen = etb_getdatalen(&tracer); ++ etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); ++ etb_ra = etb_readl(&tracer, ETBR_READADDR); ++ etb_st = etb_readl(&tracer, ETBR_STATUS); ++ etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); ++ etb_lock(&tracer); ++ } else { ++ etb_wa = etb_ra = etb_st = etb_fc = ~0; ++ datalen = -1; ++ } + +- return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" ++ ret = sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" + "ETBR_WRITEADDR:\t%08x\n" + "ETBR_READADDR:\t%08x\n" + "ETBR_STATUS:\t%08x\n" +- "ETBR_FORMATTERCTRL:\t%08x\n" +- "ETMR_CTRL:\t%08x\n" +- "ETMR_STATUS:\t%08x\n", ++ "ETBR_FORMATTERCTRL:\t%08x\n", + datalen, + tracer.ncmppairs, + etb_wa, + etb_ra, + etb_st, +- etb_fc, ++ etb_fc ++ ); ++ ++ for (id = 0; id < tracer.etm_regs_count; id++) { ++ etm_unlock(&tracer, id); ++ etm_ctrl = etm_readl(&tracer, id, ETMR_CTRL); ++ etm_st = etm_readl(&tracer, id, ETMR_STATUS); ++ etm_lock(&tracer, id); ++ ret += sprintf(buf + ret, "ETMR_CTRL:\t%08x\n" ++ "ETMR_STATUS:\t%08x\n", + etm_ctrl, + etm_st + ); ++ } ++ mutex_unlock(&tracer.mutex); ++ ++ return ret; + } + + static struct kobj_attribute trace_info_attr = +@@ -528,42 +655,260 @@ static ssize_t trace_mode_store(struct kobject *kobj, + static struct kobj_attribute trace_mode_attr = + __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store); + ++static ssize_t trace_contextid_size_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ /* 0: No context id tracing, 1: One byte, 2: Two bytes, 3: Four bytes */ ++ return sprintf(buf, "%d\n", (1 << tracer.etm_contextid_size) >> 1); ++} ++ ++static ssize_t trace_contextid_size_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ unsigned int contextid_size; ++ ++ if (sscanf(buf, "%u", &contextid_size) != 1) ++ return -EINVAL; ++ ++ if (contextid_size == 3 || contextid_size > 4) ++ return -EINVAL; ++ ++ mutex_lock(&tracer.mutex); ++ tracer.etm_contextid_size = fls(contextid_size); ++ mutex_unlock(&tracer.mutex); ++ ++ return n; ++} ++ ++static struct kobj_attribute trace_contextid_size_attr = ++ __ATTR(trace_contextid_size, 0644, ++ trace_contextid_size_show, trace_contextid_size_store); ++ ++static ssize_t trace_branch_output_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_BRANCHOUTPUT)); ++} ++ ++static ssize_t trace_branch_output_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ unsigned int branch_output; ++ ++ if (sscanf(buf, "%u", &branch_output) != 1) ++ return -EINVAL; ++ ++ mutex_lock(&tracer.mutex); ++ if (branch_output) { ++ tracer.flags |= TRACER_BRANCHOUTPUT; ++ /* Branch broadcasting is incompatible with the return stack */ ++ tracer.flags &= ~TRACER_RETURN_STACK; ++ } else { ++ tracer.flags &= ~TRACER_BRANCHOUTPUT; ++ } ++ mutex_unlock(&tracer.mutex); ++ ++ return n; ++} ++ ++static struct kobj_attribute trace_branch_output_attr = ++ __ATTR(trace_branch_output, 0644, ++ trace_branch_output_show, trace_branch_output_store); ++ ++static ssize_t trace_return_stack_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_RETURN_STACK)); ++} ++ ++static ssize_t trace_return_stack_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ unsigned int return_stack; ++ ++ if (sscanf(buf, "%u", &return_stack) != 1) ++ return -EINVAL; ++ ++ mutex_lock(&tracer.mutex); ++ if (return_stack) { ++ tracer.flags |= TRACER_RETURN_STACK; ++ /* Return stack is incompatible with branch broadcasting */ ++ tracer.flags &= ~TRACER_BRANCHOUTPUT; ++ } else { ++ tracer.flags &= ~TRACER_RETURN_STACK; ++ } ++ mutex_unlock(&tracer.mutex); ++ ++ return n; ++} ++ ++static struct kobj_attribute trace_return_stack_attr = ++ __ATTR(trace_return_stack, 0644, ++ trace_return_stack_show, trace_return_stack_store); ++ ++static ssize_t trace_timestamp_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_TIMESTAMP)); ++} ++ ++static ssize_t trace_timestamp_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ unsigned int timestamp; ++ ++ if (sscanf(buf, "%u", ×tamp) != 1) ++ return -EINVAL; ++ ++ mutex_lock(&tracer.mutex); ++ if (timestamp) ++ tracer.flags |= TRACER_TIMESTAMP; ++ else ++ tracer.flags &= ~TRACER_TIMESTAMP; ++ mutex_unlock(&tracer.mutex); ++ ++ return n; ++} ++ ++static struct kobj_attribute trace_timestamp_attr = ++ __ATTR(trace_timestamp, 0644, ++ trace_timestamp_show, trace_timestamp_store); ++ ++static ssize_t trace_range_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%08lx %08lx\n", ++ tracer.range_start, tracer.range_end); ++} ++ ++static ssize_t trace_range_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ unsigned long range_start, range_end; ++ ++ if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2) ++ return -EINVAL; ++ ++ mutex_lock(&tracer.mutex); ++ tracer.range_start = range_start; ++ tracer.range_end = range_end; ++ mutex_unlock(&tracer.mutex); ++ ++ return n; ++} ++ ++ ++static struct kobj_attribute trace_range_attr = ++ __ATTR(trace_range, 0644, trace_range_show, trace_range_store); ++ ++static ssize_t trace_data_range_show(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ char *buf) ++{ ++ unsigned long range_start; ++ u64 range_end; ++ mutex_lock(&tracer.mutex); ++ range_start = tracer.data_range_start; ++ range_end = tracer.data_range_end; ++ if (!range_end && (tracer.flags & TRACER_TRACE_DATA)) ++ range_end = 0x100000000ULL; ++ mutex_unlock(&tracer.mutex); ++ return sprintf(buf, "%08lx %08llx\n", range_start, range_end); ++} ++ ++static ssize_t trace_data_range_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ unsigned long range_start; ++ u64 range_end; ++ ++ if (sscanf(buf, "%lx %llx", &range_start, &range_end) != 2) ++ return -EINVAL; ++ ++ mutex_lock(&tracer.mutex); ++ tracer.data_range_start = range_start; ++ tracer.data_range_end = (unsigned long)range_end; ++ if (range_end) ++ tracer.flags |= TRACER_TRACE_DATA; ++ else ++ tracer.flags &= ~TRACER_TRACE_DATA; ++ mutex_unlock(&tracer.mutex); ++ ++ return n; ++} ++ ++ ++static struct kobj_attribute trace_data_range_attr = ++ __ATTR(trace_data_range, 0644, ++ trace_data_range_show, trace_data_range_store); ++ + static int etm_probe(struct amba_device *dev, const struct amba_id *id) + { + struct tracectx *t = &tracer; + int ret = 0; ++ void __iomem **new_regs; ++ int new_count; ++ u32 etmccr; ++ u32 etmidr; ++ u32 etmccer = 0; ++ u8 etm_version = 0; ++ ++ mutex_lock(&t->mutex); ++ new_count = t->etm_regs_count + 1; ++ new_regs = krealloc(t->etm_regs, ++ sizeof(t->etm_regs[0]) * new_count, GFP_KERNEL); + +- if (t->etm_regs) { +- dev_dbg(&dev->dev, "ETM already initialized\n"); +- ret = -EBUSY; ++ if (!new_regs) { ++ dev_dbg(&dev->dev, "Failed to allocate ETM register array\n"); ++ ret = -ENOMEM; + goto out; + } ++ t->etm_regs = new_regs; + + ret = amba_request_regions(dev, NULL); + if (ret) + goto out; + +- t->etm_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); +- if (!t->etm_regs) { ++ t->etm_regs[t->etm_regs_count] = ++ ioremap_nocache(dev->res.start, resource_size(&dev->res)); ++ if (!t->etm_regs[t->etm_regs_count]) { + ret = -ENOMEM; + goto out_release; + } + +- amba_set_drvdata(dev, t); ++ amba_set_drvdata(dev, t->etm_regs[t->etm_regs_count]); + +- mutex_init(&t->mutex); +- t->dev = &dev->dev; +- t->flags = TRACER_CYCLE_ACC; ++ t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA | TRACER_BRANCHOUTPUT; + t->etm_portsz = 1; ++ t->etm_contextid_size = 3; + +- etm_unlock(t); +- (void)etm_readl(t, ETMMR_PDSR); ++ etm_unlock(t, t->etm_regs_count); ++ (void)etm_readl(t, t->etm_regs_count, ETMMR_PDSR); + /* dummy first read */ +- (void)etm_readl(&tracer, ETMMR_OSSRR); +- +- t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf; +- etm_writel(t, 0x440, ETMR_CTRL); +- etm_lock(t); ++ (void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR); ++ ++ etmccr = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE); ++ t->ncmppairs = etmccr & 0xf; ++ if (etmccr & ETMCCR_ETMIDR_PRESENT) { ++ etmidr = etm_readl(t, t->etm_regs_count, ETMR_ID); ++ etm_version = ETMIDR_VERSION(etmidr); ++ if (etm_version >= ETMIDR_VERSION_3_1) ++ etmccer = etm_readl(t, t->etm_regs_count, ETMR_CCE); ++ } ++ etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL); ++ etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR); ++ etm_lock(t, t->etm_regs_count); + + ret = sysfs_create_file(&dev->dev.kobj, + &trace_running_attr.attr); +@@ -579,32 +924,97 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) + if (ret) + dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n"); + +- dev_dbg(t->dev, "ETM AMBA driver initialized.\n"); ++ ret = sysfs_create_file(&dev->dev.kobj, ++ &trace_contextid_size_attr.attr); ++ if (ret) ++ dev_dbg(&dev->dev, ++ "Failed to create trace_contextid_size in sysfs\n"); ++ ++ ret = sysfs_create_file(&dev->dev.kobj, ++ &trace_branch_output_attr.attr); ++ if (ret) ++ dev_dbg(&dev->dev, ++ "Failed to create trace_branch_output in sysfs\n"); ++ ++ if (etmccer & ETMCCER_RETURN_STACK_IMPLEMENTED) { ++ ret = sysfs_create_file(&dev->dev.kobj, ++ &trace_return_stack_attr.attr); ++ if (ret) ++ dev_dbg(&dev->dev, ++ "Failed to create trace_return_stack in sysfs\n"); ++ } ++ ++ if (etmccer & ETMCCER_TIMESTAMPING_IMPLEMENTED) { ++ ret = sysfs_create_file(&dev->dev.kobj, ++ &trace_timestamp_attr.attr); ++ if (ret) ++ dev_dbg(&dev->dev, ++ "Failed to create trace_timestamp in sysfs\n"); ++ } ++ ++ ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr); ++ if (ret) ++ dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n"); ++ ++ if (etm_version < ETMIDR_VERSION_PFT_1_0) { ++ ret = sysfs_create_file(&dev->dev.kobj, ++ &trace_data_range_attr.attr); ++ if (ret) ++ dev_dbg(&dev->dev, ++ "Failed to create trace_data_range in sysfs\n"); ++ } else { ++ tracer.flags &= ~TRACER_TRACE_DATA; ++ } ++ ++ dev_dbg(&dev->dev, "ETM AMBA driver initialized.\n"); ++ ++ /* Enable formatter if there are multiple trace sources */ ++ if (new_count > 1) ++ t->etb_fc = ETBFF_ENFCONT | ETBFF_ENFTC; ++ ++ t->etm_regs_count = new_count; + + out: ++ mutex_unlock(&t->mutex); + return ret; + + out_unmap: +- iounmap(t->etm_regs); ++ iounmap(t->etm_regs[t->etm_regs_count]); + + out_release: + amba_release_regions(dev); + ++ mutex_unlock(&t->mutex); + return ret; + } + + static int etm_remove(struct amba_device *dev) + { +- struct tracectx *t = amba_get_drvdata(dev); +- +- iounmap(t->etm_regs); +- t->etm_regs = NULL; +- +- amba_release_regions(dev); ++ int i; ++ struct tracectx *t = &tracer; ++ void __iomem *etm_regs = amba_get_drvdata(dev); + + sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr); + sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr); + sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr); ++ sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr); ++ sysfs_remove_file(&dev->dev.kobj, &trace_data_range_attr.attr); ++ ++ mutex_lock(&t->mutex); ++ for (i = 0; i < t->etm_regs_count; i++) ++ if (t->etm_regs[i] == etm_regs) ++ break; ++ for (; i < t->etm_regs_count - 1; i++) ++ t->etm_regs[i] = t->etm_regs[i + 1]; ++ t->etm_regs_count--; ++ if (!t->etm_regs_count) { ++ kfree(t->etm_regs); ++ t->etm_regs = NULL; ++ } ++ mutex_unlock(&t->mutex); ++ ++ iounmap(etm_regs); ++ amba_release_regions(dev); + + return 0; + } +@@ -614,6 +1024,10 @@ static struct amba_id etm_ids[] = { + .id = 0x0003b921, + .mask = 0x0007ffff, + }, ++ { ++ .id = 0x0003b950, ++ .mask = 0x0007ffff, ++ }, + { 0, 0 }, + }; + +@@ -631,6 +1045,8 @@ static int __init etm_init(void) + { + int retval; + ++ mutex_init(&tracer.mutex); ++ + retval = amba_driver_register(&etb_driver); + if (retval) { + printk(KERN_ERR "Failed to register etb\n"); +diff --git a/arch/arm/kernel/hibernate.c b/arch/arm/kernel/hibernate.c +index cfb354f..785c461 100644 +--- a/arch/arm/kernel/hibernate.c ++++ b/arch/arm/kernel/hibernate.c +@@ -24,6 +24,7 @@ + #include <asm/sections.h> + #include "reboot.h" + ++#ifndef CONFIG_HISI_SNAPSHOT_BOOT + int pfn_is_nosave(unsigned long pfn) + { + unsigned long nosave_begin_pfn = virt_to_pfn(&__nosave_begin); +@@ -42,7 +43,9 @@ void notrace restore_processor_state(void) + { + local_fiq_enable(); + } ++#endif + ++#ifndef CONFIG_HISI_SNAPSHOT_BOOT + /* + * Snapshot kernel memory and reset the system. + * +@@ -105,3 +108,4 @@ int swsusp_arch_resume(void) + resume_stack + ARRAY_SIZE(resume_stack)); + return 0; + } ++#endif +diff --git a/arch/arm/kernel/io.c b/arch/arm/kernel/io.c +index 9203cf8..9d5a96f 100644 +--- a/arch/arm/kernel/io.c ++++ b/arch/arm/kernel/io.c +@@ -79,6 +79,8 @@ void _memset_io(volatile void __iomem *dst, int c, size_t count) + dst++; + } + } ++DEFINE_RAW_SPINLOCK(hisilcon_lock); ++EXPORT_SYMBOL(hisilcon_lock); + + EXPORT_SYMBOL(_memcpy_fromio); + EXPORT_SYMBOL(_memcpy_toio); +diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c +index a74b53c..2412558 100644 +--- a/arch/arm/kernel/kgdb.c ++++ b/arch/arm/kernel/kgdb.c +@@ -144,6 +144,8 @@ int kgdb_arch_handle_exception(int exception_vector, int signo, + + static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr) + { ++ if (user_mode(regs)) ++ return -1; + kgdb_handle_exception(1, SIGTRAP, 0, regs); + + return 0; +@@ -151,6 +153,8 @@ static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr) + + static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr) + { ++ if (user_mode(regs)) ++ return -1; + compiled_break = 1; + kgdb_handle_exception(1, SIGTRAP, 0, regs); + +diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c +index ecefea4..617ec4a 100644 +--- a/arch/arm/kernel/process.c ++++ b/arch/arm/kernel/process.c +@@ -32,6 +32,7 @@ + #include <linux/hw_breakpoint.h> + #include <linux/leds.h> + #include <linux/reboot.h> ++#include <linux/console.h> + + #include <asm/cacheflush.h> + #include <asm/idmap.h> +@@ -60,9 +61,46 @@ static const char *isa_modes[] __maybe_unused = { + "ARM" , "Thumb" , "Jazelle", "ThumbEE" + }; + ++#ifdef CONFIG_SMP ++void arch_trigger_all_cpu_backtrace(void) ++{ ++ smp_send_all_cpu_backtrace(); ++} ++#else ++void arch_trigger_all_cpu_backtrace(void) ++{ ++ dump_stack(); ++} ++#endif ++ + extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); + typedef void (*phys_reset_t)(unsigned long); + ++#ifdef CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART ++void arm_machine_flush_console(void) ++{ ++ printk("\n"); ++ pr_emerg("Restarting %s\n", linux_banner); ++ if (console_trylock()) { ++ console_unlock(); ++ return; ++ } ++ ++ mdelay(50); ++ ++ local_irq_disable(); ++ if (!console_trylock()) ++ pr_emerg("arm_restart: Console was locked! Busting\n"); ++ else ++ pr_emerg("arm_restart: Console was locked!\n"); ++ console_unlock(); ++} ++#else ++void arm_machine_flush_console(void) ++{ ++} ++#endif ++ + /* + * A temporary stack to use for CPU reset. This is static so that we + * don't clobber it with the identity mapping. When running with this +@@ -154,6 +192,7 @@ void arch_cpu_idle_prepare(void) + + void arch_cpu_idle_enter(void) + { ++ idle_notifier_call_chain(IDLE_START); + ledtrig_cpu(CPU_LED_IDLE_START); + #ifdef CONFIG_PL310_ERRATA_769419 + wmb(); +@@ -163,6 +202,7 @@ void arch_cpu_idle_enter(void) + void arch_cpu_idle_exit(void) + { + ledtrig_cpu(CPU_LED_IDLE_END); ++ idle_notifier_call_chain(IDLE_END); + } + + #ifdef CONFIG_HOTPLUG_CPU +@@ -183,6 +223,16 @@ void arch_cpu_idle_dead(void) + */ + void machine_shutdown(void) + { ++#ifdef CONFIG_SMP ++ /* ++ * Disable preemption so we're guaranteed to ++ * run to power off or reboot and prevent ++ * the possibility of switching to another ++ * thread that might wind up blocking on ++ * one of the stopped CPUs. ++ */ ++ preempt_disable(); ++#endif + disable_nonboot_cpus(); + } + +@@ -231,6 +281,11 @@ void machine_restart(char *cmd) + local_irq_disable(); + smp_send_stop(); + ++ ++ /* Flush the console to make sure all the relevant messages make it ++ * out to the console drivers */ ++ arm_machine_flush_console(); ++ + if (arm_pm_restart) + arm_pm_restart(reboot_mode, cmd); + else +@@ -245,6 +300,77 @@ void machine_restart(char *cmd) + while (1); + } + ++/* ++ * dump a block of kernel memory from around the given address ++ */ ++static void show_data(unsigned long addr, int nbytes, const char *name) ++{ ++ int i, j; ++ int nlines; ++ u32 *p; ++ ++ /* ++ * don't attempt to dump non-kernel addresses or ++ * values that are probably just small negative numbers ++ */ ++ if (addr < PAGE_OFFSET || addr > -256UL) ++ return; ++ ++ printk("\n%s: %#lx:\n", name, addr); ++ ++ /* ++ * round address down to a 32 bit boundary ++ * and always dump a multiple of 32 bytes ++ */ ++ p = (u32 *)(addr & ~(sizeof(u32) - 1)); ++ nbytes += (addr & (sizeof(u32) - 1)); ++ nlines = (nbytes + 31) / 32; ++ ++ ++ for (i = 0; i < nlines; i++) { ++ /* ++ * just display low 16 bits of address to keep ++ * each line of the dump < 80 characters ++ */ ++ printk("%04lx ", (unsigned long)p & 0xffff); ++ for (j = 0; j < 8; j++) { ++ u32 data; ++ if (probe_kernel_address(p, data)) { ++ printk(" ********"); ++ } else { ++ printk(" %08x", data); ++ } ++ ++p; ++ } ++ printk("\n"); ++ } ++} ++ ++static void show_extra_register_data(struct pt_regs *regs, int nbytes) ++{ ++ mm_segment_t fs; ++ ++ fs = get_fs(); ++ set_fs(KERNEL_DS); ++ show_data(regs->ARM_pc - nbytes, nbytes * 2, "PC"); ++ show_data(regs->ARM_lr - nbytes, nbytes * 2, "LR"); ++ show_data(regs->ARM_sp - nbytes, nbytes * 2, "SP"); ++ show_data(regs->ARM_ip - nbytes, nbytes * 2, "IP"); ++ show_data(regs->ARM_fp - nbytes, nbytes * 2, "FP"); ++ show_data(regs->ARM_r0 - nbytes, nbytes * 2, "R0"); ++ show_data(regs->ARM_r1 - nbytes, nbytes * 2, "R1"); ++ show_data(regs->ARM_r2 - nbytes, nbytes * 2, "R2"); ++ show_data(regs->ARM_r3 - nbytes, nbytes * 2, "R3"); ++ show_data(regs->ARM_r4 - nbytes, nbytes * 2, "R4"); ++ show_data(regs->ARM_r5 - nbytes, nbytes * 2, "R5"); ++ show_data(regs->ARM_r6 - nbytes, nbytes * 2, "R6"); ++ show_data(regs->ARM_r7 - nbytes, nbytes * 2, "R7"); ++ show_data(regs->ARM_r8 - nbytes, nbytes * 2, "R8"); ++ show_data(regs->ARM_r9 - nbytes, nbytes * 2, "R9"); ++ show_data(regs->ARM_r10 - nbytes, nbytes * 2, "R10"); ++ set_fs(fs); ++} ++ + void __show_regs(struct pt_regs *regs) + { + unsigned long flags; +@@ -306,6 +432,8 @@ void __show_regs(struct pt_regs *regs) + printk("Control: %08x%s\n", ctrl, buf); + } + #endif ++ ++ show_extra_register_data(regs, 128); + } + + void show_regs(struct pt_regs * regs) +diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c +index a8e32aa..9ce02e6 100644 +--- a/arch/arm/kernel/smp.c ++++ b/arch/arm/kernel/smp.c +@@ -72,6 +72,7 @@ enum ipi_msg_type { + IPI_CPU_STOP, + IPI_IRQ_WORK, + IPI_COMPLETION, ++ IPI_CPU_BACKTRACE, + }; + + static DECLARE_COMPLETION(cpu_running); +@@ -456,6 +457,7 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = { + S(IPI_CPU_STOP, "CPU stop interrupts"), + S(IPI_IRQ_WORK, "IRQ work interrupts"), + S(IPI_COMPLETION, "completion interrupts"), ++ S(IPI_CPU_BACKTRACE, "CPU backtrace"), + }; + + static void smp_cross_call(const struct cpumask *target, unsigned int ipinr) +@@ -557,6 +559,58 @@ static void ipi_complete(unsigned int cpu) + complete(per_cpu(cpu_completion, cpu)); + } + ++static cpumask_t backtrace_mask; ++static DEFINE_RAW_SPINLOCK(backtrace_lock); ++ ++/* "in progress" flag of arch_trigger_all_cpu_backtrace */ ++static unsigned long backtrace_flag; ++ ++void smp_send_all_cpu_backtrace(void) ++{ ++ unsigned int this_cpu = smp_processor_id(); ++ int i; ++ ++ if (test_and_set_bit(0, &backtrace_flag)) ++ /* ++ * If there is already a trigger_all_cpu_backtrace() in progress ++ * (backtrace_flag == 1), don't output double cpu dump infos. ++ */ ++ return; ++ ++ cpumask_copy(&backtrace_mask, cpu_online_mask); ++ cpu_clear(this_cpu, backtrace_mask); ++ ++ pr_info("Backtrace for cpu %d (current):\n", this_cpu); ++ dump_stack(); ++ ++ pr_info("\nsending IPI to all other CPUs:\n"); ++ smp_cross_call(&backtrace_mask, IPI_CPU_BACKTRACE); ++ ++ /* Wait for up to 10 seconds for all other CPUs to do the backtrace */ ++ for (i = 0; i < 10 * 1000; i++) { ++ if (cpumask_empty(&backtrace_mask)) ++ break; ++ mdelay(1); ++ } ++ ++ clear_bit(0, &backtrace_flag); ++ smp_mb__after_atomic(); ++} ++ ++/* ++ * ipi_cpu_backtrace - handle IPI from smp_send_all_cpu_backtrace() ++ */ ++static void ipi_cpu_backtrace(unsigned int cpu, struct pt_regs *regs) ++{ ++ if (cpu_isset(cpu, backtrace_mask)) { ++ raw_spin_lock(&backtrace_lock); ++ pr_warning("IPI backtrace for cpu %d\n", cpu); ++ show_regs(regs); ++ raw_spin_unlock(&backtrace_lock); ++ cpu_clear(cpu, backtrace_mask); ++ } ++} ++ + /* + * Main handler for inter-processor interrupts + */ +@@ -623,6 +677,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs) + irq_exit(); + break; + ++ case IPI_CPU_BACKTRACE: ++ ipi_cpu_backtrace(cpu, regs); ++ break; ++ + default: + printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n", + cpu, ipinr); +diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c +index 89cfdd6..32782d2 100644 +--- a/arch/arm/kernel/topology.c ++++ b/arch/arm/kernel/topology.c +@@ -23,6 +23,7 @@ + #include <linux/slab.h> + + #include <asm/cputype.h> ++#include <asm/smp_plat.h> + #include <asm/topology.h> + + /* +@@ -290,6 +291,142 @@ static struct sched_domain_topology_level arm_topology[] = { + }; + + /* ++ * cluster_to_logical_mask - return cpu logical mask of CPUs in a cluster ++ * @socket_id: cluster HW identifier ++ * @cluster_mask: the cpumask location to be initialized, modified by the ++ * function only if return value == 0 ++ * ++ * Return: ++ * ++ * 0 on success ++ * -EINVAL if cluster_mask is NULL or there is no record matching socket_id ++ */ ++int cluster_to_logical_mask(unsigned int socket_id, cpumask_t *cluster_mask) ++{ ++ int cpu; ++ ++ if (!cluster_mask) ++ return -EINVAL; ++ ++ for_each_online_cpu(cpu) ++ if (socket_id == topology_physical_package_id(cpu)) { ++ cpumask_copy(cluster_mask, topology_core_cpumask(cpu)); ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++ ++#ifdef CONFIG_SCHED_HMP ++ ++static const char * const little_cores[] = { ++ "arm,cortex-a7", ++ NULL, ++}; ++ ++static bool is_little_cpu(struct device_node *cn) ++{ ++ const char * const *lc; ++ ++ for (lc = little_cores; *lc; lc++) ++ if (of_device_is_compatible(cn, *lc)) ++ return true; ++ return false; ++} ++ ++void __init arch_get_fast_and_slow_cpus(struct cpumask *fast, ++ struct cpumask *slow) ++{ ++ struct device_node *cn = NULL; ++ int cpu; ++ ++ cpumask_clear(fast); ++ cpumask_clear(slow); ++ ++ /* ++ * Use the config options if they are given. This helps testing ++ * HMP scheduling on systems without a big.LITTLE architecture. ++ */ ++ if (strlen(CONFIG_HMP_FAST_CPU_MASK) ++ && strlen(CONFIG_HMP_SLOW_CPU_MASK)) { ++ if (cpulist_parse(CONFIG_HMP_FAST_CPU_MASK, fast)) ++ WARN(1, "Failed to parse HMP fast cpu mask!\n"); ++ if (cpulist_parse(CONFIG_HMP_SLOW_CPU_MASK, slow)) ++ WARN(1, "Failed to parse HMP slow cpu mask!\n"); ++ return; ++ } ++ ++ /* ++ * Else, parse device tree for little cores. ++ */ ++ while ((cn = of_find_node_by_type(cn, "cpu"))) { ++ ++ const u32 *mpidr; ++ int len; ++ ++ mpidr = of_get_property(cn, "reg", &len); ++ if (!mpidr || len != 4) { ++ pr_err("* %s missing reg property\n", cn->full_name); ++ continue; ++ } ++ ++ cpu = get_logical_index(be32_to_cpup(mpidr)); ++ if (cpu == -EINVAL) { ++ pr_err("couldn't get logical index for mpidr %x\n", ++ be32_to_cpup(mpidr)); ++ break; ++ } ++ ++ if (is_little_cpu(cn)) ++ cpumask_set_cpu(cpu, slow); ++ else ++ cpumask_set_cpu(cpu, fast); ++ } ++ ++ if (!cpumask_empty(fast) && !cpumask_empty(slow)) ++ return; ++ ++ /* ++ * We didn't find both big and little cores so let's call all cores ++ * fast as this will keep the system running, with all cores being ++ * treated equal. ++ */ ++ cpumask_setall(fast); ++ cpumask_clear(slow); ++} ++ ++void __init arch_get_hmp_domains(struct list_head *hmp_domains_list) ++{ ++ struct cpumask hmp_fast_cpu_mask; ++ struct cpumask hmp_slow_cpu_mask; ++ struct hmp_domain *domain; ++ ++ arch_get_fast_and_slow_cpus(&hmp_fast_cpu_mask, &hmp_slow_cpu_mask); ++ ++ /* ++ * Initialize hmp_domains ++ * Must be ordered with respect to compute capacity. ++ * Fastest domain at head of list. ++ */ ++ if (!cpumask_empty(&hmp_slow_cpu_mask)) { ++ domain = (struct hmp_domain *) ++ kmalloc(sizeof(struct hmp_domain), GFP_KERNEL); ++ cpumask_copy(&domain->possible_cpus, &hmp_slow_cpu_mask); ++ cpumask_and(&domain->cpus, cpu_online_mask, ++ &domain->possible_cpus); ++ list_add(&domain->hmp_domains, hmp_domains_list); ++ } ++ domain = (struct hmp_domain *) ++ kmalloc(sizeof(struct hmp_domain), GFP_KERNEL); ++ cpumask_copy(&domain->possible_cpus, &hmp_fast_cpu_mask); ++ cpumask_and(&domain->cpus, cpu_online_mask, &domain->possible_cpus); ++ list_add(&domain->hmp_domains, hmp_domains_list); ++} ++#endif /* CONFIG_SCHED_HMP */ ++ ++ ++/* + * init_cpu_topology is called at boot when only one cpu is running + * which prevent simultaneous write access to cpu_topology array + */ +diff --git a/arch/arm/lib/memcpy.S b/arch/arm/lib/memcpy.S +index a9b9e22..3d71892 100644 +--- a/arch/arm/lib/memcpy.S ++++ b/arch/arm/lib/memcpy.S +@@ -55,7 +55,6 @@ + .text + + /* Prototype: void *memcpy(void *dest, const void *src, size_t n); */ +- + ENTRY(memcpy) + + #include "copy_template.S" +diff --git a/arch/arm/mach-hisi/Kconfig b/arch/arm/mach-hisi/Kconfig +index cd19433..cd5b7dc 100644 +--- a/arch/arm/mach-hisi/Kconfig ++++ b/arch/arm/mach-hisi/Kconfig +@@ -1,8 +1,7 @@ + config ARCH_HISI + bool "Hisilicon SoC Support" +- depends on ARCH_MULTI_V7 + select ARM_AMBA +- select ARM_GIC ++ select ARM_GIC if ARCH_MULTI_V7 + select ARM_TIMER_SP804 + select POWER_RESET + select POWER_RESET_HISI +@@ -40,6 +39,124 @@ config ARCH_HIX5HD2 + select PINCTRL_SINGLE + help + Support for Hisilicon HIX5HD2 SoC family ++ ++config ARCH_HI3519 ++ bool "Hisilicon Hi3519 Cortex-a7.Cortex-a17 family" if ARCH_MULTI_V7 ++ select HAVE_ARM_ARCH_TIMER ++ select ARM_CCI ++ select ARCH_HAS_RESET_CONTROLLER ++ select RESET_CONTROLLER ++ select PM_OPP ++ select PMC if SMP ++ select NEED_MACH_IO_H if PCI ++ help ++ Support for Hisilicon Hi3519 Soc family ++ ++config ARCH_HI3519V101 ++ bool "Hisilicon Hi3519V101 Cortex-a7.Cortex-a17 family" if ARCH_MULTI_V7 ++ select HAVE_ARM_ARCH_TIMER ++ select ARM_CCI ++ select ARCH_HAS_RESET_CONTROLLER ++ select RESET_CONTROLLER ++ select PM_OPP ++ select PMC if SMP ++ select NEED_MACH_IO_H if PCI ++ help ++ Support for Hisilicon Hi3519V101 Soc family ++ ++config ARCH_HI3516AV200 ++ bool "Hisilicon Hi3516AV200 Cortex-a7.Cortex-a17 family" if ARCH_MULTI_V7 ++ select HAVE_ARM_ARCH_TIMER ++ select ARM_CCI ++ select ARCH_HAS_RESET_CONTROLLER ++ select RESET_CONTROLLER ++ select PM_OPP ++ select PMC if SMP ++ select NEED_MACH_IO_H if PCI ++ help ++ Support for Hisilicon Hi3516AV200 Soc family ++ ++config ARCH_HI3559 ++ bool "Hisilicon Hi3559 Cortex-a7.Cortex-a17 family" if ARCH_MULTI_V7 ++ select HAVE_ARM_ARCH_TIMER ++ select ARM_CCI ++ select ARCH_HAS_RESET_CONTROLLER ++ select RESET_CONTROLLER ++ select PM_OPP ++ select PMC if SMP ++ select NEED_MACH_IO_H if PCI ++ help ++ Support for Hisilicon Hi3559 Soc family ++ ++config ARCH_HI3556 ++ bool "Hisilicon Hi3556 Cortex-a7.Cortex-a17 family" if ARCH_MULTI_V7 ++ select HAVE_ARM_ARCH_TIMER ++ select ARM_CCI ++ select ARCH_HAS_RESET_CONTROLLER ++ select RESET_CONTROLLER ++ select PM_OPP ++ select PMC if SMP ++ select NEED_MACH_IO_H if PCI ++ help ++ Support for Hisilicon Hi3556 Soc family ++ ++ ++if ARCH_HI3519 || ARCH_HI3519V101 || ARCH_HI3559 || ARCH_HI3556 || ARCH_HI3516AV200 ++ ++config PMC ++ bool ++ help ++ support power control for Hi3519 or Hi3519V101 or Hi3516AV200 or HI3559 Cortex-a17 or HI3556 Cortex-a17 ++ ++endif ++ ++config ARCH_HI3516CV300 ++ bool "Hisilicon Hi3516cv300 arm926ej-s family" if ARCH_MULTI_V5 ++ depends on ARCH_MULTI_V5 ++ select ARM_VIC ++ select PINCTRL ++ select PINCTRL_SINGLE ++ help ++ Support for Hisilicon Hi3516cv300 Soc family ++ ++config ARCH_HI3536C ++ bool "Hisilicon Hi3536C Cortex-a7 family" if ARCH_MULTI_V7 ++ select HAVE_ARM_ARCH_TIMER ++ select ARM_GIC ++ select ARCH_HAS_RESET_CONTROLLER ++ select RESET_CONTROLLER ++ select PINCTRL ++ select PINCTRL_SINGLE ++ select HAVE_ARM_SCU if SMP ++ help ++ Support for Hisilicon Hi3536C Soc family ++ ++config ARCH_HI3531D ++ bool "Hisilicon Hi3531D Cortex-a9 family" if ARCH_MULTI_V7 ++ select HAVE_ARM_ARCH_TIMER ++ select ARM_GIC ++ select ARCH_HAS_RESET_CONTROLLER ++ select RESET_CONTROLLER ++ select PINCTRL ++ select PINCTRL_SINGLE ++ select CACHE_L2X0 ++ select HAVE_ARM_SCU if SMP ++ select NEED_MACH_IO_H if PCI ++ help ++ Support for Hisilicon Hi3531D Soc family ++ ++config ARCH_HI3521D ++ bool "Hisilicon Hi3521D Cortex-a7 family" if ARCH_MULTI_V7 ++ select HAVE_ARM_ARCH_TIMER ++ select ARM_GIC ++ select ARCH_HAS_RESET_CONTROLLER ++ select RESET_CONTROLLER ++ select PINCTRL ++ select PINCTRL_SINGLE ++ select HAVE_ARM_SCU if SMP ++ help ++ Support for Hisilicon Hi3521D Soc family ++ + endmenu + + endif +diff --git a/arch/arm/mach-hisi/Makefile b/arch/arm/mach-hisi/Makefile +index 6b7b303..167c7b9 100644 +--- a/arch/arm/mach-hisi/Makefile ++++ b/arch/arm/mach-hisi/Makefile +@@ -6,4 +6,33 @@ CFLAGS_platmcpm.o := -march=armv7-a + + obj-y += hisilicon.o + obj-$(CONFIG_MCPM) += platmcpm.o ++ifdef CONFIG_ARCH_HI3519 ++obj-$(CONFIG_PMC) += pmc_hi3519.o ++endif ++ifdef CONFIG_ARCH_HI3519V101 ++obj-$(CONFIG_PMC) += pmc_hi3519v101.o ++endif ++ifdef CONFIG_ARCH_HI3516AV200 ++obj-$(CONFIG_PMC) += pmc_hi3516av200.o ++endif ++ifdef CONFIG_ARCH_HI3559 ++obj-$(CONFIG_PMC) += pmc_hi3559.o ++obj-$(CONFIG_PM) += pwr_hi3559.o ++endif ++ifdef CONFIG_ARCH_HI3556 ++obj-$(CONFIG_PMC) += pmc_hi3559.o ++obj-$(CONFIG_PM) += pwr_hi3559.o ++endif ++obj-$(CONFIG_ARCH_HI3536C) += pmc_hi3536c.o ++obj-$(CONFIG_ARCH_HI3531D) += pmc_hi3531d.o ++obj-$(CONFIG_ARCH_HI3521D) += pmc_hi3521d.o ++ + obj-$(CONFIG_SMP) += platsmp.o hotplug.o headsmp.o ++obj-$(CONFIG_PM) += pm.o ++obj-$(CONFIG_CACHE_L2X0) += l2cache.o ++obj-$(CONFIG_HISI_SNAPSHOT_BOOT) += fastboot/ ++ifdef CONFIG_ARCH_MULTI_V5 ++obj-$(CONFIG_HISI_SNAPSHOT_BOOT) += hibernate.o swsusp.o ++else ++obj-$(CONFIG_HISI_SNAPSHOT_BOOT) += hibernate.o swsusp.o cpu_helper_a7.o ++endif +diff --git a/arch/arm/mach-hisi/Makefile.boot b/arch/arm/mach-hisi/Makefile.boot +new file mode 100644 +index 0000000..a652afa +--- /dev/null ++++ b/arch/arm/mach-hisi/Makefile.boot +@@ -0,0 +1,18 @@ ++ifdef CONFIG_ARCH_HI3559 ++zreladdr-y := 0x88008000 ++else ++ifdef CONFIG_ARCH_HI3556 ++zreladdr-y := 0x83008000 ++else ++zreladdr-y := 0x80008000 ++endif ++ ++ifdef CONFIG_ARCH_HI3531D ++zreladdr-y := 0x40008000 ++endif ++ ++endif ++ ++params_phys-y := 0x00000100 ++initrd_phys-y := 0x00800000 ++#LOADADDR := 0x88000000 +diff --git a/arch/arm/mach-hisi/core.h b/arch/arm/mach-hisi/core.h +index 88b1f48..3ad3bec 100644 +--- a/arch/arm/mach-hisi/core.h ++++ b/arch/arm/mach-hisi/core.h +@@ -17,4 +17,38 @@ extern struct smp_operations hix5hd2_smp_ops; + extern void hix5hd2_set_cpu(int cpu, bool enable); + extern void hix5hd2_cpu_die(unsigned int cpu); + ++extern void hi3519_secondary_startup(void); ++extern void hi3519_cpu_die(unsigned int cpu); ++extern int hi3519_cpu_kill(unsigned int cpu); ++ ++extern void hi3516av200_secondary_startup(void); ++extern void hi3516av200_cpu_die(unsigned int cpu); ++extern int hi3516av200_cpu_kill(unsigned int cpu); ++ ++extern void hi3559_secondary_startup(void); ++extern void hi3559_cpu_die(unsigned int cpu); ++extern int hi3559_cpu_kill(unsigned int cpu); ++ ++extern void hi3536c_secondary_startup(void); ++extern void hi3536c_cpu_die(unsigned int cpu); ++extern int hi3536c_cpu_kill(unsigned int cpu); ++void hi3536c_scu_power_up(int cpu); ++ ++extern void hi3531d_secondary_startup(void); ++extern void hi3531d_cpu_die(unsigned int cpu); ++extern int hi3531d_cpu_kill(unsigned int cpu); ++void hi3531d_scu_power_up(int cpu); ++ ++extern void hi3521d_secondary_startup(void); ++extern void hi3521d_cpu_die(unsigned int cpu); ++extern int hi3521d_cpu_kill(unsigned int cpu); ++void hi3521d_scu_power_up(int cpu); ++ ++extern void hi_pmc_power_up(void); ++extern void hi_pmc_power_up_done(void); ++extern void hi_pmc_set_ac_inactive(void); ++extern void hi_pmc_power_down(void); ++#ifdef CONFIG_ARCH_HI3519 ++extern void hi_pmc_kill_cpu(unsigned int cpu); ++#endif + #endif +diff --git a/arch/arm/mach-hisi/cpu_helper_a7.S b/arch/arm/mach-hisi/cpu_helper_a7.S +new file mode 100644 +index 0000000..5783ef5 +--- /dev/null ++++ b/arch/arm/mach-hisi/cpu_helper_a7.S +@@ -0,0 +1,835 @@ ++#include <asm/memory.h> ++ ++ @ Aliases for mode encodings - do not change ++ .equ MODE_USR, 0x10 ++ .equ MODE_FIQ, 0x11 ++ .equ MODE_IRQ, 0x12 ++ .equ MODE_SVC, 0x13 ++ .equ MODE_MON, 0x16 @ A-profile (Security Extensions) only ++ .equ MODE_ABT, 0x17 ++ .equ MODE_UND, 0x1B ++ .equ MODE_SYS, 0x1F ++ .equ MODE_HYP, 0x1A ++ ++ .equ TTBCR_EAE, (1<<31) @ Are we using LPAE? ++ ++ .equ PFR0_THUMB_EE_SUPPORT, (1<<12) ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ ++ @ This function takes three arguments ++ @ r0: Destination start address (must be word aligned) ++ @ r1: Source start address (must be word aligned) ++ @ r2: Number of words to copy ++ @ Return value is updated destination pointer (first unwritten word) ++ ++ .global copy_words ++copy_words: ++ .func ++ push {r3} ++ cmp r2, #0 ++ beq 1f ++2: ++ ldr r3, [r1], #4 ++ str r3, [r0], #4 ++ subs r2, r2, #1 ++ bne 2b ++1: ++ pop {r3} ++ bx lr ++ ++ .endfunc ++ ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ .global save_performance_monitors ++ ++save_performance_monitors: ++ .func ++ push {r4, r8, r9, r10} ++ ++ mrc p15,0,r8,c9,c12,0 @ PMon: Control Register ++ bic r1,r8,#1 ++ mcr p15,0,r1,c9,c12,0 @ disable counter updates from here ++ isb @ 0b0 => PMCR<0> ++ mrc p15,0,r9,c9,c12,5 @ PMon: Event Counter Selection Register ++ mrc p15,0,r10,c9,c12,1 @ PMon: Count Enable Set Reg ++ stm r0!, {r8-r10} ++ mrc p15,0,r8,c9,c12,2 @ PMon: Count Enable Clear Register ++ mrc p15,0,r9,c9,c13,0 @ PMon: Cycle Counter Register ++ mrc p15,0,r10,c9,c12,3 @ PMon: Overflow flag Status Register ++ stm r0!, {r8-r10} ++ mrc p15,0,r8,c9,c14,1 @ PMon: Interrupt Enable Set Registern ++ mrc p15,0,r9,c9,c14,2 @ PMon: Interrupt Enable Clear Register ++ stm r0!, {r8-r9} ++ mrc p15,0,r8,c9,c12,0 @ Read PMon Control Register ++ ubfx r9,r8,#11,#5 @ extract # of event counters, N ++ tst r9, r9 ++ beq 1f ++ ++ mov r8,#0 ++0: ++ mcr p15,0,r8,c9,c12,5 @ PMon: select CounterN ++ isb ++ mrc p15,0,r3,c9,c13,1 @ PMon: save Event Type Register ++ mrc p15,0,r4,c9,c13,2 @ PMon: save Event Counter Register ++ stm r0!, {r3,r4} ++ add r8,r8,#1 @ increment index ++ @ cmps r8,r9 ++ cmp r8,r9 ++ bne 0b ++ ++1: ++ pop {r4, r8, r9, r10} ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global restore_performance_monitors ++restore_performance_monitors: ++ .func ++ push {r4-r6, r8-r10, lr} ++ @ NOTE: all counters disabled by PMCR ++ @<0> == 0 on reset ++ ldr r8,[r0] @ r8 = PMCR ++ add r1,r0,#20 @ r1 now points to saved PMOVSR ++ ldr r9,[r1] @ r9 = PMOVSR ++ ++ mvn r2,#0 @ generate Register of all 1's ++ mcr p15,0,r2,c9,c14,2 @ disable all counter related interrupts ++ mcr p15,0,r2,c9,c12,3 @ clear all overflow flags ++ isb ++ ++ ubfx r12,r8,#11,#5 @ extract # of event counters, N (0-31) ++ tst r12, r12 ++ beq 20f ++ ++ add r1,r0,#32 @ r1 now points to the 1st saved event ++ @counter ++ @@ Restore counters ++ mov r6,#0 ++10: ++ mcr p15,0,r6,c9,c12,5 @ PMon: select CounterN ++ isb ++ ldm r1!, {r3,r4} @ Read saved data ++ mcr p15,0,r3,c9,c13,1 @ PMon: restore Event Type Register ++ mcr p15,0,r4,c9,c13,2 @ PMon: restore Event Counter Register ++ add r6,r6,#1 @ increment index ++ @ cmps r6,r12 ++ cmp r6,r12 ++ bne 10b ++ ++20: ++ tst r9, #0x80000000 @ check for cycle count overflow flag ++ beq 40f ++ mcr p15,0,r2,c9,c13,0 @ set Cycle Counter to all 1's ++ isb ++ mov r3, #1 ++ mcr p15,0,r3,c9,c12,0 @ set the PMCR global enable bit ++ mov r3, #0x80000000 ++ mcr p15,0,r3,c9,c12,1 @ enable the Cycle Counter ++ isb ++ ++30: ++ mrc p15,0,r4,c9,c12,3 @ check cycle count overflow now set ++ movs r4,r4 @ test bit<31> ++ bpl 30b ++ mcr p15,0,r3,c9,c12,2 @ disable the Cycle Counter ++ ++40: ++ mov r1, #0 ++ mcr p15,0,r1,c9,c12,0 @ clear the PMCR global enable bit ++ isb ++ ++ @@ Restore left regs but PMCR ++ add r1,r0,#4 @ r1 now points to the PMSELR ++ ldm r1!,{r3,r4} ++ mcr p15,0,r3,c9,c12,5 @ PMon: Event Counter Selection Reg ++ mcr p15,0,r4,c9,c12,1 @ PMon: Count Enable Set Reg ++ ldm r1!, {r3,r4} ++ mcr p15,0,r4,c9,c13,0 @ PMon: Cycle Counter Register ++ ldm r1!,{r3,r4} ++ mcr p15,0,r3,c9,c14,2 @ PMon: Interrupt Enable Clear Reg ++ mcr p15,0,r4,c9,c14,1 @ PMon: Interrupt Enable Set Reg ++ ldr r3,[r1] ++ isb ++ ldr r0,[r0] ++ mcr p15,0,r0,c9,c12,0 @ restore the PM Control Register ++ isb ++ ++ pop {r4-r6, r8-r10, pc} ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global save_banked_registers ++save_banked_registers: ++ .func ++ ++ PUSH {r3, lr} ++ ++ mrs r2, CPSR @ save current mode ++ and r3, r2, #0x1f @ If we are in HYP mode then use the virt. ++ cmp r3, #MODE_HYP @ instructions to save the banked registers ++ beq save_in_hyp @ without changing the mode ++ ++ cps #MODE_SYS @ switch to System mode ++ str sp,[r0], #4 @ save the User SP ++ str lr,[r0], #4 @ save the User LR ++ cps #MODE_ABT @ switch to Abort mode ++ str sp,[r0], #4 @ save the current SP ++ mrs r3,SPSR ++ stm r0!,{r3,lr} @ save the current SPSR, LR ++ cps #MODE_UND @ switch to Undefined mode ++ str sp,[r0], #4 @ save the current SP ++ mrs r3,SPSR ++ stm r0!,{r3,lr} @ save the current SPSR, LR ++ cps #MODE_IRQ @ switch to IRQ mode ++ str sp,[r0], #4 @ save the current SP ++ mrs r3,SPSR ++ stm r0!,{r3,lr} @ save the current SPSR, LR ++ cps #MODE_FIQ @ switch to FIQ mode ++ str SP,[r0], #4 @ save the current SP ++ mrs r3,SPSR ++ stm r0!,{r8-r12,lr} @ save the current SPSR,r8-r12,LR ++ msr CPSR_cxsf, r2 @ switch back to original mode ++ ++ POP {r3, lr} ++ STR SP, [r0], #4 @ save the current SP ++ MRS r3, SPSR ++ STM r0!, {r3, r4-r12, LR} @ save the current SPSR, ++ @ r4-r12,LR ++ dsb ++ ++ bx lr ++ ++save_in_hyp : ++ @ mrs r1, SP_usr @ rewrite ++ cps #MODE_SYS @ switch to System mode ++ str sp,[r1], #4 @ save the User SP ++ stm r0!, {r1} ++ ++ @ mrs r1, SP_und @ rewrite ++ cps #MODE_UND @ switch to Undefined mode ++ str sp,[r1], #4 @ save the current SP ++ @ mrs r2, SPSR_und @ rewrite ++ cps #MODE_UND @ switch to Undefined mode ++ mrs r2,SPSR @ save the current SPSR ++ @ mrs r3, LR_und @ rewrite ++ cps #MODE_UND @ switch to Undefined mode ++ str lr,[r3], #4 @ save the current LR ++ stm r0!, {r1-r3} ++ ++ @ mrs r1, SP_abt @ rewrite ++ cps #MODE_ABT @ switch to Abort mode ++ str sp,[r1], #4 @ save the current SP ++ @ mrs r2, SPSR_abt @ rewrite ++ cps #MODE_ABT @ switch to Abort mode ++ mrs r2,SPSR @ save the current SPSR ++ @ mrs r3, LR_abt @ rewrite ++ str lr,[r3], #4 @ save the current LR ++ stm r0!, {r1-r3} ++ ++ @ mrs r1, SP_svc @ rewrite ++ cps #MODE_SVC @ switch to SVC mode ++ str sp,[r1], #4 @ save the current SP ++ @ mrs r2, SPSR_svc @ rewrite ++ cps #MODE_SVC @ switch to SVC mode ++ mrs r2,SPSR @ save the current SPSR ++ @ mrs r3, LR_svc @ rewrite ++ cps #MODE_SVC @ switch to SVC mode ++ str lr,[r3], #4 @ save the current LR ++ stm r0!, {r1-r3} ++ ++ @ mrs r1, SP_irq @ rewrite ++ cps #MODE_IRQ @ switch to IRQ mode ++ str sp,[r1], #4 @ save the current SP ++ @ mrs r2, SPSR_irq @ rewrite ++ cps #MODE_IRQ @ switch to IRQ mode ++ mrs r2,SPSR @ save the current SPSR ++ @ mrs r3, LR_irq @ rewrite ++ cps #MODE_IRQ @ switch to IRQ mode ++ str lr,[r3], #4 @ save the current LR ++ stm r0!, {r1-r3} ++ ++ @ mrs r1, SP_fiq @ rewrite ++ cps #MODE_FIQ @ switch to FIQ mode ++ str sp,[r1], #4 @ save the current SP ++ @ mrs r2, SPSR_fiq @ rewrite ++ cps #MODE_FIQ @ switch to FIQ mode ++ mrs r2,SPSR @ save the current SPSR ++ @ mrs r3, LR_fiq @ rewrite ++ cps #MODE_FIQ @ switch to FIQ mode ++ str lr,[r3], #4 @ save the current LR ++ stm r0!, {r1-r3} ++ ++ @ mrs r1, r8_fiq @ rewrite ++ @ mrs r2, r9_fiq @ rewrite ++ @ mrs r3, r10_fiq @ rewrite ++ @ stm r0!, {r1-r3} @ rewrite ++ @ mrs r1, r11_fiq @ rewrite ++ @ mrs r2, r12_fiq @ rewrite ++ @ stm r0!, {r1-r2} @ rewrite ++ cps #MODE_FIQ @ switch to FIQ mode ++ stm r0!,{r8-r12} @ save the current r8-r12 ++ ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global restore_banked_registers ++restore_banked_registers: ++ .func ++ mrs r2, CPSR @ save current mode ++ and r3, r2, #0x1f @ If we are in HYP mode then use the virt. ++ cmp r3, #MODE_HYP @ instructions to restore the banked registers ++ beq rest_in_hyp @ without changing the mode ++ ++ cps #MODE_SYS @ switch to System mode ++ ldr sp,[r0],#4 @ restore the User SP ++ ldr lr,[r0],#4 @ restore the User LR ++ cps #MODE_ABT @ switch to Abort mode ++ ldr sp,[r0],#4 @ restore the current SP ++ ldm r0!,{r3,lr} @ restore the current LR ++ msr SPSR_fsxc,r3 @ restore the current SPSR ++ cps #MODE_UND @ switch to Undefined mode ++ ldr sp,[r0],#4 @ restore the current SP ++ ldm r0!,{r3,lr} @ restore the current LR ++ msr SPSR_fsxc,r3 @ restore the current SPSR ++ cps #MODE_IRQ @ switch to IRQ mode ++ ldr sp,[r0],#4 @ restore the current SP ++ ldm r0!,{r3,lr} @ restore the current LR ++ msr SPSR_fsxc,r3 @ restore the current SPSR ++ cps #MODE_FIQ @ switch to FIQ mode ++ ldr sp,[r0],#4 @ restore the current SP ++ ldm r0!,{r8-r12,lr} @ restore the current r8-r12,LR ++ msr SPSR_fsxc,r4 @ restore the current SPSR ++ msr CPSR_cxsf, r2 @ switch back to original mode ++ ++ LDR SP, [r0], #4 @ restore the current SP ++ LDM r0!, {r3, r4-r12, LR} @ restore the current r4-r12,LR ++ MSR SPSR_fsxc, r3 @ restore the current SPSR ++ dsb ++0: ++ bx lr ++ ++rest_in_hyp: ++ ldm r0!, {r1} ++ @ msr SP_usr, r1 @ rewrite ++ cps #MODE_SYS @ switch to System mode ++ ldr sp,[r1],#4 @ restore the User SP ++ ++ ldm r0!, {r1-r3} ++ @ msr SP_und, r1 @ rewrite ++ cps #MODE_UND @ switch to Undefined mode ++ ldr sp,[r1],#4 @ restore the User SP ++ @ msr SPSR_und, r2 @ rewrite ++ cps #MODE_UND @ switch to Undefined mode ++ msr SPSR_fsxc,r2 @ restore the current SPSR ++ @ msr LR_und, r3 @ rewrite ++ cps #MODE_UND @ switch to Undefined mode ++ ldr lr,[r3],#4 @ restore the User LR ++ ++ ldm r0!, {r1-r3} ++ @ msr SP_abt, r1 @ rewrite ++ cps #MODE_ABT @ switch to Abort mode ++ ldr sp,[r1],#4 @ restore the User SP ++ @ msr SPSR_abt, r2 @ rewrite ++ cps #MODE_ABT @ switch to Abort mode ++ msr SPSR_fsxc,r2 @ restore the current SPSR ++ @ msr LR_abt, r3 @ rewrite ++ cps #MODE_ABT @ switch to Abort mode ++ ldr lr,[r3],#4 @ restore the User LR ++ ++ ldm r0!, {r1-r3} ++ @ msr SP_svc, r1 @ rewrite ++ cps #MODE_SVC @ switch to SVC mode ++ ldr sp,[r1],#4 @ restore the User SP ++ @ msr SPSR_svc, r2 @ rewrite ++ cps #MODE_SVC @ switch to SVC mode ++ msr SPSR_fsxc,r2 @ restore the current SPSR ++ @ msr LR_svc, r3 @ rewrite ++ cps #MODE_SVC @ switch to SVC mode ++ ldr lr,[r3],#4 @ restore the User LR ++ ++ ldm r0!, {r1-r3} ++ @ msr SP_irq, r1 @ rewrite ++ cps #MODE_IRQ @ switch to IRQ mode ++ ldr sp,[r1],#4 @ restore the User SP ++ @ msr SPSR_irq, r2 @ rewrite ++ cps #MODE_IRQ @ switch to IRQ mode ++ msr SPSR_fsxc,r2 @ restore the current SPSR ++ @ msr LR_irq, r3 @ rewrite ++ cps #MODE_IRQ @ switch to IRQ mode ++ ldr lr,[r3],#4 @ restore the User LR ++ ++ ldm r0!, {r1-r3} ++ @ msr SP_fiq, r1 @ rewrite ++ cps #MODE_FIQ @ switch to FIQ mode ++ ldr sp,[r1],#4 @ restore the User SP ++ @ msr SPSR_fiq, r2 @ rewrite ++ cps #MODE_FIQ @ switch to FIQ mode ++ msr SPSR_fsxc,r2 @ restore the current SPSR ++ @ msr LR_fiq, r3 @ rewrite ++ cps #MODE_FIQ @ switch to FIQ mode ++ ldr lr,[r3],#4 @ restore the User LR ++ ++ @ ldm r0!, {r1-r3} ++ @ msr r8_fiq, r1 @ rewrite ++ @ msr r9_fiq, r2 @ rewrite ++ @ msr r10_fiq, r3 @ rewrite ++ @ ldm r0!, {r1-r2} ++ @ msr r11_fiq, r1 @ rewrite ++ @ msr r12_fiq, r2 @ rewrite ++ cps #MODE_FIQ @ switch to FIQ mode ++ ldm r0!,{r8-r12} @ restore the current r8-r12 ++ ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global save_cp15 ++save_cp15: ++ .func ++ @ CSSELR Cache Size Selection Register ++ mrc p15,2,r3,c0,c0,0 ++ str r3,[r0], #4 ++ ++ @ IMPLEMENTATION DEFINED - proprietary features: ++ @ (CP15 register 15, TCM support, lockdown support, etc.) ++ ++ @ NOTE: IMP DEF registers might have save and restore order that relate ++ @ to other CP15 registers or logical grouping requirements and can ++ @ therefore occur at any point in this sequence. ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global restore_cp15 ++restore_cp15: ++ .func ++ @ CSSELR Cache Size Selection Register ++ ldr r3,[r0], #4 ++ mcr p15,2,r3,c0,c0,0 ++ ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global save_vfp ++save_vfp: ++ .func ++ @ FPU state save/restore. ++ @ FPSID,MVFR0 and MVFR1 don't get ++ @serialized/saved (Read Only). ++ mrc p15,0,r3,c1,c0,2 @ CPACR allows CP10 and CP11 access ++ ORR r2,r3,#0xF00000 ++ mcr p15,0,r2,c1,c0,2 ++ isb ++ mrc p15,0,r2,c1,c0,2 ++ and r2,r2,#0xF00000 ++ cmp r2,#0xF00000 ++ beq 0f ++ movs r2, #0 ++ b 2f ++ ++ @ Save configuration registers and enable. ++0: ++ FMRX r12,FPEXC @ vmrs r12,FPEXC ++ str r12,[r0],#4 @ Save the FPEXC ++ @ Enable FPU access to ++ @ save/restore the other registers. ++ ldr r2,=0x40000000 ++ FMXR FPEXC,r2 @ vmsr FPEXC,r2 ++ FMRX r2,FPSCR @ vmrs r2,FPSCR ++ str r2,[r0],#4 @ Save the FPSCR ++ @ Store the VFP-D16 registers. ++ vstm r0!, {D0-D15} ++ @ Check for Advanced SIMD/VFP-D32 support ++ FMRX r2,MVFR0 @ vmrs r2,MVFR0 ++ and r2,r2,#0xF @ extract the A_SIMD bitfield ++ cmp r2, #0x2 ++ blt 1f ++ @ Store the Advanced SIMD/VFP-D32 ++ @ additional registers. ++ @vstm r0!, {D16-D31} ++ ++ @ IMPLEMENTATION DEFINED: save any subarchitecture defined state ++ @ NOTE: Don't change the order of the FPEXC and CPACR restores ++ @ Restore the original En bit of FPU. ++1: ++ FMXR FPEXC,r12 @ vmsr FPEXC,r12 ++ ++ @ Restore the original CPACR value. ++2: ++ mcr p15,0,r3,c1,c0,2 ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global restore_vfp ++restore_vfp: ++ .func ++ @ FPU state save/restore. Obviously FPSID,MVFR0 and MVFR1 don't get ++ @ serialized (RO). ++ @ Modify CPACR to allow CP10 and CP11 access ++ mrc p15,0,r1,c1,c0,2 ++ ORR r2,r1,#0x00F00000 ++ mcr p15,0,r2,c1,c0,2 ++ @ Enable FPU access to save/restore ++ @ the rest of registers. ++ ldr r2,=0x40000000 ++ FMXR FPEXC, r2 @ vmsr FPEXC, r2 ++ @ Recover FPEXC and FPSCR. These will ++ @ be restored later. ++ ldm r0!,{r3,r12} ++ @ Restore the VFP-D16 registers. ++ vldm r0!, {D0-D15} ++ @ Check for Advanced SIMD/VFP-D32 support ++ FMRX r2, MVFR0 @ vmrs r2, MVFR0 ++ and r2,r2,#0xF @ extract the A_SIMD bitfield ++ cmp r2, #0x2 ++ blt 0f ++ ++ @ Store the Advanced SIMD/VFP-D32 additional registers. ++ @vldm r0!, {D16-D31} ++ ++ @ IMPLEMENTATION DEFINED: restore any subarchitecture defined state ++0: ++ @ Restore configuration registers and enable. ++ @ Restore FPSCR _before_ FPEXC since FPEXC could disable FPU ++ @ and make setting FPSCR unpredictable. ++ FMXR FPSCR,r12 @ vmsr FPSCR,r12 ++ @ Restore FPEXC after FPSCR ++ FMXR FPEXC,r3 @ vmsr FPEXC,r3 ++ @ Restore CPACR ++ @ will restore in mt_restore_control_registers ++ @ mcr p15,0,r1,c1,c0,2 ++ ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++@ Function called with two arguments: ++@ r0 contains address to store control registers ++@ r1 is non-zero if we are Secure ++ ++ .global save_control_registers ++save_control_registers: ++ .func ++ cmp r1, #0 @ Are we Secure? ++ mrc p15,0,r2,c1,c0,1 @ ACTLR - Auxiliary Control Register ++ mrc p15,0,r3,c1,c0,0 @ SCTLR - System Control Register ++ mrc p15,0,r12,c1,c0,2 @ CPACR - Coprocessor Access Control ++ @ Register ++ stm r0!, {r2-r3, r12} ++ ++ mrcne p15,0,r1,c12,c0,1 @ MVBAR - Monitor Vector Base Address ++ @ Register ++ mrcne p15,0,r2,c1,c1,0 @ Secure Configuration Register ++ mrcne p15,0,r3,c1,c1,1 @ Secure Debug Enable Register ++ mrcne p15,0,r12,c1,c1,2 @ Non-Secure Access Control Register ++ stmne r0!, {r1-r3,r12} ++ ++ mrc p15,0,r1,c13,c0,1 @ CONTEXTIDR ++ mrc p15,0,r2,c13,c0,2 @ TPIDRURW ++ mrc p15,0,r3,c13,c0,3 @ TPIDRURO ++ mrc p15,0,r12,c13,c0,4 @ TPIDRPRW ++ stm r0!, {r1-r3,r12} ++ ++ @ The next two registers are only present if ThumbEE is implemented ++ mrc p15, 0, r1, c0, c1, 0 @ Read ID_PFR0 ++ tst r1, #PFR0_THUMB_EE_SUPPORT ++ mrcne p14,6,r1,c0,c0,0 @ TEECR ++ mrcne p14,6,r2,c1,c0,0 @ TEEHBR ++ stmne r0!, {r1, r2} ++ ++ mrc p14,7,r1,c1,c0,0 @ JOSCR ++ mrc p14,7,r2,c2,c0,0 @ JMCR ++ stm r0!, {r1, r2} ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++@ Function called with two arguments: ++@ r0 contains address to read control registers ++@ r1 is non-zero if we are Secure ++ ++ .global restore_control_registers ++restore_control_registers: ++ .func ++ cmp r1, #0 @ Are we Secure? ++ ldm r0!, {r2-r3, r12} ++ mcr p15,0,r2,c1,c0,1 @ ACTLR - Auxiliary Control Register ++ mcr p15,0,r3,c1,c0,0 @ SCTLR - System Control Register ++ mcr p15,0,r12,c1,c0,2 @ CPACR - Coprocessor Access Control ++ @ Register ++ ++ ldmne r0!, {r1-r3,r12} ++ mcrne p15,0,r1,c12,c0,1 @ MVBAR - Monitor Vector Base Address ++ @ Register ++ mcrne p15,0,r2,c1,c1,0 @ Secure Configuration Register ++ mcrne p15,0,r3,c1,c1,1 @ Secure Debug Enable Register ++ mcrne p15,0,r12,c1,c1,2 @ Non-Secure Access Control Register ++ ++ ldm r0!, {r1-r3,r12} ++ mcr p15,0,r1,c13,c0,1 @ CONTEXTIDR ++ mcr p15,0,r2,c13,c0,2 @ TPIDRURW ++ mcr p15,0,r3,c13,c0,3 @ TPIDRURO ++ mcr p15,0,r12,c13,c0,4 @ TPIDRPRW ++ ++ @ The next two registers are only present if ThumbEE is implemented ++ mrc p15, 0, r1, c0, c1, 0 @ Read ID_PFR0 ++ tst r1, #PFR0_THUMB_EE_SUPPORT ++ ldmne r0!, {r1,r2} ++ mcrne p14,6,r1,c0,c0,0 @ TEECR ++ mcrne p14,6,r2,c1,c0,0 @ TEEHBR ++ ++ ldm r0!, {r1, r2} ++ mcr p14,7,r1,c1,c0,0 @ JOSCR ++ mcr p14,7,r2,c2,c0,0 @ JMCR ++ isb ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global save_mmu ++save_mmu: ++ .func ++ push {r4, r5, r6, r7} ++ @ ASSUMPTION: no useful fault address / fault status information ++ ++ mrc p15,0,r4,c12,c0,0 @ VBAR ++ mrc p15,0,r5,c2,c0,2 @ TTBCR ++ tst r5, #TTBCR_EAE @ Are we using LPAE? ++ ++ @ save 32 or 64 bit TTBRs ++ mrceq p15,0,r6,c2,c0,0 @ 32 bit TTBR0 ++ mrceq p15,0,r7,c2,c0,1 @ 32 bit TTBR1 ++ mrrcne p15,0,r6,r7,c2 @ 64 bit TTBR0 ++ stm r0!, {r4-r7} ++ mrrcne p15,1,r6,r7,c2 @ 64 bit TTBR1 ++ stmne r0!, {r6-r7} ++ ++ mrc p15,0,r4,c3,c0,0 @ DACR ++ mrc p15,0,r5,c7,c4,0 @ PAR ++ mrc p15,0,r6,c10,c2,0 @ PRRR ++ mrc p15,0,r7,c10,c2,1 @ NMRR ++ stm r0!, {r4-r7} ++ ++ pop {r4, r5, r6, r7} ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global restore_mmu ++restore_mmu: ++ .func ++ push {r4, r5, r6, r7} ++ ldm r0!, {r4-r7} ++ mcr p15,0,r4,c12,c0,0 @ VBAR ++ mcr p15,0,r5,c2,c0,2 @ TTBCR ++ ++ tst r5, #TTBCR_EAE @ Are we using LPAE? ++ ++ @ restore 32 or 64 bit TTBRs ++ mcreq p15,0,r6,c2,c0,0 @ 32 bit TTBR0 ++ mcreq p15,0,r7,c2,c0,1 @ 32 bit TTBR1 ++ mcrrne p15,0,r6,r7,c2 @ 64-bit TTBR0 ++ ldmne r0!, {r6-r7} ++ mcrrne p15,1,r6,r7,c2 @ 64-bit TTBR1 ++ ++ ldm r0!, {r4-r7} ++ mcr p15,0,r4,c3,c0,0 @ DACR ++ mcr p15,0,r5,c7,c4,0 @ PAR ++ mcr p15,0,r6,c10,c2,0 @ PRRR ++ mcr p15,0,r7,c10,c2,1 @ NMRR ++ ++ pop {r4, r5, r6, r7} ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global save_mpu ++save_mpu: ++ .func ++ mrc p15, 0, r1, c0, c0, 4 @ Read MPUIR ++ and r1, r1, #0xff00 ++ lsr r1, r1, #8 @ Extract number of MPU regions ++ ++ @ Loop over the number of regions ++10: ++ cmp r1, #0 ++ beq 20f ++ sub r1, r1, #1 ++ mcr p15, 0, r1, c6, c2, 0 @ Write RGNR ++ mrc p15, 0, r2, c6, c1, 0 @ Read MPU Region Base Address Register ++ mrc p15, 0, r3, c6, c1, 2 @ Read Data MPU Region Size and Enable ++ @ Register ++ mrc p15, 0, r12, c6, c1, 4 @ Read Region access control Register ++ stm r0!, {r2, r3, r12} ++ b 10b ++ ++20: ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global restore_mpu ++restore_mpu: ++ .func ++ mrc p15, 0, r1, c0, c0, 4 @ Read MPUIR ++ and r1, r1, #0xff00 ++ lsr r1, r1, #8 @ Extract number of MPU regions ++ ++ @ Loop over the number of regions ++10: ++ cmp r1, #0 ++ beq 20f ++ ldm r0!, {r2, r3, r12} ++ sub r1, r1, #1 ++ mcr p15, 0, r1, c6, c2, 0 @ Write RGNR ++ mcr p15, 0, r2, c6, c1, 0 @ Write MPU Region Base Address ++ @ Register ++ mcr p15, 0, r3, c6, c1, 2 @ Write Data MPU Region Size and Enable ++ @ Register ++ mcr p15, 0, r12, c6, c1, 4 @ Write Region access control Register ++ b 10b ++ ++20: ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++@ If r1 is 0, we assume that the OS is not using the Virtualization extensions, ++@ and that the warm boot code will set up CNTHCTL correctly. If r1 is non-zero ++@ then CNTHCTL is saved and restored ++@ CNTP_CVAL will be preserved as it is in the always-on domain. ++ ++ .global save_generic_timer ++save_generic_timer: ++ .func ++ mrc p15,0,r2,c14,c2,1 @ read CNTP_CTL ++ mrc p15,0,r3,c14,c2,0 @ read CNTP_TVAL ++ mrc p15,0,r12,c14,c1,0 @ read CNTKCTL ++ stm r0!, {r2, r3, r12} ++ cmp r1, #0 ++ mrcne p15,4,r1,c14,c1,0 @ read CNTHCTL ++ strne r1, [r0], #4 ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global restore_generic_timer ++restore_generic_timer: ++ .func ++ ldm r0!, {r2, r3, r12} ++ mcr p15,0,r3,c14,c2,0 @ write CNTP_TVAL ++ mcr p15,0,r12,c14,c1,0 @ write CNTKCTL ++ mcr p15,0,r2,c14,c2,1 @ write CNTP_CTL ++ cmp r1, #0 ++ ldrne r1, [r0], #4 ++ mcrne p15,4,r1,c14,c1,0 @ write CNTHCTL ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global save_fault_status ++save_fault_status: ++ .func ++ mrc p15,0,r1,c6,c0,0 @ read DFAR ++ mrc p15,0,r2,c6,c0,2 @ read IFAR ++ mrc p15,0,r3,c5,c0,0 @ read DFSR ++ mrc p15,0,r12,c5,c0,1 @ read IFSR ++ stm r0!, {r1,r2,r3,r12} ++ mrc p15,0,r1,c5,c1,0 @ read ADFSR ++ mrc p15,0,r2,c5,c1,1 @ read AIFSR ++ stm r0!, {r1,r2} ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global restore_fault_status ++restore_fault_status: ++ .func ++ ldm r0!, {r1,r2,r3,r12} ++ mcr p15,0,r1,c6,c0,0 @ write DFAR ++ mcr p15,0,r2,c6,c0,2 @ write IFAR ++ mcr p15,0,r3,c5,c0,0 @ write DFSR ++ mcr p15,0,r12,c5,c0,1 @ write IFSR ++ ldm r0!, {r1,r2} ++ mcr p15,0,r1,c5,c1,0 @ write ADFSR ++ mcr p15,0,r2,c5,c1,1 @ write AIFSR ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global write_cntp_ctl ++write_cntp_ctl: ++ .func ++ mcr p15, 0, r0, c14, c2, 1 ++ dsb ++ isb ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global read_cpuid ++read_cpuid: ++ .func ++ mrc p15, 0, r0, c0, c0, 5 ++ ands r0, r0, #0xf ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global read_clusterid ++read_clusterid: ++ .func ++ mrc p15, 0, r0, c0, c0, 5 ++ lsr r0, r0, #0x8 ++ ands r0, r0, #0xf ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global read_nsacr ++read_nsacr: ++ .func ++ mrc p15, 0, r0, c1, c1, 2 ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++ ++ .global invalidate_unified_TLB_inner_shareable ++invalidate_unified_TLB_inner_shareable: ++ .func ++ mov r0, #0 ++ mcr p15, 0, r0, c8, c3, 0 ++ bx lr ++ .endfunc ++ ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +diff --git a/arch/arm/mach-hisi/fastboot/Makefile b/arch/arm/mach-hisi/fastboot/Makefile +new file mode 100644 +index 0000000..99e1a02 +--- /dev/null ++++ b/arch/arm/mach-hisi/fastboot/Makefile +@@ -0,0 +1,4 @@ ++obj-y += hibernate_umh.o ++obj-y += hibernate_misc.o ++obj-y += hibernate_pmmon.o ++#obj-y += hibernate_fs.o +diff --git a/arch/arm/mach-hisi/fastboot/fastboot_bootldr_info.c b/arch/arm/mach-hisi/fastboot/fastboot_bootldr_info.c +new file mode 100644 +index 0000000..1d62568 +--- /dev/null ++++ b/arch/arm/mach-hisi/fastboot/fastboot_bootldr_info.c +@@ -0,0 +1,258 @@ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/proc_fs.h> ++#include <linux/seq_file.h> ++ ++/* error message prefix */ ++#define ERRP "bootinfo: " ++ ++/* debug macro */ ++#if 0 ++#define dbg(x) do { printk("BOOTINFO-CMDLINE-PART: "); printk x; } while(0) ++#else ++#define dbg(x) ++#endif ++ ++struct boot_partition { ++ char *name; ++ uint64_t size; ++ uint64_t offset; ++}; ++ ++struct cmdline_boot_partition { ++ int num_parts; ++ struct boot_partition *parts; ++}; ++ ++static char *bootpart_cmdline; ++ ++static struct cmdline_boot_partition *boot_partitions; ++ ++int __init fastboot_find_bootpartition(const char *name, ++ unsigned long long *offset, unsigned long long *size) ++{ ++ struct boot_partition *part; ++ int i; ++ ++ if (!boot_partitions) { ++ pr_err("%s: could not found partinfo\n", __func__); ++ return -EFAULT; ++ } ++ ++ for (i = 0; i < boot_partitions->num_parts; i++) { ++ part = boot_partitions->parts + i; ++ if (strcmp(name, part->name) == 0) { ++ *offset = part->offset; ++ *size = part->size; ++ return 0; ++ } ++ } ++ ++ pr_err("%s: could not found %s\n", __func__, name); ++ return -ENOENT; ++} ++ ++/* strongly based on drivers/mtd/cmdlinepart.c */ ++static struct boot_partition __init *newbootpart(char *s, ++ char **retptr, ++ int *num_parts, ++ int this_part, ++ unsigned char **extra_mem_ptr, ++ int extra_mem_size) ++{ ++ struct boot_partition *parts; ++ unsigned long long size; ++ unsigned long long offset = 0; ++ char *name; ++ int name_len; ++ unsigned char *extra_mem; ++ char delim; ++ ++ size = memparse(s, &s); ++ ++ /* check for offset */ ++ if (*s == '@') { ++ s++; ++ offset = memparse(s, &s); ++ } ++ /* now look for name */ ++ delim = 0; ++ if (*s == '(') ++ delim = ')'; ++ ++ if (delim) { ++ char *p; ++ name = ++s; ++ p = strchr(name, delim); ++ if (!p) { ++ pr_err(ERRP "no closing %c found\n", delim); ++ return NULL; ++ } ++ name_len = p - name; ++ s = p + 1; ++ } else { ++ name = NULL; ++ name_len = 13; /* Partition_000 */ ++ } ++ ++ /* record name length for memory allocation later */ ++ extra_mem_size += name_len + 1; ++ ++ /* test if more partitions are following */ ++ if (*s == ',') { ++ /* more partitions follow, parse them */ ++ parts = newbootpart(s + 1, &s, num_parts, this_part + 1, ++ &extra_mem, extra_mem_size); ++ if (!parts) ++ return NULL; ++ } else { ++ /* this is the last partition: allocate space for all */ ++ int alloc_size; ++ ++ *num_parts = this_part + 1; ++ alloc_size = *num_parts * sizeof(struct boot_partition) ++ + extra_mem_size; ++ parts = kzalloc(alloc_size, GFP_KERNEL); ++ if (!parts) { ++ pr_err(ERRP "out of memory\n"); ++ return NULL; ++ } ++ extra_mem = (unsigned char *)(parts + *num_parts); ++ } ++ ++ /* enter this partition (offset will be calculated later if it is zero at this point) */ ++ parts[this_part].size = size; ++ parts[this_part].offset = offset; ++ if (name) ++ strlcpy(extra_mem, name, name_len + 1); ++ else ++ sprintf(extra_mem, "Partition_%03d", this_part); ++ parts[this_part].name = extra_mem; ++ extra_mem += name_len + 1; ++ ++ dbg(("partition %d: name <%s>, offset %llx, size %llx\n", ++ this_part, ++ parts[this_part].name, ++ parts[this_part].offset, ++ parts[this_part].size)); ++ ++ /* return (updated) pointer to extra_mem memory */ ++ if (extra_mem_ptr) ++ *extra_mem_ptr = extra_mem; ++ ++ /* return (updated) pointer command line string */ ++ *retptr = s; ++ ++ /* return partition table */ ++ return parts; ++} ++ ++/* strongly based on drivers/mtd/cmdlinepart.c */ ++static int __init bootpart_cmdline_parse(char *s) ++{ ++ struct cmdline_boot_partition *this_bootpart = NULL; ++ struct boot_partition *parts = NULL; ++ int num_parts; ++ ++ if (!s) ++ return 0; ++ ++ parts = newbootpart(s, /* cmdline */ ++ &s, /* out: updated cmdline ptr */ ++ &num_parts, /* out: number of parts */ ++ 0, /* first partition */ ++ (unsigned char**)&this_bootpart, /* out: extra mem */ ++ sizeof(*this_bootpart) + sizeof(void*)-1 /*alignment*/); ++ if(!parts) { ++ /* ++ * An error occurred. We're either: ++ * a) out of memory, or ++ * b) in the middle of the partition spec ++ * Either way, this part is hosed and we're ++ * unlikely to succeed in parsing any more ++ */ ++ return 0; ++ } ++ ++ /* align this_bootpart */ ++ this_bootpart = (struct cmdline_boot_partition *) ++ ALIGN((unsigned long)this_bootpart, sizeof(void*)); ++ /* enter results */ ++ this_bootpart->parts = parts; ++ this_bootpart->num_parts = num_parts; ++ ++ /* does another spec follow? */ ++ if (*s != '\0') ++ pr_err(ERRP "bad character after partition (%c)\n", *s); ++ ++ boot_partitions = this_bootpart; ++ ++ return 0; ++} ++ ++static void *bootpartinfo_seq_start(struct seq_file *file, loff_t *index) ++{ ++ if (!boot_partitions) ++ return NULL; ++ if (*index >= boot_partitions->num_parts) ++ return NULL; ++ return boot_partitions->parts + *index; ++} ++ ++static void *bootpartinfo_seq_next(struct seq_file *file, void *data, ++ loff_t *index) ++{ ++ if (data == NULL) ++ return NULL; ++ if (!boot_partitions) ++ return NULL; ++ (*index)++; ++ if (*index >= boot_partitions->num_parts) ++ return NULL; ++ return boot_partitions->parts + *index; ++} ++ ++static void bootpartinfo_seq_stop(struct seq_file *file, void *data) ++{ ++} ++ ++static int bootpartinfo_seq_show(struct seq_file *file, void *data) ++{ ++ struct boot_partition *bpart = data; ++ return seq_printf(file, "0x%016llx@0x%016llx: %s\n", ++ bpart->size, bpart->offset, bpart->name); ++} ++ ++static const struct seq_operations bootpartinfo_seq_ops = { ++ .start = bootpartinfo_seq_start, ++ .next = bootpartinfo_seq_next, ++ .stop = bootpartinfo_seq_stop, ++ .show = bootpartinfo_seq_show, ++}; ++ ++static int bootpartinfo_proc_open(struct inode *inode, struct file *file) ++{ ++ return seq_open(file, &bootpartinfo_seq_ops); ++} ++ ++static const struct file_operations bootpartinfo_proc_fops = { ++ .open = bootpartinfo_proc_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++ ++static int __init fastboot_bootloader_info_init(void) ++{ ++ bootpart_cmdline_parse(bootpart_cmdline); ++ proc_create("bootpartinfo", 0444, NULL, &bootpartinfo_proc_fops); ++ return 0; ++} ++module_init(fastboot_bootloader_info_init); ++ ++static int __init bootpart_setup(char *s) ++{ ++ bootpart_cmdline = s; ++ return 1; ++} ++__setup("bootparts=", bootpart_setup); +diff --git a/arch/arm/mach-hisi/fastboot/fastboot_pm.h b/arch/arm/mach-hisi/fastboot/fastboot_pm.h +new file mode 100644 +index 0000000..277781c +--- /dev/null ++++ b/arch/arm/mach-hisi/fastboot/fastboot_pm.h +@@ -0,0 +1,8 @@ ++ ++extern int fastboot_pm_call_umh(char *event_str, unsigned long event); ++ ++extern void __exit fastboot_hibernation_misc_exit(void); ++extern int __init fastboot_hibernation_misc_init(void); ++ ++extern int fastboot_hibernate_fs_suspend(void); ++extern int fastboot_hibernate_fs_resume(void); +diff --git a/arch/arm/mach-hisi/fastboot/hibernate_bdev.c b/arch/arm/mach-hisi/fastboot/hibernate_bdev.c +new file mode 100644 +index 0000000..4eee2e6 +--- /dev/null ++++ b/arch/arm/mach-hisi/fastboot/hibernate_bdev.c +@@ -0,0 +1,261 @@ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/list.h> ++#include <linux/fs.h> ++#include <linux/blkdev.h> ++#include <linux/blkpg.h> ++#include <linux/spinlock.h> ++#include <linux/hdreg.h> ++#include <linux/init.h> ++#include <linux/mutex.h> ++#include <linux/kthread.h> ++#include <asm/uaccess.h> ++ ++struct hibernate_bdev { ++ const char *path; ++ struct mutex lock; ++ int devidx; ++ struct gendisk *disk; ++ struct request_queue *rq; ++ spinlock_t queue_lock; ++ struct task_struct *thread; ++ struct file *filp; ++ atomic_t opened; ++ void *priv; ++}; ++ ++static int HB_BDEV_MAJOR = 0; ++static const char *HB_BDEV_NAME = "hb_bdev"; ++ ++static sector_t HB_OFFSET; ++static sector_t HB_SIZE; ++ ++extern int __init fastboot_find_bootpartition(const char *name, ++ unsigned long long *offset, unsigned long long *size); ++ ++static int hb_bdev_thread(void *arg); ++ ++static inline int __hb_init_env(void) ++{ ++ unsigned long long ofs, sz; ++ int n = fastboot_find_bootpartition("hibernate", &ofs, &sz); ++ if (n < 0) ++ return n; ++ HB_OFFSET = ofs >> 9; ++ HB_SIZE = sz >> 9; ++ ++ if (!HB_BDEV_MAJOR) ++ HB_BDEV_MAJOR = register_blkdev(HB_BDEV_MAJOR, HB_BDEV_NAME); ++ ++ return 0; ++} ++ ++static int hb_bdev_open(struct block_device *bdev, fmode_t mode) ++{ ++ struct hibernate_bdev *hb = bdev->bd_disk->private_data; ++ ++ int devidx = MINOR(bdev->bd_dev); ++ if (!hb) ++ return -ERESTARTSYS; ++ ++ if (IS_ERR_OR_NULL(hb->filp)) { ++ hb->filp = filp_open(hb->path, O_RDWR|O_SYNC | O_LARGEFILE, 0); ++ if (!hb->filp) ++ hb->filp = ERR_PTR(-ERESTARTSYS); ++ if (!IS_ERR(hb->filp)) { ++ hb->thread = kthread_run(hb_bdev_thread, hb, ++ "%s%d", HB_BDEV_NAME, devidx); ++ if (IS_ERR(hb->thread)) { ++ int ret = PTR_ERR(hb->thread); ++ filp_close(hb->filp, current->files); ++ hb->filp = ERR_PTR(-ERESTARTSYS); ++ return ret; ++ } ++ } ++ } ++ ++ if (IS_ERR_OR_NULL(hb->filp)) ++ return PTR_ERR(hb->filp); ++ ++ atomic_inc(&hb->opened); ++ return 0; ++} ++ ++static int hb_bdev_release(struct gendisk *disk, fmode_t mode) ++{ ++ struct hibernate_bdev *hb = disk->private_data; ++ if (!hb) ++ return -ENXIO; ++ if (atomic_dec_and_test(&hb->opened)) { ++ kthread_stop(hb->thread); ++ if (!IS_ERR_OR_NULL(hb->filp)) ++ filp_close(hb->filp, current->files); ++ hb->filp = ERR_PTR(-ENODEV); ++ } ++ return 0; ++} ++ ++static const struct block_device_operations hb_bdev_ops = { ++ .owner = THIS_MODULE, ++ .open = hb_bdev_open, ++ .release = hb_bdev_release, ++}; ++ ++static int hb_bdev_do_request(struct hibernate_bdev *hb, ++ struct request *req) ++{ ++ unsigned long long offset_tmp; ++ unsigned long long offset, len; ++ char *buf; ++ int ret; ++ ++ if (!hb->filp) ++ return -ENODEV; ++ /*modify because linux3.x change, --liucan*/ ++#if 0 ++ if (!blk_fs_request(req)) ++ return -EIO; ++#else ++ if (req->cmd_type != REQ_TYPE_FS) ++ return 0; ++#endif ++ ++ if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > ++ get_capacity(req->rq_disk)) ++ return -ENOSPC; ++ ++ offset = (blk_rq_pos(req) + HB_OFFSET) << 9; ++ len = blk_rq_cur_bytes(req); ++ buf = req->buffer; ++ ++ offset_tmp = offset; ++ ++ switch(rq_data_dir(req)) { ++ case READ: ++ ret = vfs_read(hb->filp, (char __user*)buf, len, &offset_tmp); ++ rq_flush_dcache_pages(req); ++ break; ++ case WRITE: ++ rq_flush_dcache_pages(req); ++ ret = vfs_write(hb->filp, (char __user*)buf, len, &offset_tmp); ++ break; ++ default: ++ pr_err("%s: Unknown request %u\n", __func__, rq_data_dir(req)); ++ return -EIO; ++ } ++ ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int hb_bdev_thread(void *arg) ++{ ++ struct hibernate_bdev *hb = arg; ++ struct request_queue *rq = hb->rq; ++ struct request *req = NULL; ++ ++ set_fs(get_ds()); ++ ++ spin_lock_irq(rq->queue_lock); ++ ++ while (!kthread_should_stop()) { ++ int res; ++ ++ if (!req && !(req = blk_fetch_request(rq))) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ spin_unlock_irq(rq->queue_lock); ++ schedule(); ++ spin_lock_irq(rq->queue_lock); ++ continue; ++ } ++ ++ spin_unlock_irq(rq->queue_lock); ++ ++ mutex_lock(&hb->lock); ++ res = hb_bdev_do_request(hb, req); ++ mutex_unlock(&hb->lock); ++ ++ spin_lock_irq(rq->queue_lock); ++ ++ if (!__blk_end_request_cur(req, res)) ++ req = NULL; ++ } ++ ++ if (req) ++ __blk_end_request_all(req, -EIO); ++ ++ spin_unlock_irq(rq->queue_lock); ++ ++ return 0; ++} ++ ++static void hb_bdev_request(struct request_queue *rq) ++{ ++ struct hibernate_bdev *hb; ++ struct request *req = NULL; ++ ++ hb = rq->queuedata; ++ ++ if (!hb) ++ while ((req = blk_fetch_request(rq)) != NULL) ++ __blk_end_request_all(req, -ENODEV); ++ else ++ wake_up_process(hb->thread); ++} ++ ++static int hibernate_add_blkdev(int devidx, const char *path) ++{ ++ struct hibernate_bdev *hb; ++ struct gendisk *gd; ++ ++ hb = kzalloc(sizeof(*hb), GFP_KERNEL); ++ if (!hb) ++ return -ENOMEM; ++ hb->path = path; ++ mutex_init(&hb->lock); ++ atomic_set(&hb->opened, 0); ++ hb->filp = ERR_PTR(-ENODEV); ++ ++ gd = alloc_disk(1); ++ if (!gd) { ++ kfree(hb); ++ return -ENOMEM; ++ } ++ ++ hb->disk = gd; ++ gd->private_data = hb; ++ gd->major = HB_BDEV_MAJOR; ++ gd->first_minor = devidx; ++ gd->fops = &hb_bdev_ops; ++ snprintf(gd->disk_name, sizeof(gd->disk_name), "%s%d", ++ HB_BDEV_NAME, devidx); ++ ++ set_capacity(gd, HB_SIZE); ++ ++ spin_lock_init(&hb->queue_lock); ++ hb->rq = blk_init_queue(hb_bdev_request, &hb->queue_lock); ++ if (!hb->rq) { ++ kfree(gd); ++ kfree(hb); ++ return -ENOMEM; ++ } ++ hb->rq->queuedata = hb; ++ blk_queue_logical_block_size(hb->rq, 4096); ++ gd->queue = hb->rq; ++ ++ add_disk(gd); ++ ++ return 0; ++} ++ ++static int __init hibernate_bdev_init(void) ++{ ++ if (__hb_init_env() < 0) ++ return 0; ++ hibernate_add_blkdev(0, "/dev/block/mmcblk0"); ++ return 0; ++} ++late_initcall(hibernate_bdev_init); +diff --git a/arch/arm/mach-hisi/fastboot/hibernate_fs.c b/arch/arm/mach-hisi/fastboot/hibernate_fs.c +new file mode 100644 +index 0000000..6e179fe +--- /dev/null ++++ b/arch/arm/mach-hisi/fastboot/hibernate_fs.c +@@ -0,0 +1,1910 @@ ++/* ++ * Todo ++ * - save & restore inotify kernel space watches ++ * - save & restore eventpolls ++ * - save & restore file locks ++ * - save & restore file mapping ++ */ ++ ++#include <linux/syscalls.h> ++#include <linux/string.h> ++#include <linux/device.h> ++#include <linux/kmod.h> ++#include <linux/fs.h> ++#include <linux/mount.h> ++#include <linux/pm.h> ++#include <linux/freezer.h> ++#include <linux/slab.h> ++#include <linux/fdtable.h> ++#include <linux/file.h> ++#include <linux/freezer.h> ++#include <linux/namei.h> ++#include <linux/fs_struct.h> ++#include <linux/mnt_namespace.h> ++#include <linux/fsnotify_backend.h> ++#include <linux/inotify.h> ++#include <linux/backing-dev.h> ++#include <linux/anon_inodes.h> ++#include <linux/security.h> ++#include <linux/module.h> ++#include <linux/sysctl.h> ++#include <linux/suspend.h> ++ ++#define pr_err_bug_if(condition, fmt, ...) \ ++ do { \ ++ if (unlikely(condition)) { \ ++ printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__); \ ++ BUG(); \ ++ } \ ++ } while (0) ++ ++#define pr_err_if(condition, fmt, ...) \ ++ do { \ ++ if (unlikely(condition)) \ ++ printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__); \ ++ } while (0) ++ ++ ++struct saved_mountpoint { ++ struct list_head node; ++ char *dev_name; ++ char *dir_name; ++ char *type; ++ char *data; ++#define MNT_MOVED (MNT_INTERNAL << 1) ++ unsigned long flags; ++}; ++ ++static LIST_HEAD(saved_mountpoints); ++ ++static char path_buf_a[PATH_MAX]; ++static char path_buf_b[PATH_MAX]; ++ ++static inline char *__d_path_wrapper(struct path *path, bool b) ++{ ++ if (!b) ++ return d_path(path, &path_buf_a[0], sizeof(path_buf_a)); ++ else ++ return d_path(path, &path_buf_b[0], sizeof(path_buf_b)); ++} ++ ++static inline char *d_path_wrapper(struct path *path) ++{ ++ return __d_path_wrapper(path, 0); ++} ++ ++static inline char *d_path_of_file(struct file *file) ++{ ++ return d_path_wrapper(&file->f_path); ++} ++ ++static inline char *d_path_of_mnt(struct vfsmount *mnt) ++{ ++ struct path mntpath = { .dentry = mnt->mnt_root, .mnt = mnt }; ++ return d_path_wrapper(&mntpath); ++} ++ ++static bool is_rw_bdev(struct vfsmount *mnt) ++{ ++ struct super_block *sb; ++ bool rw_bdev = false; ++ ++ if (!mnt || !mnt->mnt_sb) ++ goto out; ++ ++ spin_lock(&sb_lock); ++ sb = mnt->mnt_sb; ++ ++ if (!hlist_unhashed(&sb->s_instances) && ++ MAJOR(sb->s_dev) != 0 && ++ sb->s_bdi != &noop_backing_dev_info && ++ !__mnt_is_readonly(mnt)) { ++ rw_bdev = true; ++ } ++ spin_unlock(&sb_lock); ++ ++out: ++ return rw_bdev; ++} ++ ++extern spinlock_t vfsmount_lock; ++ ++/* self included */ ++static bool have_rw_bdev_parent(struct vfsmount *self) ++{ ++ struct vfsmount *parent = self; ++ bool have_rw_bdev = true; ++ ++ spin_lock(&vfsmount_lock); ++repeat: ++ have_rw_bdev = is_rw_bdev(parent); ++ if (have_rw_bdev) ++ goto out; ++ ++ if (!IS_ROOT(parent->mnt_mountpoint)) { ++ parent = parent->mnt_parent; ++ goto repeat; ++ } ++out: ++ spin_unlock(&vfsmount_lock); ++ return have_rw_bdev; ++} ++ ++static inline struct mnt_namespace *get_init_mnt_ns(void) ++{ ++ struct nsproxy *nsp; ++ ++ rcu_read_lock(); ++ nsp = task_nsproxy(&init_task); ++ BUG_ON(!(nsp && nsp->mnt_ns)); ++ get_mnt_ns(nsp->mnt_ns); ++ rcu_read_unlock(); ++ ++ return nsp->mnt_ns; ++} ++ ++static bool null_check(struct vfsmount *mnt) ++{ ++ return true; ++} ++ ++static int ++iterate_mounts_safe_reverse(int (*f)(struct vfsmount *, void *), ++ bool (*check)(struct vfsmount *), void *arg) ++{ ++ struct mnt_namespace *mnt_ns; ++ struct vfsmount *mnt, *tmp; ++ int error = 0; ++ ++ if (!check) ++ check = null_check; ++ ++ mnt_ns = get_init_mnt_ns(); ++ list_for_each_entry_safe_reverse(mnt, tmp, &mnt_ns->list, mnt_list) { ++ if (!check(mnt)) ++ continue; ++ ++ mntget(mnt); ++ error = f(mnt, arg); ++ mntput_no_expire(mnt); ++ if (error) ++ goto out; ++ } ++ ++out: ++ put_mnt_ns(mnt_ns); ++ return error; ++} ++ ++extern struct dentry *lookup_hash(struct nameidata *nd); ++ ++/* copied from do_rmdir */ ++static long do_kern_rmdir(const char *pathname) ++{ ++ int error = 0; ++ struct dentry *dentry; ++ struct nameidata nd; ++ ++ error = path_lookup(pathname, LOOKUP_PARENT, &nd); ++ if (error) ++ return error; ++ ++ switch(nd.last_type) { ++ case LAST_DOTDOT: ++ error = -ENOTEMPTY; ++ goto exit1; ++ case LAST_DOT: ++ error = -EINVAL; ++ goto exit1; ++ case LAST_ROOT: ++ error = -EBUSY; ++ goto exit1; ++ } ++ ++ nd.flags &= ~LOOKUP_PARENT; ++ ++ mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); ++ dentry = lookup_hash(&nd); ++ error = PTR_ERR(dentry); ++ if (IS_ERR(dentry)) ++ goto exit2; ++ error = mnt_want_write(nd.path.mnt); ++ if (error) ++ goto exit3; ++ error = security_path_rmdir(&nd.path, dentry); ++ if (error) ++ goto exit4; ++ error = vfs_rmdir(nd.path.dentry->d_inode, dentry); ++exit4: ++ mnt_drop_write(nd.path.mnt); ++exit3: ++ dput(dentry); ++exit2: ++ mutex_unlock(&nd.path.dentry->d_inode->i_mutex); ++exit1: ++ path_put(&nd.path); ++ return error; ++} ++ ++/* move to /dev/, mostly writable and living on ram */ ++#define SAFE_ROOT "/dev/hib_safe/" ++ ++static void mount_saved_mountpoints(void) ++{ ++ struct saved_mountpoint *saved, *tmp; ++ bool move = false; ++ ++ list_for_each_entry_safe(saved, tmp, &saved_mountpoints, node) { ++ int error; ++ char *dir; ++ char *dev; ++ ++ pr_info("PM:FS: restore mount [%s:%s]\n", ++ saved->dir_name, saved->dev_name); ++ pr_debug("PM:FS: type : %s\n", saved->type); ++ pr_debug("PM:FS: flags : 0x%08lx\n", saved->flags); ++ pr_debug("PM:FS: data : %s\n", ++ saved->data ? saved->data : "null"); ++ ++ error = do_mount(saved->dev_name, saved->dir_name, ++ saved->type, saved->flags, saved->data); ++ move = saved->flags & MS_MOVE; ++ ++ dir = move ? saved->dev_name : saved->dir_name; ++ dev = move ? saved->dir_name : saved->dev_name; ++ ++ if (error) { ++ pr_err("PM:FS: %s failed [%s:%s] (%d)\n", ++ move ? "move mount" : "mount", ++ dir, dev, error); ++ } else { ++ pr_debug("PM:FS: %s partition [%s:%s]\n", ++ move ? "moved" : "mounted", dir, dev); ++ } ++ ++ if (move) { ++ struct path moved; ++ int error = kern_path(dev, 0, &moved); ++ if (!error) { ++ struct mnt_namespace *ns = ++ get_init_mnt_ns(); ++ list_move_tail(&moved.mnt->mnt_list, ++ &ns->list); ++ put_mnt_ns(ns); ++ path_put(&moved); ++ } ++ do_kern_rmdir(saved->dev_name); ++ } ++ ++ list_del(&saved->node); ++ kfree(saved->dir_name); ++ kfree(saved->dev_name); ++ kfree(saved->type); ++ if (saved->data) ++ free_page((unsigned long)saved->data); ++ kfree(saved); ++ } ++} ++ ++static inline int get_s_flags_from_mnt(struct vfsmount *mnt) ++{ ++ int mnt_flags = mnt->mnt_flags; ++ int s_flags = mnt->mnt_sb->s_flags; ++ ++ if (mnt_flags & MNT_RELATIME) ++ s_flags &= ~MS_NOATIME; ++ if (mnt_flags & MNT_NOSUID) ++ s_flags |= MS_NOSUID; ++ if (mnt_flags & MNT_NODEV) ++ s_flags |= MS_NODEV; ++ if (mnt_flags & MNT_NOEXEC) ++ s_flags |= MS_NOEXEC; ++ if (mnt_flags & MNT_NOATIME) ++ s_flags |= MS_NOATIME; ++ if (mnt_flags & MNT_NODIRATIME) ++ s_flags |= MS_NODIRATIME; ++ if (mnt_flags & MNT_READONLY) ++ s_flags |= MS_RDONLY; ++ if (!(mnt_flags & (MNT_RELATIME | MNT_NOATIME))) ++ s_flags |= MS_STRICTATIME; ++ if (mnt_flags & MNT_MOVED) ++ s_flags |= MS_MOVE; ++ ++ return s_flags; ++} ++ ++static int save_mountpoint(struct vfsmount *mnt, const char *dir_name) ++{ ++ struct saved_mountpoint *save; ++ const char *options; ++ const char *dev_name; ++ int error = -ENOMEM; ++ ++ /* when moved, need to get old path */ ++ if (mnt->mnt_flags & MNT_MOVED) { ++ dev_name = d_path_of_mnt(mnt); ++ mnt->mnt_flags &= ~MNT_MOVED; ++ } else { ++ dev_name = mnt->mnt_devname; ++ } ++ ++ save = kzalloc(sizeof(*save), GFP_ATOMIC); ++ if (!save) ++ return -ENOMEM; ++ ++ pr_info("PM:FS: save partition [%s:%s]\n", dir_name, mnt->mnt_devname); ++ pr_debug("PM:FS: type : %s\n", mnt->mnt_sb->s_type->name); ++ pr_debug("PM:FS: flags : 0x%08lx\n", mnt->mnt_sb->s_flags); ++ pr_debug("PM:FS: data : %s\n", mnt->mnt_sb->s_options); ++ ++ INIT_LIST_HEAD(&save->node); ++ ++ save->flags = get_s_flags_from_mnt(mnt); ++ save->dir_name = kstrdup(dir_name, GFP_ATOMIC); ++ if (!save->dir_name) ++ goto free_save; ++ save->dev_name = kstrdup(dev_name, GFP_ATOMIC); ++ if (!save->dev_name) ++ goto free_dirname; ++ save->type = kstrdup(mnt->mnt_sb->s_type->name, GFP_ATOMIC); ++ if (!save->type) ++ goto free_devname; ++ rcu_read_lock(); ++ options = rcu_dereference(mnt->mnt_sb->s_options); ++ if (options && options[0]) { ++ save->data = (char *)__get_free_page(GFP_ATOMIC); ++ if (!save->data) { ++ rcu_read_unlock(); ++ goto free_type; ++ } ++ memset(save->data, 0, PAGE_SIZE); ++ strncpy(save->data, options, PAGE_SIZE); ++ } ++ rcu_read_unlock(); ++ ++ list_add(&save->node, &saved_mountpoints); ++ ++ return 0; ++ ++free_type: ++ kfree(save->type); ++free_devname: ++ kfree(save->dev_name); ++free_dirname: ++ kfree(save->dir_name); ++free_save: ++ kfree(save); ++ return error; ++} ++ ++static bool check_if_task_running(struct super_block *sb) ++{ ++ struct task_struct *task; ++ bool running = false; ++ ++ /* check if running process */ ++ read_lock(&tasklist_lock); ++ for_each_process(task) { ++ struct fs_struct *fs; ++ task_lock(task); ++ fs = task->fs; ++ if (!fs) { ++ goto next; ++ } ++ read_lock(&fs->lock); ++ if (sb == fs->root.mnt->mnt_sb) { ++ pr_debug("PM:FS: task [%s] is running on [%s] (root)\n", ++ &task->comm[0], d_path_wrapper(&fs->root)); ++ running = true; ++ } ++ if (sb == fs->pwd.mnt->mnt_sb) { ++ pr_debug("PM:FS: task [%s] is running on [%s] (pwd)\n", ++ &task->comm[0], d_path_wrapper(&fs->pwd)); ++ running = true; ++ } ++ read_unlock(&fs->lock); ++ next: ++ task_unlock(task); ++ } ++ read_unlock(&tasklist_lock); ++ ++ return running; ++} ++ ++static bool check_if_file_opening(struct super_block *sb) ++{ ++ bool opening = false; ++ ++ file_list_lock(); ++ if (!list_empty(&sb->s_files)) { ++#if defined(DEBUG) ++ struct file *file; ++ list_for_each_entry(file, &sb->s_files, f_u.fu_list) { ++ pr_debug("PM:FS: file [%s] is still open\n", ++ d_path_of_file(file)); ++ } ++#endif ++ opening = true; ++ } ++ file_list_unlock(); ++ ++ return opening; ++} ++ ++#ifdef CONFIG_INOTIFY_USER ++ ++extern struct srcu_struct fsnotify_grp_srcu; ++extern struct list_head fsnotify_groups; ++extern const struct fsnotify_ops inotify_fsnotify_ops; ++ ++static bool check_if_inotify_user_watching(struct super_block *sb) ++{ ++ struct fsnotify_group *group; ++ bool watching = false; ++ int idx; ++ ++ /* check if inotify_user */ ++ idx = srcu_read_lock(&fsnotify_grp_srcu); ++ list_for_each_entry_rcu(group, &fsnotify_groups, group_list) { ++ spin_lock(&group->mark_lock); ++ if (group->ops == &inotify_fsnotify_ops) { ++ struct fsnotify_mark_entry *entry; ++ list_for_each_entry(entry, ++ &group->mark_entries, g_list) { ++ struct inode *inode = entry->inode; ++#if defined(DEBUG) ++ struct dentry *dentry; ++#endif ++ if (sb != inode->i_sb) ++ continue; ++#if defined(DEBUG) ++ dentry = d_find_alias(inode); ++ pr_debug("PM:FS: inotify [%s] still watching\n", ++ dentry->d_name.name); ++ dput(dentry); ++#endif ++ watching = true; ++ } ++ } ++ spin_unlock(&group->mark_lock); ++ } ++ srcu_read_unlock(&fsnotify_grp_srcu, idx); ++ ++ return watching; ++} ++#else ++static inline bool check_if_inotify_user_watching(struct super_block *sb) ++{ ++ return false; ++} ++#endif ++ ++extern struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root); ++ ++/* copied from may_umount_tree */ ++static bool check_if_mnt_holding(struct vfsmount *mnt) ++{ ++ bool holding = false; ++ int actual_refs = 0; ++ int minimum_refs = 0; ++ struct vfsmount *p; ++ ++ spin_lock(&vfsmount_lock); ++ for (p = mnt; p; p = next_mnt(p, mnt)) { ++ actual_refs += atomic_read(&p->mnt_count); ++ minimum_refs += 2; ++ } ++ spin_unlock(&vfsmount_lock); ++ ++ if (actual_refs > minimum_refs) { ++ pr_debug("PM:FS: [%s] actual_refs:%d,minimum_refs:%d\n", ++ d_path_of_mnt(mnt), actual_refs, minimum_refs); ++ holding = true; ++ } ++ ++ return holding; ++} ++ ++static bool check_if_unmountable(struct vfsmount *mnt) ++{ ++ struct super_block *sb = mnt->mnt_sb; ++ bool opening, running, watching, holding; ++ char *p = d_path_of_mnt(mnt); ++ ++ if (!sb) { ++ return false; ++ } ++ ++ holding = check_if_mnt_holding(mnt); ++ watching = check_if_inotify_user_watching(sb); ++ opening = check_if_file_opening(sb); ++ running = check_if_task_running(sb); ++ ++ pr_err_if(holding, "PM:FS: Still Holding [%s:%s]\n", ++ p, mnt->mnt_devname); ++ pr_err_if(watching, "PM:FS: Still Watching [%s:%s]\n", ++ p, mnt->mnt_devname); ++ pr_err_if(opening, "PM:FS: Still Opening [%s:%s]\n", ++ p, mnt->mnt_devname); ++ pr_err_if(running, "PM:FS: Still Running [%s:%s]\n", ++ p, mnt->mnt_devname); ++ ++ return !holding && !opening && !running && !watching; ++} ++ ++static inline void thaw_flusher(void) ++{ ++ struct task_struct *task, *g; ++ read_lock(&tasklist_lock); ++ do_each_thread(g, task) { ++ if (task->flags & PF_FLUSHER) ++ thaw_process(task); ++ } while_each_thread(g, task); ++ read_unlock(&tasklist_lock); ++} ++ ++static inline void freeze_flusher(void) ++{ ++ struct task_struct *task, *g; ++ read_lock(&tasklist_lock); ++ do_each_thread(g, task) { ++ if (task->flags & PF_FLUSHER) ++ freeze_task(task, false); ++ } while_each_thread(g, task); ++ read_unlock(&tasklist_lock); ++} ++ ++/* copied from sys_mkdirat */ ++static int kern_mkdirat(const char *name, int mode) ++{ ++ struct dentry *dentry; ++ struct nameidata nd; ++ int error; ++ ++ error = path_lookup(name, LOOKUP_PARENT, &nd); ++ if (error) ++ goto out_err; ++ ++ dentry = lookup_create(&nd, 1); ++ error = PTR_ERR(dentry); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ ++ error = mnt_want_write(nd.path.mnt); ++ if (error) ++ goto out_dput; ++ error = security_path_mkdir(&nd.path, dentry, mode); ++ if (error) ++ goto out_drop_write; ++ error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode); ++out_drop_write: ++ mnt_drop_write(nd.path.mnt); ++out_dput: ++ dput(dentry); ++out_unlock: ++ mutex_unlock(&nd.path.dentry->d_inode->i_mutex); ++ path_put(&nd.path); ++out_err: ++ return error; ++} ++ ++static inline int mkdir_safe(void) ++{ ++ int error = kern_mkdirat(SAFE_ROOT, 755); ++ if (error != -EEXIST) ++ return error; ++ ++ return 0; ++} ++ ++static int mkdir_on_safe(const char *name, int mode, char **safe_dir) ++{ ++ static char safe_path[PATH_MAX]; ++ int error; ++ ++ error = mkdir_safe(); ++ if (error) ++ return error; ++ ++ snprintf(&safe_path[0], sizeof(safe_path), "%s/%s", SAFE_ROOT, name); ++ ++ error = kern_mkdirat(&safe_path[0], 755); ++ if (error) ++ return error; ++ ++ if (safe_dir) ++ *safe_dir = &safe_path[0]; ++ ++ return error; ++} ++ ++static int do_move_mount_to_safe(struct vfsmount *mnt, char *from) ++{ ++ char *to; ++ char temp[9]; ++ int error; ++ ++ /* FIXME - means nothing */ ++ sprintf(&temp[0], "%p", mnt); ++ error = mkdir_on_safe(temp, 755, &to); ++ if (error) ++ return error; ++ ++ error = do_mount(from, to, NULL, MS_MOVE, NULL); ++ if (error) ++ return error; ++ ++ /* MNT_MOVED set here to pick it up during resumption and cleared. */ ++ mnt->mnt_flags |= MNT_MOVED; ++ return error; ++} ++ ++/* FIXME - Is it enough ? */ ++static inline bool is_on_ram(struct vfsmount *mnt) ++{ ++ return MAJOR(mnt->mnt_sb->s_dev) == 0 ? true : false; ++} ++ ++struct umount_mnt { ++ struct list_head node; ++ struct vfsmount *mnt; ++}; ++ ++extern int do_umount(struct vfsmount *mnt, int flags); ++ ++static int unmount_rw_backing_dev(struct vfsmount *mnt, void *unused, ++ int is_on_ram) ++{ ++ struct path path = { .dentry = mnt->mnt_root, .mnt = mnt }; ++ char *orig = __d_path_wrapper(&path, true /* b */); ++ bool move = false; ++ int error; ++ ++ /* ++ * need to move a mount, it is not neccessary to check_if_unmountable() ++ */ ++ if (is_on_ram) { ++ move = true; ++ error = do_move_mount_to_safe(mnt, orig); ++ /* d_path_mnt() gives different path after here */ ++ } else { ++ if (check_if_unmountable(mnt)) ++ error = do_umount(mnt, 0); ++ else { ++ pr_err("PM:FS: [%s:%s] is unmountable\n", ++ d_path_of_mnt(mnt), mnt->mnt_devname); ++ error = -EBUSY; ++ } ++ } ++ ++ if (error) { ++ pr_err("PM:FS: %s failed [%s:%s] (%d)\n", ++ move ? "move mount" : "unmount", ++ orig, ++ move ? d_path_of_mnt(mnt) : mnt->mnt_devname, ++ error); ++ } else { ++#if defined(DEBUG) ++ pr_debug("PM:FS: %s partition [%s:%s]\n", ++ move ? "moved" : "unmounted", ++ orig, ++ move ? d_path_of_mnt(mnt) : mnt->mnt_devname); ++#endif ++ error = save_mountpoint(mnt, orig); ++ } ++ ++ return error; ++} ++ ++static int collect_mnt_on_rw_backing_devs(struct vfsmount *mnt, void *list) ++{ ++ struct list_head *umount_list = list; ++ struct umount_mnt *umount; ++ ++ umount = kmalloc(sizeof(*umount), GFP_KERNEL); ++ if (unlikely(!umount)) ++ return -ENOMEM; ++ ++ INIT_LIST_HEAD(&umount->node); ++ umount->mnt = mntget(mnt); ++ ++ list_add_tail(&umount->node, umount_list); ++ return 0; ++} ++ ++static int unmount_rw_backing_devs(void) ++{ ++ LIST_HEAD(umount_list); ++ struct umount_mnt *umount, *tmp; ++ int error; ++ ++ error = iterate_mounts_safe_reverse(collect_mnt_on_rw_backing_devs, ++ have_rw_bdev_parent, &umount_list); ++ ++ thaw_flusher(); ++ ++ list_for_each_entry_safe(umount, tmp, &umount_list, node) { ++ struct vfsmount *mnt = umount->mnt; ++ int is_ram = 0; ++ ++ list_del(&umount->node); ++ kfree(umount); ++ ++ if (is_on_ram(mnt)) is_ram = 1; ++ error = unmount_rw_backing_dev(mnt, NULL, is_ram); ++ if (!error && !is_ram /* !-EBUSY */) { ++ pr_err_bug_if(atomic_read(&mnt->mnt_count) != 1, ++ "PM:FS: " ++ "mount count should be 1 but %d\n", ++ atomic_read(&mnt->mnt_count)); ++ } ++ /* mnt will be destroyed if no error */ ++ mntput_no_expire(mnt); ++ } ++ ++ freeze_flusher(); ++ return error; ++} ++ ++static LIST_HEAD(saved_task_fs_list); ++ ++struct saved_task_fs { ++ struct list_head list; ++ ++ struct task_struct *task; ++ char *root; ++ char *pwd; ++}; ++ ++static struct saved_task_fs *alloc_saved_task_fs(struct task_struct *task, ++ bool move_root, bool move_pwd) ++{ ++ struct fs_struct *fs = task->fs; ++ int root_len = 0; ++ int pwd_len = 0; ++ int null_len = 0; ++ struct saved_task_fs *saved; ++ ++ read_lock(&fs->lock); ++ ++ if (move_root) { ++ root_len = strlen(d_path_wrapper(&fs->root)); ++ null_len++; ++ } ++ if (move_pwd) { ++ pwd_len = strlen(d_path_wrapper(&fs->pwd)); ++ null_len++; ++ } ++ ++ saved = kzalloc(sizeof(*saved) + root_len + pwd_len + null_len, ++ GFP_ATOMIC); ++ if (!saved) ++ goto unlock; ++ ++ INIT_LIST_HEAD(&saved->list); ++ saved->task = task; ++ ++ if (move_root) { ++ saved->root = (char *)saved + sizeof(*saved); ++ strcpy(saved->root, d_path_wrapper(&fs->root)); ++ } ++ if (move_pwd) { ++ if (saved->root) ++ saved->pwd = saved->root + root_len + 1; ++ else ++ saved->pwd = (char *)saved + sizeof(*saved); ++ strcpy(saved->pwd, d_path_wrapper(&fs->pwd)); ++ } ++ ++unlock: ++ read_unlock(&fs->lock); ++ return saved; ++} ++ ++static int move_task_fs_to(struct task_struct *task, ++ const char *root, const char *pwd) ++{ ++ struct path root_path, *old_root = NULL; ++ struct path pwd_path; ++ int error; ++ ++ if (root) { ++ error = kern_path(root, LOOKUP_FOLLOW, &root_path); ++ if (error) { ++ pr_err("PM:FS: failed to get root path [%s]\n", root); ++ return error; ++ } ++ ++ /* to rollback root when moving pwd fails */ ++ old_root = &task->fs->root; ++ path_get(old_root); ++ ++ set_fs_root(task->fs, &root_path); ++ path_put(&root_path); ++ } ++ if (pwd) { ++ error = kern_path(pwd, LOOKUP_FOLLOW, &pwd_path); ++ if (error) { ++ pr_err("PM:FS: failed to get pwd path [%s]\n", pwd); ++ goto out; ++ } ++ ++ set_fs_pwd(task->fs, &pwd_path); ++ path_put(&pwd_path); ++ } ++ ++out: ++ if (old_root) { ++ if (error) ++ set_fs_root(task->fs, old_root); ++ path_put(old_root); ++ } ++ ++ return error; ++} ++ ++static int move_task_fs_to_safe(struct task_struct *task, ++ bool move_root, bool move_pwd) ++{ ++ struct saved_task_fs *saved; ++ char *root = NULL; ++ char *pwd = NULL; ++ int error; ++ ++ if (!move_root && !move_pwd) ++ return 0; ++ ++ /* sleeping function called from invalid context */ ++ error = mkdir_safe(); ++ if (error) ++ return error; ++ ++ error = -ENOMEM; ++ saved = alloc_saved_task_fs(task, move_root, move_pwd); ++ if (!saved) ++ goto rm_safe; ++ ++ if (move_root) { ++ root = SAFE_ROOT; ++ pr_info("PM:FS: move task [%s] on root:%s to %s\n", ++ &task->comm[0], ++ d_path_wrapper(&task->fs->root), root); ++ ++ } ++ if (move_pwd) { ++ pwd = SAFE_ROOT; ++ pr_info("PM:FS: move task [%s] on pwd:%s to %s\n", ++ &task->comm[0], ++ d_path_wrapper(&task->fs->pwd), pwd); ++ } ++ ++ error = move_task_fs_to(task, root, pwd); ++ if (error) ++ goto free_saved; ++ ++ list_add(&saved->list, &saved_task_fs_list); ++ return 0; ++ ++free_saved: ++ kfree(saved); ++rm_safe: ++ do_kern_rmdir(SAFE_ROOT); ++ return error; ++} ++ ++static int save_task_fs_on_rw_backing_devs(void) ++{ ++ struct task_struct *task; ++ int error = 0; ++ ++ for_each_process(task) { ++ struct fs_struct *fs; ++ bool move_root = false; ++ bool move_pwd = false; ++ ++ fs = task->fs; ++ if (!fs) ++ goto next; ++ ++ if (have_rw_bdev_parent(fs->root.mnt)) ++ move_root = true; ++ if (have_rw_bdev_parent(fs->pwd.mnt)) ++ move_pwd = true; ++ ++ if (move_root || move_pwd) ++ error = move_task_fs_to_safe(task, move_root, move_pwd); ++ ++ next: ++ if (error) ++ break; ++ } ++ ++ return error; ++} ++ ++static void restore_saved_task_fs(void) ++{ ++ struct saved_task_fs *saved, *n; ++ ++ list_for_each_entry_safe(saved, n, &saved_task_fs_list, list) { ++ int error; ++ ++ if (saved->root) ++ pr_debug("PM:FS: restore task [%s] on root:%s\n", ++ &saved->task->comm[0], saved->root); ++ if (saved->pwd) ++ pr_debug("PM:FS: restore task [%s] on pwd:%s\n", ++ &saved->task->comm[0], saved->pwd); ++ ++ error = move_task_fs_to(saved->task, saved->root, saved->pwd); ++ if (unlikely(error)) { ++ pr_err("PM:FS: failed to restore fs for task [%s] " ++ "root:%s pwd:%s (%d)\n", ++ &saved->task->comm[0], ++ saved->root ? saved->root : "null", ++ saved->pwd ? saved->pwd : "null", ++ error); ++ } ++ list_del(&saved->list); ++ kfree(saved); ++ } ++ ++ do_kern_rmdir(SAFE_ROOT); ++} ++ ++/* ++ * FIXME ++ * How can inode-like one be used instead of file_path ? ++ * in other words, use alternative inode structure and file_path as a dentry ++ */ ++static LIST_HEAD(file_path_list); ++ ++struct file_path { ++ struct list_head node; /* file_path_list */ ++ struct list_head fobj_list; ++ ++ char *path; ++ ++ //counter ++}; ++ ++/* one per struct file instance */ ++struct file_object { ++ struct list_head same_fpath; /* fobj_list */ ++ struct list_head fdesc_list; ++ ++ struct file *file; /* make it null when f_count is zero */ ++ ++ atomic_long_t count; ++ unsigned int flags; ++ fmode_t mode; ++ loff_t pos; ++ loff_t size; ++}; ++ ++struct file_descriptor { ++ struct list_head same_fobj; /* fdesc_list */ ++ ++ int fd; ++ struct task_struct *task; ++}; ++ ++static loff_t noentry_lseek(struct file *file, loff_t offset, int orig) ++{ ++ pr_err("PM:FS: [%s] lseek(%lld, %d) on [%s]", ++ ¤t->comm[0], offset, orig, d_path_of_file(file)); ++ return -EBADF; ++} ++ ++static ssize_t noentry_read(struct file *file, ++ char __user *buf, size_t count, loff_t *ppos) ++{ ++ pr_err("PM:FS: [%s] read(%p, %d, %lld) on [%s]\n", ++ ¤t->comm[0], buf, count, *ppos, d_path_of_file(file)); ++ return -EBADF; ++} ++ ++static ssize_t noentry_write(struct file *file, ++ const char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ pr_err("PM:FS: [%s] write(%p, %d, %lld) on [%s]\n", ++ ¤t->comm[0], buf, count, *ppos, d_path_of_file(file)); ++ return -EBADF; ++} ++ ++static int noentry_release(struct inode *inode, struct file *file) ++{ ++ pr_err("PM:FS: [%s] close() on [%s]\n", ++ ¤t->group_leader->comm[0], d_path_of_file(file)); ++ return 0; ++} ++ ++/* ++ * FIXME ++ * - check whether more callbacks are necessary. ++ */ ++static const struct file_operations noentry_fops = { ++ .llseek = noentry_lseek, ++ .read = noentry_read, ++ .write = noentry_write, ++ .release = noentry_release, ++}; ++ ++static inline bool is_noentry_file(const struct file *file) ++{ ++ return file->f_op == &noentry_fops; ++} ++ ++/* ++ * NOTE ++ * It's not sufficient to get file path from anon_inodefs for debugging. ++ * It just gives us short "anon_inode:[kind]". We override anon_inodefs's ++ * d_dname() so we can get full file path pointed by noentry file. ++ * This means all d_op is overriden and could be a problem if anon_inodefs ++ * implements other dentry operations. But it does not at this time. ++ */ ++/* copied from dynamic_dname but it uses bigger static buffer */ ++static char *__dynamic_dname(struct dentry *dentry, ++ char *buffer, int buflen, const char *fmt, ...) ++{ ++ va_list args; ++ int sz; ++ ++ va_start(args, fmt); ++ sz = vsnprintf(path_buf_b, sizeof(path_buf_b), fmt, args) + 1; ++ va_end(args); ++ ++ if (sz > sizeof(path_buf_b) || sz > buflen) ++ return ERR_PTR(-ENAMETOOLONG); ++ ++ buffer += buflen - sz; ++ return memcpy(buffer, path_buf_b, sz); ++} ++ ++static char *noentry_dentry_dname(struct dentry *dentry, ++ char *buffer, int buflen) ++{ ++ char *p = __dynamic_dname(dentry, buffer, buflen, "noentry:%s", ++ dentry->d_name.name); ++ if (IS_ERR(p)) ++ return "noentry:nofilenameavailble"; ++ else ++ return p; ++} ++ ++static const struct dentry_operations noentry_dentry_operations = { ++ .d_dname = noentry_dentry_dname, ++}; ++ ++static struct file *get_noentry_file(const char *name) ++{ ++ struct file *file; ++ ++ file = anon_inode_getfile(name, &noentry_fops, NULL, 0); ++ if (IS_ERR(file)) ++ goto out; ++ ++ /* override d_op to get full filename */ ++ file->f_dentry->d_op = &noentry_dentry_operations; ++ ++out: ++ return file; ++} ++ ++static inline bool is_task_fd_slot_empty(struct task_struct *task, unsigned fd) ++{ ++ struct files_struct *files = task->files; ++ struct fdtable *fdt = files_fdtable(files); ++ bool is_empty = true; ++ ++ if (fd >= fdt->max_fds) { ++ is_empty = false; ++ goto out; ++ } ++ if (rcu_dereference_raw(fdt->fd[fd]) != NULL) { ++ is_empty = false; ++ goto out; ++ } ++ if (fd <= files->next_fd) ++ files->next_fd = fd + 1; ++out: ++ return is_empty; ++} ++ ++static void task_fd_install(struct task_struct *task, ++ unsigned int fd, int flags, struct file *file) ++{ ++ struct files_struct *files = task->files; ++ struct fdtable *fdt; ++ ++ spin_lock(&files->file_lock); ++ ++ fdt = files_fdtable(files); ++ BUG_ON(!is_task_fd_slot_empty(task, fd)); ++ ++ FD_SET(fd, fdt->open_fds); ++ if (flags & O_CLOEXEC) ++ FD_SET(fd, fdt->close_on_exec); ++ else ++ FD_CLR(fd, fdt->close_on_exec); ++ rcu_assign_pointer(fdt->fd[fd], file); ++ ++ spin_unlock(&files->file_lock); ++} ++ ++static off_t filp_lseek(struct file *file, off_t offset, unsigned int origin) ++{ ++ off_t retval; ++ ++ if (!file) ++ return -EBADF; ++ ++ retval = -EINVAL; ++ if (origin <= SEEK_MAX) { ++ loff_t res = vfs_llseek(file, offset, origin); ++ retval = res; ++ if (res != (loff_t)retval) ++ retval = -EOVERFLOW; ++ } ++ ++ return retval; ++} ++ ++static inline char *get_file_flags_readable(unsigned int flags, fmode_t mode) ++{ ++ static char open_flags[64]; ++ snprintf(&open_flags[0], sizeof(open_flags), ++ "%s%s%s", ++ (flags & O_ACCMODE) == O_RDWR ? "RDWR ": ++ ((flags & O_ACCMODE) == O_RDONLY ? "RD ": ++ ((flags & O_ACCMODE) == O_WRONLY ? "WR ":" ")), ++ (mode | flags) & FMODE_EXEC ? "EXEC ":" ", ++ flags & O_DIRECTORY ? "DIR ":""); ++ return &open_flags[0]; ++} ++ ++static void restore_file_descriptors(struct file_object *fobj) ++{ ++ struct file_descriptor *fdesc, *tmp; ++ ++ list_for_each_entry_safe(fdesc, tmp, &fobj->fdesc_list, same_fobj) { ++ /* ++ * Failed to open both of the original file and noentry file. ++ * leave the fd and fdslot empty so that the process's syscalls ++ * can fail with -EBADF. ++ */ ++ if (unlikely(!fobj->file)) { ++ pr_err("PM:FS: " ++ "have no file to restore [%s(%d)][fd:%d]\n", ++ &fdesc->task->comm[0], fdesc->task->pid, ++ fdesc->fd); ++ goto free; ++ } ++ /* install fd and get file instance */ ++ task_fd_install(fdesc->task, ++ fdesc->fd, fobj->flags, fobj->file); ++ get_file(fobj->file); ++ ++ pr_info("PM:FS: restore fd [%s(%d)][fd:%d]\n", ++ &fdesc->task->comm[0], fdesc->task->pid, fdesc->fd); ++ ++free: ++ list_del(&fdesc->same_fobj); ++ kfree(fdesc); ++ } ++} ++ ++static void restore_file_objects(struct file_path *fpath) ++{ ++ const char *filename = fpath->path; ++ struct file_object *fobj, *tmp; ++ ++ list_for_each_entry_safe(fobj, tmp, &fpath->fobj_list, same_fpath) { ++ struct file *file; ++ ++ /* in case of error on suspension, file object is alive */ ++ if (unlikely(fobj->file)) ++ goto restore_fdesc; ++ ++ /* we must put the file after all fds are restored */ ++ file = filp_open(filename, fobj->flags, fobj->mode); ++ if (unlikely(IS_ERR(file))) { ++ pr_err("PM:FS: opening [%s] failed (%d)\n", ++ filename, (int)PTR_ERR(file)); ++ ++ file = get_noentry_file(filename); ++ if (IS_ERR(file)) { ++ pr_err("PM:FS: getting noentry file failed " ++ "(%ld) not recoverable\n", ++ PTR_ERR(file)); ++ /* ++ * make it null so we can recognize it during ++ * fd restoration. ++ */ ++ file = NULL; ++ } else { ++ pr_err("PM:FS: " ++ "got noentry file instead of [%s]\n", ++ filename); ++ } ++ } else { ++ /* ++ * sanity check - if oops, something's wroing, fix ! ++ */ ++ pr_err_bug_if(fobj->flags != file->f_flags, ++ "PM:FS: mismatch file flags, should be " ++ "%x (but %x)\n", ++ fobj->flags, file->f_flags); ++ pr_err_bug_if(fobj->mode != file->f_mode, ++ "PM:FS: mismatch file mode, should be " ++ "%x (but %x)\n", ++ fobj->mode, file->f_mode); ++ ++ /* size truncated ? */ ++ if (unlikely(fobj->pos > ++ i_size_read(file->f_dentry->d_inode))) { ++ pr_err("PM:FS: file size (%lld) smaller than " ++ "offset (%lld)\n", ++ i_size_read(file->f_dentry->d_inode), ++ fobj->pos); ++ /* set pos with inode's size */ ++ fobj->pos = ++ i_size_read(file->f_dentry->d_inode); ++ } ++ ++ /* seek offset */ ++ BUG_ON(filp_lseek(file, fobj->pos, 0) < 0); ++ ++ pr_debug("PM:FS: restore file object [%p:%s]\n", ++ file, filename); ++ pr_debug("PM:FS: open flags : %s\n", ++ get_file_flags_readable(fobj->flags, ++ fobj->mode)); ++ pr_debug("PM:FS: open mode : 0x%08x\n", fobj->mode); ++ pr_debug("PM:FS: offset : %lld\n", fobj->pos); ++ pr_debug("PM:FS: size : %lld\n", ++ i_size_read(file->f_dentry->d_inode)); ++ } ++ fobj->file = file; ++ ++ restore_fdesc: ++ /* task's file descriptors */ ++ restore_file_descriptors(fobj); ++ ++ /* original file or noentry file */ ++ if (likely(fobj->file)) { ++ /* ++ * sanity check - if oops, something's wroing, fix ! ++ * file_count can be 1 (in case someting's wrong), so ++ * check it before dropping reference. ++ */ ++ pr_err_bug_if(file_count(fobj->file) < 2, ++ "PM:FS: file count is %ld\n", ++ file_count(fobj->file)); ++ pr_err_bug_if((file_count(fobj->file) - 1) != ++ atomic_long_read(&fobj->count), ++ "PM:FS: mismatch file count, it should " ++ "be %ld (but %ld)\n", ++ atomic_long_read(&fobj->count), ++ file_count(fobj->file) - 1); ++ } ++ ++ /* ++ * we should put the file because additional count is held in ++ * filp_open(). ++ */ ++ if (likely(fobj->file)) ++ fput(fobj->file); ++ ++ list_del(&fobj->same_fpath); ++ kfree(fobj); ++ } ++} ++ ++static void open_saved_files(void) ++{ ++ struct file_path *fpath, *tmp; ++ ++ list_for_each_entry_safe(fpath, tmp, &file_path_list, node) { ++ pr_info("PM:FS: restore file [%s]", fpath->path); ++ restore_file_objects(fpath); ++ list_del(&fpath->node); ++ kfree(fpath); ++ } ++} ++ ++static struct file_path *get_file_path(struct file *file) ++{ ++ const char *filename = d_path_of_file(file); ++ struct file_path *fpath; ++ ++ /* search first */ ++ list_for_each_entry(fpath, &file_path_list, node) { ++ if (!strcmp(fpath->path, filename)) ++ return fpath; ++ } ++ ++ /* new a file_path */ ++ fpath = kmalloc(sizeof(*fpath) + strlen(filename) + 1, GFP_ATOMIC); ++ if (!fpath) ++ return ERR_PTR(-ENOMEM); ++ ++ INIT_LIST_HEAD(&fpath->node); ++ INIT_LIST_HEAD(&fpath->fobj_list); ++ fpath->path = (char *)fpath + sizeof(*fpath); ++ strcpy(fpath->path, filename); ++ ++ list_add_tail(&fpath->node, &file_path_list); ++ ++ pr_info("PM:FS: save file [%s]\n", filename); ++ return fpath; ++} ++ ++static struct file_object *get_file_object(struct file *file) ++{ ++ struct file_path *fpath; ++ struct file_object *fobj; ++ ++ fpath = get_file_path(file); ++ if (IS_ERR(fpath)) ++ return ERR_PTR(-ENOMEM); ++ ++ /* search first */ ++ list_for_each_entry(fobj, &fpath->fobj_list, same_fpath) { ++ if (fobj->file && (fobj->file == file)) ++ return fobj; ++ } ++ ++ /* new a file_object */ ++ fobj = kzalloc(sizeof(*fobj), GFP_ATOMIC); ++ if (!fobj) ++ return ERR_PTR(-ENOMEM); ++ ++ INIT_LIST_HEAD(&fobj->same_fpath); ++ INIT_LIST_HEAD(&fobj->fdesc_list); ++ fobj->file = file; ++ atomic_long_set(&fobj->count, file_count(file)); ++ fobj->flags = file->f_flags & ~(O_CREAT | O_TRUNC | O_EXCL); ++ fobj->mode = file->f_mode; ++ fobj->pos = file->f_pos; ++ fobj->size = i_size_read(file->f_dentry->d_inode); ++ ++ list_add_tail(&fobj->same_fpath, &fpath->fobj_list); ++ ++ pr_debug("PM:FS: save file object [%p:%s]\n", file, fpath->path); ++ pr_debug("PM:FS: open count: %ld\n", file_count(file)); ++ pr_debug("PM:FS: open flags: %s\n", ++ get_file_flags_readable(file->f_flags, file->f_mode)); ++ pr_debug("PM:FS: open mode : 0x%08x\n", file->f_mode); ++ pr_debug("PM:FS: offset : %lld\n", file->f_pos); ++ pr_debug("PM:FS: size : %lld\n", ++ i_size_read(file->f_dentry->d_inode)); ++ ++ /* ++ * grab additional 1 ref to prevent it from being destroyed in filp_cl- ++ * ose() which may sleep. ++ * file object will be destoryed at the end of close_files_on_rw_backi- ++ * ng_devs(). ++ */ ++ get_file(fobj->file); ++ ++ return fobj; ++} ++ ++static void __put_unused_fd(struct files_struct *files, unsigned int fd) ++{ ++ struct fdtable *fdt = files_fdtable(files); ++ __FD_CLR(fd, fdt->open_fds); ++ if (fd < files->next_fd) ++ files->next_fd = fd; ++} ++ ++static int check_file_busy(struct task_struct *task, struct file *file) ++{ ++ struct list_head *l; ++ int nr_epolls = 0; ++ struct inode *inode = file->f_path.dentry->d_inode; ++ struct file_lock *flock; ++ int nr_flocks = 0; ++ struct vm_area_struct *vma; ++ struct address_space *mapping; ++ struct prio_tree_iter iter; ++ int nr_mappings = 0; ++ int busy = 0; ++ ++ /* FIXME - remove this and save & restore eventpolls */ ++ spin_lock(&file->f_lock); ++ list_for_each(l, &file->f_ep_links) ++ nr_epolls++; ++ spin_unlock(&file->f_lock); ++ ++ /* lock_kernel(); */ ++ for (flock = inode->i_flock; flock != NULL; flock = flock->fl_next) ++ nr_flocks++; ++ /* unlock_kernel(); */ ++ ++ mapping = file->f_mapping; ++ spin_lock(&mapping->i_mmap_lock); ++ vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, 0, ULONG_MAX) { ++ nr_mappings++; ++ } ++ spin_unlock(&mapping->i_mmap_lock); ++ ++ if (unlikely(nr_epolls)) { ++ busy = 1; ++ pr_err("PM:FS: file [%s] (opened by [%s:%d]) has eventpolls " ++ "(%d)\n", d_path_of_file(file), ++ &task->comm[0], task->pid, nr_epolls); ++ } ++ if (unlikely(nr_flocks)) { ++ busy = 1; ++ pr_err("PM:FS: file [%s] (opened by [%s:%d]) has locks (%d)\n", ++ d_path_of_file(file), ++ &task->comm[0], task->pid, nr_flocks); ++ } ++ if (unlikely(nr_mappings)) { ++ busy = 1; ++ pr_err("PM:FS: " ++ "file [%s] (opened by [%s:%d]) has been mmapped (%d)\n", ++ d_path_of_file(file), ++ &task->comm[0], task->pid, nr_mappings); ++ } ++ ++ return busy; ++} ++ ++static int task_save_close_file(struct task_struct *task, int fd) ++{ ++ struct files_struct *files = task->files; ++ struct fdtable *fdt = files_fdtable(files); ++ struct file *file = fcheck_files(files, fd); ++ struct file_object *fobj; ++ struct file_descriptor *fdesc; ++ ++ if (check_file_busy(task, file)) ++ return -EBUSY; ++ ++ fobj = get_file_object(file); ++ if (IS_ERR(fobj)) ++ return PTR_ERR(fobj); ++ ++ fdesc = kmalloc(sizeof(*fdesc), GFP_ATOMIC); ++ if (!fdesc) ++ return -ENOMEM; ++ ++ pr_info("PM:FS: save fd [%s(%d)][fd:%d]\n", ++ &task->comm[0], task->pid, fd); ++ ++ /* backup fd */ ++ INIT_LIST_HEAD(&fdesc->same_fobj); ++ fdesc->fd = fd; ++ fdesc->task = task; ++ ++ /* ++ * decrease count (close), ++ * it should be 2 at least, 1 held in get_file_object(). ++ */ ++ BUG_ON(file_count(file) == 1); ++ fput(file); ++ ++ /* free fd slot */ ++ rcu_assign_pointer(fdt->fd[fd], NULL); ++ FD_CLR(fd, fdt->close_on_exec); ++ __put_unused_fd(files, fd); ++ ++ /* link to fobj */ ++ list_add_tail(&fdesc->same_fobj, &fobj->fdesc_list); ++ ++ return 0; ++} ++ ++static bool is_on_rw_backing_dev(struct file *file) ++{ ++ struct vfsmount *mnt = file->f_vfsmnt; ++ return is_rw_bdev(mnt); ++} ++ ++static int task_close_file_on_rw_backing_dev(struct task_struct *task, int fd) ++{ ++ struct files_struct *files = task->files; ++ struct file *file = fcheck_files(files, fd); ++ int error = 0; ++ ++ if (!file) { ++ pr_err("PM:FS: no file structure\n"); ++ error = -EBADF; ++ goto out; ++ } ++ ++ if (!is_on_rw_backing_dev(file)) ++ goto out; ++ ++ if (!frozen(task)) { ++ pr_err("PM:FS: task [%s] that opened file [%s] not frozen\n", ++ &task->comm[0], d_path_of_file(file)); ++ error = -EBUSY; ++ goto out; ++ } ++ ++ error = task_save_close_file(task, fd); ++ if (error) { ++ pr_err("PM:FS: failed to close file %s on process %s\n", ++ &task->comm[0], d_path_of_file(file)); ++ goto out; ++ } ++ ++out: ++ return error; ++} ++ ++static int task_close_files_on_rw_backing_devs(struct task_struct *task) ++{ ++ struct files_struct *files = task->files; ++ struct fdtable *fdt; ++ int i = 0; ++ int error = 0; ++ ++ if (!frozen(task) || !files) ++ return 0; ++ ++ spin_lock(&files->file_lock); ++ fdt = files_fdtable(files); ++ for (i = 0; i < fdt->max_fds; i++) { ++ struct file *file = fcheck_files(files, i); ++ if (!file) ++ continue; ++ error = task_close_file_on_rw_backing_dev(task, i); ++ if (error) ++ goto out; ++ } ++out: ++ spin_unlock(&files->file_lock); ++ return error; ++} ++ ++static int close_files_on_rw_backing_devs(void) ++{ ++ struct task_struct *task; ++ struct file_path *fpath; ++ int error = 0; ++ ++ read_lock(&tasklist_lock); ++ for_each_process(task) { ++ task_lock(task); ++ error = task_close_files_on_rw_backing_devs(task); ++ task_unlock(task); ++ if (error) { ++ goto unlock; ++ } ++ } ++unlock: ++ read_unlock(&tasklist_lock); ++ ++ /* ++ * On error, do not free files on file_path_list here. ++ * They will be restored by simply increasing files' counts not to ++ * bother closing and reopening files. ++ */ ++ if (unlikely(error)) ++ goto out; ++ ++ /* free files */ ++ list_for_each_entry(fpath, &file_path_list, node) { ++ struct file_object *fobj; ++ list_for_each_entry(fobj, &fpath->fobj_list, same_fpath) { ++ /* sanity check - if oops, something's wroing, fix ! */ ++ pr_err_bug_if(file_count(fobj->file) != 1, ++ "PM:FS: " ++ "file count is %ld, should be 1\n", ++ file_count(fobj->file)); ++ /* free and nullify, reinstantiated on resumption. */ ++ BUG_ON(filp_close(fobj->file, task->files) < 0); ++ fobj->file = NULL; ++ } ++ } ++ ++out: ++ return error; ++} ++ ++#ifdef CONFIG_INOTIFY_USER ++ ++struct saved_inotify_user { ++ struct list_head node; ++ struct fsnotify_group *group; ++ int wd; ++ u32 mask; ++ char *path; ++}; ++ ++static LIST_HEAD(saved_inotify_users); ++ ++extern void inotify_free_mark(struct fsnotify_mark_entry *entry); ++extern struct kmem_cache *inotify_inode_mark_cachep __read_mostly; ++extern int inotify_max_user_watches __read_mostly; ++extern void inotify_remove_from_idr(struct fsnotify_group *group, ++ struct inotify_inode_mark_entry *ientry); ++ ++/* copied from inotify_new_watch() */ ++static int restore_saved_inotify_user(struct saved_inotify_user *saved, ++ struct path *path) ++{ ++ struct fsnotify_group *group = saved->group; ++ const char *filepath = saved->path; ++ int wd = saved->wd; ++ u32 mask = saved->mask; ++ struct inotify_inode_mark_entry *tmp_ientry; ++ struct inode *inode; ++ int ret; ++ ++ /* don't allow invalid bits: we don't want flags set */ ++ if (unlikely(!mask)) ++ return -EINVAL; ++ ++ inode = path->dentry->d_inode; ++ ++ tmp_ientry = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL); ++ if (unlikely(!tmp_ientry)) ++ return -ENOMEM; ++ ++ tmp_ientry->path = kstrdup(filepath, GFP_KERNEL); ++ if (unlikely(!tmp_ientry->path)) { ++ kmem_cache_free(inotify_inode_mark_cachep, tmp_ientry); ++ return -ENOMEM; ++ } ++ ++ fsnotify_init_mark(&tmp_ientry->fsn_entry, inotify_free_mark); ++ tmp_ientry->fsn_entry.mask = mask; ++ tmp_ientry->wd = -1; ++ ++ ret = -ENOSPC; ++ if (atomic_read(&group->inotify_data.user->inotify_watches) >= ++ inotify_max_user_watches) ++ goto out_err; ++retry: ++ ret = -ENOMEM; ++ if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL))) ++ goto out_err; ++ ++ /* we are putting the mark on the idr, take a reference */ ++ fsnotify_get_mark(&tmp_ientry->fsn_entry); ++ ++ spin_lock(&group->inotify_data.idr_lock); ++ ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry, ++ wd, &tmp_ientry->wd); ++ spin_unlock(&group->inotify_data.idr_lock); ++ if (ret) { ++ /* we didn't get on the idr, drop the idr reference */ ++ fsnotify_put_mark(&tmp_ientry->fsn_entry); ++ ++ /* idr was out of memory allocate and try again */ ++ if (ret == -EAGAIN) ++ goto retry; ++ goto out_err; ++ } ++ ++ BUG_ON(wd != tmp_ientry->wd); ++ ++ /* we are on the idr, now get on the inode */ ++ ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); ++ if (ret) { ++ /* we failed to get on the inode, get off the idr */ ++ inotify_remove_from_idr(group, tmp_ientry); ++ goto out_err; ++ } ++ ++ /* update the idr hint, who cares about races, it's just a hint */ ++ group->inotify_data.last_wd = tmp_ientry->wd; ++ ++ /* increment the number of watches the user has */ ++ atomic_inc(&group->inotify_data.user->inotify_watches); ++ ++ /* return the watch descriptor for this tmp_ientry entry */ ++ ret = tmp_ientry->wd; ++ ++ /* if this mark added a new event update the group mask */ ++ if (mask & ~group->mask) ++ fsnotify_recalc_group_mask(group); ++ ++ pr_info("PM:FS: restore inotify user [%s]\n", filepath); ++ pr_debug("PM:FS: user : %p\n", group->inotify_data.user); ++ pr_debug("PM:FS: watches: %d\n", ++ atomic_read(&group->inotify_data.user->inotify_watches)); ++ pr_debug("PM:FS: group : %p\n", group); ++ pr_debug("PM:FS: mask : 0x%08x\n", mask); ++ pr_debug("PM:FS: wd : %d\n", wd); ++ ++out_err: ++ /* match the ref from fsnotify_init_markentry() */ ++ fsnotify_put_mark(&tmp_ientry->fsn_entry); ++ ++ return ret; ++} ++ ++static void restore_saved_inotify_users(void) ++{ ++ struct saved_inotify_user *saved, *tmp; ++ struct path path; ++ ++ list_for_each_entry_safe(saved, tmp, &saved_inotify_users, node) { ++ int error; ++ ++ error = kern_path(saved->path, 0, &path); ++ if (error /* -ENOENT */) { ++ struct file *file; ++ ++ pr_err("PM:FS: not found path [%s] (%d)\n", ++ saved->path, error); ++ ++ file = get_noentry_file(saved->path); ++ if (IS_ERR(file)) { ++ error = PTR_ERR(file); ++ pr_err("PM:FS: failed to get noentry file, " ++ "not recoverable (%d)\n", error); ++ goto next; ++ } ++ pr_err("PM:FS: got noentry file and put inotify user " ++ "on it\n"); ++ path = file->f_path; ++ path_get(&path); ++ /* ++ * inotify registeration needs only f_path, not file ++ * itself. ++ */ ++ filp_close(file, NULL); ++ } ++ ++ error = restore_saved_inotify_user(saved, &path); ++#if 0 ++ if (!error) { ++ /* ++ * In real world, it's right to regard restored files ++ * as modified in most cases ? ++ */ ++ fsnotify_enable(); ++ fsnotify_modify(path.dentry); ++ fsnotify_disable(); ++ } ++#endif ++ path_put(&path); ++ next: ++ list_del(&saved->node); ++ kfree(saved); ++ } ++} ++ ++static int save_inotify_user_group_on_mnt(struct fsnotify_group *group, ++ struct vfsmount *mnt) ++{ ++ struct fsnotify_mark_entry *entry, *tmp; ++ int error = 0; ++ ++ spin_lock(&group->mark_lock); ++ list_for_each_entry_safe(entry, tmp, &group->mark_entries, g_list) { ++ struct inode *inode = entry->inode; ++ struct inotify_inode_mark_entry *ientry = ++ container_of(entry, struct inotify_inode_mark_entry, ++ fsn_entry); ++ struct user_struct *user = group->inotify_data.user; ++ struct saved_inotify_user *save; ++ char *path_str = ientry->path; ++ ++ if (inode->i_sb != mnt->mnt_sb) ++ continue; ++ ++ save = kzalloc(sizeof(*save) + strlen(path_str) + 1, ++ GFP_ATOMIC); ++ if (!save) { ++ error = -ENOMEM; ++ goto unlock; ++ } ++ INIT_LIST_HEAD(&save->node); ++ save->path = (char *)save + sizeof(*save); ++ ++ strcpy(save->path, path_str); ++ save->group = group; ++ save->wd = ientry->wd; ++ save->mask = entry->mask; ++ list_add(&save->node, &saved_inotify_users); ++ pr_info("PM:FS: save inotify user [%s]\n", save->path); ++ pr_debug("PM:FS: user : %p\n", user); ++ pr_debug("PM:FS: watches: %d\n", ++ atomic_read(&user->inotify_watches)); ++ pr_debug("PM:FS: group : %p\n", group); ++ pr_debug("PM:FS: mask : 0x%08x\n", entry->mask); ++ pr_debug("PM:FS: wd : %d\n", ientry->wd); ++ ++ fsnotify_get_mark(entry); ++ spin_unlock(&group->mark_lock); ++ ++ fsnotify_destroy_mark_by_entry(entry); ++ fsnotify_put_mark(entry); ++ ++ spin_lock(&group->mark_lock); ++ } ++ ++unlock: ++ spin_unlock(&group->mark_lock); ++ return error; ++} ++ ++static int save_inotify_users_on_mnt(struct vfsmount *mnt, void *unused) ++{ ++ struct fsnotify_group *group; ++ int idx; ++ int ret = 0; ++ ++ idx = srcu_read_lock(&fsnotify_grp_srcu); ++ list_for_each_entry_rcu(group, &fsnotify_groups, group_list) { ++ if (group->ops == &inotify_fsnotify_ops) { ++ ret = save_inotify_user_group_on_mnt(group, mnt); ++ if (ret) ++ goto unlock; ++ } ++ } ++unlock: ++ srcu_read_unlock(&fsnotify_grp_srcu, idx); ++ return ret; ++} ++ ++static int save_inotify_users_on_rw_backing_devs(void) ++{ ++ return iterate_mounts_safe_reverse(save_inotify_users_on_mnt, ++ is_rw_bdev, NULL); ++} ++ ++#else ++inline static int save_inotify_users_on_rw_partitions(void) { return 0; } ++inline static int restore_saved_inotify_users(void) { return 0; } ++ ++#endif /* CONFIG_INOTIFY_USER */ ++ ++int fastboot_hibernate_fs_suspend(void) ++{ ++ int error; ++ ++ pr_debug("PM:FS: Got PM_POST_FREEZE_PROCESS event\n"); ++ ++ error = save_inotify_users_on_rw_backing_devs(); ++ if (error) ++ goto restore_inotify_users; ++ error = close_files_on_rw_backing_devs(); ++ if (error) ++ goto open; ++ error = save_task_fs_on_rw_backing_devs(); ++ if (error) ++ goto restore_task_fs; ++ error = unmount_rw_backing_devs(); ++ if (error) ++ goto mount; ++ ++ pr_debug("PM:FS: Done with PM_POST_FREEZE_PROCESS event\n"); ++ return 0; ++ ++mount: ++ mount_saved_mountpoints(); ++restore_task_fs: ++ restore_saved_task_fs(); ++open: ++ open_saved_files(); ++restore_inotify_users: ++ restore_saved_inotify_users(); ++ ++ pr_err("PM:FS: failure in suspending filesystem (%d)\n", error); ++ return error; ++} ++ ++int fastboot_hibernate_fs_resume(void) ++{ ++ pr_debug("PM:FS: Got PM_THAW_PROCESS_PREPARE event\n"); ++ ++ /* no error check for task'fs */ ++ mount_saved_mountpoints(); ++ restore_saved_task_fs(); ++ open_saved_files(); ++ restore_saved_inotify_users(); ++ ++ pr_debug("PM:FS: Done with PM_THAW_PROCESS_PREPARE event\n"); ++ return 0; ++} +diff --git a/arch/arm/mach-hisi/fastboot/hibernate_misc.c b/arch/arm/mach-hisi/fastboot/hibernate_misc.c +new file mode 100644 +index 0000000..bd8529a +--- /dev/null ++++ b/arch/arm/mach-hisi/fastboot/hibernate_misc.c +@@ -0,0 +1,188 @@ ++#include <linux/mm.h> ++#include <linux/kernel.h> ++#include <linux/nsproxy.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/rwlock.h> ++#include <linux/sched.h> ++#include <linux/file.h> ++#include <linux/fdtable.h> ++#include <linux/fs.h> ++#include <linux/list.h> ++#include <linux/rculist.h> ++#include <linux/err.h> ++#include <linux/seq_file.h> ++#include <linux/fs_struct.h> ++#include <linux/proc_fs.h> ++#include <linux/mount.h> ++#include <linux/mnt_namespace.h> ++#include <asm/uaccess.h> ++ ++static DEFINE_MUTEX(file_mutex); ++ ++struct sb_files_mount ++{ ++ struct seq_file m; ++ struct path path; ++ struct vfsmount *mnt; ++}; ++ ++extern int kern_path(const char *, unsigned int, struct path *); ++extern char *get_task_comm(char *, struct task_struct *); ++ ++static char mnt_path[64] = {0,}; ++ ++static inline char *__dpath_of_filp(struct file *filp, char *buf, int len) ++{ ++ struct path mnt_path; ++ mnt_path.dentry = filp->f_dentry; ++ mnt_path.mnt = filp->f_vfsmnt; ++ return d_path(&mnt_path, buf, len); ++} ++ ++static void *sb_files_seq_start(struct seq_file *m, loff_t *pos) ++{ ++ struct sb_files_mount *priv = m->private; ++ mutex_lock(&file_mutex); ++ ++ /* fix me */ ++/*#ifdef CONFIG_SMP ++ return seq_list_start(priv->mnt->mnt_sb->s_files, *pos); ++#else*/ ++ return seq_list_start(&priv->mnt->mnt_sb->s_files, *pos); ++/*#endif*/ ++} ++ ++static void *m_next(struct seq_file *m, void *v, loff_t *pos) ++{ ++ struct sb_files_mount *priv = m->private; ++ ++ /* fix me */ ++/*#ifdef CONFIG_SMP ++ return seq_list_next(v, priv->mnt->mnt_sb->s_files, pos); ++#else*/ ++ return seq_list_next(v, &priv->mnt->mnt_sb->s_files, pos); ++/*#endif*/ ++} ++ ++static void m_stop(struct seq_file *m, void *v) ++{ ++ mutex_unlock(&file_mutex); ++} ++ ++static int sb_files_seq_show(struct seq_file *m, void *v) ++{ ++ struct file *filp = list_entry(v, struct file, f_u.fu_llist); ++ char buf[128], *path; ++ unsigned int acc; ++ struct vm_area_struct *vma; ++ struct address_space *mapping; ++ int mmap_count = 0; ++ ++ path = __dpath_of_filp(filp, buf, sizeof(buf)); ++ acc = filp->f_flags & O_ACCMODE; ++ mapping = filp->f_mapping; ++ ++ vma_interval_tree_foreach(vma, &mapping->i_mmap, 0, ULONG_MAX) { ++ mmap_count++; ++ } ++ ++ seq_printf(m, "[%s]\n", path); ++ seq_printf(m, "\topen count: %ld\n", file_count(filp)); ++ seq_printf(m, "\topen flags: %s%s%s%s\n", ++ (filp->f_mode | filp->f_flags) & FMODE_EXEC ? "EXEC ":" ", ++ filp->f_flags & O_DIRECTORY ? "DIR ":" ", ++ acc == O_RDWR ? "RDWR ": ++ (acc == O_RDONLY ? "RD ": ++ (acc == O_WRONLY ? " WR ":" ")), ++ mmap_count ? "MMAP":""); ++ ++ return 0; ++} ++ ++static const struct seq_operations sb_files_seq_ops = { ++ .start = sb_files_seq_start, ++ .next = m_next, ++ .stop = m_stop, ++ .show = sb_files_seq_show, ++}; ++ ++static int sb_files_open(struct inode *inode, struct file *file) ++{ ++ struct sb_files_mount *priv; ++ int ret = -1; ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ if (mnt_path[0] != 0) ++ ret = kern_path(mnt_path, 0, &priv->path); ++ ++ if (ret) ++ ret = kern_path("/", 0, &priv->path); ++ ++ priv->mnt = collect_mounts(&priv->path); ++ ++ file->private_data = &priv->m; ++ ret = seq_open(file, &sb_files_seq_ops); ++ if (ret) { ++ path_put(&priv->path); ++ drop_collected_mounts(priv->mnt); ++ kfree(priv); ++ return ret; ++ } ++ ++ priv->m.private = priv; ++ return 0; ++} ++ ++static int sb_files_release(struct inode *inode, struct file *file) ++{ ++ struct sb_files_mount *priv = file->private_data; ++ if (priv) { ++ path_put(&priv->path); ++ drop_collected_mounts(priv->mnt); ++ kfree(priv); ++ } ++ return 0; ++} ++ ++static ssize_t sb_files_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ size_t c = sizeof(mnt_path)-1 < count ? sizeof(mnt_path)-1 : count; ++ int ret; ++ if (unlikely((ret = copy_from_user(mnt_path, buf, c)) < 0)) ++ return -EFAULT; ++ mnt_path[c] = 0; ++ if (c > 1) { ++ if (mnt_path[c-1] == '\n') ++ mnt_path[c-1] = '\0'; ++ } ++ *ppos += c; ++ return c; ++} ++ ++static const struct file_operations sb_files_proc_fops = { ++ .open = sb_files_open, ++ .release = sb_files_release, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ ++ /* FIXME */ ++ .write = sb_files_write, ++}; ++ ++int __init fastboot_hibernation_misc_init(void) ++{ ++ proc_create("dump_sb_files", 0444, NULL, &sb_files_proc_fops); ++ return 0; ++} ++ ++#ifdef MODULE ++void __exit fastboot_hibernation_misc_exit(void) ++{ ++ remove_proc_entry("dump_sb_files", NULL); ++} ++#endif +diff --git a/arch/arm/mach-hisi/fastboot/hibernate_pmmon.c b/arch/arm/mach-hisi/fastboot/hibernate_pmmon.c +new file mode 100644 +index 0000000..5add8d7 +--- /dev/null ++++ b/arch/arm/mach-hisi/fastboot/hibernate_pmmon.c +@@ -0,0 +1,107 @@ ++#include <linux/module.h> ++#include <linux/sysctl.h> ++#include <linux/suspend.h> ++ ++#include "fastboot_pm.h" ++ ++#if defined(CONFIG_PM) && defined(CONFIG_SUSPEND) ++ ++static char *pm_umh_str; ++static int fastboot_pm_monitor(struct notifier_block *this, ++ unsigned long event, void *ptr) ++{ ++ switch (event) { ++ case PM_HIBERNATION_PREPARE: /* starting pm_suspend_to_hibernation */ ++ pm_umh_str = "PM_HIBERNATION_PREPARE"; ++ break; ++ case PM_POST_HIBERNATION: /* end of pm_resume_from_hibernation */ ++ pm_umh_str = "PM_POST_HIBERNATION"; ++ break; ++ case PM_SUSPEND_PREPARE: /* starting pm_suspend */ ++ pm_umh_str = "PM_SUSPEND_PREPARE"; ++ break; ++ case PM_POST_SUSPEND: /* end of pm_resume */ ++ pm_umh_str = "PM_POST_SUSPEND"; ++ break; ++ case PM_RESTORE_PREPARE: /* pm_suspend failure ? */ ++ pm_umh_str = "PM_RESTORE_PREPARE"; ++ break; ++ case PM_POST_RESTORE: /* pm_suspend failure ? */ ++ pm_umh_str = "PM_POST_RESTORE"; ++ break; ++ case PM_POST_FREEZE_PROCESS: ++ pm_umh_str = "PM_POST_FREEZE_PROCESS"; ++ break; ++ case PM_THAW_PROCESS_PREPARE: ++ pm_umh_str = "PM_THAW_PROCESS_PREPARE"; ++ /*mark by liucan*/ ++ //fastboot_hibernate_fs_resume(); ++ break; ++ case PM_POST_DEVICE_SUSPEND: ++ pm_umh_str = "PM_POST_DEVICE_SUSPEND"; ++ break; ++ case PM_RESUME_DEVICE_PREPARE: ++ pm_umh_str = "PM_RESUME_DEVICE_PREPARE"; ++ break; ++ default: ++ printk("%s: unknown event, ignoring...\n", __func__); ++ return 0; ++ } ++ ++ /* FIXME: ++ * The callee must be on a initrd in order to handle DEVICE events. ++ * Or it will be blocked forever because a storage was suspended. ++ */ ++ if (event != PM_POST_DEVICE_SUSPEND ++ && event != PM_RESUME_DEVICE_PREPARE) { ++ fastboot_pm_call_umh(pm_umh_str, event); ++ } ++ ++ switch (event) { ++ case PM_POST_FREEZE_PROCESS: ++ // mark by liucan. ++ //fastboot_hibernate_fs_suspend(); ++ //break; ++ case PM_HIBERNATION_PREPARE: ++ case PM_POST_HIBERNATION: ++ case PM_SUSPEND_PREPARE: ++ case PM_POST_SUSPEND: ++ case PM_RESTORE_PREPARE: ++ case PM_POST_RESTORE: ++ case PM_THAW_PROCESS_PREPARE: ++ case PM_POST_DEVICE_SUSPEND: ++ case PM_RESUME_DEVICE_PREPARE: ++ default: ++ return 0; ++ } ++ ++ return 0; ++} ++ ++static struct notifier_block fastboot_pm_notifier = { ++ .notifier_call = fastboot_pm_monitor, ++}; ++ ++static int __init fastboot_pm_monitor_init(void) ++{ ++ register_pm_notifier(&fastboot_pm_notifier); ++ return 0; ++} ++#if 0 ++#ifdef MODULE ++static void __exit fastboot_pm_monitor_exit(void) ++{ ++ printk("hahaha\n"); ++ unregister_pm_notifier(&fastboot_pm_notifier); ++} ++ ++module_init(fastboot_pm_monitor_init); ++module_exit(fastboot_pm_monitor_exit); ++#else ++late_initcall(fastboot_pm_monitor_init); ++#endif ++#endif ++late_initcall(fastboot_pm_monitor_init); ++#endif /* CONFIG_PM && CONFIG_SUSPEND */ ++ ++MODULE_LICENSE("GPL"); +diff --git a/arch/arm/mach-hisi/fastboot/hibernate_umh.c b/arch/arm/mach-hisi/fastboot/hibernate_umh.c +new file mode 100644 +index 0000000..9f56da4 +--- /dev/null ++++ b/arch/arm/mach-hisi/fastboot/hibernate_umh.c +@@ -0,0 +1,79 @@ ++#include <linux/module.h> ++#include <linux/sysctl.h> ++#include <linux/suspend.h> ++ ++#include "fastboot_pm.h" ++ ++/* ++ * ToDo ++ * add uevent ++ */ ++ ++#if defined(CONFIG_PM) && defined(CONFIG_SUSPEND) ++ ++#define PM_EVENT_HELPER_LEN (32) ++ ++static char pm_umh_helper[PM_EVENT_HELPER_LEN] = { 0, }; ++static struct ctl_table_header *ctl; ++ ++static struct ctl_table pm_umh_ctl[] = { ++ { ++ .procname = "pm_notifier", ++ .data = &pm_umh_helper, ++ .maxlen = PM_EVENT_HELPER_LEN, ++ .mode = 0644, ++ .proc_handler = proc_dostring, ++ }, ++ {} ++}; ++ ++static struct ctl_path pm_umh_root[] = { ++ { ++ .procname = "kernel", ++ }, ++ {} ++}; ++ ++int fastboot_pm_call_umh(char *event_str, unsigned long event) ++{ ++ char *argv[3] = {pm_umh_helper, event_str, NULL}; ++ char *envp[3] = {"HOME=/", NULL, NULL }; ++ int ret; ++ ++ if (!pm_umh_helper[0]) ++ return 0; ++ ++ /* NOTE: may cannot access to Android's property services. */ ++ envp[1] = "PATH=/usr/bin:/usr/sbin:/bin:/sbin"; ++ ret = call_usermodehelper_fns_force(argv[0], argv, envp, ++ UMH_WAIT_PROC, NULL, NULL, NULL); ++ if (ret < 0) ++ pr_err("%s: helper failed(%d)\n", __func__, ret); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(fastboot_pm_call_umh); ++ ++static int __init fastboot_pm_umh_init(void) ++{ ++ ctl = register_sysctl_paths(pm_umh_root, pm_umh_ctl); ++ fastboot_hibernation_misc_init(); ++ return 0; ++} ++ ++#if 0 ++#ifdef MODULE ++static void __exit fastboot_pm_umh_exit(void) ++{ ++ fastboot_hibernation_misc_exit(); ++ unregister_sysctl_table(ctl); ++} ++ ++module_init(fastboot_pm_umh_init); ++module_exit(fastboot_pm_umh_exit); ++#else ++late_initcall(fastboot_pm_umh_init); ++#endif ++#endif ++late_initcall(fastboot_pm_umh_init); ++#endif /* CONFIG_PM && CONFIG_SUSPEND */ +diff --git a/arch/arm/mach-hisi/headsmp.S b/arch/arm/mach-hisi/headsmp.S +index 278889c..0d0abd4 100644 +--- a/arch/arm/mach-hisi/headsmp.S ++++ b/arch/arm/mach-hisi/headsmp.S +@@ -8,9 +8,292 @@ + */ + #include <linux/linkage.h> + #include <linux/init.h> ++#include <asm/assembler.h> + + __CPUINIT + + ENTRY(hix5hd2_secondary_startup) + bl v7_invalidate_l1 + b secondary_startup ++ENDPROC(hix5hd2_secondary_startup) ++ ++ ++ENTRY(hi3519_secondary_startup) ++ /* Set A17 acinactive to 1, bit 8 is acinactive */ ++ bl hi_pmc_clear_a17_ac ++ ++ bl cci_enable_port_for_self ++ ++ /* config l2ctl */ ++ mrc p15, 1, r0, c9, c0, 2 ++ mov r2, #0x100000 ++ orr r1, r1, r2 ++ mcr p15, 1, r0, c9, c0, 2 ++ ++ /* ++ * set SMP bit ACTLR register for A17 slave core ++ */ ++ mrc p15, 0, r0, c1, c0, 1 ++ orr r0, #(1 << 6) ++ mcr p15, 0, r0, c1, c0, 1 ++ ++ bl v7_invalidate_l1 ++ ++ b secondary_startup ++ENDPROC(hi3519_secondary_startup) ++ ++ENTRY(hi3519_cpu_resume) ++ safe_svcmode_maskall r1 ++ ++ /* Set A17 acinactive to 0, bit 8 is acinactive */ ++ bl hi_pmc_clear_a17_ac ++ ++ bl cci_enable_port_for_self ++ ++ /* config l2ctl, just follow the datasheet why?*/ ++ mrc p15, 1, r0, c9, c0, 2 ++ mov r2, #0x100000 ++ orr r1, r1, r2 ++ mcr p15, 1, r0, c9, c0, 2 ++ ++ /* ++ * set SMP bit ACTLR register for A17 slave core ++ */ ++ mrc p15, 0, r0, c1, c0, 1 ++ orr r0, #(1 << 6) ++ mcr p15, 0, r0, c1, c0, 1 ++ ++ b cpu_resume ++ ++ENDPROC(hi3519_cpu_resume) ++ ++ENTRY(hi3516av200_secondary_startup) ++ /* Set A17 acinactive to 1, bit 8 is acinactive */ ++ bl hi_pmc_clear_a17_ac ++ ++ bl cci_enable_port_for_self ++ ++ /* config l2ctl */ ++ mrc p15, 1, r0, c9, c0, 2 ++ mov r2, #0x100000 ++ orr r1, r1, r2 ++ mcr p15, 1, r0, c9, c0, 2 ++ ++ /* ++ * set SMP bit ACTLR register for A17 slave core ++ */ ++ mrc p15, 0, r0, c1, c0, 1 ++ orr r0, #(1 << 6) ++ mcr p15, 0, r0, c1, c0, 1 ++ ++ bl v7_invalidate_l1 ++ ++ b secondary_startup ++ENDPROC(hi3516av200_secondary_startup) ++ ++ENTRY(hi3516av200_cpu_resume) ++ safe_svcmode_maskall r1 ++ ++ /* Set A17 acinactive to 0, bit 8 is acinactive */ ++ bl hi_pmc_clear_a17_ac ++ ++ bl cci_enable_port_for_self ++ ++ /* config l2ctl, just follow the datasheet why?*/ ++ mrc p15, 1, r0, c9, c0, 2 ++ mov r2, #0x100000 ++ orr r1, r1, r2 ++ mcr p15, 1, r0, c9, c0, 2 ++ ++ /* ++ * set SMP bit ACTLR register for A17 slave core ++ */ ++ mrc p15, 0, r0, c1, c0, 1 ++ orr r0, #(1 << 6) ++ mcr p15, 0, r0, c1, c0, 1 ++ ++ b cpu_resume ++ ++ENDPROC(hi3516av200_cpu_resume) ++ ++ENTRY(hi3559_secondary_startup) ++ /* Set A17 acinactive to 1, bit 8 is acinactive */ ++ bl hi_pmc_clear_a17_ac ++ ++ bl cci_enable_port_for_self ++ ++ /* config l2ctl */ ++ mrc p15, 1, r0, c9, c0, 2 ++ mov r2, #0x100000 ++ orr r1, r1, r2 ++ mcr p15, 1, r0, c9, c0, 2 ++ ++ /* ++ * set SMP bit ACTLR register for A17 slave core ++ */ ++ mrc p15, 0, r0, c1, c0, 1 ++ orr r0, #(1 << 6) ++ mcr p15, 0, r0, c1, c0, 1 ++ ++ bl v7_invalidate_l1 ++ ++ b secondary_startup ++ENDPROC(hi3559_secondary_startup) ++ ++ENTRY(hi3559_cpu_resume) ++ safe_svcmode_maskall r1 ++ ++ /* Set A17 acinactive to 0, bit 8 is acinactive */ ++ bl hi_pmc_clear_a17_ac ++ ++ bl cci_enable_port_for_self ++ ++ /* config l2ctl, just follow the datasheet why?*/ ++ mrc p15, 1, r0, c9, c0, 2 ++ mov r2, #0x100000 ++ orr r1, r1, r2 ++ mcr p15, 1, r0, c9, c0, 2 ++ ++ /* ++ * set SMP bit ACTLR register for A17 slave core ++ */ ++ mrc p15, 0, r0, c1, c0, 1 ++ orr r0, #(1 << 6) ++ mcr p15, 0, r0, c1, c0, 1 ++ ++ b cpu_resume ++ ++ENDPROC(hi3559_cpu_resume) ++ ++#ifdef CONFIG_ARCH_HI3531D ++@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ++@ ++@ corrupt: r0, r1, r2, r3 ++@ ++.align 2 ++ ++flash_cache_all: ++ ++ /* disable MMU stuff and caches */ ++ mrc p15, 0, r0, c1, c0, 0 ++ orr r0, r0, #0x00002000 /* clear bits 13 (--V-) */ ++ bic r0, r0, #0x00000007 /* clear bits 2:0 (-CAM) */ ++ orr r0, r0, #0x00000002 /* set bit 1 (--A-) Align */ ++ orr r0, r0, #0x00000800 /* set bit 12 (Z---) BTB */ ++ mcr p15, 0, r0, c1, c0, 0 ++ ++ /* ++ * Invalidate L1 I/D ++ */ ++ mov r0, #0 /* set up for MCR */ ++ mcr p15, 0, r0, c8, c7, 0 /* invalidate TLBs */ ++ mcr p15, 0, r0, c7, c5, 0 /* invalidate icache */ ++ ++ /* Invalidate L1 D-cache */ ++ mcr p15, 2, r0, c0, c0, 0 /* select L1 data cache */ ++ /* Read Current Cache Size Identification Register */ ++ mrc p15, 1, r3, c0, c0, 0 ++ ldr r1, =0x1ff ++ and r3, r1, r3, LSR #13 /* r3 = (number of sets -1) */ ++ mov r0, #0 ++way_loop: ++ mov r1, #0 /* r1->set counter */ ++line_loop: ++ mov r2, r0, LSL #30 ++ orr r2, r1, LSL #5 /* r2->set/way cache-op format */ ++ mcr p15, 0, r2, c7, c6, 2 /* Invalidate line described by r2 */ ++ add r1, r1, #1 /* Increment set counter */ ++ cmp r1, r3 /* Check if the last set is reached */ ++ ble line_loop /* if not, continue the set_loop */ ++ add r0, r0, #1 /* else, Increment way counter */ ++ cmp r0, #4 /* Check if the last way is reached */ ++ blt way_loop /* if not, continue the way_loop */ ++ ++ mov pc, lr ++#endif ++ ++ENTRY(hi3536c_secondary_startup) ++ /* set the cpu to SVC32 mode */ ++ mrs r0, cpsr ++ bic r0, r0, #0x1f /* r0 = ((~0x1F) & r0) */ ++ orr r0, r0, #0xd3 /* r0 = (0xd3 | r0) */ ++ msr cpsr, r0 ++ ++ mrc p15, 0, r0, c0, c0, 5 ++ and r0, r0, #15 ++ adr r4, 1f ++ ldmia r4, {r5, r6} ++ sub r4, r4, r5 ++ add r6, r6, r4 ++pen_36c: ldr r7, [r6] ++ cmp r7, r0 ++ bne pen_36c ++ ++ /* ++ * we've been released from the holding pen: secondary_stack ++ * should now contain the SVC stack for this core ++ */ ++ b secondary_startup ++ ++1: .long . ++ .long pen_release ++ENDPROC(hi3536c_secondary_startup) ++ ++ENTRY(hi3531d_secondary_startup) ++ /* set the cpu to SVC32 mode */ ++ mrs r0, cpsr ++ bic r0, r0, #0x1f /* r0 = ((~0x1F) & r0) */ ++ orr r0, r0, #0xd3 /* r0 = (0xd3 | r0) */ ++ msr cpsr, r0 ++ ++#ifdef CONFIG_ARCH_HI3531D ++ bl flash_cache_all ++#endif ++ ++ mrc p15, 0, r0, c0, c0, 5 ++ and r0, r0, #15 ++ adr r4, 1f ++ ldmia r4, {r5, r6} ++ sub r4, r4, r5 ++ add r6, r6, r4 ++pen: ldr r7, [r6] ++ cmp r7, r0 ++ bne pen ++ ++ /* ++ * we've been released from the holding pen: secondary_stack ++ * should now contain the SVC stack for this core ++ */ ++ b secondary_startup ++ ++ 1: .long . ++ .long pen_release ++ENDPROC(hi3531d_secondary_startup) ++ ++ENTRY(hi3521d_secondary_startup) ++ /* set the cpu to SVC32 mode */ ++ mrs r0, cpsr ++ bic r0, r0, #0x1f /* r0 = ((~0x1F) & r0) */ ++ orr r0, r0, #0xd3 /* r0 = (0xd3 | r0) */ ++ msr cpsr, r0 ++ ++ mrc p15, 0, r0, c0, c0, 5 ++ and r0, r0, #15 ++ adr r4, 1f ++ ldmia r4, {r5, r6} ++ sub r4, r4, r5 ++ add r6, r6, r4 ++pen_21d: ldr r7, [r6] ++ cmp r7, r0 ++ bne pen_21d ++ ++ /* ++ * we've been released from the holding pen: secondary_stack ++ * should now contain the SVC stack for this core ++ */ ++ b secondary_startup ++ ++1: .long . ++ .long pen_release ++ENDPROC(hi3521d_secondary_startup) ++ +diff --git a/arch/arm/mach-hisi/hibernate.c b/arch/arm/mach-hisi/hibernate.c +new file mode 100644 +index 0000000..bd90b23 +--- /dev/null ++++ b/arch/arm/mach-hisi/hibernate.c +@@ -0,0 +1,220 @@ ++/* ++ * ARM Cortex-A7 save/restore for suspend to disk (Hibernation) ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/spinlock.h> ++#include <linux/poll.h> ++#include <linux/delay.h> ++#include <linux/sysrq.h> ++#include <linux/proc_fs.h> ++#include <linux/pm.h> ++#include <linux/device.h> ++#include <linux/suspend.h> ++#include <asm/irq.h> ++#include <asm/uaccess.h> ++#include <asm/tlbflush.h> ++#include <asm/suspend.h> ++#include <asm/cacheflush.h> ++ ++typedef struct fault_regs { ++ unsigned long dfar; ++ unsigned long ifar; ++ unsigned long ifsr; ++ unsigned long dfsr; ++ unsigned long adfsr; ++ unsigned long aifsr; ++} cp15_fault_regs; ++ ++typedef struct vfp_context { ++ unsigned long fpexc; ++ unsigned long fpscr; ++ /*we just used low 32bit of d0-d15*/ ++ unsigned long d0_15[16]; ++} vfp_context; ++ ++typedef struct generic_timer_context { ++ unsigned long cntfrq; ++ unsigned long long cntvoff; ++ unsigned long cnthctl; ++ unsigned long cntkctl; ++ unsigned long long cntp_cval; ++ unsigned long cntp_tval; ++ unsigned long cntp_ctl; ++ unsigned long long cntv_cval; ++ unsigned long cntv_tval; ++ unsigned long cntv_ctl; ++ unsigned long long cnthp_cval; ++ unsigned long cnthp_tval; ++ unsigned long cnthp_ctl; ++} gen_timer_context; ++#if __LINUX_ARM_ARCH__ > 5 ++typedef struct ns_saved_context { ++ unsigned long cp15_misc_regs[2]; /* cp15 miscellaneous registers */ ++ unsigned long cp15_ctrl_regs[20]; /* cp15 control registers */ ++ unsigned long cp15_mmu_regs[16]; /* cp15 mmu registers */ ++ cp15_fault_regs cp15_fault_regs; /* cp15 fault status registers */ ++ gen_timer_context gen_timer_regs; ++ vfp_context vfp_regs; ++} saved_context; ++#else /* for ARM9 */ ++/* image of the saved processor state */ ++typedef struct ns_saved_context { ++ /* ++ * Structure saved_context would be used to hold processor state ++ * except caller and callee registers, just before suspending. ++ */ ++ ++ /* coprocessor 15 registers */ ++ // __u32 ID_code; /* read only reg */ ++ // __u32 cache_type; /* read only reg */ ++ // __u32 TCM_stat; /* read only reg */ ++ __u32 CR; ++ __u32 TTBR; ++ __u32 DACR; ++ __u32 D_FSR; ++ __u32 I_FSR; ++ __u32 FAR; ++ // __u32 COR; /*write only reg */ ++ // __u32 TLBOR; /*write only reg */ ++ __u32 D_CLR; ++ __u32 I_CLR; ++ __u32 D_TCMRR; ++ __u32 I_TCMRR; ++ __u32 TLBLR; ++ __u32 FCSE; ++ __u32 CID; ++} saved_context; ++#endif ++#ifdef CONFIG_DEBUG_LL ++extern void printascii(const char *); ++#endif ++ ++extern const void __nosave_begin, __nosave_end; ++ ++/*save&restore helper functions*/ ++extern asmlinkage void save_cp15(void *pointer); ++extern asmlinkage void save_control_registers(void *pointer, int is_secure); ++extern asmlinkage void save_mmu(void *pointer); ++extern asmlinkage void save_fault_status(void *pointer); ++extern asmlinkage void save_vfp(void *pointer); ++extern asmlinkage void save_generic_timer(void *pointer, int is_hyp); ++extern asmlinkage void restore_control_registers(void *pointer, int is_secure); ++ ++extern asmlinkage void restore_vfp(void *pointer); ++extern asmlinkage void restore_cp15(void *pointer); ++extern asmlinkage void restore_mmu(void *pointer); ++extern asmlinkage void restore_fault_status(void *pointer); ++extern asmlinkage void restore_generic_timer(void *pointer, int is_hyp); ++extern asmlinkage void invalidate_unified_TLB_inner_shareable(void); ++ ++/*variable define here*/ ++saved_context saved_ctx __attribute__((aligned(PAGE_SIZE))); ++ ++const void *saved_processor_context = &saved_ctx; ++ ++int pfn_is_nosave(unsigned long pfn) ++{ ++ unsigned long nosave_begin_pfn = __pa_symbol(&__nosave_begin) ++ >> PAGE_SHIFT; ++ unsigned long nosave_end_pfn = PAGE_ALIGN(__pa_symbol(&__nosave_end)) ++ >> PAGE_SHIFT; ++ ++ return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn); ++} ++ ++ ++void __save_processor_state(saved_context *ctxt) ++{ ++#ifdef CONFIG_DEBUG_LL ++ printascii("__save_processor_state(): enter\n"); ++#endif ++#if __LINUX_ARM_ARCH__ > 5 ++ save_cp15(ctxt->cp15_misc_regs); ++ ++ save_control_registers(ctxt->cp15_ctrl_regs, 0x0); ++ ++ save_mmu(ctxt->cp15_mmu_regs); ++ ++ save_fault_status(&ctxt->cp15_fault_regs); ++ ++ save_vfp(&ctxt->vfp_regs); ++#else /* for ARM9 */ ++ /* save coprocessor 15 registers */ ++ asm volatile ("mrc p15, 0, %0, c1, c0, 0" : "=r" (ctxt->CR)); ++ asm volatile ("mrc p15, 0, %0, c3, c0, 0" : "=r" (ctxt->DACR)); ++ asm volatile ("mrc p15, 0, %0, c5, c0, 0" : "=r" (ctxt->D_FSR)); ++ asm volatile ("mrc p15, 0, %0, c5, c0, 1" : "=r" (ctxt->I_FSR)); ++ asm volatile ("mrc p15, 0, %0, c6, c0, 0" : "=r" (ctxt->FAR)); ++ asm volatile ("mrc p15, 0, %0, c9, c0, 0" : "=r" (ctxt->D_CLR)); ++ asm volatile ("mrc p15, 0, %0, c9, c0, 1" : "=r" (ctxt->I_CLR)); ++ asm volatile ("mrc p15, 0, %0, c9, c1, 0" : "=r" (ctxt->D_TCMRR)); ++ asm volatile ("mrc p15, 0, %0, c9, c1, 1" : "=r" (ctxt->I_TCMRR)); ++ asm volatile ("mrc p15, 0, %0, c10, c0, 0" : "=r" (ctxt->TLBLR)); ++ asm volatile ("mrc p15, 0, %0, c13, c0, 0" : "=r" (ctxt->FCSE)); ++ asm volatile ("mrc p15, 0, %0, c13, c0, 1" : "=r" (ctxt->CID)); ++ asm volatile ("mrc p15, 0, %0, c2, c0, 0" : "=r" (ctxt->TTBR)); ++#endif ++ ++#ifdef CONFIG_DEBUG_LL ++ printascii("__save_processor_state(): exit\n"); ++#endif ++} ++ ++ ++void __restore_processor_state(saved_context *ctxt) ++{ ++#ifdef CONFIG_DEBUG_LL ++ printascii("__restore_processor_state(): enter\n"); ++#endif ++#if __LINUX_ARM_ARCH__ > 5 ++ restore_vfp(&ctxt->vfp_regs); ++ ++ restore_fault_status(&ctxt->cp15_fault_regs); ++ ++ restore_mmu(ctxt->cp15_mmu_regs); ++ ++ invalidate_unified_TLB_inner_shareable(); ++ ++ restore_control_registers(ctxt->cp15_ctrl_regs, 0x0); ++ isb(); ++ dsb(); ++ ++ restore_cp15(ctxt->cp15_misc_regs); ++#else /*for arm9 */ ++ /* restore coprocessor 15 registers */ ++ asm volatile ("mcr p15, 0, %0, c2, c0, 0" : : "r" (ctxt->TTBR)); ++ asm volatile ("mcr p15, 0, %0, c13, c0, 1" : : "r" (ctxt->CID)); ++ asm volatile ("mcr p15, 0, %0, c13, c0, 0" : : "r" (ctxt->FCSE)); ++ asm volatile ("mcr p15, 0, %0, c10, c0, 0" : : "r" (ctxt->TLBLR)); ++ asm volatile ("mcr p15, 0, %0, c9, c1, 1" : : "r" (ctxt->I_TCMRR)); ++ asm volatile ("mcr p15, 0, %0, c9, c1, 0" : : "r" (ctxt->D_TCMRR)); ++ asm volatile ("mcr p15, 0, %0, c9, c0, 1" : : "r" (ctxt->I_CLR)); ++ asm volatile ("mcr p15, 0, %0, c9, c0, 0" : : "r" (ctxt->D_CLR)); ++ asm volatile ("mcr p15, 0, %0, c6, c0, 0" : : "r" (ctxt->FAR)); ++ asm volatile ("mcr p15, 0, %0, c5, c0, 1" : : "r" (ctxt->I_FSR)); ++ asm volatile ("mcr p15, 0, %0, c5, c0, 0" : : "r" (ctxt->D_FSR)); ++ asm volatile ("mcr p15, 0, %0, c3, c0, 0" : : "r" (ctxt->DACR)); ++ asm volatile ("mcr p15, 0, %0, c1, c0, 0" : : "r" (ctxt->CR)); ++#endif ++ ++#ifdef CONFIG_DEBUG_LL ++ printascii("__restore_processor_state(): done\n"); ++#endif ++} ++ ++void save_processor_state(void) ++{ ++ preempt_disable(); ++ __save_processor_state(&saved_ctx); ++} ++EXPORT_SYMBOL(save_processor_state); ++ ++void restore_processor_state(void) ++{ ++ __restore_processor_state(&saved_ctx); ++ preempt_enable(); ++} ++EXPORT_SYMBOL(restore_processor_state); +diff --git a/arch/arm/mach-hisi/hisilicon.c b/arch/arm/mach-hisi/hisilicon.c +index 7744c35..43d9d78 100644 +--- a/arch/arm/mach-hisi/hisilicon.c ++++ b/arch/arm/mach-hisi/hisilicon.c +@@ -17,6 +17,7 @@ + + #include <asm/mach/arch.h> + #include <asm/mach/map.h> ++#include <mach/io.h> + + #define HI3620_SYSCTRL_PHYS_BASE 0xfc802000 + #define HI3620_SYSCTRL_VIRT_BASE 0xfe802000 +@@ -45,6 +46,268 @@ static void __init hi3620_map_io(void) + iotable_init(hi3620_io_desc, ARRAY_SIZE(hi3620_io_desc)); + } + ++extern unsigned long long notrace sched_clock(void); ++unsigned long long hi_sched_clock(void) ++{ ++ return sched_clock(); ++} ++EXPORT_SYMBOL(hi_sched_clock); ++ ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++static struct map_desc hi3519_io_desc[] __initdata = { ++ /* hi3519_IOCH1 */ ++ { ++ .virtual = HI3519_IOCH1_VIRT, ++ .pfn = __phys_to_pfn(HI3519_IOCH1_PHYS), ++ .length = HI3519_IOCH1_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3519_IOCH2 */ ++ { ++ .virtual = HI3519_IOCH2_VIRT, ++ .pfn = __phys_to_pfn(HI3519_IOCH2_PHYS), ++ .length = HI3519_IOCH2_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3519_IOCH2 */ ++ { ++ .virtual = HI3519_IOCH3_VIRT, ++ .pfn = __phys_to_pfn(HI3519_IOCH3_PHYS), ++ .length = HI3519_IOCH3_SIZE, ++ .type = MT_DEVICE ++ }, ++ ++}; ++ ++static void __init hi3519_reserve(void) ++{ ++#ifdef CONFIG_DMA_CMA ++ extern int hisi_declare_heap_memory(void); ++ hisi_declare_heap_memory(); ++#endif ++} ++#endif ++ ++#ifdef CONFIG_ARCH_HI3516AV200 ++static struct map_desc hi3516av200_io_desc[] __initdata = { ++ /* hi3516av200_IOCH1 */ ++ { ++ .virtual = HI3516AV200_IOCH1_VIRT, ++ .pfn = __phys_to_pfn(HI3516AV200_IOCH1_PHYS), ++ .length = HI3516AV200_IOCH1_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3516av200_IOCH2 */ ++ { ++ .virtual = HI3516AV200_IOCH2_VIRT, ++ .pfn = __phys_to_pfn(HI3516AV200_IOCH2_PHYS), ++ .length = HI3516AV200_IOCH2_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3516av200_IOCH2 */ ++ { ++ .virtual = HI3516AV200_IOCH3_VIRT, ++ .pfn = __phys_to_pfn(HI3516AV200_IOCH3_PHYS), ++ .length = HI3516AV200_IOCH3_SIZE, ++ .type = MT_DEVICE ++ }, ++ ++}; ++ ++static void __init hi3516av200_reserve(void) ++{ ++#ifdef CONFIG_DMA_CMA ++ extern int hisi_declare_heap_memory(void); ++ hisi_declare_heap_memory(); ++#endif ++} ++#endif ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++static struct map_desc hi3559_io_desc[] __initdata = { ++ /* hi3559_IOCH1 */ ++ { ++ .virtual = HI3559_IOCH1_VIRT, ++ .pfn = __phys_to_pfn(HI3559_IOCH1_PHYS), ++ .length = HI3559_IOCH1_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3559_IOCH2 */ ++ { ++ .virtual = HI3559_IOCH2_VIRT, ++ .pfn = __phys_to_pfn(HI3559_IOCH2_PHYS), ++ .length = HI3559_IOCH2_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3559_IOCH2 */ ++ { ++ .virtual = HI3559_IOCH3_VIRT, ++ .pfn = __phys_to_pfn(HI3559_IOCH3_PHYS), ++ .length = HI3559_IOCH3_SIZE, ++ .type = MT_DEVICE ++ }, ++ ++}; ++#endif ++ ++#ifdef CONFIG_ARCH_HI3536C ++static struct map_desc hi3536c_io_desc[] __initdata = { ++ /* hi3536c_IOCH1 */ ++ { ++ .virtual = HI3536C_IOCH1_VIRT, ++ .pfn = __phys_to_pfn(HI3536C_IOCH1_PHYS), ++ .length = HI3536C_IOCH1_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3536c_IOCH2 */ ++ { ++ .virtual = HI3536C_IOCH2_VIRT, ++ .pfn = __phys_to_pfn(HI3536C_IOCH2_PHYS), ++ .length = HI3536C_IOCH2_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3536c_IOCH3 */ ++ { ++ .virtual = HI3536C_IOCH3_VIRT, ++ .pfn = __phys_to_pfn(HI3536C_IOCH3_PHYS), ++ .length = HI3536C_IOCH3_SIZE, ++ .type = MT_DEVICE ++ }, ++}; ++ ++static void __init hi3536c_map_io(void) ++{ ++ iotable_init(hi3536c_io_desc, ARRAY_SIZE(hi3536c_io_desc)); ++} ++ ++static void __init hi3536c_init_early(void) ++{ ++ ++} ++#endif /* CONFIG_ARCH_HI3536C */ ++ ++#ifdef CONFIG_ARCH_HI3531D ++static struct map_desc hi3531d_io_desc[] __initdata = { ++ /* hi3531d_IOCH1 */ ++ { ++ .virtual = HI3531D_IOCH1_VIRT, ++ .pfn = __phys_to_pfn(HI3531D_IOCH1_PHYS), ++ .length = HI3531D_IOCH1_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3531d_IOCH2 */ ++ { ++ .virtual = HI3531D_IOCH2_VIRT, ++ .pfn = __phys_to_pfn(HI3531D_IOCH2_PHYS), ++ .length = HI3531D_IOCH2_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3531d_IOCH3 */ ++ { ++ .virtual = HI3531D_IOCH3_VIRT, ++ .pfn = __phys_to_pfn(HI3531D_IOCH3_PHYS), ++ .length = HI3531D_IOCH3_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3531d_IOCH4 */ ++ { ++ .virtual = HI3531D_IOCH4_VIRT, ++ .pfn = __phys_to_pfn(HI3531D_IOCH4_PHYS), ++ .length = HI3531D_IOCH4_SIZE, ++ .type = MT_DEVICE ++ }, ++ ++}; ++ ++void __init hi3531d_map_io(void) ++{ ++ iotable_init(hi3531d_io_desc, ARRAY_SIZE(hi3531d_io_desc)); ++} ++ ++static void __init hi3531d_init_early(void) ++{ ++ /* ++ * 1. enable L1 prefetch [2] ++ * 4. enable allocation in one cache way only. [8] ++ */ ++ asm volatile ( ++ " mrc p15, 0, r0, c1, c0, 1\n" ++ " orr r0, r0, #0x104\n" ++ " mcr p15, 0, r0, c1, c0, 1\n" ++ : ++ : ++ : "r0", "cc"); ++ ++} ++#endif ++ ++#ifdef CONFIG_ARCH_HI3521D ++static struct map_desc hi3521d_io_desc[] __initdata = { ++ /* hi3521d_IOCH1 */ ++ { ++ .virtual = HI3521D_IOCH1_VIRT, ++ .pfn = __phys_to_pfn(HI3521D_IOCH1_PHYS), ++ .length = HI3521D_IOCH1_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3521d_IOCH2 */ ++ { ++ .virtual = HI3521D_IOCH2_VIRT, ++ .pfn = __phys_to_pfn(HI3521D_IOCH2_PHYS), ++ .length = HI3521D_IOCH2_SIZE, ++ .type = MT_DEVICE ++ }, ++ /* hi3521d_IOCH3 */ ++ { ++ .virtual = HI3521D_IOCH3_VIRT, ++ .pfn = __phys_to_pfn(HI3521D_IOCH3_PHYS), ++ .length = HI3521D_IOCH3_SIZE, ++ .type = MT_DEVICE ++ }, ++}; ++ ++static void __init hi3521d_map_io(void) ++{ ++ iotable_init(hi3521d_io_desc, ARRAY_SIZE(hi3521d_io_desc)); ++} ++ ++static void __init hi3521d_init_early(void) ++{ ++ ++} ++#endif /* CONFIG_ARCH_HI3521D */ ++ ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++static void __init hi3519_map_io(void) ++{ ++ /* debug_ll_io_init(); */ ++ iotable_init(hi3519_io_desc, ARRAY_SIZE(hi3519_io_desc)); ++} ++#endif ++ ++#ifdef CONFIG_ARCH_HI3516AV200 ++static void __init hi3516av200_map_io(void) ++{ ++ /* debug_ll_io_init(); */ ++ iotable_init(hi3516av200_io_desc, ARRAY_SIZE(hi3516av200_io_desc)); ++} ++#endif ++ ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++static void __init hi3559_map_io(void) ++{ ++ /* debug_ll_io_init(); */ ++ iotable_init(hi3559_io_desc, ARRAY_SIZE(hi3559_io_desc)); ++} ++ ++static void __init hi3559_reserve(void) ++{ ++#ifdef CONFIG_DMA_CMA ++ extern int hisi_declare_heap_memory(void); ++ hisi_declare_heap_memory(); ++#endif ++} ++#endif ++ + static const char *hi3xxx_compat[] __initconst = { + "hisilicon,hi3620-hi4511", + NULL, +@@ -72,3 +335,100 @@ static const char *hip04_compat[] __initconst = { + DT_MACHINE_START(HIP04, "Hisilicon HiP04 (Flattened Device Tree)") + .dt_compat = hip04_compat, + MACHINE_END ++ ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++static const char *hi3519_compat[] __initconst = { ++ "hisilicon,hi3519", ++ "hisilicon,hi3519v101", ++ NULL, ++}; ++#endif ++ ++#ifdef CONFIG_ARCH_HI3516AV200 ++static const char *hi3516av200_compat[] __initconst = { ++ "hisilicon,hi3516av200", ++ NULL, ++}; ++#endif ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++static const char *hi3559_compat[] __initconst = { ++ "hisilicon,hi3559", ++ "hisilicon,hi3556", ++ NULL, ++}; ++#endif ++ ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++DT_MACHINE_START(HI3519_DT, "Hisilicon Hi3519 (Flattened Device Tree)") ++ .map_io = hi3519_map_io, ++ .dt_compat = hi3519_compat, ++ .reserve = hi3519_reserve, ++MACHINE_END ++#endif ++ ++#ifdef CONFIG_ARCH_HI3516AV200 ++DT_MACHINE_START(HI3516AV200_DT, "Hisilicon Hi3516av200 (Flattened Device Tree)") ++ .map_io = hi3516av200_map_io, ++ .dt_compat = hi3516av200_compat, ++ .reserve = hi3516av200_reserve, ++MACHINE_END ++#endif ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++DT_MACHINE_START(HI3559_DT, "Hisilicon Hi3559/Hi3556 (Flattened Device Tree)") ++ .map_io = hi3559_map_io, ++ .dt_compat = hi3559_compat, ++ .reserve = hi3559_reserve, ++MACHINE_END ++#endif ++ ++#ifdef CONFIG_ARCH_HI3516CV300 ++static const char *hi3516cv300_compat[] __initconst = { ++ "hisilicon,hi3516cv300", ++ NULL, ++}; ++ ++DT_MACHINE_START(HI3516CV300_DT, ++ "Hisilicon Hi3516cv300 (Flattened Device Tree)") ++ .dt_compat = hi3516cv300_compat, ++MACHINE_END ++#endif ++ ++#ifdef CONFIG_ARCH_HI3536C ++static const char *hi3536c_compat[] __initconst = { ++ "hisilicon,hi3536c", ++ NULL, ++}; ++ ++DT_MACHINE_START(HI3536C_DT, "Hisilicon Hi3536C (Flattened Device Tree)") ++ .map_io = hi3536c_map_io, ++ .dt_compat = hi3536c_compat, ++ .init_early = hi3536c_init_early, ++MACHINE_END ++#endif ++ ++#ifdef CONFIG_ARCH_HI3531D ++static const char *hi3531d_compat[] __initconst = { ++ "hisilicon,hi3531d", ++ NULL, ++}; ++ ++DT_MACHINE_START(HI3531D_DT, "Hisilicon Hi3531D (Flattened Device Tree)") ++ .map_io = hi3531d_map_io, ++ .dt_compat = hi3531d_compat, ++ .init_early = hi3531d_init_early, ++MACHINE_END ++#endif ++ ++#ifdef CONFIG_ARCH_HI3521D ++static const char *hi3521d_compat[] __initconst = { ++ "hisilicon,hi3521d", ++ NULL, ++}; ++ ++DT_MACHINE_START(HI3521D_DT, "Hisilicon Hi3521D (Flattened Device Tree)") ++ .map_io = hi3521d_map_io, ++ .dt_compat = hi3521d_compat, ++ .init_early = hi3521d_init_early, ++MACHINE_END ++#endif ++ +diff --git a/arch/arm/mach-hisi/hotplug.c b/arch/arm/mach-hisi/hotplug.c +index 84e6919..5f3ebf6 100644 +--- a/arch/arm/mach-hisi/hotplug.c ++++ b/arch/arm/mach-hisi/hotplug.c +@@ -11,11 +11,22 @@ + #include <linux/delay.h> + #include <linux/io.h> + #include <linux/of_address.h> ++#include <linux/arm-cci.h> ++#include <linux/irqchip/arm-gic.h> + #include <linux/of_platform.h> + #include <asm/cacheflush.h> + #include <asm/smp_plat.h> ++#include <asm/cp15.h> ++ + #include "core.h" + ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3536C) \ ++ || defined(CONFIG_ARCH_HI3521D)) ++#include <mach/io.h> ++#include <mach/platform.h> ++#endif ++ + /* Sysctrl registers in Hi3620 SoC */ + #define SCISOEN 0xc0 + #define SCISODIS 0xc4 +@@ -257,4 +268,289 @@ void hix5hd2_cpu_die(unsigned int cpu) + flush_cache_all(); + hix5hd2_set_cpu(cpu, false); + } ++ ++int hi3519_cpu_kill(unsigned int cpu) ++{ ++#ifdef CONFIG_ARCH_HI3519 ++ hi_pmc_kill_cpu(cpu); ++#endif ++ return 1; ++} ++ ++int hi3559_cpu_kill(unsigned int cpu) ++{ ++ return 1; ++} ++ ++int hi3516av200_cpu_kill(unsigned int cpu) ++{ ++ return 1; ++} ++ ++void hi3519_cpu_die(unsigned int cpu) ++{ ++ /*avoid interrupt to disturb wfi&&pmc sequence */ ++ gic_cpu_if_down(); ++ /* disable Dcache */ ++ set_cr(get_cr() & ~CR_C); ++ /* CLREX */ ++ asm volatile ("clrex"); ++ ++ /* Clean & Invalidata L1 Data Cache, L2 Cache */ ++ /* flush_cache_all(); */ ++ ++ /* clean&invalidate l1 cache */ ++ asm volatile("mov r0, #0\n"); ++ asm volatile("mcr p15, 1, r0, c15, c14, 0\n"); ++ asm volatile("dsb\n"); ++ ++ /* clean&invalidate l2 cache */ ++ asm volatile("mov r0, #2\n"); ++ asm volatile("mcr p15, 1, r0, c15, c14, 0\n"); ++ asm volatile("dsb\n"); ++ ++ /* Set ACTLR.SMP to 0, AMP -> SMP */ ++ asm volatile ( ++ " mrc p15, 0, r0, c1, c0, 1\n" ++ " bic r0, #0x40\n" ++ " mcr p15, 0, r0, c1, c0, 1\n" ++ : ++ : ++ : "r0", "cc"); ++ ++ /* disable cci snoop */ ++ cci_disable_port_by_cpu(cpu_logical_map(cpu)); ++ ++ /* avoid debug event wake up cpu */ ++ /* asm volatile("mcr p14, 0, %0, c1, c3, 0" : : "r" (1)); */ ++ ++ /* ISB & DSB */ ++ isb(); ++ dsb(); ++ ++ /* clear core ac inactive */ ++ hi_pmc_set_ac_inactive(); ++ ++ /* power down */ ++ hi_pmc_power_down(); ++ ++ dsb(); ++ /* wfi */ ++ while (1) ++ wfi(); ++ ++} ++ ++void hi3516av200_cpu_die(unsigned int cpu) ++{ ++ /*avoid interrupt to disturb wfi&&pmc sequence */ ++ gic_cpu_if_down(); ++ /* disable Dcache */ ++ set_cr(get_cr() & ~CR_C); ++ /* CLREX */ ++ asm volatile ("clrex"); ++ ++ /* Clean & Invalidata L1 Data Cache, L2 Cache */ ++ /* flush_cache_all(); */ ++ ++ /* clean&invalidate l1 cache */ ++ asm volatile("mov r0, #0\n"); ++ asm volatile("mcr p15, 1, r0, c15, c14, 0\n"); ++ asm volatile("dsb\n"); ++ ++ /* clean&invalidate l2 cache */ ++ asm volatile("mov r0, #2\n"); ++ asm volatile("mcr p15, 1, r0, c15, c14, 0\n"); ++ asm volatile("dsb\n"); ++ ++ /* Set ACTLR.SMP to 0, AMP -> SMP */ ++ asm volatile ( ++ " mrc p15, 0, r0, c1, c0, 1\n" ++ " bic r0, #0x40\n" ++ " mcr p15, 0, r0, c1, c0, 1\n" ++ : ++ : ++ : "r0", "cc"); ++ ++ /* disable cci snoop */ ++ cci_disable_port_by_cpu(cpu_logical_map(cpu)); ++ ++ /* avoid debug event wake up cpu */ ++ /* asm volatile("mcr p14, 0, %0, c1, c3, 0" : : "r" (1)); */ ++ ++ /* ISB & DSB */ ++ isb(); ++ dsb(); ++ ++ /* clear core ac inactive */ ++ hi_pmc_set_ac_inactive(); ++ ++ /* power down */ ++ hi_pmc_power_down(); ++ ++ dsb(); ++ /* wfi */ ++ while (1) ++ wfi(); ++ ++} ++ ++void hi3559_cpu_die(unsigned int cpu) ++{ ++ /*avoid interrupt to disturb wfi&&pmc sequence */ ++ gic_cpu_if_down(); ++ /* disable Dcache */ ++ set_cr(get_cr() & ~CR_C); ++ /* CLREX */ ++ asm volatile ("clrex"); ++ ++ /* Clean & Invalidata L1 Data Cache, L2 Cache */ ++ /* flush_cache_all(); */ ++ ++ /* clean&invalidate l1 cache */ ++ asm volatile("mov r0, #0\n"); ++ asm volatile("mcr p15, 1, r0, c15, c14, 0\n"); ++ asm volatile("dsb\n"); ++ ++ /* clean&invalidate l2 cache */ ++ asm volatile("mov r0, #2\n"); ++ asm volatile("mcr p15, 1, r0, c15, c14, 0\n"); ++ asm volatile("dsb\n"); ++ ++ /* Set ACTLR.SMP to 0, AMP -> SMP */ ++ asm volatile ( ++ " mrc p15, 0, r0, c1, c0, 1\n" ++ " bic r0, #0x40\n" ++ " mcr p15, 0, r0, c1, c0, 1\n" ++ : ++ : ++ : "r0", "cc"); ++ ++ /* disable cci snoop */ ++ cci_disable_port_by_cpu(cpu_logical_map(cpu)); ++ ++ /* avoid debug event wake up cpu */ ++ /* asm volatile("mcr p14, 0, %0, c1, c3, 0" : : "r" (1)); */ ++ ++ /* ISB & DSB */ ++ isb(); ++ dsb(); ++ ++ /* clear core ac inactive */ ++ hi_pmc_set_ac_inactive(); ++ ++ /* power down */ ++ hi_pmc_power_down(); ++ ++ dsb(); ++ /* wfi */ ++ while (1) ++ wfi(); ++ ++} ++ ++int hi3536c_cpu_kill(unsigned int cpu) ++{ ++ return 1; ++} ++ ++void hi3536c_scu_power_up(int cpu) ++{ ++#ifdef CONFIG_ARCH_HI3536C ++ unsigned int regval; ++ ++ /* clear the slave cpu reset */ ++ regval = readl(__io_address(CRG_REG_BASE + REG_A7_SRST_CRG)); ++ regval &= ~CORE1_SRST_REQ; ++ writel(regval, __io_address(CRG_REG_BASE + REG_A7_SRST_CRG)); ++#endif ++} ++ ++static inline void hi3536c_scu_power_off(int cpu) ++{ ++#ifdef CONFIG_ARCH_HI3536C ++ unsigned long regval; ++ ++ regval = readl(__io_address(CRG_REG_BASE + REG_A7_SRST_CRG)); ++ regval |= (DBG1_SRST_REQ | CORE1_SRST_REQ); ++ writel(regval, __io_address(CRG_REG_BASE + REG_A7_SRST_CRG)); ++#endif ++} ++ ++void hi3536c_cpu_die(unsigned int cpu) ++{ ++ flush_cache_all(); ++ hi3536c_scu_power_off(cpu); ++ BUG(); ++} ++ ++int hi3531d_cpu_kill(unsigned int cpu) ++{ ++ return 1; ++} ++ ++void hi3531d_scu_power_up(int cpu) ++{ ++#ifdef CONFIG_ARCH_HI3531D ++ unsigned int regval; ++ ++ /* clear the slave cpu reset */ ++ regval = readl(__io_address(CRG_REG_BASE + REG_A9_SRST_CRG)); ++ regval &= ~CPU1_SRST_REQ; ++ writel(regval, __io_address(CRG_REG_BASE + REG_A9_SRST_CRG)); ++#endif ++} ++ ++static inline void hi3531d_scu_power_off(int cpu) ++{ ++#ifdef CONFIG_ARCH_HI3531D ++ unsigned long regval; ++ ++ regval = readl(__io_address(CRG_REG_BASE + REG_A9_SRST_CRG)); ++ regval |= (WDG1_SRST_REQ | DBG1_SRST_REQ | CPU1_SRST_REQ); ++ writel(regval, __io_address(CRG_REG_BASE + REG_A9_SRST_CRG)); ++#endif ++} ++ ++void hi3531d_cpu_die(unsigned int cpu) ++{ ++ flush_cache_all(); ++ hi3531d_scu_power_off(cpu); ++ BUG(); ++} ++ ++int hi3521d_cpu_kill(unsigned int cpu) ++{ ++ return 1; ++} ++ ++void hi3521d_scu_power_up(int cpu) ++{ ++#ifdef CONFIG_ARCH_HI3521D ++ unsigned int regval; ++ ++ /* clear the slave cpu reset */ ++ regval = readl(__io_address(CRG_REG_BASE + REG_A7_SRST_CRG)); ++ regval &= ~CORE1_SRST_REQ; ++ writel(regval, __io_address(CRG_REG_BASE + REG_A7_SRST_CRG)); ++#endif ++} ++ ++static inline void hi3521d_scu_power_off(int cpu) ++{ ++#ifdef CONFIG_ARCH_HI3521D ++ unsigned long regval; ++ ++ regval = readl(__io_address(CRG_REG_BASE + REG_A7_SRST_CRG)); ++ regval |= (DBG1_SRST_REQ | CORE1_SRST_REQ); ++ writel(regval, __io_address(CRG_REG_BASE + REG_A7_SRST_CRG)); ++#endif ++} ++ ++void hi3521d_cpu_die(unsigned int cpu) ++{ ++ flush_cache_all(); ++ hi3521d_scu_power_off(cpu); ++ BUG(); ++} + #endif +diff --git a/arch/arm/mach-hisi/l2cache.c b/arch/arm/mach-hisi/l2cache.c +new file mode 100644 +index 0000000..52aa2ec +--- /dev/null ++++ b/arch/arm/mach-hisi/l2cache.c +@@ -0,0 +1,71 @@ ++/* ++ * Copyright (c) 2015-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#define pr_fmt(fmt) "l2cache: " fmt ++ ++#include <linux/io.h> ++#include <linux/init.h> ++#include <asm/cacheflush.h> ++#include <asm/hardware/cache-l2x0.h> ++#include <mach/io.h> ++#include <mach/platform.h> ++ ++static void __iomem *l2x0_virt_base = __io_address(REG_BASE_L2CACHE); ++ ++static int __init l2_cache_init(void) ++{ ++ u32 val; ++ /* ++ * Bits Value Description ++ * [31] 0 : SBZ ++ * [30] 1 : Double linefill enable (L3) ++ * [29] 1 : Instruction prefetching enable ++ * [28] 1 : Data prefetching enabled ++ * [27] 0 : Double linefill on WRAP read enabled (L3) ++ * [26:25] 0 : SBZ ++ * [24] 1 : Prefetch drop enable (L3) ++ * [23] 0 : Incr double Linefill enable (L3) ++ * [22] 0 : SBZ ++ * [21] 0 : Not same ID on exclusive sequence enable (L3) ++ * [20:5] 0 : SBZ ++ * [4:0] 0 : use the Prefetch offset values 0. ++ */ ++ /* writel_relaxed(0x71000000, l2x0_virt_base + L2X0_PREFETCH_CTRL); */ ++ writel_relaxed(0x71000000, l2x0_virt_base + L310_PREFETCH_CTRL); ++ ++ val = __raw_readl(l2x0_virt_base + L2X0_AUX_CTRL); ++ val |= (1 << 30); /* Early BRESP enabled */ ++ val |= (1 << 0); /* Full Line of Zero Enable */ ++ writel_relaxed(val, l2x0_virt_base + L2X0_AUX_CTRL); ++ l2x0_init(l2x0_virt_base, 0x00430000, 0xFFB0FFFF); ++ /* ++ * 2. enable L2 prefetch hint [1]a ++ * 3. enable write full line of zeros mode. [3]a ++ * a: This feature must be enabled only when the slaves ++ * connected on the Cortex-A9 AXI master port support it. ++ */ ++ asm volatile ( ++ " mrc p15, 0, r0, c1, c0, 1\n" ++ " orr r0, r0, #0x02\n" ++ " mcr p15, 0, r0, c1, c0, 1\n" ++ : ++ : ++ : "r0", "cc"); ++ ++ return 0; ++} ++early_initcall(l2_cache_init); +diff --git a/arch/arm/mach-hisi/platsmp.c b/arch/arm/mach-hisi/platsmp.c +index 575dd82..da14096 100644 +--- a/arch/arm/mach-hisi/platsmp.c ++++ b/arch/arm/mach-hisi/platsmp.c +@@ -7,19 +7,48 @@ + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ ++ ++#include <linux/delay.h> + #include <linux/smp.h> + #include <linux/io.h> + #include <linux/of_address.h> +- + #include <asm/cacheflush.h> + #include <asm/smp_plat.h> + #include <asm/smp_scu.h> + ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3536C) \ ++ || defined(CONFIG_ARCH_HI3521D)) ++#include <mach/io.h> ++#include <mach/platform.h> ++#endif ++ + #include "core.h" + + #define HIX5HD2_BOOT_ADDRESS 0xffff0000 ++#define HI3519_BOOT_ADDRESS 0x00000000 ++#define HI3559_BOOT_ADDRESS 0x00000000 ++#define HI3516AV200_BOOT_ADDRESS 0x00000000 ++#define HI3536C_BOOT_ADDRESS 0x00000000 ++#define HI3531D_BOOT_ADDRESS 0x00000000 ++#define HI3521D_BOOT_ADDRESS 0x00000000 + ++static void __iomem *hi3519_bootaddr; ++static void __iomem *hi3559_bootaddr; ++static void __iomem *hi3516av200_bootaddr; ++static void __iomem *hi3536c_bootaddr; ++static void __iomem *hi3531d_bootaddr; ++static void __iomem *hi3521d_bootaddr; + static void __iomem *ctrl_base; ++static DEFINE_SPINLOCK(boot_lock); ++ ++static void __cpuinit hi_write_pen_release(int val) ++{ ++ pen_release = val; ++ smp_wmb(); ++ __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release)); ++ outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1)); ++} + + void hi3xxx_set_cpu_jump(int cpu, void *jump_addr) + { +@@ -132,5 +161,620 @@ struct smp_operations hix5hd2_smp_ops __initdata = { + #endif + }; + ++void hi3519_set_cpu_jump(unsigned int cpu, phys_addr_t jumpaddr) ++{ ++ /* only cortex-a17 boot from phys 0 address */ ++ if (cpu != 1) ++ return; ++ /* ldr pc, [rc, #-4] */ ++ writel_relaxed(0xe51ff004, hi3519_bootaddr); ++ /* pc jump phy address */ ++ writel_relaxed(jumpaddr, hi3519_bootaddr + 4); ++ ++ dsb(); ++} ++ ++void hi3516av200_set_cpu_jump(unsigned int cpu, phys_addr_t jumpaddr) ++{ ++ /* only cortex-a17 boot from phys 0 address */ ++ if (cpu != 1) ++ return; ++ /* ldr pc, [rc, #-4] */ ++ writel_relaxed(0xe51ff004, hi3516av200_bootaddr); ++ /* pc jump phy address */ ++ writel_relaxed(jumpaddr, hi3516av200_bootaddr + 4); ++ ++ dsb(); ++} ++ ++void hi3559_set_cpu_jump(unsigned int cpu, phys_addr_t jumpaddr) ++{ ++ /* only cortex-a17 boot from phys 0 address */ ++ if (cpu != 1) ++ return; ++ /* ldr pc, [rc, #-4] */ ++ writel_relaxed(0xe51ff004, hi3559_bootaddr); ++ /* pc jump phy address */ ++ writel_relaxed(jumpaddr, hi3559_bootaddr + 4); ++ ++ dsb(); ++} ++ ++static void __init hi3519_smp_prepare_cpus(unsigned int max_cpus) ++{ ++ if (!hi3519_bootaddr) ++ hi3519_bootaddr = ioremap(HI3519_BOOT_ADDRESS, PAGE_SIZE); ++ ++ sync_cache_w(&hi3519_bootaddr); ++} ++ ++static void __init hi3516av200_smp_prepare_cpus(unsigned int max_cpus) ++{ ++ if (!hi3516av200_bootaddr) ++ hi3516av200_bootaddr = ioremap(HI3516AV200_BOOT_ADDRESS, PAGE_SIZE); ++ ++ sync_cache_w(&hi3516av200_bootaddr); ++} ++ ++static void __init hi3559_smp_prepare_cpus(unsigned int max_cpus) ++{ ++ if (!hi3559_bootaddr) ++ hi3559_bootaddr = ioremap(HI3559_BOOT_ADDRESS, PAGE_SIZE); ++ ++ sync_cache_w(&hi3559_bootaddr); ++} ++ ++static int hi3519_boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ flush_cache_all(); ++ ++ hi3519_set_cpu_jump(cpu, virt_to_phys(hi3519_secondary_startup)); ++ ++ hi_pmc_power_up(); ++ ++ return 0; ++} ++ ++static int hi3516av200_boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ flush_cache_all(); ++ ++ hi3516av200_set_cpu_jump(cpu, virt_to_phys(hi3516av200_secondary_startup)); ++ ++ hi_pmc_power_up(); ++ ++ return 0; ++} ++ ++static int hi3559_boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ flush_cache_all(); ++ ++ hi3559_set_cpu_jump(cpu, virt_to_phys(hi3559_secondary_startup)); ++ ++ hi_pmc_power_up(); ++ ++ return 0; ++} ++ ++static void HI3519_secondary_init(unsigned int cpu) ++{ ++/* ++ hi_pmc_power_up_done(); ++*/ ++} ++ ++static void HI3516av200_secondary_init(unsigned int cpu) ++{ ++/* ++ hi_pmc_power_up_done(); ++*/ ++} ++ ++static void HI3559_secondary_init(unsigned int cpu) ++{ ++/* ++ hi_pmc_power_up_done(); ++*/ ++} ++ ++void hi3536c_set_cpu_jump(unsigned int cpu, phys_addr_t jumpaddr) ++{ ++ /* only cortex-a17 boot from phys 0 address */ ++ if (cpu != 1) ++ return; ++ /* ldr pc, [rc, #-4] */ ++ writel_relaxed(0xe51ff004, hi3536c_bootaddr); ++ /* pc jump phy address */ ++ writel_relaxed(jumpaddr, hi3536c_bootaddr + 4); ++ ++ dsb(); ++} ++ ++void hi3531d_set_cpu_jump(unsigned int cpu, phys_addr_t jumpaddr) ++{ ++ /* only cortex-a17 boot from phys 0 address */ ++ if (cpu != 1) ++ return; ++ /* ldr pc, [rc, #-4] */ ++ writel_relaxed(0xe51ff004, hi3531d_bootaddr); ++ /* pc jump phy address */ ++ writel_relaxed(jumpaddr, hi3531d_bootaddr + 4); ++ ++ dsb(); ++} ++ ++void hi3521d_set_cpu_jump(unsigned int cpu, phys_addr_t jumpaddr) ++{ ++ /* only cortex-a17 boot from phys 0 address */ ++ if (cpu != 1) ++ return; ++ /* ldr pc, [rc, #-4] */ ++ writel_relaxed(0xe51ff004, hi3521d_bootaddr); ++ /* pc jump phy address */ ++ writel_relaxed(jumpaddr, hi3521d_bootaddr + 4); ++ ++ dsb(); ++} ++ ++static void __iomem *scu_base_addr(void) ++{ ++#ifdef CONFIG_ARCH_HI3531D ++ return __io_address(A9_PERI_BASE + REG_A9_PERI_SCU); ++#elif (defined(CONFIG_ARCH_HI3536C) \ ++ || defined(CONFIG_ARCH_HI3521D)) ++ return __io_address(A7_PERI_BASE + REG_A7_PERI_SCU); ++#endif ++ return NULL; ++} ++ ++static void __init hi3536c_smp_prepare_cpus(unsigned int max_cpus) ++{ ++ void __iomem *base = NULL; ++ ++ base = scu_base_addr(); ++ if (!base) ++ return; ++ ++ scu_enable(base); ++} ++ ++static void __init hi3531d_smp_prepare_cpus(unsigned int max_cpus) ++{ ++ scu_enable(scu_base_addr()); ++} ++ ++static void __init hi3521d_smp_prepare_cpus(unsigned int max_cpus) ++{ ++ scu_enable(scu_base_addr()); ++} ++ ++/*****************************************************************************/ ++/* ++ * copy startup code to sram, and flash cache. ++ * @start_addr: slave start phy address ++ * @jump_addr: slave jump phy address ++ */ ++static void hi3536c_set_scu_boot_addr(unsigned int start_addr, ++ unsigned int jump_addr) ++{ ++ unsigned int *virtaddr; ++ unsigned int *p_virtaddr; ++ ++ p_virtaddr = virtaddr = ioremap(start_addr, PAGE_SIZE); ++ ++ *p_virtaddr++ = 0xe51ff004; /* ldr pc, [pc, #-4] */ ++ *p_virtaddr++ = jump_addr; /* pc jump phy address */ ++ ++ smp_wmb(); ++ ++ __cpuc_flush_dcache_area((void *)virtaddr, ++ (size_t)((char *)p_virtaddr - (char *)virtaddr)); ++ outer_clean_range(__pa(virtaddr), __pa(p_virtaddr)); ++ ++ iounmap(virtaddr); ++} ++ ++/*****************************************************************************/ ++/* ++ * copy startup code to sram, and flash cache. ++ * @start_addr: slave start phy address ++ * @jump_addr: slave jump phy address ++ */ ++static void hi3531d_set_scu_boot_addr(unsigned int start_addr, ++ unsigned int jump_addr) ++{ ++ unsigned int *virtaddr; ++ unsigned int *p_virtaddr; ++ ++ p_virtaddr = virtaddr = ioremap(start_addr, PAGE_SIZE); ++ ++ *p_virtaddr++ = 0xe51ff004; /* ldr pc, [pc, #-4] */ ++ *p_virtaddr++ = jump_addr; /* pc jump phy address */ ++ ++ smp_wmb(); ++ ++ __cpuc_flush_dcache_area((void *)virtaddr, ++ (size_t)((char *)p_virtaddr - (char *)virtaddr)); ++ outer_clean_range(__pa(virtaddr), __pa(p_virtaddr)); ++ ++ iounmap(virtaddr); ++} ++ ++static void hi3521d_set_scu_boot_addr(unsigned int start_addr, ++ unsigned int jump_addr) ++{ ++ unsigned int *virtaddr; ++ unsigned int *p_virtaddr; ++ ++ p_virtaddr = virtaddr = ioremap(start_addr, PAGE_SIZE); ++ ++ *p_virtaddr++ = 0xe51ff004; /* ldr pc, [pc, #-4] */ ++ *p_virtaddr++ = jump_addr; /* pc jump phy address */ ++ ++ smp_wmb(); ++ ++ __cpuc_flush_dcache_area((void *)virtaddr, ++ (size_t)((char *)p_virtaddr - (char *)virtaddr)); ++ outer_clean_range(__pa(virtaddr), __pa(p_virtaddr)); ++ ++ iounmap(virtaddr); ++} ++ ++static int __cpuinit hi3536c_boot_secondary(unsigned int cpu, ++ struct task_struct *idle) ++{ ++ unsigned long timeout; ++ ++ hi3536c_set_scu_boot_addr(0x00000000, ++ (unsigned int)virt_to_phys(hi3536c_secondary_startup)); ++ ++ /* ++ * set synchronisation state between this boot processor ++ * and the secondary one ++ */ ++ spin_lock(&boot_lock); ++ ++ hi3536c_scu_power_up(cpu); ++ /* ++ * The secondary processor is waiting to be released from ++ * the holding pen - release it, then wait for it to flag ++ * that it has been released by resetting pen_release. ++ * ++ * Note that "pen_release" is the hardware CPU ID, whereas ++ * "cpu" is Linux's internal ID. ++ */ ++ hi_write_pen_release(cpu); ++ ++ /* ++ * Send the secondary CPU a soft interrupt, thereby causing ++ * the boot monitor to read the system wide flags register, ++ * and branch to the address found there. ++ */ ++ arch_send_wakeup_ipi_mask(cpumask_of(cpu)); ++ ++ /* ++ * Send the secondary CPU a soft interrupt, thereby causing ++ * the boot monitor to read the system wide flags register, ++ * and branch to the address found there. ++ */ ++ timeout = jiffies + (5 * HZ); ++ while (time_before(jiffies, timeout)) { ++ smp_rmb(); ++ if (pen_release == -1) ++ break; ++ ++ udelay(10); ++ } ++ ++ /* ++ * now the secondary core is starting up let it run its ++ * calibrations, then wait for it to finish ++ */ ++ spin_unlock(&boot_lock); ++ ++ return pen_release != -1 ? -ENOSYS : 0; ++} ++ ++static int hi3531d_boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ unsigned long timeout; ++ ++ hi3531d_set_scu_boot_addr(0x00000000, ++ (unsigned int)virt_to_phys(hi3531d_secondary_startup)); ++ ++ /* ++ * * set synchronisation state between this boot processor ++ * * and the secondary one ++ * */ ++ spin_lock(&boot_lock); ++ ++ hi3531d_scu_power_up(cpu); ++ ++ /* ++ * The secondary processor is waiting to be released from ++ * the holding pen - release it, then wait for it to flag ++ * that it has been released by resetting pen_release. ++ * ++ * Note that "pen_release" is the hardware CPU ID, whereas ++ * "cpu" is Linux's internal ID. ++ */ ++ hi_write_pen_release(cpu); ++ ++ /* ++ * Send the secondary CPU a soft interrupt, thereby causing ++ * the boot monitor to read the system wide flags register, ++ * and branch to the address found there. ++ */ ++ arch_send_wakeup_ipi_mask(cpumask_of(cpu)); ++ ++ /* ++ * Send the secondary CPU a soft interrupt, thereby causing ++ * the boot monitor to read the system wide flags register, ++ * and branch to the address found there. ++ */ ++ timeout = jiffies + (5 * HZ); ++ while (time_before(jiffies, timeout)) { ++ smp_rmb(); ++ if (pen_release == -1) ++ break; ++ udelay(10); ++ } ++ ++ /* ++ * now the secondary core is starting up let it run its ++ * calibrations, then wait for it to finish ++ */ ++ spin_unlock(&boot_lock); ++ return pen_release != -1 ? -ENOSYS : 0; ++} ++ ++static int __cpuinit hi3521d_boot_secondary(unsigned int cpu, ++ struct task_struct *idle) ++{ ++ unsigned long timeout; ++ ++ hi3521d_set_scu_boot_addr(0x00000000, ++ (unsigned int)virt_to_phys(hi3521d_secondary_startup)); ++ ++ /* ++ * set synchronisation state between this boot processor ++ * and the secondary one ++ */ ++ spin_lock(&boot_lock); ++ ++ hi3521d_scu_power_up(cpu); ++ /* ++ * The secondary processor is waiting to be released from ++ * the holding pen - release it, then wait for it to flag ++ * that it has been released by resetting pen_release. ++ * ++ * Note that "pen_release" is the hardware CPU ID, whereas ++ * "cpu" is Linux's internal ID. ++ */ ++ hi_write_pen_release(cpu); ++ ++ /* ++ * Send the secondary CPU a soft interrupt, thereby causing ++ * the boot monitor to read the system wide flags register, ++ * and branch to the address found there. ++ */ ++ arch_send_wakeup_ipi_mask(cpumask_of(cpu)); ++ ++ /* ++ * Send the secondary CPU a soft interrupt, thereby causing ++ * the boot monitor to read the system wide flags register, ++ * and branch to the address found there. ++ */ ++ timeout = jiffies + (5 * HZ); ++ while (time_before(jiffies, timeout)) { ++ smp_rmb(); ++ if (pen_release == -1) ++ break; ++ ++ udelay(10); ++ } ++ ++ /* ++ * now the secondary core is starting up let it run its ++ * calibrations, then wait for it to finish ++ */ ++ spin_unlock(&boot_lock); ++ ++ return pen_release != -1 ? -ENOSYS : 0; ++} ++ ++static void __cpuinit hi3536c_secondary_init(unsigned int cpu) ++{ ++ /* ++ * let the primary processor know we're out of the ++ * pen, then head off into the C entry point ++ */ ++ hi_write_pen_release(-1); ++ ++ /* ++ * Synchronise with the boot thread. ++ */ ++ spin_lock(&boot_lock); ++ spin_unlock(&boot_lock); ++} ++ ++static void hi3531d_secondary_init(unsigned int cpu) ++{ ++ /* ++ * 1. enable L1 prefetch [2] ++ * 2. enable L2 prefetch hint [1]a ++ * 3. enable write full line of zeros mode. [3]a ++ * 4. enable allocation in one cache way only. [8] ++ * a: This feature must be enabled only when the slaves ++ * connected on the Cortex-A17 AXI master port support it. ++ */ ++ asm volatile ( ++ " mrc p15, 0, r0, c1, c0, 1\n" ++ " orr r0, r0, #0x0104\n" ++ " orr r0, r0, #0x02\n" ++ " mcr p15, 0, r0, c1, c0, 1\n" ++ : ++ : ++ : "r0", "cc"); ++ ++ /* ++ * * let the primary processor know we're out of the ++ * * pen, then head off into the C entry point ++ * */ ++ hi_write_pen_release(-1); ++ ++ /* ++ * Synchronise with the boot thread. ++ */ ++ spin_lock(&boot_lock); ++ spin_unlock(&boot_lock); ++} ++ ++static void __cpuinit hi3521d_secondary_init(unsigned int cpu) ++{ ++ /* ++ * let the primary processor know we're out of the ++ * pen, then head off into the C entry point ++ */ ++ hi_write_pen_release(-1); ++ ++ /* ++ * Synchronise with the boot thread. ++ */ ++ spin_lock(&boot_lock); ++ spin_unlock(&boot_lock); ++} ++ ++static void __init hi3536c_smp_init_cpus(void) ++{ ++ unsigned int i, ncores, l2ctlr; ++ ++ asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr)); ++ ncores = ((l2ctlr >> 24) & 0x3) + 1; ++ ++ /* sanity check */ ++ if (ncores > NR_CPUS) { ++ printk(KERN_WARNING ++ "Realview: no. of cores (%d) greater than configured " ++ "maximum of %d - clipping\n", ++ ncores, NR_CPUS); ++ ncores = NR_CPUS; ++ } ++ ++ for (i = 0; i < ncores; i++) ++ set_cpu_possible(i, true); ++} ++ ++static void __init hi3531d_smp_init_cpus(void) ++{ ++ void __iomem *scu_base = scu_base_addr(); ++ unsigned int i, ncores; ++ ++ ncores = scu_base ? scu_get_core_count(scu_base) : 1; ++ ++ /* sanity check */ ++ if (ncores > NR_CPUS) { ++ printk(KERN_WARNING ++ "Realview: no. of cores (%d) greater than configured " ++ "maximum of %d - clipping\n", ++ ncores, NR_CPUS); ++ ncores = NR_CPUS; ++ } ++ ++ for (i = 0; i < ncores; i++) ++ set_cpu_possible(i, true); ++} ++ ++static void __init hi3521d_smp_init_cpus(void) ++{ ++ unsigned int i, ncores, l2ctlr; ++ ++ asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr)); ++ ncores = ((l2ctlr >> 24) & 0x3) + 1; ++ ++ /* sanity check */ ++ if (ncores > NR_CPUS) { ++ printk(KERN_WARNING ++ "Realview: no. of cores (%d) greater than configured " ++ "maximum of %d - clipping\n", ++ ncores, NR_CPUS); ++ ncores = NR_CPUS; ++ } ++ ++ for (i = 0; i < ncores; i++) ++ set_cpu_possible(i, true); ++} ++ ++struct smp_operations hi3536c_smp_ops __initdata = { ++ .smp_init_cpus = hi3536c_smp_init_cpus, ++ .smp_prepare_cpus = hi3536c_smp_prepare_cpus, ++ .smp_secondary_init = hi3536c_secondary_init, ++ .smp_boot_secondary = hi3536c_boot_secondary, ++#ifdef CONFIG_HOTPLUG_CPU ++ .cpu_die = hi3536c_cpu_die, ++ .cpu_kill = hi3536c_cpu_kill, ++#endif ++}; ++ ++struct smp_operations hi3531d_smp_ops __initdata = { ++ .smp_init_cpus = hi3531d_smp_init_cpus, ++ .smp_prepare_cpus = hi3531d_smp_prepare_cpus, ++ .smp_secondary_init = hi3531d_secondary_init, ++ .smp_boot_secondary = hi3531d_boot_secondary, ++#ifdef CONFIG_HOTPLUG_CPU ++ .cpu_die = hi3531d_cpu_die, ++ .cpu_kill = hi3531d_cpu_kill, ++#endif ++}; ++ ++struct smp_operations hi3521d_smp_ops __initdata = { ++ .smp_init_cpus = hi3521d_smp_init_cpus, ++ .smp_prepare_cpus = hi3521d_smp_prepare_cpus, ++ .smp_secondary_init = hi3521d_secondary_init, ++ .smp_boot_secondary = hi3521d_boot_secondary, ++#ifdef CONFIG_HOTPLUG_CPU ++ .cpu_die = hi3521d_cpu_die, ++ .cpu_kill = hi3521d_cpu_kill, ++#endif ++}; ++ ++struct smp_operations hi3519_smp_ops __initdata = { ++ .smp_prepare_cpus = hi3519_smp_prepare_cpus, ++ .smp_boot_secondary = hi3519_boot_secondary, ++ .smp_secondary_init = HI3519_secondary_init, ++#ifdef CONFIG_HOTPLUG_CPU ++ .cpu_die = hi3519_cpu_die, ++ .cpu_kill = hi3519_cpu_kill, ++#endif ++}; ++ ++struct smp_operations hi3516av200_smp_ops __initdata = { ++ .smp_prepare_cpus = hi3516av200_smp_prepare_cpus, ++ .smp_boot_secondary = hi3516av200_boot_secondary, ++ .smp_secondary_init = HI3516av200_secondary_init, ++#ifdef CONFIG_HOTPLUG_CPU ++ .cpu_die = hi3516av200_cpu_die, ++ .cpu_kill = hi3516av200_cpu_kill, ++#endif ++}; ++ ++struct smp_operations hi3559_smp_ops __initdata = { ++ .smp_prepare_cpus = hi3559_smp_prepare_cpus, ++ .smp_boot_secondary = hi3559_boot_secondary, ++ .smp_secondary_init = HI3559_secondary_init, ++#ifdef CONFIG_HOTPLUG_CPU ++ .cpu_die = hi3559_cpu_die, ++ .cpu_kill = hi3559_cpu_kill, ++#endif ++}; ++ ++ + CPU_METHOD_OF_DECLARE(hi3xxx_smp, "hisilicon,hi3620-smp", &hi3xxx_smp_ops); + CPU_METHOD_OF_DECLARE(hix5hd2_smp, "hisilicon,hix5hd2-smp", &hix5hd2_smp_ops); ++CPU_METHOD_OF_DECLARE(hi3519_smp, "hisilicon,hi3519-smp", &hi3519_smp_ops); ++CPU_METHOD_OF_DECLARE(hi3559_smp, "hisilicon,hi3559-smp", &hi3559_smp_ops); ++CPU_METHOD_OF_DECLARE(hi3516av200_smp, "hisilicon,hi3516av200-smp", &hi3516av200_smp_ops); ++CPU_METHOD_OF_DECLARE(hi3536c_smp, "hisilicon,hi3536c-smp", &hi3536c_smp_ops); ++CPU_METHOD_OF_DECLARE(hi3531d_smp, "hisilicon,hi3531d-smp", &hi3531d_smp_ops); ++CPU_METHOD_OF_DECLARE(hi3521d_smp, "hisilicon,hi3521d-smp", &hi3521d_smp_ops); +diff --git a/arch/arm/mach-hisi/pm.c b/arch/arm/mach-hisi/pm.c +new file mode 100644 +index 0000000..c4b8870 +--- /dev/null ++++ b/arch/arm/mach-hisi/pm.c +@@ -0,0 +1,288 @@ ++/* ++ * power mangager control for hisilicon soc ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/pm.h> ++#include <linux/suspend.h> ++#include <asm/memory.h> ++#include <mach/io.h> ++#include <mach/platform.h> ++ ++#include <linux/delay.h> ++#include <linux/suspend.h> ++#include <linux/syscalls.h> ++#include <asm/mach/time.h> ++#include <linux/slab.h> ++ ++#include <asm/mach/time.h> ++#include <asm/hardware/arm_timer.h> ++#include <asm/mach/irq.h> ++ ++#include <linux/sched.h> ++#include <linux/mm.h> ++#include <linux/reboot.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++#include <linux/kallsyms.h> ++#include <asm/cacheflush.h> ++#include <asm/processor.h> ++ ++#include <asm/mach/map.h> ++#include <linux/of_address.h> ++#include <linux/platform_device.h> ++ ++struct hibvt_pm_dev { ++ void __iomem *sys_ctrl_base; ++ void __iomem *time0_base; ++}; ++ ++static struct hibvt_pm_dev g_hibvt_pm; ++ ++void (*hi_power_off)(void) = NULL; ++EXPORT_SYMBOL(hi_power_off); ++ ++void hi_pm_power_off(void) ++{ ++ /* Disable interrupts first */ ++ local_irq_disable(); ++ local_fiq_disable(); ++ ++ /* Clean and invalidate caches */ ++ flush_cache_all(); ++ ++ /* Turn off caching */ ++ cpu_proc_fin(); ++ ++ /* Push out any further dirty data, and ensure cache is empty */ ++ flush_cache_all(); ++ ++ /* ++ * Now call the architecture specific reboot code. ++ */ ++ if(hi_power_off) ++ hi_power_off(); ++ /* ++ * Whoops - the architecture was unable to reboot. ++ * Tell the user! ++ */ ++ mdelay(1000); ++ ++ printk(KERN_EMERG "Poweroff failed -- System halted\n"); ++ ++ while (1) ++ ; ++} ++ ++/*hibernate methods*/ ++#ifdef CONFIG_HIBERNATION ++unsigned long saved_interrupt_mask[128]; ++unsigned long saved_cpu_target_mask[128]; ++ ++typedef struct __timer_register { ++ unsigned long timer_load; ++ unsigned long timer_value; ++ unsigned long timer_ctrl; ++ unsigned long timer_bgload; ++} timer_register; ++ ++static timer_register timer0[2]; ++static int timer_init_value; ++static int save_timer0(void) ++{ ++ void __iomem *timer0_base_addr = g_hibvt_pm.time0_base; ++ ++ timer_init_value = readl(g_hibvt_pm.sys_ctrl_base); ++ /*protect timer0_0*/ ++ timer0[0].timer_load = readl(timer0_base_addr + TIMER_LOAD); ++ timer0[0].timer_value = readl(timer0_base_addr + TIMER_VALUE); ++ timer0[0].timer_ctrl = readl(timer0_base_addr + TIMER_CTRL); ++ timer0[0].timer_bgload = readl(timer0_base_addr + TIMER_BGLOAD); ++ /*disable timer0_0 */ ++ writel(0, timer0_base_addr + TIMER_CTRL); ++ ++ /*protect timer0_1*/ ++ timer0[1].timer_load = readl(timer0_base_addr + 0x20 + TIMER_LOAD); ++ timer0[1].timer_value = readl(timer0_base_addr + 0x20 + TIMER_VALUE); ++ timer0[1].timer_ctrl = readl(timer0_base_addr + 0x20 + TIMER_CTRL); ++ timer0[1].timer_bgload = readl(timer0_base_addr + 0x20 + TIMER_BGLOAD); ++ /*disable timer0_1 */ ++ writel(0, timer0_base_addr + 0x20 + TIMER_CTRL); ++ ++ return 0; ++} ++ ++ ++static int restore_timer0(void) ++{ ++ void __iomem *timer0_base_addr = g_hibvt_pm.time0_base; ++ ++ writel(timer_init_value, g_hibvt_pm.sys_ctrl_base); ++ /* restore timer0_0 */ ++ writel(0, timer0_base_addr + TIMER_CTRL); ++ writel(1, timer0_base_addr + TIMER_INTCLR); ++ ++ writel(timer0[0].timer_value, timer0_base_addr + TIMER_LOAD); ++ writel(timer0[0].timer_ctrl, timer0_base_addr + TIMER_CTRL); ++ writel(timer0[0].timer_bgload, timer0_base_addr + TIMER_BGLOAD); ++ ++ /* restore timer0_1 */ ++ writel(0, timer0_base_addr + 0x20 + TIMER_CTRL); ++ writel(1, timer0_base_addr + 0x20 + TIMER_INTCLR); ++ ++ writel(timer0[1].timer_value, timer0_base_addr + 0x20 + TIMER_LOAD); ++ writel(timer0[1].timer_ctrl, timer0_base_addr + 0x20 + TIMER_CTRL); ++ writel(timer0[1].timer_bgload, timer0_base_addr + 0x20 + TIMER_BGLOAD); ++ ++ return 0; ++} ++static int hi_hiber_begin(void) ++{ ++ return 0; ++} ++ ++static void hi_hiber_end(void) ++{ ++} ++ ++static int hi_hiber_pre_snapshot(void) ++{ ++ save_timer0(); ++ return 0; ++} ++ ++static void hi_hiber_finish(void) ++{ ++ restore_timer0(); ++} ++ ++static int hi_hiber_prepare(void) ++{ ++ return 0; ++} ++ ++static void hi_hiber_leave(void) ++{ ++} ++ ++static int hi_hiber_enter(void) ++{ ++#ifdef CONFIG_DEFAULT_MTD ++ hi_pm_power_off(); ++#else ++ kernel_restart(NULL); ++#endif ++ return 0; ++} ++ ++static int hi_hiber_restore(void) ++{ ++ return 0; ++} ++ ++static void hi_hiber_restore_cleanup(void) ++{ ++} ++ ++static void hi_hiber_recover(void) ++{ ++} ++ ++struct platform_hibernation_ops hi_hibernation_ops = { ++ .begin = hi_hiber_begin, ++ .end = hi_hiber_end, ++ .pre_snapshot = hi_hiber_pre_snapshot, ++ .finish = hi_hiber_finish, ++ .prepare = hi_hiber_prepare, ++ .enter = hi_hiber_enter, ++ .leave = hi_hiber_leave, ++ .pre_restore = hi_hiber_restore, ++ .restore_cleanup = hi_hiber_restore_cleanup, ++ .recover = hi_hiber_recover, ++}; ++#endif /* CONFIG_HIBERNATION */ ++ ++ ++/*pm methods*/ ++static int hi_pm_enter(suspend_state_t state) ++{ ++ return 0; ++} ++ ++int hi_pm_valid(suspend_state_t state) ++{ ++ return 1; ++} ++ ++static const struct platform_suspend_ops hi_pm_ops = { ++ .enter = hi_pm_enter, ++ .valid = hi_pm_valid, ++}; ++ ++static int hi_pm_probe(struct platform_device *pdev) ++{ ++ g_hibvt_pm.sys_ctrl_base = of_iomap(pdev->dev.of_node, 0); ++ if (!g_hibvt_pm.sys_ctrl_base) { ++ pr_err("%s: failed to map system controller registers\n", ++ __func__); ++ return -ENOMEM; ++ } ++ ++ g_hibvt_pm.time0_base = of_iomap(pdev->dev.of_node, 1); ++ if (!g_hibvt_pm.time0_base) { ++ pr_err("%s: failed to map timer registers\n", __func__); ++ iounmap(g_hibvt_pm.sys_ctrl_base); ++ return -ENOMEM; ++ } ++ ++ pm_power_off = hi_pm_power_off; ++ ++ suspend_set_ops(&hi_pm_ops); ++ ++#ifdef CONFIG_HIBERNATION ++ /* registering hibernation call backs */ ++ hibernation_set_ops(&hi_hibernation_ops); ++#endif ++ return 0; ++} ++ ++static const struct of_device_id hi_pm_match_table[] = { ++ { .compatible = "hisilicon,hibvt-pm" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, hi_pm_match_table); ++ ++static struct platform_driver hi_pm_driver = { ++ .probe = hi_pm_probe, ++ .driver = { ++ .name = "hibvt-pm", ++ .of_match_table = hi_pm_match_table, ++ }, ++}; ++ ++static int __init hi_pm_init(void) ++{ ++ return platform_driver_register(&hi_pm_driver); ++} ++module_init(hi_pm_init); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("HiSilicon BVT PM Driver"); +diff --git a/arch/arm/mach-hisi/pmc_hi3516av200.c b/arch/arm/mach-hisi/pmc_hi3516av200.c +new file mode 100644 +index 0000000..df36e44 +--- /dev/null ++++ b/arch/arm/mach-hisi/pmc_hi3516av200.c +@@ -0,0 +1,207 @@ ++/* ++ * power mangager control for hisilicon hi3516av200 soc ++ * ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * Authors: zengtao@hisilicon.com ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/io.h> ++#include <linux/linkage.h> ++#include <linux/bug.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/resource.h> ++#include <mach/platform.h> ++ ++#include <linux/delay.h> ++ ++#define PERI_PMC77 (0x134) ++#define PERI_PMC79 (0x13c) ++#define PERI_PMC85 (0x154) ++ ++static void __iomem *pmc_base; ++static u32 pmc_phys_addr; ++ ++#define PMC_ADDRESS(reg) (pmc_base + reg) ++/* set bitfield of reg from start bit to end - 1 bit */ ++static void reg_bit_set(u32 reg, u32 start, u32 end, u32 val) ++{ ++ u32 regval, mask; ++ ++ regval = readl((void __iomem *)PMC_ADDRESS(reg)); ++ mask = ((0xffffffff << (32 - start)) >> (32 - start)) ++ | ((0xffffffff >> end) << end); ++ ++ regval &= mask; ++ regval |= (val << start); ++ ++ writel(regval, (void __iomem *)PMC_ADDRESS(reg)); ++} ++ ++/* get bitfield of reg from start bit to end - 1 bit */ ++static u32 reg_bit_get(u32 reg, u32 start, u32 end) ++{ ++ u32 regval; ++ ++ ++ regval = readl((void __iomem *)PMC_ADDRESS(reg)); ++ regval = (regval << (32 - end)) >> (32 - end); ++ regval = regval >> start; ++ ++ ++ return regval; ++} ++ ++void hi_pmc_power_up_done(void) ++{ ++ writel(0, (void __iomem *)PMC_ADDRESS(PERI_PMC85)); ++ writel(1, (void __iomem *)PMC_ADDRESS(PERI_PMC85)); ++ ++} ++ ++/* before power down set ac inactive */ ++void hi_pmc_set_ac_inactive(void) ++{ ++ reg_bit_set(PERI_PMC79, 8, 9, 1); ++} ++ ++/* after powerup clear ac inactive */ ++void hi_pmc_clear_ac_inactive(void) ++{ ++ reg_bit_set(PERI_PMC79, 8, 9, 0); ++} ++EXPORT_SYMBOL(hi_pmc_clear_ac_inactive); ++ ++/* call from assable context */ ++asmlinkage void __naked hi_pmc_clear_a17_ac(void) ++{ ++ asm volatile("\n" ++ "adr r2, 1f\n" ++ "ldmia r2, {r1, r3}\n" ++ "sub r0, r2, r1\n" ++ "ldr r2, [r0, r3]\n" ++ "ldr r0, ="__stringify(PERI_PMC79)"\n" ++ "add r0, r0, r2\n" ++ "ldr r1, [r0]\n" ++ "bic r1, #0x100\n" ++ "str r1, [r0]\n" ++ "mov r0, #0\n" ++ "bx lr\n" ++ ++ ".align 2\n" ++ "1: .word .\n" ++ " .word pmc_phys_addr\n" ++ ); ++ ++ unreachable(); ++} ++ ++static void hi_pmc_config(void) ++{ ++ /* enable pmc timeout */ ++ reg_bit_set(PERI_PMC77, 12, 13, 1); ++ /* enable pmc auto mode */ ++ reg_bit_set(PERI_PMC79, 0, 2, 0); ++ /* enable irq triger source power on */ ++ reg_bit_set(PERI_PMC79, 7, 8, 1); ++} ++ ++/* cpu hotplug powerup */ ++void hi_pmc_power_up(void) ++{ ++ u32 power_state; ++ ++ hi_pmc_config(); ++ ++ /* make sure it powerup state when power up */ ++ power_state = reg_bit_get(PERI_PMC79, 12, 16); ++ BUG_ON(power_state != 0); ++ ++ /* disable interrupt wakeup */ ++ reg_bit_set(PERI_PMC79, 5, 6, 0); ++ ++ /* power on */ ++ reg_bit_set(PERI_PMC79, 3, 4, 0); ++ reg_bit_set(PERI_PMC79, 3, 4, 1); ++} ++ ++/* cpu hotplug powerdown */ ++void hi_pmc_power_down(void) ++{ ++ u32 power_state; ++ ++ power_state = reg_bit_get(PERI_PMC79, 12, 16); ++ BUG_ON(power_state != 6); ++ ++ /* disable interrupt wakeup */ ++ reg_bit_set(PERI_PMC79, 5, 6, 0); ++ ++ /* power off */ ++ reg_bit_set(PERI_PMC79, 4, 5, 0); ++ reg_bit_set(PERI_PMC79, 4, 5, 1); ++} ++ ++/* cpuidle powerdown */ ++void hi_pmc_automode_power_down(void) ++{ ++ u32 power_state; ++ ++ power_state = reg_bit_get(PERI_PMC79, 12, 16); ++ BUG_ON(power_state != 6); ++ ++ /* enable interrupt wakeup */ ++ reg_bit_set(PERI_PMC79, 5, 6, 1); ++ ++ /* power off */ ++ reg_bit_set(PERI_PMC79, 4, 5, 0); ++ reg_bit_set(PERI_PMC79, 4, 5, 1); ++ ++} ++EXPORT_SYMBOL(hi_pmc_automode_power_down); ++ ++ ++/* enable timeout */ ++static int hi_pmc_init(void) ++{ ++ struct device_node *np; ++ struct resource res; ++ int ret = -ENODEV; ++ ++ np = of_find_compatible_node(NULL, NULL, "hisilicon,pmc"); ++ if (!np) ++ goto err; ++ ++ pmc_base = of_iomap(np, 0); ++ if (!pmc_base) { ++ pr_err("failed to map pmc base\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ ret = of_address_to_resource(np, 0, &res); ++ if (ret) { ++ pr_err("failed to get pmc base phys\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ pmc_phys_addr = res.start; ++ ++err: ++ return ret; ++} ++ ++early_initcall(hi_pmc_init); +diff --git a/arch/arm/mach-hisi/pmc_hi3519.c b/arch/arm/mach-hisi/pmc_hi3519.c +new file mode 100644 +index 0000000..17689f3 +--- /dev/null ++++ b/arch/arm/mach-hisi/pmc_hi3519.c +@@ -0,0 +1,281 @@ ++/* ++ * power mangager control for hisilicon hi3519 soc ++ * ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * Authors: zengtao@hisilicon.com ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/io.h> ++#include <linux/linkage.h> ++#include <linux/bug.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/resource.h> ++#include <mach/io.h> ++#include <mach/platform.h> ++ ++#include <linux/delay.h> ++ ++#define PERI_PMC77 (0x134) ++#define PERI_PMC79 (0x13c) ++#define PERI_PMC85 (0x154) ++ ++static void __iomem *pmc_base; ++static u32 pmc_phys_addr; ++ ++#define PMC_ADDRESS(reg) (pmc_base + reg) ++/* set bitfield of reg from start bit to end - 1 bit */ ++static void reg_bit_set(u32 reg, u32 start, u32 end, u32 val) ++{ ++ u32 regval, mask; ++ ++ regval = readl((void __iomem *)PMC_ADDRESS(reg)); ++ mask = ((0xffffffff << (32 - start)) >> (32 - start)) ++ | ((0xffffffff >> end) << end); ++ ++ regval &= mask; ++ regval |= (val << start); ++ ++ writel(regval, (void __iomem *)PMC_ADDRESS(reg)); ++} ++ ++/* get bitfield of reg from start bit to end - 1 bit */ ++static u32 reg_bit_get(u32 reg, u32 start, u32 end) ++{ ++ u32 regval; ++ ++ regval = readl((void __iomem *)PMC_ADDRESS(reg)); ++ regval = (regval << (32 - end)) >> (32 - end); ++ regval = regval >> start; ++ ++ return regval; ++} ++ ++void hi_pmc_power_up_done(void) ++{ ++ writel(0, (void __iomem *)PMC_ADDRESS(PERI_PMC85)); ++ writel(1, (void __iomem *)PMC_ADDRESS(PERI_PMC85)); ++ ++} ++ ++/* before power down set ac inactive */ ++void hi_pmc_set_ac_inactive(void) ++{ ++ reg_bit_set(PERI_PMC79, 8, 9, 1); ++} ++ ++/* after powerup clear ac inactive */ ++void hi_pmc_clear_ac_inactive(void) ++{ ++ reg_bit_set(PERI_PMC79, 8, 9, 0); ++} ++EXPORT_SYMBOL(hi_pmc_clear_ac_inactive); ++ ++/* ++ * call from assable context, same as the ++ * hi_pmc_clear_ac_inactive ++ */ ++asmlinkage void __naked hi_pmc_clear_a17_ac(void) ++{ ++ asm volatile("\n" ++ "adr r2, 1f\n" ++ "ldmia r2, {r1, r3}\n" ++ "sub r0, r2, r1\n" ++ "ldr r2, [r0, r3]\n" ++ "ldr r0, ="__stringify(PERI_PMC79)"\n" ++ "add r0, r0, r2\n" ++ "ldr r1, [r0]\n" ++ "bic r1, #0x100\n" ++ "str r1, [r0]\n" ++ "mov r0, #0\n" ++ "bx lr\n" ++ ++ ".align 2\n" ++ "1: .word .\n" ++ " .word pmc_phys_addr\n" ++ ); ++ ++ unreachable(); ++} ++ ++/* cpu hotplug powerup */ ++void hi_pmc_power_up(void) ++{ ++ u32 power_state; ++ ++ writel(readl((void *)IO_ADDRESS(0x12030004)) | 0x40000000, ++ (void *)IO_ADDRESS(0x12030004)); ++ ++ reg_bit_set(PERI_PMC79, 0, 2, 0x2); ++ ++ writel(readl((void *)IO_ADDRESS(CRG_REG_BASE + REG_PERI_CRG10)) ++ | (1<<21), ++ (void *)IO_ADDRESS(CRG_REG_BASE + REG_PERI_CRG10)); ++ ++ reg_bit_set(PERI_PMC77, 1, 2, 0x0); ++ ++ mdelay(1); ++ power_state = reg_bit_get(PERI_PMC77, 2, 3); ++ while (power_state) ++ power_state = reg_bit_get(PERI_PMC77, 2, 3); ++ ++ reg_bit_set(PERI_PMC77, 0, 1, 0x0); ++ writel(readl((void *)IO_ADDRESS(CRG_REG_BASE + REG_PERI_CRG10)) ++ & 0xfff7ffff, ++ (void *)IO_ADDRESS(CRG_REG_BASE + REG_PERI_CRG10)); ++ ++ reg_bit_set(PERI_PMC77, 5, 6, 0x1); ++ mdelay(1); ++ power_state = reg_bit_get(PERI_PMC77, 6, 7); ++ while (power_state != 1) ++ power_state = reg_bit_get(PERI_PMC77, 6, 7); ++ ++ writel(readl((void *)IO_ADDRESS(CRG_REG_BASE + REG_PERI_CRG10)) ++ & 0xfffffffd, ++ (void *)IO_ADDRESS(CRG_REG_BASE + REG_PERI_CRG10)); ++} ++ ++void hi_pmc_kill_cpu(unsigned int cpu) ++{ ++ unsigned int state; ++ ++ while (1) { ++ state = readl((void *)IO_ADDRESS(0x12030004)) & 0x40000000; ++ if (!state) ++ break; ++ } ++ ++ while (1) { ++ state = reg_bit_get(PERI_PMC77, 7, 9); ++ if (state == 3) ++ break; ++ } ++ ++ while (1) { ++ state = reg_bit_get(PERI_PMC77, 3, 5); ++ if (state == 0) ++ break; ++ } ++ ++ reg_bit_set(PERI_PMC77, 5, 6, 0x0); ++ ++ while (1) { ++ state = reg_bit_get(PERI_PMC77, 6, 7); ++ if (state == 0) ++ break; ++ } ++ ++ reg_bit_set(PERI_PMC77, 0, 1, 0x1); ++ ++ writel(readl((void *)IO_ADDRESS(CRG_REG_BASE + REG_PERI_CRG10)) ++ | (1<<19), ++ (void *)IO_ADDRESS(CRG_REG_BASE + REG_PERI_CRG10)); ++ writel(readl((void *)IO_ADDRESS(CRG_REG_BASE + REG_PERI_CRG10)) ++ & 0xffdfffff, ++ (void *)IO_ADDRESS(CRG_REG_BASE + REG_PERI_CRG10)); ++ ++ reg_bit_set(PERI_PMC77, 1, 2, 0x1); ++ ++ while (1) { ++ state = reg_bit_get(PERI_PMC77, 2, 3); ++ if (state == 1) ++ break; ++ } ++} ++ ++/* cpu hotplug powerdown */ ++void hi_pmc_power_down(void) ++{ ++#if 0 ++ u32 power_state; ++ ++ power_state = reg_bit_get(PERI_PMC79, 12, 16); ++ BUG_ON(power_state != 6); ++ ++ /* disable interrupt wakeup */ ++ reg_bit_set(PERI_PMC79, 5, 6, 0); ++ ++ /* power off */ ++ reg_bit_set(PERI_PMC79, 4, 5, 0); ++ reg_bit_set(PERI_PMC79, 4, 5, 1); ++#endif ++ reg_bit_set(PERI_PMC79, 0, 2, 2); ++ writel(readl((void *)IO_ADDRESS(0x12030004)) & 0xbfffffff, ++ (void *)IO_ADDRESS(0x12030004)); ++ ++} ++ ++/* cpuidle powerdown */ ++void hi_pmc_automode_power_down(void) ++{ ++ u32 power_state; ++ ++ power_state = reg_bit_get(PERI_PMC79, 12, 16); ++ BUG_ON(power_state != 6); ++ ++ /* enable interrupt wakeup */ ++ reg_bit_set(PERI_PMC79, 5, 6, 1); ++ ++ /* power off */ ++ reg_bit_set(PERI_PMC79, 4, 5, 0); ++ reg_bit_set(PERI_PMC79, 4, 5, 1); ++ ++} ++EXPORT_SYMBOL(hi_pmc_automode_power_down); ++ ++static void hi_pmc_config(void) ++{ ++ /* enable pmc timeout */ ++ reg_bit_set(PERI_PMC77, 12, 13, 1); ++ /* enable pmc auto mode */ ++ reg_bit_set(PERI_PMC79, 0, 2, 0); ++ /* enable irq triger source power on */ ++ reg_bit_set(PERI_PMC79, 7, 8, 1); ++} ++ ++/* enable timeout */ ++static int hi_pmc_init(void) ++{ ++ struct device_node *np; ++ struct resource res; ++ int ret = -ENODEV; ++ ++ np = of_find_compatible_node(NULL, NULL, "hisilicon,pmc"); ++ if (!np) ++ goto err; ++ ++ pmc_base = of_iomap(np, 0); ++ if (!pmc_base) { ++ pr_err("failed to map pmc base\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ ret = of_address_to_resource(np, 0, &res); ++ if (ret) { ++ pr_err("failed to get pmc base phys\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ pmc_phys_addr = res.start; ++ ++ hi_pmc_config(); ++err: ++ return ret; ++} ++ ++early_initcall(hi_pmc_init); +diff --git a/arch/arm/mach-hisi/pmc_hi3519v101.c b/arch/arm/mach-hisi/pmc_hi3519v101.c +new file mode 100644 +index 0000000..e652fb9 +--- /dev/null ++++ b/arch/arm/mach-hisi/pmc_hi3519v101.c +@@ -0,0 +1,207 @@ ++/* ++ * power mangager control for hisilicon hi3519 soc ++ * ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * Authors: zengtao@hisilicon.com ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/io.h> ++#include <linux/linkage.h> ++#include <linux/bug.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/resource.h> ++#include <mach/platform.h> ++ ++#include <linux/delay.h> ++ ++#define PERI_PMC77 (0x134) ++#define PERI_PMC79 (0x13c) ++#define PERI_PMC85 (0x154) ++ ++static void __iomem *pmc_base; ++static u32 pmc_phys_addr; ++ ++#define PMC_ADDRESS(reg) (pmc_base + reg) ++/* set bitfield of reg from start bit to end - 1 bit */ ++static void reg_bit_set(u32 reg, u32 start, u32 end, u32 val) ++{ ++ u32 regval, mask; ++ ++ regval = readl((void __iomem *)PMC_ADDRESS(reg)); ++ mask = ((0xffffffff << (32 - start)) >> (32 - start)) ++ | ((0xffffffff >> end) << end); ++ ++ regval &= mask; ++ regval |= (val << start); ++ ++ writel(regval, (void __iomem *)PMC_ADDRESS(reg)); ++} ++ ++/* get bitfield of reg from start bit to end - 1 bit */ ++static u32 reg_bit_get(u32 reg, u32 start, u32 end) ++{ ++ u32 regval; ++ ++ ++ regval = readl((void __iomem *)PMC_ADDRESS(reg)); ++ regval = (regval << (32 - end)) >> (32 - end); ++ regval = regval >> start; ++ ++ ++ return regval; ++} ++ ++void hi_pmc_power_up_done(void) ++{ ++ writel(0, (void __iomem *)PMC_ADDRESS(PERI_PMC85)); ++ writel(1, (void __iomem *)PMC_ADDRESS(PERI_PMC85)); ++ ++} ++ ++/* before power down set ac inactive */ ++void hi_pmc_set_ac_inactive(void) ++{ ++ reg_bit_set(PERI_PMC79, 8, 9, 1); ++} ++ ++/* after powerup clear ac inactive */ ++void hi_pmc_clear_ac_inactive(void) ++{ ++ reg_bit_set(PERI_PMC79, 8, 9, 0); ++} ++EXPORT_SYMBOL(hi_pmc_clear_ac_inactive); ++ ++/* call from assable context */ ++asmlinkage void __naked hi_pmc_clear_a17_ac(void) ++{ ++ asm volatile("\n" ++ "adr r2, 1f\n" ++ "ldmia r2, {r1, r3}\n" ++ "sub r0, r2, r1\n" ++ "ldr r2, [r0, r3]\n" ++ "ldr r0, ="__stringify(PERI_PMC79)"\n" ++ "add r0, r0, r2\n" ++ "ldr r1, [r0]\n" ++ "bic r1, #0x100\n" ++ "str r1, [r0]\n" ++ "mov r0, #0\n" ++ "bx lr\n" ++ ++ ".align 2\n" ++ "1: .word .\n" ++ " .word pmc_phys_addr\n" ++ ); ++ ++ unreachable(); ++} ++ ++static void hi_pmc_config(void) ++{ ++ /* enable pmc timeout */ ++ reg_bit_set(PERI_PMC77, 12, 13, 1); ++ /* enable pmc auto mode */ ++ reg_bit_set(PERI_PMC79, 0, 2, 0); ++ /* enable irq triger source power on */ ++ reg_bit_set(PERI_PMC79, 7, 8, 1); ++} ++ ++/* cpu hotplug powerup */ ++void hi_pmc_power_up(void) ++{ ++ u32 power_state; ++ ++ hi_pmc_config(); ++ ++ /* make sure it powerup state when power up */ ++ power_state = reg_bit_get(PERI_PMC79, 12, 16); ++ BUG_ON(power_state != 0); ++ ++ /* disable interrupt wakeup */ ++ reg_bit_set(PERI_PMC79, 5, 6, 0); ++ ++ /* power on */ ++ reg_bit_set(PERI_PMC79, 3, 4, 0); ++ reg_bit_set(PERI_PMC79, 3, 4, 1); ++} ++ ++/* cpu hotplug powerdown */ ++void hi_pmc_power_down(void) ++{ ++ u32 power_state; ++ ++ power_state = reg_bit_get(PERI_PMC79, 12, 16); ++ BUG_ON(power_state != 6); ++ ++ /* disable interrupt wakeup */ ++ reg_bit_set(PERI_PMC79, 5, 6, 0); ++ ++ /* power off */ ++ reg_bit_set(PERI_PMC79, 4, 5, 0); ++ reg_bit_set(PERI_PMC79, 4, 5, 1); ++} ++ ++/* cpuidle powerdown */ ++void hi_pmc_automode_power_down(void) ++{ ++ u32 power_state; ++ ++ power_state = reg_bit_get(PERI_PMC79, 12, 16); ++ BUG_ON(power_state != 6); ++ ++ /* enable interrupt wakeup */ ++ reg_bit_set(PERI_PMC79, 5, 6, 1); ++ ++ /* power off */ ++ reg_bit_set(PERI_PMC79, 4, 5, 0); ++ reg_bit_set(PERI_PMC79, 4, 5, 1); ++ ++} ++EXPORT_SYMBOL(hi_pmc_automode_power_down); ++ ++ ++/* enable timeout */ ++static int hi_pmc_init(void) ++{ ++ struct device_node *np; ++ struct resource res; ++ int ret = -ENODEV; ++ ++ np = of_find_compatible_node(NULL, NULL, "hisilicon,pmc"); ++ if (!np) ++ goto err; ++ ++ pmc_base = of_iomap(np, 0); ++ if (!pmc_base) { ++ pr_err("failed to map pmc base\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ ret = of_address_to_resource(np, 0, &res); ++ if (ret) { ++ pr_err("failed to get pmc base phys\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ pmc_phys_addr = res.start; ++ ++err: ++ return ret; ++} ++ ++early_initcall(hi_pmc_init); +diff --git a/arch/arm/mach-hisi/pmc_hi3521d.c b/arch/arm/mach-hisi/pmc_hi3521d.c +new file mode 100644 +index 0000000..54b9294 +--- /dev/null ++++ b/arch/arm/mach-hisi/pmc_hi3521d.c +@@ -0,0 +1,40 @@ ++/* ++ * Copyright (c) 2015-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/linkage.h> ++ ++asmlinkage void __naked cci_enable_port_for_self(void) ++{ ++} ++ ++asmlinkage void __naked hi_pmc_clear_a17_ac(void) ++{ ++} ++ ++void hi_pmc_power_up(void) ++{ ++} ++ ++void hi_pmc_set_ac_inactive(void) ++{ ++} ++ ++void hi_pmc_power_down(void) ++{ ++} ++ +diff --git a/arch/arm/mach-hisi/pmc_hi3531d.c b/arch/arm/mach-hisi/pmc_hi3531d.c +new file mode 100644 +index 0000000..54b9294 +--- /dev/null ++++ b/arch/arm/mach-hisi/pmc_hi3531d.c +@@ -0,0 +1,40 @@ ++/* ++ * Copyright (c) 2015-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/linkage.h> ++ ++asmlinkage void __naked cci_enable_port_for_self(void) ++{ ++} ++ ++asmlinkage void __naked hi_pmc_clear_a17_ac(void) ++{ ++} ++ ++void hi_pmc_power_up(void) ++{ ++} ++ ++void hi_pmc_set_ac_inactive(void) ++{ ++} ++ ++void hi_pmc_power_down(void) ++{ ++} ++ +diff --git a/arch/arm/mach-hisi/pmc_hi3536c.c b/arch/arm/mach-hisi/pmc_hi3536c.c +new file mode 100644 +index 0000000..54b9294 +--- /dev/null ++++ b/arch/arm/mach-hisi/pmc_hi3536c.c +@@ -0,0 +1,40 @@ ++/* ++ * Copyright (c) 2015-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/linkage.h> ++ ++asmlinkage void __naked cci_enable_port_for_self(void) ++{ ++} ++ ++asmlinkage void __naked hi_pmc_clear_a17_ac(void) ++{ ++} ++ ++void hi_pmc_power_up(void) ++{ ++} ++ ++void hi_pmc_set_ac_inactive(void) ++{ ++} ++ ++void hi_pmc_power_down(void) ++{ ++} ++ +diff --git a/arch/arm/mach-hisi/pmc_hi3559.c b/arch/arm/mach-hisi/pmc_hi3559.c +new file mode 100644 +index 0000000..7767ef2 +--- /dev/null ++++ b/arch/arm/mach-hisi/pmc_hi3559.c +@@ -0,0 +1,207 @@ ++/* ++ * power mangager control for hisilicon hi3559 soc ++ * ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * Authors: zengtao@hisilicon.com ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/io.h> ++#include <linux/linkage.h> ++#include <linux/bug.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/resource.h> ++#include <mach/platform.h> ++ ++#include <linux/delay.h> ++ ++#define PERI_PMC77 (0x134) ++#define PERI_PMC79 (0x13c) ++#define PERI_PMC85 (0x154) ++ ++static void __iomem *pmc_base; ++static u32 pmc_phys_addr; ++ ++#define PMC_ADDRESS(reg) (pmc_base + reg) ++/* set bitfield of reg from start bit to end - 1 bit */ ++static void reg_bit_set(u32 reg, u32 start, u32 end, u32 val) ++{ ++ u32 regval, mask; ++ ++ regval = readl((void __iomem *)PMC_ADDRESS(reg)); ++ mask = ((0xffffffff << (32 - start)) >> (32 - start)) ++ | ((0xffffffff >> end) << end); ++ ++ regval &= mask; ++ regval |= (val << start); ++ ++ writel(regval, (void __iomem *)PMC_ADDRESS(reg)); ++} ++ ++/* get bitfield of reg from start bit to end - 1 bit */ ++static u32 reg_bit_get(u32 reg, u32 start, u32 end) ++{ ++ u32 regval; ++ ++ ++ regval = readl((void __iomem *)PMC_ADDRESS(reg)); ++ regval = (regval << (32 - end)) >> (32 - end); ++ regval = regval >> start; ++ ++ ++ return regval; ++} ++ ++void hi_pmc_power_up_done(void) ++{ ++ writel(0, (void __iomem *)PMC_ADDRESS(PERI_PMC85)); ++ writel(1, (void __iomem *)PMC_ADDRESS(PERI_PMC85)); ++ ++} ++ ++/* before power down set ac inactive */ ++void hi_pmc_set_ac_inactive(void) ++{ ++ reg_bit_set(PERI_PMC79, 8, 9, 1); ++} ++ ++/* after powerup clear ac inactive */ ++void hi_pmc_clear_ac_inactive(void) ++{ ++ reg_bit_set(PERI_PMC79, 8, 9, 0); ++} ++EXPORT_SYMBOL(hi_pmc_clear_ac_inactive); ++ ++/* call from assable context */ ++asmlinkage void __naked hi_pmc_clear_a17_ac(void) ++{ ++ asm volatile("\n" ++ "adr r2, 1f\n" ++ "ldmia r2, {r1, r3}\n" ++ "sub r0, r2, r1\n" ++ "ldr r2, [r0, r3]\n" ++ "ldr r0, ="__stringify(PERI_PMC79)"\n" ++ "add r0, r0, r2\n" ++ "ldr r1, [r0]\n" ++ "bic r1, #0x100\n" ++ "str r1, [r0]\n" ++ "mov r0, #0\n" ++ "bx lr\n" ++ ++ ".align 2\n" ++ "1: .word .\n" ++ " .word pmc_phys_addr\n" ++ ); ++ ++ unreachable(); ++} ++ ++static void hi_pmc_config(void) ++{ ++ /* enable pmc timeout */ ++ reg_bit_set(PERI_PMC77, 12, 13, 1); ++ /* enable pmc auto mode */ ++ reg_bit_set(PERI_PMC79, 0, 2, 0); ++ /* enable irq triger source power on */ ++ reg_bit_set(PERI_PMC79, 7, 8, 1); ++} ++ ++/* cpu hotplug powerup */ ++void hi_pmc_power_up(void) ++{ ++ u32 power_state; ++ ++ hi_pmc_config(); ++ ++ /* make sure it powerup state when power up */ ++ power_state = reg_bit_get(PERI_PMC79, 12, 16); ++ BUG_ON(power_state != 0); ++ ++ /* disable interrupt wakeup */ ++ reg_bit_set(PERI_PMC79, 5, 6, 0); ++ ++ /* power on */ ++ reg_bit_set(PERI_PMC79, 3, 4, 0); ++ reg_bit_set(PERI_PMC79, 3, 4, 1); ++} ++ ++/* cpu hotplug powerdown */ ++void hi_pmc_power_down(void) ++{ ++ u32 power_state; ++ ++ power_state = reg_bit_get(PERI_PMC79, 12, 16); ++ BUG_ON(power_state != 6); ++ ++ /* disable interrupt wakeup */ ++ reg_bit_set(PERI_PMC79, 5, 6, 0); ++ ++ /* power off */ ++ reg_bit_set(PERI_PMC79, 4, 5, 0); ++ reg_bit_set(PERI_PMC79, 4, 5, 1); ++} ++ ++/* cpuidle powerdown */ ++void hi_pmc_automode_power_down(void) ++{ ++ u32 power_state; ++ ++ power_state = reg_bit_get(PERI_PMC79, 12, 16); ++ BUG_ON(power_state != 6); ++ ++ /* enable interrupt wakeup */ ++ reg_bit_set(PERI_PMC79, 5, 6, 1); ++ ++ /* power off */ ++ reg_bit_set(PERI_PMC79, 4, 5, 0); ++ reg_bit_set(PERI_PMC79, 4, 5, 1); ++ ++} ++EXPORT_SYMBOL(hi_pmc_automode_power_down); ++ ++ ++/* enable timeout */ ++static int hi_pmc_init(void) ++{ ++ struct device_node *np; ++ struct resource res; ++ int ret = -ENODEV; ++ ++ np = of_find_compatible_node(NULL, NULL, "hisilicon,pmc"); ++ if (!np) ++ goto err; ++ ++ pmc_base = of_iomap(np, 0); ++ if (!pmc_base) { ++ pr_err("failed to map pmc base\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ ret = of_address_to_resource(np, 0, &res); ++ if (ret) { ++ pr_err("failed to get pmc base phys\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ pmc_phys_addr = res.start; ++ ++err: ++ return ret; ++} ++ ++early_initcall(hi_pmc_init); +diff --git a/arch/arm/mach-hisi/pwr_hi3559.c b/arch/arm/mach-hisi/pwr_hi3559.c +new file mode 100644 +index 0000000..38a07fb +--- /dev/null ++++ b/arch/arm/mach-hisi/pwr_hi3559.c +@@ -0,0 +1,81 @@ ++/* ++ * The power mangager control for hisilicon soc ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/io.h> ++#include <linux/linkage.h> ++#include <linux/bug.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/resource.h> ++#include <mach/platform.h> ++ ++#define PWR_CTRL0 (0x34) ++ ++static void __iomem *pwr_base; ++ ++#define PWR_ADDRESS(reg) (pwr_base + reg) ++/* set bitfield of reg from start bit to end - 1 bit */ ++static void reg_bit_set(u32 reg, u32 start, u32 end, u32 val) ++{ ++ u32 regval, mask; ++ ++ regval = readl((void __iomem *)PWR_ADDRESS(reg)); ++ mask = ((0xffffffff << (32 - start)) >> (32 - start)) ++ | ((0xffffffff >> end) << end); ++ ++ regval &= mask; ++ regval |= (val << start); ++ ++ writel(regval, (void __iomem *)PWR_ADDRESS(reg)); ++} ++ ++/* power off interface */ ++void hi_pwr_off(void) ++{ ++ /* set pwr_off_ctrl to 0x1 not 0x2 */ ++ /* if set to 0x2, must wait about 2 seconds when power on */ ++ reg_bit_set(PWR_CTRL0, 0, 1, 0x1); ++} ++ ++extern void (*hi_power_off)(void); ++static int hi_pwr_init(void) ++{ ++ struct device_node *np; ++ int ret = 0; ++ ++ np = of_find_compatible_node(NULL, NULL, "hisilicon,hi_pwr"); ++ if (!np) { ++ ret = -ENODEV; ++ goto err; ++ } ++ ++ pwr_base = of_iomap(np, 0); ++ if (!pwr_base) { ++ pr_err("failed to map pwr base\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ hi_power_off = hi_pwr_off; ++err: ++ return ret; ++} ++early_initcall(hi_pwr_init); ++ +diff --git a/arch/arm/mach-hisi/swsusp.S b/arch/arm/mach-hisi/swsusp.S +new file mode 100644 +index 0000000..03983bc +--- /dev/null ++++ b/arch/arm/mach-hisi/swsusp.S +@@ -0,0 +1,306 @@ ++/* ++ * Hibernation support specific for ARM ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2010 Texas Instruments, Inc. ++ * Copyright (C) 2006 Rafael J. Wysocki <rjw at sisk.pl> ++ * ++ * Contact: Hiroshi DOYU <Hiroshi.DOYU at nokia.com> ++ * ++ * License terms: GNU General Public License (GPL) version 2 ++ */ ++ ++ ++#include <linux/linkage.h> ++.text ++ ++#define LOCAL_WORD(x) \ ++ .data ; \ ++ .p2align 2 ; \ ++ .type x, #object ; \ ++ .size x, 4 ; \ ++x: ; \ ++ .long 1 ++ ++#define WORD_ADDR(x) \ ++ .align 2 ; \ ++.L##x: ; \ ++ .word x ++ ++/*user*/ ++LOCAL_WORD(saved_context_r0) ++LOCAL_WORD(saved_context_r1) ++LOCAL_WORD(saved_context_r2) ++LOCAL_WORD(saved_context_r3) ++LOCAL_WORD(saved_context_r4) ++LOCAL_WORD(saved_context_r5) ++LOCAL_WORD(saved_context_r6) ++LOCAL_WORD(saved_context_r7) ++LOCAL_WORD(saved_context_r8) ++LOCAL_WORD(saved_context_r9) ++LOCAL_WORD(saved_context_r10) ++LOCAL_WORD(saved_context_r11) ++LOCAL_WORD(saved_context_r12) ++LOCAL_WORD(saved_context_r13) ++LOCAL_WORD(saved_context_r14) ++LOCAL_WORD(saved_cpsr) ++ ++LOCAL_WORD(saved_context_r8_fiq) ++LOCAL_WORD(saved_context_r9_fiq) ++LOCAL_WORD(saved_context_r10_fiq) ++LOCAL_WORD(saved_context_r11_fiq) ++LOCAL_WORD(saved_context_r12_fiq) ++LOCAL_WORD(saved_context_r13_fiq) ++LOCAL_WORD(saved_context_r14_fiq) ++LOCAL_WORD(saved_spsr_fiq) ++ ++LOCAL_WORD(saved_context_r13_irq) ++LOCAL_WORD(saved_context_r14_irq) ++LOCAL_WORD(saved_spsr_irq) ++ ++LOCAL_WORD(saved_context_r13_svc) ++LOCAL_WORD(saved_context_r14_svc) ++LOCAL_WORD(saved_spsr_svc) ++ ++LOCAL_WORD(saved_context_r13_abt) ++LOCAL_WORD(saved_context_r14_abt) ++LOCAL_WORD(saved_spsr_abt) ++ ++LOCAL_WORD(saved_context_r13_und) ++LOCAL_WORD(saved_context_r14_und) ++LOCAL_WORD(saved_spsr_und) ++ ++#define CHANGE_MODE(x) \ ++ mov r1, r0 ; \ ++ bic r1, r1, #0x1f ; \ ++ orr r1, r1, #0x##x ; \ ++ msr cpsr_c, r1 ++ ++ENTRY(swsusp_arch_suspend) ++ /* ++ * Save current program status register ++ */ ++ ldr r3, .Lsaved_cpsr ++ mrs r0, cpsr ++ str r0, [r3] ++ ++ CHANGE_MODE(1f) /* Change to system(user) mode*/ ++ ldr r3, .Lsaved_context_r0 ++ stmia r3, {r0-r14} ++ ++ CHANGE_MODE(11) /* change to fiq mode */ ++ /* save nonvolatile int register */ ++ ldr r3, .Lsaved_context_r8_fiq ++ stmia r3, {r8-r14} ++ /* save spsr_fiq register */ ++ ldr r3, .Lsaved_spsr_fiq ++ mrs r1, spsr ++ str r1, [r3] ++ ++ CHANGE_MODE(12) /* change to irq mode */ ++ /* save nonvolatile int register */ ++ ldr r3, .Lsaved_context_r13_irq ++ stmia r3, {r13-r14} ++ /* save spsr_irq register */ ++ ldr r3, .Lsaved_spsr_irq ++ mrs r1, spsr ++ str r1, [r3] ++ ++ CHANGE_MODE(13) /* change to svc mode */ ++ /* save nonvolatile int register */ ++ ldr r3, .Lsaved_context_r13_svc ++ stmia r3, {r13-r14} ++ /* save spsr_svc register */ ++ ldr r3, .Lsaved_spsr_svc ++ mrs r1, spsr ++ str r1, [r3] ++ ++ CHANGE_MODE(17) /* change to abt mode */ ++ /* save nonvolatile int register */ ++ ldr r3, .Lsaved_context_r13_abt ++ stmia r3, {r13-r14} ++ /* save spsr_abt register */ ++ ldr r3, .Lsaved_spsr_abt ++ mrs r1, spsr ++ str r1, [r3] ++ ++ CHANGE_MODE(1b) /* change to und mode */ ++ /* save nonvolatile int register */ ++ ldr r3, .Lsaved_context_r13_und ++ stmia r3, {r13-r14} ++ /* save spsr_und register */ ++ ldr r3, .Lsaved_spsr_und ++ mrs r1, spsr ++ str r1, [r3] ++ ++ /* ++ * Go back to original SVC mode ++ */ ++ msr cpsr_c, r0 ++ ++ bl swsusp_save ++ ++#ifdef CONFIG_DEBUG_LL ++ adr r0, str_marker_1 ++ bl printascii ++ mov r0, #0 ++#endif ++ ++ /* ++ * Restore return address ++ */ ++ ldr r3, .Lsaved_context_r14_svc ++ ldr lr, [r3] ++ mov pc, lr ++ ++ WORD_ADDR(saved_context_r0) ++ WORD_ADDR(saved_cpsr) ++ ++ WORD_ADDR(saved_context_r8_fiq) ++ WORD_ADDR(saved_spsr_fiq) ++ ++ WORD_ADDR(saved_context_r13_irq) ++ WORD_ADDR(saved_spsr_irq) ++ ++ WORD_ADDR(saved_context_r13_svc) ++ WORD_ADDR(saved_context_r14_svc) ++ WORD_ADDR(saved_spsr_svc) ++ ++ WORD_ADDR(saved_context_r13_abt) ++ WORD_ADDR(saved_spsr_abt) ++ ++ WORD_ADDR(saved_context_r13_und) ++ WORD_ADDR(saved_spsr_und) ++ ++ENDPROC(swsusp_arch_suspend) ++ ++ENTRY(swsusp_arch_resume) ++ ++#ifdef CONFIG_DEBUG_LL ++ adr r0, str_marker_1 ++ bl printascii ++ mov r0, #0 ++#endif ++ ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ /* FIXME: need to check the cp15, c0 #4, ++ * "Instruction Set Attributes Register 4" ++ */ ++ #dsb ++ #isb ++ ++#else /* CONFIG_HISI_SNAPSHOT_BOOT */ ++ ++ /* ++ * Restore_pblist is the starting point for loaded pages ++ */ ++ ldr r0, .Lrestore_pblist ++ ldr r6, [r0] ++ ++ .Lcopy_loop: ++ ldr r4, [r6] /* src IOW present address */ ++ ldr r5, [r6, #4] /* dst IOW original address*/ ++ mov r9, #1024 /* No. of entries in one page(4 bytes each entry)*/ ++ ++ .Lcopy_one_page: ++ /* ++ * This loop could be optimized by using stm and ldm. ++ */ ++ ldr r8, [r4], #4 ++ str r8, [r5], #4 ++ subs r9, r9, #1 ++ bne .Lcopy_one_page ++ ++ /* The last field of struct pbe is a pointer to the next pbe struct */ ++ ldr r6, [r6, #8] ++ cmp r6, #0 ++ bne .Lcopy_loop ++#endif /* CONFIG_HISI_SNAPSHOT_BOOT */ ++ /* ++ * Restore SVC context ++ */ ++ ldr r3, .Lsaved_context_r13_svc ++ ldmia r3, {r13-r14} ++ ldr r3, .Lsaved_spsr_svc ++ ldr r1, [r3] ++ msr spsr_cxsf, r1 ++ ++ mrs r0, cpsr /* Save current mode into r0 */ ++ ++ CHANGE_MODE(11) /* change to fiq mode */ ++ /* restore nonvolatile int register */ ++ ldr r3, .Lsaved_context_r8_fiq ++ ldmia r3, {r8-r14} ++ /* restore spsr_fiq register */ ++ ldr r3, .Lsaved_spsr_fiq ++ ldr r1, [r3] ++ msr spsr_cxsf, r1 ++ ++ CHANGE_MODE(12) /* change to irq mode */ ++ /* restore nonvolatile int register */ ++ ldr r3, .Lsaved_context_r13_irq ++ ldmia r3, {r13-r14} ++ /* restore spsr_irq register */ ++ ldr r3, .Lsaved_spsr_irq ++ ldr r1, [r3] ++ msr spsr_cxsf, r1 ++ ++ CHANGE_MODE(17) /* change to abt mode */ ++ /* restore nonvolatile int register */ ++ ldr r3, .Lsaved_context_r13_abt ++ ldmia r3, {r13-r14} ++ /* restore spsr_abt register */ ++ ldr r3, .Lsaved_spsr_abt ++ ldr r1, [r3] ++ msr spsr_cxsf, r1 ++ ++ CHANGE_MODE(1b) /* change to und mode */ ++ /* restore nonvolatile int register */ ++ ldr r3, .Lsaved_context_r13_und ++ ldmia r3, {r13-r14} ++ /* restore spsr_und register */ ++ ldr r3, .Lsaved_spsr_und ++ ldr r1, [r3] ++ msr spsr_cxsf, r1 ++ ++ CHANGE_MODE(1f) /* Change to system(user) mode */ ++ ++ /* in_suspend = 0 */ ++ ldr r3,.Lsaved_in_suspend ++ mov r1,#0 ++ str r1,[r3] ++ ++ /* ++ * Restore User context ++ */ ++ ldr r3, .Lsaved_context_r0 ++ ldmia r3, {r0-r14} ++ ldr r3, .Lsaved_cpsr ++ ldr r1, [r3] ++ msr cpsr_cxsf, r1 ++ ++ /* ++ * Flush TLB (Invalidate unified TLB unlocked entries) ++ */ ++ mov r1, #0 ++ mcr p15, 0, r1, c8, c7, 0 ++ ++#ifdef CONFIG_DEBUG_LL ++ adr r0, str_marker_1 ++ bl printascii ++ mov r0, #0 ++#endif ++ ++ /* Set the return value */ ++ mov r0, #0 ++ ++ /* Restore return address */ ++ ldr r3, .Lsaved_context_r14_svc ++ ldr lr, [r3] ++ mov pc, lr ++ENDPROC(swsusp_arch_resume) ++ ++str_marker_1: .asciz "swsusp_arch_mark!!!\n\0" ++ ++ .align 4 ++.Lsaved_in_suspend: .long in_suspend +diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S +index 2465995..11da0f5 100644 +--- a/arch/arm/mm/cache-v6.S ++++ b/arch/arm/mm/cache-v6.S +@@ -270,6 +270,11 @@ v6_dma_clean_range: + * - end - virtual end address of region + */ + ENTRY(v6_dma_flush_range) ++#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT ++ sub r2, r1, r0 ++ cmp r2, #CONFIG_CACHE_FLUSH_RANGE_LIMIT ++ bhi v6_dma_flush_dcache_all ++#endif + #ifdef CONFIG_DMA_CACHE_RWFO + ldrb r2, [r0] @ read for ownership + strb r2, [r0] @ write for ownership +@@ -292,6 +297,18 @@ ENTRY(v6_dma_flush_range) + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer + ret lr + ++#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT ++v6_dma_flush_dcache_all: ++ mov r0, #0 ++#ifdef HARVARD_CACHE ++ mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate ++#else ++ mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate ++#endif ++ mcr p15, 0, r0, c7, c10, 4 @ drain write buffer ++ mov pc, lr ++#endif ++ + /* + * dma_map_area(start, size, dir) + * - start - kernel virtual start address +diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c +index e890711..369dc9b 100644 +--- a/arch/arm/mm/dma-mapping.c ++++ b/arch/arm/mm/dma-mapping.c +@@ -221,7 +221,7 @@ static u64 get_coherent_dma_mask(struct device *dev) + return mask; + } + +-static void __dma_clear_buffer(struct page *page, size_t size) ++void __dma_clear_buffer(struct page *page, size_t size) + { + /* + * Ensure that the allocated pages are zeroed, and that any data +@@ -246,6 +246,7 @@ static void __dma_clear_buffer(struct page *page, size_t size) + outer_flush_range(__pa(ptr), __pa(ptr) + size); + } + } ++EXPORT_SYMBOL(__dma_clear_buffer); + + /* + * Allocate a DMA buffer for 'dev' of size 'size' using the +@@ -465,6 +466,12 @@ static void __dma_remap(struct page *page, size_t size, pgprot_t prot) + flush_tlb_kernel_range(start, end); + } + ++void hisi_flush_tlb_kernel_range(unsigned long start, unsigned long end) ++{ ++ flush_tlb_kernel_range(start, end); ++} ++EXPORT_SYMBOL(hisi_flush_tlb_kernel_range); ++ + static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp, + pgprot_t prot, struct page **ret_page, + const void *caller) +diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c +index eb8830a..b784579 100644 +--- a/arch/arm/mm/fault.c ++++ b/arch/arm/mm/fault.c +@@ -274,10 +274,10 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) + local_irq_enable(); + + /* +- * If we're in an interrupt or have no user ++ * If we're in an interrupt, or have no irqs, or have no user + * context, we must not take the fault.. + */ +- if (in_atomic() || !mm) ++ if (in_atomic() || irqs_disabled() || !mm) + goto no_context; + + if (user_mode(regs)) +diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig +index dc2d66c..43fe143 100644 +--- a/arch/arm64/Kconfig ++++ b/arch/arm64/Kconfig +@@ -37,6 +37,7 @@ config ARM64 + select HAVE_ARCH_AUDITSYSCALL + select HAVE_ARCH_JUMP_LABEL + select HAVE_ARCH_KGDB ++ select HAVE_ARCH_SECCOMP_FILTER + select HAVE_ARCH_TRACEHOOK + select HAVE_BPF_JIT + select HAVE_C_RECORDMCOUNT +@@ -88,6 +89,10 @@ config MMU + config NO_IOPORT_MAP + def_bool y if !PCI + ++config ILLEGAL_POINTER_VALUE ++ hex ++ default 0xdead000000000000 ++ + config STACKTRACE_SUPPORT + def_bool y + +@@ -474,6 +479,19 @@ config ARCH_HAS_CACHE_LINE_SIZE + + source "mm/Kconfig" + ++config SECCOMP ++ bool "Enable seccomp to safely compute untrusted bytecode" ++ ---help--- ++ This kernel feature is useful for number crunching applications ++ that may need to compute untrusted bytecode during their ++ execution. By using pipes or other transports made available to ++ the process as file descriptors supporting the read/write ++ syscalls, it's possible to isolate those applications in ++ their own address space using seccomp. Once seccomp is ++ enabled via prctl(PR_SET_SECCOMP), it cannot be disabled ++ and the task is only allowed to execute a few safe syscalls ++ defined by each seccomp mode. ++ + config XEN_DOM0 + def_bool y + depends on XEN +@@ -490,6 +508,74 @@ config FORCE_MAX_ZONEORDER + default "14" if (ARM64_64K_PAGES && TRANSPARENT_HUGEPAGE) + default "11" + ++menuconfig ARMV8_DEPRECATED ++ bool "Emulate deprecated/obsolete ARMv8 instructions" ++ depends on COMPAT ++ help ++ Legacy software support may require certain instructions ++ that have been deprecated or obsoleted in the architecture. ++ ++ Enable this config to enable selective emulation of these ++ features. ++ ++ If unsure, say Y ++ ++if ARMV8_DEPRECATED ++ ++config SWP_EMULATION ++ bool "Emulate SWP/SWPB instructions" ++ help ++ ARMv8 obsoletes the use of A32 SWP/SWPB instructions such that ++ they are always undefined. Say Y here to enable software ++ emulation of these instructions for userspace using LDXR/STXR. ++ ++ In some older versions of glibc [<=2.8] SWP is used during futex ++ trylock() operations with the assumption that the code will not ++ be preempted. This invalid assumption may be more likely to fail ++ with SWP emulation enabled, leading to deadlock of the user ++ application. ++ ++ NOTE: when accessing uncached shared regions, LDXR/STXR rely ++ on an external transaction monitoring block called a global ++ monitor to maintain update atomicity. If your system does not ++ implement a global monitor, this option can cause programs that ++ perform SWP operations to uncached memory to deadlock. ++ ++ If unsure, say Y ++ ++config CP15_BARRIER_EMULATION ++ bool "Emulate CP15 Barrier instructions" ++ help ++ The CP15 barrier instructions - CP15ISB, CP15DSB, and ++ CP15DMB - are deprecated in ARMv8 (and ARMv7). It is ++ strongly recommended to use the ISB, DSB, and DMB ++ instructions instead. ++ ++ Say Y here to enable software emulation of these ++ instructions for AArch32 userspace code. When this option is ++ enabled, CP15 barrier usage is traced which can help ++ identify software that needs updating. ++ ++ If unsure, say Y ++ ++config SETEND_EMULATION ++ bool "Emulate SETEND instruction" ++ help ++ The SETEND instruction alters the data-endianness of the ++ AArch32 EL0, and is deprecated in ARMv8. ++ ++ Say Y here to enable software emulation of the instruction ++ for AArch32 userspace code. ++ ++ Note: All the cpus on the system must have mixed endian support at EL0 ++ for this feature to be enabled. If a new CPU - which doesn't support mixed ++ endian - is hotplugged in after this feature has been enabled, there could ++ be unexpected results in the applications. ++ ++ If unsure, say Y ++ ++endif ++ + endmenu + + menu "Boot options" +@@ -502,6 +588,23 @@ config CMDLINE + entering them here. As a minimum, you should specify the the + root device (e.g. root=/dev/nfs). + ++choice ++ prompt "Kernel command line type" if CMDLINE != "" ++ default CMDLINE_FROM_BOOTLOADER ++ ++config CMDLINE_FROM_BOOTLOADER ++ bool "Use bootloader kernel arguments if available" ++ help ++ Uses the command-line options passed by the boot loader. If ++ the boot loader doesn't provide any, the default kernel command ++ string provided in CMDLINE will be used. ++ ++config CMDLINE_EXTEND ++ bool "Extend bootloader kernel arguments" ++ help ++ The command-line arguments provided by the boot loader will be ++ appended to the default kernel command string. ++ + config CMDLINE_FORCE + bool "Always use the default kernel command string" + help +@@ -509,6 +612,7 @@ config CMDLINE_FORCE + loader passes other arguments to the kernel. + This is useful if you cannot or don't want to change the + command-line options your boot loader passes to the kernel. ++endchoice + + config EFI_STUB + bool +@@ -530,6 +634,21 @@ config EFI + allow the kernel to be booted as an EFI application. This + is only useful on systems that have UEFI firmware. + ++config BUILD_ARM64_APPENDED_DTB_IMAGE ++ bool "Build a concatenated Image.gz/dtb by default" ++ depends on OF ++ help ++ Enabling this option will cause a concatenated Image.gz and list of ++ DTBs to be built by default (instead of a standalone Image.gz.) ++ The image will built in arch/arm64/boot/Image.gz-dtb ++ ++config BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES ++ string "Default dtb names" ++ depends on BUILD_ARM64_APPENDED_DTB_IMAGE ++ help ++ Space separated list of names of dtbs to append when ++ building a concatenated Image.gz-dtb. ++ + endmenu + + menu "Userspace binary formats" +diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug +index 0a12933..8dd3a55 100644 +--- a/arch/arm64/Kconfig.debug ++++ b/arch/arm64/Kconfig.debug +@@ -54,4 +54,6 @@ config DEBUG_SET_MODULE_RONX + against certain classes of kernel exploits. + If in doubt, say "N". + ++source "drivers/hwtracing/coresight/Kconfig" ++ + endmenu +diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile +index 20901ff..18daa9f 100644 +--- a/arch/arm64/Makefile ++++ b/arch/arm64/Makefile +@@ -20,6 +20,7 @@ LIBGCC := $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name) + KBUILD_DEFCONFIG := defconfig + + KBUILD_CFLAGS += -mgeneral-regs-only ++KBUILD_CFLAGS += -fno-pic + ifeq ($(CONFIG_CPU_BIG_ENDIAN), y) + KBUILD_CPPFLAGS += -mbig-endian + AS += -EB +@@ -54,7 +55,12 @@ libs-y += $(LIBGCC) + libs-$(CONFIG_EFI_STUB) += drivers/firmware/efi/libstub/ + + # Default target when executing plain make ++ifeq ($(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE),y) ++KBUILD_IMAGE := Image.gz-dtb ++else + KBUILD_IMAGE := Image.gz ++endif ++ + KBUILD_DTBS := dtbs + + all: $(KBUILD_IMAGE) $(KBUILD_DTBS) +@@ -70,8 +76,16 @@ zinstall install: vmlinux + %.dtb: scripts + $(Q)$(MAKE) $(build)=$(boot)/dts $(boot)/dts/$@ + +-dtbs: scripts +- $(Q)$(MAKE) $(build)=$(boot)/dts dtbs ++PHONY += dtbs dtbs_install ++ ++dtbs: prepare scripts ++ $(Q)$(MAKE) $(build)=$(boot)/dts ++ ++dtbs_install: ++ $(Q)$(MAKE) $(dtbinst)=$(boot)/dts ++ ++Image.gz-dtb: vmlinux scripts dtbs ++ $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + + PHONY += vdso_install + vdso_install: +@@ -85,6 +99,7 @@ define archhelp + echo '* Image.gz - Compressed kernel image (arch/$(ARCH)/boot/Image.gz)' + echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)' + echo '* dtbs - Build device tree blobs for enabled boards' ++ echo ' dtbs_install - Install dtbs to $(INSTALL_DTBS_PATH)' + echo ' install - Install uncompressed kernel' + echo ' zinstall - Install compressed kernel' + echo ' Install using (your) ~/bin/installkernel or' +diff --git a/arch/arm64/boot/.gitignore b/arch/arm64/boot/.gitignore +index 8dab0bb..eb35511 100644 +--- a/arch/arm64/boot/.gitignore ++++ b/arch/arm64/boot/.gitignore +@@ -1,2 +1,3 @@ + Image + Image.gz ++Image.gz-dtb +diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile +index 5a0e3ab..71f513f 100644 +--- a/arch/arm64/boot/Makefile ++++ b/arch/arm64/boot/Makefile +@@ -14,14 +14,27 @@ + # Based on the ia64 boot/Makefile. + # + ++include $(srctree)/arch/arm64/boot/dts/Makefile ++ + targets := Image Image.gz + ++DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES)) ++ifneq ($(DTB_NAMES),) ++DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES)) ++DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST)) ++else ++DTB_OBJS := $(shell find $(obj)/dts/ -name \*.dtb) ++endif ++ + $(obj)/Image: vmlinux FORCE + $(call if_changed,objcopy) + + $(obj)/Image.gz: $(obj)/Image FORCE + $(call if_changed,gzip) + ++$(obj)/Image.gz-dtb: $(obj)/Image.gz $(DTB_OBJS) FORCE ++ $(call if_changed,cat) ++ + install: $(obj)/Image + $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \ + $(obj)/Image System.map "$(INSTALL_PATH)" +diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile +index f8001a6..68d2567 100644 +--- a/arch/arm64/boot/dts/Makefile ++++ b/arch/arm64/boot/dts/Makefile +@@ -1,10 +1,6 @@ +-dtb-$(CONFIG_ARCH_THUNDER) += thunder-88xx.dtb +-dtb-$(CONFIG_ARCH_VEXPRESS) += rtsm_ve-aemv8a.dtb foundation-v8.dtb +-dtb-$(CONFIG_ARCH_XGENE) += apm-mustang.dtb ++dts-dirs += apm ++dts-dirs += arm ++dts-dirs += cavium + +-targets += dtbs +-targets += $(dtb-y) +- +-dtbs: $(addprefix $(obj)/, $(dtb-y)) +- +-clean-files := *.dtb ++always := $(dtb-y) ++subdir-y := $(dts-dirs) +diff --git a/arch/arm64/boot/dts/apm-mustang.dts b/arch/arm64/boot/dts/apm-mustang.dts +deleted file mode 100644 +index 2e25de0..0000000 +--- a/arch/arm64/boot/dts/apm-mustang.dts ++++ /dev/null +@@ -1,50 +0,0 @@ +-/* +- * dts file for AppliedMicro (APM) Mustang Board +- * +- * Copyright (C) 2013, Applied Micro Circuits Corporation +- * +- * 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. +- */ +- +-/dts-v1/; +- +-/include/ "apm-storm.dtsi" +- +-/ { +- model = "APM X-Gene Mustang board"; +- compatible = "apm,mustang", "apm,xgene-storm"; +- +- chosen { }; +- +- memory { +- device_type = "memory"; +- reg = < 0x1 0x00000000 0x0 0x80000000 >; /* Updated by bootloader */ +- }; +-}; +- +-&pcie0clk { +- status = "ok"; +-}; +- +-&pcie0 { +- status = "ok"; +-}; +- +-&serial0 { +- status = "ok"; +-}; +- +-&menet { +- status = "ok"; +-}; +- +-&sgenet0 { +- status = "ok"; +-}; +- +-&xgenet { +- status = "ok"; +-}; +diff --git a/arch/arm64/boot/dts/apm-storm.dtsi b/arch/arm64/boot/dts/apm-storm.dtsi +deleted file mode 100644 +index f1ad9c2..0000000 +--- a/arch/arm64/boot/dts/apm-storm.dtsi ++++ /dev/null +@@ -1,660 +0,0 @@ +-/* +- * dts file for AppliedMicro (APM) X-Gene Storm SOC +- * +- * Copyright (C) 2013, Applied Micro Circuits Corporation +- * +- * 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. +- */ +- +-/ { +- compatible = "apm,xgene-storm"; +- interrupt-parent = <&gic>; +- #address-cells = <2>; +- #size-cells = <2>; +- +- cpus { +- #address-cells = <2>; +- #size-cells = <0>; +- +- cpu@000 { +- device_type = "cpu"; +- compatible = "apm,potenza", "arm,armv8"; +- reg = <0x0 0x000>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x1 0x0000fff8>; +- }; +- cpu@001 { +- device_type = "cpu"; +- compatible = "apm,potenza", "arm,armv8"; +- reg = <0x0 0x001>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x1 0x0000fff8>; +- }; +- cpu@100 { +- device_type = "cpu"; +- compatible = "apm,potenza", "arm,armv8"; +- reg = <0x0 0x100>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x1 0x0000fff8>; +- }; +- cpu@101 { +- device_type = "cpu"; +- compatible = "apm,potenza", "arm,armv8"; +- reg = <0x0 0x101>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x1 0x0000fff8>; +- }; +- cpu@200 { +- device_type = "cpu"; +- compatible = "apm,potenza", "arm,armv8"; +- reg = <0x0 0x200>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x1 0x0000fff8>; +- }; +- cpu@201 { +- device_type = "cpu"; +- compatible = "apm,potenza", "arm,armv8"; +- reg = <0x0 0x201>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x1 0x0000fff8>; +- }; +- cpu@300 { +- device_type = "cpu"; +- compatible = "apm,potenza", "arm,armv8"; +- reg = <0x0 0x300>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x1 0x0000fff8>; +- }; +- cpu@301 { +- device_type = "cpu"; +- compatible = "apm,potenza", "arm,armv8"; +- reg = <0x0 0x301>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x1 0x0000fff8>; +- }; +- }; +- +- gic: interrupt-controller@78010000 { +- compatible = "arm,cortex-a15-gic"; +- #interrupt-cells = <3>; +- interrupt-controller; +- reg = <0x0 0x78010000 0x0 0x1000>, /* GIC Dist */ +- <0x0 0x78020000 0x0 0x1000>, /* GIC CPU */ +- <0x0 0x78040000 0x0 0x2000>, /* GIC VCPU Control */ +- <0x0 0x78060000 0x0 0x2000>; /* GIC VCPU */ +- interrupts = <1 9 0xf04>; /* GIC Maintenence IRQ */ +- }; +- +- timer { +- compatible = "arm,armv8-timer"; +- interrupts = <1 0 0xff01>, /* Secure Phys IRQ */ +- <1 13 0xff01>, /* Non-secure Phys IRQ */ +- <1 14 0xff01>, /* Virt IRQ */ +- <1 15 0xff01>; /* Hyp IRQ */ +- clock-frequency = <50000000>; +- }; +- +- soc { +- compatible = "simple-bus"; +- #address-cells = <2>; +- #size-cells = <2>; +- ranges; +- +- clocks { +- #address-cells = <2>; +- #size-cells = <2>; +- ranges; +- refclk: refclk { +- compatible = "fixed-clock"; +- #clock-cells = <1>; +- clock-frequency = <100000000>; +- clock-output-names = "refclk"; +- }; +- +- pcppll: pcppll@17000100 { +- compatible = "apm,xgene-pcppll-clock"; +- #clock-cells = <1>; +- clocks = <&refclk 0>; +- clock-names = "pcppll"; +- reg = <0x0 0x17000100 0x0 0x1000>; +- clock-output-names = "pcppll"; +- type = <0>; +- }; +- +- socpll: socpll@17000120 { +- compatible = "apm,xgene-socpll-clock"; +- #clock-cells = <1>; +- clocks = <&refclk 0>; +- clock-names = "socpll"; +- reg = <0x0 0x17000120 0x0 0x1000>; +- clock-output-names = "socpll"; +- type = <1>; +- }; +- +- socplldiv2: socplldiv2 { +- compatible = "fixed-factor-clock"; +- #clock-cells = <1>; +- clocks = <&socpll 0>; +- clock-names = "socplldiv2"; +- clock-mult = <1>; +- clock-div = <2>; +- clock-output-names = "socplldiv2"; +- }; +- +- qmlclk: qmlclk { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- clock-names = "qmlclk"; +- reg = <0x0 0x1703C000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "qmlclk"; +- }; +- +- ethclk: ethclk { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- clock-names = "ethclk"; +- reg = <0x0 0x17000000 0x0 0x1000>; +- reg-names = "div-reg"; +- divider-offset = <0x238>; +- divider-width = <0x9>; +- divider-shift = <0x0>; +- clock-output-names = "ethclk"; +- }; +- +- menetclk: menetclk { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <ðclk 0>; +- reg = <0x0 0x1702C000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "menetclk"; +- }; +- +- sge0clk: sge0clk@1f21c000 { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f21c000 0x0 0x1000>; +- reg-names = "csr-reg"; +- csr-mask = <0x3>; +- clock-output-names = "sge0clk"; +- }; +- +- xge0clk: xge0clk@1f61c000 { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f61c000 0x0 0x1000>; +- reg-names = "csr-reg"; +- csr-mask = <0x3>; +- clock-output-names = "xge0clk"; +- }; +- +- sataphy1clk: sataphy1clk@1f21c000 { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f21c000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "sataphy1clk"; +- status = "disabled"; +- csr-offset = <0x4>; +- csr-mask = <0x00>; +- enable-offset = <0x0>; +- enable-mask = <0x06>; +- }; +- +- sataphy2clk: sataphy1clk@1f22c000 { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f22c000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "sataphy2clk"; +- status = "ok"; +- csr-offset = <0x4>; +- csr-mask = <0x3a>; +- enable-offset = <0x0>; +- enable-mask = <0x06>; +- }; +- +- sataphy3clk: sataphy1clk@1f23c000 { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f23c000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "sataphy3clk"; +- status = "ok"; +- csr-offset = <0x4>; +- csr-mask = <0x3a>; +- enable-offset = <0x0>; +- enable-mask = <0x06>; +- }; +- +- sata01clk: sata01clk@1f21c000 { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f21c000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "sata01clk"; +- csr-offset = <0x4>; +- csr-mask = <0x05>; +- enable-offset = <0x0>; +- enable-mask = <0x39>; +- }; +- +- sata23clk: sata23clk@1f22c000 { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f22c000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "sata23clk"; +- csr-offset = <0x4>; +- csr-mask = <0x05>; +- enable-offset = <0x0>; +- enable-mask = <0x39>; +- }; +- +- sata45clk: sata45clk@1f23c000 { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f23c000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "sata45clk"; +- csr-offset = <0x4>; +- csr-mask = <0x05>; +- enable-offset = <0x0>; +- enable-mask = <0x39>; +- }; +- +- rtcclk: rtcclk@17000000 { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x17000000 0x0 0x2000>; +- reg-names = "csr-reg"; +- csr-offset = <0xc>; +- csr-mask = <0x2>; +- enable-offset = <0x10>; +- enable-mask = <0x2>; +- clock-output-names = "rtcclk"; +- }; +- +- rngpkaclk: rngpkaclk@17000000 { +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x17000000 0x0 0x2000>; +- reg-names = "csr-reg"; +- csr-offset = <0xc>; +- csr-mask = <0x10>; +- enable-offset = <0x10>; +- enable-mask = <0x10>; +- clock-output-names = "rngpkaclk"; +- }; +- +- pcie0clk: pcie0clk@1f2bc000 { +- status = "disabled"; +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f2bc000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "pcie0clk"; +- }; +- +- pcie1clk: pcie1clk@1f2cc000 { +- status = "disabled"; +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f2cc000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "pcie1clk"; +- }; +- +- pcie2clk: pcie2clk@1f2dc000 { +- status = "disabled"; +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f2dc000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "pcie2clk"; +- }; +- +- pcie3clk: pcie3clk@1f50c000 { +- status = "disabled"; +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f50c000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "pcie3clk"; +- }; +- +- pcie4clk: pcie4clk@1f51c000 { +- status = "disabled"; +- compatible = "apm,xgene-device-clock"; +- #clock-cells = <1>; +- clocks = <&socplldiv2 0>; +- reg = <0x0 0x1f51c000 0x0 0x1000>; +- reg-names = "csr-reg"; +- clock-output-names = "pcie4clk"; +- }; +- }; +- +- pcie0: pcie@1f2b0000 { +- status = "disabled"; +- device_type = "pci"; +- compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; +- #interrupt-cells = <1>; +- #size-cells = <2>; +- #address-cells = <3>; +- reg = < 0x00 0x1f2b0000 0x0 0x00010000 /* Controller registers */ +- 0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */ +- reg-names = "csr", "cfg"; +- ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */ +- 0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */ +- dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 +- 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; +- interrupt-map-mask = <0x0 0x0 0x0 0x7>; +- interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1 +- 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1 +- 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1 +- 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>; +- dma-coherent; +- clocks = <&pcie0clk 0>; +- }; +- +- pcie1: pcie@1f2c0000 { +- status = "disabled"; +- device_type = "pci"; +- compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; +- #interrupt-cells = <1>; +- #size-cells = <2>; +- #address-cells = <3>; +- reg = < 0x00 0x1f2c0000 0x0 0x00010000 /* Controller registers */ +- 0xd0 0xd0000000 0x0 0x00040000>; /* PCI config space */ +- reg-names = "csr", "cfg"; +- ranges = <0x01000000 0x0 0x00000000 0xd0 0x10000000 0x00 0x00010000 /* io */ +- 0x02000000 0x0 0x80000000 0xd1 0x80000000 0x00 0x80000000>; /* mem */ +- dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 +- 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; +- interrupt-map-mask = <0x0 0x0 0x0 0x7>; +- interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc8 0x1 +- 0x0 0x0 0x0 0x2 &gic 0x0 0xc9 0x1 +- 0x0 0x0 0x0 0x3 &gic 0x0 0xca 0x1 +- 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>; +- dma-coherent; +- clocks = <&pcie1clk 0>; +- }; +- +- pcie2: pcie@1f2d0000 { +- status = "disabled"; +- device_type = "pci"; +- compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; +- #interrupt-cells = <1>; +- #size-cells = <2>; +- #address-cells = <3>; +- reg = < 0x00 0x1f2d0000 0x0 0x00010000 /* Controller registers */ +- 0x90 0xd0000000 0x0 0x00040000>; /* PCI config space */ +- reg-names = "csr", "cfg"; +- ranges = <0x01000000 0x0 0x00000000 0x90 0x10000000 0x0 0x00010000 /* io */ +- 0x02000000 0x0 0x80000000 0x91 0x80000000 0x0 0x80000000>; /* mem */ +- dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 +- 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; +- interrupt-map-mask = <0x0 0x0 0x0 0x7>; +- interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xce 0x1 +- 0x0 0x0 0x0 0x2 &gic 0x0 0xcf 0x1 +- 0x0 0x0 0x0 0x3 &gic 0x0 0xd0 0x1 +- 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>; +- dma-coherent; +- clocks = <&pcie2clk 0>; +- }; +- +- pcie3: pcie@1f500000 { +- status = "disabled"; +- device_type = "pci"; +- compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; +- #interrupt-cells = <1>; +- #size-cells = <2>; +- #address-cells = <3>; +- reg = < 0x00 0x1f500000 0x0 0x00010000 /* Controller registers */ +- 0xa0 0xd0000000 0x0 0x00040000>; /* PCI config space */ +- reg-names = "csr", "cfg"; +- ranges = <0x01000000 0x0 0x00000000 0xa0 0x10000000 0x0 0x00010000 /* io */ +- 0x02000000 0x0 0x80000000 0xa1 0x80000000 0x0 0x80000000>; /* mem */ +- dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 +- 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; +- interrupt-map-mask = <0x0 0x0 0x0 0x7>; +- interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xd4 0x1 +- 0x0 0x0 0x0 0x2 &gic 0x0 0xd5 0x1 +- 0x0 0x0 0x0 0x3 &gic 0x0 0xd6 0x1 +- 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>; +- dma-coherent; +- clocks = <&pcie3clk 0>; +- }; +- +- pcie4: pcie@1f510000 { +- status = "disabled"; +- device_type = "pci"; +- compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; +- #interrupt-cells = <1>; +- #size-cells = <2>; +- #address-cells = <3>; +- reg = < 0x00 0x1f510000 0x0 0x00010000 /* Controller registers */ +- 0xc0 0xd0000000 0x0 0x00200000>; /* PCI config space */ +- reg-names = "csr", "cfg"; +- ranges = <0x01000000 0x0 0x00000000 0xc0 0x10000000 0x0 0x00010000 /* io */ +- 0x02000000 0x0 0x80000000 0xc1 0x80000000 0x0 0x80000000>; /* mem */ +- dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 +- 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; +- interrupt-map-mask = <0x0 0x0 0x0 0x7>; +- interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xda 0x1 +- 0x0 0x0 0x0 0x2 &gic 0x0 0xdb 0x1 +- 0x0 0x0 0x0 0x3 &gic 0x0 0xdc 0x1 +- 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>; +- dma-coherent; +- clocks = <&pcie4clk 0>; +- }; +- +- serial0: serial@1c020000 { +- status = "disabled"; +- device_type = "serial"; +- compatible = "ns16550a"; +- reg = <0 0x1c020000 0x0 0x1000>; +- reg-shift = <2>; +- clock-frequency = <10000000>; /* Updated by bootloader */ +- interrupt-parent = <&gic>; +- interrupts = <0x0 0x4c 0x4>; +- }; +- +- serial1: serial@1c021000 { +- status = "disabled"; +- device_type = "serial"; +- compatible = "ns16550a"; +- reg = <0 0x1c021000 0x0 0x1000>; +- reg-shift = <2>; +- clock-frequency = <10000000>; /* Updated by bootloader */ +- interrupt-parent = <&gic>; +- interrupts = <0x0 0x4d 0x4>; +- }; +- +- serial2: serial@1c022000 { +- status = "disabled"; +- device_type = "serial"; +- compatible = "ns16550a"; +- reg = <0 0x1c022000 0x0 0x1000>; +- reg-shift = <2>; +- clock-frequency = <10000000>; /* Updated by bootloader */ +- interrupt-parent = <&gic>; +- interrupts = <0x0 0x4e 0x4>; +- }; +- +- serial3: serial@1c023000 { +- status = "disabled"; +- device_type = "serial"; +- compatible = "ns16550a"; +- reg = <0 0x1c023000 0x0 0x1000>; +- reg-shift = <2>; +- clock-frequency = <10000000>; /* Updated by bootloader */ +- interrupt-parent = <&gic>; +- interrupts = <0x0 0x4f 0x4>; +- }; +- +- phy1: phy@1f21a000 { +- compatible = "apm,xgene-phy"; +- reg = <0x0 0x1f21a000 0x0 0x100>; +- #phy-cells = <1>; +- clocks = <&sataphy1clk 0>; +- status = "disabled"; +- apm,tx-boost-gain = <30 30 30 30 30 30>; +- apm,tx-eye-tuning = <2 10 10 2 10 10>; +- }; +- +- phy2: phy@1f22a000 { +- compatible = "apm,xgene-phy"; +- reg = <0x0 0x1f22a000 0x0 0x100>; +- #phy-cells = <1>; +- clocks = <&sataphy2clk 0>; +- status = "ok"; +- apm,tx-boost-gain = <30 30 30 30 30 30>; +- apm,tx-eye-tuning = <1 10 10 2 10 10>; +- }; +- +- phy3: phy@1f23a000 { +- compatible = "apm,xgene-phy"; +- reg = <0x0 0x1f23a000 0x0 0x100>; +- #phy-cells = <1>; +- clocks = <&sataphy3clk 0>; +- status = "ok"; +- apm,tx-boost-gain = <31 31 31 31 31 31>; +- apm,tx-eye-tuning = <2 10 10 2 10 10>; +- }; +- +- sata1: sata@1a000000 { +- compatible = "apm,xgene-ahci"; +- reg = <0x0 0x1a000000 0x0 0x1000>, +- <0x0 0x1f210000 0x0 0x1000>, +- <0x0 0x1f21d000 0x0 0x1000>, +- <0x0 0x1f21e000 0x0 0x1000>, +- <0x0 0x1f217000 0x0 0x1000>; +- interrupts = <0x0 0x86 0x4>; +- dma-coherent; +- status = "disabled"; +- clocks = <&sata01clk 0>; +- phys = <&phy1 0>; +- phy-names = "sata-phy"; +- }; +- +- sata2: sata@1a400000 { +- compatible = "apm,xgene-ahci"; +- reg = <0x0 0x1a400000 0x0 0x1000>, +- <0x0 0x1f220000 0x0 0x1000>, +- <0x0 0x1f22d000 0x0 0x1000>, +- <0x0 0x1f22e000 0x0 0x1000>, +- <0x0 0x1f227000 0x0 0x1000>; +- interrupts = <0x0 0x87 0x4>; +- dma-coherent; +- status = "ok"; +- clocks = <&sata23clk 0>; +- phys = <&phy2 0>; +- phy-names = "sata-phy"; +- }; +- +- sata3: sata@1a800000 { +- compatible = "apm,xgene-ahci"; +- reg = <0x0 0x1a800000 0x0 0x1000>, +- <0x0 0x1f230000 0x0 0x1000>, +- <0x0 0x1f23d000 0x0 0x1000>, +- <0x0 0x1f23e000 0x0 0x1000>; +- interrupts = <0x0 0x88 0x4>; +- dma-coherent; +- status = "ok"; +- clocks = <&sata45clk 0>; +- phys = <&phy3 0>; +- phy-names = "sata-phy"; +- }; +- +- rtc: rtc@10510000 { +- compatible = "apm,xgene-rtc"; +- reg = <0x0 0x10510000 0x0 0x400>; +- interrupts = <0x0 0x46 0x4>; +- #clock-cells = <1>; +- clocks = <&rtcclk 0>; +- }; +- +- menet: ethernet@17020000 { +- compatible = "apm,xgene-enet"; +- status = "disabled"; +- reg = <0x0 0x17020000 0x0 0xd100>, +- <0x0 0X17030000 0x0 0Xc300>, +- <0x0 0X10000000 0x0 0X200>; +- reg-names = "enet_csr", "ring_csr", "ring_cmd"; +- interrupts = <0x0 0x3c 0x4>; +- dma-coherent; +- clocks = <&menetclk 0>; +- /* mac address will be overwritten by the bootloader */ +- local-mac-address = [00 00 00 00 00 00]; +- phy-connection-type = "rgmii"; +- phy-handle = <&menetphy>; +- mdio { +- compatible = "apm,xgene-mdio"; +- #address-cells = <1>; +- #size-cells = <0>; +- menetphy: menetphy@3 { +- compatible = "ethernet-phy-id001c.c915"; +- reg = <0x3>; +- }; +- +- }; +- }; +- +- sgenet0: ethernet@1f210000 { +- compatible = "apm,xgene-enet"; +- status = "disabled"; +- reg = <0x0 0x1f210000 0x0 0xd100>, +- <0x0 0x1f200000 0x0 0Xc300>, +- <0x0 0x1B000000 0x0 0X200>; +- reg-names = "enet_csr", "ring_csr", "ring_cmd"; +- interrupts = <0x0 0xA0 0x4>; +- dma-coherent; +- clocks = <&sge0clk 0>; +- local-mac-address = [00 00 00 00 00 00]; +- phy-connection-type = "sgmii"; +- }; +- +- xgenet: ethernet@1f610000 { +- compatible = "apm,xgene-enet"; +- status = "disabled"; +- reg = <0x0 0x1f610000 0x0 0xd100>, +- <0x0 0x1f600000 0x0 0Xc300>, +- <0x0 0x18000000 0x0 0X200>; +- reg-names = "enet_csr", "ring_csr", "ring_cmd"; +- interrupts = <0x0 0x60 0x4>; +- dma-coherent; +- clocks = <&xge0clk 0>; +- /* mac address will be overwritten by the bootloader */ +- local-mac-address = [00 00 00 00 00 00]; +- phy-connection-type = "xgmii"; +- }; +- +- rng: rng@10520000 { +- compatible = "apm,xgene-rng"; +- reg = <0x0 0x10520000 0x0 0x100>; +- interrupts = <0x0 0x41 0x4>; +- clocks = <&rngpkaclk 0>; +- }; +- }; +-}; +diff --git a/arch/arm64/boot/dts/apm/Makefile b/arch/arm64/boot/dts/apm/Makefile +new file mode 100644 +index 0000000..a2afabb +--- /dev/null ++++ b/arch/arm64/boot/dts/apm/Makefile +@@ -0,0 +1,5 @@ ++dtb-$(CONFIG_ARCH_XGENE) += apm-mustang.dtb ++ ++always := $(dtb-y) ++subdir-y := $(dts-dirs) ++clean-files := *.dtb +diff --git a/arch/arm64/boot/dts/apm/apm-mustang.dts b/arch/arm64/boot/dts/apm/apm-mustang.dts +new file mode 100644 +index 0000000..2e25de0 +--- /dev/null ++++ b/arch/arm64/boot/dts/apm/apm-mustang.dts +@@ -0,0 +1,50 @@ ++/* ++ * dts file for AppliedMicro (APM) Mustang Board ++ * ++ * Copyright (C) 2013, Applied Micro Circuits Corporation ++ * ++ * 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. ++ */ ++ ++/dts-v1/; ++ ++/include/ "apm-storm.dtsi" ++ ++/ { ++ model = "APM X-Gene Mustang board"; ++ compatible = "apm,mustang", "apm,xgene-storm"; ++ ++ chosen { }; ++ ++ memory { ++ device_type = "memory"; ++ reg = < 0x1 0x00000000 0x0 0x80000000 >; /* Updated by bootloader */ ++ }; ++}; ++ ++&pcie0clk { ++ status = "ok"; ++}; ++ ++&pcie0 { ++ status = "ok"; ++}; ++ ++&serial0 { ++ status = "ok"; ++}; ++ ++&menet { ++ status = "ok"; ++}; ++ ++&sgenet0 { ++ status = "ok"; ++}; ++ ++&xgenet { ++ status = "ok"; ++}; +diff --git a/arch/arm64/boot/dts/apm/apm-storm.dtsi b/arch/arm64/boot/dts/apm/apm-storm.dtsi +new file mode 100644 +index 0000000..f1ad9c2 +--- /dev/null ++++ b/arch/arm64/boot/dts/apm/apm-storm.dtsi +@@ -0,0 +1,660 @@ ++/* ++ * dts file for AppliedMicro (APM) X-Gene Storm SOC ++ * ++ * Copyright (C) 2013, Applied Micro Circuits Corporation ++ * ++ * 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. ++ */ ++ ++/ { ++ compatible = "apm,xgene-storm"; ++ interrupt-parent = <&gic>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ cpus { ++ #address-cells = <2>; ++ #size-cells = <0>; ++ ++ cpu@000 { ++ device_type = "cpu"; ++ compatible = "apm,potenza", "arm,armv8"; ++ reg = <0x0 0x000>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x1 0x0000fff8>; ++ }; ++ cpu@001 { ++ device_type = "cpu"; ++ compatible = "apm,potenza", "arm,armv8"; ++ reg = <0x0 0x001>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x1 0x0000fff8>; ++ }; ++ cpu@100 { ++ device_type = "cpu"; ++ compatible = "apm,potenza", "arm,armv8"; ++ reg = <0x0 0x100>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x1 0x0000fff8>; ++ }; ++ cpu@101 { ++ device_type = "cpu"; ++ compatible = "apm,potenza", "arm,armv8"; ++ reg = <0x0 0x101>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x1 0x0000fff8>; ++ }; ++ cpu@200 { ++ device_type = "cpu"; ++ compatible = "apm,potenza", "arm,armv8"; ++ reg = <0x0 0x200>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x1 0x0000fff8>; ++ }; ++ cpu@201 { ++ device_type = "cpu"; ++ compatible = "apm,potenza", "arm,armv8"; ++ reg = <0x0 0x201>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x1 0x0000fff8>; ++ }; ++ cpu@300 { ++ device_type = "cpu"; ++ compatible = "apm,potenza", "arm,armv8"; ++ reg = <0x0 0x300>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x1 0x0000fff8>; ++ }; ++ cpu@301 { ++ device_type = "cpu"; ++ compatible = "apm,potenza", "arm,armv8"; ++ reg = <0x0 0x301>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x1 0x0000fff8>; ++ }; ++ }; ++ ++ gic: interrupt-controller@78010000 { ++ compatible = "arm,cortex-a15-gic"; ++ #interrupt-cells = <3>; ++ interrupt-controller; ++ reg = <0x0 0x78010000 0x0 0x1000>, /* GIC Dist */ ++ <0x0 0x78020000 0x0 0x1000>, /* GIC CPU */ ++ <0x0 0x78040000 0x0 0x2000>, /* GIC VCPU Control */ ++ <0x0 0x78060000 0x0 0x2000>; /* GIC VCPU */ ++ interrupts = <1 9 0xf04>; /* GIC Maintenence IRQ */ ++ }; ++ ++ timer { ++ compatible = "arm,armv8-timer"; ++ interrupts = <1 0 0xff01>, /* Secure Phys IRQ */ ++ <1 13 0xff01>, /* Non-secure Phys IRQ */ ++ <1 14 0xff01>, /* Virt IRQ */ ++ <1 15 0xff01>; /* Hyp IRQ */ ++ clock-frequency = <50000000>; ++ }; ++ ++ soc { ++ compatible = "simple-bus"; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ clocks { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ refclk: refclk { ++ compatible = "fixed-clock"; ++ #clock-cells = <1>; ++ clock-frequency = <100000000>; ++ clock-output-names = "refclk"; ++ }; ++ ++ pcppll: pcppll@17000100 { ++ compatible = "apm,xgene-pcppll-clock"; ++ #clock-cells = <1>; ++ clocks = <&refclk 0>; ++ clock-names = "pcppll"; ++ reg = <0x0 0x17000100 0x0 0x1000>; ++ clock-output-names = "pcppll"; ++ type = <0>; ++ }; ++ ++ socpll: socpll@17000120 { ++ compatible = "apm,xgene-socpll-clock"; ++ #clock-cells = <1>; ++ clocks = <&refclk 0>; ++ clock-names = "socpll"; ++ reg = <0x0 0x17000120 0x0 0x1000>; ++ clock-output-names = "socpll"; ++ type = <1>; ++ }; ++ ++ socplldiv2: socplldiv2 { ++ compatible = "fixed-factor-clock"; ++ #clock-cells = <1>; ++ clocks = <&socpll 0>; ++ clock-names = "socplldiv2"; ++ clock-mult = <1>; ++ clock-div = <2>; ++ clock-output-names = "socplldiv2"; ++ }; ++ ++ qmlclk: qmlclk { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ clock-names = "qmlclk"; ++ reg = <0x0 0x1703C000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "qmlclk"; ++ }; ++ ++ ethclk: ethclk { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ clock-names = "ethclk"; ++ reg = <0x0 0x17000000 0x0 0x1000>; ++ reg-names = "div-reg"; ++ divider-offset = <0x238>; ++ divider-width = <0x9>; ++ divider-shift = <0x0>; ++ clock-output-names = "ethclk"; ++ }; ++ ++ menetclk: menetclk { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <ðclk 0>; ++ reg = <0x0 0x1702C000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "menetclk"; ++ }; ++ ++ sge0clk: sge0clk@1f21c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f21c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ csr-mask = <0x3>; ++ clock-output-names = "sge0clk"; ++ }; ++ ++ xge0clk: xge0clk@1f61c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f61c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ csr-mask = <0x3>; ++ clock-output-names = "xge0clk"; ++ }; ++ ++ sataphy1clk: sataphy1clk@1f21c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f21c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "sataphy1clk"; ++ status = "disabled"; ++ csr-offset = <0x4>; ++ csr-mask = <0x00>; ++ enable-offset = <0x0>; ++ enable-mask = <0x06>; ++ }; ++ ++ sataphy2clk: sataphy1clk@1f22c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f22c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "sataphy2clk"; ++ status = "ok"; ++ csr-offset = <0x4>; ++ csr-mask = <0x3a>; ++ enable-offset = <0x0>; ++ enable-mask = <0x06>; ++ }; ++ ++ sataphy3clk: sataphy1clk@1f23c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f23c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "sataphy3clk"; ++ status = "ok"; ++ csr-offset = <0x4>; ++ csr-mask = <0x3a>; ++ enable-offset = <0x0>; ++ enable-mask = <0x06>; ++ }; ++ ++ sata01clk: sata01clk@1f21c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f21c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "sata01clk"; ++ csr-offset = <0x4>; ++ csr-mask = <0x05>; ++ enable-offset = <0x0>; ++ enable-mask = <0x39>; ++ }; ++ ++ sata23clk: sata23clk@1f22c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f22c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "sata23clk"; ++ csr-offset = <0x4>; ++ csr-mask = <0x05>; ++ enable-offset = <0x0>; ++ enable-mask = <0x39>; ++ }; ++ ++ sata45clk: sata45clk@1f23c000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f23c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "sata45clk"; ++ csr-offset = <0x4>; ++ csr-mask = <0x05>; ++ enable-offset = <0x0>; ++ enable-mask = <0x39>; ++ }; ++ ++ rtcclk: rtcclk@17000000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x17000000 0x0 0x2000>; ++ reg-names = "csr-reg"; ++ csr-offset = <0xc>; ++ csr-mask = <0x2>; ++ enable-offset = <0x10>; ++ enable-mask = <0x2>; ++ clock-output-names = "rtcclk"; ++ }; ++ ++ rngpkaclk: rngpkaclk@17000000 { ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x17000000 0x0 0x2000>; ++ reg-names = "csr-reg"; ++ csr-offset = <0xc>; ++ csr-mask = <0x10>; ++ enable-offset = <0x10>; ++ enable-mask = <0x10>; ++ clock-output-names = "rngpkaclk"; ++ }; ++ ++ pcie0clk: pcie0clk@1f2bc000 { ++ status = "disabled"; ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f2bc000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "pcie0clk"; ++ }; ++ ++ pcie1clk: pcie1clk@1f2cc000 { ++ status = "disabled"; ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f2cc000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "pcie1clk"; ++ }; ++ ++ pcie2clk: pcie2clk@1f2dc000 { ++ status = "disabled"; ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f2dc000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "pcie2clk"; ++ }; ++ ++ pcie3clk: pcie3clk@1f50c000 { ++ status = "disabled"; ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f50c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "pcie3clk"; ++ }; ++ ++ pcie4clk: pcie4clk@1f51c000 { ++ status = "disabled"; ++ compatible = "apm,xgene-device-clock"; ++ #clock-cells = <1>; ++ clocks = <&socplldiv2 0>; ++ reg = <0x0 0x1f51c000 0x0 0x1000>; ++ reg-names = "csr-reg"; ++ clock-output-names = "pcie4clk"; ++ }; ++ }; ++ ++ pcie0: pcie@1f2b0000 { ++ status = "disabled"; ++ device_type = "pci"; ++ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; ++ #interrupt-cells = <1>; ++ #size-cells = <2>; ++ #address-cells = <3>; ++ reg = < 0x00 0x1f2b0000 0x0 0x00010000 /* Controller registers */ ++ 0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */ ++ reg-names = "csr", "cfg"; ++ ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */ ++ 0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */ ++ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 ++ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; ++ interrupt-map-mask = <0x0 0x0 0x0 0x7>; ++ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1 ++ 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1 ++ 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1 ++ 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>; ++ dma-coherent; ++ clocks = <&pcie0clk 0>; ++ }; ++ ++ pcie1: pcie@1f2c0000 { ++ status = "disabled"; ++ device_type = "pci"; ++ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; ++ #interrupt-cells = <1>; ++ #size-cells = <2>; ++ #address-cells = <3>; ++ reg = < 0x00 0x1f2c0000 0x0 0x00010000 /* Controller registers */ ++ 0xd0 0xd0000000 0x0 0x00040000>; /* PCI config space */ ++ reg-names = "csr", "cfg"; ++ ranges = <0x01000000 0x0 0x00000000 0xd0 0x10000000 0x00 0x00010000 /* io */ ++ 0x02000000 0x0 0x80000000 0xd1 0x80000000 0x00 0x80000000>; /* mem */ ++ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 ++ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; ++ interrupt-map-mask = <0x0 0x0 0x0 0x7>; ++ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc8 0x1 ++ 0x0 0x0 0x0 0x2 &gic 0x0 0xc9 0x1 ++ 0x0 0x0 0x0 0x3 &gic 0x0 0xca 0x1 ++ 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>; ++ dma-coherent; ++ clocks = <&pcie1clk 0>; ++ }; ++ ++ pcie2: pcie@1f2d0000 { ++ status = "disabled"; ++ device_type = "pci"; ++ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; ++ #interrupt-cells = <1>; ++ #size-cells = <2>; ++ #address-cells = <3>; ++ reg = < 0x00 0x1f2d0000 0x0 0x00010000 /* Controller registers */ ++ 0x90 0xd0000000 0x0 0x00040000>; /* PCI config space */ ++ reg-names = "csr", "cfg"; ++ ranges = <0x01000000 0x0 0x00000000 0x90 0x10000000 0x0 0x00010000 /* io */ ++ 0x02000000 0x0 0x80000000 0x91 0x80000000 0x0 0x80000000>; /* mem */ ++ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 ++ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; ++ interrupt-map-mask = <0x0 0x0 0x0 0x7>; ++ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xce 0x1 ++ 0x0 0x0 0x0 0x2 &gic 0x0 0xcf 0x1 ++ 0x0 0x0 0x0 0x3 &gic 0x0 0xd0 0x1 ++ 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>; ++ dma-coherent; ++ clocks = <&pcie2clk 0>; ++ }; ++ ++ pcie3: pcie@1f500000 { ++ status = "disabled"; ++ device_type = "pci"; ++ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; ++ #interrupt-cells = <1>; ++ #size-cells = <2>; ++ #address-cells = <3>; ++ reg = < 0x00 0x1f500000 0x0 0x00010000 /* Controller registers */ ++ 0xa0 0xd0000000 0x0 0x00040000>; /* PCI config space */ ++ reg-names = "csr", "cfg"; ++ ranges = <0x01000000 0x0 0x00000000 0xa0 0x10000000 0x0 0x00010000 /* io */ ++ 0x02000000 0x0 0x80000000 0xa1 0x80000000 0x0 0x80000000>; /* mem */ ++ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 ++ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; ++ interrupt-map-mask = <0x0 0x0 0x0 0x7>; ++ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xd4 0x1 ++ 0x0 0x0 0x0 0x2 &gic 0x0 0xd5 0x1 ++ 0x0 0x0 0x0 0x3 &gic 0x0 0xd6 0x1 ++ 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>; ++ dma-coherent; ++ clocks = <&pcie3clk 0>; ++ }; ++ ++ pcie4: pcie@1f510000 { ++ status = "disabled"; ++ device_type = "pci"; ++ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; ++ #interrupt-cells = <1>; ++ #size-cells = <2>; ++ #address-cells = <3>; ++ reg = < 0x00 0x1f510000 0x0 0x00010000 /* Controller registers */ ++ 0xc0 0xd0000000 0x0 0x00200000>; /* PCI config space */ ++ reg-names = "csr", "cfg"; ++ ranges = <0x01000000 0x0 0x00000000 0xc0 0x10000000 0x0 0x00010000 /* io */ ++ 0x02000000 0x0 0x80000000 0xc1 0x80000000 0x0 0x80000000>; /* mem */ ++ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 ++ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; ++ interrupt-map-mask = <0x0 0x0 0x0 0x7>; ++ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xda 0x1 ++ 0x0 0x0 0x0 0x2 &gic 0x0 0xdb 0x1 ++ 0x0 0x0 0x0 0x3 &gic 0x0 0xdc 0x1 ++ 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>; ++ dma-coherent; ++ clocks = <&pcie4clk 0>; ++ }; ++ ++ serial0: serial@1c020000 { ++ status = "disabled"; ++ device_type = "serial"; ++ compatible = "ns16550a"; ++ reg = <0 0x1c020000 0x0 0x1000>; ++ reg-shift = <2>; ++ clock-frequency = <10000000>; /* Updated by bootloader */ ++ interrupt-parent = <&gic>; ++ interrupts = <0x0 0x4c 0x4>; ++ }; ++ ++ serial1: serial@1c021000 { ++ status = "disabled"; ++ device_type = "serial"; ++ compatible = "ns16550a"; ++ reg = <0 0x1c021000 0x0 0x1000>; ++ reg-shift = <2>; ++ clock-frequency = <10000000>; /* Updated by bootloader */ ++ interrupt-parent = <&gic>; ++ interrupts = <0x0 0x4d 0x4>; ++ }; ++ ++ serial2: serial@1c022000 { ++ status = "disabled"; ++ device_type = "serial"; ++ compatible = "ns16550a"; ++ reg = <0 0x1c022000 0x0 0x1000>; ++ reg-shift = <2>; ++ clock-frequency = <10000000>; /* Updated by bootloader */ ++ interrupt-parent = <&gic>; ++ interrupts = <0x0 0x4e 0x4>; ++ }; ++ ++ serial3: serial@1c023000 { ++ status = "disabled"; ++ device_type = "serial"; ++ compatible = "ns16550a"; ++ reg = <0 0x1c023000 0x0 0x1000>; ++ reg-shift = <2>; ++ clock-frequency = <10000000>; /* Updated by bootloader */ ++ interrupt-parent = <&gic>; ++ interrupts = <0x0 0x4f 0x4>; ++ }; ++ ++ phy1: phy@1f21a000 { ++ compatible = "apm,xgene-phy"; ++ reg = <0x0 0x1f21a000 0x0 0x100>; ++ #phy-cells = <1>; ++ clocks = <&sataphy1clk 0>; ++ status = "disabled"; ++ apm,tx-boost-gain = <30 30 30 30 30 30>; ++ apm,tx-eye-tuning = <2 10 10 2 10 10>; ++ }; ++ ++ phy2: phy@1f22a000 { ++ compatible = "apm,xgene-phy"; ++ reg = <0x0 0x1f22a000 0x0 0x100>; ++ #phy-cells = <1>; ++ clocks = <&sataphy2clk 0>; ++ status = "ok"; ++ apm,tx-boost-gain = <30 30 30 30 30 30>; ++ apm,tx-eye-tuning = <1 10 10 2 10 10>; ++ }; ++ ++ phy3: phy@1f23a000 { ++ compatible = "apm,xgene-phy"; ++ reg = <0x0 0x1f23a000 0x0 0x100>; ++ #phy-cells = <1>; ++ clocks = <&sataphy3clk 0>; ++ status = "ok"; ++ apm,tx-boost-gain = <31 31 31 31 31 31>; ++ apm,tx-eye-tuning = <2 10 10 2 10 10>; ++ }; ++ ++ sata1: sata@1a000000 { ++ compatible = "apm,xgene-ahci"; ++ reg = <0x0 0x1a000000 0x0 0x1000>, ++ <0x0 0x1f210000 0x0 0x1000>, ++ <0x0 0x1f21d000 0x0 0x1000>, ++ <0x0 0x1f21e000 0x0 0x1000>, ++ <0x0 0x1f217000 0x0 0x1000>; ++ interrupts = <0x0 0x86 0x4>; ++ dma-coherent; ++ status = "disabled"; ++ clocks = <&sata01clk 0>; ++ phys = <&phy1 0>; ++ phy-names = "sata-phy"; ++ }; ++ ++ sata2: sata@1a400000 { ++ compatible = "apm,xgene-ahci"; ++ reg = <0x0 0x1a400000 0x0 0x1000>, ++ <0x0 0x1f220000 0x0 0x1000>, ++ <0x0 0x1f22d000 0x0 0x1000>, ++ <0x0 0x1f22e000 0x0 0x1000>, ++ <0x0 0x1f227000 0x0 0x1000>; ++ interrupts = <0x0 0x87 0x4>; ++ dma-coherent; ++ status = "ok"; ++ clocks = <&sata23clk 0>; ++ phys = <&phy2 0>; ++ phy-names = "sata-phy"; ++ }; ++ ++ sata3: sata@1a800000 { ++ compatible = "apm,xgene-ahci"; ++ reg = <0x0 0x1a800000 0x0 0x1000>, ++ <0x0 0x1f230000 0x0 0x1000>, ++ <0x0 0x1f23d000 0x0 0x1000>, ++ <0x0 0x1f23e000 0x0 0x1000>; ++ interrupts = <0x0 0x88 0x4>; ++ dma-coherent; ++ status = "ok"; ++ clocks = <&sata45clk 0>; ++ phys = <&phy3 0>; ++ phy-names = "sata-phy"; ++ }; ++ ++ rtc: rtc@10510000 { ++ compatible = "apm,xgene-rtc"; ++ reg = <0x0 0x10510000 0x0 0x400>; ++ interrupts = <0x0 0x46 0x4>; ++ #clock-cells = <1>; ++ clocks = <&rtcclk 0>; ++ }; ++ ++ menet: ethernet@17020000 { ++ compatible = "apm,xgene-enet"; ++ status = "disabled"; ++ reg = <0x0 0x17020000 0x0 0xd100>, ++ <0x0 0X17030000 0x0 0Xc300>, ++ <0x0 0X10000000 0x0 0X200>; ++ reg-names = "enet_csr", "ring_csr", "ring_cmd"; ++ interrupts = <0x0 0x3c 0x4>; ++ dma-coherent; ++ clocks = <&menetclk 0>; ++ /* mac address will be overwritten by the bootloader */ ++ local-mac-address = [00 00 00 00 00 00]; ++ phy-connection-type = "rgmii"; ++ phy-handle = <&menetphy>; ++ mdio { ++ compatible = "apm,xgene-mdio"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ menetphy: menetphy@3 { ++ compatible = "ethernet-phy-id001c.c915"; ++ reg = <0x3>; ++ }; ++ ++ }; ++ }; ++ ++ sgenet0: ethernet@1f210000 { ++ compatible = "apm,xgene-enet"; ++ status = "disabled"; ++ reg = <0x0 0x1f210000 0x0 0xd100>, ++ <0x0 0x1f200000 0x0 0Xc300>, ++ <0x0 0x1B000000 0x0 0X200>; ++ reg-names = "enet_csr", "ring_csr", "ring_cmd"; ++ interrupts = <0x0 0xA0 0x4>; ++ dma-coherent; ++ clocks = <&sge0clk 0>; ++ local-mac-address = [00 00 00 00 00 00]; ++ phy-connection-type = "sgmii"; ++ }; ++ ++ xgenet: ethernet@1f610000 { ++ compatible = "apm,xgene-enet"; ++ status = "disabled"; ++ reg = <0x0 0x1f610000 0x0 0xd100>, ++ <0x0 0x1f600000 0x0 0Xc300>, ++ <0x0 0x18000000 0x0 0X200>; ++ reg-names = "enet_csr", "ring_csr", "ring_cmd"; ++ interrupts = <0x0 0x60 0x4>; ++ dma-coherent; ++ clocks = <&xge0clk 0>; ++ /* mac address will be overwritten by the bootloader */ ++ local-mac-address = [00 00 00 00 00 00]; ++ phy-connection-type = "xgmii"; ++ }; ++ ++ rng: rng@10520000 { ++ compatible = "apm,xgene-rng"; ++ reg = <0x0 0x10520000 0x0 0x100>; ++ interrupts = <0x0 0x41 0x4>; ++ clocks = <&rngpkaclk 0>; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/arm/Makefile b/arch/arm64/boot/dts/arm/Makefile +new file mode 100644 +index 0000000..301a0da +--- /dev/null ++++ b/arch/arm64/boot/dts/arm/Makefile +@@ -0,0 +1,7 @@ ++dtb-$(CONFIG_ARCH_VEXPRESS) += foundation-v8.dtb ++dtb-$(CONFIG_ARCH_VEXPRESS) += juno.dtb ++dtb-$(CONFIG_ARCH_VEXPRESS) += rtsm_ve-aemv8a.dtb ++ ++always := $(dtb-y) ++subdir-y := $(dts-dirs) ++clean-files := *.dtb +diff --git a/arch/arm64/boot/dts/arm/foundation-v8.dts b/arch/arm64/boot/dts/arm/foundation-v8.dts +new file mode 100644 +index 0000000..4eac8dc +--- /dev/null ++++ b/arch/arm64/boot/dts/arm/foundation-v8.dts +@@ -0,0 +1,240 @@ ++/* ++ * ARM Ltd. ++ * ++ * ARMv8 Foundation model DTS ++ */ ++ ++/dts-v1/; ++ ++/memreserve/ 0x80000000 0x00010000; ++ ++/ { ++ model = "Foundation-v8A"; ++ compatible = "arm,foundation-aarch64", "arm,vexpress"; ++ interrupt-parent = <&gic>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ chosen { }; ++ ++ aliases { ++ serial0 = &v2m_serial0; ++ serial1 = &v2m_serial1; ++ serial2 = &v2m_serial2; ++ serial3 = &v2m_serial3; ++ }; ++ ++ cpus { ++ #address-cells = <2>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x0>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x0 0x8000fff8>; ++ next-level-cache = <&L2_0>; ++ }; ++ cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x1>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x0 0x8000fff8>; ++ next-level-cache = <&L2_0>; ++ }; ++ cpu@2 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x2>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x0 0x8000fff8>; ++ next-level-cache = <&L2_0>; ++ }; ++ cpu@3 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x3>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x0 0x8000fff8>; ++ next-level-cache = <&L2_0>; ++ }; ++ ++ L2_0: l2-cache0 { ++ compatible = "cache"; ++ }; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0x00000000 0x80000000 0 0x80000000>, ++ <0x00000008 0x80000000 0 0x80000000>; ++ }; ++ ++ gic: interrupt-controller@2c001000 { ++ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ reg = <0x0 0x2c001000 0 0x1000>, ++ <0x0 0x2c002000 0 0x1000>, ++ <0x0 0x2c004000 0 0x2000>, ++ <0x0 0x2c006000 0 0x2000>; ++ interrupts = <1 9 0xf04>; ++ }; ++ ++ timer { ++ compatible = "arm,armv8-timer"; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>, ++ <1 11 0xf08>, ++ <1 10 0xf08>; ++ clock-frequency = <100000000>; ++ }; ++ ++ pmu { ++ compatible = "arm,armv8-pmuv3"; ++ interrupts = <0 60 4>, ++ <0 61 4>, ++ <0 62 4>, ++ <0 63 4>; ++ }; ++ ++ smb { ++ compatible = "arm,vexpress,v2m-p1", "simple-bus"; ++ arm,v2m-memory-map = "rs1"; ++ #address-cells = <2>; /* SMB chipselect number and offset */ ++ #size-cells = <1>; ++ ++ ranges = <0 0 0 0x08000000 0x04000000>, ++ <1 0 0 0x14000000 0x04000000>, ++ <2 0 0 0x18000000 0x04000000>, ++ <3 0 0 0x1c000000 0x04000000>, ++ <4 0 0 0x0c000000 0x04000000>, ++ <5 0 0 0x10000000 0x04000000>; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 63>; ++ interrupt-map = <0 0 0 &gic 0 0 4>, ++ <0 0 1 &gic 0 1 4>, ++ <0 0 2 &gic 0 2 4>, ++ <0 0 3 &gic 0 3 4>, ++ <0 0 4 &gic 0 4 4>, ++ <0 0 5 &gic 0 5 4>, ++ <0 0 6 &gic 0 6 4>, ++ <0 0 7 &gic 0 7 4>, ++ <0 0 8 &gic 0 8 4>, ++ <0 0 9 &gic 0 9 4>, ++ <0 0 10 &gic 0 10 4>, ++ <0 0 11 &gic 0 11 4>, ++ <0 0 12 &gic 0 12 4>, ++ <0 0 13 &gic 0 13 4>, ++ <0 0 14 &gic 0 14 4>, ++ <0 0 15 &gic 0 15 4>, ++ <0 0 16 &gic 0 16 4>, ++ <0 0 17 &gic 0 17 4>, ++ <0 0 18 &gic 0 18 4>, ++ <0 0 19 &gic 0 19 4>, ++ <0 0 20 &gic 0 20 4>, ++ <0 0 21 &gic 0 21 4>, ++ <0 0 22 &gic 0 22 4>, ++ <0 0 23 &gic 0 23 4>, ++ <0 0 24 &gic 0 24 4>, ++ <0 0 25 &gic 0 25 4>, ++ <0 0 26 &gic 0 26 4>, ++ <0 0 27 &gic 0 27 4>, ++ <0 0 28 &gic 0 28 4>, ++ <0 0 29 &gic 0 29 4>, ++ <0 0 30 &gic 0 30 4>, ++ <0 0 31 &gic 0 31 4>, ++ <0 0 32 &gic 0 32 4>, ++ <0 0 33 &gic 0 33 4>, ++ <0 0 34 &gic 0 34 4>, ++ <0 0 35 &gic 0 35 4>, ++ <0 0 36 &gic 0 36 4>, ++ <0 0 37 &gic 0 37 4>, ++ <0 0 38 &gic 0 38 4>, ++ <0 0 39 &gic 0 39 4>, ++ <0 0 40 &gic 0 40 4>, ++ <0 0 41 &gic 0 41 4>, ++ <0 0 42 &gic 0 42 4>; ++ ++ ethernet@2,02000000 { ++ compatible = "smsc,lan91c111"; ++ reg = <2 0x02000000 0x10000>; ++ interrupts = <15>; ++ }; ++ ++ v2m_clk24mhz: clk24mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ clock-output-names = "v2m:clk24mhz"; ++ }; ++ ++ v2m_refclk1mhz: refclk1mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <1000000>; ++ clock-output-names = "v2m:refclk1mhz"; ++ }; ++ ++ v2m_refclk32khz: refclk32khz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <32768>; ++ clock-output-names = "v2m:refclk32khz"; ++ }; ++ ++ iofpga@3,00000000 { ++ compatible = "arm,amba-bus", "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 3 0 0x200000>; ++ ++ v2m_sysreg: sysreg@010000 { ++ compatible = "arm,vexpress-sysreg"; ++ reg = <0x010000 0x1000>; ++ }; ++ ++ v2m_serial0: uart@090000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x090000 0x1000>; ++ interrupts = <5>; ++ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ v2m_serial1: uart@0a0000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x0a0000 0x1000>; ++ interrupts = <6>; ++ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ v2m_serial2: uart@0b0000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x0b0000 0x1000>; ++ interrupts = <7>; ++ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ v2m_serial3: uart@0c0000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x0c0000 0x1000>; ++ interrupts = <8>; ++ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ virtio_block@0130000 { ++ compatible = "virtio,mmio"; ++ reg = <0x130000 0x200>; ++ interrupts = <42>; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/arm/juno-clocks.dtsi b/arch/arm64/boot/dts/arm/juno-clocks.dtsi +new file mode 100644 +index 0000000..c9b89ef +--- /dev/null ++++ b/arch/arm64/boot/dts/arm/juno-clocks.dtsi +@@ -0,0 +1,44 @@ ++/* ++ * ARM Juno Platform clocks ++ * ++ * Copyright (c) 2013-2014 ARM Ltd ++ * ++ * This file is licensed under a dual GPLv2 or BSD license. ++ * ++ */ ++ ++ /* SoC fixed clocks */ ++ soc_uartclk: refclk7273800hz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <7273800>; ++ clock-output-names = "juno:uartclk"; ++ }; ++ ++ soc_usb48mhz: clk48mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <48000000>; ++ clock-output-names = "clk48mhz"; ++ }; ++ ++ soc_smc50mhz: clk50mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <50000000>; ++ clock-output-names = "smc_clk"; ++ }; ++ ++ soc_refclk100mhz: refclk100mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <100000000>; ++ clock-output-names = "apb_pclk"; ++ }; ++ ++ soc_faxiclk: refclk533mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <533000000>; ++ clock-output-names = "faxi_clk"; ++ }; +diff --git a/arch/arm64/boot/dts/arm/juno-motherboard.dtsi b/arch/arm64/boot/dts/arm/juno-motherboard.dtsi +new file mode 100644 +index 0000000..c138b95 +--- /dev/null ++++ b/arch/arm64/boot/dts/arm/juno-motherboard.dtsi +@@ -0,0 +1,129 @@ ++/* ++ * ARM Juno Platform motherboard peripherals ++ * ++ * Copyright (c) 2013-2014 ARM Ltd ++ * ++ * This file is licensed under a dual GPLv2 or BSD license. ++ * ++ */ ++ ++ mb_clk24mhz: clk24mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ clock-output-names = "juno_mb:clk24mhz"; ++ }; ++ ++ mb_clk25mhz: clk25mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <25000000>; ++ clock-output-names = "juno_mb:clk25mhz"; ++ }; ++ ++ motherboard { ++ compatible = "arm,vexpress,v2p-p1", "simple-bus"; ++ #address-cells = <2>; /* SMB chipselect number and offset */ ++ #size-cells = <1>; ++ #interrupt-cells = <1>; ++ ranges; ++ model = "V2M-Juno"; ++ arm,hbi = <0x252>; ++ arm,vexpress,site = <0>; ++ arm,v2m-memory-map = "rs1"; ++ ++ mb_fixed_3v3: fixedregulator@0 { ++ compatible = "regulator-fixed"; ++ regulator-name = "MCC_SB_3V3"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ ethernet@2,00000000 { ++ compatible = "smsc,lan9118", "smsc,lan9115"; ++ reg = <2 0x00000000 0x10000>; ++ interrupts = <3>; ++ phy-mode = "mii"; ++ reg-io-width = <4>; ++ smsc,irq-active-high; ++ smsc,irq-push-pull; ++ clocks = <&mb_clk25mhz>; ++ vdd33a-supply = <&mb_fixed_3v3>; ++ vddvario-supply = <&mb_fixed_3v3>; ++ }; ++ ++ usb@5,00000000 { ++ compatible = "nxp,usb-isp1763"; ++ reg = <5 0x00000000 0x20000>; ++ bus-width = <16>; ++ interrupts = <4>; ++ }; ++ ++ iofpga@3,00000000 { ++ compatible = "arm,amba-bus", "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 3 0 0x200000>; ++ ++ mmci@050000 { ++ compatible = "arm,pl180", "arm,primecell"; ++ reg = <0x050000 0x1000>; ++ interrupts = <5>; ++ /* cd-gpios = <&v2m_mmc_gpios 0 0>; ++ wp-gpios = <&v2m_mmc_gpios 1 0>; */ ++ max-frequency = <12000000>; ++ vmmc-supply = <&mb_fixed_3v3>; ++ clocks = <&mb_clk24mhz>, <&soc_smc50mhz>; ++ clock-names = "mclk", "apb_pclk"; ++ }; ++ ++ kmi@060000 { ++ compatible = "arm,pl050", "arm,primecell"; ++ reg = <0x060000 0x1000>; ++ interrupts = <8>; ++ clocks = <&mb_clk24mhz>, <&soc_smc50mhz>; ++ clock-names = "KMIREFCLK", "apb_pclk"; ++ }; ++ ++ kmi@070000 { ++ compatible = "arm,pl050", "arm,primecell"; ++ reg = <0x070000 0x1000>; ++ interrupts = <8>; ++ clocks = <&mb_clk24mhz>, <&soc_smc50mhz>; ++ clock-names = "KMIREFCLK", "apb_pclk"; ++ }; ++ ++ wdt@0f0000 { ++ compatible = "arm,sp805", "arm,primecell"; ++ reg = <0x0f0000 0x10000>; ++ interrupts = <7>; ++ clocks = <&mb_clk24mhz>, <&soc_smc50mhz>; ++ clock-names = "wdogclk", "apb_pclk"; ++ }; ++ ++ v2m_timer01: timer@110000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ reg = <0x110000 0x10000>; ++ interrupts = <9>; ++ clocks = <&mb_clk24mhz>, <&soc_smc50mhz>; ++ clock-names = "timclken1", "apb_pclk"; ++ }; ++ ++ v2m_timer23: timer@120000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ reg = <0x120000 0x10000>; ++ interrupts = <9>; ++ clocks = <&mb_clk24mhz>, <&soc_smc50mhz>; ++ clock-names = "timclken1", "apb_pclk"; ++ }; ++ ++ rtc@170000 { ++ compatible = "arm,pl031", "arm,primecell"; ++ reg = <0x170000 0x10000>; ++ interrupts = <0>; ++ clocks = <&soc_smc50mhz>; ++ clock-names = "apb_pclk"; ++ }; ++ }; ++ }; +diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts +new file mode 100644 +index 0000000..5e9110a +--- /dev/null ++++ b/arch/arm64/boot/dts/arm/juno.dts +@@ -0,0 +1,238 @@ ++/* ++ * ARM Ltd. Juno Platform ++ * ++ * Copyright (c) 2013-2014 ARM Ltd. ++ * ++ * This file is licensed under a dual GPLv2 or BSD license. ++ */ ++ ++/dts-v1/; ++ ++#include <dt-bindings/interrupt-controller/arm-gic.h> ++ ++/ { ++ model = "ARM Juno development board (r0)"; ++ compatible = "arm,juno", "arm,vexpress"; ++ interrupt-parent = <&gic>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ aliases { ++ serial0 = &soc_uart0; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ psci { ++ compatible = "arm,psci-0.2"; ++ method = "smc"; ++ }; ++ ++ cpus { ++ #address-cells = <2>; ++ #size-cells = <0>; ++ ++ A57_0: cpu@0 { ++ compatible = "arm,cortex-a57","arm,armv8"; ++ reg = <0x0 0x0>; ++ device_type = "cpu"; ++ enable-method = "psci"; ++ next-level-cache = <&A57_L2>; ++ }; ++ ++ A57_1: cpu@1 { ++ compatible = "arm,cortex-a57","arm,armv8"; ++ reg = <0x0 0x1>; ++ device_type = "cpu"; ++ enable-method = "psci"; ++ next-level-cache = <&A57_L2>; ++ }; ++ ++ A53_0: cpu@100 { ++ compatible = "arm,cortex-a53","arm,armv8"; ++ reg = <0x0 0x100>; ++ device_type = "cpu"; ++ enable-method = "psci"; ++ next-level-cache = <&A53_L2>; ++ }; ++ ++ A53_1: cpu@101 { ++ compatible = "arm,cortex-a53","arm,armv8"; ++ reg = <0x0 0x101>; ++ device_type = "cpu"; ++ enable-method = "psci"; ++ next-level-cache = <&A53_L2>; ++ }; ++ ++ A53_2: cpu@102 { ++ compatible = "arm,cortex-a53","arm,armv8"; ++ reg = <0x0 0x102>; ++ device_type = "cpu"; ++ enable-method = "psci"; ++ next-level-cache = <&A53_L2>; ++ }; ++ ++ A53_3: cpu@103 { ++ compatible = "arm,cortex-a53","arm,armv8"; ++ reg = <0x0 0x103>; ++ device_type = "cpu"; ++ enable-method = "psci"; ++ next-level-cache = <&A53_L2>; ++ }; ++ ++ A57_L2: l2-cache0 { ++ compatible = "cache"; ++ }; ++ ++ A53_L2: l2-cache1 { ++ compatible = "cache"; ++ }; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ /* last 16MB of the first memory area is reserved for secure world use by firmware */ ++ reg = <0x00000000 0x80000000 0x0 0x7f000000>, ++ <0x00000008 0x80000000 0x1 0x80000000>; ++ }; ++ ++ gic: interrupt-controller@2c001000 { ++ compatible = "arm,gic-400", "arm,cortex-a15-gic"; ++ reg = <0x0 0x2c010000 0 0x1000>, ++ <0x0 0x2c02f000 0 0x2000>, ++ <0x0 0x2c04f000 0 0x2000>, ++ <0x0 0x2c06f000 0 0x2000>; ++ #address-cells = <0>; ++ #interrupt-cells = <3>; ++ interrupt-controller; ++ interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_HIGH)>; ++ }; ++ ++ timer { ++ compatible = "arm,armv8-timer"; ++ interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_LOW)>, ++ <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_LOW)>, ++ <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_LOW)>, ++ <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_LOW)>; ++ }; ++ ++ pmu { ++ compatible = "arm,armv8-pmuv3"; ++ interrupts = <GIC_SPI 02 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 06 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-affinity = <&A57_0>, ++ <&A57_1>, ++ <&A53_0>, ++ <&A53_1>, ++ <&A53_2>, ++ <&A53_3>; ++ }; ++ ++ /include/ "juno-clocks.dtsi" ++ ++ dma@7ff00000 { ++ compatible = "arm,pl330", "arm,primecell"; ++ reg = <0x0 0x7ff00000 0 0x1000>; ++ #dma-cells = <1>; ++ #dma-channels = <8>; ++ #dma-requests = <32>; ++ interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&soc_faxiclk>; ++ clock-names = "apb_pclk"; ++ }; ++ ++ soc_uart0: uart@7ff80000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x0 0x7ff80000 0x0 0x1000>; ++ interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&soc_uartclk>, <&soc_refclk100mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ i2c@7ffa0000 { ++ compatible = "snps,designware-i2c"; ++ reg = <0x0 0x7ffa0000 0x0 0x1000>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>; ++ clock-frequency = <400000>; ++ i2c-sda-hold-time-ns = <500>; ++ clocks = <&soc_smc50mhz>; ++ ++ dvi0: dvi-transmitter@70 { ++ compatible = "nxp,tda998x"; ++ reg = <0x70>; ++ }; ++ ++ dvi1: dvi-transmitter@71 { ++ compatible = "nxp,tda998x"; ++ reg = <0x71>; ++ }; ++ }; ++ ++ ohci@7ffb0000 { ++ compatible = "generic-ohci"; ++ reg = <0x0 0x7ffb0000 0x0 0x10000>; ++ interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&soc_usb48mhz>; ++ }; ++ ++ ehci@7ffc0000 { ++ compatible = "generic-ehci"; ++ reg = <0x0 0x7ffc0000 0x0 0x10000>; ++ interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&soc_usb48mhz>; ++ }; ++ ++ memory-controller@7ffd0000 { ++ compatible = "arm,pl354", "arm,primecell"; ++ reg = <0 0x7ffd0000 0 0x1000>; ++ interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>, ++ <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&soc_smc50mhz>; ++ clock-names = "apb_pclk"; ++ }; ++ ++ smb { ++ compatible = "simple-bus"; ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ranges = <0 0 0 0x08000000 0x04000000>, ++ <1 0 0 0x14000000 0x04000000>, ++ <2 0 0 0x18000000 0x04000000>, ++ <3 0 0 0x1c000000 0x04000000>, ++ <4 0 0 0x0c000000 0x04000000>, ++ <5 0 0 0x10000000 0x04000000>; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 15>; ++ interrupt-map = <0 0 0 &gic 0 68 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 1 &gic 0 69 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 2 &gic 0 70 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 3 &gic 0 160 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 4 &gic 0 161 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 5 &gic 0 162 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 6 &gic 0 163 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 7 &gic 0 164 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 8 &gic 0 165 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 9 &gic 0 166 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 10 &gic 0 167 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 11 &gic 0 168 IRQ_TYPE_LEVEL_HIGH>, ++ <0 0 12 &gic 0 169 IRQ_TYPE_LEVEL_HIGH>; ++ ++ /include/ "juno-motherboard.dtsi" ++ }; ++}; +diff --git a/arch/arm64/boot/dts/arm/rtsm_ve-aemv8a.dts b/arch/arm64/boot/dts/arm/rtsm_ve-aemv8a.dts +new file mode 100644 +index 0000000..20addab +--- /dev/null ++++ b/arch/arm64/boot/dts/arm/rtsm_ve-aemv8a.dts +@@ -0,0 +1,167 @@ ++/* ++ * ARM Ltd. Fast Models ++ * ++ * Architecture Envelope Model (AEM) ARMv8-A ++ * ARMAEMv8AMPCT ++ * ++ * RTSM_VE_AEMv8A.lisa ++ */ ++ ++/dts-v1/; ++ ++/memreserve/ 0x80000000 0x00010000; ++ ++/ { ++ model = "RTSM_VE_AEMv8A"; ++ compatible = "arm,rtsm_ve,aemv8a", "arm,vexpress"; ++ interrupt-parent = <&gic>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ chosen { }; ++ ++ aliases { ++ serial0 = &v2m_serial0; ++ serial1 = &v2m_serial1; ++ serial2 = &v2m_serial2; ++ serial3 = &v2m_serial3; ++ }; ++ ++ cpus { ++ #address-cells = <2>; ++ #size-cells = <0>; ++ ++ cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x0>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x0 0x8000fff8>; ++ next-level-cache = <&L2_0>; ++ }; ++ cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x1>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x0 0x8000fff8>; ++ next-level-cache = <&L2_0>; ++ }; ++ cpu@2 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x2>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x0 0x8000fff8>; ++ next-level-cache = <&L2_0>; ++ }; ++ cpu@3 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x3>; ++ enable-method = "spin-table"; ++ cpu-release-addr = <0x0 0x8000fff8>; ++ next-level-cache = <&L2_0>; ++ }; ++ ++ L2_0: l2-cache0 { ++ compatible = "cache"; ++ }; ++ }; ++ ++ memory@80000000 { ++ device_type = "memory"; ++ reg = <0x00000000 0x80000000 0 0x80000000>, ++ <0x00000008 0x80000000 0 0x80000000>; ++ }; ++ ++ gic: interrupt-controller@2c001000 { ++ compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ reg = <0x0 0x2c001000 0 0x1000>, ++ <0x0 0x2c002000 0 0x1000>, ++ <0x0 0x2c004000 0 0x2000>, ++ <0x0 0x2c006000 0 0x2000>; ++ interrupts = <1 9 0xf04>; ++ }; ++ ++ timer { ++ compatible = "arm,armv8-timer"; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>, ++ <1 11 0xf08>, ++ <1 10 0xf08>; ++ clock-frequency = <100000000>; ++ }; ++ ++ pmu { ++ compatible = "arm,armv8-pmuv3"; ++ interrupts = <0 60 4>, ++ <0 61 4>, ++ <0 62 4>, ++ <0 63 4>; ++ }; ++ ++ smb { ++ compatible = "simple-bus"; ++ ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ranges = <0 0 0 0x08000000 0x04000000>, ++ <1 0 0 0x14000000 0x04000000>, ++ <2 0 0 0x18000000 0x04000000>, ++ <3 0 0 0x1c000000 0x04000000>, ++ <4 0 0 0x0c000000 0x04000000>, ++ <5 0 0 0x10000000 0x04000000>; ++ ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 63>; ++ interrupt-map = <0 0 0 &gic 0 0 4>, ++ <0 0 1 &gic 0 1 4>, ++ <0 0 2 &gic 0 2 4>, ++ <0 0 3 &gic 0 3 4>, ++ <0 0 4 &gic 0 4 4>, ++ <0 0 5 &gic 0 5 4>, ++ <0 0 6 &gic 0 6 4>, ++ <0 0 7 &gic 0 7 4>, ++ <0 0 8 &gic 0 8 4>, ++ <0 0 9 &gic 0 9 4>, ++ <0 0 10 &gic 0 10 4>, ++ <0 0 11 &gic 0 11 4>, ++ <0 0 12 &gic 0 12 4>, ++ <0 0 13 &gic 0 13 4>, ++ <0 0 14 &gic 0 14 4>, ++ <0 0 15 &gic 0 15 4>, ++ <0 0 16 &gic 0 16 4>, ++ <0 0 17 &gic 0 17 4>, ++ <0 0 18 &gic 0 18 4>, ++ <0 0 19 &gic 0 19 4>, ++ <0 0 20 &gic 0 20 4>, ++ <0 0 21 &gic 0 21 4>, ++ <0 0 22 &gic 0 22 4>, ++ <0 0 23 &gic 0 23 4>, ++ <0 0 24 &gic 0 24 4>, ++ <0 0 25 &gic 0 25 4>, ++ <0 0 26 &gic 0 26 4>, ++ <0 0 27 &gic 0 27 4>, ++ <0 0 28 &gic 0 28 4>, ++ <0 0 29 &gic 0 29 4>, ++ <0 0 30 &gic 0 30 4>, ++ <0 0 31 &gic 0 31 4>, ++ <0 0 32 &gic 0 32 4>, ++ <0 0 33 &gic 0 33 4>, ++ <0 0 34 &gic 0 34 4>, ++ <0 0 35 &gic 0 35 4>, ++ <0 0 36 &gic 0 36 4>, ++ <0 0 37 &gic 0 37 4>, ++ <0 0 38 &gic 0 38 4>, ++ <0 0 39 &gic 0 39 4>, ++ <0 0 40 &gic 0 40 4>, ++ <0 0 41 &gic 0 41 4>, ++ <0 0 42 &gic 0 42 4>; ++ ++ /include/ "rtsm_ve-motherboard.dtsi" ++ }; ++}; +diff --git a/arch/arm64/boot/dts/arm/rtsm_ve-motherboard.dtsi b/arch/arm64/boot/dts/arm/rtsm_ve-motherboard.dtsi +new file mode 100644 +index 0000000..c46cbb2 +--- /dev/null ++++ b/arch/arm64/boot/dts/arm/rtsm_ve-motherboard.dtsi +@@ -0,0 +1,273 @@ ++/* ++ * ARM Ltd. Fast Models ++ * ++ * Versatile Express (VE) system model ++ * Motherboard component ++ * ++ * VEMotherBoard.lisa ++ */ ++ ++ motherboard { ++ arm,v2m-memory-map = "rs1"; ++ compatible = "arm,vexpress,v2m-p1", "simple-bus"; ++ #address-cells = <2>; /* SMB chipselect number and offset */ ++ #size-cells = <1>; ++ #interrupt-cells = <1>; ++ ranges; ++ ++ flash@0,00000000 { ++ compatible = "arm,vexpress-flash", "cfi-flash"; ++ reg = <0 0x00000000 0x04000000>, ++ <4 0x00000000 0x04000000>; ++ bank-width = <4>; ++ }; ++ ++ v2m_video_ram: vram@2,00000000 { ++ compatible = "arm,vexpress-vram"; ++ reg = <2 0x00000000 0x00800000>; ++ }; ++ ++ ethernet@2,02000000 { ++ compatible = "smsc,lan91c111"; ++ reg = <2 0x02000000 0x10000>; ++ interrupts = <15>; ++ }; ++ ++ v2m_clk24mhz: clk24mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ clock-output-names = "v2m:clk24mhz"; ++ }; ++ ++ v2m_refclk1mhz: refclk1mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <1000000>; ++ clock-output-names = "v2m:refclk1mhz"; ++ }; ++ ++ v2m_refclk32khz: refclk32khz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <32768>; ++ clock-output-names = "v2m:refclk32khz"; ++ }; ++ ++ iofpga@3,00000000 { ++ compatible = "arm,amba-bus", "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 3 0 0x200000>; ++ ++ v2m_sysreg: sysreg@010000 { ++ compatible = "arm,vexpress-sysreg"; ++ reg = <0x010000 0x1000>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ }; ++ ++ v2m_sysctl: sysctl@020000 { ++ compatible = "arm,sp810", "arm,primecell"; ++ reg = <0x020000 0x1000>; ++ clocks = <&v2m_refclk32khz>, <&v2m_refclk1mhz>, <&v2m_clk24mhz>; ++ clock-names = "refclk", "timclk", "apb_pclk"; ++ #clock-cells = <1>; ++ clock-output-names = "timerclken0", "timerclken1", "timerclken2", "timerclken3"; ++ }; ++ ++ aaci@040000 { ++ compatible = "arm,pl041", "arm,primecell"; ++ reg = <0x040000 0x1000>; ++ interrupts = <11>; ++ clocks = <&v2m_clk24mhz>; ++ clock-names = "apb_pclk"; ++ }; ++ ++ mmci@050000 { ++ compatible = "arm,pl180", "arm,primecell"; ++ reg = <0x050000 0x1000>; ++ interrupts = <9 10>; ++ cd-gpios = <&v2m_sysreg 0 0>; ++ wp-gpios = <&v2m_sysreg 1 0>; ++ max-frequency = <12000000>; ++ vmmc-supply = <&v2m_fixed_3v3>; ++ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; ++ clock-names = "mclk", "apb_pclk"; ++ }; ++ ++ kmi@060000 { ++ compatible = "arm,pl050", "arm,primecell"; ++ reg = <0x060000 0x1000>; ++ interrupts = <12>; ++ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; ++ clock-names = "KMIREFCLK", "apb_pclk"; ++ }; ++ ++ kmi@070000 { ++ compatible = "arm,pl050", "arm,primecell"; ++ reg = <0x070000 0x1000>; ++ interrupts = <13>; ++ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; ++ clock-names = "KMIREFCLK", "apb_pclk"; ++ }; ++ ++ v2m_serial0: uart@090000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x090000 0x1000>; ++ interrupts = <5>; ++ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ v2m_serial1: uart@0a0000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x0a0000 0x1000>; ++ interrupts = <6>; ++ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ v2m_serial2: uart@0b0000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x0b0000 0x1000>; ++ interrupts = <7>; ++ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ v2m_serial3: uart@0c0000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x0c0000 0x1000>; ++ interrupts = <8>; ++ clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ wdt@0f0000 { ++ compatible = "arm,sp805", "arm,primecell"; ++ reg = <0x0f0000 0x1000>; ++ interrupts = <0>; ++ clocks = <&v2m_refclk32khz>, <&v2m_clk24mhz>; ++ clock-names = "wdogclk", "apb_pclk"; ++ }; ++ ++ v2m_timer01: timer@110000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ reg = <0x110000 0x1000>; ++ interrupts = <2>; ++ clocks = <&v2m_sysctl 0>, <&v2m_sysctl 1>, <&v2m_clk24mhz>; ++ clock-names = "timclken1", "timclken2", "apb_pclk"; ++ }; ++ ++ v2m_timer23: timer@120000 { ++ compatible = "arm,sp804", "arm,primecell"; ++ reg = <0x120000 0x1000>; ++ interrupts = <3>; ++ clocks = <&v2m_sysctl 2>, <&v2m_sysctl 3>, <&v2m_clk24mhz>; ++ clock-names = "timclken1", "timclken2", "apb_pclk"; ++ }; ++ ++ rtc@170000 { ++ compatible = "arm,pl031", "arm,primecell"; ++ reg = <0x170000 0x1000>; ++ interrupts = <4>; ++ clocks = <&v2m_clk24mhz>; ++ clock-names = "apb_pclk"; ++ }; ++ ++ clcd@1f0000 { ++ compatible = "arm,pl111", "arm,primecell"; ++ reg = <0x1f0000 0x1000>; ++ interrupt-names = "combined"; ++ interrupts = <14>; ++ clocks = <&v2m_oscclk1>, <&v2m_clk24mhz>; ++ clock-names = "clcdclk", "apb_pclk"; ++ arm,pl11x,framebuffer = <0x18000000 0x00180000>; ++ memory-region = <&v2m_video_ram>; ++ max-memory-bandwidth = <130000000>; /* 16bpp @ 63.5MHz */ ++ ++ port { ++ v2m_clcd_pads: endpoint { ++ remote-endpoint = <&v2m_clcd_panel>; ++ arm,pl11x,tft-r0g0b0-pads = <0 8 16>; ++ }; ++ }; ++ ++ panel { ++ compatible = "panel-dpi"; ++ ++ port { ++ v2m_clcd_panel: endpoint { ++ remote-endpoint = <&v2m_clcd_pads>; ++ }; ++ }; ++ ++ panel-timing { ++ clock-frequency = <63500127>; ++ hactive = <1024>; ++ hback-porch = <152>; ++ hfront-porch = <48>; ++ hsync-len = <104>; ++ vactive = <768>; ++ vback-porch = <23>; ++ vfront-porch = <3>; ++ vsync-len = <4>; ++ }; ++ }; ++ }; ++ ++ virtio_block@0130000 { ++ compatible = "virtio,mmio"; ++ reg = <0x130000 0x200>; ++ interrupts = <42>; ++ }; ++ }; ++ ++ v2m_fixed_3v3: fixedregulator@0 { ++ compatible = "regulator-fixed"; ++ regulator-name = "3V3"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ mcc { ++ compatible = "arm,vexpress,config-bus"; ++ arm,vexpress,config-bridge = <&v2m_sysreg>; ++ ++ v2m_oscclk1: osc@1 { ++ /* CLCD clock */ ++ compatible = "arm,vexpress-osc"; ++ arm,vexpress-sysreg,func = <1 1>; ++ freq-range = <23750000 63500000>; ++ #clock-cells = <0>; ++ clock-output-names = "v2m:oscclk1"; ++ }; ++ ++ reset@0 { ++ compatible = "arm,vexpress-reset"; ++ arm,vexpress-sysreg,func = <5 0>; ++ }; ++ ++ muxfpga@0 { ++ compatible = "arm,vexpress-muxfpga"; ++ arm,vexpress-sysreg,func = <7 0>; ++ }; ++ ++ shutdown@0 { ++ compatible = "arm,vexpress-shutdown"; ++ arm,vexpress-sysreg,func = <8 0>; ++ }; ++ ++ reboot@0 { ++ compatible = "arm,vexpress-reboot"; ++ arm,vexpress-sysreg,func = <9 0>; ++ }; ++ ++ dvimode@0 { ++ compatible = "arm,vexpress-dvimode"; ++ arm,vexpress-sysreg,func = <11 0>; ++ }; ++ }; ++ }; +diff --git a/arch/arm64/boot/dts/cavium/Makefile b/arch/arm64/boot/dts/cavium/Makefile +new file mode 100644 +index 0000000..e34f89d +--- /dev/null ++++ b/arch/arm64/boot/dts/cavium/Makefile +@@ -0,0 +1,5 @@ ++dtb-$(CONFIG_ARCH_THUNDER) += thunder-88xx.dtb ++ ++always := $(dtb-y) ++subdir-y := $(dts-dirs) ++clean-files := *.dtb +diff --git a/arch/arm64/boot/dts/cavium/thunder-88xx.dts b/arch/arm64/boot/dts/cavium/thunder-88xx.dts +new file mode 100644 +index 0000000..800ba65 +--- /dev/null ++++ b/arch/arm64/boot/dts/cavium/thunder-88xx.dts +@@ -0,0 +1,67 @@ ++/* ++ * Cavium Thunder DTS file - Thunder board description ++ * ++ * Copyright (C) 2014, Cavium Inc. ++ * ++ * 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/ "thunder-88xx.dtsi" ++ ++/ { ++ model = "Cavium ThunderX CN88XX board"; ++ compatible = "cavium,thunder-88xx"; ++ ++ aliases { ++ serial0 = &uaa0; ++ serial1 = &uaa1; ++ }; ++ ++ memory@00000000 { ++ device_type = "memory"; ++ reg = <0x0 0x00000000 0x0 0x80000000>; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/cavium/thunder-88xx.dtsi b/arch/arm64/boot/dts/cavium/thunder-88xx.dtsi +new file mode 100644 +index 0000000..d8c0bdc +--- /dev/null ++++ b/arch/arm64/boot/dts/cavium/thunder-88xx.dtsi +@@ -0,0 +1,401 @@ ++/* ++ * Cavium Thunder DTS file - Thunder SoC description ++ * ++ * Copyright (C) 2014, Cavium Inc. ++ * ++ * 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. ++ */ ++ ++/ { ++ compatible = "cavium,thunder-88xx"; ++ interrupt-parent = <&gic0>; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ psci { ++ compatible = "arm,psci-0.2"; ++ method = "smc"; ++ }; ++ ++ cpus { ++ #address-cells = <2>; ++ #size-cells = <0>; ++ ++ cpu@000 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x000>; ++ enable-method = "psci"; ++ }; ++ cpu@001 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x001>; ++ enable-method = "psci"; ++ }; ++ cpu@002 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x002>; ++ enable-method = "psci"; ++ }; ++ cpu@003 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x003>; ++ enable-method = "psci"; ++ }; ++ cpu@004 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x004>; ++ enable-method = "psci"; ++ }; ++ cpu@005 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x005>; ++ enable-method = "psci"; ++ }; ++ cpu@006 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x006>; ++ enable-method = "psci"; ++ }; ++ cpu@007 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x007>; ++ enable-method = "psci"; ++ }; ++ cpu@008 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x008>; ++ enable-method = "psci"; ++ }; ++ cpu@009 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x009>; ++ enable-method = "psci"; ++ }; ++ cpu@00a { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x00a>; ++ enable-method = "psci"; ++ }; ++ cpu@00b { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x00b>; ++ enable-method = "psci"; ++ }; ++ cpu@00c { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x00c>; ++ enable-method = "psci"; ++ }; ++ cpu@00d { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x00d>; ++ enable-method = "psci"; ++ }; ++ cpu@00e { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x00e>; ++ enable-method = "psci"; ++ }; ++ cpu@00f { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x00f>; ++ enable-method = "psci"; ++ }; ++ cpu@100 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x100>; ++ enable-method = "psci"; ++ }; ++ cpu@101 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x101>; ++ enable-method = "psci"; ++ }; ++ cpu@102 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x102>; ++ enable-method = "psci"; ++ }; ++ cpu@103 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x103>; ++ enable-method = "psci"; ++ }; ++ cpu@104 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x104>; ++ enable-method = "psci"; ++ }; ++ cpu@105 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x105>; ++ enable-method = "psci"; ++ }; ++ cpu@106 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x106>; ++ enable-method = "psci"; ++ }; ++ cpu@107 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x107>; ++ enable-method = "psci"; ++ }; ++ cpu@108 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x108>; ++ enable-method = "psci"; ++ }; ++ cpu@109 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x109>; ++ enable-method = "psci"; ++ }; ++ cpu@10a { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x10a>; ++ enable-method = "psci"; ++ }; ++ cpu@10b { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x10b>; ++ enable-method = "psci"; ++ }; ++ cpu@10c { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x10c>; ++ enable-method = "psci"; ++ }; ++ cpu@10d { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x10d>; ++ enable-method = "psci"; ++ }; ++ cpu@10e { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x10e>; ++ enable-method = "psci"; ++ }; ++ cpu@10f { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x10f>; ++ enable-method = "psci"; ++ }; ++ cpu@200 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x200>; ++ enable-method = "psci"; ++ }; ++ cpu@201 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x201>; ++ enable-method = "psci"; ++ }; ++ cpu@202 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x202>; ++ enable-method = "psci"; ++ }; ++ cpu@203 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x203>; ++ enable-method = "psci"; ++ }; ++ cpu@204 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x204>; ++ enable-method = "psci"; ++ }; ++ cpu@205 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x205>; ++ enable-method = "psci"; ++ }; ++ cpu@206 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x206>; ++ enable-method = "psci"; ++ }; ++ cpu@207 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x207>; ++ enable-method = "psci"; ++ }; ++ cpu@208 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x208>; ++ enable-method = "psci"; ++ }; ++ cpu@209 { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x209>; ++ enable-method = "psci"; ++ }; ++ cpu@20a { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x20a>; ++ enable-method = "psci"; ++ }; ++ cpu@20b { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x20b>; ++ enable-method = "psci"; ++ }; ++ cpu@20c { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x20c>; ++ enable-method = "psci"; ++ }; ++ cpu@20d { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x20d>; ++ enable-method = "psci"; ++ }; ++ cpu@20e { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x20e>; ++ enable-method = "psci"; ++ }; ++ cpu@20f { ++ device_type = "cpu"; ++ compatible = "cavium,thunder", "arm,armv8"; ++ reg = <0x0 0x20f>; ++ enable-method = "psci"; ++ }; ++ }; ++ ++ timer { ++ compatible = "arm,armv8-timer"; ++ interrupts = <1 13 0xff01>, ++ <1 14 0xff01>, ++ <1 11 0xff01>, ++ <1 10 0xff01>; ++ }; ++ ++ soc { ++ compatible = "simple-bus"; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ refclk50mhz: refclk50mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <50000000>; ++ clock-output-names = "refclk50mhz"; ++ }; ++ ++ gic0: interrupt-controller@8010,00000000 { ++ compatible = "arm,gic-v3"; ++ #interrupt-cells = <3>; ++ interrupt-controller; ++ reg = <0x8010 0x00000000 0x0 0x010000>, /* GICD */ ++ <0x8010 0x80000000 0x0 0x600000>; /* GICR */ ++ interrupts = <1 9 0xf04>; ++ }; ++ ++ uaa0: serial@87e0,24000000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x87e0 0x24000000 0x0 0x1000>; ++ interrupts = <1 21 4>; ++ clocks = <&refclk50mhz>; ++ clock-names = "apb_pclk"; ++ }; ++ ++ uaa1: serial@87e0,25000000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x87e0 0x25000000 0x0 0x1000>; ++ interrupts = <1 22 4>; ++ clocks = <&refclk50mhz>; ++ clock-names = "apb_pclk"; ++ }; ++ }; ++}; +diff --git a/arch/arm64/boot/dts/foundation-v8.dts b/arch/arm64/boot/dts/foundation-v8.dts +deleted file mode 100644 +index 4a06090..0000000 +--- a/arch/arm64/boot/dts/foundation-v8.dts ++++ /dev/null +@@ -1,232 +0,0 @@ +-/* +- * ARM Ltd. +- * +- * ARMv8 Foundation model DTS +- */ +- +-/dts-v1/; +- +-/memreserve/ 0x80000000 0x00010000; +- +-/ { +- model = "Foundation-v8A"; +- compatible = "arm,foundation-aarch64", "arm,vexpress"; +- interrupt-parent = <&gic>; +- #address-cells = <2>; +- #size-cells = <2>; +- +- chosen { }; +- +- aliases { +- serial0 = &v2m_serial0; +- serial1 = &v2m_serial1; +- serial2 = &v2m_serial2; +- serial3 = &v2m_serial3; +- }; +- +- cpus { +- #address-cells = <2>; +- #size-cells = <0>; +- +- cpu@0 { +- device_type = "cpu"; +- compatible = "arm,armv8"; +- reg = <0x0 0x0>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x0 0x8000fff8>; +- }; +- cpu@1 { +- device_type = "cpu"; +- compatible = "arm,armv8"; +- reg = <0x0 0x1>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x0 0x8000fff8>; +- }; +- cpu@2 { +- device_type = "cpu"; +- compatible = "arm,armv8"; +- reg = <0x0 0x2>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x0 0x8000fff8>; +- }; +- cpu@3 { +- device_type = "cpu"; +- compatible = "arm,armv8"; +- reg = <0x0 0x3>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x0 0x8000fff8>; +- }; +- }; +- +- memory@80000000 { +- device_type = "memory"; +- reg = <0x00000000 0x80000000 0 0x80000000>, +- <0x00000008 0x80000000 0 0x80000000>; +- }; +- +- gic: interrupt-controller@2c001000 { +- compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; +- #interrupt-cells = <3>; +- #address-cells = <0>; +- interrupt-controller; +- reg = <0x0 0x2c001000 0 0x1000>, +- <0x0 0x2c002000 0 0x1000>, +- <0x0 0x2c004000 0 0x2000>, +- <0x0 0x2c006000 0 0x2000>; +- interrupts = <1 9 0xf04>; +- }; +- +- timer { +- compatible = "arm,armv8-timer"; +- interrupts = <1 13 0xff01>, +- <1 14 0xff01>, +- <1 11 0xff01>, +- <1 10 0xff01>; +- clock-frequency = <100000000>; +- }; +- +- pmu { +- compatible = "arm,armv8-pmuv3"; +- interrupts = <0 60 4>, +- <0 61 4>, +- <0 62 4>, +- <0 63 4>; +- }; +- +- smb { +- compatible = "arm,vexpress,v2m-p1", "simple-bus"; +- arm,v2m-memory-map = "rs1"; +- #address-cells = <2>; /* SMB chipselect number and offset */ +- #size-cells = <1>; +- +- ranges = <0 0 0 0x08000000 0x04000000>, +- <1 0 0 0x14000000 0x04000000>, +- <2 0 0 0x18000000 0x04000000>, +- <3 0 0 0x1c000000 0x04000000>, +- <4 0 0 0x0c000000 0x04000000>, +- <5 0 0 0x10000000 0x04000000>; +- +- #interrupt-cells = <1>; +- interrupt-map-mask = <0 0 63>; +- interrupt-map = <0 0 0 &gic 0 0 4>, +- <0 0 1 &gic 0 1 4>, +- <0 0 2 &gic 0 2 4>, +- <0 0 3 &gic 0 3 4>, +- <0 0 4 &gic 0 4 4>, +- <0 0 5 &gic 0 5 4>, +- <0 0 6 &gic 0 6 4>, +- <0 0 7 &gic 0 7 4>, +- <0 0 8 &gic 0 8 4>, +- <0 0 9 &gic 0 9 4>, +- <0 0 10 &gic 0 10 4>, +- <0 0 11 &gic 0 11 4>, +- <0 0 12 &gic 0 12 4>, +- <0 0 13 &gic 0 13 4>, +- <0 0 14 &gic 0 14 4>, +- <0 0 15 &gic 0 15 4>, +- <0 0 16 &gic 0 16 4>, +- <0 0 17 &gic 0 17 4>, +- <0 0 18 &gic 0 18 4>, +- <0 0 19 &gic 0 19 4>, +- <0 0 20 &gic 0 20 4>, +- <0 0 21 &gic 0 21 4>, +- <0 0 22 &gic 0 22 4>, +- <0 0 23 &gic 0 23 4>, +- <0 0 24 &gic 0 24 4>, +- <0 0 25 &gic 0 25 4>, +- <0 0 26 &gic 0 26 4>, +- <0 0 27 &gic 0 27 4>, +- <0 0 28 &gic 0 28 4>, +- <0 0 29 &gic 0 29 4>, +- <0 0 30 &gic 0 30 4>, +- <0 0 31 &gic 0 31 4>, +- <0 0 32 &gic 0 32 4>, +- <0 0 33 &gic 0 33 4>, +- <0 0 34 &gic 0 34 4>, +- <0 0 35 &gic 0 35 4>, +- <0 0 36 &gic 0 36 4>, +- <0 0 37 &gic 0 37 4>, +- <0 0 38 &gic 0 38 4>, +- <0 0 39 &gic 0 39 4>, +- <0 0 40 &gic 0 40 4>, +- <0 0 41 &gic 0 41 4>, +- <0 0 42 &gic 0 42 4>; +- +- ethernet@2,02000000 { +- compatible = "smsc,lan91c111"; +- reg = <2 0x02000000 0x10000>; +- interrupts = <15>; +- }; +- +- v2m_clk24mhz: clk24mhz { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <24000000>; +- clock-output-names = "v2m:clk24mhz"; +- }; +- +- v2m_refclk1mhz: refclk1mhz { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <1000000>; +- clock-output-names = "v2m:refclk1mhz"; +- }; +- +- v2m_refclk32khz: refclk32khz { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <32768>; +- clock-output-names = "v2m:refclk32khz"; +- }; +- +- iofpga@3,00000000 { +- compatible = "arm,amba-bus", "simple-bus"; +- #address-cells = <1>; +- #size-cells = <1>; +- ranges = <0 3 0 0x200000>; +- +- v2m_sysreg: sysreg@010000 { +- compatible = "arm,vexpress-sysreg"; +- reg = <0x010000 0x1000>; +- }; +- +- v2m_serial0: uart@090000 { +- compatible = "arm,pl011", "arm,primecell"; +- reg = <0x090000 0x1000>; +- interrupts = <5>; +- clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; +- clock-names = "uartclk", "apb_pclk"; +- }; +- +- v2m_serial1: uart@0a0000 { +- compatible = "arm,pl011", "arm,primecell"; +- reg = <0x0a0000 0x1000>; +- interrupts = <6>; +- clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; +- clock-names = "uartclk", "apb_pclk"; +- }; +- +- v2m_serial2: uart@0b0000 { +- compatible = "arm,pl011", "arm,primecell"; +- reg = <0x0b0000 0x1000>; +- interrupts = <7>; +- clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; +- clock-names = "uartclk", "apb_pclk"; +- }; +- +- v2m_serial3: uart@0c0000 { +- compatible = "arm,pl011", "arm,primecell"; +- reg = <0x0c0000 0x1000>; +- interrupts = <8>; +- clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; +- clock-names = "uartclk", "apb_pclk"; +- }; +- +- virtio_block@0130000 { +- compatible = "virtio,mmio"; +- reg = <0x130000 0x200>; +- interrupts = <42>; +- }; +- }; +- }; +-}; +diff --git a/arch/arm64/boot/dts/include/dt-bindings b/arch/arm64/boot/dts/include/dt-bindings +new file mode 120000 +index 0000000..08c00e4 +--- /dev/null ++++ b/arch/arm64/boot/dts/include/dt-bindings +@@ -0,0 +1 @@ ++../../../../../include/dt-bindings +\ No newline at end of file +diff --git a/arch/arm64/boot/dts/rtsm_ve-aemv8a.dts b/arch/arm64/boot/dts/rtsm_ve-aemv8a.dts +deleted file mode 100644 +index 572005e..0000000 +--- a/arch/arm64/boot/dts/rtsm_ve-aemv8a.dts ++++ /dev/null +@@ -1,159 +0,0 @@ +-/* +- * ARM Ltd. Fast Models +- * +- * Architecture Envelope Model (AEM) ARMv8-A +- * ARMAEMv8AMPCT +- * +- * RTSM_VE_AEMv8A.lisa +- */ +- +-/dts-v1/; +- +-/memreserve/ 0x80000000 0x00010000; +- +-/ { +- model = "RTSM_VE_AEMv8A"; +- compatible = "arm,rtsm_ve,aemv8a", "arm,vexpress"; +- interrupt-parent = <&gic>; +- #address-cells = <2>; +- #size-cells = <2>; +- +- chosen { }; +- +- aliases { +- serial0 = &v2m_serial0; +- serial1 = &v2m_serial1; +- serial2 = &v2m_serial2; +- serial3 = &v2m_serial3; +- }; +- +- cpus { +- #address-cells = <2>; +- #size-cells = <0>; +- +- cpu@0 { +- device_type = "cpu"; +- compatible = "arm,armv8"; +- reg = <0x0 0x0>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x0 0x8000fff8>; +- }; +- cpu@1 { +- device_type = "cpu"; +- compatible = "arm,armv8"; +- reg = <0x0 0x1>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x0 0x8000fff8>; +- }; +- cpu@2 { +- device_type = "cpu"; +- compatible = "arm,armv8"; +- reg = <0x0 0x2>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x0 0x8000fff8>; +- }; +- cpu@3 { +- device_type = "cpu"; +- compatible = "arm,armv8"; +- reg = <0x0 0x3>; +- enable-method = "spin-table"; +- cpu-release-addr = <0x0 0x8000fff8>; +- }; +- }; +- +- memory@80000000 { +- device_type = "memory"; +- reg = <0x00000000 0x80000000 0 0x80000000>, +- <0x00000008 0x80000000 0 0x80000000>; +- }; +- +- gic: interrupt-controller@2c001000 { +- compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; +- #interrupt-cells = <3>; +- #address-cells = <0>; +- interrupt-controller; +- reg = <0x0 0x2c001000 0 0x1000>, +- <0x0 0x2c002000 0 0x1000>, +- <0x0 0x2c004000 0 0x2000>, +- <0x0 0x2c006000 0 0x2000>; +- interrupts = <1 9 0xf04>; +- }; +- +- timer { +- compatible = "arm,armv8-timer"; +- interrupts = <1 13 0xff01>, +- <1 14 0xff01>, +- <1 11 0xff01>, +- <1 10 0xff01>; +- clock-frequency = <100000000>; +- }; +- +- pmu { +- compatible = "arm,armv8-pmuv3"; +- interrupts = <0 60 4>, +- <0 61 4>, +- <0 62 4>, +- <0 63 4>; +- }; +- +- smb { +- compatible = "simple-bus"; +- +- #address-cells = <2>; +- #size-cells = <1>; +- ranges = <0 0 0 0x08000000 0x04000000>, +- <1 0 0 0x14000000 0x04000000>, +- <2 0 0 0x18000000 0x04000000>, +- <3 0 0 0x1c000000 0x04000000>, +- <4 0 0 0x0c000000 0x04000000>, +- <5 0 0 0x10000000 0x04000000>; +- +- #interrupt-cells = <1>; +- interrupt-map-mask = <0 0 63>; +- interrupt-map = <0 0 0 &gic 0 0 4>, +- <0 0 1 &gic 0 1 4>, +- <0 0 2 &gic 0 2 4>, +- <0 0 3 &gic 0 3 4>, +- <0 0 4 &gic 0 4 4>, +- <0 0 5 &gic 0 5 4>, +- <0 0 6 &gic 0 6 4>, +- <0 0 7 &gic 0 7 4>, +- <0 0 8 &gic 0 8 4>, +- <0 0 9 &gic 0 9 4>, +- <0 0 10 &gic 0 10 4>, +- <0 0 11 &gic 0 11 4>, +- <0 0 12 &gic 0 12 4>, +- <0 0 13 &gic 0 13 4>, +- <0 0 14 &gic 0 14 4>, +- <0 0 15 &gic 0 15 4>, +- <0 0 16 &gic 0 16 4>, +- <0 0 17 &gic 0 17 4>, +- <0 0 18 &gic 0 18 4>, +- <0 0 19 &gic 0 19 4>, +- <0 0 20 &gic 0 20 4>, +- <0 0 21 &gic 0 21 4>, +- <0 0 22 &gic 0 22 4>, +- <0 0 23 &gic 0 23 4>, +- <0 0 24 &gic 0 24 4>, +- <0 0 25 &gic 0 25 4>, +- <0 0 26 &gic 0 26 4>, +- <0 0 27 &gic 0 27 4>, +- <0 0 28 &gic 0 28 4>, +- <0 0 29 &gic 0 29 4>, +- <0 0 30 &gic 0 30 4>, +- <0 0 31 &gic 0 31 4>, +- <0 0 32 &gic 0 32 4>, +- <0 0 33 &gic 0 33 4>, +- <0 0 34 &gic 0 34 4>, +- <0 0 35 &gic 0 35 4>, +- <0 0 36 &gic 0 36 4>, +- <0 0 37 &gic 0 37 4>, +- <0 0 38 &gic 0 38 4>, +- <0 0 39 &gic 0 39 4>, +- <0 0 40 &gic 0 40 4>, +- <0 0 41 &gic 0 41 4>, +- <0 0 42 &gic 0 42 4>; +- +- /include/ "rtsm_ve-motherboard.dtsi" +- }; +-}; +diff --git a/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi b/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi +deleted file mode 100644 +index c46cbb2..0000000 +--- a/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi ++++ /dev/null +@@ -1,273 +0,0 @@ +-/* +- * ARM Ltd. Fast Models +- * +- * Versatile Express (VE) system model +- * Motherboard component +- * +- * VEMotherBoard.lisa +- */ +- +- motherboard { +- arm,v2m-memory-map = "rs1"; +- compatible = "arm,vexpress,v2m-p1", "simple-bus"; +- #address-cells = <2>; /* SMB chipselect number and offset */ +- #size-cells = <1>; +- #interrupt-cells = <1>; +- ranges; +- +- flash@0,00000000 { +- compatible = "arm,vexpress-flash", "cfi-flash"; +- reg = <0 0x00000000 0x04000000>, +- <4 0x00000000 0x04000000>; +- bank-width = <4>; +- }; +- +- v2m_video_ram: vram@2,00000000 { +- compatible = "arm,vexpress-vram"; +- reg = <2 0x00000000 0x00800000>; +- }; +- +- ethernet@2,02000000 { +- compatible = "smsc,lan91c111"; +- reg = <2 0x02000000 0x10000>; +- interrupts = <15>; +- }; +- +- v2m_clk24mhz: clk24mhz { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <24000000>; +- clock-output-names = "v2m:clk24mhz"; +- }; +- +- v2m_refclk1mhz: refclk1mhz { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <1000000>; +- clock-output-names = "v2m:refclk1mhz"; +- }; +- +- v2m_refclk32khz: refclk32khz { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <32768>; +- clock-output-names = "v2m:refclk32khz"; +- }; +- +- iofpga@3,00000000 { +- compatible = "arm,amba-bus", "simple-bus"; +- #address-cells = <1>; +- #size-cells = <1>; +- ranges = <0 3 0 0x200000>; +- +- v2m_sysreg: sysreg@010000 { +- compatible = "arm,vexpress-sysreg"; +- reg = <0x010000 0x1000>; +- gpio-controller; +- #gpio-cells = <2>; +- }; +- +- v2m_sysctl: sysctl@020000 { +- compatible = "arm,sp810", "arm,primecell"; +- reg = <0x020000 0x1000>; +- clocks = <&v2m_refclk32khz>, <&v2m_refclk1mhz>, <&v2m_clk24mhz>; +- clock-names = "refclk", "timclk", "apb_pclk"; +- #clock-cells = <1>; +- clock-output-names = "timerclken0", "timerclken1", "timerclken2", "timerclken3"; +- }; +- +- aaci@040000 { +- compatible = "arm,pl041", "arm,primecell"; +- reg = <0x040000 0x1000>; +- interrupts = <11>; +- clocks = <&v2m_clk24mhz>; +- clock-names = "apb_pclk"; +- }; +- +- mmci@050000 { +- compatible = "arm,pl180", "arm,primecell"; +- reg = <0x050000 0x1000>; +- interrupts = <9 10>; +- cd-gpios = <&v2m_sysreg 0 0>; +- wp-gpios = <&v2m_sysreg 1 0>; +- max-frequency = <12000000>; +- vmmc-supply = <&v2m_fixed_3v3>; +- clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; +- clock-names = "mclk", "apb_pclk"; +- }; +- +- kmi@060000 { +- compatible = "arm,pl050", "arm,primecell"; +- reg = <0x060000 0x1000>; +- interrupts = <12>; +- clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; +- clock-names = "KMIREFCLK", "apb_pclk"; +- }; +- +- kmi@070000 { +- compatible = "arm,pl050", "arm,primecell"; +- reg = <0x070000 0x1000>; +- interrupts = <13>; +- clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; +- clock-names = "KMIREFCLK", "apb_pclk"; +- }; +- +- v2m_serial0: uart@090000 { +- compatible = "arm,pl011", "arm,primecell"; +- reg = <0x090000 0x1000>; +- interrupts = <5>; +- clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; +- clock-names = "uartclk", "apb_pclk"; +- }; +- +- v2m_serial1: uart@0a0000 { +- compatible = "arm,pl011", "arm,primecell"; +- reg = <0x0a0000 0x1000>; +- interrupts = <6>; +- clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; +- clock-names = "uartclk", "apb_pclk"; +- }; +- +- v2m_serial2: uart@0b0000 { +- compatible = "arm,pl011", "arm,primecell"; +- reg = <0x0b0000 0x1000>; +- interrupts = <7>; +- clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; +- clock-names = "uartclk", "apb_pclk"; +- }; +- +- v2m_serial3: uart@0c0000 { +- compatible = "arm,pl011", "arm,primecell"; +- reg = <0x0c0000 0x1000>; +- interrupts = <8>; +- clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; +- clock-names = "uartclk", "apb_pclk"; +- }; +- +- wdt@0f0000 { +- compatible = "arm,sp805", "arm,primecell"; +- reg = <0x0f0000 0x1000>; +- interrupts = <0>; +- clocks = <&v2m_refclk32khz>, <&v2m_clk24mhz>; +- clock-names = "wdogclk", "apb_pclk"; +- }; +- +- v2m_timer01: timer@110000 { +- compatible = "arm,sp804", "arm,primecell"; +- reg = <0x110000 0x1000>; +- interrupts = <2>; +- clocks = <&v2m_sysctl 0>, <&v2m_sysctl 1>, <&v2m_clk24mhz>; +- clock-names = "timclken1", "timclken2", "apb_pclk"; +- }; +- +- v2m_timer23: timer@120000 { +- compatible = "arm,sp804", "arm,primecell"; +- reg = <0x120000 0x1000>; +- interrupts = <3>; +- clocks = <&v2m_sysctl 2>, <&v2m_sysctl 3>, <&v2m_clk24mhz>; +- clock-names = "timclken1", "timclken2", "apb_pclk"; +- }; +- +- rtc@170000 { +- compatible = "arm,pl031", "arm,primecell"; +- reg = <0x170000 0x1000>; +- interrupts = <4>; +- clocks = <&v2m_clk24mhz>; +- clock-names = "apb_pclk"; +- }; +- +- clcd@1f0000 { +- compatible = "arm,pl111", "arm,primecell"; +- reg = <0x1f0000 0x1000>; +- interrupt-names = "combined"; +- interrupts = <14>; +- clocks = <&v2m_oscclk1>, <&v2m_clk24mhz>; +- clock-names = "clcdclk", "apb_pclk"; +- arm,pl11x,framebuffer = <0x18000000 0x00180000>; +- memory-region = <&v2m_video_ram>; +- max-memory-bandwidth = <130000000>; /* 16bpp @ 63.5MHz */ +- +- port { +- v2m_clcd_pads: endpoint { +- remote-endpoint = <&v2m_clcd_panel>; +- arm,pl11x,tft-r0g0b0-pads = <0 8 16>; +- }; +- }; +- +- panel { +- compatible = "panel-dpi"; +- +- port { +- v2m_clcd_panel: endpoint { +- remote-endpoint = <&v2m_clcd_pads>; +- }; +- }; +- +- panel-timing { +- clock-frequency = <63500127>; +- hactive = <1024>; +- hback-porch = <152>; +- hfront-porch = <48>; +- hsync-len = <104>; +- vactive = <768>; +- vback-porch = <23>; +- vfront-porch = <3>; +- vsync-len = <4>; +- }; +- }; +- }; +- +- virtio_block@0130000 { +- compatible = "virtio,mmio"; +- reg = <0x130000 0x200>; +- interrupts = <42>; +- }; +- }; +- +- v2m_fixed_3v3: fixedregulator@0 { +- compatible = "regulator-fixed"; +- regulator-name = "3V3"; +- regulator-min-microvolt = <3300000>; +- regulator-max-microvolt = <3300000>; +- regulator-always-on; +- }; +- +- mcc { +- compatible = "arm,vexpress,config-bus"; +- arm,vexpress,config-bridge = <&v2m_sysreg>; +- +- v2m_oscclk1: osc@1 { +- /* CLCD clock */ +- compatible = "arm,vexpress-osc"; +- arm,vexpress-sysreg,func = <1 1>; +- freq-range = <23750000 63500000>; +- #clock-cells = <0>; +- clock-output-names = "v2m:oscclk1"; +- }; +- +- reset@0 { +- compatible = "arm,vexpress-reset"; +- arm,vexpress-sysreg,func = <5 0>; +- }; +- +- muxfpga@0 { +- compatible = "arm,vexpress-muxfpga"; +- arm,vexpress-sysreg,func = <7 0>; +- }; +- +- shutdown@0 { +- compatible = "arm,vexpress-shutdown"; +- arm,vexpress-sysreg,func = <8 0>; +- }; +- +- reboot@0 { +- compatible = "arm,vexpress-reboot"; +- arm,vexpress-sysreg,func = <9 0>; +- }; +- +- dvimode@0 { +- compatible = "arm,vexpress-dvimode"; +- arm,vexpress-sysreg,func = <11 0>; +- }; +- }; +- }; +diff --git a/arch/arm64/boot/dts/thunder-88xx.dts b/arch/arm64/boot/dts/thunder-88xx.dts +deleted file mode 100644 +index 800ba65..0000000 +--- a/arch/arm64/boot/dts/thunder-88xx.dts ++++ /dev/null +@@ -1,67 +0,0 @@ +-/* +- * Cavium Thunder DTS file - Thunder board description +- * +- * Copyright (C) 2014, Cavium Inc. +- * +- * 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/ "thunder-88xx.dtsi" +- +-/ { +- model = "Cavium ThunderX CN88XX board"; +- compatible = "cavium,thunder-88xx"; +- +- aliases { +- serial0 = &uaa0; +- serial1 = &uaa1; +- }; +- +- memory@00000000 { +- device_type = "memory"; +- reg = <0x0 0x00000000 0x0 0x80000000>; +- }; +-}; +diff --git a/arch/arm64/boot/dts/thunder-88xx.dtsi b/arch/arm64/boot/dts/thunder-88xx.dtsi +deleted file mode 100644 +index d8c0bdc..0000000 +--- a/arch/arm64/boot/dts/thunder-88xx.dtsi ++++ /dev/null +@@ -1,401 +0,0 @@ +-/* +- * Cavium Thunder DTS file - Thunder SoC description +- * +- * Copyright (C) 2014, Cavium Inc. +- * +- * 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. +- */ +- +-/ { +- compatible = "cavium,thunder-88xx"; +- interrupt-parent = <&gic0>; +- #address-cells = <2>; +- #size-cells = <2>; +- +- psci { +- compatible = "arm,psci-0.2"; +- method = "smc"; +- }; +- +- cpus { +- #address-cells = <2>; +- #size-cells = <0>; +- +- cpu@000 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x000>; +- enable-method = "psci"; +- }; +- cpu@001 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x001>; +- enable-method = "psci"; +- }; +- cpu@002 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x002>; +- enable-method = "psci"; +- }; +- cpu@003 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x003>; +- enable-method = "psci"; +- }; +- cpu@004 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x004>; +- enable-method = "psci"; +- }; +- cpu@005 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x005>; +- enable-method = "psci"; +- }; +- cpu@006 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x006>; +- enable-method = "psci"; +- }; +- cpu@007 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x007>; +- enable-method = "psci"; +- }; +- cpu@008 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x008>; +- enable-method = "psci"; +- }; +- cpu@009 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x009>; +- enable-method = "psci"; +- }; +- cpu@00a { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x00a>; +- enable-method = "psci"; +- }; +- cpu@00b { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x00b>; +- enable-method = "psci"; +- }; +- cpu@00c { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x00c>; +- enable-method = "psci"; +- }; +- cpu@00d { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x00d>; +- enable-method = "psci"; +- }; +- cpu@00e { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x00e>; +- enable-method = "psci"; +- }; +- cpu@00f { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x00f>; +- enable-method = "psci"; +- }; +- cpu@100 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x100>; +- enable-method = "psci"; +- }; +- cpu@101 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x101>; +- enable-method = "psci"; +- }; +- cpu@102 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x102>; +- enable-method = "psci"; +- }; +- cpu@103 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x103>; +- enable-method = "psci"; +- }; +- cpu@104 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x104>; +- enable-method = "psci"; +- }; +- cpu@105 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x105>; +- enable-method = "psci"; +- }; +- cpu@106 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x106>; +- enable-method = "psci"; +- }; +- cpu@107 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x107>; +- enable-method = "psci"; +- }; +- cpu@108 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x108>; +- enable-method = "psci"; +- }; +- cpu@109 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x109>; +- enable-method = "psci"; +- }; +- cpu@10a { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x10a>; +- enable-method = "psci"; +- }; +- cpu@10b { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x10b>; +- enable-method = "psci"; +- }; +- cpu@10c { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x10c>; +- enable-method = "psci"; +- }; +- cpu@10d { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x10d>; +- enable-method = "psci"; +- }; +- cpu@10e { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x10e>; +- enable-method = "psci"; +- }; +- cpu@10f { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x10f>; +- enable-method = "psci"; +- }; +- cpu@200 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x200>; +- enable-method = "psci"; +- }; +- cpu@201 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x201>; +- enable-method = "psci"; +- }; +- cpu@202 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x202>; +- enable-method = "psci"; +- }; +- cpu@203 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x203>; +- enable-method = "psci"; +- }; +- cpu@204 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x204>; +- enable-method = "psci"; +- }; +- cpu@205 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x205>; +- enable-method = "psci"; +- }; +- cpu@206 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x206>; +- enable-method = "psci"; +- }; +- cpu@207 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x207>; +- enable-method = "psci"; +- }; +- cpu@208 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x208>; +- enable-method = "psci"; +- }; +- cpu@209 { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x209>; +- enable-method = "psci"; +- }; +- cpu@20a { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x20a>; +- enable-method = "psci"; +- }; +- cpu@20b { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x20b>; +- enable-method = "psci"; +- }; +- cpu@20c { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x20c>; +- enable-method = "psci"; +- }; +- cpu@20d { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x20d>; +- enable-method = "psci"; +- }; +- cpu@20e { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x20e>; +- enable-method = "psci"; +- }; +- cpu@20f { +- device_type = "cpu"; +- compatible = "cavium,thunder", "arm,armv8"; +- reg = <0x0 0x20f>; +- enable-method = "psci"; +- }; +- }; +- +- timer { +- compatible = "arm,armv8-timer"; +- interrupts = <1 13 0xff01>, +- <1 14 0xff01>, +- <1 11 0xff01>, +- <1 10 0xff01>; +- }; +- +- soc { +- compatible = "simple-bus"; +- #address-cells = <2>; +- #size-cells = <2>; +- ranges; +- +- refclk50mhz: refclk50mhz { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <50000000>; +- clock-output-names = "refclk50mhz"; +- }; +- +- gic0: interrupt-controller@8010,00000000 { +- compatible = "arm,gic-v3"; +- #interrupt-cells = <3>; +- interrupt-controller; +- reg = <0x8010 0x00000000 0x0 0x010000>, /* GICD */ +- <0x8010 0x80000000 0x0 0x600000>; /* GICR */ +- interrupts = <1 9 0xf04>; +- }; +- +- uaa0: serial@87e0,24000000 { +- compatible = "arm,pl011", "arm,primecell"; +- reg = <0x87e0 0x24000000 0x0 0x1000>; +- interrupts = <1 21 4>; +- clocks = <&refclk50mhz>; +- clock-names = "apb_pclk"; +- }; +- +- uaa1: serial@87e0,25000000 { +- compatible = "arm,pl011", "arm,primecell"; +- reg = <0x87e0 0x25000000 0x0 0x1000>; +- interrupts = <1 22 4>; +- clocks = <&refclk50mhz>; +- clock-names = "apb_pclk"; +- }; +- }; +-}; +diff --git a/arch/arm64/include/asm/compat.h b/arch/arm64/include/asm/compat.h +index 56de5aa..3fb053f 100644 +--- a/arch/arm64/include/asm/compat.h ++++ b/arch/arm64/include/asm/compat.h +@@ -205,6 +205,13 @@ typedef struct compat_siginfo { + compat_long_t _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; ++ ++ /* SIGSYS */ ++ struct { ++ compat_uptr_t _call_addr; /* calling user insn */ ++ int _syscall; /* triggering system call number */ ++ compat_uint_t _arch; /* AUDIT_ARCH_* of syscall */ ++ } _sigsys; + } _sifields; + } compat_siginfo_t; + +diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h +index c008bae..3ae74b5 100644 +--- a/arch/arm64/include/asm/cpufeature.h ++++ b/arch/arm64/include/asm/cpufeature.h +@@ -54,6 +54,9 @@ static inline void cpus_set_cap(unsigned int num) + + void check_local_cpu_errata(void); + ++bool cpu_supports_mixed_endian_el0(void); ++bool system_supports_mixed_endian_el0(void); ++ + #endif /* __ASSEMBLY__ */ + + #endif +diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h +index 8adb986..a84ec60 100644 +--- a/arch/arm64/include/asm/cputype.h ++++ b/arch/arm64/include/asm/cputype.h +@@ -72,6 +72,18 @@ + + #define APM_CPU_PART_POTENZA 0x000 + ++#define ID_AA64MMFR0_BIGENDEL0_SHIFT 16 ++#define ID_AA64MMFR0_BIGENDEL0_MASK (0xf << ID_AA64MMFR0_BIGENDEL0_SHIFT) ++#define ID_AA64MMFR0_BIGENDEL0(mmfr0) \ ++ (((mmfr0) & ID_AA64MMFR0_BIGENDEL0_MASK) >> ID_AA64MMFR0_BIGENDEL0_SHIFT) ++#define ID_AA64MMFR0_BIGEND_SHIFT 8 ++#define ID_AA64MMFR0_BIGEND_MASK (0xf << ID_AA64MMFR0_BIGEND_SHIFT) ++#define ID_AA64MMFR0_BIGEND(mmfr0) \ ++ (((mmfr0) & ID_AA64MMFR0_BIGEND_MASK) >> ID_AA64MMFR0_BIGEND_SHIFT) ++ ++#define SCTLR_EL1_CP15BEN (0x1 << 5) ++#define SCTLR_EL1_SED (0x1 << 8) ++ + #ifndef __ASSEMBLY__ + + /* +@@ -104,6 +116,11 @@ static inline u32 __attribute_const__ read_cpuid_cachetype(void) + return read_cpuid(CTR_EL0); + } + ++static inline bool id_aa64mmfr0_mixed_endian_el0(u64 mmfr0) ++{ ++ return (ID_AA64MMFR0_BIGEND(mmfr0) == 0x1) || ++ (ID_AA64MMFR0_BIGENDEL0(mmfr0) == 0x1); ++} + #endif /* __ASSEMBLY__ */ + + #endif +diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h +index 56a9e63..e2ff32a 100644 +--- a/arch/arm64/include/asm/insn.h ++++ b/arch/arm64/include/asm/insn.h +@@ -354,6 +354,16 @@ bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn); + int aarch64_insn_patch_text_nosync(void *addr, u32 insn); + int aarch64_insn_patch_text_sync(void *addrs[], u32 insns[], int cnt); + int aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt); ++ ++bool aarch32_insn_is_wide(u32 insn); ++ ++#define A32_RN_OFFSET 16 ++#define A32_RT_OFFSET 12 ++#define A32_RT2_OFFSET 0 ++ ++u32 aarch32_insn_extract_reg_num(u32 insn, int offset); ++u32 aarch32_insn_mcr_extract_opc2(u32 insn); ++u32 aarch32_insn_mcr_extract_crm(u32 insn); + #endif /* __ASSEMBLY__ */ + + #endif /* __ASM_INSN_H */ +diff --git a/arch/arm64/include/asm/opcodes.h b/arch/arm64/include/asm/opcodes.h +new file mode 100644 +index 0000000..4e603ea +--- /dev/null ++++ b/arch/arm64/include/asm/opcodes.h +@@ -0,0 +1 @@ ++#include <../../arm/include/asm/opcodes.h> +diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h +index 41ed9e1..d6dd9fd 100644 +--- a/arch/arm64/include/asm/ptrace.h ++++ b/arch/arm64/include/asm/ptrace.h +@@ -58,6 +58,13 @@ + #define COMPAT_PSR_Z_BIT 0x40000000 + #define COMPAT_PSR_N_BIT 0x80000000 + #define COMPAT_PSR_IT_MASK 0x0600fc00 /* If-Then execution state mask */ ++ ++#ifdef CONFIG_CPU_BIG_ENDIAN ++#define COMPAT_PSR_ENDSTATE COMPAT_PSR_E_BIT ++#else ++#define COMPAT_PSR_ENDSTATE 0 ++#endif ++ + /* + * These are 'magic' values for PTRACE_PEEKUSR that return info about where a + * process is located in memory. +diff --git a/arch/arm64/include/asm/seccomp.h b/arch/arm64/include/asm/seccomp.h +new file mode 100644 +index 0000000..c76fac9 +--- /dev/null ++++ b/arch/arm64/include/asm/seccomp.h +@@ -0,0 +1,25 @@ ++/* ++ * arch/arm64/include/asm/seccomp.h ++ * ++ * Copyright (C) 2014 Linaro Limited ++ * Author: AKASHI Takahiro <takahiro.akashi@linaro.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef _ASM_SECCOMP_H ++#define _ASM_SECCOMP_H ++ ++#include <asm/unistd.h> ++ ++#ifdef CONFIG_COMPAT ++#define __NR_seccomp_read_32 __NR_compat_read ++#define __NR_seccomp_write_32 __NR_compat_write ++#define __NR_seccomp_exit_32 __NR_compat_exit ++#define __NR_seccomp_sigreturn_32 __NR_compat_rt_sigreturn ++#endif /* CONFIG_COMPAT */ ++ ++#include <asm-generic/seccomp.h> ++ ++#endif /* _ASM_SECCOMP_H */ +diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h +index 10ca8ff..232e4ba 100644 +--- a/arch/arm64/include/asm/traps.h ++++ b/arch/arm64/include/asm/traps.h +@@ -18,6 +18,22 @@ + #ifndef __ASM_TRAP_H + #define __ASM_TRAP_H + ++#include <linux/list.h> ++ ++struct pt_regs; ++ ++struct undef_hook { ++ struct list_head node; ++ u32 instr_mask; ++ u32 instr_val; ++ u64 pstate_mask; ++ u64 pstate_val; ++ int (*fn)(struct pt_regs *regs, u32 instr); ++}; ++ ++void register_undef_hook(struct undef_hook *hook); ++void unregister_undef_hook(struct undef_hook *hook); ++ + static inline int in_exception_text(unsigned long ptr) + { + extern char __exception_text_start[]; +diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h +index 6d2bf41..49c9aef 100644 +--- a/arch/arm64/include/asm/unistd.h ++++ b/arch/arm64/include/asm/unistd.h +@@ -31,6 +31,9 @@ + * Compat syscall numbers used by the AArch64 kernel. + */ + #define __NR_compat_restart_syscall 0 ++#define __NR_compat_exit 1 ++#define __NR_compat_read 3 ++#define __NR_compat_write 4 + #define __NR_compat_sigreturn 119 + #define __NR_compat_rt_sigreturn 173 + +diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h +index 9dfdac4..8893ceb 100644 +--- a/arch/arm64/include/asm/unistd32.h ++++ b/arch/arm64/include/asm/unistd32.h +@@ -787,7 +787,8 @@ __SYSCALL(__NR_sched_setattr, sys_sched_setattr) + __SYSCALL(__NR_sched_getattr, sys_sched_getattr) + #define __NR_renameat2 382 + __SYSCALL(__NR_renameat2, sys_renameat2) +- /* 383 for seccomp */ ++#define __NR_seccomp 383 ++__SYSCALL(__NR_seccomp, sys_seccomp) + #define __NR_getrandom 384 + __SYSCALL(__NR_getrandom, sys_getrandom) + #define __NR_memfd_create 385 +diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile +index da22728..0a44aa5 100644 +--- a/arch/arm64/kernel/Makefile ++++ b/arch/arm64/kernel/Makefile +@@ -5,6 +5,7 @@ + CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) + AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) + CFLAGS_efi-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) ++CFLAGS_armv8_deprecated.o := -I$(src) + + CFLAGS_REMOVE_ftrace.o = -pg + CFLAGS_REMOVE_insn.o = -pg +@@ -14,11 +15,12 @@ CFLAGS_REMOVE_return_address.o = -pg + arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ + entry-fpsimd.o process.o ptrace.o setup.o signal.o \ + sys.o stacktrace.o time.o traps.o io.o vdso.o \ +- hyp-stub.o psci.o cpu_ops.o insn.o return_address.o \ +- cpuinfo.o cpu_errata.o alternative.o ++ hyp-stub.o psci.o psci-call.o cpu_ops.o insn.o \ ++ return_address.o cpuinfo.o cpu_errata.o alternative.o + + arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ +- sys_compat.o ++ sys_compat.o \ ++ ../../arm/kernel/opcodes.o + arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o + arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o + arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o +@@ -31,6 +33,7 @@ arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o + arm64-obj-$(CONFIG_KGDB) += kgdb.o + arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o + arm64-obj-$(CONFIG_PCI) += pci.o ++arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o + + obj-y += $(arm64-obj-y) vdso/ + obj-m += $(arm64-obj-m) +diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c +new file mode 100644 +index 0000000..7922c2e +--- /dev/null ++++ b/arch/arm64/kernel/armv8_deprecated.c +@@ -0,0 +1,662 @@ ++/* ++ * Copyright (C) 2014 ARM Limited ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/cpu.h> ++#include <linux/init.h> ++#include <linux/list.h> ++#include <linux/perf_event.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/sysctl.h> ++ ++#include <asm/insn.h> ++#include <asm/opcodes.h> ++#include <asm/system_misc.h> ++#include <asm/traps.h> ++#include <asm/uaccess.h> ++#include <asm/cpufeature.h> ++ ++#define CREATE_TRACE_POINTS ++#include "trace-events-emulation.h" ++ ++/* ++ * The runtime support for deprecated instruction support can be in one of ++ * following three states - ++ * ++ * 0 = undef ++ * 1 = emulate (software emulation) ++ * 2 = hw (supported in hardware) ++ */ ++enum insn_emulation_mode { ++ INSN_UNDEF, ++ INSN_EMULATE, ++ INSN_HW, ++}; ++ ++enum legacy_insn_status { ++ INSN_DEPRECATED, ++ INSN_OBSOLETE, ++}; ++ ++struct insn_emulation_ops { ++ const char *name; ++ enum legacy_insn_status status; ++ struct undef_hook *hooks; ++ int (*set_hw_mode)(bool enable); ++}; ++ ++struct insn_emulation { ++ struct list_head node; ++ struct insn_emulation_ops *ops; ++ int current_mode; ++ int min; ++ int max; ++}; ++ ++static LIST_HEAD(insn_emulation); ++static int nr_insn_emulated; ++static DEFINE_RAW_SPINLOCK(insn_emulation_lock); ++ ++static void register_emulation_hooks(struct insn_emulation_ops *ops) ++{ ++ struct undef_hook *hook; ++ ++ BUG_ON(!ops->hooks); ++ ++ for (hook = ops->hooks; hook->instr_mask; hook++) ++ register_undef_hook(hook); ++ ++ pr_notice("Registered %s emulation handler\n", ops->name); ++} ++ ++static void remove_emulation_hooks(struct insn_emulation_ops *ops) ++{ ++ struct undef_hook *hook; ++ ++ BUG_ON(!ops->hooks); ++ ++ for (hook = ops->hooks; hook->instr_mask; hook++) ++ unregister_undef_hook(hook); ++ ++ pr_notice("Removed %s emulation handler\n", ops->name); ++} ++ ++static void enable_insn_hw_mode(void *data) ++{ ++ struct insn_emulation *insn = (struct insn_emulation *)data; ++ if (insn->ops->set_hw_mode) ++ insn->ops->set_hw_mode(true); ++} ++ ++static void disable_insn_hw_mode(void *data) ++{ ++ struct insn_emulation *insn = (struct insn_emulation *)data; ++ if (insn->ops->set_hw_mode) ++ insn->ops->set_hw_mode(false); ++} ++ ++/* Run set_hw_mode(mode) on all active CPUs */ ++static int run_all_cpu_set_hw_mode(struct insn_emulation *insn, bool enable) ++{ ++ if (!insn->ops->set_hw_mode) ++ return -EINVAL; ++ if (enable) ++ on_each_cpu(enable_insn_hw_mode, (void *)insn, true); ++ else ++ on_each_cpu(disable_insn_hw_mode, (void *)insn, true); ++ return 0; ++} ++ ++/* ++ * Run set_hw_mode for all insns on a starting CPU. ++ * Returns: ++ * 0 - If all the hooks ran successfully. ++ * -EINVAL - At least one hook is not supported by the CPU. ++ */ ++static int run_all_insn_set_hw_mode(unsigned long cpu) ++{ ++ int rc = 0; ++ unsigned long flags; ++ struct insn_emulation *insn; ++ ++ raw_spin_lock_irqsave(&insn_emulation_lock, flags); ++ list_for_each_entry(insn, &insn_emulation, node) { ++ bool enable = (insn->current_mode == INSN_HW); ++ if (insn->ops->set_hw_mode && insn->ops->set_hw_mode(enable)) { ++ pr_warn("CPU[%ld] cannot support the emulation of %s", ++ cpu, insn->ops->name); ++ rc = -EINVAL; ++ } ++ } ++ raw_spin_unlock_irqrestore(&insn_emulation_lock, flags); ++ return rc; ++} ++ ++static int update_insn_emulation_mode(struct insn_emulation *insn, ++ enum insn_emulation_mode prev) ++{ ++ int ret = 0; ++ ++ switch (prev) { ++ case INSN_UNDEF: /* Nothing to be done */ ++ break; ++ case INSN_EMULATE: ++ remove_emulation_hooks(insn->ops); ++ break; ++ case INSN_HW: ++ if (!run_all_cpu_set_hw_mode(insn, false)) ++ pr_notice("Disabled %s support\n", insn->ops->name); ++ break; ++ } ++ ++ switch (insn->current_mode) { ++ case INSN_UNDEF: ++ break; ++ case INSN_EMULATE: ++ register_emulation_hooks(insn->ops); ++ break; ++ case INSN_HW: ++ ret = run_all_cpu_set_hw_mode(insn, true); ++ if (!ret) ++ pr_notice("Enabled %s support\n", insn->ops->name); ++ break; ++ } ++ ++ return ret; ++} ++ ++static void register_insn_emulation(struct insn_emulation_ops *ops) ++{ ++ unsigned long flags; ++ struct insn_emulation *insn; ++ ++ insn = kzalloc(sizeof(*insn), GFP_KERNEL); ++ insn->ops = ops; ++ insn->min = INSN_UNDEF; ++ ++ switch (ops->status) { ++ case INSN_DEPRECATED: ++ insn->current_mode = INSN_EMULATE; ++ /* Disable the HW mode if it was turned on at early boot time */ ++ run_all_cpu_set_hw_mode(insn, false); ++ insn->max = INSN_HW; ++ break; ++ case INSN_OBSOLETE: ++ insn->current_mode = INSN_UNDEF; ++ insn->max = INSN_EMULATE; ++ break; ++ } ++ ++ raw_spin_lock_irqsave(&insn_emulation_lock, flags); ++ list_add(&insn->node, &insn_emulation); ++ nr_insn_emulated++; ++ raw_spin_unlock_irqrestore(&insn_emulation_lock, flags); ++ ++ /* Register any handlers if required */ ++ update_insn_emulation_mode(insn, INSN_UNDEF); ++} ++ ++static int emulation_proc_handler(struct ctl_table *table, int write, ++ void __user *buffer, size_t *lenp, ++ loff_t *ppos) ++{ ++ int ret = 0; ++ struct insn_emulation *insn = (struct insn_emulation *) table->data; ++ enum insn_emulation_mode prev_mode = insn->current_mode; ++ ++ table->data = &insn->current_mode; ++ ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); ++ ++ if (ret || !write || prev_mode == insn->current_mode) ++ goto ret; ++ ++ ret = update_insn_emulation_mode(insn, prev_mode); ++ if (ret) { ++ /* Mode change failed, revert to previous mode. */ ++ insn->current_mode = prev_mode; ++ update_insn_emulation_mode(insn, INSN_UNDEF); ++ } ++ret: ++ table->data = insn; ++ return ret; ++} ++ ++static struct ctl_table ctl_abi[] = { ++ { ++ .procname = "abi", ++ .mode = 0555, ++ }, ++ { } ++}; ++ ++static void register_insn_emulation_sysctl(struct ctl_table *table) ++{ ++ unsigned long flags; ++ int i = 0; ++ struct insn_emulation *insn; ++ struct ctl_table *insns_sysctl, *sysctl; ++ ++ insns_sysctl = kzalloc(sizeof(*sysctl) * (nr_insn_emulated + 1), ++ GFP_KERNEL); ++ ++ raw_spin_lock_irqsave(&insn_emulation_lock, flags); ++ list_for_each_entry(insn, &insn_emulation, node) { ++ sysctl = &insns_sysctl[i]; ++ ++ sysctl->mode = 0644; ++ sysctl->maxlen = sizeof(int); ++ ++ sysctl->procname = insn->ops->name; ++ sysctl->data = insn; ++ sysctl->extra1 = &insn->min; ++ sysctl->extra2 = &insn->max; ++ sysctl->proc_handler = emulation_proc_handler; ++ i++; ++ } ++ raw_spin_unlock_irqrestore(&insn_emulation_lock, flags); ++ ++ table->child = insns_sysctl; ++ register_sysctl_table(table); ++} ++ ++/* ++ * Implement emulation of the SWP/SWPB instructions using load-exclusive and ++ * store-exclusive. ++ * ++ * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>] ++ * Where: Rt = destination ++ * Rt2 = source ++ * Rn = address ++ */ ++ ++/* ++ * Error-checking SWP macros implemented using ldxr{b}/stxr{b} ++ */ ++#define __user_swpX_asm(data, addr, res, temp, B) \ ++ __asm__ __volatile__( \ ++ " mov %w2, %w1\n" \ ++ "0: ldxr"B" %w1, [%3]\n" \ ++ "1: stxr"B" %w0, %w2, [%3]\n" \ ++ " cbz %w0, 2f\n" \ ++ " mov %w0, %w4\n" \ ++ "2:\n" \ ++ " .pushsection .fixup,\"ax\"\n" \ ++ " .align 2\n" \ ++ "3: mov %w0, %w5\n" \ ++ " b 2b\n" \ ++ " .popsection" \ ++ " .pushsection __ex_table,\"a\"\n" \ ++ " .align 3\n" \ ++ " .quad 0b, 3b\n" \ ++ " .quad 1b, 3b\n" \ ++ " .popsection" \ ++ : "=&r" (res), "+r" (data), "=&r" (temp) \ ++ : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \ ++ : "memory") ++ ++#define __user_swp_asm(data, addr, res, temp) \ ++ __user_swpX_asm(data, addr, res, temp, "") ++#define __user_swpb_asm(data, addr, res, temp) \ ++ __user_swpX_asm(data, addr, res, temp, "b") ++ ++/* ++ * Bit 22 of the instruction encoding distinguishes between ++ * the SWP and SWPB variants (bit set means SWPB). ++ */ ++#define TYPE_SWPB (1 << 22) ++ ++/* ++ * Set up process info to signal segmentation fault - called on access error. ++ */ ++static void set_segfault(struct pt_regs *regs, unsigned long addr) ++{ ++ siginfo_t info; ++ ++ down_read(¤t->mm->mmap_sem); ++ if (find_vma(current->mm, addr) == NULL) ++ info.si_code = SEGV_MAPERR; ++ else ++ info.si_code = SEGV_ACCERR; ++ up_read(¤t->mm->mmap_sem); ++ ++ info.si_signo = SIGSEGV; ++ info.si_errno = 0; ++ info.si_addr = (void *) instruction_pointer(regs); ++ ++ pr_debug("SWP{B} emulation: access caused memory abort!\n"); ++ arm64_notify_die("Illegal memory access", regs, &info, 0); ++} ++ ++static int emulate_swpX(unsigned int address, unsigned int *data, ++ unsigned int type) ++{ ++ unsigned int res = 0; ++ ++ if ((type != TYPE_SWPB) && (address & 0x3)) { ++ /* SWP to unaligned address not permitted */ ++ pr_debug("SWP instruction on unaligned pointer!\n"); ++ return -EFAULT; ++ } ++ ++ while (1) { ++ unsigned long temp; ++ ++ if (type == TYPE_SWPB) ++ __user_swpb_asm(*data, address, res, temp); ++ else ++ __user_swp_asm(*data, address, res, temp); ++ ++ if (likely(res != -EAGAIN) || signal_pending(current)) ++ break; ++ ++ cond_resched(); ++ } ++ ++ return res; ++} ++ ++/* ++ * swp_handler logs the id of calling process, dissects the instruction, sanity ++ * checks the memory location, calls emulate_swpX for the actual operation and ++ * deals with fixup/error handling before returning ++ */ ++static int swp_handler(struct pt_regs *regs, u32 instr) ++{ ++ u32 destreg, data, type, address = 0; ++ int rn, rt2, res = 0; ++ ++ perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc); ++ ++ type = instr & TYPE_SWPB; ++ ++ switch (arm_check_condition(instr, regs->pstate)) { ++ case ARM_OPCODE_CONDTEST_PASS: ++ break; ++ case ARM_OPCODE_CONDTEST_FAIL: ++ /* Condition failed - return to next instruction */ ++ goto ret; ++ case ARM_OPCODE_CONDTEST_UNCOND: ++ /* If unconditional encoding - not a SWP, undef */ ++ return -EFAULT; ++ default: ++ return -EINVAL; ++ } ++ ++ rn = aarch32_insn_extract_reg_num(instr, A32_RN_OFFSET); ++ rt2 = aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET); ++ ++ address = (u32)regs->user_regs.regs[rn]; ++ data = (u32)regs->user_regs.regs[rt2]; ++ destreg = aarch32_insn_extract_reg_num(instr, A32_RT_OFFSET); ++ ++ pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n", ++ rn, address, destreg, ++ aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET), data); ++ ++ /* Check access in reasonable access range for both SWP and SWPB */ ++ if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) { ++ pr_debug("SWP{B} emulation: access to 0x%08x not allowed!\n", ++ address); ++ goto fault; ++ } ++ ++ res = emulate_swpX(address, &data, type); ++ if (res == -EFAULT) ++ goto fault; ++ else if (res == 0) ++ regs->user_regs.regs[destreg] = data; ++ ++ret: ++ if (type == TYPE_SWPB) ++ trace_instruction_emulation("swpb", regs->pc); ++ else ++ trace_instruction_emulation("swp", regs->pc); ++ ++ pr_warn_ratelimited("\"%s\" (%ld) uses obsolete SWP{B} instruction at 0x%llx\n", ++ current->comm, (unsigned long)current->pid, regs->pc); ++ ++ regs->pc += 4; ++ return 0; ++ ++fault: ++ set_segfault(regs, address); ++ ++ return 0; ++} ++ ++/* ++ * Only emulate SWP/SWPB executed in ARM state/User mode. ++ * The kernel must be SWP free and SWP{B} does not exist in Thumb. ++ */ ++static struct undef_hook swp_hooks[] = { ++ { ++ .instr_mask = 0x0fb00ff0, ++ .instr_val = 0x01000090, ++ .pstate_mask = COMPAT_PSR_MODE_MASK, ++ .pstate_val = COMPAT_PSR_MODE_USR, ++ .fn = swp_handler ++ }, ++ { } ++}; ++ ++static struct insn_emulation_ops swp_ops = { ++ .name = "swp", ++ .status = INSN_OBSOLETE, ++ .hooks = swp_hooks, ++ .set_hw_mode = NULL, ++}; ++ ++static int cp15barrier_handler(struct pt_regs *regs, u32 instr) ++{ ++ perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc); ++ ++ switch (arm_check_condition(instr, regs->pstate)) { ++ case ARM_OPCODE_CONDTEST_PASS: ++ break; ++ case ARM_OPCODE_CONDTEST_FAIL: ++ /* Condition failed - return to next instruction */ ++ goto ret; ++ case ARM_OPCODE_CONDTEST_UNCOND: ++ /* If unconditional encoding - not a barrier instruction */ ++ return -EFAULT; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (aarch32_insn_mcr_extract_crm(instr)) { ++ case 10: ++ /* ++ * dmb - mcr p15, 0, Rt, c7, c10, 5 ++ * dsb - mcr p15, 0, Rt, c7, c10, 4 ++ */ ++ if (aarch32_insn_mcr_extract_opc2(instr) == 5) { ++ dmb(sy); ++ trace_instruction_emulation( ++ "mcr p15, 0, Rt, c7, c10, 5 ; dmb", regs->pc); ++ } else { ++ dsb(sy); ++ trace_instruction_emulation( ++ "mcr p15, 0, Rt, c7, c10, 4 ; dsb", regs->pc); ++ } ++ break; ++ case 5: ++ /* ++ * isb - mcr p15, 0, Rt, c7, c5, 4 ++ * ++ * Taking an exception or returning from one acts as an ++ * instruction barrier. So no explicit barrier needed here. ++ */ ++ trace_instruction_emulation( ++ "mcr p15, 0, Rt, c7, c5, 4 ; isb", regs->pc); ++ break; ++ } ++ ++ret: ++ pr_warn_ratelimited("\"%s\" (%ld) uses deprecated CP15 Barrier instruction at 0x%llx\n", ++ current->comm, (unsigned long)current->pid, regs->pc); ++ ++ regs->pc += 4; ++ return 0; ++} ++ ++static inline void config_sctlr_el1(u32 clear, u32 set) ++{ ++ u32 val; ++ ++ asm volatile("mrs %0, sctlr_el1" : "=r" (val)); ++ val &= ~clear; ++ val |= set; ++ asm volatile("msr sctlr_el1, %0" : : "r" (val)); ++} ++ ++static int cp15_barrier_set_hw_mode(bool enable) ++{ ++ if (enable) ++ config_sctlr_el1(0, SCTLR_EL1_CP15BEN); ++ else ++ config_sctlr_el1(SCTLR_EL1_CP15BEN, 0); ++ return 0; ++} ++ ++static struct undef_hook cp15_barrier_hooks[] = { ++ { ++ .instr_mask = 0x0fff0fdf, ++ .instr_val = 0x0e070f9a, ++ .pstate_mask = COMPAT_PSR_MODE_MASK, ++ .pstate_val = COMPAT_PSR_MODE_USR, ++ .fn = cp15barrier_handler, ++ }, ++ { ++ .instr_mask = 0x0fff0fff, ++ .instr_val = 0x0e070f95, ++ .pstate_mask = COMPAT_PSR_MODE_MASK, ++ .pstate_val = COMPAT_PSR_MODE_USR, ++ .fn = cp15barrier_handler, ++ }, ++ { } ++}; ++ ++static struct insn_emulation_ops cp15_barrier_ops = { ++ .name = "cp15_barrier", ++ .status = INSN_DEPRECATED, ++ .hooks = cp15_barrier_hooks, ++ .set_hw_mode = cp15_barrier_set_hw_mode, ++}; ++ ++static int setend_set_hw_mode(bool enable) ++{ ++ if (!cpu_supports_mixed_endian_el0()) ++ return -EINVAL; ++ ++ if (enable) ++ config_sctlr_el1(SCTLR_EL1_SED, 0); ++ else ++ config_sctlr_el1(0, SCTLR_EL1_SED); ++ return 0; ++} ++ ++static int compat_setend_handler(struct pt_regs *regs, u32 big_endian) ++{ ++ char *insn; ++ ++ perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc); ++ ++ if (big_endian) { ++ insn = "setend be"; ++ regs->pstate |= COMPAT_PSR_E_BIT; ++ } else { ++ insn = "setend le"; ++ regs->pstate &= ~COMPAT_PSR_E_BIT; ++ } ++ ++ trace_instruction_emulation(insn, regs->pc); ++ pr_warn_ratelimited("\"%s\" (%ld) uses deprecated setend instruction at 0x%llx\n", ++ current->comm, (unsigned long)current->pid, regs->pc); ++ ++ return 0; ++} ++ ++static int a32_setend_handler(struct pt_regs *regs, u32 instr) ++{ ++ int rc = compat_setend_handler(regs, (instr >> 9) & 1); ++ regs->pc += 4; ++ return rc; ++} ++ ++static int t16_setend_handler(struct pt_regs *regs, u32 instr) ++{ ++ int rc = compat_setend_handler(regs, (instr >> 3) & 1); ++ regs->pc += 2; ++ return rc; ++} ++ ++static struct undef_hook setend_hooks[] = { ++ { ++ .instr_mask = 0xfffffdff, ++ .instr_val = 0xf1010000, ++ .pstate_mask = COMPAT_PSR_MODE_MASK, ++ .pstate_val = COMPAT_PSR_MODE_USR, ++ .fn = a32_setend_handler, ++ }, ++ { ++ /* Thumb mode */ ++ .instr_mask = 0x0000fff7, ++ .instr_val = 0x0000b650, ++ .pstate_mask = (COMPAT_PSR_T_BIT | COMPAT_PSR_MODE_MASK), ++ .pstate_val = (COMPAT_PSR_T_BIT | COMPAT_PSR_MODE_USR), ++ .fn = t16_setend_handler, ++ }, ++ {} ++}; ++ ++static struct insn_emulation_ops setend_ops = { ++ .name = "setend", ++ .status = INSN_DEPRECATED, ++ .hooks = setend_hooks, ++ .set_hw_mode = setend_set_hw_mode, ++}; ++ ++static int insn_cpu_hotplug_notify(struct notifier_block *b, ++ unsigned long action, void *hcpu) ++{ ++ int rc = 0; ++ if ((action & ~CPU_TASKS_FROZEN) == CPU_STARTING) ++ rc = run_all_insn_set_hw_mode((unsigned long)hcpu); ++ ++ return notifier_from_errno(rc); ++} ++ ++static struct notifier_block insn_cpu_hotplug_notifier = { ++ .notifier_call = insn_cpu_hotplug_notify, ++}; ++ ++/* ++ * Invoked as late_initcall, since not needed before init spawned. ++ */ ++static int __init armv8_deprecated_init(void) ++{ ++ if (IS_ENABLED(CONFIG_SWP_EMULATION)) ++ register_insn_emulation(&swp_ops); ++ ++ if (IS_ENABLED(CONFIG_CP15_BARRIER_EMULATION)) ++ register_insn_emulation(&cp15_barrier_ops); ++ ++ if (IS_ENABLED(CONFIG_SETEND_EMULATION)) { ++ if(system_supports_mixed_endian_el0()) ++ register_insn_emulation(&setend_ops); ++ else ++ pr_info("setend instruction emulation is not supported on the system"); ++ } ++ ++ register_cpu_notifier(&insn_cpu_hotplug_notifier); ++ register_insn_emulation_sysctl(ctl_abi); ++ ++ return 0; ++} ++ ++late_initcall(armv8_deprecated_init); +diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c +index 16d6d03..e86b9b4 100644 +--- a/arch/arm64/kernel/cpuinfo.c ++++ b/arch/arm64/kernel/cpuinfo.c +@@ -35,6 +35,27 @@ + */ + DEFINE_PER_CPU(struct cpuinfo_arm64, cpu_data); + static struct cpuinfo_arm64 boot_cpu_data; ++static bool mixed_endian_el0 = true; ++ ++bool cpu_supports_mixed_endian_el0(void) ++{ ++ return id_aa64mmfr0_mixed_endian_el0(read_cpuid(ID_AA64MMFR0_EL1)); ++} ++ ++bool system_supports_mixed_endian_el0(void) ++{ ++ return mixed_endian_el0; ++} ++ ++static void update_mixed_endian_el0_support(struct cpuinfo_arm64 *info) ++{ ++ mixed_endian_el0 &= id_aa64mmfr0_mixed_endian_el0(info->reg_id_aa64mmfr0); ++} ++ ++static void update_cpu_features(struct cpuinfo_arm64 *info) ++{ ++ update_mixed_endian_el0_support(info); ++} + + static char *icache_policy_str[] = { + [ICACHE_POLICY_RESERVED] = "RESERVED/UNKNOWN", +@@ -189,6 +210,7 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) + cpuinfo_detect_icache_policy(info); + + check_local_cpu_errata(); ++ update_cpu_features(info); + } + + void cpuinfo_store_cpu(void) +diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S +index 6c99b46..c19999c 100644 +--- a/arch/arm64/kernel/entry.S ++++ b/arch/arm64/kernel/entry.S +@@ -475,8 +475,8 @@ el0_da: + bic x0, x26, #(0xff << 56) + mov x1, x25 + mov x2, sp +- adr lr, ret_to_user +- b do_mem_abort ++ bl do_mem_abort ++ b ret_to_user + el0_ia: + /* + * Instruction abort handling +@@ -488,8 +488,8 @@ el0_ia: + mov x0, x26 + orr x1, x25, #1 << 24 // use reserved ISS bit for instruction aborts + mov x2, sp +- adr lr, ret_to_user +- b do_mem_abort ++ bl do_mem_abort ++ b ret_to_user + el0_fpsimd_acc: + /* + * Floating Point or Advanced SIMD access +@@ -498,8 +498,8 @@ el0_fpsimd_acc: + ct_user_exit + mov x0, x25 + mov x1, sp +- adr lr, ret_to_user +- b do_fpsimd_acc ++ bl do_fpsimd_acc ++ b ret_to_user + el0_fpsimd_exc: + /* + * Floating Point or Advanced SIMD exception +@@ -508,8 +508,8 @@ el0_fpsimd_exc: + ct_user_exit + mov x0, x25 + mov x1, sp +- adr lr, ret_to_user +- b do_fpsimd_exc ++ bl do_fpsimd_exc ++ b ret_to_user + el0_sp_pc: + /* + * Stack or PC alignment exception handling +@@ -521,8 +521,8 @@ el0_sp_pc: + mov x0, x26 + mov x1, x25 + mov x2, sp +- adr lr, ret_to_user +- b do_sp_pc_abort ++ bl do_sp_pc_abort ++ b ret_to_user + el0_undef: + /* + * Undefined instruction +@@ -531,8 +531,8 @@ el0_undef: + enable_dbg_and_irq + ct_user_exit + mov x0, sp +- adr lr, ret_to_user +- b do_undefinstr ++ bl do_undefinstr ++ b ret_to_user + el0_dbg: + /* + * Debug exception handling +@@ -551,8 +551,8 @@ el0_inv: + mov x0, sp + mov x1, #BAD_SYNC + mrs x2, esr_el1 +- adr lr, ret_to_user +- b bad_mode ++ bl bad_mode ++ b ret_to_user + ENDPROC(el0_sync) + + .align 6 +@@ -674,14 +674,15 @@ el0_svc_naked: // compat entry point + ldr x16, [tsk, #TI_FLAGS] // check for syscall hooks + tst x16, #_TIF_SYSCALL_WORK + b.ne __sys_trace +- adr lr, ret_fast_syscall // return address + cmp scno, sc_nr // check upper syscall limit + b.hs ni_sys + ldr x16, [stbl, scno, lsl #3] // address in the syscall table +- br x16 // call sys_* routine ++ blr x16 // call sys_* routine ++ b ret_fast_syscall + ni_sys: + mov x0, sp +- b do_ni_syscall ++ bl do_ni_syscall ++ b ret_fast_syscall + ENDPROC(el0_svc) + + /* +@@ -689,26 +690,38 @@ ENDPROC(el0_svc) + * switches, and waiting for our parent to respond. + */ + __sys_trace: +- mov x0, sp ++ mov w0, #-1 // set default errno for ++ cmp scno, x0 // user-issued syscall(-1) ++ b.ne 1f ++ mov x0, #-ENOSYS ++ str x0, [sp, #S_X0] ++1: mov x0, sp + bl syscall_trace_enter +- adr lr, __sys_trace_return // return address ++ cmp w0, #-1 // skip the syscall? ++ b.eq __sys_trace_return_skipped + uxtw scno, w0 // syscall number (possibly new) + mov x1, sp // pointer to regs + cmp scno, sc_nr // check upper syscall limit +- b.hs ni_sys ++ b.hs __ni_sys_trace + ldp x0, x1, [sp] // restore the syscall args + ldp x2, x3, [sp, #S_X2] + ldp x4, x5, [sp, #S_X4] + ldp x6, x7, [sp, #S_X6] + ldr x16, [stbl, scno, lsl #3] // address in the syscall table +- br x16 // call sys_* routine ++ blr x16 // call sys_* routine + + __sys_trace_return: +- str x0, [sp] // save returned x0 ++ str x0, [sp, #S_X0] // save returned x0 ++__sys_trace_return_skipped: + mov x0, sp + bl syscall_trace_exit + b ret_to_user + ++__ni_sys_trace: ++ mov x0, sp ++ bl do_ni_syscall ++ b __sys_trace_return ++ + /* + * Special system call wrappers. + */ +diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c +index 8cd27fe..7e9327a 100644 +--- a/arch/arm64/kernel/insn.c ++++ b/arch/arm64/kernel/insn.c +@@ -960,3 +960,29 @@ u32 aarch64_insn_gen_logical_shifted_reg(enum aarch64_insn_register dst, + + return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift); + } ++ ++bool aarch32_insn_is_wide(u32 insn) ++{ ++ return insn >= 0xe800; ++} ++ ++/* ++ * Macros/defines for extracting register numbers from instruction. ++ */ ++u32 aarch32_insn_extract_reg_num(u32 insn, int offset) ++{ ++ return (insn & (0xf << offset)) >> offset; ++} ++ ++#define OPC2_MASK 0x7 ++#define OPC2_OFFSET 5 ++u32 aarch32_insn_mcr_extract_opc2(u32 insn) ++{ ++ return (insn & (OPC2_MASK << OPC2_OFFSET)) >> OPC2_OFFSET; ++} ++ ++#define CRM_MASK 0xf ++u32 aarch32_insn_mcr_extract_crm(u32 insn) ++{ ++ return insn & CRM_MASK; ++} +diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c +index fde9923..07c4c53 100644 +--- a/arch/arm64/kernel/process.c ++++ b/arch/arm64/kernel/process.c +@@ -163,6 +163,70 @@ void machine_restart(char *cmd) + while (1); + } + ++/* ++ * dump a block of kernel memory from around the given address ++ */ ++static void show_data(unsigned long addr, int nbytes, const char *name) ++{ ++ int i, j; ++ int nlines; ++ u32 *p; ++ ++ /* ++ * don't attempt to dump non-kernel addresses or ++ * values that are probably just small negative numbers ++ */ ++ if (addr < PAGE_OFFSET || addr > -256UL) ++ return; ++ ++ printk("\n%s: %#lx:\n", name, addr); ++ ++ /* ++ * round address down to a 32 bit boundary ++ * and always dump a multiple of 32 bytes ++ */ ++ p = (u32 *)(addr & ~(sizeof(u32) - 1)); ++ nbytes += (addr & (sizeof(u32) - 1)); ++ nlines = (nbytes + 31) / 32; ++ ++ ++ for (i = 0; i < nlines; i++) { ++ /* ++ * just display low 16 bits of address to keep ++ * each line of the dump < 80 characters ++ */ ++ printk("%04lx ", (unsigned long)p & 0xffff); ++ for (j = 0; j < 8; j++) { ++ u32 data; ++ if (probe_kernel_address(p, data)) { ++ printk(" ********"); ++ } else { ++ printk(" %08x", data); ++ } ++ ++p; ++ } ++ printk("\n"); ++ } ++} ++ ++static void show_extra_register_data(struct pt_regs *regs, int nbytes) ++{ ++ mm_segment_t fs; ++ unsigned int i; ++ ++ fs = get_fs(); ++ set_fs(KERNEL_DS); ++ show_data(regs->pc - nbytes, nbytes * 2, "PC"); ++ show_data(regs->regs[30] - nbytes, nbytes * 2, "LR"); ++ show_data(regs->sp - nbytes, nbytes * 2, "SP"); ++ for (i = 0; i < 30; i++) { ++ char name[4]; ++ snprintf(name, sizeof(name), "X%u", i); ++ show_data(regs->regs[i] - nbytes, nbytes * 2, name); ++ } ++ set_fs(fs); ++} ++ + void __show_regs(struct pt_regs *regs) + { + int i, top_reg; +@@ -189,6 +253,8 @@ void __show_regs(struct pt_regs *regs) + if (i % 2 == 0) + printk("\n"); + } ++ if (!user_mode(regs)) ++ show_extra_register_data(regs, 128); + printk("\n"); + } + +diff --git a/arch/arm64/kernel/psci-call.S b/arch/arm64/kernel/psci-call.S +new file mode 100644 +index 0000000..cf83e61 +--- /dev/null ++++ b/arch/arm64/kernel/psci-call.S +@@ -0,0 +1,28 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * 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. ++ * ++ * Copyright (C) 2015 ARM Limited ++ * ++ * Author: Will Deacon <will.deacon@arm.com> ++ */ ++ ++#include <linux/linkage.h> ++ ++/* int __invoke_psci_fn_hvc(u64 function_id, u64 arg0, u64 arg1, u64 arg2) */ ++ENTRY(__invoke_psci_fn_hvc) ++ hvc #0 ++ ret ++ENDPROC(__invoke_psci_fn_hvc) ++ ++/* int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1, u64 arg2) */ ++ENTRY(__invoke_psci_fn_smc) ++ smc #0 ++ ret ++ENDPROC(__invoke_psci_fn_smc) +diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c +index 663da77..81c081e 100644 +--- a/arch/arm64/kernel/psci.c ++++ b/arch/arm64/kernel/psci.c +@@ -57,6 +57,9 @@ static struct psci_operations psci_ops; + static int (*invoke_psci_fn)(u64, u64, u64, u64); + typedef int (*psci_initcall_t)(const struct device_node *); + ++asmlinkage int __invoke_psci_fn_hvc(u64, u64, u64, u64); ++asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64); ++ + enum psci_function { + PSCI_FN_CPU_SUSPEND, + PSCI_FN_CPU_ON, +@@ -109,40 +112,6 @@ static void psci_power_state_unpack(u32 power_state, + PSCI_0_2_POWER_STATE_AFFL_SHIFT; + } + +-/* +- * The following two functions are invoked via the invoke_psci_fn pointer +- * and will not be inlined, allowing us to piggyback on the AAPCS. +- */ +-static noinline int __invoke_psci_fn_hvc(u64 function_id, u64 arg0, u64 arg1, +- u64 arg2) +-{ +- asm volatile( +- __asmeq("%0", "x0") +- __asmeq("%1", "x1") +- __asmeq("%2", "x2") +- __asmeq("%3", "x3") +- "hvc #0\n" +- : "+r" (function_id) +- : "r" (arg0), "r" (arg1), "r" (arg2)); +- +- return function_id; +-} +- +-static noinline int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1, +- u64 arg2) +-{ +- asm volatile( +- __asmeq("%0", "x0") +- __asmeq("%1", "x1") +- __asmeq("%2", "x2") +- __asmeq("%3", "x3") +- "smc #0\n" +- : "+r" (function_id) +- : "r" (arg0), "r" (arg1), "r" (arg2)); +- +- return function_id; +-} +- + static int psci_get_version(void) + { + int err; +diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c +index 8a4ae8e..d882b83 100644 +--- a/arch/arm64/kernel/ptrace.c ++++ b/arch/arm64/kernel/ptrace.c +@@ -27,6 +27,7 @@ + #include <linux/smp.h> + #include <linux/ptrace.h> + #include <linux/user.h> ++#include <linux/seccomp.h> + #include <linux/security.h> + #include <linux/init.h> + #include <linux/signal.h> +@@ -551,6 +552,32 @@ static int tls_set(struct task_struct *target, const struct user_regset *regset, + return ret; + } + ++static int system_call_get(struct task_struct *target, ++ const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ void *kbuf, void __user *ubuf) ++{ ++ int syscallno = task_pt_regs(target)->syscallno; ++ ++ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, ++ &syscallno, 0, -1); ++} ++ ++static int system_call_set(struct task_struct *target, ++ const struct user_regset *regset, ++ unsigned int pos, unsigned int count, ++ const void *kbuf, const void __user *ubuf) ++{ ++ int syscallno, ret; ++ ++ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &syscallno, 0, -1); ++ if (ret) ++ return ret; ++ ++ task_pt_regs(target)->syscallno = syscallno; ++ return ret; ++} ++ + enum aarch64_regset { + REGSET_GPR, + REGSET_FPR, +@@ -559,6 +586,7 @@ enum aarch64_regset { + REGSET_HW_BREAK, + REGSET_HW_WATCH, + #endif ++ REGSET_SYSTEM_CALL, + }; + + static const struct user_regset aarch64_regsets[] = { +@@ -608,6 +636,14 @@ static const struct user_regset aarch64_regsets[] = { + .set = hw_break_set, + }, + #endif ++ [REGSET_SYSTEM_CALL] = { ++ .core_note_type = NT_ARM_SYSTEM_CALL, ++ .n = 1, ++ .size = sizeof(int), ++ .align = sizeof(int), ++ .get = system_call_get, ++ .set = system_call_set, ++ }, + }; + + static const struct user_regset_view user_aarch64_view = { +@@ -1114,6 +1150,10 @@ static void tracehook_report_syscall(struct pt_regs *regs, + + asmlinkage int syscall_trace_enter(struct pt_regs *regs) + { ++ /* Do the secure computing check first; failures should be fast. */ ++ if (secure_computing() == -1) ++ return -1; ++ + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER); + +diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c +index d502a86..ae8be92 100644 +--- a/arch/arm64/kernel/setup.c ++++ b/arch/arm64/kernel/setup.c +@@ -314,6 +314,8 @@ static void __init setup_machine_fdt(phys_addr_t dt_phys) + while (true) + cpu_relax(); + } ++ ++ dump_stack_set_arch_desc("%s (DT)", of_flat_dt_get_machine_name()); + } + + /* +diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c +index 76920d4..6acffec 100644 +--- a/arch/arm64/kernel/signal32.c ++++ b/arch/arm64/kernel/signal32.c +@@ -185,6 +185,12 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from) + err |= __put_user(from->si_uid, &to->si_uid); + err |= __put_user(from->si_int, &to->si_int); + break; ++ case __SI_SYS: ++ err |= __put_user((compat_uptr_t)(unsigned long) ++ from->si_call_addr, &to->si_call_addr); ++ err |= __put_user(from->si_syscall, &to->si_syscall); ++ err |= __put_user(from->si_arch, &to->si_arch); ++ break; + default: /* this is just in case for now ... */ + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); +@@ -433,7 +439,7 @@ static void compat_setup_return(struct pt_regs *regs, struct k_sigaction *ka, + { + compat_ulong_t handler = ptr_to_compat(ka->sa.sa_handler); + compat_ulong_t retcode; +- compat_ulong_t spsr = regs->pstate & ~PSR_f; ++ compat_ulong_t spsr = regs->pstate & ~(PSR_f | COMPAT_PSR_E_BIT); + int thumb; + + /* Check if the handler is written for ARM or Thumb */ +@@ -447,6 +453,9 @@ static void compat_setup_return(struct pt_regs *regs, struct k_sigaction *ka, + /* The IT state must be cleared for both ARM and Thumb-2 */ + spsr &= ~COMPAT_PSR_IT_MASK; + ++ /* Restore the original endianness */ ++ spsr |= COMPAT_PSR_ENDSTATE; ++ + if (ka->sa.sa_flags & SA_RESTORER) { + retcode = ptr_to_compat(ka->sa.sa_restorer); + } else { +diff --git a/arch/arm64/kernel/trace-events-emulation.h b/arch/arm64/kernel/trace-events-emulation.h +new file mode 100644 +index 0000000..ae1dd59 +--- /dev/null ++++ b/arch/arm64/kernel/trace-events-emulation.h +@@ -0,0 +1,35 @@ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM emulation ++ ++#if !defined(_TRACE_EMULATION_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _TRACE_EMULATION_H ++ ++#include <linux/tracepoint.h> ++ ++TRACE_EVENT(instruction_emulation, ++ ++ TP_PROTO(const char *instr, u64 addr), ++ TP_ARGS(instr, addr), ++ ++ TP_STRUCT__entry( ++ __string(instr, instr) ++ __field(u64, addr) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(instr, instr); ++ __entry->addr = addr; ++ ), ++ ++ TP_printk("instr=\"%s\" addr=0x%llx", __get_str(instr), __entry->addr) ++); ++ ++#endif /* _TRACE_EMULATION_H */ ++ ++/* This part must be outside protection */ ++#undef TRACE_INCLUDE_PATH ++#undef TRACE_INCLUDE_FILE ++#define TRACE_INCLUDE_PATH . ++ ++#define TRACE_INCLUDE_FILE trace-events-emulation ++#include <trace/define_trace.h> +diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c +index de1b085..0a801e3 100644 +--- a/arch/arm64/kernel/traps.c ++++ b/arch/arm64/kernel/traps.c +@@ -259,6 +259,69 @@ void arm64_notify_die(const char *str, struct pt_regs *regs, + } + } + ++static LIST_HEAD(undef_hook); ++static DEFINE_RAW_SPINLOCK(undef_lock); ++ ++void register_undef_hook(struct undef_hook *hook) ++{ ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&undef_lock, flags); ++ list_add(&hook->node, &undef_hook); ++ raw_spin_unlock_irqrestore(&undef_lock, flags); ++} ++ ++void unregister_undef_hook(struct undef_hook *hook) ++{ ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&undef_lock, flags); ++ list_del(&hook->node); ++ raw_spin_unlock_irqrestore(&undef_lock, flags); ++} ++ ++static int call_undef_hook(struct pt_regs *regs) ++{ ++ struct undef_hook *hook; ++ unsigned long flags; ++ u32 instr; ++ int (*fn)(struct pt_regs *regs, u32 instr) = NULL; ++ void __user *pc = (void __user *)instruction_pointer(regs); ++ ++ if (!user_mode(regs)) ++ return 1; ++ ++ if (compat_thumb_mode(regs)) { ++ /* 16-bit Thumb instruction */ ++ if (get_user(instr, (u16 __user *)pc)) ++ goto exit; ++ instr = le16_to_cpu(instr); ++ if (aarch32_insn_is_wide(instr)) { ++ u32 instr2; ++ ++ if (get_user(instr2, (u16 __user *)(pc + 2))) ++ goto exit; ++ instr2 = le16_to_cpu(instr2); ++ instr = (instr << 16) | instr2; ++ } ++ } else { ++ /* 32-bit ARM instruction */ ++ if (get_user(instr, (u32 __user *)pc)) ++ goto exit; ++ instr = le32_to_cpu(instr); ++ } ++ ++ raw_spin_lock_irqsave(&undef_lock, flags); ++ list_for_each_entry(hook, &undef_hook, node) ++ if ((instr & hook->instr_mask) == hook->instr_val && ++ (regs->pstate & hook->pstate_mask) == hook->pstate_val) ++ fn = hook->fn; ++ ++ raw_spin_unlock_irqrestore(&undef_lock, flags); ++exit: ++ return fn ? fn(regs, instr) : 1; ++} ++ + asmlinkage void __exception do_undefinstr(struct pt_regs *regs) + { + siginfo_t info; +@@ -268,6 +331,9 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs) + if (!aarch32_break_handler(regs)) + return; + ++ if (call_undef_hook(regs) == 0) ++ return; ++ + if (show_unhandled_signals && unhandled_signal(current, SIGILL) && + printk_ratelimit()) { + pr_info("%s[%d]: undefined instruction: pc=%p\n", +diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c +index f752943..b45b313 100644 +--- a/arch/arm64/mm/init.c ++++ b/arch/arm64/mm/init.c +@@ -114,9 +114,11 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max) + } + + #ifdef CONFIG_HAVE_ARCH_PFN_VALID ++#define PFN_MASK ((1UL << (64 - PAGE_SHIFT)) - 1) ++ + int pfn_valid(unsigned long pfn) + { +- return memblock_is_memory(pfn << PAGE_SHIFT); ++ return (pfn & PFN_MASK) == pfn && memblock_is_memory(pfn << PAGE_SHIFT); + } + EXPORT_SYMBOL(pfn_valid); + #endif +diff --git a/arch/x86/include/asm/idle.h b/arch/x86/include/asm/idle.h +index c5d1785..02bab09 100644 +--- a/arch/x86/include/asm/idle.h ++++ b/arch/x86/include/asm/idle.h +@@ -1,13 +1,6 @@ + #ifndef _ASM_X86_IDLE_H + #define _ASM_X86_IDLE_H + +-#define IDLE_START 1 +-#define IDLE_END 2 +- +-struct notifier_block; +-void idle_notifier_register(struct notifier_block *n); +-void idle_notifier_unregister(struct notifier_block *n); +- + #ifdef CONFIG_X86_64 + void enter_idle(void); + void exit_idle(void); +diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c +index a388bb8..0885df5 100644 +--- a/arch/x86/kernel/process.c ++++ b/arch/x86/kernel/process.c +@@ -42,19 +42,6 @@ __visible DEFINE_PER_CPU_SHARED_ALIGNED(struct tss_struct, init_tss) = INIT_TSS; + + #ifdef CONFIG_X86_64 + static DEFINE_PER_CPU(unsigned char, is_idle); +-static ATOMIC_NOTIFIER_HEAD(idle_notifier); +- +-void idle_notifier_register(struct notifier_block *n) +-{ +- atomic_notifier_chain_register(&idle_notifier, n); +-} +-EXPORT_SYMBOL_GPL(idle_notifier_register); +- +-void idle_notifier_unregister(struct notifier_block *n) +-{ +- atomic_notifier_chain_unregister(&idle_notifier, n); +-} +-EXPORT_SYMBOL_GPL(idle_notifier_unregister); + #endif + + struct kmem_cache *task_xstate_cachep; +@@ -262,14 +249,14 @@ static inline void play_dead(void) + void enter_idle(void) + { + this_cpu_write(is_idle, 1); +- atomic_notifier_call_chain(&idle_notifier, IDLE_START, NULL); ++ idle_notifier_call_chain(IDLE_START); + } + + static void __exit_idle(void) + { + if (x86_test_and_clear_bit_percpu(0, is_idle) == 0) + return; +- atomic_notifier_call_chain(&idle_notifier, IDLE_END, NULL); ++ idle_notifier_call_chain(IDLE_END); + } + + /* Called from interrupts to signify idle end */ +diff --git a/block/bsg.c b/block/bsg.c +index 276e869..fc60769 100644 +--- a/block/bsg.c ++++ b/block/bsg.c +@@ -677,6 +677,9 @@ bsg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) + + dprintk("%s: write %Zd bytes\n", bd->name, count); + ++ if (unlikely(segment_eq(get_fs(), KERNEL_DS))) ++ return -EINVAL; ++ + bsg_set_block(bd, file); + + bytes_written = 0; +diff --git a/block/genhd.c b/block/genhd.c +index c2fb3f7..b529e50 100644 +--- a/block/genhd.c ++++ b/block/genhd.c +@@ -829,6 +829,7 @@ static void disk_seqf_stop(struct seq_file *seqf, void *v) + if (iter) { + class_dev_iter_exit(iter); + kfree(iter); ++ seqf->private = NULL; + } + } + +@@ -1116,6 +1117,22 @@ static void disk_release(struct device *dev) + blk_put_queue(disk->queue); + kfree(disk); + } ++ ++static int disk_uevent(struct device *dev, struct kobj_uevent_env *env) ++{ ++ struct gendisk *disk = dev_to_disk(dev); ++ struct disk_part_iter piter; ++ struct hd_struct *part; ++ int cnt = 0; ++ ++ disk_part_iter_init(&piter, disk, 0); ++ while((part = disk_part_iter_next(&piter))) ++ cnt++; ++ disk_part_iter_exit(&piter); ++ add_uevent_var(env, "NPARTS=%u", cnt); ++ return 0; ++} ++ + struct class block_class = { + .name = "block", + }; +@@ -1135,6 +1152,7 @@ static struct device_type disk_type = { + .groups = disk_attr_groups, + .release = disk_release, + .devnode = block_devnode, ++ .uevent = disk_uevent, + }; + + #ifdef CONFIG_PROC_FS +diff --git a/block/ioprio.c b/block/ioprio.c +index 31666c9..3cf3ede 100644 +--- a/block/ioprio.c ++++ b/block/ioprio.c +@@ -148,9 +148,11 @@ static int get_task_ioprio(struct task_struct *p) + ret = security_task_getioprio(p); + if (ret) + goto out; ++ task_lock(p); + ret = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, IOPRIO_NORM); + if (p->io_context) + ret = p->io_context->ioprio; ++ task_unlock(p); + out: + return ret; + } +diff --git a/block/partition-generic.c b/block/partition-generic.c +index 0d9e5f9..47284e7 100644 +--- a/block/partition-generic.c ++++ b/block/partition-generic.c +@@ -217,10 +217,21 @@ static void part_release(struct device *dev) + kfree(p); + } + ++static int part_uevent(struct device *dev, struct kobj_uevent_env *env) ++{ ++ struct hd_struct *part = dev_to_part(dev); ++ ++ add_uevent_var(env, "PARTN=%u", part->partno); ++ if (part->info && part->info->volname[0]) ++ add_uevent_var(env, "PARTNAME=%s", part->info->volname); ++ return 0; ++} ++ + struct device_type part_type = { + .name = "partition", + .groups = part_attr_groups, + .release = part_release, ++ .uevent = part_uevent, + }; + + static void delete_partition_rcu_cb(struct rcu_head *head) +diff --git a/drivers/Kconfig b/drivers/Kconfig +index 1a693d3..845873a 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -100,6 +100,8 @@ source "drivers/memstick/Kconfig" + + source "drivers/leds/Kconfig" + ++source "drivers/switch/Kconfig" ++ + source "drivers/accessibility/Kconfig" + + source "drivers/infiniband/Kconfig" +@@ -182,4 +184,8 @@ source "drivers/ras/Kconfig" + + source "drivers/thunderbolt/Kconfig" + ++source "drivers/hidmac/Kconfig" ++ ++source "drivers/hisilicon/Kconfig" ++ + endmenu +diff --git a/drivers/Makefile b/drivers/Makefile +index ebee555..744a120 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -117,6 +117,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle/ + obj-y += mmc/ + obj-$(CONFIG_MEMSTICK) += memstick/ + obj-y += leds/ ++obj-$(CONFIG_SWITCH) += switch/ + obj-$(CONFIG_INFINIBAND) += infiniband/ + obj-$(CONFIG_SGI_SN) += sn/ + obj-y += firmware/ +@@ -161,3 +162,6 @@ obj-$(CONFIG_POWERCAP) += powercap/ + obj-$(CONFIG_MCB) += mcb/ + obj-$(CONFIG_RAS) += ras/ + obj-$(CONFIG_THUNDERBOLT) += thunderbolt/ ++obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/ ++obj-$(CONFIG_HI_DMAC) += hidmac/ ++obj-y += hisilicon/ +diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c +index d24fa19..6d4e44e 100644 +--- a/drivers/acpi/thermal.c ++++ b/drivers/acpi/thermal.c +@@ -800,7 +800,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, + result = + thermal_zone_bind_cooling_device + (thermal, trip, cdev, +- THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); ++ THERMAL_NO_LIMIT, THERMAL_NO_LIMIT, ++ THERMAL_WEIGHT_DEFAULT); + else + result = + thermal_zone_unbind_cooling_device +@@ -824,7 +825,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, + if (bind) + result = thermal_zone_bind_cooling_device + (thermal, trip, cdev, +- THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); ++ THERMAL_NO_LIMIT, THERMAL_NO_LIMIT, ++ THERMAL_WEIGHT_DEFAULT); + else + result = thermal_zone_unbind_cooling_device + (thermal, trip, cdev); +@@ -841,7 +843,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, + result = thermal_zone_bind_cooling_device + (thermal, THERMAL_TRIPS_NONE, + cdev, THERMAL_NO_LIMIT, +- THERMAL_NO_LIMIT); ++ THERMAL_NO_LIMIT, ++ THERMAL_WEIGHT_DEFAULT); + else + result = thermal_zone_unbind_cooling_device + (thermal, THERMAL_TRIPS_NONE, +diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c +index 47bbdc1..a4ac490 100644 +--- a/drivers/amba/bus.c ++++ b/drivers/amba/bus.c +@@ -336,7 +336,7 @@ int amba_device_add(struct amba_device *dev, struct resource *parent) + + amba_put_disable_pclk(dev); + +- if (cid == AMBA_CID) ++ if (cid == AMBA_CID || cid == CORESIGHT_CID) + dev->periphid = pid; + + if (!dev->periphid) +diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig +index cd4cccb..c4d06e0 100644 +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -2,6 +2,8 @@ + # SATA/PATA driver configuration + # + ++source "drivers/ata/Kconfig.hiahci" ++ + config HAVE_PATA_PLATFORM + bool + help +diff --git a/drivers/ata/Kconfig.hiahci b/drivers/ata/Kconfig.hiahci +new file mode 100644 +index 0000000..ddee693 +--- /dev/null ++++ b/drivers/ata/Kconfig.hiahci +@@ -0,0 +1,36 @@ ++menuconfig HI_SATA ++ bool "hisilicon sata device support" ++ depends on (ARCH_HI3531D || ARCH_HI3521D || ARCH_HI3536C) ++ default y if (ARCH_HI3531D || ARCH_HI3521D || ARCH_HI3536C) ++ select ATA ++ select ATA_VERBOSE_ERROR ++ select SATA_PMP ++ select SATA_AHCI_PLATFORM ++ ++if HI_SATA ++config HI_SATA_IOBASE ++ hex "hi sata IO address" ++ default "0x11010000" if (ARCH_HI3531D || ARCH_HI3521D || ARCH_HI3536C) ++ help ++ hisilicon sata io base address. ++ ++config HI_SATA_FBS ++ int "hi sata FIS-Based switching" ++ default 1 ++ range 0 1 ++ help ++ Hisatav200 supports FBS. ++ FBS is FIS-Based switching. ++ Choose y if you want to use it. ++ ++config HI_SATA_NCQ ++ int "hi sata Native Command Queuing" ++ default 1 ++ range 0 1 ++ help ++ Hisatav200 supports NCQ. ++ NCQ is Native Command Queuing. ++ Choose y if you want to use it. ++ ++endif ++ +diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile +index ae41107..d674ff1 100644 +--- a/drivers/ata/Makefile ++++ b/drivers/ata/Makefile +@@ -1,5 +1,6 @@ + + obj-$(CONFIG_ATA) += libata.o ++obj-$(CONFIG_HI_SATA) += hi_sata_dbg.o + + # non-SFF interface + obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 40f0e34..a773f34 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -334,6 +334,9 @@ struct ahci_host_priv { + bool got_runtime_pm; /* Did we do pm_runtime_get? */ + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + struct regulator *target_pwr; /* Optional */ ++#define PCI_AHCI 0 ++#define ORI_AHCI 1 ++ u32 type; + /* + * If platform uses PHYs. There is a 1:1 relation between the port number and + * the PHY position in this array. +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 06f1d59..590f823 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -22,6 +22,11 @@ + #include <linux/ahci_platform.h> + #include "ahci.h" + ++static unsigned int ncq_en = CONFIG_HI_SATA_NCQ; ++module_param(ncq_en, uint, 0600); ++MODULE_PARM_DESC(ncq_en, "ahci ncq flag (default:1)"); ++extern unsigned int sata_port_map; ++ + static const struct ata_port_info ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, +@@ -31,7 +36,11 @@ static const struct ata_port_info ahci_port_info = { + + static int ahci_probe(struct platform_device *pdev) + { ++#if (!defined(CONFIG_ARCH_HI3531D) \ ++ && !defined(CONFIG_ARCH_HI3521D) \ ++ && !defined(CONFIG_ARCH_HI3536C)) + struct device *dev = &pdev->dev; ++#endif + struct ahci_host_priv *hpriv; + int rc; + +@@ -43,8 +52,17 @@ static int ahci_probe(struct platform_device *pdev) + if (rc) + return rc; + ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3521D) \ ++ || defined(CONFIG_ARCH_HI3536C)) ++ hpriv->type = ORI_AHCI; ++ hpriv->force_port_map = sata_port_map; ++ if (!ncq_en) ++ hpriv->flags |= AHCI_HFLAG_NO_NCQ; ++#else + if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci")) + hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ; ++#endif + + rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info); + if (rc) +diff --git a/drivers/ata/hi_sata_dbg.c b/drivers/ata/hi_sata_dbg.c +new file mode 100644 +index 0000000..8442fbc +--- /dev/null ++++ b/drivers/ata/hi_sata_dbg.c +@@ -0,0 +1,158 @@ ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/module.h> ++#include <linux/libata.h> ++#include <mach/io.h> ++#include "ahci.h" ++#include "hi_sata_dbg.h" ++ ++void hi_sata_mem_dump(unsigned int *addr, unsigned int size) ++{ ++ int ix; ++ ++ for (ix = 0; ix < size; ix += 0x04, addr++) { ++ if (!(ix & 0x0F)) ++ pr_debug("\n0x%08X: ", ++ (unsigned int)virt_to_phys(addr)); ++ pr_debug("%08X ", *addr); ++ } ++} ++EXPORT_SYMBOL(hi_sata_mem_dump); ++ ++void hi_sata_phys_mem_dump(unsigned int addr, unsigned int size) ++{ ++ hi_sata_mem_dump(phys_to_virt(addr), size); ++} ++EXPORT_SYMBOL(hi_sata_phys_mem_dump); ++ ++void hi_ahci_reg_dump(void) ++{ ++ int ix; ++ unsigned int regbase; ++ ++ regbase = CONFIG_HI_SATA_IOBASE; ++ pr_debug("AHCI GHC Register dump:"); ++ for (ix = 0; ix <= 0x28; ix += 0x04) { ++ if (!(ix & 0x0F)) ++ pr_debug("\n0x%08X: ", (regbase + ix)); ++ pr_debug("%08X ", readl(__io_address(regbase + ix))); ++ } ++ pr_debug("\n"); ++ ++ regbase = CONFIG_HI_SATA_IOBASE + 0x0100; ++ pr_debug("AHCI PORT 0 Register dump:"); ++ for (ix = 0; ix <= 0x7F; ix += 0x04) { ++ if (!(ix & 0x0F)) ++ pr_debug("\n0x%08X: ", (regbase + ix)); ++ pr_debug("%08X ", readl(__io_address(regbase + ix))); ++ } ++ pr_debug("\n"); ++} ++EXPORT_SYMBOL(hi_ahci_reg_dump); ++ ++void hi_ahci_rx_fis_dump(struct ata_link *link, int pmp_port_num) ++{ ++ struct ahci_port_priv *pp = NULL; ++ ++ pp = link->ap->private_data; ++ if (NULL == pp) { ++ pr_debug("Error: pp=NULL\n"); ++ return; ++ } ++ pr_debug("ACHI Received FIS:"); ++ hi_sata_phys_mem_dump((unsigned int)(pp->rx_fis_dma), ++ AHCI_RX_FIS_SZ * pmp_port_num); ++ pr_debug("\n"); ++} ++EXPORT_SYMBOL_GPL(hi_ahci_rx_fis_dump); ++ ++void hi_ata_taskfile_dump(struct ata_taskfile *tf) ++{ ++ if (NULL == tf) { ++ pr_debug("Error: tf=NULL\n"); ++ return; ++ } ++ ++ pr_debug("Taskfile dump:\n"); ++ pr_debug("flags:0x%08lX, protocol:0x%02X, command:0x%02X, device:0x%02X, ctl:0x%02X\n", ++ tf->flags, tf->protocol, tf->command, tf->device, tf->ctl); ++ pr_debug("feature:0x%08X, nsect:0x%02X, lbal:0x%02X, lbam:0x%02X, lbah:0x%02X\n", ++ tf->feature, tf->nsect, tf->lbal, tf->lbam, tf->lbah); ++ pr_debug("hob_feature:0x%08X, hob_nsect:0x%02X, hob_lbal:0x%02X, hob_lbam:0x%02X, hob_lbah:0x%02X\n", ++ tf->hob_feature, tf->hob_nsect, tf->hob_lbal, ++ tf->hob_lbam, tf->hob_lbah); ++} ++EXPORT_SYMBOL_GPL(hi_ata_taskfile_dump); ++ ++static void __hi_ahci_st_md(void __iomem *addr) ++{ ++ unsigned int *addr_v; ++ unsigned int *tmp; ++ unsigned int i; ++ ++ addr_v = (unsigned int *)addr; ++ ++ pr_debug("\n\n"); ++ for (i = 0; i < 16; i++) { ++ tmp = addr_v + i * 4; ++ pr_debug("%8x: %8x %8x %8x %8x\n", ++ (unsigned int)(addr + i * 16), ++ *tmp, *(tmp + 1), *(tmp + 2), *(tmp + 3)); ++ } ++ ++ pr_debug("\n"); ++} ++ ++void hi_ahci_st_dump(void __iomem *port_base) ++{ ++ unsigned int tmp; ++ ++ pr_debug("\n**********Dmac status**********\n"); ++ tmp = readl(port_base + 0x58); ++ pr_debug("txdmac_curr_st:0x%2x\n", (tmp>>24) & 0xf); ++ tmp = readl(port_base + 0x64); ++ pr_debug("rxdmac_curr_st:0x%2x\n", (tmp>>24) & 0xf); ++ tmp = readl(port_base + 0x70); ++ pr_debug("dmac tx fifo:count-0x%x-empty-%x-ful-%x\n", ++ (tmp>>0) & 0xff, ++ (tmp>>16) & 0x1, (tmp>>17) & 0x1); ++ pr_debug("dmac rx fifo:count-0x%x-empty-%x-ful-%x\n", ++ (tmp>>8) & 0xff, ++ (tmp>>18) & 0x1, (tmp>>19) & 0x1); ++ ++ pr_debug("\n"); ++ pr_debug("**********HBA status**********\n"); ++ tmp = readl(port_base + 0x50); ++ pr_debug("pxxx_curr_st:0x%2x ndrx_curr_st:0x%2x\n", ++ (tmp>>24) & 0xf, ++ (tmp>>16) & 0xff); ++ pr_debug("cfis_curr_st:0x%2x piox_curr_st:0x%2x\n", ++ (tmp>>12) & 0xf, ++ (tmp>>8) & 0xf); ++ pr_debug("pmxx_curr_st:0x%2x errx_curr_st:0x%2x\n", ++ (tmp>>4) & 0xf, ++ (tmp>>0) & 0xf); ++ ++ pr_debug("\n"); ++ pr_debug("**********Link status**********\n"); ++ tmp = readl(port_base + 0x54); ++ pr_debug("link_curr_st:0x%2x\n", (tmp>>24) & 0x1f); ++ pr_debug("link tx fifo:count-0x%x-empty-%x-ful-%x\n", ++ (tmp>>0) & 0x1f, ++ (tmp>>5) & 0x1, (tmp>>6) & 0x1); ++ pr_debug("link rx fifo:count-0x%x-empty-%x-ful-%x\n", ++ (tmp>>8) & 0x1f, ++ (tmp>>13) & 0x1, (tmp>>14) & 0x1); ++ pr_debug("link df fifo:count-0x%x-empty-%x-ful-%x\n\n", ++ (tmp>>16) & 0x1f, ++ (tmp>>21) & 0x1, (tmp>>22) & 0x1); ++ ++ pr_debug("**********CMD header**********\n"); ++ tmp = readl(port_base + 0x0); ++ __hi_ahci_st_md(phys_to_virt(tmp)); ++ __hi_ahci_st_md(phys_to_virt(tmp+0x100)); ++ __hi_ahci_st_md(phys_to_virt(tmp+0x200)); ++ __hi_ahci_st_md(phys_to_virt(tmp+0x300)); ++} ++EXPORT_SYMBOL_GPL(hi_ahci_st_dump); ++ +diff --git a/drivers/ata/hi_sata_dbg.h b/drivers/ata/hi_sata_dbg.h +new file mode 100644 +index 0000000..f8398db +--- /dev/null ++++ b/drivers/ata/hi_sata_dbg.h +@@ -0,0 +1,47 @@ ++ ++#ifndef _HI_SATA_DBG_H ++#define _HI_SATA_DBG_H ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/libata.h> ++#include "ahci.h" ++ ++ ++void hi_sata_mem_dump(unsigned int *addr, unsigned int size); ++void hi_sata_phys_mem_dump(unsigned int addr, unsigned int size); ++void hi_ahci_rx_fis_dump(struct ata_link *link, int pmp_port_num); ++void hi_ata_taskfile_dump(struct ata_taskfile *tf); ++void hi_ahci_st_dump(void __iomem *port_base); ++void hi_ahci_reg_dump(void); ++ ++#define HI_AHCI_REG_DUMP(X) \ ++do {\ ++ pr_debug("------------------[ Start ]--------------------\n"); \ ++ pr_debug("Dump AHCI registers at %s %d\n", __func__, __LINE__); \ ++ hi_ahci_reg_dump(); \ ++ pr_debug("------------------[ End ]--------------------\n");\ ++} while (0) ++ ++#define hi_sata_readl(addr) do {\ ++ unsigned int reg = readl((unsigned int)addr); \ ++ pr_debug("HI_AHCI(REG) %s:%d: readl(0x%08X) = 0x%08X\n",\ ++ __func__, __LINE__, (unsigned int)addr, reg); \ ++ reg;\ ++ } while (0) ++ ++#define hi_sata_writel(v, addr) do { writel(v, (unsigned int)addr); \ ++ pr_debug("HI_AHCI(REG) %s:%d: writel(0x%08X) = 0x%08X\n",\ ++ __func__, __LINE__, (unsigned int)addr, \ ++ (unsigned int)(v)); \ ++ } while (0) ++ ++#undef HI_DUMP_AHCI_REG_OPS ++#ifdef HI_DUMP_AHCI_REG_OPS ++#define readl(addr) hi_sata_readl(addr) ++#define write(v, addr) hi_sata_writel(v, addr) ++#endif ++ ++#endif /* _HI_SATA_DBG_H */ ++ ++ ++ +diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c +index de88999..1aefe5c 100644 +--- a/drivers/ata/libahci.c ++++ b/drivers/ata/libahci.c +@@ -46,6 +46,9 @@ + #include "ahci.h" + #include "libata.h" + ++#include <mach/io.h> ++#include <mach/platform.h> ++ + static int ahci_skip_host_reset; + int ahci_ignore_sss; + EXPORT_SYMBOL_GPL(ahci_ignore_sss); +@@ -56,6 +59,33 @@ MODULE_PARM_DESC(skip_host_reset, "skip global host reset (0=don't skip, 1=skip) + module_param_named(ignore_sss, ahci_ignore_sss, int, 0444); + MODULE_PARM_DESC(ignore_sss, "Ignore staggered spinup flag (0=don't ignore, 1=ignore)"); + ++static int fbs_en = CONFIG_HI_SATA_FBS; ++module_param(fbs_en, uint, 0600); ++MODULE_PARM_DESC(fbs_en, "ahci fbs flags (default:1)"); ++ ++#ifdef CONFIG_HI_NANO_PHY_SATA ++extern void hisi_sata_reset_rxtx_assert(unsigned int port_no); ++extern void hisi_sata_reset_rxtx_deassert(unsigned int port_no); ++extern void hi_sata_set_eq(unsigned int port_no); ++extern void hi_sata_eq_recovery(unsigned int port_no); ++#endif ++ ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3521D) \ ++ || defined(CONFIG_ARCH_HI3536C)) ++#define AHCI_TIMEOUT_COUNT 10 ++#define AHCI_POLL_TIMER (20 * HZ) ++ ++struct ata_fbs_ctrl { ++ unsigned int fbs_enable_ctrl; /* fbs enable or disable control switch */ ++ unsigned int fbs_mode_ctrl; /* 1.5G: fbs disable, 3G/6G: fbs enable */ ++ unsigned int fbs_enable_flag; ++ unsigned int fbs_disable_flag; ++ unsigned int fbs_cmd_issue_flag; ++ struct timer_list poll_timer; ++}; ++static struct ata_fbs_ctrl fbs_ctrl[4]; ++#endif + static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + unsigned hints); + static ssize_t ahci_led_show(struct ata_port *ap, char *buf); +@@ -1295,8 +1325,32 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class, + bool fbs_disabled = false; + int rc; + ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3521D) \ ++ || defined(CONFIG_ARCH_HI3536C)) ++ unsigned int port_num = ap->port_no; ++#endif ++ + DPRINTK("ENTER\n"); + ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3521D) \ ++ || defined(CONFIG_ARCH_HI3536C)) ++ if (fbs_ctrl[port_num].fbs_enable_ctrl && ++ (link->pmp == SATA_PMP_CTRL_PORT) && ++ (hpriv->type == ORI_AHCI)) { ++ struct ahci_port_priv *pp = ap->private_data; ++ ++ if (pp->fbs_enabled == false) ++ ahci_enable_fbs(ap); ++ ++ fbs_ctrl[port_num].fbs_enable_flag = 0; ++ fbs_ctrl[port_num].fbs_disable_flag = 0; ++ fbs_ctrl[port_num].fbs_cmd_issue_flag = 0; ++ ++ } ++#endif ++ + /* prepare for SRST (AHCI-1.1 10.4.1) */ + rc = ahci_kick_engine(ap); + if (rc && rc != -EOPNOTSUPP) +@@ -1325,6 +1379,10 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class, + AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY, msecs)) { + rc = -EIO; + reason = "1st FIS failed"; ++#ifdef CONFIG_HI_NANO_PHY_SATA ++ hisi_sata_reset_rxtx_assert(ap->port_no); ++ hisi_sata_reset_rxtx_deassert(ap->port_no); ++#endif + goto fail; + } + +@@ -1471,9 +1529,23 @@ static void ahci_postreset(struct ata_link *link, unsigned int *class) + struct ata_port *ap = link->ap; + void __iomem *port_mmio = ahci_port_base(ap); + u32 new_tmp, tmp; ++#ifdef CONFIG_HI_NANO_PHY_SATA ++ u32 sstatus; ++#endif + + ata_std_postreset(link, class); + ++#ifdef CONFIG_HI_NANO_PHY_SATA ++ /* recovery EQ, when SATA link up 6Gbps for Nano PHY */ ++ if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0 && ++ ((sstatus & 0xf) == 0x3)) { /* link online */ ++ if (((sstatus >> 4) & 0xf) == 3) /* 3: 6Gbps, 2: 3Gbps, 1: 1.5Gbps */ ++ hi_sata_eq_recovery(ap->port_no); ++ } else ++ hi_sata_set_eq(ap->port_no); ++ ++#endif ++ + /* Make sure port's ATAPI bit is set appropriately */ + new_tmp = tmp = readl(port_mmio + PORT_CMD); + if (*class == ATA_DEV_ATAPI) +@@ -1514,6 +1586,70 @@ static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc) + struct ata_port *ap = qc->ap; + struct ahci_port_priv *pp = ap->private_data; + ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3521D) \ ++ || defined(CONFIG_ARCH_HI3536C)) ++ struct ahci_host_priv *hpriv = ap->host->private_data; ++ int is_atapi = ata_is_atapi(qc->tf.protocol); ++ void __iomem *port_mmio = ahci_port_base(ap); ++ unsigned int port_num = ap->port_no; ++ unsigned int cmd_timeout_count; ++ ++ if (fbs_ctrl[port_num].fbs_enable_ctrl && ++ (ap->link.pmp == SATA_PMP_CTRL_PORT) && ++ (hpriv->type == ORI_AHCI)) { ++ if (is_atapi || fbs_ctrl[ap->port_no].fbs_cmd_issue_flag) { ++ mod_timer(&fbs_ctrl[port_num].poll_timer, ++ jiffies + AHCI_POLL_TIMER); ++ ++ if (!fbs_ctrl[port_num].fbs_disable_flag) { ++ cmd_timeout_count = 0; ++ while (readl(port_mmio + PORT_SCR_ACT) ++ || readl(port_mmio ++ + PORT_CMD_ISSUE) ++ || readl(port_mmio ++ + PORT_IRQ_STAT)) { ++ cmd_timeout_count++; ++ if (cmd_timeout_count >= ++ AHCI_TIMEOUT_COUNT) { ++ fbs_ctrl[ap->port_no]. ++ fbs_cmd_issue_flag = 1; ++ return ATA_DEFER_LINK; ++ } ++ } ++ ++ if (pp->fbs_enabled == true) ++ ahci_disable_fbs(ap); ++ ++ ap->excl_link = NULL; ++ ap->nr_active_links = 0; ++ fbs_ctrl[port_num].fbs_disable_flag = 1; ++ fbs_ctrl[port_num].fbs_enable_flag = 0; ++ fbs_ctrl[ap->port_no].fbs_cmd_issue_flag = 0; ++ } ++ } else { ++ if (fbs_ctrl[port_num].fbs_enable_flag) { ++ cmd_timeout_count = 0; ++ while (readl(port_mmio + PORT_SCR_ACT) ++ || readl(port_mmio ++ + PORT_CMD_ISSUE) ++ || readl(port_mmio ++ + PORT_IRQ_STAT)) { ++ cmd_timeout_count++; ++ if (cmd_timeout_count >= ++ AHCI_TIMEOUT_COUNT) { ++ return ATA_DEFER_LINK; ++ } ++ } ++ ++ if (pp->fbs_enabled == false) ++ ahci_enable_fbs(ap); ++ fbs_ctrl[port_num].fbs_enable_flag = 0; ++ fbs_ctrl[port_num].fbs_disable_flag = 0; ++ } ++ } ++ } ++#endif + if (!sata_pmp_attached(ap) || pp->fbs_enabled) + return ata_std_qc_defer(qc); + else +@@ -1558,6 +1694,9 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc) + ahci_fill_cmd_slot(pp, qc->tag, opts); + } + ++#if (!defined(CONFIG_ARCH_HI3531D) \ ++ && !defined(CONFIG_ARCH_HI3536C) \ ++ && !defined(CONFIG_ARCH_HI3521D)) + static void ahci_fbs_dec_intr(struct ata_port *ap) + { + struct ahci_port_priv *pp = ap->private_data; +@@ -1581,6 +1720,7 @@ static void ahci_fbs_dec_intr(struct ata_port *ap) + if (fbs & PORT_FBS_DEC) + dev_err(ap->host->dev, "failed to clear device error\n"); + } ++#endif + + static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) + { +@@ -1684,11 +1824,24 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) + + /* okay, let's hand over to EH */ + +- if (irq_stat & PORT_IRQ_FREEZE) +- ata_port_freeze(ap); +- else if (fbs_need_dec) { ++ if (irq_stat & PORT_IRQ_FREEZE) { ++ if ((irq_stat & PORT_IRQ_IF_ERR) && fbs_need_dec) { ++ ata_link_abort(link); ++#if (!defined(CONFIG_ARCH_HI3531D) \ ++ && !defined(CONFIG_ARCH_HI3536C) \ ++ && !defined(CONFIG_ARCH_HI3521D)) ++ ++ ahci_fbs_dec_intr(ap); ++#endif ++ } else ++ ata_port_freeze(ap); ++ } else if (fbs_need_dec) { + ata_link_abort(link); ++#if (!defined(CONFIG_ARCH_HI3531D) \ ++ && !defined(CONFIG_ARCH_HI3536C) \ ++ && !defined(CONFIG_ARCH_HI3521D)) + ahci_fbs_dec_intr(ap); ++#endif + } else + ata_port_abort(ap); + } +@@ -2092,7 +2245,11 @@ static void ahci_enable_fbs(struct ata_port *ap) + writel(fbs | PORT_FBS_EN, port_mmio + PORT_FBS); + fbs = readl(port_mmio + PORT_FBS); + if (fbs & PORT_FBS_EN) { ++#if (!defined(CONFIG_ARCH_HI3531D) \ ++ && !defined(CONFIG_ARCH_HI3536C) \ ++ && !defined(CONFIG_ARCH_HI3521D)) + dev_info(ap->host->dev, "FBS is enabled\n"); ++#endif + pp->fbs_enabled = true; + pp->fbs_last_dev = -1; /* initialization */ + } else +@@ -2127,11 +2284,20 @@ static void ahci_disable_fbs(struct ata_port *ap) + if (fbs & PORT_FBS_EN) + dev_err(ap->host->dev, "Failed to disable FBS\n"); + else { ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3521D) \ ++ || defined(CONFIG_ARCH_HI3536C)) + dev_info(ap->host->dev, "FBS is disabled\n"); ++#endif + pp->fbs_enabled = false; + } + + hpriv->start_engine(ap); ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3521D) \ ++ || defined(CONFIG_ARCH_HI3536C)) ++ writel(HI_SATA_FIFOTH_VALUE, (port_mmio + HI_SATA_PORT_FIFOTH)); ++#endif + } + + static void ahci_pmp_attach(struct ata_port *ap) +@@ -2140,11 +2306,26 @@ static void ahci_pmp_attach(struct ata_port *ap) + struct ahci_port_priv *pp = ap->private_data; + u32 cmd; + ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3521D) \ ++ || defined(CONFIG_ARCH_HI3536C)) ++ struct ahci_host_priv *hpriv = ap->host->private_data; ++ unsigned int port_num = ap->port_no; ++#endif ++ + cmd = readl(port_mmio + PORT_CMD); + cmd |= PORT_CMD_PMP; + writel(cmd, port_mmio + PORT_CMD); + + ahci_enable_fbs(ap); ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3521D) \ ++ || defined(CONFIG_ARCH_HI3536C)) ++ if (hpriv->type == ORI_AHCI) { ++ if (!fbs_ctrl[port_num].fbs_enable_ctrl) ++ ahci_disable_fbs(ap); ++ } ++#endif + + pp->intr_mask |= PORT_IRQ_BAD_PMP; + +@@ -2211,6 +2392,21 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) + } + #endif + ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3521D) \ ++ || defined(CONFIG_ARCH_HI3536C)) ++static void ahci_poll_func(unsigned long arg) ++{ ++ struct ata_port *ap = (struct ata_port *)arg; ++ unsigned int port_num = ap->port_no; ++ ++ if (ap->link.pmp == SATA_PMP_CTRL_PORT) { ++ fbs_ctrl[port_num].fbs_enable_flag = 1; ++ fbs_ctrl[port_num].fbs_disable_flag = 0; ++ } ++} ++#endif ++ + static int ahci_port_start(struct ata_port *ap) + { + struct ahci_host_priv *hpriv = ap->host->private_data; +@@ -2304,6 +2500,22 @@ static int ahci_port_start(struct ata_port *ap) + + ap->private_data = pp; + ++#if (defined(CONFIG_ARCH_HI3531D) \ ++ || defined(CONFIG_ARCH_HI3521D) \ ++ || defined(CONFIG_ARCH_HI3536C)) ++ if (hpriv->type == ORI_AHCI) { ++ fbs_ctrl[ap->port_no].fbs_enable_ctrl = fbs_en; ++ fbs_ctrl[ap->port_no].fbs_enable_flag = 0; ++ fbs_ctrl[ap->port_no].fbs_disable_flag = 0; ++ fbs_ctrl[ap->port_no].fbs_cmd_issue_flag = 0; ++ ++ init_timer(&fbs_ctrl[ap->port_no].poll_timer); ++ fbs_ctrl[ap->port_no].poll_timer.function = ahci_poll_func; ++ fbs_ctrl[ap->port_no].poll_timer.data = (unsigned long)ap; ++ fbs_ctrl[ap->port_no].poll_timer.expires = jiffies + AHCI_POLL_TIMER; ++ } ++#endif ++ + /* engage engines, captain */ + return ahci_port_resume(ap); + } +diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c +index 0b03f90..4ca5f2d 100644 +--- a/drivers/ata/libahci_platform.c ++++ b/drivers/ata/libahci_platform.c +@@ -24,6 +24,7 @@ + #include <linux/ahci_platform.h> + #include <linux/phy/phy.h> + #include <linux/pm_runtime.h> ++#include <mach/io.h> + #include "ahci.h" + + static void ahci_host_stop(struct ata_host *host); +diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig +index df04227..c7d273b 100644 +--- a/drivers/base/Kconfig ++++ b/drivers/base/Kconfig +@@ -220,8 +220,13 @@ config GENERIC_CPU_DEVICES + bool + default n + ++config HAVE_CPU_AUTOPROBE ++ def_bool ARCH_HAS_CPU_AUTOPROBE ++ + config GENERIC_CPU_AUTOPROBE + bool ++ depends on !ARCH_HAS_CPU_AUTOPROBE ++ select HAVE_CPU_AUTOPROBE + + config SOC_BUS + bool +diff --git a/drivers/base/core.c b/drivers/base/core.c +index 842d047..93ad1c7 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -531,6 +531,7 @@ static DEVICE_ATTR_RO(dev); + + /* /sys/devices/ */ + struct kset *devices_kset; ++EXPORT_SYMBOL(devices_kset); + + /** + * device_create_file - create sysfs attribute file for device. +diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c +index 006b1bc..8a38bf8 100644 +--- a/drivers/base/cpu.c ++++ b/drivers/base/cpu.c +@@ -287,6 +287,7 @@ static void cpu_device_release(struct device *dev) + */ + } + ++#ifdef CONFIG_HAVE_CPU_AUTOPROBE + #ifdef CONFIG_GENERIC_CPU_AUTOPROBE + static ssize_t print_cpu_modalias(struct device *dev, + struct device_attribute *attr, +@@ -309,6 +310,9 @@ static ssize_t print_cpu_modalias(struct device *dev, + buf[n++] = '\n'; + return n; + } ++#else ++#define print_cpu_modalias arch_print_cpu_modalias ++#endif + + static int cpu_uevent(struct device *dev, struct kobj_uevent_env *env) + { +@@ -342,7 +346,7 @@ int register_cpu(struct cpu *cpu, int num) + cpu->dev.offline_disabled = !cpu->hotpluggable; + cpu->dev.offline = !cpu_online(num); + cpu->dev.of_node = of_get_cpu_node(num, NULL); +-#ifdef CONFIG_GENERIC_CPU_AUTOPROBE ++#ifdef CONFIG_HAVE_CPU_AUTOPROBE + cpu->dev.bus->uevent = cpu_uevent; + #endif + cpu->dev.groups = common_cpu_attr_groups; +@@ -366,7 +370,7 @@ struct device *get_cpu_device(unsigned cpu) + } + EXPORT_SYMBOL_GPL(get_cpu_device); + +-#ifdef CONFIG_GENERIC_CPU_AUTOPROBE ++#ifdef CONFIG_HAVE_CPU_AUTOPROBE + static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); + #endif + +@@ -380,7 +384,7 @@ static struct attribute *cpu_root_attrs[] = { + &cpu_attrs[2].attr.attr, + &dev_attr_kernel_max.attr, + &dev_attr_offline.attr, +-#ifdef CONFIG_GENERIC_CPU_AUTOPROBE ++#ifdef CONFIG_HAVE_CPU_AUTOPROBE + &dev_attr_modalias.attr, + #endif + NULL +diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c +index 950fff9..6e57cc1 100644 +--- a/drivers/base/dma-contiguous.c ++++ b/drivers/base/dma-contiguous.c +@@ -195,6 +195,7 @@ struct page *dma_alloc_from_contiguous(struct device *dev, int count, + + return cma_alloc(dev_get_cma_area(dev), count, align); + } ++EXPORT_SYMBOL(dma_alloc_from_contiguous); + + /** + * dma_release_from_contiguous() - release allocated pages +@@ -211,6 +212,7 @@ bool dma_release_from_contiguous(struct device *dev, struct page *pages, + { + return cma_release(dev_get_cma_area(dev), pages, count); + } ++EXPORT_SYMBOL(dma_release_from_contiguous); + + /* + * Support for reserved memory regions defined in device tree +diff --git a/drivers/base/platform.c b/drivers/base/platform.c +index 360272c..43d6530 100644 +--- a/drivers/base/platform.c ++++ b/drivers/base/platform.c +@@ -731,7 +731,7 @@ static ssize_t driver_override_store(struct device *dev, + const char *buf, size_t count) + { + struct platform_device *pdev = to_platform_device(dev); +- char *driver_override, *old = pdev->driver_override, *cp; ++ char *driver_override, *old, *cp; + + if (count > PATH_MAX) + return -EINVAL; +@@ -744,6 +744,9 @@ static ssize_t driver_override_store(struct device *dev, + if (cp) + *cp = '\0'; + ++ device_lock(dev); ++ old = pdev->driver_override; ++ + if (strlen(driver_override)) { + pdev->driver_override = driver_override; + } else { +@@ -751,6 +754,8 @@ static ssize_t driver_override_store(struct device *dev, + pdev->driver_override = NULL; + } + ++ device_unlock(dev); ++ + kfree(old); + + return count; +@@ -760,8 +765,12 @@ static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, char *buf) + { + struct platform_device *pdev = to_platform_device(dev); ++ ssize_t len; + +- return sprintf(buf, "%s\n", pdev->driver_override); ++ device_lock(dev); ++ len = sprintf(buf, "%s\n", pdev->driver_override); ++ device_unlock(dev); ++ return len; + } + static DEVICE_ATTR_RW(driver_override); + +diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c +index 9717d5f..bf957ca 100644 +--- a/drivers/base/power/main.c ++++ b/drivers/base/power/main.c +@@ -32,10 +32,13 @@ + #include <linux/cpufreq.h> + #include <linux/cpuidle.h> + #include <linux/timer.h> ++#include <linux/wakeup_reason.h> + + #include "../base.h" + #include "power.h" +- ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++extern int pm_notifier_call_chain(unsigned long val); ++#endif + typedef int (*pm_callback_t)(struct device *); + + /* +@@ -58,6 +61,12 @@ struct suspend_stats suspend_stats; + static DEFINE_MUTEX(dpm_list_mtx); + static pm_message_t pm_transition; + ++static void dpm_drv_timeout(unsigned long data); ++struct dpm_drv_wd_data { ++ struct device *dev; ++ struct task_struct *tsk; ++}; ++ + static int async_error; + + static char *pm_verb(int event) +@@ -828,6 +837,30 @@ static void async_resume(void *data, async_cookie_t cookie) + } + + /** ++ * dpm_drv_timeout - Driver suspend / resume watchdog handler ++ * @data: struct device which timed out ++ * ++ * Called when a driver has timed out suspending or resuming. ++ * There's not much we can do here to recover so ++ * BUG() out for a crash-dump ++ * ++ */ ++static void dpm_drv_timeout(unsigned long data) ++{ ++ struct dpm_drv_wd_data *wd_data = (void *)data; ++ struct device *dev = wd_data->dev; ++ struct task_struct *tsk = wd_data->tsk; ++ ++ printk(KERN_EMERG "**** DPM device timeout: %s (%s)\n", dev_name(dev), ++ (dev->driver ? dev->driver->name : "no driver")); ++ ++ printk(KERN_EMERG "dpm suspend stack:\n"); ++ show_stack(tsk, NULL); ++ ++ BUG(); ++} ++ ++/** + * dpm_resume - Execute "resume" callbacks for non-sysdev devices. + * @state: PM transition of the system being carried out. + * +@@ -973,6 +1006,9 @@ void dpm_complete(pm_message_t state) + */ + void dpm_resume_end(pm_message_t state) + { ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ pm_notifier_call_chain(PM_RESUME_DEVICE_PREPARE); ++#endif + dpm_resume(state); + dpm_complete(state); + } +@@ -1336,6 +1372,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) + pm_callback_t callback = NULL; + char *info = NULL; + int error = 0; ++ struct timer_list timer; ++ struct dpm_drv_wd_data data; ++ char suspend_abort[MAX_SUSPEND_ABORT_LEN]; + DECLARE_DPM_WATCHDOG_ON_STACK(wd); + + dpm_wait_for_children(dev, async); +@@ -1353,12 +1392,23 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) + pm_wakeup_event(dev, 0); + + if (pm_wakeup_pending()) { ++ pm_get_active_wakeup_sources(suspend_abort, ++ MAX_SUSPEND_ABORT_LEN); ++ log_suspend_abort_reason(suspend_abort); + async_error = -EBUSY; + goto Complete; + } + + if (dev->power.syscore) + goto Complete; ++ ++ data.dev = dev; ++ data.tsk = get_current(); ++ init_timer_on_stack(&timer); ++ timer.expires = jiffies + HZ * 12; ++ timer.function = dpm_drv_timeout; ++ timer.data = (unsigned long)&data; ++ add_timer(&timer); + + if (dev->power.direct_complete) { + if (pm_runtime_status_suspended(dev)) { +@@ -1439,6 +1489,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) + device_unlock(dev); + dpm_watchdog_clear(&wd); + ++ del_timer_sync(&timer); ++ destroy_timer_on_stack(&timer); ++ + Complete: + complete_all(&dev->power.completion); + if (error) +@@ -1661,6 +1714,10 @@ int dpm_suspend_start(pm_message_t state) + dpm_save_failed_step(SUSPEND_PREPARE); + } else + error = dpm_suspend(state); ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ if (!error) ++ pm_notifier_call_chain(PM_POST_DEVICE_SUSPEND); ++#endif + return error; + } + EXPORT_SYMBOL_GPL(dpm_suspend_start); +diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c +index c2744b3..f1cf53a 100644 +--- a/drivers/base/power/wakeup.c ++++ b/drivers/base/power/wakeup.c +@@ -14,6 +14,7 @@ + #include <linux/suspend.h> + #include <linux/seq_file.h> + #include <linux/debugfs.h> ++#include <linux/types.h> + #include <trace/events/power.h> + + #include "power.h" +@@ -668,6 +669,37 @@ void pm_wakeup_event(struct device *dev, unsigned int msec) + } + EXPORT_SYMBOL_GPL(pm_wakeup_event); + ++void pm_get_active_wakeup_sources(char *pending_wakeup_source, size_t max) ++{ ++ struct wakeup_source *ws, *last_active_ws = NULL; ++ int len = 0; ++ bool active = false; ++ ++ rcu_read_lock(); ++ list_for_each_entry_rcu(ws, &wakeup_sources, entry) { ++ if (ws->active) { ++ if (!active) ++ len += scnprintf(pending_wakeup_source, max, ++ "Pending Wakeup Sources: "); ++ len += scnprintf(pending_wakeup_source + len, max - len, ++ "%s ", ws->name); ++ active = true; ++ } else if (!active && ++ (!last_active_ws || ++ ktime_to_ns(ws->last_time) > ++ ktime_to_ns(last_active_ws->last_time))) { ++ last_active_ws = ws; ++ } ++ } ++ if (!active && last_active_ws) { ++ scnprintf(pending_wakeup_source, max, ++ "Last active Wakeup Source: %s", ++ last_active_ws->name); ++ } ++ rcu_read_unlock(); ++} ++EXPORT_SYMBOL_GPL(pm_get_active_wakeup_sources); ++ + void pm_print_active_wakeup_sources(void) + { + struct wakeup_source *ws; +diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c +index 8d98a32..96c34a9 100644 +--- a/drivers/base/syscore.c ++++ b/drivers/base/syscore.c +@@ -11,6 +11,7 @@ + #include <linux/module.h> + #include <linux/suspend.h> + #include <trace/events/power.h> ++#include <linux/wakeup_reason.h> + + static LIST_HEAD(syscore_ops_list); + static DEFINE_MUTEX(syscore_ops_lock); +@@ -75,6 +76,8 @@ int syscore_suspend(void) + return 0; + + err_out: ++ log_suspend_abort_reason("System core suspend callback %pF failed", ++ ops->suspend); + pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend); + + list_for_each_entry_continue(ops, &syscore_ops_list, node) +diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig +index efefd12..b96a3a5 100644 +--- a/drivers/char/Kconfig ++++ b/drivers/char/Kconfig +@@ -6,6 +6,19 @@ menu "Character devices" + + source "drivers/tty/Kconfig" + ++config DEVMEM ++ bool "Memory device driver" ++ default y ++ help ++ The memory driver provides two character devices, mem and kmem, which ++ provide access to the system's memory. The mem device is a view of ++ physical memory, and each byte in the device corresponds to the ++ matching physical address. The kmem device is the same as mem, but ++ the addresses correspond to the kernel's virtual address space rather ++ than physical memory. These devices are standard parts of a Linux ++ system and most users should say Y here. You might say N if very ++ security conscience or memory is tight. ++ + config DEVKMEM + bool "/dev/kmem virtual device support" + default y +@@ -579,6 +592,10 @@ config DEVPORT + depends on ISA || PCI + default y + ++config DCC_TTY ++ tristate "DCC tty driver" ++ depends on ARM ++ + source "drivers/s390/char/Kconfig" + + config MSM_SMD_PKT +diff --git a/drivers/char/Makefile b/drivers/char/Makefile +index d06cde2..e38fb5b 100644 +--- a/drivers/char/Makefile ++++ b/drivers/char/Makefile +@@ -55,6 +55,7 @@ obj-$(CONFIG_PCMCIA) += pcmcia/ + obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o + obj-$(CONFIG_TCG_TPM) += tpm/ + ++obj-$(CONFIG_DCC_TTY) += dcc_tty.o + obj-$(CONFIG_PS3_FLASH) += ps3flash.o + + obj-$(CONFIG_JS_RTC) += js-rtc.o +diff --git a/drivers/char/dcc_tty.c b/drivers/char/dcc_tty.c +new file mode 100644 +index 0000000..0a62d41 +--- /dev/null ++++ b/drivers/char/dcc_tty.c +@@ -0,0 +1,326 @@ ++/* drivers/char/dcc_tty.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/platform_device.h> ++#include <linux/delay.h> ++#include <linux/console.h> ++#include <linux/hrtimer.h> ++#include <linux/tty.h> ++#include <linux/tty_driver.h> ++#include <linux/tty_flip.h> ++ ++MODULE_DESCRIPTION("DCC TTY Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("1.0"); ++ ++DEFINE_SPINLOCK(g_dcc_tty_lock); ++static struct hrtimer g_dcc_timer; ++static char g_dcc_buffer[16]; ++static int g_dcc_buffer_head; ++static int g_dcc_buffer_count; ++static unsigned g_dcc_write_delay_usecs = 1; ++static struct tty_driver *g_dcc_tty_driver; ++static struct tty_struct *g_dcc_tty; ++static int g_dcc_tty_open_count; ++ ++static void dcc_poll_locked(void) ++{ ++ char ch; ++ int rch; ++ int written; ++ ++ while (g_dcc_buffer_count) { ++ ch = g_dcc_buffer[g_dcc_buffer_head]; ++ asm( ++ "mrc 14, 0, r15, c0, c1, 0\n" ++ "mcrcc 14, 0, %1, c0, c5, 0\n" ++ "movcc %0, #1\n" ++ "movcs %0, #0\n" ++ : "=r" (written) ++ : "r" (ch) ++ ); ++ if (written) { ++ if (ch == '\n') ++ g_dcc_buffer[g_dcc_buffer_head] = '\r'; ++ else { ++ g_dcc_buffer_head = (g_dcc_buffer_head + 1) % ARRAY_SIZE(g_dcc_buffer); ++ g_dcc_buffer_count--; ++ if (g_dcc_tty) ++ tty_wakeup(g_dcc_tty); ++ } ++ g_dcc_write_delay_usecs = 1; ++ } else { ++ if (g_dcc_write_delay_usecs > 0x100) ++ break; ++ g_dcc_write_delay_usecs <<= 1; ++ udelay(g_dcc_write_delay_usecs); ++ } ++ } ++ ++ if (g_dcc_tty && !test_bit(TTY_THROTTLED, &g_dcc_tty->flags)) { ++ asm( ++ "mrc 14, 0, %0, c0, c1, 0\n" ++ "tst %0, #(1 << 30)\n" ++ "moveq %0, #-1\n" ++ "mrcne 14, 0, %0, c0, c5, 0\n" ++ : "=r" (rch) ++ ); ++ if (rch >= 0) { ++ ch = rch; ++ tty_insert_flip_string(g_dcc_tty->port, &ch, 1); ++ tty_flip_buffer_push(g_dcc_tty->port); ++ } ++ } ++ ++ ++ if (g_dcc_buffer_count) ++ hrtimer_start(&g_dcc_timer, ktime_set(0, g_dcc_write_delay_usecs * NSEC_PER_USEC), HRTIMER_MODE_REL); ++ else ++ hrtimer_start(&g_dcc_timer, ktime_set(0, 20 * NSEC_PER_MSEC), HRTIMER_MODE_REL); ++} ++ ++static int dcc_tty_open(struct tty_struct * tty, struct file * filp) ++{ ++ int ret; ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); ++ if (g_dcc_tty == NULL || g_dcc_tty == tty) { ++ g_dcc_tty = tty; ++ g_dcc_tty_open_count++; ++ ret = 0; ++ } else ++ ret = -EBUSY; ++ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); ++ ++ printk("dcc_tty_open, tty %p, f_flags %x, returned %d\n", tty, filp->f_flags, ret); ++ ++ return ret; ++} ++ ++static void dcc_tty_close(struct tty_struct * tty, struct file * filp) ++{ ++ printk("dcc_tty_close, tty %p, f_flags %x\n", tty, filp->f_flags); ++ if (g_dcc_tty == tty) { ++ if (--g_dcc_tty_open_count == 0) ++ g_dcc_tty = NULL; ++ } ++} ++ ++static int dcc_write(const unsigned char *buf_start, int count) ++{ ++ const unsigned char *buf = buf_start; ++ unsigned long irq_flags; ++ int copy_len; ++ int space_left; ++ int tail; ++ ++ if (count < 1) ++ return 0; ++ ++ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); ++ do { ++ tail = (g_dcc_buffer_head + g_dcc_buffer_count) % ARRAY_SIZE(g_dcc_buffer); ++ copy_len = ARRAY_SIZE(g_dcc_buffer) - tail; ++ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; ++ if (copy_len > space_left) ++ copy_len = space_left; ++ if (copy_len > count) ++ copy_len = count; ++ memcpy(&g_dcc_buffer[tail], buf, copy_len); ++ g_dcc_buffer_count += copy_len; ++ buf += copy_len; ++ count -= copy_len; ++ if (copy_len < count && copy_len < space_left) { ++ space_left -= copy_len; ++ copy_len = count; ++ if (copy_len > space_left) { ++ copy_len = space_left; ++ } ++ memcpy(g_dcc_buffer, buf, copy_len); ++ buf += copy_len; ++ count -= copy_len; ++ g_dcc_buffer_count += copy_len; ++ } ++ dcc_poll_locked(); ++ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; ++ } while(count && space_left); ++ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); ++ return buf - buf_start; ++} ++ ++static int dcc_tty_write(struct tty_struct * tty, const unsigned char *buf, int count) ++{ ++ int ret; ++ /* printk("dcc_tty_write %p, %d\n", buf, count); */ ++ ret = dcc_write(buf, count); ++ if (ret != count) ++ printk("dcc_tty_write %p, %d, returned %d\n", buf, count, ret); ++ return ret; ++} ++ ++static int dcc_tty_write_room(struct tty_struct *tty) ++{ ++ int space_left; ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); ++ space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; ++ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); ++ return space_left; ++} ++ ++static int dcc_tty_chars_in_buffer(struct tty_struct *tty) ++{ ++ int ret; ++ asm( ++ "mrc 14, 0, %0, c0, c1, 0\n" ++ "mov %0, %0, LSR #30\n" ++ "and %0, %0, #1\n" ++ : "=r" (ret) ++ ); ++ return ret; ++} ++ ++static void dcc_tty_unthrottle(struct tty_struct * tty) ++{ ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); ++ dcc_poll_locked(); ++ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); ++} ++ ++static enum hrtimer_restart dcc_tty_timer_func(struct hrtimer *timer) ++{ ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); ++ dcc_poll_locked(); ++ spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); ++ return HRTIMER_NORESTART; ++} ++ ++void dcc_console_write(struct console *co, const char *b, unsigned count) ++{ ++#if 1 ++ dcc_write(b, count); ++#else ++ /* blocking printk */ ++ while (count > 0) { ++ int written; ++ written = dcc_write(b, count); ++ if (written) { ++ b += written; ++ count -= written; ++ } ++ } ++#endif ++} ++ ++static struct tty_driver *dcc_console_device(struct console *c, int *index) ++{ ++ *index = 0; ++ return g_dcc_tty_driver; ++} ++ ++static int __init dcc_console_setup(struct console *co, char *options) ++{ ++ if (co->index != 0) ++ return -ENODEV; ++ return 0; ++} ++ ++ ++static struct console dcc_console = ++{ ++ .name = "ttyDCC", ++ .write = dcc_console_write, ++ .device = dcc_console_device, ++ .setup = dcc_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++}; ++ ++static struct tty_operations dcc_tty_ops = { ++ .open = dcc_tty_open, ++ .close = dcc_tty_close, ++ .write = dcc_tty_write, ++ .write_room = dcc_tty_write_room, ++ .chars_in_buffer = dcc_tty_chars_in_buffer, ++ .unthrottle = dcc_tty_unthrottle, ++}; ++ ++static int __init dcc_tty_init(void) ++{ ++ int ret; ++ ++ hrtimer_init(&g_dcc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ g_dcc_timer.function = dcc_tty_timer_func; ++ ++ g_dcc_tty_driver = alloc_tty_driver(1); ++ if (!g_dcc_tty_driver) { ++ printk(KERN_ERR "dcc_tty_probe: alloc_tty_driver failed\n"); ++ ret = -ENOMEM; ++ goto err_alloc_tty_driver_failed; ++ } ++ g_dcc_tty_driver->owner = THIS_MODULE; ++ g_dcc_tty_driver->driver_name = "dcc"; ++ g_dcc_tty_driver->name = "ttyDCC"; ++ g_dcc_tty_driver->major = 0; // auto assign ++ g_dcc_tty_driver->minor_start = 0; ++ g_dcc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; ++ g_dcc_tty_driver->subtype = SERIAL_TYPE_NORMAL; ++ g_dcc_tty_driver->init_termios = tty_std_termios; ++ g_dcc_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; ++ tty_set_operations(g_dcc_tty_driver, &dcc_tty_ops); ++ ret = tty_register_driver(g_dcc_tty_driver); ++ if (ret) { ++ printk(KERN_ERR "dcc_tty_probe: tty_register_driver failed, %d\n", ret); ++ goto err_tty_register_driver_failed; ++ } ++ tty_register_device(g_dcc_tty_driver, 0, NULL); ++ ++ register_console(&dcc_console); ++ hrtimer_start(&g_dcc_timer, ktime_set(0, 0), HRTIMER_MODE_REL); ++ ++ return 0; ++ ++err_tty_register_driver_failed: ++ put_tty_driver(g_dcc_tty_driver); ++ g_dcc_tty_driver = NULL; ++err_alloc_tty_driver_failed: ++ return ret; ++} ++ ++static void __exit dcc_tty_exit(void) ++{ ++ int ret; ++ ++ tty_unregister_device(g_dcc_tty_driver, 0); ++ ret = tty_unregister_driver(g_dcc_tty_driver); ++ if (ret < 0) { ++ printk(KERN_ERR "dcc_tty_remove: tty_unregister_driver failed, %d\n", ret); ++ } else { ++ put_tty_driver(g_dcc_tty_driver); ++ } ++ g_dcc_tty_driver = NULL; ++} ++ ++module_init(dcc_tty_init); ++module_exit(dcc_tty_exit); ++ ++ +diff --git a/drivers/char/mem.c b/drivers/char/mem.c +index 524b707..f6ea960 100644 +--- a/drivers/char/mem.c ++++ b/drivers/char/mem.c +@@ -58,6 +58,7 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) + } + #endif + ++#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) + #ifdef CONFIG_STRICT_DEVMEM + static inline int range_is_allowed(unsigned long pfn, unsigned long size) + { +@@ -83,7 +84,9 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size) + return 1; + } + #endif ++#endif + ++#ifdef CONFIG_DEVMEM + void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr) + { + } +@@ -216,6 +219,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf, + *ppos += written; + return written; + } ++#endif /* CONFIG_DEVMEM */ ++ ++#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) + + int __weak phys_mem_access_prot_allowed(struct file *file, + unsigned long pfn, unsigned long size, pgprot_t *vma_prot) +@@ -338,6 +344,49 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma) + return 0; + } + ++static int mmap_venc(struct file *file, struct vm_area_struct *vma) ++{ ++ size_t size = (vma->vm_end - vma->vm_start)>>1; ++ ++ if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) ++ return -EINVAL; ++ ++ if (!private_mapping_ok(vma)) ++ return -ENOSYS; ++ ++ if (!range_is_allowed(vma->vm_pgoff, size)) ++ return -EPERM; ++ ++ if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size, ++ &vma->vm_page_prot)) ++ return -EINVAL; ++ ++ vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, ++ size, ++ vma->vm_page_prot); ++ ++ vma->vm_ops = &mmap_mem_ops; ++ ++ /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */ ++ if (remap_pfn_range(vma, ++ vma->vm_start, ++ vma->vm_pgoff, ++ size, ++ vma->vm_page_prot)) ++ return -EAGAIN; ++ ++ if (remap_pfn_range(vma, ++ vma->vm_start + size, ++ vma->vm_pgoff, ++ size, ++ vma->vm_page_prot)) ++ return -EAGAIN; ++ ++ return 0; ++} ++ ++#endif /* CONFIG_DEVMEM */ ++ + #ifdef CONFIG_DEVKMEM + static int mmap_kmem(struct file *file, struct vm_area_struct *vma) + { +@@ -667,6 +716,8 @@ static loff_t null_lseek(struct file *file, loff_t offset, int orig) + return file->f_pos = 0; + } + ++#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT) ++ + /* + * The memory devices use the full 32/64 bits of the offset, and so we cannot + * check against negative addresses: they are ok. The return value is weird, +@@ -700,10 +751,14 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig) + return ret; + } + ++#endif ++ ++#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT) + static int open_port(struct inode *inode, struct file *filp) + { + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; + } ++#endif + + #define zero_lseek null_lseek + #define full_lseek null_lseek +@@ -712,6 +767,7 @@ static int open_port(struct inode *inode, struct file *filp) + #define open_mem open_port + #define open_kmem open_mem + ++#ifdef CONFIG_DEVMEM + static const struct file_operations mem_fops = { + .llseek = memory_lseek, + .read = read_mem, +@@ -721,6 +777,17 @@ static const struct file_operations mem_fops = { + .get_unmapped_area = get_unmapped_area_mem, + }; + ++ ++static const struct file_operations venc_fops = { ++ .llseek = memory_lseek, ++ .read = read_mem, ++ .write = write_mem, ++ .mmap = mmap_venc, ++ .open = open_mem, ++ .get_unmapped_area = get_unmapped_area_mem, ++}; ++#endif ++ + #ifdef CONFIG_DEVKMEM + static const struct file_operations kmem_fops = { + .llseek = memory_lseek, +@@ -782,7 +849,9 @@ static const struct memdev { + const struct file_operations *fops; + struct backing_dev_info *dev_info; + } devlist[] = { ++#ifdef CONFIG_DEVMEM + [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi }, ++#endif + #ifdef CONFIG_DEVKMEM + [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi }, + #endif +@@ -797,6 +866,9 @@ static const struct memdev { + #ifdef CONFIG_PRINTK + [11] = { "kmsg", 0644, &kmsg_fops, NULL }, + #endif ++#ifdef CONFIG_DEVMEM ++ [13] = { "vencmem", 0, &venc_fops, &directly_mappable_cdev_bdi }, ++#endif + }; + + static int memory_open(struct inode *inode, struct file *filp) +diff --git a/drivers/char/random.c b/drivers/char/random.c +index 9cd6968..f41b114 100644 +--- a/drivers/char/random.c ++++ b/drivers/char/random.c +@@ -661,7 +661,8 @@ retry: + if (r == &nonblocking_pool) { + prandom_reseed_late(); + wake_up_interruptible(&urandom_init_wait); +- pr_notice("random: %s pool is initialized\n", r->name); ++ /* Don't want to print this sentence and annotate it! 2015-12-23 */ ++ /*pr_notice("random: %s pool is initialized\n", r->name);*/ + } + } + +diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig +index 455fd17..dd112f0 100644 +--- a/drivers/clk/Kconfig ++++ b/drivers/clk/Kconfig +@@ -134,6 +134,24 @@ config COMMON_CLK_PXA + ---help--- + Sypport for the Marvell PXA SoC. + ++config COMMON_CLK_FREQ_STATS_ACCOUNTING ++ bool "Enable clock frequency stats accounting" ++ depends on COMMON_CLK ++ depends on DEBUG_FS ++ ---help--- ++ Allows accounting of the time spent by various clocks in each ++ of its operating frequency. The stats get reported as a part ++ of clk_summary. Would be be useful in finding out which ++ components are running at what power states to debug ++ battery consumption issues. ++ ++config COMMON_CLK_BEGIN_ACCOUNTING_FROM_BOOT ++ bool "Start clock frequency stats accounting from boot" ++ depends on COMMON_CLK_FREQ_STATS_ACCOUNTING ++ ---help--- ++ Enabling this option starts the frequency accounting right from ++ the boot. ++ + source "drivers/clk/qcom/Kconfig" + + endmenu +diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile +index d5fba5b..62d74f2 100644 +--- a/drivers/clk/Makefile ++++ b/drivers/clk/Makefile +@@ -46,6 +46,10 @@ obj-$(CONFIG_ARCH_BERLIN) += berlin/ + obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ + obj-$(CONFIG_ARCH_HIP04) += hisilicon/ + obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ ++obj-$(CONFIG_ARCH_HISI) += hisilicon/ ++obj-$(CONFIG_ARCH_HI3536C) += hisilicon/ ++obj-$(CONFIG_ARCH_HI3531D) += hisilicon/ ++obj-$(CONFIG_ARCH_HI3521D) += hisilicon/ + obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ + ifeq ($(CONFIG_COMMON_CLK), y) + obj-$(CONFIG_ARCH_MMP) += mmp/ +diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c +index 7d74830..d2f0a56 100644 +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -114,6 +114,134 @@ static struct hlist_head *orphan_list[] = { + NULL, + }; + ++#ifdef CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING ++ ++#ifdef CONFIG_COMMON_CLK_BEGIN_ACCOUNTING_FROM_BOOT ++static bool freq_stats_on = true; ++#else ++static bool freq_stats_on; ++#endif /*CONFIG_COMMON_CLK_BEGIN_ACCOUNTING_FROM_BOOT*/ ++ ++static void free_tree(struct rb_node *node) ++{ ++ struct freq_stats *this; ++ ++ if (!node) ++ return; ++ ++ free_tree(node->rb_left); ++ free_tree(node->rb_right); ++ ++ this = rb_entry(node, struct freq_stats, node); ++ kfree(this); ++} ++ ++static struct freq_stats *freq_stats_insert(struct rb_root *freq_stats_table, ++ unsigned long rate) ++{ ++ struct rb_node **new = &(freq_stats_table->rb_node), *parent = NULL; ++ struct freq_stats *this; ++ ++ /* Figure out where to put new node */ ++ while (*new) { ++ this = rb_entry(*new, struct freq_stats, node); ++ parent = *new; ++ ++ if (rate < this->rate) ++ new = &((*new)->rb_left); ++ else if (rate > this->rate) ++ new = &((*new)->rb_right); ++ else ++ return this; ++ } ++ ++ this = kzalloc(sizeof(*this), GFP_ATOMIC); ++ this->rate = rate; ++ ++ /* Add new node and rebalance tree. */ ++ rb_link_node(&this->node, parent, new); ++ rb_insert_color(&this->node, freq_stats_table); ++ ++ return this; ++} ++ ++static void generic_print_freq_stats_table(struct seq_file *m, ++ struct clk *clk, ++ bool indent, int level) ++{ ++ struct rb_node *pos; ++ struct freq_stats *cur; ++ ++ if (indent) ++ seq_printf(m, "%*s*%s%20s", level * 3 + 1, "", ++ !clk->current_freq_stats ? "[" : "", ++ "default_freq"); ++ else ++ seq_printf(m, "%2s%20s", !clk->current_freq_stats ? "[" : "", ++ "default_freq"); ++ ++ if (!clk->current_freq_stats && !ktime_equal(clk->start_time, ++ ktime_set(0, 0))) ++ seq_printf(m, "%40llu", ++ ktime_to_ms(ktime_add(clk->default_freq_time, ++ ktime_sub(ktime_get(), clk->start_time)))); ++ else ++ seq_printf(m, "%40llu", ktime_to_ms(clk->default_freq_time)); ++ ++ if (!clk->current_freq_stats) ++ seq_puts(m, "]"); ++ ++ seq_puts(m, "\n"); ++ ++ for (pos = rb_first(&clk->freq_stats_table); pos; pos = rb_next(pos)) { ++ cur = rb_entry(pos, typeof(*cur), node); ++ ++ if (indent) ++ seq_printf(m, "%*s*%s%20lu", level * 3 + 1, "", ++ cur->rate == clk->rate ? "[" : "", cur->rate); ++ else ++ seq_printf(m, "%2s%20lu", cur->rate == clk->rate ? ++ "[" : "", cur->rate); ++ ++ if (cur->rate == clk->rate && !ktime_equal(clk->start_time, ++ ktime_set(0, 0))) ++ seq_printf(m, "%40llu", ++ ktime_to_ms(ktime_add(cur->time_spent, ++ ktime_sub(ktime_get(), clk->start_time)))); ++ else ++ seq_printf(m, "%40llu", ktime_to_ms(cur->time_spent)); ++ ++ if (cur->rate == clk->rate) ++ seq_puts(m, "]"); ++ seq_puts(m, "\n"); ++ } ++} ++ ++static int clock_print_freq_stats_table(struct seq_file *m, void *unused) ++{ ++ struct clk *clk = m->private; ++ ++ if (!(clk->flags & CLK_GET_RATE_NOCACHE)) ++ generic_print_freq_stats_table(m, clk, false, 0); ++ ++ return 0; ++} ++ ++static int freq_stats_table_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, clock_print_freq_stats_table, ++ inode->i_private); ++} ++ ++static const struct file_operations freq_stats_table_fops = { ++ .open = freq_stats_table_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++#endif /*CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING*/ ++ ++ + static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) + { + if (!c) +@@ -124,6 +252,12 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) + 30 - level * 3, c->name, + c->enable_count, c->prepare_count, clk_get_rate(c), + clk_get_accuracy(c), clk_get_phase(c)); ++ ++#ifdef CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING ++ if (!(c->flags & CLK_GET_RATE_NOCACHE)) ++ generic_print_freq_stats_table(s, c, true, level); ++#endif /*CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING*/ ++ + } + + static void clk_summary_show_subtree(struct seq_file *s, struct clk *c, +@@ -240,6 +374,78 @@ static const struct file_operations clk_dump_fops = { + .release = single_release, + }; + ++#ifdef CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING ++static int freq_stats_get(void *unused, u64 *val) ++{ ++ *val = freq_stats_on; ++ return 0; ++} ++ ++static void clk_traverse_subtree(struct clk *clk, int freq_stats_on) ++{ ++ struct clk *child; ++ struct rb_node *node; ++ ++ if (!clk) ++ return; ++ ++ if (freq_stats_on) { ++ for (node = rb_first(&clk->freq_stats_table); ++ node; node = rb_next(node)) ++ rb_entry(node, struct freq_stats, node)->time_spent = ++ ktime_set(0, 0); ++ ++ clk->current_freq_stats = freq_stats_insert( ++ &clk->freq_stats_table, ++ clk_get_rate(clk)); ++ ++ if (clk->enable_count > 0) ++ clk->start_time = ktime_get(); ++ } else { ++ if (clk->enable_count > 0) { ++ if (!clk->current_freq_stats) ++ clk->default_freq_time = ++ ktime_add(clk->default_freq_time, ++ ktime_sub(ktime_get(), clk->start_time)); ++ else ++ clk->current_freq_stats->time_spent = ++ ktime_add(clk->current_freq_stats->time_spent, ++ ktime_sub(ktime_get(), clk->start_time)); ++ ++ clk->start_time = ktime_set(0, 0); ++ } ++ } ++ hlist_for_each_entry(child, &clk->children, child_node) ++ clk_traverse_subtree(child, freq_stats_on); ++} ++ ++static int freq_stats_set(void *data, u64 val) ++{ ++ struct clk *c; ++ unsigned long flags; ++ struct hlist_head **lists = (struct hlist_head **)data; ++ ++ clk_prepare_lock(); ++ flags = clk_enable_lock(); ++ ++ if (val == 0) ++ freq_stats_on = 0; ++ else ++ freq_stats_on = 1; ++ ++ for (; *lists; lists++) ++ hlist_for_each_entry(c, *lists, child_node) ++ clk_traverse_subtree(c, freq_stats_on); ++ ++ clk_enable_unlock(flags); ++ clk_prepare_unlock(); ++ ++ return 0; ++} ++DEFINE_SIMPLE_ATTRIBUTE(freq_stats_fops, freq_stats_get, ++ freq_stats_set, "%llu\n"); ++#endif /*CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING*/ ++ + static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) + { + struct dentry *d; +@@ -291,6 +497,14 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) + if (!d) + goto err_out; + ++#ifdef CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING ++ d = debugfs_create_file("frequency_stats_table", S_IRUGO, clk->dentry, ++ clk, &freq_stats_table_fops); ++ ++ if (!d) ++ goto err_out; ++#endif /*CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING*/ ++ + if (clk->ops->debug_init) { + ret = clk->ops->debug_init(clk->hw, clk->dentry); + if (ret) +@@ -403,6 +617,13 @@ static int __init clk_debug_init(void) + if (!d) + return -ENOMEM; + ++#ifdef CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING ++ d = debugfs_create_file("freq_stats_on", S_IRUGO|S_IWUSR, ++ rootdir, &all_lists, &freq_stats_fops); ++ if (!d) ++ return -ENOMEM; ++#endif /*CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING*/ ++ + mutex_lock(&clk_debug_lock); + hlist_for_each_entry(clk, &clk_debug_list, debug_node) + clk_debug_create_one(clk, rootdir); +@@ -852,6 +1073,22 @@ static void __clk_disable(struct clk *clk) + if (clk->ops->disable) + clk->ops->disable(clk->hw); + ++#ifdef CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING ++ ++ if (freq_stats_on) { ++ if (!clk->current_freq_stats) ++ clk->default_freq_time = ++ ktime_add(clk->default_freq_time, ++ ktime_sub(ktime_get(), clk->start_time)); ++ else ++ clk->current_freq_stats->time_spent = ++ ktime_add(clk->current_freq_stats->time_spent, ++ ktime_sub(ktime_get(), clk->start_time)); ++ ++ clk->start_time = ktime_set(0, 0); ++ } ++#endif /*CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING*/ ++ + __clk_disable(clk->parent); + } + +@@ -903,6 +1140,11 @@ static int __clk_enable(struct clk *clk) + return ret; + } + } ++ ++#ifdef CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING ++ if (freq_stats_on) ++ clk->start_time = ktime_get(); ++#endif /*CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING*/ + } + + clk->enable_count++; +@@ -1483,6 +1725,32 @@ static void clk_change_rate(struct clk *clk) + + clk->rate = clk_recalc(clk, best_parent_rate); + ++#ifdef CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING ++ if (freq_stats_on) { ++ if (!ktime_equal(clk->start_time, ktime_set(0, 0))) { ++ if (!clk->current_freq_stats) ++ clk->default_freq_time = ++ ktime_add(clk->default_freq_time, ++ ktime_sub(ktime_get(), ++ clk->start_time)); ++ else ++ clk->current_freq_stats->time_spent = ++ ktime_add( ++ clk->current_freq_stats->time_spent, ++ ktime_sub(ktime_get(), ++ clk->start_time)); ++ } ++ ++ clk->current_freq_stats = freq_stats_insert( ++ &clk->freq_stats_table, ++ clk->rate); ++ ++ if (clk->enable_count > 0) ++ clk->start_time = ktime_get(); ++ } ++#endif /*CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING*/ ++ ++ + if (clk->notifier_count && old_rate != clk->rate) + __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); + +@@ -2112,6 +2380,11 @@ static void __clk_release(struct kref *ref) + + kfree(clk->parent_names); + kfree(clk->name); ++ ++#ifdef CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING ++ free_tree(clk->freq_stats_table.rb_node); ++#endif/*CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING*/ ++ + kfree(clk); + } + +diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile +index 038c02f..cf455ca 100644 +--- a/drivers/clk/hisilicon/Makefile ++++ b/drivers/clk/hisilicon/Makefile +@@ -2,8 +2,17 @@ + # Hisilicon Clock specific Makefile + # + +-obj-y += clk.o clkgate-separated.o ++obj-y += clk.o clkgate-separated.o clk_ops.o + + obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o + obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o + obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o ++obj-$(CONFIG_ARCH_HI3516CV300) += clk-hi3516cv300.o reset.o ++obj-$(CONFIG_ARCH_HI3519) += clk-hi3519.o reset.o ++obj-$(CONFIG_ARCH_HI3519V101) += clk-hi3519v101.o reset.o ++obj-$(CONFIG_ARCH_HI3516AV200) += clk-hi3516av200.o reset.o ++obj-$(CONFIG_ARCH_HI3559) += clk-hi3559.o reset.o ++obj-$(CONFIG_ARCH_HI3556) += clk-hi3556.o reset.o ++obj-$(CONFIG_ARCH_HI3536C) += clk-hi3536c.o reset.o ++obj-$(CONFIG_ARCH_HI3531D) += clk-hi3531d.o reset.o ++obj-$(CONFIG_ARCH_HI3521D) += clk-hi3521d.o reset.o +diff --git a/drivers/clk/hisilicon/clk-hi3516av200.c b/drivers/clk/hisilicon/clk-hi3516av200.c +new file mode 100644 +index 0000000..ca4c165 +--- /dev/null ++++ b/drivers/clk/hisilicon/clk-hi3516av200.c +@@ -0,0 +1,385 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/of_address.h> ++#include <dt-bindings/clock/hi3516av200-clock.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <mach/io.h> ++ ++#include "clk.h" ++#include "reset.h" ++ ++/******************************************************************************/ ++struct hi3516av200_pll_clock { ++ u32 id; ++ const char *name; ++ const char *parent_name; ++ u32 ctrl_reg1; ++ u8 frac_shift; ++ u8 frac_width; ++ u8 postdiv1_shift; ++ u8 postdiv1_width; ++ u8 postdiv2_shift; ++ u8 postdiv2_width; ++ u32 ctrl_reg2; ++ u8 fbdiv_shift; ++ u8 fbdiv_width; ++ u8 refdiv_shift; ++ u8 refdiv_width; ++}; ++ ++struct hi3516av200_clk_pll { ++ struct clk_hw hw; ++ u32 id; ++ void __iomem *ctrl_reg1; ++ u8 frac_shift; ++ u8 frac_width; ++ u8 postdiv1_shift; ++ u8 postdiv1_width; ++ u8 postdiv2_shift; ++ u8 postdiv2_width; ++ void __iomem *ctrl_reg2; ++ u8 fbdiv_shift; ++ u8 fbdiv_width; ++ u8 refdiv_shift; ++ u8 refdiv_width; ++}; ++ ++static struct hisi_fixed_rate_clock hi3516av200_fixed_rate_clks[] __initdata = { ++ { HI3516AV200_FIXED_2376M, "2376m", NULL, CLK_IS_ROOT, 2376000000UL, }, ++ { HI3516AV200_FIXED_1188M, "1188m", NULL, CLK_IS_ROOT, 1188000000, }, ++ { HI3516AV200_FIXED_594M, "594m", NULL, CLK_IS_ROOT, 594000000, }, ++ { HI3516AV200_FIXED_297M, "297m", NULL, CLK_IS_ROOT, 297000000, }, ++ { HI3516AV200_FIXED_148P5M, "148p5m", NULL, CLK_IS_ROOT, 148500000, }, ++ { HI3516AV200_FIXED_74P25M, "74p25m", NULL, CLK_IS_ROOT, 74250000, }, ++ { HI3516AV200_FIXED_792M, "792m", NULL, CLK_IS_ROOT, 792000000, }, ++ { HI3516AV200_FIXED_475M, "475m", NULL, CLK_IS_ROOT, 475000000, }, ++ { HI3516AV200_FIXED_340M, "340m", NULL, CLK_IS_ROOT, 340000000, }, ++ { HI3516AV200_FIXED_72M, "72m", NULL, CLK_IS_ROOT, 72000000, }, ++ { HI3516AV200_FIXED_400M, "400m", NULL, CLK_IS_ROOT, 400000000, }, ++ { HI3516AV200_FIXED_200M, "200m", NULL, CLK_IS_ROOT, 200000000, }, ++ { HI3516AV200_FIXED_54M, "54m", NULL, CLK_IS_ROOT, 54000000, }, ++ { HI3516AV200_FIXED_27M, "27m", NULL, CLK_IS_ROOT, 1188000000, }, ++ { HI3516AV200_FIXED_37P125M, "37p125m", NULL, CLK_IS_ROOT, 37125000, }, ++ { HI3516AV200_FIXED_3000M, "3000m", NULL, CLK_IS_ROOT, 3000000000UL, }, ++ { HI3516AV200_FIXED_1500M, "1500m", NULL, CLK_IS_ROOT, 1500000000, }, ++ { HI3516AV200_FIXED_500M, "500m", NULL, CLK_IS_ROOT, 500000000, }, ++ { HI3516AV200_FIXED_250M, "250m", NULL, CLK_IS_ROOT, 250000000, }, ++ { HI3516AV200_FIXED_125M, "125m", NULL, CLK_IS_ROOT, 125000000, }, ++ { HI3516AV200_FIXED_1000M, "1000m", NULL, CLK_IS_ROOT, 1000000000, }, ++ { HI3516AV200_FIXED_600M, "600m", NULL, CLK_IS_ROOT, 600000000, }, ++ { HI3516AV200_FIXED_750M, "750m", NULL, CLK_IS_ROOT, 750000000, }, ++ { HI3516AV200_FIXED_150M, "150m", NULL, CLK_IS_ROOT, 150000000, }, ++ { HI3516AV200_FIXED_75M, "75m", NULL, CLK_IS_ROOT, 75000000, }, ++ { HI3516AV200_FIXED_300M, "300m", NULL, CLK_IS_ROOT, 300000000, }, ++ { HI3516AV200_FIXED_60M, "60m", NULL, CLK_IS_ROOT, 60000000, }, ++ { HI3516AV200_FIXED_214M, "214m", NULL, CLK_IS_ROOT, 214000000, }, ++ { HI3516AV200_FIXED_107M, "107m", NULL, CLK_IS_ROOT, 107000000, }, ++ { HI3516AV200_FIXED_100M, "100m", NULL, CLK_IS_ROOT, 100000000, }, ++ { HI3516AV200_FIXED_50M, "50m", NULL, CLK_IS_ROOT, 50000000, }, ++ { HI3516AV200_FIXED_25M, "25m", NULL, CLK_IS_ROOT, 25000000, }, ++ { HI3516AV200_FIXED_24M, "24m", NULL, CLK_IS_ROOT, 24000000, }, ++ { HI3516AV200_FIXED_3M, "3m", NULL, CLK_IS_ROOT, 3000000, }, ++ { HI3516AV200_FIXED_198M, "198m", NULL, CLK_IS_ROOT, 198000000, }, ++ { HI3516AV200_FIXED_396M, "396m", NULL, CLK_IS_ROOT, 396000000, }, ++}; ++ ++static const char *sysaxi_mux_p[] __initconst = {"24m", "198m", }; ++static u32 sysaxi_mux_table[] = {0, 1}; ++ ++static const char *fmc_mux_p[] __initconst = { ++ "24m", "75m", "125m", "150m", "198m", "250m", "300m", "396m", }; ++static u32 fmc_mux_table[] = {0, 1, 2, 3, 4, 5, 6, 7}; ++ ++static const char *mmc_mux_p[] __initconst = { ++ "100m", "198m", "396m", "594m", "792m", "1188m", }; ++static u32 mmc_mux_table[] = {0, 1, 3, 4, 5, 7}; ++ ++static const char *a17_mux_p[] __initconst = { ++ "24m", "apll", "594m", "792m", }; ++static u32 a17_mux_table[] = {0, 1, 3, 4}; ++ ++static const char *i2c_mux_p[] __initconst = {"clk_sysapb", "50m"}; ++static u32 i2c_mux_table[] = {0, 1}; ++ ++static struct hisi_mux_clock hi3516av200_mux_clks[] __initdata = { ++ { HI3516AV200_SYSAXI_MUX, "sysaxi_mux", sysaxi_mux_p, ++ ARRAY_SIZE(sysaxi_mux_p), ++ CLK_SET_RATE_PARENT, 0x34, 12, 2, 0, sysaxi_mux_table, }, ++ { HI3516AV200_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, }, ++ { HI3516AV200_MMC0_MUX, "mmc0_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 10, 3, 0, mmc_mux_table, }, ++ { HI3516AV200_MMC1_MUX, "mmc1_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 2, 3, 0, mmc_mux_table, }, ++ { HI3516AV200_MMC2_MUX, "mmc2_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 18, 3, 0, mmc_mux_table, }, ++ { HI3516AV200_A17_MUX, "a17_mux", a17_mux_p, ARRAY_SIZE(a17_mux_p), ++ CLK_SET_RATE_PARENT, 0x34, 4, 3, 0, a17_mux_table, }, ++ { HI3516AV200_I2C_MUX, "i2c_mux", i2c_mux_p, ARRAY_SIZE(i2c_mux_p), ++ CLK_SET_RATE_PARENT, 0xe4, 26, 1, 0, i2c_mux_table, }, ++ ++}; ++ ++static struct hisi_fixed_factor_clock ++ hi3516av200_fixed_factor_clks[] __initdata = { ++ { HI3516AV200_SYSAPB_CLK, "clk_sysapb", "sysaxi_mux", 1, 4, ++ CLK_SET_RATE_PARENT}, ++ { HI3516AV200_MMC0_FAC_CLK, "mmc0_fac", "mmc0_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++ { HI3516AV200_MMC1_FAC_CLK, "mmc1_fac", "mmc1_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++ { HI3516AV200_MMC2_FAC_CLK, "mmc2_fac", "mmc2_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++}; ++ ++static struct hisi_gate_clock hi3516av200_gate_clks[] __initdata = { ++#ifdef CONFIG_HIFMC ++ /* fmc */ ++ { HI3516AV200_FMC_CLK, "clk_fmc", "fmc_mux", ++ CLK_SET_RATE_PARENT, 0xc0, 1, 0, }, ++#endif ++ /* mmc */ ++ { HI3516AV200_MMC0_CLK, "clk_mmc0", "mmc0_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 9, 0, }, ++ { HI3516AV200_MMC1_CLK, "clk_mmc1", "mmc1_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 1, 0, }, ++ { HI3516AV200_MMC2_CLK, "clk_mmc2", "mmc2_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 17, 0, }, ++ ++ /* usb ctrl */ ++ { HI3516AV200_USB2_CTRL_UTMI0_REQ, "usb2_cttl_utmi0_req", NULL, ++ CLK_SET_RATE_PARENT, 0xb4, 5, 1, }, ++ { HI3516AV200_USB2_HRST_REQ, "usb2_hrst_req", NULL, ++ CLK_SET_RATE_PARENT, 0xb4, 0, 1, }, ++ { HI3516AV200_USB3_CLK, "usb3_vcc_srst_req2", NULL, ++ CLK_SET_RATE_PARENT, 0xb8, 0, 1, }, ++ ++ /* uart */ ++ { HI3516AV200_UART0_CLK, "clk_uart0", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 20, 0, }, ++ { HI3516AV200_UART1_CLK, "clk_uart1", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 21, 0, }, ++ { HI3516AV200_UART2_CLK, "clk_uart2", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 22, 0, }, ++ { HI3516AV200_UART3_CLK, "clk_uart3", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 23, 0, }, ++ { HI3516AV200_UART4_CLK, "clk_uart4", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 24, 0, }, ++ ++ /* ethernet mac */ ++ { HI3516AV200_ETH_CLK, "clk_eth", NULL, ++ CLK_IS_ROOT, 0xcc, 1, 0, }, ++ { HI3516AV200_ETH_MACIF_CLK, "clk_eth_macif", NULL, ++ CLK_IS_ROOT, 0xcc, 3, 0, }, ++ ++ /* spi */ ++ { HI3516AV200_SPI0_CLK, "clk_spi0", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 16, 0, }, ++ { HI3516AV200_SPI1_CLK, "clk_spi1", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 17, 0, }, ++ { HI3516AV200_SPI2_CLK, "clk_spi2", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 18, 0, }, ++ { HI3516AV200_SPI3_CLK, "clk_spi3", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 28, 0, }, ++}; ++ ++static struct hi3516av200_pll_clock hi3516av200_pll_clks[] __initdata = { ++ { HI3516AV200_APLL_CLK, "apll", NULL, 0x0, 0, 24, 24, 3, 28, 3, ++ 0x4, 0, 12, 12, 6}, ++}; ++ ++ ++#define to_pll_clk(_hw) container_of(_hw, struct hi3516av200_clk_pll, hw) ++ ++static void hi3516av200_calc_pll(u32 *frac_val, ++ u32 *postdiv1_val, ++ u32 *postdiv2_val, ++ u32 *fbdiv_val, ++ u32 *refdiv_val, ++ unsigned long rate) ++{ ++ u64 rem; ++ *frac_val = 0; ++ rem = do_div(rate, 1000000); ++ *fbdiv_val = rate; ++ *refdiv_val = 24; ++ rem = rem * (1 << 24); ++ do_div(rem, 1000000); ++ *frac_val = rem; ++} ++ ++static int clk_pll_set_rate(struct clk_hw *hw, ++ unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct hi3516av200_clk_pll *clk = to_pll_clk(hw); ++ u32 frac_val, postdiv1_val, postdiv2_val, fbdiv_val, refdiv_val; ++ u32 val; ++ ++ /*Fixme ignore postdives now because apll don't use them*/ ++ postdiv1_val = postdiv2_val = 0; ++ ++ hi3516av200_calc_pll(&frac_val, &postdiv1_val, &postdiv2_val, ++ &fbdiv_val, &refdiv_val, rate); ++ ++ val = readl_relaxed(clk->ctrl_reg1); ++ val &= ~(((1 << clk->frac_width) - 1) << clk->frac_shift); ++ val &= ~(((1 << clk->postdiv1_width) - 1) << clk->postdiv1_shift); ++ val &= ~(((1 << clk->postdiv2_width) - 1) << clk->postdiv2_shift); ++ ++ val |= frac_val << clk->frac_shift; ++ val |= postdiv1_val << clk->postdiv1_shift; ++ val |= postdiv2_val << clk->postdiv2_shift; ++ writel_relaxed(val, clk->ctrl_reg1); ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val &= ~(((1 << clk->fbdiv_width) - 1) << clk->fbdiv_shift); ++ val &= ~(((1 << clk->refdiv_width) - 1) << clk->refdiv_shift); ++ ++ val |= fbdiv_val << clk->fbdiv_shift; ++ val |= refdiv_val << clk->refdiv_shift; ++ writel_relaxed(val, clk->ctrl_reg2); ++ ++ return 0; ++} ++ ++static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct hi3516av200_clk_pll *clk = to_pll_clk(hw); ++ u64 frac_val, fbdiv_val, refdiv_val; ++ u32 val; ++ u64 tmp, rate; ++ ++ val = readl_relaxed(clk->ctrl_reg1); ++ val = val >> clk->frac_shift; ++ val &= ((1 << clk->frac_width) - 1); ++ frac_val = val; ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val = val >> clk->fbdiv_shift; ++ val &= ((1 << clk->fbdiv_width) - 1); ++ fbdiv_val = val; ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val = val >> clk->refdiv_shift; ++ val &= ((1 << clk->refdiv_width) - 1); ++ refdiv_val = val; ++ ++ /* rate = 24000000 * (fbdiv + frac / (1<<24) ) / refdiv */ ++ rate = 0; ++ tmp = 24000000 * fbdiv_val; ++ rate += tmp; ++ do_div(rate, refdiv_val); ++ ++ return rate; ++} ++ ++ ++static long clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *best_parent_rate, ++ struct clk **best_parent_p) ++{ ++ return rate; ++} ++ ++static struct clk_ops clk_pll_ops = { ++ .set_rate = clk_pll_set_rate, ++ .determine_rate = clk_pll_determine_rate, ++ .recalc_rate = clk_pll_recalc_rate, ++}; ++ ++void __init hi3516av200_clk_register_pll(struct hi3516av200_pll_clock *clks, ++ int nums, struct hisi_clock_data *data) ++{ ++ void __iomem *base = data->base; ++ int i; ++ ++ for (i = 0; i < nums; i++) { ++ struct hi3516av200_clk_pll *p_clk; ++ struct clk *clk; ++ struct clk_init_data init; ++ ++ p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL); ++ if (!p_clk) ++ return; ++ ++ init.name = clks[i].name; ++ init.flags = CLK_IS_BASIC; ++ init.parent_names = ++ (clks[i].parent_name ? &clks[i].parent_name : NULL); ++ init.num_parents = (clks[i].parent_name ? 1 : 0); ++ init.ops = &clk_pll_ops; ++ ++ p_clk->ctrl_reg1 = base + clks[i].ctrl_reg1; ++ p_clk->frac_shift = clks[i].frac_shift; ++ p_clk->frac_width = clks[i].frac_width; ++ p_clk->postdiv1_shift = clks[i].postdiv1_shift; ++ p_clk->postdiv1_width = clks[i].postdiv1_width; ++ p_clk->postdiv2_shift = clks[i].postdiv2_shift; ++ p_clk->postdiv2_width = clks[i].postdiv2_width; ++ ++ p_clk->ctrl_reg2 = base + clks[i].ctrl_reg2; ++ p_clk->fbdiv_shift = clks[i].fbdiv_shift; ++ p_clk->fbdiv_width = clks[i].fbdiv_width; ++ p_clk->refdiv_shift = clks[i].refdiv_shift; ++ p_clk->refdiv_width = clks[i].refdiv_width; ++ p_clk->hw.init = &init; ++ ++ clk = clk_register(NULL, &p_clk->hw); ++ if (IS_ERR(clk)) { ++ kfree(p_clk); ++ pr_err("%s: failed to register clock %s\n", ++ __func__, clks[i].name); ++ continue; ++ } ++ ++ data->clk_data.clks[clks[i].id] = clk; ++ } ++ ++ ++} ++ ++static void __init hi3516av200_clk_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ ++ clk_data = hisi_clk_init(np, HI3516AV200_NR_CLKS); ++ if (!clk_data) ++ return; ++ if (IS_ENABLED(CONFIG_RESET_CONTROLLER)) ++ hisi_reset_init(np, HI3516AV200_NR_RSTS); ++ ++ hisi_clk_register_fixed_rate(hi3516av200_fixed_rate_clks, ++ ARRAY_SIZE(hi3516av200_fixed_rate_clks), ++ clk_data); ++ hi3516av200_clk_register_pll(hi3516av200_pll_clks, ++ ARRAY_SIZE(hi3516av200_pll_clks), clk_data); ++ ++ hisi_clk_register_mux(hi3516av200_mux_clks, ++ ARRAY_SIZE(hi3516av200_mux_clks), ++ clk_data); ++ hisi_clk_register_fixed_factor(hi3516av200_fixed_factor_clks, ++ ARRAY_SIZE(hi3516av200_fixed_factor_clks), clk_data); ++ hisi_clk_register_gate(hi3516av200_gate_clks, ++ ARRAY_SIZE(hi3516av200_gate_clks), clk_data); ++} ++ ++CLK_OF_DECLARE(hi3516av200_clk, "hisilicon,hi3516av200-clock", ++ hi3516av200_clk_init); +diff --git a/drivers/clk/hisilicon/clk-hi3516cv300.c b/drivers/clk/hisilicon/clk-hi3516cv300.c +new file mode 100644 +index 0000000..4d41475 +--- /dev/null ++++ b/drivers/clk/hisilicon/clk-hi3516cv300.c +@@ -0,0 +1,244 @@ ++/* ++ * Hi3516cv300 Clock Driver ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <dt-bindings/clock/hi3516cv300-clock.h> ++#include "clk.h" ++#include "reset.h" ++ ++static struct ++hisi_fixed_rate_clock hi3516cv300_fixed_rate_clks_crg[] __initdata = { ++ { HI3516CV300_FIXED_3M, "3m", NULL, CLK_IS_ROOT, 3000000, }, ++ { HI3516CV300_FIXED_6M, "6m", NULL, CLK_IS_ROOT, 6000000, }, ++ { HI3516CV300_FIXED_12M, "12m", NULL, CLK_IS_ROOT, 12000000, }, ++ { HI3516CV300_FIXED_15M, "15m", NULL, CLK_IS_ROOT, 15000000, }, ++ { HI3516CV300_FIXED_24M, "24m", NULL, CLK_IS_ROOT, 24000000, }, ++ { HI3516CV300_FIXED_25M, "25m", NULL, CLK_IS_ROOT, 25000000, }, ++ { HI3516CV300_FIXED_27M, "27m", NULL, CLK_IS_ROOT, 27000000, }, ++ { HI3516CV300_FIXED_37P125M, "37.125m", NULL, CLK_IS_ROOT, 37125000, }, ++ { HI3516CV300_FIXED_44M, "44m", NULL, CLK_IS_ROOT, 44000000, }, ++ { HI3516CV300_FIXED_49P5M, "49.5m", NULL, CLK_IS_ROOT, 49500000, }, ++ { HI3516CV300_FIXED_50M, "50m", NULL, CLK_IS_ROOT, 50000000, }, ++ { HI3516CV300_FIXED_54M, "54m", NULL, CLK_IS_ROOT, 54000000, }, ++ { HI3516CV300_FIXED_74P25M, "74.25m", NULL, CLK_IS_ROOT, 74250000, }, ++ { HI3516CV300_FIXED_75M, "75m", NULL, CLK_IS_ROOT, 75000000, }, ++ { HI3516CV300_FIXED_83P3M, "83.3m", NULL, CLK_IS_ROOT, 83300000, }, ++ { HI3516CV300_FIXED_99M, "99m", NULL, CLK_IS_ROOT, 99000000, }, ++ { HI3516CV300_FIXED_100M, "100m", NULL, CLK_IS_ROOT, 100000000, }, ++ { HI3516CV300_FIXED_125M, "125m", NULL, CLK_IS_ROOT, 125000000, }, ++ { HI3516CV300_FIXED_148P5M, "148.5m", NULL, CLK_IS_ROOT, 148500000, }, ++ { HI3516CV300_FIXED_150M, "150m", NULL, CLK_IS_ROOT, 150000000, }, ++ { HI3516CV300_FIXED_166P6M, "166.6m", NULL, CLK_IS_ROOT, 166600000, }, ++ { HI3516CV300_FIXED_198M, "198m", NULL, CLK_IS_ROOT, 198000000, }, ++ { HI3516CV300_FIXED_200M, "200m", NULL, CLK_IS_ROOT, 200000000, }, ++ { HI3516CV300_FIXED_250M, "250m", NULL, CLK_IS_ROOT, 250000000, }, ++ { HI3516CV300_FIXED_297M, "297m", NULL, CLK_IS_ROOT, 297000000, }, ++ { HI3516CV300_FIXED_300M, "300m", NULL, CLK_IS_ROOT, 300000000, }, ++ { HI3516CV300_FIXED_396M, "396m", NULL, CLK_IS_ROOT, 396000000, }, ++ { HI3516CV300_FIXED_400M, "400m", NULL, CLK_IS_ROOT, 400000000, }, ++}; ++ ++static const char *apb_mux_p[] __initconst = {"24m", "50m"}; ++static const char *uart_mux_p[] __initconst = {"24m", "6m"}; ++static const char *fmc_mux_p[] __initconst = { ++ "24m", "83.3m", "148.5m", "198m", "297m" ++}; ++static const char *mmc_mux_p[] __initconst = {"49.5m"}; ++static const char *mmc2_mux_p[] __initconst = {"99m", "49.5m"}; ++static const char *sensor_mux_p[] = {"74.25m", "37.125m", "54m", "27m", "24m", "25m", "24m", "25m"}; ++static const char *viu_mux_p[] = {"83.3m", "125m", "148.5m", "198m", "250m"}; ++static const char *vedu_mux_p[] = {"166.6m", "198m"}; ++static const char *vpss_mux_p[] = {"148.5m", "198m", "250m"}; ++static const char *vgs_mux_p[] = {"198m", "250m", "297m"}; ++static const char *ive_mux_p[] = {"198m", "250m", "297m"}; ++static const char *pwm_mux_p[] = {"3m", "50m", "24m", "24m"}; ++ ++static u32 apb_mux_table[] = {0, 1}; ++static u32 uart_mux_table[] = {0, 1}; ++static u32 fmc_mux_table[] = {0, 1, 2, 3, 4}; ++static u32 mmc_mux_table[] = {0}; ++static u32 mmc2_mux_table[] = {0, 2}; ++static u32 pwm_mux_table[] = {0, 1, 2, 3}; ++ ++static u32 sensor_mux_p_table[] = {0, 1, 2, 3, 4, 5, 6, 7}; ++static u32 viu_mux_p_table[] = {0, 1, 2, 3, 4}; ++static u32 vedu_mux_p_table[] = {0, 1}; ++static u32 vpss_mux_p_table[] = {0, 1, 2}; ++static u32 vgs_mux_p_table[] = {0, 1, 2}; ++static u32 ive_mux_p_table[] = {0, 1, 2}; ++static struct hisi_mux_clock hi3516cv300_mux_clks_crg[] __initdata = { ++ { HI3516CV300_APB_CLK, "apb", apb_mux_p, ARRAY_SIZE(apb_mux_p), ++ CLK_SET_RATE_PARENT, 0x30, 0, 1, 0, apb_mux_table, }, ++ { HI3516CV300_UART_MUX, "uart_mux", uart_mux_p, ARRAY_SIZE(uart_mux_p), ++ CLK_SET_RATE_PARENT, 0xe4, 19, 1, 0, uart_mux_table, }, ++ { HI3516CV300_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, }, ++ { HI3516CV300_MMC0_MUX, "mmc0_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 4, 2, 0, mmc_mux_table, }, ++ { HI3516CV300_MMC1_MUX, "mmc1_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 12, 2, 0, mmc_mux_table, }, ++ { HI3516CV300_MMC2_MUX, "mmc2_mux", mmc2_mux_p, ARRAY_SIZE(mmc2_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 20, 2, 0, mmc2_mux_table, }, ++ { HI3516CV300_MMC3_MUX, "mmc3_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc8, 4, 2, 0, mmc_mux_table, }, ++ { HI3516CV300_PWM_MUX, "pwm_mux", pwm_mux_p, ARRAY_SIZE(pwm_mux_p), ++ CLK_SET_RATE_PARENT, 0x38, 2, 2, 0, pwm_mux_table, }, ++ ++ /* media mux clock*/ ++ { HI3516CV300_SENSOR_MUX, "sensor_mux", sensor_mux_p, ARRAY_SIZE(sensor_mux_p), ++ CLK_SET_RATE_PARENT, 0x2c, 23, 3, 0, sensor_mux_p_table, }, ++ { HI3516CV300_VIU_MUX, "viu_mux", viu_mux_p, ARRAY_SIZE(viu_mux_p), ++ CLK_SET_RATE_PARENT, 0x2c, 2, 3, 0, viu_mux_p_table, }, ++ { HI3516CV300_VEDU_MUX, "vedu_mux", vedu_mux_p, ARRAY_SIZE(vedu_mux_p), ++ CLK_SET_RATE_PARENT, 0x40, 10, 2, 0, vedu_mux_p_table, }, ++ { HI3516CV300_VPSS_MUX, "vpss_mux", vpss_mux_p, ARRAY_SIZE(vpss_mux_p), ++ CLK_SET_RATE_PARENT, 0x48, 10, 2, 0, vpss_mux_p_table, }, ++ { HI3516CV300_VGS_MUX, "vgs_mux", vgs_mux_p, ARRAY_SIZE(vgs_mux_p), ++ CLK_SET_RATE_PARENT, 0x5c, 10, 2, 0, vgs_mux_p_table, }, ++ { HI3516CV300_IVE_MUX, "ive_mux", ive_mux_p, ARRAY_SIZE(ive_mux_p), ++ CLK_SET_RATE_PARENT, 0x6c, 2, 2, 0, ive_mux_p_table, }, ++}; ++ ++static struct hisi_divider_clock hi3516cv300_div_clks[] = { ++ { HI3516CV300_ISP_DIV, "isp_div", "viu_mux", 0, 0x2c, 17, 1, 0, NULL, }, ++}; ++ ++static struct hisi_gate_clock hi3516cv300_gate_clks_crg[] __initdata = { ++ /* uart */ ++ { HI3516CV300_UART0_CLK, "clk_uart0", "uart_mux", ++ CLK_SET_RATE_PARENT, 0xe4, 15, 0, }, ++ { HI3516CV300_UART1_CLK, "clk_uart1", "uart_mux", ++ CLK_SET_RATE_PARENT, 0xe4, 16, 0, }, ++ { HI3516CV300_UART2_CLK, "clk_uart2", "uart_mux", ++ CLK_SET_RATE_PARENT, 0xe4, 17, 0, }, ++ /* spi */ ++ { HI3516CV300_SPI0_CLK, "clk_spi0", "100m", ++ CLK_SET_RATE_PARENT, 0xe4, 13, 0, }, ++ { HI3516CV300_SPI1_CLK, "clk_spi1", "100m", ++ CLK_SET_RATE_PARENT, 0xe4, 14, 0, }, ++ /* fmc */ ++ { HI3516CV300_FMC_CLK, "clk_fmc", "fmc_mux", ++ CLK_SET_RATE_PARENT, 0xc0, 1, 0, }, ++ { HI3516CV300_MMC0_CLK, "clk_mmc0", "mmc0_mux", ++ CLK_SET_RATE_PARENT, 0xc4, 1, 0, }, ++ { HI3516CV300_MMC1_CLK, "clk_mmc1", "mmc1_mux", ++ CLK_SET_RATE_PARENT, 0xc4, 9, 0, }, ++ { HI3516CV300_MMC2_CLK, "clk_mmc2", "mmc2_mux", ++ CLK_SET_RATE_PARENT, 0xc4, 17, 0, }, ++ { HI3516CV300_MMC3_CLK, "clk_mmc3", "mmc3_mux", ++ CLK_SET_RATE_PARENT, 0xc8, 1, 0, }, ++ /* ethernet mac */ ++ { HI3516CV300_ETH_CLK, "clk_eth", NULL, CLK_IS_ROOT, 0xec, 1, 0, }, ++ { HI3516CV300_USB2_BUS_CLK, "clk_usb2_bus", NULL, ++ CLK_SET_RATE_PARENT, 0xb8, 8, 1, }, ++ { HI3516CV300_UTMI0_CLK, "clk_utmi0", NULL, ++ CLK_SET_RATE_PARENT, 0xb8, 11, 1, }, ++ { HI3516CV300_USB2_CLK, "clk_usb2", NULL, ++ CLK_SET_RATE_PARENT, 0xb8, 12, 1, }, ++ { HI3516CV300_DMAC_CLK, "clk_dmac", NULL, CLK_IS_ROOT, 0xd8, 5, 0, }, ++ ++ { HI3516CV300_PWM_CLK, "clk_pwm", "pwm_mux", CLK_SET_RATE_PARENT, ++ 0x38, 1, 0, }, ++ ++ /* media gate clock*/ ++ { HI3516CV300_SENSOR_CLK, "clk_sensor", "sensor_mux", CLK_SET_RATE_PARENT, ++ 0x2c, 26, 0, }, ++ { HI3516CV300_MIPI_CLK, "clk_mipi", NULL, CLK_SET_RATE_PARENT, ++ 0x2c, 15, 0, }, ++ { HI3516CV300_ISP_CLK, "clk_isp", "isp_div", CLK_SET_RATE_PARENT, 0x2c, 18, 0, }, ++ { HI3516CV300_VIU_CLK, "clk_viu", "viu_mux", CLK_SET_RATE_PARENT, ++ 0x2c, 0, 0, }, ++ { HI3516CV300_VEDU_CLK, "clk_vedu", "vedu_mux", CLK_SET_RATE_PARENT, ++ 0x40, 1, 0, }, ++ { HI3516CV300_VPSS_CLK, "clk_vpss", "vpss_mux", CLK_SET_RATE_PARENT, ++ 0x48, 1, 0, }, ++ { HI3516CV300_VGS_CLK, "clk_vgs", "vgs_mux", CLK_SET_RATE_PARENT, ++ 0x5c, 1, 0, }, ++ { HI3516CV300_JPGE_CLK, "clk_jpge", NULL, CLK_SET_RATE_PARENT, ++ 0x60, 1, 0, }, ++ { HI3516CV300_IVE_CLK, "clk_ive", "ive_mux", CLK_SET_RATE_PARENT, ++ 0x6c, 1, 0, }, ++ { HI3516CV300_AIAO_CLK, "clk_aiao", NULL, CLK_SET_RATE_PARENT, ++ 0x8c, 1, 0, }, ++}; ++ ++static void __init hi3516cv300_clk_crg_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ unsigned int count = 0; ++ ++ clk_data = hisi_clk_init(np, HI3516CV300_CRG_NR_CLKS); ++ if (!clk_data) ++ return; ++ ++ hisi_clk_register_fixed_rate(hi3516cv300_fixed_rate_clks_crg, ++ ARRAY_SIZE(hi3516cv300_fixed_rate_clks_crg), ++ clk_data); ++ hisi_clk_register_mux(hi3516cv300_mux_clks_crg, ++ ARRAY_SIZE(hi3516cv300_mux_clks_crg), clk_data); ++ hisi_clk_register_divider(hi3516cv300_div_clks, ++ ARRAY_SIZE(hi3516cv300_div_clks), clk_data); ++ hisi_clk_register_gate(hi3516cv300_gate_clks_crg, ++ ARRAY_SIZE(hi3516cv300_gate_clks_crg), clk_data); ++ ++ if (!of_property_read_u32(np, "#reset-cells", &count) && (count == 2)) ++ hisi_reset_init(np, HI3516CV300_CRG_NR_RSTS); ++} ++ ++static const char *timer_mux_p[] __initconst = { "3m", "apb" }; ++static u32 timer_mux_table[] = {0, 1}; ++ ++static struct hisi_mux_clock hi3516cv300_mux_clks_sys[] __initdata = { ++ { HI3516CV300_TIME00_CLK, "timer00", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 16, 1, 0, timer_mux_table, }, ++ ++ { HI3516CV300_TIME01_CLK, "timer01", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 18, 1, 0, timer_mux_table, }, ++ ++ { HI3516CV300_TIME10_CLK, "timer10", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 20, 1, 0, timer_mux_table, }, ++ ++ { HI3516CV300_TIME11_CLK, "timer11", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 22, 1, 0, timer_mux_table, }, ++}; ++ ++static void __init hi3516cv300_clk_sys_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ unsigned int count = 0; ++ ++ clk_data = hisi_clk_init(np, HI3516CV300_SYS_NR_CLKS); ++ if (!clk_data) ++ return; ++ ++ hisi_clk_register_mux(hi3516cv300_mux_clks_sys, ++ ARRAY_SIZE(hi3516cv300_mux_clks_sys), clk_data); ++ ++ if (!of_property_read_u32(np, "#reset-cells", &count) && (count == 2)) ++ hisi_reset_init(np, HI3516CV300_SYS_NR_RSTS); ++} ++ ++CLK_OF_DECLARE(hi3516cv300_clk, "hisilicon,hi3516cv300-crg", ++ hi3516cv300_clk_crg_init); ++CLK_OF_DECLARE(hi3516cv300_clk_sys, "hisilicon,hi3516cv300-sys", ++ hi3516cv300_clk_sys_init); +diff --git a/drivers/clk/hisilicon/clk-hi3519.c b/drivers/clk/hisilicon/clk-hi3519.c +new file mode 100644 +index 0000000..1cd0676 +--- /dev/null ++++ b/drivers/clk/hisilicon/clk-hi3519.c +@@ -0,0 +1,375 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/of_address.h> ++#include <dt-bindings/clock/hi3519-clock.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <mach/io.h> ++ ++#include "clk.h" ++#include "reset.h" ++ ++/******************************************************************************/ ++struct hi3519_pll_clock { ++ u32 id; ++ const char *name; ++ const char *parent_name; ++ u32 ctrl_reg1; ++ u8 frac_shift; ++ u8 frac_width; ++ u8 postdiv1_shift; ++ u8 postdiv1_width; ++ u8 postdiv2_shift; ++ u8 postdiv2_width; ++ u32 ctrl_reg2; ++ u8 fbdiv_shift; ++ u8 fbdiv_width; ++ u8 refdiv_shift; ++ u8 refdiv_width; ++}; ++ ++struct hi3519_clk_pll { ++ struct clk_hw hw; ++ u32 id; ++ void __iomem *ctrl_reg1; ++ u8 frac_shift; ++ u8 frac_width; ++ u8 postdiv1_shift; ++ u8 postdiv1_width; ++ u8 postdiv2_shift; ++ u8 postdiv2_width; ++ void __iomem *ctrl_reg2; ++ u8 fbdiv_shift; ++ u8 fbdiv_width; ++ u8 refdiv_shift; ++ u8 refdiv_width; ++}; ++ ++static struct hisi_fixed_rate_clock hi3519_fixed_rate_clks[] __initdata = { ++ { HI3519_FIXED_2376M, "2376m", NULL, CLK_IS_ROOT, 2376000000UL, }, ++ { HI3519_FIXED_1188M, "1188m", NULL, CLK_IS_ROOT, 1188000000, }, ++ { HI3519_FIXED_594M, "594m", NULL, CLK_IS_ROOT, 594000000, }, ++ { HI3519_FIXED_297M, "297m", NULL, CLK_IS_ROOT, 297000000, }, ++ { HI3519_FIXED_148P5M, "148p5m", NULL, CLK_IS_ROOT, 148500000, }, ++ { HI3519_FIXED_74P25M, "74p25m", NULL, CLK_IS_ROOT, 74250000, }, ++ { HI3519_FIXED_792M, "792m", NULL, CLK_IS_ROOT, 792000000, }, ++ { HI3519_FIXED_475M, "475m", NULL, CLK_IS_ROOT, 475000000, }, ++ { HI3519_FIXED_340M, "340m", NULL, CLK_IS_ROOT, 340000000, }, ++ { HI3519_FIXED_72M, "72m", NULL, CLK_IS_ROOT, 72000000, }, ++ { HI3519_FIXED_400M, "400m", NULL, CLK_IS_ROOT, 400000000, }, ++ { HI3519_FIXED_200M, "200m", NULL, CLK_IS_ROOT, 200000000, }, ++ { HI3519_FIXED_54M, "54m", NULL, CLK_IS_ROOT, 54000000, }, ++ { HI3519_FIXED_27M, "27m", NULL, CLK_IS_ROOT, 1188000000, }, ++ { HI3519_FIXED_37P125M, "37p125m", NULL, CLK_IS_ROOT, 37125000, }, ++ { HI3519_FIXED_3000M, "3000m", NULL, CLK_IS_ROOT, 3000000000UL, }, ++ { HI3519_FIXED_1500M, "1500m", NULL, CLK_IS_ROOT, 1500000000, }, ++ { HI3519_FIXED_500M, "500m", NULL, CLK_IS_ROOT, 500000000, }, ++ { HI3519_FIXED_250M, "250m", NULL, CLK_IS_ROOT, 250000000, }, ++ { HI3519_FIXED_125M, "125m", NULL, CLK_IS_ROOT, 125000000, }, ++ { HI3519_FIXED_1000M, "1000m", NULL, CLK_IS_ROOT, 1000000000, }, ++ { HI3519_FIXED_600M, "600m", NULL, CLK_IS_ROOT, 600000000, }, ++ { HI3519_FIXED_750M, "750m", NULL, CLK_IS_ROOT, 750000000, }, ++ { HI3519_FIXED_150M, "150m", NULL, CLK_IS_ROOT, 150000000, }, ++ { HI3519_FIXED_75M, "75m", NULL, CLK_IS_ROOT, 75000000, }, ++ { HI3519_FIXED_300M, "300m", NULL, CLK_IS_ROOT, 300000000, }, ++ { HI3519_FIXED_60M, "60m", NULL, CLK_IS_ROOT, 60000000, }, ++ { HI3519_FIXED_214M, "214m", NULL, CLK_IS_ROOT, 214000000, }, ++ { HI3519_FIXED_107M, "107m", NULL, CLK_IS_ROOT, 107000000, }, ++ { HI3519_FIXED_100M, "100m", NULL, CLK_IS_ROOT, 100000000, }, ++ { HI3519_FIXED_50M, "50m", NULL, CLK_IS_ROOT, 50000000, }, ++ { HI3519_FIXED_25M, "25m", NULL, CLK_IS_ROOT, 25000000, }, ++ { HI3519_FIXED_24M, "24m", NULL, CLK_IS_ROOT, 24000000, }, ++ { HI3519_FIXED_3M, "3m", NULL, CLK_IS_ROOT, 3000000, }, ++}; ++ ++static const char *sysaxi_mux_p[] __initconst = {"24m", "200m", }; ++static u32 sysaxi_mux_table[] = {0, 1}; ++ ++static const char *fmc_mux_p[] __initconst = { ++ "24m", "75m", "125m", "150m", "200m", "250m", "300m", "400m", }; ++static u32 fmc_mux_table[] = {0, 1, 2, 3, 4, 5, 6, 7}; ++ ++static const char *mmc_mux_p[] __initconst = { ++ "100m", "200m", "300m", "400m", "594m", "792m", }; ++static u32 mmc_mux_table[] = {0, 1, 2, 3, 4, 5}; ++ ++static const char *a17_mux_p[] __initconst = { ++ "400m", "500m", "594m", "792m", "1000m", "apll"}; ++static u32 a17_mux_table[] = {7, 6, 5, 4, 3, 1}; ++ ++static const char *i2c_mux_p[] __initconst = {"clk_sysapb", "50m"}; ++static u32 i2c_mux_table[] = {0, 1}; ++ ++static struct hisi_mux_clock hi3519_mux_clks[] __initdata = { ++ { HI3519_SYSAXI_MUX, "sysaxi_mux", sysaxi_mux_p, ++ ARRAY_SIZE(sysaxi_mux_p), ++ CLK_SET_RATE_PARENT, 0x34, 12, 2, 0, sysaxi_mux_table, }, ++ { HI3519_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, }, ++ { HI3519_MMC0_MUX, "mmc0_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 10, 3, 0, mmc_mux_table, }, ++ { HI3519_MMC1_MUX, "mmc1_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 2, 3, 0, mmc_mux_table, }, ++ { HI3519_MMC2_MUX, "mmc2_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 18, 3, 0, mmc_mux_table, }, ++ { HI3519_A17_MUX, "a17_mux", a17_mux_p, ARRAY_SIZE(a17_mux_p), ++ CLK_SET_RATE_PARENT, 0x34, 4, 3, 0, a17_mux_table, }, ++ { HI3519_I2C_MUX, "i2c_mux", i2c_mux_p, ARRAY_SIZE(i2c_mux_p), ++ CLK_SET_RATE_PARENT, 0xe4, 26, 1, 0, i2c_mux_table, }, ++}; ++ ++static struct hisi_fixed_factor_clock hi3519_fixed_factor_clks[] __initdata = { ++ { HI3519_SYSAPB_CLK, "clk_sysapb", "sysaxi_mux", 1, 4, ++ CLK_SET_RATE_PARENT}, ++ { HI3519_MMC0_FAC_CLK, "mmc0_fac", "mmc0_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++ { HI3519_MMC1_FAC_CLK, "mmc1_fac", "mmc1_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++ { HI3519_MMC2_FAC_CLK, "mmc2_fac", "mmc2_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++}; ++ ++static struct hisi_gate_clock hi3519_gate_clks[] __initdata = { ++#ifdef CONFIG_HIFMC ++ /* fmc */ ++ { HI3519_FMC_CLK, "clk_fmc", "fmc_mux", ++ CLK_SET_RATE_PARENT, 0xc0, 1, 0, }, ++#endif ++ /* mmc */ ++ { HI3519_MMC0_CLK, "clk_mmc0", "mmc0_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 9, 0, }, ++ { HI3519_MMC1_CLK, "clk_mmc1", "mmc1_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 1, 0, }, ++ { HI3519_MMC2_CLK, "clk_mmc2", "mmc2_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 17, 0, }, ++ ++ /* usb ctrl */ ++ { HI3519_USB2_CTRL_UTMI0_REQ, "usb2_cttl_utmi0_req", NULL, ++ CLK_SET_RATE_PARENT, 0xb4, 5, 1, }, ++ { HI3519_USB2_HRST_REQ, "usb2_hrst_req", NULL, ++ CLK_SET_RATE_PARENT, 0xb4, 0, 1, }, ++ { HI3519_USB3_CLK, "usb3_vcc_srst_req2", NULL, ++ CLK_SET_RATE_PARENT, 0xb8, 0, 1, }, ++ /* uart */ ++ { HI3519_UART0_CLK, "clk_uart0", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 20, 0, }, ++ { HI3519_UART1_CLK, "clk_uart1", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 21, 0, }, ++ { HI3519_UART2_CLK, "clk_uart2", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 22, 0, }, ++ { HI3519_UART3_CLK, "clk_uart3", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 23, 0, }, ++ { HI3519_UART4_CLK, "clk_uart4", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 24, 0, }, ++ /* ethernet mac */ ++ { HI3519_ETH_CLK, "clk_eth", NULL, ++ CLK_IS_ROOT, 0xcc, 1, 0, }, ++ { HI3519_ETH_MACIF_CLK, "clk_eth_macif", NULL, ++ CLK_IS_ROOT, 0xcc, 3, 0, }, ++ /* spi */ ++ { HI3519_SPI0_CLK, "clk_spi0", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 16, 0, }, ++ { HI3519_SPI1_CLK, "clk_spi1", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 17, 0, }, ++ { HI3519_SPI2_CLK, "clk_spi2", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 18, 0, }, ++}; ++ ++static struct hi3519_pll_clock hi3519_pll_clks[] __initdata = { ++ { HI3519_APLL_CLK, "apll", NULL, 0x0, 0, 24, 24, 3, 28, 3, ++ 0x4, 0, 12, 12, 6}, ++}; ++ ++ ++#define to_pll_clk(_hw) container_of(_hw, struct hi3519_clk_pll, hw) ++ ++static void hi3519_calc_pll(u32 *frac_val, u32 *postdiv1_val, u32 *postdiv2_val, ++ u32 *fbdiv_val, u32 *refdiv_val, unsigned long rate) ++{ ++ u64 rem; ++ *frac_val = 0; ++ rem = do_div(rate, 1000000); ++ *fbdiv_val = rate; ++ *refdiv_val = 24; ++ rem = rem * (1 << 24); ++ do_div(rem, 1000000); ++ *frac_val = rem; ++} ++ ++static int clk_pll_set_rate(struct clk_hw *hw, ++ unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct hi3519_clk_pll *clk = to_pll_clk(hw); ++ u32 frac_val, postdiv1_val, postdiv2_val, fbdiv_val, refdiv_val; ++ u32 val; ++ ++ /*Fixme ignore postdives now because apll don't use them*/ ++ postdiv1_val = postdiv2_val = 0; ++ ++ hi3519_calc_pll(&frac_val, &postdiv1_val, &postdiv2_val, ++ &fbdiv_val, &refdiv_val, rate); ++ ++ val = readl_relaxed(clk->ctrl_reg1); ++ val &= ~(((1 << clk->frac_width) - 1) << clk->frac_shift); ++ val &= ~(((1 << clk->postdiv1_width) - 1) << clk->postdiv1_shift); ++ val &= ~(((1 << clk->postdiv2_width) - 1) << clk->postdiv2_shift); ++ ++ val |= frac_val << clk->frac_shift; ++ val |= postdiv1_val << clk->postdiv1_shift; ++ val |= postdiv2_val << clk->postdiv2_shift; ++ writel_relaxed(val, clk->ctrl_reg1); ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val &= ~(((1 << clk->fbdiv_width) - 1) << clk->fbdiv_shift); ++ val &= ~(((1 << clk->refdiv_width) - 1) << clk->refdiv_shift); ++ ++ val |= fbdiv_val << clk->fbdiv_shift; ++ val |= refdiv_val << clk->refdiv_shift; ++ writel_relaxed(val, clk->ctrl_reg2); ++ ++ return 0; ++} ++ ++static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct hi3519_clk_pll *clk = to_pll_clk(hw); ++ u64 frac_val, fbdiv_val, refdiv_val; ++ u32 val; ++ u64 tmp, rate; ++ ++ val = readl_relaxed(clk->ctrl_reg1); ++ val = val >> clk->frac_shift; ++ val &= ((1 << clk->frac_width) - 1); ++ frac_val = val; ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val = val >> clk->fbdiv_shift; ++ val &= ((1 << clk->fbdiv_width) - 1); ++ fbdiv_val = val; ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val = val >> clk->refdiv_shift; ++ val &= ((1 << clk->refdiv_width) - 1) ; ++ refdiv_val = val; ++ ++ /* rate = 24000000 * (fbdiv + frac / (1<<24) ) / refdiv */ ++ rate = 0; ++ tmp = 24000000 * fbdiv_val; ++ rate += tmp; ++#if 0 ++ tmp = 24000000 * frac_val; ++ do_div(tmp, (1<<24)); ++ rate += tmp; ++#endif ++ do_div(rate, refdiv_val); ++ ++ return rate; ++} ++ ++ ++static long clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *best_parent_rate, ++ struct clk **best_parent_p) ++{ ++ return rate; ++} ++ ++static struct clk_ops clk_pll_ops = { ++ .set_rate = clk_pll_set_rate, ++ .determine_rate = clk_pll_determine_rate, ++ .recalc_rate = clk_pll_recalc_rate, ++}; ++ ++void __init hi3519_clk_register_pll(struct hi3519_pll_clock *clks, ++ int nums, struct hisi_clock_data *data) ++{ ++ void __iomem *base = data->base; ++ int i; ++ ++ for (i = 0; i < nums; i++) { ++ struct hi3519_clk_pll *p_clk; ++ struct clk *clk; ++ struct clk_init_data init; ++ ++ p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL); ++ if (!p_clk) ++ return; ++ ++ init.name = clks[i].name; ++ init.flags = CLK_IS_BASIC; ++ init.parent_names = ++ (clks[i].parent_name ? &clks[i].parent_name : NULL); ++ init.num_parents = (clks[i].parent_name ? 1 : 0); ++ init.ops = &clk_pll_ops; ++ ++ p_clk->ctrl_reg1 = base + clks[i].ctrl_reg1; ++ p_clk->frac_shift = clks[i].frac_shift; ++ p_clk->frac_width = clks[i].frac_width; ++ p_clk->postdiv1_shift = clks[i].postdiv1_shift; ++ p_clk->postdiv1_width = clks[i].postdiv1_width; ++ p_clk->postdiv2_shift = clks[i].postdiv2_shift; ++ p_clk->postdiv2_width = clks[i].postdiv2_width; ++ ++ p_clk->ctrl_reg2 = base + clks[i].ctrl_reg2; ++ p_clk->fbdiv_shift = clks[i].fbdiv_shift; ++ p_clk->fbdiv_width = clks[i].fbdiv_width; ++ p_clk->refdiv_shift = clks[i].refdiv_shift; ++ p_clk->refdiv_width = clks[i].refdiv_width; ++ p_clk->hw.init = &init; ++ ++ clk = clk_register(NULL, &p_clk->hw); ++ if (IS_ERR(clk)) { ++ kfree(p_clk); ++ pr_err("%s: failed to register clock %s\n", ++ __func__, clks[i].name); ++ continue; ++ } ++ ++ data->clk_data.clks[clks[i].id] = clk; ++ } ++ ++ ++} ++ ++static void __init hi3519_clk_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ ++ clk_data = hisi_clk_init(np, HI3519_NR_CLKS); ++ if (!clk_data) ++ return; ++ if (IS_ENABLED(CONFIG_RESET_CONTROLLER)) ++ hisi_reset_init(np, HI3519_NR_RSTS); ++ ++ hisi_clk_register_fixed_rate(hi3519_fixed_rate_clks, ++ ARRAY_SIZE(hi3519_fixed_rate_clks), ++ clk_data); ++ hi3519_clk_register_pll(hi3519_pll_clks, ++ ARRAY_SIZE(hi3519_pll_clks), clk_data); ++ ++ hisi_clk_register_mux(hi3519_mux_clks, ARRAY_SIZE(hi3519_mux_clks), ++ clk_data); ++ hisi_clk_register_fixed_factor(hi3519_fixed_factor_clks, ++ ARRAY_SIZE(hi3519_fixed_factor_clks), clk_data); ++ hisi_clk_register_gate(hi3519_gate_clks, ++ ARRAY_SIZE(hi3519_gate_clks), clk_data); ++} ++ ++CLK_OF_DECLARE(hi3519_clk, "hisilicon,hi3519-clock", hi3519_clk_init); +diff --git a/drivers/clk/hisilicon/clk-hi3519v101.c b/drivers/clk/hisilicon/clk-hi3519v101.c +new file mode 100644 +index 0000000..1f30e6c +--- /dev/null ++++ b/drivers/clk/hisilicon/clk-hi3519v101.c +@@ -0,0 +1,385 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/of_address.h> ++#include <dt-bindings/clock/hi3519-clock.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <mach/io.h> ++ ++#include "clk.h" ++#include "reset.h" ++ ++/******************************************************************************/ ++struct hi3519v101_pll_clock { ++ u32 id; ++ const char *name; ++ const char *parent_name; ++ u32 ctrl_reg1; ++ u8 frac_shift; ++ u8 frac_width; ++ u8 postdiv1_shift; ++ u8 postdiv1_width; ++ u8 postdiv2_shift; ++ u8 postdiv2_width; ++ u32 ctrl_reg2; ++ u8 fbdiv_shift; ++ u8 fbdiv_width; ++ u8 refdiv_shift; ++ u8 refdiv_width; ++}; ++ ++struct hi3519v101_clk_pll { ++ struct clk_hw hw; ++ u32 id; ++ void __iomem *ctrl_reg1; ++ u8 frac_shift; ++ u8 frac_width; ++ u8 postdiv1_shift; ++ u8 postdiv1_width; ++ u8 postdiv2_shift; ++ u8 postdiv2_width; ++ void __iomem *ctrl_reg2; ++ u8 fbdiv_shift; ++ u8 fbdiv_width; ++ u8 refdiv_shift; ++ u8 refdiv_width; ++}; ++ ++static struct hisi_fixed_rate_clock hi3519v101_fixed_rate_clks[] __initdata = { ++ { HI3519_FIXED_2376M, "2376m", NULL, CLK_IS_ROOT, 2376000000UL, }, ++ { HI3519_FIXED_1188M, "1188m", NULL, CLK_IS_ROOT, 1188000000, }, ++ { HI3519_FIXED_594M, "594m", NULL, CLK_IS_ROOT, 594000000, }, ++ { HI3519_FIXED_297M, "297m", NULL, CLK_IS_ROOT, 297000000, }, ++ { HI3519_FIXED_148P5M, "148p5m", NULL, CLK_IS_ROOT, 148500000, }, ++ { HI3519_FIXED_74P25M, "74p25m", NULL, CLK_IS_ROOT, 74250000, }, ++ { HI3519_FIXED_792M, "792m", NULL, CLK_IS_ROOT, 792000000, }, ++ { HI3519_FIXED_475M, "475m", NULL, CLK_IS_ROOT, 475000000, }, ++ { HI3519_FIXED_340M, "340m", NULL, CLK_IS_ROOT, 340000000, }, ++ { HI3519_FIXED_72M, "72m", NULL, CLK_IS_ROOT, 72000000, }, ++ { HI3519_FIXED_400M, "400m", NULL, CLK_IS_ROOT, 400000000, }, ++ { HI3519_FIXED_200M, "200m", NULL, CLK_IS_ROOT, 200000000, }, ++ { HI3519_FIXED_54M, "54m", NULL, CLK_IS_ROOT, 54000000, }, ++ { HI3519_FIXED_27M, "27m", NULL, CLK_IS_ROOT, 1188000000, }, ++ { HI3519_FIXED_37P125M, "37p125m", NULL, CLK_IS_ROOT, 37125000, }, ++ { HI3519_FIXED_3000M, "3000m", NULL, CLK_IS_ROOT, 3000000000UL, }, ++ { HI3519_FIXED_1500M, "1500m", NULL, CLK_IS_ROOT, 1500000000, }, ++ { HI3519_FIXED_500M, "500m", NULL, CLK_IS_ROOT, 500000000, }, ++ { HI3519_FIXED_250M, "250m", NULL, CLK_IS_ROOT, 250000000, }, ++ { HI3519_FIXED_125M, "125m", NULL, CLK_IS_ROOT, 125000000, }, ++ { HI3519_FIXED_1000M, "1000m", NULL, CLK_IS_ROOT, 1000000000, }, ++ { HI3519_FIXED_600M, "600m", NULL, CLK_IS_ROOT, 600000000, }, ++ { HI3519_FIXED_750M, "750m", NULL, CLK_IS_ROOT, 750000000, }, ++ { HI3519_FIXED_150M, "150m", NULL, CLK_IS_ROOT, 150000000, }, ++ { HI3519_FIXED_75M, "75m", NULL, CLK_IS_ROOT, 75000000, }, ++ { HI3519_FIXED_300M, "300m", NULL, CLK_IS_ROOT, 300000000, }, ++ { HI3519_FIXED_60M, "60m", NULL, CLK_IS_ROOT, 60000000, }, ++ { HI3519_FIXED_214M, "214m", NULL, CLK_IS_ROOT, 214000000, }, ++ { HI3519_FIXED_107M, "107m", NULL, CLK_IS_ROOT, 107000000, }, ++ { HI3519_FIXED_100M, "100m", NULL, CLK_IS_ROOT, 100000000, }, ++ { HI3519_FIXED_50M, "50m", NULL, CLK_IS_ROOT, 50000000, }, ++ { HI3519_FIXED_25M, "25m", NULL, CLK_IS_ROOT, 25000000, }, ++ { HI3519_FIXED_24M, "24m", NULL, CLK_IS_ROOT, 24000000, }, ++ { HI3519_FIXED_3M, "3m", NULL, CLK_IS_ROOT, 3000000, }, ++ { HI3519_FIXED_198M, "198m", NULL, CLK_IS_ROOT, 198000000, }, ++ { HI3519_FIXED_396M, "396m", NULL, CLK_IS_ROOT, 396000000, }, ++}; ++ ++static const char *sysaxi_mux_p[] __initconst = {"24m", "198m", }; ++static u32 sysaxi_mux_table[] = {0, 1}; ++ ++static const char *fmc_mux_p[] __initconst = { ++ "24m", "75m", "125m", "150m", "198m", "250m", "300m", "396m", }; ++static u32 fmc_mux_table[] = {0, 1, 2, 3, 4, 5, 6, 7}; ++ ++static const char *mmc_mux_p[] __initconst = { ++ "100m", "198m", "396m", "594m", "792m", "1188m", }; ++static u32 mmc_mux_table[] = {0, 1, 3, 4, 5, 7}; ++ ++static const char *a17_mux_p[] __initconst = { ++ "24m", "apll", "594m", "792m", }; ++static u32 a17_mux_table[] = {0, 1, 3, 4}; ++ ++static const char *i2c_mux_p[] __initconst = {"clk_sysapb", "50m"}; ++static u32 i2c_mux_table[] = {0, 1}; ++ ++static struct hisi_mux_clock hi3519v101_mux_clks[] __initdata = { ++ { HI3519_SYSAXI_MUX, "sysaxi_mux", sysaxi_mux_p, ++ ARRAY_SIZE(sysaxi_mux_p), ++ CLK_SET_RATE_PARENT, 0x34, 12, 2, 0, sysaxi_mux_table, }, ++ { HI3519_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, }, ++ { HI3519_MMC0_MUX, "mmc0_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 10, 3, 0, mmc_mux_table, }, ++ { HI3519_MMC1_MUX, "mmc1_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 2, 3, 0, mmc_mux_table, }, ++ { HI3519_MMC2_MUX, "mmc2_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 18, 3, 0, mmc_mux_table, }, ++ { HI3519_A17_MUX, "a17_mux", a17_mux_p, ARRAY_SIZE(a17_mux_p), ++ CLK_SET_RATE_PARENT, 0x34, 4, 3, 0, a17_mux_table, }, ++ { HI3519_I2C_MUX, "i2c_mux", i2c_mux_p, ARRAY_SIZE(i2c_mux_p), ++ CLK_SET_RATE_PARENT, 0xe4, 26, 1, 0, i2c_mux_table, }, ++ ++}; ++ ++static struct hisi_fixed_factor_clock ++ hi3519v101_fixed_factor_clks[] __initdata = { ++ { HI3519_SYSAPB_CLK, "clk_sysapb", "sysaxi_mux", 1, 4, ++ CLK_SET_RATE_PARENT}, ++ { HI3519_MMC0_FAC_CLK, "mmc0_fac", "mmc0_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++ { HI3519_MMC1_FAC_CLK, "mmc1_fac", "mmc1_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++ { HI3519_MMC2_FAC_CLK, "mmc2_fac", "mmc2_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++}; ++ ++static struct hisi_gate_clock hi3519v101_gate_clks[] __initdata = { ++#ifdef CONFIG_HIFMC ++ /* fmc */ ++ { HI3519_FMC_CLK, "clk_fmc", "fmc_mux", ++ CLK_SET_RATE_PARENT, 0xc0, 1, 0, }, ++#endif ++ /* mmc */ ++ { HI3519_MMC0_CLK, "clk_mmc0", "mmc0_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 9, 0, }, ++ { HI3519_MMC1_CLK, "clk_mmc1", "mmc1_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 1, 0, }, ++ { HI3519_MMC2_CLK, "clk_mmc2", "mmc2_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 17, 0, }, ++ ++ /* usb ctrl */ ++ { HI3519_USB2_CTRL_UTMI0_REQ, "usb2_cttl_utmi0_req", NULL, ++ CLK_SET_RATE_PARENT, 0xb4, 5, 1, }, ++ { HI3519_USB2_HRST_REQ, "usb2_hrst_req", NULL, ++ CLK_SET_RATE_PARENT, 0xb4, 0, 1, }, ++ { HI3519_USB3_CLK, "usb3_vcc_srst_req2", NULL, ++ CLK_SET_RATE_PARENT, 0xb8, 0, 1, }, ++ ++ /* uart */ ++ { HI3519_UART0_CLK, "clk_uart0", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 20, 0, }, ++ { HI3519_UART1_CLK, "clk_uart1", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 21, 0, }, ++ { HI3519_UART2_CLK, "clk_uart2", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 22, 0, }, ++ { HI3519_UART3_CLK, "clk_uart3", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 23, 0, }, ++ { HI3519_UART4_CLK, "clk_uart4", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 24, 0, }, ++ ++ /* ethernet mac */ ++ { HI3519_ETH_CLK, "clk_eth", NULL, ++ CLK_IS_ROOT, 0xcc, 1, 0, }, ++ { HI3519_ETH_MACIF_CLK, "clk_eth_macif", NULL, ++ CLK_IS_ROOT, 0xcc, 3, 0, }, ++ ++ /* spi */ ++ { HI3519_SPI0_CLK, "clk_spi0", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 16, 0, }, ++ { HI3519_SPI1_CLK, "clk_spi1", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 17, 0, }, ++ { HI3519_SPI2_CLK, "clk_spi2", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 18, 0, }, ++ { HI3519_SPI3_CLK, "clk_spi3", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 28, 0, }, ++}; ++ ++static struct hi3519v101_pll_clock hi3519v101_pll_clks[] __initdata = { ++ { HI3519_APLL_CLK, "apll", NULL, 0x0, 0, 24, 24, 3, 28, 3, ++ 0x4, 0, 12, 12, 6}, ++}; ++ ++ ++#define to_pll_clk(_hw) container_of(_hw, struct hi3519v101_clk_pll, hw) ++ ++static void hi3519v101_calc_pll(u32 *frac_val, ++ u32 *postdiv1_val, ++ u32 *postdiv2_val, ++ u32 *fbdiv_val, ++ u32 *refdiv_val, ++ unsigned long rate) ++{ ++ u64 rem; ++ *frac_val = 0; ++ rem = do_div(rate, 1000000); ++ *fbdiv_val = rate; ++ *refdiv_val = 24; ++ rem = rem * (1 << 24); ++ do_div(rem, 1000000); ++ *frac_val = rem; ++} ++ ++static int clk_pll_set_rate(struct clk_hw *hw, ++ unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct hi3519v101_clk_pll *clk = to_pll_clk(hw); ++ u32 frac_val, postdiv1_val, postdiv2_val, fbdiv_val, refdiv_val; ++ u32 val; ++ ++ /*Fixme ignore postdives now because apll don't use them*/ ++ postdiv1_val = postdiv2_val = 0; ++ ++ hi3519v101_calc_pll(&frac_val, &postdiv1_val, &postdiv2_val, ++ &fbdiv_val, &refdiv_val, rate); ++ ++ val = readl_relaxed(clk->ctrl_reg1); ++ val &= ~(((1 << clk->frac_width) - 1) << clk->frac_shift); ++ val &= ~(((1 << clk->postdiv1_width) - 1) << clk->postdiv1_shift); ++ val &= ~(((1 << clk->postdiv2_width) - 1) << clk->postdiv2_shift); ++ ++ val |= frac_val << clk->frac_shift; ++ val |= postdiv1_val << clk->postdiv1_shift; ++ val |= postdiv2_val << clk->postdiv2_shift; ++ writel_relaxed(val, clk->ctrl_reg1); ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val &= ~(((1 << clk->fbdiv_width) - 1) << clk->fbdiv_shift); ++ val &= ~(((1 << clk->refdiv_width) - 1) << clk->refdiv_shift); ++ ++ val |= fbdiv_val << clk->fbdiv_shift; ++ val |= refdiv_val << clk->refdiv_shift; ++ writel_relaxed(val, clk->ctrl_reg2); ++ ++ return 0; ++} ++ ++static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct hi3519v101_clk_pll *clk = to_pll_clk(hw); ++ u64 frac_val, fbdiv_val, refdiv_val; ++ u32 val; ++ u64 tmp, rate; ++ ++ val = readl_relaxed(clk->ctrl_reg1); ++ val = val >> clk->frac_shift; ++ val &= ((1 << clk->frac_width) - 1); ++ frac_val = val; ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val = val >> clk->fbdiv_shift; ++ val &= ((1 << clk->fbdiv_width) - 1); ++ fbdiv_val = val; ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val = val >> clk->refdiv_shift; ++ val &= ((1 << clk->refdiv_width) - 1); ++ refdiv_val = val; ++ ++ /* rate = 24000000 * (fbdiv + frac / (1<<24) ) / refdiv */ ++ rate = 0; ++ tmp = 24000000 * fbdiv_val; ++ rate += tmp; ++ do_div(rate, refdiv_val); ++ ++ return rate; ++} ++ ++ ++static long clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *best_parent_rate, ++ struct clk **best_parent_p) ++{ ++ return rate; ++} ++ ++static struct clk_ops clk_pll_ops = { ++ .set_rate = clk_pll_set_rate, ++ .determine_rate = clk_pll_determine_rate, ++ .recalc_rate = clk_pll_recalc_rate, ++}; ++ ++void __init hi3519v101_clk_register_pll(struct hi3519v101_pll_clock *clks, ++ int nums, struct hisi_clock_data *data) ++{ ++ void __iomem *base = data->base; ++ int i; ++ ++ for (i = 0; i < nums; i++) { ++ struct hi3519v101_clk_pll *p_clk; ++ struct clk *clk; ++ struct clk_init_data init; ++ ++ p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL); ++ if (!p_clk) ++ return; ++ ++ init.name = clks[i].name; ++ init.flags = CLK_IS_BASIC; ++ init.parent_names = ++ (clks[i].parent_name ? &clks[i].parent_name : NULL); ++ init.num_parents = (clks[i].parent_name ? 1 : 0); ++ init.ops = &clk_pll_ops; ++ ++ p_clk->ctrl_reg1 = base + clks[i].ctrl_reg1; ++ p_clk->frac_shift = clks[i].frac_shift; ++ p_clk->frac_width = clks[i].frac_width; ++ p_clk->postdiv1_shift = clks[i].postdiv1_shift; ++ p_clk->postdiv1_width = clks[i].postdiv1_width; ++ p_clk->postdiv2_shift = clks[i].postdiv2_shift; ++ p_clk->postdiv2_width = clks[i].postdiv2_width; ++ ++ p_clk->ctrl_reg2 = base + clks[i].ctrl_reg2; ++ p_clk->fbdiv_shift = clks[i].fbdiv_shift; ++ p_clk->fbdiv_width = clks[i].fbdiv_width; ++ p_clk->refdiv_shift = clks[i].refdiv_shift; ++ p_clk->refdiv_width = clks[i].refdiv_width; ++ p_clk->hw.init = &init; ++ ++ clk = clk_register(NULL, &p_clk->hw); ++ if (IS_ERR(clk)) { ++ kfree(p_clk); ++ pr_err("%s: failed to register clock %s\n", ++ __func__, clks[i].name); ++ continue; ++ } ++ ++ data->clk_data.clks[clks[i].id] = clk; ++ } ++ ++ ++} ++ ++static void __init hi3519v101_clk_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ ++ clk_data = hisi_clk_init(np, HI3519_NR_CLKS); ++ if (!clk_data) ++ return; ++ if (IS_ENABLED(CONFIG_RESET_CONTROLLER)) ++ hisi_reset_init(np, HI3519_NR_RSTS); ++ ++ hisi_clk_register_fixed_rate(hi3519v101_fixed_rate_clks, ++ ARRAY_SIZE(hi3519v101_fixed_rate_clks), ++ clk_data); ++ hi3519v101_clk_register_pll(hi3519v101_pll_clks, ++ ARRAY_SIZE(hi3519v101_pll_clks), clk_data); ++ ++ hisi_clk_register_mux(hi3519v101_mux_clks, ++ ARRAY_SIZE(hi3519v101_mux_clks), ++ clk_data); ++ hisi_clk_register_fixed_factor(hi3519v101_fixed_factor_clks, ++ ARRAY_SIZE(hi3519v101_fixed_factor_clks), clk_data); ++ hisi_clk_register_gate(hi3519v101_gate_clks, ++ ARRAY_SIZE(hi3519v101_gate_clks), clk_data); ++} ++ ++CLK_OF_DECLARE(hi3519v101_clk, "hisilicon,hi3519v101-clock", ++ hi3519v101_clk_init); +diff --git a/drivers/clk/hisilicon/clk-hi3521d.c b/drivers/clk/hisilicon/clk-hi3521d.c +new file mode 100644 +index 0000000..a74cc92 +--- /dev/null ++++ b/drivers/clk/hisilicon/clk-hi3521d.c +@@ -0,0 +1,179 @@ ++/* ++ * Hi3521D Clock Driver ++ * ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <dt-bindings/clock/hi3521d-clock.h> ++#include "clk.h" ++#include "reset.h" ++ ++static struct ++hisi_fixed_rate_clock hi3521d_fixed_rate_clks_crg[] __initdata = { ++ { HI3521D_FIXED_3M, "3m", NULL, CLK_IS_ROOT, 3000000, }, ++ { HI3521D_FIXED_6M, "6m", NULL, CLK_IS_ROOT, 6000000, }, ++ { HI3521D_FIXED_12M, "12m", NULL, CLK_IS_ROOT, 12000000, }, ++ { HI3521D_FIXED_24M, "24m", NULL, CLK_IS_ROOT, 24000000, }, ++ { HI3521D_FIXED_83P3M, "83.3m", NULL, CLK_IS_ROOT, 83300000, }, ++ { HI3521D_FIXED_100M, "100m", NULL, CLK_IS_ROOT, 100000000, }, ++ { HI3521D_FIXED_125M, "125m", NULL, CLK_IS_ROOT, 125000000, }, ++ { HI3521D_FIXED_150M, "150m", NULL, CLK_IS_ROOT, 150000000, }, ++ { HI3521D_FIXED_200M, "200m", NULL, CLK_IS_ROOT, 200000000, }, ++ { HI3521D_FIXED_250M, "250m", NULL, CLK_IS_ROOT, 250000000, }, ++ { HI3521D_FIXED_300M, "300m", NULL, CLK_IS_ROOT, 300000000, }, ++ { HI3521D_FIXED_324M, "324m", NULL, CLK_IS_ROOT, 324000000, }, ++ { HI3521D_FIXED_342M, "342m", NULL, CLK_IS_ROOT, 342000000, }, ++ { HI3521D_FIXED_342M, "375m", NULL, CLK_IS_ROOT, 375000000, }, ++ { HI3521D_FIXED_400M, "400m", NULL, CLK_IS_ROOT, 400000000, }, ++ { HI3521D_FIXED_448M, "448m", NULL, CLK_IS_ROOT, 448000000, }, ++ { HI3521D_FIXED_500M, "500m", NULL, CLK_IS_ROOT, 500000000, }, ++ { HI3521D_FIXED_540M, "540m", NULL, CLK_IS_ROOT, 540000000, }, ++ { HI3521D_FIXED_600M, "600m", NULL, CLK_IS_ROOT, 600000000, }, ++ { HI3521D_FIXED_750M, "750m", NULL, CLK_IS_ROOT, 750000000, }, ++ { HI3521D_FIXED_1500M, "1500m", NULL, CLK_IS_ROOT, 1500000000UL, }, ++}; ++ ++static const char *sysaxi_mux_p[] __initconst = { ++ "24m", "200m", "250m", "300m"}; ++/* If syaaxi bus clock is 200MHz, so the APB clock is 50MHz, factor is 4 */ ++static const char *uart_mux_p[] __initconst = {"sysaxi_mux", "24m", "2m"}; ++static const char *fmc_mux_p[] __initconst = { ++ "24m", "83.3m", "150m"}; ++ ++static const char *viu_mux_p[] = {"300m", "200m", "150m"}; ++static u32 sysaxi_mux_table[] = {0, 1, 2, 3}; ++static u32 uart_mux_table[] = {0, 1, 2}; ++static u32 fmc_mux_table[] = {0, 1, 2}; ++static u32 viu_mux_p_table[] = {0, 1, 2}; ++ ++static struct hisi_mux_clock hi3521d_mux_clks_crg[] __initdata = { ++ { HI3521D_SYSAXI_CLK, "sysaxi_mux", sysaxi_mux_p, ++ ARRAY_SIZE(sysaxi_mux_p), ++ CLK_SET_RATE_PARENT, 0x50, 2, 2, 0, sysaxi_mux_table, }, ++ { HI3521D_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), ++ CLK_SET_RATE_PARENT, 0x148, 2, 2, 0, fmc_mux_table, }, ++ { HI3521D_UART_MUX, "uart_mux", uart_mux_p, ++ ARRAY_SIZE(uart_mux_p), ++ CLK_SET_RATE_PARENT, 0x154, 19, 2, 0, uart_mux_table, }, ++ { HI3521D_VIU_MUX, "viu_mux", viu_mux_p, ARRAY_SIZE(viu_mux_p), ++ CLK_SET_RATE_PARENT, 0x98, 5, 2, 0, viu_mux_p_table, }, ++}; ++ ++static struct hisi_fixed_factor_clock ++ hi3521d_fixed_factor_clks[] __initdata = { ++ { HI3521D_SYSAXI_CLK, "clk_sysaxi", "sysaxi_mux", 1, 4, ++ CLK_SET_RATE_PARENT}, ++}; ++ ++static struct hisi_gate_clock hi3521d_gate_clks[] __initdata = { ++#ifdef CONFIG_HIFMC ++ /* fmc */ ++ { HI3521D_FMC_CLK, "clk_fmc", "fmc_mux", ++ CLK_SET_RATE_PARENT, 0x148, 1, 0, }, ++#endif ++ /* uart */ ++ { HI3521D_UART0_CLK, "clk_uart0", "24m", ++ CLK_SET_RATE_PARENT, 0x154, 15, 0, }, ++ { HI3521D_UART1_CLK, "clk_uart1", "24m", ++ CLK_SET_RATE_PARENT, 0x154, 16, 0, }, ++ { HI3521D_UART2_CLK, "clk_uart2", "24m", ++ CLK_SET_RATE_PARENT, 0x154, 17, 0, }, ++ { HI3521D_UART3_CLK, "clk_uart3", "24m", ++ CLK_SET_RATE_PARENT, 0x154, 18, 0, }, ++ /* ethernet mac */ ++ { HI3521D_ETH_PHY_CLK, "clk_eth_phy", NULL, ++ CLK_SET_RATE_PARENT, 0x14c, 6, 0, }, ++ { HI3521D_ETH_PUB_CLK, "clk_eth_pub", "clk_eth_phy", ++ CLK_SET_RATE_PARENT, 0x14c, 7, 0, }, ++ { HI3521D_ETH_CLK, "clk_eth", "clk_eth_pub", ++ CLK_SET_RATE_PARENT, 0x14c, 1, 0, }, ++ { HI3521D_ETH_MACIF_CLK, "clk_eth_macif", "clk_eth_pub", ++ CLK_SET_RATE_PARENT, 0x14c, 3, 0, }, ++ /* spi */ ++ { HI3521D_SPI0_CLK, "clk_spi0", "clk_sysaxi", ++ CLK_SET_RATE_PARENT, 0x154, 13, 0, }, ++ { HI3521D_VIU_CLK, "clk_viu", "viu_mux", CLK_SET_RATE_PARENT, ++ 0x98, 2, 0, }, ++ /* dma */ ++ { HI3521D_DMAC_CLK, "clk_dmac", "50m", CLK_SET_RATE_PARENT, ++ 0x144, 1, 0, }, ++}; ++ ++static void __init hi3521d_clk_crg_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ unsigned int count = 0; ++ ++ clk_data = hisi_clk_init(np, HI3521D_CRG_NR_CLKS); ++ if (!clk_data) ++ return; ++ ++ hisi_clk_register_fixed_rate(hi3521d_fixed_rate_clks_crg, ++ ARRAY_SIZE(hi3521d_fixed_rate_clks_crg), ++ clk_data); ++ hisi_clk_register_mux(hi3521d_mux_clks_crg, ++ ARRAY_SIZE(hi3521d_mux_clks_crg), clk_data); ++ hisi_clk_register_fixed_factor(hi3521d_fixed_factor_clks, ++ ARRAY_SIZE(hi3521d_fixed_factor_clks), clk_data); ++ hisi_clk_register_gate(hi3521d_gate_clks, ++ ARRAY_SIZE(hi3521d_gate_clks), clk_data); ++ ++ if (!of_property_read_u32(np, "#reset-cells", &count) && (count == 2)) ++ hisi_reset_init(np, HI3521D_CRG_NR_RSTS); ++} ++ ++static const char *timer_mux_p[] __initconst = { "3m", "clk_sysapb" }; ++static u32 timer_mux_table[] = {0, 1}; ++ ++static struct hisi_mux_clock hi3521d_mux_clks_sys[] __initdata = { ++ { HI3521D_TIME0_0_CLK, "timer00", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 16, 1, 0, timer_mux_table, }, ++ ++ { HI3521D_TIME0_1_CLK, "timer01", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 18, 1, 0, timer_mux_table, }, ++ ++ { HI3521D_TIME1_2_CLK, "timer12", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 20, 1, 0, timer_mux_table, }, ++ ++ { HI3521D_TIME1_3_CLK, "timer13", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 22, 1, 0, timer_mux_table, }, ++}; ++ ++static void __init hi3521d_clk_sys_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ unsigned int count = 0; ++ ++ clk_data = hisi_clk_init(np, HI3521D_SYS_NR_CLKS); ++ if (!clk_data) ++ return; ++ ++ hisi_clk_register_mux(hi3521d_mux_clks_sys, ++ ARRAY_SIZE(hi3521d_mux_clks_sys), clk_data); ++ ++ if (!of_property_read_u32(np, "#reset-cells", &count) && (count == 2)) ++ hisi_reset_init(np, HI3521D_SYS_NR_RSTS); ++} ++ ++CLK_OF_DECLARE(hi3521d_clk_crg, "hisilicon,hi3521d-clock", ++ hi3521d_clk_crg_init); ++CLK_OF_DECLARE(hi3521d_clk_sys, "hisilicon,sysctrl", hi3521d_clk_sys_init); ++ +diff --git a/drivers/clk/hisilicon/clk-hi3531d.c b/drivers/clk/hisilicon/clk-hi3531d.c +new file mode 100644 +index 0000000..5dc519e +--- /dev/null ++++ b/drivers/clk/hisilicon/clk-hi3531d.c +@@ -0,0 +1,205 @@ ++/* ++ * Hi3531D Clock Driver ++ * ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <dt-bindings/clock/hi3531d-clock.h> ++#include "clk.h" ++#include "reset.h" ++ ++static struct ++hisi_fixed_rate_clock hi3531d_fixed_rate_clks_crg[] __initdata = { ++ { HI3531D_FIXED_3M, "3m", NULL, CLK_IS_ROOT, 3000000, }, ++ { HI3531D_FIXED_6M, "6m", NULL, CLK_IS_ROOT, 6000000, }, ++ { HI3531D_FIXED_12M, "12m", NULL, CLK_IS_ROOT, 12000000, }, ++ { HI3531D_FIXED_15M, "15m", NULL, CLK_IS_ROOT, 15000000, }, ++ { HI3531D_FIXED_24M, "24m", NULL, CLK_IS_ROOT, 24000000, }, ++ { HI3531D_FIXED_44M, "44m", NULL, CLK_IS_ROOT, 44000000, }, ++ { HI3531D_FIXED_49P5, "49.5m", NULL, CLK_IS_ROOT, 49500000, }, ++ { HI3531D_FIXED_50M, "50m", NULL, CLK_IS_ROOT, 50000000, }, ++ { HI3531D_FIXED_54M, "54m", NULL, CLK_IS_ROOT, 54000000, }, ++ { HI3531D_FIXED_75M, "75m", NULL, CLK_IS_ROOT, 75000000, }, ++ { HI3531D_FIXED_83P3M, "83.3m", NULL, CLK_IS_ROOT, 83300000, }, ++ { HI3531D_FIXED_99M, "99m", NULL, CLK_IS_ROOT, 99000000, }, ++ { HI3531D_FIXED_100M, "100m", NULL, CLK_IS_ROOT, 100000000, }, ++ { HI3531D_FIXED_125M, "125m", NULL, CLK_IS_ROOT, 125000000, }, ++ { HI3531D_FIXED_150M, "150m", NULL, CLK_IS_ROOT, 150000000, }, ++ { HI3531D_FIXED_200M, "200m", NULL, CLK_IS_ROOT, 200000000, }, ++ { HI3531D_FIXED_250M, "250m", NULL, CLK_IS_ROOT, 250000000, }, ++ { HI3531D_FIXED_300M, "300m", NULL, CLK_IS_ROOT, 300000000, }, ++ { HI3531D_FIXED_324M, "324m", NULL, CLK_IS_ROOT, 324000000, }, ++ { HI3531D_FIXED_342M, "342m", NULL, CLK_IS_ROOT, 342000000, }, ++ { HI3531D_FIXED_342M, "375m", NULL, CLK_IS_ROOT, 375000000, }, ++ { HI3531D_FIXED_400M, "400m", NULL, CLK_IS_ROOT, 400000000, }, ++ { HI3531D_FIXED_448M, "448m", NULL, CLK_IS_ROOT, 448000000, }, ++ { HI3531D_FIXED_500M, "500m", NULL, CLK_IS_ROOT, 500000000, }, ++ { HI3531D_FIXED_540M, "540m", NULL, CLK_IS_ROOT, 540000000, }, ++ { HI3531D_FIXED_600M, "600m", NULL, CLK_IS_ROOT, 600000000, }, ++ { HI3531D_FIXED_750M, "750m", NULL, CLK_IS_ROOT, 750000000, }, ++ { HI3531D_FIXED_1500M, "1500m", NULL, CLK_IS_ROOT, 1500000000UL, }, ++}; ++ ++static const char *periaxi_mux_p[] __initconst = { ++ "24m", "200m", "300m"}; ++static const char *sysaxi_mux_p[] __initconst = { ++ "24m", "324m", "375m"}; ++/* PERIAXI clock is 200MHz, so the APB clock is 50MHz, factor is 4 */ ++static const char *uart_mux_p[] __initconst = {"clk_sysapb", "24m", "2m"}; ++static const char *fmc_mux_p[] __initconst = { ++ "24m", "83.3m", "125m", "150m"}; ++static const char *nfc_mux_p[] __initconst = { ++ "24m", "200m"}; ++ ++static const char *viu_mux_p[] = {"300m", "200m", "150m"}; ++ ++static u32 periaxi_mux_table[] = {0, 1, 2}; ++static u32 sysaxi_mux_table[] = {0, 1, 2}; ++static u32 uart_mux_table[] = {0, 1, 2}; ++static u32 fmc_mux_table[] = {0, 1, 2, 3}; ++static u32 nfc_mux_table[] = {0, 1}; ++static u32 viu_mux_p_table[] = {0, 1, 2}; ++ ++static struct hisi_mux_clock hi3531d_mux_clks_crg[] __initdata = { ++ { HI3531D_PERIAXI_CLK, "periaxi_mux", periaxi_mux_p, ++ ARRAY_SIZE(periaxi_mux_p), ++ CLK_SET_RATE_PARENT, 0x50, 0, 2, 0, periaxi_mux_table, }, ++ { HI3531D_SYSAXI_CLK, "sysaxi_mux", sysaxi_mux_p, ++ ARRAY_SIZE(sysaxi_mux_p), ++ CLK_SET_RATE_PARENT, 0x50, 2, 2, 0, sysaxi_mux_table, }, ++ { HI3531D_NFC_MUX, "nfc_mux", nfc_mux_p, ARRAY_SIZE(nfc_mux_p), ++ CLK_SET_RATE_PARENT, 0x13c, 2, 1, 0, nfc_mux_table, }, ++ { HI3531D_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), ++ CLK_SET_RATE_PARENT, 0x148, 2, 2, 0, fmc_mux_table, }, ++ { HI3531D_UART_MUX, "uart_mux", uart_mux_p, ++ ARRAY_SIZE(uart_mux_p), ++ CLK_SET_RATE_PARENT, 0x154, 19, 2, 0, uart_mux_table, }, ++ ++ /* media mux clock*/ ++ { HI3531D_VIU_MUX, "viu_mux", viu_mux_p, ARRAY_SIZE(viu_mux_p), ++ CLK_SET_RATE_PARENT, 0x98, 5, 2, 0, viu_mux_p_table, }, ++ ++}; ++ ++static struct hisi_fixed_factor_clock ++ hi3531d_fixed_factor_clks[] __initdata = { ++ { HI3531D_PERIAXI_CLK, "clk_sysapb", "periaxi_mux", 1, 4, ++ CLK_SET_RATE_PARENT}, ++ { HI3531D_SYSAXI_CLK, "clk_sysaxi", "sysaxi_mux", 1, 4, ++ CLK_SET_RATE_PARENT}, ++}; ++ ++static struct hisi_gate_clock hi3531d_gate_clks[] __initdata = { ++#ifdef CONFIG_HIFMC ++ /* fmc */ ++ { HI3531D_FMC_CLK, "clk_fmc", "fmc_mux", ++ CLK_SET_RATE_PARENT, 0x148, 1, 0, }, ++#endif ++#ifdef CONFIG_HINFC ++ /* hinfc610 */ ++ { HI3531D_NFC_CLK, "clk_nfc", "nfc_mux", ++ CLK_SET_RATE_PARENT, 0x13c, 1, 0, }, ++#endif ++ /* uart */ ++ { HI3531D_UART0_CLK, "clk_uart0", "24m", ++ CLK_SET_RATE_PARENT, 0x154, 15, 0, }, ++ { HI3531D_UART1_CLK, "clk_uart1", "24m", ++ CLK_SET_RATE_PARENT, 0x154, 16, 0, }, ++ { HI3531D_UART2_CLK, "clk_uart2", "24m", ++ CLK_SET_RATE_PARENT, 0x154, 17, 0, }, ++ { HI3531D_UART3_CLK, "clk_uart3", "24m", ++ CLK_SET_RATE_PARENT, 0x154, 18, 0, }, ++ /* ethernet mac */ ++ { HI3531D_ETH_CLK, "clk_eth", NULL, ++ CLK_IS_ROOT, 0x14c, 1, 0, }, ++ { HI3531D_ETH_MACIF_CLK, "clk_eth_macif", NULL, ++ CLK_IS_ROOT, 0x14c, 3, 0, }, ++ /* spi */ ++ { HI3531D_SPI0_CLK, "clk_spi0", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0x154, 13, 0, }, ++ /* media gate clock*/ ++ { HI3531D_VIU_CLK, "clk_viu", "viu_mux", CLK_SET_RATE_PARENT, ++ 0x98, 2, 0, }, ++ /* dma */ ++ { HI3531D_DMAC_CLK, "clk_dmac", "50m", CLK_SET_RATE_PARENT, ++ 0x144, 1, 0, }, ++}; ++ ++static void __init hi3531d_clk_crg_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ unsigned int count = 0; ++ ++ clk_data = hisi_clk_init(np, HI3531D_CRG_NR_CLKS); ++ if (!clk_data) ++ return; ++ ++ hisi_clk_register_fixed_rate(hi3531d_fixed_rate_clks_crg, ++ ARRAY_SIZE(hi3531d_fixed_rate_clks_crg), ++ clk_data); ++ hisi_clk_register_mux(hi3531d_mux_clks_crg, ++ ARRAY_SIZE(hi3531d_mux_clks_crg), clk_data); ++ hisi_clk_register_fixed_factor(hi3531d_fixed_factor_clks, ++ ARRAY_SIZE(hi3531d_fixed_factor_clks), clk_data); ++ hisi_clk_register_gate(hi3531d_gate_clks, ++ ARRAY_SIZE(hi3531d_gate_clks), clk_data); ++ ++ if (!of_property_read_u32(np, "#reset-cells", &count) && (count == 2)) ++ hisi_reset_init(np, HI3531D_CRG_NR_RSTS); ++} ++ ++static const char *timer_mux_p[] __initconst = { "3m", "clk_sysapb" }; ++static u32 timer_mux_table[] = {0, 1}; ++ ++static struct hisi_mux_clock hi3531d_mux_clks_sys[] __initdata = { ++ { HI3531D_TIME0_0_CLK, "timer00", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 16, 1, 0, timer_mux_table, }, ++ ++ { HI3531D_TIME0_1_CLK, "timer01", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 18, 1, 0, timer_mux_table, }, ++ ++ { HI3531D_TIME1_2_CLK, "timer12", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 20, 1, 0, timer_mux_table, }, ++ ++ { HI3531D_TIME1_3_CLK, "timer13", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 22, 1, 0, timer_mux_table, }, ++}; ++ ++static void __init hi3531d_clk_sys_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ unsigned int count = 0; ++ ++ clk_data = hisi_clk_init(np, HI3531D_SYS_NR_CLKS); ++ if (!clk_data) ++ return; ++ ++ hisi_clk_register_mux(hi3531d_mux_clks_sys, ++ ARRAY_SIZE(hi3531d_mux_clks_sys), clk_data); ++ ++ if (!of_property_read_u32(np, "#reset-cells", &count) && (count == 2)) ++ hisi_reset_init(np, HI3531D_SYS_NR_RSTS); ++} ++ ++CLK_OF_DECLARE(hi3531d_clk_crg, "hisilicon,hi3531d-clock", ++ hi3531d_clk_crg_init); ++CLK_OF_DECLARE(hi3531d_clk_sys, "hisilicon,sysctrl", hi3531d_clk_sys_init); ++ +diff --git a/drivers/clk/hisilicon/clk-hi3536c.c b/drivers/clk/hisilicon/clk-hi3536c.c +new file mode 100644 +index 0000000..e3ebd35 +--- /dev/null ++++ b/drivers/clk/hisilicon/clk-hi3536c.c +@@ -0,0 +1,178 @@ ++/* ++ * Hi3536C Clock Driver ++ * ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <dt-bindings/clock/hi3536c-clock.h> ++#include "clk.h" ++#include "reset.h" ++ ++static struct ++hisi_fixed_rate_clock hi3536c_fixed_rate_clks_crg[] __initdata = { ++ { HI3536C_FIXED_3M, "3m", NULL, CLK_IS_ROOT, 3000000, }, ++ { HI3536C_FIXED_6M, "6m", NULL, CLK_IS_ROOT, 6000000, }, ++ { HI3536C_FIXED_12M, "12m", NULL, CLK_IS_ROOT, 12000000, }, ++ { HI3536C_FIXED_24M, "24m", NULL, CLK_IS_ROOT, 24000000, }, ++ { HI3536C_FIXED_83P3M, "83.3m", NULL, CLK_IS_ROOT, 83300000, }, ++ { HI3536C_FIXED_100M, "100m", NULL, CLK_IS_ROOT, 100000000, }, ++ { HI3536C_FIXED_125M, "125m", NULL, CLK_IS_ROOT, 125000000, }, ++ { HI3536C_FIXED_150M, "150m", NULL, CLK_IS_ROOT, 150000000, }, ++ { HI3536C_FIXED_200M, "200m", NULL, CLK_IS_ROOT, 200000000, }, ++ { HI3536C_FIXED_250M, "250m", NULL, CLK_IS_ROOT, 250000000, }, ++ { HI3536C_FIXED_300M, "300m", NULL, CLK_IS_ROOT, 300000000, }, ++ { HI3536C_FIXED_324M, "324m", NULL, CLK_IS_ROOT, 324000000, }, ++ { HI3536C_FIXED_342M, "342m", NULL, CLK_IS_ROOT, 342000000, }, ++ { HI3536C_FIXED_342M, "375m", NULL, CLK_IS_ROOT, 375000000, }, ++ { HI3536C_FIXED_400M, "400m", NULL, CLK_IS_ROOT, 400000000, }, ++ { HI3536C_FIXED_448M, "448m", NULL, CLK_IS_ROOT, 448000000, }, ++ { HI3536C_FIXED_500M, "500m", NULL, CLK_IS_ROOT, 500000000, }, ++ { HI3536C_FIXED_540M, "540m", NULL, CLK_IS_ROOT, 540000000, }, ++ { HI3536C_FIXED_600M, "600m", NULL, CLK_IS_ROOT, 600000000, }, ++ { HI3536C_FIXED_750M, "750m", NULL, CLK_IS_ROOT, 750000000, }, ++ { HI3536C_FIXED_1500M, "1500m", NULL, CLK_IS_ROOT, 1500000000UL, }, ++}; ++ ++static const char *sysaxi_mux_p[] __initconst = { ++ "24m", "200m", "250m", "300m"}; ++/* If syaaxi bus clock is 200MHz, so the APB clock is 50MHz, factor is 4 */ ++static const char *uart_mux_p[] __initconst = {"sysaxi_mux", "24m", "2m"}; ++static const char *fmc_mux_p[] __initconst = { ++ "24m", "83.3m", "150m"}; ++ ++static u32 sysaxi_mux_table[] = {0, 1, 2, 3}; ++static u32 uart_mux_table[] = {0, 1, 2}; ++static u32 fmc_mux_table[] = {0, 1, 2}; ++ ++static struct hisi_mux_clock hi3536c_mux_clks_crg[] __initdata = { ++ { HI3536C_SYSAXI_CLK, "sysaxi_mux", sysaxi_mux_p, ++ ARRAY_SIZE(sysaxi_mux_p), ++ CLK_SET_RATE_PARENT, 0x50, 2, 2, 0, sysaxi_mux_table, }, ++ { HI3536C_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), ++ CLK_SET_RATE_PARENT, 0x148, 2, 2, 0, fmc_mux_table, }, ++ { HI3536C_UART_MUX, "uart_mux", uart_mux_p, ++ ARRAY_SIZE(uart_mux_p), ++ CLK_SET_RATE_PARENT, 0x154, 19, 2, 0, uart_mux_table, }, ++}; ++ ++static struct hisi_fixed_factor_clock ++ hi3536c_fixed_factor_clks[] __initdata = { ++ { HI3536C_SYSAXI_CLK, "clk_sysaxi", "sysaxi_mux", 1, 4, ++ CLK_SET_RATE_PARENT}, ++}; ++ ++static struct hisi_gate_clock hi3536c_gate_clks[] __initdata = { ++#ifdef CONFIG_HIFMC ++ /* fmc */ ++ { HI3536C_FMC_CLK, "clk_fmc", "fmc_mux", ++ CLK_SET_RATE_PARENT, 0x148, 1, 0, }, ++#endif ++ /* uart */ ++ { HI3536C_UART0_CLK, "clk_uart0", "24m", ++ CLK_SET_RATE_PARENT, 0x154, 15, 0, }, ++ { HI3536C_UART1_CLK, "clk_uart1", "24m", ++ CLK_SET_RATE_PARENT, 0x154, 16, 0, }, ++ { HI3536C_UART2_CLK, "clk_uart2", "24m", ++ CLK_SET_RATE_PARENT, 0x154, 17, 0, }, ++ /* ethernet mac */ ++ { HI3536C_ETH_PHY_CLK, "clk_eth_phy", NULL, ++ CLK_SET_RATE_PARENT, 0x14c, 6, 0, }, ++ { HI3536C_ETH_PUB_CLK, "clk_eth_pub", "clk_eth_phy", ++ CLK_SET_RATE_PARENT, 0x14c, 7, 0, }, ++ { HI3536C_ETH_CLK, "clk_eth", "clk_eth_pub", ++ CLK_SET_RATE_PARENT, 0x14c, 1, 0, }, ++ { HI3536C_ETH_MACIF_CLK, "clk_eth_macif", "clk_eth_pub", ++ CLK_SET_RATE_PARENT, 0x14c, 3, 0, }, ++ /* ethernet mac1 */ ++ { HI3536C_ETH1_PHY_CLK, "clk_eth1_phy", NULL, ++ CLK_SET_RATE_PARENT, 0x14c, 14, 0, }, ++ { HI3536C_ETH1_CLK, "clk_eth1", "clk_eth1_phy", ++ CLK_SET_RATE_PARENT, 0x14c, 9, 0, }, ++ { HI3536C_ETH_MACIF1_CLK, "clk_eth_macif1", "clk_eth1_phy", ++ CLK_SET_RATE_PARENT, 0x14c, 11, 0, }, ++ /* spi */ ++ { HI3536C_SPI0_CLK, "clk_spi0", "clk_sysaxi", ++ CLK_SET_RATE_PARENT, 0x154, 13, 0, }, ++ /* dma */ ++ { HI3536C_DMAC_CLK, "clk_dmac", "50m", CLK_SET_RATE_PARENT, ++ 0x144, 1, 0, }, ++}; ++ ++static void __init hi3536c_clk_crg_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ unsigned int count = 0; ++ ++ clk_data = hisi_clk_init(np, HI3536C_CRG_NR_CLKS); ++ if (!clk_data) ++ return; ++ ++ hisi_clk_register_fixed_rate(hi3536c_fixed_rate_clks_crg, ++ ARRAY_SIZE(hi3536c_fixed_rate_clks_crg), ++ clk_data); ++ hisi_clk_register_mux(hi3536c_mux_clks_crg, ++ ARRAY_SIZE(hi3536c_mux_clks_crg), clk_data); ++ hisi_clk_register_fixed_factor(hi3536c_fixed_factor_clks, ++ ARRAY_SIZE(hi3536c_fixed_factor_clks), clk_data); ++ hisi_clk_register_gate(hi3536c_gate_clks, ++ ARRAY_SIZE(hi3536c_gate_clks), clk_data); ++ ++ if (!of_property_read_u32(np, "#reset-cells", &count) && (count == 2)) ++ hisi_reset_init(np, HI3536C_CRG_NR_RSTS); ++} ++ ++static const char *timer_mux_p[] __initconst = { "3m", "clk_sysapb" }; ++static u32 timer_mux_table[] = {0, 1}; ++ ++static struct hisi_mux_clock hi3536c_mux_clks_sys[] __initdata = { ++ { HI3536C_TIME0_0_CLK, "timer00", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 16, 1, 0, timer_mux_table, }, ++ ++ { HI3536C_TIME0_1_CLK, "timer01", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 18, 1, 0, timer_mux_table, }, ++ ++ { HI3536C_TIME1_2_CLK, "timer12", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 20, 1, 0, timer_mux_table, }, ++ ++ { HI3536C_TIME1_3_CLK, "timer13", timer_mux_p, ++ ARRAY_SIZE(timer_mux_p), CLK_SET_RATE_PARENT, ++ 0x0, 22, 1, 0, timer_mux_table, }, ++}; ++ ++static void __init hi3536c_clk_sys_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ unsigned int count = 0; ++ ++ clk_data = hisi_clk_init(np, HI3536C_SYS_NR_CLKS); ++ if (!clk_data) ++ return; ++ ++ hisi_clk_register_mux(hi3536c_mux_clks_sys, ++ ARRAY_SIZE(hi3536c_mux_clks_sys), clk_data); ++ ++ if (!of_property_read_u32(np, "#reset-cells", &count) && (count == 2)) ++ hisi_reset_init(np, HI3536C_SYS_NR_RSTS); ++} ++ ++CLK_OF_DECLARE(hi3536c_clk_crg, "hisilicon,hi3536c-clock", ++ hi3536c_clk_crg_init); ++CLK_OF_DECLARE(hi3536c_clk_sys, "hisilicon,sysctrl", hi3536c_clk_sys_init); ++ +diff --git a/drivers/clk/hisilicon/clk-hi3556.c b/drivers/clk/hisilicon/clk-hi3556.c +new file mode 100644 +index 0000000..56e160f +--- /dev/null ++++ b/drivers/clk/hisilicon/clk-hi3556.c +@@ -0,0 +1,384 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/of_address.h> ++#include <dt-bindings/clock/hi3556-clock.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <mach/io.h> ++ ++#include "clk.h" ++#include "reset.h" ++ ++/******************************************************************************/ ++struct hi3556_pll_clock { ++ u32 id; ++ const char *name; ++ const char *parent_name; ++ u32 ctrl_reg1; ++ u8 frac_shift; ++ u8 frac_width; ++ u8 postdiv1_shift; ++ u8 postdiv1_width; ++ u8 postdiv2_shift; ++ u8 postdiv2_width; ++ u32 ctrl_reg2; ++ u8 fbdiv_shift; ++ u8 fbdiv_width; ++ u8 refdiv_shift; ++ u8 refdiv_width; ++}; ++ ++struct hi3556_clk_pll { ++ struct clk_hw hw; ++ u32 id; ++ void __iomem *ctrl_reg1; ++ u8 frac_shift; ++ u8 frac_width; ++ u8 postdiv1_shift; ++ u8 postdiv1_width; ++ u8 postdiv2_shift; ++ u8 postdiv2_width; ++ void __iomem *ctrl_reg2; ++ u8 fbdiv_shift; ++ u8 fbdiv_width; ++ u8 refdiv_shift; ++ u8 refdiv_width; ++}; ++ ++static struct hisi_fixed_rate_clock hi3556_fixed_rate_clks[] __initdata = { ++ { HI3556_FIXED_2376M, "2376m", NULL, CLK_IS_ROOT, 2376000000UL, }, ++ { HI3556_FIXED_1188M, "1188m", NULL, CLK_IS_ROOT, 1188000000, }, ++ { HI3556_FIXED_594M, "594m", NULL, CLK_IS_ROOT, 594000000, }, ++ { HI3556_FIXED_297M, "297m", NULL, CLK_IS_ROOT, 297000000, }, ++ { HI3556_FIXED_148P5M, "148p5m", NULL, CLK_IS_ROOT, 148500000, }, ++ { HI3556_FIXED_74P25M, "74p25m", NULL, CLK_IS_ROOT, 74250000, }, ++ { HI3556_FIXED_792M, "792m", NULL, CLK_IS_ROOT, 792000000, }, ++ { HI3556_FIXED_475M, "475m", NULL, CLK_IS_ROOT, 475000000, }, ++ { HI3556_FIXED_340M, "340m", NULL, CLK_IS_ROOT, 340000000, }, ++ { HI3556_FIXED_72M, "72m", NULL, CLK_IS_ROOT, 72000000, }, ++ { HI3556_FIXED_400M, "400m", NULL, CLK_IS_ROOT, 400000000, }, ++ { HI3556_FIXED_200M, "200m", NULL, CLK_IS_ROOT, 200000000, }, ++ { HI3556_FIXED_54M, "54m", NULL, CLK_IS_ROOT, 54000000, }, ++ { HI3556_FIXED_27M, "27m", NULL, CLK_IS_ROOT, 1188000000, }, ++ { HI3556_FIXED_37P125M, "37p125m", NULL, CLK_IS_ROOT, 37125000, }, ++ { HI3556_FIXED_3000M, "3000m", NULL, CLK_IS_ROOT, 3000000000UL, }, ++ { HI3556_FIXED_1500M, "1500m", NULL, CLK_IS_ROOT, 1500000000, }, ++ { HI3556_FIXED_500M, "500m", NULL, CLK_IS_ROOT, 500000000, }, ++ { HI3556_FIXED_250M, "250m", NULL, CLK_IS_ROOT, 250000000, }, ++ { HI3556_FIXED_125M, "125m", NULL, CLK_IS_ROOT, 125000000, }, ++ { HI3556_FIXED_1000M, "1000m", NULL, CLK_IS_ROOT, 1000000000, }, ++ { HI3556_FIXED_600M, "600m", NULL, CLK_IS_ROOT, 600000000, }, ++ { HI3556_FIXED_750M, "750m", NULL, CLK_IS_ROOT, 750000000, }, ++ { HI3556_FIXED_150M, "150m", NULL, CLK_IS_ROOT, 150000000, }, ++ { HI3556_FIXED_75M, "75m", NULL, CLK_IS_ROOT, 75000000, }, ++ { HI3556_FIXED_300M, "300m", NULL, CLK_IS_ROOT, 300000000, }, ++ { HI3556_FIXED_60M, "60m", NULL, CLK_IS_ROOT, 60000000, }, ++ { HI3556_FIXED_214M, "214m", NULL, CLK_IS_ROOT, 214000000, }, ++ { HI3556_FIXED_107M, "107m", NULL, CLK_IS_ROOT, 107000000, }, ++ { HI3556_FIXED_100M, "100m", NULL, CLK_IS_ROOT, 100000000, }, ++ { HI3556_FIXED_50M, "50m", NULL, CLK_IS_ROOT, 50000000, }, ++ { HI3556_FIXED_25M, "25m", NULL, CLK_IS_ROOT, 25000000, }, ++ { HI3556_FIXED_24M, "24m", NULL, CLK_IS_ROOT, 24000000, }, ++ { HI3556_FIXED_3M, "3m", NULL, CLK_IS_ROOT, 3000000, }, ++ { HI3556_FIXED_198M, "198m", NULL, CLK_IS_ROOT, 198000000, }, ++ { HI3556_FIXED_396M, "396m", NULL, CLK_IS_ROOT, 396000000, }, ++}; ++ ++static const char *sysaxi_mux_p[] __initconst = {"24m", "198m", }; ++static u32 sysaxi_mux_table[] = {0, 1}; ++ ++static const char *fmc_mux_p[] __initconst = { ++ "24m", "75m", "125m", "150m", "198m", "250m", "300m", "396m", }; ++static u32 fmc_mux_table[] = {0, 1, 2, 3, 4, 5, 6, 7}; ++ ++static const char *mmc_mux_p[] __initconst = { ++ "100m", "198m", "396m", "594m", "792m", "1188m", }; ++static u32 mmc_mux_table[] = {0, 1, 3, 4, 5, 7}; ++ ++static const char *a17_mux_p[] __initconst = { ++ "24m", "apll", "594m", "792m", }; ++static u32 a17_mux_table[] = {0, 1, 3, 4}; ++ ++static const char *i2c_mux_p[] __initconst = {"clk_sysapb", "50m"}; ++static u32 i2c_mux_table[] = {0, 1}; ++ ++static struct hisi_mux_clock hi3556_mux_clks[] __initdata = { ++ { HI3556_SYSAXI_MUX, "sysaxi_mux", sysaxi_mux_p, ++ ARRAY_SIZE(sysaxi_mux_p), ++ CLK_SET_RATE_PARENT, 0x34, 12, 2, 0, sysaxi_mux_table, }, ++ { HI3556_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, }, ++ { HI3556_MMC0_MUX, "mmc0_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 10, 3, 0, mmc_mux_table, }, ++ { HI3556_MMC1_MUX, "mmc1_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 2, 3, 0, mmc_mux_table, }, ++ { HI3556_MMC2_MUX, "mmc2_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 18, 3, 0, mmc_mux_table, }, ++ { HI3556_A17_MUX, "a17_mux", a17_mux_p, ARRAY_SIZE(a17_mux_p), ++ CLK_SET_RATE_PARENT, 0x34, 4, 3, 0, a17_mux_table, }, ++ { HI3556_I2C_MUX, "i2c_mux", i2c_mux_p, ARRAY_SIZE(i2c_mux_p), ++ CLK_SET_RATE_PARENT, 0xe4, 26, 1, 0, i2c_mux_table, }, ++ ++}; ++ ++static struct hisi_fixed_factor_clock ++ hi3556_fixed_factor_clks[] __initdata = { ++ { HI3556_SYSAPB_CLK, "clk_sysapb", "sysaxi_mux", 1, 4, ++ CLK_SET_RATE_PARENT}, ++ { HI3556_MMC0_FAC_CLK, "mmc0_fac", "mmc0_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++ { HI3556_MMC1_FAC_CLK, "mmc1_fac", "mmc1_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++ { HI3556_MMC2_FAC_CLK, "mmc2_fac", "mmc2_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++}; ++ ++static struct hisi_gate_clock hi3556_gate_clks[] __initdata = { ++#ifdef CONFIG_HIFMC ++ /* fmc */ ++ { HI3556_FMC_CLK, "clk_fmc", "fmc_mux", ++ CLK_SET_RATE_PARENT, 0xc0, 1, 0, }, ++#endif ++ /* mmc */ ++ { HI3556_MMC0_CLK, "clk_mmc0", "mmc0_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 9, 0, }, ++ { HI3556_MMC1_CLK, "clk_mmc1", "mmc1_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 1, 0, }, ++ { HI3556_MMC2_CLK, "clk_mmc2", "mmc2_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 17, 0, }, ++ ++ /* usb ctrl */ ++ { HI3556_USB2_CTRL_UTMI0_REQ, "usb2_cttl_utmi0_req", NULL, ++ CLK_SET_RATE_PARENT, 0xb4, 5, 1, }, ++ { HI3556_USB2_HRST_REQ, "usb2_hrst_req", NULL, ++ CLK_SET_RATE_PARENT, 0xb4, 0, 1, }, ++ { HI3556_USB3_CLK, "usb3_vcc_srst_req2", NULL, ++ CLK_SET_RATE_PARENT, 0xb8, 0, 1, }, ++ ++ /* uart */ ++ { HI3556_UART0_CLK, "clk_uart0", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 20, 0, }, ++ { HI3556_UART1_CLK, "clk_uart1", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 21, 0, }, ++ { HI3556_UART2_CLK, "clk_uart2", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 22, 0, }, ++ { HI3556_UART3_CLK, "clk_uart3", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 23, 0, }, ++ { HI3556_UART4_CLK, "clk_uart4", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 24, 0, }, ++ ++ /* ethernet mac */ ++ { HI3556_ETH_CLK, "clk_eth", NULL, ++ CLK_IS_ROOT, 0xcc, 1, 0, }, ++ { HI3556_ETH_MACIF_CLK, "clk_eth_macif", NULL, ++ CLK_IS_ROOT, 0xcc, 3, 0, }, ++ ++ /* spi */ ++ { HI3556_SPI0_CLK, "clk_spi0", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 16, 0, }, ++ { HI3556_SPI1_CLK, "clk_spi1", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 17, 0, }, ++ { HI3556_SPI2_CLK, "clk_spi2", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 18, 0, }, ++ { HI3556_SPI3_CLK, "clk_spi3", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 28, 0, }, ++}; ++ ++static struct hi3556_pll_clock hi3556_pll_clks[] __initdata = { ++ { HI3556_APLL_CLK, "apll", NULL, 0x0, 0, 24, 24, 3, 28, 3, ++ 0x4, 0, 12, 12, 6}, ++}; ++ ++ ++#define to_pll_clk(_hw) container_of(_hw, struct hi3556_clk_pll, hw) ++ ++static void hi3556_calc_pll(u32 *frac_val, ++ u32 *postdiv1_val, ++ u32 *postdiv2_val, ++ u32 *fbdiv_val, ++ u32 *refdiv_val, ++ unsigned long rate) ++{ ++ u64 rem; ++ *frac_val = 0; ++ rem = do_div(rate, 1000000); ++ *fbdiv_val = rate; ++ *refdiv_val = 24; ++ rem = rem * (1 << 24); ++ do_div(rem, 1000000); ++ *frac_val = rem; ++} ++ ++static int clk_pll_set_rate(struct clk_hw *hw, ++ unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct hi3556_clk_pll *clk = to_pll_clk(hw); ++ u32 frac_val, postdiv1_val, postdiv2_val, fbdiv_val, refdiv_val; ++ u32 val; ++ ++ /*Fixme ignore postdives now because apll don't use them*/ ++ postdiv1_val = postdiv2_val = 0; ++ ++ hi3556_calc_pll(&frac_val, &postdiv1_val, &postdiv2_val, ++ &fbdiv_val, &refdiv_val, rate); ++ ++ val = readl_relaxed(clk->ctrl_reg1); ++ val &= ~(((1 << clk->frac_width) - 1) << clk->frac_shift); ++ val &= ~(((1 << clk->postdiv1_width) - 1) << clk->postdiv1_shift); ++ val &= ~(((1 << clk->postdiv2_width) - 1) << clk->postdiv2_shift); ++ ++ val |= frac_val << clk->frac_shift; ++ val |= postdiv1_val << clk->postdiv1_shift; ++ val |= postdiv2_val << clk->postdiv2_shift; ++ writel_relaxed(val, clk->ctrl_reg1); ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val &= ~(((1 << clk->fbdiv_width) - 1) << clk->fbdiv_shift); ++ val &= ~(((1 << clk->refdiv_width) - 1) << clk->refdiv_shift); ++ ++ val |= fbdiv_val << clk->fbdiv_shift; ++ val |= refdiv_val << clk->refdiv_shift; ++ writel_relaxed(val, clk->ctrl_reg2); ++ ++ return 0; ++} ++ ++static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct hi3556_clk_pll *clk = to_pll_clk(hw); ++ u64 frac_val, fbdiv_val, refdiv_val; ++ u32 val; ++ u64 tmp, rate; ++ ++ val = readl_relaxed(clk->ctrl_reg1); ++ val = val >> clk->frac_shift; ++ val &= ((1 << clk->frac_width) - 1); ++ frac_val = val; ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val = val >> clk->fbdiv_shift; ++ val &= ((1 << clk->fbdiv_width) - 1); ++ fbdiv_val = val; ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val = val >> clk->refdiv_shift; ++ val &= ((1 << clk->refdiv_width) - 1); ++ refdiv_val = val; ++ ++ /* rate = 24000000 * (fbdiv + frac / (1<<24) ) / refdiv */ ++ rate = 0; ++ tmp = 24000000 * fbdiv_val; ++ rate += tmp; ++ do_div(rate, refdiv_val); ++ ++ return rate; ++} ++ ++ ++static long clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *best_parent_rate, ++ struct clk **best_parent_p) ++{ ++ return rate; ++} ++ ++static struct clk_ops clk_pll_ops = { ++ .set_rate = clk_pll_set_rate, ++ .determine_rate = clk_pll_determine_rate, ++ .recalc_rate = clk_pll_recalc_rate, ++}; ++ ++void __init hi3556_clk_register_pll(struct hi3556_pll_clock *clks, ++ int nums, struct hisi_clock_data *data) ++{ ++ void __iomem *base = data->base; ++ int i; ++ ++ for (i = 0; i < nums; i++) { ++ struct hi3556_clk_pll *p_clk; ++ struct clk *clk; ++ struct clk_init_data init; ++ ++ p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL); ++ if (!p_clk) ++ return; ++ ++ init.name = clks[i].name; ++ init.flags = CLK_IS_BASIC; ++ init.parent_names = ++ (clks[i].parent_name ? &clks[i].parent_name : NULL); ++ init.num_parents = (clks[i].parent_name ? 1 : 0); ++ init.ops = &clk_pll_ops; ++ ++ p_clk->ctrl_reg1 = base + clks[i].ctrl_reg1; ++ p_clk->frac_shift = clks[i].frac_shift; ++ p_clk->frac_width = clks[i].frac_width; ++ p_clk->postdiv1_shift = clks[i].postdiv1_shift; ++ p_clk->postdiv1_width = clks[i].postdiv1_width; ++ p_clk->postdiv2_shift = clks[i].postdiv2_shift; ++ p_clk->postdiv2_width = clks[i].postdiv2_width; ++ ++ p_clk->ctrl_reg2 = base + clks[i].ctrl_reg2; ++ p_clk->fbdiv_shift = clks[i].fbdiv_shift; ++ p_clk->fbdiv_width = clks[i].fbdiv_width; ++ p_clk->refdiv_shift = clks[i].refdiv_shift; ++ p_clk->refdiv_width = clks[i].refdiv_width; ++ p_clk->hw.init = &init; ++ ++ clk = clk_register(NULL, &p_clk->hw); ++ if (IS_ERR(clk)) { ++ kfree(p_clk); ++ pr_err("%s: failed to register clock %s\n", ++ __func__, clks[i].name); ++ continue; ++ } ++ ++ data->clk_data.clks[clks[i].id] = clk; ++ } ++ ++ ++} ++ ++static void __init hi3556_clk_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ ++ clk_data = hisi_clk_init(np, HI3556_NR_CLKS); ++ if (!clk_data) ++ return; ++ if (IS_ENABLED(CONFIG_RESET_CONTROLLER)) ++ hisi_reset_init(np, HI3556_NR_RSTS); ++ ++ hisi_clk_register_fixed_rate(hi3556_fixed_rate_clks, ++ ARRAY_SIZE(hi3556_fixed_rate_clks), ++ clk_data); ++ hi3556_clk_register_pll(hi3556_pll_clks, ++ ARRAY_SIZE(hi3556_pll_clks), clk_data); ++ ++ hisi_clk_register_mux(hi3556_mux_clks, ++ ARRAY_SIZE(hi3556_mux_clks), ++ clk_data); ++ hisi_clk_register_fixed_factor(hi3556_fixed_factor_clks, ++ ARRAY_SIZE(hi3556_fixed_factor_clks), clk_data); ++ hisi_clk_register_gate(hi3556_gate_clks, ++ ARRAY_SIZE(hi3556_gate_clks), clk_data); ++} ++ ++CLK_OF_DECLARE(hi3556_clk, "hisilicon,hi3556-clock", hi3556_clk_init); +diff --git a/drivers/clk/hisilicon/clk-hi3559.c b/drivers/clk/hisilicon/clk-hi3559.c +new file mode 100644 +index 0000000..1ae6f6e +--- /dev/null ++++ b/drivers/clk/hisilicon/clk-hi3559.c +@@ -0,0 +1,384 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/of_address.h> ++#include <dt-bindings/clock/hi3559-clock.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <mach/io.h> ++ ++#include "clk.h" ++#include "reset.h" ++ ++/******************************************************************************/ ++struct hi3559_pll_clock { ++ u32 id; ++ const char *name; ++ const char *parent_name; ++ u32 ctrl_reg1; ++ u8 frac_shift; ++ u8 frac_width; ++ u8 postdiv1_shift; ++ u8 postdiv1_width; ++ u8 postdiv2_shift; ++ u8 postdiv2_width; ++ u32 ctrl_reg2; ++ u8 fbdiv_shift; ++ u8 fbdiv_width; ++ u8 refdiv_shift; ++ u8 refdiv_width; ++}; ++ ++struct hi3559_clk_pll { ++ struct clk_hw hw; ++ u32 id; ++ void __iomem *ctrl_reg1; ++ u8 frac_shift; ++ u8 frac_width; ++ u8 postdiv1_shift; ++ u8 postdiv1_width; ++ u8 postdiv2_shift; ++ u8 postdiv2_width; ++ void __iomem *ctrl_reg2; ++ u8 fbdiv_shift; ++ u8 fbdiv_width; ++ u8 refdiv_shift; ++ u8 refdiv_width; ++}; ++ ++static struct hisi_fixed_rate_clock hi3559_fixed_rate_clks[] __initdata = { ++ { HI3559_FIXED_2376M, "2376m", NULL, CLK_IS_ROOT, 2376000000UL, }, ++ { HI3559_FIXED_1188M, "1188m", NULL, CLK_IS_ROOT, 1188000000, }, ++ { HI3559_FIXED_594M, "594m", NULL, CLK_IS_ROOT, 594000000, }, ++ { HI3559_FIXED_297M, "297m", NULL, CLK_IS_ROOT, 297000000, }, ++ { HI3559_FIXED_148P5M, "148p5m", NULL, CLK_IS_ROOT, 148500000, }, ++ { HI3559_FIXED_74P25M, "74p25m", NULL, CLK_IS_ROOT, 74250000, }, ++ { HI3559_FIXED_792M, "792m", NULL, CLK_IS_ROOT, 792000000, }, ++ { HI3559_FIXED_475M, "475m", NULL, CLK_IS_ROOT, 475000000, }, ++ { HI3559_FIXED_340M, "340m", NULL, CLK_IS_ROOT, 340000000, }, ++ { HI3559_FIXED_72M, "72m", NULL, CLK_IS_ROOT, 72000000, }, ++ { HI3559_FIXED_400M, "400m", NULL, CLK_IS_ROOT, 400000000, }, ++ { HI3559_FIXED_200M, "200m", NULL, CLK_IS_ROOT, 200000000, }, ++ { HI3559_FIXED_54M, "54m", NULL, CLK_IS_ROOT, 54000000, }, ++ { HI3559_FIXED_27M, "27m", NULL, CLK_IS_ROOT, 1188000000, }, ++ { HI3559_FIXED_37P125M, "37p125m", NULL, CLK_IS_ROOT, 37125000, }, ++ { HI3559_FIXED_3000M, "3000m", NULL, CLK_IS_ROOT, 3000000000UL, }, ++ { HI3559_FIXED_1500M, "1500m", NULL, CLK_IS_ROOT, 1500000000, }, ++ { HI3559_FIXED_500M, "500m", NULL, CLK_IS_ROOT, 500000000, }, ++ { HI3559_FIXED_250M, "250m", NULL, CLK_IS_ROOT, 250000000, }, ++ { HI3559_FIXED_125M, "125m", NULL, CLK_IS_ROOT, 125000000, }, ++ { HI3559_FIXED_1000M, "1000m", NULL, CLK_IS_ROOT, 1000000000, }, ++ { HI3559_FIXED_600M, "600m", NULL, CLK_IS_ROOT, 600000000, }, ++ { HI3559_FIXED_750M, "750m", NULL, CLK_IS_ROOT, 750000000, }, ++ { HI3559_FIXED_150M, "150m", NULL, CLK_IS_ROOT, 150000000, }, ++ { HI3559_FIXED_75M, "75m", NULL, CLK_IS_ROOT, 75000000, }, ++ { HI3559_FIXED_300M, "300m", NULL, CLK_IS_ROOT, 300000000, }, ++ { HI3559_FIXED_60M, "60m", NULL, CLK_IS_ROOT, 60000000, }, ++ { HI3559_FIXED_214M, "214m", NULL, CLK_IS_ROOT, 214000000, }, ++ { HI3559_FIXED_107M, "107m", NULL, CLK_IS_ROOT, 107000000, }, ++ { HI3559_FIXED_100M, "100m", NULL, CLK_IS_ROOT, 100000000, }, ++ { HI3559_FIXED_50M, "50m", NULL, CLK_IS_ROOT, 50000000, }, ++ { HI3559_FIXED_25M, "25m", NULL, CLK_IS_ROOT, 25000000, }, ++ { HI3559_FIXED_24M, "24m", NULL, CLK_IS_ROOT, 24000000, }, ++ { HI3559_FIXED_3M, "3m", NULL, CLK_IS_ROOT, 3000000, }, ++ { HI3559_FIXED_198M, "198m", NULL, CLK_IS_ROOT, 198000000, }, ++ { HI3559_FIXED_396M, "396m", NULL, CLK_IS_ROOT, 396000000, }, ++}; ++ ++static const char *sysaxi_mux_p[] __initconst = {"24m", "198m", }; ++static u32 sysaxi_mux_table[] = {0, 1}; ++ ++static const char *fmc_mux_p[] __initconst = { ++ "24m", "75m", "125m", "150m", "198m", "250m", "300m", "396m", }; ++static u32 fmc_mux_table[] = {0, 1, 2, 3, 4, 5, 6, 7}; ++ ++static const char *mmc_mux_p[] __initconst = { ++ "100m", "198m", "396m", "594m", "792m", "1188m", }; ++static u32 mmc_mux_table[] = {0, 1, 3, 4, 5, 7}; ++ ++static const char *a17_mux_p[] __initconst = { ++ "24m", "apll", "594m", "792m", }; ++static u32 a17_mux_table[] = {0, 1, 3, 4}; ++ ++static const char *i2c_mux_p[] __initconst = {"clk_sysapb", "50m"}; ++static u32 i2c_mux_table[] = {0, 1}; ++ ++static struct hisi_mux_clock hi3559_mux_clks[] __initdata = { ++ { HI3559_SYSAXI_MUX, "sysaxi_mux", sysaxi_mux_p, ++ ARRAY_SIZE(sysaxi_mux_p), ++ CLK_SET_RATE_PARENT, 0x34, 12, 2, 0, sysaxi_mux_table, }, ++ { HI3559_FMC_MUX, "fmc_mux", fmc_mux_p, ARRAY_SIZE(fmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc0, 2, 3, 0, fmc_mux_table, }, ++ { HI3559_MMC0_MUX, "mmc0_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 10, 3, 0, mmc_mux_table, }, ++ { HI3559_MMC1_MUX, "mmc1_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 2, 3, 0, mmc_mux_table, }, ++ { HI3559_MMC2_MUX, "mmc2_mux", mmc_mux_p, ARRAY_SIZE(mmc_mux_p), ++ CLK_SET_RATE_PARENT, 0xc4, 18, 3, 0, mmc_mux_table, }, ++ { HI3559_A17_MUX, "a17_mux", a17_mux_p, ARRAY_SIZE(a17_mux_p), ++ CLK_SET_RATE_PARENT, 0x34, 4, 3, 0, a17_mux_table, }, ++ { HI3559_I2C_MUX, "i2c_mux", i2c_mux_p, ARRAY_SIZE(i2c_mux_p), ++ CLK_SET_RATE_PARENT, 0xe4, 26, 1, 0, i2c_mux_table, }, ++ ++}; ++ ++static struct hisi_fixed_factor_clock ++ hi3559_fixed_factor_clks[] __initdata = { ++ { HI3559_SYSAPB_CLK, "clk_sysapb", "sysaxi_mux", 1, 4, ++ CLK_SET_RATE_PARENT}, ++ { HI3559_MMC0_FAC_CLK, "mmc0_fac", "mmc0_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++ { HI3559_MMC1_FAC_CLK, "mmc1_fac", "mmc1_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++ { HI3559_MMC2_FAC_CLK, "mmc2_fac", "mmc2_mux", 1, 8, ++ CLK_SET_RATE_PARENT}, ++}; ++ ++static struct hisi_gate_clock hi3559_gate_clks[] __initdata = { ++#ifdef CONFIG_HIFMC ++ /* fmc */ ++ { HI3559_FMC_CLK, "clk_fmc", "fmc_mux", ++ CLK_SET_RATE_PARENT, 0xc0, 1, 0, }, ++#endif ++ /* mmc */ ++ { HI3559_MMC0_CLK, "clk_mmc0", "mmc0_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 9, 0, }, ++ { HI3559_MMC1_CLK, "clk_mmc1", "mmc1_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 1, 0, }, ++ { HI3559_MMC2_CLK, "clk_mmc2", "mmc2_fac", ++ CLK_SET_RATE_PARENT, 0xc4, 17, 0, }, ++ ++ /* usb ctrl */ ++ { HI3559_USB2_CTRL_UTMI0_REQ, "usb2_cttl_utmi0_req", NULL, ++ CLK_SET_RATE_PARENT, 0xb4, 5, 1, }, ++ { HI3559_USB2_HRST_REQ, "usb2_hrst_req", NULL, ++ CLK_SET_RATE_PARENT, 0xb4, 0, 1, }, ++ { HI3559_USB3_CLK, "usb3_vcc_srst_req2", NULL, ++ CLK_SET_RATE_PARENT, 0xb8, 0, 1, }, ++ ++ /* uart */ ++ { HI3559_UART0_CLK, "clk_uart0", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 20, 0, }, ++ { HI3559_UART1_CLK, "clk_uart1", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 21, 0, }, ++ { HI3559_UART2_CLK, "clk_uart2", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 22, 0, }, ++ { HI3559_UART3_CLK, "clk_uart3", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 23, 0, }, ++ { HI3559_UART4_CLK, "clk_uart4", "24m", ++ CLK_SET_RATE_PARENT, 0xe4, 24, 0, }, ++ ++ /* ethernet mac */ ++ { HI3559_ETH_CLK, "clk_eth", NULL, ++ CLK_IS_ROOT, 0xcc, 1, 0, }, ++ { HI3559_ETH_MACIF_CLK, "clk_eth_macif", NULL, ++ CLK_IS_ROOT, 0xcc, 3, 0, }, ++ ++ /* spi */ ++ { HI3559_SPI0_CLK, "clk_spi0", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 16, 0, }, ++ { HI3559_SPI1_CLK, "clk_spi1", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 17, 0, }, ++ { HI3559_SPI2_CLK, "clk_spi2", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 18, 0, }, ++ { HI3559_SPI3_CLK, "clk_spi3", "clk_sysapb", ++ CLK_SET_RATE_PARENT, 0xe4, 28, 0, }, ++}; ++ ++static struct hi3559_pll_clock hi3559_pll_clks[] __initdata = { ++ { HI3559_APLL_CLK, "apll", NULL, 0x0, 0, 24, 24, 3, 28, 3, ++ 0x4, 0, 12, 12, 6}, ++}; ++ ++ ++#define to_pll_clk(_hw) container_of(_hw, struct hi3559_clk_pll, hw) ++ ++static void hi3559_calc_pll(u32 *frac_val, ++ u32 *postdiv1_val, ++ u32 *postdiv2_val, ++ u32 *fbdiv_val, ++ u32 *refdiv_val, ++ unsigned long rate) ++{ ++ u64 rem; ++ *frac_val = 0; ++ rem = do_div(rate, 1000000); ++ *fbdiv_val = rate; ++ *refdiv_val = 24; ++ rem = rem * (1 << 24); ++ do_div(rem, 1000000); ++ *frac_val = rem; ++} ++ ++static int clk_pll_set_rate(struct clk_hw *hw, ++ unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct hi3559_clk_pll *clk = to_pll_clk(hw); ++ u32 frac_val, postdiv1_val, postdiv2_val, fbdiv_val, refdiv_val; ++ u32 val; ++ ++ /*Fixme ignore postdives now because apll don't use them*/ ++ postdiv1_val = postdiv2_val = 0; ++ ++ hi3559_calc_pll(&frac_val, &postdiv1_val, &postdiv2_val, ++ &fbdiv_val, &refdiv_val, rate); ++ ++ val = readl_relaxed(clk->ctrl_reg1); ++ val &= ~(((1 << clk->frac_width) - 1) << clk->frac_shift); ++ val &= ~(((1 << clk->postdiv1_width) - 1) << clk->postdiv1_shift); ++ val &= ~(((1 << clk->postdiv2_width) - 1) << clk->postdiv2_shift); ++ ++ val |= frac_val << clk->frac_shift; ++ val |= postdiv1_val << clk->postdiv1_shift; ++ val |= postdiv2_val << clk->postdiv2_shift; ++ writel_relaxed(val, clk->ctrl_reg1); ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val &= ~(((1 << clk->fbdiv_width) - 1) << clk->fbdiv_shift); ++ val &= ~(((1 << clk->refdiv_width) - 1) << clk->refdiv_shift); ++ ++ val |= fbdiv_val << clk->fbdiv_shift; ++ val |= refdiv_val << clk->refdiv_shift; ++ writel_relaxed(val, clk->ctrl_reg2); ++ ++ return 0; ++} ++ ++static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct hi3559_clk_pll *clk = to_pll_clk(hw); ++ u64 frac_val, fbdiv_val, refdiv_val; ++ u32 val; ++ u64 tmp, rate; ++ ++ val = readl_relaxed(clk->ctrl_reg1); ++ val = val >> clk->frac_shift; ++ val &= ((1 << clk->frac_width) - 1); ++ frac_val = val; ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val = val >> clk->fbdiv_shift; ++ val &= ((1 << clk->fbdiv_width) - 1); ++ fbdiv_val = val; ++ ++ val = readl_relaxed(clk->ctrl_reg2); ++ val = val >> clk->refdiv_shift; ++ val &= ((1 << clk->refdiv_width) - 1); ++ refdiv_val = val; ++ ++ /* rate = 24000000 * (fbdiv + frac / (1<<24) ) / refdiv */ ++ rate = 0; ++ tmp = 24000000 * fbdiv_val; ++ rate += tmp; ++ do_div(rate, refdiv_val); ++ ++ return rate; ++} ++ ++ ++static long clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *best_parent_rate, ++ struct clk **best_parent_p) ++{ ++ return rate; ++} ++ ++static struct clk_ops clk_pll_ops = { ++ .set_rate = clk_pll_set_rate, ++ .determine_rate = clk_pll_determine_rate, ++ .recalc_rate = clk_pll_recalc_rate, ++}; ++ ++void __init hi3559_clk_register_pll(struct hi3559_pll_clock *clks, ++ int nums, struct hisi_clock_data *data) ++{ ++ void __iomem *base = data->base; ++ int i; ++ ++ for (i = 0; i < nums; i++) { ++ struct hi3559_clk_pll *p_clk; ++ struct clk *clk; ++ struct clk_init_data init; ++ ++ p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL); ++ if (!p_clk) ++ return; ++ ++ init.name = clks[i].name; ++ init.flags = CLK_IS_BASIC; ++ init.parent_names = ++ (clks[i].parent_name ? &clks[i].parent_name : NULL); ++ init.num_parents = (clks[i].parent_name ? 1 : 0); ++ init.ops = &clk_pll_ops; ++ ++ p_clk->ctrl_reg1 = base + clks[i].ctrl_reg1; ++ p_clk->frac_shift = clks[i].frac_shift; ++ p_clk->frac_width = clks[i].frac_width; ++ p_clk->postdiv1_shift = clks[i].postdiv1_shift; ++ p_clk->postdiv1_width = clks[i].postdiv1_width; ++ p_clk->postdiv2_shift = clks[i].postdiv2_shift; ++ p_clk->postdiv2_width = clks[i].postdiv2_width; ++ ++ p_clk->ctrl_reg2 = base + clks[i].ctrl_reg2; ++ p_clk->fbdiv_shift = clks[i].fbdiv_shift; ++ p_clk->fbdiv_width = clks[i].fbdiv_width; ++ p_clk->refdiv_shift = clks[i].refdiv_shift; ++ p_clk->refdiv_width = clks[i].refdiv_width; ++ p_clk->hw.init = &init; ++ ++ clk = clk_register(NULL, &p_clk->hw); ++ if (IS_ERR(clk)) { ++ kfree(p_clk); ++ pr_err("%s: failed to register clock %s\n", ++ __func__, clks[i].name); ++ continue; ++ } ++ ++ data->clk_data.clks[clks[i].id] = clk; ++ } ++ ++ ++} ++ ++static void __init hi3559_clk_init(struct device_node *np) ++{ ++ struct hisi_clock_data *clk_data; ++ ++ clk_data = hisi_clk_init(np, HI3559_NR_CLKS); ++ if (!clk_data) ++ return; ++ if (IS_ENABLED(CONFIG_RESET_CONTROLLER)) ++ hisi_reset_init(np, HI3559_NR_RSTS); ++ ++ hisi_clk_register_fixed_rate(hi3559_fixed_rate_clks, ++ ARRAY_SIZE(hi3559_fixed_rate_clks), ++ clk_data); ++ hi3559_clk_register_pll(hi3559_pll_clks, ++ ARRAY_SIZE(hi3559_pll_clks), clk_data); ++ ++ hisi_clk_register_mux(hi3559_mux_clks, ++ ARRAY_SIZE(hi3559_mux_clks), ++ clk_data); ++ hisi_clk_register_fixed_factor(hi3559_fixed_factor_clks, ++ ARRAY_SIZE(hi3559_fixed_factor_clks), clk_data); ++ hisi_clk_register_gate(hi3559_gate_clks, ++ ARRAY_SIZE(hi3559_gate_clks), clk_data); ++} ++ ++CLK_OF_DECLARE(hi3559_clk, "hisilicon,hi3559-clock", hi3559_clk_init); +diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c +index a078e84..870a505 100644 +--- a/drivers/clk/hisilicon/clk.c ++++ b/drivers/clk/hisilicon/clk.c +@@ -232,3 +232,25 @@ void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks, + data->clk_data.clks[clks[i].id] = clk; + } + } ++ ++void __init hisi_clk_register_ops(struct hiclk_hw *clks, ++ int nums, struct hisi_clock_data *data) ++{ ++ struct clk *clk; ++ int i; ++ ++ for (i = 0; i < nums; i++) { ++ struct hiclk_hw *hihw = &clks[i]; ++ ++ clk = clk_register_ops_table(NULL, hihw, NULL); ++ if (IS_ERR(clk)) { ++ pr_err("%s: failed to register clock %s\n", ++ __func__, clks[i].name); ++ continue; ++ } ++ ++ clk_register_clkdev(clk, clks[i].name, NULL); ++ ++ data->clk_data.clks[clks[i].id] = clk; ++ } ++} +diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h +index 31083ff..46e7417 100644 +--- a/drivers/clk/hisilicon/clk.h ++++ b/drivers/clk/hisilicon/clk.h +@@ -30,6 +30,50 @@ + #include <linux/io.h> + #include <linux/spinlock.h> + ++#define CLK(_id, _mask, _value, _rstbit, _rate, _ops) \ ++{.id = _id, \ ++ .name = #_id, \ ++ .offset = _id, \ ++ .mask = _mask, \ ++ .value = _value, \ ++ .rstbit = _rstbit, \ ++ .rate = _rate, \ ++ .ops = _ops,} ++ ++#define CLK_SHARED(_id, _off, _mask, _value, _rstbit, _rate, _ops) \ ++{.id = _id, \ ++ .name = #_id, \ ++ .offset = _off, \ ++ .mask = _mask, \ ++ .value = _value, \ ++ .rstbit = _rstbit, \ ++ .rate = _rate, \ ++ .ops = _ops,} ++ ++struct hiclk_hw { ++ int id; ++ const char *name; ++ u32 offset; ++ u32 mask; ++ u32 value; ++ u32 rstbit; ++ ++ unsigned long rate; ++ struct clk_ops *ops; ++ struct clk_hw hw; ++ ++#define CLKHW_RESET (0x01) ++#define CLKHW_ENABLE (0x02) ++ ++ u32 flags; ++}; ++ ++extern struct clk_ops clk_ops_hiusb2_host; ++extern struct clk_ops clk_ops_hiusb3; ++extern struct clk_ops clk_ops_himci; ++ ++#define to_hiclk_hw(_hw) container_of(_hw, struct hiclk_hw, hw) ++ + struct hisi_clock_data { + struct clk_onecell_data clk_data; + void __iomem *base; +@@ -96,6 +140,9 @@ struct clk *hisi_register_clkgate_sep(struct device *, const char *, + u8, spinlock_t *); + + struct hisi_clock_data __init *hisi_clk_init(struct device_node *, int); ++struct clk *clk_register_ops_table(struct device *, ++ struct hiclk_hw *, ++ struct clk_ops *); + void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *, + int, struct hisi_clock_data *); + void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *, +@@ -108,4 +155,6 @@ void __init hisi_clk_register_gate(struct hisi_gate_clock *, + int, struct hisi_clock_data *); + void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *, + int, struct hisi_clock_data *); ++void __init hisi_clk_register_ops(struct hiclk_hw *, ++ int, struct hisi_clock_data *); + #endif /* __HISI_CLK_H */ +diff --git a/drivers/clk/hisilicon/clk_ops.c b/drivers/clk/hisilicon/clk_ops.c +new file mode 100644 +index 0000000..268c232 +--- /dev/null ++++ b/drivers/clk/hisilicon/clk_ops.c +@@ -0,0 +1,53 @@ ++/* ++ * Hisilicon clock separated gate driver ++ * ++ * Copyright (c) 2012-2013 Hisilicon Limited. ++ * Copyright (c) 2012-2013 Linaro Limited. ++ * ++ * Author: Haojian Zhuang <haojian.zhuang@linaro.org> ++ * Xin Li <li.xin@linaro.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/clk-provider.h> ++#include <linux/clkdev.h> ++#include <linux/io.h> ++#include <linux/slab.h> ++#include <linux/clk.h> ++ ++#include "clk.h" ++ ++struct clk *clk_register_ops_table(struct device *dev, ++ struct hiclk_hw *hihw, ++ struct clk_ops *clkops) ++{ ++ struct clk *clk; ++ struct clk_init_data init; ++ ++ init.name = hihw->name; ++ init.flags = CLK_IS_ROOT | CLK_IS_BASIC; ++ init.parent_names = NULL; ++ init.num_parents = 0; ++ init.ops = hihw->ops; ++ ++ hihw->hw.init = &init; ++ ++ clk = clk_register(dev, &hihw->hw); ++ if (IS_ERR(clk)) { ++ pr_err("%s: register clock fail.\n", __func__); ++ return NULL; ++ } ++ ++ return clk; ++} +diff --git a/drivers/clk/hisilicon/reset.c b/drivers/clk/hisilicon/reset.c +new file mode 100644 +index 0000000..85b4ae7 +--- /dev/null ++++ b/drivers/clk/hisilicon/reset.c +@@ -0,0 +1,147 @@ ++/* ++ * Hisilicon Reset Controller driver ++ * ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/of_platform.h> ++#include <linux/platform_device.h> ++#include <linux/reset-controller.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/types.h> ++ ++#define HISI_RESET_BIT_SHIFT 0 ++#define HISI_RESET_BIT_WIDTH 16 ++#define HISI_RESET_OFFSET_SHIFT 16 ++#define HISI_RESET_OFFSET_WIDTH 16 ++ ++struct hisi_reset_controller { ++ spinlock_t lock; ++ void __iomem *membase; ++ struct reset_controller_dev rcdev; ++}; ++ ++ ++#define to_hisi_reset_controller(rcdev) \ ++ container_of(rcdev, struct hisi_reset_controller, rcdev) ++ ++/*31 16 0 ++ |---reset_spec->args[0]---|---reset_spec->args[1]---| ++ |-------reg_offset--------|--------reg_bit----------|*/ ++static int hisi_reset_of_xlate(struct reset_controller_dev *rcdev, ++ const struct of_phandle_args *reset_spec) ++{ ++ unsigned int offset, bit, id; ++ __be32 *addr; ++ u64 size; ++ ++ if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells)) ++ return -EINVAL; ++ ++ addr = (__be32 *)of_get_address(rcdev->of_node, 0, &size, NULL); ++ if (!addr) ++ return -EINVAL; ++ ++ if (reset_spec->args[1] >= 32 ++ || reset_spec->args[0] + reset_spec->args[1] / 8 > size) ++ return -EINVAL; ++ ++ offset = reset_spec->args[0] & (BIT(HISI_RESET_OFFSET_WIDTH) - 1); ++ bit = (reset_spec->args[1] & (BIT(HISI_RESET_BIT_WIDTH) - 1)); ++ id = offset << HISI_RESET_OFFSET_SHIFT | bit; ++ ++ return id; ++} ++ ++static int hisi_reset_assert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev); ++ unsigned int offset, bit; ++ unsigned long flags; ++ u32 reg; ++ ++ offset = id >> HISI_RESET_OFFSET_SHIFT; ++ offset &= (BIT(HISI_RESET_OFFSET_WIDTH) - 1); ++ bit = id & (BIT(HISI_RESET_BIT_WIDTH) - 1); ++ ++ spin_lock_irqsave(&rstc->lock, flags); ++ ++ reg = readl(rstc->membase + offset); ++ writel(reg | BIT(bit), rstc->membase + offset); ++ ++ spin_unlock_irqrestore(&rstc->lock, flags); ++ ++ return 0; ++} ++ ++static int hisi_reset_deassert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev); ++ unsigned int offset, bit; ++ unsigned long flags; ++ u32 reg; ++ ++ offset = id >> HISI_RESET_OFFSET_SHIFT; ++ offset &= (BIT(HISI_RESET_OFFSET_WIDTH) - 1); ++ bit = id & (BIT(HISI_RESET_BIT_WIDTH) - 1); ++ ++ spin_lock_irqsave(&rstc->lock, flags); ++ ++ reg = readl(rstc->membase + offset); ++ writel(reg & ~BIT(bit), rstc->membase + offset); ++ ++ spin_unlock_irqrestore(&rstc->lock, flags); ++ ++ return 0; ++} ++ ++static struct reset_control_ops hisi_reset_ops = { ++ .assert = hisi_reset_assert, ++ .deassert = hisi_reset_deassert, ++}; ++ ++int __init hisi_reset_init(struct device_node *np, ++ int nr_rsts) ++{ ++ struct hisi_reset_controller *rstc; ++ ++ rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); ++ if (!rstc) ++ return -ENOMEM; ++ ++ rstc->membase = of_iomap(np, 0); ++ if (!rstc->membase) ++ return -EINVAL; ++ ++ spin_lock_init(&rstc->lock); ++ ++ rstc->rcdev.owner = THIS_MODULE; ++ rstc->rcdev.nr_resets = nr_rsts; ++ rstc->rcdev.ops = &hisi_reset_ops; ++ rstc->rcdev.of_node = np; ++ rstc->rcdev.of_reset_n_cells = 2; ++ rstc->rcdev.of_xlate = hisi_reset_of_xlate; ++ ++ return reset_controller_register(&rstc->rcdev); ++} +diff --git a/drivers/clk/hisilicon/reset.h b/drivers/clk/hisilicon/reset.h +new file mode 100644 +index 0000000..74bea4e +--- /dev/null ++++ b/drivers/clk/hisilicon/reset.h +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef __HISI_RESET_H ++#define __HISI_RESET_H ++ ++#include <linux/of.h> ++ ++int __init hisi_reset_init(struct device_node *np, int nr_rsts); ++ ++#endif /* __HISI_RESET_H */ +diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig +index 9042060..497b2f5 100644 +--- a/drivers/clocksource/Kconfig ++++ b/drivers/clocksource/Kconfig +@@ -110,6 +110,14 @@ config ARM_ARCH_TIMER_EVTSTREAM + This must be disabled for hardware validation purposes to detect any + hardware anomalies of missing events. + ++config ARM_ARCH_TIMER_VCT_ACCESS ++ bool "Support for ARM architected timer virtual counter access in userspace" ++ default n ++ depends on ARM_ARCH_TIMER ++ help ++ This option enables support for reading the ARM architected timer's ++ virtual counter in userspace. ++ + config ARM_GLOBAL_TIMER + bool + select CLKSRC_OF if OF +diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c +index 84b4c8b..fdf3959 100644 +--- a/drivers/clocksource/arm_arch_timer.c ++++ b/drivers/clocksource/arm_arch_timer.c +@@ -339,7 +339,10 @@ static void arch_counter_set_user_access(void) + | ARCH_TIMER_USR_PCT_ACCESS_EN); + + /* Enable user access to the virtual counter */ +- cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN; ++ if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_VCT_ACCESS)) ++ cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN; ++ else ++ cntkctl &= ~ARCH_TIMER_USR_VCT_ACCESS_EN; + + arch_timer_set_cntkctl(cntkctl); + } +diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig +index 3489f8f..548311f 100644 +--- a/drivers/cpufreq/Kconfig ++++ b/drivers/cpufreq/Kconfig +@@ -102,6 +102,16 @@ config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE + Be aware that not all cpufreq drivers support the conservative + governor. If unsure have a look at the help section of the + driver. Fallback governor will be the performance governor. ++ ++config CPU_FREQ_DEFAULT_GOV_INTERACTIVE ++ bool "interactive" ++ select CPU_FREQ_GOV_INTERACTIVE ++ help ++ Use the CPUFreq governor 'interactive' as default. This allows ++ you to get a full dynamic cpu frequency capable system by simply ++ loading your cpufreq low-level hardware driver, using the ++ 'interactive' governor for latency-sensitive workloads. ++ + endchoice + + config CPU_FREQ_GOV_PERFORMANCE +@@ -159,6 +169,23 @@ config CPU_FREQ_GOV_ONDEMAND + + If in doubt, say N. + ++config CPU_FREQ_GOV_INTERACTIVE ++ tristate "'interactive' cpufreq policy governor" ++ help ++ 'interactive' - This driver adds a dynamic cpufreq policy governor ++ designed for latency-sensitive workloads. ++ ++ This governor attempts to reduce the latency of clock ++ increases so that the system is more responsive to ++ interactive workloads. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cpufreq_interactive. ++ ++ For details, take a look at linux/Documentation/cpu-freq. ++ ++ If in doubt, say N. ++ + config CPU_FREQ_GOV_CONSERVATIVE + tristate "'conservative' cpufreq governor" + depends on CPU_FREQ +diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm +index 83a75dc..28c9b5f 100644 +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -247,3 +247,4 @@ config ARM_TEGRA_CPUFREQ + default y + help + This adds the CPUFreq driver support for TEGRA SOCs. ++ +diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile +index 40c53dc..b89b1f7 100644 +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -11,6 +11,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o + obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o + obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o + obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o ++obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o + obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o + + obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o +diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c +new file mode 100644 +index 0000000..e569e0b +--- /dev/null ++++ b/drivers/cpufreq/cpufreq_interactive.c +@@ -0,0 +1,1338 @@ ++/* ++ * drivers/cpufreq/cpufreq_interactive.c ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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. ++ * ++ * Author: Mike Chan (mike@android.com) ++ * ++ */ ++ ++#include <linux/cpu.h> ++#include <linux/cpumask.h> ++#include <linux/cpufreq.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/rwsem.h> ++#include <linux/sched.h> ++#include <linux/sched/rt.h> ++#include <linux/tick.h> ++#include <linux/time.h> ++#include <linux/timer.h> ++#include <linux/workqueue.h> ++#include <linux/kthread.h> ++#include <linux/slab.h> ++ ++#define CREATE_TRACE_POINTS ++#include <trace/events/cpufreq_interactive.h> ++ ++struct cpufreq_interactive_cpuinfo { ++ struct timer_list cpu_timer; ++ struct timer_list cpu_slack_timer; ++ spinlock_t load_lock; /* protects the next 4 fields */ ++ u64 time_in_idle; ++ u64 time_in_idle_timestamp; ++ u64 cputime_speedadj; ++ u64 cputime_speedadj_timestamp; ++ struct cpufreq_policy *policy; ++ struct cpufreq_frequency_table *freq_table; ++ spinlock_t target_freq_lock; /*protects target freq */ ++ unsigned int target_freq; ++ unsigned int floor_freq; ++ u64 pol_floor_val_time; /* policy floor_validate_time */ ++ u64 loc_floor_val_time; /* per-cpu floor_validate_time */ ++ u64 pol_hispeed_val_time; /* policy hispeed_validate_time */ ++ u64 loc_hispeed_val_time; /* per-cpu hispeed_validate_time */ ++ struct rw_semaphore enable_sem; ++ int governor_enabled; ++}; ++ ++static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); ++ ++/* realtime thread handles frequency scaling */ ++static struct task_struct *speedchange_task; ++static cpumask_t speedchange_cpumask; ++static spinlock_t speedchange_cpumask_lock; ++static struct mutex gov_lock; ++ ++/* Target load. Lower values result in higher CPU speeds. */ ++#define DEFAULT_TARGET_LOAD 90 ++static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD}; ++ ++#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC) ++#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE ++static unsigned int default_above_hispeed_delay[] = { ++ DEFAULT_ABOVE_HISPEED_DELAY }; ++ ++struct cpufreq_interactive_tunables { ++ int usage_count; ++ /* Hi speed to bump to from lo speed when load burst (default max) */ ++ unsigned int hispeed_freq; ++ /* Go to hi speed when CPU load at or above this value. */ ++#define DEFAULT_GO_HISPEED_LOAD 99 ++ unsigned long go_hispeed_load; ++ /* Target load. Lower values result in higher CPU speeds. */ ++ spinlock_t target_loads_lock; ++ unsigned int *target_loads; ++ int ntarget_loads; ++ /* ++ * The minimum amount of time to spend at a frequency before we can ramp ++ * down. ++ */ ++#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC) ++ unsigned long min_sample_time; ++ /* ++ * The sample rate of the timer used to increase frequency ++ */ ++ unsigned long timer_rate; ++ /* ++ * Wait this long before raising speed above hispeed, by default a ++ * single timer interval. ++ */ ++ spinlock_t above_hispeed_delay_lock; ++ unsigned int *above_hispeed_delay; ++ int nabove_hispeed_delay; ++ /* Non-zero means indefinite speed boost active */ ++ int boost_val; ++ /* Duration of a boot pulse in usecs */ ++ int boostpulse_duration_val; ++ /* End time of boost pulse in ktime converted to usecs */ ++ u64 boostpulse_endtime; ++ bool boosted; ++ /* ++ * Max additional time to wait in idle, beyond timer_rate, at speeds ++ * above minimum before wakeup to reduce speed, or -1 if unnecessary. ++ */ ++#define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE) ++ int timer_slack_val; ++ bool io_is_busy; ++}; ++ ++/* For cases where we have single governor instance for system */ ++static struct cpufreq_interactive_tunables *common_tunables; ++ ++static struct attribute_group *get_sysfs_attr(void); ++ ++static void cpufreq_interactive_timer_resched( ++ struct cpufreq_interactive_cpuinfo *pcpu) ++{ ++ struct cpufreq_interactive_tunables *tunables = ++ pcpu->policy->governor_data; ++ unsigned long expires; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&pcpu->load_lock, flags); ++ pcpu->time_in_idle = ++ get_cpu_idle_time(smp_processor_id(), ++ &pcpu->time_in_idle_timestamp, ++ tunables->io_is_busy); ++ pcpu->cputime_speedadj = 0; ++ pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; ++ expires = jiffies + usecs_to_jiffies(tunables->timer_rate); ++ mod_timer_pinned(&pcpu->cpu_timer, expires); ++ ++ if (tunables->timer_slack_val >= 0 && ++ pcpu->target_freq > pcpu->policy->min) { ++ expires += usecs_to_jiffies(tunables->timer_slack_val); ++ mod_timer_pinned(&pcpu->cpu_slack_timer, expires); ++ } ++ ++ spin_unlock_irqrestore(&pcpu->load_lock, flags); ++} ++ ++/* The caller shall take enable_sem write semaphore to avoid any timer race. ++ * The cpu_timer and cpu_slack_timer must be deactivated when calling this ++ * function. ++ */ ++static void cpufreq_interactive_timer_start( ++ struct cpufreq_interactive_tunables *tunables, int cpu) ++{ ++ struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); ++ unsigned long expires = jiffies + ++ usecs_to_jiffies(tunables->timer_rate); ++ unsigned long flags; ++ ++ pcpu->cpu_timer.expires = expires; ++ add_timer_on(&pcpu->cpu_timer, cpu); ++ if (tunables->timer_slack_val >= 0 && ++ pcpu->target_freq > pcpu->policy->min) { ++ expires += usecs_to_jiffies(tunables->timer_slack_val); ++ pcpu->cpu_slack_timer.expires = expires; ++ add_timer_on(&pcpu->cpu_slack_timer, cpu); ++ } ++ ++ spin_lock_irqsave(&pcpu->load_lock, flags); ++ pcpu->time_in_idle = ++ get_cpu_idle_time(cpu, &pcpu->time_in_idle_timestamp, ++ tunables->io_is_busy); ++ pcpu->cputime_speedadj = 0; ++ pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; ++ spin_unlock_irqrestore(&pcpu->load_lock, flags); ++} ++ ++static unsigned int freq_to_above_hispeed_delay( ++ struct cpufreq_interactive_tunables *tunables, ++ unsigned int freq) ++{ ++ int i; ++ unsigned int ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); ++ ++ for (i = 0; i < tunables->nabove_hispeed_delay - 1 && ++ freq >= tunables->above_hispeed_delay[i+1]; i += 2) ++ ; ++ ++ ret = tunables->above_hispeed_delay[i]; ++ spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); ++ return ret; ++} ++ ++static unsigned int freq_to_targetload( ++ struct cpufreq_interactive_tunables *tunables, unsigned int freq) ++{ ++ int i; ++ unsigned int ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tunables->target_loads_lock, flags); ++ ++ for (i = 0; i < tunables->ntarget_loads - 1 && ++ freq >= tunables->target_loads[i+1]; i += 2) ++ ; ++ ++ ret = tunables->target_loads[i]; ++ spin_unlock_irqrestore(&tunables->target_loads_lock, flags); ++ return ret; ++} ++ ++/* ++ * If increasing frequencies never map to a lower target load then ++ * choose_freq() will find the minimum frequency that does not exceed its ++ * target load given the current load. ++ */ ++static unsigned int choose_freq(struct cpufreq_interactive_cpuinfo *pcpu, ++ unsigned int loadadjfreq) ++{ ++ unsigned int freq = pcpu->policy->cur; ++ unsigned int prevfreq, freqmin, freqmax; ++ unsigned int tl; ++ int index; ++ ++ freqmin = 0; ++ freqmax = UINT_MAX; ++ ++ do { ++ prevfreq = freq; ++ tl = freq_to_targetload(pcpu->policy->governor_data, freq); ++ ++ /* ++ * Find the lowest frequency where the computed load is less ++ * than or equal to the target load. ++ */ ++ ++ if (cpufreq_frequency_table_target( ++ pcpu->policy, pcpu->freq_table, loadadjfreq / tl, ++ CPUFREQ_RELATION_L, &index)) ++ break; ++ freq = pcpu->freq_table[index].frequency; ++ ++ if (freq > prevfreq) { ++ /* The previous frequency is too low. */ ++ freqmin = prevfreq; ++ ++ if (freq >= freqmax) { ++ /* ++ * Find the highest frequency that is less ++ * than freqmax. ++ */ ++ if (cpufreq_frequency_table_target( ++ pcpu->policy, pcpu->freq_table, ++ freqmax - 1, CPUFREQ_RELATION_H, ++ &index)) ++ break; ++ freq = pcpu->freq_table[index].frequency; ++ ++ if (freq == freqmin) { ++ /* ++ * The first frequency below freqmax ++ * has already been found to be too ++ * low. freqmax is the lowest speed ++ * we found that is fast enough. ++ */ ++ freq = freqmax; ++ break; ++ } ++ } ++ } else if (freq < prevfreq) { ++ /* The previous frequency is high enough. */ ++ freqmax = prevfreq; ++ ++ if (freq <= freqmin) { ++ /* ++ * Find the lowest frequency that is higher ++ * than freqmin. ++ */ ++ if (cpufreq_frequency_table_target( ++ pcpu->policy, pcpu->freq_table, ++ freqmin + 1, CPUFREQ_RELATION_L, ++ &index)) ++ break; ++ freq = pcpu->freq_table[index].frequency; ++ ++ /* ++ * If freqmax is the first frequency above ++ * freqmin then we have already found that ++ * this speed is fast enough. ++ */ ++ if (freq == freqmax) ++ break; ++ } ++ } ++ ++ /* If same frequency chosen as previous then done. */ ++ } while (freq != prevfreq); ++ ++ return freq; ++} ++ ++static u64 update_load(int cpu) ++{ ++ struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); ++ struct cpufreq_interactive_tunables *tunables = ++ pcpu->policy->governor_data; ++ u64 now; ++ u64 now_idle; ++ unsigned int delta_idle; ++ unsigned int delta_time; ++ u64 active_time; ++ ++ now_idle = get_cpu_idle_time(cpu, &now, tunables->io_is_busy); ++ delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); ++ delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); ++ ++ if (delta_time <= delta_idle) ++ active_time = 0; ++ else ++ active_time = delta_time - delta_idle; ++ ++ pcpu->cputime_speedadj += active_time * pcpu->policy->cur; ++ ++ pcpu->time_in_idle = now_idle; ++ pcpu->time_in_idle_timestamp = now; ++ return now; ++} ++ ++static void cpufreq_interactive_timer(unsigned long data) ++{ ++ u64 now; ++ unsigned int delta_time; ++ u64 cputime_speedadj; ++ int cpu_load; ++ struct cpufreq_interactive_cpuinfo *pcpu = ++ &per_cpu(cpuinfo, data); ++ struct cpufreq_interactive_tunables *tunables = ++ pcpu->policy->governor_data; ++ unsigned int new_freq; ++ unsigned int loadadjfreq; ++ unsigned int index; ++ unsigned long flags; ++ u64 max_fvtime; ++ ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ return; ++ if (!pcpu->governor_enabled) ++ goto exit; ++ ++ spin_lock_irqsave(&pcpu->load_lock, flags); ++ now = update_load(data); ++ delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp); ++ cputime_speedadj = pcpu->cputime_speedadj; ++ spin_unlock_irqrestore(&pcpu->load_lock, flags); ++ ++ if (WARN_ON_ONCE(!delta_time)) ++ goto rearm; ++ ++ spin_lock_irqsave(&pcpu->target_freq_lock, flags); ++ do_div(cputime_speedadj, delta_time); ++ loadadjfreq = (unsigned int)cputime_speedadj * 100; ++ cpu_load = loadadjfreq / pcpu->policy->cur; ++ tunables->boosted = tunables->boost_val || now < tunables->boostpulse_endtime; ++ ++ if (cpu_load >= tunables->go_hispeed_load || tunables->boosted) { ++ if (pcpu->policy->cur < tunables->hispeed_freq) { ++ new_freq = tunables->hispeed_freq; ++ } else { ++ new_freq = choose_freq(pcpu, loadadjfreq); ++ ++ if (new_freq < tunables->hispeed_freq) ++ new_freq = tunables->hispeed_freq; ++ } ++ } else { ++ new_freq = choose_freq(pcpu, loadadjfreq); ++ if (new_freq > tunables->hispeed_freq && ++ pcpu->policy->cur < tunables->hispeed_freq) ++ new_freq = tunables->hispeed_freq; ++ } ++ ++ if (pcpu->policy->cur >= tunables->hispeed_freq && ++ new_freq > pcpu->policy->cur && ++ now - pcpu->pol_hispeed_val_time < ++ freq_to_above_hispeed_delay(tunables, pcpu->policy->cur)) { ++ trace_cpufreq_interactive_notyet( ++ data, cpu_load, pcpu->target_freq, ++ pcpu->policy->cur, new_freq); ++ spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); ++ goto rearm; ++ } ++ ++ pcpu->loc_hispeed_val_time = now; ++ ++ if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, ++ new_freq, CPUFREQ_RELATION_L, ++ &index)) { ++ spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); ++ goto rearm; ++ } ++ ++ new_freq = pcpu->freq_table[index].frequency; ++ ++ /* ++ * Do not scale below floor_freq unless we have been at or above the ++ * floor frequency for the minimum sample time since last validated. ++ */ ++ max_fvtime = max(pcpu->pol_floor_val_time, pcpu->loc_floor_val_time); ++ if (new_freq < pcpu->floor_freq && ++ pcpu->target_freq >= pcpu->policy->cur) { ++ if (now - max_fvtime < tunables->min_sample_time) { ++ trace_cpufreq_interactive_notyet( ++ data, cpu_load, pcpu->target_freq, ++ pcpu->policy->cur, new_freq); ++ spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); ++ goto rearm; ++ } ++ } ++ ++ /* ++ * Update the timestamp for checking whether speed has been held at ++ * or above the selected frequency for a minimum of min_sample_time, ++ * if not boosted to hispeed_freq. If boosted to hispeed_freq then we ++ * allow the speed to drop as soon as the boostpulse duration expires ++ * (or the indefinite boost is turned off). ++ */ ++ ++ if (!tunables->boosted || new_freq > tunables->hispeed_freq) { ++ pcpu->floor_freq = new_freq; ++ if (pcpu->target_freq >= pcpu->policy->cur || ++ new_freq >= pcpu->policy->cur) ++ pcpu->loc_floor_val_time = now; ++ } ++ ++ if (pcpu->target_freq == new_freq && ++ pcpu->target_freq <= pcpu->policy->cur) { ++ trace_cpufreq_interactive_already( ++ data, cpu_load, pcpu->target_freq, ++ pcpu->policy->cur, new_freq); ++ spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); ++ goto rearm; ++ } ++ ++ trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, ++ pcpu->policy->cur, new_freq); ++ ++ pcpu->target_freq = new_freq; ++ spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); ++ spin_lock_irqsave(&speedchange_cpumask_lock, flags); ++ cpumask_set_cpu(data, &speedchange_cpumask); ++ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); ++ wake_up_process(speedchange_task); ++ ++rearm: ++ if (!timer_pending(&pcpu->cpu_timer)) ++ cpufreq_interactive_timer_resched(pcpu); ++ ++exit: ++ up_read(&pcpu->enable_sem); ++ return; ++} ++ ++static void cpufreq_interactive_idle_end(void) ++{ ++ struct cpufreq_interactive_cpuinfo *pcpu = ++ &per_cpu(cpuinfo, smp_processor_id()); ++ ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ return; ++ if (!pcpu->governor_enabled) { ++ up_read(&pcpu->enable_sem); ++ return; ++ } ++ ++ /* Arm the timer for 1-2 ticks later if not already. */ ++ if (!timer_pending(&pcpu->cpu_timer)) { ++ cpufreq_interactive_timer_resched(pcpu); ++ } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) { ++ del_timer(&pcpu->cpu_timer); ++ del_timer(&pcpu->cpu_slack_timer); ++ cpufreq_interactive_timer(smp_processor_id()); ++ } ++ ++ up_read(&pcpu->enable_sem); ++} ++ ++static int cpufreq_interactive_speedchange_task(void *data) ++{ ++ unsigned int cpu; ++ cpumask_t tmp_mask; ++ unsigned long flags; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ ++ while (1) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ spin_lock_irqsave(&speedchange_cpumask_lock, flags); ++ ++ if (cpumask_empty(&speedchange_cpumask)) { ++ spin_unlock_irqrestore(&speedchange_cpumask_lock, ++ flags); ++ schedule(); ++ ++ if (kthread_should_stop()) ++ break; ++ ++ spin_lock_irqsave(&speedchange_cpumask_lock, flags); ++ } ++ ++ set_current_state(TASK_RUNNING); ++ tmp_mask = speedchange_cpumask; ++ cpumask_clear(&speedchange_cpumask); ++ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); ++ ++ for_each_cpu(cpu, &tmp_mask) { ++ unsigned int j; ++ unsigned int max_freq = 0; ++ struct cpufreq_interactive_cpuinfo *pjcpu; ++ u64 hvt = ~0ULL, fvt = 0; ++ ++ pcpu = &per_cpu(cpuinfo, cpu); ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ continue; ++ if (!pcpu->governor_enabled) { ++ up_read(&pcpu->enable_sem); ++ continue; ++ } ++ ++ for_each_cpu(j, pcpu->policy->cpus) { ++ pjcpu = &per_cpu(cpuinfo, j); ++ ++ fvt = max(fvt, pjcpu->loc_floor_val_time); ++ if (pjcpu->target_freq > max_freq) { ++ max_freq = pjcpu->target_freq; ++ hvt = pjcpu->loc_hispeed_val_time; ++ } else if (pjcpu->target_freq == max_freq) { ++ hvt = min(hvt, pjcpu->loc_hispeed_val_time); ++ } ++ } ++ for_each_cpu(j, pcpu->policy->cpus) { ++ pjcpu = &per_cpu(cpuinfo, j); ++ pjcpu->pol_floor_val_time = fvt; ++ } ++ ++ if (max_freq != pcpu->policy->cur) { ++ __cpufreq_driver_target(pcpu->policy, ++ max_freq, ++ CPUFREQ_RELATION_H); ++ for_each_cpu(j, pcpu->policy->cpus) { ++ pjcpu = &per_cpu(cpuinfo, j); ++ pjcpu->pol_hispeed_val_time = hvt; ++ } ++ } ++ trace_cpufreq_interactive_setspeed(cpu, ++ pcpu->target_freq, ++ pcpu->policy->cur); ++ ++ up_read(&pcpu->enable_sem); ++ } ++ } ++ ++ return 0; ++} ++ ++static void cpufreq_interactive_boost(struct cpufreq_interactive_tunables *tunables) ++{ ++ int i; ++ int anyboost = 0; ++ unsigned long flags[2]; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ ++ tunables->boosted = true; ++ ++ spin_lock_irqsave(&speedchange_cpumask_lock, flags[0]); ++ ++ for_each_online_cpu(i) { ++ pcpu = &per_cpu(cpuinfo, i); ++ if (tunables != pcpu->policy->governor_data) ++ continue; ++ ++ spin_lock_irqsave(&pcpu->target_freq_lock, flags[1]); ++ if (pcpu->target_freq < tunables->hispeed_freq) { ++ pcpu->target_freq = tunables->hispeed_freq; ++ cpumask_set_cpu(i, &speedchange_cpumask); ++ pcpu->pol_hispeed_val_time = ++ ktime_to_us(ktime_get()); ++ anyboost = 1; ++ } ++ spin_unlock_irqrestore(&pcpu->target_freq_lock, flags[1]); ++ } ++ ++ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags[0]); ++ ++ if (anyboost) ++ wake_up_process(speedchange_task); ++} ++ ++static int cpufreq_interactive_notifier( ++ struct notifier_block *nb, unsigned long val, void *data) ++{ ++ struct cpufreq_freqs *freq = data; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ int cpu; ++ unsigned long flags; ++ ++ if (val == CPUFREQ_POSTCHANGE) { ++ pcpu = &per_cpu(cpuinfo, freq->cpu); ++ if (!down_read_trylock(&pcpu->enable_sem)) ++ return 0; ++ if (!pcpu->governor_enabled) { ++ up_read(&pcpu->enable_sem); ++ return 0; ++ } ++ ++ for_each_cpu(cpu, pcpu->policy->cpus) { ++ struct cpufreq_interactive_cpuinfo *pjcpu = ++ &per_cpu(cpuinfo, cpu); ++ if (cpu != freq->cpu) { ++ if (!down_read_trylock(&pjcpu->enable_sem)) ++ continue; ++ if (!pjcpu->governor_enabled) { ++ up_read(&pjcpu->enable_sem); ++ continue; ++ } ++ } ++ spin_lock_irqsave(&pjcpu->load_lock, flags); ++ update_load(cpu); ++ spin_unlock_irqrestore(&pjcpu->load_lock, flags); ++ if (cpu != freq->cpu) ++ up_read(&pjcpu->enable_sem); ++ } ++ ++ up_read(&pcpu->enable_sem); ++ } ++ return 0; ++} ++ ++static struct notifier_block cpufreq_notifier_block = { ++ .notifier_call = cpufreq_interactive_notifier, ++}; ++ ++static unsigned int *get_tokenized_data(const char *buf, int *num_tokens) ++{ ++ const char *cp; ++ int i; ++ int ntokens = 1; ++ unsigned int *tokenized_data; ++ int err = -EINVAL; ++ ++ cp = buf; ++ while ((cp = strpbrk(cp + 1, " :"))) ++ ntokens++; ++ ++ if (!(ntokens & 0x1)) ++ goto err; ++ ++ tokenized_data = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL); ++ if (!tokenized_data) { ++ err = -ENOMEM; ++ goto err; ++ } ++ ++ cp = buf; ++ i = 0; ++ while (i < ntokens) { ++ if (sscanf(cp, "%u", &tokenized_data[i++]) != 1) ++ goto err_kfree; ++ ++ cp = strpbrk(cp, " :"); ++ if (!cp) ++ break; ++ cp++; ++ } ++ ++ if (i != ntokens) ++ goto err_kfree; ++ ++ *num_tokens = ntokens; ++ return tokenized_data; ++ ++err_kfree: ++ kfree(tokenized_data); ++err: ++ return ERR_PTR(err); ++} ++ ++static ssize_t show_target_loads( ++ struct cpufreq_interactive_tunables *tunables, ++ char *buf) ++{ ++ int i; ++ ssize_t ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tunables->target_loads_lock, flags); ++ ++ for (i = 0; i < tunables->ntarget_loads; i++) ++ ret += sprintf(buf + ret, "%u%s", tunables->target_loads[i], ++ i & 0x1 ? ":" : " "); ++ ++ sprintf(buf + ret - 1, "\n"); ++ spin_unlock_irqrestore(&tunables->target_loads_lock, flags); ++ return ret; ++} ++ ++static ssize_t store_target_loads( ++ struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ntokens; ++ unsigned int *new_target_loads = NULL; ++ unsigned long flags; ++ ++ new_target_loads = get_tokenized_data(buf, &ntokens); ++ if (IS_ERR(new_target_loads)) ++ return PTR_RET(new_target_loads); ++ ++ spin_lock_irqsave(&tunables->target_loads_lock, flags); ++ if (tunables->target_loads != default_target_loads) ++ kfree(tunables->target_loads); ++ tunables->target_loads = new_target_loads; ++ tunables->ntarget_loads = ntokens; ++ spin_unlock_irqrestore(&tunables->target_loads_lock, flags); ++ return count; ++} ++ ++static ssize_t show_above_hispeed_delay( ++ struct cpufreq_interactive_tunables *tunables, char *buf) ++{ ++ int i; ++ ssize_t ret = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); ++ ++ for (i = 0; i < tunables->nabove_hispeed_delay; i++) ++ ret += sprintf(buf + ret, "%u%s", ++ tunables->above_hispeed_delay[i], ++ i & 0x1 ? ":" : " "); ++ ++ sprintf(buf + ret - 1, "\n"); ++ spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); ++ return ret; ++} ++ ++static ssize_t store_above_hispeed_delay( ++ struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ntokens; ++ unsigned int *new_above_hispeed_delay = NULL; ++ unsigned long flags; ++ ++ new_above_hispeed_delay = get_tokenized_data(buf, &ntokens); ++ if (IS_ERR(new_above_hispeed_delay)) ++ return PTR_RET(new_above_hispeed_delay); ++ ++ spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); ++ if (tunables->above_hispeed_delay != default_above_hispeed_delay) ++ kfree(tunables->above_hispeed_delay); ++ tunables->above_hispeed_delay = new_above_hispeed_delay; ++ tunables->nabove_hispeed_delay = ntokens; ++ spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); ++ return count; ++ ++} ++ ++static ssize_t show_hispeed_freq(struct cpufreq_interactive_tunables *tunables, ++ char *buf) ++{ ++ return sprintf(buf, "%u\n", tunables->hispeed_freq); ++} ++ ++static ssize_t store_hispeed_freq(struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ret; ++ long unsigned int val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ tunables->hispeed_freq = val; ++ return count; ++} ++ ++static ssize_t show_go_hispeed_load(struct cpufreq_interactive_tunables ++ *tunables, char *buf) ++{ ++ return sprintf(buf, "%lu\n", tunables->go_hispeed_load); ++} ++ ++static ssize_t store_go_hispeed_load(struct cpufreq_interactive_tunables ++ *tunables, const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ tunables->go_hispeed_load = val; ++ return count; ++} ++ ++static ssize_t show_min_sample_time(struct cpufreq_interactive_tunables ++ *tunables, char *buf) ++{ ++ return sprintf(buf, "%lu\n", tunables->min_sample_time); ++} ++ ++static ssize_t store_min_sample_time(struct cpufreq_interactive_tunables ++ *tunables, const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ tunables->min_sample_time = val; ++ return count; ++} ++ ++static ssize_t show_timer_rate(struct cpufreq_interactive_tunables *tunables, ++ char *buf) ++{ ++ return sprintf(buf, "%lu\n", tunables->timer_rate); ++} ++ ++static ssize_t store_timer_rate(struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val, val_round; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ ++ val_round = jiffies_to_usecs(usecs_to_jiffies(val)); ++ if (val != val_round) ++ pr_warn("timer_rate not aligned to jiffy. Rounded up to %lu\n", ++ val_round); ++ ++ tunables->timer_rate = val_round; ++ return count; ++} ++ ++static ssize_t show_timer_slack(struct cpufreq_interactive_tunables *tunables, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", tunables->timer_slack_val); ++} ++ ++static ssize_t store_timer_slack(struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtol(buf, 10, &val); ++ if (ret < 0) ++ return ret; ++ ++ tunables->timer_slack_val = val; ++ return count; ++} ++ ++static ssize_t show_boost(struct cpufreq_interactive_tunables *tunables, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", tunables->boost_val); ++} ++ ++static ssize_t store_boost(struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ ++ tunables->boost_val = val; ++ ++ if (tunables->boost_val) { ++ trace_cpufreq_interactive_boost("on"); ++ if (!tunables->boosted) ++ cpufreq_interactive_boost(tunables); ++ } else { ++ tunables->boostpulse_endtime = ktime_to_us(ktime_get()); ++ trace_cpufreq_interactive_unboost("off"); ++ } ++ ++ return count; ++} ++ ++static ssize_t store_boostpulse(struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ ++ tunables->boostpulse_endtime = ktime_to_us(ktime_get()) + ++ tunables->boostpulse_duration_val; ++ trace_cpufreq_interactive_boost("pulse"); ++ if (!tunables->boosted) ++ cpufreq_interactive_boost(tunables); ++ return count; ++} ++ ++static ssize_t show_boostpulse_duration(struct cpufreq_interactive_tunables ++ *tunables, char *buf) ++{ ++ return sprintf(buf, "%d\n", tunables->boostpulse_duration_val); ++} ++ ++static ssize_t store_boostpulse_duration(struct cpufreq_interactive_tunables ++ *tunables, const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ ++ tunables->boostpulse_duration_val = val; ++ return count; ++} ++ ++static ssize_t show_io_is_busy(struct cpufreq_interactive_tunables *tunables, ++ char *buf) ++{ ++ return sprintf(buf, "%u\n", tunables->io_is_busy); ++} ++ ++static ssize_t store_io_is_busy(struct cpufreq_interactive_tunables *tunables, ++ const char *buf, size_t count) ++{ ++ int ret; ++ unsigned long val; ++ ++ ret = kstrtoul(buf, 0, &val); ++ if (ret < 0) ++ return ret; ++ tunables->io_is_busy = val; ++ return count; ++} ++ ++/* ++ * Create show/store routines ++ * - sys: One governor instance for complete SYSTEM ++ * - pol: One governor instance per struct cpufreq_policy ++ */ ++#define show_gov_pol_sys(file_name) \ ++static ssize_t show_##file_name##_gov_sys \ ++(struct kobject *kobj, struct attribute *attr, char *buf) \ ++{ \ ++ return show_##file_name(common_tunables, buf); \ ++} \ ++ \ ++static ssize_t show_##file_name##_gov_pol \ ++(struct cpufreq_policy *policy, char *buf) \ ++{ \ ++ return show_##file_name(policy->governor_data, buf); \ ++} ++ ++#define store_gov_pol_sys(file_name) \ ++static ssize_t store_##file_name##_gov_sys \ ++(struct kobject *kobj, struct attribute *attr, const char *buf, \ ++ size_t count) \ ++{ \ ++ return store_##file_name(common_tunables, buf, count); \ ++} \ ++ \ ++static ssize_t store_##file_name##_gov_pol \ ++(struct cpufreq_policy *policy, const char *buf, size_t count) \ ++{ \ ++ return store_##file_name(policy->governor_data, buf, count); \ ++} ++ ++#define show_store_gov_pol_sys(file_name) \ ++show_gov_pol_sys(file_name); \ ++store_gov_pol_sys(file_name) ++ ++show_store_gov_pol_sys(target_loads); ++show_store_gov_pol_sys(above_hispeed_delay); ++show_store_gov_pol_sys(hispeed_freq); ++show_store_gov_pol_sys(go_hispeed_load); ++show_store_gov_pol_sys(min_sample_time); ++show_store_gov_pol_sys(timer_rate); ++show_store_gov_pol_sys(timer_slack); ++show_store_gov_pol_sys(boost); ++store_gov_pol_sys(boostpulse); ++show_store_gov_pol_sys(boostpulse_duration); ++show_store_gov_pol_sys(io_is_busy); ++ ++#define gov_sys_attr_rw(_name) \ ++static struct global_attr _name##_gov_sys = \ ++__ATTR(_name, 0644, show_##_name##_gov_sys, store_##_name##_gov_sys) ++ ++#define gov_pol_attr_rw(_name) \ ++static struct freq_attr _name##_gov_pol = \ ++__ATTR(_name, 0644, show_##_name##_gov_pol, store_##_name##_gov_pol) ++ ++#define gov_sys_pol_attr_rw(_name) \ ++ gov_sys_attr_rw(_name); \ ++ gov_pol_attr_rw(_name) ++ ++gov_sys_pol_attr_rw(target_loads); ++gov_sys_pol_attr_rw(above_hispeed_delay); ++gov_sys_pol_attr_rw(hispeed_freq); ++gov_sys_pol_attr_rw(go_hispeed_load); ++gov_sys_pol_attr_rw(min_sample_time); ++gov_sys_pol_attr_rw(timer_rate); ++gov_sys_pol_attr_rw(timer_slack); ++gov_sys_pol_attr_rw(boost); ++gov_sys_pol_attr_rw(boostpulse_duration); ++gov_sys_pol_attr_rw(io_is_busy); ++ ++static struct global_attr boostpulse_gov_sys = ++ __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_sys); ++ ++static struct freq_attr boostpulse_gov_pol = ++ __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_pol); ++ ++/* One Governor instance for entire system */ ++static struct attribute *interactive_attributes_gov_sys[] = { ++ &target_loads_gov_sys.attr, ++ &above_hispeed_delay_gov_sys.attr, ++ &hispeed_freq_gov_sys.attr, ++ &go_hispeed_load_gov_sys.attr, ++ &min_sample_time_gov_sys.attr, ++ &timer_rate_gov_sys.attr, ++ &timer_slack_gov_sys.attr, ++ &boost_gov_sys.attr, ++ &boostpulse_gov_sys.attr, ++ &boostpulse_duration_gov_sys.attr, ++ &io_is_busy_gov_sys.attr, ++ NULL, ++}; ++ ++static struct attribute_group interactive_attr_group_gov_sys = { ++ .attrs = interactive_attributes_gov_sys, ++ .name = "interactive", ++}; ++ ++/* Per policy governor instance */ ++static struct attribute *interactive_attributes_gov_pol[] = { ++ &target_loads_gov_pol.attr, ++ &above_hispeed_delay_gov_pol.attr, ++ &hispeed_freq_gov_pol.attr, ++ &go_hispeed_load_gov_pol.attr, ++ &min_sample_time_gov_pol.attr, ++ &timer_rate_gov_pol.attr, ++ &timer_slack_gov_pol.attr, ++ &boost_gov_pol.attr, ++ &boostpulse_gov_pol.attr, ++ &boostpulse_duration_gov_pol.attr, ++ &io_is_busy_gov_pol.attr, ++ NULL, ++}; ++ ++static struct attribute_group interactive_attr_group_gov_pol = { ++ .attrs = interactive_attributes_gov_pol, ++ .name = "interactive", ++}; ++ ++static struct attribute_group *get_sysfs_attr(void) ++{ ++ if (have_governor_per_policy()) ++ return &interactive_attr_group_gov_pol; ++ else ++ return &interactive_attr_group_gov_sys; ++} ++ ++static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, ++ unsigned long val, ++ void *data) ++{ ++ if (val == IDLE_END) ++ cpufreq_interactive_idle_end(); ++ ++ return 0; ++} ++ ++static struct notifier_block cpufreq_interactive_idle_nb = { ++ .notifier_call = cpufreq_interactive_idle_notifier, ++}; ++ ++static int cpufreq_governor_interactive(struct cpufreq_policy *policy, ++ unsigned int event) ++{ ++ int rc; ++ unsigned int j; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ struct cpufreq_frequency_table *freq_table; ++ struct cpufreq_interactive_tunables *tunables; ++ unsigned long flags; ++ ++ if (have_governor_per_policy()) ++ tunables = policy->governor_data; ++ else ++ tunables = common_tunables; ++ ++ WARN_ON(!tunables && (event != CPUFREQ_GOV_POLICY_INIT)); ++ ++ switch (event) { ++ case CPUFREQ_GOV_POLICY_INIT: ++ if (have_governor_per_policy()) { ++ WARN_ON(tunables); ++ } else if (tunables) { ++ tunables->usage_count++; ++ policy->governor_data = tunables; ++ return 0; ++ } ++ ++ tunables = kzalloc(sizeof(*tunables), GFP_KERNEL); ++ if (!tunables) { ++ pr_err("%s: POLICY_INIT: kzalloc failed\n", __func__); ++ return -ENOMEM; ++ } ++ ++ tunables->usage_count = 1; ++ tunables->above_hispeed_delay = default_above_hispeed_delay; ++ tunables->nabove_hispeed_delay = ++ ARRAY_SIZE(default_above_hispeed_delay); ++ tunables->go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; ++ tunables->target_loads = default_target_loads; ++ tunables->ntarget_loads = ARRAY_SIZE(default_target_loads); ++ tunables->min_sample_time = DEFAULT_MIN_SAMPLE_TIME; ++ tunables->timer_rate = DEFAULT_TIMER_RATE; ++ tunables->boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME; ++ tunables->timer_slack_val = DEFAULT_TIMER_SLACK; ++ ++ spin_lock_init(&tunables->target_loads_lock); ++ spin_lock_init(&tunables->above_hispeed_delay_lock); ++ ++ policy->governor_data = tunables; ++ if (!have_governor_per_policy()) { ++ common_tunables = tunables; ++ WARN_ON(cpufreq_get_global_kobject()); ++ } ++ ++ rc = sysfs_create_group(get_governor_parent_kobj(policy), ++ get_sysfs_attr()); ++ if (rc) { ++ kfree(tunables); ++ policy->governor_data = NULL; ++ if (!have_governor_per_policy()) { ++ common_tunables = NULL; ++ cpufreq_put_global_kobject(); ++ } ++ return rc; ++ } ++ ++ if (!policy->governor->initialized) { ++ idle_notifier_register(&cpufreq_interactive_idle_nb); ++ cpufreq_register_notifier(&cpufreq_notifier_block, ++ CPUFREQ_TRANSITION_NOTIFIER); ++ } ++ ++ break; ++ ++ case CPUFREQ_GOV_POLICY_EXIT: ++ if (!--tunables->usage_count) { ++ if (policy->governor->initialized == 1) { ++ cpufreq_unregister_notifier(&cpufreq_notifier_block, ++ CPUFREQ_TRANSITION_NOTIFIER); ++ idle_notifier_unregister(&cpufreq_interactive_idle_nb); ++ } ++ ++ sysfs_remove_group(get_governor_parent_kobj(policy), ++ get_sysfs_attr()); ++ ++ if (!have_governor_per_policy()) ++ cpufreq_put_global_kobject(); ++ ++ kfree(tunables); ++ common_tunables = NULL; ++ } ++ ++ policy->governor_data = NULL; ++ break; ++ ++ case CPUFREQ_GOV_START: ++ mutex_lock(&gov_lock); ++ ++ freq_table = cpufreq_frequency_get_table(policy->cpu); ++ if (!tunables->hispeed_freq) ++ tunables->hispeed_freq = policy->max; ++ ++ for_each_cpu(j, policy->cpus) { ++ pcpu = &per_cpu(cpuinfo, j); ++ pcpu->policy = policy; ++ pcpu->target_freq = policy->cur; ++ pcpu->freq_table = freq_table; ++ pcpu->floor_freq = pcpu->target_freq; ++ pcpu->pol_floor_val_time = ++ ktime_to_us(ktime_get()); ++ pcpu->loc_floor_val_time = pcpu->pol_floor_val_time; ++ pcpu->pol_hispeed_val_time = pcpu->pol_floor_val_time; ++ pcpu->loc_hispeed_val_time = pcpu->pol_floor_val_time; ++ down_write(&pcpu->enable_sem); ++ del_timer_sync(&pcpu->cpu_timer); ++ del_timer_sync(&pcpu->cpu_slack_timer); ++ cpufreq_interactive_timer_start(tunables, j); ++ pcpu->governor_enabled = 1; ++ up_write(&pcpu->enable_sem); ++ } ++ ++ mutex_unlock(&gov_lock); ++ break; ++ ++ case CPUFREQ_GOV_STOP: ++ mutex_lock(&gov_lock); ++ for_each_cpu(j, policy->cpus) { ++ pcpu = &per_cpu(cpuinfo, j); ++ down_write(&pcpu->enable_sem); ++ pcpu->governor_enabled = 0; ++ del_timer_sync(&pcpu->cpu_timer); ++ del_timer_sync(&pcpu->cpu_slack_timer); ++ up_write(&pcpu->enable_sem); ++ } ++ ++ mutex_unlock(&gov_lock); ++ break; ++ ++ case CPUFREQ_GOV_LIMITS: ++ if (policy->max < policy->cur) ++ __cpufreq_driver_target(policy, ++ policy->max, CPUFREQ_RELATION_H); ++ else if (policy->min > policy->cur) ++ __cpufreq_driver_target(policy, ++ policy->min, CPUFREQ_RELATION_L); ++ for_each_cpu(j, policy->cpus) { ++ pcpu = &per_cpu(cpuinfo, j); ++ ++ down_read(&pcpu->enable_sem); ++ if (pcpu->governor_enabled == 0) { ++ up_read(&pcpu->enable_sem); ++ continue; ++ } ++ ++ spin_lock_irqsave(&pcpu->target_freq_lock, flags); ++ if (policy->max < pcpu->target_freq) ++ pcpu->target_freq = policy->max; ++ else if (policy->min > pcpu->target_freq) ++ pcpu->target_freq = policy->min; ++ ++ spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); ++ up_read(&pcpu->enable_sem); ++ } ++ break; ++ } ++ return 0; ++} ++ ++#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE ++static ++#endif ++struct cpufreq_governor cpufreq_gov_interactive = { ++ .name = "interactive", ++ .governor = cpufreq_governor_interactive, ++ .max_transition_latency = 10000000, ++ .owner = THIS_MODULE, ++}; ++ ++static void cpufreq_interactive_nop_timer(unsigned long data) ++{ ++} ++ ++static int __init cpufreq_interactive_init(void) ++{ ++ unsigned int i; ++ struct cpufreq_interactive_cpuinfo *pcpu; ++ struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; ++ ++ /* Initalize per-cpu timers */ ++ for_each_possible_cpu(i) { ++ pcpu = &per_cpu(cpuinfo, i); ++ init_timer_deferrable(&pcpu->cpu_timer); ++ pcpu->cpu_timer.function = cpufreq_interactive_timer; ++ pcpu->cpu_timer.data = i; ++ init_timer(&pcpu->cpu_slack_timer); ++ pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer; ++ spin_lock_init(&pcpu->load_lock); ++ spin_lock_init(&pcpu->target_freq_lock); ++ init_rwsem(&pcpu->enable_sem); ++ } ++ ++ spin_lock_init(&speedchange_cpumask_lock); ++ mutex_init(&gov_lock); ++ speedchange_task = ++ kthread_create(cpufreq_interactive_speedchange_task, NULL, ++ "cfinteractive"); ++ if (IS_ERR(speedchange_task)) ++ return PTR_ERR(speedchange_task); ++ ++ sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m); ++ get_task_struct(speedchange_task); ++ ++ /* NB: wake up so the thread does not look hung to the freezer */ ++ wake_up_process(speedchange_task); ++ ++ return cpufreq_register_governor(&cpufreq_gov_interactive); ++} ++ ++#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE ++fs_initcall(cpufreq_interactive_init); ++#else ++module_init(cpufreq_interactive_init); ++#endif ++ ++static void __exit cpufreq_interactive_exit(void) ++{ ++ cpufreq_unregister_governor(&cpufreq_gov_interactive); ++ kthread_stop(speedchange_task); ++ put_task_struct(speedchange_task); ++} ++ ++module_exit(cpufreq_interactive_exit); ++ ++MODULE_AUTHOR("Mike Chan <mike@android.com>"); ++MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for " ++ "Latency sensitive workloads"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c +index 0cd9b4d..a2f1d41 100644 +--- a/drivers/cpufreq/cpufreq_stats.c ++++ b/drivers/cpufreq/cpufreq_stats.c +@@ -13,6 +13,9 @@ + #include <linux/cpufreq.h> + #include <linux/module.h> + #include <linux/slab.h> ++#include <linux/sort.h> ++#include <linux/of.h> ++#include <linux/sched.h> + #include <linux/cputime.h> + + static spinlock_t cpufreq_stats_lock; +@@ -31,7 +34,28 @@ struct cpufreq_stats { + #endif + }; + ++struct all_cpufreq_stats { ++ unsigned int state_num; ++ cputime64_t *time_in_state; ++ unsigned int *freq_table; ++}; ++ ++struct cpufreq_power_stats { ++ unsigned int state_num; ++ unsigned int *curr; ++ unsigned int *freq_table; ++}; ++ ++struct all_freq_table { ++ unsigned int *freq_table; ++ unsigned int table_size; ++}; ++ ++static struct all_freq_table *all_freq_table; ++ ++static DEFINE_PER_CPU(struct all_cpufreq_stats *, all_cpufreq_stats); + static DEFINE_PER_CPU(struct cpufreq_stats *, cpufreq_stats_table); ++static DEFINE_PER_CPU(struct cpufreq_power_stats *, cpufreq_power_stats); + + struct cpufreq_stats_attribute { + struct attribute attr; +@@ -41,14 +65,24 @@ struct cpufreq_stats_attribute { + static int cpufreq_stats_update(unsigned int cpu) + { + struct cpufreq_stats *stat; ++ struct all_cpufreq_stats *all_stat; + unsigned long long cur_time; + + cur_time = get_jiffies_64(); + spin_lock(&cpufreq_stats_lock); + stat = per_cpu(cpufreq_stats_table, cpu); +- if (stat->time_in_state) ++ all_stat = per_cpu(all_cpufreq_stats, cpu); ++ if (!stat) { ++ spin_unlock(&cpufreq_stats_lock); ++ return 0; ++ } ++ if (stat->time_in_state) { + stat->time_in_state[stat->last_index] += + cur_time - stat->last_time; ++ if (all_stat) ++ all_stat->time_in_state[stat->last_index] += ++ cur_time - stat->last_time; ++ } + stat->last_time = cur_time; + spin_unlock(&cpufreq_stats_lock); + return 0; +@@ -79,6 +113,104 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf) + return len; + } + ++static int get_index_all_cpufreq_stat(struct all_cpufreq_stats *all_stat, ++ unsigned int freq) ++{ ++ int i; ++ if (!all_stat) ++ return -1; ++ for (i = 0; i < all_stat->state_num; i++) { ++ if (all_stat->freq_table[i] == freq) ++ return i; ++ } ++ return -1; ++} ++ ++void acct_update_power(struct task_struct *task, cputime_t cputime) { ++ struct cpufreq_power_stats *powerstats; ++ struct cpufreq_stats *stats; ++ unsigned int cpu_num, curr; ++ ++ if (!task) ++ return; ++ cpu_num = task_cpu(task); ++ powerstats = per_cpu(cpufreq_power_stats, cpu_num); ++ stats = per_cpu(cpufreq_stats_table, cpu_num); ++ if (!powerstats || !stats) ++ return; ++ ++ curr = powerstats->curr[stats->last_index]; ++ if (task->cpu_power != ULLONG_MAX) ++ task->cpu_power += curr * cputime_to_usecs(cputime); ++} ++EXPORT_SYMBOL_GPL(acct_update_power); ++ ++static ssize_t show_current_in_state(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ unsigned int i, cpu; ++ struct cpufreq_power_stats *powerstats; ++ ++ spin_lock(&cpufreq_stats_lock); ++ for_each_possible_cpu(cpu) { ++ powerstats = per_cpu(cpufreq_power_stats, cpu); ++ if (!powerstats) ++ continue; ++ len += scnprintf(buf + len, PAGE_SIZE - len, "CPU%d:", cpu); ++ for (i = 0; i < powerstats->state_num; i++) ++ len += scnprintf(buf + len, PAGE_SIZE - len, ++ "%d=%d ", powerstats->freq_table[i], ++ powerstats->curr[i]); ++ len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); ++ } ++ spin_unlock(&cpufreq_stats_lock); ++ return len; ++} ++ ++static ssize_t show_all_time_in_state(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ ssize_t len = 0; ++ unsigned int i, cpu, freq, index; ++ struct all_cpufreq_stats *all_stat; ++ struct cpufreq_policy *policy; ++ ++ len += scnprintf(buf + len, PAGE_SIZE - len, "freq\t\t"); ++ for_each_possible_cpu(cpu) { ++ len += scnprintf(buf + len, PAGE_SIZE - len, "cpu%d\t\t", cpu); ++ if (cpu_online(cpu)) ++ cpufreq_stats_update(cpu); ++ } ++ ++ if (!all_freq_table) ++ goto out; ++ for (i = 0; i < all_freq_table->table_size; i++) { ++ freq = all_freq_table->freq_table[i]; ++ len += scnprintf(buf + len, PAGE_SIZE - len, "\n%u\t\t", freq); ++ for_each_possible_cpu(cpu) { ++ policy = cpufreq_cpu_get(cpu); ++ if (policy == NULL) ++ continue; ++ all_stat = per_cpu(all_cpufreq_stats, policy->cpu); ++ index = get_index_all_cpufreq_stat(all_stat, freq); ++ if (index != -1) { ++ len += scnprintf(buf + len, PAGE_SIZE - len, ++ "%llu\t\t", (unsigned long long) ++ cputime64_to_clock_t(all_stat->time_in_state[index])); ++ } else { ++ len += scnprintf(buf + len, PAGE_SIZE - len, ++ "N/A\t\t"); ++ } ++ cpufreq_cpu_put(policy); ++ } ++ } ++ ++out: ++ len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); ++ return len; ++} ++ + #ifdef CONFIG_CPU_FREQ_STAT_DETAILS + static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf) + { +@@ -142,6 +274,12 @@ static struct attribute_group stats_attr_group = { + .name = "stats" + }; + ++static struct kobj_attribute _attr_all_time_in_state = __ATTR(all_time_in_state, ++ 0444, show_all_time_in_state, NULL); ++ ++static struct kobj_attribute _attr_current_in_state = __ATTR(current_in_state, ++ 0444, show_current_in_state, NULL); ++ + static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq) + { + int index; +@@ -180,17 +318,54 @@ static void cpufreq_stats_free_table(unsigned int cpu) + cpufreq_cpu_put(policy); + } + +-static int __cpufreq_stats_create_table(struct cpufreq_policy *policy) ++static void cpufreq_allstats_free(void) ++{ ++ int cpu; ++ struct all_cpufreq_stats *all_stat; ++ ++ sysfs_remove_file(cpufreq_global_kobject, ++ &_attr_all_time_in_state.attr); ++ ++ for_each_possible_cpu(cpu) { ++ all_stat = per_cpu(all_cpufreq_stats, cpu); ++ if (!all_stat) ++ continue; ++ kfree(all_stat->time_in_state); ++ kfree(all_stat); ++ per_cpu(all_cpufreq_stats, cpu) = NULL; ++ } ++ if (all_freq_table) { ++ kfree(all_freq_table->freq_table); ++ kfree(all_freq_table); ++ all_freq_table = NULL; ++ } ++} ++ ++static void cpufreq_powerstats_free(void) ++{ ++ int cpu; ++ struct cpufreq_power_stats *powerstats; ++ ++ sysfs_remove_file(cpufreq_global_kobject, &_attr_current_in_state.attr); ++ ++ for_each_possible_cpu(cpu) { ++ powerstats = per_cpu(cpufreq_power_stats, cpu); ++ if (!powerstats) ++ continue; ++ kfree(powerstats->curr); ++ kfree(powerstats); ++ per_cpu(cpufreq_power_stats, cpu) = NULL; ++ } ++} ++ ++static int __cpufreq_stats_create_table(struct cpufreq_policy *policy, ++ struct cpufreq_frequency_table *table, int count) + { +- unsigned int i, count = 0, ret = 0; ++ unsigned int i, ret = 0; + struct cpufreq_stats *stat; + unsigned int alloc_size; + unsigned int cpu = policy->cpu; +- struct cpufreq_frequency_table *pos, *table; +- +- table = cpufreq_frequency_get_table(cpu); +- if (unlikely(!table)) +- return 0; ++ struct cpufreq_frequency_table *pos; + + if (per_cpu(cpufreq_stats_table, cpu)) + return -EBUSY; +@@ -205,9 +380,6 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy) + stat->cpu = cpu; + per_cpu(cpufreq_stats_table, cpu) = stat; + +- cpufreq_for_each_valid_entry(pos, table) +- count++; +- + alloc_size = count * sizeof(int) + count * sizeof(u64); + + #ifdef CONFIG_CPU_FREQ_STAT_DETAILS +@@ -242,10 +414,159 @@ error_out: + return ret; + } + ++static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy) ++{ ++ struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, ++ policy->last_cpu); ++ ++ pr_debug("Updating stats_table for new_cpu %u from last_cpu %u\n", ++ policy->cpu, policy->last_cpu); ++ per_cpu(cpufreq_stats_table, policy->cpu) = per_cpu(cpufreq_stats_table, ++ policy->last_cpu); ++ per_cpu(cpufreq_stats_table, policy->last_cpu) = NULL; ++ stat->cpu = policy->cpu; ++} ++ ++static void cpufreq_powerstats_create(unsigned int cpu, ++ struct cpufreq_frequency_table *table, int count) { ++ unsigned int alloc_size, i = 0, ret = 0; ++ struct cpufreq_power_stats *powerstats; ++ struct cpufreq_frequency_table *pos; ++ struct device_node *cpu_node; ++ char device_path[16]; ++ ++ powerstats = kzalloc(sizeof(struct cpufreq_power_stats), ++ GFP_KERNEL); ++ if (!powerstats) ++ return; ++ ++ /* Allocate memory for freq table per cpu as well as clockticks per ++ * freq*/ ++ alloc_size = count * sizeof(unsigned int) + ++ count * sizeof(unsigned int); ++ powerstats->curr = kzalloc(alloc_size, GFP_KERNEL); ++ if (!powerstats->curr) { ++ kfree(powerstats); ++ return; ++ } ++ powerstats->freq_table = powerstats->curr + count; ++ ++ spin_lock(&cpufreq_stats_lock); ++ i = 0; ++ cpufreq_for_each_valid_entry(pos, table) ++ powerstats->freq_table[i++] = pos->frequency; ++ powerstats->state_num = i; ++ ++ snprintf(device_path, sizeof(device_path), "/cpus/cpu@%d", cpu); ++ cpu_node = of_find_node_by_path(device_path); ++ if (cpu_node) { ++ ret = of_property_read_u32_array(cpu_node, "current", ++ powerstats->curr, count); ++ if (ret) { ++ kfree(powerstats->curr); ++ kfree(powerstats); ++ powerstats = NULL; ++ } ++ } ++ per_cpu(cpufreq_power_stats, cpu) = powerstats; ++ spin_unlock(&cpufreq_stats_lock); ++} ++ ++static int compare_for_sort(const void *lhs_ptr, const void *rhs_ptr) ++{ ++ unsigned int lhs = *(const unsigned int *)(lhs_ptr); ++ unsigned int rhs = *(const unsigned int *)(rhs_ptr); ++ if (lhs < rhs) ++ return -1; ++ if (lhs > rhs) ++ return 1; ++ return 0; ++} ++ ++static bool check_all_freq_table(unsigned int freq) ++{ ++ int i; ++ for (i = 0; i < all_freq_table->table_size; i++) { ++ if (freq == all_freq_table->freq_table[i]) ++ return true; ++ } ++ return false; ++} ++ ++static void create_all_freq_table(void) ++{ ++ all_freq_table = kzalloc(sizeof(struct all_freq_table), ++ GFP_KERNEL); ++ if (!all_freq_table) ++ pr_warn("could not allocate memory for all_freq_table\n"); ++ return; ++} ++ ++static void add_all_freq_table(unsigned int freq) ++{ ++ unsigned int size; ++ size = sizeof(unsigned int) * (all_freq_table->table_size + 1); ++ all_freq_table->freq_table = krealloc(all_freq_table->freq_table, ++ size, GFP_ATOMIC); ++ if (IS_ERR(all_freq_table->freq_table)) { ++ pr_warn("Could not reallocate memory for freq_table\n"); ++ all_freq_table->freq_table = NULL; ++ return; ++ } ++ all_freq_table->freq_table[all_freq_table->table_size++] = freq; ++} ++ ++static void cpufreq_allstats_create(unsigned int cpu, ++ struct cpufreq_frequency_table *table, int count) ++{ ++ int i , j = 0; ++ unsigned int alloc_size; ++ struct all_cpufreq_stats *all_stat; ++ bool sort_needed = false; ++ ++ all_stat = kzalloc(sizeof(struct all_cpufreq_stats), ++ GFP_KERNEL); ++ if (!all_stat) { ++ pr_warn("Cannot allocate memory for cpufreq stats\n"); ++ return; ++ } ++ ++ /*Allocate memory for freq table per cpu as well as clockticks per freq*/ ++ alloc_size = count * sizeof(int) + count * sizeof(cputime64_t); ++ all_stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL); ++ if (!all_stat->time_in_state) { ++ pr_warn("Cannot allocate memory for cpufreq time_in_state\n"); ++ kfree(all_stat); ++ all_stat = NULL; ++ return; ++ } ++ all_stat->freq_table = (unsigned int *) ++ (all_stat->time_in_state + count); ++ ++ spin_lock(&cpufreq_stats_lock); ++ for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { ++ unsigned int freq = table[i].frequency; ++ if (freq == CPUFREQ_ENTRY_INVALID) ++ continue; ++ all_stat->freq_table[j++] = freq; ++ if (all_freq_table && !check_all_freq_table(freq)) { ++ add_all_freq_table(freq); ++ sort_needed = true; ++ } ++ } ++ if (sort_needed) ++ sort(all_freq_table->freq_table, all_freq_table->table_size, ++ sizeof(unsigned int), &compare_for_sort, NULL); ++ all_stat->state_num = j; ++ per_cpu(all_cpufreq_stats, cpu) = all_stat; ++ spin_unlock(&cpufreq_stats_lock); ++} ++ + static void cpufreq_stats_create_table(unsigned int cpu) + { + struct cpufreq_policy *policy; +- ++ struct cpufreq_frequency_table *table, *pos; ++ int count = 0; + /* + * "likely(!policy)" because normally cpufreq_stats will be registered + * before cpufreq driver +@@ -254,37 +575,52 @@ static void cpufreq_stats_create_table(unsigned int cpu) + if (likely(!policy)) + return; + +- __cpufreq_stats_create_table(policy); ++ table = cpufreq_frequency_get_table(policy->cpu); ++ if (likely(table)) { ++ cpufreq_for_each_valid_entry(pos, table) ++ count++; + +- cpufreq_cpu_put(policy); +-} ++ if (!per_cpu(all_cpufreq_stats, cpu)) ++ cpufreq_allstats_create(cpu, table, count); + +-static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy) +-{ +- struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, +- policy->last_cpu); ++ if (!per_cpu(cpufreq_power_stats, cpu)) ++ cpufreq_powerstats_create(cpu, table, count); + +- pr_debug("Updating stats_table for new_cpu %u from last_cpu %u\n", +- policy->cpu, policy->last_cpu); +- per_cpu(cpufreq_stats_table, policy->cpu) = per_cpu(cpufreq_stats_table, +- policy->last_cpu); +- per_cpu(cpufreq_stats_table, policy->last_cpu) = NULL; +- stat->cpu = policy->cpu; ++ __cpufreq_stats_create_table(policy, table, count); ++ } ++ cpufreq_cpu_put(policy); + } + + static int cpufreq_stat_notifier_policy(struct notifier_block *nb, + unsigned long val, void *data) + { +- int ret = 0; ++ int ret = 0, count = 0; + struct cpufreq_policy *policy = data; ++ struct cpufreq_frequency_table *table, *pos; ++ unsigned int cpu_num, cpu = policy->cpu; + + if (val == CPUFREQ_UPDATE_POLICY_CPU) { + cpufreq_stats_update_policy_cpu(policy); + return 0; + } + ++ table = cpufreq_frequency_get_table(cpu); ++ if (!table) ++ return 0; ++ ++ cpufreq_for_each_valid_entry(pos, table) ++ count++; ++ ++ if (!per_cpu(all_cpufreq_stats, cpu)) ++ cpufreq_allstats_create(cpu, table, count); ++ ++ for_each_possible_cpu(cpu_num) { ++ if (!per_cpu(cpufreq_power_stats, cpu_num)) ++ cpufreq_powerstats_create(cpu_num, table, count); ++ } ++ + if (val == CPUFREQ_CREATE_POLICY) +- ret = __cpufreq_stats_create_table(policy); ++ ret = __cpufreq_stats_create_table(policy, table, count); + else if (val == CPUFREQ_REMOVE_POLICY) + __cpufreq_stats_free_table(policy); + +@@ -359,6 +695,18 @@ static int __init cpufreq_stats_init(void) + return ret; + } + ++ create_all_freq_table(); ++ WARN_ON(cpufreq_get_global_kobject()); ++ ret = sysfs_create_file(cpufreq_global_kobject, ++ &_attr_all_time_in_state.attr); ++ if (ret) ++ pr_warn("Cannot create sysfs file for cpufreq stats\n"); ++ ++ ret = sysfs_create_file(cpufreq_global_kobject, ++ &_attr_current_in_state.attr); ++ if (ret) ++ pr_warn("Cannot create sysfs file for cpufreq current stats\n"); ++ + return 0; + } + static void __exit cpufreq_stats_exit(void) +@@ -371,6 +719,9 @@ static void __exit cpufreq_stats_exit(void) + CPUFREQ_TRANSITION_NOTIFIER); + for_each_online_cpu(cpu) + cpufreq_stats_free_table(cpu); ++ cpufreq_allstats_free(); ++ cpufreq_powerstats_free(); ++ cpufreq_put_global_kobject(); + } + + MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>"); +diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm +index 8c16ab2..289bf08 100644 +--- a/drivers/cpuidle/Kconfig.arm ++++ b/drivers/cpuidle/Kconfig.arm +@@ -63,3 +63,33 @@ config ARM_MVEBU_V7_CPUIDLE + depends on ARCH_MVEBU + help + Select this to enable cpuidle on Armada 370, 38x and XP processors. ++ ++config ARM_HI3519_CPUIDLE ++ bool "CPU Idle Driver for Hi3519 processors" ++ depends on ARCH_HI3519 ++ help ++ Select this to enable cpuidle on Hi3519 processors. ++ ++config ARM_HI3519V101_CPUIDLE ++ bool "CPU Idle Driver for Hi3519v101 processors" ++ depends on ARCH_HI3519V101 ++ help ++ Select this to enable cpuidle on Hi3519v101 processors. ++ ++config ARM_HI3516AV200_CPUIDLE ++ bool "CPU Idle Driver for Hi3516av200 processors" ++ depends on ARCH_HI3516AV200 ++ help ++ Select this to enable cpuidle on Hi3516av200 processors. ++ ++config ARM_HI3559_CPUIDLE ++ bool "CPU Idle Driver for Hi3559 processors" ++ depends on ARCH_HI3559 ++ help ++ Select this to enable cpuidle on Hi3559 processors. ++ ++config ARM_HI3556_CPUIDLE ++ bool "CPU Idle Driver for Hi3556 processors" ++ depends on ARCH_HI3556 ++ help ++ Select this to enable cpuidle on Hi3556 processors. +diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile +index 4d177b9..00cda8b 100644 +--- a/drivers/cpuidle/Makefile ++++ b/drivers/cpuidle/Makefile +@@ -17,7 +17,10 @@ obj-$(CONFIG_ARM_ZYNQ_CPUIDLE) += cpuidle-zynq.o + obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o + obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o + obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o +- ++obj-$(CONFIG_ARM_HI3519_CPUIDLE) += cpuidle-hi3519.o ++obj-$(CONFIG_ARM_HI3519V101_CPUIDLE) += cpuidle-hi3519.o ++obj-$(CONFIG_ARM_HI3516AV200_CPUIDLE) += cpuidle-hi3516av200.o ++obj-$(CONFIG_ARM_HI3556_CPUIDLE) += cpuidle-hi3559.o + ############################################################################### + # MIPS drivers + obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o +diff --git a/drivers/cpuidle/cpuidle-hi3516av200.c b/drivers/cpuidle/cpuidle-hi3516av200.c +new file mode 100644 +index 0000000..5d318ad +--- /dev/null ++++ b/drivers/cpuidle/cpuidle-hi3516av200.c +@@ -0,0 +1,286 @@ ++/* ++ * ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/bitmap.h> ++#include <linux/cpuidle.h> ++#include <linux/cpu_pm.h> ++#include <linux/clockchips.h> ++#include <linux/debugfs.h> ++#include <linux/hrtimer.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/tick.h> ++#include <linux/vexpress.h> ++#include <linux/cpuidle.h> ++#include <asm/cputype.h> ++#include <asm/idmap.h> ++#include <asm/proc-fns.h> ++#include <asm/suspend.h> ++#include <linux/of.h> ++#include <asm/smp_plat.h> ++ ++#include <asm/cacheflush.h> ++#include <asm/tlbflush.h> ++#include <asm/cp15.h> ++#include <asm/arch_timer.h> ++ ++#include <linux/irqchip/arm-gic.h> ++#include <linux/arm-cci.h> ++#include <linux/delay.h> ++ ++ ++/* extern functions */ ++extern void hi3516av200_set_cpu_jump(int cpu, phys_addr_t jumpaddr); ++extern void hi3516av200_cpu_resume(void); ++extern void hi_pmc_set_ac_inactive(void); ++extern void hi_pmc_automode_power_down(void); ++extern void hi_pmc_power_up_done(void); ++ ++static int bl_cpuidle_simple_enter(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int index); ++ ++int bl_cpuidle_simple_enter(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int index) ++{ ++#if defined(CPUIDLE_DEBUG) ++ int cpuid = smp_processor_id(); ++ ++ pr_debug("%s cpu:%d enter", __func__, cpuid); ++#endif ++ cpu_do_idle(); ++ return index; ++} ++ ++static int bl_enter_cpu_powerdown(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int idx); ++ ++ ++static struct cpuidle_state bl_cpuidle_set[] __initdata = { ++ [0] = { ++ .enter = bl_cpuidle_simple_enter, ++ .exit_latency = 1, ++ .target_residency = 1, ++ .power_usage = UINT_MAX, ++ .flags = CPUIDLE_FLAG_TIME_VALID, ++ .name = "WFI", ++ .desc = "ARM WFI", ++ }, ++ [1] = { ++ .enter = bl_enter_cpu_powerdown, ++ .exit_latency = 500, ++ .target_residency = 1000, ++ .flags = CPUIDLE_FLAG_TIME_VALID | ++ CPUIDLE_FLAG_TIMER_STOP, ++ .name = "C1", ++ .desc = "ARM cpu A17 Cluster power down", ++ }, ++}; ++ ++static struct cpuidle_driver bl_idle_driver = { ++ .name = "bl_idle", ++ .owner = THIS_MODULE, ++ .safe_state_index = 0 ++}; ++ ++static DEFINE_PER_CPU(struct cpuidle_device, bl_idle_dev); ++ ++static void bl_cpu_smp_disable(void) ++{ ++ /* Set ACTLR.SMP to 0, AMP -> SMP */ ++ asm volatile ( ++ " mrc p15, 0, r0, c1, c0, 1\n" ++ " bic r0, #0x40\n" ++ " mcr p15, 0, r0, c1, c0, 1\n" ++ : ++ : ++ : "r0", "cc"); ++} ++ ++/* ++ * switch to init_mm, init_mm page table entries must in memory ++ * not in the cpu cache ++ */ ++static void setup_mm_for_idle(void) ++{ ++ struct mm_struct *mm = &init_mm; ++ ++ cpu_switch_mm(mm->pgd, mm); ++ local_flush_bp_all(); ++ local_flush_tlb_all(); ++} ++ ++static void bl_cpu_powerdown(u64 expected_residency) ++{ ++ int cpu = smp_processor_id(); ++ /* disable irq */ ++ if (WARN(!irqs_disabled(), "Interrupts should be disabled\n")) ++ local_irq_disable(); ++ ++ /* ++ * Once the Dcache is disabled and the tlb is empty, the ptw will ++ * fetch page table entries for the code after Dcache disable from ++ * memory, but the mm is active_mm from last user process's mm, and ++ * the mm->pgd may located in cpu cache(see dcache_clean_area), so ++ * switch to init_mm to avoid access fault. ++ */ ++ setup_mm_for_idle(); ++ /* move the power code here to measure the idle enter time */ ++ hi_pmc_automode_power_down(); ++ ++ gic_cpu_if_down(); ++ ++ /* close Dcache */ ++ set_cr(get_cr() & ~CR_C); ++ /* CLREX */ ++ asm volatile ("clrex"); ++ ++ /* Clean & Invalidata L1 Data Cache, L2 Cache */ ++/* for cortex a17 just one single instruction is enough ++ flush_cache_all(); ++*/ ++ /* clean&invalidate l1 cache */ ++ asm volatile("mov r0, #0"); ++ asm volatile("mcr p15, 1, r0, c15, c14, 0"); ++ asm volatile("dsb"); ++ ++ /* clean&invalidate l2 cache */ ++ asm volatile("mov r0, #2"); ++ asm volatile("mcr p15, 1, r0, c15, c14, 0"); ++ asm volatile("dsb"); ++ ++ /* switch SMP to AMP(ACTLR.SMP->1'b0) */ ++ bl_cpu_smp_disable(); ++ ++ /* disable cci snoop */ ++ cci_disable_port_by_cpu(cpu_logical_map(cpu)); ++ ++ /* asm volatile("mcr p14, 0, %0, c1, c3, 0" : : "r" (1)); */ ++ ++ /* ISB & DSB */ ++ isb(); ++ dsb(); ++ ++ hi_pmc_set_ac_inactive(); ++/* ++ hi_pmc_automode_power_down(); ++*/ ++ dsb(); ++ /* WFI */ ++ while (1) ++ wfi(); ++ ++ BUG(); ++} ++ ++static int bl_cpu_powered_up(void) ++{ ++ /*dcache enble*/ ++ set_cr(get_cr() | CR_C); ++ ++ hi_pmc_power_up_done(); ++ ++ return 0; ++} ++ ++ ++static int notrace bl_cpu_powerdown_finisher(unsigned long arg) ++{ ++ hi3516av200_set_cpu_jump(smp_processor_id(), ++ (phys_addr_t)virt_to_phys(hi3516av200_cpu_resume)); ++ ++ bl_cpu_powerdown(0); ++ ++ return 1; ++} ++ ++/* ++ * bl_enter_cpu_powerdown - Programs CPU to enter the specified state ++ * @dev: cpuidle device ++ * @drv: The target state to be programmed ++ * @idx: state index ++ * ++ * Called from the CPUidle framework to program the device to the ++ * specified target state selected by the governor. ++ */ ++int bl_enter_cpu_powerdown(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int idx) ++{ ++ int cpuid = smp_processor_id(); ++ ++ /* A7 can not power down */ ++ if (cpuid == 0) { ++ cpu_do_idle(); ++ return idx; ++ } ++ ++ BUG_ON(!irqs_disabled()); ++ ++ cpu_pm_enter(); ++ ++ cpu_suspend((unsigned long) dev, bl_cpu_powerdown_finisher); ++ ++ bl_cpu_powered_up(); ++ ++ cpu_pm_exit(); ++ ++ return idx; ++} ++/* ++ * bl_idle_init ++ * ++ * Registers the bl specific cpuidle driver with the cpuidle ++ * framework with the valid set of states. ++ */ ++ ++static int __init bl_idle_init(void) ++{ ++ struct cpuidle_device *dev; ++ int i, cpu_id; ++ struct cpuidle_driver *drv = &bl_idle_driver; ++ ++ drv->state_count = (sizeof(bl_cpuidle_set) / ++ sizeof(struct cpuidle_state)); ++ ++ ++ for (i = 0; i < drv->state_count; i++) { ++ memcpy(&drv->states[i], &bl_cpuidle_set[i], ++ sizeof(struct cpuidle_state)); ++ } ++ cpuidle_register_driver(drv); ++ ++ for_each_cpu(cpu_id, cpu_online_mask) { ++ /* cpu 0 use default idle */ ++ if (cpu_id == 0) ++ continue; ++ pr_err("CPUidle for CPU%d registered\n", cpu_id); ++ dev = &per_cpu(bl_idle_dev, cpu_id); ++ dev->cpu = cpu_id; ++ ++ if (cpuidle_register_device(dev)) { ++ pr_err("%s: Cpuidle register device failed\n", ++ __func__); ++ return -EIO; ++ } ++ } ++ ++ ++ return 0; ++} ++ ++device_initcall(bl_idle_init); +diff --git a/drivers/cpuidle/cpuidle-hi3519.c b/drivers/cpuidle/cpuidle-hi3519.c +new file mode 100644 +index 0000000..f3d952c +--- /dev/null ++++ b/drivers/cpuidle/cpuidle-hi3519.c +@@ -0,0 +1,274 @@ ++/** ++ * cpuidle-hi3519.c ++ * ++ * Copyright (c) 2009-2014, HiSilicon Technologies Co., Ltd. ++ * All rights reserved. ++ */ ++ ++#include <linux/bitmap.h> ++#include <linux/cpuidle.h> ++#include <linux/cpu_pm.h> ++#include <linux/clockchips.h> ++#include <linux/debugfs.h> ++#include <linux/hrtimer.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/tick.h> ++#include <linux/vexpress.h> ++#include <linux/cpuidle.h> ++#include <asm/cputype.h> ++#include <asm/idmap.h> ++#include <asm/proc-fns.h> ++#include <asm/suspend.h> ++#include <linux/of.h> ++#include <asm/smp_plat.h> ++ ++#include <asm/cacheflush.h> ++#include <asm/tlbflush.h> ++#include <asm/cp15.h> ++#include <asm/arch_timer.h> ++ ++#include <linux/irqchip/arm-gic.h> ++#include <linux/arm-cci.h> ++#include <linux/delay.h> ++ ++ ++/* extern functions */ ++extern void hi3519_set_cpu_jump(int cpu, phys_addr_t jumpaddr); ++extern void hi3519_cpu_resume(void); ++extern void hi_pmc_set_ac_inactive(void); ++extern void hi_pmc_automode_power_down(void); ++extern void hi_pmc_power_up_done(void); ++ ++static int bl_cpuidle_simple_enter(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int index); ++ ++int bl_cpuidle_simple_enter(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int index) ++{ ++#if defined(CPUIDLE_DEBUG) ++ int cpuid = smp_processor_id(); ++ ++ pr_debug("%s cpu:%d enter", __func__, cpuid); ++#endif ++ cpu_do_idle(); ++ return index; ++} ++ ++static int bl_enter_cpu_powerdown(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int idx); ++ ++ ++static struct cpuidle_state bl_cpuidle_set[] __initdata = { ++ [0] = { ++ .enter = bl_cpuidle_simple_enter, ++ .exit_latency = 1, ++ .target_residency = 1, ++ .power_usage = UINT_MAX, ++ .flags = CPUIDLE_FLAG_TIME_VALID, ++ .name = "WFI", ++ .desc = "ARM WFI", ++ }, ++ [1] = { ++ .enter = bl_enter_cpu_powerdown, ++ .exit_latency = 500, ++ .target_residency = 1000, ++ .flags = CPUIDLE_FLAG_TIME_VALID | ++ CPUIDLE_FLAG_TIMER_STOP, ++ .name = "C1", ++ .desc = "ARM cpu A17 Cluster power down", ++ }, ++}; ++ ++static struct cpuidle_driver bl_idle_driver = { ++ .name = "bl_idle", ++ .owner = THIS_MODULE, ++ .safe_state_index = 0 ++}; ++ ++static DEFINE_PER_CPU(struct cpuidle_device, bl_idle_dev); ++ ++static void bl_cpu_smp_disable(void) ++{ ++ /* Set ACTLR.SMP to 0, AMP -> SMP */ ++ asm volatile ( ++ " mrc p15, 0, r0, c1, c0, 1\n" ++ " bic r0, #0x40\n" ++ " mcr p15, 0, r0, c1, c0, 1\n" ++ : ++ : ++ : "r0", "cc"); ++} ++ ++/* ++ * switch to init_mm, init_mm page table entries must in memory ++ * not in the cpu cache ++ */ ++static void setup_mm_for_idle(void) ++{ ++ struct mm_struct *mm = &init_mm; ++ ++ cpu_switch_mm(mm->pgd, mm); ++ local_flush_bp_all(); ++ local_flush_tlb_all(); ++} ++ ++static void bl_cpu_powerdown(u64 expected_residency) ++{ ++ int cpu = smp_processor_id(); ++ /* disable irq */ ++ if (WARN(!irqs_disabled(), "Interrupts should be disabled\n")) ++ local_irq_disable(); ++ ++ /* ++ * Once the Dcache is disabled and the tlb is empty, the ptw will ++ * fetch page table entries for the code after Dcache disable from ++ * memory, but the mm is active_mm from last user process's mm, and ++ * the mm->pgd may located in cpu cache(see dcache_clean_area), so ++ * switch to init_mm to avoid access fault. ++ */ ++ setup_mm_for_idle(); ++ /* move the power code here to measure the idle enter time */ ++ hi_pmc_automode_power_down(); ++ ++ gic_cpu_if_down(); ++ ++ /* close Dcache */ ++ set_cr(get_cr() & ~CR_C); ++ /* CLREX */ ++ asm volatile ("clrex"); ++ ++ /* Clean & Invalidata L1 Data Cache, L2 Cache */ ++/* for cortex a17 just one single instruction is enough ++ flush_cache_all(); ++*/ ++ /* clean&invalidate l1 cache */ ++ asm volatile("mov r0, #0"); ++ asm volatile("mcr p15, 1, r0, c15, c14, 0"); ++ asm volatile("dsb"); ++ ++ /* clean&invalidate l2 cache */ ++ asm volatile("mov r0, #2"); ++ asm volatile("mcr p15, 1, r0, c15, c14, 0"); ++ asm volatile("dsb"); ++ ++ /* switch SMP to AMP(ACTLR.SMP->1'b0) */ ++ bl_cpu_smp_disable(); ++ ++ /* disable cci snoop */ ++ cci_disable_port_by_cpu(cpu_logical_map(cpu)); ++ ++ /* asm volatile("mcr p14, 0, %0, c1, c3, 0" : : "r" (1)); */ ++ ++ /* ISB & DSB */ ++ isb(); ++ dsb(); ++ ++ hi_pmc_set_ac_inactive(); ++/* ++ hi_pmc_automode_power_down(); ++*/ ++ dsb(); ++ /* WFI */ ++ while (1) ++ wfi(); ++ ++ BUG(); ++} ++ ++static int bl_cpu_powered_up(void) ++{ ++ /*dcache enble*/ ++ set_cr(get_cr() | CR_C); ++ ++ hi_pmc_power_up_done(); ++ ++ return 0; ++} ++ ++ ++static int notrace bl_cpu_powerdown_finisher(unsigned long arg) ++{ ++ hi3519_set_cpu_jump(smp_processor_id(), ++ (phys_addr_t)virt_to_phys(hi3519_cpu_resume)); ++ ++ bl_cpu_powerdown(0); ++ ++ return 1; ++} ++ ++/* ++ * bl_enter_cpu_powerdown - Programs CPU to enter the specified state ++ * @dev: cpuidle device ++ * @drv: The target state to be programmed ++ * @idx: state index ++ * ++ * Called from the CPUidle framework to program the device to the ++ * specified target state selected by the governor. ++ */ ++int bl_enter_cpu_powerdown(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int idx) ++{ ++ int cpuid = smp_processor_id(); ++ ++ /* A7 can not power down */ ++ if (cpuid == 0) { ++ cpu_do_idle(); ++ return idx; ++ } ++ ++ BUG_ON(!irqs_disabled()); ++ ++ cpu_pm_enter(); ++ ++ cpu_suspend((unsigned long) dev, bl_cpu_powerdown_finisher); ++ ++ bl_cpu_powered_up(); ++ ++ cpu_pm_exit(); ++ ++ return idx; ++} ++/* ++ * bl_idle_init ++ * ++ * Registers the bl specific cpuidle driver with the cpuidle ++ * framework with the valid set of states. ++ */ ++ ++static int __init bl_idle_init(void) ++{ ++ struct cpuidle_device *dev; ++ int i, cpu_id; ++ struct cpuidle_driver *drv = &bl_idle_driver; ++ ++ drv->state_count = (sizeof(bl_cpuidle_set) / ++ sizeof(struct cpuidle_state)); ++ ++ ++ for (i = 0; i < drv->state_count; i++) { ++ memcpy(&drv->states[i], &bl_cpuidle_set[i], ++ sizeof(struct cpuidle_state)); ++ } ++ cpuidle_register_driver(drv); ++ ++ for_each_cpu(cpu_id, cpu_online_mask) { ++ /* cpu 0 use default idle */ ++ if (cpu_id == 0) ++ continue; ++ pr_err("CPUidle for CPU%d registered\n", cpu_id); ++ dev = &per_cpu(bl_idle_dev, cpu_id); ++ dev->cpu = cpu_id; ++ ++ if (cpuidle_register_device(dev)) { ++ pr_err("%s: Cpuidle register device failed\n", ++ __func__); ++ return -EIO; ++ } ++ } ++ ++ ++ return 0; ++} ++ ++device_initcall(bl_idle_init); +diff --git a/drivers/cpuidle/cpuidle-hi3559.c b/drivers/cpuidle/cpuidle-hi3559.c +new file mode 100644 +index 0000000..1e18ae4 +--- /dev/null ++++ b/drivers/cpuidle/cpuidle-hi3559.c +@@ -0,0 +1,253 @@ ++/** ++ * cpuidle-hi3559.c ++ * ++ * Copyright (c) 2009-2014, HiSilicon Technologies Co., Ltd. ++ * All rights reserved. ++ */ ++ ++#include <linux/bitmap.h> ++#include <linux/cpuidle.h> ++#include <linux/cpu_pm.h> ++#include <linux/clockchips.h> ++#include <linux/debugfs.h> ++#include <linux/hrtimer.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/tick.h> ++#include <linux/vexpress.h> ++#include <linux/cpuidle.h> ++#include <asm/cputype.h> ++#include <asm/idmap.h> ++#include <asm/proc-fns.h> ++#include <asm/suspend.h> ++#include <linux/of.h> ++#include <asm/smp_plat.h> ++ ++#include <asm/cacheflush.h> ++#include <asm/tlbflush.h> ++#include <asm/cp15.h> ++#include <asm/arch_timer.h> ++ ++#include <linux/irqchip/arm-gic.h> ++#include <linux/arm-cci.h> ++#include <linux/delay.h> ++ ++ ++/* extern functions */ ++extern void hi3559_set_cpu_jump(int cpu, phys_addr_t jumpaddr); ++extern void hi3559_cpu_resume(void); ++extern void hi_pmc_set_ac_inactive(void); ++extern void hi_pmc_automode_power_down(void); ++extern void hi_pmc_power_up_done(void); ++ ++static int bl_cpuidle_simple_enter(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int index); ++ ++int bl_cpuidle_simple_enter(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int index) ++{ ++#if defined(CPUIDLE_DEBUG) ++ int cpuid = smp_processor_id(); ++ ++ pr_debug("%s cpu:%d enter", __func__, cpuid); ++#endif ++ cpu_do_idle(); ++ return index; ++} ++ ++static int bl_enter_cpu_powerdown(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int idx); ++ ++ ++static struct cpuidle_state bl_cpuidle_set[] __initdata = { ++ [0] = { ++ .enter = bl_cpuidle_simple_enter, ++ .exit_latency = 1, ++ .target_residency = 1, ++ .power_usage = UINT_MAX, ++ .flags = CPUIDLE_FLAG_TIME_VALID, ++ .name = "WFI", ++ .desc = "ARM WFI", ++ }, ++ [1] = { ++ .enter = bl_enter_cpu_powerdown, ++ .exit_latency = 500, ++ .target_residency = 1000, ++ .flags = CPUIDLE_FLAG_TIME_VALID | ++ CPUIDLE_FLAG_TIMER_STOP, ++ .name = "C1", ++ .desc = "ARM cpu A17 Cluster power down", ++ }, ++}; ++ ++static struct cpuidle_driver bl_idle_driver = { ++ .name = "bl_idle", ++ .owner = THIS_MODULE, ++ .safe_state_index = 0 ++}; ++ ++static DEFINE_PER_CPU(struct cpuidle_device, bl_idle_dev); ++ ++static void bl_cpu_smp_disable(void) ++{ ++ /* Set ACTLR.SMP to 0, AMP -> SMP */ ++ asm volatile ( ++ " mrc p15, 0, r0, c1, c0, 1\n" ++ " bic r0, #0x40\n" ++ " mcr p15, 0, r0, c1, c0, 1\n" ++ : ++ : ++ : "r0", "cc"); ++} ++ ++static void bl_cpu_powerdown(u64 expected_residency) ++{ ++ int cpu = smp_processor_id(); ++ /* disable irq */ ++ if (WARN(!irqs_disabled(), "Interrupts should be disabled\n")) ++ local_irq_disable(); ++ ++ /* move the power code here to measure the idle enter time */ ++ hi_pmc_automode_power_down(); ++ ++ gic_cpu_if_down(); ++ ++ /* close Dcache */ ++ set_cr(get_cr() & ~CR_C); ++ /* CLREX */ ++ asm volatile ("clrex"); ++ ++ /* Clean & Invalidata L1 Data Cache, L2 Cache */ ++/* for cortex a17 just one single instruction is enough ++ flush_cache_all(); ++*/ ++ /* clean&invalidate l1 cache */ ++ asm volatile("mov r0, #0"); ++ asm volatile("mcr p15, 1, r0, c15, c14, 0"); ++ asm volatile("dsb"); ++ ++ /* clean&invalidate l2 cache */ ++ asm volatile("mov r0, #2"); ++ asm volatile("mcr p15, 1, r0, c15, c14, 0"); ++ asm volatile("dsb"); ++ ++ /* switch SMP to AMP(ACTLR.SMP->1'b0) */ ++ bl_cpu_smp_disable(); ++ ++ /* disable cci snoop */ ++ cci_disable_port_by_cpu(cpu_logical_map(cpu)); ++ ++ /* asm volatile("mcr p14, 0, %0, c1, c3, 0" : : "r" (1)); */ ++ ++ /* ISB & DSB */ ++ isb(); ++ dsb(); ++ ++ hi_pmc_set_ac_inactive(); ++/* ++ hi_pmc_automode_power_down(); ++*/ ++ dsb(); ++ /* WFI */ ++ while (1) ++ wfi(); ++ ++ BUG(); ++} ++ ++static int bl_cpu_powered_up(void) ++{ ++ /*dcache enble*/ ++ set_cr(get_cr() | CR_C); ++ ++ hi_pmc_power_up_done(); ++ ++ return 0; ++} ++ ++ ++static int notrace bl_cpu_powerdown_finisher(unsigned long arg) ++{ ++ hi3559_set_cpu_jump(smp_processor_id(), ++ (phys_addr_t)virt_to_phys(hi3559_cpu_resume)); ++ ++ bl_cpu_powerdown(0); ++ ++ return 1; ++} ++ ++/* ++ * bl_enter_cpu_powerdown - Programs CPU to enter the specified state ++ * @dev: cpuidle device ++ * @drv: The target state to be programmed ++ * @idx: state index ++ * ++ * Called from the CPUidle framework to program the device to the ++ * specified target state selected by the governor. ++ */ ++int bl_enter_cpu_powerdown(struct cpuidle_device *dev, ++ struct cpuidle_driver *drv, int idx) ++{ ++ int cpuid = smp_processor_id(); ++ ++ /* A7 can not power down */ ++ if (cpuid == 0) { ++ cpu_do_idle(); ++ return idx; ++ } ++ ++ BUG_ON(!irqs_disabled()); ++ ++ cpu_pm_enter(); ++ ++ cpu_suspend((unsigned long) dev, bl_cpu_powerdown_finisher); ++ ++ bl_cpu_powered_up(); ++ ++ cpu_pm_exit(); ++ ++ return idx; ++} ++/* ++ * bl_idle_init ++ * ++ * Registers the bl specific cpuidle driver with the cpuidle ++ * framework with the valid set of states. ++ */ ++ ++static int __init bl_idle_init(void) ++{ ++ struct cpuidle_device *dev; ++ int i, cpu_id; ++ struct cpuidle_driver *drv = &bl_idle_driver; ++ ++ drv->state_count = (sizeof(bl_cpuidle_set) / ++ sizeof(struct cpuidle_state)); ++ ++ ++ for (i = 0; i < drv->state_count; i++) { ++ memcpy(&drv->states[i], &bl_cpuidle_set[i], ++ sizeof(struct cpuidle_state)); ++ } ++ cpuidle_register_driver(drv); ++ ++ for_each_cpu(cpu_id, cpu_online_mask) { ++ /* cpu 0 use default idle */ ++ if (cpu_id == 0) ++ continue; ++ pr_err("CPUidle for CPU%d registered\n", cpu_id); ++ dev = &per_cpu(bl_idle_dev, cpu_id); ++ dev->cpu = cpu_id; ++ ++ if (cpuidle_register_device(dev)) { ++ pr_err("%s: Cpuidle register device failed\n", ++ __func__); ++ return -EIO; ++ } ++ } ++ ++ ++ return 0; ++} ++ ++device_initcall(bl_idle_init); +diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c +index 710a233..386261a 100644 +--- a/drivers/cpuidle/governors/menu.c ++++ b/drivers/cpuidle/governors/menu.c +@@ -178,7 +178,12 @@ static inline int performance_multiplier(unsigned long nr_iowaiters, unsigned lo + + /* for higher loadavg, we are more reluctant */ + +- mult += 2 * get_loadavg(load); ++ /* ++ * this doesn't work as intended - it is almost always 0, but can ++ * sometimes, depending on workload, spike very high into the hundreds ++ * even when the average cpu load is under 10%. ++ */ ++ /* mult += 2 * get_loadavg(); */ + + /* for IO wait tasks (per cpu!) we add 5x each */ + mult += 10 * nr_iowaiters; +diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c +index e34024b..f2bfbec 100644 +--- a/drivers/dma/amba-pl08x.c ++++ b/drivers/dma/amba-pl08x.c +@@ -87,6 +87,8 @@ + #include <linux/init.h> + #include <linux/interrupt.h> + #include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_dma.h> + #include <linux/pm_runtime.h> + #include <linux/seq_file.h> + #include <linux/slab.h> +@@ -103,16 +105,20 @@ struct pl08x_driver_data; + /** + * struct vendor_data - vendor-specific config parameters for PL08x derivatives + * @channels: the number of channels available in this variant ++ * @signals: the number of request signals available from the hardware + * @dualmaster: whether this version supports dual AHB masters or not. + * @nomadik: whether the channels have Nomadik security extension bits + * that need to be checked for permission before use and some registers are + * missing + * @pl080s: whether this version is a PL080S, which has separate register and + * LLI word for transfer size. ++ * @max_transfer_size: the maximum single element transfer size for this ++ * PL08x variant. + */ + struct vendor_data { + u8 config_offset; + u8 channels; ++ u8 signals; + bool dualmaster; + bool nomadik; + bool pl080s; +@@ -231,7 +237,7 @@ struct pl08x_dma_chan { + struct virt_dma_chan vc; + struct pl08x_phy_chan *phychan; + const char *name; +- const struct pl08x_channel_data *cd; ++ struct pl08x_channel_data *cd; + struct dma_slave_config cfg; + struct pl08x_txd *at; + struct pl08x_driver_data *host; +@@ -472,7 +478,7 @@ static void pl08x_terminate_phy_chan(struct pl08x_driver_data *pl08x, + u32 val = readl(ch->reg_config); + + val &= ~(PL080_CONFIG_ENABLE | PL080_CONFIG_ERR_IRQ_MASK | +- PL080_CONFIG_TC_IRQ_MASK); ++ PL080_CONFIG_TC_IRQ_MASK); + + writel(val, ch->reg_config); + +@@ -1354,9 +1360,9 @@ static u32 pl08x_burst(u32 maxburst) + + for (i = 0; i < ARRAY_SIZE(burst_sizes); i++) + if (burst_sizes[i].burstwords <= maxburst) +- break; ++ return burst_sizes[i].reg; + +- return burst_sizes[i].reg; ++ return ~0; + } + + static u32 pl08x_get_cctl(struct pl08x_dma_chan *plchan, +@@ -1380,6 +1386,8 @@ static u32 pl08x_get_cctl(struct pl08x_dma_chan *plchan, + maxburst = 1; + + burst = pl08x_burst(maxburst); ++ if (burst == ~0) ++ return ~0; + cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT; + cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT; + +@@ -1892,6 +1900,12 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, + + if (slave) { + chan->cd = &pl08x->pd->slave_channels[i]; ++ /* ++ * Some implementations have muxed signals, whereas some ++ * use a mux in front of the signals and need dynamic ++ * assignment of signals. ++ */ ++ chan->signal = i; + pl08x_dma_slave_init(chan); + } else { + chan->cd = &pl08x->pd->memcpy_channel; +@@ -2015,10 +2029,204 @@ static inline void init_pl08x_debugfs(struct pl08x_driver_data *pl08x) + } + #endif + ++#ifdef CONFIG_OF ++static struct dma_chan *pl08x_find_chan_id(struct pl08x_driver_data *pl08x, ++ u32 id) ++{ ++ struct pl08x_dma_chan *chan; ++ ++ list_for_each_entry(chan, &pl08x->slave.channels, vc.chan.device_node) { ++ if (chan->signal == id) ++ return &chan->vc.chan; ++ } ++ ++ return NULL; ++} ++ ++static struct dma_chan *pl08x_of_xlate(struct of_phandle_args *dma_spec, ++ struct of_dma *ofdma) ++{ ++ struct pl08x_driver_data *pl08x = ofdma->of_dma_data; ++ struct dma_chan *dma_chan; ++ struct pl08x_dma_chan *plchan; ++ ++ if (!pl08x) ++ return NULL; ++ ++ if (dma_spec->args_count != 2) { ++ dev_err(&pl08x->adev->dev, ++ "DMA channel translation requires two cells\n"); ++ return NULL; ++ } ++ ++ dma_chan = pl08x_find_chan_id(pl08x, dma_spec->args[0]); ++ if (!dma_chan) { ++ dev_err(&pl08x->adev->dev, ++ "DMA slave channel not found\n"); ++ return NULL; ++ } ++ ++ plchan = to_pl08x_chan(dma_chan); ++ dev_dbg(&pl08x->adev->dev, ++ "translated channel for signal %d\n", ++ dma_spec->args[0]); ++ ++ /* Augment channel data for applicable AHB buses */ ++ plchan->cd->periph_buses = dma_spec->args[1]; ++ return dma_get_slave_channel(dma_chan); ++} ++ ++static int pl08x_of_probe(struct amba_device *adev, ++ struct pl08x_driver_data *pl08x, ++ struct device_node *np) ++{ ++ struct pl08x_platform_data *pd; ++ struct pl08x_channel_data *chanp = NULL; ++ u32 cctl_memcpy = 0; ++ u32 val; ++ int ret; ++ int i; ++ ++ pd = devm_kzalloc(&adev->dev, sizeof(*pd), GFP_KERNEL); ++ if (!pd) ++ return -ENOMEM; ++ ++ /* Eligible bus masters for fetching LLIs */ ++ if (of_property_read_bool(np, "lli-bus-interface-ahb1")) ++ pd->lli_buses |= PL08X_AHB1; ++ if (of_property_read_bool(np, "lli-bus-interface-ahb2")) ++ pd->lli_buses |= PL08X_AHB2; ++ if (!pd->lli_buses) { ++ dev_info(&adev->dev, "no bus masters for LLIs stated, assume all\n"); ++ pd->lli_buses |= PL08X_AHB1 | PL08X_AHB2; ++ } ++ ++ /* Eligible bus masters for memory access */ ++ if (of_property_read_bool(np, "mem-bus-interface-ahb1")) ++ pd->mem_buses |= PL08X_AHB1; ++ if (of_property_read_bool(np, "mem-bus-interface-ahb2")) ++ pd->mem_buses |= PL08X_AHB2; ++ if (!pd->mem_buses) { ++ dev_info(&adev->dev, "no bus masters for memory stated, assume all\n"); ++ pd->mem_buses |= PL08X_AHB1 | PL08X_AHB2; ++ } ++ ++ /* Parse the memcpy channel properties */ ++ ret = of_property_read_u32(np, "memcpy-burst-size", &val); ++ if (ret) { ++ dev_info(&adev->dev, "no memcpy burst size specified, using 1 byte\n"); ++ val = 1; ++ } ++ switch (val) { ++ default: ++ dev_err(&adev->dev, "illegal burst size for memcpy, set to 1\n"); ++ /* Fall through */ ++ case 1: ++ cctl_memcpy |= PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT | ++ PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT; ++ break; ++ case 4: ++ cctl_memcpy |= PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT | ++ PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT; ++ break; ++ case 8: ++ cctl_memcpy |= PL080_BSIZE_8 << PL080_CONTROL_SB_SIZE_SHIFT | ++ PL080_BSIZE_8 << PL080_CONTROL_DB_SIZE_SHIFT; ++ break; ++ case 16: ++ cctl_memcpy |= PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | ++ PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT; ++ break; ++ case 32: ++ cctl_memcpy |= PL080_BSIZE_32 << PL080_CONTROL_SB_SIZE_SHIFT | ++ PL080_BSIZE_32 << PL080_CONTROL_DB_SIZE_SHIFT; ++ break; ++ case 64: ++ cctl_memcpy |= PL080_BSIZE_64 << PL080_CONTROL_SB_SIZE_SHIFT | ++ PL080_BSIZE_64 << PL080_CONTROL_DB_SIZE_SHIFT; ++ break; ++ case 128: ++ cctl_memcpy |= PL080_BSIZE_128 << PL080_CONTROL_SB_SIZE_SHIFT | ++ PL080_BSIZE_128 << PL080_CONTROL_DB_SIZE_SHIFT; ++ break; ++ case 256: ++ cctl_memcpy |= PL080_BSIZE_256 << PL080_CONTROL_SB_SIZE_SHIFT | ++ PL080_BSIZE_256 << PL080_CONTROL_DB_SIZE_SHIFT; ++ break; ++ } ++ ++ ret = of_property_read_u32(np, "memcpy-bus-width", &val); ++ if (ret) { ++ dev_info(&adev->dev, "no memcpy bus width specified, using 8 bits\n"); ++ val = 8; ++ } ++ switch (val) { ++ default: ++ dev_err(&adev->dev, "illegal bus width for memcpy, set to 8 bits\n"); ++ /* Fall through */ ++ case 8: ++ cctl_memcpy |= PL080_WIDTH_8BIT << PL080_CONTROL_SWIDTH_SHIFT | ++ PL080_WIDTH_8BIT << PL080_CONTROL_DWIDTH_SHIFT; ++ break; ++ case 16: ++ cctl_memcpy |= PL080_WIDTH_16BIT << PL080_CONTROL_SWIDTH_SHIFT | ++ PL080_WIDTH_16BIT << PL080_CONTROL_DWIDTH_SHIFT; ++ break; ++ case 32: ++ cctl_memcpy |= PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | ++ PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT; ++ break; ++ } ++ ++ /* This is currently the only thing making sense */ ++ cctl_memcpy |= PL080_CONTROL_PROT_SYS; ++ ++ /* Set up memcpy channel */ ++ pd->memcpy_channel.bus_id = "memcpy"; ++ pd->memcpy_channel.cctl_memcpy = cctl_memcpy; ++ /* Use the buses that can access memory, obviously */ ++ pd->memcpy_channel.periph_buses = pd->mem_buses; ++ ++ /* ++ * Allocate channel data for all possible slave channels (one ++ * for each possible signal), channels will then be allocated ++ * for a device and have it's AHB interfaces set up at ++ * translation time. ++ */ ++ chanp = devm_kcalloc(&adev->dev, ++ pl08x->vd->signals, ++ sizeof(struct pl08x_channel_data), ++ GFP_KERNEL); ++ if (!chanp) ++ return -ENOMEM; ++ ++ pd->slave_channels = chanp; ++ for (i = 0; i < pl08x->vd->signals; i++) { ++ /* chanp->periph_buses will be assigned at translation */ ++ chanp->bus_id = kasprintf(GFP_KERNEL, "slave%d", i); ++ chanp++; ++ } ++ pd->num_slave_channels = pl08x->vd->signals; ++ ++ pl08x->pd = pd; ++ ++ return of_dma_controller_register(adev->dev.of_node, pl08x_of_xlate, ++ pl08x); ++} ++#else ++static inline int pl08x_of_probe(struct amba_device *adev, ++ struct pl08x_driver_data *pl08x, ++ struct device_node *np) ++{ ++ return -EINVAL; ++} ++#endif ++ + static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) + { + struct pl08x_driver_data *pl08x; + const struct vendor_data *vd = id->data; ++ struct device_node *np = adev->dev.of_node; + u32 tsfr_size; + int ret = 0; + int i; +@@ -2039,6 +2247,10 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) + goto out_no_pl08x; + } + ++ /* Assign useful pointers to the driver state */ ++ pl08x->adev = adev; ++ pl08x->vd = vd; ++ + /* Initialize memcpy engine */ + dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask); + pl08x->memcpy.dev = &adev->dev; +@@ -2066,15 +2278,17 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) + /* Get the platform data */ + pl08x->pd = dev_get_platdata(&adev->dev); + if (!pl08x->pd) { +- dev_err(&adev->dev, "no platform data supplied\n"); +- ret = -EINVAL; +- goto out_no_platdata; ++ if (np) { ++ ret = pl08x_of_probe(adev, pl08x, np); ++ if (ret) ++ goto out_no_platdata; ++ } else { ++ dev_err(&adev->dev, "no platform data supplied\n"); ++ ret = -EINVAL; ++ goto out_no_platdata; ++ } + } + +- /* Assign useful pointers to the driver state */ +- pl08x->adev = adev; +- pl08x->vd = vd; +- + /* By default, AHB1 only. If dualmaster, from platform */ + pl08x->lli_buses = PL08X_AHB1; + pl08x->mem_buses = PL08X_AHB1; +@@ -2224,9 +2438,18 @@ out_no_pl08x: + } + + /* PL080 has 8 channels and the PL080 have just 2 */ ++static struct vendor_data vendor_pl080_hibvt = { ++ .config_offset = PL080_CH_CONFIG, ++ .channels = 4, ++ .signals = 16, ++ .dualmaster = true, ++ .max_transfer_size = PL080_CONTROL_TRANSFER_SIZE_MASK, ++}; ++ + static struct vendor_data vendor_pl080 = { + .config_offset = PL080_CH_CONFIG, + .channels = 8, ++ .signals = 16, + .dualmaster = true, + .max_transfer_size = PL080_CONTROL_TRANSFER_SIZE_MASK, + }; +@@ -2234,6 +2457,7 @@ static struct vendor_data vendor_pl080 = { + static struct vendor_data vendor_nomadik = { + .config_offset = PL080_CH_CONFIG, + .channels = 8, ++ .signals = 32, + .dualmaster = true, + .nomadik = true, + .max_transfer_size = PL080_CONTROL_TRANSFER_SIZE_MASK, +@@ -2242,6 +2466,7 @@ static struct vendor_data vendor_nomadik = { + static struct vendor_data vendor_pl080s = { + .config_offset = PL080S_CH_CONFIG, + .channels = 8, ++ .signals = 32, + .pl080s = true, + .max_transfer_size = PL080S_CONTROL_TRANSFER_SIZE_MASK, + }; +@@ -2249,11 +2474,18 @@ static struct vendor_data vendor_pl080s = { + static struct vendor_data vendor_pl081 = { + .config_offset = PL080_CH_CONFIG, + .channels = 2, ++ .signals = 16, + .dualmaster = false, + .max_transfer_size = PL080_CONTROL_TRANSFER_SIZE_MASK, + }; + + static struct amba_id pl08x_ids[] = { ++ /* Hisilicon BVT PL080 variant */ ++ { ++ .id = 0x0a141080, ++ .mask = 0xffffffff, ++ .data = &vendor_pl080_hibvt, ++ }, + /* Samsung PL080S variant */ + { + .id = 0x0a141080, +diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c +index 84b49cf..30b2818 100644 +--- a/drivers/gpio/gpio-pl061.c ++++ b/drivers/gpio/gpio-pl061.c +@@ -12,6 +12,7 @@ + #include <linux/spinlock.h> + #include <linux/errno.h> + #include <linux/module.h> ++#include <linux/interrupt.h> + #include <linux/io.h> + #include <linux/ioport.h> + #include <linux/irq.h> +@@ -181,15 +182,12 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger) + return 0; + } + +-static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) ++static irqreturn_t pl061_irq_handler(int irq, void *data) + { + unsigned long pending; + int offset; +- struct gpio_chip *gc = irq_desc_get_handler_data(desc); ++ struct gpio_chip *gc = data; + struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); +- struct irq_chip *irqchip = irq_desc_get_chip(desc); +- +- chained_irq_enter(irqchip, desc); + + pending = readb(chip->base + GPIOMIS); + writeb(pending, chip->base + GPIOIC); +@@ -199,7 +197,7 @@ static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) + offset)); + } + +- chained_irq_exit(irqchip, desc); ++ return IRQ_HANDLED; + } + + static void pl061_irq_mask(struct irq_data *d) +@@ -254,7 +252,13 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) + return -ENODEV; + } + } else { +- chip->gc.base = -1; ++ if (dev->of_node) { ++ i = of_alias_get_id(dev->of_node, "gpio"); ++ chip->gc.base = i * PL061_GPIO_NR; ++ } ++ ++ if (chip->gc.base < 0) ++ chip->gc.base = -1; + irq_base = 0; + } + +@@ -296,8 +300,19 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) + dev_info(&adev->dev, "could not add irqchip\n"); + return ret; + } +- gpiochip_set_chained_irqchip(&chip->gc, &pl061_irqchip, +- irq, pl061_irq_handler); ++ ++ ret = devm_request_irq(dev, irq, pl061_irq_handler, IRQF_SHARED, ++ dev_name(dev), &chip->gc); ++ ++ /* Set the parent IRQ for all affected IRQs */ ++ for (i = 0; i < chip->gc.ngpio; i++) ++ irq_set_parent(irq_find_mapping(chip->gc.irqdomain, i), irq); ++ ++ if (ret) { ++ dev_info(dev, "request irq failed: %d\n", ret); ++ return ret; ++ } ++ + + for (i = 0; i < PL061_GPIO_NR; i++) { + if (pdata) { +diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h +index ba71522..07ce046 100644 +--- a/drivers/gpu/drm/i915/intel_drv.h ++++ b/drivers/gpu/drm/i915/intel_drv.h +@@ -35,9 +35,6 @@ + #include <drm/drm_fb_helper.h> + #include <drm/drm_dp_mst_helper.h> + +-#define DIV_ROUND_CLOSEST_ULL(ll, d) \ +-({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) +- + /** + * _wait_for - magic (register) wait macro + * +diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c +index 8bc193f..ce5a385 100644 +--- a/drivers/gpu/drm/i915/intel_panel.c ++++ b/drivers/gpu/drm/i915/intel_panel.c +@@ -30,6 +30,7 @@ + + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + ++#include <linux/kernel.h> + #include <linux/moduleparam.h> + #include "intel_drv.h" + +diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c +index 01c7a08..1def4bb 100644 +--- a/drivers/hid/hid-input.c ++++ b/drivers/hid/hid-input.c +@@ -1468,8 +1468,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) + * UGCI) cram a lot of unrelated inputs into the + * same interface. */ + hidinput->report = report; +- if (drv->input_configured) +- drv->input_configured(hid, hidinput); ++ if (drv->input_configured && ++ drv->input_configured(hid, hidinput)) ++ goto out_cleanup; + if (input_register_device(hidinput->input)) + goto out_cleanup; + hidinput = NULL; +@@ -1490,8 +1491,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) + } + + if (hidinput) { +- if (drv->input_configured) +- drv->input_configured(hid, hidinput); ++ if (drv->input_configured && ++ drv->input_configured(hid, hidinput)) ++ goto out_cleanup; + if (input_register_device(hidinput->input)) + goto out_cleanup; + } +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index 51e25b9..e6cab62 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -372,6 +372,16 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, + td->inputmode_value = MT_INPUTMODE_TOUCHPAD; + } + ++ /* Only map fields from TouchScreen or TouchPad collections. ++ * We need to ignore fields that belong to other collections ++ * such as Mouse that might have the same GenericDesktop usages. */ ++ if (field->application == HID_DG_TOUCHSCREEN) ++ set_bit(INPUT_PROP_DIRECT, hi->input->propbit); ++ else if (field->application == HID_DG_TOUCHPAD) ++ set_bit(INPUT_PROP_POINTER, hi->input->propbit); ++ else ++ return 0; ++ + if (usage->usage_index) + prev_usage = &field->usage[usage->usage_index - 1]; + +@@ -701,12 +711,13 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) + mt_sync_frame(td, report->field[0]->hidinput->input); + } + +-static void mt_touch_input_configured(struct hid_device *hdev, ++static int mt_touch_input_configured(struct hid_device *hdev, + struct hid_input *hi) + { + struct mt_device *td = hid_get_drvdata(hdev); + struct mt_class *cls = &td->mtclass; + struct input_dev *input = hi->input; ++ int ret; + + if (!td->maxcontacts) + td->maxcontacts = MT_DEFAULT_MAXCONTACT; +@@ -721,9 +732,12 @@ static void mt_touch_input_configured(struct hid_device *hdev, + if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) + td->mt_flags |= INPUT_MT_DROP_UNUSED; + +- input_mt_init_slots(input, td->maxcontacts, td->mt_flags); ++ ret = input_mt_init_slots(input, td->maxcontacts, td->mt_flags); ++ if (ret) ++ return ret; + + td->mt_flags = 0; ++ return 0; + } + + static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, +@@ -877,15 +891,16 @@ static void mt_post_parse(struct mt_device *td) + cls->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; + } + +-static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) ++static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) + { + struct mt_device *td = hid_get_drvdata(hdev); + char *name; + const char *suffix = NULL; + struct hid_field *field = hi->report->field[0]; ++ int ret = 0; + + if (hi->report->id == td->mt_report_id) +- mt_touch_input_configured(hdev, hi); ++ ret = mt_touch_input_configured(hdev, hi); + + /* + * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" +@@ -936,6 +951,7 @@ static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) + hi->input->name = name; + } + } ++ return ret; + } + + static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) +diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c +index 29f328f..8060650 100644 +--- a/drivers/hid/hid-steelseries.c ++++ b/drivers/hid/hid-steelseries.c +@@ -254,6 +254,11 @@ static int steelseries_srws1_probe(struct hid_device *hdev, + goto err_free; + } + ++ if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 16)) { ++ ret = -ENODEV; ++ goto err_free; ++ } ++ + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); +diff --git a/drivers/hidmac/Kconfig b/drivers/hidmac/Kconfig +new file mode 100644 +index 0000000..3286382 +--- /dev/null ++++ b/drivers/hidmac/Kconfig +@@ -0,0 +1,27 @@ ++# ++# Sensor device configuration ++# ++ ++config HI_DMAC ++ tristate "Hisilicon DMAC Controller support" ++ depends on (ARCH_HISI) ++ help ++ The Direction Memory Access(DMA) is a high-speed data transfer ++ operation. It supports data read/write between peripherals and ++ memories without using the CPU. ++ Hisilicon DMA Controller(DMAC) directly transfers data between ++ a memory and a peripheral, between peripherals, or between memories. ++ This avoids the CPU intervention and reduces the interrupt handling ++ overhead of the CPU. ++ ++if HI_DMAC ++config HI_DMAC_IO_BASE ++ hex "hi dmac register base address" ++ depends on ARCH_HI3519 || ARCH_HI3519V101 || ARCH_HI3559 || ARCH_HI3556 || ARCH_HI3516AV200 ++ default "0x10030000" if ARCH_HI3519 || ARCH_HI3519V101 || ARCH_HI3559 || ARCH_HI3556 || ARCH_HI3516AV200 ++ ++config HI_DMAC_CHANNEL_NUM ++ int "hi dmac channel num" ++ default "4" if ARCH_HI3519 || ARCH_HI3519V101 || ARCH_HI3559 || ARCH_HI3556 || ARCH_HI3516AV200 ++ default "4" if ARCH_HI3516CV300 || ARCH_HI3531D || ARCH_HI3521D || ARCH_HI3536C ++endif +diff --git a/drivers/hidmac/Makefile b/drivers/hidmac/Makefile +new file mode 100644 +index 0000000..3aa24f3 +--- /dev/null ++++ b/drivers/hidmac/Makefile +@@ -0,0 +1,10 @@ ++# ++# Makefile for the hi dmac drivers. ++# ++ ++ifdef CONFIG_HI_DMAC_IO_BASE ++obj-$(CONFIG_HI_DMAC) += hi_dmac.o ++else ++# get the io resource from DTS ++obj-$(CONFIG_HI_DMAC) += hi_pl08x.o ++endif +diff --git a/drivers/hidmac/hi_dmac.c b/drivers/hidmac/hi_dmac.c +new file mode 100644 +index 0000000..8e1f226 +--- /dev/null ++++ b/drivers/hidmac/hi_dmac.c +@@ -0,0 +1,1216 @@ ++/* ++ * Copyright (c) 2014 Hisilicon Co., Ltd. ++ * ++ * 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. ++ * ++ * History: ++ * 17-August-2006 create this file ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/i2c.h> ++#include <linux/init.h> ++#include <linux/time.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/dma-mapping.h> ++#include <linux/pm_runtime.h> ++#include <linux/slab.h> ++#include <mach/io.h> ++#include <linux/hidmac.h> ++#include "hi_dmac.h" ++ ++#ifdef CONFIG_ARCH_HI3519 ++#include "hidmac_hi3519.h" ++#include "hidmac_hi3519.c" ++#endif ++ ++#ifdef CONFIG_ARCH_HI3519V101 ++#include "hidmac_hi3519.h" ++#include "hidmac_hi3519v101.c" ++#endif ++ ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++#include "hidmac_hi3559.h" ++#include "hidmac_hi3559.c" ++#endif ++ ++#ifdef CONFIG_ARCH_HI3516AV200 ++#include "hidmac_hi3516av200.h" ++#include "hidmac_hi3516av200.c" ++#endif ++ ++void *reg_dmac_base_va; ++#define IO_DMAC_ADDRESS(x) (reg_dmac_base_va + ((x)-(DMAC_BASE_REG))) ++ ++#define RX 0 ++#define TX 1 ++ ++static int dmac_channel[CHANNEL_NUM] = {0, 1, 2, 3}; ++ ++#define CLR_INT(i) ((*(unsigned int *)IO_DMAC_ADDRESS\ ++ (DMAC_BASE_REG+0x008)) = (1 << i)) ++ ++int g_channel_status[CHANNEL_NUM]; ++ ++/* #define DEBUG */ ++ ++#define DEBUG ++#ifdef DEBUG ++#define dma_err printk ++#else ++#define dma_err(fmt, ...) do {} while (0) ++#endif ++ ++/* ++ *Define Memory range ++ */ ++mem_addr mem_num[MEM_MAX_NUM] = { ++ {DDRAM_ADRS, DDRAM_SIZE}, ++ {FLASH_BASE, FLASH_SIZE} ++}; ++ ++typedef void REG_ISR(int *p_dma_chn, int *p_dma_status); ++REG_ISR *function[CHANNEL_NUM]; ++ ++unsigned int pllihead[2]; ++/* ++ * memory address validity check ++ * ++static int mem_check_valid(unsigned int addr) ++{ ++ unsigned int cnt; ++ ++ for (cnt = 0; cnt < MEM_MAX_NUM; cnt++) { ++ if ((addr >= mem_num[cnt].addr_base) && ++ (addr <= (mem_num[cnt].addr_base + mem_num[cnt].size))) ++ return 0; ++ } ++ ++ return -1; ++} */ ++ ++/* ++ * dmac interrupt handle function ++ */ ++irqreturn_t dmac_isr(int irq, void *dev_id) ++{ ++ unsigned int channel_status; ++ unsigned int channel_tc_status, channel_err_status; ++ unsigned int i; ++ ++ /*read the status of current interrupt */ ++ dmac_readw(DMAC_INTSTATUS, channel_status); ++ ++ /*decide which channel has trigger the interrupt*/ ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) { ++ if ((((channel_status >> i) & 0x1) == 0x01)) { ++ /* [HSCP201306240006],l00181524,20130625 */ ++ /* The INT status should be read first then clear it */ ++ /* CLR_INT(i); */ ++ dmac_readw(DMAC_INTTCSTATUS, channel_tc_status); ++ dmac_readw(DMAC_INTERRORSTATUS, channel_err_status); ++ CLR_INT(i); ++ /*¡¾HSCP201403110002¡¿ l00183122 20140723*/ ++ if (g_channel_status[i] == DMAC_CHN_VACANCY ++ && (function[i]) == NULL) { ++ if ((0x01 == ((channel_tc_status >> i) & 0x01))) ++ dmac_writew(DMAC_INTTCCLEAR, ++ (0x01 << i)); ++ else if ((0x01 == ((channel_err_status ++ >> i)&0x01))) ++ dmac_writew(DMAC_INTERRCLR, ++ (0x01 << i)); ++ continue; ++ } ++ ++ /* save the current channel transfer */ ++ /* status to g_channel_status[i] */ ++ if ((0x01 == ((channel_tc_status >> i) & 0x01))) { ++ g_channel_status[i] = DMAC_CHN_SUCCESS; ++ dmac_writew(DMAC_INTTCCLEAR, (0x01 << i)); ++ } else if ((0x01 == ((channel_err_status >> i)&0x01))) { ++ g_channel_status[i] = -DMAC_CHN_ERROR; ++ dmac_writew(DMAC_INTERRCLR, (0x01 << i)); ++ } else ++ pr_err("Isr Error in DMAC_IntHandeler"); ++ pr_err("%d! channel\n", i); ++ ++ if ((function[i]) != NULL) ++ function[i](&i, &g_channel_status[i]); ++ } ++ } ++ ++ return IRQ_RETVAL(1); ++} ++ ++/* ++ * update the state of channels ++ */ ++#define HI_DMA_UPDATE_TIMEOUT (5 * HZ) ++static int dma_update_status(unsigned int channel) ++{ ++ ++ unsigned int channel_status; ++ unsigned int channel_tc_status[3]; ++ unsigned int channel_err_status[3]; ++ unsigned int i = channel, j; ++ unsigned long update_jiffies_timeout; ++ ++ update_jiffies_timeout = jiffies + HI_DMA_UPDATE_TIMEOUT; ++ ++ while (1) { ++ for (j = 0; j < 3; j++) { ++ dmac_readw(DMAC_INTTCSTATUS, channel_status); ++ channel_tc_status[j] = (channel_status >> i) & 0x01; ++ dmac_readw(DMAC_INTERRORSTATUS, channel_status); ++ channel_err_status[j] = (channel_status >> i) & 0x01; ++ } ++ ++ if ((channel_tc_status[0] == 0x1) && ++ (channel_tc_status[1] == 0x1) && ++ (channel_tc_status[2] == 0x1)) { ++ g_channel_status[i] = DMAC_CHN_SUCCESS; ++ dmac_writew(DMAC_INTTCCLEAR, (0x01 << i)); ++ break; ++ } else if ((channel_err_status[0] == 0x1) && ++ (channel_err_status[1] == 0x1) && ++ (channel_err_status[2] == 0x1)) { ++ g_channel_status[i] = -DMAC_CHN_ERROR; ++ dma_err("Error in DMAC %d finish!\n", i); ++ dmac_writew(DMAC_INTERRCLR, (0x01 << i)); ++ break; ++ } ++ ++ if (!time_before(jiffies, update_jiffies_timeout)) { ++ dma_err("Timeout in DMAC %d!\n", i); ++ g_channel_status[i] = -DMAC_CHN_TIMEOUT; ++ break; ++ } ++ } ++ ++ return g_channel_status[i]; ++} ++ ++ ++/* ++ * check the state of channels ++ */ ++static int dmac_check_over(unsigned int channel) ++{ ++ int status = 0; ++ ++ if (-DMAC_CHN_ERROR == g_channel_status[channel]) { ++ dma_err("Error transfer %d finished\n", channel); ++ dmac_writew(DMAC_CxCONFIG(channel), DMAC_CxDISABLE); ++ g_channel_status[channel] = DMAC_CHN_VACANCY; ++ status = -DMAC_CHN_ERROR; ++ } else if (DMAC_NOT_FINISHED == g_channel_status[channel]) ++ status = DMAC_NOT_FINISHED; ++ else if (DMAC_CHN_ALLOCAT == g_channel_status[channel]) ++ status = DMAC_CHN_ALLOCAT; ++ else if (DMAC_CHN_VACANCY == g_channel_status[channel]) ++ status = DMAC_CHN_VACANCY; ++ else if (-DMAC_CHN_TIMEOUT == g_channel_status[channel]) { ++ dma_err("transfer %d timeout!\n", channel); ++ status = -DMAC_CHN_TIMEOUT; ++ } else if (DMAC_CHN_SUCCESS == g_channel_status[channel]) ++ /*The transfer of Channel %d has finished successfully!*/ ++ status = DMAC_CHN_SUCCESS; ++ else { ++ dmac_writew(DMAC_CxCONFIG(channel), DMAC_CxDISABLE); ++ g_channel_status[channel] = DMAC_CHN_VACANCY; ++ status = -DMAC_CHN_ERROR; ++ } ++ return status; ++} ++ ++spinlock_t my_lcok = __SPIN_LOCK_UNLOCKED(old_style_spin_init); ++unsigned long flags; ++ ++/* ++ * allocate channel. ++ */ ++int dmac_channel_allocate(void *pisr) ++{ ++ unsigned int i, channelinfo, g_channelinfo; ++ ++ for (i = 0; i < CHANNEL_NUM; i++) ++ dmac_check_over(dmac_channel[i]); ++ ++ dmac_readw(DMAC_ENBLDCHNS, g_channelinfo); ++ g_channelinfo = g_channelinfo & 0x00ff; ++ ++ for (i = 0; i < CHANNEL_NUM; i++) { ++ if (g_channel_status[dmac_channel[i]] == DMAC_CHN_VACANCY) { ++ channelinfo = g_channelinfo >> dmac_channel[i]; ++ if (0x00 == (channelinfo & 0x01)) { ++ /*clear the interrupt in this channel */ ++ dmac_writew(DMAC_INTERRCLR, ++ (0x01 << dmac_channel[i])); ++ dmac_writew(DMAC_INTTCCLEAR, ++ (0x01 << dmac_channel[i])); ++ ++ g_channel_status[dmac_channel[i]] ++ = DMAC_CHN_ALLOCAT; ++ return dmac_channel[i]; ++ } ++ } ++ } ++ ++ dma_err("no to alloc\n"); ++ return -EINVAL; ++} ++EXPORT_SYMBOL(dmac_channel_allocate); ++ ++int dmac_register_isr(unsigned int channel, void *pisr) ++{ ++ if (channel > CHANNEL_NUM - 1) { ++ dma_err("channel which choosed %d is error !\n", channel); ++ return -1; ++ } ++ ++ if (g_channel_status[channel] != DMAC_CHN_VACANCY) { ++ dma_err("dma chn %d is in used!\n", channel); ++ return -1; ++ } ++ ++ /*clear the interrupt in this channel */ ++ dmac_writew(DMAC_INTERRCLR, (0x01 << channel)); ++ dmac_writew(DMAC_INTTCCLEAR, (0x01 << channel)); ++ ++ function[channel] = (void *)pisr; ++ g_channel_status[channel] = DMAC_CHN_ALLOCAT; ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_register_isr); ++ ++/* ++ * free channel ++ */ ++int dmac_channel_free(unsigned int channel) ++{ ++ ++ if (channel >= DMAC_MAX_CHANNELS) { ++ dma_err("channel larger %d\n", DMAC_MAX_CHANNELS); ++ return -EINVAL; ++ } ++ ++ g_channel_status[channel] = DMAC_CHN_VACANCY; ++ return 0; ++} ++EXPORT_SYMBOL(dmac_channel_free); ++ ++static unsigned int dmac_check_request(unsigned int peripheral_addr, ++ int direction) ++{ ++ int i; ++ /* check request pipe with peripheral_addr */ ++ for (i = direction; i < DMAC_MAX_PERIPHERALS; i = i + 2) { ++ if (g_peripheral[i].peri_addr == peripheral_addr) ++ return i; ++ } ++ ++ dma_err("Invalid devaddr\n"); ++ ++ return -1; ++} ++ ++/* ++ * init dmac register ++ * clear interrupt flags ++ * called by dma_driver_init ++ */ ++int dmac_init(void) ++{ ++ unsigned int i, tempvalue; ++ ++ hidmac_clk_en(); ++ hidmac_unreset(); ++ ++ dmac_readw(DMAC_CONFIG, tempvalue); ++ if (tempvalue == 0) { ++ dmac_writew(DMAC_CONFIG, DMAC_CONFIG_VAL); ++ dmac_writew(DMAC_INTTCCLEAR, 0xFF); ++ dmac_writew(DMAC_INTERRCLR, 0xFF); ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) ++ dmac_writew(DMAC_CxCONFIG(i), DMAC_CxDISABLE); ++ } ++ ++ return 0; ++} ++ ++ ++/* ++ * alloc_dma_lli_space ++ * output: ++ * ppheadlli[0]: memory physics address ++ * ppheadlli[1]: virtual address ++ * ++ */ ++int allocate_dmalli_space(unsigned int *ppheadlli, unsigned int page_num) ++{ ++ dma_addr_t dma_phys; ++ void *dma_virt; ++ ++ dma_virt = dma_alloc_coherent(NULL, page_num*PAGE_SIZE, ++ &dma_phys, GFP_DMA | __GFP_WAIT); ++ if (NULL == dma_virt) { ++ dma_err("can't get dma mem from system\n"); ++ return -1; ++ } ++ ++ ppheadlli[0] = (unsigned int)(dma_phys); ++ ppheadlli[1] = (unsigned int)(dma_virt); ++ ++ return 0; ++} ++EXPORT_SYMBOL(allocate_dmalli_space); ++ ++/* ++ * free_dma_lli_space ++ */ ++int free_dmalli_space(unsigned int *ppheadlli, unsigned int page_num) ++{ ++ dma_addr_t dma_phys; ++ unsigned int dma_virt; ++ ++ dma_phys = (dma_addr_t)(ppheadlli[0]); ++ dma_virt = ppheadlli[1]; ++ ++ dma_free_coherent(NULL, page_num*PAGE_SIZE, ++ (void *)dma_virt, dma_phys); ++ ++ ppheadlli[0] = 0; ++ ppheadlli[1] = 0; ++ return 0; ++} ++EXPORT_SYMBOL(free_dmalli_space); ++ ++/* ++ * config register for memory to memory DMA transfer without LLI ++ * note: ++ * it is necessary to call dmac_channelstart for channel enable ++ */ ++int dmac_start_m2m(unsigned int channel, unsigned int psource, ++ unsigned int pdest, unsigned int uwnumtransfers) ++{ ++ unsigned int uwchannel_num, tmp_trasnsfer; ++ ++ if (uwnumtransfers > (MAXTRANSFERSIZE << 2)) { ++ dma_err("Invalidate transfer size,size=%x\n", uwnumtransfers); ++ return -EINVAL; ++ } ++ ++ uwchannel_num = channel; ++ ++ if ((uwchannel_num == DMAC_CHANNEL_INVALID) ++ || (uwchannel_num > CHANNEL_NUM)) { ++ pr_err("failure of DMAC channel allocation in M2M function!\n"); ++ return -EFAULT; ++ } ++ ++ /* dmac_writew (DMAC_CxCONFIG(uwchannel_num), DMAC_CxDISABLE); */ ++ dmac_writew(DMAC_CxSRCADDR(uwchannel_num), psource); ++ dmac_writew(DMAC_CxDESTADDR(uwchannel_num), pdest); ++ dmac_writew(DMAC_CxLLI(uwchannel_num), 0); ++ tmp_trasnsfer = (uwnumtransfers >> 2) & 0xfff; ++ tmp_trasnsfer = tmp_trasnsfer | (DMAC_CxCONTROL_M2M & (~0xfff)); ++ dmac_writew(DMAC_CxCONTROL(uwchannel_num), tmp_trasnsfer); ++ dmac_writew(DMAC_CxCONFIG(uwchannel_num), DMAC_CxCONFIG_M2M); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_start_m2m); ++ ++/* ++ * channel enable ++ * start a dma transfer immediately ++ */ ++int dmac_channelstart(unsigned int u32channel) ++{ ++ ++ unsigned int reg_value; ++ ++ if (u32channel >= DMAC_MAX_CHANNELS) { ++ dma_err("channel larger %d\n", DMAC_MAX_CHANNELS); ++ return -EINVAL; ++ } ++ ++ g_channel_status[u32channel] = DMAC_NOT_FINISHED; ++ dmac_readw(DMAC_CxCONFIG(u32channel), reg_value); ++ dmac_writew(DMAC_CxCONFIG(u32channel), ++ (reg_value | DMAC_CHANNEL_ENABLE)); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_channelstart); ++ ++/* ++ * wait for transfer end ++ */ ++int dmac_wait(int channel) ++{ ++ int ret_result, ret = 0; ++ ++ if (channel < 0) ++ return -1; ++ ++ while (1) { ++ ret_result = dma_update_status(channel); ++ if (ret_result == -DMAC_CHN_ERROR) { ++ dma_err("Transfer Error.\n"); ++ ret = -1; ++ goto end; ++ } else if (ret_result == DMAC_NOT_FINISHED) ++ udelay(10); ++ else if (ret_result == DMAC_CHN_SUCCESS) { ++ ret = DMAC_CHN_SUCCESS; ++ goto end; ++ } ++ else if (ret_result == DMAC_CHN_VACANCY) { ++ ret = DMAC_CHN_SUCCESS; ++ goto end; ++ } else if (ret_result == -DMAC_CHN_TIMEOUT) { ++ dma_err("Timeout.\n"); ++ dmac_writew(DMAC_CxCONFIG(channel), DMAC_CxDISABLE); ++ g_channel_status[channel] = DMAC_CHN_VACANCY; ++ ret = -1; ++ goto end; ++ } ++ } ++end: ++ dmac_channelclose(channel); ++ return ret; ++} ++EXPORT_SYMBOL(dmac_wait); ++ ++/* ++ * buile LLI for memory to memory DMA transfer ++ */ ++int dmac_buildllim2m_isp(unsigned int *ppheadlli, unsigned int *psource, ++ unsigned int *pdest, unsigned int *length, ++ unsigned int lli_num) ++{ ++ unsigned int address, phy_address; ++ unsigned int j; ++ ++ if (ppheadlli != NULL) { ++ phy_address = (unsigned int)(ppheadlli[0]); ++ dma_debug("phy_address: 0x%X\n",phy_address); ++ address = (unsigned int)(ppheadlli[1]); ++ dma_debug("address: 0x%X\n",address); ++ for (j = 0; j < lli_num; j++) { ++ dma_debug("psource[%d]: 0x%X\n", j, psource[j]); ++ dmac_writew(address, psource[j]); ++ address += 4; ++ phy_address += 4; ++ dma_debug("pdest[%d]: 0x%X\n", j, pdest[j]); ++ dmac_writew(address, pdest[j]); ++ address += 4; ++ phy_address += 4; ++ ++ /* if the last node, next_lli_addr = 0*/ ++ if (j == (lli_num - 1)) ++ dmac_writew(address, 0); ++ else ++ dmac_writew(address, ++ (((phy_address + 8) & (~0x03)) ++ | DMAC_CxLLI_LM)); ++ ++ address += 4; ++ phy_address += 4; ++ ++ if (j == (lli_num - 1)) { ++ dmac_writew(address, ((DMAC_CxCONTROL_LLIM2M ++ &(~0xfff)) | (length[j] >> 2) ++ | 0x80000000)); ++ } else { ++ dmac_writew(address, ++ (((DMAC_CxCONTROL_LLIM2M&(~0xfff)) | ++ (length[j] >> 2)) & 0x7fffffff)); ++ } ++ ++ address += 4; ++ phy_address += 4; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_buildllim2m_isp); ++ ++/* ++ * buile LLI for memory to memory DMA transfer ++ */ ++int dmac_buildllim2m(unsigned int *ppheadlli, unsigned int pdest, ++ unsigned int psource, unsigned int totaltransfersize, ++ unsigned int uwnumtransfers) ++{ ++ unsigned int lli_num = 0; ++ unsigned int last_lli = 0; ++ unsigned int address , phy_address, srcaddr, denstaddr; ++ unsigned int j; ++ ++ lli_num = (totaltransfersize / uwnumtransfers); ++ ++ if ((totaltransfersize % uwnumtransfers) != 0) ++ last_lli = 1, ++lli_num; ++ ++ if (ppheadlli != NULL) { ++ phy_address = (unsigned int)(ppheadlli[0]); ++ address = (unsigned int)(ppheadlli[1]); ++ for (j = 0; j < lli_num; j++) { ++ srcaddr = (psource + (j*uwnumtransfers)); ++ dmac_writew(address, srcaddr); ++ address += 4; ++ phy_address += 4; ++ denstaddr = (pdest + (j*uwnumtransfers)); ++ dmac_writew(address, denstaddr); ++ address += 4; ++ phy_address += 4; ++ if (j == (lli_num - 1)) ++ dmac_writew(address, 0); ++ else ++ dmac_writew(address, ++ (((phy_address + 8) & (~0x03)) ++ | DMAC_CxLLI_LM)); ++ ++ address += 4; ++ phy_address += 4; ++ ++ if ((j == (lli_num - 1)) && (last_lli == 0)) ++ dmac_writew(address, ((DMAC_CxCONTROL_LLIM2M ++ &(~0xfff)) | (uwnumtransfers >> 2) ++ | 0x80000000)); ++ else if ((j == (lli_num - 1)) && (last_lli == 1)) ++ dmac_writew(address, ((DMAC_CxCONTROL_LLIM2M ++ & (~0xfff)) | ((totaltransfersize ++ % uwnumtransfers) >> 2) | 0x80000000)); ++ else ++ dmac_writew(address, ++ (((DMAC_CxCONTROL_LLIM2M&(~0xfff)) | ++ (uwnumtransfers >> 2)) & 0x7fffffff)); ++ ++ address += 4; ++ phy_address += 4; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_buildllim2m); ++ ++/* ++ * disable channel ++ * used before the operation of register configuration ++ */ ++int dmac_channelclose(unsigned int channel) ++{ ++ unsigned int reg_value, count; ++ ++ if (channel >= DMAC_MAX_CHANNELS) { ++ dma_err("channel larger than total.\n"); ++ return -EINVAL; ++ } ++ ++ dmac_readw(DMAC_CxCONFIG(channel), reg_value); ++ ++#define CHANNEL_CLOSE_IMMEDIATE ++#ifdef CHANNEL_CLOSE_IMMEDIATE ++ reg_value &= 0xFFFFFFFE; ++ dmac_writew(DMAC_CxCONFIG(channel) , reg_value); ++#else ++ reg_value |= DMAC_CONFIGURATIONx_HALT_DMA_ENABLE; ++ /*ignore incoming dma request*/ ++ dmac_writew(DMAC_CxCONFIG(channel), reg_value); ++ dmac_readw(DMAC_CxCONFIG(channel), reg_value); ++ /*if FIFO is empty*/ ++ while ((reg_value & DMAC_CONFIGURATIONx_ACTIVE) ++ == DMAC_CONFIGURATIONx_ACTIVE) ++ dmac_readw(DMAC_CxCONFIG(channel), reg_value); ++ reg_value &= 0xFFFFFFFE; ++ dmac_writew(DMAC_CxCONFIG(channel), reg_value); ++#endif ++ ++ dmac_readw(DMAC_ENBLDCHNS, reg_value); ++ reg_value = reg_value & 0x00ff; ++ count = 0; ++ while (((reg_value >> channel) & 0x1) == 1) { ++ dmac_readw(DMAC_ENBLDCHNS, reg_value); ++ reg_value = reg_value & 0x00ff; ++ if (count++ > 10000) { ++ dma_err("close failure.\n"); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_channelclose); ++ ++/* ++ * load configuration from LLI for memory to memory ++ */ ++int dmac_start_llim2m(unsigned int channel, unsigned int *pfirst_lli) ++{ ++ unsigned int uwchannel_num; ++ dmac_lli plli; ++ unsigned int first_lli; ++ ++ if (NULL == pfirst_lli) { ++ dma_err("Invalidate LLI head!\n"); ++ return -EFAULT; ++ } ++ ++ uwchannel_num = channel; ++ if ((uwchannel_num == DMAC_CHANNEL_INVALID) || ++ (uwchannel_num > 7)) { ++ dma_err("failure of DMAC channel allocation in"); ++ dma_err("LLIM2M function,channel=%x!\n ", uwchannel_num); ++ return -EINVAL; ++ } ++ ++ memset(&plli, 0, sizeof(plli)); ++ first_lli = (unsigned int)pfirst_lli[1]; ++ dmac_readw(first_lli, plli.src_addr); ++ dmac_readw(first_lli+4, plli.dst_addr); ++ dmac_readw(first_lli+8, plli.next_lli); ++ dmac_readw(first_lli+12, plli.lli_transfer_ctrl); ++ ++ dmac_channelclose(uwchannel_num); ++ dmac_writew(DMAC_INTTCCLEAR, (0x1 << uwchannel_num)); ++ dmac_writew(DMAC_INTERRCLR, (0x1 << uwchannel_num)); ++ dmac_writew(DMAC_SYNC, 0x0); ++ ++ dmac_writew(DMAC_CxCONFIG(uwchannel_num), DMAC_CxDISABLE); ++ dmac_writew(DMAC_CxSRCADDR(uwchannel_num), ++ (unsigned int)(plli.src_addr)); ++ dmac_writew(DMAC_CxDESTADDR(uwchannel_num), ++ (unsigned int)(plli.dst_addr)); ++ dmac_writew(DMAC_CxLLI(uwchannel_num), (unsigned int)(plli.next_lli)); ++ dmac_writew(DMAC_CxCONTROL(uwchannel_num), ++ (unsigned int)(plli.lli_transfer_ctrl)); ++ dmac_writew(DMAC_CxCONFIG(uwchannel_num), DMAC_CxCONFIG_LLIM2M); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_start_llim2m); ++ ++/* ++ * load configuration from LLI for memory and peripheral ++ */ ++int dmac_start_llim2p(unsigned int channel, unsigned int *pfirst_lli, ++ unsigned int uwperipheralid) ++{ ++ unsigned int uwchannel_num; ++ dmac_lli plli; ++ unsigned int first_lli; ++ unsigned int temp = 0; ++ ++ if (NULL == pfirst_lli) { ++ dma_err("Invalidate LLI head!\n"); ++ return -EINVAL; ++ } ++ uwchannel_num = channel; ++ if ((uwchannel_num == DMAC_CHANNEL_INVALID) || ++ (uwchannel_num > CHANNEL_NUM)) { ++ dma_err("failure of DMAC channel allocation in"); ++ dma_err("LLIM2P function, channel=%x!\n ", uwchannel_num); ++ return -EINVAL; ++ } ++ ++ memset(&plli, 0, sizeof(plli)); ++ first_lli = (unsigned int)pfirst_lli[1]; ++ dmac_readw(first_lli, plli.src_addr); ++ dmac_readw(first_lli+4, plli.dst_addr); ++ dmac_readw(first_lli+8, plli.next_lli); ++ dmac_readw(first_lli+12, plli.lli_transfer_ctrl); ++ ++ dmac_channelclose(uwchannel_num); ++ dmac_writew(DMAC_INTTCCLEAR, (0x1<<uwchannel_num)); ++ dmac_writew(DMAC_INTERRCLR, (0x1<<uwchannel_num)); ++ dmac_writew(DMAC_SYNC, 0x0); ++ ++ dmac_readw(DMAC_CxCONFIG(uwchannel_num), temp); ++ dmac_writew(DMAC_CxCONFIG(uwchannel_num), temp|DMAC_CxDISABLE); ++ dmac_writew(DMAC_CxSRCADDR(uwchannel_num), plli.src_addr); ++ dmac_writew(DMAC_CxDESTADDR(uwchannel_num), plli.dst_addr); ++ dmac_writew(DMAC_CxLLI(uwchannel_num), plli.next_lli); ++ dmac_writew(DMAC_CxCONTROL(uwchannel_num), plli.lli_transfer_ctrl); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_start_llim2p); ++ ++/* ++ * enable memory and peripheral dma transfer ++ * note: ++ * it is necessary to call dmac_channelstart to enable channel ++ */ ++int dmac_start_m2p(unsigned int channel, unsigned int memaddr, ++ unsigned int uwperipheralid, unsigned int uwnumtransfers, ++ unsigned int next_lli_addr) ++{ ++ ++ unsigned int uwtrans_control = 0; ++ unsigned int addtmp, tmp; ++ unsigned int uwdst_addr = 0, uwsrc_addr = 0; ++ unsigned int uwwidth; ++ int uwchannel_num; ++ ++ addtmp = memaddr; ++ ++ if ((uwperipheralid > 15)) { ++ dma_err("Invalid peripheral id%x\n", uwperipheralid); ++ return -EINVAL; ++ } ++ ++ uwchannel_num = (int)channel; ++ if ((uwchannel_num == DMAC_CHANNEL_INVALID) ++ || (uwchannel_num > CHANNEL_NUM) || (uwchannel_num < 0)) { ++ dma_err("failure alloc\n"); ++ return -EFAULT; ++ } ++ ++ /* must modified with different peripheral */ ++ uwwidth = g_peripheral[uwperipheralid].transfer_width; ++ ++ /* check transfer direction * ++ * even number-->TX, odd number-->RX*/ ++ uwsrc_addr = memaddr; ++ uwdst_addr = (unsigned int)(g_peripheral[uwperipheralid].peri_addr); ++ ++ tmp = uwnumtransfers >> uwwidth; ++ if (tmp & (~0x0fff)) { ++ dma_err("Invalidate size%x\n", uwnumtransfers); ++ return -EINVAL; ++ } ++ ++ tmp = tmp & 0xfff; ++ uwtrans_control = tmp | ++ (g_peripheral[uwperipheralid].transfer_ctrl & (~0xfff)); ++ dmac_writew(DMAC_INTTCCLEAR, (0x1<<(unsigned int)uwchannel_num)); ++ dmac_writew(DMAC_INTERRCLR, (0x1<<(unsigned int)uwchannel_num)); ++ dmac_writew(DMAC_CxSRCADDR(uwchannel_num), (unsigned int)uwsrc_addr); ++ dmac_writew(DMAC_CxDESTADDR(uwchannel_num), (unsigned int)uwdst_addr); ++ dmac_writew(DMAC_CxCONTROL(uwchannel_num), ++ (unsigned int)uwtrans_control); ++ dmac_writew(DMAC_CxCONFIG(uwchannel_num), ++ (g_peripheral[uwperipheralid].transfer_cfg)); ++ ++ return 0; ++} ++ ++/* ++ * enable memory and peripheral dma transfer ++ * note: ++ * it is necessary to call dmac_channelstart to enable channel ++ */ ++int dmac_start_p2m(unsigned int channel, unsigned int memaddr, ++ unsigned int uwperipheralid, unsigned int uwnumtransfers, ++ unsigned int next_lli_addr) ++{ ++ unsigned int uwtrans_control = 0; ++ unsigned int addtmp, tmp; ++ unsigned int uwdst_addr = 0, uwsrc_addr = 0; ++ unsigned int uwwidth; ++ int uwchannel_num; ++ ++ addtmp = memaddr; ++ ++ if ((uwperipheralid > 15)) { ++ dma_err("Invalid peripheral id%x\n", uwperipheralid); ++ return -EINVAL; ++ } ++ ++ uwchannel_num = (int)channel; ++ if ((uwchannel_num == DMAC_CHANNEL_INVALID) ++ || (uwchannel_num > 3) || (uwchannel_num < 0)) { ++ dma_err("failure alloc\n"); ++ return -EFAULT; ++ } ++ ++ /* must modified with different peripheral */ ++ uwwidth = g_peripheral[uwperipheralid].transfer_width; ++ ++ /* check transfer direction * ++ * even number-->TX, odd number-->RX*/ ++ uwsrc_addr = (unsigned int)(g_peripheral[uwperipheralid].peri_addr); ++ uwdst_addr = memaddr; ++ ++ tmp = uwnumtransfers >> uwwidth; ++ if (tmp & (~0x0fff)) { ++ dma_err("Invalidate size%x\n", uwnumtransfers); ++ return -EINVAL; ++ } ++ ++ tmp = tmp & 0xfff; ++ uwtrans_control = tmp | ++ (g_peripheral[uwperipheralid].transfer_ctrl & (~0xfff)); ++ dmac_writew(DMAC_INTTCCLEAR, (0x1<<(unsigned int)uwchannel_num)); ++ dmac_writew(DMAC_INTERRCLR, (0x1<<(unsigned int)uwchannel_num)); ++ dmac_writew(DMAC_CxSRCADDR(uwchannel_num), (unsigned int)uwsrc_addr); ++ dmac_writew(DMAC_CxDESTADDR(uwchannel_num), (unsigned int)uwdst_addr); ++ dmac_writew(DMAC_CxCONTROL(uwchannel_num), ++ (unsigned int)uwtrans_control); ++ dmac_writew(DMAC_CxCONFIG(uwchannel_num), ++ (g_peripheral[uwperipheralid].transfer_cfg)); ++ ++ return 0; ++} ++ ++/* ++ * execute memory to memory dma transfer without LLI ++ */ ++int dmac_m2m_transfer(unsigned int source, unsigned int dest, ++ unsigned int length) ++{ ++ unsigned int ulchnn, dma_size = 0; ++ unsigned int dma_count, left_size; ++ ++ left_size = length; ++ dma_count = 0; ++ ulchnn = dmac_channel_allocate(NULL); ++ ++ ulchnn = 2; ++ ++ dma_err("use channel %d\n", ulchnn); ++ ++ while ((left_size >> 2) >= 0xffc) { ++ dma_size = 0xffc; ++ left_size -= (dma_size << 2); ++ dma_err("left_size is %x.", left_size); ++ dmac_start_m2m(ulchnn, (unsigned int)(source ++ + dma_count * (dma_size << 2)), ++ (unsigned int)(dest + dma_count * (dma_size << 2)), ++ (dma_size << 2)); ++ if (dmac_channelstart(ulchnn) != 0) { ++ dma_err("start channel error...\n"); ++ return -1; ++ } ++ ++ if (dmac_wait(ulchnn) != DMAC_CHN_SUCCESS) { ++ dma_err("dma transfer error...\n"); ++ return -1; ++ } ++ ++ dma_count++; ++ } ++ ++ dmac_start_m2m(ulchnn, (source + dma_count * (dma_size << 2)), ++ (dest + dma_count * (dma_size << 2)), (left_size << 2)); ++ ++ if (dmac_channelstart(ulchnn) != 0) ++ return -1; ++ ++ if (dmac_wait(ulchnn) != DMAC_CHN_SUCCESS) ++ return -1; ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_m2m_transfer); ++ ++/* ++ * execute memory to peripheral dma transfer without LLI ++ */ ++int dmac_m2p_transfer(unsigned int memaddr, unsigned int uwperipheralid, ++ unsigned int length) ++{ ++ unsigned int ulchnn, dma_size = 0; ++ unsigned int dma_count, left_size; ++ unsigned int uwwidth; ++ ++ left_size = length; ++ dma_count = 0; ++ ++ ulchnn = dmac_channel_allocate(NULL); ++ if (DMAC_CHANNEL_INVALID == ulchnn) ++ return -1; ++ ++ uwwidth = g_peripheral[uwperipheralid].transfer_width; ++ ++ while ((left_size >> uwwidth) >= 0xffc) { ++ dma_size = 0xffc; ++ left_size -= (dma_size << uwwidth); ++ ++ if (dmac_start_m2p(ulchnn, ++ (unsigned int)(memaddr + dma_count * dma_size), ++ uwperipheralid, (dma_size << uwwidth), 0) < 0) ++ return -1; ++ ++ if (dmac_channelstart(ulchnn) != 0) ++ return -1; ++ ++ if (dmac_wait(ulchnn) != DMAC_CHN_SUCCESS) { ++ dmac_channel_free(ulchnn); ++ return -1; ++ } ++ ++ dma_count++; ++ } ++ ++ pr_debug("memaddr=0x%x\n", (unsigned int)(memaddr ++ + dma_count * dma_size)); ++ ++ if (dmac_start_m2p(ulchnn, ++ (unsigned int)(memaddr + dma_count * dma_size), ++ uwperipheralid, left_size, 0) < 0) ++ return -1; ++ ++ if (dmac_channelstart(ulchnn) != 0) ++ return -1; ++ ++ return ulchnn; ++} ++ ++/* ++ * execute memory to peripheral dma transfer without LLI ++ */ ++int dmac_p2m_transfer(unsigned int memaddr, unsigned int uwperipheralid, ++ unsigned int length) ++{ ++ unsigned int ulchnn, dma_size = 0; ++ unsigned int dma_count, left_size; ++ unsigned int uwwidth; ++ ++ left_size = length; ++ dma_count = 0; ++ ++ ulchnn = dmac_channel_allocate(NULL); ++ if (DMAC_CHANNEL_INVALID == ulchnn) ++ return -1; ++ ++ uwwidth = g_peripheral[uwperipheralid].transfer_width; ++ ++ while ((left_size >> uwwidth) >= 0xffc) { ++ dma_size = 0xffc; ++ left_size -= (dma_size << uwwidth); ++ ++ if (dmac_start_p2m(ulchnn, ++ (unsigned int)(memaddr + dma_count * dma_size), ++ uwperipheralid, (dma_size << uwwidth), 0) < 0) ++ return -1; ++ ++ if (dmac_channelstart(ulchnn) != 0) ++ return -1; ++ ++ if (dmac_wait(ulchnn) != DMAC_CHN_SUCCESS) { ++ dmac_channel_free(ulchnn); ++ return -1; ++ } ++ ++ dma_count++; ++ } ++ ++ pr_debug("memaddr=0x%x\n", (unsigned int)(memaddr ++ + dma_count * dma_size)); ++ ++ if (dmac_start_p2m(ulchnn, ++ (unsigned int)(memaddr + dma_count * dma_size), ++ uwperipheralid, left_size, 0) < 0) ++ return -1; ++ ++ if (dmac_channelstart(ulchnn) != 0) ++ return -1; ++ ++ return ulchnn; ++} ++ ++/* ++ * memory to memory dma transfer with LLI ++ * ++ * @source ++ * @dest ++ * @length ++ * @num ++ * */ ++int do_dma_llim2m_isp(unsigned int *source, ++ unsigned int *dest, ++ unsigned int *length, ++ unsigned int num) ++{ ++ unsigned int chnn; ++ int ret = 0; ++ ++ /* the dma channel is default using 2 */ ++ chnn = 2; ++ ++ ret = dmac_buildllim2m_isp(pllihead, source, dest, length, num); ++ ++ if (ret) { ++ dma_err("build lli error...\n"); ++ return -1; ++ } ++ ++ /* dmac_register_isr(chnn, dmac_channel_close); */ ++ ret = dmac_start_llim2m(chnn, pllihead); ++ if (ret) ++ return -1; ++ ++ if (dmac_channelstart(chnn) != 0) { ++ dma_err("start channel error...\n"); ++ return -1; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(do_dma_llim2m_isp); ++ ++int do_dma_m2p(unsigned int memaddr, unsigned int peripheral_addr, ++ unsigned int length) ++{ ++ int ret = 0; ++ int uwperipheralid; ++ ++ uwperipheralid = dmac_check_request(peripheral_addr, TX); ++ if (uwperipheralid < 0) { ++ dma_err("m2p:Invalid devaddr\n"); ++ return -1; ++ } ++ ++ ret = dmac_m2p_transfer(memaddr, uwperipheralid, length); ++ if (ret == -1) { ++ dma_err("m2p:trans err\n"); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++int do_dma_p2m(unsigned int memaddr, unsigned int peripheral_addr, ++ unsigned int length) ++{ ++ int ret = -1; ++ int uwperipheralid; ++ ++ uwperipheralid = dmac_check_request(peripheral_addr, RX); ++ if (uwperipheralid < 0) { ++ dma_err("p2m:Invalid devaddr.\n"); ++ return -1; ++ } ++ ++ ret = dmac_p2m_transfer(memaddr, uwperipheralid, length); ++ if (ret == -1) { ++ dma_err("p2m:trans err\n"); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Apply DMA interrupt resource ++ * init channel state ++ */ ++static int hi_dmac_probe(struct platform_device *platdev) ++{ ++ unsigned int i; ++ ++ reg_dmac_base_va = (void *)IO_ADDRESS(DMAC_BASE_REG); ++ ++ dmac_init(); ++ ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) ++ g_channel_status[i] = DMAC_CHN_VACANCY; ++ ++ return 0; ++} ++ ++static int hi_dmac_remove(struct platform_device *platdev) ++{ ++ int i; ++ ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) ++ g_channel_status[i] = DMAC_CHN_VACANCY; ++ ++ return 0; ++} ++ ++static int hi_dmac_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ int i; ++ ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) ++ g_channel_status[i] = DMAC_CHN_VACANCY; ++ ++ return 0; ++} ++ ++static int hi_dmac_resume(struct platform_device *dev) ++{ ++ int i; ++ ++ dmac_init(); ++ ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) ++ g_channel_status[i] = DMAC_CHN_VACANCY; ++ ++ return 0; ++} ++ ++struct platform_device hi_dmac_device = { ++ .name = "hisilicon-dmac", ++ .id = 0, ++}; ++ ++static struct platform_driver hi_dmac_driver = { ++ .probe = hi_dmac_probe, ++ .remove = hi_dmac_remove, ++ .suspend = hi_dmac_suspend, ++ .resume = hi_dmac_resume, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "hisilicon-dmac", ++ }, ++}; ++ ++int __init dma_driver_init(void) ++{ ++ int ret = 0; ++ ++ ret = platform_device_register(&hi_dmac_device); ++ if (ret) { ++ pr_err("register netdevice device failed!"); ++ goto _error_register_device; ++ } ++ ++ ret = platform_driver_register(&hi_dmac_driver); ++ if (ret) { ++ pr_err("register netdevice driver failed!"); ++ goto _error_register_driver; ++ } ++ ++ return ret; ++ ++_error_register_driver: ++ platform_device_unregister(&hi_dmac_device); ++_error_register_device: ++ return -1; ++} ++ ++static void __exit dma_driver_exit(void) ++{ ++ platform_driver_unregister(&hi_dmac_driver); ++ ++ platform_device_unregister(&hi_dmac_device); ++} ++ ++module_init(dma_driver_init); ++module_exit(dma_driver_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Hisilicon"); +diff --git a/drivers/hidmac/hi_dmac.h b/drivers/hidmac/hi_dmac.h +new file mode 100644 +index 0000000..11f2b23 +--- /dev/null ++++ b/drivers/hidmac/hi_dmac.h +@@ -0,0 +1,79 @@ ++/* ./drivers/hidmac/hi_dmac.h ++ * ++ * ++ * History: ++ * 17-August-2006 create this file ++ */ ++#ifndef __HI_DMAC_H__ ++#define __HI_DMAC_H__ ++ ++#define dmac_writew(addr, value)\ ++ hi_writel(value, (void *)(addr)) ++#define dmac_readw(addr, v)\ ++ v = hi_readl((void *)(addr)) ++ ++/*#define DMA_DEBUG*/ ++#ifdef DMA_DEBUG ++#define dma_debug printk ++#else ++#define dma_debug(fmt, ...) do {} while (0); ++#endif ++ ++#define DMAC_CONFIGURATIONx_HALT_DMA_ENABLE (0x01L<<18) ++#define DMAC_CONFIGURATIONx_ACTIVE (0x01L<<17) ++#define DMAC_CONFIGURATIONx_CHANNEL_ENABLE 1 ++#define DMAC_CONFIGURATIONx_CHANNEL_DISABLE 0 ++ ++/*definition for the return value*/ ++#define DMAC_ERROR_BASE 100 ++#define DMAC_CHANNEL_INVALID (DMAC_ERROR_BASE+1) ++ ++#define DMAC_TRXFERSIZE_INVALID (DMAC_ERROR_BASE+2) ++#define DMAC_SOURCE_ADDRESS_INVALID (DMAC_ERROR_BASE+3) ++#define DMAC_DESTINATION_ADDRESS_INVALID (DMAC_ERROR_BASE+4) ++#define DMAC_MEMORY_ADDRESS_INVALID (DMAC_ERROR_BASE+5) ++#define DMAC_PERIPHERAL_ID_INVALID (DMAC_ERROR_BASE+6) ++#define DMAC_DIRECTION_ERROR (DMAC_ERROR_BASE+7) ++#define DMAC_TRXFER_ERROR (DMAC_ERROR_BASE+8) ++#define DMAC_LLIHEAD_ERROR (DMAC_ERROR_BASE+9) ++#define DMAC_SWIDTH_ERROR (DMAC_ERROR_BASE+0xa) ++#define DMAC_LLI_ADDRESS_INVALID (DMAC_ERROR_BASE+0xb) ++#define DMAC_TRANS_CONTROL_INVALID (DMAC_ERROR_BASE+0xc) ++#define DMAC_MEMORY_ALLOCATE_ERROR (DMAC_ERROR_BASE+0xd) ++#define DMAC_NOT_FINISHED (DMAC_ERROR_BASE+0xe) ++ ++#define DMAC_TIMEOUT (DMAC_ERROR_BASE+0xf) ++#define DMAC_CHN_SUCCESS (DMAC_ERROR_BASE+0x10) ++#define DMAC_CHN_ERROR (DMAC_ERROR_BASE+0x11) ++#define DMAC_CHN_TIMEOUT (DMAC_ERROR_BASE+0x12) ++#define DMAC_CHN_ALLOCAT (DMAC_ERROR_BASE+0x13) ++#define DMAC_CHN_VACANCY (DMAC_ERROR_BASE+0x14) ++ ++#define DMAC_CONFIGURATIONx_ACTIVE_NOT 0 ++ ++/*the means the bit in the channel control register*/ ++#define DMAC_TRANS_SIZE 0xff0 ++ ++/*DMAC peripheral structure*/ ++typedef struct dmac_peripheral { ++ /* peripherial ID*/ ++ unsigned int peri_id; ++ /*peripheral data register address*/ ++ unsigned int peri_addr; ++ /*default channel control word*/ ++ unsigned int transfer_ctrl; ++ /*default channel configuration word*/ ++ unsigned int transfer_cfg; ++ /*default channel configuration word*/ ++ unsigned int transfer_width; ++} dmac_peripheral; ++ ++typedef struct mem_addr { ++ unsigned int addr_base; ++ unsigned int size; ++} mem_addr; ++ ++typedef unsigned int dma_addr_t; ++/* #define PAGE_SIZE 0x1000 */ ++ ++#endif /* End of #ifndef __HI_INC_ECSDMACC_H__ */ +diff --git a/drivers/hidmac/hi_pl08x.c b/drivers/hidmac/hi_pl08x.c +new file mode 100644 +index 0000000..7375ed9 +--- /dev/null ++++ b/drivers/hidmac/hi_pl08x.c +@@ -0,0 +1,1282 @@ ++/* ++ * ++ * Copyright (c) 2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/i2c.h> ++#include <linux/init.h> ++#include <linux/time.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/dma-mapping.h> ++#include <linux/pm_runtime.h> ++#include <linux/slab.h> ++#include <linux/of.h> ++#include <linux/clk.h> ++#include <linux/reset.h> ++#include <mach/io.h> ++#include <linux/hidmac.h> ++ ++#include "hi_dmac.h" ++ ++#ifdef CONFIG_ARCH_HI3516CV300 ++#include "hidmac_hi3516cv300.h" ++#endif ++ ++#ifdef CONFIG_ARCH_HI3531D ++#include "hidmac_hi3531d.h" ++#endif ++ ++#ifdef CONFIG_ARCH_HI3521D ++#include "hidmac_hi3521d.h" ++#endif ++ ++#ifdef CONFIG_ARCH_HI3536C ++#include "hidmac_hi3536c.h" ++#endif ++ ++#define RX 0 ++#define TX 1 ++ ++static int dmac_channel[CHANNEL_NUM] = {0, 1, 2, 3}; ++ ++int g_channel_status[CHANNEL_NUM]; ++ ++/* #define DEBUG */ ++ ++#define DEBUG ++#ifdef DEBUG ++#define dma_err printk ++#else ++#define dma_err(fmt, ...) do {} while (0) ++#endif ++ ++/* ++ *Define Memory range ++ */ ++mem_addr mem_num[MEM_MAX_NUM] = { ++ {DDRAM_ADRS, DDRAM_SIZE}, ++ {FLASH_BASE, FLASH_SIZE} ++}; ++ ++typedef void REG_ISR(int *p_dma_chn, int *p_dma_status); ++REG_ISR *function[CHANNEL_NUM]; ++ ++struct hidmac_host { ++ struct clk *clk; ++ struct reset_control *rstc; ++ void __iomem *regbase; ++ ++ int irq; ++}; ++ ++void __iomem *dma_regbase; ++unsigned int pllihead[2]; ++ ++#define CLR_INT(i) ((*(unsigned int *)(dma_regbase+0x008)) = (1 << i)) ++ ++/* ++ * memory address validity check ++ * ++static int mem_check_valid(unsigned int addr) ++{ ++ unsigned int cnt; ++ ++ for (cnt = 0; cnt < MEM_MAX_NUM; cnt++) { ++ if ((addr >= mem_num[cnt].addr_base) && ++ (addr <= (mem_num[cnt].addr_base + mem_num[cnt].size))) ++ return 0; ++ } ++ ++ return -1; ++} */ ++ ++/* ++ * dmac interrupt handle function ++ */ ++irqreturn_t dmac_isr(int irq, void *dev_id) ++{ ++ struct hidmac_host *dma = dev_id; ++ unsigned int channel_status; ++ unsigned int channel_tc_status, channel_err_status; ++ unsigned int i; ++ ++ /*read the status of current interrupt */ ++ dmac_readw(dma->regbase + DMAC_INTSTATUS, channel_status); ++ ++ /*decide which channel has trigger the interrupt*/ ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) { ++ if ((((channel_status >> i) & 0x1) == 0x01)) { ++ /* [HSCP201306240006],l00181524,20130625 */ ++ /* The INT status should be read first then clear it */ ++ /* CLR_INT(i); */ ++ dmac_readw(dma->regbase + DMAC_INTTCSTATUS, channel_tc_status); ++ dmac_readw(dma->regbase + DMAC_INTERRORSTATUS, channel_err_status); ++ CLR_INT(i); ++ /*??HSCP201403110002?? l00183122 20140723*/ ++ if (g_channel_status[i] == DMAC_CHN_VACANCY ++ && (function[i]) == NULL) { ++ if ((0x01 == ((channel_tc_status >> i) & 0x01))) ++ dmac_writew(dma->regbase + DMAC_INTTCCLEAR, ++ (0x01 << i)); ++ else if ((0x01 == ((channel_err_status ++ >> i)&0x01))) ++ dmac_writew(dma->regbase + DMAC_INTERRCLR, ++ (0x01 << i)); ++ continue; ++ } ++ ++ /* save the current channel transfer */ ++ /* status to g_channel_status[i] */ ++ if ((0x01 == ((channel_tc_status >> i) & 0x01))) { ++ g_channel_status[i] = DMAC_CHN_SUCCESS; ++ dmac_writew(dma->regbase + DMAC_INTTCCLEAR, (0x01 << i)); ++ } else if ((0x01 == ((channel_err_status >> i)&0x01))) { ++ g_channel_status[i] = -DMAC_CHN_ERROR; ++ dmac_writew(dma->regbase + DMAC_INTERRCLR, (0x01 << i)); ++ } else { ++ pr_err("Isr Error in DMAC_IntHandeler"); ++ pr_err("%d! channel\n", i); ++ } ++ ++ if ((function[i]) != NULL) ++ function[i](&i, &g_channel_status[i]); ++ } ++ } ++ ++ return IRQ_RETVAL(1); ++} ++ ++/* ++ * update the state of channels ++ */ ++#define HI_DMA_UPDATE_TIMEOUT 5000000 ++static int dma_update_status(unsigned int channel) ++{ ++ ++ unsigned int channel_status; ++ unsigned int channel_tc_status[3]; ++ unsigned int channel_err_status[3]; ++ unsigned int i = channel, j, time = 0; ++ ++ ++ while (1) { ++ for (j = 0; j < 3; j++) { ++ dmac_readw(dma_regbase + DMAC_RAWINTTCSTATUS, ++ channel_status); ++ channel_tc_status[j] = (channel_status >> i) & 0x01; ++ dmac_readw(dma_regbase + DMAC_RAWINTERRORSTATUS, ++ channel_status); ++ channel_err_status[j] = (channel_status >> i) & 0x01; ++ } ++ ++ if ((channel_tc_status[0] == 0x1) && ++ (channel_tc_status[1] == 0x1) && ++ (channel_tc_status[2] == 0x1)) { ++ g_channel_status[i] = DMAC_CHN_SUCCESS; ++ dmac_writew(dma_regbase + DMAC_INTTCCLEAR, (0x01 << i)); ++ break; ++ } else if ((channel_err_status[0] == 0x1) && ++ (channel_err_status[1] == 0x1) && ++ (channel_err_status[2] == 0x1)) { ++ g_channel_status[i] = -DMAC_CHN_ERROR; ++ dma_err("Error in DMAC %d finish!\n", i); ++ dmac_writew(dma_regbase + DMAC_INTERRCLR, (0x01 << i)); ++ break; ++ } ++ ++ if (++time == HI_DMA_UPDATE_TIMEOUT) { ++ dma_err("Timeout in DMAC %d!\n", i); ++ g_channel_status[i] = -DMAC_CHN_TIMEOUT; ++ break; ++ } ++ } ++ ++ return g_channel_status[i]; ++} ++ ++ ++/* ++ * check the state of channels ++ */ ++static int dmac_check_over(unsigned int channel) ++{ ++ int status = 0; ++ ++ if (-DMAC_CHN_ERROR == g_channel_status[channel]) { ++ dma_err("Error transfer %d finished\n", channel); ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(channel), DMAC_CxDISABLE); ++ g_channel_status[channel] = DMAC_CHN_VACANCY; ++ status = -DMAC_CHN_ERROR; ++ } else if (DMAC_NOT_FINISHED == g_channel_status[channel]) ++ status = DMAC_NOT_FINISHED; ++ else if (DMAC_CHN_ALLOCAT == g_channel_status[channel]) ++ status = DMAC_CHN_ALLOCAT; ++ else if (DMAC_CHN_VACANCY == g_channel_status[channel]) ++ status = DMAC_CHN_VACANCY; ++ else if (-DMAC_CHN_TIMEOUT == g_channel_status[channel]) { ++ dma_err("transfer %d timeout!\n", channel); ++ status = -DMAC_CHN_TIMEOUT; ++ } else if (DMAC_CHN_SUCCESS == g_channel_status[channel]) ++ /*The transfer of Channel %d has finished successfully!*/ ++ status = DMAC_CHN_SUCCESS; ++ else { ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(channel), DMAC_CxDISABLE); ++ g_channel_status[channel] = DMAC_CHN_VACANCY; ++ status = -DMAC_CHN_ERROR; ++ } ++ return status; ++} ++ ++spinlock_t my_lcok = __SPIN_LOCK_UNLOCKED(old_style_spin_init); ++unsigned long flags; ++ ++/* ++ * allocate channel. ++ */ ++int dmac_channel_allocate(void *pisr) ++{ ++ unsigned int i, channelinfo, g_channelinfo; ++ ++ for (i = 0; i < CHANNEL_NUM; i++) ++ dmac_check_over(dmac_channel[i]); ++ ++ dmac_readw(dma_regbase + DMAC_ENBLDCHNS, g_channelinfo); ++ g_channelinfo = g_channelinfo & 0x00ff; ++ ++ for (i = 0; i < CHANNEL_NUM; i++) { ++ if (g_channel_status[dmac_channel[i]] == DMAC_CHN_VACANCY) { ++ channelinfo = g_channelinfo >> dmac_channel[i]; ++ if (0x00 == (channelinfo & 0x01)) { ++ /*clear the interrupt in this channel */ ++ dmac_writew(dma_regbase + DMAC_INTERRCLR, ++ (0x01 << dmac_channel[i])); ++ dmac_writew(dma_regbase + DMAC_INTTCCLEAR, ++ (0x01 << dmac_channel[i])); ++ ++ g_channel_status[dmac_channel[i]] ++ = DMAC_CHN_ALLOCAT; ++ return dmac_channel[i]; ++ } ++ } ++ } ++ ++ dma_err("no to alloc\n"); ++ return -EINVAL; ++} ++EXPORT_SYMBOL(dmac_channel_allocate); ++ ++int dmac_register_isr(unsigned int channel, void *pisr) ++{ ++ if (channel > CHANNEL_NUM - 1) { ++ dma_err("channel which choosed %d is error !\n", channel); ++ return -1; ++ } ++ ++ if (g_channel_status[channel] != DMAC_CHN_VACANCY) { ++ dma_err("dma chn %d is in used!\n", channel); ++ return -1; ++ } ++ ++ /*clear the interrupt in this channel */ ++ dmac_writew(dma_regbase + DMAC_INTERRCLR, (0x01 << channel)); ++ dmac_writew(dma_regbase + DMAC_INTTCCLEAR, (0x01 << channel)); ++ ++ function[channel] = (void *)pisr; ++ g_channel_status[channel] = DMAC_CHN_ALLOCAT; ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_register_isr); ++ ++/* ++ * free channel ++ */ ++int dmac_channel_free(unsigned int channel) ++{ ++ if (channel >= DMAC_MAX_CHANNELS) { ++ dma_err("channel larger than total.\n"); ++ return -EINVAL; ++ } ++ ++ g_channel_status[channel] = DMAC_CHN_VACANCY; ++ return 0; ++} ++EXPORT_SYMBOL(dmac_channel_free); ++ ++static unsigned int dmac_check_request(unsigned int peripheral_addr, ++ int direction) ++{ ++ int i; ++ /* check request pipe with peripheral_addr */ ++ for (i = direction; i < DMAC_MAX_PERIPHERALS; i = i + 2) { ++ if (g_peripheral[i].peri_addr == peripheral_addr) ++ return i; ++ } ++ ++ dma_err("Invalid devaddr\n"); ++ ++ return -1; ++} ++ ++/* ++ * init dmac register ++ * clear interrupt flags ++ * called by dma_driver_init ++ */ ++int dmac_init(struct hidmac_host *dma) ++{ ++ unsigned int i, tempvalue; ++ int ret; ++ ++ clk_prepare_enable(dma->clk); ++ reset_control_deassert(dma->rstc); ++ ++ dmac_readw(dma->regbase + DMAC_CONFIG, tempvalue); ++ if (tempvalue == 0) { ++ dmac_writew(dma->regbase + DMAC_CONFIG, ++ DMAC_CONFIG_VAL); ++ dmac_writew(dma->regbase + DMAC_INTTCCLEAR, 0xFF); ++ dmac_writew(dma->regbase + DMAC_INTERRCLR, 0xFF); ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) { ++ dmac_writew(dma->regbase + DMAC_CxCONFIG(i), ++ DMAC_CxDISABLE); ++ function[i] = NULL; ++ } ++ } ++ ++ /* creat LLI */ ++ /* alloc space for dma lli, as the source is uncontinuous, so... */ ++ ret = allocate_dmalli_space(pllihead, 1); ++ if (ret < 0) ++ return -1; ++ ++ if (request_irq(dma->irq, dmac_isr, 0, "hi_dma", dma)) { ++ dma_err("DMA Irq %d request failed\n", dma->irq); ++ free_dmalli_space(pllihead, 1); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++/* ++ * alloc_dma_lli_space ++ * output: ++ * ppheadlli[0]: memory physics address ++ * ppheadlli[1]: virtual address ++ * ++ */ ++int allocate_dmalli_space(unsigned int *ppheadlli, unsigned int page_num) ++{ ++ dma_addr_t dma_phys; ++ void *dma_virt; ++ ++ dma_virt = dma_alloc_coherent(NULL, page_num*PAGE_SIZE, ++ &dma_phys, GFP_DMA | __GFP_WAIT); ++ if (NULL == dma_virt) { ++ dma_err("can't get dma mem from system\n"); ++ return -1; ++ } ++ ++ ppheadlli[0] = (unsigned int)(dma_phys); ++ ppheadlli[1] = (unsigned int)(dma_virt); ++ ++ return 0; ++} ++EXPORT_SYMBOL(allocate_dmalli_space); ++ ++/* ++ * free_dma_lli_space ++ */ ++int free_dmalli_space(unsigned int *ppheadlli, unsigned int page_num) ++{ ++ dma_addr_t dma_phys; ++ unsigned int dma_virt; ++ ++ dma_phys = (dma_addr_t)(ppheadlli[0]); ++ dma_virt = ppheadlli[1]; ++ ++ dma_free_coherent(NULL, page_num*PAGE_SIZE, ++ (void *)dma_virt, dma_phys); ++ ++ ppheadlli[0] = 0; ++ ppheadlli[1] = 0; ++ return 0; ++} ++EXPORT_SYMBOL(free_dmalli_space); ++ ++/* ++ * config register for memory to memory DMA transfer without LLI ++ * note: ++ * it is necessary to call dmac_channelstart for channel enable ++ */ ++int dmac_start_m2m(unsigned int channel, unsigned int psource, ++ unsigned int pdest, unsigned int uwnumtransfers) ++{ ++ unsigned int uwchannel_num, tmp_trasnsfer; ++ ++ if (uwnumtransfers > (MAXTRANSFERSIZE << 2)) { ++ dma_err("Invalidate transfer size,size=%x\n", uwnumtransfers); ++ return -EINVAL; ++ } ++ ++ uwchannel_num = channel; ++ ++ if ((uwchannel_num == DMAC_CHANNEL_INVALID) ++ || (uwchannel_num > CHANNEL_NUM)) { ++ pr_err("failure of DMAC channel allocation in M2M function!\n"); ++ return -EFAULT; ++ } ++ ++ /* dmac_writew (DMAC_CxCONFIG(uwchannel_num), DMAC_CxDISABLE); */ ++ dmac_writew(dma_regbase + DMAC_CxSRCADDR(uwchannel_num), psource); ++ dmac_writew(dma_regbase + DMAC_CxDESTADDR(uwchannel_num), pdest); ++ dmac_writew(dma_regbase + DMAC_CxLLI(uwchannel_num), 0); ++ tmp_trasnsfer = (uwnumtransfers >> 2) & 0xfff; ++ tmp_trasnsfer = tmp_trasnsfer | (DMAC_CxCONTROL_M2M & (~0xfff)); ++ dmac_writew(dma_regbase + DMAC_CxCONTROL(uwchannel_num), tmp_trasnsfer); ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(uwchannel_num), DMAC_CxCONFIG_M2M); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_start_m2m); ++ ++/* ++ * channel enable ++ * start a dma transfer immediately ++ */ ++int dmac_channelstart(unsigned int u32channel) ++{ ++ ++ unsigned int reg_value; ++ ++ if (u32channel >= DMAC_MAX_CHANNELS) { ++ dma_err("channel larger %d\n", DMAC_MAX_CHANNELS); ++ return -EINVAL; ++ } ++ ++ g_channel_status[u32channel] = DMAC_NOT_FINISHED; ++ dmac_readw(dma_regbase + DMAC_CxCONFIG(u32channel), reg_value); ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(u32channel), ++ (reg_value | DMAC_CHANNEL_ENABLE)); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_channelstart); ++ ++/* ++ * wait for transfer end ++ */ ++int dmac_wait(int channel) ++{ ++ int ret_result, ret = 0; ++ ++ if (channel < 0) ++ return -1; ++ ++ while (1) { ++ ret_result = dma_update_status(channel); ++ if (ret_result == -DMAC_CHN_ERROR) { ++ dma_err("Transfer Error.\n"); ++ ret = -1; ++ goto end; ++ } else if (ret_result == DMAC_NOT_FINISHED) ++ udelay(10); ++ else if (ret_result == DMAC_CHN_SUCCESS) { ++ ret = DMAC_CHN_SUCCESS; ++ goto end; ++ } ++ else if (ret_result == DMAC_CHN_VACANCY) { ++ ret = DMAC_CHN_SUCCESS; ++ goto end; ++ } else if (ret_result == -DMAC_CHN_TIMEOUT) { ++ dma_err("Timeout.\n"); ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(channel), DMAC_CxDISABLE); ++ g_channel_status[channel] = DMAC_CHN_VACANCY; ++ ret = -1; ++ goto end; ++ } ++ } ++end: ++ dmac_channelclose(channel); ++ return ret; ++} ++EXPORT_SYMBOL(dmac_wait); ++ ++/* ++ * buile LLI for memory to memory DMA transfer ++ */ ++int dmac_buildllim2m_isp(unsigned int *ppheadlli, unsigned int *psource, ++ unsigned int *pdest, unsigned int *length, ++ unsigned int lli_num) ++{ ++ unsigned int address, phy_address; ++ unsigned int j; ++ ++ if (ppheadlli != NULL) { ++ phy_address = (unsigned int)(ppheadlli[0]); ++ dma_debug("phy_address: 0x%X\n",phy_address); ++ address = (unsigned int)(ppheadlli[1]); ++ dma_debug("address: 0x%X\n",address); ++ for (j = 0; j < lli_num; j++) { ++ dma_debug("psource[%d]: 0x%X\n", j, psource[j]); ++ dmac_writew(address, psource[j]); ++ address += 4; ++ phy_address += 4; ++ dma_debug("pdest[%d]: 0x%X\n", j, pdest[j]); ++ dmac_writew(address, pdest[j]); ++ address += 4; ++ phy_address += 4; ++ ++ /* if the last node, next_lli_addr = 0*/ ++ if (j == (lli_num - 1)) ++ dmac_writew(address, 0); ++ else ++ dmac_writew(address, ++ (((phy_address + 8) & (~0x03)) ++ | DMAC_CxLLI_LM)); ++ ++ address += 4; ++ phy_address += 4; ++ ++ if (j == (lli_num - 1)) { ++ dmac_writew(address, ((DMAC_CxCONTROL_LLIM2M_ISP ++ &(~0xfff)) | (length[j]) ++ | 0x80000000)); ++ } else { ++ dmac_writew(address, ++ (((DMAC_CxCONTROL_LLIM2M_ISP&(~0xfff)) | ++ (length[j])) & 0x7fffffff)); ++ } ++ ++ address += 4; ++ phy_address += 4; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_buildllim2m_isp); ++ ++/* ++ * buile LLI for memory to memory DMA transfer ++ */ ++int dmac_buildllim2m(unsigned int *ppheadlli, unsigned int pdest, ++ unsigned int psource, unsigned int totaltransfersize, ++ unsigned int uwnumtransfers) ++{ ++ unsigned int lli_num = 0; ++ unsigned int last_lli = 0; ++ unsigned int address , phy_address, srcaddr, denstaddr; ++ unsigned int j; ++ ++ lli_num = (totaltransfersize / uwnumtransfers); ++ ++ if ((totaltransfersize % uwnumtransfers) != 0) ++ last_lli = 1, ++lli_num; ++ ++ if (ppheadlli != NULL) { ++ phy_address = (unsigned int)(ppheadlli[0]); ++ address = (unsigned int)(ppheadlli[1]); ++ for (j = 0; j < lli_num; j++) { ++ srcaddr = (psource + (j*uwnumtransfers)); ++ dmac_writew(address, srcaddr); ++ address += 4; ++ phy_address += 4; ++ denstaddr = (pdest + (j*uwnumtransfers)); ++ dmac_writew(address, denstaddr); ++ address += 4; ++ phy_address += 4; ++ if (j == (lli_num - 1)) ++ dmac_writew(address, 0); ++ else ++ dmac_writew(address, ++ (((phy_address + 8) & (~0x03)) ++ | DMAC_CxLLI_LM)); ++ ++ address += 4; ++ phy_address += 4; ++ ++ if ((j == (lli_num - 1)) && (last_lli == 0)) ++ dmac_writew(address, ((DMAC_CxCONTROL_LLIM2M ++ &(~0xfff)) | (uwnumtransfers >> 2) ++ | 0x80000000)); ++ else if ((j == (lli_num - 1)) && (last_lli == 1)) ++ dmac_writew(address, ((DMAC_CxCONTROL_LLIM2M ++ & (~0xfff)) | ((totaltransfersize ++ % uwnumtransfers) >> 2) | 0x80000000)); ++ else ++ dmac_writew(address, ++ (((DMAC_CxCONTROL_LLIM2M&(~0xfff)) | ++ (uwnumtransfers >> 2)) & 0x7fffffff)); ++ ++ address += 4; ++ phy_address += 4; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_buildllim2m); ++ ++/* ++ * disable channel ++ * used before the operation of register configuration ++ */ ++int dmac_channelclose(unsigned int channel) ++{ ++ unsigned int reg_value, count; ++ ++ if (channel >= DMAC_MAX_CHANNELS) { ++ dma_err("channel larger than total.\n"); ++ return -EINVAL; ++ } ++ ++ dmac_readw(dma_regbase + DMAC_CxCONFIG(channel), reg_value); ++ ++#define CHANNEL_CLOSE_IMMEDIATE ++#ifdef CHANNEL_CLOSE_IMMEDIATE ++ reg_value &= 0xFFFFFFFE; ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(channel) , reg_value); ++#else ++ reg_value |= DMAC_CONFIGURATIONx_HALT_DMA_ENABLE; ++ /*ignore incoming dma request*/ ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(channel), reg_value); ++ dmac_readw(dma_regbase + DMAC_CxCONFIG(channel), reg_value); ++ /*if FIFO is empty*/ ++ while ((reg_value & DMAC_CONFIGURATIONx_ACTIVE) ++ == DMAC_CONFIGURATIONx_ACTIVE) ++ dmac_readw(dma_regbase + DMAC_CxCONFIG(channel), reg_value); ++ reg_value &= 0xFFFFFFFE; ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(channel), reg_value); ++#endif ++ ++ dmac_readw(dma_regbase + DMAC_ENBLDCHNS, reg_value); ++ reg_value = reg_value & 0x00ff; ++ count = 0; ++ while (((reg_value >> channel) & 0x1) == 1) { ++ dmac_readw(dma_regbase + DMAC_ENBLDCHNS, reg_value); ++ reg_value = reg_value & 0x00ff; ++ if (count++ > 10000) { ++ dma_err("close failure.\n"); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_channelclose); ++ ++/* ++ * load configuration from LLI for memory to memory ++ */ ++int dmac_start_llim2m(unsigned int channel, unsigned int *pfirst_lli) ++{ ++ unsigned int uwchannel_num; ++ dmac_lli plli; ++ unsigned int first_lli; ++ ++ if (NULL == pfirst_lli) { ++ dma_err("Invalidate LLI head!\n"); ++ return -EFAULT; ++ } ++ ++ uwchannel_num = channel; ++ if ((uwchannel_num == DMAC_CHANNEL_INVALID) || ++ (uwchannel_num > 7)) { ++ dma_err("failure of DMAC channel allocation in"); ++ dma_err("LLIM2M function,channel=%x!\n ", uwchannel_num); ++ return -EINVAL; ++ } ++ ++ memset(&plli, 0, sizeof(plli)); ++ first_lli = (unsigned int)pfirst_lli[1]; ++ dmac_readw(first_lli, plli.src_addr); ++ dmac_readw(first_lli+4, plli.dst_addr); ++ dmac_readw(first_lli+8, plli.next_lli); ++ dmac_readw(first_lli+12, plli.lli_transfer_ctrl); ++ ++ dmac_channelclose(uwchannel_num); ++ dmac_writew(dma_regbase + DMAC_INTTCCLEAR, (0x1 << uwchannel_num)); ++ dmac_writew(dma_regbase + DMAC_INTERRCLR, (0x1 << uwchannel_num)); ++ dmac_writew(dma_regbase + DMAC_SYNC, 0x0); ++ ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(uwchannel_num), ++ DMAC_CxDISABLE); ++ dmac_writew(dma_regbase + DMAC_CxSRCADDR(uwchannel_num), ++ (unsigned int)(plli.src_addr)); ++ dmac_writew(dma_regbase + DMAC_CxDESTADDR(uwchannel_num), ++ (unsigned int)(plli.dst_addr)); ++ dmac_writew(dma_regbase + DMAC_CxLLI(uwchannel_num), ++ (unsigned int)(plli.next_lli)); ++ dmac_writew(dma_regbase + DMAC_CxCONTROL(uwchannel_num), ++ (unsigned int)(plli.lli_transfer_ctrl)); ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(uwchannel_num), ++ DMAC_CxCONFIG_LLIM2M); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_start_llim2m); ++ ++/* ++ * load configuration from LLI for memory and peripheral ++ */ ++int dmac_start_llim2p(unsigned int channel, unsigned int *pfirst_lli, ++ unsigned int uwperipheralid) ++{ ++ unsigned int uwchannel_num; ++ dmac_lli plli; ++ unsigned int first_lli; ++ unsigned int temp = 0; ++ ++ if (NULL == pfirst_lli) { ++ dma_err("Invalidate LLI head!\n"); ++ return -EINVAL; ++ } ++ uwchannel_num = channel; ++ if ((uwchannel_num == DMAC_CHANNEL_INVALID) || ++ (uwchannel_num > CHANNEL_NUM)) { ++ dma_err("failure of DMAC channel allocation in"); ++ dma_err("LLIM2P function, channel=%x!\n ", uwchannel_num); ++ return -EINVAL; ++ } ++ ++ memset(&plli, 0, sizeof(plli)); ++ first_lli = (unsigned int)pfirst_lli[1]; ++ dmac_readw(first_lli, plli.src_addr); ++ dmac_readw(first_lli+4, plli.dst_addr); ++ dmac_readw(first_lli+8, plli.next_lli); ++ dmac_readw(first_lli+12, plli.lli_transfer_ctrl); ++ ++ dmac_channelclose(uwchannel_num); ++ dmac_writew(dma_regbase + DMAC_INTTCCLEAR, (0x1<<uwchannel_num)); ++ dmac_writew(dma_regbase + DMAC_INTERRCLR, (0x1<<uwchannel_num)); ++ dmac_writew(dma_regbase + DMAC_SYNC, 0x0); ++ ++ dmac_readw(dma_regbase + DMAC_CxCONFIG(uwchannel_num), temp); ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(uwchannel_num), ++ temp|DMAC_CxDISABLE); ++ dmac_writew(dma_regbase + DMAC_CxSRCADDR(uwchannel_num), ++ plli.src_addr); ++ dmac_writew(dma_regbase + DMAC_CxDESTADDR(uwchannel_num), ++ plli.dst_addr); ++ dmac_writew(dma_regbase + DMAC_CxLLI(uwchannel_num), ++ plli.next_lli); ++ dmac_writew(dma_regbase + DMAC_CxCONTROL(uwchannel_num), ++ plli.lli_transfer_ctrl); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_start_llim2p); ++ ++/* ++ * enable memory and peripheral dma transfer ++ * note: ++ * it is necessary to call dmac_channelstart to enable channel ++ */ ++int dmac_start_m2p(unsigned int channel, unsigned int memaddr, ++ unsigned int uwperipheralid, unsigned int uwnumtransfers, ++ unsigned int next_lli_addr) ++{ ++ ++ unsigned int uwtrans_control = 0; ++ unsigned int addtmp, tmp; ++ unsigned int uwdst_addr = 0, uwsrc_addr = 0; ++ unsigned int uwwidth; ++ int uwchannel_num; ++ ++ addtmp = memaddr; ++ ++ if ((uwperipheralid > 15)) { ++ dma_err("Invalid peripheral id%x\n", uwperipheralid); ++ return -EINVAL; ++ } ++ ++ uwchannel_num = (int)channel; ++ if ((uwchannel_num == DMAC_CHANNEL_INVALID) ++ || (uwchannel_num > CHANNEL_NUM) || (uwchannel_num < 0)) { ++ dma_err("failure alloc\n"); ++ return -EFAULT; ++ } ++ ++ /* must modified with different peripheral */ ++ uwwidth = g_peripheral[uwperipheralid].transfer_width; ++ ++ /* check transfer direction * ++ * even number-->TX, odd number-->RX*/ ++ uwsrc_addr = memaddr; ++ uwdst_addr = (unsigned int)(g_peripheral[uwperipheralid].peri_addr); ++ ++ tmp = uwnumtransfers >> uwwidth; ++ if (tmp & (~0x0fff)) { ++ dma_err("Invalidate size%x\n", uwnumtransfers); ++ return -EINVAL; ++ } ++ ++ tmp = tmp & 0xfff; ++ uwtrans_control = tmp | ++ (g_peripheral[uwperipheralid].transfer_ctrl & (~0xfff)); ++ dmac_writew(dma_regbase + DMAC_INTTCCLEAR, (0x1<<(unsigned int)uwchannel_num)); ++ dmac_writew(dma_regbase + DMAC_INTERRCLR, (0x1<<(unsigned int)uwchannel_num)); ++ dmac_writew(dma_regbase + DMAC_CxSRCADDR(uwchannel_num), (unsigned int)uwsrc_addr); ++ dmac_writew(dma_regbase + DMAC_CxDESTADDR(uwchannel_num), (unsigned int)uwdst_addr); ++ dmac_writew(dma_regbase + DMAC_CxCONTROL(uwchannel_num), ++ (unsigned int)uwtrans_control); ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(uwchannel_num), ++ (g_peripheral[uwperipheralid].transfer_cfg)); ++ ++ return 0; ++} ++ ++/* ++ * enable memory and peripheral dma transfer ++ * note: ++ * it is necessary to call dmac_channelstart to enable channel ++ */ ++int dmac_start_p2m(unsigned int channel, unsigned int memaddr, ++ unsigned int uwperipheralid, unsigned int uwnumtransfers, ++ unsigned int next_lli_addr) ++{ ++ unsigned int uwtrans_control = 0; ++ unsigned int addtmp, tmp; ++ unsigned int uwdst_addr = 0, uwsrc_addr = 0; ++ unsigned int uwwidth; ++ int uwchannel_num; ++ ++ addtmp = memaddr; ++ ++ if ((uwperipheralid > 15)) { ++ dma_err("Invalid peripheral id%x\n", uwperipheralid); ++ return -EINVAL; ++ } ++ ++ uwchannel_num = (int)channel; ++ if ((uwchannel_num == DMAC_CHANNEL_INVALID) ++ || (uwchannel_num > 3) || (uwchannel_num < 0)) { ++ dma_err("failure alloc\n"); ++ return -EFAULT; ++ } ++ ++ /* must modified with different peripheral */ ++ uwwidth = g_peripheral[uwperipheralid].transfer_width; ++ ++ /* check transfer direction * ++ * even number-->TX, odd number-->RX*/ ++ uwsrc_addr = (unsigned int)(g_peripheral[uwperipheralid].peri_addr); ++ uwdst_addr = memaddr; ++ ++ tmp = uwnumtransfers >> uwwidth; ++ if (tmp & (~0x0fff)) { ++ dma_err("Invalidate size%x\n", uwnumtransfers); ++ return -EINVAL; ++ } ++ ++ tmp = tmp & 0xfff; ++ uwtrans_control = tmp | ++ (g_peripheral[uwperipheralid].transfer_ctrl & (~0xfff)); ++ dmac_writew(dma_regbase + DMAC_INTTCCLEAR, (0x1<<(unsigned int)uwchannel_num)); ++ dmac_writew(dma_regbase + DMAC_INTERRCLR, (0x1<<(unsigned int)uwchannel_num)); ++ dmac_writew(dma_regbase + DMAC_CxSRCADDR(uwchannel_num), ++ (unsigned int)uwsrc_addr); ++ dmac_writew(dma_regbase + DMAC_CxDESTADDR(uwchannel_num), ++ (unsigned int)uwdst_addr); ++ dmac_writew(dma_regbase + DMAC_CxCONTROL(uwchannel_num), ++ (unsigned int)uwtrans_control); ++ dmac_writew(dma_regbase + DMAC_CxCONFIG(uwchannel_num), ++ (g_peripheral[uwperipheralid].transfer_cfg)); ++ ++ return 0; ++} ++ ++/* ++ * execute memory to memory dma transfer without LLI ++ */ ++int dmac_m2m_transfer(unsigned int source, unsigned int dest, ++ unsigned int length) ++{ ++ unsigned int ulchnn, dma_size = 0; ++ unsigned int dma_count, left_size; ++ ++ left_size = length; ++ dma_count = 0; ++ ulchnn = dmac_channel_allocate(NULL); ++ ++ ulchnn = 2; ++ ++ dma_err("use channel %d\n", ulchnn); ++ ++ while ((left_size >> 2) >= 0xffc) { ++ dma_size = 0xffc; ++ left_size -= (dma_size << 2); ++ dma_err("left_size is %x.", left_size); ++ dmac_start_m2m(ulchnn, (unsigned int)(source ++ + dma_count * (dma_size << 2)), ++ (unsigned int)(dest + dma_count * (dma_size << 2)), ++ (dma_size << 2)); ++ if (dmac_channelstart(ulchnn) != 0) { ++ dma_err("start channel error...\n"); ++ return -1; ++ } ++ ++ if (dmac_wait(ulchnn) != DMAC_CHN_SUCCESS) { ++ dma_err("dma transfer error...\n"); ++ return -1; ++ } ++ ++ dma_count++; ++ } ++ ++ dmac_start_m2m(ulchnn, (source + dma_count * (dma_size << 2)), ++ (dest + dma_count * (dma_size << 2)), (left_size << 2)); ++ ++ if (dmac_channelstart(ulchnn) != 0) ++ return -1; ++ ++ if (dmac_wait(ulchnn) != DMAC_CHN_SUCCESS) ++ return -1; ++ ++ return 0; ++} ++EXPORT_SYMBOL(dmac_m2m_transfer); ++ ++/* ++ * execute memory to peripheral dma transfer without LLI ++ */ ++int dmac_m2p_transfer(unsigned int memaddr, unsigned int uwperipheralid, ++ unsigned int length) ++{ ++ unsigned int ulchnn, dma_size = 0; ++ unsigned int dma_count, left_size; ++ unsigned int uwwidth; ++ ++ left_size = length; ++ dma_count = 0; ++ ++ ulchnn = dmac_channel_allocate(NULL); ++ if (DMAC_CHANNEL_INVALID == ulchnn) ++ return -1; ++ ++ uwwidth = g_peripheral[uwperipheralid].transfer_width; ++ ++ while ((left_size >> uwwidth) >= 0xffc) { ++ dma_size = 0xffc; ++ left_size -= (dma_size << uwwidth); ++ ++ if (dmac_start_m2p(ulchnn, ++ (unsigned int)(memaddr + dma_count * dma_size), ++ uwperipheralid, (dma_size << uwwidth), 0) < 0) ++ return -1; ++ ++ if (dmac_channelstart(ulchnn) != 0) ++ return -1; ++ ++ if (dmac_wait(ulchnn) != DMAC_CHN_SUCCESS) { ++ dmac_channel_free(ulchnn); ++ return -1; ++ } ++ ++ dma_count++; ++ } ++ ++ pr_debug("memaddr=0x%x\n", (unsigned int)(memaddr ++ + dma_count * dma_size)); ++ ++ if (dmac_start_m2p(ulchnn, ++ (unsigned int)(memaddr + dma_count * dma_size), ++ uwperipheralid, left_size, 0) < 0) ++ return -1; ++ ++ if (dmac_channelstart(ulchnn) != 0) ++ return -1; ++ ++ return ulchnn; ++} ++ ++/* ++ * execute memory to peripheral dma transfer without LLI ++ */ ++int dmac_p2m_transfer(unsigned int memaddr, unsigned int uwperipheralid, ++ unsigned int length) ++{ ++ unsigned int ulchnn, dma_size = 0; ++ unsigned int dma_count, left_size; ++ unsigned int uwwidth; ++ ++ left_size = length; ++ dma_count = 0; ++ ++ ulchnn = dmac_channel_allocate(NULL); ++ if (DMAC_CHANNEL_INVALID == ulchnn) ++ return -1; ++ ++ uwwidth = g_peripheral[uwperipheralid].transfer_width; ++ ++ while ((left_size >> uwwidth) >= 0xffc) { ++ dma_size = 0xffc; ++ left_size -= (dma_size << uwwidth); ++ ++ if (dmac_start_p2m(ulchnn, ++ (unsigned int)(memaddr + dma_count * dma_size), ++ uwperipheralid, (dma_size << uwwidth), 0) < 0) ++ return -1; ++ ++ if (dmac_channelstart(ulchnn) != 0) ++ return -1; ++ ++ if (dmac_wait(ulchnn) != DMAC_CHN_SUCCESS) { ++ dmac_channel_free(ulchnn); ++ return -1; ++ } ++ ++ dma_count++; ++ } ++ ++ pr_debug("memaddr=0x%x\n", (unsigned int)(memaddr ++ + dma_count * dma_size)); ++ ++ if (dmac_start_p2m(ulchnn, ++ (unsigned int)(memaddr + dma_count * dma_size), ++ uwperipheralid, left_size, 0) < 0) ++ return -1; ++ ++ if (dmac_channelstart(ulchnn) != 0) ++ return -1; ++ ++ return ulchnn; ++} ++ ++/* ++ * memory to memory dma transfer with LLI ++ * ++ * @source ++ * @dest ++ * @length ++ * @num ++ * */ ++int do_dma_llim2m_isp(unsigned int *source, ++ unsigned int *dest, ++ unsigned int *length, ++ unsigned int num) ++{ ++ unsigned int chnn; ++ int ret = 0; ++ ++ /* the dma channel is default using 2 */ ++ chnn = 2; ++ ++ ret = dmac_buildllim2m_isp(pllihead, source, dest, length, num); ++ ++ if (ret) { ++ dma_err("build lli error...\n"); ++ return -1; ++ } ++ ++ /* dmac_register_isr(chnn, dmac_channel_close); */ ++ ret = dmac_start_llim2m(chnn, pllihead); ++ if (ret) ++ return -1; ++ ++ if (dmac_channelstart(chnn) != 0) { ++ dma_err("start channel error...\n"); ++ return -1; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(do_dma_llim2m_isp); ++ ++int do_dma_m2p(unsigned int memaddr, unsigned int peripheral_addr, ++ unsigned int length) ++{ ++ int ret = 0; ++ int uwperipheralid; ++ ++ uwperipheralid = dmac_check_request(peripheral_addr, TX); ++ if (uwperipheralid < 0) { ++ dma_err("m2p:Invalid devaddr\n"); ++ return -1; ++ } ++ ++ ret = dmac_m2p_transfer(memaddr, uwperipheralid, length); ++ if (ret == -1) { ++ dma_err("m2p:trans err\n"); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++int do_dma_p2m(unsigned int memaddr, unsigned int peripheral_addr, ++ unsigned int length) ++{ ++ int ret = -1; ++ int uwperipheralid; ++ ++ uwperipheralid = dmac_check_request(peripheral_addr, RX); ++ if (uwperipheralid < 0) { ++ dma_err("p2m:Invalid devaddr.\n"); ++ return -1; ++ } ++ ++ ret = dmac_p2m_transfer(memaddr, uwperipheralid, length); ++ if (ret == -1) { ++ dma_err("p2m:trans err\n"); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Apply DMA interrupt resource ++ * init channel state ++ */ ++static int hi_dmac_probe(struct platform_device *platdev) ++{ ++ unsigned int i; ++ struct hidmac_host *dma; ++ struct resource *res; ++ int ret; ++ ++ dma = devm_kzalloc(&platdev->dev, sizeof(*dma), GFP_KERNEL); ++ if (!dma) ++ return -ENOMEM; ++ ++ res = platform_get_resource(platdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&platdev->dev, "no mmio resource\n"); ++ return -ENODEV; ++ } ++ ++ dma->regbase = devm_ioremap_resource(&platdev->dev, res); ++ if (IS_ERR(dma->regbase)) ++ return PTR_ERR(dma->regbase); ++ ++ dma->clk = devm_clk_get(&platdev->dev, NULL); ++ if (IS_ERR(dma->clk)) ++ return PTR_ERR(dma->clk); ++ ++ dma->rstc = devm_reset_control_get(&platdev->dev, "dma-reset"); ++ if (IS_ERR(dma->rstc)) ++ return PTR_ERR(dma->rstc); ++ ++ dma->irq = platform_get_irq(platdev, 0); ++ if (unlikely(dma->irq < 0)) ++ return -ENODEV; ++ ++ dma_regbase = dma->regbase; ++ ++ ret = dmac_init(dma); ++ if (ret) ++ return -ENODEV; ++ ++ platform_set_drvdata(platdev, dma); ++ ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) ++ g_channel_status[i] = DMAC_CHN_VACANCY; ++ ++ return ret; ++} ++ ++static int hi_dmac_remove(struct platform_device *platdev) ++{ ++ int i; ++ struct hidmac_host *dma = platform_get_drvdata(platdev); ++ ++ clk_disable_unprepare(dma->clk); ++ ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) ++ g_channel_status[i] = DMAC_CHN_VACANCY; ++ ++ free_dmalli_space(pllihead, 1); ++ ++ return 0; ++} ++ ++static int hi_dmac_suspend(struct platform_device *platdev, ++ pm_message_t state) ++{ ++ int i; ++ struct hidmac_host *dma = platform_get_drvdata(platdev); ++ ++ clk_prepare_enable(dma->clk); ++ ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) ++ g_channel_status[i] = DMAC_CHN_VACANCY; ++ ++ clk_disable_unprepare(dma->clk); ++ ++ return 0; ++} ++ ++static int hi_dmac_resume(struct platform_device *platdev) ++{ ++ int i; ++ struct hidmac_host *dma = platform_get_drvdata(platdev); ++ unsigned int tempvalue; ++ ++ clk_prepare_enable(dma->clk); ++ reset_control_deassert(dma->rstc); ++ ++ dmac_readw(dma->regbase + DMAC_CONFIG, tempvalue); ++ if (tempvalue == 0) { ++ dmac_writew(dma->regbase + DMAC_CONFIG, ++ DMAC_CONFIG_VAL); ++ dmac_writew(dma->regbase + DMAC_INTTCCLEAR, 0xFF); ++ dmac_writew(dma->regbase + DMAC_INTERRCLR, 0xFF); ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) { ++ dmac_writew(dma->regbase + DMAC_CxCONFIG(i), ++ DMAC_CxDISABLE); ++ function[i] = NULL; ++ } ++ } ++ ++ for (i = 0; i < DMAC_MAX_CHANNELS; i++) ++ g_channel_status[i] = DMAC_CHN_VACANCY; ++ ++ return 0; ++} ++ ++static const struct of_device_id hisi_dmac_dt_ids[] = { ++ { .compatible = "hisilicon,hisi-dmac"}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, hisi_dmac_dt_ids); ++ ++static struct platform_driver hisi_dmac_driver = { ++ .driver = { ++ .name = "hisi-dmac", ++ .of_match_table = hisi_dmac_dt_ids, ++ }, ++ .probe = hi_dmac_probe, ++ .remove = hi_dmac_remove, ++ .suspend = hi_dmac_suspend, ++ .resume = hi_dmac_resume, ++}; ++ ++module_platform_driver(hisi_dmac_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Hisilicon"); ++MODULE_DESCRIPTION("HiSilicon DMA Controller driver"); +diff --git a/drivers/hidmac/hidmac_hi3516av200.c b/drivers/hidmac/hidmac_hi3516av200.c +new file mode 100644 +index 0000000..cf9132a +--- /dev/null ++++ b/drivers/hidmac/hidmac_hi3516av200.c +@@ -0,0 +1,90 @@ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/i2c.h> ++#include <linux/init.h> ++#include <linux/time.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/pm_runtime.h> ++#include <linux/slab.h> ++#include <linux/io.h> ++ ++#define SYS_CRG_BASE IO_ADDRESS(0x12010000) ++#define REG_PERI_CRG56 0xe0 ++#define DMAC_CLK_EN (1 << 1) ++#define DMAC_SRST_REQ (1 << 0) ++ ++/* ++ * DMA config array! ++ * DREQ, FIFO, CONTROL, CONFIG, BITWIDTH ++ */ ++dmac_peripheral g_peripheral[DMAC_MAX_PERIPHERALS] = { ++ /* periphal 0: UART0 RX, 8bit width */ ++ {0, UART0_DATA_REG, 0x99000000, 0xd000, 0}, ++ ++ /* periphal 1: UART0 TX, 8bit width */ ++ {1, UART0_DATA_REG, 0x96000000, 0xc840, 0}, ++ ++ /*periphal 2: UART1 RX, 8bit width */ ++ {2, UART1_DATA_REG, 0x99000000, 0xd004, 0}, ++ ++ /*periphal 3: UART1 TX, 8bit width */ ++ {3, UART1_DATA_REG, 0x96000000, 0xc8c0, 0}, ++ ++ /*periphal 4: UART2 RX, 8bit width */ ++ {4, UART2_DATA_REG, 0x99000000, 0xd008, 0}, ++ ++ /*periphal 5: UART2 TX, 8bit width */ ++ {5, UART2_DATA_REG, 0x96000000, 0xc940, 0}, ++ ++ /*periphal 6: UART3 RX, 8bit width */ ++ {6, UART3_DATA_REG, 0x99000000, 0xd00c, 0}, ++ ++ /*periphal 7: UART3 TX, 8bit width */ ++ {7, UART3_DATA_REG, 0x96000000, 0xc9c0, 0}, ++ ++ /*periphal 8: I2C0 RX, 8bit width */ ++ {8, I2C0_DATA_REG, 0x99000000, 0xd010, 0}, ++ ++ /*periphal 9: I2C0 TX, 8bit width */ ++ {9, I2C0_DATA_REG, 0x96000000, 0xca40, 0}, ++ ++ /*periphal 10: I2C1 RX, 8bit width */ ++ {10, I2C1_DATA_REG, 0x99000000, 0xd014, 0}, ++ ++ /*periphal 11: I2C1 TX, 8bit width */ ++ {11, I2C1_DATA_REG, 0x96000000, 0xcac0, 0}, ++ ++ /*periphal 12: I2C2 RX, 8bit width */ ++ {12, SPI2_DATA_REG, 0x99000000, 0xd018, 0}, ++ ++ /*periphal 13: I2C2 TX, 8bit width */ ++ {13, SPI2_DATA_REG, 0x96000000, 0xcb40, 0}, ++ ++ /*periphal 14: I2C3 RX, 8bit width */ ++ {14, I2C2_DATA_REG, 0x99000000, 0xd01c, 0}, ++ ++ /*periphal 15: I2C3 TX, 8bit width */ ++ {15, I2C2_DATA_REG, 0x96000000, 0xcbc0, 0}, ++}; ++ ++void hidmac_clk_en(void) ++{ ++ unsigned int tmp; ++ ++ dmac_readw(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++ tmp |= DMAC_CLK_EN; ++ dmac_writew(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++} ++ ++void hidmac_unreset(void) ++{ ++ unsigned int tmp; ++ ++ dmac_readw(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++ tmp &= ~DMAC_SRST_REQ; ++ dmac_writew(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++} +diff --git a/drivers/hidmac/hidmac_hi3516av200.h b/drivers/hidmac/hidmac_hi3516av200.h +new file mode 100644 +index 0000000..a0e7beb +--- /dev/null ++++ b/drivers/hidmac/hidmac_hi3516av200.h +@@ -0,0 +1,99 @@ ++#ifndef __HI_DMAC_HI3516AV200_H__ ++#define __HI_DMAC_HI3516AV200_H__ ++ ++#define DDRAM_ADRS 0x80000000 /* fixed */ ++#define DDRAM_SIZE 0x3FFFFFFF /* 1GB DDR. */ ++ ++#define FLASH_BASE 0x10000000 ++#define FLASH_SIZE 0x04000000 /* (32MB) */ ++ ++#define DMAC_BASE_REG CONFIG_HI_DMAC_IO_BASE ++#define DMAC_INTTCCLEAR IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X08) ++ ++#define DMAC_INTSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X00) ++#define DMAC_INTTCSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X04) ++#define DMAC_INTERRORSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X0C) ++ ++#define DMAC_INTERRCLR IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X10) ++#define DMAC_RAWINTTCSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X14) ++#define DMAC_RAWINTERRORSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X18) ++#define DMAC_ENBLDCHNS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X1C) ++#define DMAC_CONFIG IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X30) ++#define DMAC_SYNC IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X34) ++ ++#define DMAC_MAXTRANSFERSIZE 0x0fff /*the max length is denoted by 0-11bit*/ ++#define MAXTRANSFERSIZE DMAC_MAXTRANSFERSIZE ++#define DMAC_CxDISABLE 0x00 ++#define DMAC_CxENABLE 0x01 ++ ++/*the definition for DMAC channel register*/ ++#define DMAC_CxBASE(i) IO_DMAC_ADDRESS(DMAC_BASE_REG + 0x100+i*0x20) ++#define DMAC_CxSRCADDR(i) DMAC_CxBASE(i) ++#define DMAC_CxDESTADDR(i) (DMAC_CxBASE(i)+0x04) ++#define DMAC_CxLLI(i) (DMAC_CxBASE(i)+0x08) ++#define DMAC_CxCONTROL(i) (DMAC_CxBASE(i)+0x0C) ++#define DMAC_CxCONFIG(i) (DMAC_CxBASE(i)+0x10) ++ ++/*the means the bit in the channel control register*/ ++#define DMAC_CxCONTROL_M2M 0x9d480000 /* Dwidth=32,burst size=4 */ ++#define DMAC_CxCONTROL_LLIM2M 0x0f480000 /* Dwidth=32,burst size=1 */ ++#define DMAC_CxLLI_LM 0x01 ++ ++#define DMAC_CxCONFIG_M2M 0xc000 ++#define DMAC_CxCONFIG_LLIM2M 0xc000 ++ ++/*#define DMAC_CxCONFIG_M2M 0x4001*/ ++#define DMAC_CHANNEL_ENABLE 1 ++#define DMAC_CHANNEL_DISABLE 0xfffffffe ++ ++#define DMAC_CxCONTROL_P2M 0x89409000 ++#define DMAC_CxCONFIG_P2M 0xd000 ++ ++#define DMAC_CxCONTROL_M2P 0x86089000 ++#define DMAC_CxCONFIG_M2P 0xc800 ++ ++#define DMAC_CxCONFIG_SIO_P2M 0x0000d000 ++#define DMAC_CxCONFIG_SIO_M2P 0x0000c800 ++ ++/*default the config and sync regsiter for DMAC controller*/ ++/*M1,M2 little endian, enable DMAC*/ ++#define DMAC_CONFIG_VAL 0x01 ++/*enable the sync logic for the 16 peripheral*/ ++#define DMAC_SYNC_VAL 0x0 ++ ++#define DMAC_MAX_PERIPHERALS 16 ++#define MEM_MAX_NUM 2 ++#define CHANNEL_NUM CONFIG_HI_DMAC_CHANNEL_NUM ++#define DMAC_MAX_CHANNELS CHANNEL_NUM ++ ++#define REG_BASE_UART0 0x12100000 ++#define UART0_DATA_REG (REG_BASE_UART0 + 0x0) ++ ++#define REG_BASE_UART1 0x12101000 ++#define UART1_DATA_REG (REG_BASE_UART1 + 0x0) ++ ++#define REG_BASE_UART2 0x12102000 ++#define UART2_DATA_REG (REG_BASE_UART2 + 0x0) ++ ++#define REG_BASE_UART3 0x12103000 ++#define UART3_DATA_REG (REG_BASE_UART3 + 0x0) ++ ++#define REG_BASE_I2C0 0x12110000 ++#define I2C0_DATA_REG (REG_BASE_I2C0 + 0x10) ++ ++#define REG_BASE_I2C1 0x12111000 ++#define I2C1_DATA_REG (REG_BASE_I2C1 + 0x10) ++ ++#define REG_BASE_I2C2 0x12112000 ++#define I2C2_DATA_REG (REG_BASE_I2C2 + 0x10) ++ ++#define REG_BASE_I2C3 0x12113000 ++#define I2C3_DATA_REG (REG_BASE_I2C3 + 0x10) ++ ++#define REG_BASE_SPI2 0x12122000 ++#define SPI2_DATA_REG (REG_BASE_SPI2 + 0x8) ++ ++/*the transfer control and configuration value for different peripheral*/ ++ ++extern int g_channel_status[CHANNEL_NUM]; ++#endif +diff --git a/drivers/hidmac/hidmac_hi3516cv300.h b/drivers/hidmac/hidmac_hi3516cv300.h +new file mode 100644 +index 0000000..61fbf09 +--- /dev/null ++++ b/drivers/hidmac/hidmac_hi3516cv300.h +@@ -0,0 +1,139 @@ ++#ifndef __HI_DMAC_HI3516CV300_H__ ++#define __HI_DMAC_HI3516CV300_H__ ++ ++#define DDRAM_ADRS 0x80000000 /* fixed */ ++#define DDRAM_SIZE 0x3FFFFFFF /* 1GB DDR. */ ++ ++#define FLASH_BASE 0x10000000 ++#define FLASH_SIZE 0x04000000 /* (32MB) */ ++ ++#define DMAC_INTSTATUS 0X00 ++#define DMAC_INTTCSTATUS 0X04 ++#define DMAC_INTTCCLEAR 0X08 ++#define DMAC_INTERRORSTATUS 0X0C ++ ++#define DMAC_INTERRCLR 0X10 ++#define DMAC_RAWINTTCSTATUS 0X14 ++#define DMAC_RAWINTERRORSTATUS 0X18 ++#define DMAC_ENBLDCHNS 0X1C ++#define DMAC_CONFIG 0X30 ++#define DMAC_SYNC 0X34 ++ ++#define DMAC_MAXTRANSFERSIZE 0x0fff /*the max length is denoted by 0-11bit*/ ++#define MAXTRANSFERSIZE DMAC_MAXTRANSFERSIZE ++#define DMAC_CxDISABLE 0x00 ++#define DMAC_CxENABLE 0x01 ++ ++/*the definition for DMAC channel register*/ ++#define DMAC_CxBASE(i) (0x100+i*0x20) ++#define DMAC_CxSRCADDR(i) DMAC_CxBASE(i) ++#define DMAC_CxDESTADDR(i) (DMAC_CxBASE(i)+0x04) ++#define DMAC_CxLLI(i) (DMAC_CxBASE(i)+0x08) ++#define DMAC_CxCONTROL(i) (DMAC_CxBASE(i)+0x0C) ++#define DMAC_CxCONFIG(i) (DMAC_CxBASE(i)+0x10) ++ ++/*the means the bit in the channel control register*/ ++#define DMAC_CxCONTROL_M2M 0x9d480000 /* Dwidth=32,burst size=4 */ ++#define DMAC_CxCONTROL_LLIM2M 0x0f480000 /* Dwidth=32,burst size=1 */ ++#define DMAC_CxCONTROL_LLIM2M_ISP 0x0b489000 /* Dwidth=32,burst size=1 */ ++#define DMAC_CxLLI_LM 0x01 ++ ++#define NUM_HAL_INTERRUPT_DMAC (14 + 16) ++ ++#define DMAC_CxCONFIG_M2M 0xc000 ++#define DMAC_CxCONFIG_LLIM2M 0xc000 ++ ++/*#define DMAC_CxCONFIG_M2M 0x4001*/ ++#define DMAC_CHANNEL_ENABLE 1 ++#define DMAC_CHANNEL_DISABLE 0xfffffffe ++ ++#define DMAC_CxCONTROL_P2M 0x89409000 ++#define DMAC_CxCONFIG_P2M 0xd000 ++ ++#define DMAC_CxCONTROL_M2P 0x86089000 ++#define DMAC_CxCONFIG_M2P 0xc800 ++ ++#define DMAC_CxCONFIG_SIO_P2M 0x0000d000 ++#define DMAC_CxCONFIG_SIO_M2P 0x0000c800 ++ ++/*default the config and sync regsiter for DMAC controller*/ ++/*M1,M2 little endian, enable DMAC*/ ++#define DMAC_CONFIG_VAL 0x01 ++/*enable the sync logic for the 16 peripheral*/ ++#define DMAC_SYNC_VAL 0x0 ++ ++#define DMAC_MAX_PERIPHERALS 16 ++#define MEM_MAX_NUM 2 ++#define CHANNEL_NUM CONFIG_HI_DMAC_CHANNEL_NUM ++#define DMAC_MAX_CHANNELS CHANNEL_NUM ++ ++#define REG_BASE_I2C0 0x12110000 ++#define I2C0_DATA_REG (REG_BASE_I2C0 + 0x14) ++ ++#define REG_BASE_I2C1 0x12112000 ++#define I2C1_DATA_REG (REG_BASE_I2C1 + 0x14) ++ ++#define REG_BASE_UART0 0x12100000 ++#define UART0_DATA_REG (REG_BASE_UART0 + 0x0) ++ ++#define REG_BASE_UART1 0x12101000 ++#define UART1_DATA_REG (REG_BASE_UART1 + 0x0) ++ ++#define REG_BASE_UART2 0x12102000 ++#define UART2_DATA_REG (REG_BASE_UART2 + 0x0) ++ ++#define REG_BASE_SPI0 0x12110000 ++#define SPI0_DATA_REG (REG_BASE_SPI0 + 0x8) ++ ++#define REG_BASE_SPI1 0x12121000 ++#define SPI1_DATA_REG (REG_BASE_SPI1 + 0x8) ++ ++/*the transfer control and configuration value for different peripheral*/ ++ ++extern int g_channel_status[CHANNEL_NUM]; ++ ++dmac_peripheral g_peripheral[DMAC_MAX_PERIPHERALS] = { ++ /*periphal 0: I2C0 RX, 8bit width */ ++ {0, I2C0_DATA_REG, 0x99000000, 0xd010, 0}, ++ ++ /*periphal 1: I2C0 TX, 8bit width */ ++ {1, I2C0_DATA_REG, 0x96000000, 0xca40, 0}, ++ ++ /*periphal 4: I2C1 RX, 8bit width */ ++ {4, I2C1_DATA_REG, 0x99000000, 0xd014, 0}, ++ ++ /*periphal 5: I2C1 TX, 8bit width */ ++ {5, I2C1_DATA_REG, 0x96000000, 0xcac0, 0}, ++ ++ /*periphal 6: UART0 RX, 8bit width */ ++ {6, UART0_DATA_REG, 0x99000000, 0xd000, 0}, ++ ++ /*periphal 7: UART0 TX, 8bit width */ ++ {7, UART0_DATA_REG, 0x96000000, 0xc840, 0}, ++ ++ /*periphal 8: UART1 RX, 8bit width */ ++ {8, UART1_DATA_REG, 0x99000000, 0xd004, 0}, ++ ++ /*periphal 9: UART1 TX, 8bit width */ ++ {9, UART1_DATA_REG, 0x96000000, 0xc8c0, 0}, ++ ++ /*periphal 10: UART2 RX, 8bit width */ ++ {10, UART2_DATA_REG, 0x99000000, 0xd008, 0}, ++ ++ /*periphal 11: UART2 TX, 8bit width */ ++ {11, UART2_DATA_REG, 0x96000000, 0xc940, 0}, ++ ++ /*periphal 12: I2C2 RX, 8bit width */ ++ {12, SPI0_DATA_REG, 0x99000000, 0xd018, 0}, ++ ++ /*periphal 13: I2C2 TX, 8bit width */ ++ {13, SPI0_DATA_REG, 0x96000000, 0xcb40, 0}, ++ ++ /*periphal 14: I2C3 RX, 8bit width */ ++ {14, SPI1_DATA_REG, 0x99000000, 0xd01c, 0}, ++ ++ /*periphal 15: I2C3 TX, 8bit width */ ++ {15, SPI1_DATA_REG, 0x96000000, 0xcbc0, 0}, ++}; ++ ++#endif +diff --git a/drivers/hidmac/hidmac_hi3519.c b/drivers/hidmac/hidmac_hi3519.c +new file mode 100644 +index 0000000..c1f0234 +--- /dev/null ++++ b/drivers/hidmac/hidmac_hi3519.c +@@ -0,0 +1,90 @@ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/i2c.h> ++#include <linux/init.h> ++#include <linux/time.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/pm_runtime.h> ++#include <linux/slab.h> ++#include <linux/io.h> ++ ++#define SYS_CRG_BASE IO_ADDRESS(0x12010000) ++#define REG_PERI_CRG56 0xe0 ++#define DMAC_CLK_EN (1 << 1) ++#define DMAC_SRST_REQ (1 << 0) ++ ++/* ++ * DMA config array! ++ * DREQ, FIFO, CONTROL, CONFIG, BITWIDTH ++ */ ++dmac_peripheral g_peripheral[DMAC_MAX_PERIPHERALS] = { ++ /* periphal 0: UART0 RX, 8bit width */ ++ {0, UART0_DATA_REG, 0x99000000, 0xd000, 0}, ++ ++ /* periphal 1: UART0 TX, 8bit width */ ++ {1, UART0_DATA_REG, 0x96000000, 0xc840, 0}, ++ ++ /*periphal 2: UART1 RX, 8bit width */ ++ {2, UART1_DATA_REG, 0x99000000, 0xd004, 0}, ++ ++ /*periphal 3: UART1 TX, 8bit width */ ++ {3, UART1_DATA_REG, 0x96000000, 0xc8c0, 0}, ++ ++ /*periphal 4: UART2 RX, 8bit width */ ++ {4, UART2_DATA_REG, 0x99000000, 0xd008, 0}, ++ ++ /*periphal 5: UART2 TX, 8bit width */ ++ {5, UART2_DATA_REG, 0x96000000, 0xc940, 0}, ++ ++ /*periphal 6: UART3 RX, 8bit width */ ++ {6, UART3_DATA_REG, 0x99000000, 0xd00c, 0}, ++ ++ /*periphal 7: UART3 TX, 8bit width */ ++ {7, UART3_DATA_REG, 0x96000000, 0xc9c0, 0}, ++ ++ /*periphal 8: I2C0 RX, 8bit width */ ++ {8, I2C0_DATA_REG, 0x99000000, 0xd010, 0}, ++ ++ /*periphal 9: I2C0 TX, 8bit width */ ++ {9, I2C0_DATA_REG, 0x96000000, 0xca40, 0}, ++ ++ /*periphal 10: I2C1 RX, 8bit width */ ++ {10, I2C1_DATA_REG, 0x99000000, 0xd014, 0}, ++ ++ /*periphal 11: I2C1 TX, 8bit width */ ++ {11, I2C1_DATA_REG, 0x96000000, 0xcac0, 0}, ++ ++ /*periphal 12: I2C2 RX, 8bit width */ ++ {12, I2C2_DATA_REG, 0x99000000, 0xd018, 0}, ++ ++ /*periphal 13: I2C2 TX, 8bit width */ ++ {13, I2C2_DATA_REG, 0x96000000, 0xcb40, 0}, ++ ++ /*periphal 14: I2C3 RX, 8bit width */ ++ {14, I2C3_DATA_REG, 0x99000000, 0xd01c, 0}, ++ ++ /*periphal 15: I2C3 TX, 8bit width */ ++ {15, I2C3_DATA_REG, 0x96000000, 0xcbc0, 0}, ++}; ++ ++void hidmac_clk_en(void) ++{ ++ unsigned int tmp; ++ ++ dmac_readw(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++ tmp |= DMAC_CLK_EN; ++ dmac_writew(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++} ++ ++void hidmac_unreset(void) ++{ ++ unsigned int tmp; ++ ++ dmac_readw(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++ tmp &= ~DMAC_SRST_REQ; ++ dmac_writew(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++} +diff --git a/drivers/hidmac/hidmac_hi3519.h b/drivers/hidmac/hidmac_hi3519.h +new file mode 100644 +index 0000000..edd7e0a +--- /dev/null ++++ b/drivers/hidmac/hidmac_hi3519.h +@@ -0,0 +1,99 @@ ++#ifndef __HI_DMAC_HI3519_H__ ++#define __HI_DMAC_HI3519_H__ ++ ++#define DDRAM_ADRS 0x80000000 /* fixed */ ++#define DDRAM_SIZE 0x3FFFFFFF /* 1GB DDR. */ ++ ++#define FLASH_BASE 0x10000000 ++#define FLASH_SIZE 0x04000000 /* (32MB) */ ++ ++#define DMAC_BASE_REG CONFIG_HI_DMAC_IO_BASE ++#define DMAC_INTTCCLEAR IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X08) ++ ++#define DMAC_INTSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X00) ++#define DMAC_INTTCSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X04) ++#define DMAC_INTERRORSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X0C) ++ ++#define DMAC_INTERRCLR IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X10) ++#define DMAC_RAWINTTCSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X14) ++#define DMAC_RAWINTERRORSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X18) ++#define DMAC_ENBLDCHNS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X1C) ++#define DMAC_CONFIG IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X30) ++#define DMAC_SYNC IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X34) ++ ++#define DMAC_MAXTRANSFERSIZE 0x0fff /*the max length is denoted by 0-11bit*/ ++#define MAXTRANSFERSIZE DMAC_MAXTRANSFERSIZE ++#define DMAC_CxDISABLE 0x00 ++#define DMAC_CxENABLE 0x01 ++ ++/*the definition for DMAC channel register*/ ++#define DMAC_CxBASE(i) IO_DMAC_ADDRESS(DMAC_BASE_REG + 0x100+i*0x20) ++#define DMAC_CxSRCADDR(i) DMAC_CxBASE(i) ++#define DMAC_CxDESTADDR(i) (DMAC_CxBASE(i)+0x04) ++#define DMAC_CxLLI(i) (DMAC_CxBASE(i)+0x08) ++#define DMAC_CxCONTROL(i) (DMAC_CxBASE(i)+0x0C) ++#define DMAC_CxCONFIG(i) (DMAC_CxBASE(i)+0x10) ++ ++/*the means the bit in the channel control register*/ ++#define DMAC_CxCONTROL_M2M 0x9d480000 /* Dwidth=32,burst size=4 */ ++#define DMAC_CxCONTROL_LLIM2M 0x0f480000 /* Dwidth=32,burst size=1 */ ++#define DMAC_CxLLI_LM 0x01 ++ ++#define DMAC_CxCONFIG_M2M 0xc000 ++#define DMAC_CxCONFIG_LLIM2M 0xc000 ++ ++/*#define DMAC_CxCONFIG_M2M 0x4001*/ ++#define DMAC_CHANNEL_ENABLE 1 ++#define DMAC_CHANNEL_DISABLE 0xfffffffe ++ ++#define DMAC_CxCONTROL_P2M 0x89409000 ++#define DMAC_CxCONFIG_P2M 0xd000 ++ ++#define DMAC_CxCONTROL_M2P 0x86089000 ++#define DMAC_CxCONFIG_M2P 0xc800 ++ ++#define DMAC_CxCONFIG_SIO_P2M 0x0000d000 ++#define DMAC_CxCONFIG_SIO_M2P 0x0000c800 ++ ++/*default the config and sync regsiter for DMAC controller*/ ++/*M1,M2 little endian, enable DMAC*/ ++#define DMAC_CONFIG_VAL 0x01 ++/*enable the sync logic for the 16 peripheral*/ ++#define DMAC_SYNC_VAL 0x0 ++ ++#define DMAC_MAX_PERIPHERALS 16 ++#define MEM_MAX_NUM 2 ++#define CHANNEL_NUM CONFIG_HI_DMAC_CHANNEL_NUM ++#define DMAC_MAX_CHANNELS CHANNEL_NUM ++ ++#define REG_BASE_UART0 0x12100000 ++#define UART0_DATA_REG (REG_BASE_UART0 + 0x0) ++ ++#define REG_BASE_UART1 0x12101000 ++#define UART1_DATA_REG (REG_BASE_UART1 + 0x0) ++ ++#define REG_BASE_UART2 0x12102000 ++#define UART2_DATA_REG (REG_BASE_UART2 + 0x0) ++ ++#define REG_BASE_UART3 0x12103000 ++#define UART3_DATA_REG (REG_BASE_UART3 + 0x0) ++ ++#define REG_BASE_I2C0 0x12110000 ++#define I2C0_DATA_REG (REG_BASE_I2C0 + 0x10) ++ ++#define REG_BASE_I2C1 0x12111000 ++#define I2C1_DATA_REG (REG_BASE_I2C1 + 0x10) ++ ++#define REG_BASE_I2C2 0x12112000 ++#define I2C2_DATA_REG (REG_BASE_I2C2 + 0x10) ++ ++#define REG_BASE_I2C3 0x12113000 ++#define I2C3_DATA_REG (REG_BASE_I2C3 + 0x10) ++ ++#define REG_BASE_SPI2 0x12122000 ++#define SPI2_DATA_REG (REG_BASE_SPI2 + 0x8) ++ ++/*the transfer control and configuration value for different peripheral*/ ++ ++extern int g_channel_status[CHANNEL_NUM]; ++#endif +diff --git a/drivers/hidmac/hidmac_hi3519v101.c b/drivers/hidmac/hidmac_hi3519v101.c +new file mode 100644 +index 0000000..cf9132a +--- /dev/null ++++ b/drivers/hidmac/hidmac_hi3519v101.c +@@ -0,0 +1,90 @@ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/i2c.h> ++#include <linux/init.h> ++#include <linux/time.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/pm_runtime.h> ++#include <linux/slab.h> ++#include <linux/io.h> ++ ++#define SYS_CRG_BASE IO_ADDRESS(0x12010000) ++#define REG_PERI_CRG56 0xe0 ++#define DMAC_CLK_EN (1 << 1) ++#define DMAC_SRST_REQ (1 << 0) ++ ++/* ++ * DMA config array! ++ * DREQ, FIFO, CONTROL, CONFIG, BITWIDTH ++ */ ++dmac_peripheral g_peripheral[DMAC_MAX_PERIPHERALS] = { ++ /* periphal 0: UART0 RX, 8bit width */ ++ {0, UART0_DATA_REG, 0x99000000, 0xd000, 0}, ++ ++ /* periphal 1: UART0 TX, 8bit width */ ++ {1, UART0_DATA_REG, 0x96000000, 0xc840, 0}, ++ ++ /*periphal 2: UART1 RX, 8bit width */ ++ {2, UART1_DATA_REG, 0x99000000, 0xd004, 0}, ++ ++ /*periphal 3: UART1 TX, 8bit width */ ++ {3, UART1_DATA_REG, 0x96000000, 0xc8c0, 0}, ++ ++ /*periphal 4: UART2 RX, 8bit width */ ++ {4, UART2_DATA_REG, 0x99000000, 0xd008, 0}, ++ ++ /*periphal 5: UART2 TX, 8bit width */ ++ {5, UART2_DATA_REG, 0x96000000, 0xc940, 0}, ++ ++ /*periphal 6: UART3 RX, 8bit width */ ++ {6, UART3_DATA_REG, 0x99000000, 0xd00c, 0}, ++ ++ /*periphal 7: UART3 TX, 8bit width */ ++ {7, UART3_DATA_REG, 0x96000000, 0xc9c0, 0}, ++ ++ /*periphal 8: I2C0 RX, 8bit width */ ++ {8, I2C0_DATA_REG, 0x99000000, 0xd010, 0}, ++ ++ /*periphal 9: I2C0 TX, 8bit width */ ++ {9, I2C0_DATA_REG, 0x96000000, 0xca40, 0}, ++ ++ /*periphal 10: I2C1 RX, 8bit width */ ++ {10, I2C1_DATA_REG, 0x99000000, 0xd014, 0}, ++ ++ /*periphal 11: I2C1 TX, 8bit width */ ++ {11, I2C1_DATA_REG, 0x96000000, 0xcac0, 0}, ++ ++ /*periphal 12: I2C2 RX, 8bit width */ ++ {12, SPI2_DATA_REG, 0x99000000, 0xd018, 0}, ++ ++ /*periphal 13: I2C2 TX, 8bit width */ ++ {13, SPI2_DATA_REG, 0x96000000, 0xcb40, 0}, ++ ++ /*periphal 14: I2C3 RX, 8bit width */ ++ {14, I2C2_DATA_REG, 0x99000000, 0xd01c, 0}, ++ ++ /*periphal 15: I2C3 TX, 8bit width */ ++ {15, I2C2_DATA_REG, 0x96000000, 0xcbc0, 0}, ++}; ++ ++void hidmac_clk_en(void) ++{ ++ unsigned int tmp; ++ ++ dmac_readw(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++ tmp |= DMAC_CLK_EN; ++ dmac_writew(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++} ++ ++void hidmac_unreset(void) ++{ ++ unsigned int tmp; ++ ++ dmac_readw(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++ tmp &= ~DMAC_SRST_REQ; ++ dmac_writew(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++} +diff --git a/drivers/hidmac/hidmac_hi3521d.h b/drivers/hidmac/hidmac_hi3521d.h +new file mode 100644 +index 0000000..67877ff +--- /dev/null ++++ b/drivers/hidmac/hidmac_hi3521d.h +@@ -0,0 +1,120 @@ ++#ifndef __HI_DMAC_HI3521D_H__ ++#define __HI_DMAC_HI3521D_H__ ++ ++#define DDRAM_ADRS 0x80000000 /* fixed */ ++#define DDRAM_SIZE 0x1FFFFFFF /* 512M DDR. */ ++ ++#define FLASH_BASE 0x10000000 ++#define FLASH_SIZE 0x04000000 /* (32MB) */ ++ ++#define DMAC_INTSTATUS 0X00 ++#define DMAC_INTTCSTATUS 0X04 ++#define DMAC_INTTCCLEAR 0X08 ++#define DMAC_INTERRORSTATUS 0X0C ++ ++#define DMAC_INTERRCLR 0X10 ++#define DMAC_RAWINTTCSTATUS 0X14 ++#define DMAC_RAWINTERRORSTATUS 0X18 ++#define DMAC_ENBLDCHNS 0X1C ++#define DMAC_CONFIG 0X30 ++#define DMAC_SYNC 0X34 ++ ++#define DMAC_MAXTRANSFERSIZE 0x0fff /*the max length is denoted by 0-11bit*/ ++#define MAXTRANSFERSIZE DMAC_MAXTRANSFERSIZE ++#define DMAC_CxDISABLE 0x00 ++#define DMAC_CxENABLE 0x01 ++ ++/*the definition for DMAC channel register*/ ++#define DMAC_CxBASE(i) (0x100+i*0x20) ++#define DMAC_CxSRCADDR(i) DMAC_CxBASE(i) ++#define DMAC_CxDESTADDR(i) (DMAC_CxBASE(i)+0x04) ++#define DMAC_CxLLI(i) (DMAC_CxBASE(i)+0x08) ++#define DMAC_CxCONTROL(i) (DMAC_CxBASE(i)+0x0C) ++#define DMAC_CxCONFIG(i) (DMAC_CxBASE(i)+0x10) ++ ++/*the means the bit in the channel control register*/ ++#define DMAC_CxCONTROL_M2M 0x9d480000 /* Dwidth=32,burst size=4 */ ++#define DMAC_CxCONTROL_LLIM2M 0x0f480000 /* Dwidth=32,burst size=1 */ ++#define DMAC_CxCONTROL_LLIM2M_ISP 0x0b489000 /* Dwidth=32,burst size=1 */ ++#define DMAC_CxLLI_LM 0x01 ++ ++#define NUM_HAL_INTERRUPT_DMAC (14 + 16) ++ ++#define DMAC_CxCONFIG_M2M 0xc000 ++#define DMAC_CxCONFIG_LLIM2M 0xc000 ++ ++/*#define DMAC_CxCONFIG_M2M 0x4001*/ ++#define DMAC_CHANNEL_ENABLE 1 ++#define DMAC_CHANNEL_DISABLE 0xfffffffe ++ ++#define DMAC_CxCONTROL_P2M 0x89409000 ++#define DMAC_CxCONFIG_P2M 0xd000 ++ ++#define DMAC_CxCONTROL_M2P 0x86089000 ++#define DMAC_CxCONFIG_M2P 0xc800 ++ ++#define DMAC_CxCONFIG_SIO_P2M 0x0000d000 ++#define DMAC_CxCONFIG_SIO_M2P 0x0000c800 ++ ++/*default the config and sync regsiter for DMAC controller*/ ++/*M1,M2 little endian, enable DMAC*/ ++#define DMAC_CONFIG_VAL 0x01 ++/*enable the sync logic for the 16 peripheral*/ ++#define DMAC_SYNC_VAL 0x0 ++ ++#define DMAC_MAX_PERIPHERALS 16 ++#define MEM_MAX_NUM 2 ++#define CHANNEL_NUM CONFIG_HI_DMAC_CHANNEL_NUM ++#define DMAC_MAX_CHANNELS CHANNEL_NUM ++ ++#define REG_BASE_I2C0 0x120c0000 ++#define I2C0_DATA_RXF (REG_BASE_I2C0 + 0x10) ++#define I2C0_DATA_TXF (REG_BASE_I2C0 + 0x10) ++ ++ ++#define REG_BASE_UART0 0x12080000 ++#define UART0_DATA_REG (REG_BASE_UART0 + 0x0) ++ ++#define REG_BASE_UART1 0x12090000 ++#define UART1_DATA_REG (REG_BASE_UART1 + 0x0) ++ ++#define REG_BASE_UART2 0x120a0000 ++#define UART2_DATA_REG (REG_BASE_UART2 + 0x0) ++ ++/*the transfer control and configuration value for different peripheral*/ ++ ++extern int g_channel_status[CHANNEL_NUM]; ++ ++dmac_peripheral g_peripheral[DMAC_MAX_PERIPHERALS] = { ++ /*periphal 0: UART0 RX, 8bit width */ ++ {0, UART0_DATA_REG, 0x99000000, 0xd000, 0}, ++ ++ /*periphal 1: UART0 TX, 8bit width */ ++ {1, UART0_DATA_REG, 0x96000000, 0xc840, 0}, ++ ++ /*periphal 2: UART1 RX, 8bit width */ ++ {2, UART1_DATA_REG, 0x99000000, 0xd004, 0}, ++ ++ /*periphal 3: UART1 TX, 8bit width */ ++ {3, UART1_DATA_REG, 0x96000000, 0xc8c0, 0}, ++ ++ /*periphal 4: UART2 RX, 8bit width */ ++ {4, UART2_DATA_REG, 0x99000000, 0xd008, 0}, ++ ++ /*periphal 5: UART2 TX, 8bit width */ ++ {5, UART2_DATA_REG, 0x96000000, 0xc940, 0}, ++ ++ /*periphal 6: ssp RX, 8bit width */ ++ {6, 0, 0x99000000, 0xd00c, 0}, ++ ++ /*periphal 7: ssp TX, 8bit width */ ++ {7, 0, 0x96000000, 0xc9c0, 0}, ++ ++ /*periphal 8: I2C0 RX, 8bit width */ ++ {8, I2C0_DATA_RXF, 0x99000000, 0x1010, 0}, ++ ++ /*periphal 9: I2C0 TX, 8bit width */ ++ {9, I2C0_DATA_TXF, 0x96000000, 0x0a40, 0}, ++}; ++ ++#endif +diff --git a/drivers/hidmac/hidmac_hi3531d.h b/drivers/hidmac/hidmac_hi3531d.h +new file mode 100644 +index 0000000..91d00b0 +--- /dev/null ++++ b/drivers/hidmac/hidmac_hi3531d.h +@@ -0,0 +1,135 @@ ++#ifndef __HI_DMAC_HI3531D_H__ ++#define __HI_DMAC_HI3531D_H__ ++ ++#define DDRAM_ADRS 0x80000000 /* fixed */ ++#define DDRAM_SIZE 0x1FFFFFFF /* 512M DDR. */ ++ ++#define FLASH_BASE 0x10000000 ++#define FLASH_SIZE 0x04000000 /* (32MB) */ ++ ++#define DMAC_INTSTATUS 0X00 ++#define DMAC_INTTCSTATUS 0X04 ++#define DMAC_INTTCCLEAR 0X08 ++#define DMAC_INTERRORSTATUS 0X0C ++ ++#define DMAC_INTERRCLR 0X10 ++#define DMAC_RAWINTTCSTATUS 0X14 ++#define DMAC_RAWINTERRORSTATUS 0X18 ++#define DMAC_ENBLDCHNS 0X1C ++#define DMAC_CONFIG 0X30 ++#define DMAC_SYNC 0X34 ++ ++#define DMAC_MAXTRANSFERSIZE 0x0fff /*the max length is denoted by 0-11bit*/ ++#define MAXTRANSFERSIZE DMAC_MAXTRANSFERSIZE ++#define DMAC_CxDISABLE 0x00 ++#define DMAC_CxENABLE 0x01 ++ ++/*the definition for DMAC channel register*/ ++#define DMAC_CxBASE(i) (0x100+i*0x20) ++#define DMAC_CxSRCADDR(i) DMAC_CxBASE(i) ++#define DMAC_CxDESTADDR(i) (DMAC_CxBASE(i)+0x04) ++#define DMAC_CxLLI(i) (DMAC_CxBASE(i)+0x08) ++#define DMAC_CxCONTROL(i) (DMAC_CxBASE(i)+0x0C) ++#define DMAC_CxCONFIG(i) (DMAC_CxBASE(i)+0x10) ++ ++/*the means the bit in the channel control register*/ ++#define DMAC_CxCONTROL_M2M 0x9d480000 /* Dwidth=32,burst size=4 */ ++#define DMAC_CxCONTROL_LLIM2M 0x0f480000 /* Dwidth=32,burst size=1 */ ++#define DMAC_CxCONTROL_LLIM2M_ISP 0x0b489000 /* Dwidth=32,burst size=1 */ ++#define DMAC_CxLLI_LM 0x01 ++ ++#define NUM_HAL_INTERRUPT_DMAC (14 + 16) ++ ++#define DMAC_CxCONFIG_M2M 0xc000 ++#define DMAC_CxCONFIG_LLIM2M 0xc000 ++ ++/*#define DMAC_CxCONFIG_M2M 0x4001*/ ++#define DMAC_CHANNEL_ENABLE 1 ++#define DMAC_CHANNEL_DISABLE 0xfffffffe ++ ++#define DMAC_CxCONTROL_P2M 0x89409000 ++#define DMAC_CxCONFIG_P2M 0xd000 ++ ++#define DMAC_CxCONTROL_M2P 0x86089000 ++#define DMAC_CxCONFIG_M2P 0xc800 ++ ++#define DMAC_CxCONFIG_SIO_P2M 0x0000d000 ++#define DMAC_CxCONFIG_SIO_M2P 0x0000c800 ++ ++/*default the config and sync regsiter for DMAC controller*/ ++/*M1,M2 little endian, enable DMAC*/ ++#define DMAC_CONFIG_VAL 0x01 ++/*enable the sync logic for the 16 peripheral*/ ++#define DMAC_SYNC_VAL 0x0 ++ ++#define DMAC_MAX_PERIPHERALS 16 ++#define MEM_MAX_NUM 2 ++#define CHANNEL_NUM CONFIG_HI_DMAC_CHANNEL_NUM ++#define DMAC_MAX_CHANNELS CHANNEL_NUM ++ ++#define REG_BASE_I2C0 0x120c0000 ++#define I2C0_DATA_RXF (REG_BASE_I2C0 + 0x10) ++#define I2C0_DATA_TXF (REG_BASE_I2C0 + 0x10) ++ ++#define REG_BASE_I2C1 0x120e0000 ++#define I2C1_DATA_RXF (REG_BASE_I2C1 + 0x10) ++#define I2C1_DATA_TXF (REG_BASE_I2C1 + 0x10) ++ ++#define REG_BASE_UART0 0x12080000 ++#define UART0_DATA_REG (REG_BASE_UART0 + 0x0) ++ ++#define REG_BASE_UART1 0x12090000 ++#define UART1_DATA_REG (REG_BASE_UART1 + 0x0) ++ ++#define REG_BASE_UART2 0x120a0000 ++#define UART2_DATA_REG (REG_BASE_UART2 + 0x0) ++ ++/*the transfer control and configuration value for different peripheral*/ ++ ++extern int g_channel_status[CHANNEL_NUM]; ++ ++dmac_peripheral g_peripheral[DMAC_MAX_PERIPHERALS] = { ++ /*periphal 0: UART0 RX, 8bit width */ ++ {0, UART0_DATA_REG, 0x99000000, 0xd000, 0}, ++ ++ /*periphal 1: UART0 TX, 8bit width */ ++ {1, UART0_DATA_REG, 0x96000000, 0xc840, 0}, ++ ++ /*periphal 2: UART1 RX, 8bit width */ ++ {2, UART1_DATA_REG, 0x99000000, 0xd004, 0}, ++ ++ /*periphal 3: UART1 TX, 8bit width */ ++ {3, UART1_DATA_REG, 0x96000000, 0xc8c0, 0}, ++ ++ /*periphal 4: UART2 RX, 8bit width */ ++ {4, UART2_DATA_REG, 0x99000000, 0xd008, 0}, ++ ++ /*periphal 5: UART2 TX, 8bit width */ ++ {5, UART2_DATA_REG, 0x96000000, 0xc940, 0}, ++ ++ /*periphal 6: SSP RX, 8bit width */ ++ {6, 0, 0x99000000, 0xd00c, 0}, ++ ++ /*periphal 7: SSP TX, 8bit width */ ++ {7, 0, 0x96000000, 0xc9c0, 0}, ++ ++ /*periphal 8: I2C0 RX, 8bit width */ ++ {8, I2C0_DATA_RXF, 0x99000000, 0x1010, 0}, ++ ++ /*periphal 9: I2C0 TX, 8bit width */ ++ {9, I2C0_DATA_TXF, 0x96000000, 0x0a40, 0}, ++ ++ /*periphal 10: UART3 RX, 8bit width */ ++ {10, 0, 0x99000000, 0xd014, 0}, ++ ++ /*periphal 11: UART3 TX, 8bit width */ ++ {11, 0, 0x96000000, 0xcb00, 0}, ++ ++ /*periphal 12: I2C1 RX, 8bit width */ ++ {12, I2C1_DATA_RXF, 0x99000000, 0x1018, 0}, ++ ++ /*periphal 13: I2C1 TX, 8bit width */ ++ {13, I2C1_DATA_TXF, 0x96000000, 0x0b40, 0}, ++}; ++ ++#endif +diff --git a/drivers/hidmac/hidmac_hi3536c.h b/drivers/hidmac/hidmac_hi3536c.h +new file mode 100644 +index 0000000..b34bdf2 +--- /dev/null ++++ b/drivers/hidmac/hidmac_hi3536c.h +@@ -0,0 +1,121 @@ ++#ifndef __HI_DMAC_HI3536C_H__ ++#define __HI_DMAC_HI3536C_H__ ++ ++#define DDRAM_ADRS 0x80000000 /* fixed */ ++#define DDRAM_SIZE 0x1FFFFFFF /* 512M DDR. */ ++ ++#define FLASH_BASE 0x10000000 ++#define FLASH_SIZE 0x04000000 /* (32MB) */ ++ ++#define DMAC_INTSTATUS 0X00 ++#define DMAC_INTTCSTATUS 0X04 ++#define DMAC_INTTCCLEAR 0X08 ++#define DMAC_INTERRORSTATUS 0X0C ++ ++#define DMAC_INTERRCLR 0X10 ++#define DMAC_RAWINTTCSTATUS 0X14 ++#define DMAC_RAWINTERRORSTATUS 0X18 ++#define DMAC_ENBLDCHNS 0X1C ++#define DMAC_CONFIG 0X30 ++#define DMAC_SYNC 0X34 ++ ++#define DMAC_MAXTRANSFERSIZE 0x0fff /*the max length is denoted by 0-11bit*/ ++#define MAXTRANSFERSIZE DMAC_MAXTRANSFERSIZE ++#define DMAC_CxDISABLE 0x00 ++#define DMAC_CxENABLE 0x01 ++ ++/*the definition for DMAC channel register*/ ++#define DMAC_CxBASE(i) (0x100+i*0x20) ++#define DMAC_CxSRCADDR(i) DMAC_CxBASE(i) ++#define DMAC_CxDESTADDR(i) (DMAC_CxBASE(i)+0x04) ++#define DMAC_CxLLI(i) (DMAC_CxBASE(i)+0x08) ++#define DMAC_CxCONTROL(i) (DMAC_CxBASE(i)+0x0C) ++#define DMAC_CxCONFIG(i) (DMAC_CxBASE(i)+0x10) ++ ++/*the means the bit in the channel control register*/ ++#define DMAC_CxCONTROL_M2M 0x9d480000 /* Dwidth=32,burst size=4 */ ++#define DMAC_CxCONTROL_LLIM2M 0x0f480000 /* Dwidth=32,burst size=1 */ ++#define DMAC_CxCONTROL_LLIM2M_ISP 0x0b489000 /* Dwidth=32,burst size=1 */ ++#define DMAC_CxLLI_LM 0x01 ++ ++#define NUM_HAL_INTERRUPT_DMAC (14 + 16) ++ ++#define DMAC_CxCONFIG_M2M 0xc000 ++#define DMAC_CxCONFIG_LLIM2M 0xc000 ++ ++/*#define DMAC_CxCONFIG_M2M 0x4001*/ ++#define DMAC_CHANNEL_ENABLE 1 ++#define DMAC_CHANNEL_DISABLE 0xfffffffe ++ ++#define DMAC_CxCONTROL_P2M 0x89409000 ++#define DMAC_CxCONFIG_P2M 0xd000 ++ ++#define DMAC_CxCONTROL_M2P 0x86089000 ++#define DMAC_CxCONFIG_M2P 0xc800 ++ ++#define DMAC_CxCONFIG_SIO_P2M 0x0000d000 ++#define DMAC_CxCONFIG_SIO_M2P 0x0000c800 ++ ++/*default the config and sync regsiter for DMAC controller*/ ++/*M1,M2 little endian, enable DMAC*/ ++#define DMAC_CONFIG_VAL 0x01 ++/*enable the sync logic for the 16 peripheral*/ ++#define DMAC_SYNC_VAL 0x0 ++ ++#define DMAC_MAX_PERIPHERALS 16 ++#define MEM_MAX_NUM 2 ++#define CHANNEL_NUM CONFIG_HI_DMAC_CHANNEL_NUM ++#define DMAC_MAX_CHANNELS CHANNEL_NUM ++ ++#define REG_BASE_I2C0 0x120c0000 ++#define I2C0_DATA_RXF (REG_BASE_I2C0 + 0x10) ++#define I2C0_DATA_TXF (REG_BASE_I2C0 + 0x10) ++ ++ ++#define REG_BASE_UART0 0x12080000 ++#define UART0_DATA_REG (REG_BASE_UART0 + 0x0) ++ ++#define REG_BASE_UART1 0x12090000 ++#define UART1_DATA_REG (REG_BASE_UART1 + 0x0) ++ ++#define REG_BASE_UART2 0x120a0000 ++#define UART2_DATA_REG (REG_BASE_UART2 + 0x0) ++ ++/*the transfer control and configuration value for different peripheral*/ ++ ++extern int g_channel_status[CHANNEL_NUM]; ++ ++dmac_peripheral g_peripheral[DMAC_MAX_PERIPHERALS] = { ++ /*periphal 0: UART0 RX, 8bit width */ ++ {0, UART0_DATA_REG, 0x99000000, 0xd000, 0}, ++ ++ /*periphal 1: UART0 TX, 8bit width */ ++ {1, UART0_DATA_REG, 0x96000000, 0xc840, 0}, ++ ++ /*periphal 2: UART1 RX, 8bit width */ ++ {2, UART1_DATA_REG, 0x99000000, 0xd004, 0}, ++ ++ /*periphal 3: UART1 TX, 8bit width */ ++ {3, UART1_DATA_REG, 0x96000000, 0xc8c0, 0}, ++ ++ /*periphal 4: UART2 RX, 8bit width */ ++ {4, UART2_DATA_REG, 0x99000000, 0xd008, 0}, ++ ++ /*periphal 5: UART2 TX, 8bit width */ ++ {5, UART2_DATA_REG, 0x96000000, 0xc940, 0}, ++ ++ /*periphal 6: SSP RX, 8bit width */ ++ {6, 0, 0x99000000, 0xd00c, 0}, ++ ++ /*periphal 7: SSP TX, 8bit width */ ++ {7, 0, 0x96000000, 0xc9c0, 0}, ++ ++ /*periphal 8: I2C0 RX, 8bit width */ ++ {8, I2C0_DATA_RXF, 0x99000000, 0x1010, 0}, ++ ++ /*periphal 9: I2C0 TX, 8bit width */ ++ {9, I2C0_DATA_TXF, 0x96000000, 0x0a40, 0}, ++ ++}; ++ ++#endif +diff --git a/drivers/hidmac/hidmac_hi3559.c b/drivers/hidmac/hidmac_hi3559.c +new file mode 100644 +index 0000000..3d8df63 +--- /dev/null ++++ b/drivers/hidmac/hidmac_hi3559.c +@@ -0,0 +1,90 @@ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/i2c.h> ++#include <linux/init.h> ++#include <linux/time.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/pm_runtime.h> ++#include <linux/slab.h> ++#include <linux/io.h> ++ ++#define SYS_CRG_BASE IO_ADDRESS(0x12010000) ++#define REG_PERI_CRG56 0xe0 ++#define DMAC_CLK_EN (1 << 1) ++#define DMAC_SRST_REQ (1 << 0) ++ ++/* ++ * DMA config array! ++ * DREQ, FIFO, CONTROL, CONFIG, BITWIDTH ++ */ ++dmac_peripheral g_peripheral[DMAC_MAX_PERIPHERALS] = { ++ /* periphal 0: UART0 RX, 8bit width */ ++ {0, UART0_DATA_REG, 0x99000000, 0xd000, 0}, ++ ++ /* periphal 1: UART0 TX, 8bit width */ ++ {1, UART0_DATA_REG, 0x96000000, 0xc840, 0}, ++ ++ /*periphal 2: UART1 RX, 8bit width */ ++ {2, UART1_DATA_REG, 0x99000000, 0xd004, 0}, ++ ++ /*periphal 3: UART1 TX, 8bit width */ ++ {3, UART1_DATA_REG, 0x96000000, 0xc8c0, 0}, ++ ++ /*periphal 4: UART2 RX, 8bit width */ ++ {4, UART2_DATA_REG, 0x99000000, 0xd008, 0}, ++ ++ /*periphal 5: UART2 TX, 8bit width */ ++ {5, UART2_DATA_REG, 0x96000000, 0xc940, 0}, ++ ++ /*periphal 6: I2C3 RX, 8bit width */ ++ {6, I2C3_DATA_REG, 0x99000000, 0xd00c, 0}, ++ ++ /*periphal 7: I2C3 TX, 8bit width */ ++ {7, I2C3_DATA_REG, 0x96000000, 0xc9c0, 0}, ++ ++ /*periphal 8: I2C0 RX, 8bit width */ ++ {8, I2C0_DATA_REG, 0x99000000, 0xd010, 0}, ++ ++ /*periphal 9: I2C0 TX, 8bit width */ ++ {9, I2C0_DATA_REG, 0x96000000, 0xca40, 0}, ++ ++ /*periphal 10: I2C1 RX, 8bit width */ ++ {10, I2C1_DATA_REG, 0x99000000, 0xd014, 0}, ++ ++ /*periphal 11: I2C1 TX, 8bit width */ ++ {11, I2C1_DATA_REG, 0x96000000, 0xcac0, 0}, ++ ++ /*periphal 12: SPI2 RX, 8bit width */ ++ {12, SPI2_DATA_REG, 0x99000000, 0xd018, 0}, ++ ++ /*periphal 13: SPI2 TX, 8bit width */ ++ {13, SPI2_DATA_REG, 0x96000000, 0xcb40, 0}, ++ ++ /*periphal 14: I2C2 RX, 8bit width */ ++ {14, I2C2_DATA_REG, 0x99000000, 0xd01c, 0}, ++ ++ /*periphal 15: I2C2 TX, 8bit width */ ++ {15, I2C2_DATA_REG, 0x96000000, 0xcbc0, 0}, ++}; ++ ++void hidmac_clk_en(void) ++{ ++ unsigned int tmp; ++ ++ dmac_readw(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++ tmp |= DMAC_CLK_EN; ++ dmac_writew(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++} ++ ++void hidmac_unreset(void) ++{ ++ unsigned int tmp; ++ ++ dmac_readw(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++ tmp &= ~DMAC_SRST_REQ; ++ dmac_writew(SYS_CRG_BASE + REG_PERI_CRG56, tmp); ++} +diff --git a/drivers/hidmac/hidmac_hi3559.h b/drivers/hidmac/hidmac_hi3559.h +new file mode 100644 +index 0000000..cbf943e +--- /dev/null ++++ b/drivers/hidmac/hidmac_hi3559.h +@@ -0,0 +1,99 @@ ++#ifndef __HI_DMAC_HI3559_H__ ++#define __HI_DMAC_HI3559_H__ ++ ++#define DDRAM_ADRS 0x80000000 /* fixed */ ++#define DDRAM_SIZE 0x3FFFFFFF /* 1GB DDR. */ ++ ++#define FLASH_BASE 0x10000000 ++#define FLASH_SIZE 0x04000000 /* (32MB) */ ++ ++#define DMAC_BASE_REG CONFIG_HI_DMAC_IO_BASE ++#define DMAC_INTTCCLEAR IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X08) ++ ++#define DMAC_INTSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X00) ++#define DMAC_INTTCSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X04) ++#define DMAC_INTERRORSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X0C) ++ ++#define DMAC_INTERRCLR IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X10) ++#define DMAC_RAWINTTCSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X14) ++#define DMAC_RAWINTERRORSTATUS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X18) ++#define DMAC_ENBLDCHNS IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X1C) ++#define DMAC_CONFIG IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X30) ++#define DMAC_SYNC IO_DMAC_ADDRESS(DMAC_BASE_REG + 0X34) ++ ++#define DMAC_MAXTRANSFERSIZE 0x0fff /*the max length is denoted by 0-11bit*/ ++#define MAXTRANSFERSIZE DMAC_MAXTRANSFERSIZE ++#define DMAC_CxDISABLE 0x00 ++#define DMAC_CxENABLE 0x01 ++ ++/*the definition for DMAC channel register*/ ++#define DMAC_CxBASE(i) IO_DMAC_ADDRESS(DMAC_BASE_REG + 0x100+i*0x20) ++#define DMAC_CxSRCADDR(i) DMAC_CxBASE(i) ++#define DMAC_CxDESTADDR(i) (DMAC_CxBASE(i)+0x04) ++#define DMAC_CxLLI(i) (DMAC_CxBASE(i)+0x08) ++#define DMAC_CxCONTROL(i) (DMAC_CxBASE(i)+0x0C) ++#define DMAC_CxCONFIG(i) (DMAC_CxBASE(i)+0x10) ++ ++/*the means the bit in the channel control register*/ ++#define DMAC_CxCONTROL_M2M 0x9d480000 /* Dwidth=32,burst size=4 */ ++#define DMAC_CxCONTROL_LLIM2M 0x0f480000 /* Dwidth=32,burst size=1 */ ++#define DMAC_CxLLI_LM 0x01 ++ ++#define DMAC_CxCONFIG_M2M 0xc000 ++#define DMAC_CxCONFIG_LLIM2M 0xc000 ++ ++/*#define DMAC_CxCONFIG_M2M 0x4001*/ ++#define DMAC_CHANNEL_ENABLE 1 ++#define DMAC_CHANNEL_DISABLE 0xfffffffe ++ ++#define DMAC_CxCONTROL_P2M 0x89409000 ++#define DMAC_CxCONFIG_P2M 0xd000 ++ ++#define DMAC_CxCONTROL_M2P 0x86089000 ++#define DMAC_CxCONFIG_M2P 0xc800 ++ ++#define DMAC_CxCONFIG_SIO_P2M 0x0000d000 ++#define DMAC_CxCONFIG_SIO_M2P 0x0000c800 ++ ++/*default the config and sync regsiter for DMAC controller*/ ++/*M1,M2 little endian, enable DMAC*/ ++#define DMAC_CONFIG_VAL 0x01 ++/*enable the sync logic for the 16 peripheral*/ ++#define DMAC_SYNC_VAL 0x0 ++ ++#define DMAC_MAX_PERIPHERALS 16 ++#define MEM_MAX_NUM 2 ++#define CHANNEL_NUM CONFIG_HI_DMAC_CHANNEL_NUM ++#define DMAC_MAX_CHANNELS CHANNEL_NUM ++ ++#define REG_BASE_UART0 0x12100000 ++#define UART0_DATA_REG (REG_BASE_UART0 + 0x0) ++ ++#define REG_BASE_UART1 0x12101000 ++#define UART1_DATA_REG (REG_BASE_UART1 + 0x0) ++ ++#define REG_BASE_UART2 0x12102000 ++#define UART2_DATA_REG (REG_BASE_UART2 + 0x0) ++ ++#define REG_BASE_UART3 0x12103000 ++#define UART3_DATA_REG (REG_BASE_UART3 + 0x0) ++ ++#define REG_BASE_I2C0 0x12110000 ++#define I2C0_DATA_REG (REG_BASE_I2C0 + 0x10) ++ ++#define REG_BASE_I2C1 0x12111000 ++#define I2C1_DATA_REG (REG_BASE_I2C1 + 0x10) ++ ++#define REG_BASE_I2C2 0x12112000 ++#define I2C2_DATA_REG (REG_BASE_I2C2 + 0x10) ++ ++#define REG_BASE_I2C3 0x12113000 ++#define I2C3_DATA_REG (REG_BASE_I2C3 + 0x10) ++ ++#define REG_BASE_SPI2 0x12122000 ++#define SPI2_DATA_REG (REG_BASE_SPI2 + 0x8) ++ ++/*the transfer control and configuration value for different peripheral*/ ++ ++extern int g_channel_status[CHANNEL_NUM]; ++#endif +diff --git a/drivers/hisilicon/Kconfig b/drivers/hisilicon/Kconfig +new file mode 100644 +index 0000000..df2670b +--- /dev/null ++++ b/drivers/hisilicon/Kconfig +@@ -0,0 +1,5 @@ ++menu "Hisilicon driver support" ++ ++source "drivers/hisilicon/cma/Kconfig" ++ ++endmenu +diff --git a/drivers/hisilicon/Makefile b/drivers/hisilicon/Makefile +new file mode 100644 +index 0000000..12004b2 +--- /dev/null ++++ b/drivers/hisilicon/Makefile +@@ -0,0 +1,3 @@ ++ ++obj-y += clocksource/ ++obj-$(CONFIG_CMA) += cma/ +diff --git a/drivers/hisilicon/clocksource/Makefile b/drivers/hisilicon/clocksource/Makefile +new file mode 100644 +index 0000000..2c6bfe5 +--- /dev/null ++++ b/drivers/hisilicon/clocksource/Makefile +@@ -0,0 +1,4 @@ ++#TODO: ARM64 Timer ++ccflags-y += -Iarch/arm/include ++ ++obj-y += timer.o hrtimer_test.o +diff --git a/drivers/hisilicon/clocksource/hrtimer_test.c b/drivers/hisilicon/clocksource/hrtimer_test.c +new file mode 100644 +index 0000000..645b0cf +--- /dev/null ++++ b/drivers/hisilicon/clocksource/hrtimer_test.c +@@ -0,0 +1,175 @@ ++/* ++ * hrtimer() test kernel module ++ * ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/debugfs.h> ++#include <linux/delay.h> ++#include <linux/ktime.h> ++#include <linux/module.h> ++#include <linux/uaccess.h> ++#include <linux/sched.h> ++#include <linux/hrtimer.h> ++ ++#define DEFAULT_ITERATIONS 100 ++ ++#define DEBUGFS_FILENAME "hrtimer_test" ++ ++ ++static DEFINE_MUTEX(hrtimer_test_lock); ++static struct dentry *hrtimer_test_debugfs_file; ++static int hrtimer_test_usecs; ++static int hrtimer_test_iterations = DEFAULT_ITERATIONS; ++ ++static int hrtimer_test_single(struct seq_file *s, int usecs, uint32_t iters) ++{ ++ int min = 0, max = 0, fail_count = 0; ++ uint64_t sum = 0; ++ uint64_t avg; ++ int i; ++ /* Allow hrtimer to be up to 0.5% fast */ ++ int allowed_error_ns = usecs * 5; ++ ktime_t waittime; ++ ++ for (i = 0; i < iters; ++i) { ++ struct timespec ts1, ts2; ++ int time_passed; ++ struct sched_param param = { .sched_priority = 99 }; ++ sched_setscheduler(current, SCHED_FIFO, ¶m); ++ waittime = ns_to_ktime(usecs * 1000); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ ktime_get_ts(&ts1); ++ schedule_hrtimeout(&waittime, HRTIMER_MODE_REL); ++ ktime_get_ts(&ts2); ++ time_passed = timespec_to_ns(&ts2) - timespec_to_ns(&ts1); ++ ++ if (i == 0 || time_passed < min) ++ min = time_passed; ++ if (i == 0 || time_passed > max) ++ max = time_passed; ++ if ((time_passed + allowed_error_ns) / 1000 < usecs) ++ ++fail_count; ++ WARN_ON(time_passed < 0); ++ sum += time_passed; ++ } ++ ++ avg = sum; ++ do_div(avg, iters); ++ seq_printf(s, "%d usecs x %d: exp=%d allowed=%d min=%d avg=%lld max=%d", ++ usecs, iters, usecs * 1000, ++ (usecs * 1000) - allowed_error_ns, min, avg, max); ++ if (fail_count) ++ seq_printf(s, " FAIL=%d", fail_count); ++ seq_puts(s, "\n"); ++ ++ return 0; ++} ++ ++static int hrtimer_test_show(struct seq_file *s, void *v) ++{ ++ int usecs; ++ int iters; ++ int ret = 0; ++ ++ mutex_lock(&hrtimer_test_lock); ++ usecs = hrtimer_test_usecs; ++ iters = hrtimer_test_iterations; ++ mutex_unlock(&hrtimer_test_lock); ++ ++ if (usecs > 0 && iters > 0) { ++ return hrtimer_test_single(s, usecs, iters); ++ } else if (usecs == 0) { ++ struct timespec ts; ++ ++ ktime_get_ts(&ts); ++ seq_printf(s, "hrtimer() test (lpj=%ld kt=%ld.%09ld)\n", ++ loops_per_jiffy, ts.tv_sec, ts.tv_nsec); ++ seq_puts(s, "usage:\n"); ++ seq_puts(s, "echo usecs [ITERS] > " DEBUGFS_FILENAME "\n"); ++ seq_puts(s, "cat " DEBUGFS_FILENAME "\n"); ++ } ++ ++ return ret; ++} ++ ++static int hrtimer_test_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, hrtimer_test_show, inode->i_private); ++} ++ ++static ssize_t hrtimer_test_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *pos) ++{ ++ char lbuf[32]; ++ int ret; ++ int usecs; ++ int iters; ++ ++ if (count >= sizeof(lbuf)) ++ return -EINVAL; ++ ++ if (copy_from_user(lbuf, buf, count)) ++ return -EFAULT; ++ lbuf[count] = '\0'; ++ ++ ret = sscanf(lbuf, "%d %d", &usecs, &iters); ++ if (ret < 1) ++ return -EINVAL; ++ else if (ret < 2) ++ iters = DEFAULT_ITERATIONS; ++ ++ mutex_lock(&hrtimer_test_lock); ++ hrtimer_test_usecs = usecs; ++ hrtimer_test_iterations = iters; ++ mutex_unlock(&hrtimer_test_lock); ++ ++ return count; ++} ++ ++static const struct file_operations hrtimer_test_debugfs_ops = { ++ .owner = THIS_MODULE, ++ .open = hrtimer_test_open, ++ .read = seq_read, ++ .write = hrtimer_test_write, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int __init hrtimer_test_init(void) ++{ ++ mutex_lock(&hrtimer_test_lock); ++ hrtimer_test_debugfs_file = debugfs_create_file(DEBUGFS_FILENAME, ++ S_IRUSR, NULL, NULL, &hrtimer_test_debugfs_ops); ++ mutex_unlock(&hrtimer_test_lock); ++ ++ return 0; ++} ++ ++module_init(hrtimer_test_init); ++ ++static void __exit hrtimer_test_exit(void) ++{ ++ mutex_lock(&hrtimer_test_lock); ++ debugfs_remove(hrtimer_test_debugfs_file); ++ mutex_unlock(&hrtimer_test_lock); ++} ++ ++module_exit(hrtimer_test_exit); ++ ++MODULE_AUTHOR("David Riley <davidriley@chromium.org>"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/hisilicon/clocksource/timer.c b/drivers/hisilicon/clocksource/timer.c +new file mode 100644 +index 0000000..53106c1 +--- /dev/null ++++ b/drivers/hisilicon/clocksource/timer.c +@@ -0,0 +1,356 @@ ++/* ++ * ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include <linux/sched.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/err.h> ++#include <linux/clk.h> ++#include <linux/clockchips.h> ++#include <linux/cpu.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/percpu.h> ++#include <linux/clkdev.h> ++#include <linux/clk-private.h> ++#include <linux/sched_clock.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/of_irq.h> ++#include <asm/hardware/arm_timer.h> ++ ++/*****************************************************************************/ ++//TODO: ARM64 Timer ++#ifdef CONFIG_ARM64 ++#define IOMEM(x) ((void __force __iomem *)(x)) ++#endif ++ ++struct hisi_clocksource { ++ void __iomem *base; ++ struct clocksource clksrc; ++}; ++ ++struct hisi_clock_event_device { ++ struct clock_event_device evt; ++ void __iomem *base; ++ char name[10]; ++}; ++ ++static struct hisi_clocksource hisi_clocksource = {0}; ++static struct hisi_clock_event_device __percpu *hisi_local_timer_evt; ++static struct irqaction __percpu *hisi_event_irq; ++static void __iomem *hisi_timer_base[NR_CPUS] = {0}; ++static int hisi_timer_irqs[NR_CPUS] = {0}; ++static unsigned long hisi_clkevt_reload; ++static unsigned long hisi_clk_rate; ++/*****************************************************************************/ ++ ++static int hisi_set_next_event(unsigned long next, ++ struct clock_event_device *evt) ++{ ++ unsigned long ctrl; ++ struct hisi_clock_event_device *hisi_evt; ++ ++ hisi_evt = container_of(evt, struct hisi_clock_event_device, evt); ++ ctrl = readl(hisi_evt->base + TIMER_CTRL); ++ writel(next, hisi_evt->base + TIMER_LOAD); ++ writel(ctrl | TIMER_CTRL_ENABLE, hisi_evt->base + TIMER_CTRL); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static void hisi_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE; ++ struct hisi_clock_event_device *hisi_evt; ++ ++ hisi_evt = container_of(evt, struct hisi_clock_event_device, evt); ++ ++ writel(ctrl, hisi_evt->base + TIMER_CTRL); ++ ++ switch (mode) { ++ case CLOCK_EVT_MODE_PERIODIC: ++ writel(hisi_clkevt_reload, hisi_evt->base + TIMER_LOAD); ++ ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; ++ break; ++ ++ case CLOCK_EVT_MODE_ONESHOT: ++ /* period set, and timer enabled in 'next_event' hook */ ++ ctrl |= TIMER_CTRL_ONESHOT; ++ break; ++ ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ default: ++ break; ++ } ++ ++ writel(ctrl, hisi_evt->base + TIMER_CTRL); ++} ++/*****************************************************************************/ ++ ++static irqreturn_t hisi_evt_isr(int irq, void *dev_id) ++{ ++ struct hisi_clock_event_device *hisi_evt = dev_id; ++ struct clock_event_device *evt = &hisi_evt->evt; ++ ++ /* clear the interrupt */ ++ writel(1, hisi_evt->base + TIMER_INTCLR); ++ ++ evt->event_handler(evt); ++ ++ return IRQ_HANDLED; ++} ++/*****************************************************************************/ ++ ++static inline struct hisi_clocksource *to_hisi_clksrc(struct clocksource *cs) ++{ ++ return container_of(cs, struct hisi_clocksource, clksrc); ++} ++/*****************************************************************************/ ++ ++static void hisi_clocksource_start(void __iomem *base) ++{ ++ writel(0, IOMEM(base + TIMER_CTRL)); ++ writel(0xffffffff, IOMEM(base + TIMER_LOAD)); ++ writel(0xffffffff, IOMEM(base + TIMER_VALUE)); ++ writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, ++ IOMEM(base + TIMER_CTRL)); ++} ++/*****************************************************************************/ ++ ++static cycle_t hisi_clocksource_read(struct clocksource *cs) ++{ ++ return ~readl_relaxed(to_hisi_clksrc(cs)->base + TIMER_VALUE); ++} ++/*****************************************************************************/ ++ ++static notrace u64 hisi_sched_clock_read(void) ++{ ++ u32 regval = ~readl_relaxed(hisi_clocksource.base + TIMER_VALUE); ++ ++ return (u64)regval; ++} ++/*****************************************************************************/ ++ ++static void hisi_clocksource_resume(struct clocksource *cs) ++{ ++ hisi_clocksource_start(to_hisi_clksrc(cs)->base); ++} ++/*****************************************************************************/ ++ ++static void __init hisi_clocksource_init(void __iomem *base, struct clk *clk) ++{ ++ struct clocksource *clksrc = &hisi_clocksource.clksrc; ++ ++ clksrc->name = clk->name; ++ clksrc->rating = 200; ++ clksrc->read = hisi_clocksource_read; ++ clksrc->mask = CLOCKSOURCE_MASK(32), ++ clksrc->flags = CLOCK_SOURCE_IS_CONTINUOUS, ++ clksrc->resume = hisi_clocksource_resume, ++ ++ hisi_clocksource.base = base; ++ ++ hisi_clocksource_start(base); ++ ++ clocksource_register_hz(clksrc, hisi_clk_rate); ++ sched_clock_register(hisi_sched_clock_read, 32, hisi_clk_rate); ++} ++//TODO: ARM64 Timer ++u64 hisi_get_clocksource_val(void) ++{ ++ struct clocksource *clksrc = &hisi_clocksource.clksrc; ++ if (!clksrc || !clksrc->read) ++ BUG(); ++ return clksrc->read(clksrc); ++} ++/*****************************************************************************/ ++ ++static int hisi_local_timer_setup(struct clock_event_device *clk) ++{ ++ struct hisi_clock_event_device *hisi_evt; ++ unsigned int cpu = smp_processor_id(); ++ struct irqaction *action = this_cpu_ptr(hisi_event_irq); ++ ++ hisi_evt = container_of(clk, struct hisi_clock_event_device, evt); ++ ++ hisi_evt->base = hisi_timer_base[cpu]; ++ snprintf(hisi_evt->name, sizeof(hisi_evt->name), "tick%d", cpu); ++ ++ clk->name = hisi_evt->name; ++ clk->cpumask = cpumask_of(cpu); ++ clk->set_next_event = hisi_set_next_event; ++ clk->set_mode = hisi_set_mode; ++ clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; ++ clk->rating = 450; ++ clk->irq = hisi_timer_irqs[cpu]; ++ clk->mode = CLOCK_EVT_MODE_UNUSED; ++ ++ action->name = clk->name; ++ action->dev_id = hisi_evt; ++ action->irq = clk->irq; ++ action->flags = IRQF_TIMER | IRQF_NOBALANCING; ++ action->handler = hisi_evt_isr; ++ BUG_ON(setup_irq(clk->irq, action)); ++ irq_force_affinity(clk->irq, clk->cpumask); ++ ++ clockevents_config_and_register(clk, hisi_clk_rate, 0xf, 0x7fffffff); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static void hisi_local_timer_stop(struct clock_event_device *clk) ++{ ++ pr_info("hisi_local_timer_teardown disable IRQ%d cpu #%d\n", ++ clk->irq, smp_processor_id()); ++ ++ disable_irq(clk->irq); ++ remove_irq(clk->irq, this_cpu_ptr(hisi_event_irq)); ++ ++ clk->set_mode(CLOCK_EVT_MODE_UNUSED, clk); ++} ++/*****************************************************************************/ ++ ++static int hisi_local_timer_cpu_notify(struct notifier_block *self, ++ unsigned long action, void *hcpu) ++{ ++ struct hisi_clock_event_device *hisi_evt; ++ /* ++ * Grab cpu pointer in each case to avoid spurious ++ * preemptible warnings ++ */ ++ switch (action & ~CPU_TASKS_FROZEN) { ++ case CPU_STARTING: ++ hisi_evt = this_cpu_ptr(hisi_local_timer_evt); ++ hisi_local_timer_setup(&hisi_evt->evt); ++ break; ++ case CPU_DYING: ++ hisi_evt = this_cpu_ptr(hisi_local_timer_evt); ++ hisi_local_timer_stop(&hisi_evt->evt); ++ break; ++ } ++ ++ return NOTIFY_OK; ++} ++/*****************************************************************************/ ++ ++static struct notifier_block hisi_local_timer_cpu_nb = { ++ .notifier_call = hisi_local_timer_cpu_notify, ++}; ++/*****************************************************************************/ ++static int __init hisi_local_timer_register(void) ++{ ++ int err; ++ int cpu = smp_processor_id(); ++ struct hisi_clock_event_device *hisi_evt; ++ ++ hisi_local_timer_evt = alloc_percpu(struct hisi_clock_event_device); ++ if (!hisi_local_timer_evt) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ hisi_event_irq = alloc_percpu(struct irqaction); ++ if (!hisi_event_irq) { ++ err = -ENOMEM; ++ goto out_event_irq; ++ } ++ ++ irq_set_affinity(hisi_timer_irqs[cpu], cpumask_of(0)); ++ ++ err = register_cpu_notifier(&hisi_local_timer_cpu_nb); ++ if (err) ++ goto out_notifier; ++ ++ /* Immediately configure the timer on the boot CPU */ ++ hisi_evt = this_cpu_ptr(hisi_local_timer_evt); ++ hisi_local_timer_setup(&hisi_evt->evt); ++ ++ return 0; ++ ++out_notifier: ++ free_percpu(hisi_event_irq); ++ ++out_event_irq: ++ free_percpu(hisi_local_timer_evt); ++ ++out: ++ return err; ++} ++ ++static void __init hisi_timer_init(struct device_node *node) ++{ ++ u32 i, nr_irqs; ++ struct clk *clk; ++ void __iomem *base; ++ ++ nr_irqs = of_irq_count(node); ++ if (nr_irqs > num_possible_cpus()) ++ nr_irqs = num_possible_cpus(); ++ ++ for (i = 0; i < nr_irqs; i++) { ++ hisi_timer_irqs[i] = irq_of_parse_and_map(node, i); ++ hisi_timer_base[i] = of_iomap(node, i + 1); ++ if (!hisi_timer_base[i]) { ++ pr_err("can not iomap timer %d\n", i); ++ goto out_unmap; ++ } ++ } ++ ++ base = of_iomap(node, 0); ++ if (WARN_ON(!base)) ++ goto out_unmap; ++ ++ clk = of_clk_get(node, 0); ++ if (IS_ERR(clk)) ++ goto out_clk; ++ ++ clk_prepare_enable(clk); ++ ++ hisi_clk_rate = clk_get_rate(clk); ++ hisi_clkevt_reload = DIV_ROUND_CLOSEST(hisi_clk_rate, HZ); ++ ++ hisi_clocksource_init(base, clk); ++ hisi_local_timer_register(); ++ ++ return; ++ ++out_clk: ++ iounmap(base); ++ ++out_unmap: ++ for (i = 0; i < nr_irqs; i++) { ++ if (hisi_timer_base[i]) ++ iounmap(hisi_timer_base[i]); ++ } ++} ++//TODO: ARM64 Timer ++u32 hisi_timer_get_rate(void) ++{ ++ return hisi_clk_rate; ++} ++ ++CLOCKSOURCE_OF_DECLARE(hisi_timer, "hisilicon,timer", ++ hisi_timer_init); +diff --git a/drivers/hisilicon/cma/Kconfig b/drivers/hisilicon/cma/Kconfig +new file mode 100644 +index 0000000..7472dcc +--- /dev/null ++++ b/drivers/hisilicon/cma/Kconfig +@@ -0,0 +1,16 @@ ++ ++config CMA_MEM_SHARED ++ bool "Support sharing CMA memory with the heap" ++ depends on CMA && DMA_CMA ++ default no ++ help ++ Support sharing CMA memory with the heap. ++ ++config CMA_ADVANCE_SHARE ++ bool "Support cma advance share" ++ depends on CMA && DMA_CMA ++ select CMA_MEM_SHARED ++ default no ++ help ++ Support advance sharing CMA memory with the heap. ++ CMA Multiplex Ratio will be improved when this macro defined. +diff --git a/drivers/hisilicon/cma/Makefile b/drivers/hisilicon/cma/Makefile +new file mode 100644 +index 0000000..eefda7f +--- /dev/null ++++ b/drivers/hisilicon/cma/Makefile +@@ -0,0 +1,2 @@ ++ ++obj-$(CONFIG_CMA) += hi_cma.o +diff --git a/drivers/hisilicon/cma/hi_cma.c b/drivers/hisilicon/cma/hi_cma.c +new file mode 100644 +index 0000000..d0395ed +--- /dev/null ++++ b/drivers/hisilicon/cma/hi_cma.c +@@ -0,0 +1,141 @@ ++#include <linux/device.h> ++#include <linux/dma-mapping.h> ++#include <linux/dma-contiguous.h> ++#include <linux/module.h> ++#include <linux/string.h> ++#include <linux/cma.h> ++ ++#define NAME_LEN_MAX 64 ++#define ZONE_MAX 64 ++ ++struct cma_zone { ++ struct device pdev; ++ char name[NAME_LEN_MAX]; ++ u32 gfp; ++ u32 phys_start; ++ u32 nbytes; ++ u32 alloc_type; ++ u32 block_align; ++}; ++ ++static u32 num_zones; ++static struct cma_zone hisi_zone[ZONE_MAX]; ++ ++static int use_bootargs; ++ ++unsigned int get_cma_size(void) ++{ ++ int i; ++ u64 total = 0; ++ ++ for (i = 0; i < num_zones; i++) ++ total += hisi_zone[i].nbytes; ++ ++ /* unit is M */ ++ return (unsigned int)(total >> 20); ++} ++ ++static int __init hisi_mmz_parse_cmdline(char *s) ++{ ++ char *line, *tmp; ++ char tmpline[256]; ++ ++ if (NULL == s) { ++ pr_info("There is no cma zone!\n"); ++ return 0; ++ } ++ strncpy(tmpline, s, sizeof(tmpline)); ++ tmpline[sizeof(tmpline)-1] = '\0'; ++ tmp = tmpline; ++ ++ while ((line = strsep(&tmp, ":")) != NULL) { ++ int i; ++ char *argv[6]; ++ ++ for (i = 0; (argv[i] = strsep(&line, ",")) != NULL;) ++ if (++i == ARRAY_SIZE(argv)) ++ break; ++ ++ hisi_zone[num_zones].pdev.coherent_dma_mask = DMA_BIT_MASK(64); ++ if (i == 4) { ++ strlcpy(hisi_zone[num_zones].name, argv[0], NAME_LEN_MAX); ++ hisi_zone[num_zones].gfp = memparse(argv[1], NULL); ++ hisi_zone[num_zones].phys_start = memparse(argv[2], NULL); ++ hisi_zone[num_zones].nbytes = memparse(argv[3], NULL); ++ } ++ ++ else if (i == 6) { ++ strlcpy(hisi_zone[num_zones].name, argv[0], NAME_LEN_MAX); ++ hisi_zone[num_zones].gfp = memparse(argv[1], NULL); ++ hisi_zone[num_zones].phys_start = memparse(argv[2], NULL); ++ hisi_zone[num_zones].nbytes = memparse(argv[3], NULL); ++ hisi_zone[num_zones].alloc_type = memparse(argv[4], NULL); ++ hisi_zone[num_zones].block_align = memparse(argv[5], NULL); ++ } else { ++ pr_err("hisi ion parameter is not correct\n"); ++ continue; ++ } ++ ++ num_zones++; ++ } ++ if (num_zones != 0) ++ use_bootargs = 1; ++ ++ return 0; ++} ++early_param("mmz", hisi_mmz_parse_cmdline); ++ ++struct cma_zone *hisi_get_cma_zone(const char *name) ++{ ++ int i = 0; ++ ++ for (i = 0; i < num_zones; i++) ++ if (strcmp(hisi_zone[i].name, name) == 0) ++ break; ++ ++ if (i == num_zones) ++ return NULL; ++ ++ return &hisi_zone[i]; ++} ++EXPORT_SYMBOL(hisi_get_cma_zone); ++ ++struct device *hisi_get_cma_device(const char *name) ++{ ++ int i = 0; ++ ++ for (i = 0; i < num_zones; i++) ++ if (strcmp(hisi_zone[i].name, name) == 0) ++ break; ++ ++ if (i == num_zones) ++ return NULL; ++ ++ return &hisi_zone[i].pdev; ++} ++EXPORT_SYMBOL(hisi_get_cma_device); ++ ++int hisi_declare_heap_memory(void) ++{ ++ int i; ++ int ret = 0; ++ ++ if (use_bootargs == 0) { ++ pr_info("cmz zone is not set!\n"); ++ return ret; ++ } ++ ++ for (i = 0; i < num_zones; i++) { ++ ret = dma_declare_contiguous(&hisi_zone[i].pdev, ++ hisi_zone[i].nbytes, hisi_zone[i].phys_start, 0); ++ if (ret) ++ panic("declare cma zone %s base: %u size:%uMB failed. ret:%d", ++ hisi_zone[i].name, hisi_zone[i].phys_start, ++ hisi_zone[i].nbytes>>20, ret); ++ hisi_zone[i].phys_start = cma_get_base(hisi_zone[i].pdev.cma_area); ++ hisi_zone[i].nbytes = cma_get_size(hisi_zone[i].pdev.cma_area); ++ ++ /*FIXME need to fix dma_declare_contiguous return value &&value type*/ ++ } ++ return ret; ++} +diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c +index d16dbb3..e7c8bf9 100644 +--- a/drivers/hwmon/lm75.c ++++ b/drivers/hwmon/lm75.c +@@ -176,6 +176,10 @@ static struct attribute *lm75_attrs[] = { + }; + ATTRIBUTE_GROUPS(lm75); + ++static const struct thermal_zone_of_device_ops lm75_of_thermal_ops = { ++ .get_temp = lm75_read_temp, ++}; ++ + /*-----------------------------------------------------------------------*/ + + /* device probe and removal */ +@@ -291,10 +295,9 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) + if (IS_ERR(data->hwmon_dev)) + return PTR_ERR(data->hwmon_dev); + +- data->tz = thermal_zone_of_sensor_register(data->hwmon_dev, +- 0, ++ data->tz = thermal_zone_of_sensor_register(data->hwmon_dev, 0, + data->hwmon_dev, +- lm75_read_temp, NULL); ++ &lm75_of_thermal_ops); + if (IS_ERR(data->tz)) + data->tz = NULL; + +diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c +index 31597c5..b2c90bd 100644 +--- a/drivers/hwmon/ntc_thermistor.c ++++ b/drivers/hwmon/ntc_thermistor.c +@@ -495,6 +495,10 @@ static const struct attribute_group ntc_attr_group = { + .attrs = ntc_attributes, + }; + ++static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = { ++ .get_temp = ntc_read_temp, ++}; ++ + static int ntc_thermistor_probe(struct platform_device *pdev) + { + const struct of_device_id *of_id = +@@ -588,7 +592,7 @@ static int ntc_thermistor_probe(struct platform_device *pdev) + pdev_id->name); + + data->tz = thermal_zone_of_sensor_register(data->dev, 0, data->dev, +- ntc_read_temp, NULL); ++ &ntc_of_thermal_ops); + if (IS_ERR(data->tz)) { + dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n"); + data->tz = NULL; +diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c +index 5171995..ba9f478 100644 +--- a/drivers/hwmon/tmp102.c ++++ b/drivers/hwmon/tmp102.c +@@ -158,6 +158,10 @@ ATTRIBUTE_GROUPS(tmp102); + #define TMP102_CONFIG (TMP102_CONF_TM | TMP102_CONF_EM | TMP102_CONF_CR1) + #define TMP102_CONFIG_RD_ONLY (TMP102_CONF_R0 | TMP102_CONF_R1 | TMP102_CONF_AL) + ++static const struct thermal_zone_of_device_ops tmp102_of_thermal_ops = { ++ .get_temp = tmp102_read_temp, ++}; ++ + static int tmp102_probe(struct i2c_client *client, + const struct i2c_device_id *id) + { +@@ -215,7 +219,7 @@ static int tmp102_probe(struct i2c_client *client, + } + tmp102->hwmon_dev = hwmon_dev; + tmp102->tz = thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev, +- tmp102_read_temp, NULL); ++ &tmp102_of_thermal_ops); + if (IS_ERR(tmp102->tz)) + tmp102->tz = NULL; + +diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig +new file mode 100644 +index 0000000..fc1f1ae +--- /dev/null ++++ b/drivers/hwtracing/coresight/Kconfig +@@ -0,0 +1,61 @@ ++# ++# Coresight configuration ++# ++menuconfig CORESIGHT ++ bool "CoreSight Tracing Support" ++ select ARM_AMBA ++ help ++ This framework provides a kernel interface for the CoreSight debug ++ and trace drivers to register themselves with. It's intended to build ++ a topological view of the CoreSight components based on a DT ++ specification and configure the right serie of components when a ++ trace source gets enabled. ++ ++if CORESIGHT ++config CORESIGHT_LINKS_AND_SINKS ++ bool "CoreSight Link and Sink drivers" ++ help ++ This enables support for CoreSight link and sink drivers that are ++ responsible for transporting and collecting the trace data ++ respectively. Link and sinks are dynamically aggregated with a trace ++ entity at run time to form a complete trace path. ++ ++config CORESIGHT_LINK_AND_SINK_TMC ++ bool "Coresight generic TMC driver" ++ depends on CORESIGHT_LINKS_AND_SINKS ++ help ++ This enables support for the Trace Memory Controller driver. ++ Depending on its configuration the device can act as a link (embedded ++ trace router - ETR) or sink (embedded trace FIFO). The driver ++ complies with the generic implementation of the component without ++ special enhancement or added features. ++ ++config CORESIGHT_SINK_TPIU ++ bool "Coresight generic TPIU driver" ++ depends on CORESIGHT_LINKS_AND_SINKS ++ help ++ This enables support for the Trace Port Interface Unit driver, ++ responsible for bridging the gap between the on-chip coresight ++ components and a trace for bridging the gap between the on-chip ++ coresight components and a trace port collection engine, typically ++ connected to an external host for use case capturing more traces than ++ the on-board coresight memory can handle. ++ ++config CORESIGHT_SINK_ETBV10 ++ bool "Coresight ETBv1.0 driver" ++ depends on CORESIGHT_LINKS_AND_SINKS ++ help ++ This enables support for the Embedded Trace Buffer version 1.0 driver ++ that complies with the generic implementation of the component without ++ special enhancement or added features. ++ ++config CORESIGHT_SOURCE_ETM3X ++ bool "CoreSight Embedded Trace Macrocell 3.x driver" ++ depends on !ARM64 ++ select CORESIGHT_LINKS_AND_SINKS ++ help ++ This driver provides support for processor ETM3.x and PTM1.x modules, ++ which allows tracing the instructions that a processor is executing ++ This is primarily useful for instruction level tracing. Depending ++ the ETM version data tracing may also be available. ++endif +diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile +new file mode 100644 +index 0000000..4b4bec8 +--- /dev/null ++++ b/drivers/hwtracing/coresight/Makefile +@@ -0,0 +1,11 @@ ++# ++# Makefile for CoreSight drivers. ++# ++obj-$(CONFIG_CORESIGHT) += coresight.o ++obj-$(CONFIG_OF) += of_coresight.o ++obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o ++obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o ++obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o ++obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ ++ coresight-replicator.o ++obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o +diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c +new file mode 100644 +index 0000000..4004986 +--- /dev/null ++++ b/drivers/hwtracing/coresight/coresight-etb10.c +@@ -0,0 +1,527 @@ ++/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/device.h> ++#include <linux/io.h> ++#include <linux/err.h> ++#include <linux/fs.h> ++#include <linux/miscdevice.h> ++#include <linux/uaccess.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/clk.h> ++#include <linux/seq_file.h> ++#include <linux/coresight.h> ++#include <linux/amba/bus.h> ++ ++#include "coresight-priv.h" ++ ++#define ETB_RAM_DEPTH_REG 0x004 ++#define ETB_STATUS_REG 0x00c ++#define ETB_RAM_READ_DATA_REG 0x010 ++#define ETB_RAM_READ_POINTER 0x014 ++#define ETB_RAM_WRITE_POINTER 0x018 ++#define ETB_TRG 0x01c ++#define ETB_CTL_REG 0x020 ++#define ETB_RWD_REG 0x024 ++#define ETB_FFSR 0x300 ++#define ETB_FFCR 0x304 ++#define ETB_ITMISCOP0 0xee0 ++#define ETB_ITTRFLINACK 0xee4 ++#define ETB_ITTRFLIN 0xee8 ++#define ETB_ITATBDATA0 0xeeC ++#define ETB_ITATBCTR2 0xef0 ++#define ETB_ITATBCTR1 0xef4 ++#define ETB_ITATBCTR0 0xef8 ++ ++/* register description */ ++/* STS - 0x00C */ ++#define ETB_STATUS_RAM_FULL BIT(0) ++/* CTL - 0x020 */ ++#define ETB_CTL_CAPT_EN BIT(0) ++/* FFCR - 0x304 */ ++#define ETB_FFCR_EN_FTC BIT(0) ++#define ETB_FFCR_FON_MAN BIT(6) ++#define ETB_FFCR_STOP_FI BIT(12) ++#define ETB_FFCR_STOP_TRIGGER BIT(13) ++ ++#define ETB_FFCR_BIT 6 ++#define ETB_FFSR_BIT 1 ++#define ETB_FRAME_SIZE_WORDS 4 ++ ++/** ++ * struct etb_drvdata - specifics associated to an ETB component ++ * @base: memory mapped base address for this component. ++ * @dev: the device entity associated to this component. ++ * @csdev: component vitals needed by the framework. ++ * @miscdev: specifics to handle "/dev/xyz.etb" entry. ++ * @clk: the clock this component is associated to. ++ * @spinlock: only one at a time pls. ++ * @in_use: synchronise user space access to etb buffer. ++ * @buf: area of memory where ETB buffer content gets sent. ++ * @buffer_depth: size of @buf. ++ * @enable: this ETB is being used. ++ * @trigger_cntr: amount of words to store after a trigger. ++ */ ++struct etb_drvdata { ++ void __iomem *base; ++ struct device *dev; ++ struct coresight_device *csdev; ++ struct miscdevice miscdev; ++ struct clk *clk; ++ spinlock_t spinlock; ++ atomic_t in_use; ++ u8 *buf; ++ u32 buffer_depth; ++ bool enable; ++ u32 trigger_cntr; ++}; ++ ++static unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata) ++{ ++ int ret; ++ u32 depth = 0; ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ /* RO registers don't need locking */ ++ depth = readl_relaxed(drvdata->base + ETB_RAM_DEPTH_REG); ++ ++ clk_disable_unprepare(drvdata->clk); ++ return depth; ++} ++ ++static void etb_enable_hw(struct etb_drvdata *drvdata) ++{ ++ int i; ++ u32 depth; ++ ++ CS_UNLOCK(drvdata->base); ++ ++ depth = drvdata->buffer_depth; ++ /* reset write RAM pointer address */ ++ writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER); ++ /* clear entire RAM buffer */ ++ for (i = 0; i < depth; i++) ++ writel_relaxed(0x0, drvdata->base + ETB_RWD_REG); ++ ++ /* reset write RAM pointer address */ ++ writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER); ++ /* reset read RAM pointer address */ ++ writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER); ++ ++ writel_relaxed(drvdata->trigger_cntr, drvdata->base + ETB_TRG); ++ writel_relaxed(ETB_FFCR_EN_FTC | ETB_FFCR_STOP_TRIGGER, ++ drvdata->base + ETB_FFCR); ++ /* ETB trace capture enable */ ++ writel_relaxed(ETB_CTL_CAPT_EN, drvdata->base + ETB_CTL_REG); ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static int etb_enable(struct coresight_device *csdev) ++{ ++ struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ int ret; ++ unsigned long flags; ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ etb_enable_hw(drvdata); ++ drvdata->enable = true; ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ ++ dev_info(drvdata->dev, "ETB enabled\n"); ++ return 0; ++} ++ ++static void etb_disable_hw(struct etb_drvdata *drvdata) ++{ ++ u32 ffcr; ++ ++ CS_UNLOCK(drvdata->base); ++ ++ ffcr = readl_relaxed(drvdata->base + ETB_FFCR); ++ /* stop formatter when a stop has completed */ ++ ffcr |= ETB_FFCR_STOP_FI; ++ writel_relaxed(ffcr, drvdata->base + ETB_FFCR); ++ /* manually generate a flush of the system */ ++ ffcr |= ETB_FFCR_FON_MAN; ++ writel_relaxed(ffcr, drvdata->base + ETB_FFCR); ++ ++ if (coresight_timeout(drvdata->base, ETB_FFCR, ETB_FFCR_BIT, 0)) { ++ dev_err(drvdata->dev, ++ "timeout observed when probing at offset %#x\n", ++ ETB_FFCR); ++ } ++ ++ /* disable trace capture */ ++ writel_relaxed(0x0, drvdata->base + ETB_CTL_REG); ++ ++ if (coresight_timeout(drvdata->base, ETB_FFSR, ETB_FFSR_BIT, 1)) { ++ dev_err(drvdata->dev, ++ "timeout observed when probing at offset %#x\n", ++ ETB_FFCR); ++ } ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static void etb_dump_hw(struct etb_drvdata *drvdata) ++{ ++ int i; ++ u8 *buf_ptr; ++ u32 read_data, depth; ++ u32 read_ptr, write_ptr; ++ u32 frame_off, frame_endoff; ++ ++ CS_UNLOCK(drvdata->base); ++ ++ read_ptr = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER); ++ write_ptr = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER); ++ ++ frame_off = write_ptr % ETB_FRAME_SIZE_WORDS; ++ frame_endoff = ETB_FRAME_SIZE_WORDS - frame_off; ++ if (frame_off) { ++ dev_err(drvdata->dev, ++ "write_ptr: %lu not aligned to formatter frame size\n", ++ (unsigned long)write_ptr); ++ dev_err(drvdata->dev, "frameoff: %lu, frame_endoff: %lu\n", ++ (unsigned long)frame_off, (unsigned long)frame_endoff); ++ write_ptr += frame_endoff; ++ } ++ ++ if ((readl_relaxed(drvdata->base + ETB_STATUS_REG) ++ & ETB_STATUS_RAM_FULL) == 0) ++ writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER); ++ else ++ writel_relaxed(write_ptr, drvdata->base + ETB_RAM_READ_POINTER); ++ ++ depth = drvdata->buffer_depth; ++ buf_ptr = drvdata->buf; ++ for (i = 0; i < depth; i++) { ++ read_data = readl_relaxed(drvdata->base + ++ ETB_RAM_READ_DATA_REG); ++ *buf_ptr++ = read_data >> 0; ++ *buf_ptr++ = read_data >> 8; ++ *buf_ptr++ = read_data >> 16; ++ *buf_ptr++ = read_data >> 24; ++ } ++ ++ if (frame_off) { ++ buf_ptr -= (frame_endoff * 4); ++ for (i = 0; i < frame_endoff; i++) { ++ *buf_ptr++ = 0x0; ++ *buf_ptr++ = 0x0; ++ *buf_ptr++ = 0x0; ++ *buf_ptr++ = 0x0; ++ } ++ } ++ ++ writel_relaxed(read_ptr, drvdata->base + ETB_RAM_READ_POINTER); ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static void etb_disable(struct coresight_device *csdev) ++{ ++ struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ etb_disable_hw(drvdata); ++ etb_dump_hw(drvdata); ++ drvdata->enable = false; ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ ++ clk_disable_unprepare(drvdata->clk); ++ ++ dev_info(drvdata->dev, "ETB disabled\n"); ++} ++ ++static const struct coresight_ops_sink etb_sink_ops = { ++ .enable = etb_enable, ++ .disable = etb_disable, ++}; ++ ++static const struct coresight_ops etb_cs_ops = { ++ .sink_ops = &etb_sink_ops, ++}; ++ ++static void etb_dump(struct etb_drvdata *drvdata) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ if (drvdata->enable) { ++ etb_disable_hw(drvdata); ++ etb_dump_hw(drvdata); ++ etb_enable_hw(drvdata); ++ } ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ ++ dev_info(drvdata->dev, "ETB dumped\n"); ++} ++ ++static int etb_open(struct inode *inode, struct file *file) ++{ ++ struct etb_drvdata *drvdata = container_of(file->private_data, ++ struct etb_drvdata, miscdev); ++ ++ if (atomic_cmpxchg(&drvdata->in_use, 0, 1)) ++ return -EBUSY; ++ ++ dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__); ++ return 0; ++} ++ ++static ssize_t etb_read(struct file *file, char __user *data, ++ size_t len, loff_t *ppos) ++{ ++ u32 depth; ++ struct etb_drvdata *drvdata = container_of(file->private_data, ++ struct etb_drvdata, miscdev); ++ ++ etb_dump(drvdata); ++ ++ depth = drvdata->buffer_depth; ++ if (*ppos + len > depth * 4) ++ len = depth * 4 - *ppos; ++ ++ if (copy_to_user(data, drvdata->buf + *ppos, len)) { ++ dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__); ++ return -EFAULT; ++ } ++ ++ *ppos += len; ++ ++ dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n", ++ __func__, len, (int)(depth * 4 - *ppos)); ++ return len; ++} ++ ++static int etb_release(struct inode *inode, struct file *file) ++{ ++ struct etb_drvdata *drvdata = container_of(file->private_data, ++ struct etb_drvdata, miscdev); ++ atomic_set(&drvdata->in_use, 0); ++ ++ dev_dbg(drvdata->dev, "%s: released\n", __func__); ++ return 0; ++} ++ ++static const struct file_operations etb_fops = { ++ .owner = THIS_MODULE, ++ .open = etb_open, ++ .read = etb_read, ++ .release = etb_release, ++ .llseek = no_llseek, ++}; ++ ++static ssize_t status_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int ret; ++ unsigned long flags; ++ u32 etb_rdr, etb_sr, etb_rrp, etb_rwp; ++ u32 etb_trg, etb_cr, etb_ffsr, etb_ffcr; ++ struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ goto out; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ CS_UNLOCK(drvdata->base); ++ ++ etb_rdr = readl_relaxed(drvdata->base + ETB_RAM_DEPTH_REG); ++ etb_sr = readl_relaxed(drvdata->base + ETB_STATUS_REG); ++ etb_rrp = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER); ++ etb_rwp = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER); ++ etb_trg = readl_relaxed(drvdata->base + ETB_TRG); ++ etb_cr = readl_relaxed(drvdata->base + ETB_CTL_REG); ++ etb_ffsr = readl_relaxed(drvdata->base + ETB_FFSR); ++ etb_ffcr = readl_relaxed(drvdata->base + ETB_FFCR); ++ ++ CS_LOCK(drvdata->base); ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ ++ clk_disable_unprepare(drvdata->clk); ++ ++ return sprintf(buf, ++ "Depth:\t\t0x%x\n" ++ "Status:\t\t0x%x\n" ++ "RAM read ptr:\t0x%x\n" ++ "RAM wrt ptr:\t0x%x\n" ++ "Trigger cnt:\t0x%x\n" ++ "Control:\t0x%x\n" ++ "Flush status:\t0x%x\n" ++ "Flush ctrl:\t0x%x\n", ++ etb_rdr, etb_sr, etb_rrp, etb_rwp, ++ etb_trg, etb_cr, etb_ffsr, etb_ffcr); ++out: ++ return -EINVAL; ++} ++static DEVICE_ATTR_RO(status); ++ ++static ssize_t trigger_cntr_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ unsigned long val = drvdata->trigger_cntr; ++ ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t trigger_cntr_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->trigger_cntr = val; ++ return size; ++} ++static DEVICE_ATTR_RW(trigger_cntr); ++ ++static struct attribute *coresight_etb_attrs[] = { ++ &dev_attr_trigger_cntr.attr, ++ &dev_attr_status.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(coresight_etb); ++ ++static int etb_probe(struct amba_device *adev, const struct amba_id *id) ++{ ++ int ret; ++ void __iomem *base; ++ struct device *dev = &adev->dev; ++ struct coresight_platform_data *pdata = NULL; ++ struct etb_drvdata *drvdata; ++ struct resource *res = &adev->res; ++ struct coresight_desc *desc; ++ struct device_node *np = adev->dev.of_node; ++ ++ if (np) { ++ pdata = of_get_coresight_platform_data(dev, np); ++ if (IS_ERR(pdata)) ++ return PTR_ERR(pdata); ++ adev->dev.platform_data = pdata; ++ } ++ ++ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); ++ if (!drvdata) ++ return -ENOMEM; ++ ++ drvdata->dev = &adev->dev; ++ dev_set_drvdata(dev, drvdata); ++ ++ /* validity for the resource is already checked by the AMBA core */ ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ drvdata->base = base; ++ ++ spin_lock_init(&drvdata->spinlock); ++ ++ drvdata->clk = adev->pclk; ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ drvdata->buffer_depth = etb_get_buffer_depth(drvdata); ++ clk_disable_unprepare(drvdata->clk); ++ ++ if (drvdata->buffer_depth < 0) ++ return -EINVAL; ++ ++ drvdata->buf = devm_kzalloc(dev, ++ drvdata->buffer_depth * 4, GFP_KERNEL); ++ if (!drvdata->buf) ++ return -ENOMEM; ++ ++ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ desc->type = CORESIGHT_DEV_TYPE_SINK; ++ desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; ++ desc->ops = &etb_cs_ops; ++ desc->pdata = pdata; ++ desc->dev = dev; ++ desc->groups = coresight_etb_groups; ++ drvdata->csdev = coresight_register(desc); ++ if (IS_ERR(drvdata->csdev)) ++ return PTR_ERR(drvdata->csdev); ++ ++ drvdata->miscdev.name = pdata->name; ++ drvdata->miscdev.minor = MISC_DYNAMIC_MINOR; ++ drvdata->miscdev.fops = &etb_fops; ++ ret = misc_register(&drvdata->miscdev); ++ if (ret) ++ goto err_misc_register; ++ ++ dev_info(dev, "ETB initialized\n"); ++ return 0; ++ ++err_misc_register: ++ coresight_unregister(drvdata->csdev); ++ return ret; ++} ++ ++static int etb_remove(struct amba_device *adev) ++{ ++ struct etb_drvdata *drvdata = amba_get_drvdata(adev); ++ ++ misc_deregister(&drvdata->miscdev); ++ coresight_unregister(drvdata->csdev); ++ return 0; ++} ++ ++static struct amba_id etb_ids[] = { ++ { ++ .id = 0x0003b907, ++ .mask = 0x0003ffff, ++ }, ++ { 0, 0}, ++}; ++ ++static struct amba_driver etb_driver = { ++ .drv = { ++ .name = "coresight-etb10", ++ .owner = THIS_MODULE, ++ }, ++ .probe = etb_probe, ++ .remove = etb_remove, ++ .id_table = etb_ids, ++}; ++ ++module_amba_driver(etb_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("CoreSight Embedded Trace Buffer driver"); +diff --git a/drivers/hwtracing/coresight/coresight-etm-cp14.c b/drivers/hwtracing/coresight/coresight-etm-cp14.c +new file mode 100644 +index 0000000..12a2206 +--- /dev/null ++++ b/drivers/hwtracing/coresight/coresight-etm-cp14.c +@@ -0,0 +1,591 @@ ++/* Copyright (c) 2012, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/bug.h> ++#include <asm/hardware/cp14.h> ++ ++#include "coresight-etm.h" ++ ++int etm_readl_cp14(u32 reg, unsigned int *val) ++{ ++ switch (reg) { ++ case ETMCR: ++ *val = etm_read(ETMCR); ++ return 0; ++ case ETMCCR: ++ *val = etm_read(ETMCCR); ++ return 0; ++ case ETMTRIGGER: ++ *val = etm_read(ETMTRIGGER); ++ return 0; ++ case ETMSR: ++ *val = etm_read(ETMSR); ++ return 0; ++ case ETMSCR: ++ *val = etm_read(ETMSCR); ++ return 0; ++ case ETMTSSCR: ++ *val = etm_read(ETMTSSCR); ++ return 0; ++ case ETMTEEVR: ++ *val = etm_read(ETMTEEVR); ++ return 0; ++ case ETMTECR1: ++ *val = etm_read(ETMTECR1); ++ return 0; ++ case ETMFFLR: ++ *val = etm_read(ETMFFLR); ++ return 0; ++ case ETMACVRn(0): ++ *val = etm_read(ETMACVR0); ++ return 0; ++ case ETMACVRn(1): ++ *val = etm_read(ETMACVR1); ++ return 0; ++ case ETMACVRn(2): ++ *val = etm_read(ETMACVR2); ++ return 0; ++ case ETMACVRn(3): ++ *val = etm_read(ETMACVR3); ++ return 0; ++ case ETMACVRn(4): ++ *val = etm_read(ETMACVR4); ++ return 0; ++ case ETMACVRn(5): ++ *val = etm_read(ETMACVR5); ++ return 0; ++ case ETMACVRn(6): ++ *val = etm_read(ETMACVR6); ++ return 0; ++ case ETMACVRn(7): ++ *val = etm_read(ETMACVR7); ++ return 0; ++ case ETMACVRn(8): ++ *val = etm_read(ETMACVR8); ++ return 0; ++ case ETMACVRn(9): ++ *val = etm_read(ETMACVR9); ++ return 0; ++ case ETMACVRn(10): ++ *val = etm_read(ETMACVR10); ++ return 0; ++ case ETMACVRn(11): ++ *val = etm_read(ETMACVR11); ++ return 0; ++ case ETMACVRn(12): ++ *val = etm_read(ETMACVR12); ++ return 0; ++ case ETMACVRn(13): ++ *val = etm_read(ETMACVR13); ++ return 0; ++ case ETMACVRn(14): ++ *val = etm_read(ETMACVR14); ++ return 0; ++ case ETMACVRn(15): ++ *val = etm_read(ETMACVR15); ++ return 0; ++ case ETMACTRn(0): ++ *val = etm_read(ETMACTR0); ++ return 0; ++ case ETMACTRn(1): ++ *val = etm_read(ETMACTR1); ++ return 0; ++ case ETMACTRn(2): ++ *val = etm_read(ETMACTR2); ++ return 0; ++ case ETMACTRn(3): ++ *val = etm_read(ETMACTR3); ++ return 0; ++ case ETMACTRn(4): ++ *val = etm_read(ETMACTR4); ++ return 0; ++ case ETMACTRn(5): ++ *val = etm_read(ETMACTR5); ++ return 0; ++ case ETMACTRn(6): ++ *val = etm_read(ETMACTR6); ++ return 0; ++ case ETMACTRn(7): ++ *val = etm_read(ETMACTR7); ++ return 0; ++ case ETMACTRn(8): ++ *val = etm_read(ETMACTR8); ++ return 0; ++ case ETMACTRn(9): ++ *val = etm_read(ETMACTR9); ++ return 0; ++ case ETMACTRn(10): ++ *val = etm_read(ETMACTR10); ++ return 0; ++ case ETMACTRn(11): ++ *val = etm_read(ETMACTR11); ++ return 0; ++ case ETMACTRn(12): ++ *val = etm_read(ETMACTR12); ++ return 0; ++ case ETMACTRn(13): ++ *val = etm_read(ETMACTR13); ++ return 0; ++ case ETMACTRn(14): ++ *val = etm_read(ETMACTR14); ++ return 0; ++ case ETMACTRn(15): ++ *val = etm_read(ETMACTR15); ++ return 0; ++ case ETMCNTRLDVRn(0): ++ *val = etm_read(ETMCNTRLDVR0); ++ return 0; ++ case ETMCNTRLDVRn(1): ++ *val = etm_read(ETMCNTRLDVR1); ++ return 0; ++ case ETMCNTRLDVRn(2): ++ *val = etm_read(ETMCNTRLDVR2); ++ return 0; ++ case ETMCNTRLDVRn(3): ++ *val = etm_read(ETMCNTRLDVR3); ++ return 0; ++ case ETMCNTENRn(0): ++ *val = etm_read(ETMCNTENR0); ++ return 0; ++ case ETMCNTENRn(1): ++ *val = etm_read(ETMCNTENR1); ++ return 0; ++ case ETMCNTENRn(2): ++ *val = etm_read(ETMCNTENR2); ++ return 0; ++ case ETMCNTENRn(3): ++ *val = etm_read(ETMCNTENR3); ++ return 0; ++ case ETMCNTRLDEVRn(0): ++ *val = etm_read(ETMCNTRLDEVR0); ++ return 0; ++ case ETMCNTRLDEVRn(1): ++ *val = etm_read(ETMCNTRLDEVR1); ++ return 0; ++ case ETMCNTRLDEVRn(2): ++ *val = etm_read(ETMCNTRLDEVR2); ++ return 0; ++ case ETMCNTRLDEVRn(3): ++ *val = etm_read(ETMCNTRLDEVR3); ++ return 0; ++ case ETMCNTVRn(0): ++ *val = etm_read(ETMCNTVR0); ++ return 0; ++ case ETMCNTVRn(1): ++ *val = etm_read(ETMCNTVR1); ++ return 0; ++ case ETMCNTVRn(2): ++ *val = etm_read(ETMCNTVR2); ++ return 0; ++ case ETMCNTVRn(3): ++ *val = etm_read(ETMCNTVR3); ++ return 0; ++ case ETMSQ12EVR: ++ *val = etm_read(ETMSQ12EVR); ++ return 0; ++ case ETMSQ21EVR: ++ *val = etm_read(ETMSQ21EVR); ++ return 0; ++ case ETMSQ23EVR: ++ *val = etm_read(ETMSQ23EVR); ++ return 0; ++ case ETMSQ31EVR: ++ *val = etm_read(ETMSQ31EVR); ++ return 0; ++ case ETMSQ32EVR: ++ *val = etm_read(ETMSQ32EVR); ++ return 0; ++ case ETMSQ13EVR: ++ *val = etm_read(ETMSQ13EVR); ++ return 0; ++ case ETMSQR: ++ *val = etm_read(ETMSQR); ++ return 0; ++ case ETMEXTOUTEVRn(0): ++ *val = etm_read(ETMEXTOUTEVR0); ++ return 0; ++ case ETMEXTOUTEVRn(1): ++ *val = etm_read(ETMEXTOUTEVR1); ++ return 0; ++ case ETMEXTOUTEVRn(2): ++ *val = etm_read(ETMEXTOUTEVR2); ++ return 0; ++ case ETMEXTOUTEVRn(3): ++ *val = etm_read(ETMEXTOUTEVR3); ++ return 0; ++ case ETMCIDCVRn(0): ++ *val = etm_read(ETMCIDCVR0); ++ return 0; ++ case ETMCIDCVRn(1): ++ *val = etm_read(ETMCIDCVR1); ++ return 0; ++ case ETMCIDCVRn(2): ++ *val = etm_read(ETMCIDCVR2); ++ return 0; ++ case ETMCIDCMR: ++ *val = etm_read(ETMCIDCMR); ++ return 0; ++ case ETMIMPSPEC0: ++ *val = etm_read(ETMIMPSPEC0); ++ return 0; ++ case ETMIMPSPEC1: ++ *val = etm_read(ETMIMPSPEC1); ++ return 0; ++ case ETMIMPSPEC2: ++ *val = etm_read(ETMIMPSPEC2); ++ return 0; ++ case ETMIMPSPEC3: ++ *val = etm_read(ETMIMPSPEC3); ++ return 0; ++ case ETMIMPSPEC4: ++ *val = etm_read(ETMIMPSPEC4); ++ return 0; ++ case ETMIMPSPEC5: ++ *val = etm_read(ETMIMPSPEC5); ++ return 0; ++ case ETMIMPSPEC6: ++ *val = etm_read(ETMIMPSPEC6); ++ return 0; ++ case ETMIMPSPEC7: ++ *val = etm_read(ETMIMPSPEC7); ++ return 0; ++ case ETMSYNCFR: ++ *val = etm_read(ETMSYNCFR); ++ return 0; ++ case ETMIDR: ++ *val = etm_read(ETMIDR); ++ return 0; ++ case ETMCCER: ++ *val = etm_read(ETMCCER); ++ return 0; ++ case ETMEXTINSELR: ++ *val = etm_read(ETMEXTINSELR); ++ return 0; ++ case ETMTESSEICR: ++ *val = etm_read(ETMTESSEICR); ++ return 0; ++ case ETMEIBCR: ++ *val = etm_read(ETMEIBCR); ++ return 0; ++ case ETMTSEVR: ++ *val = etm_read(ETMTSEVR); ++ return 0; ++ case ETMAUXCR: ++ *val = etm_read(ETMAUXCR); ++ return 0; ++ case ETMTRACEIDR: ++ *val = etm_read(ETMTRACEIDR); ++ return 0; ++ case ETMVMIDCVR: ++ *val = etm_read(ETMVMIDCVR); ++ return 0; ++ case ETMOSLSR: ++ *val = etm_read(ETMOSLSR); ++ return 0; ++ case ETMOSSRR: ++ *val = etm_read(ETMOSSRR); ++ return 0; ++ case ETMPDCR: ++ *val = etm_read(ETMPDCR); ++ return 0; ++ case ETMPDSR: ++ *val = etm_read(ETMPDSR); ++ return 0; ++ default: ++ *val = 0; ++ return -EINVAL; ++ } ++} ++ ++int etm_writel_cp14(u32 reg, u32 val) ++{ ++ switch (reg) { ++ case ETMCR: ++ etm_write(val, ETMCR); ++ break; ++ case ETMTRIGGER: ++ etm_write(val, ETMTRIGGER); ++ break; ++ case ETMSR: ++ etm_write(val, ETMSR); ++ break; ++ case ETMTSSCR: ++ etm_write(val, ETMTSSCR); ++ break; ++ case ETMTEEVR: ++ etm_write(val, ETMTEEVR); ++ break; ++ case ETMTECR1: ++ etm_write(val, ETMTECR1); ++ break; ++ case ETMFFLR: ++ etm_write(val, ETMFFLR); ++ break; ++ case ETMACVRn(0): ++ etm_write(val, ETMACVR0); ++ break; ++ case ETMACVRn(1): ++ etm_write(val, ETMACVR1); ++ break; ++ case ETMACVRn(2): ++ etm_write(val, ETMACVR2); ++ break; ++ case ETMACVRn(3): ++ etm_write(val, ETMACVR3); ++ break; ++ case ETMACVRn(4): ++ etm_write(val, ETMACVR4); ++ break; ++ case ETMACVRn(5): ++ etm_write(val, ETMACVR5); ++ break; ++ case ETMACVRn(6): ++ etm_write(val, ETMACVR6); ++ break; ++ case ETMACVRn(7): ++ etm_write(val, ETMACVR7); ++ break; ++ case ETMACVRn(8): ++ etm_write(val, ETMACVR8); ++ break; ++ case ETMACVRn(9): ++ etm_write(val, ETMACVR9); ++ break; ++ case ETMACVRn(10): ++ etm_write(val, ETMACVR10); ++ break; ++ case ETMACVRn(11): ++ etm_write(val, ETMACVR11); ++ break; ++ case ETMACVRn(12): ++ etm_write(val, ETMACVR12); ++ break; ++ case ETMACVRn(13): ++ etm_write(val, ETMACVR13); ++ break; ++ case ETMACVRn(14): ++ etm_write(val, ETMACVR14); ++ break; ++ case ETMACVRn(15): ++ etm_write(val, ETMACVR15); ++ break; ++ case ETMACTRn(0): ++ etm_write(val, ETMACTR0); ++ break; ++ case ETMACTRn(1): ++ etm_write(val, ETMACTR1); ++ break; ++ case ETMACTRn(2): ++ etm_write(val, ETMACTR2); ++ break; ++ case ETMACTRn(3): ++ etm_write(val, ETMACTR3); ++ break; ++ case ETMACTRn(4): ++ etm_write(val, ETMACTR4); ++ break; ++ case ETMACTRn(5): ++ etm_write(val, ETMACTR5); ++ break; ++ case ETMACTRn(6): ++ etm_write(val, ETMACTR6); ++ break; ++ case ETMACTRn(7): ++ etm_write(val, ETMACTR7); ++ break; ++ case ETMACTRn(8): ++ etm_write(val, ETMACTR8); ++ break; ++ case ETMACTRn(9): ++ etm_write(val, ETMACTR9); ++ break; ++ case ETMACTRn(10): ++ etm_write(val, ETMACTR10); ++ break; ++ case ETMACTRn(11): ++ etm_write(val, ETMACTR11); ++ break; ++ case ETMACTRn(12): ++ etm_write(val, ETMACTR12); ++ break; ++ case ETMACTRn(13): ++ etm_write(val, ETMACTR13); ++ break; ++ case ETMACTRn(14): ++ etm_write(val, ETMACTR14); ++ break; ++ case ETMACTRn(15): ++ etm_write(val, ETMACTR15); ++ break; ++ case ETMCNTRLDVRn(0): ++ etm_write(val, ETMCNTRLDVR0); ++ break; ++ case ETMCNTRLDVRn(1): ++ etm_write(val, ETMCNTRLDVR1); ++ break; ++ case ETMCNTRLDVRn(2): ++ etm_write(val, ETMCNTRLDVR2); ++ break; ++ case ETMCNTRLDVRn(3): ++ etm_write(val, ETMCNTRLDVR3); ++ break; ++ case ETMCNTENRn(0): ++ etm_write(val, ETMCNTENR0); ++ break; ++ case ETMCNTENRn(1): ++ etm_write(val, ETMCNTENR1); ++ break; ++ case ETMCNTENRn(2): ++ etm_write(val, ETMCNTENR2); ++ break; ++ case ETMCNTENRn(3): ++ etm_write(val, ETMCNTENR3); ++ break; ++ case ETMCNTRLDEVRn(0): ++ etm_write(val, ETMCNTRLDEVR0); ++ break; ++ case ETMCNTRLDEVRn(1): ++ etm_write(val, ETMCNTRLDEVR1); ++ break; ++ case ETMCNTRLDEVRn(2): ++ etm_write(val, ETMCNTRLDEVR2); ++ break; ++ case ETMCNTRLDEVRn(3): ++ etm_write(val, ETMCNTRLDEVR3); ++ break; ++ case ETMCNTVRn(0): ++ etm_write(val, ETMCNTVR0); ++ break; ++ case ETMCNTVRn(1): ++ etm_write(val, ETMCNTVR1); ++ break; ++ case ETMCNTVRn(2): ++ etm_write(val, ETMCNTVR2); ++ break; ++ case ETMCNTVRn(3): ++ etm_write(val, ETMCNTVR3); ++ break; ++ case ETMSQ12EVR: ++ etm_write(val, ETMSQ12EVR); ++ break; ++ case ETMSQ21EVR: ++ etm_write(val, ETMSQ21EVR); ++ break; ++ case ETMSQ23EVR: ++ etm_write(val, ETMSQ23EVR); ++ break; ++ case ETMSQ31EVR: ++ etm_write(val, ETMSQ31EVR); ++ break; ++ case ETMSQ32EVR: ++ etm_write(val, ETMSQ32EVR); ++ break; ++ case ETMSQ13EVR: ++ etm_write(val, ETMSQ13EVR); ++ break; ++ case ETMSQR: ++ etm_write(val, ETMSQR); ++ break; ++ case ETMEXTOUTEVRn(0): ++ etm_write(val, ETMEXTOUTEVR0); ++ break; ++ case ETMEXTOUTEVRn(1): ++ etm_write(val, ETMEXTOUTEVR1); ++ break; ++ case ETMEXTOUTEVRn(2): ++ etm_write(val, ETMEXTOUTEVR2); ++ break; ++ case ETMEXTOUTEVRn(3): ++ etm_write(val, ETMEXTOUTEVR3); ++ break; ++ case ETMCIDCVRn(0): ++ etm_write(val, ETMCIDCVR0); ++ break; ++ case ETMCIDCVRn(1): ++ etm_write(val, ETMCIDCVR1); ++ break; ++ case ETMCIDCVRn(2): ++ etm_write(val, ETMCIDCVR2); ++ break; ++ case ETMCIDCMR: ++ etm_write(val, ETMCIDCMR); ++ break; ++ case ETMIMPSPEC0: ++ etm_write(val, ETMIMPSPEC0); ++ break; ++ case ETMIMPSPEC1: ++ etm_write(val, ETMIMPSPEC1); ++ break; ++ case ETMIMPSPEC2: ++ etm_write(val, ETMIMPSPEC2); ++ break; ++ case ETMIMPSPEC3: ++ etm_write(val, ETMIMPSPEC3); ++ break; ++ case ETMIMPSPEC4: ++ etm_write(val, ETMIMPSPEC4); ++ break; ++ case ETMIMPSPEC5: ++ etm_write(val, ETMIMPSPEC5); ++ break; ++ case ETMIMPSPEC6: ++ etm_write(val, ETMIMPSPEC6); ++ break; ++ case ETMIMPSPEC7: ++ etm_write(val, ETMIMPSPEC7); ++ break; ++ case ETMSYNCFR: ++ etm_write(val, ETMSYNCFR); ++ break; ++ case ETMEXTINSELR: ++ etm_write(val, ETMEXTINSELR); ++ break; ++ case ETMTESSEICR: ++ etm_write(val, ETMTESSEICR); ++ break; ++ case ETMEIBCR: ++ etm_write(val, ETMEIBCR); ++ break; ++ case ETMTSEVR: ++ etm_write(val, ETMTSEVR); ++ break; ++ case ETMAUXCR: ++ etm_write(val, ETMAUXCR); ++ break; ++ case ETMTRACEIDR: ++ etm_write(val, ETMTRACEIDR); ++ break; ++ case ETMVMIDCVR: ++ etm_write(val, ETMVMIDCVR); ++ break; ++ case ETMOSLAR: ++ etm_write(val, ETMOSLAR); ++ break; ++ case ETMOSSRR: ++ etm_write(val, ETMOSSRR); ++ break; ++ case ETMPDCR: ++ etm_write(val, ETMPDCR); ++ break; ++ case ETMPDSR: ++ etm_write(val, ETMPDSR); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} +diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h +new file mode 100644 +index 0000000..501c5fa +--- /dev/null ++++ b/drivers/hwtracing/coresight/coresight-etm.h +@@ -0,0 +1,251 @@ ++/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _CORESIGHT_CORESIGHT_ETM_H ++#define _CORESIGHT_CORESIGHT_ETM_H ++ ++#include <linux/spinlock.h> ++#include "coresight-priv.h" ++ ++/* ++ * Device registers: ++ * 0x000 - 0x2FC: Trace registers ++ * 0x300 - 0x314: Management registers ++ * 0x318 - 0xEFC: Trace registers ++ * ++ * Coresight registers ++ * 0xF00 - 0xF9C: Management registers ++ * 0xFA0 - 0xFA4: Management registers in PFTv1.0 ++ * Trace registers in PFTv1.1 ++ * 0xFA8 - 0xFFC: Management registers ++ */ ++ ++/* Trace registers (0x000-0x2FC) */ ++#define ETMCR 0x000 ++#define ETMCCR 0x004 ++#define ETMTRIGGER 0x008 ++#define ETMSR 0x010 ++#define ETMSCR 0x014 ++#define ETMTSSCR 0x018 ++#define ETMTECR2 0x01c ++#define ETMTEEVR 0x020 ++#define ETMTECR1 0x024 ++#define ETMFFLR 0x02c ++#define ETMACVRn(n) (0x040 + (n * 4)) ++#define ETMACTRn(n) (0x080 + (n * 4)) ++#define ETMCNTRLDVRn(n) (0x140 + (n * 4)) ++#define ETMCNTENRn(n) (0x150 + (n * 4)) ++#define ETMCNTRLDEVRn(n) (0x160 + (n * 4)) ++#define ETMCNTVRn(n) (0x170 + (n * 4)) ++#define ETMSQ12EVR 0x180 ++#define ETMSQ21EVR 0x184 ++#define ETMSQ23EVR 0x188 ++#define ETMSQ31EVR 0x18c ++#define ETMSQ32EVR 0x190 ++#define ETMSQ13EVR 0x194 ++#define ETMSQR 0x19c ++#define ETMEXTOUTEVRn(n) (0x1a0 + (n * 4)) ++#define ETMCIDCVRn(n) (0x1b0 + (n * 4)) ++#define ETMCIDCMR 0x1bc ++#define ETMIMPSPEC0 0x1c0 ++#define ETMIMPSPEC1 0x1c4 ++#define ETMIMPSPEC2 0x1c8 ++#define ETMIMPSPEC3 0x1cc ++#define ETMIMPSPEC4 0x1d0 ++#define ETMIMPSPEC5 0x1d4 ++#define ETMIMPSPEC6 0x1d8 ++#define ETMIMPSPEC7 0x1dc ++#define ETMSYNCFR 0x1e0 ++#define ETMIDR 0x1e4 ++#define ETMCCER 0x1e8 ++#define ETMEXTINSELR 0x1ec ++#define ETMTESSEICR 0x1f0 ++#define ETMEIBCR 0x1f4 ++#define ETMTSEVR 0x1f8 ++#define ETMAUXCR 0x1fc ++#define ETMTRACEIDR 0x200 ++#define ETMVMIDCVR 0x240 ++/* Management registers (0x300-0x314) */ ++#define ETMOSLAR 0x300 ++#define ETMOSLSR 0x304 ++#define ETMOSSRR 0x308 ++#define ETMPDCR 0x310 ++#define ETMPDSR 0x314 ++#define ETM_MAX_ADDR_CMP 16 ++#define ETM_MAX_CNTR 4 ++#define ETM_MAX_CTXID_CMP 3 ++ ++/* Register definition */ ++/* ETMCR - 0x00 */ ++#define ETMCR_PWD_DWN BIT(0) ++#define ETMCR_STALL_MODE BIT(7) ++#define ETMCR_ETM_PRG BIT(10) ++#define ETMCR_ETM_EN BIT(11) ++#define ETMCR_CYC_ACC BIT(12) ++#define ETMCR_CTXID_SIZE (BIT(14)|BIT(15)) ++#define ETMCR_TIMESTAMP_EN BIT(28) ++/* ETMCCR - 0x04 */ ++#define ETMCCR_FIFOFULL BIT(23) ++/* ETMPDCR - 0x310 */ ++#define ETMPDCR_PWD_UP BIT(3) ++/* ETMTECR1 - 0x024 */ ++#define ETMTECR1_ADDR_COMP_1 BIT(0) ++#define ETMTECR1_INC_EXC BIT(24) ++#define ETMTECR1_START_STOP BIT(25) ++/* ETMCCER - 0x1E8 */ ++#define ETMCCER_TIMESTAMP BIT(22) ++ ++#define ETM_MODE_EXCLUDE BIT(0) ++#define ETM_MODE_CYCACC BIT(1) ++#define ETM_MODE_STALL BIT(2) ++#define ETM_MODE_TIMESTAMP BIT(3) ++#define ETM_MODE_CTXID BIT(4) ++#define ETM_MODE_ALL 0x1f ++ ++#define ETM_SQR_MASK 0x3 ++#define ETM_TRACEID_MASK 0x3f ++#define ETM_EVENT_MASK 0x1ffff ++#define ETM_SYNC_MASK 0xfff ++#define ETM_ALL_MASK 0xffffffff ++ ++#define ETMSR_PROG_BIT 1 ++#define ETM_SEQ_STATE_MAX_VAL (0x2) ++#define PORT_SIZE_MASK (GENMASK(21, 21) | GENMASK(6, 4)) ++ ++#define ETM_HARD_WIRE_RES_A /* Hard wired, always true */ \ ++ ((0x0f << 0) | \ ++ /* Resource index A */ \ ++ (0x06 << 4)) ++ ++#define ETM_ADD_COMP_0 /* Single addr comparator 1 */ \ ++ ((0x00 << 7) | \ ++ /* Resource index B */ \ ++ (0x00 << 11)) ++ ++#define ETM_EVENT_NOT_A BIT(14) /* NOT(A) */ ++ ++#define ETM_DEFAULT_EVENT_VAL (ETM_HARD_WIRE_RES_A | \ ++ ETM_ADD_COMP_0 | \ ++ ETM_EVENT_NOT_A) ++/** ++ * struct etm_drvdata - specifics associated to an ETM component ++ * @base: memory mapped base address for this component. ++ * @dev: the device entity associated to this component. ++ * @csdev: component vitals needed by the framework. ++ * @clk: the clock this component is associated to. ++ * @spinlock: only one at a time pls. ++ * @cpu: the cpu this component is affined to. ++ * @port_size: port size as reported by ETMCR bit 4-6 and 21. ++ * @arch: ETM/PTM version number. ++ * @use_cpu14: true if management registers need to be accessed via CP14. ++ * @enable: is this ETM/PTM currently tracing. ++ * @sticky_enable: true if ETM base configuration has been done. ++ * @boot_enable:true if we should start tracing at boot time. ++ * @os_unlock: true if access to management registers is allowed. ++ * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR. ++ * @nr_cntr: Number of counters as found in ETMCCR bit 13-15. ++ * @nr_ext_inp: Number of external input as found in ETMCCR bit 17-19. ++ * @nr_ext_out: Number of external output as found in ETMCCR bit 20-22. ++ * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25. ++ * @etmccr: value of register ETMCCR. ++ * @etmccer: value of register ETMCCER. ++ * @traceid: value of the current ID for this component. ++ * @mode: controls various modes supported by this ETM/PTM. ++ * @ctrl: used in conjunction with @mode. ++ * @trigger_event: setting for register ETMTRIGGER. ++ * @startstop_ctrl: setting for register ETMTSSCR. ++ * @enable_event: setting for register ETMTEEVR. ++ * @enable_ctrl1: setting for register ETMTECR1. ++ * @fifofull_level: setting for register ETMFFLR. ++ * @addr_idx: index for the address comparator selection. ++ * @addr_val: value for address comparator register. ++ * @addr_acctype: access type for address comparator register. ++ * @addr_type: current status of the comparator register. ++ * @cntr_idx: index for the counter register selection. ++ * @cntr_rld_val: reload value of a counter register. ++ * @cntr_event: control for counter enable register. ++ * @cntr_rld_event: value for counter reload event register. ++ * @cntr_val: counter value register. ++ * @seq_12_event: event causing the transition from 1 to 2. ++ * @seq_21_event: event causing the transition from 2 to 1. ++ * @seq_23_event: event causing the transition from 2 to 3. ++ * @seq_31_event: event causing the transition from 3 to 1. ++ * @seq_32_event: event causing the transition from 3 to 2. ++ * @seq_13_event: event causing the transition from 1 to 3. ++ * @seq_curr_state: current value of the sequencer register. ++ * @ctxid_idx: index for the context ID registers. ++ * @ctxid_val: value for the context ID to trigger on. ++ * @ctxid_mask: mask applicable to all the context IDs. ++ * @sync_freq: Synchronisation frequency. ++ * @timestamp_event: Defines an event that requests the insertion ++ of a timestamp into the trace stream. ++ */ ++struct etm_drvdata { ++ void __iomem *base; ++ struct device *dev; ++ struct coresight_device *csdev; ++ struct clk *clk; ++ spinlock_t spinlock; ++ int cpu; ++ int port_size; ++ u8 arch; ++ bool use_cp14; ++ bool enable; ++ bool sticky_enable; ++ bool boot_enable; ++ bool os_unlock; ++ u8 nr_addr_cmp; ++ u8 nr_cntr; ++ u8 nr_ext_inp; ++ u8 nr_ext_out; ++ u8 nr_ctxid_cmp; ++ u32 etmccr; ++ u32 etmccer; ++ u32 traceid; ++ u32 mode; ++ u32 ctrl; ++ u32 trigger_event; ++ u32 startstop_ctrl; ++ u32 enable_event; ++ u32 enable_ctrl1; ++ u32 fifofull_level; ++ u8 addr_idx; ++ u32 addr_val[ETM_MAX_ADDR_CMP]; ++ u32 addr_acctype[ETM_MAX_ADDR_CMP]; ++ u32 addr_type[ETM_MAX_ADDR_CMP]; ++ u8 cntr_idx; ++ u32 cntr_rld_val[ETM_MAX_CNTR]; ++ u32 cntr_event[ETM_MAX_CNTR]; ++ u32 cntr_rld_event[ETM_MAX_CNTR]; ++ u32 cntr_val[ETM_MAX_CNTR]; ++ u32 seq_12_event; ++ u32 seq_21_event; ++ u32 seq_23_event; ++ u32 seq_31_event; ++ u32 seq_32_event; ++ u32 seq_13_event; ++ u32 seq_curr_state; ++ u8 ctxid_idx; ++ u32 ctxid_val[ETM_MAX_CTXID_CMP]; ++ u32 ctxid_mask; ++ u32 sync_freq; ++ u32 timestamp_event; ++}; ++ ++enum etm_addr_type { ++ ETM_ADDR_TYPE_NONE, ++ ETM_ADDR_TYPE_SINGLE, ++ ETM_ADDR_TYPE_RANGE, ++ ETM_ADDR_TYPE_START, ++ ETM_ADDR_TYPE_STOP, ++}; ++#endif +diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c +new file mode 100644 +index 0000000..c965f57 +--- /dev/null ++++ b/drivers/hwtracing/coresight/coresight-etm3x.c +@@ -0,0 +1,1932 @@ ++/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/device.h> ++#include <linux/io.h> ++#include <linux/err.h> ++#include <linux/fs.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/smp.h> ++#include <linux/sysfs.h> ++#include <linux/stat.h> ++#include <linux/clk.h> ++#include <linux/cpu.h> ++#include <linux/of.h> ++#include <linux/coresight.h> ++#include <linux/amba/bus.h> ++#include <linux/seq_file.h> ++#include <linux/uaccess.h> ++#include <asm/sections.h> ++ ++#include "coresight-etm.h" ++ ++static int boot_enable; ++module_param_named(boot_enable, boot_enable, int, S_IRUGO); ++ ++/* The number of ETM/PTM currently registered */ ++static int etm_count; ++static struct etm_drvdata *etmdrvdata[NR_CPUS]; ++ ++static inline void etm_writel(struct etm_drvdata *drvdata, ++ u32 val, u32 off) ++{ ++ if (drvdata->use_cp14) { ++ if (etm_writel_cp14(off, val)) { ++ dev_err(drvdata->dev, ++ "invalid CP14 access to ETM reg: %#x", off); ++ } ++ } else { ++ writel_relaxed(val, drvdata->base + off); ++ } ++} ++ ++static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) ++{ ++ u32 val; ++ ++ if (drvdata->use_cp14) { ++ if (etm_readl_cp14(off, &val)) { ++ dev_err(drvdata->dev, ++ "invalid CP14 access to ETM reg: %#x", off); ++ } ++ } else { ++ val = readl_relaxed(drvdata->base + off); ++ } ++ ++ return val; ++} ++ ++/* ++ * Memory mapped writes to clear os lock are not supported on some processors ++ * and OS lock must be unlocked before any memory mapped access on such ++ * processors, otherwise memory mapped reads/writes will be invalid. ++ */ ++static void etm_os_unlock(void *info) ++{ ++ struct etm_drvdata *drvdata = (struct etm_drvdata *)info; ++ /* Writing any value to ETMOSLAR unlocks the trace registers */ ++ etm_writel(drvdata, 0x0, ETMOSLAR); ++ isb(); ++} ++ ++static void etm_set_pwrdwn(struct etm_drvdata *drvdata) ++{ ++ u32 etmcr; ++ ++ /* Ensure pending cp14 accesses complete before setting pwrdwn */ ++ mb(); ++ isb(); ++ etmcr = etm_readl(drvdata, ETMCR); ++ etmcr |= ETMCR_PWD_DWN; ++ etm_writel(drvdata, etmcr, ETMCR); ++} ++ ++static void etm_clr_pwrdwn(struct etm_drvdata *drvdata) ++{ ++ u32 etmcr; ++ ++ etmcr = etm_readl(drvdata, ETMCR); ++ etmcr &= ~ETMCR_PWD_DWN; ++ etm_writel(drvdata, etmcr, ETMCR); ++ /* Ensure pwrup completes before subsequent cp14 accesses */ ++ mb(); ++ isb(); ++} ++ ++static void etm_set_pwrup(struct etm_drvdata *drvdata) ++{ ++ u32 etmpdcr; ++ ++ etmpdcr = readl_relaxed(drvdata->base + ETMPDCR); ++ etmpdcr |= ETMPDCR_PWD_UP; ++ writel_relaxed(etmpdcr, drvdata->base + ETMPDCR); ++ /* Ensure pwrup completes before subsequent cp14 accesses */ ++ mb(); ++ isb(); ++} ++ ++static void etm_clr_pwrup(struct etm_drvdata *drvdata) ++{ ++ u32 etmpdcr; ++ ++ /* Ensure pending cp14 accesses complete before clearing pwrup */ ++ mb(); ++ isb(); ++ etmpdcr = readl_relaxed(drvdata->base + ETMPDCR); ++ etmpdcr &= ~ETMPDCR_PWD_UP; ++ writel_relaxed(etmpdcr, drvdata->base + ETMPDCR); ++} ++ ++/** ++ * coresight_timeout_etm - loop until a bit has changed to a specific state. ++ * @drvdata: etm's private data structure. ++ * @offset: address of a register, starting from @addr. ++ * @position: the position of the bit of interest. ++ * @value: the value the bit should have. ++ * ++ * Basically the same as @coresight_timeout except for the register access ++ * method where we have to account for CP14 configurations. ++ ++ * Return: 0 as soon as the bit has taken the desired state or -EAGAIN if ++ * TIMEOUT_US has elapsed, which ever happens first. ++ */ ++ ++static int coresight_timeout_etm(struct etm_drvdata *drvdata, u32 offset, ++ int position, int value) ++{ ++ int i; ++ u32 val; ++ ++ for (i = TIMEOUT_US; i > 0; i--) { ++ val = etm_readl(drvdata, offset); ++ /* Waiting on the bit to go from 0 to 1 */ ++ if (value) { ++ if (val & BIT(position)) ++ return 0; ++ /* Waiting on the bit to go from 1 to 0 */ ++ } else { ++ if (!(val & BIT(position))) ++ return 0; ++ } ++ ++ /* ++ * Delay is arbitrary - the specification doesn't say how long ++ * we are expected to wait. Extra check required to make sure ++ * we don't wait needlessly on the last iteration. ++ */ ++ if (i - 1) ++ udelay(1); ++ } ++ ++ return -EAGAIN; ++} ++ ++ ++static void etm_set_prog(struct etm_drvdata *drvdata) ++{ ++ u32 etmcr; ++ ++ etmcr = etm_readl(drvdata, ETMCR); ++ etmcr |= ETMCR_ETM_PRG; ++ etm_writel(drvdata, etmcr, ETMCR); ++ /* ++ * Recommended by spec for cp14 accesses to ensure etmcr write is ++ * complete before polling etmsr ++ */ ++ isb(); ++ if (coresight_timeout_etm(drvdata, ETMSR, ETMSR_PROG_BIT, 1)) { ++ dev_err(drvdata->dev, ++ "timeout observed when probing at offset %#x\n", ETMSR); ++ } ++} ++ ++static void etm_clr_prog(struct etm_drvdata *drvdata) ++{ ++ u32 etmcr; ++ ++ etmcr = etm_readl(drvdata, ETMCR); ++ etmcr &= ~ETMCR_ETM_PRG; ++ etm_writel(drvdata, etmcr, ETMCR); ++ /* ++ * Recommended by spec for cp14 accesses to ensure etmcr write is ++ * complete before polling etmsr ++ */ ++ isb(); ++ if (coresight_timeout_etm(drvdata, ETMSR, ETMSR_PROG_BIT, 0)) { ++ dev_err(drvdata->dev, ++ "timeout observed when probing at offset %#x\n", ETMSR); ++ } ++} ++ ++static void etm_set_default(struct etm_drvdata *drvdata) ++{ ++ int i; ++ ++ drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; ++ drvdata->enable_event = ETM_HARD_WIRE_RES_A; ++ ++ drvdata->seq_12_event = ETM_DEFAULT_EVENT_VAL; ++ drvdata->seq_21_event = ETM_DEFAULT_EVENT_VAL; ++ drvdata->seq_23_event = ETM_DEFAULT_EVENT_VAL; ++ drvdata->seq_31_event = ETM_DEFAULT_EVENT_VAL; ++ drvdata->seq_32_event = ETM_DEFAULT_EVENT_VAL; ++ drvdata->seq_13_event = ETM_DEFAULT_EVENT_VAL; ++ drvdata->timestamp_event = ETM_DEFAULT_EVENT_VAL; ++ ++ for (i = 0; i < drvdata->nr_cntr; i++) { ++ drvdata->cntr_rld_val[i] = 0x0; ++ drvdata->cntr_event[i] = ETM_DEFAULT_EVENT_VAL; ++ drvdata->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL; ++ drvdata->cntr_val[i] = 0x0; ++ } ++ ++ drvdata->seq_curr_state = 0x0; ++ drvdata->ctxid_idx = 0x0; ++ for (i = 0; i < drvdata->nr_ctxid_cmp; i++) ++ drvdata->ctxid_val[i] = 0x0; ++ drvdata->ctxid_mask = 0x0; ++} ++ ++static void etm_enable_hw(void *info) ++{ ++ int i; ++ u32 etmcr; ++ struct etm_drvdata *drvdata = info; ++ ++ CS_UNLOCK(drvdata->base); ++ ++ /* Turn engine on */ ++ etm_clr_pwrdwn(drvdata); ++ /* Apply power to trace registers */ ++ etm_set_pwrup(drvdata); ++ /* Make sure all registers are accessible */ ++ etm_os_unlock(drvdata); ++ ++ etm_set_prog(drvdata); ++ ++ etmcr = etm_readl(drvdata, ETMCR); ++ etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG); ++ etmcr |= drvdata->port_size; ++ etm_writel(drvdata, drvdata->ctrl | etmcr, ETMCR); ++ etm_writel(drvdata, drvdata->trigger_event, ETMTRIGGER); ++ etm_writel(drvdata, drvdata->startstop_ctrl, ETMTSSCR); ++ etm_writel(drvdata, drvdata->enable_event, ETMTEEVR); ++ etm_writel(drvdata, drvdata->enable_ctrl1, ETMTECR1); ++ etm_writel(drvdata, drvdata->fifofull_level, ETMFFLR); ++ for (i = 0; i < drvdata->nr_addr_cmp; i++) { ++ etm_writel(drvdata, drvdata->addr_val[i], ETMACVRn(i)); ++ etm_writel(drvdata, drvdata->addr_acctype[i], ETMACTRn(i)); ++ } ++ for (i = 0; i < drvdata->nr_cntr; i++) { ++ etm_writel(drvdata, drvdata->cntr_rld_val[i], ETMCNTRLDVRn(i)); ++ etm_writel(drvdata, drvdata->cntr_event[i], ETMCNTENRn(i)); ++ etm_writel(drvdata, drvdata->cntr_rld_event[i], ++ ETMCNTRLDEVRn(i)); ++ etm_writel(drvdata, drvdata->cntr_val[i], ETMCNTVRn(i)); ++ } ++ etm_writel(drvdata, drvdata->seq_12_event, ETMSQ12EVR); ++ etm_writel(drvdata, drvdata->seq_21_event, ETMSQ21EVR); ++ etm_writel(drvdata, drvdata->seq_23_event, ETMSQ23EVR); ++ etm_writel(drvdata, drvdata->seq_31_event, ETMSQ31EVR); ++ etm_writel(drvdata, drvdata->seq_32_event, ETMSQ32EVR); ++ etm_writel(drvdata, drvdata->seq_13_event, ETMSQ13EVR); ++ etm_writel(drvdata, drvdata->seq_curr_state, ETMSQR); ++ for (i = 0; i < drvdata->nr_ext_out; i++) ++ etm_writel(drvdata, ETM_DEFAULT_EVENT_VAL, ETMEXTOUTEVRn(i)); ++ for (i = 0; i < drvdata->nr_ctxid_cmp; i++) ++ etm_writel(drvdata, drvdata->ctxid_val[i], ETMCIDCVRn(i)); ++ etm_writel(drvdata, drvdata->ctxid_mask, ETMCIDCMR); ++ etm_writel(drvdata, drvdata->sync_freq, ETMSYNCFR); ++ /* No external input selected */ ++ etm_writel(drvdata, 0x0, ETMEXTINSELR); ++ etm_writel(drvdata, drvdata->timestamp_event, ETMTSEVR); ++ /* No auxiliary control selected */ ++ etm_writel(drvdata, 0x0, ETMAUXCR); ++ etm_writel(drvdata, drvdata->traceid, ETMTRACEIDR); ++ /* No VMID comparator value selected */ ++ etm_writel(drvdata, 0x0, ETMVMIDCVR); ++ ++ /* Ensures trace output is enabled from this ETM */ ++ etm_writel(drvdata, drvdata->ctrl | ETMCR_ETM_EN | etmcr, ETMCR); ++ ++ etm_clr_prog(drvdata); ++ CS_LOCK(drvdata->base); ++ ++ dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); ++} ++ ++static int etm_trace_id_simple(struct etm_drvdata *drvdata) ++{ ++ if (!drvdata->enable) ++ return drvdata->traceid; ++ ++ return (etm_readl(drvdata, ETMTRACEIDR) & ETM_TRACEID_MASK); ++} ++ ++static int etm_trace_id(struct coresight_device *csdev) ++{ ++ struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ unsigned long flags; ++ int trace_id = -1; ++ ++ if (!drvdata->enable) ++ return drvdata->traceid; ++ ++ if (clk_prepare_enable(drvdata->clk)) ++ goto out; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ ++ CS_UNLOCK(drvdata->base); ++ trace_id = (etm_readl(drvdata, ETMTRACEIDR) & ETM_TRACEID_MASK); ++ CS_LOCK(drvdata->base); ++ ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ clk_disable_unprepare(drvdata->clk); ++out: ++ return trace_id; ++} ++ ++static int etm_enable(struct coresight_device *csdev) ++{ ++ struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ int ret; ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ goto err_clk; ++ ++ spin_lock(&drvdata->spinlock); ++ ++ /* ++ * Configure the ETM only if the CPU is online. If it isn't online ++ * hw configuration will take place when 'CPU_STARTING' is received ++ * in @etm_cpu_callback. ++ */ ++ if (cpu_online(drvdata->cpu)) { ++ ret = smp_call_function_single(drvdata->cpu, ++ etm_enable_hw, drvdata, 1); ++ if (ret) ++ goto err; ++ } ++ ++ drvdata->enable = true; ++ drvdata->sticky_enable = true; ++ ++ spin_unlock(&drvdata->spinlock); ++ ++ dev_info(drvdata->dev, "ETM tracing enabled\n"); ++ return 0; ++err: ++ spin_unlock(&drvdata->spinlock); ++ clk_disable_unprepare(drvdata->clk); ++err_clk: ++ return ret; ++} ++ ++static void etm_disable_hw(void *info) ++{ ++ int i; ++ struct etm_drvdata *drvdata = info; ++ ++ CS_UNLOCK(drvdata->base); ++ etm_set_prog(drvdata); ++ ++ /* Program trace enable to low by using always false event */ ++ etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR); ++ ++ /* Read back sequencer and counters for post trace analysis */ ++ drvdata->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); ++ ++ for (i = 0; i < drvdata->nr_cntr; i++) ++ drvdata->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i)); ++ ++ etm_set_pwrdwn(drvdata); ++ CS_LOCK(drvdata->base); ++ ++ dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); ++} ++ ++static void etm_disable(struct coresight_device *csdev) ++{ ++ struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ ++ /* ++ * Taking hotplug lock here protects from clocks getting disabled ++ * with tracing being left on (crash scenario) if user disable occurs ++ * after cpu online mask indicates the cpu is offline but before the ++ * DYING hotplug callback is serviced by the ETM driver. ++ */ ++ get_online_cpus(); ++ spin_lock(&drvdata->spinlock); ++ ++ /* ++ * Executing etm_disable_hw on the cpu whose ETM is being disabled ++ * ensures that register writes occur when cpu is powered. ++ */ ++ smp_call_function_single(drvdata->cpu, etm_disable_hw, drvdata, 1); ++ drvdata->enable = false; ++ ++ spin_unlock(&drvdata->spinlock); ++ put_online_cpus(); ++ ++ clk_disable_unprepare(drvdata->clk); ++ ++ dev_info(drvdata->dev, "ETM tracing disabled\n"); ++} ++ ++static const struct coresight_ops_source etm_source_ops = { ++ .trace_id = etm_trace_id, ++ .enable = etm_enable, ++ .disable = etm_disable, ++}; ++ ++static const struct coresight_ops etm_cs_ops = { ++ .source_ops = &etm_source_ops, ++}; ++ ++static ssize_t nr_addr_cmp_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->nr_addr_cmp; ++ return sprintf(buf, "%#lx\n", val); ++} ++static DEVICE_ATTR_RO(nr_addr_cmp); ++ ++static ssize_t nr_cntr_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->nr_cntr; ++ return sprintf(buf, "%#lx\n", val); ++} ++static DEVICE_ATTR_RO(nr_cntr); ++ ++static ssize_t nr_ctxid_cmp_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->nr_ctxid_cmp; ++ return sprintf(buf, "%#lx\n", val); ++} ++static DEVICE_ATTR_RO(nr_ctxid_cmp); ++ ++static ssize_t etmsr_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int ret; ++ unsigned long flags, val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ CS_UNLOCK(drvdata->base); ++ ++ val = etm_readl(drvdata, ETMSR); ++ ++ CS_LOCK(drvdata->base); ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ clk_disable_unprepare(drvdata->clk); ++ ++ return sprintf(buf, "%#lx\n", val); ++} ++static DEVICE_ATTR_RO(etmsr); ++ ++static ssize_t reset_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int i, ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ if (val) { ++ spin_lock(&drvdata->spinlock); ++ drvdata->mode = ETM_MODE_EXCLUDE; ++ drvdata->ctrl = 0x0; ++ drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; ++ drvdata->startstop_ctrl = 0x0; ++ drvdata->addr_idx = 0x0; ++ for (i = 0; i < drvdata->nr_addr_cmp; i++) { ++ drvdata->addr_val[i] = 0x0; ++ drvdata->addr_acctype[i] = 0x0; ++ drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; ++ } ++ drvdata->cntr_idx = 0x0; ++ ++ etm_set_default(drvdata); ++ spin_unlock(&drvdata->spinlock); ++ } ++ ++ return size; ++} ++static DEVICE_ATTR_WO(reset); ++ ++static ssize_t mode_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->mode; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t mode_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ spin_lock(&drvdata->spinlock); ++ drvdata->mode = val & ETM_MODE_ALL; ++ ++ if (drvdata->mode & ETM_MODE_EXCLUDE) ++ drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC; ++ else ++ drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC; ++ ++ if (drvdata->mode & ETM_MODE_CYCACC) ++ drvdata->ctrl |= ETMCR_CYC_ACC; ++ else ++ drvdata->ctrl &= ~ETMCR_CYC_ACC; ++ ++ if (drvdata->mode & ETM_MODE_STALL) { ++ if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) { ++ dev_warn(drvdata->dev, "stall mode not supported\n"); ++ ret = -EINVAL; ++ goto err_unlock; ++ } ++ drvdata->ctrl |= ETMCR_STALL_MODE; ++ } else ++ drvdata->ctrl &= ~ETMCR_STALL_MODE; ++ ++ if (drvdata->mode & ETM_MODE_TIMESTAMP) { ++ if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) { ++ dev_warn(drvdata->dev, "timestamp not supported\n"); ++ ret = -EINVAL; ++ goto err_unlock; ++ } ++ drvdata->ctrl |= ETMCR_TIMESTAMP_EN; ++ } else ++ drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN; ++ ++ if (drvdata->mode & ETM_MODE_CTXID) ++ drvdata->ctrl |= ETMCR_CTXID_SIZE; ++ else ++ drvdata->ctrl &= ~ETMCR_CTXID_SIZE; ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++ ++err_unlock: ++ spin_unlock(&drvdata->spinlock); ++ return ret; ++} ++static DEVICE_ATTR_RW(mode); ++ ++static ssize_t trigger_event_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->trigger_event; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t trigger_event_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->trigger_event = val & ETM_EVENT_MASK; ++ ++ return size; ++} ++static DEVICE_ATTR_RW(trigger_event); ++ ++static ssize_t enable_event_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->enable_event; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t enable_event_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->enable_event = val & ETM_EVENT_MASK; ++ ++ return size; ++} ++static DEVICE_ATTR_RW(enable_event); ++ ++static ssize_t fifofull_level_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->fifofull_level; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t fifofull_level_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->fifofull_level = val; ++ ++ return size; ++} ++static DEVICE_ATTR_RW(fifofull_level); ++ ++static ssize_t addr_idx_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->addr_idx; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t addr_idx_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ if (val >= drvdata->nr_addr_cmp) ++ return -EINVAL; ++ ++ /* ++ * Use spinlock to ensure index doesn't change while it gets ++ * dereferenced multiple times within a spinlock block elsewhere. ++ */ ++ spin_lock(&drvdata->spinlock); ++ drvdata->addr_idx = val; ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(addr_idx); ++ ++static ssize_t addr_single_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ u8 idx; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ spin_lock(&drvdata->spinlock); ++ idx = drvdata->addr_idx; ++ if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || ++ drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { ++ spin_unlock(&drvdata->spinlock); ++ return -EINVAL; ++ } ++ ++ val = drvdata->addr_val[idx]; ++ spin_unlock(&drvdata->spinlock); ++ ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t addr_single_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ u8 idx; ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ spin_lock(&drvdata->spinlock); ++ idx = drvdata->addr_idx; ++ if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || ++ drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { ++ spin_unlock(&drvdata->spinlock); ++ return -EINVAL; ++ } ++ ++ drvdata->addr_val[idx] = val; ++ drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(addr_single); ++ ++static ssize_t addr_range_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ u8 idx; ++ unsigned long val1, val2; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ spin_lock(&drvdata->spinlock); ++ idx = drvdata->addr_idx; ++ if (idx % 2 != 0) { ++ spin_unlock(&drvdata->spinlock); ++ return -EPERM; ++ } ++ if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && ++ drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || ++ (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && ++ drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { ++ spin_unlock(&drvdata->spinlock); ++ return -EPERM; ++ } ++ ++ val1 = drvdata->addr_val[idx]; ++ val2 = drvdata->addr_val[idx + 1]; ++ spin_unlock(&drvdata->spinlock); ++ ++ return sprintf(buf, "%#lx %#lx\n", val1, val2); ++} ++ ++static ssize_t addr_range_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ u8 idx; ++ unsigned long val1, val2; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) ++ return -EINVAL; ++ /* Lower address comparator cannot have a higher address value */ ++ if (val1 > val2) ++ return -EINVAL; ++ ++ spin_lock(&drvdata->spinlock); ++ idx = drvdata->addr_idx; ++ if (idx % 2 != 0) { ++ spin_unlock(&drvdata->spinlock); ++ return -EPERM; ++ } ++ if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && ++ drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || ++ (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && ++ drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { ++ spin_unlock(&drvdata->spinlock); ++ return -EPERM; ++ } ++ ++ drvdata->addr_val[idx] = val1; ++ drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; ++ drvdata->addr_val[idx + 1] = val2; ++ drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; ++ drvdata->enable_ctrl1 |= (1 << (idx/2)); ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(addr_range); ++ ++static ssize_t addr_start_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ u8 idx; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ spin_lock(&drvdata->spinlock); ++ idx = drvdata->addr_idx; ++ if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || ++ drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { ++ spin_unlock(&drvdata->spinlock); ++ return -EPERM; ++ } ++ ++ val = drvdata->addr_val[idx]; ++ spin_unlock(&drvdata->spinlock); ++ ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t addr_start_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ u8 idx; ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ spin_lock(&drvdata->spinlock); ++ idx = drvdata->addr_idx; ++ if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || ++ drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { ++ spin_unlock(&drvdata->spinlock); ++ return -EPERM; ++ } ++ ++ drvdata->addr_val[idx] = val; ++ drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; ++ drvdata->startstop_ctrl |= (1 << idx); ++ drvdata->enable_ctrl1 |= BIT(25); ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(addr_start); ++ ++static ssize_t addr_stop_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ u8 idx; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ spin_lock(&drvdata->spinlock); ++ idx = drvdata->addr_idx; ++ if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || ++ drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { ++ spin_unlock(&drvdata->spinlock); ++ return -EPERM; ++ } ++ ++ val = drvdata->addr_val[idx]; ++ spin_unlock(&drvdata->spinlock); ++ ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t addr_stop_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ u8 idx; ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ spin_lock(&drvdata->spinlock); ++ idx = drvdata->addr_idx; ++ if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || ++ drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { ++ spin_unlock(&drvdata->spinlock); ++ return -EPERM; ++ } ++ ++ drvdata->addr_val[idx] = val; ++ drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; ++ drvdata->startstop_ctrl |= (1 << (idx + 16)); ++ drvdata->enable_ctrl1 |= ETMTECR1_START_STOP; ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(addr_stop); ++ ++static ssize_t addr_acctype_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ spin_lock(&drvdata->spinlock); ++ val = drvdata->addr_acctype[drvdata->addr_idx]; ++ spin_unlock(&drvdata->spinlock); ++ ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t addr_acctype_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ spin_lock(&drvdata->spinlock); ++ drvdata->addr_acctype[drvdata->addr_idx] = val; ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(addr_acctype); ++ ++static ssize_t cntr_idx_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->cntr_idx; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t cntr_idx_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ if (val >= drvdata->nr_cntr) ++ return -EINVAL; ++ /* ++ * Use spinlock to ensure index doesn't change while it gets ++ * dereferenced multiple times within a spinlock block elsewhere. ++ */ ++ spin_lock(&drvdata->spinlock); ++ drvdata->cntr_idx = val; ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(cntr_idx); ++ ++static ssize_t cntr_rld_val_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ spin_lock(&drvdata->spinlock); ++ val = drvdata->cntr_rld_val[drvdata->cntr_idx]; ++ spin_unlock(&drvdata->spinlock); ++ ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t cntr_rld_val_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ spin_lock(&drvdata->spinlock); ++ drvdata->cntr_rld_val[drvdata->cntr_idx] = val; ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(cntr_rld_val); ++ ++static ssize_t cntr_event_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ spin_lock(&drvdata->spinlock); ++ val = drvdata->cntr_event[drvdata->cntr_idx]; ++ spin_unlock(&drvdata->spinlock); ++ ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t cntr_event_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ spin_lock(&drvdata->spinlock); ++ drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(cntr_event); ++ ++static ssize_t cntr_rld_event_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ spin_lock(&drvdata->spinlock); ++ val = drvdata->cntr_rld_event[drvdata->cntr_idx]; ++ spin_unlock(&drvdata->spinlock); ++ ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t cntr_rld_event_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ spin_lock(&drvdata->spinlock); ++ drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(cntr_rld_event); ++ ++static ssize_t cntr_val_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int i, ret = 0; ++ u32 val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ if (!drvdata->enable) { ++ spin_lock(&drvdata->spinlock); ++ for (i = 0; i < drvdata->nr_cntr; i++) ++ ret += sprintf(buf, "counter %d: %x\n", ++ i, drvdata->cntr_val[i]); ++ spin_unlock(&drvdata->spinlock); ++ return ret; ++ } ++ ++ for (i = 0; i < drvdata->nr_cntr; i++) { ++ val = etm_readl(drvdata, ETMCNTVRn(i)); ++ ret += sprintf(buf, "counter %d: %x\n", i, val); ++ } ++ ++ return ret; ++} ++ ++static ssize_t cntr_val_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ spin_lock(&drvdata->spinlock); ++ drvdata->cntr_val[drvdata->cntr_idx] = val; ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(cntr_val); ++ ++static ssize_t seq_12_event_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->seq_12_event; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t seq_12_event_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->seq_12_event = val & ETM_EVENT_MASK; ++ return size; ++} ++static DEVICE_ATTR_RW(seq_12_event); ++ ++static ssize_t seq_21_event_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->seq_21_event; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t seq_21_event_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->seq_21_event = val & ETM_EVENT_MASK; ++ return size; ++} ++static DEVICE_ATTR_RW(seq_21_event); ++ ++static ssize_t seq_23_event_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->seq_23_event; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t seq_23_event_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->seq_23_event = val & ETM_EVENT_MASK; ++ return size; ++} ++static DEVICE_ATTR_RW(seq_23_event); ++ ++static ssize_t seq_31_event_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->seq_31_event; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t seq_31_event_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->seq_31_event = val & ETM_EVENT_MASK; ++ return size; ++} ++static DEVICE_ATTR_RW(seq_31_event); ++ ++static ssize_t seq_32_event_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->seq_32_event; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t seq_32_event_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->seq_32_event = val & ETM_EVENT_MASK; ++ return size; ++} ++static DEVICE_ATTR_RW(seq_32_event); ++ ++static ssize_t seq_13_event_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->seq_13_event; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t seq_13_event_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->seq_13_event = val & ETM_EVENT_MASK; ++ return size; ++} ++static DEVICE_ATTR_RW(seq_13_event); ++ ++static ssize_t seq_curr_state_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int ret; ++ unsigned long val, flags; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ if (!drvdata->enable) { ++ val = drvdata->seq_curr_state; ++ goto out; ++ } ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ ++ CS_UNLOCK(drvdata->base); ++ val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); ++ CS_LOCK(drvdata->base); ++ ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ clk_disable_unprepare(drvdata->clk); ++out: ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t seq_curr_state_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ if (val > ETM_SEQ_STATE_MAX_VAL) ++ return -EINVAL; ++ ++ drvdata->seq_curr_state = val; ++ ++ return size; ++} ++static DEVICE_ATTR_RW(seq_curr_state); ++ ++static ssize_t ctxid_idx_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->ctxid_idx; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t ctxid_idx_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ if (val >= drvdata->nr_ctxid_cmp) ++ return -EINVAL; ++ ++ /* ++ * Use spinlock to ensure index doesn't change while it gets ++ * dereferenced multiple times within a spinlock block elsewhere. ++ */ ++ spin_lock(&drvdata->spinlock); ++ drvdata->ctxid_idx = val; ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(ctxid_idx); ++ ++static ssize_t ctxid_val_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ spin_lock(&drvdata->spinlock); ++ val = drvdata->ctxid_val[drvdata->ctxid_idx]; ++ spin_unlock(&drvdata->spinlock); ++ ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t ctxid_val_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ spin_lock(&drvdata->spinlock); ++ drvdata->ctxid_val[drvdata->ctxid_idx] = val; ++ spin_unlock(&drvdata->spinlock); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(ctxid_val); ++ ++static ssize_t ctxid_mask_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->ctxid_mask; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t ctxid_mask_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->ctxid_mask = val; ++ return size; ++} ++static DEVICE_ATTR_RW(ctxid_mask); ++ ++static ssize_t sync_freq_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->sync_freq; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t sync_freq_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->sync_freq = val & ETM_SYNC_MASK; ++ return size; ++} ++static DEVICE_ATTR_RW(sync_freq); ++ ++static ssize_t timestamp_event_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ val = drvdata->timestamp_event; ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t timestamp_event_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->timestamp_event = val & ETM_EVENT_MASK; ++ return size; ++} ++static DEVICE_ATTR_RW(timestamp_event); ++ ++static ssize_t status_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int ret; ++ unsigned long flags; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ ++ CS_UNLOCK(drvdata->base); ++ ret = sprintf(buf, ++ "ETMCCR: 0x%08x\n" ++ "ETMCCER: 0x%08x\n" ++ "ETMSCR: 0x%08x\n" ++ "ETMIDR: 0x%08x\n" ++ "ETMCR: 0x%08x\n" ++ "ETMTRACEIDR: 0x%08x\n" ++ "Enable event: 0x%08x\n" ++ "Enable start/stop: 0x%08x\n" ++ "Enable control: CR1 0x%08x CR2 0x%08x\n" ++ "CPU affinity: %d\n", ++ drvdata->etmccr, drvdata->etmccer, ++ etm_readl(drvdata, ETMSCR), etm_readl(drvdata, ETMIDR), ++ etm_readl(drvdata, ETMCR), etm_trace_id_simple(drvdata), ++ etm_readl(drvdata, ETMTEEVR), ++ etm_readl(drvdata, ETMTSSCR), ++ etm_readl(drvdata, ETMTECR1), ++ etm_readl(drvdata, ETMTECR2), ++ drvdata->cpu); ++ CS_LOCK(drvdata->base); ++ ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ clk_disable_unprepare(drvdata->clk); ++ ++ return ret; ++} ++static DEVICE_ATTR_RO(status); ++ ++static ssize_t traceid_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int ret; ++ unsigned long val, flags; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ if (!drvdata->enable) { ++ val = drvdata->traceid; ++ goto out; ++ } ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ CS_UNLOCK(drvdata->base); ++ ++ val = (etm_readl(drvdata, ETMTRACEIDR) & ETM_TRACEID_MASK); ++ ++ CS_LOCK(drvdata->base); ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ clk_disable_unprepare(drvdata->clk); ++out: ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t traceid_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->traceid = val & ETM_TRACEID_MASK; ++ return size; ++} ++static DEVICE_ATTR_RW(traceid); ++ ++static struct attribute *coresight_etm_attrs[] = { ++ &dev_attr_nr_addr_cmp.attr, ++ &dev_attr_nr_cntr.attr, ++ &dev_attr_nr_ctxid_cmp.attr, ++ &dev_attr_etmsr.attr, ++ &dev_attr_reset.attr, ++ &dev_attr_mode.attr, ++ &dev_attr_trigger_event.attr, ++ &dev_attr_enable_event.attr, ++ &dev_attr_fifofull_level.attr, ++ &dev_attr_addr_idx.attr, ++ &dev_attr_addr_single.attr, ++ &dev_attr_addr_range.attr, ++ &dev_attr_addr_start.attr, ++ &dev_attr_addr_stop.attr, ++ &dev_attr_addr_acctype.attr, ++ &dev_attr_cntr_idx.attr, ++ &dev_attr_cntr_rld_val.attr, ++ &dev_attr_cntr_event.attr, ++ &dev_attr_cntr_rld_event.attr, ++ &dev_attr_cntr_val.attr, ++ &dev_attr_seq_12_event.attr, ++ &dev_attr_seq_21_event.attr, ++ &dev_attr_seq_23_event.attr, ++ &dev_attr_seq_31_event.attr, ++ &dev_attr_seq_32_event.attr, ++ &dev_attr_seq_13_event.attr, ++ &dev_attr_seq_curr_state.attr, ++ &dev_attr_ctxid_idx.attr, ++ &dev_attr_ctxid_val.attr, ++ &dev_attr_ctxid_mask.attr, ++ &dev_attr_sync_freq.attr, ++ &dev_attr_timestamp_event.attr, ++ &dev_attr_status.attr, ++ &dev_attr_traceid.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(coresight_etm); ++ ++static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action, ++ void *hcpu) ++{ ++ unsigned int cpu = (unsigned long)hcpu; ++ ++ if (!etmdrvdata[cpu]) ++ goto out; ++ ++ switch (action & (~CPU_TASKS_FROZEN)) { ++ case CPU_STARTING: ++ spin_lock(&etmdrvdata[cpu]->spinlock); ++ if (!etmdrvdata[cpu]->os_unlock) { ++ etm_os_unlock(etmdrvdata[cpu]); ++ etmdrvdata[cpu]->os_unlock = true; ++ } ++ ++ if (etmdrvdata[cpu]->enable) ++ etm_enable_hw(etmdrvdata[cpu]); ++ spin_unlock(&etmdrvdata[cpu]->spinlock); ++ break; ++ ++ case CPU_ONLINE: ++ if (etmdrvdata[cpu]->boot_enable && ++ !etmdrvdata[cpu]->sticky_enable) ++ coresight_enable(etmdrvdata[cpu]->csdev); ++ break; ++ ++ case CPU_DYING: ++ spin_lock(&etmdrvdata[cpu]->spinlock); ++ if (etmdrvdata[cpu]->enable) ++ etm_disable_hw(etmdrvdata[cpu]); ++ spin_unlock(&etmdrvdata[cpu]->spinlock); ++ break; ++ } ++out: ++ return NOTIFY_OK; ++} ++ ++static struct notifier_block etm_cpu_notifier = { ++ .notifier_call = etm_cpu_callback, ++}; ++ ++static bool etm_arch_supported(u8 arch) ++{ ++ switch (arch) { ++ case ETM_ARCH_V3_3: ++ break; ++ case ETM_ARCH_V3_5: ++ break; ++ case PFT_ARCH_V1_0: ++ break; ++ case PFT_ARCH_V1_1: ++ break; ++ default: ++ return false; ++ } ++ return true; ++} ++ ++static void etm_init_arch_data(void *info) ++{ ++ u32 etmidr; ++ u32 etmccr; ++ struct etm_drvdata *drvdata = info; ++ ++ CS_UNLOCK(drvdata->base); ++ ++ /* First dummy read */ ++ (void)etm_readl(drvdata, ETMPDSR); ++ /* Provide power to ETM: ETMPDCR[3] == 1 */ ++ etm_set_pwrup(drvdata); ++ /* ++ * Clear power down bit since when this bit is set writes to ++ * certain registers might be ignored. ++ */ ++ etm_clr_pwrdwn(drvdata); ++ /* ++ * Set prog bit. It will be set from reset but this is included to ++ * ensure it is set ++ */ ++ etm_set_prog(drvdata); ++ ++ /* Find all capabilities */ ++ etmidr = etm_readl(drvdata, ETMIDR); ++ drvdata->arch = BMVAL(etmidr, 4, 11); ++ drvdata->port_size = etm_readl(drvdata, ETMCR) & PORT_SIZE_MASK; ++ ++ drvdata->etmccer = etm_readl(drvdata, ETMCCER); ++ etmccr = etm_readl(drvdata, ETMCCR); ++ drvdata->etmccr = etmccr; ++ drvdata->nr_addr_cmp = BMVAL(etmccr, 0, 3) * 2; ++ drvdata->nr_cntr = BMVAL(etmccr, 13, 15); ++ drvdata->nr_ext_inp = BMVAL(etmccr, 17, 19); ++ drvdata->nr_ext_out = BMVAL(etmccr, 20, 22); ++ drvdata->nr_ctxid_cmp = BMVAL(etmccr, 24, 25); ++ ++ etm_set_pwrdwn(drvdata); ++ etm_clr_pwrup(drvdata); ++ CS_LOCK(drvdata->base); ++} ++ ++static void etm_init_default_data(struct etm_drvdata *drvdata) ++{ ++ /* ++ * A trace ID of value 0 is invalid, so let's start at some ++ * random value that fits in 7 bits and will be just as good. ++ */ ++ static int etm3x_traceid = 0x10; ++ ++ u32 flags = (1 << 0 | /* instruction execute*/ ++ 3 << 3 | /* ARM instruction */ ++ 0 << 5 | /* No data value comparison */ ++ 0 << 7 | /* No exact mach */ ++ 0 << 8 | /* Ignore context ID */ ++ 0 << 10); /* Security ignored */ ++ ++ /* ++ * Initial configuration only - guarantees sources handled by ++ * this driver have a unique ID at startup time but not between ++ * all other types of sources. For that we lean on the core ++ * framework. ++ */ ++ drvdata->traceid = etm3x_traceid++; ++ drvdata->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN); ++ drvdata->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; ++ if (drvdata->nr_addr_cmp >= 2) { ++ drvdata->addr_val[0] = (u32) _stext; ++ drvdata->addr_val[1] = (u32) _etext; ++ drvdata->addr_acctype[0] = flags; ++ drvdata->addr_acctype[1] = flags; ++ drvdata->addr_type[0] = ETM_ADDR_TYPE_RANGE; ++ drvdata->addr_type[1] = ETM_ADDR_TYPE_RANGE; ++ } ++ ++ etm_set_default(drvdata); ++} ++ ++static int etm_probe(struct amba_device *adev, const struct amba_id *id) ++{ ++ int ret; ++ void __iomem *base; ++ struct device *dev = &adev->dev; ++ struct coresight_platform_data *pdata = NULL; ++ struct etm_drvdata *drvdata; ++ struct resource *res = &adev->res; ++ struct coresight_desc *desc; ++ struct device_node *np = adev->dev.of_node; ++ ++ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); ++ if (!drvdata) ++ return -ENOMEM; ++ ++ if (np) { ++ pdata = of_get_coresight_platform_data(dev, np); ++ if (IS_ERR(pdata)) ++ return PTR_ERR(pdata); ++ ++ adev->dev.platform_data = pdata; ++ drvdata->use_cp14 = of_property_read_bool(np, "arm,cp14"); ++ } ++ ++ drvdata->dev = &adev->dev; ++ dev_set_drvdata(dev, drvdata); ++ ++ /* Validity for the resource is already checked by the AMBA core */ ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ drvdata->base = base; ++ ++ spin_lock_init(&drvdata->spinlock); ++ ++ drvdata->clk = adev->pclk; ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ drvdata->cpu = pdata ? pdata->cpu : 0; ++ ++ get_online_cpus(); ++ etmdrvdata[drvdata->cpu] = drvdata; ++ ++ if (!smp_call_function_single(drvdata->cpu, etm_os_unlock, drvdata, 1)) ++ drvdata->os_unlock = true; ++ ++ if (smp_call_function_single(drvdata->cpu, ++ etm_init_arch_data, drvdata, 1)) ++ dev_err(dev, "ETM arch init failed\n"); ++ ++ if (!etm_count++) ++ register_hotcpu_notifier(&etm_cpu_notifier); ++ ++ put_online_cpus(); ++ ++ if (etm_arch_supported(drvdata->arch) == false) { ++ ret = -EINVAL; ++ goto err_arch_supported; ++ } ++ etm_init_default_data(drvdata); ++ ++ clk_disable_unprepare(drvdata->clk); ++ ++ desc->type = CORESIGHT_DEV_TYPE_SOURCE; ++ desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; ++ desc->ops = &etm_cs_ops; ++ desc->pdata = pdata; ++ desc->dev = dev; ++ desc->groups = coresight_etm_groups; ++ drvdata->csdev = coresight_register(desc); ++ if (IS_ERR(drvdata->csdev)) { ++ ret = PTR_ERR(drvdata->csdev); ++ goto err_arch_supported; ++ } ++ ++ dev_info(dev, "ETM initialized\n"); ++ ++ if (boot_enable) { ++ coresight_enable(drvdata->csdev); ++ drvdata->boot_enable = true; ++ } ++ ++ return 0; ++ ++err_arch_supported: ++ clk_disable_unprepare(drvdata->clk); ++ if (--etm_count == 0) ++ unregister_hotcpu_notifier(&etm_cpu_notifier); ++ return ret; ++} ++ ++static int etm_remove(struct amba_device *adev) ++{ ++ struct etm_drvdata *drvdata = amba_get_drvdata(adev); ++ ++ coresight_unregister(drvdata->csdev); ++ if (--etm_count == 0) ++ unregister_hotcpu_notifier(&etm_cpu_notifier); ++ ++ return 0; ++} ++ ++static struct amba_id etm_ids[] = { ++ { /* ETM 3.3 */ ++ .id = 0x0003b921, ++ .mask = 0x0003ffff, ++ }, ++ { /* ETM 3.5 */ ++ .id = 0x0003b956, ++ .mask = 0x0003ffff, ++ }, ++ { /* PTM 1.0 */ ++ .id = 0x0003b950, ++ .mask = 0x0003ffff, ++ }, ++ { /* PTM 1.1 */ ++ .id = 0x0003b95f, ++ .mask = 0x0003ffff, ++ }, ++ { 0, 0}, ++}; ++ ++static struct amba_driver etm_driver = { ++ .drv = { ++ .name = "coresight-etm3x", ++ .owner = THIS_MODULE, ++ }, ++ .probe = etm_probe, ++ .remove = etm_remove, ++ .id_table = etm_ids, ++}; ++ ++int __init etm_init(void) ++{ ++ return amba_driver_register(&etm_driver); ++} ++module_init(etm_init); ++ ++void __exit etm_exit(void) ++{ ++ amba_driver_unregister(&etm_driver); ++} ++module_exit(etm_exit); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("CoreSight Program Flow Trace driver"); +diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c +new file mode 100644 +index 0000000..3db36f7 +--- /dev/null ++++ b/drivers/hwtracing/coresight/coresight-funnel.c +@@ -0,0 +1,258 @@ ++/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/device.h> ++#include <linux/err.h> ++#include <linux/fs.h> ++#include <linux/slab.h> ++#include <linux/clk.h> ++#include <linux/coresight.h> ++#include <linux/amba/bus.h> ++ ++#include "coresight-priv.h" ++ ++#define FUNNEL_FUNCTL 0x000 ++#define FUNNEL_PRICTL 0x004 ++ ++#define FUNNEL_HOLDTIME_MASK 0xf00 ++#define FUNNEL_HOLDTIME_SHFT 0x8 ++#define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT) ++ ++/** ++ * struct funnel_drvdata - specifics associated to a funnel component ++ * @base: memory mapped base address for this component. ++ * @dev: the device entity associated to this component. ++ * @csdev: component vitals needed by the framework. ++ * @clk: the clock this component is associated to. ++ * @priority: port selection order. ++ */ ++struct funnel_drvdata { ++ void __iomem *base; ++ struct device *dev; ++ struct coresight_device *csdev; ++ struct clk *clk; ++ unsigned long priority; ++}; ++ ++static void funnel_enable_hw(struct funnel_drvdata *drvdata, int port) ++{ ++ u32 functl; ++ ++ CS_UNLOCK(drvdata->base); ++ ++ functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); ++ functl &= ~FUNNEL_HOLDTIME_MASK; ++ functl |= FUNNEL_HOLDTIME; ++ functl |= (1 << port); ++ writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); ++ writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL); ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static int funnel_enable(struct coresight_device *csdev, int inport, ++ int outport) ++{ ++ struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ int ret; ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ funnel_enable_hw(drvdata, inport); ++ ++ dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport); ++ return 0; ++} ++ ++static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport) ++{ ++ u32 functl; ++ ++ CS_UNLOCK(drvdata->base); ++ ++ functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); ++ functl &= ~(1 << inport); ++ writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static void funnel_disable(struct coresight_device *csdev, int inport, ++ int outport) ++{ ++ struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ ++ funnel_disable_hw(drvdata, inport); ++ ++ clk_disable_unprepare(drvdata->clk); ++ ++ dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport); ++} ++ ++static const struct coresight_ops_link funnel_link_ops = { ++ .enable = funnel_enable, ++ .disable = funnel_disable, ++}; ++ ++static const struct coresight_ops funnel_cs_ops = { ++ .link_ops = &funnel_link_ops, ++}; ++ ++static ssize_t priority_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ unsigned long val = drvdata->priority; ++ ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t priority_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->priority = val; ++ return size; ++} ++static DEVICE_ATTR_RW(priority); ++ ++static u32 get_funnel_ctrl_hw(struct funnel_drvdata *drvdata) ++{ ++ u32 functl; ++ ++ CS_UNLOCK(drvdata->base); ++ functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); ++ CS_LOCK(drvdata->base); ++ ++ return functl; ++} ++ ++static ssize_t funnel_ctrl_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int ret; ++ u32 val; ++ struct funnel_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ val = get_funnel_ctrl_hw(drvdata); ++ clk_disable_unprepare(drvdata->clk); ++ ++ return sprintf(buf, "%#x\n", val); ++} ++static DEVICE_ATTR_RO(funnel_ctrl); ++ ++static struct attribute *coresight_funnel_attrs[] = { ++ &dev_attr_funnel_ctrl.attr, ++ &dev_attr_priority.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(coresight_funnel); ++ ++static int funnel_probe(struct amba_device *adev, const struct amba_id *id) ++{ ++ void __iomem *base; ++ struct device *dev = &adev->dev; ++ struct coresight_platform_data *pdata = NULL; ++ struct funnel_drvdata *drvdata; ++ struct resource *res = &adev->res; ++ struct coresight_desc *desc; ++ struct device_node *np = adev->dev.of_node; ++ ++ if (np) { ++ pdata = of_get_coresight_platform_data(dev, np); ++ if (IS_ERR(pdata)) ++ return PTR_ERR(pdata); ++ adev->dev.platform_data = pdata; ++ } ++ ++ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); ++ if (!drvdata) ++ return -ENOMEM; ++ ++ drvdata->dev = &adev->dev; ++ dev_set_drvdata(dev, drvdata); ++ ++ /* Validity for the resource is already checked by the AMBA core */ ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ drvdata->base = base; ++ ++ drvdata->clk = adev->pclk; ++ ++ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ desc->type = CORESIGHT_DEV_TYPE_LINK; ++ desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG; ++ desc->ops = &funnel_cs_ops; ++ desc->pdata = pdata; ++ desc->dev = dev; ++ desc->groups = coresight_funnel_groups; ++ drvdata->csdev = coresight_register(desc); ++ if (IS_ERR(drvdata->csdev)) ++ return PTR_ERR(drvdata->csdev); ++ ++ dev_info(dev, "FUNNEL initialized\n"); ++ return 0; ++} ++ ++static int funnel_remove(struct amba_device *adev) ++{ ++ struct funnel_drvdata *drvdata = amba_get_drvdata(adev); ++ ++ coresight_unregister(drvdata->csdev); ++ return 0; ++} ++ ++static struct amba_id funnel_ids[] = { ++ { ++ .id = 0x0003b908, ++ .mask = 0x0003ffff, ++ }, ++ { 0, 0}, ++}; ++ ++static struct amba_driver funnel_driver = { ++ .drv = { ++ .name = "coresight-funnel", ++ .owner = THIS_MODULE, ++ }, ++ .probe = funnel_probe, ++ .remove = funnel_remove, ++ .id_table = funnel_ids, ++}; ++ ++module_amba_driver(funnel_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("CoreSight Funnel driver"); +diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h +new file mode 100644 +index 0000000..62fcd98 +--- /dev/null ++++ b/drivers/hwtracing/coresight/coresight-priv.h +@@ -0,0 +1,63 @@ ++/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _CORESIGHT_PRIV_H ++#define _CORESIGHT_PRIV_H ++ ++#include <linux/bitops.h> ++#include <linux/io.h> ++#include <linux/coresight.h> ++ ++/* ++ * Coresight management registers (0xf00-0xfcc) ++ * 0xfa0 - 0xfa4: Management registers in PFTv1.0 ++ * Trace registers in PFTv1.1 ++ */ ++#define CORESIGHT_ITCTRL 0xf00 ++#define CORESIGHT_CLAIMSET 0xfa0 ++#define CORESIGHT_CLAIMCLR 0xfa4 ++#define CORESIGHT_LAR 0xfb0 ++#define CORESIGHT_LSR 0xfb4 ++#define CORESIGHT_AUTHSTATUS 0xfb8 ++#define CORESIGHT_DEVID 0xfc8 ++#define CORESIGHT_DEVTYPE 0xfcc ++ ++#define TIMEOUT_US 100 ++#define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb) ++ ++static inline void CS_LOCK(void __iomem *addr) ++{ ++ do { ++ /* Wait for things to settle */ ++ mb(); ++ writel_relaxed(0x0, addr + CORESIGHT_LAR); ++ } while (0); ++} ++ ++static inline void CS_UNLOCK(void __iomem *addr) ++{ ++ do { ++ writel_relaxed(CORESIGHT_UNLOCK, addr + CORESIGHT_LAR); ++ /* Make sure everyone has seen this */ ++ mb(); ++ } while (0); ++} ++ ++#ifdef CONFIG_CORESIGHT_SOURCE_ETM3X ++extern int etm_readl_cp14(u32 off, unsigned int *val); ++extern int etm_writel_cp14(u32 off, u32 val); ++#else ++static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } ++static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } ++#endif ++ ++#endif +diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c +new file mode 100644 +index 0000000..cdf0553 +--- /dev/null ++++ b/drivers/hwtracing/coresight/coresight-replicator.c +@@ -0,0 +1,137 @@ ++/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/io.h> ++#include <linux/err.h> ++#include <linux/slab.h> ++#include <linux/clk.h> ++#include <linux/of.h> ++#include <linux/coresight.h> ++ ++#include "coresight-priv.h" ++ ++/** ++ * struct replicator_drvdata - specifics associated to a replicator component ++ * @dev: the device entity associated with this component ++ * @csdev: component vitals needed by the framework ++ */ ++struct replicator_drvdata { ++ struct device *dev; ++ struct coresight_device *csdev; ++}; ++ ++static int replicator_enable(struct coresight_device *csdev, int inport, ++ int outport) ++{ ++ struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ ++ dev_info(drvdata->dev, "REPLICATOR enabled\n"); ++ return 0; ++} ++ ++static void replicator_disable(struct coresight_device *csdev, int inport, ++ int outport) ++{ ++ struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ ++ dev_info(drvdata->dev, "REPLICATOR disabled\n"); ++} ++ ++static const struct coresight_ops_link replicator_link_ops = { ++ .enable = replicator_enable, ++ .disable = replicator_disable, ++}; ++ ++static const struct coresight_ops replicator_cs_ops = { ++ .link_ops = &replicator_link_ops, ++}; ++ ++static int replicator_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct coresight_platform_data *pdata = NULL; ++ struct replicator_drvdata *drvdata; ++ struct coresight_desc *desc; ++ struct device_node *np = pdev->dev.of_node; ++ ++ if (np) { ++ pdata = of_get_coresight_platform_data(dev, np); ++ if (IS_ERR(pdata)) ++ return PTR_ERR(pdata); ++ pdev->dev.platform_data = pdata; ++ } ++ ++ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); ++ if (!drvdata) ++ return -ENOMEM; ++ ++ drvdata->dev = &pdev->dev; ++ platform_set_drvdata(pdev, drvdata); ++ ++ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ desc->type = CORESIGHT_DEV_TYPE_LINK; ++ desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT; ++ desc->ops = &replicator_cs_ops; ++ desc->pdata = pdev->dev.platform_data; ++ desc->dev = &pdev->dev; ++ drvdata->csdev = coresight_register(desc); ++ if (IS_ERR(drvdata->csdev)) ++ return PTR_ERR(drvdata->csdev); ++ ++ dev_info(dev, "REPLICATOR initialized\n"); ++ return 0; ++} ++ ++static int replicator_remove(struct platform_device *pdev) ++{ ++ struct replicator_drvdata *drvdata = platform_get_drvdata(pdev); ++ ++ coresight_unregister(drvdata->csdev); ++ return 0; ++} ++ ++static struct of_device_id replicator_match[] = { ++ {.compatible = "arm,coresight-replicator"}, ++ {} ++}; ++ ++static struct platform_driver replicator_driver = { ++ .probe = replicator_probe, ++ .remove = replicator_remove, ++ .driver = { ++ .name = "coresight-replicator", ++ .of_match_table = replicator_match, ++ }, ++}; ++ ++static int __init replicator_init(void) ++{ ++ return platform_driver_register(&replicator_driver); ++} ++module_init(replicator_init); ++ ++static void __exit replicator_exit(void) ++{ ++ platform_driver_unregister(&replicator_driver); ++} ++module_exit(replicator_exit); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("CoreSight Replicator driver"); +diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c +new file mode 100644 +index 0000000..7147f3d +--- /dev/null ++++ b/drivers/hwtracing/coresight/coresight-tmc.c +@@ -0,0 +1,822 @@ ++/* Copyright (c) 2012, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/device.h> ++#include <linux/io.h> ++#include <linux/err.h> ++#include <linux/fs.h> ++#include <linux/miscdevice.h> ++#include <linux/uaccess.h> ++#include <linux/slab.h> ++#include <linux/dma-mapping.h> ++#include <linux/spinlock.h> ++#include <linux/clk.h> ++#include <linux/of.h> ++#include <linux/coresight.h> ++#include <linux/amba/bus.h> ++ ++#include "coresight-priv.h" ++ ++#define TMC_RSZ 0x004 ++#define TMC_STS 0x00c ++#define TMC_RRD 0x010 ++#define TMC_RRP 0x014 ++#define TMC_RWP 0x018 ++#define TMC_TRG 0x01c ++#define TMC_CTL 0x020 ++#define TMC_RWD 0x024 ++#define TMC_MODE 0x028 ++#define TMC_LBUFLEVEL 0x02c ++#define TMC_CBUFLEVEL 0x030 ++#define TMC_BUFWM 0x034 ++#define TMC_RRPHI 0x038 ++#define TMC_RWPHI 0x03c ++#define TMC_AXICTL 0x110 ++#define TMC_DBALO 0x118 ++#define TMC_DBAHI 0x11c ++#define TMC_FFSR 0x300 ++#define TMC_FFCR 0x304 ++#define TMC_PSCR 0x308 ++#define TMC_ITMISCOP0 0xee0 ++#define TMC_ITTRFLIN 0xee8 ++#define TMC_ITATBDATA0 0xeec ++#define TMC_ITATBCTR2 0xef0 ++#define TMC_ITATBCTR1 0xef4 ++#define TMC_ITATBCTR0 0xef8 ++ ++/* register description */ ++/* TMC_CTL - 0x020 */ ++#define TMC_CTL_CAPT_EN BIT(0) ++/* TMC_STS - 0x00C */ ++#define TMC_STS_TRIGGERED BIT(1) ++/* TMC_AXICTL - 0x110 */ ++#define TMC_AXICTL_PROT_CTL_B0 BIT(0) ++#define TMC_AXICTL_PROT_CTL_B1 BIT(1) ++#define TMC_AXICTL_SCT_GAT_MODE BIT(7) ++#define TMC_AXICTL_WR_BURST_LEN 0xF00 ++/* TMC_FFCR - 0x304 */ ++#define TMC_FFCR_EN_FMT BIT(0) ++#define TMC_FFCR_EN_TI BIT(1) ++#define TMC_FFCR_FON_FLIN BIT(4) ++#define TMC_FFCR_FON_TRIG_EVT BIT(5) ++#define TMC_FFCR_FLUSHMAN BIT(6) ++#define TMC_FFCR_TRIGON_TRIGIN BIT(8) ++#define TMC_FFCR_STOP_ON_FLUSH BIT(12) ++ ++#define TMC_STS_TRIGGERED_BIT 2 ++#define TMC_FFCR_FLUSHMAN_BIT 6 ++ ++enum tmc_config_type { ++ TMC_CONFIG_TYPE_ETB, ++ TMC_CONFIG_TYPE_ETR, ++ TMC_CONFIG_TYPE_ETF, ++}; ++ ++enum tmc_mode { ++ TMC_MODE_CIRCULAR_BUFFER, ++ TMC_MODE_SOFTWARE_FIFO, ++ TMC_MODE_HARDWARE_FIFO, ++}; ++ ++enum tmc_mem_intf_width { ++ TMC_MEM_INTF_WIDTH_32BITS = 0x2, ++ TMC_MEM_INTF_WIDTH_64BITS = 0x3, ++ TMC_MEM_INTF_WIDTH_128BITS = 0x4, ++ TMC_MEM_INTF_WIDTH_256BITS = 0x5, ++}; ++ ++/** ++ * struct tmc_drvdata - specifics associated to an TMC component ++ * @base: memory mapped base address for this component. ++ * @dev: the device entity associated to this component. ++ * @csdev: component vitals needed by the framework. ++ * @miscdev: specifics to handle "/dev/xyz.tmc" entry. ++ * @clk: the clock this component is associated to. ++ * @spinlock: only one at a time pls. ++ * @read_count: manages preparation of buffer for reading. ++ * @buf: area of memory where trace data get sent. ++ * @paddr: DMA start location in RAM. ++ * @vaddr: virtual representation of @paddr. ++ * @size: @buf size. ++ * @enable: this TMC is being used. ++ * @config_type: TMC variant, must be of type @tmc_config_type. ++ * @trigger_cntr: amount of words to store after a trigger. ++ */ ++struct tmc_drvdata { ++ void __iomem *base; ++ struct device *dev; ++ struct coresight_device *csdev; ++ struct miscdevice miscdev; ++ struct clk *clk; ++ spinlock_t spinlock; ++ int read_count; ++ bool reading; ++ char *buf; ++ dma_addr_t paddr; ++ void __iomem *vaddr; ++ u32 size; ++ bool enable; ++ enum tmc_config_type config_type; ++ u32 trigger_cntr; ++}; ++ ++static void tmc_wait_for_ready(struct tmc_drvdata *drvdata) ++{ ++ /* Ensure formatter, unformatter and hardware fifo are empty */ ++ if (coresight_timeout(drvdata->base, ++ TMC_STS, TMC_STS_TRIGGERED_BIT, 1)) { ++ dev_err(drvdata->dev, ++ "timeout observed when probing at offset %#x\n", ++ TMC_STS); ++ } ++} ++ ++static void tmc_flush_and_stop(struct tmc_drvdata *drvdata) ++{ ++ u32 ffcr; ++ ++ ffcr = readl_relaxed(drvdata->base + TMC_FFCR); ++ ffcr |= TMC_FFCR_STOP_ON_FLUSH; ++ writel_relaxed(ffcr, drvdata->base + TMC_FFCR); ++ ffcr |= TMC_FFCR_FLUSHMAN; ++ writel_relaxed(ffcr, drvdata->base + TMC_FFCR); ++ /* Ensure flush completes */ ++ if (coresight_timeout(drvdata->base, ++ TMC_FFCR, TMC_FFCR_FLUSHMAN_BIT, 0)) { ++ dev_err(drvdata->dev, ++ "timeout observed when probing at offset %#x\n", ++ TMC_FFCR); ++ } ++ ++ tmc_wait_for_ready(drvdata); ++} ++ ++static void tmc_enable_hw(struct tmc_drvdata *drvdata) ++{ ++ writel_relaxed(TMC_CTL_CAPT_EN, drvdata->base + TMC_CTL); ++} ++ ++static void tmc_disable_hw(struct tmc_drvdata *drvdata) ++{ ++ writel_relaxed(0x0, drvdata->base + TMC_CTL); ++} ++ ++static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) ++{ ++ /* Zero out the memory to help with debug */ ++ memset(drvdata->buf, 0, drvdata->size); ++ ++ CS_UNLOCK(drvdata->base); ++ ++ writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); ++ writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | ++ TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | ++ TMC_FFCR_TRIGON_TRIGIN, ++ drvdata->base + TMC_FFCR); ++ ++ writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); ++ tmc_enable_hw(drvdata); ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) ++{ ++ u32 axictl; ++ ++ /* Zero out the memory to help with debug */ ++ memset(drvdata->vaddr, 0, drvdata->size); ++ ++ CS_UNLOCK(drvdata->base); ++ ++ writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ); ++ writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); ++ ++ axictl = readl_relaxed(drvdata->base + TMC_AXICTL); ++ axictl |= TMC_AXICTL_WR_BURST_LEN; ++ writel_relaxed(axictl, drvdata->base + TMC_AXICTL); ++ axictl &= ~TMC_AXICTL_SCT_GAT_MODE; ++ writel_relaxed(axictl, drvdata->base + TMC_AXICTL); ++ axictl = (axictl & ++ ~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) | ++ TMC_AXICTL_PROT_CTL_B1; ++ writel_relaxed(axictl, drvdata->base + TMC_AXICTL); ++ ++ writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO); ++ writel_relaxed(0x0, drvdata->base + TMC_DBAHI); ++ writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | ++ TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | ++ TMC_FFCR_TRIGON_TRIGIN, ++ drvdata->base + TMC_FFCR); ++ writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); ++ tmc_enable_hw(drvdata); ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata) ++{ ++ CS_UNLOCK(drvdata->base); ++ ++ writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE); ++ writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI, ++ drvdata->base + TMC_FFCR); ++ writel_relaxed(0x0, drvdata->base + TMC_BUFWM); ++ tmc_enable_hw(drvdata); ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) ++{ ++ int ret; ++ unsigned long flags; ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ if (drvdata->reading) { ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ clk_disable_unprepare(drvdata->clk); ++ return -EBUSY; ++ } ++ ++ if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { ++ tmc_etb_enable_hw(drvdata); ++ } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { ++ tmc_etr_enable_hw(drvdata); ++ } else { ++ if (mode == TMC_MODE_CIRCULAR_BUFFER) ++ tmc_etb_enable_hw(drvdata); ++ else ++ tmc_etf_enable_hw(drvdata); ++ } ++ drvdata->enable = true; ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ ++ dev_info(drvdata->dev, "TMC enabled\n"); ++ return 0; ++} ++ ++static int tmc_enable_sink(struct coresight_device *csdev) ++{ ++ struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ ++ return tmc_enable(drvdata, TMC_MODE_CIRCULAR_BUFFER); ++} ++ ++static int tmc_enable_link(struct coresight_device *csdev, int inport, ++ int outport) ++{ ++ struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ ++ return tmc_enable(drvdata, TMC_MODE_HARDWARE_FIFO); ++} ++ ++static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) ++{ ++ enum tmc_mem_intf_width memwidth; ++ u8 memwords; ++ char *bufp; ++ u32 read_data; ++ int i; ++ ++ memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10); ++ if (memwidth == TMC_MEM_INTF_WIDTH_32BITS) ++ memwords = 1; ++ else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS) ++ memwords = 2; ++ else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS) ++ memwords = 4; ++ else ++ memwords = 8; ++ ++ bufp = drvdata->buf; ++ while (1) { ++ for (i = 0; i < memwords; i++) { ++ read_data = readl_relaxed(drvdata->base + TMC_RRD); ++ if (read_data == 0xFFFFFFFF) ++ return; ++ memcpy(bufp, &read_data, 4); ++ bufp += 4; ++ } ++ } ++} ++ ++static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) ++{ ++ CS_UNLOCK(drvdata->base); ++ ++ tmc_flush_and_stop(drvdata); ++ tmc_etb_dump_hw(drvdata); ++ tmc_disable_hw(drvdata); ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) ++{ ++ u32 rwp, val; ++ ++ rwp = readl_relaxed(drvdata->base + TMC_RWP); ++ val = readl_relaxed(drvdata->base + TMC_STS); ++ ++ /* How much memory do we still have */ ++ if (val & BIT(0)) ++ drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; ++ else ++ drvdata->buf = drvdata->vaddr; ++} ++ ++static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) ++{ ++ CS_UNLOCK(drvdata->base); ++ ++ tmc_flush_and_stop(drvdata); ++ tmc_etr_dump_hw(drvdata); ++ tmc_disable_hw(drvdata); ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) ++{ ++ CS_UNLOCK(drvdata->base); ++ ++ tmc_flush_and_stop(drvdata); ++ tmc_disable_hw(drvdata); ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ if (drvdata->reading) ++ goto out; ++ ++ if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { ++ tmc_etb_disable_hw(drvdata); ++ } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { ++ tmc_etr_disable_hw(drvdata); ++ } else { ++ if (mode == TMC_MODE_CIRCULAR_BUFFER) ++ tmc_etb_disable_hw(drvdata); ++ else ++ tmc_etf_disable_hw(drvdata); ++ } ++out: ++ drvdata->enable = false; ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ ++ clk_disable_unprepare(drvdata->clk); ++ ++ dev_info(drvdata->dev, "TMC disabled\n"); ++} ++ ++static void tmc_disable_sink(struct coresight_device *csdev) ++{ ++ struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ ++ tmc_disable(drvdata, TMC_MODE_CIRCULAR_BUFFER); ++} ++ ++static void tmc_disable_link(struct coresight_device *csdev, int inport, ++ int outport) ++{ ++ struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ ++ tmc_disable(drvdata, TMC_MODE_HARDWARE_FIFO); ++} ++ ++static const struct coresight_ops_sink tmc_sink_ops = { ++ .enable = tmc_enable_sink, ++ .disable = tmc_disable_sink, ++}; ++ ++static const struct coresight_ops_link tmc_link_ops = { ++ .enable = tmc_enable_link, ++ .disable = tmc_disable_link, ++}; ++ ++static const struct coresight_ops tmc_etb_cs_ops = { ++ .sink_ops = &tmc_sink_ops, ++}; ++ ++static const struct coresight_ops tmc_etr_cs_ops = { ++ .sink_ops = &tmc_sink_ops, ++}; ++ ++static const struct coresight_ops tmc_etf_cs_ops = { ++ .sink_ops = &tmc_sink_ops, ++ .link_ops = &tmc_link_ops, ++}; ++ ++static int tmc_read_prepare(struct tmc_drvdata *drvdata) ++{ ++ int ret; ++ unsigned long flags; ++ enum tmc_mode mode; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ if (!drvdata->enable) ++ goto out; ++ ++ if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { ++ tmc_etb_disable_hw(drvdata); ++ } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { ++ tmc_etr_disable_hw(drvdata); ++ } else { ++ mode = readl_relaxed(drvdata->base + TMC_MODE); ++ if (mode == TMC_MODE_CIRCULAR_BUFFER) { ++ tmc_etb_disable_hw(drvdata); ++ } else { ++ ret = -ENODEV; ++ goto err; ++ } ++ } ++out: ++ drvdata->reading = true; ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ ++ dev_info(drvdata->dev, "TMC read start\n"); ++ return 0; ++err: ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ return ret; ++} ++ ++static void tmc_read_unprepare(struct tmc_drvdata *drvdata) ++{ ++ unsigned long flags; ++ enum tmc_mode mode; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ if (!drvdata->enable) ++ goto out; ++ ++ if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { ++ tmc_etb_enable_hw(drvdata); ++ } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { ++ tmc_etr_enable_hw(drvdata); ++ } else { ++ mode = readl_relaxed(drvdata->base + TMC_MODE); ++ if (mode == TMC_MODE_CIRCULAR_BUFFER) ++ tmc_etb_enable_hw(drvdata); ++ } ++out: ++ drvdata->reading = false; ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ ++ dev_info(drvdata->dev, "TMC read end\n"); ++} ++ ++static int tmc_open(struct inode *inode, struct file *file) ++{ ++ struct tmc_drvdata *drvdata = container_of(file->private_data, ++ struct tmc_drvdata, miscdev); ++ int ret = 0; ++ ++ if (drvdata->read_count++) ++ goto out; ++ ++ ret = tmc_read_prepare(drvdata); ++ if (ret) ++ return ret; ++out: ++ nonseekable_open(inode, file); ++ ++ dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__); ++ return 0; ++} ++ ++static ssize_t tmc_read(struct file *file, char __user *data, size_t len, ++ loff_t *ppos) ++{ ++ struct tmc_drvdata *drvdata = container_of(file->private_data, ++ struct tmc_drvdata, miscdev); ++ char *bufp = drvdata->buf + *ppos; ++ ++ if (*ppos + len > drvdata->size) ++ len = drvdata->size - *ppos; ++ ++ if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { ++ if (bufp == (char *)(drvdata->vaddr + drvdata->size)) ++ bufp = drvdata->vaddr; ++ else if (bufp > (char *)(drvdata->vaddr + drvdata->size)) ++ bufp -= drvdata->size; ++ if ((bufp + len) > (char *)(drvdata->vaddr + drvdata->size)) ++ len = (char *)(drvdata->vaddr + drvdata->size) - bufp; ++ } ++ ++ if (copy_to_user(data, bufp, len)) { ++ dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__); ++ return -EFAULT; ++ } ++ ++ *ppos += len; ++ ++ dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n", ++ __func__, len, (int)(drvdata->size - *ppos)); ++ return len; ++} ++ ++static int tmc_release(struct inode *inode, struct file *file) ++{ ++ struct tmc_drvdata *drvdata = container_of(file->private_data, ++ struct tmc_drvdata, miscdev); ++ ++ if (--drvdata->read_count) { ++ if (drvdata->read_count < 0) { ++ dev_err(drvdata->dev, "mismatched close\n"); ++ drvdata->read_count = 0; ++ } ++ goto out; ++ } ++ ++ tmc_read_unprepare(drvdata); ++out: ++ dev_dbg(drvdata->dev, "%s: released\n", __func__); ++ return 0; ++} ++ ++static const struct file_operations tmc_fops = { ++ .owner = THIS_MODULE, ++ .open = tmc_open, ++ .read = tmc_read, ++ .release = tmc_release, ++ .llseek = no_llseek, ++}; ++ ++static ssize_t status_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int ret; ++ unsigned long flags; ++ u32 tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg; ++ u32 tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr; ++ u32 devid; ++ struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ goto out; ++ ++ spin_lock_irqsave(&drvdata->spinlock, flags); ++ CS_UNLOCK(drvdata->base); ++ ++ tmc_rsz = readl_relaxed(drvdata->base + TMC_RSZ); ++ tmc_sts = readl_relaxed(drvdata->base + TMC_STS); ++ tmc_rrp = readl_relaxed(drvdata->base + TMC_RRP); ++ tmc_rwp = readl_relaxed(drvdata->base + TMC_RWP); ++ tmc_trg = readl_relaxed(drvdata->base + TMC_TRG); ++ tmc_ctl = readl_relaxed(drvdata->base + TMC_CTL); ++ tmc_ffsr = readl_relaxed(drvdata->base + TMC_FFSR); ++ tmc_ffcr = readl_relaxed(drvdata->base + TMC_FFCR); ++ tmc_mode = readl_relaxed(drvdata->base + TMC_MODE); ++ tmc_pscr = readl_relaxed(drvdata->base + TMC_PSCR); ++ devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); ++ ++ CS_LOCK(drvdata->base); ++ spin_unlock_irqrestore(&drvdata->spinlock, flags); ++ ++ clk_disable_unprepare(drvdata->clk); ++ ++ return sprintf(buf, ++ "Depth:\t\t0x%x\n" ++ "Status:\t\t0x%x\n" ++ "RAM read ptr:\t0x%x\n" ++ "RAM wrt ptr:\t0x%x\n" ++ "Trigger cnt:\t0x%x\n" ++ "Control:\t0x%x\n" ++ "Flush status:\t0x%x\n" ++ "Flush ctrl:\t0x%x\n" ++ "Mode:\t\t0x%x\n" ++ "PSRC:\t\t0x%x\n" ++ "DEVID:\t\t0x%x\n", ++ tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg, ++ tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr, devid); ++out: ++ return -EINVAL; ++} ++static DEVICE_ATTR_RO(status); ++ ++static ssize_t trigger_cntr_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ unsigned long val = drvdata->trigger_cntr; ++ ++ return sprintf(buf, "%#lx\n", val); ++} ++ ++static ssize_t trigger_cntr_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return ret; ++ ++ drvdata->trigger_cntr = val; ++ return size; ++} ++static DEVICE_ATTR_RW(trigger_cntr); ++ ++static struct attribute *coresight_etb_attrs[] = { ++ &dev_attr_trigger_cntr.attr, ++ &dev_attr_status.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(coresight_etb); ++ ++static struct attribute *coresight_etr_attrs[] = { ++ &dev_attr_trigger_cntr.attr, ++ &dev_attr_status.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(coresight_etr); ++ ++static struct attribute *coresight_etf_attrs[] = { ++ &dev_attr_trigger_cntr.attr, ++ &dev_attr_status.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(coresight_etf); ++ ++static int tmc_probe(struct amba_device *adev, const struct amba_id *id) ++{ ++ int ret = 0; ++ u32 devid; ++ void __iomem *base; ++ struct device *dev = &adev->dev; ++ struct coresight_platform_data *pdata = NULL; ++ struct tmc_drvdata *drvdata; ++ struct resource *res = &adev->res; ++ struct coresight_desc *desc; ++ struct device_node *np = adev->dev.of_node; ++ ++ if (np) { ++ pdata = of_get_coresight_platform_data(dev, np); ++ if (IS_ERR(pdata)) ++ return PTR_ERR(pdata); ++ adev->dev.platform_data = pdata; ++ } ++ ++ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); ++ if (!drvdata) ++ return -ENOMEM; ++ ++ drvdata->dev = &adev->dev; ++ dev_set_drvdata(dev, drvdata); ++ ++ /* Validity for the resource is already checked by the AMBA core */ ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ drvdata->base = base; ++ ++ spin_lock_init(&drvdata->spinlock); ++ ++ drvdata->clk = adev->pclk; ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); ++ drvdata->config_type = BMVAL(devid, 6, 7); ++ ++ if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { ++ if (np) ++ ret = of_property_read_u32(np, ++ "arm,buffer-size", ++ &drvdata->size); ++ if (ret) ++ drvdata->size = SZ_1M; ++ } else { ++ drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4; ++ } ++ ++ clk_disable_unprepare(drvdata->clk); ++ ++ if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { ++ drvdata->vaddr = dma_alloc_coherent(dev, drvdata->size, ++ &drvdata->paddr, GFP_KERNEL); ++ if (!drvdata->vaddr) ++ return -ENOMEM; ++ ++ memset(drvdata->vaddr, 0, drvdata->size); ++ drvdata->buf = drvdata->vaddr; ++ } else { ++ drvdata->buf = devm_kzalloc(dev, drvdata->size, GFP_KERNEL); ++ if (!drvdata->buf) ++ return -ENOMEM; ++ } ++ ++ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); ++ if (!desc) { ++ ret = -ENOMEM; ++ goto err_devm_kzalloc; ++ } ++ ++ desc->pdata = pdata; ++ desc->dev = dev; ++ desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; ++ ++ if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { ++ desc->type = CORESIGHT_DEV_TYPE_SINK; ++ desc->ops = &tmc_etb_cs_ops; ++ desc->groups = coresight_etb_groups; ++ } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { ++ desc->type = CORESIGHT_DEV_TYPE_SINK; ++ desc->ops = &tmc_etr_cs_ops; ++ desc->groups = coresight_etr_groups; ++ } else { ++ desc->type = CORESIGHT_DEV_TYPE_LINKSINK; ++ desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; ++ desc->ops = &tmc_etf_cs_ops; ++ desc->groups = coresight_etf_groups; ++ } ++ ++ drvdata->csdev = coresight_register(desc); ++ if (IS_ERR(drvdata->csdev)) { ++ ret = PTR_ERR(drvdata->csdev); ++ goto err_devm_kzalloc; ++ } ++ ++ drvdata->miscdev.name = pdata->name; ++ drvdata->miscdev.minor = MISC_DYNAMIC_MINOR; ++ drvdata->miscdev.fops = &tmc_fops; ++ ret = misc_register(&drvdata->miscdev); ++ if (ret) ++ goto err_misc_register; ++ ++ dev_info(dev, "TMC initialized\n"); ++ return 0; ++ ++err_misc_register: ++ coresight_unregister(drvdata->csdev); ++err_devm_kzalloc: ++ if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) ++ dma_free_coherent(dev, drvdata->size, ++ &drvdata->paddr, GFP_KERNEL); ++ return ret; ++} ++ ++static int tmc_remove(struct amba_device *adev) ++{ ++ struct tmc_drvdata *drvdata = amba_get_drvdata(adev); ++ ++ misc_deregister(&drvdata->miscdev); ++ coresight_unregister(drvdata->csdev); ++ if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) ++ dma_free_coherent(drvdata->dev, drvdata->size, ++ &drvdata->paddr, GFP_KERNEL); ++ ++ return 0; ++} ++ ++static struct amba_id tmc_ids[] = { ++ { ++ .id = 0x0003b961, ++ .mask = 0x0003ffff, ++ }, ++ { 0, 0}, ++}; ++ ++static struct amba_driver tmc_driver = { ++ .drv = { ++ .name = "coresight-tmc", ++ .owner = THIS_MODULE, ++ }, ++ .probe = tmc_probe, ++ .remove = tmc_remove, ++ .id_table = tmc_ids, ++}; ++ ++module_amba_driver(tmc_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("CoreSight Trace Memory Controller driver"); +diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c +new file mode 100644 +index 0000000..3b33af2 +--- /dev/null ++++ b/drivers/hwtracing/coresight/coresight-tpiu.c +@@ -0,0 +1,207 @@ ++/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/io.h> ++#include <linux/err.h> ++#include <linux/slab.h> ++#include <linux/clk.h> ++#include <linux/coresight.h> ++#include <linux/amba/bus.h> ++ ++#include "coresight-priv.h" ++ ++#define TPIU_SUPP_PORTSZ 0x000 ++#define TPIU_CURR_PORTSZ 0x004 ++#define TPIU_SUPP_TRIGMODES 0x100 ++#define TPIU_TRIG_CNTRVAL 0x104 ++#define TPIU_TRIG_MULT 0x108 ++#define TPIU_SUPP_TESTPATM 0x200 ++#define TPIU_CURR_TESTPATM 0x204 ++#define TPIU_TEST_PATREPCNTR 0x208 ++#define TPIU_FFSR 0x300 ++#define TPIU_FFCR 0x304 ++#define TPIU_FSYNC_CNTR 0x308 ++#define TPIU_EXTCTL_INPORT 0x400 ++#define TPIU_EXTCTL_OUTPORT 0x404 ++#define TPIU_ITTRFLINACK 0xee4 ++#define TPIU_ITTRFLIN 0xee8 ++#define TPIU_ITATBDATA0 0xeec ++#define TPIU_ITATBCTR2 0xef0 ++#define TPIU_ITATBCTR1 0xef4 ++#define TPIU_ITATBCTR0 0xef8 ++ ++/** register definition **/ ++/* FFCR - 0x304 */ ++#define FFCR_FON_MAN BIT(6) ++ ++/** ++ * @base: memory mapped base address for this component. ++ * @dev: the device entity associated to this component. ++ * @csdev: component vitals needed by the framework. ++ * @clk: the clock this component is associated to. ++ */ ++struct tpiu_drvdata { ++ void __iomem *base; ++ struct device *dev; ++ struct coresight_device *csdev; ++ struct clk *clk; ++}; ++ ++static void tpiu_enable_hw(struct tpiu_drvdata *drvdata) ++{ ++ CS_UNLOCK(drvdata->base); ++ ++ /* TODO: fill this up */ ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static int tpiu_enable(struct coresight_device *csdev) ++{ ++ struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ int ret; ++ ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ tpiu_enable_hw(drvdata); ++ ++ dev_info(drvdata->dev, "TPIU enabled\n"); ++ return 0; ++} ++ ++static void tpiu_disable_hw(struct tpiu_drvdata *drvdata) ++{ ++ CS_UNLOCK(drvdata->base); ++ ++ /* Clear formatter controle reg. */ ++ writel_relaxed(0x0, drvdata->base + TPIU_FFCR); ++ /* Generate manual flush */ ++ writel_relaxed(FFCR_FON_MAN, drvdata->base + TPIU_FFCR); ++ ++ CS_LOCK(drvdata->base); ++} ++ ++static void tpiu_disable(struct coresight_device *csdev) ++{ ++ struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); ++ ++ tpiu_disable_hw(drvdata); ++ ++ clk_disable_unprepare(drvdata->clk); ++ ++ dev_info(drvdata->dev, "TPIU disabled\n"); ++} ++ ++static const struct coresight_ops_sink tpiu_sink_ops = { ++ .enable = tpiu_enable, ++ .disable = tpiu_disable, ++}; ++ ++static const struct coresight_ops tpiu_cs_ops = { ++ .sink_ops = &tpiu_sink_ops, ++}; ++ ++static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) ++{ ++ int ret; ++ void __iomem *base; ++ struct device *dev = &adev->dev; ++ struct coresight_platform_data *pdata = NULL; ++ struct tpiu_drvdata *drvdata; ++ struct resource *res = &adev->res; ++ struct coresight_desc *desc; ++ struct device_node *np = adev->dev.of_node; ++ ++ if (np) { ++ pdata = of_get_coresight_platform_data(dev, np); ++ if (IS_ERR(pdata)) ++ return PTR_ERR(pdata); ++ adev->dev.platform_data = pdata; ++ } ++ ++ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); ++ if (!drvdata) ++ return -ENOMEM; ++ ++ drvdata->dev = &adev->dev; ++ dev_set_drvdata(dev, drvdata); ++ ++ /* Validity for the resource is already checked by the AMBA core */ ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ drvdata->base = base; ++ ++ drvdata->clk = adev->pclk; ++ ret = clk_prepare_enable(drvdata->clk); ++ if (ret) ++ return ret; ++ ++ /* Disable tpiu to support older devices */ ++ tpiu_disable_hw(drvdata); ++ ++ clk_disable_unprepare(drvdata->clk); ++ ++ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ desc->type = CORESIGHT_DEV_TYPE_SINK; ++ desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT; ++ desc->ops = &tpiu_cs_ops; ++ desc->pdata = pdata; ++ desc->dev = dev; ++ drvdata->csdev = coresight_register(desc); ++ if (IS_ERR(drvdata->csdev)) ++ return PTR_ERR(drvdata->csdev); ++ ++ dev_info(dev, "TPIU initialized\n"); ++ return 0; ++} ++ ++static int tpiu_remove(struct amba_device *adev) ++{ ++ struct tpiu_drvdata *drvdata = amba_get_drvdata(adev); ++ ++ coresight_unregister(drvdata->csdev); ++ return 0; ++} ++ ++static struct amba_id tpiu_ids[] = { ++ { ++ .id = 0x0003b912, ++ .mask = 0x0003ffff, ++ }, ++ { 0, 0}, ++}; ++ ++static struct amba_driver tpiu_driver = { ++ .drv = { ++ .name = "coresight-tpiu", ++ .owner = THIS_MODULE, ++ }, ++ .probe = tpiu_probe, ++ .remove = tpiu_remove, ++ .id_table = tpiu_ids, ++}; ++ ++module_amba_driver(tpiu_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("CoreSight Trace Port Interface Unit driver"); +diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c +new file mode 100644 +index 0000000..894531d +--- /dev/null ++++ b/drivers/hwtracing/coresight/coresight.c +@@ -0,0 +1,720 @@ ++/* Copyright (c) 2012, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/device.h> ++#include <linux/io.h> ++#include <linux/err.h> ++#include <linux/export.h> ++#include <linux/slab.h> ++#include <linux/mutex.h> ++#include <linux/clk.h> ++#include <linux/coresight.h> ++#include <linux/of_platform.h> ++#include <linux/delay.h> ++ ++#include "coresight-priv.h" ++ ++static DEFINE_MUTEX(coresight_mutex); ++ ++static int coresight_id_match(struct device *dev, void *data) ++{ ++ int trace_id, i_trace_id; ++ struct coresight_device *csdev, *i_csdev; ++ ++ csdev = data; ++ i_csdev = to_coresight_device(dev); ++ ++ /* ++ * No need to care about oneself and components that are not ++ * sources or not enabled ++ */ ++ if (i_csdev == csdev || !i_csdev->enable || ++ i_csdev->type != CORESIGHT_DEV_TYPE_SOURCE) ++ return 0; ++ ++ /* Get the source ID for both compoment */ ++ trace_id = source_ops(csdev)->trace_id(csdev); ++ i_trace_id = source_ops(i_csdev)->trace_id(i_csdev); ++ ++ /* All you need is one */ ++ if (trace_id == i_trace_id) ++ return 1; ++ ++ return 0; ++} ++ ++static int coresight_source_is_unique(struct coresight_device *csdev) ++{ ++ int trace_id = source_ops(csdev)->trace_id(csdev); ++ ++ /* this shouldn't happen */ ++ if (trace_id < 0) ++ return 0; ++ ++ return !bus_for_each_dev(&coresight_bustype, NULL, ++ csdev, coresight_id_match); ++} ++ ++static int coresight_find_link_inport(struct coresight_device *csdev) ++{ ++ int i; ++ struct coresight_device *parent; ++ struct coresight_connection *conn; ++ ++ parent = container_of(csdev->path_link.next, ++ struct coresight_device, path_link); ++ ++ for (i = 0; i < parent->nr_outport; i++) { ++ conn = &parent->conns[i]; ++ if (conn->child_dev == csdev) ++ return conn->child_port; ++ } ++ ++ dev_err(&csdev->dev, "couldn't find inport, parent: %s, child: %s\n", ++ dev_name(&parent->dev), dev_name(&csdev->dev)); ++ ++ return 0; ++} ++ ++static int coresight_find_link_outport(struct coresight_device *csdev) ++{ ++ int i; ++ struct coresight_device *child; ++ struct coresight_connection *conn; ++ ++ child = container_of(csdev->path_link.prev, ++ struct coresight_device, path_link); ++ ++ for (i = 0; i < csdev->nr_outport; i++) { ++ conn = &csdev->conns[i]; ++ if (conn->child_dev == child) ++ return conn->outport; ++ } ++ ++ dev_err(&csdev->dev, "couldn't find outport, parent: %s, child: %s\n", ++ dev_name(&csdev->dev), dev_name(&child->dev)); ++ ++ return 0; ++} ++ ++static int coresight_enable_sink(struct coresight_device *csdev) ++{ ++ int ret; ++ ++ if (!csdev->enable) { ++ if (sink_ops(csdev)->enable) { ++ ret = sink_ops(csdev)->enable(csdev); ++ if (ret) ++ return ret; ++ } ++ csdev->enable = true; ++ } ++ ++ atomic_inc(csdev->refcnt); ++ ++ return 0; ++} ++ ++static void coresight_disable_sink(struct coresight_device *csdev) ++{ ++ if (atomic_dec_return(csdev->refcnt) == 0) { ++ if (sink_ops(csdev)->disable) { ++ sink_ops(csdev)->disable(csdev); ++ csdev->enable = false; ++ } ++ } ++} ++ ++static int coresight_enable_link(struct coresight_device *csdev) ++{ ++ int ret; ++ int link_subtype; ++ int refport, inport, outport; ++ ++ inport = coresight_find_link_inport(csdev); ++ outport = coresight_find_link_outport(csdev); ++ link_subtype = csdev->subtype.link_subtype; ++ ++ if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) ++ refport = inport; ++ else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) ++ refport = outport; ++ else ++ refport = 0; ++ ++ if (atomic_inc_return(&csdev->refcnt[refport]) == 1) { ++ if (link_ops(csdev)->enable) { ++ ret = link_ops(csdev)->enable(csdev, inport, outport); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ csdev->enable = true; ++ ++ return 0; ++} ++ ++static void coresight_disable_link(struct coresight_device *csdev) ++{ ++ int i, nr_conns; ++ int link_subtype; ++ int refport, inport, outport; ++ ++ inport = coresight_find_link_inport(csdev); ++ outport = coresight_find_link_outport(csdev); ++ link_subtype = csdev->subtype.link_subtype; ++ ++ if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) { ++ refport = inport; ++ nr_conns = csdev->nr_inport; ++ } else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) { ++ refport = outport; ++ nr_conns = csdev->nr_outport; ++ } else { ++ refport = 0; ++ nr_conns = 1; ++ } ++ ++ if (atomic_dec_return(&csdev->refcnt[refport]) == 0) { ++ if (link_ops(csdev)->disable) ++ link_ops(csdev)->disable(csdev, inport, outport); ++ } ++ ++ for (i = 0; i < nr_conns; i++) ++ if (atomic_read(&csdev->refcnt[i]) != 0) ++ return; ++ ++ csdev->enable = false; ++} ++ ++static int coresight_enable_source(struct coresight_device *csdev) ++{ ++ int ret; ++ ++ if (!coresight_source_is_unique(csdev)) { ++ dev_warn(&csdev->dev, "traceID %d not unique\n", ++ source_ops(csdev)->trace_id(csdev)); ++ return -EINVAL; ++ } ++ ++ if (!csdev->enable) { ++ if (source_ops(csdev)->enable) { ++ ret = source_ops(csdev)->enable(csdev); ++ if (ret) ++ return ret; ++ } ++ csdev->enable = true; ++ } ++ ++ atomic_inc(csdev->refcnt); ++ ++ return 0; ++} ++ ++static void coresight_disable_source(struct coresight_device *csdev) ++{ ++ if (atomic_dec_return(csdev->refcnt) == 0) { ++ if (source_ops(csdev)->disable) { ++ source_ops(csdev)->disable(csdev); ++ csdev->enable = false; ++ } ++ } ++} ++ ++static int coresight_enable_path(struct list_head *path) ++{ ++ int ret = 0; ++ struct coresight_device *cd; ++ ++ list_for_each_entry(cd, path, path_link) { ++ if (cd == list_first_entry(path, struct coresight_device, ++ path_link)) { ++ ret = coresight_enable_sink(cd); ++ } else if (list_is_last(&cd->path_link, path)) { ++ /* ++ * Don't enable the source just yet - this needs to ++ * happen at the very end when all links and sink ++ * along the path have been configured properly. ++ */ ++ ; ++ } else { ++ ret = coresight_enable_link(cd); ++ } ++ if (ret) ++ goto err; ++ } ++ ++ return 0; ++err: ++ list_for_each_entry_continue_reverse(cd, path, path_link) { ++ if (cd == list_first_entry(path, struct coresight_device, ++ path_link)) { ++ coresight_disable_sink(cd); ++ } else if (list_is_last(&cd->path_link, path)) { ++ ; ++ } else { ++ coresight_disable_link(cd); ++ } ++ } ++ ++ return ret; ++} ++ ++static int coresight_disable_path(struct list_head *path) ++{ ++ struct coresight_device *cd; ++ ++ list_for_each_entry_reverse(cd, path, path_link) { ++ if (cd == list_first_entry(path, struct coresight_device, ++ path_link)) { ++ coresight_disable_sink(cd); ++ } else if (list_is_last(&cd->path_link, path)) { ++ /* ++ * The source has already been stopped, no need ++ * to do it again here. ++ */ ++ ; ++ } else { ++ coresight_disable_link(cd); ++ } ++ } ++ ++ return 0; ++} ++ ++static int coresight_build_paths(struct coresight_device *csdev, ++ struct list_head *path, ++ bool enable) ++{ ++ int i, ret = -EINVAL; ++ struct coresight_connection *conn; ++ ++ list_add(&csdev->path_link, path); ++ ++ if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || ++ csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && ++ csdev->activated) { ++ if (enable) ++ ret = coresight_enable_path(path); ++ else ++ ret = coresight_disable_path(path); ++ } else { ++ for (i = 0; i < csdev->nr_outport; i++) { ++ conn = &csdev->conns[i]; ++ if (coresight_build_paths(conn->child_dev, ++ path, enable) == 0) ++ ret = 0; ++ } ++ } ++ ++ if (list_first_entry(path, struct coresight_device, path_link) != csdev) ++ dev_err(&csdev->dev, "wrong device in %s\n", __func__); ++ ++ list_del(&csdev->path_link); ++ ++ return ret; ++} ++ ++int coresight_enable(struct coresight_device *csdev) ++{ ++ int ret = 0; ++ LIST_HEAD(path); ++ ++ mutex_lock(&coresight_mutex); ++ if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) { ++ ret = -EINVAL; ++ dev_err(&csdev->dev, "wrong device type in %s\n", __func__); ++ goto out; ++ } ++ if (csdev->enable) ++ goto out; ++ ++ if (coresight_build_paths(csdev, &path, true)) { ++ dev_err(&csdev->dev, "building path(s) failed\n"); ++ goto out; ++ } ++ ++ if (coresight_enable_source(csdev)) ++ dev_err(&csdev->dev, "source enable failed\n"); ++out: ++ mutex_unlock(&coresight_mutex); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(coresight_enable); ++ ++void coresight_disable(struct coresight_device *csdev) ++{ ++ LIST_HEAD(path); ++ ++ mutex_lock(&coresight_mutex); ++ if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) { ++ dev_err(&csdev->dev, "wrong device type in %s\n", __func__); ++ goto out; ++ } ++ if (!csdev->enable) ++ goto out; ++ ++ coresight_disable_source(csdev); ++ if (coresight_build_paths(csdev, &path, false)) ++ dev_err(&csdev->dev, "releasing path(s) failed\n"); ++ ++out: ++ mutex_unlock(&coresight_mutex); ++} ++EXPORT_SYMBOL_GPL(coresight_disable); ++ ++static ssize_t enable_sink_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct coresight_device *csdev = to_coresight_device(dev); ++ ++ return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned)csdev->activated); ++} ++ ++static ssize_t enable_sink_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret; ++ unsigned long val; ++ struct coresight_device *csdev = to_coresight_device(dev); ++ ++ ret = kstrtoul(buf, 10, &val); ++ if (ret) ++ return ret; ++ ++ if (val) ++ csdev->activated = true; ++ else ++ csdev->activated = false; ++ ++ return size; ++ ++} ++static DEVICE_ATTR_RW(enable_sink); ++ ++static ssize_t enable_source_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct coresight_device *csdev = to_coresight_device(dev); ++ ++ return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned)csdev->enable); ++} ++ ++static ssize_t enable_source_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ int ret = 0; ++ unsigned long val; ++ struct coresight_device *csdev = to_coresight_device(dev); ++ ++ ret = kstrtoul(buf, 10, &val); ++ if (ret) ++ return ret; ++ ++ if (val) { ++ ret = coresight_enable(csdev); ++ if (ret) ++ return ret; ++ } else { ++ coresight_disable(csdev); ++ } ++ ++ return size; ++} ++static DEVICE_ATTR_RW(enable_source); ++ ++static struct attribute *coresight_sink_attrs[] = { ++ &dev_attr_enable_sink.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(coresight_sink); ++ ++static struct attribute *coresight_source_attrs[] = { ++ &dev_attr_enable_source.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(coresight_source); ++ ++static struct device_type coresight_dev_type[] = { ++ { ++ .name = "none", ++ }, ++ { ++ .name = "sink", ++ .groups = coresight_sink_groups, ++ }, ++ { ++ .name = "link", ++ }, ++ { ++ .name = "linksink", ++ .groups = coresight_sink_groups, ++ }, ++ { ++ .name = "source", ++ .groups = coresight_source_groups, ++ }, ++}; ++ ++static void coresight_device_release(struct device *dev) ++{ ++ struct coresight_device *csdev = to_coresight_device(dev); ++ ++ kfree(csdev); ++} ++ ++static int coresight_orphan_match(struct device *dev, void *data) ++{ ++ int i; ++ bool still_orphan = false; ++ struct coresight_device *csdev, *i_csdev; ++ struct coresight_connection *conn; ++ ++ csdev = data; ++ i_csdev = to_coresight_device(dev); ++ ++ /* No need to check oneself */ ++ if (csdev == i_csdev) ++ return 0; ++ ++ /* Move on to another component if no connection is orphan */ ++ if (!i_csdev->orphan) ++ return 0; ++ /* ++ * Circle throuch all the connection of that component. If we find ++ * an orphan connection whose name matches @csdev, link it. ++ */ ++ for (i = 0; i < i_csdev->nr_outport; i++) { ++ conn = &i_csdev->conns[i]; ++ ++ /* We have found at least one orphan connection */ ++ if (conn->child_dev == NULL) { ++ /* Does it match this newly added device? */ ++ if (!strcmp(dev_name(&csdev->dev), conn->child_name)) { ++ conn->child_dev = csdev; ++ } else { ++ /* This component still has an orphan */ ++ still_orphan = true; ++ } ++ } ++ } ++ ++ i_csdev->orphan = still_orphan; ++ ++ /* ++ * Returning '0' ensures that all known component on the ++ * bus will be checked. ++ */ ++ return 0; ++} ++ ++static void coresight_fixup_orphan_conns(struct coresight_device *csdev) ++{ ++ /* ++ * No need to check for a return value as orphan connection(s) ++ * are hooked-up with each newly added component. ++ */ ++ bus_for_each_dev(&coresight_bustype, NULL, ++ csdev, coresight_orphan_match); ++} ++ ++ ++static int coresight_name_match(struct device *dev, void *data) ++{ ++ char *to_match; ++ struct coresight_device *i_csdev; ++ ++ to_match = data; ++ i_csdev = to_coresight_device(dev); ++ ++ if (!strcmp(to_match, dev_name(&i_csdev->dev))) ++ return 1; ++ ++ return 0; ++} ++ ++static void coresight_fixup_device_conns(struct coresight_device *csdev) ++{ ++ int i; ++ struct device *dev = NULL; ++ struct coresight_connection *conn; ++ ++ for (i = 0; i < csdev->nr_outport; i++) { ++ conn = &csdev->conns[i]; ++ dev = bus_find_device(&coresight_bustype, NULL, ++ (void *)conn->child_name, ++ coresight_name_match); ++ ++ if (dev) { ++ conn->child_dev = to_coresight_device(dev); ++ } else { ++ csdev->orphan = true; ++ conn->child_dev = NULL; ++ } ++ } ++} ++ ++/** ++ * coresight_timeout - loop until a bit has changed to a specific state. ++ * @addr: base address of the area of interest. ++ * @offset: address of a register, starting from @addr. ++ * @position: the position of the bit of interest. ++ * @value: the value the bit should have. ++ * ++ * Return: 0 as soon as the bit has taken the desired state or -EAGAIN if ++ * TIMEOUT_US has elapsed, which ever happens first. ++ */ ++ ++int coresight_timeout(void __iomem *addr, u32 offset, int position, int value) ++{ ++ int i; ++ u32 val; ++ ++ for (i = TIMEOUT_US; i > 0; i--) { ++ val = __raw_readl(addr + offset); ++ /* waiting on the bit to go from 0 to 1 */ ++ if (value) { ++ if (val & BIT(position)) ++ return 0; ++ /* waiting on the bit to go from 1 to 0 */ ++ } else { ++ if (!(val & BIT(position))) ++ return 0; ++ } ++ ++ /* ++ * Delay is arbitrary - the specification doesn't say how long ++ * we are expected to wait. Extra check required to make sure ++ * we don't wait needlessly on the last iteration. ++ */ ++ if (i - 1) ++ udelay(1); ++ } ++ ++ return -EAGAIN; ++} ++ ++struct bus_type coresight_bustype = { ++ .name = "coresight", ++}; ++ ++static int __init coresight_init(void) ++{ ++ return bus_register(&coresight_bustype); ++} ++postcore_initcall(coresight_init); ++ ++struct coresight_device *coresight_register(struct coresight_desc *desc) ++{ ++ int i; ++ int ret; ++ int link_subtype; ++ int nr_refcnts = 1; ++ atomic_t *refcnts = NULL; ++ struct coresight_device *csdev; ++ struct coresight_connection *conns; ++ ++ csdev = kzalloc(sizeof(*csdev), GFP_KERNEL); ++ if (!csdev) { ++ ret = -ENOMEM; ++ goto err_kzalloc_csdev; ++ } ++ ++ if (desc->type == CORESIGHT_DEV_TYPE_LINK || ++ desc->type == CORESIGHT_DEV_TYPE_LINKSINK) { ++ link_subtype = desc->subtype.link_subtype; ++ ++ if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) ++ nr_refcnts = desc->pdata->nr_inport; ++ else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) ++ nr_refcnts = desc->pdata->nr_outport; ++ } ++ ++ refcnts = kcalloc(nr_refcnts, sizeof(*refcnts), GFP_KERNEL); ++ if (!refcnts) { ++ ret = -ENOMEM; ++ goto err_kzalloc_refcnts; ++ } ++ ++ csdev->refcnt = refcnts; ++ ++ csdev->nr_inport = desc->pdata->nr_inport; ++ csdev->nr_outport = desc->pdata->nr_outport; ++ conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL); ++ if (!conns) { ++ ret = -ENOMEM; ++ goto err_kzalloc_conns; ++ } ++ ++ for (i = 0; i < csdev->nr_outport; i++) { ++ conns[i].outport = desc->pdata->outports[i]; ++ conns[i].child_name = desc->pdata->child_names[i]; ++ conns[i].child_port = desc->pdata->child_ports[i]; ++ } ++ ++ csdev->conns = conns; ++ ++ csdev->type = desc->type; ++ csdev->subtype = desc->subtype; ++ csdev->ops = desc->ops; ++ csdev->orphan = false; ++ ++ csdev->dev.type = &coresight_dev_type[desc->type]; ++ csdev->dev.groups = desc->groups; ++ csdev->dev.parent = desc->dev; ++ csdev->dev.release = coresight_device_release; ++ csdev->dev.bus = &coresight_bustype; ++ dev_set_name(&csdev->dev, "%s", desc->pdata->name); ++ ++ ret = device_register(&csdev->dev); ++ if (ret) ++ goto err_device_register; ++ ++ mutex_lock(&coresight_mutex); ++ ++ coresight_fixup_device_conns(csdev); ++ coresight_fixup_orphan_conns(csdev); ++ ++ mutex_unlock(&coresight_mutex); ++ ++ return csdev; ++ ++err_device_register: ++ kfree(conns); ++err_kzalloc_conns: ++ kfree(refcnts); ++err_kzalloc_refcnts: ++ kfree(csdev); ++err_kzalloc_csdev: ++ return ERR_PTR(ret); ++} ++EXPORT_SYMBOL_GPL(coresight_register); ++ ++void coresight_unregister(struct coresight_device *csdev) ++{ ++ mutex_lock(&coresight_mutex); ++ ++ kfree(csdev->conns); ++ device_unregister(&csdev->dev); ++ ++ mutex_unlock(&coresight_mutex); ++} ++EXPORT_SYMBOL_GPL(coresight_unregister); ++ ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c +new file mode 100644 +index 0000000..f3cc8e9 +--- /dev/null ++++ b/drivers/hwtracing/coresight/of_coresight.c +@@ -0,0 +1,200 @@ ++/* Copyright (c) 2012, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/err.h> ++#include <linux/slab.h> ++#include <linux/clk.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/of_graph.h> ++#include <linux/of_platform.h> ++#include <linux/platform_device.h> ++#include <linux/amba/bus.h> ++#include <linux/coresight.h> ++#include <linux/cpumask.h> ++#include <asm/smp_plat.h> ++ ++ ++static int of_dev_node_match(struct device *dev, void *data) ++{ ++ return dev->of_node == data; ++} ++ ++static struct device * ++of_coresight_get_endpoint_device(struct device_node *endpoint) ++{ ++ struct device *dev = NULL; ++ ++ /* ++ * If we have a non-configuable replicator, it will be found on the ++ * platform bus. ++ */ ++ dev = bus_find_device(&platform_bus_type, NULL, ++ endpoint, of_dev_node_match); ++ if (dev) ++ return dev; ++ ++ /* ++ * We have a configurable component - circle through the AMBA bus ++ * looking for the device that matches the endpoint node. ++ */ ++ return bus_find_device(&amba_bustype, NULL, ++ endpoint, of_dev_node_match); ++} ++ ++static struct device_node *of_get_coresight_endpoint( ++ const struct device_node *parent, struct device_node *prev) ++{ ++ struct device_node *node = of_graph_get_next_endpoint(parent, prev); ++ ++ of_node_put(prev); ++ return node; ++} ++ ++static void of_coresight_get_ports(struct device_node *node, ++ int *nr_inport, int *nr_outport) ++{ ++ struct device_node *ep = NULL; ++ int in = 0, out = 0; ++ ++ do { ++ ep = of_get_coresight_endpoint(node, ep); ++ if (!ep) ++ break; ++ ++ if (of_property_read_bool(ep, "slave-mode")) ++ in++; ++ else ++ out++; ++ ++ } while (ep); ++ ++ *nr_inport = in; ++ *nr_outport = out; ++} ++ ++static int of_coresight_alloc_memory(struct device *dev, ++ struct coresight_platform_data *pdata) ++{ ++ /* List of output port on this component */ ++ pdata->outports = devm_kzalloc(dev, pdata->nr_outport * ++ sizeof(*pdata->outports), ++ GFP_KERNEL); ++ if (!pdata->outports) ++ return -ENOMEM; ++ ++ /* Children connected to this component via @outports */ ++ pdata->child_names = devm_kzalloc(dev, pdata->nr_outport * ++ sizeof(*pdata->child_names), ++ GFP_KERNEL); ++ if (!pdata->child_names) ++ return -ENOMEM; ++ ++ /* Port number on the child this component is connected to */ ++ pdata->child_ports = devm_kzalloc(dev, pdata->nr_outport * ++ sizeof(*pdata->child_ports), ++ GFP_KERNEL); ++ if (!pdata->child_ports) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++struct coresight_platform_data *of_get_coresight_platform_data( ++ struct device *dev, struct device_node *node) ++{ ++ int i = 0, ret = 0, cpu; ++ struct coresight_platform_data *pdata; ++ struct of_endpoint endpoint, rendpoint; ++ struct device *rdev; ++ struct device_node *dn; ++ struct device_node *ep = NULL; ++ struct device_node *rparent = NULL; ++ struct device_node *rport = NULL; ++ ++ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) ++ return ERR_PTR(-ENOMEM); ++ ++ /* Use device name as sysfs handle */ ++ pdata->name = dev_name(dev); ++ ++ /* Get the number of input and output port for this component */ ++ of_coresight_get_ports(node, &pdata->nr_inport, &pdata->nr_outport); ++ ++ if (pdata->nr_outport) { ++ ret = of_coresight_alloc_memory(dev, pdata); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ /* Iterate through each port to discover topology */ ++ do { ++ /* Get a handle on a port */ ++ ep = of_get_coresight_endpoint(node, ep); ++ if (!ep) ++ break; ++ ++ /* ++ * No need to deal with input ports, processing for as ++ * processing for output ports will deal with them. ++ */ ++ if (of_find_property(ep, "slave-mode", NULL)) ++ continue; ++ ++ /* Get a handle on the local endpoint */ ++ ret = of_graph_parse_endpoint(ep, &endpoint); ++ ++ if (ret) ++ continue; ++ ++ /* The local out port number */ ++ pdata->outports[i] = endpoint.id; ++ ++ /* ++ * Get a handle on the remote port and parent ++ * attached to it. ++ */ ++ rparent = of_graph_get_remote_port_parent(ep); ++ rport = of_graph_get_remote_port(ep); ++ ++ if (!rparent || !rport) ++ continue; ++ ++ if (of_graph_parse_endpoint(rport, &rendpoint)) ++ continue; ++ ++ rdev = of_coresight_get_endpoint_device(rparent); ++ if (!rdev) ++ continue; ++ ++ pdata->child_names[i] = dev_name(rdev); ++ pdata->child_ports[i] = rendpoint.id; ++ ++ i++; ++ } while (ep); ++ } ++ ++ /* Affinity defaults to CPU0 */ ++ pdata->cpu = 0; ++ dn = of_parse_phandle(node, "cpu", 0); ++ for (cpu = 0; dn && cpu < nr_cpu_ids; cpu++) { ++ if (dn == of_get_cpu_node(cpu, NULL)) { ++ pdata->cpu = cpu; ++ break; ++ } ++ } ++ ++ return pdata; ++} ++EXPORT_SYMBOL_GPL(of_get_coresight_platform_data); +diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index 917c358..cec7392 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -501,6 +501,16 @@ config I2C_GPIO + This is a very simple bitbanging I2C driver utilizing the + arch-neutral GPIO API to control the SCL and SDA lines. + ++config I2C_HIBVT ++ tristate "Hisilicon BVT I2C Controller" ++ depends on ARCH_HISI ++ help ++ Say Y here to include support for Hisilicon BVT I2C controller in the ++ Hisilicon BVT SoCs. ++ ++ This driver can also be built as a module. If so, the module ++ will be called i2c-hibvt. ++ + config I2C_HIGHLANDER + tristate "Highlander FPGA SMBus interface" + depends on SH_HIGHLANDER +@@ -513,6 +523,16 @@ config I2C_HIGHLANDER + This driver can also be built as a module. If so, the module + will be called i2c-highlander. + ++config I2C_HISI_V110 ++ tristate "Hisilicon I2C V110 Controller" ++ depends on ARCH_HISI ++ help ++ Say Y here to include support for Hisilicon I2C V110 controller in the ++ Hisilicon BVT SoCs. ++ ++ This driver can also be built as a module. If so, the module ++ will be called i2c-hisiv110. ++ + config I2C_IBM_IIC + tristate "IBM PPC 4xx on-chip I2C interface" + depends on 4xx +@@ -1044,4 +1064,84 @@ config SCx200_ACB + This support is also available as a module. If so, the module + will be called scx200_acb. + ++config HI_I2C ++ tristate "Hisilicon I2C Controller support" ++ depends on (ARCH_HI3519 || ARCH_HI3531D || ARCH_HI3521D || ARCH_HI3536C) ++ help ++ Hisilicon I2C controller has 3 buses. ++ We can access some sensors though it. ++ This IP is only used in HI3519/HI3531D/HI3521D/HI3536C chip. ++ ++if HI_I2C ++config HI_I2C0_IO_BASE ++ hex "hi i2c0 register base address" ++ default "0x12110000" if ARCH_HI3519 ++ default "0x120c0000" if (ARCH_HI3531D || ARCH_HI3521D || ARCH_HI3536C) ++ ++config HI_I2C0_IO_SIZE ++ hex "hi i2c0 register size" ++ default "0x1000" if (ARCH_HI3519 || ARCH_HI3531D || ARCH_HI3521D|| ARCH_HI3536C) ++ ++config HI_I2C1_IO_BASE ++ hex "hi i2c1 register base address" ++ depends on (ARCH_HI3519 || ARCH_HI3531D) ++ default "0x12111000" if ARCH_HI3519 ++ default "0x122e0000" if ARCH_HI3531D ++ ++config HI_I2C1_IO_SIZE ++ hex "hi i2c1 register size" ++ depends on (ARCH_HI3519 || ARCH_HI3531D) ++ default "0x1000" if (ARCH_HI3519 || ARCH_HI3531D) ++ ++config HI_I2C2_IO_BASE ++ hex "hi i2c2 register base address" ++ depends on ARCH_HI3519 ++ default "0x12112000" if ARCH_HI3519 ++ ++config HI_I2C2_IO_SIZE ++ hex "hi i2c2 register size" ++ depends on ARCH_HI3519 ++ default "0x1000" if ARCH_HI3519 ++ ++config HI_I2C3_IO_BASE ++ hex "hi i2c3 register base address" ++ depends on ARCH_HI3519 ++ default "0x12113000" if ARCH_HI3519 ++ ++config HI_I2C3_IO_SIZE ++ hex "hi i2c3 register size" ++ depends on ARCH_HI3519 ++ default "0x1000" if ARCH_HI3519 ++ ++config HI_I2C_RETRIES ++ hex "hi i2c retry times" ++ default "0x1" if (ARCH_HI3519 || ARCH_HI3531D || ARCH_HI3521D || ARCH_HI3536C) ++ ++config HI_I2C_TX_FIFO ++ hex "hi i2c tx fifo" ++ default "0x8" if (ARCH_HI3519 || ARCH_HI3531D || ARCH_HI3521D || ARCH_HI3536C) ++ ++config HI_I2C_RX_FIFO ++ hex "hi i2c rx fifo" ++ default "0x8" if (ARCH_HI3519 || ARCH_HI3531D || ARCH_HI3521D || ARCH_HI3536C) ++ ++config HI_I2C0_CLK_LIMIT ++ int "hi i2c0 clock limit" ++ default "100000" if (ARCH_HI3519 || ARCH_HI3531D || ARCH_HI3521D || ARCH_HI3536C) ++ ++config HI_I2C1_CLK_LIMIT ++ int "hi i2c1 clock limit" ++ depends on (ARCH_HI3519 || ARCH_HI3531D) ++ default "100000" if (ARCH_HI3519 || ARCH_HI3531D) ++ ++config HI_I2C2_CLK_LIMIT ++ int "hi i2c2 clock limit" ++ depends on ARCH_HI3519 ++ default "100000" if ARCH_HI3519 ++ ++config HI_I2C3_CLK_LIMIT ++ int "hi i2c3 clock limit" ++ depends on ARCH_HI3519 ++ default "100000" if ARCH_HI3519 ++endif + endmenu +diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile +index 78d56c5..bdc2c01 100644 +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -47,7 +47,9 @@ obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o + obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o + obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o + obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o ++obj-$(CONFIG_I2C_HIBVT) += i2c-hibvt.o + obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o ++obj-$(CONFIG_I2C_HISI_V110) += i2c-hisi-v110.o + obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o + obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o + obj-$(CONFIG_I2C_IMX) += i2c-imx.o +@@ -103,4 +105,5 @@ obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o + obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o + obj-$(CONFIG_SCx200_ACB) += scx200_acb.o + ++obj-$(CONFIG_HI_I2C) += i2c-hisilicon.o + ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG +diff --git a/drivers/i2c/busses/i2c-hibvt.c b/drivers/i2c/busses/i2c-hibvt.c +new file mode 100644 +index 0000000..478478b +--- /dev/null ++++ b/drivers/i2c/busses/i2c-hibvt.c +@@ -0,0 +1,920 @@ ++/* ++ * Hisilicon BVT I2C Controller Driver ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * Authors: wenpan@hisilicon.com ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/i2c.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++ ++/* ++ * I2C Registers offsets ++ */ ++#define HIBVT_I2C_GLB 0x0 ++#define HIBVT_I2C_SCL_H 0x4 ++#define HIBVT_I2C_SCL_L 0x8 ++#define HIBVT_I2C_DATA1 0x10 ++#define HIBVT_I2C_TXF 0x20 ++#define HIBVT_I2C_RXF 0x24 ++#define HIBVT_I2C_CMD_BASE 0x30 ++#define HIBVT_I2C_LOOP1 0xb0 ++#define HIBVT_I2C_DST1 0xb4 ++#define HIBVT_I2C_TX_WATER 0xc8 ++#define HIBVT_I2C_RX_WATER 0xcc ++#define HIBVT_I2C_CTRL1 0xd0 ++#define HIBVT_I2C_CTRL2 0xd4 ++#define HIBVT_I2C_STAT 0xd8 ++#define HIBVT_I2C_INTR_RAW 0xe0 ++#define HIBVT_I2C_INTR_EN 0xe4 ++#define HIBVT_I2C_INTR_STAT 0xe8 ++ ++/* ++ * I2C Global Config Register -- HIBVT_I2C_GLB ++ */ ++#define GLB_EN_MASK BIT(0) ++#define GLB_SDA_HOLD_MASK GENMASK(23, 8) ++#define GLB_SDA_HOLD_SHIFT (8) ++ ++/* ++ * I2C Timing CMD Register -- HIBVT_I2C_CMD_BASE + n * 4 (n = 0, 1, 2, ... 31) ++ */ ++#define CMD_EXIT 0x0 ++#define CMD_TX_S 0x1 ++#define CMD_TX_D1_2 0x4 ++#define CMD_TX_D1_1 0x5 ++#define CMD_TX_FIFO 0x9 ++#define CMD_RX_FIFO 0x12 ++#define CMD_RX_ACK 0x13 ++#define CMD_IGN_ACK 0x15 ++#define CMD_TX_ACK 0x16 ++#define CMD_TX_NACK 0x17 ++#define CMD_JMP1 0x18 ++#define CMD_UP_TXF 0x1d ++#define CMD_TX_RS 0x1e ++#define CMD_TX_P 0x1f ++ ++/* ++ * I2C Control Register 1 -- HIBVT_I2C_CTRL1 ++ */ ++#define CTRL1_CMD_START_MASK BIT(0) ++ ++/* ++ * I2C Status Register -- HIBVT_I2C_STAT ++ */ ++#define STAT_RXF_NOE_MASK BIT(16) /* RX FIFO not empty flag */ ++#define STAT_TXF_NOF_MASK BIT(19) /* TX FIFO not full flag */ ++ ++ ++/* ++ * I2C Interrupt status and mask Register -- ++ * HIBVT_I2C_INTR_RAW, HIBVT_I2C_STAT, HIBVT_I2C_INTR_STAT ++ */ ++#define INTR_ABORT_MASK (BIT(0) | BIT(11)) ++#define INTR_RX_MASK BIT(2) ++#define INTR_TX_MASK BIT(4) ++#define INTR_CMD_DONE_MASK BIT(12) ++#define INTR_USE_MASK (INTR_ABORT_MASK \ ++ |INTR_RX_MASK \ ++ | INTR_TX_MASK \ ++ | INTR_CMD_DONE_MASK) ++#define INTR_ALL_MASK GENMASK(31, 0) ++ ++#define I2C_DEFAULT_FREQUENCY 100000 ++#define I2C_TXF_DEPTH 64 ++#define I2C_RXF_DEPTH 64 ++#define I2C_TXF_WATER 32 ++#define I2C_RXF_WATER 32 ++#define I2C_WAIT_TIMEOUT 0x10000 ++#define I2C_IRQ_TIMEOUT (msecs_to_jiffies(1000)) ++ ++ ++struct hibvt_i2c_dev { ++ struct device *dev; ++ struct i2c_adapter adap; ++ void __iomem *base; ++ struct clk *clk; ++ int irq; ++ ++ unsigned int freq; ++ struct i2c_msg *msg; ++ unsigned int msg_num; ++ unsigned int msg_idx; ++ unsigned int msg_buf_ptr; ++ struct completion msg_complete; ++ ++ spinlock_t lock; ++ int status; ++}; ++static inline void hibvt_i2c_disable(struct hibvt_i2c_dev *i2c); ++static inline void hibvt_i2c_cfg_irq(struct hibvt_i2c_dev *i2c, ++ unsigned int flag); ++static inline unsigned int hibvt_i2c_clr_irq(struct hibvt_i2c_dev *i2c); ++static inline void hibvt_i2c_enable(struct hibvt_i2c_dev *i2c); ++ ++#define CHECK_SDA_IN_SHIFT (16) ++#define GPIO_MODE_SHIFT (8) ++#define FORCE_SCL_OEN_SHIFT (4) ++#define FORCE_SDA_OEN_SHIFT (0) ++ ++static void hibvt_i2c_rescue(struct hibvt_i2c_dev *i2c) ++{ ++ unsigned int val; ++ unsigned int time_cnt; ++ int index; ++ ++ hibvt_i2c_disable(i2c); ++ hibvt_i2c_cfg_irq(i2c, 0); ++ hibvt_i2c_clr_irq(i2c); ++ ++ val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) | (0x1 << FORCE_SDA_OEN_SHIFT); ++ writel(val, i2c->base + HIBVT_I2C_CTRL2); ++ ++ time_cnt = 0; ++ do { ++ for (index = 0; index < 9; index++) { ++ val = (0x1 << GPIO_MODE_SHIFT) | 0x1; ++ writel(val, i2c->base + HIBVT_I2C_CTRL2); ++ ++ udelay(5); ++ ++ val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) | (0x1 << FORCE_SDA_OEN_SHIFT); ++ writel(val, i2c->base + HIBVT_I2C_CTRL2); ++ ++ udelay(5); ++ } ++ ++ time_cnt++; ++ if (time_cnt > I2C_WAIT_TIMEOUT) { ++ dev_err(i2c->dev, "wait Timeout!\n"); ++ goto disable_rescue; ++ } ++ ++ val = readl(i2c->base + HIBVT_I2C_CTRL2); ++ } while(!(val & (0x1 << CHECK_SDA_IN_SHIFT))); ++ ++ ++ val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) | (0x1 << FORCE_SDA_OEN_SHIFT); ++ writel(val, i2c->base + HIBVT_I2C_CTRL2); ++ ++ val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT); ++ writel(val, i2c->base + HIBVT_I2C_CTRL2); ++ ++ udelay(10); ++ ++ val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) | (0x1 << FORCE_SDA_OEN_SHIFT); ++ writel(val, i2c->base + HIBVT_I2C_CTRL2); ++ ++disable_rescue: ++ val = (0x1 << FORCE_SCL_OEN_SHIFT) | 0x1; ++ writel(val, i2c->base + HIBVT_I2C_CTRL2); ++} ++ ++static inline void hibvt_i2c_disable(struct hibvt_i2c_dev *i2c) ++{ ++ unsigned int val; ++ ++ val = readl(i2c->base + HIBVT_I2C_GLB); ++ val &= ~GLB_EN_MASK; ++ writel(val, i2c->base + HIBVT_I2C_GLB); ++} ++ ++static inline void hibvt_i2c_enable(struct hibvt_i2c_dev *i2c) ++{ ++ unsigned int val; ++ ++ val = readl(i2c->base + HIBVT_I2C_GLB); ++ val |= GLB_EN_MASK; ++ writel(val, i2c->base + HIBVT_I2C_GLB); ++} ++ ++static inline void hibvt_i2c_cfg_irq(struct hibvt_i2c_dev *i2c, ++ unsigned int flag) ++{ ++ writel(flag, i2c->base + HIBVT_I2C_INTR_EN); ++} ++ ++static inline void hibvt_i2c_disable_irq(struct hibvt_i2c_dev *i2c, ++ unsigned int flag) ++{ ++ unsigned int val; ++ ++ val = readl(i2c->base + HIBVT_I2C_INTR_EN); ++ val &= ~flag; ++ writel(val, i2c->base + HIBVT_I2C_INTR_EN); ++} ++ ++static inline unsigned int hibvt_i2c_clr_irq(struct hibvt_i2c_dev *i2c) ++{ ++ unsigned int val; ++ ++ val = readl(i2c->base + HIBVT_I2C_INTR_STAT); ++ writel(INTR_ALL_MASK, i2c->base + HIBVT_I2C_INTR_RAW); ++ ++ return val; ++} ++ ++static inline void hibvt_i2c_cmdreg_set(struct hibvt_i2c_dev *i2c, ++ unsigned int cmd, unsigned int *offset) ++{ ++ dev_dbg(i2c->dev, "hii2c reg: offset=0x%x, cmd=0x%x...\n", ++ *offset * 4, cmd); ++ writel(cmd, i2c->base + HIBVT_I2C_CMD_BASE + *offset * 4); ++ (*offset)++; ++} ++ ++/* ++ * config i2c slave addr ++ */ ++static inline void hibvt_i2c_set_addr(struct hibvt_i2c_dev *i2c) ++{ ++ struct i2c_msg *msg = i2c->msg; ++ u16 addr; ++ ++ if (msg->flags & I2C_M_TEN) { ++ /* First byte is 11110XX0 where XX is upper 2 bits */ ++ addr = ((msg->addr & 0x300) << 1) | 0xf000; ++ if (msg->flags & I2C_M_RD) ++ addr |= 1 << 8; ++ ++ /* Second byte is the remaining 8 bits */ ++ addr |= msg->addr & 0xff; ++ } else { ++ addr = (msg->addr & 0x7f) << 1; ++ if (msg->flags & I2C_M_RD) ++ addr |= 1; ++ } ++ ++ writel(addr, i2c->base + HIBVT_I2C_DATA1); ++} ++ ++/* ++ * Start command sequence ++ */ ++static inline void hibvt_i2c_start_cmd(struct hibvt_i2c_dev *i2c) ++{ ++ unsigned int val; ++ ++ val = readl(i2c->base + HIBVT_I2C_CTRL1); ++ val |= CTRL1_CMD_START_MASK; ++ writel(val, i2c->base + HIBVT_I2C_CTRL1); ++} ++ ++static int hibvt_i2c_wait_rx_noempty(struct hibvt_i2c_dev *i2c) ++{ ++ unsigned int time_cnt = 0; ++ unsigned int val; ++ ++ do { ++ val = readl(i2c->base + HIBVT_I2C_STAT); ++ if (val & STAT_RXF_NOE_MASK) ++ return 0; ++ ++ udelay(50); ++ } while (time_cnt++ < I2C_WAIT_TIMEOUT); ++ ++ hibvt_i2c_rescue(i2c); ++ ++ dev_err(i2c->dev, "wait rx no empty timeout, RIS: 0x%x, SR: 0x%x\n", ++ readl(i2c->base + HIBVT_I2C_INTR_RAW), val); ++ return -EIO; ++} ++ ++static int hibvt_i2c_wait_tx_nofull(struct hibvt_i2c_dev *i2c) ++{ ++ unsigned int time_cnt = 0; ++ unsigned int val; ++ ++ do { ++ val = readl(i2c->base + HIBVT_I2C_STAT); ++ if (val & STAT_TXF_NOF_MASK) ++ return 0; ++ ++ udelay(50); ++ } while (time_cnt++ < I2C_WAIT_TIMEOUT); ++ ++ hibvt_i2c_rescue(i2c); ++ ++ dev_err(i2c->dev, "wait rx no empty timeout, RIS: 0x%x, SR: 0x%x\n", ++ readl(i2c->base + HIBVT_I2C_INTR_RAW), val); ++ return -EIO; ++} ++ ++static int hibvt_i2c_wait_idle(struct hibvt_i2c_dev *i2c) ++{ ++ unsigned int time_cnt = 0; ++ unsigned int val; ++ ++ do { ++ val = readl(i2c->base + HIBVT_I2C_INTR_RAW); ++ if (val & (INTR_ABORT_MASK)) { ++ dev_err(i2c->dev, "wait idle abort!, RIS: 0x%x\n", ++ val); ++ return -EIO; ++ } ++ ++ if (val & INTR_CMD_DONE_MASK) ++ return 0; ++ ++ udelay(50); ++ } while (time_cnt++ < I2C_WAIT_TIMEOUT); ++ ++ hibvt_i2c_rescue(i2c); ++ ++ dev_err(i2c->dev, "wait idle timeout, RIS: 0x%x, SR: 0x%x\n", ++ val, readl(i2c->base + HIBVT_I2C_STAT)); ++ ++ return -EIO; ++} ++ ++static void hibvt_i2c_set_freq(struct hibvt_i2c_dev *i2c) ++{ ++ unsigned int max_freq, freq; ++ unsigned int clk_rate; ++ unsigned int val; ++ ++ freq = i2c->freq; ++ clk_rate = clk_get_rate(i2c->clk); ++ max_freq = clk_rate >> 1; ++ ++ if (freq > max_freq) { ++ i2c->freq = max_freq; ++ freq = i2c->freq; ++ } ++ ++ if (!freq) { ++ pr_err("hibvt_i2c_set_freq:freq can't be zero!"); ++ return; ++ } ++ ++ /* set SCLH and SCLL depend on clk_rate and freq */ ++ if (freq <= 100000) { ++ /* in normal mode F_scl: freq ++ i2c_scl_hcnt = (F_i2c / F_scl) * 0.5 ++ i2c_scl_hcnt = (F_i2c / F_scl) * 0.5 ++ */ ++ val = clk_rate / (freq * 2); ++ writel(val, i2c->base + HIBVT_I2C_SCL_H); ++ writel(val, i2c->base + HIBVT_I2C_SCL_L); ++ } else { ++ /* in fast mode F_scl: freq ++ i2c_scl_hcnt = (F_i2c / F_scl) * 0.36 ++ i2c_scl_hcnt = (F_i2c / F_scl) * 0.64 ++ */ ++ val = ((clk_rate / 100) * 36) / freq; ++ writel(val, i2c->base + HIBVT_I2C_SCL_H); ++ val = ((clk_rate / 100) * 64) / freq; ++ writel(val, i2c->base + HIBVT_I2C_SCL_L); ++ } ++ ++ val = readl(i2c->base + HIBVT_I2C_GLB); ++ val &= ~GLB_SDA_HOLD_MASK; ++ val |= ((0xa << GLB_SDA_HOLD_SHIFT) & GLB_SDA_HOLD_MASK); ++ writel(val, i2c->base + HIBVT_I2C_GLB); ++} ++ ++/* ++ * set i2c controller TX and RX FIFO water ++ */ ++static inline void hibvt_i2c_set_water(struct hibvt_i2c_dev *i2c) ++{ ++ writel(I2C_TXF_WATER, i2c->base + HIBVT_I2C_TX_WATER); ++ writel(I2C_RXF_WATER, i2c->base + HIBVT_I2C_RX_WATER); ++} ++ ++/* ++ * initialise the controller, set i2c bus interface freq ++ */ ++static void hibvt_i2c_hw_init(struct hibvt_i2c_dev *i2c) ++{ ++ hibvt_i2c_disable(i2c); ++ hibvt_i2c_disable_irq(i2c, INTR_ALL_MASK); ++ hibvt_i2c_set_freq(i2c); ++ hibvt_i2c_set_water(i2c); ++} ++ ++/* ++ * hibvt_i2c_cfg_cmd - config i2c controller command sequence ++ * ++ * After all the timing command is configured, ++ * and then start the command, you can i2c communication, ++ * and then only need to read and write i2c fifo. ++ */ ++static void hibvt_i2c_cfg_cmd(struct hibvt_i2c_dev *i2c) ++{ ++ struct i2c_msg *msg = i2c->msg; ++ int offset = 0; ++ ++ if (i2c->msg_idx == 0) ++ hibvt_i2c_cmdreg_set(i2c, CMD_TX_S, &offset); ++ else ++ hibvt_i2c_cmdreg_set(i2c, CMD_TX_RS, &offset); ++ ++ if (msg->flags & I2C_M_TEN) { ++ if (i2c->msg_idx == 0) { ++ hibvt_i2c_cmdreg_set(i2c, CMD_TX_D1_2, &offset); ++ hibvt_i2c_cmdreg_set(i2c, CMD_TX_D1_1, &offset); ++ } else { ++ hibvt_i2c_cmdreg_set(i2c, CMD_TX_D1_2, &offset); ++ } ++ } else { ++ hibvt_i2c_cmdreg_set(i2c, CMD_TX_D1_1, &offset); ++ } ++ ++ if (msg->flags & I2C_M_IGNORE_NAK) ++ hibvt_i2c_cmdreg_set(i2c, CMD_IGN_ACK, &offset); ++ else ++ hibvt_i2c_cmdreg_set(i2c, CMD_RX_ACK, &offset); ++ ++ if (msg->flags & I2C_M_RD) { ++ if (msg->len >= 2) { ++ writel(offset, i2c->base + HIBVT_I2C_DST1); ++ writel(msg->len - 2, i2c->base + HIBVT_I2C_LOOP1); ++ hibvt_i2c_cmdreg_set(i2c, CMD_RX_FIFO, &offset); ++ hibvt_i2c_cmdreg_set(i2c, CMD_TX_ACK, &offset); ++ hibvt_i2c_cmdreg_set(i2c, CMD_JMP1, &offset); ++ } ++ hibvt_i2c_cmdreg_set(i2c, CMD_RX_FIFO, &offset); ++ hibvt_i2c_cmdreg_set(i2c, CMD_TX_NACK, &offset); ++ } else { ++ writel(offset, i2c->base + HIBVT_I2C_DST1); ++ writel(msg->len - 1, i2c->base + HIBVT_I2C_LOOP1); ++ hibvt_i2c_cmdreg_set(i2c, CMD_UP_TXF, &offset); ++ hibvt_i2c_cmdreg_set(i2c, CMD_TX_FIFO, &offset); ++ ++ if (msg->flags & I2C_M_IGNORE_NAK) ++ hibvt_i2c_cmdreg_set(i2c, CMD_IGN_ACK, &offset); ++ else ++ hibvt_i2c_cmdreg_set(i2c, CMD_RX_ACK, &offset); ++ ++ hibvt_i2c_cmdreg_set(i2c, CMD_JMP1, &offset); ++ } ++ ++ if ((i2c->msg_idx == (i2c->msg_num - 1)) || (msg->flags & I2C_M_STOP)) { ++ dev_dbg(i2c->dev, "run to %s %d...TX STOP\n", ++ __func__, __LINE__); ++ hibvt_i2c_cmdreg_set(i2c, CMD_TX_P, &offset); ++ } ++ ++ hibvt_i2c_cmdreg_set(i2c, CMD_EXIT, &offset); ++} ++ ++static int hibvt_i2c_polling_xfer_one_msg(struct hibvt_i2c_dev *i2c) ++{ ++ int status; ++ unsigned int val; ++ struct i2c_msg *msg = i2c->msg; ++ ++ dev_dbg(i2c->dev, "[%s,%d]msg->flags=0x%x, len=0x%x\n", ++ __func__, __LINE__, msg->flags, msg->len); ++ ++ hibvt_i2c_enable(i2c); ++ hibvt_i2c_clr_irq(i2c); ++ hibvt_i2c_set_addr(i2c); ++ hibvt_i2c_cfg_cmd(i2c); ++ hibvt_i2c_start_cmd(i2c); ++ ++ i2c->msg_buf_ptr = 0; ++ ++ if (msg->flags & I2C_M_RD) { ++ while (i2c->msg_buf_ptr < msg->len) { ++ status = hibvt_i2c_wait_rx_noempty(i2c); ++ if (status) ++ goto end; ++ ++ val = readl(i2c->base + HIBVT_I2C_RXF); ++ msg->buf[i2c->msg_buf_ptr] = val; ++ i2c->msg_buf_ptr++; ++ ++ } ++ } else { ++ while (i2c->msg_buf_ptr < msg->len) { ++ status = hibvt_i2c_wait_tx_nofull(i2c); ++ if (status) ++ goto end; ++ ++ val = msg->buf[i2c->msg_buf_ptr]; ++ writel(val, i2c->base + HIBVT_I2C_TXF); ++ i2c->msg_buf_ptr++; ++ } ++ } ++ ++ status = hibvt_i2c_wait_idle(i2c); ++end: ++ hibvt_i2c_disable(i2c); ++ ++ return status; ++} ++ ++static irqreturn_t hibvt_i2c_isr(int irq, void *dev_id) ++{ ++ struct hibvt_i2c_dev *i2c = dev_id; ++ unsigned int irq_status; ++ struct i2c_msg *msg = i2c->msg; ++ ++ spin_lock(&i2c->lock); ++ ++ irq_status = hibvt_i2c_clr_irq(i2c); ++ dev_dbg(i2c->dev, "%s RIS: 0x%x\n", __func__, irq_status); ++ ++ if (!irq_status) { ++ dev_dbg(i2c->dev, "no irq\n"); ++ goto end; ++ } ++ ++ if (irq_status & INTR_ABORT_MASK) { ++ dev_err(i2c->dev, "irq handle abort, RIS: 0x%x\n", ++ irq_status); ++ i2c->status = -EIO; ++ hibvt_i2c_disable_irq(i2c, INTR_ALL_MASK); ++ ++ complete(&i2c->msg_complete); ++ goto end; ++ } ++ ++ if (msg->flags & I2C_M_RD) { ++ while ((readl(i2c->base + HIBVT_I2C_STAT) & STAT_RXF_NOE_MASK) ++ && (i2c->msg_buf_ptr < msg->len)) { ++ msg->buf[i2c->msg_buf_ptr] = ++ readl(i2c->base + HIBVT_I2C_RXF); ++ i2c->msg_buf_ptr++; ++ } ++ } else { ++ while ((readl(i2c->base + HIBVT_I2C_STAT) & STAT_TXF_NOF_MASK) ++ && (i2c->msg_buf_ptr < msg->len)) { ++ writel(msg->buf[i2c->msg_buf_ptr], ++ i2c->base + HIBVT_I2C_TXF); ++ i2c->msg_buf_ptr++; ++ } ++ } ++ ++ if (i2c->msg_buf_ptr >= msg->len) ++ hibvt_i2c_disable_irq(i2c, INTR_TX_MASK | INTR_RX_MASK); ++ ++ if (irq_status & INTR_CMD_DONE_MASK) { ++ dev_dbg(i2c->dev, "cmd done\n"); ++ i2c->status = 0; ++ hibvt_i2c_disable_irq(i2c, INTR_ALL_MASK); ++ ++ complete(&i2c->msg_complete); ++ } ++ ++end: ++ spin_unlock(&i2c->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++static int hibvt_i2c_interrupt_xfer_one_msg(struct hibvt_i2c_dev *i2c) ++{ ++ int status; ++ struct i2c_msg *msg = i2c->msg; ++ unsigned long timeout; ++ unsigned long flags; ++ ++ dev_dbg(i2c->dev, "[%s,%d]msg->flags=0x%x, len=0x%x\n", ++ __func__, __LINE__, msg->flags, msg->len); ++ ++ reinit_completion(&i2c->msg_complete); ++ i2c->msg_buf_ptr = 0; ++ i2c->status = -EIO; ++ ++ spin_lock_irqsave(&i2c->lock, flags); ++ hibvt_i2c_enable(i2c); ++ hibvt_i2c_clr_irq(i2c); ++ if (msg->flags & I2C_M_RD) ++ hibvt_i2c_cfg_irq(i2c, INTR_USE_MASK & ~INTR_TX_MASK); ++ else ++ hibvt_i2c_cfg_irq(i2c, INTR_USE_MASK & ~INTR_RX_MASK); ++ ++ hibvt_i2c_set_addr(i2c); ++ hibvt_i2c_cfg_cmd(i2c); ++ hibvt_i2c_start_cmd(i2c); ++ spin_unlock_irqrestore(&i2c->lock, flags); ++ ++ timeout = wait_for_completion_timeout(&i2c->msg_complete, ++ I2C_IRQ_TIMEOUT); ++ ++ if (timeout == 0) { ++ hibvt_i2c_disable_irq(i2c, INTR_ALL_MASK); ++ status = -EIO; ++ dev_err(i2c->dev, "%s timeout\n", ++ msg->flags & I2C_M_RD ? "rx" : "tx"); ++ } else { ++ status = i2c->status; ++ } ++ ++ hibvt_i2c_disable(i2c); ++ ++ return status; ++} ++ ++/* ++ * Master transfer function ++ */ ++static int hibvt_i2c_xfer(struct i2c_adapter *adap, ++ struct i2c_msg *msgs, int num) ++{ ++ struct hibvt_i2c_dev *i2c = i2c_get_adapdata(adap); ++ int status = -EINVAL; ++ unsigned long flags; ++ ++ if (!msgs || (num <= 0)) { ++ dev_err(i2c->dev, "msgs == NULL || num <= 0, Invalid argument!\n"); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&i2c->lock, flags); ++ ++ i2c->msg = msgs; ++ i2c->msg_num = num; ++ i2c->msg_idx = 0; ++ ++ /* FIXME: The wait_for_completion_timeout in hibvt_i2c_interrupt_xfer_one_msg ++ * function can not be locked by spin_lock_irqsave. And actually I2C interrupt ++ * tranfer is rarely used, so we ignore the irq setting to limit the interrupt ++ * way. But we keep these codes below, reserve for future modifications */ ++ i2c->irq = -1; ++ ++ if (i2c->irq >= 0) { ++ while (i2c->msg_idx < i2c->msg_num) { ++ status = hibvt_i2c_interrupt_xfer_one_msg(i2c); ++ if (status) ++ break; ++ ++ i2c->msg++; ++ i2c->msg_idx++; ++ } ++ } else { ++ while (i2c->msg_idx < i2c->msg_num) { ++ status = hibvt_i2c_polling_xfer_one_msg(i2c); ++ if (status) ++ break; ++ ++ i2c->msg++; ++ i2c->msg_idx++; ++ } ++ } ++ ++ if (!status || i2c->msg_idx > 0) ++ status = i2c->msg_idx; ++ ++ spin_unlock_irqrestore(&i2c->lock, flags); ++ ++ return status; ++} ++ ++/* HI I2C READ * ++ * hi_i2c_master_recv - issue a single I2C message in master receive mode ++ * @client: Handle to slave device ++ * @buf: Where to store data read from slave ++ * @count: How many bytes to read, must be less than 64k since msg.len is u16 ++ * ++ * Returns negative errno, or else the number of bytes read. ++ */ ++int hi_i2c_master_recv(const struct i2c_client *client, char *buf, ++ int count) ++{ ++ printk("Wrong interface call." ++ "hi_i2c_transfer is the only interface to i2c read!!!\n"); ++ ++ return -EIO; ++} ++EXPORT_SYMBOL(hi_i2c_master_recv); ++ ++/*HI I2C WRITE* ++ * hi_i2c_master_send - issue a single I2C message in master transmit mode ++ * @client: Handle to slave device ++ * @buf: Data that will be written to the slave ++ * @count: How many bytes to write, must be less than 64k since msg.len is u16 ++ * ++ * Returns negative errno, or else the number of bytes written. ++ */ ++int hi_i2c_master_send(const struct i2c_client *client, ++ const char *buf, int count) ++{ ++ struct i2c_adapter *adap = client->adapter; ++ struct i2c_msg msg; ++ int msgs_count; ++ ++ if ((client->addr > 0x3ff) ++ || (((client->flags & I2C_M_TEN) == 0) && (client->addr > 0x7f))) { ++ printk(KERN_ERR "dev address out of range\n"); ++ return -EINVAL; ++ } ++ ++ msg.addr = client->addr; ++ msg.flags = client->flags; ++ msg.len = count; ++ ++ if (!buf) { ++ printk(KERN_ERR "Invalid buf == NULL!!!\n"); ++ return -EINVAL; ++ } ++ msg.buf = (__u8 *)buf; ++ ++ msgs_count = hibvt_i2c_xfer(adap, &msg, 1); ++ ++ return (msgs_count == 1) ? count : -EIO; ++} ++EXPORT_SYMBOL(hi_i2c_master_send); ++ ++/** ++ * hi_i2c_transfer - execute a single or combined I2C message ++ * @adap: Handle to I2C bus ++ * @msgs: One or more messages to execute before STOP is issued to ++ * terminate the operation; each message begins with a START. ++ * @num: Number of messages to be executed. ++ * ++ * Returns negative errno, else the number of messages executed. ++ * ++ * Note that there is no requirement that each message be sent to ++ * the same slave address, although that is the most common model. ++ */ ++int hi_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, ++ int num) ++{ ++ int msgs_count; ++ ++ if ((msgs[0].addr > 0x3ff) ++ || (((msgs[0].flags & I2C_M_TEN) == 0) && (msgs[0].addr > 0x7f))) { ++ printk(KERN_ERR "msgs[0] dev address out of range\n"); ++ return -EINVAL; ++ } ++ ++ if ((msgs[1].addr > 0x3ff) ++ || (((msgs[1].flags & I2C_M_TEN) == 0) && (msgs[1].addr > 0x7f))) { ++ printk(KERN_ERR "msgs[1] dev address out of range\n"); ++ return -EINVAL; ++ } ++ ++ msgs_count = hibvt_i2c_xfer(adap, msgs, num); ++ ++ return msgs_count; ++} ++EXPORT_SYMBOL(hi_i2c_transfer); ++ ++static u32 hibvt_i2c_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR ++ | I2C_FUNC_PROTOCOL_MANGLING; ++} ++ ++static const struct i2c_algorithm hibvt_i2c_algo = { ++ .master_xfer = hibvt_i2c_xfer, ++ .functionality = hibvt_i2c_func, ++}; ++ ++static int hibvt_i2c_probe(struct platform_device *pdev) ++{ ++ int status; ++ struct hibvt_i2c_dev *i2c; ++ struct i2c_adapter *adap; ++ struct resource *res; ++ ++ i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); ++ if (!i2c) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, i2c); ++ i2c->dev = &pdev->dev; ++ spin_lock_init(&i2c->lock); ++ init_completion(&i2c->msg_complete); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ i2c->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(i2c->base)) { ++ dev_err(i2c->dev, "cannot ioremap resource\n"); ++ return -ENOMEM; ++ } ++ ++ i2c->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(i2c->clk)) { ++ dev_err(i2c->dev, "cannot get clock\n"); ++ return -ENOENT; ++ } ++ clk_prepare_enable(i2c->clk); ++ ++ if (of_property_read_u32(pdev->dev.of_node, "clock-frequency", ++ &i2c->freq)) { ++ dev_warn(i2c->dev, "setting default clock-frequency@%dHz\n", ++ I2C_DEFAULT_FREQUENCY); ++ i2c->freq = I2C_DEFAULT_FREQUENCY; ++ } ++ ++ /* i2c controller initialization, disable interrupt */ ++ hibvt_i2c_hw_init(i2c); ++ ++ i2c->irq = platform_get_irq(pdev, 0); ++ status = devm_request_irq(&pdev->dev, i2c->irq, hibvt_i2c_isr, ++ IRQF_SHARED, dev_name(&pdev->dev), i2c); ++ if (status) { ++ dev_dbg(i2c->dev, "falling back to polling mode"); ++ i2c->irq = -1; ++ } ++ ++ adap = &i2c->adap; ++ i2c_set_adapdata(adap, i2c); ++ adap->owner = THIS_MODULE; ++ strlcpy(adap->name, "hibvt-i2c", sizeof(adap->name)); ++ adap->dev.parent = &pdev->dev; ++ adap->dev.of_node = pdev->dev.of_node; ++ adap->algo = &hibvt_i2c_algo; ++ ++ /* Add the i2c adapter */ ++ status = i2c_add_adapter(adap); ++ if (status) { ++ dev_err(i2c->dev, "failed to add bus to i2c core\n"); ++ goto err_add_adapter; ++ } ++ ++ dev_info(i2c->dev, "%s%d@%dhz registered\n", ++ adap->name, adap->nr, i2c->freq); ++ ++ return 0; ++ ++err_add_adapter: ++ clk_disable_unprepare(i2c->clk); ++ return status; ++} ++ ++static int hibvt_i2c_remove(struct platform_device *pdev) ++{ ++ struct hibvt_i2c_dev *i2c = platform_get_drvdata(pdev); ++ ++ clk_disable_unprepare(i2c->clk); ++ i2c_del_adapter(&i2c->adap); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int hibvt_i2c_suspend(struct device *dev) ++{ ++ struct hibvt_i2c_dev *i2c = dev_get_drvdata(dev); ++ ++ i2c_lock_adapter(&i2c->adap); ++ clk_disable_unprepare(i2c->clk); ++ i2c_unlock_adapter(&i2c->adap); ++ ++ return 0; ++} ++ ++static int hibvt_i2c_resume(struct device *dev) ++{ ++ struct hibvt_i2c_dev *i2c = dev_get_drvdata(dev); ++ ++ i2c_lock_adapter(&i2c->adap); ++ clk_prepare_enable(i2c->clk); ++ hibvt_i2c_hw_init(i2c); ++ i2c_unlock_adapter(&i2c->adap); ++ ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(hibvt_i2c_dev_pm, hibvt_i2c_suspend, ++ hibvt_i2c_resume); ++ ++static const struct of_device_id hibvt_i2c_match[] = { ++ { .compatible = "hisilicon,hibvt-i2c"}, ++ { .compatible = "hisilicon,hi3516cv300-i2c"}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, hibvt_i2c_match); ++ ++static struct platform_driver hibvt_i2c_driver = { ++ .driver = { ++ .name = "hibvt-i2c", ++ .of_match_table = hibvt_i2c_match, ++ .pm = &hibvt_i2c_dev_pm, ++ }, ++ .probe = hibvt_i2c_probe, ++ .remove = hibvt_i2c_remove, ++}; ++ ++module_platform_driver(hibvt_i2c_driver); ++ ++MODULE_AUTHOR("Pan Wen, <wenpan@hisilicon.com>"); ++MODULE_DESCRIPTION("HISILICON BVT I2C Bus driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/i2c/busses/i2c-hisi-v110.c b/drivers/i2c/busses/i2c-hisi-v110.c +new file mode 100644 +index 0000000..9e0e4ba +--- /dev/null ++++ b/drivers/i2c/busses/i2c-hisi-v110.c +@@ -0,0 +1,1263 @@ ++/* ++ * Hisilicon BVT I2C Controller Driver ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/dma-mapping.h> ++#include <linux/i2c.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++ ++#ifdef CONFIG_HI_DMAC ++#include <linux/hidmac.h> ++#include <mach/io.h> ++#endif ++ ++/* ++ * I2C Registers offsets ++ */ ++#define HII2C_CR 0x00 ++#define HII2C_TAR 0x04 ++#define I2C_DATA_CMD_REG 0x010 ++#define HII2C_SCL_H 0x1C ++#define HII2C_SCL_L 0x20 ++#define HII2C_IM 0x30 ++#define HII2C_RI 0x34 ++#define HII2C_IC 0x40 ++#define HII2C_EN 0x6C ++#define HII2C_SR 0x70 ++#define HII2C_SDA_HOLD 0x7C ++#define HII2C_TX_ABRT 0x80 ++#define I2C_DMA_CTRL_REG 0x088 ++#define I2C_DMA_TDLR 0x08C ++#define I2C_DMA_RDLR 0x090 ++#define HII2C_LOCK 0xAC ++#define HII2C_CR1 0xB0 ++#define HII2C_DATA 0xB4 ++#define HII2C_SEQU_CMD0 0xB8 ++#define I2C_DMA_CMD0 0x0B8 ++#define I2C_DMA_CMD1 0x0BC ++#define I2C_DMA_CMD2 0x0C0 ++ ++/* ++ * I2C Control Register -- HII2C_CR ++ */ ++#define CR_RESTART_EN (0x1 << 5) ++ ++/* ++ * I2C Slave Address Register -- HII2C_TAR ++ */ ++#define TAR_10BIT_MODE (0x1 << 12) ++#define TAR_SPECIAL (0x1 << 11) ++#define TAR_ADDR (0x3ff << 0) ++#define TAR_ADDR_SHIFT 0 ++ ++/* ++ * I2C Interrupt status and mask Register -- HII2C_IM ++ */ ++#define INT_TX_ABRT (1 << 6) ++#define MASK_ALL_INT 0xffffffff ++ ++/* ++ * I2C Status Register -- HII2C_SR ++ */ ++#define SR_BUSY (0x1 << 0) ++ ++ ++/* ++ * I2C Lock Register -- HII2C_LOCK ++ */ ++#define UNLOCK_VALUE 0x1ACCE551 ++ ++/* ++ * I2C Single Control Register -- HII2C_CR1 ++ */ ++#define CR1_SINGLE_MODE_EN (0x1 << 31) ++#define CR1_READ_MODE (0x1 << 30) ++#define CR1_H_WIDE (0x1 << 29) ++#define CR1_L_WIDE (0x1 << 28) ++#define CR1_STATUS_CLR (0xf << 24) ++#define CR1_TX_ABRT (0x1 << 23) ++#define CR1_TFNF (0x1 << 21) ++#define CR1_TFE (0x1 << 20) ++#define CR1_RFF (0x1 << 9) ++#define CR1_RFNE (0x1 << 8) ++ ++/* ++ * I2C Single CMD Register -- HII2C_DATA ++ */ ++#define DATA_H (0xffff << 16) ++#define DATA_L 0xffff ++#define DATA_H_SHIFT 16 ++#define DATA_L_SHIFT 0 ++ ++/* ++ * I2C Sequence CMD0 Register -- HII2C_SEQU_CMD0 ++ */ ++#define SEQU_MODE_EN (0x1 << 31) ++#define SEQU_MODE_WRITE (0x1 << 30) ++#define SEQU_ADDR_8BIT (0x0 << 28) ++#define SEQU_ADDR_16BIT (0x1 << 28) ++ ++#define HII2C_TIMEOUT 0x400 ++ ++unsigned int clk_rate = 0; ++ ++typedef enum i2c_mode { ++ I2C_MODE_SINGLE, ++ I2C_MODE_DMA, ++} i2c_mode_t; ++ ++struct hi_i2c { ++ struct platform_device *pdev; ++ struct i2c_adapter *adap; ++ struct device *dev; ++ resource_size_t phybase; ++ void __iomem *virtbase; ++ struct clk *clk; ++ ++ struct i2c_msg *msg; ++ ++ unsigned int frequency; ++ ++ __u16 last_slave_addr; ++ i2c_mode_t last_mode; ++ spinlock_t lock; ++}; ++ ++static inline void hi_i2c_disable(struct hi_i2c *i2c) ++{ ++ writel(0x0, i2c->virtbase + HII2C_EN); ++} ++ ++static inline void hi_i2c_enable(struct hi_i2c *i2c) ++{ ++ writel(0x1, i2c->virtbase + HII2C_EN); ++} ++ ++static inline void hi_i2c_disable_irq(struct hi_i2c *i2c, unsigned int flag) ++{ ++ unsigned int val; ++ ++ val = readl(i2c->virtbase + HII2C_IM); ++ val |= flag; ++ writel(val, i2c->virtbase + HII2C_IM); ++} ++ ++static inline void hi_i2c_set_slave_addr(struct hi_i2c *i2c, __u16 addr, ++ unsigned char ten_bit_flag) ++{ ++ unsigned int val; ++ ++ hi_i2c_disable(i2c); ++ ++ val = readl(i2c->virtbase + HII2C_TAR); ++ val &= ~TAR_ADDR; ++ val |= (addr << TAR_ADDR_SHIFT) & TAR_ADDR; ++ ++ if (ten_bit_flag) ++ val |= TAR_10BIT_MODE; ++ else ++ val &= ~TAR_10BIT_MODE; ++ ++ writel(val, i2c->virtbase + HII2C_TAR); ++ ++ i2c->last_slave_addr = addr; ++ ++ hi_i2c_enable(i2c); ++} ++ ++static inline void hi_i2c_set_mode(struct hi_i2c *i2c, i2c_mode_t mode) ++{ ++ unsigned int val; ++ ++ hi_i2c_disable(i2c); ++ ++ if (mode == I2C_MODE_SINGLE) { ++ val = readl(i2c->virtbase + HII2C_SEQU_CMD0); ++ val &= ~SEQU_MODE_EN; ++ writel(val, i2c->virtbase + HII2C_SEQU_CMD0); ++ ++ val = readl(i2c->virtbase + HII2C_CR1); ++ val |= CR1_SINGLE_MODE_EN; ++ writel(val, i2c->virtbase + HII2C_CR1); ++ } else { ++ val = readl(i2c->virtbase + HII2C_CR1); ++ val &= ~CR1_SINGLE_MODE_EN; ++ writel(val, i2c->virtbase + HII2C_CR1); ++ } ++ ++ i2c->last_mode = mode; ++ ++ hi_i2c_enable(i2c); ++} ++ ++static inline int hi_i2c_clr_status(struct hi_i2c *i2c) ++{ ++ unsigned int val; ++ ++ val = readl(i2c->virtbase + HII2C_CR1); ++ val |= CR1_STATUS_CLR; ++ writel(val & (~CR1_SINGLE_MODE_EN), i2c->virtbase + HII2C_CR1); ++ writel(val, i2c->virtbase + HII2C_CR1); ++ ++ writel(0x1, i2c->virtbase + HII2C_IC); ++ ++ hi_i2c_disable(i2c); ++ ++ hi_i2c_enable(i2c); ++ ++ return 0; ++} ++ ++static u32 hi_i2c_get_delay_val(struct hi_i2c *i2c) ++{ ++ unsigned int scl_l; ++ u32 val; ++ ++ scl_l = readl(i2c->virtbase + HII2C_SCL_L); ++ ++ /* delay_val = scl_l * 2.5 * 1000 * 1000 / clk_rate */ ++ val = (25 * 100000 / clk_rate) + 1;// +1 is to make the time plenty more ++ val = val * scl_l; ++ ++ return val; ++} ++ ++static int hi_i2c_wait_idle(struct hi_i2c *i2c) ++{ ++ unsigned int time_cnt; ++ unsigned int val; ++ struct device *dev = i2c->dev; ++ ++ time_cnt = 0; ++ do { ++ val = readl(i2c->virtbase + HII2C_RI); ++ if (val & INT_TX_ABRT) { ++ dev_err(dev, "%s: wait last fifo is empyt abort!", ++ __func__); ++ dev_err(dev, "RI: %#x\n", val); ++ val = readl(i2c->virtbase + HII2C_TX_ABRT); ++ dev_err(i2c->dev, "TX_ABRT:0x%x\n", val); ++ return -EIO; ++ } ++ ++ val = readl(i2c->virtbase + HII2C_CR1); ++ if (val & CR1_RFNE) ++ readl(i2c->virtbase + HII2C_DATA); ++ ++ if (val & CR1_TFE & (~CR1_RFNE)) ++ break; ++ ++ if (time_cnt >= HII2C_TIMEOUT) { ++ dev_err(dev, "%s: wit last fifo is empty timeout!", ++ __func__); ++ dev_err(dev, "CR1:0x%x\n", val); ++ return -EBUSY; ++ } ++ time_cnt++; ++ udelay(50); ++ } while (1); ++ ++ udelay(10); ++ ++ time_cnt = 0; ++ do { ++ val = readl(i2c->virtbase + HII2C_RI); ++ if (val & INT_TX_ABRT) { ++ dev_err(dev, "%s: wait last i2c is idle abort!", ++ __func__); ++ dev_err(dev, "RI: %#x\n", val); ++ val = readl(i2c->virtbase + HII2C_TX_ABRT); ++ dev_err(i2c->dev, "TX_ABRT:0x%x\n", val); ++ return -EIO; ++ } ++ ++ val = readl(i2c->virtbase + HII2C_SR); ++ if (!(val & SR_BUSY)) ++ break; ++ ++ if (time_cnt >= HII2C_TIMEOUT) { ++ dev_err(dev, "%s: wait last i2c is idle timeout!", ++ __func__); ++ dev_err(dev, "CR1:0x%x\n", val); ++ return -EBUSY; ++ } ++ time_cnt++; ++ udelay(50); ++ } while (1); ++ ++ return 0; ++} ++ ++static int hi_i2c_wait_tx_nofull(struct hi_i2c *i2c) ++{ ++ unsigned int val; ++ unsigned int time_cnt; ++ struct device *dev = i2c->dev; ++ ++ time_cnt = 0; ++ do { ++ val = readl(i2c->virtbase + HII2C_RI); ++ if (val & INT_TX_ABRT) { ++ dev_err(dev, "wait tx no full abort, last RI: %#x\n", ++ val); ++ val = readl(i2c->virtbase + HII2C_TX_ABRT); ++ dev_err(i2c->dev, "TX_ABRT:0x%x\n", val); ++ return -EIO; ++ } ++ ++ val = readl(i2c->virtbase + HII2C_CR1); ++ if (val & CR1_RFNE) ++ readl(i2c->virtbase + HII2C_DATA); ++ ++ if (val & CR1_TFNF) ++ break; ++ ++ if (time_cnt >= HII2C_TIMEOUT) { ++ dev_err(dev, "wait tx no full timeout, last CR1: %#x\n", ++ val); ++ return -EBUSY; ++ } ++ time_cnt++; ++ udelay(50); ++ } while (1); ++ ++ return 0; ++} ++ ++static int hi_i2c_wait_rx_noempty(struct hi_i2c *i2c) ++{ ++ unsigned int val; ++ unsigned int time_cnt; ++ struct device *dev = i2c->dev; ++ ++ time_cnt = 0; ++ do { ++ val = readl(i2c->virtbase + HII2C_RI); ++ if (val & INT_TX_ABRT) { ++ dev_err(dev, "wait rx no empty abort, RI: %#x\n", val); ++ val = readl(i2c->virtbase + HII2C_TX_ABRT); ++ dev_err(i2c->dev, "TX_ABRT:0x%x\n", val); ++ return -EIO; ++ } ++ ++ val = readl(i2c->virtbase + HII2C_CR1); ++ if (val & CR1_RFNE) ++ break; ++ ++ if (time_cnt >= HII2C_TIMEOUT) { ++ dev_err(dev, "\nwait rx no empty timeout, CR1: %#x\n", ++ val); ++ return -EBUSY; ++ } ++ time_cnt++; ++ udelay(50); ++ } while (1); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_HI_DMAC ++#define REVERT_HL_BYTE(value) ((value >> 8) | ((value & 0xFF) << 8)) ++ ++void hi_i2c_dma_start(struct hi_i2c *i2c, unsigned int dir) ++{ ++ writel((1 << dir), i2c->virtbase + I2C_DMA_CTRL_REG); ++} ++ ++void hi_i2c_dmac_config(struct hi_i2c *i2c, unsigned int dir) ++{ ++ /* 1. enable RX(0) or TX(1) in DMA mode */ ++ hi_i2c_dma_start(i2c, dir); ++ ++ /* 2. set dma fifo */ ++ writel(4, i2c->virtbase + I2C_DMA_TDLR); ++ writel(4, i2c->virtbase + I2C_DMA_RDLR); ++} ++ ++void hi_i2c_start_rx(struct hi_i2c *i2c, unsigned int reg_addr, ++ unsigned int length) ++{ ++ unsigned int reg; ++ ++ writel(reg_addr, i2c->virtbase + I2C_DMA_CMD1); ++ writel(length, i2c->virtbase + I2C_DMA_CMD2); ++ ++ reg = readl(i2c->virtbase + I2C_DMA_CMD0); ++ ++ /*start dma rx*/ ++ reg |= SEQU_MODE_EN; ++ reg &= ~SEQU_MODE_WRITE; ++ writel(reg, i2c->virtbase + I2C_DMA_CMD0); ++} ++ ++void hi_i2c_start_tx(struct hi_i2c *i2c, unsigned int reg_addr, ++ unsigned int length) ++{ ++ unsigned int reg; ++ ++ writel(reg_addr, i2c->virtbase + I2C_DMA_CMD1); ++ writel(length, i2c->virtbase + I2C_DMA_CMD2); ++ ++ reg = readl(i2c->virtbase + I2C_DMA_CMD0); ++ ++ /*start dma tx*/ ++ reg |= SEQU_MODE_EN; ++ reg |= SEQU_MODE_WRITE; ++ writel(reg, i2c->virtbase + I2C_DMA_CMD0); ++} ++ ++int dma_to_i2c(unsigned int src, unsigned int dst, unsigned int length) ++{ ++ int chan; ++ ++ chan = do_dma_m2p(src, dst, length); ++ if (chan == -1) ++ pr_err("dma_to_i2c error\n"); ++ ++ return chan; ++} ++ ++ ++int i2c_to_dma(unsigned int src, unsigned int dst, unsigned int length) ++{ ++ int chan; ++ ++ chan = do_dma_p2m(dst, src, length); ++ if (chan == -1) ++ pr_err("dma_p2m error...\n"); ++ ++ return chan; ++} ++ ++static int hi_i2c_do_dma_write(struct hi_i2c *i2c, ++ unsigned int reg_addr, unsigned int reg_addr_num, ++ unsigned int dma_buf, unsigned int len) ++{ ++ int chan; ++ struct i2c_msg *msg = i2c->msg; ++ unsigned int temp_reg = reg_addr; ++ ++ if ((msg->addr != i2c->last_slave_addr) ++ || (i2c->last_mode != I2C_MODE_DMA)) { ++ hi_i2c_set_slave_addr(i2c, msg->addr, msg->flags & I2C_M_TEN); ++ hi_i2c_set_mode(i2c, I2C_MODE_DMA); ++ } ++ ++ if (2 == reg_addr_num) { ++ /* switch high byte and low byte */ ++ temp_reg = REVERT_HL_BYTE(reg_addr); ++ writel(SEQU_ADDR_16BIT, i2c->virtbase + I2C_DMA_CMD0); ++ } else { ++ writel(SEQU_ADDR_8BIT, i2c->virtbase + I2C_DMA_CMD0); ++ } ++ ++ /* 2. config i2c into DMA mode */ ++ hi_i2c_dmac_config(i2c, 0x1); ++ ++ /* 3. start i2c logic to write */ ++ hi_i2c_start_tx(i2c, temp_reg, len - 1); ++ ++ /* 4. transmit DATA from DMAC to I2C in DMA mode */ ++ chan = dma_to_i2c(dma_buf, (i2c->phybase + I2C_DATA_CMD_REG), ++ len); ++ if (chan == -1) { ++ return -1; ++ } ++ ++ if (dmac_wait(chan) != DMAC_CHN_SUCCESS) { ++ dev_err(i2c->dev, "dma wait failed\n"); ++ hi_i2c_clr_status(i2c); ++ dmac_channel_free(chan); ++ return -1; ++ } ++ ++ hi_i2c_wait_idle(i2c); ++ hi_i2c_clr_status(i2c); ++ ++ dmac_channel_free(chan); ++ ++ return 0; ++} ++ ++static int hi_i2c_do_dma_read(struct hi_i2c *i2c, ++ unsigned int reg_addr, unsigned int reg_addr_num, ++ unsigned int dma_buf, unsigned int len) ++{ ++ int chan; ++ struct i2c_msg *msg = i2c->msg; ++ unsigned int temp_reg = reg_addr; ++ ++ if ((msg->addr != i2c->last_slave_addr) ++ || (I2C_MODE_DMA != i2c->last_mode)) { ++ hi_i2c_set_slave_addr(i2c, msg->addr, msg->flags & I2C_M_TEN); ++ hi_i2c_set_mode(i2c, I2C_MODE_DMA); ++ } ++ ++ if (2 == reg_addr_num) { ++ /* switch high byte and low byte */ ++ temp_reg = REVERT_HL_BYTE(reg_addr); ++ writel(SEQU_ADDR_16BIT, i2c->virtbase + I2C_DMA_CMD0); ++ } else { ++ writel(SEQU_ADDR_8BIT, i2c->virtbase + I2C_DMA_CMD0); ++ } ++ ++ /* 2. config i2c into DMA mode */ ++ hi_i2c_dmac_config(i2c, 0x0); ++ ++ /* 3. transmit DATA from I2C to DMAC in DMA mode */ ++ chan = i2c_to_dma((i2c->phybase + I2C_DATA_CMD_REG), ++ dma_buf, len); ++ if (chan == -1) { ++ return -1; ++ } ++ ++ /* 4. start i2c logic to read */ ++ hi_i2c_start_rx(i2c, temp_reg, len - 1); ++ ++ if (dmac_wait(chan) != DMAC_CHN_SUCCESS) { ++ dev_err(i2c->dev, "dma wait failed\n"); ++ hi_i2c_clr_status(i2c); ++ dmac_channel_free(chan); ++ return -1; ++ } ++ ++ hi_i2c_wait_idle(i2c); ++ hi_i2c_clr_status(i2c); ++ ++ dmac_channel_free(chan); ++ ++ return 0; ++} ++#else ++static int hi_i2c_do_dma_write(struct hi_i2c *i2c, ++ unsigned int reg_addr, unsigned int reg_addr_num, ++ unsigned int dma_buf, unsigned int len) ++{ ++ dev_err(i2c->dev, "DMA is not enabled!"); ++ return -1; ++} ++ ++static int hi_i2c_do_dma_read(struct hi_i2c *i2c, ++ unsigned int reg_addr, unsigned int reg_addr_num, ++ unsigned int dma_buf, unsigned int len) ++{ ++ dev_err(i2c->dev, "DMA is not enabled!"); ++ return -1; ++} ++#endif ++ ++int hi_i2c_dma_write(const struct i2c_client *client, unsigned int dma_buf, ++ unsigned int reg_addr, unsigned int reg_addr_num, ++ unsigned int len) ++{ ++ struct i2c_adapter *adap = client->adapter; ++ struct hi_i2c *i2c = i2c_get_adapdata(adap); ++ struct i2c_msg msg; ++ int status; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&i2c->lock, flags); ++ ++ memset(&msg, 0x0, sizeof(struct i2c_msg)); ++ msg.addr = client->addr; ++ msg.flags = client->flags; ++ msg.len = len; ++ ++ i2c->msg = &msg; ++ ++ status = hi_i2c_do_dma_write(i2c, reg_addr, reg_addr_num, dma_buf, len); ++ ++ spin_unlock_irqrestore(&i2c->lock, flags); ++ ++ return status; ++} ++EXPORT_SYMBOL(hi_i2c_dma_write); ++ ++int hi_i2c_dma_read(const struct i2c_client *client, unsigned int dma_buf, ++ unsigned int reg_addr, unsigned int reg_addr_num, ++ unsigned int len) ++{ ++ struct i2c_adapter *adap = client->adapter; ++ struct hi_i2c *i2c = i2c_get_adapdata(adap); ++ struct i2c_msg msg; ++ int status; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&i2c->lock, flags); ++ ++ memset(&msg, 0x0, sizeof(struct i2c_msg)); ++ msg.addr = client->addr; ++ msg.flags = client->flags; ++ msg.flags |= I2C_M_RD; ++ msg.len = len; ++ ++ i2c->msg = &msg; ++ ++ status = hi_i2c_do_dma_read(i2c, reg_addr, reg_addr_num, dma_buf, len); ++ ++ spin_unlock_irqrestore(&i2c->lock, flags); ++ ++ return status; ++} ++EXPORT_SYMBOL(hi_i2c_dma_read); ++ ++static void hi_i2c_set_rate(struct hi_i2c *i2c) ++{ ++ unsigned int max_frequency, freq; ++ unsigned int scl_h, scl_l, sda_hold; ++ ++ freq = i2c->frequency; ++ clk_rate = clk_get_rate(i2c->clk); ++ BUG_ON(!clk_rate); ++ ++ max_frequency = clk_rate >> 1; ++ ++ if (freq > max_frequency) { ++ i2c->frequency = max_frequency; ++ freq = i2c->frequency; ++ } ++ ++ if (!freq) { ++ pr_err("freq can not be zero...\n"); ++ return; ++ } ++ ++ /* set SCLH and SCLL depend on clk_rate and freq */ ++ if (freq <= 100000) { ++ /* in normal mode F_scl: freq ++ i2c_scl_hcnt = (F_i2c / F_scl) * 0.5 ++ i2c_scl_hcnt = (F_i2c / F_scl) * 0.5 ++ */ ++ scl_h = clk_rate / (freq * 2); ++ scl_l = scl_h; ++ } else { ++ /* in fast mode F_scl: freq ++ i2c_scl_hcnt = (F_i2c / F_scl) * 0.36 ++ i2c_scl_hcnt = (F_i2c / F_scl) * 0.64 ++ */ ++ scl_h = ((clk_rate / 100) * 36) / freq; ++ scl_l = ((clk_rate / 100) * 64) / freq; ++ } ++ ++ writel(scl_h, i2c->virtbase + HII2C_SCL_H); ++ writel(scl_l, i2c->virtbase + HII2C_SCL_L); ++ ++ sda_hold = scl_h / 2; ++ writel(sda_hold, i2c->virtbase + HII2C_SDA_HOLD); ++} ++ ++static void hi_i2c_hw_init(struct hi_i2c *i2c) ++{ ++ unsigned int val; ++ ++ /* unlock hi_i2c controller to access */ ++ writel(UNLOCK_VALUE, i2c->virtbase + HII2C_LOCK); ++ ++ hi_i2c_disable(i2c); ++ ++ val = readl(i2c->virtbase + HII2C_CR); ++ val |= CR_RESTART_EN; ++ writel(val, i2c->virtbase + HII2C_CR); ++ ++ hi_i2c_set_rate(i2c); ++ ++ hi_i2c_disable_irq(i2c, MASK_ALL_INT); ++ ++ writel(0x0, i2c->virtbase + HII2C_TAR); ++ hi_i2c_set_slave_addr(i2c, 0, 0); ++ hi_i2c_set_mode(i2c, I2C_MODE_SINGLE); ++ i2c->last_slave_addr = 0; ++ i2c->last_mode = I2C_MODE_SINGLE; ++ ++ hi_i2c_enable(i2c); ++} ++ ++static int hi_i2c_polling_xfer_one_msg(struct hi_i2c *i2c) ++{ ++ int status; ++ unsigned val; ++ struct i2c_msg *msg = i2c->msg; ++ unsigned int tmp; ++ unsigned int data_h = 0; ++ unsigned int data_l = 0; ++ ++ dev_dbg(i2c->dev, "[%s,%d]\n", __func__, __LINE__); ++ ++ tmp = !CR1_READ_MODE; ++ switch (msg->len) { ++ case 2: ++ tmp &= ~CR1_H_WIDE; ++ tmp &= ~CR1_L_WIDE; ++ ++ data_h = msg->buf[0]; ++ data_l = msg->buf[1]; ++ break; ++ case 3: ++ tmp |= CR1_H_WIDE; ++ tmp &= ~CR1_L_WIDE; ++ ++ data_h = msg->buf[0]; ++ data_h |= msg->buf[1] << 8; ++ data_l = msg->buf[2]; ++ break; ++ case 4: ++ tmp |= CR1_H_WIDE; ++ tmp |= CR1_L_WIDE; ++ ++ data_h = msg->buf[0]; ++ data_h |= msg->buf[1] << 8; ++ data_l = msg->buf[2]; ++ data_l |= msg->buf[3] << 8; ++ break; ++ default: ++ dev_err(i2c->dev, "Unsupported this length: %d!\n", msg->len); ++ return -EIO; ++ } ++ ++ if ((msg->addr != i2c->last_slave_addr) ++ || (I2C_MODE_SINGLE != i2c->last_mode)) { ++ hi_i2c_set_slave_addr(i2c, msg->addr, msg->flags & I2C_M_TEN); ++ hi_i2c_set_mode(i2c, I2C_MODE_SINGLE); ++ } ++ ++ val = readl(i2c->virtbase + HII2C_CR1); ++ val &= ~CR1_H_WIDE & ~CR1_L_WIDE & ~CR1_READ_MODE; ++ val |= tmp; ++ writel(val, i2c->virtbase + HII2C_CR1); ++ do { ++ status = hi_i2c_wait_tx_nofull(i2c); ++ if (status) ++ break; ++ ++ val = (data_h << DATA_H_SHIFT) | (data_l << DATA_L_SHIFT); ++ writel(val, i2c->virtbase + HII2C_DATA); ++ } while (0); ++ ++ udelay(hi_i2c_get_delay_val(i2c)); ++ ++ if (!status) ++ status = hi_i2c_wait_idle(i2c); ++ ++ hi_i2c_clr_status(i2c); ++ ++ return status; ++} ++ ++static int hi_i2c_polling_xfer_two_msg(struct hi_i2c *i2c) ++{ ++ int status; ++ unsigned val; ++ struct i2c_msg *msg = i2c->msg; ++ unsigned int tmp; ++ unsigned int data_h = 0; ++ unsigned int data_l = 0; ++ ++ ++ dev_dbg(i2c->dev, "[%s,%d]\n", __func__, __LINE__); ++ ++ tmp = CR1_READ_MODE; ++ switch (msg->len) { ++ case 1: ++ tmp &= ~CR1_H_WIDE; ++ ++ data_h = msg->buf[0]; ++ break; ++ case 2: ++ tmp |= CR1_H_WIDE; ++ ++ data_h = msg->buf[0]; ++ data_h |= msg->buf[1] << 8; ++ break; ++ default: ++ dev_err(i2c->dev, "Unsupported this length: %d!\n", msg->len); ++ return -EIO; ++ } ++ ++ msg++; ++ ++ switch (msg->len) { ++ case 1: ++ tmp &= ~CR1_L_WIDE; ++ break; ++ case 2: ++ tmp |= CR1_L_WIDE; ++ break; ++ default: ++ dev_err(i2c->dev, "Unsupported this length: %d!\n", msg->len); ++ return -EIO; ++ } ++ ++ if ((msg->addr != i2c->last_slave_addr) ++ || (I2C_MODE_SINGLE != i2c->last_mode)) { ++ hi_i2c_set_slave_addr(i2c, msg->addr, msg->flags & I2C_M_TEN); ++ hi_i2c_set_mode(i2c, I2C_MODE_SINGLE); ++ } ++ ++ val = readl(i2c->virtbase + HII2C_CR1); ++ val &= ~CR1_H_WIDE & ~CR1_L_WIDE & ~CR1_READ_MODE; ++ val |= tmp; ++ writel(val, i2c->virtbase + HII2C_CR1); ++ do { ++ status = hi_i2c_wait_tx_nofull(i2c); ++ if (status) ++ break; ++ ++ val = data_h << DATA_H_SHIFT; ++ writel(val, i2c->virtbase + HII2C_DATA); ++ ++ status = hi_i2c_wait_rx_noempty(i2c); ++ if (status) ++ break; ++ ++ data_l = readl(i2c->virtbase + HII2C_DATA) & DATA_L; ++ ++ switch (msg->len) { ++ case 1: ++ msg->buf[0] = data_l & 0xff; ++ break; ++ case 2: ++ msg->buf[0] = data_l & 0xff; ++ msg->buf[1] = (data_l >> 8) & 0xff; ++ break; ++ default: ++ status = -EIO; ++ dev_err(i2c->dev, "Unsupported this length: %d!\n", ++ msg->len); ++ break; ++ } ++ } while (0); ++ ++ if (!status) ++ status = hi_i2c_wait_idle(i2c); ++ ++ hi_i2c_clr_status(i2c); ++ ++ return status; ++} ++ ++static int hi_i2c_dma_xfer_one_msg(struct hi_i2c *i2c) ++{ ++ int status; ++ struct i2c_msg *msg = i2c->msg; ++ dma_addr_t dma_buf; ++ unsigned int reg_addr; ++ unsigned int reg_width; ++ ++ dev_dbg(i2c->dev, "[%s,%d]\n", __func__, __LINE__); ++ ++ if (msg->len < 2) { ++ dev_err(i2c->dev, "Unsupported this length: %d!\n", msg->len); ++ return -EIO; ++ } ++ reg_addr = msg->buf[0]; ++ reg_width = 1; ++ ++ dma_buf = dma_map_single(i2c->dev, msg->buf, msg->len, DMA_TO_DEVICE); ++ ++ if (dma_mapping_error(i2c->dev, dma_buf)) { ++ dev_err(i2c->dev, "DMA mapping failed\n"); ++ return -EINVAL; ++ } ++ ++ status = hi_i2c_do_dma_write(i2c, reg_addr, reg_width, ++ dma_buf + reg_width, msg->len - reg_width); ++ ++ dma_unmap_single(i2c->dev, dma_buf, msg->len, DMA_TO_DEVICE); ++ ++ return status; ++} ++ ++static int hi_i2c_dma_xfer_two_msg(struct hi_i2c *i2c) ++{ ++ int status; ++ struct i2c_msg *msg = i2c->msg; ++ dma_addr_t dma_buf; ++ unsigned int reg_addr; ++ unsigned int reg_width; ++ ++ dev_dbg(i2c->dev, "[%s,%d]\n", __func__, __LINE__); ++ ++ switch (msg->len) { ++ case 1: ++ reg_addr = msg->buf[0]; ++ reg_width = 1; ++ break; ++ case 2: ++ reg_addr = msg->buf[0] << 8; ++ reg_addr |= msg->buf[1]; ++ reg_width = 2; ++ break; ++ default: ++ dev_err(i2c->dev, "Unsupported this length: %d!\n", msg->len); ++ return -EIO; ++ } ++ ++ msg++; ++ ++ if (msg->len <= 0) { ++ dev_err(i2c->dev, "Unsupported this length: %d!\n", msg->len); ++ return -EIO; ++ } ++ ++ dma_buf = dma_map_single(i2c->dev, msg->buf, msg->len, DMA_FROM_DEVICE); ++ ++ if (dma_mapping_error(i2c->dev, dma_buf)) { ++ dev_err(i2c->dev, "DMA mapping failed\n"); ++ return -EINVAL; ++ } ++ ++ status = hi_i2c_do_dma_read(i2c, reg_addr, reg_width, dma_buf, ++ msg->len); ++ ++ dma_unmap_single(i2c->dev, dma_buf, msg->len, DMA_FROM_DEVICE); ++ ++ return status; ++} ++ ++static int hi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, ++ int num) ++{ ++ struct hi_i2c *i2c = i2c_get_adapdata(adap); ++ int status; ++ struct i2c_msg *msg = i2c->msg; ++ unsigned int msg_idx; ++ unsigned long flags; ++ ++ if (!msgs || (num <= 0)) { ++ dev_err(i2c->dev, "msgs == NULL || num <= 0, Invalid argument!\n"); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&i2c->lock, flags); ++ ++ i2c->msg = msgs; ++ msg_idx = 0; ++ ++ while (msg_idx < num) { ++ msg = i2c->msg; ++ ++ if (msg->flags & I2C_M_RD) { ++ status = -EIO; ++ dev_err(i2c->dev, "Unsupported read-only!\n"); ++ break; ++ } ++ ++ if ((msg_idx < (num - 1)) && ((msg + 1)->flags & I2C_M_RD)) { ++ if ((msg + 1)->len <= 2) ++ status = hi_i2c_polling_xfer_two_msg(i2c); ++ else ++ status = hi_i2c_dma_xfer_two_msg(i2c); ++ ++ if (status) ++ break; ++ ++ i2c->msg += 2; ++ msg_idx += 2; ++ } else { ++ if (msg->len <= 4) ++ status = hi_i2c_polling_xfer_one_msg(i2c); ++ else ++ status = hi_i2c_dma_xfer_one_msg(i2c); ++ ++ if (status) ++ break; ++ ++ i2c->msg++; ++ msg_idx++; ++ } ++ } ++ ++ if (!status || msg_idx > 0) ++ status = msg_idx; ++ else ++ status = -EIO; ++ ++ spin_unlock_irqrestore(&i2c->lock, flags); ++ ++ return status; ++} ++ ++/* HI I2C READ * ++ * hi_i2c_master_recv - issue a single I2C message in master receive mode ++ * @client: Handle to slave device ++ * @buf: Where to store data read from slave ++ * @count: How many bytes to read, must be less than 64k since msg.len is u16 ++ * ++ * Returns negative errno, or else the number of bytes read. ++ */ ++int hi_i2c_master_recv(const struct i2c_client *client, char *buf, ++ int count) ++{ ++ printk("Wrong interface call." ++ "hi_i2c_transfer is the only interface to i2c read!!!\n"); ++ ++ return -EIO; ++} ++EXPORT_SYMBOL(hi_i2c_master_recv); ++ ++/*HI I2C WRITE* ++ * hi_i2c_master_send - issue a single I2C message in master transmit mode ++ * @client: Handle to slave device ++ * @buf: Data that will be written to the slave ++ * @count: How many bytes to write, must be less than 64k since msg.len is u16 ++ * ++ * Returns negative errno, or else the number of bytes written. ++ */ ++int hi_i2c_master_send(const struct i2c_client *client, ++ const char *buf, int count) ++{ ++ struct i2c_adapter *adap = client->adapter; ++ struct i2c_msg msg; ++ int msgs_count; ++ ++ if ((client->addr > 0x3ff) ++ || (((client->flags & I2C_M_TEN) == 0) && (client->addr > 0x7f))) { ++ printk(KERN_ERR "dev address out of range\n"); ++ return -EINVAL; ++ } ++ ++ msg.addr = client->addr; ++ msg.flags = client->flags; ++ msg.len = count; ++ ++ if (!buf) { ++ printk(KERN_ERR "Invalid buf == NULL!!!\n"); ++ return -EINVAL; ++ } ++ msg.buf = (__u8 *)buf; ++ ++ msgs_count = hi_i2c_xfer(adap, &msg, 1); ++ ++ return (msgs_count == 1) ? count : -EIO; ++} ++EXPORT_SYMBOL(hi_i2c_master_send); ++ ++/** ++ * hi_i2c_transfer - execute a single or combined I2C message ++ * @adap: Handle to I2C bus ++ * @msgs: One or more messages to execute before STOP is issued to ++ * terminate the operation; each message begins with a START. ++ * @num: Number of messages to be executed. ++ * ++ * Returns negative errno, else the number of messages executed. ++ * ++ * Note that there is no requirement that each message be sent to ++ * the same slave address, although that is the most common model. ++ */ ++ ++int hi_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, ++ int num) ++{ ++ int msgs_count; ++ ++ if ((msgs[0].addr > 0x3ff) ++ || (((msgs[0].flags & I2C_M_TEN) == 0) && (msgs[0].addr > 0x7f))) { ++ printk(KERN_ERR "msgs[0] dev address out of range\n"); ++ return -EINVAL; ++ } ++ ++ if ((msgs[1].addr > 0x3ff) ++ || (((msgs[1].flags & I2C_M_TEN) == 0) && (msgs[1].addr > 0x7f))) { ++ printk(KERN_ERR "msgs[1] dev address out of range\n"); ++ return -EINVAL; ++ } ++ ++ msgs_count = hi_i2c_xfer(adap, msgs, num); ++ ++ return msgs_count; ++} ++EXPORT_SYMBOL(hi_i2c_transfer); ++ ++static u32 hi_i2c_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR ++ | I2C_FUNC_PROTOCOL_MANGLING; ++} ++ ++static const struct i2c_algorithm hi_i2c_algorithm = { ++ .master_xfer = hi_i2c_xfer, ++ .functionality = hi_i2c_func, ++}; ++ ++/* hi_i2c_parse_dt ++ * ++ * Parse the device tree node. ++ */ ++static int hi_i2c_parse_dt(struct hi_i2c *i2c) ++{ ++ struct device *dev = i2c->dev; ++ struct device_node *np = dev->of_node; ++ ++ if (!np) { ++ dev_err(dev, "no dt node defined\n"); ++ return -ENODEV; ++ } ++ ++ if (of_property_read_u32(np, "clock-frequency", &i2c->frequency)) { ++ dev_dbg(dev, "of read error clock-frequency = %d\n", ++ i2c->frequency); ++ i2c->frequency = 4000000; ++ } ++ ++ return 0; ++} ++ ++static int hi_i2c_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct hi_i2c *i2c; ++ struct i2c_adapter *adap; ++ struct resource *res; ++ int status; ++ ++ dev_info(dev, "HISILICON I2C V110 bus driver\n"); ++ ++ adap = devm_kzalloc(&pdev->dev, ++ sizeof(struct i2c_adapter) + sizeof(struct hi_i2c), ++ GFP_KERNEL); ++ if (!adap) ++ return -ENOMEM; ++ ++ adap->dev.parent = dev; ++ adap->dev.of_node = dev->of_node; ++ i2c_set_adapdata(adap, &adap[1]); ++ ++ adap->owner = THIS_MODULE; ++ adap->class = I2C_CLASS_DEPRECATED; ++ adap->algo = &hi_i2c_algorithm; ++ strlcpy(adap->name, "hisi-i2c-v110", sizeof(adap->name)); ++ ++ i2c = i2c_get_adapdata(adap); ++ platform_set_drvdata(pdev, i2c); ++ spin_lock_init(&i2c->lock); ++ ++ i2c->pdev = pdev; ++ i2c->adap = adap; ++ i2c->dev = dev; ++ ++ status = hi_i2c_parse_dt(i2c); ++ if (status) ++ goto err_ioremap; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ goto err_ioremap; ++ ++ i2c->virtbase = devm_ioremap_resource(dev, res); ++ if (i2c->virtbase == NULL) { ++ status = PTR_ERR(i2c->virtbase); ++ dev_err(dev, "cannot ioremap resource\n"); ++ goto err_ioremap; ++ } ++ i2c->phybase = res->start; ++ dev_info(dev, "mapped registers from 0x%x to 0x%p\n", ++ res->start, i2c->virtbase); ++ ++ /* find the clock and enable it */ ++ i2c->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(i2c->clk)) { ++ status = PTR_ERR(i2c->clk); ++ dev_err(dev, "cannot get clock\n"); ++ goto err_clk; ++ } ++ status = clk_prepare_enable(i2c->clk); ++ if (status) { ++ dev_err(dev, "could not enable clock\n"); ++ goto err_clk; ++ } ++ ++ hi_i2c_hw_init(i2c); ++ ++ status = i2c_add_adapter(i2c->adap); ++ if (status < 0) { ++ dev_err(dev, "failed to add bus to i2c core\n"); ++ goto err_add_adapter; ++ } ++ ++ return 0; ++ ++err_add_adapter: ++ clk_disable_unprepare(i2c->clk); ++err_clk: ++err_ioremap: ++ ++ return status; ++} ++ ++/* hi_i2c_remove ++ * ++ * called when device is removed from the bus ++ */ ++static int hi_i2c_remove(struct platform_device *pdev) ++{ ++ struct hi_i2c *i2c = platform_get_drvdata(pdev); ++ ++ clk_disable_unprepare(i2c->clk); ++ i2c_del_adapter(i2c->adap); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int hi_i2c_runtime_suspend(struct device *dev) ++{ ++ return 0; ++} ++ ++static int hi_i2c_runtime_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct hi_i2c *i2c = platform_get_drvdata(pdev); ++ ++ hi_i2c_hw_init(i2c); ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops hi_i2c_dev_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(hi_i2c_runtime_suspend, ++ hi_i2c_runtime_resume) ++}; ++ ++static const struct of_device_id hi_i2c_match[] = { ++ { .compatible = "hisilicon,hisi-i2c-v110"}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, hi_i2c_match); ++ ++static struct platform_driver hi_i2c_driver = { ++ .probe = hi_i2c_probe, ++ .remove = hi_i2c_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "hisi-i2c-v110", ++ .pm = &hi_i2c_dev_pm_ops, ++ .of_match_table = of_match_ptr(hi_i2c_match), ++ }, ++}; ++ ++module_platform_driver(hi_i2c_driver); ++ ++MODULE_DESCRIPTION("HISILICON I2C V110 Bus driver"); ++MODULE_AUTHOR("BVT OSDRV"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/i2c/busses/i2c-hisilicon.c b/drivers/i2c/busses/i2c-hisilicon.c +new file mode 100644 +index 0000000..66c5a0a +--- /dev/null ++++ b/drivers/i2c/busses/i2c-hisilicon.c +@@ -0,0 +1,1169 @@ ++/* linux/drivers/i2c/busses/i2c-hisilicon.c ++ * ++ * HISILICON I2C Controller ++ * ++ * Copyright (c) 2014 Hisilicon Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/i2c.h> ++#include <linux/init.h> ++#include <linux/time.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/pm_runtime.h> ++#include <linux/slab.h> ++#include <linux/io.h> ++#include <mach/io.h> ++ ++#include "i2c-hisilicon.h" ++#include <linux/dma-mapping.h> ++ ++#ifdef CONFIG_HI_DMAC ++#include <linux/hidmac.h> ++#endif ++ ++#define HI_I2C "hisi_i2c" ++ ++#define hi_err(x...) \ ++ do { \ ++ pr_alert("%s->%d: ", __func__, __LINE__); \ ++ pr_alert(x); \ ++ pr_alert("\n"); \ ++ } while (0) ++ ++/* #define HI_I2C_DEBUG */ ++ ++#ifdef HI_I2C_DEBUG ++ ++#define hi_msg(x...) \ ++ do { \ ++ pr_alert("%s (line:%d) ", __func__, __LINE__); \ ++ pr_alert(x); \ ++ } while (0) ++#else ++#define hi_msg(args...) do { } while (0) ++#endif ++ ++#define I2C_WAIT_TIME_OUT 20000 ++ ++#define I2C_DFT_RATE (100000) ++ ++struct hi_i2c { ++ unsigned char __iomem *regbase; ++ struct device *dev; ++ struct resource *mem; ++ unsigned int irq; ++ struct i2c_adapter adap; ++ struct i2c_msg *msgs; ++ __u16 msg_num; ++ __u16 msg_addr; ++ unsigned int msg_index; ++ struct hi_platform_i2c *pdata; ++ unsigned int g_last_dev_addr; ++ unsigned int g_last_mode; ++ spinlock_t spinlock; ++}; ++ ++unsigned int get_apb_clk(void); ++ ++static int hi_i2c_abortprocess(struct hi_i2c *pinfo) ++{ ++ unsigned int auto_status; ++ unsigned int tx_src; ++ ++ tx_src = readl(pinfo->regbase + I2C_TX_ABRT_SRC); ++ hi_err("tx_abrt_src is %x.\n", tx_src); ++ ++ auto_status = readl(pinfo->regbase + I2C_AUTO_REG); ++ ++ /* clear 0xB0 err status */ ++ /* auto_mst_tx_abrt_clr ++ auto_tx_cmd_fifo_over_clr ++ auto_rx_cmd_fifo_under_clr ++ auto_rx_cmd_fifo_over_clr ++ */ ++ auto_status |= 0x0f000000; ++ writel(auto_status, pinfo->regbase + I2C_AUTO_REG); ++ writel(0x1, pinfo->regbase + I2C_CLR_INTR_REG); ++ ++ /* disable i2c */ ++ writel(0, pinfo->regbase + I2C_ENABLE_REG); ++ ++ /* enable i2c */ ++ writel(0x1, pinfo->regbase + I2C_ENABLE_REG); ++ ++ return 0; ++} ++ ++void hi_i2c_set_rate(struct hi_i2c *pinfo) ++{ ++ unsigned int apb_clk, scl_h, scl_l, hold; ++ ++ /* get apb bus clk for diff plat */ ++ apb_clk = get_apb_clk(); ++ ++ /* set SCLH and SCLL depend on apb_clk and def_rate */ ++ if (pinfo->pdata->clk_limit <= I2C_DFT_RATE) { ++ /* in normal mode F_scl: def_rate ++ i2c_scl_hcnt = (F_i2c / F_scl) * 0.5 ++ i2c_scl_hcnt = (F_i2c / F_scl) * 0.5 ++ */ ++ scl_h = (apb_clk / I2C_DFT_RATE) / 2; ++ scl_l = scl_h; ++ } else { ++ /* in fast mode F_scl: def_rate ++ i2c_scl_hcnt = (F_i2c / F_scl) * 0.36 ++ i2c_scl_hcnt = (F_i2c / F_scl) * 0.64 ++ */ ++ scl_h = ((apb_clk / 100) * 36) / pinfo->pdata->clk_limit; ++ scl_l = ((apb_clk / 100) * 64) / pinfo->pdata->clk_limit; ++ } ++ ++ writel(scl_h, pinfo->regbase + I2C_SCL_H_REG); ++ writel(scl_l, pinfo->regbase + I2C_SCL_L_REG); ++ ++ /* set hi_i2c hold time */ ++ hold = scl_h / 2; ++ writel(hold, pinfo->regbase + I2C_SDA_HOLD_REG); ++} ++ ++void hi_i2c_hw_init(struct hi_i2c *pinfo) ++{ ++ unsigned int temp, rx_fifo, tx_fifo; ++ ++ /* unlock hi_i2c controller to access */ ++ writel(HI_I2C_UNLOCK_VALUE, pinfo->regbase + I2C_LOCK_REG); ++ ++ /* disable hi_i2c controller */ ++ temp = readl(pinfo->regbase + I2C_ENABLE_REG); ++ writel((temp & ~HI_I2C_ENABLE), pinfo->regbase + I2C_ENABLE_REG); ++ ++ /* disable hi_i2c auto_mode */ ++ writel(HI_I2C_AUTO_MODE_OFF, pinfo->regbase + I2C_AUTO_REG); ++ ++ /* set hi_i2c in fast mode */ ++ writel(HI_I2C_FAST_MODE, pinfo->regbase + I2C_CON_REG); ++ ++ /* set hi_i2c rate */ ++ hi_i2c_set_rate(pinfo); ++ ++ rx_fifo = CONFIG_HI_I2C_RX_FIFO; ++ tx_fifo = CONFIG_HI_I2C_TX_FIFO; ++ ++ /* set hi_i2c fifo */ ++ writel(rx_fifo, pinfo->regbase + I2C_RX_TL_REG); ++ writel(tx_fifo, pinfo->regbase + I2C_TX_TL_REG); ++ ++ /* enable interrupt mask */ ++ writel(DISABLE_ALL_INTERRUPTS, pinfo->regbase + I2C_INTR_MASK_REG); ++ ++ /* enable hi_i2c controller */ ++ temp = readl(pinfo->regbase + I2C_ENABLE_REG); ++ writel((temp | HI_I2C_ENABLE), pinfo->regbase + I2C_ENABLE_REG); ++ ++ pinfo->g_last_dev_addr = 0; ++ pinfo->g_last_mode = I2C_MODE_NONE; ++ ++ pinfo->msgs = NULL; ++ pinfo->msg_num = 0; ++} ++ ++#define HII2C_IC 0x40 ++#define HII2C_CR1 0xB0 ++#define HII2C_EN 0x6C ++#define CR1_SINGLE_MODE_EN (0x1 << 31) ++#define CR1_STATUS_CLR (0xf << 24) ++static inline void hi_i2c_disable(struct hi_i2c *i2c) ++{ ++ writel(0x0, i2c->regbase + HII2C_EN); ++} ++ ++static inline void hi_i2c_enable(struct hi_i2c *i2c) ++{ ++ writel(0x1, i2c->regbase + HII2C_EN); ++} ++ ++static inline int hi_i2c_clr_status(struct hi_i2c *i2c) ++{ ++ unsigned int val; ++ ++ val = readl(i2c->regbase + HII2C_CR1); ++ val |= CR1_STATUS_CLR; ++ writel(val & (~CR1_SINGLE_MODE_EN), i2c->regbase + HII2C_CR1); ++ writel(val, i2c->regbase + HII2C_CR1); ++ ++ writel(0x1, i2c->regbase + HII2C_IC); ++ ++ hi_i2c_disable(i2c); ++ ++ hi_i2c_enable(i2c); ++ ++ return 0; ++} ++ ++int hi_i2c_wait_idle(struct hi_i2c *pinfo) ++{ ++ unsigned int val; ++ unsigned int time_cnt; ++ ++ time_cnt = 0; ++ do { ++ val = readl(pinfo->regbase + I2C_INTR_RAW_REG); ++ if (val & I2C_RAW_TX_ABORT) { ++ hi_err("wait last i2c fifo is empty abort! "\ ++ "int_raw_status: %#x!\n", val); ++ return hi_i2c_abortprocess(pinfo); ++ } ++ ++ val = readl(pinfo->regbase + I2C_AUTO_REG); ++ if (!IS_RX_FIFO_EMPTY(val)) ++ readl(pinfo->regbase + I2C_TX_RX_REG); ++ ++ if (IS_FIFO_EMPTY(val)) ++ break; ++ ++ if (time_cnt > I2C_WAIT_TIME_OUT) { ++ hi_err("wait last i2c fifo is empty timeout! "\ ++ "auto_status: %#x\n", val); ++ return -EBUSY; ++ } ++ time_cnt++; ++ udelay(50); ++ } while (1); ++ ++ udelay(10); ++ ++ time_cnt = 0; ++ do { ++ val = readl(pinfo->regbase + I2C_INTR_RAW_REG); ++ if (val & I2C_RAW_TX_ABORT) { ++ hi_err("wait last i2c is idle abort! "\ ++ "int_raw_status: %#x!\n", val); ++ return hi_i2c_abortprocess(pinfo); ++ } ++ ++ val = readl(pinfo->regbase + I2C_STATUS_REG); ++ if (IS_I2C_IDLE(val)) ++ break; ++ ++ if (time_cnt > I2C_WAIT_TIME_OUT) { ++ hi_err("wait last i2c is idle timeout! "\ ++ "auto_status: %#x\n", val); ++ return -EBUSY; ++ } ++ time_cnt++; ++ udelay(50); ++ } while (1); ++ ++ return 0; ++} ++ ++/* wait until tx fifo is not full */ ++int hi_i2c_wait_txfifo_notfull(struct hi_i2c *pinfo) ++{ ++ unsigned int val; ++ unsigned int time_cnt; ++ ++ time_cnt = 0; ++ do { ++ val = readl(pinfo->regbase + I2C_INTR_RAW_REG); ++ if (val & I2C_RAW_TX_ABORT) { ++ hi_err("abort! last int_raw_status: %#x!\n", val); ++ return hi_i2c_abortprocess(pinfo); ++ } ++ ++ val = readl(pinfo->regbase + I2C_AUTO_REG); ++ if (!IS_RX_FIFO_EMPTY(val)) ++ readl(pinfo->regbase + I2C_TX_RX_REG); ++ ++ if (val & I2c_AUTO_TX_FIFO_NOT_FULL) ++ break; ++ ++ if (time_cnt > I2C_WAIT_TIME_OUT) { ++ hi_err("timeout! last auto_status: %#x\n", val); ++ return -EBUSY; ++ } ++ time_cnt++; ++ udelay(50); ++ } while (1); ++ ++ return 0; ++} ++ ++/* wait until tx fifo is not empty */ ++int hi_i2c_wait_rxfifo_notempty(struct hi_i2c *pinfo) ++{ ++ unsigned int val; ++ unsigned int time_cnt; ++ ++ time_cnt = 0; ++ do { ++ val = readl(pinfo->regbase + I2C_INTR_RAW_REG); ++ if ((val & I2C_RAW_TX_ABORT) == I2C_RAW_TX_ABORT) { ++ hi_err("abort! int_raw_status: %#x!\n", val); ++ hi_i2c_abortprocess(pinfo); ++ return -EIO; ++ } ++ ++ val = readl(pinfo->regbase + I2C_AUTO_REG); ++ if (!IS_RX_FIFO_EMPTY(val)) ++ break; ++ ++ if (time_cnt > I2C_WAIT_TIME_OUT) { ++ hi_err("timeout! auto_status: %#x\n", val); ++ hi_i2c_abortprocess(pinfo); ++ return -EBUSY; ++ } ++ time_cnt++; ++ udelay(50); ++ } while (1); ++ ++ return 0; ++} ++ ++static inline int hi_i2c_set_dev_addr_and_mode(struct hi_i2c *pinfo, ++ unsigned int work_mode) ++{ ++ unsigned int dev_addr = pinfo->msgs->addr; ++ ++ if ((pinfo->g_last_dev_addr == dev_addr) ++ && (pinfo->g_last_mode == work_mode)) ++ return 0; ++ ++ /* wait until all cmd in fifo is finished and i2c is idle */ ++ if (hi_i2c_wait_idle(pinfo) < 0) ++ return -1; ++ ++ /* disable i2c */ ++ writel(0x0, pinfo->regbase + I2C_ENABLE_REG); ++ /* clear interrupt */ ++ writel(0x1, pinfo->regbase + I2C_CLR_INTR_REG); ++ /* enable interrupt mask */ ++ writel(DISABLE_ALL_INTERRUPTS, pinfo->regbase + I2C_INTR_MASK_REG); ++ /* clear err status */ ++ writel(0x0f000000, pinfo->regbase + I2C_AUTO_REG); ++ ++ /* different device, need to reinit i2c ctrl */ ++ if ((pinfo->g_last_dev_addr) != dev_addr) { ++ /* set slave dev addr */ ++ writel((dev_addr & 0xff)>>1, pinfo->regbase + I2C_TAR_REG); ++ pinfo->g_last_dev_addr = dev_addr; ++ } ++ ++ if (pinfo->g_last_mode != work_mode) { ++ ++ /* set auto mode */ ++ if (work_mode == I2C_MODE_AUTO) { ++ writel(0x0, pinfo->regbase + I2C_DMA_CMD0); ++ writel(0x80000000, pinfo->regbase + I2C_AUTO_REG); ++ pinfo->g_last_mode = work_mode; ++ } else if (work_mode == I2C_MODE_DMA) { ++ writel(0x0, pinfo->regbase + I2C_AUTO_REG); ++ pinfo->g_last_mode = work_mode; ++ } else { ++ hi_err("invalid i2c mode\n"); ++ return -1; ++ } ++ } ++ ++ /* enable i2c */ ++ writel(0x1, pinfo->regbase + I2C_ENABLE_REG); ++ ++ hi_msg("\n@@@@@@@@@@\n"); ++ ++ return 0; ++} ++ ++int hi_i2c_write(struct hi_i2c *pinfo) ++{ ++ unsigned int reg_val; ++ unsigned int temp_reg; ++ unsigned int temp_data; ++ unsigned int temp_auto_reg; ++ unsigned int min_msgs_len = 0; ++ struct i2c_msg *msgs = pinfo->msgs; ++ ++ min_msgs_len = (msgs->flags & I2C_M_16BIT_REG) ? 2 : 1; ++ min_msgs_len += (msgs->flags & I2C_M_16BIT_DATA) ? 2 : 1; ++ if (msgs->len < min_msgs_len){ ++ hi_err("Unsupported this length: %d!\n", msgs->len); ++ return -1; ++ } ++ ++ if (hi_i2c_set_dev_addr_and_mode(pinfo, I2C_MODE_AUTO) < 0) ++ return -1; ++ ++ temp_auto_reg = HI_I2C_WRITE; ++ ++ if (msgs->flags & I2C_M_16BIT_REG) { ++ /* 16bit reg addr */ ++ temp_auto_reg |= I2C_AUTO_ADDR; ++ ++ /* switch high byte and low byte */ ++ temp_reg = msgs->buf[pinfo->msg_index] << 8; ++ ++ pinfo->msg_index++; ++ ++ temp_reg |= msgs->buf[pinfo->msg_index]; ++ ++ pinfo->msg_index++; ++ } else { ++ temp_reg = msgs->buf[pinfo->msg_index]; ++ pinfo->msg_index++; ++ } ++ ++ if (msgs->flags & I2C_M_16BIT_DATA) { ++ /* 16bit data */ ++ temp_auto_reg |= I2C_AUTO_DATA; ++ ++ /* switch high byte and low byte */ ++ temp_data = msgs->buf[pinfo->msg_index] << 8; ++ ++ pinfo->msg_index++; ++ ++ temp_data |= msgs->buf[pinfo->msg_index]; ++ ++ pinfo->msg_index++; ++ } else { ++ temp_data = msgs->buf[pinfo->msg_index]; ++ pinfo->msg_index++; ++ } ++ ++ writel(temp_auto_reg, pinfo->regbase + I2C_AUTO_REG); ++ hi_msg("temp_auto_reg: 0x%x\n", temp_auto_reg); ++ ++ /* set write reg&data */ ++ reg_val = (temp_reg << REG_SHIFT) | temp_data; ++ ++ /* wait until tx fifo not full */ ++ if (hi_i2c_wait_txfifo_notfull(pinfo) < 0) ++ return -1; ++ ++ hi_msg("reg_val = %x\n", reg_val); ++ ++ writel(reg_val, pinfo->regbase + I2C_TX_RX_REG); ++ ++ hi_msg("dev_addr =%x, reg_addr = %x, Data = %x\n", ++ pinfo->msgs->addr, pinfo->msgs->buf[0], pinfo->msgs->buf[1]); ++ ++ return pinfo->msg_index; ++} ++ ++unsigned int hi_i2c_read(struct hi_i2c *pinfo) ++{ ++ unsigned int reg_val; ++ unsigned int temp_reg; ++ unsigned int ret_data = 0xffff; ++ unsigned int temp_auto_reg; ++ unsigned int data_num = 0; ++ unsigned int min_msgs_len = 0; ++ struct i2c_msg *msgs = pinfo->msgs; ++ ++ min_msgs_len = (msgs->flags & I2C_M_16BIT_REG) ? 2 : 1; ++ if (msgs->len < min_msgs_len){ ++ hi_err("Unsupported this length: %d!\n", msgs->len); ++ return -1; ++ } ++ ++ if (hi_i2c_set_dev_addr_and_mode(pinfo, I2C_MODE_AUTO) < 0) ++ return -1; ++ ++ temp_auto_reg = HI_I2C_READ; ++ ++ if (msgs->flags & I2C_M_16BIT_REG) { ++ /* 16bit reg addr */ ++ temp_auto_reg |= I2C_AUTO_ADDR; ++ ++ /* switch high byte and low byte */ ++ temp_reg = msgs->buf[pinfo->msg_index] << 8; ++ pinfo->msg_index++; ++ temp_reg |= msgs->buf[pinfo->msg_index]; ++ } else { ++ temp_reg = msgs->buf[pinfo->msg_index]; ++ pinfo->msg_index++; ++ } ++ ++ if (msgs->flags & I2C_M_16BIT_DATA) ++ /* 16bit data */ ++ temp_auto_reg |= I2C_AUTO_DATA; ++ ++ writel(temp_auto_reg, pinfo->regbase + I2C_AUTO_REG); ++ hi_msg("temp_auto_reg: 0x%x\n", temp_auto_reg); ++ ++ /* 1. write addr */ ++ reg_val = temp_reg << REG_SHIFT; ++ hi_msg("reg_val %x\n", reg_val); ++ ++ /* wait until tx fifo not full */ ++ if (hi_i2c_wait_txfifo_notfull(pinfo) < 0) ++ return -1; ++ ++ /* regaddr */ ++ writel(reg_val, pinfo->regbase + I2C_TX_RX_REG); ++ ++ /* 2. read return data */ ++ /* wait until rx fifo not empty */ ++ if (hi_i2c_wait_rxfifo_notempty(pinfo) < 0) ++ return -1; ++ ++ ret_data = readl(pinfo->regbase + I2C_TX_RX_REG) & DATA_16BIT_MASK; ++ hi_msg("ret_data = %x\n", ret_data); ++ ++ if (msgs->flags & I2C_M_16BIT_DATA) { ++ pinfo->msgs->buf[0] = ret_data & DATA_8BIT_MASK; ++ pinfo->msgs->buf[1] = (ret_data >> 8) & DATA_8BIT_MASK; ++ data_num = 2; ++ } else { ++ pinfo->msgs->buf[0] = ret_data & DATA_8BIT_MASK; ++ data_num = 1; ++ } ++ ++ writel(0x1, pinfo->regbase + I2C_CLR_INTR_REG); ++ ++ return data_num; ++} ++ ++/************************************ ++ * dma functions * ++************************************/ ++#ifdef CONFIG_HI_DMAC ++void hi_i2c_dma_start(struct hi_i2c *pinfo, unsigned int dir) ++{ ++ writel((1 << dir), pinfo->regbase + I2C_DMA_CTRL_REG); ++} ++ ++void hi_i2c_dmac_config(struct hi_i2c *pinfo, unsigned int dir) ++{ ++ /* 1. enable RX(0) or TX(1) in DMA mode */ ++ hi_i2c_dma_start(pinfo, dir); ++ ++ /* 2. set dma fifo */ ++ writel(4, pinfo->regbase + I2C_DMA_TDLR); ++ writel(4, pinfo->regbase + I2C_DMA_RDLR); ++} ++ ++void hi_i2c_start_rx(struct hi_i2c *pinfo, unsigned int reg_addr, ++ unsigned int length) ++{ ++ unsigned int reg; ++ ++ writel(reg_addr, pinfo->regbase + I2C_DMA_CMD1); ++ writel(length, pinfo->regbase + I2C_DMA_CMD2); ++ ++ reg = readl(pinfo->regbase + I2C_DMA_CMD0); ++ ++ /*start tx*/ ++ reg &= ~0x40000000; ++ writel((0x80000000 | reg), pinfo->regbase + I2C_DMA_CMD0); ++} ++ ++void hi_i2c_start_tx(struct hi_i2c *pinfo, unsigned int reg_addr, ++ unsigned int length) ++{ ++ unsigned int reg; ++ ++ writel(reg_addr, pinfo->regbase + I2C_DMA_CMD1); ++ writel(length, pinfo->regbase + I2C_DMA_CMD2); ++ ++ reg = readl(pinfo->regbase + I2C_DMA_CMD0); ++ ++ /*start rx*/ ++ writel((0xc0000000 | reg), pinfo->regbase + I2C_DMA_CMD0); ++} ++ ++int dma_to_i2c(unsigned int src, unsigned int dst, unsigned int length) ++{ ++ int chan; ++ ++ chan = do_dma_m2p(src, dst, length); ++ if (chan == -1) ++ hi_err("dma_to_i2c error\n"); ++ ++ return chan; ++} ++ ++ ++int i2c_to_dma(unsigned int src, unsigned int dst, unsigned int length) ++{ ++ int chan; ++ ++ chan = do_dma_p2m(dst, src, length); ++ if (chan == -1) ++ hi_err("dma_p2m error...\n"); ++ ++ return chan; ++} ++ ++static int hi_i2c_do_dma_write(struct hi_i2c *pinfo, unsigned int reg_addr, ++ unsigned int reg_addr_num, unsigned int dma_buf, ++ unsigned int length) ++{ ++ unsigned int temp_reg = reg_addr; ++ int chan, ret = 0; ++ ++ /* 1. switch i2c devaddr and dma mode*/ ++ if (hi_i2c_set_dev_addr_and_mode(pinfo, I2C_MODE_DMA) < 0) { ++ hi_err("HI_I2C_Dma_Write error...\n"); ++ ret = -1; ++ } ++ ++ if (2 == reg_addr_num) { ++ /* switch high byte and low byte */ ++ temp_reg = REVERT_HL_BYTE(reg_addr); ++ writel(0x10000000, pinfo->regbase + I2C_DMA_CMD0); ++ } else { ++ writel(0x0, pinfo->regbase + I2C_DMA_CMD0); ++ } ++ ++ /* 2. config i2c into DMA mode */ ++ hi_i2c_dmac_config(pinfo, 0x1); ++ ++ /* 3. start i2c logic to write */ ++ hi_i2c_start_tx(pinfo, temp_reg, length - 1); ++ ++ /* 4. transmit DATA from DMAC to I2C in DMA mode */ ++ chan = dma_to_i2c(dma_buf, (pinfo->mem->start + I2C_DATA_CMD_REG), ++ length); ++ if (chan == -1) { ++ ret = -1; ++ goto fail_0; ++ } ++ ++ if (dmac_wait(chan) != DMAC_CHN_SUCCESS) { ++ hi_err("dma wait failed\n"); ++ ret = -1; ++ goto fail_1; ++ } ++ ++ ret = hi_i2c_wait_idle(pinfo); ++fail_1: ++ dmac_channel_free(chan); ++fail_0: ++ hi_i2c_clr_status(pinfo); ++ return ret; ++} ++ ++static int hi_i2c_do_dma_read(struct hi_i2c *pinfo, unsigned int reg_addr, ++ unsigned int reg_addr_num, unsigned int dma_buf, ++ unsigned int length) ++{ ++ unsigned int temp_reg = reg_addr; ++ int chan, ret = 0; ++ ++ /* 1. switch i2c devaddr and dma mode*/ ++ if (hi_i2c_set_dev_addr_and_mode(pinfo, I2C_MODE_DMA) < 0) { ++ ret = -1; ++ } ++ ++ if (2 == reg_addr_num) { ++ /* switch high byte and low byte */ ++ temp_reg = REVERT_HL_BYTE(reg_addr); ++ writel(0x10000000, pinfo->regbase + I2C_DMA_CMD0); ++ } else { ++ writel(0x0, pinfo->regbase + I2C_DMA_CMD0); ++ } ++ ++ /* 2. config i2c into DMA mode */ ++ hi_i2c_dmac_config(pinfo, 0x0); ++ ++ /* 3. transmit DATA from I2C to DMAC in DMA mode */ ++ chan = i2c_to_dma((pinfo->mem->start + I2C_DATA_CMD_REG), ++ dma_buf, length); ++ if (chan == -1) { ++ ret = -1; ++ goto fail_0; ++ } ++ ++ /* 4. start i2c logic to read */ ++ hi_i2c_start_rx(pinfo, temp_reg, length - 1); ++ ++ if (dmac_wait(chan) != DMAC_CHN_SUCCESS) { ++ hi_err("dma wait failed\n"); ++ ret = -1; ++ goto fail_1; ++ } ++ ++ ret = hi_i2c_wait_idle(pinfo); ++fail_1: ++ dmac_channel_free(chan); ++fail_0: ++ hi_i2c_clr_status(pinfo); ++ return ret; ++} ++ ++#else ++static int hi_i2c_do_dma_write(struct hi_i2c *pinfo, ++ unsigned int reg_addr, unsigned int reg_addr_num, ++ unsigned int dma_buf, unsigned int length) ++{ ++ hi_err("DMA is not enabled!"); ++ return -1; ++} ++ ++static int hi_i2c_do_dma_read(struct hi_i2c *pinfo, ++ unsigned int reg_addr, unsigned int reg_addr_num, ++ unsigned int dma_buf, unsigned int length) ++{ ++ hi_err("DMA is not enabled!"); ++ return -1; ++} ++#endif ++ ++int hi_i2c_dma_write(const struct i2c_client *client, unsigned int dma_buf, ++ unsigned int reg_addr, unsigned int reg_addr_num, ++ unsigned int length) ++{ ++ struct i2c_adapter *adap = client->adapter; ++ struct hi_i2c *pinfo = (struct hi_i2c *)i2c_get_adapdata(adap); ++ struct i2c_msg msgs; ++ int ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&pinfo->spinlock, flags); ++ ++ memset(&msgs, 0x0, sizeof(struct i2c_msg)); ++ msgs.addr = client->addr; ++ msgs.flags = client->flags; ++ msgs.len = length; ++ ++ pinfo->msgs = &msgs; ++ pinfo->msg_num = length; ++ pinfo->msg_index = 0; ++ ++ ret = hi_i2c_do_dma_write(pinfo, reg_addr, reg_addr_num, dma_buf, ++ length); ++ ++ spin_unlock_irqrestore(&pinfo->spinlock, flags); ++ ++ return ret; ++} ++EXPORT_SYMBOL(hi_i2c_dma_write); ++ ++int hi_i2c_dma_read(const struct i2c_client *client, unsigned int dma_buf, ++ unsigned int reg_addr, unsigned int reg_addr_num, ++ unsigned int length) ++{ ++ struct i2c_adapter *adap = client->adapter; ++ struct hi_i2c *pinfo = (struct hi_i2c *)i2c_get_adapdata(adap); ++ struct i2c_msg msgs; ++ int ret; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&pinfo->spinlock, flags); ++ ++ memset(&msgs, 0x0, sizeof(struct i2c_msg)); ++ msgs.addr = client->addr; ++ msgs.flags = client->flags; ++ msgs.flags |= I2C_M_RD; ++ msgs.len = length; ++ ++ pinfo->msgs = &msgs; ++ pinfo->msg_num = length; ++ pinfo->msg_index = 0; ++ ++ ret = hi_i2c_do_dma_read(pinfo, reg_addr, reg_addr_num, dma_buf, ++ length); ++ ++ spin_unlock_irqrestore(&pinfo->spinlock, flags); ++ ++ return ret; ++} ++EXPORT_SYMBOL(hi_i2c_dma_read); ++ ++static int hi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, ++ int num) ++{ ++ struct hi_i2c *pinfo = (struct hi_i2c *)i2c_get_adapdata(adap); ++ dma_addr_t dma_buf; ++ __u16 len; ++ unsigned int reg_addr; ++ unsigned int reg_width; ++ int ret; ++ unsigned long flags; ++ ++ if (!msgs || (num <= 0)) { ++ hi_err("msgs == NULL || num <= 0, Invalid argument!\n"); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&pinfo->spinlock, flags); ++ ++ pinfo->msgs = msgs; ++ pinfo->msg_num = num; ++ pinfo->msg_index = 0; ++ ++ len = pinfo->msgs->len; ++ if (pinfo->msgs->flags & I2C_M_16BIT_REG) { ++ reg_addr = pinfo->msgs->buf[0]; ++ reg_addr |= pinfo->msgs->buf[1] << 8; ++ reg_width = 2; ++ } else { ++ reg_addr = pinfo->msgs->buf[0]; ++ reg_width = 1; ++ } ++ ++ if (pinfo->msgs->flags & I2C_M_DMA) { ++ if (pinfo->msgs->flags & I2C_M_16BIT_DATA) { ++ hi_err("I2C DMA no support I2C_M_16BIT_DATA\n"); ++ ret = -EINVAL; ++ goto end; ++ } ++ ++ if (((pinfo->msgs->flags & I2C_M_RD) && (len <= 0)) || ++ (!(pinfo->msgs->flags & I2C_M_RD) && ++ (len <= reg_width))) { ++ hi_err("msgs->len == %d, Invalid argument!\n", ++ len); ++ ret = -EINVAL; ++ goto end; ++ } ++ ++ dma_buf = dma_map_single(pinfo->dev, ++ pinfo->msgs->buf, len, ++ DMA_BIDIRECTIONAL); ++ if (dma_mapping_error(pinfo->dev, dma_buf)) { ++ hi_err("DMA mapping failed\n"); ++ ret = -EINVAL; ++ goto end; ++ } ++ if (pinfo->msgs->flags & I2C_M_RD) ++ ret = hi_i2c_do_dma_read(pinfo, reg_addr, ++ reg_width, dma_buf, len); ++ else ++ ret = hi_i2c_do_dma_write(pinfo, reg_addr, ++ reg_width, dma_buf + reg_width, ++ len - reg_width); ++ ++ dma_unmap_single(pinfo->dev, dma_buf, len, ++ DMA_BIDIRECTIONAL); ++ ++ /*normally the ret = 0, so forced the return value to 1.*/ ++ if (ret) ++ ret = -EIO; ++ else ++ ret = 1; ++ } else { ++ if (pinfo->msgs->flags & I2C_M_RD){ ++ ret = hi_i2c_read(pinfo); ++ } else{ ++ ret = hi_i2c_write(pinfo); ++ } ++ /*(ret = data_num) or (ret = msg_index),so force the return value to 1, ++ * docking the upper interface. ++ */ ++ if (ret < 0) ++ ret = -EIO; ++ else ++ ret = 1; ++ } ++ ++end: ++ spin_unlock_irqrestore(&pinfo->spinlock, flags); ++ ++ /* ++ * If everything went ok (i.e. 1 msg transmitted), (ret = 1) means return #bytes ++ * transmitted, else return error code. see i2c-core.c ++ */ ++ return ret; ++} ++ ++/* HI I2C READ * ++ * hi_i2c_master_recv - issue a single I2C message in master receive mode ++ * @client: Handle to slave device ++ * @buf: Where to store data read from slave ++ * @count: How many bytes to read, must be less than 64k since msg.len is u16 ++ * ++ * Returns negative errno, or else the number of bytes read. ++ */ ++int hi_i2c_master_recv(const struct i2c_client *client, char *buf, ++ int count) ++{ ++ struct i2c_adapter *adap = client->adapter; ++ struct i2c_msg msgs; ++ unsigned int reg_width, data_width, max_width; ++ int msgs_count; ++ ++ memset(&msgs, 0x0, sizeof(struct i2c_msg)); ++ msgs.addr = client->addr; ++ msgs.flags = client->flags; ++ msgs.flags |= I2C_M_RD; ++ ++ if (client->flags & I2C_M_16BIT_REG) ++ reg_width = 2; ++ else ++ reg_width = 1; ++ ++ if (client->flags & I2C_M_16BIT_DATA) ++ data_width = 2; ++ else ++ data_width = 1; ++ ++ max_width = max_t(size_t, reg_width, data_width); ++ ++ if (count > max_width) { ++ msgs.flags |= I2C_M_DMA; ++ msgs.len = count; ++ } else if (count <= 0 ) { ++ hi_err("ERR. Invalid count: 0x%d!!!\n", count); ++ return -EINVAL; ++ } else ++ msgs.len = max_width; ++ ++ if (!buf) { ++ hi_err("ERR. Invalid buf == NULL!!!\n"); ++ return -EINVAL; ++ } ++ msgs.buf = buf; ++ ++ msgs_count = hi_i2c_xfer(adap, &msgs, 1); ++ ++ return (msgs_count == 1) ? count : -EIO; ++} ++EXPORT_SYMBOL(hi_i2c_master_recv); ++ ++/*HI I2C WRITE* ++ * hi_i2c_master_send - issue a single I2C message in master transmit mode ++ * @client: Handle to slave device ++ * @buf: Data that will be written to the slave ++ * @count: How many bytes to write, must be less than 64k since msg.len is u16 ++ * ++ * Returns negative errno, or else the number of bytes written. ++ */ ++int hi_i2c_master_send(const struct i2c_client *client, ++ const char *buf, int count) ++{ ++ struct i2c_adapter *adap = client->adapter; ++ struct i2c_msg msgs; ++ unsigned int reg_width, data_width; ++ int msgs_count; ++ ++ memset(&msgs, 0x0, sizeof(struct i2c_msg)); ++ msgs.addr = client->addr; ++ msgs.flags = client->flags; ++ ++ if (client->flags & I2C_M_16BIT_REG) ++ reg_width = 2; ++ else ++ reg_width = 1; ++ ++ if (client->flags & I2C_M_16BIT_DATA) ++ data_width = 2; ++ else ++ data_width = 1; ++ ++ if (count - reg_width > data_width) ++ msgs.flags |= I2C_M_DMA; ++ else if (count - reg_width < data_width) { ++ hi_err("ERR. Invalid count!!!\n"); ++ return -EINVAL; ++ } ++ ++ msgs.len = count; ++ ++ if (!buf) { ++ hi_err("ERR. Invalid buf! == NULL!!\n"); ++ return -EINVAL; ++ } ++ msgs.buf = (__u8 *)buf; ++ ++ msgs_count = hi_i2c_xfer(adap, &msgs, 1); ++ ++ return (msgs_count == 1) ? count : -EIO; ++} ++EXPORT_SYMBOL(hi_i2c_master_send); ++ ++/** ++ * hi_i2c_transfer - execute a single or combined I2C message ++ * @adap: Handle to I2C bus ++ * @msgs: One or more messages to execute before STOP is issued to ++ * terminate the operation; each message begins with a START. ++ * @num: Number of messages to be executed. ++ * ++ * Returns negative errno, else the number of messages executed. ++ * ++ * Note that there is no requirement that each message be sent to ++ * the same slave address, although that is the most common model. ++ */ ++int hi_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, ++ int num) ++{ ++ printk("Wrong interface call." ++ "hi_i2c_master_recv is the only interface to i2c read!!!\n"); ++ ++ return -EIO; ++} ++EXPORT_SYMBOL(hi_i2c_transfer); ++/**************************************************************/ ++ ++static u32 hi_i2c_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static const struct i2c_algorithm hi_i2c_algo = { ++ .master_xfer = hi_i2c_xfer, ++ .functionality = hi_i2c_func, ++}; ++ ++static int hi_i2c_probe(struct platform_device *pdev) ++{ ++ int errorcode; ++ struct hi_i2c *pinfo; ++ struct i2c_adapter *adap; ++ struct resource *mem; ++ struct hi_platform_i2c *platform_info; ++ ++ platform_info = ++ (struct hi_platform_i2c *)pdev->dev.platform_data; ++ if (platform_info == NULL) { ++ dev_err(&pdev->dev, "%s: Can't get platform_data!\n", ++ __func__); ++ errorcode = -EPERM; ++ goto i2c_errorcode_na; ++ } ++ ++ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (mem == NULL) { ++ dev_err(&pdev->dev, "Get I2C mem resource failed!\n"); ++ errorcode = -ENXIO; ++ goto i2c_errorcode_na; ++ } ++ ++ pinfo = kzalloc(sizeof(struct hi_i2c), GFP_KERNEL); ++ if (pinfo == NULL) { ++ dev_err(&pdev->dev, "Out of memory!\n"); ++ errorcode = -ENOMEM; ++ goto i2c_errorcode_na; ++ } ++ ++ pinfo->regbase = (unsigned char __iomem *)IO_ADDRESS(mem->start); ++ pinfo->mem = mem; ++ pinfo->dev = &pdev->dev; ++ pinfo->pdata = platform_info; ++ pinfo->g_last_dev_addr = 0; ++ ++ spin_lock_init(&pinfo->spinlock); ++ ++ hi_i2c_hw_init(pinfo); ++ ++ platform_set_drvdata(pdev, pinfo); ++ ++ adap = &pinfo->adap; ++ i2c_set_adapdata(adap, pinfo); ++ adap->owner = THIS_MODULE; ++ adap->class = platform_info->i2c_class; ++ strlcpy(adap->name, pdev->name, sizeof(adap->name)); ++ adap->algo = &hi_i2c_algo; ++ adap->dev.parent = &pdev->dev; ++ adap->nr = pdev->id; ++ adap->retries = CONFIG_HI_I2C_RETRIES; ++ errorcode = i2c_add_numbered_adapter(adap); ++ if (errorcode) { ++ dev_err(&pdev->dev, ++ "%s: Adding I2C adapter failed!\n", __func__); ++ goto i2c_errorcode_free_irq; ++ } ++ ++ dev_notice(&pdev->dev, ++ "Hisilicon [%s] probed!\n", ++ dev_name(&pinfo->adap.dev)); ++ ++ goto i2c_errorcode_na; ++ ++i2c_errorcode_free_irq: ++ free_irq(pinfo->irq, pinfo); ++ kfree(pinfo); ++ ++i2c_errorcode_na: ++ return errorcode; ++} ++ ++static int hi_i2c_remove(struct platform_device *pdev) ++{ ++ struct hi_i2c *pinfo = NULL; ++ ++ pinfo = platform_get_drvdata(pdev); ++ ++ if (pinfo) { ++ i2c_del_adapter(&pinfo->adap); ++ ++ free_irq(pinfo->irq, pinfo); ++ ++ kfree(pinfo); ++ } ++ ++ dev_notice(&pdev->dev, ++ "Remove Hisilicon Media Processor" ++ "I2C adapter.\n"); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int hi_i2c_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct hi_i2c *pinfo; ++ ++ pinfo = platform_get_drvdata(pdev); ++ ++ hi_i2c_abortprocess(pinfo); ++ ++ return 0; ++} ++ ++static int hi_i2c_resume(struct platform_device *pdev) ++{ ++ struct hi_i2c *pinfo; ++ ++ pinfo = platform_get_drvdata(pdev); ++ ++ hi_i2c_hw_init(pinfo); ++ ++ return 0; ++} ++#else ++#define hi_i2c_suspend NULL ++#define hi_i2c_resume NULL ++#endif ++ ++static struct platform_driver hi_i2c_driver = { ++ .probe = hi_i2c_probe, ++ .remove = hi_i2c_remove, ++ .suspend = hi_i2c_suspend, ++ .resume = hi_i2c_resume, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = HI_I2C, ++ }, ++}; ++ ++#ifdef CONFIG_ARCH_HI3519 ++#include "i2c_hi3519.c" ++#endif ++#ifdef CONFIG_ARCH_HI3536C ++#include "i2c_hi3536c.c" ++#endif ++#ifdef CONFIG_ARCH_HI3531D ++#include "i2c_hi3531d.c" ++#endif ++#ifdef CONFIG_ARCH_HI3521D ++#include "i2c_hi3521d.c" ++#endif ++ ++module_init(hi_i2c_module_init); ++module_exit(hi_i2c_module_exit); ++ ++MODULE_DESCRIPTION("HISILICON I2C Bus driver"); ++MODULE_AUTHOR("BVT OSDRV"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/i2c/busses/i2c-hisilicon.h b/drivers/i2c/busses/i2c-hisilicon.h +new file mode 100644 +index 0000000..778fa1f +--- /dev/null ++++ b/drivers/i2c/busses/i2c-hisilicon.h +@@ -0,0 +1,108 @@ ++#ifndef __HI_I2C_H__ ++#define __HI_I2C_H__ ++ ++#define I2C_CON_REG 0x000 ++#define I2C_TAR_REG 0x004 ++#define I2C_DATA_CMD_REG 0x010 ++#define I2C_SCL_H_REG 0x01C ++#define I2C_SCL_L_REG 0x020 ++#define I2C_INTR_STAT_REG 0x02C ++#define I2C_INTR_MASK_REG 0x030 ++#define I2C_INTR_RAW_REG 0x034 ++#define I2C_RX_TL_REG 0x038 ++#define I2C_TX_TL_REG 0x03C ++#define I2C_CLR_INTR_REG 0x040 ++#define I2C_CLR_RX_OVER_REG 0x048 ++#define I2C_CLR_TX_OVER_REG 0x04C ++#define I2C_ENABLE_REG 0x06C ++#define I2C_STATUS_REG 0x070 ++#define I2C_TXFLR_REG 0x074 ++#define I2C_RXFLR_REG 0x078 ++#define I2C_SDA_HOLD_REG 0x07C ++#define I2C_TX_ABRT_SRC 0x080 ++#define I2C_DMA_CTRL_REG 0x088 ++#define I2C_DMA_TDLR 0x08C ++#define I2C_DMA_RDLR 0x090 ++#define I2C_LPIF_STATE 0x0A8 ++#define I2C_LOCK_REG 0x0AC ++#define I2C_AUTO_REG 0x0B0 ++#define I2C_TX_RX_REG 0x0B4 ++#define I2C_DMA_CMD0 0x0B8 ++#define I2C_DMA_CMD1 0x0BC ++#define I2C_DMA_CMD2 0x0C0 ++#define I2C_ENABLE_STATUS_REG 0x09C ++ ++#define HI_I2C_FAST_MODE 0x65 ++ ++#define HI_I2C_UNLOCK_VALUE 0x1ACCE551 ++ ++#define HI_I2C_ENABLE (1 << 0) ++ ++#define HI_I2C_AUTO_MODE_OFF 0x0f000000 ++ ++#define HI_I2C_WRITE 0x80000000 ++#define HI_I2C_READ 0xc0000000 ++ ++#define READ_OPERATION (1) ++#define WRITE_OPERATION 0xfe ++ ++#define CMD_I2C_WRITE 0x01 ++#define CMD_I2C_READ 0x03 ++ ++/* I2C_COM_REG */ ++#define I2C_SEND_ACK (~(1 << 4)) ++#define I2C_START (1 << 3) ++#define I2C_READ (1 << 2) ++#define I2C_WRITE (1 << 1) ++#define I2C_STOP (1 << 0) ++ ++/* I2C_ENABLE_REG */ ++#define I2C_ENABLE (1 << 0) ++ ++#define I2C_RAW_TX_ABORT (1 << 6) ++ ++/*I2C_INTR_STAT_REG */ ++#define I2C_AUTO_RX_FIFO_NOT_EMPTY (1 << 8) ++#define I2C_AUTO_TX_FIFO_EMPTRY (1 << 20) ++#define I2c_AUTO_TX_FIFO_NOT_FULL (1 << 21) ++#define I2C_TX_ABRT (1 << 23) ++#define I2C_AUTO_DATA (1 << 28) ++#define I2C_AUTO_ADDR (1 << 29) ++ ++/* I2C_STATUS */ ++#define I2C_STATUS_WORKING (1 << 0) ++ ++#define IS_TX_FIFO_EMPTY(status) (((status) &\ ++ I2C_AUTO_TX_FIFO_EMPTRY) == I2C_AUTO_TX_FIFO_EMPTRY) ++#define IS_RX_FIFO_EMPTY(status) (((status) &\ ++ I2C_AUTO_RX_FIFO_NOT_EMPTY) == 0) ++#define IS_FIFO_EMPTY(status) (IS_RX_FIFO_EMPTY(status) &&\ ++ IS_TX_FIFO_EMPTY(status)) ++#define IS_I2C_IDLE(status) (((status) & I2C_STATUS_WORKING) == 0) ++ ++#define REG_SHIFT 16 ++#define DATA_16BIT_MASK 0xFFFF ++#define DATA_8BIT_MASK 0xFFFF ++ ++#define REVERT_HL_BYTE(value) ((value >> 8) | ((value & 0xFF) << 8)) ++ ++/* ++ * I2C Interrupt related Macros ++ */ ++#define DEFAULT_I2C_REG_IMSC 0x0UL ++#define DISABLE_ALL_INTERRUPTS ((~DEFAULT_I2C_REG_IMSC) & 0xfff) ++#define ENABLE_ALL_INTERRUPTS DEFAULT_I2C_REG_IMSC ++ ++typedef enum i2c_mode_e { ++ I2C_MODE_AUTO, ++ I2C_MODE_DMA, ++ I2C_MODE_NONE, ++} i2c_mode_e; ++ ++struct hi_platform_i2c { ++ int clk_limit; ++ unsigned int i2c_class; ++ unsigned int clk_rate; ++}; ++ ++#endif +diff --git a/drivers/i2c/busses/i2c_hi3519.c b/drivers/i2c/busses/i2c_hi3519.c +new file mode 100644 +index 0000000..b15ae88 +--- /dev/null ++++ b/drivers/i2c/busses/i2c_hi3519.c +@@ -0,0 +1,147 @@ ++#include "i2c_hi3519.h" ++ ++unsigned int get_apb_clk(void) ++{ ++ unsigned int apb_clk; ++ ++ apb_clk = get_bus_clk() / 4; ++ ++ return apb_clk; ++} ++ ++static struct resource hi_i2c0_resources[] = { ++ [0] = { ++ .start = CONFIG_HI_I2C0_IO_BASE, ++ .end = CONFIG_HI_I2C0_IO_BASE ++ + CONFIG_HI_I2C0_IO_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++struct hi_platform_i2c hi_i2c0_platform_data = { ++ .clk_limit = CONFIG_HI_I2C0_CLK_LIMIT, ++ .i2c_class = I2C_CLASS_DDC, ++}; ++ ++struct platform_device hi_i2c0_device = { ++ .name = HI_I2C, ++ .id = 0, ++ .resource = hi_i2c0_resources, ++ .num_resources = ARRAY_SIZE(hi_i2c0_resources), ++ .dev = { ++ .platform_data = &hi_i2c0_platform_data, ++ } ++}; ++ ++static struct resource hi_i2c1_resources[] = { ++ [0] = { ++ .start = CONFIG_HI_I2C1_IO_BASE, ++ .end = CONFIG_HI_I2C1_IO_BASE ++ + CONFIG_HI_I2C1_IO_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++struct hi_platform_i2c hi_i2c1_platform_data = { ++ .clk_limit = CONFIG_HI_I2C1_CLK_LIMIT, ++ .i2c_class = I2C_CLASS_DDC, ++}; ++ ++struct platform_device hi_i2c1_device = { ++ .name = HI_I2C, ++ .id = 1, ++ .resource = hi_i2c1_resources, ++ .num_resources = ARRAY_SIZE(hi_i2c1_resources), ++ .dev = { ++ .platform_data = &hi_i2c1_platform_data, ++ } ++}; ++ ++static struct resource hi_i2c2_resources[] = { ++ [0] = { ++ .start = CONFIG_HI_I2C2_IO_BASE, ++ .end = CONFIG_HI_I2C2_IO_BASE ++ + CONFIG_HI_I2C2_IO_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++struct hi_platform_i2c hi_i2c2_platform_data = { ++ .clk_limit = CONFIG_HI_I2C2_CLK_LIMIT, ++ .i2c_class = I2C_CLASS_DDC, ++}; ++ ++struct platform_device hi_i2c2_device = { ++ .name = HI_I2C, ++ .id = 2, ++ .resource = hi_i2c2_resources, ++ .num_resources = ARRAY_SIZE(hi_i2c2_resources), ++ .dev = { ++ .platform_data = &hi_i2c2_platform_data, ++ } ++}; ++ ++static struct resource hi_i2c3_resources[] = { ++ [0] = { ++ .start = CONFIG_HI_I2C3_IO_BASE, ++ .end = CONFIG_HI_I2C3_IO_BASE ++ + CONFIG_HI_I2C3_IO_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++struct hi_platform_i2c hi_i2c3_platform_data = { ++ .clk_limit = CONFIG_HI_I2C3_CLK_LIMIT, ++ .i2c_class = I2C_CLASS_DDC, ++}; ++ ++struct platform_device hi_i2c3_device = { ++ .name = HI_I2C, ++ .id = 3, ++ .resource = hi_i2c3_resources, ++ .num_resources = ARRAY_SIZE(hi_i2c3_resources), ++ .dev = { ++ .platform_data = &hi_i2c3_platform_data, ++ } ++}; ++ ++static struct platform_device *hi_i2c_devices[] __initdata = { ++ &hi_i2c0_device, ++ &hi_i2c1_device, ++ &hi_i2c2_device, ++ &hi_i2c3_device, ++}; ++ ++static int __init hi_i2c_module_init(void) ++{ ++ int ret; ++ ++ ++ ret = platform_add_devices(hi_i2c_devices, ARRAY_SIZE(hi_i2c_devices)); ++ if (ret) { ++ hi_err("i2c device register failed!\n"); ++ return ret; ++ } ++ ++ ret = platform_driver_register(&hi_i2c_driver); ++ if (ret) { ++ platform_device_unregister(&hi_i2c3_device); ++ platform_device_unregister(&hi_i2c2_device); ++ platform_device_unregister(&hi_i2c1_device); ++ platform_device_unregister(&hi_i2c0_device); ++ hi_err("i2c driver register failed!\n"); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static void __exit hi_i2c_module_exit(void) ++{ ++ platform_driver_unregister(&hi_i2c_driver); ++ ++ platform_device_unregister(&hi_i2c3_device); ++ platform_device_unregister(&hi_i2c2_device); ++ platform_device_unregister(&hi_i2c1_device); ++ platform_device_unregister(&hi_i2c0_device); ++} +diff --git a/drivers/i2c/busses/i2c_hi3519.h b/drivers/i2c/busses/i2c_hi3519.h +new file mode 100644 +index 0000000..898668e +--- /dev/null ++++ b/drivers/i2c/busses/i2c_hi3519.h +@@ -0,0 +1,12 @@ ++#include <linux/platform_device.h> ++ ++#define CRG_REG_BASE 0x12010000 ++#define A7_AXI_SCALE_REG IO_ADDRESS(CRG_REG_BASE + 0x34) ++ ++#define get_bus_clk() ({\ ++ unsigned long tmp_reg, busclk = 0;\ ++ tmp_reg = readl((void *)A7_AXI_SCALE_REG);\ ++ if (0x1 == ((tmp_reg >> 12) & 0x3))\ ++ busclk = 200000000;\ ++ busclk;\ ++ }) +diff --git a/drivers/i2c/busses/i2c_hi3521d.c b/drivers/i2c/busses/i2c_hi3521d.c +new file mode 100644 +index 0000000..6223a41 +--- /dev/null ++++ b/drivers/i2c/busses/i2c_hi3521d.c +@@ -0,0 +1,66 @@ ++#include <mach/platform.h> ++ ++unsigned int get_apb_clk(void) ++{ ++ unsigned int apb_clk; ++ ++ apb_clk = get_bus_clk() / CFG_TIMER_PER ; ++ ++ return apb_clk; ++} ++ ++static struct resource hi_i2c0_resources[] = { ++ [0] = { ++ .start = CONFIG_HI_I2C0_IO_BASE, ++ .end = CONFIG_HI_I2C0_IO_BASE ++ + CONFIG_HI_I2C0_IO_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++struct hi_platform_i2c hi_i2c0_platform_data = { ++ .clk_limit = CONFIG_HI_I2C0_CLK_LIMIT, ++ .i2c_class = I2C_CLASS_DDC, ++}; ++ ++struct platform_device hi_i2c0_device = { ++ .name = HI_I2C, ++ .id = 0, ++ .resource = hi_i2c0_resources, ++ .num_resources = ARRAY_SIZE(hi_i2c0_resources), ++ .dev = { ++ .platform_data = &hi_i2c0_platform_data, ++ } ++}; ++ ++static struct platform_device *hi_i2c_devices[] __initdata = { ++ &hi_i2c0_device, ++}; ++ ++static int __init hi_i2c_module_init(void) ++{ ++ int ret; ++ ++ ++ ret = platform_add_devices(hi_i2c_devices, ARRAY_SIZE(hi_i2c_devices)); ++ if (ret) { ++ hi_err("i2c device register failed!\n"); ++ return ret; ++ } ++ ++ ret = platform_driver_register(&hi_i2c_driver); ++ if (ret) { ++ platform_device_unregister(&hi_i2c0_device); ++ hi_err("i2c driver register failed!\n"); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static void __exit hi_i2c_module_exit(void) ++{ ++ platform_driver_unregister(&hi_i2c_driver); ++ ++ platform_device_unregister(&hi_i2c0_device); ++} +diff --git a/drivers/i2c/busses/i2c_hi3531d.c b/drivers/i2c/busses/i2c_hi3531d.c +new file mode 100644 +index 0000000..fdabe84 +--- /dev/null ++++ b/drivers/i2c/busses/i2c_hi3531d.c +@@ -0,0 +1,93 @@ ++#include <mach/platform.h> ++ ++unsigned int get_apb_clk(void) ++{ ++ unsigned int apb_clk; ++ ++ apb_clk = get_bus_clk() / CFG_TIMER_PER ; ++ ++ return apb_clk; ++} ++ ++static struct resource hi_i2c0_resources[] = { ++ [0] = { ++ .start = CONFIG_HI_I2C0_IO_BASE, ++ .end = CONFIG_HI_I2C0_IO_BASE ++ + CONFIG_HI_I2C0_IO_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++struct hi_platform_i2c hi_i2c0_platform_data = { ++ .clk_limit = CONFIG_HI_I2C0_CLK_LIMIT, ++ .i2c_class = I2C_CLASS_DDC, ++}; ++ ++struct platform_device hi_i2c0_device = { ++ .name = HI_I2C, ++ .id = 0, ++ .resource = hi_i2c0_resources, ++ .num_resources = ARRAY_SIZE(hi_i2c0_resources), ++ .dev = { ++ .platform_data = &hi_i2c0_platform_data, ++ } ++}; ++ ++static struct resource hi_i2c1_resources[] = { ++ [0] = { ++ .start = CONFIG_HI_I2C1_IO_BASE, ++ .end = CONFIG_HI_I2C1_IO_BASE ++ + CONFIG_HI_I2C1_IO_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++struct hi_platform_i2c hi_i2c1_platform_data = { ++ .clk_limit = CONFIG_HI_I2C1_CLK_LIMIT, ++ .i2c_class = I2C_CLASS_DDC, ++}; ++ ++struct platform_device hi_i2c1_device = { ++ .name = HI_I2C, ++ .id = 1, ++ .resource = hi_i2c1_resources, ++ .num_resources = ARRAY_SIZE(hi_i2c1_resources), ++ .dev = { ++ .platform_data = &hi_i2c1_platform_data, ++ } ++}; ++ ++static struct platform_device *hi_i2c_devices[] __initdata = { ++ &hi_i2c0_device, ++ &hi_i2c1_device, ++}; ++ ++static int __init hi_i2c_module_init(void) ++{ ++ int ret; ++ ++ ++ ret = platform_add_devices(hi_i2c_devices, ARRAY_SIZE(hi_i2c_devices)); ++ if (ret) { ++ hi_err("i2c device register failed!\n"); ++ return ret; ++ } ++ ++ ret = platform_driver_register(&hi_i2c_driver); ++ if (ret) { ++ platform_device_unregister(&hi_i2c1_device); ++ platform_device_unregister(&hi_i2c0_device); ++ hi_err("i2c driver register failed!\n"); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static void __exit hi_i2c_module_exit(void) ++{ ++ platform_driver_unregister(&hi_i2c_driver); ++ ++ platform_device_unregister(&hi_i2c1_device); ++ platform_device_unregister(&hi_i2c0_device); ++} +diff --git a/drivers/i2c/busses/i2c_hi3536c.c b/drivers/i2c/busses/i2c_hi3536c.c +new file mode 100644 +index 0000000..6223a41 +--- /dev/null ++++ b/drivers/i2c/busses/i2c_hi3536c.c +@@ -0,0 +1,66 @@ ++#include <mach/platform.h> ++ ++unsigned int get_apb_clk(void) ++{ ++ unsigned int apb_clk; ++ ++ apb_clk = get_bus_clk() / CFG_TIMER_PER ; ++ ++ return apb_clk; ++} ++ ++static struct resource hi_i2c0_resources[] = { ++ [0] = { ++ .start = CONFIG_HI_I2C0_IO_BASE, ++ .end = CONFIG_HI_I2C0_IO_BASE ++ + CONFIG_HI_I2C0_IO_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++struct hi_platform_i2c hi_i2c0_platform_data = { ++ .clk_limit = CONFIG_HI_I2C0_CLK_LIMIT, ++ .i2c_class = I2C_CLASS_DDC, ++}; ++ ++struct platform_device hi_i2c0_device = { ++ .name = HI_I2C, ++ .id = 0, ++ .resource = hi_i2c0_resources, ++ .num_resources = ARRAY_SIZE(hi_i2c0_resources), ++ .dev = { ++ .platform_data = &hi_i2c0_platform_data, ++ } ++}; ++ ++static struct platform_device *hi_i2c_devices[] __initdata = { ++ &hi_i2c0_device, ++}; ++ ++static int __init hi_i2c_module_init(void) ++{ ++ int ret; ++ ++ ++ ret = platform_add_devices(hi_i2c_devices, ARRAY_SIZE(hi_i2c_devices)); ++ if (ret) { ++ hi_err("i2c device register failed!\n"); ++ return ret; ++ } ++ ++ ret = platform_driver_register(&hi_i2c_driver); ++ if (ret) { ++ platform_device_unregister(&hi_i2c0_device); ++ hi_err("i2c driver register failed!\n"); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static void __exit hi_i2c_module_exit(void) ++{ ++ platform_driver_unregister(&hi_i2c_driver); ++ ++ platform_device_unregister(&hi_i2c0_device); ++} +diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c +index 7bd1b5c..b5ec010 100644 +--- a/drivers/i2c/i2c-core.c ++++ b/drivers/i2c/i2c-core.c +@@ -881,9 +881,14 @@ static int i2c_check_client_addr_validity(const struct i2c_client *client) + if (client->addr > 0x3ff) + return -EINVAL; + } else { ++#ifdef CONFIG_HI_I2C + /* 7-bit address, reject the general call address */ ++ if (client->addr == 0x00 || client->addr > 0xfe) ++ return -EINVAL; ++#else + if (client->addr == 0x00 || client->addr > 0x7f) + return -EINVAL; ++#endif + } + return 0; + } +@@ -2123,7 +2128,11 @@ int i2c_master_send(const struct i2c_client *client, const char *buf, int count) + struct i2c_msg msg; + + msg.addr = client->addr; ++#ifdef CONFIG_HI_I2C ++ msg.flags = client->flags; ++#else + msg.flags = client->flags & I2C_M_TEN; ++#endif + msg.len = count; + msg.buf = (char *)buf; + +@@ -2152,7 +2161,11 @@ int i2c_master_recv(const struct i2c_client *client, char *buf, int count) + int ret; + + msg.addr = client->addr; ++#ifdef CONFIG_HI_I2C ++ msg.flags = client->flags; ++#else + msg.flags = client->flags & I2C_M_TEN; ++#endif + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = buf; +diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c +index 71c7a39..d9dd11c 100644 +--- a/drivers/i2c/i2c-dev.c ++++ b/drivers/i2c/i2c-dev.c +@@ -138,22 +138,69 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count, + { + char *tmp; + int ret; +- ++#ifdef CONFIG_HI_I2C ++ unsigned reg_width; ++ unsigned data_width; ++#endif + struct i2c_client *client = file->private_data; + + if (count > 8192) + count = 8192; + ++#ifdef CONFIG_HI_I2C ++ if (client->flags & I2C_M_16BIT_REG) ++ reg_width = 2; ++ else ++ reg_width = 1; ++ ++ if (client->flags & I2C_M_16BIT_DATA) ++ data_width = 2; ++ else ++ data_width = 1; ++ ++ if (client->flags & I2C_M_DMA) ++ tmp = kmalloc(max_t(size_t, reg_width, count), ++ GFP_KERNEL); ++ else ++ tmp = kmalloc(max_t(size_t, reg_width, data_width), ++ GFP_KERNEL); ++ ++ if (tmp == NULL) ++ return -ENOMEM; ++ ++ if (copy_from_user(tmp, buf, reg_width)) ++ return -EFAULT; ++#else + tmp = kmalloc(count, GFP_KERNEL); + if (tmp == NULL) + return -ENOMEM; ++#endif + + pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n", + iminor(file_inode(file)), count); + ++#ifdef CONFIG_HI_I2C ++ if (client->flags & I2C_M_DMA) ++ ret = i2c_master_recv(client, tmp, ++ max_t(size_t, reg_width, count)); ++ else ++ ret = i2c_master_recv(client, tmp, ++ max_t(size_t, reg_width, data_width)); ++#else + ret = i2c_master_recv(client, tmp, count); ++#endif ++ ++#ifdef CONFIG_HI_I2C ++ if (ret >= 0) { ++ if (client->flags & I2C_M_DMA) ++ ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret; ++ else ++ ret = copy_to_user(buf, tmp, data_width) ? -EFAULT : ret; ++ } ++#else + if (ret >= 0) + ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret; ++#endif + kfree(tmp); + return ret; + } +@@ -168,6 +215,9 @@ static ssize_t i2cdev_write(struct file *file, const char __user *buf, + if (count > 8192) + count = 8192; + ++ if (count == 0) ++ return -EINVAL; ++ + tmp = memdup_user(buf, count); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); +@@ -272,6 +322,11 @@ static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client, + break; + } + ++ if (rdwr_pa[i].len == 0) { ++ res = -EINVAL; ++ break; ++ } ++ + data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf; + rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len); + if (IS_ERR(rdwr_pa[i].buf)) { +@@ -431,8 +486,13 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + * the PEC flag already set, the i2c-dev driver won't see + * (or use) this setting. + */ ++#ifdef CONFIG_HI_I2C ++ if ((arg > 0x3ff) || ++ (((client->flags & I2C_M_TEN) == 0) && arg > 0xfe)) ++#else + if ((arg > 0x3ff) || + (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) ++#endif + return -EINVAL; + if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg)) + return -EBUSY; +@@ -451,6 +511,26 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + else + client->flags &= ~I2C_CLIENT_PEC; + return 0; ++#ifdef CONFIG_HI_I2C ++ case I2C_16BIT_REG: ++ if (arg) ++ client->flags |= I2C_M_16BIT_REG; ++ else ++ client->flags &= ~I2C_M_16BIT_REG; ++ return 0; ++ case I2C_16BIT_DATA: ++ if (arg) ++ client->flags |= I2C_M_16BIT_DATA; ++ else ++ client->flags &= ~I2C_M_16BIT_DATA; ++ return 0; ++ case I2C_DMA: ++ if (arg) ++ client->flags |= I2C_M_DMA; ++ else ++ client->flags &= ~I2C_M_DMA; ++ return 0; ++#endif + case I2C_FUNCS: + funcs = i2c_get_functionality(client->adapter); + return put_user(funcs, (unsigned long __user *)arg); +diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c +index 35c02ae..6da5492 100644 +--- a/drivers/iio/industrialio-event.c ++++ b/drivers/iio/industrialio-event.c +@@ -492,6 +492,7 @@ int iio_device_register_eventset(struct iio_dev *indio_dev) + + error_free_setup_event_lines: + iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list); ++ mutex_destroy(&indio_dev->event_interface->read_lock); + kfree(indio_dev->event_interface); + indio_dev->event_interface = NULL; + return ret; +@@ -517,5 +518,6 @@ void iio_device_unregister_eventset(struct iio_dev *indio_dev) + return; + iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list); + kfree(indio_dev->event_interface->group.attrs); ++ mutex_destroy(&indio_dev->event_interface->read_lock); + kfree(indio_dev->event_interface); + } +diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig +index a11ff74..518efa2 100644 +--- a/drivers/input/Kconfig ++++ b/drivers/input/Kconfig +@@ -174,6 +174,25 @@ config INPUT_APMPOWER + To compile this driver as a module, choose M here: the + module will be called apm-power. + ++config INPUT_KEYRESET ++ tristate "Reset key" ++ depends on INPUT ++ select INPUT_KEYCOMBO ++ ---help--- ++ Say Y here if you want to reboot when some keys are pressed; ++ ++ To compile this driver as a module, choose M here: the ++ module will be called keyreset. ++ ++config INPUT_KEYCOMBO ++ tristate "Key combo" ++ depends on INPUT ++ ---help--- ++ Say Y here if you want to take action when some keys are pressed; ++ ++ To compile this driver as a module, choose M here: the ++ module will be called keycombo. ++ + comment "Input Device Drivers" + + source "drivers/input/keyboard/Kconfig" +diff --git a/drivers/input/Makefile b/drivers/input/Makefile +index 5ca3f63..ee4c065 100644 +--- a/drivers/input/Makefile ++++ b/drivers/input/Makefile +@@ -25,3 +25,6 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ + obj-$(CONFIG_INPUT_MISC) += misc/ + + obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o ++obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o ++obj-$(CONFIG_INPUT_KEYCOMBO) += keycombo.o ++ +diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c +index 8afa28e..738a2a3 100644 +--- a/drivers/input/evdev.c ++++ b/drivers/input/evdev.c +@@ -26,6 +26,7 @@ + #include <linux/major.h> + #include <linux/device.h> + #include <linux/cdev.h> ++#include <linux/wakelock.h> + #include "input-compat.h" + + struct evdev { +@@ -46,6 +47,9 @@ struct evdev_client { + unsigned int tail; + unsigned int packet_head; /* [future] position of the first element of next packet */ + spinlock_t buffer_lock; /* protects access to buffer, head and tail */ ++ struct wake_lock wake_lock; ++ bool use_wake_lock; ++ char name[28]; + struct fasync_struct *fasync; + struct evdev *evdev; + struct list_head node; +@@ -149,10 +153,14 @@ static void __pass_event(struct evdev_client *client, + client->buffer[client->tail].value = 0; + + client->packet_head = client->tail; ++ if (client->use_wake_lock) ++ wake_unlock(&client->wake_lock); + } + + if (event->type == EV_SYN && event->code == SYN_REPORT) { + client->packet_head = client->head; ++ if (client->use_wake_lock) ++ wake_lock(&client->wake_lock); + kill_fasync(&client->fasync, SIGIO, POLL_IN); + } + } +@@ -371,6 +379,9 @@ static int evdev_release(struct inode *inode, struct file *file) + + evdev_detach_client(evdev, client); + ++ if (client->use_wake_lock) ++ wake_lock_destroy(&client->wake_lock); ++ + if (is_vmalloc_addr(client)) + vfree(client); + else +@@ -407,6 +418,8 @@ static int evdev_open(struct inode *inode, struct file *file) + + client->bufsize = bufsize; + spin_lock_init(&client->buffer_lock); ++ snprintf(client->name, sizeof(client->name), "%s-%d", ++ dev_name(&evdev->dev), task_tgid_vnr(current)); + client->evdev = evdev; + evdev_attach_client(evdev, client); + +@@ -473,6 +486,9 @@ static int evdev_fetch_next_event(struct evdev_client *client, + if (have_event) { + *event = client->buffer[client->tail++]; + client->tail &= client->bufsize - 1; ++ if (client->use_wake_lock && ++ client->packet_head == client->tail) ++ wake_unlock(&client->wake_lock); + } + + spin_unlock_irq(&client->buffer_lock); +@@ -795,6 +811,11 @@ static int evdev_handle_mt_request(struct input_dev *dev, + return 0; + } + ++/* ++ * HACK: disable conflicting EVIOCREVOKE until Android userspace stops using ++ * EVIOCSSUSPENDBLOCK ++ */ ++/* + static int evdev_revoke(struct evdev *evdev, struct evdev_client *client, + struct file *file) + { +@@ -805,6 +826,36 @@ static int evdev_revoke(struct evdev *evdev, struct evdev_client *client, + + return 0; + } ++*/ ++ ++static int evdev_enable_suspend_block(struct evdev *evdev, ++ struct evdev_client *client) ++{ ++ if (client->use_wake_lock) ++ return 0; ++ ++ spin_lock_irq(&client->buffer_lock); ++ wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name); ++ client->use_wake_lock = true; ++ if (client->packet_head != client->tail) ++ wake_lock(&client->wake_lock); ++ spin_unlock_irq(&client->buffer_lock); ++ return 0; ++} ++ ++static int evdev_disable_suspend_block(struct evdev *evdev, ++ struct evdev_client *client) ++{ ++ if (!client->use_wake_lock) ++ return 0; ++ ++ spin_lock_irq(&client->buffer_lock); ++ client->use_wake_lock = false; ++ wake_lock_destroy(&client->wake_lock); ++ spin_unlock_irq(&client->buffer_lock); ++ ++ return 0; ++} + + static long evdev_do_ioctl(struct file *file, unsigned int cmd, + void __user *p, int compat_mode) +@@ -868,12 +919,17 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, + else + return evdev_ungrab(evdev, client); + ++ /* ++ * HACK: disable conflicting EVIOCREVOKE until Android userspace stops ++ * using EVIOCSSUSPENDBLOCK ++ */ ++ /* + case EVIOCREVOKE: + if (p) + return -EINVAL; + else + return evdev_revoke(evdev, client, file); +- ++ */ + case EVIOCSCLOCKID: + if (copy_from_user(&i, p, sizeof(unsigned int))) + return -EFAULT; +@@ -893,6 +949,15 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, + + case EVIOCSKEYCODE_V2: + return evdev_handle_set_keycode_v2(dev, p); ++ ++ case EVIOCGSUSPENDBLOCK: ++ return put_user(client->use_wake_lock, ip); ++ ++ case EVIOCSSUSPENDBLOCK: ++ if (p) ++ return evdev_enable_suspend_block(evdev, client); ++ else ++ return evdev_disable_suspend_block(evdev, client); + } + + size = _IOC_SIZE(cmd); +diff --git a/drivers/input/keycombo.c b/drivers/input/keycombo.c +new file mode 100644 +index 0000000..2fba451 +--- /dev/null ++++ b/drivers/input/keycombo.c +@@ -0,0 +1,261 @@ ++/* drivers/input/keycombo.c ++ * ++ * Copyright (C) 2014 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/input.h> ++#include <linux/keycombo.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/reboot.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++ ++struct keycombo_state { ++ struct input_handler input_handler; ++ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; ++ unsigned long upbit[BITS_TO_LONGS(KEY_CNT)]; ++ unsigned long key[BITS_TO_LONGS(KEY_CNT)]; ++ spinlock_t lock; ++ struct workqueue_struct *wq; ++ int key_down_target; ++ int key_down; ++ int key_up; ++ struct delayed_work key_down_work; ++ int delay; ++ struct work_struct key_up_work; ++ void (*key_up_fn)(void *); ++ void (*key_down_fn)(void *); ++ void *priv; ++ int key_is_down; ++ struct wakeup_source combo_held_wake_source; ++ struct wakeup_source combo_up_wake_source; ++}; ++ ++static void do_key_down(struct work_struct *work) ++{ ++ struct delayed_work *dwork = container_of(work, struct delayed_work, ++ work); ++ struct keycombo_state *state = container_of(dwork, ++ struct keycombo_state, key_down_work); ++ if (state->key_down_fn) ++ state->key_down_fn(state->priv); ++} ++ ++static void do_key_up(struct work_struct *work) ++{ ++ struct keycombo_state *state = container_of(work, struct keycombo_state, ++ key_up_work); ++ if (state->key_up_fn) ++ state->key_up_fn(state->priv); ++ __pm_relax(&state->combo_up_wake_source); ++} ++ ++static void keycombo_event(struct input_handle *handle, unsigned int type, ++ unsigned int code, int value) ++{ ++ unsigned long flags; ++ struct keycombo_state *state = handle->private; ++ ++ if (type != EV_KEY) ++ return; ++ ++ if (code >= KEY_MAX) ++ return; ++ ++ if (!test_bit(code, state->keybit)) ++ return; ++ ++ spin_lock_irqsave(&state->lock, flags); ++ if (!test_bit(code, state->key) == !value) ++ goto done; ++ __change_bit(code, state->key); ++ if (test_bit(code, state->upbit)) { ++ if (value) ++ state->key_up++; ++ else ++ state->key_up--; ++ } else { ++ if (value) ++ state->key_down++; ++ else ++ state->key_down--; ++ } ++ if (state->key_down == state->key_down_target && state->key_up == 0) { ++ __pm_stay_awake(&state->combo_held_wake_source); ++ state->key_is_down = 1; ++ if (queue_delayed_work(state->wq, &state->key_down_work, ++ state->delay)) ++ pr_debug("Key down work already queued!"); ++ } else if (state->key_is_down) { ++ if (!cancel_delayed_work(&state->key_down_work)) { ++ __pm_stay_awake(&state->combo_up_wake_source); ++ queue_work(state->wq, &state->key_up_work); ++ } ++ __pm_relax(&state->combo_held_wake_source); ++ state->key_is_down = 0; ++ } ++done: ++ spin_unlock_irqrestore(&state->lock, flags); ++} ++ ++static int keycombo_connect(struct input_handler *handler, ++ struct input_dev *dev, ++ const struct input_device_id *id) ++{ ++ int i; ++ int ret; ++ struct input_handle *handle; ++ struct keycombo_state *state = ++ container_of(handler, struct keycombo_state, input_handler); ++ for (i = 0; i < KEY_MAX; i++) { ++ if (test_bit(i, state->keybit) && test_bit(i, dev->keybit)) ++ break; ++ } ++ if (i == KEY_MAX) ++ return -ENODEV; ++ ++ handle = kzalloc(sizeof(*handle), GFP_KERNEL); ++ if (!handle) ++ return -ENOMEM; ++ ++ handle->dev = dev; ++ handle->handler = handler; ++ handle->name = KEYCOMBO_NAME; ++ handle->private = state; ++ ++ ret = input_register_handle(handle); ++ if (ret) ++ goto err_input_register_handle; ++ ++ ret = input_open_device(handle); ++ if (ret) ++ goto err_input_open_device; ++ ++ return 0; ++ ++err_input_open_device: ++ input_unregister_handle(handle); ++err_input_register_handle: ++ kfree(handle); ++ return ret; ++} ++ ++static void keycombo_disconnect(struct input_handle *handle) ++{ ++ input_close_device(handle); ++ input_unregister_handle(handle); ++ kfree(handle); ++} ++ ++static const struct input_device_id keycombo_ids[] = { ++ { ++ .flags = INPUT_DEVICE_ID_MATCH_EVBIT, ++ .evbit = { BIT_MASK(EV_KEY) }, ++ }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(input, keycombo_ids); ++ ++static int keycombo_probe(struct platform_device *pdev) ++{ ++ int ret; ++ int key, *keyp; ++ struct keycombo_state *state; ++ struct keycombo_platform_data *pdata = pdev->dev.platform_data; ++ ++ if (!pdata) ++ return -EINVAL; ++ ++ state = kzalloc(sizeof(*state), GFP_KERNEL); ++ if (!state) ++ return -ENOMEM; ++ ++ spin_lock_init(&state->lock); ++ keyp = pdata->keys_down; ++ while ((key = *keyp++)) { ++ if (key >= KEY_MAX) ++ continue; ++ state->key_down_target++; ++ __set_bit(key, state->keybit); ++ } ++ if (pdata->keys_up) { ++ keyp = pdata->keys_up; ++ while ((key = *keyp++)) { ++ if (key >= KEY_MAX) ++ continue; ++ __set_bit(key, state->keybit); ++ __set_bit(key, state->upbit); ++ } ++ } ++ ++ state->wq = alloc_ordered_workqueue("keycombo", 0); ++ if (!state->wq) ++ return -ENOMEM; ++ ++ state->priv = pdata->priv; ++ ++ if (pdata->key_down_fn) ++ state->key_down_fn = pdata->key_down_fn; ++ INIT_DELAYED_WORK(&state->key_down_work, do_key_down); ++ ++ if (pdata->key_up_fn) ++ state->key_up_fn = pdata->key_up_fn; ++ INIT_WORK(&state->key_up_work, do_key_up); ++ ++ wakeup_source_init(&state->combo_held_wake_source, "key combo"); ++ wakeup_source_init(&state->combo_up_wake_source, "key combo up"); ++ state->delay = msecs_to_jiffies(pdata->key_down_delay); ++ ++ state->input_handler.event = keycombo_event; ++ state->input_handler.connect = keycombo_connect; ++ state->input_handler.disconnect = keycombo_disconnect; ++ state->input_handler.name = KEYCOMBO_NAME; ++ state->input_handler.id_table = keycombo_ids; ++ ret = input_register_handler(&state->input_handler); ++ if (ret) { ++ kfree(state); ++ return ret; ++ } ++ platform_set_drvdata(pdev, state); ++ return 0; ++} ++ ++int keycombo_remove(struct platform_device *pdev) ++{ ++ struct keycombo_state *state = platform_get_drvdata(pdev); ++ input_unregister_handler(&state->input_handler); ++ destroy_workqueue(state->wq); ++ kfree(state); ++ return 0; ++} ++ ++ ++struct platform_driver keycombo_driver = { ++ .driver.name = KEYCOMBO_NAME, ++ .probe = keycombo_probe, ++ .remove = keycombo_remove, ++}; ++ ++static int __init keycombo_init(void) ++{ ++ return platform_driver_register(&keycombo_driver); ++} ++ ++static void __exit keycombo_exit(void) ++{ ++ return platform_driver_unregister(&keycombo_driver); ++} ++ ++module_init(keycombo_init); ++module_exit(keycombo_exit); +diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c +new file mode 100644 +index 0000000..7fbf724 +--- /dev/null ++++ b/drivers/input/keyreset.c +@@ -0,0 +1,145 @@ ++/* drivers/input/keyreset.c ++ * ++ * Copyright (C) 2014 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/input.h> ++#include <linux/keyreset.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/reboot.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/syscalls.h> ++#include <linux/keycombo.h> ++ ++struct keyreset_state { ++ int restart_requested; ++ int (*reset_fn)(void); ++ struct platform_device *pdev_child; ++ struct work_struct restart_work; ++}; ++ ++static void do_restart(struct work_struct *unused) ++{ ++ sys_sync(); ++ kernel_restart(NULL); ++} ++ ++static void do_reset_fn(void *priv) ++{ ++ struct keyreset_state *state = priv; ++ if (state->restart_requested) ++ panic("keyboard reset failed, %d", state->restart_requested); ++ if (state->reset_fn) { ++ state->restart_requested = state->reset_fn(); ++ } else { ++ pr_info("keyboard reset\n"); ++ schedule_work(&state->restart_work); ++ state->restart_requested = 1; ++ } ++} ++ ++static int keyreset_probe(struct platform_device *pdev) ++{ ++ int ret = -ENOMEM; ++ struct keycombo_platform_data *pdata_child; ++ struct keyreset_platform_data *pdata = pdev->dev.platform_data; ++ int up_size = 0, down_size = 0, size; ++ int key, *keyp; ++ struct keyreset_state *state; ++ ++ if (!pdata) ++ return -EINVAL; ++ state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); ++ if (!state) ++ return -ENOMEM; ++ ++ state->pdev_child = platform_device_alloc(KEYCOMBO_NAME, ++ PLATFORM_DEVID_AUTO); ++ if (!state->pdev_child) ++ return -ENOMEM; ++ state->pdev_child->dev.parent = &pdev->dev; ++ INIT_WORK(&state->restart_work, do_restart); ++ ++ keyp = pdata->keys_down; ++ while ((key = *keyp++)) { ++ if (key >= KEY_MAX) ++ continue; ++ down_size++; ++ } ++ if (pdata->keys_up) { ++ keyp = pdata->keys_up; ++ while ((key = *keyp++)) { ++ if (key >= KEY_MAX) ++ continue; ++ up_size++; ++ } ++ } ++ size = sizeof(struct keycombo_platform_data) ++ + sizeof(int) * (down_size + 1); ++ pdata_child = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); ++ if (!pdata_child) ++ goto error; ++ memcpy(pdata_child->keys_down, pdata->keys_down, ++ sizeof(int) * down_size); ++ if (up_size > 0) { ++ pdata_child->keys_up = devm_kzalloc(&pdev->dev, up_size + 1, ++ GFP_KERNEL); ++ if (!pdata_child->keys_up) ++ goto error; ++ memcpy(pdata_child->keys_up, pdata->keys_up, ++ sizeof(int) * up_size); ++ if (!pdata_child->keys_up) ++ goto error; ++ } ++ state->reset_fn = pdata->reset_fn; ++ pdata_child->key_down_fn = do_reset_fn; ++ pdata_child->priv = state; ++ pdata_child->key_down_delay = pdata->key_down_delay; ++ ret = platform_device_add_data(state->pdev_child, pdata_child, size); ++ if (ret) ++ goto error; ++ platform_set_drvdata(pdev, state); ++ return platform_device_add(state->pdev_child); ++error: ++ platform_device_put(state->pdev_child); ++ return ret; ++} ++ ++int keyreset_remove(struct platform_device *pdev) ++{ ++ struct keyreset_state *state = platform_get_drvdata(pdev); ++ platform_device_put(state->pdev_child); ++ return 0; ++} ++ ++ ++struct platform_driver keyreset_driver = { ++ .driver.name = KEYRESET_NAME, ++ .probe = keyreset_probe, ++ .remove = keyreset_remove, ++}; ++ ++static int __init keyreset_init(void) ++{ ++ return platform_driver_register(&keyreset_driver); ++} ++ ++static void __exit keyreset_exit(void) ++{ ++ return platform_driver_unregister(&keyreset_driver); ++} ++ ++module_init(keyreset_init); ++module_exit(keyreset_exit); +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index 23297ab..838824b 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -319,6 +319,17 @@ config INPUT_ATI_REMOTE2 + To compile this driver as a module, choose M here: the module will be + called ati_remote2. + ++config INPUT_KEYCHORD ++ tristate "Key chord input driver support" ++ help ++ Say Y here if you want to enable the key chord driver ++ accessible at /dev/keychord. This driver can be used ++ for receiving notifications when client specified key ++ combinations are pressed. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called keychord. ++ + config INPUT_KEYSPAN_REMOTE + tristate "Keyspan DMR USB remote control" + depends on USB_ARCH_HAS_HCD +@@ -454,6 +465,11 @@ config INPUT_SGI_BTNS + To compile this driver as a module, choose M here: the + module will be called sgi_btns. + ++config INPUT_GPIO ++ tristate "GPIO driver support" ++ help ++ Say Y here if you want to support gpio based keys, wheels etc... ++ + config HP_SDC_RTC + tristate "HP SDC Real Time Clock" + depends on (GSC || HP300) && SERIO +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +index 19c7603..f77268e 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -31,9 +31,11 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o + obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o + obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o + obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o ++obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o + obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o + obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o + obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o ++obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o + obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o + obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o + obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o +diff --git a/drivers/input/misc/gpio_axis.c b/drivers/input/misc/gpio_axis.c +new file mode 100644 +index 0000000..0acf4a5 +--- /dev/null ++++ b/drivers/input/misc/gpio_axis.c +@@ -0,0 +1,192 @@ ++/* drivers/input/misc/gpio_axis.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/gpio.h> ++#include <linux/gpio_event.h> ++#include <linux/interrupt.h> ++#include <linux/slab.h> ++ ++struct gpio_axis_state { ++ struct gpio_event_input_devs *input_devs; ++ struct gpio_event_axis_info *info; ++ uint32_t pos; ++}; ++ ++uint16_t gpio_axis_4bit_gray_map_table[] = { ++ [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */ ++ [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */ ++ [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */ ++ [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */ ++ [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */ ++ [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */ ++ [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */ ++ [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */ ++}; ++uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in) ++{ ++ return gpio_axis_4bit_gray_map_table[in]; ++} ++ ++uint16_t gpio_axis_5bit_singletrack_map_table[] = { ++ [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */ ++ [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */ ++ [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */ ++ [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */ ++ [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */ ++ [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */ ++ [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */ ++ [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */ ++ [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */ ++ [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */ ++}; ++uint16_t gpio_axis_5bit_singletrack_map( ++ struct gpio_event_axis_info *info, uint16_t in) ++{ ++ return gpio_axis_5bit_singletrack_map_table[in]; ++} ++ ++static void gpio_event_update_axis(struct gpio_axis_state *as, int report) ++{ ++ struct gpio_event_axis_info *ai = as->info; ++ int i; ++ int change; ++ uint16_t state = 0; ++ uint16_t pos; ++ uint16_t old_pos = as->pos; ++ for (i = ai->count - 1; i >= 0; i--) ++ state = (state << 1) | gpio_get_value(ai->gpio[i]); ++ pos = ai->map(ai, state); ++ if (ai->flags & GPIOEAF_PRINT_RAW) ++ pr_info("axis %d-%d raw %x, pos %d -> %d\n", ++ ai->type, ai->code, state, old_pos, pos); ++ if (report && pos != old_pos) { ++ if (ai->type == EV_REL) { ++ change = (ai->decoded_size + pos - old_pos) % ++ ai->decoded_size; ++ if (change > ai->decoded_size / 2) ++ change -= ai->decoded_size; ++ if (change == ai->decoded_size / 2) { ++ if (ai->flags & GPIOEAF_PRINT_EVENT) ++ pr_info("axis %d-%d unknown direction, " ++ "pos %d -> %d\n", ai->type, ++ ai->code, old_pos, pos); ++ change = 0; /* no closest direction */ ++ } ++ if (ai->flags & GPIOEAF_PRINT_EVENT) ++ pr_info("axis %d-%d change %d\n", ++ ai->type, ai->code, change); ++ input_report_rel(as->input_devs->dev[ai->dev], ++ ai->code, change); ++ } else { ++ if (ai->flags & GPIOEAF_PRINT_EVENT) ++ pr_info("axis %d-%d now %d\n", ++ ai->type, ai->code, pos); ++ input_event(as->input_devs->dev[ai->dev], ++ ai->type, ai->code, pos); ++ } ++ input_sync(as->input_devs->dev[ai->dev]); ++ } ++ as->pos = pos; ++} ++ ++static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id) ++{ ++ struct gpio_axis_state *as = dev_id; ++ gpio_event_update_axis(as, 1); ++ return IRQ_HANDLED; ++} ++ ++int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func) ++{ ++ int ret; ++ int i; ++ int irq; ++ struct gpio_event_axis_info *ai; ++ struct gpio_axis_state *as; ++ ++ ai = container_of(info, struct gpio_event_axis_info, info); ++ if (func == GPIO_EVENT_FUNC_SUSPEND) { ++ for (i = 0; i < ai->count; i++) ++ disable_irq(gpio_to_irq(ai->gpio[i])); ++ return 0; ++ } ++ if (func == GPIO_EVENT_FUNC_RESUME) { ++ for (i = 0; i < ai->count; i++) ++ enable_irq(gpio_to_irq(ai->gpio[i])); ++ return 0; ++ } ++ ++ if (func == GPIO_EVENT_FUNC_INIT) { ++ *data = as = kmalloc(sizeof(*as), GFP_KERNEL); ++ if (as == NULL) { ++ ret = -ENOMEM; ++ goto err_alloc_axis_state_failed; ++ } ++ as->input_devs = input_devs; ++ as->info = ai; ++ if (ai->dev >= input_devs->count) { ++ pr_err("gpio_event_axis: bad device index %d >= %d " ++ "for %d:%d\n", ai->dev, input_devs->count, ++ ai->type, ai->code); ++ ret = -EINVAL; ++ goto err_bad_device_index; ++ } ++ ++ input_set_capability(input_devs->dev[ai->dev], ++ ai->type, ai->code); ++ if (ai->type == EV_ABS) { ++ input_set_abs_params(input_devs->dev[ai->dev], ai->code, ++ 0, ai->decoded_size - 1, 0, 0); ++ } ++ for (i = 0; i < ai->count; i++) { ++ ret = gpio_request(ai->gpio[i], "gpio_event_axis"); ++ if (ret < 0) ++ goto err_request_gpio_failed; ++ ret = gpio_direction_input(ai->gpio[i]); ++ if (ret < 0) ++ goto err_gpio_direction_input_failed; ++ ret = irq = gpio_to_irq(ai->gpio[i]); ++ if (ret < 0) ++ goto err_get_irq_num_failed; ++ ret = request_irq(irq, gpio_axis_irq_handler, ++ IRQF_TRIGGER_RISING | ++ IRQF_TRIGGER_FALLING, ++ "gpio_event_axis", as); ++ if (ret < 0) ++ goto err_request_irq_failed; ++ } ++ gpio_event_update_axis(as, 0); ++ return 0; ++ } ++ ++ ret = 0; ++ as = *data; ++ for (i = ai->count - 1; i >= 0; i--) { ++ free_irq(gpio_to_irq(ai->gpio[i]), as); ++err_request_irq_failed: ++err_get_irq_num_failed: ++err_gpio_direction_input_failed: ++ gpio_free(ai->gpio[i]); ++err_request_gpio_failed: ++ ; ++ } ++err_bad_device_index: ++ kfree(as); ++ *data = NULL; ++err_alloc_axis_state_failed: ++ return ret; ++} +diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c +new file mode 100644 +index 0000000..90f07eb +--- /dev/null ++++ b/drivers/input/misc/gpio_event.c +@@ -0,0 +1,228 @@ ++/* drivers/input/misc/gpio_event.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/input.h> ++#include <linux/gpio_event.h> ++#include <linux/hrtimer.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++ ++struct gpio_event { ++ struct gpio_event_input_devs *input_devs; ++ const struct gpio_event_platform_data *info; ++ void *state[0]; ++}; ++ ++static int gpio_input_event( ++ struct input_dev *dev, unsigned int type, unsigned int code, int value) ++{ ++ int i; ++ int devnr; ++ int ret = 0; ++ int tmp_ret; ++ struct gpio_event_info **ii; ++ struct gpio_event *ip = input_get_drvdata(dev); ++ ++ for (devnr = 0; devnr < ip->input_devs->count; devnr++) ++ if (ip->input_devs->dev[devnr] == dev) ++ break; ++ if (devnr == ip->input_devs->count) { ++ pr_err("gpio_input_event: unknown device %p\n", dev); ++ return -EIO; ++ } ++ ++ for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) { ++ if ((*ii)->event) { ++ tmp_ret = (*ii)->event(ip->input_devs, *ii, ++ &ip->state[i], ++ devnr, type, code, value); ++ if (tmp_ret) ++ ret = tmp_ret; ++ } ++ } ++ return ret; ++} ++ ++static int gpio_event_call_all_func(struct gpio_event *ip, int func) ++{ ++ int i; ++ int ret; ++ struct gpio_event_info **ii; ++ ++ if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) { ++ ii = ip->info->info; ++ for (i = 0; i < ip->info->info_count; i++, ii++) { ++ if ((*ii)->func == NULL) { ++ ret = -ENODEV; ++ pr_err("gpio_event_probe: Incomplete pdata, " ++ "no function\n"); ++ goto err_no_func; ++ } ++ if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend) ++ continue; ++ ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i], ++ func); ++ if (ret) { ++ pr_err("gpio_event_probe: function failed\n"); ++ goto err_func_failed; ++ } ++ } ++ return 0; ++ } ++ ++ ret = 0; ++ i = ip->info->info_count; ++ ii = ip->info->info + i; ++ while (i > 0) { ++ i--; ++ ii--; ++ if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend) ++ continue; ++ (*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1); ++err_func_failed: ++err_no_func: ++ ; ++ } ++ return ret; ++} ++ ++static void __maybe_unused gpio_event_suspend(struct gpio_event *ip) ++{ ++ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); ++ if (ip->info->power) ++ ip->info->power(ip->info, 0); ++} ++ ++static void __maybe_unused gpio_event_resume(struct gpio_event *ip) ++{ ++ if (ip->info->power) ++ ip->info->power(ip->info, 1); ++ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); ++} ++ ++static int gpio_event_probe(struct platform_device *pdev) ++{ ++ int err; ++ struct gpio_event *ip; ++ struct gpio_event_platform_data *event_info; ++ int dev_count = 1; ++ int i; ++ int registered = 0; ++ ++ event_info = pdev->dev.platform_data; ++ if (event_info == NULL) { ++ pr_err("gpio_event_probe: No pdata\n"); ++ return -ENODEV; ++ } ++ if ((!event_info->name && !event_info->names[0]) || ++ !event_info->info || !event_info->info_count) { ++ pr_err("gpio_event_probe: Incomplete pdata\n"); ++ return -ENODEV; ++ } ++ if (!event_info->name) ++ while (event_info->names[dev_count]) ++ dev_count++; ++ ip = kzalloc(sizeof(*ip) + ++ sizeof(ip->state[0]) * event_info->info_count + ++ sizeof(*ip->input_devs) + ++ sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL); ++ if (ip == NULL) { ++ err = -ENOMEM; ++ pr_err("gpio_event_probe: Failed to allocate private data\n"); ++ goto err_kp_alloc_failed; ++ } ++ ip->input_devs = (void*)&ip->state[event_info->info_count]; ++ platform_set_drvdata(pdev, ip); ++ ++ for (i = 0; i < dev_count; i++) { ++ struct input_dev *input_dev = input_allocate_device(); ++ if (input_dev == NULL) { ++ err = -ENOMEM; ++ pr_err("gpio_event_probe: " ++ "Failed to allocate input device\n"); ++ goto err_input_dev_alloc_failed; ++ } ++ input_set_drvdata(input_dev, ip); ++ input_dev->name = event_info->name ? ++ event_info->name : event_info->names[i]; ++ input_dev->event = gpio_input_event; ++ ip->input_devs->dev[i] = input_dev; ++ } ++ ip->input_devs->count = dev_count; ++ ip->info = event_info; ++ if (event_info->power) ++ ip->info->power(ip->info, 1); ++ ++ err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); ++ if (err) ++ goto err_call_all_func_failed; ++ ++ for (i = 0; i < dev_count; i++) { ++ err = input_register_device(ip->input_devs->dev[i]); ++ if (err) { ++ pr_err("gpio_event_probe: Unable to register %s " ++ "input device\n", ip->input_devs->dev[i]->name); ++ goto err_input_register_device_failed; ++ } ++ registered++; ++ } ++ ++ return 0; ++ ++err_input_register_device_failed: ++ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); ++err_call_all_func_failed: ++ if (event_info->power) ++ ip->info->power(ip->info, 0); ++ for (i = 0; i < registered; i++) ++ input_unregister_device(ip->input_devs->dev[i]); ++ for (i = dev_count - 1; i >= registered; i--) { ++ input_free_device(ip->input_devs->dev[i]); ++err_input_dev_alloc_failed: ++ ; ++ } ++ kfree(ip); ++err_kp_alloc_failed: ++ return err; ++} ++ ++static int gpio_event_remove(struct platform_device *pdev) ++{ ++ struct gpio_event *ip = platform_get_drvdata(pdev); ++ int i; ++ ++ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); ++ if (ip->info->power) ++ ip->info->power(ip->info, 0); ++ for (i = 0; i < ip->input_devs->count; i++) ++ input_unregister_device(ip->input_devs->dev[i]); ++ kfree(ip); ++ return 0; ++} ++ ++static struct platform_driver gpio_event_driver = { ++ .probe = gpio_event_probe, ++ .remove = gpio_event_remove, ++ .driver = { ++ .name = GPIO_EVENT_DEV_NAME, ++ }, ++}; ++ ++module_platform_driver(gpio_event_driver); ++ ++MODULE_DESCRIPTION("GPIO Event Driver"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c +new file mode 100644 +index 0000000..eefd027 +--- /dev/null ++++ b/drivers/input/misc/gpio_input.c +@@ -0,0 +1,390 @@ ++/* drivers/input/misc/gpio_input.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/gpio.h> ++#include <linux/gpio_event.h> ++#include <linux/hrtimer.h> ++#include <linux/input.h> ++#include <linux/interrupt.h> ++#include <linux/slab.h> ++#include <linux/pm_wakeup.h> ++ ++enum { ++ DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ ++ DEBOUNCE_PRESSED = BIT(1), ++ DEBOUNCE_NOTPRESSED = BIT(2), ++ DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */ ++ DEBOUNCE_POLL = BIT(4), /* Stable polling state */ ++ ++ DEBOUNCE_UNKNOWN = ++ DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED, ++}; ++ ++struct gpio_key_state { ++ struct gpio_input_state *ds; ++ uint8_t debounce; ++}; ++ ++struct gpio_input_state { ++ struct gpio_event_input_devs *input_devs; ++ const struct gpio_event_input_info *info; ++ struct hrtimer timer; ++ int use_irq; ++ int debounce_count; ++ spinlock_t irq_lock; ++ struct wakeup_source *ws; ++ struct gpio_key_state key_state[0]; ++}; ++ ++static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) ++{ ++ int i; ++ int pressed; ++ struct gpio_input_state *ds = ++ container_of(timer, struct gpio_input_state, timer); ++ unsigned gpio_flags = ds->info->flags; ++ unsigned npolarity; ++ int nkeys = ds->info->keymap_size; ++ const struct gpio_event_direct_entry *key_entry; ++ struct gpio_key_state *key_state; ++ unsigned long irqflags; ++ uint8_t debounce; ++ bool sync_needed; ++ ++#if 0 ++ key_entry = kp->keys_info->keymap; ++ key_state = kp->key_state; ++ for (i = 0; i < nkeys; i++, key_entry++, key_state++) ++ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, ++ gpio_read_detect_status(key_entry->gpio)); ++#endif ++ key_entry = ds->info->keymap; ++ key_state = ds->key_state; ++ sync_needed = false; ++ spin_lock_irqsave(&ds->irq_lock, irqflags); ++ for (i = 0; i < nkeys; i++, key_entry++, key_state++) { ++ debounce = key_state->debounce; ++ if (debounce & DEBOUNCE_WAIT_IRQ) ++ continue; ++ if (key_state->debounce & DEBOUNCE_UNSTABLE) { ++ debounce = key_state->debounce = DEBOUNCE_UNKNOWN; ++ enable_irq(gpio_to_irq(key_entry->gpio)); ++ if (gpio_flags & GPIOEDF_PRINT_KEY_UNSTABLE) ++ pr_info("gpio_keys_scan_keys: key %x-%x, %d " ++ "(%d) continue debounce\n", ++ ds->info->type, key_entry->code, ++ i, key_entry->gpio); ++ } ++ npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH); ++ pressed = gpio_get_value(key_entry->gpio) ^ npolarity; ++ if (debounce & DEBOUNCE_POLL) { ++ if (pressed == !(debounce & DEBOUNCE_PRESSED)) { ++ ds->debounce_count++; ++ key_state->debounce = DEBOUNCE_UNKNOWN; ++ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) ++ pr_info("gpio_keys_scan_keys: key %x-" ++ "%x, %d (%d) start debounce\n", ++ ds->info->type, key_entry->code, ++ i, key_entry->gpio); ++ } ++ continue; ++ } ++ if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) { ++ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) ++ pr_info("gpio_keys_scan_keys: key %x-%x, %d " ++ "(%d) debounce pressed 1\n", ++ ds->info->type, key_entry->code, ++ i, key_entry->gpio); ++ key_state->debounce = DEBOUNCE_PRESSED; ++ continue; ++ } ++ if (!pressed && (debounce & DEBOUNCE_PRESSED)) { ++ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) ++ pr_info("gpio_keys_scan_keys: key %x-%x, %d " ++ "(%d) debounce pressed 0\n", ++ ds->info->type, key_entry->code, ++ i, key_entry->gpio); ++ key_state->debounce = DEBOUNCE_NOTPRESSED; ++ continue; ++ } ++ /* key is stable */ ++ ds->debounce_count--; ++ if (ds->use_irq) ++ key_state->debounce |= DEBOUNCE_WAIT_IRQ; ++ else ++ key_state->debounce |= DEBOUNCE_POLL; ++ if (gpio_flags & GPIOEDF_PRINT_KEYS) ++ pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) " ++ "changed to %d\n", ds->info->type, ++ key_entry->code, i, key_entry->gpio, pressed); ++ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, ++ key_entry->code, pressed); ++ sync_needed = true; ++ } ++ if (sync_needed) { ++ for (i = 0; i < ds->input_devs->count; i++) ++ input_sync(ds->input_devs->dev[i]); ++ } ++ ++#if 0 ++ key_entry = kp->keys_info->keymap; ++ key_state = kp->key_state; ++ for (i = 0; i < nkeys; i++, key_entry++, key_state++) { ++ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, ++ gpio_read_detect_status(key_entry->gpio)); ++ } ++#endif ++ ++ if (ds->debounce_count) ++ hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL); ++ else if (!ds->use_irq) ++ hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL); ++ else ++ __pm_relax(ds->ws); ++ ++ spin_unlock_irqrestore(&ds->irq_lock, irqflags); ++ ++ return HRTIMER_NORESTART; ++} ++ ++static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) ++{ ++ struct gpio_key_state *ks = dev_id; ++ struct gpio_input_state *ds = ks->ds; ++ int keymap_index = ks - ds->key_state; ++ const struct gpio_event_direct_entry *key_entry; ++ unsigned long irqflags; ++ int pressed; ++ ++ if (!ds->use_irq) ++ return IRQ_HANDLED; ++ ++ key_entry = &ds->info->keymap[keymap_index]; ++ ++ if (ds->info->debounce_time.tv64) { ++ spin_lock_irqsave(&ds->irq_lock, irqflags); ++ if (ks->debounce & DEBOUNCE_WAIT_IRQ) { ++ ks->debounce = DEBOUNCE_UNKNOWN; ++ if (ds->debounce_count++ == 0) { ++ __pm_stay_awake(ds->ws); ++ hrtimer_start( ++ &ds->timer, ds->info->debounce_time, ++ HRTIMER_MODE_REL); ++ } ++ if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE) ++ pr_info("gpio_event_input_irq_handler: " ++ "key %x-%x, %d (%d) start debounce\n", ++ ds->info->type, key_entry->code, ++ keymap_index, key_entry->gpio); ++ } else { ++ disable_irq_nosync(irq); ++ ks->debounce = DEBOUNCE_UNSTABLE; ++ } ++ spin_unlock_irqrestore(&ds->irq_lock, irqflags); ++ } else { ++ pressed = gpio_get_value(key_entry->gpio) ^ ++ !(ds->info->flags & GPIOEDF_ACTIVE_HIGH); ++ if (ds->info->flags & GPIOEDF_PRINT_KEYS) ++ pr_info("gpio_event_input_irq_handler: key %x-%x, %d " ++ "(%d) changed to %d\n", ++ ds->info->type, key_entry->code, keymap_index, ++ key_entry->gpio, pressed); ++ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, ++ key_entry->code, pressed); ++ input_sync(ds->input_devs->dev[key_entry->dev]); ++ } ++ return IRQ_HANDLED; ++} ++ ++static int gpio_event_input_request_irqs(struct gpio_input_state *ds) ++{ ++ int i; ++ int err; ++ unsigned int irq; ++ unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; ++ ++ for (i = 0; i < ds->info->keymap_size; i++) { ++ err = irq = gpio_to_irq(ds->info->keymap[i].gpio); ++ if (err < 0) ++ goto err_gpio_get_irq_num_failed; ++ err = request_irq(irq, gpio_event_input_irq_handler, ++ req_flags, "gpio_keys", &ds->key_state[i]); ++ if (err) { ++ pr_err("gpio_event_input_request_irqs: request_irq " ++ "failed for input %d, irq %d\n", ++ ds->info->keymap[i].gpio, irq); ++ goto err_request_irq_failed; ++ } ++ if (ds->info->info.no_suspend) { ++ err = enable_irq_wake(irq); ++ if (err) { ++ pr_err("gpio_event_input_request_irqs: " ++ "enable_irq_wake failed for input %d, " ++ "irq %d\n", ++ ds->info->keymap[i].gpio, irq); ++ goto err_enable_irq_wake_failed; ++ } ++ } ++ } ++ return 0; ++ ++ for (i = ds->info->keymap_size - 1; i >= 0; i--) { ++ irq = gpio_to_irq(ds->info->keymap[i].gpio); ++ if (ds->info->info.no_suspend) ++ disable_irq_wake(irq); ++err_enable_irq_wake_failed: ++ free_irq(irq, &ds->key_state[i]); ++err_request_irq_failed: ++err_gpio_get_irq_num_failed: ++ ; ++ } ++ return err; ++} ++ ++int gpio_event_input_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func) ++{ ++ int ret; ++ int i; ++ unsigned long irqflags; ++ struct gpio_event_input_info *di; ++ struct gpio_input_state *ds = *data; ++ char *wlname; ++ ++ di = container_of(info, struct gpio_event_input_info, info); ++ ++ if (func == GPIO_EVENT_FUNC_SUSPEND) { ++ if (ds->use_irq) ++ for (i = 0; i < di->keymap_size; i++) ++ disable_irq(gpio_to_irq(di->keymap[i].gpio)); ++ hrtimer_cancel(&ds->timer); ++ return 0; ++ } ++ if (func == GPIO_EVENT_FUNC_RESUME) { ++ spin_lock_irqsave(&ds->irq_lock, irqflags); ++ if (ds->use_irq) ++ for (i = 0; i < di->keymap_size; i++) ++ enable_irq(gpio_to_irq(di->keymap[i].gpio)); ++ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); ++ spin_unlock_irqrestore(&ds->irq_lock, irqflags); ++ return 0; ++ } ++ ++ if (func == GPIO_EVENT_FUNC_INIT) { ++ if (ktime_to_ns(di->poll_time) <= 0) ++ di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC); ++ ++ *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) * ++ di->keymap_size, GFP_KERNEL); ++ if (ds == NULL) { ++ ret = -ENOMEM; ++ pr_err("gpio_event_input_func: " ++ "Failed to allocate private data\n"); ++ goto err_ds_alloc_failed; ++ } ++ ds->debounce_count = di->keymap_size; ++ ds->input_devs = input_devs; ++ ds->info = di; ++ wlname = kasprintf(GFP_KERNEL, "gpio_input:%s%s", ++ input_devs->dev[0]->name, ++ (input_devs->count > 1) ? "..." : ""); ++ ++ ds->ws = wakeup_source_register(wlname); ++ kfree(wlname); ++ if (!ds->ws) { ++ ret = -ENOMEM; ++ pr_err("gpio_event_input_func: " ++ "Failed to allocate wakeup source\n"); ++ goto err_ws_failed; ++ } ++ ++ spin_lock_init(&ds->irq_lock); ++ ++ for (i = 0; i < di->keymap_size; i++) { ++ int dev = di->keymap[i].dev; ++ if (dev >= input_devs->count) { ++ pr_err("gpio_event_input_func: bad device " ++ "index %d >= %d for key code %d\n", ++ dev, input_devs->count, ++ di->keymap[i].code); ++ ret = -EINVAL; ++ goto err_bad_keymap; ++ } ++ input_set_capability(input_devs->dev[dev], di->type, ++ di->keymap[i].code); ++ ds->key_state[i].ds = ds; ++ ds->key_state[i].debounce = DEBOUNCE_UNKNOWN; ++ } ++ ++ for (i = 0; i < di->keymap_size; i++) { ++ ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in"); ++ if (ret) { ++ pr_err("gpio_event_input_func: gpio_request " ++ "failed for %d\n", di->keymap[i].gpio); ++ goto err_gpio_request_failed; ++ } ++ ret = gpio_direction_input(di->keymap[i].gpio); ++ if (ret) { ++ pr_err("gpio_event_input_func: " ++ "gpio_direction_input failed for %d\n", ++ di->keymap[i].gpio); ++ goto err_gpio_configure_failed; ++ } ++ } ++ ++ ret = gpio_event_input_request_irqs(ds); ++ ++ spin_lock_irqsave(&ds->irq_lock, irqflags); ++ ds->use_irq = ret == 0; ++ ++ pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s " ++ "mode\n", input_devs->dev[0]->name, ++ (input_devs->count > 1) ? "..." : "", ++ ret == 0 ? "interrupt" : "polling"); ++ ++ hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ ds->timer.function = gpio_event_input_timer_func; ++ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); ++ spin_unlock_irqrestore(&ds->irq_lock, irqflags); ++ return 0; ++ } ++ ++ ret = 0; ++ spin_lock_irqsave(&ds->irq_lock, irqflags); ++ hrtimer_cancel(&ds->timer); ++ if (ds->use_irq) { ++ for (i = di->keymap_size - 1; i >= 0; i--) { ++ int irq = gpio_to_irq(di->keymap[i].gpio); ++ if (ds->info->info.no_suspend) ++ disable_irq_wake(irq); ++ free_irq(irq, &ds->key_state[i]); ++ } ++ } ++ spin_unlock_irqrestore(&ds->irq_lock, irqflags); ++ ++ for (i = di->keymap_size - 1; i >= 0; i--) { ++err_gpio_configure_failed: ++ gpio_free(di->keymap[i].gpio); ++err_gpio_request_failed: ++ ; ++ } ++err_bad_keymap: ++ wakeup_source_unregister(ds->ws); ++err_ws_failed: ++ kfree(ds); ++err_ds_alloc_failed: ++ return ret; ++} +diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c +new file mode 100644 +index 0000000..eaa9e89 +--- /dev/null ++++ b/drivers/input/misc/gpio_matrix.c +@@ -0,0 +1,441 @@ ++/* drivers/input/misc/gpio_matrix.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/gpio.h> ++#include <linux/gpio_event.h> ++#include <linux/hrtimer.h> ++#include <linux/interrupt.h> ++#include <linux/slab.h> ++#include <linux/wakelock.h> ++ ++struct gpio_kp { ++ struct gpio_event_input_devs *input_devs; ++ struct gpio_event_matrix_info *keypad_info; ++ struct hrtimer timer; ++ struct wake_lock wake_lock; ++ int current_output; ++ unsigned int use_irq:1; ++ unsigned int key_state_changed:1; ++ unsigned int last_key_state_changed:1; ++ unsigned int some_keys_pressed:2; ++ unsigned int disabled_irq:1; ++ unsigned long keys_pressed[0]; ++}; ++ ++static void clear_phantom_key(struct gpio_kp *kp, int out, int in) ++{ ++ struct gpio_event_matrix_info *mi = kp->keypad_info; ++ int key_index = out * mi->ninputs + in; ++ unsigned short keyentry = mi->keymap[key_index]; ++ unsigned short keycode = keyentry & MATRIX_KEY_MASK; ++ unsigned short dev = keyentry >> MATRIX_CODE_BITS; ++ ++ if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) { ++ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) ++ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " ++ "cleared\n", keycode, out, in, ++ mi->output_gpios[out], mi->input_gpios[in]); ++ __clear_bit(key_index, kp->keys_pressed); ++ } else { ++ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) ++ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " ++ "not cleared\n", keycode, out, in, ++ mi->output_gpios[out], mi->input_gpios[in]); ++ } ++} ++ ++static int restore_keys_for_input(struct gpio_kp *kp, int out, int in) ++{ ++ int rv = 0; ++ int key_index; ++ ++ key_index = out * kp->keypad_info->ninputs + in; ++ while (out < kp->keypad_info->noutputs) { ++ if (test_bit(key_index, kp->keys_pressed)) { ++ rv = 1; ++ clear_phantom_key(kp, out, in); ++ } ++ key_index += kp->keypad_info->ninputs; ++ out++; ++ } ++ return rv; ++} ++ ++static void remove_phantom_keys(struct gpio_kp *kp) ++{ ++ int out, in, inp; ++ int key_index; ++ ++ if (kp->some_keys_pressed < 3) ++ return; ++ ++ for (out = 0; out < kp->keypad_info->noutputs; out++) { ++ inp = -1; ++ key_index = out * kp->keypad_info->ninputs; ++ for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) { ++ if (test_bit(key_index, kp->keys_pressed)) { ++ if (inp == -1) { ++ inp = in; ++ continue; ++ } ++ if (inp >= 0) { ++ if (!restore_keys_for_input(kp, out + 1, ++ inp)) ++ break; ++ clear_phantom_key(kp, out, inp); ++ inp = -2; ++ } ++ restore_keys_for_input(kp, out, in); ++ } ++ } ++ } ++} ++ ++static void report_key(struct gpio_kp *kp, int key_index, int out, int in) ++{ ++ struct gpio_event_matrix_info *mi = kp->keypad_info; ++ int pressed = test_bit(key_index, kp->keys_pressed); ++ unsigned short keyentry = mi->keymap[key_index]; ++ unsigned short keycode = keyentry & MATRIX_KEY_MASK; ++ unsigned short dev = keyentry >> MATRIX_CODE_BITS; ++ ++ if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) { ++ if (keycode == KEY_RESERVED) { ++ if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS) ++ pr_info("gpiomatrix: unmapped key, %d-%d " ++ "(%d-%d) changed to %d\n", ++ out, in, mi->output_gpios[out], ++ mi->input_gpios[in], pressed); ++ } else { ++ if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS) ++ pr_info("gpiomatrix: key %x, %d-%d (%d-%d) " ++ "changed to %d\n", keycode, ++ out, in, mi->output_gpios[out], ++ mi->input_gpios[in], pressed); ++ input_report_key(kp->input_devs->dev[dev], keycode, pressed); ++ } ++ } ++} ++ ++static void report_sync(struct gpio_kp *kp) ++{ ++ int i; ++ ++ for (i = 0; i < kp->input_devs->count; i++) ++ input_sync(kp->input_devs->dev[i]); ++} ++ ++static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) ++{ ++ int out, in; ++ int key_index; ++ int gpio; ++ struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer); ++ struct gpio_event_matrix_info *mi = kp->keypad_info; ++ unsigned gpio_keypad_flags = mi->flags; ++ unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH); ++ ++ out = kp->current_output; ++ if (out == mi->noutputs) { ++ out = 0; ++ kp->last_key_state_changed = kp->key_state_changed; ++ kp->key_state_changed = 0; ++ kp->some_keys_pressed = 0; ++ } else { ++ key_index = out * mi->ninputs; ++ for (in = 0; in < mi->ninputs; in++, key_index++) { ++ gpio = mi->input_gpios[in]; ++ if (gpio_get_value(gpio) ^ !polarity) { ++ if (kp->some_keys_pressed < 3) ++ kp->some_keys_pressed++; ++ kp->key_state_changed |= !__test_and_set_bit( ++ key_index, kp->keys_pressed); ++ } else ++ kp->key_state_changed |= __test_and_clear_bit( ++ key_index, kp->keys_pressed); ++ } ++ gpio = mi->output_gpios[out]; ++ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) ++ gpio_set_value(gpio, !polarity); ++ else ++ gpio_direction_input(gpio); ++ out++; ++ } ++ kp->current_output = out; ++ if (out < mi->noutputs) { ++ gpio = mi->output_gpios[out]; ++ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) ++ gpio_set_value(gpio, polarity); ++ else ++ gpio_direction_output(gpio, polarity); ++ hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL); ++ return HRTIMER_NORESTART; ++ } ++ if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) { ++ if (kp->key_state_changed) { ++ hrtimer_start(&kp->timer, mi->debounce_delay, ++ HRTIMER_MODE_REL); ++ return HRTIMER_NORESTART; ++ } ++ kp->key_state_changed = kp->last_key_state_changed; ++ } ++ if (kp->key_state_changed) { ++ if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS) ++ remove_phantom_keys(kp); ++ key_index = 0; ++ for (out = 0; out < mi->noutputs; out++) ++ for (in = 0; in < mi->ninputs; in++, key_index++) ++ report_key(kp, key_index, out, in); ++ report_sync(kp); ++ } ++ if (!kp->use_irq || kp->some_keys_pressed) { ++ hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL); ++ return HRTIMER_NORESTART; ++ } ++ ++ /* No keys are pressed, reenable interrupt */ ++ for (out = 0; out < mi->noutputs; out++) { ++ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) ++ gpio_set_value(mi->output_gpios[out], polarity); ++ else ++ gpio_direction_output(mi->output_gpios[out], polarity); ++ } ++ for (in = 0; in < mi->ninputs; in++) ++ enable_irq(gpio_to_irq(mi->input_gpios[in])); ++ wake_unlock(&kp->wake_lock); ++ return HRTIMER_NORESTART; ++} ++ ++static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) ++{ ++ int i; ++ struct gpio_kp *kp = dev_id; ++ struct gpio_event_matrix_info *mi = kp->keypad_info; ++ unsigned gpio_keypad_flags = mi->flags; ++ ++ if (!kp->use_irq) { ++ /* ignore interrupt while registering the handler */ ++ kp->disabled_irq = 1; ++ disable_irq_nosync(irq_in); ++ return IRQ_HANDLED; ++ } ++ ++ for (i = 0; i < mi->ninputs; i++) ++ disable_irq_nosync(gpio_to_irq(mi->input_gpios[i])); ++ for (i = 0; i < mi->noutputs; i++) { ++ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) ++ gpio_set_value(mi->output_gpios[i], ++ !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH)); ++ else ++ gpio_direction_input(mi->output_gpios[i]); ++ } ++ wake_lock(&kp->wake_lock); ++ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); ++ return IRQ_HANDLED; ++} ++ ++static int gpio_keypad_request_irqs(struct gpio_kp *kp) ++{ ++ int i; ++ int err; ++ unsigned int irq; ++ unsigned long request_flags; ++ struct gpio_event_matrix_info *mi = kp->keypad_info; ++ ++ switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) { ++ default: ++ request_flags = IRQF_TRIGGER_FALLING; ++ break; ++ case GPIOKPF_ACTIVE_HIGH: ++ request_flags = IRQF_TRIGGER_RISING; ++ break; ++ case GPIOKPF_LEVEL_TRIGGERED_IRQ: ++ request_flags = IRQF_TRIGGER_LOW; ++ break; ++ case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH: ++ request_flags = IRQF_TRIGGER_HIGH; ++ break; ++ } ++ ++ for (i = 0; i < mi->ninputs; i++) { ++ err = irq = gpio_to_irq(mi->input_gpios[i]); ++ if (err < 0) ++ goto err_gpio_get_irq_num_failed; ++ err = request_irq(irq, gpio_keypad_irq_handler, request_flags, ++ "gpio_kp", kp); ++ if (err) { ++ pr_err("gpiomatrix: request_irq failed for input %d, " ++ "irq %d\n", mi->input_gpios[i], irq); ++ goto err_request_irq_failed; ++ } ++ err = enable_irq_wake(irq); ++ if (err) { ++ pr_err("gpiomatrix: set_irq_wake failed for input %d, " ++ "irq %d\n", mi->input_gpios[i], irq); ++ } ++ disable_irq(irq); ++ if (kp->disabled_irq) { ++ kp->disabled_irq = 0; ++ enable_irq(irq); ++ } ++ } ++ return 0; ++ ++ for (i = mi->noutputs - 1; i >= 0; i--) { ++ free_irq(gpio_to_irq(mi->input_gpios[i]), kp); ++err_request_irq_failed: ++err_gpio_get_irq_num_failed: ++ ; ++ } ++ return err; ++} ++ ++int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func) ++{ ++ int i; ++ int err; ++ int key_count; ++ struct gpio_kp *kp; ++ struct gpio_event_matrix_info *mi; ++ ++ mi = container_of(info, struct gpio_event_matrix_info, info); ++ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) { ++ /* TODO: disable scanning */ ++ return 0; ++ } ++ ++ if (func == GPIO_EVENT_FUNC_INIT) { ++ if (mi->keymap == NULL || ++ mi->input_gpios == NULL || ++ mi->output_gpios == NULL) { ++ err = -ENODEV; ++ pr_err("gpiomatrix: Incomplete pdata\n"); ++ goto err_invalid_platform_data; ++ } ++ key_count = mi->ninputs * mi->noutputs; ++ ++ *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) * ++ BITS_TO_LONGS(key_count), GFP_KERNEL); ++ if (kp == NULL) { ++ err = -ENOMEM; ++ pr_err("gpiomatrix: Failed to allocate private data\n"); ++ goto err_kp_alloc_failed; ++ } ++ kp->input_devs = input_devs; ++ kp->keypad_info = mi; ++ for (i = 0; i < key_count; i++) { ++ unsigned short keyentry = mi->keymap[i]; ++ unsigned short keycode = keyentry & MATRIX_KEY_MASK; ++ unsigned short dev = keyentry >> MATRIX_CODE_BITS; ++ if (dev >= input_devs->count) { ++ pr_err("gpiomatrix: bad device index %d >= " ++ "%d for key code %d\n", ++ dev, input_devs->count, keycode); ++ err = -EINVAL; ++ goto err_bad_keymap; ++ } ++ if (keycode && keycode <= KEY_MAX) ++ input_set_capability(input_devs->dev[dev], ++ EV_KEY, keycode); ++ } ++ ++ for (i = 0; i < mi->noutputs; i++) { ++ err = gpio_request(mi->output_gpios[i], "gpio_kp_out"); ++ if (err) { ++ pr_err("gpiomatrix: gpio_request failed for " ++ "output %d\n", mi->output_gpios[i]); ++ goto err_request_output_gpio_failed; ++ } ++ if (gpio_cansleep(mi->output_gpios[i])) { ++ pr_err("gpiomatrix: unsupported output gpio %d," ++ " can sleep\n", mi->output_gpios[i]); ++ err = -EINVAL; ++ goto err_output_gpio_configure_failed; ++ } ++ if (mi->flags & GPIOKPF_DRIVE_INACTIVE) ++ err = gpio_direction_output(mi->output_gpios[i], ++ !(mi->flags & GPIOKPF_ACTIVE_HIGH)); ++ else ++ err = gpio_direction_input(mi->output_gpios[i]); ++ if (err) { ++ pr_err("gpiomatrix: gpio_configure failed for " ++ "output %d\n", mi->output_gpios[i]); ++ goto err_output_gpio_configure_failed; ++ } ++ } ++ for (i = 0; i < mi->ninputs; i++) { ++ err = gpio_request(mi->input_gpios[i], "gpio_kp_in"); ++ if (err) { ++ pr_err("gpiomatrix: gpio_request failed for " ++ "input %d\n", mi->input_gpios[i]); ++ goto err_request_input_gpio_failed; ++ } ++ err = gpio_direction_input(mi->input_gpios[i]); ++ if (err) { ++ pr_err("gpiomatrix: gpio_direction_input failed" ++ " for input %d\n", mi->input_gpios[i]); ++ goto err_gpio_direction_input_failed; ++ } ++ } ++ kp->current_output = mi->noutputs; ++ kp->key_state_changed = 1; ++ ++ hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ kp->timer.function = gpio_keypad_timer_func; ++ wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp"); ++ err = gpio_keypad_request_irqs(kp); ++ kp->use_irq = err == 0; ++ ++ pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for " ++ "%s%s in %s mode\n", input_devs->dev[0]->name, ++ (input_devs->count > 1) ? "..." : "", ++ kp->use_irq ? "interrupt" : "polling"); ++ ++ if (kp->use_irq) ++ wake_lock(&kp->wake_lock); ++ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); ++ ++ return 0; ++ } ++ ++ err = 0; ++ kp = *data; ++ ++ if (kp->use_irq) ++ for (i = mi->noutputs - 1; i >= 0; i--) ++ free_irq(gpio_to_irq(mi->input_gpios[i]), kp); ++ ++ hrtimer_cancel(&kp->timer); ++ wake_lock_destroy(&kp->wake_lock); ++ for (i = mi->noutputs - 1; i >= 0; i--) { ++err_gpio_direction_input_failed: ++ gpio_free(mi->input_gpios[i]); ++err_request_input_gpio_failed: ++ ; ++ } ++ for (i = mi->noutputs - 1; i >= 0; i--) { ++err_output_gpio_configure_failed: ++ gpio_free(mi->output_gpios[i]); ++err_request_output_gpio_failed: ++ ; ++ } ++err_bad_keymap: ++ kfree(kp); ++err_kp_alloc_failed: ++err_invalid_platform_data: ++ return err; ++} +diff --git a/drivers/input/misc/gpio_output.c b/drivers/input/misc/gpio_output.c +new file mode 100644 +index 0000000..2aac2fa +--- /dev/null ++++ b/drivers/input/misc/gpio_output.c +@@ -0,0 +1,97 @@ ++/* drivers/input/misc/gpio_output.c ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/gpio.h> ++#include <linux/gpio_event.h> ++ ++int gpio_event_output_event( ++ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, ++ void **data, unsigned int dev, unsigned int type, ++ unsigned int code, int value) ++{ ++ int i; ++ struct gpio_event_output_info *oi; ++ oi = container_of(info, struct gpio_event_output_info, info); ++ if (type != oi->type) ++ return 0; ++ if (!(oi->flags & GPIOEDF_ACTIVE_HIGH)) ++ value = !value; ++ for (i = 0; i < oi->keymap_size; i++) ++ if (dev == oi->keymap[i].dev && code == oi->keymap[i].code) ++ gpio_set_value(oi->keymap[i].gpio, value); ++ return 0; ++} ++ ++int gpio_event_output_func( ++ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, ++ void **data, int func) ++{ ++ int ret; ++ int i; ++ struct gpio_event_output_info *oi; ++ oi = container_of(info, struct gpio_event_output_info, info); ++ ++ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) ++ return 0; ++ ++ if (func == GPIO_EVENT_FUNC_INIT) { ++ int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH); ++ ++ for (i = 0; i < oi->keymap_size; i++) { ++ int dev = oi->keymap[i].dev; ++ if (dev >= input_devs->count) { ++ pr_err("gpio_event_output_func: bad device " ++ "index %d >= %d for key code %d\n", ++ dev, input_devs->count, ++ oi->keymap[i].code); ++ ret = -EINVAL; ++ goto err_bad_keymap; ++ } ++ input_set_capability(input_devs->dev[dev], oi->type, ++ oi->keymap[i].code); ++ } ++ ++ for (i = 0; i < oi->keymap_size; i++) { ++ ret = gpio_request(oi->keymap[i].gpio, ++ "gpio_event_output"); ++ if (ret) { ++ pr_err("gpio_event_output_func: gpio_request " ++ "failed for %d\n", oi->keymap[i].gpio); ++ goto err_gpio_request_failed; ++ } ++ ret = gpio_direction_output(oi->keymap[i].gpio, ++ output_level); ++ if (ret) { ++ pr_err("gpio_event_output_func: " ++ "gpio_direction_output failed for %d\n", ++ oi->keymap[i].gpio); ++ goto err_gpio_direction_output_failed; ++ } ++ } ++ return 0; ++ } ++ ++ ret = 0; ++ for (i = oi->keymap_size - 1; i >= 0; i--) { ++err_gpio_direction_output_failed: ++ gpio_free(oi->keymap[i].gpio); ++err_gpio_request_failed: ++ ; ++ } ++err_bad_keymap: ++ return ret; ++} ++ +diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c +new file mode 100644 +index 0000000..a5ea27a +--- /dev/null ++++ b/drivers/input/misc/keychord.c +@@ -0,0 +1,391 @@ ++/* ++ * drivers/input/misc/keychord.c ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/poll.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/spinlock.h> ++#include <linux/fs.h> ++#include <linux/miscdevice.h> ++#include <linux/keychord.h> ++#include <linux/sched.h> ++ ++#define KEYCHORD_NAME "keychord" ++#define BUFFER_SIZE 16 ++ ++MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>"); ++MODULE_DESCRIPTION("Key chord input driver"); ++MODULE_SUPPORTED_DEVICE("keychord"); ++MODULE_LICENSE("GPL"); ++ ++#define NEXT_KEYCHORD(kc) ((struct input_keychord *) \ ++ ((char *)kc + sizeof(struct input_keychord) + \ ++ kc->count * sizeof(kc->keycodes[0]))) ++ ++struct keychord_device { ++ struct input_handler input_handler; ++ int registered; ++ ++ /* list of keychords to monitor */ ++ struct input_keychord *keychords; ++ int keychord_count; ++ ++ /* bitmask of keys contained in our keychords */ ++ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; ++ /* current state of the keys */ ++ unsigned long keystate[BITS_TO_LONGS(KEY_CNT)]; ++ /* number of keys that are currently pressed */ ++ int key_down; ++ ++ /* second input_device_id is needed for null termination */ ++ struct input_device_id device_ids[2]; ++ ++ spinlock_t lock; ++ wait_queue_head_t waitq; ++ unsigned char head; ++ unsigned char tail; ++ __u16 buff[BUFFER_SIZE]; ++}; ++ ++static int check_keychord(struct keychord_device *kdev, ++ struct input_keychord *keychord) ++{ ++ int i; ++ ++ if (keychord->count != kdev->key_down) ++ return 0; ++ ++ for (i = 0; i < keychord->count; i++) { ++ if (!test_bit(keychord->keycodes[i], kdev->keystate)) ++ return 0; ++ } ++ ++ /* we have a match */ ++ return 1; ++} ++ ++static void keychord_event(struct input_handle *handle, unsigned int type, ++ unsigned int code, int value) ++{ ++ struct keychord_device *kdev = handle->private; ++ struct input_keychord *keychord; ++ unsigned long flags; ++ int i, got_chord = 0; ++ ++ if (type != EV_KEY || code >= KEY_MAX) ++ return; ++ ++ spin_lock_irqsave(&kdev->lock, flags); ++ /* do nothing if key state did not change */ ++ if (!test_bit(code, kdev->keystate) == !value) ++ goto done; ++ __change_bit(code, kdev->keystate); ++ if (value) ++ kdev->key_down++; ++ else ++ kdev->key_down--; ++ ++ /* don't notify on key up */ ++ if (!value) ++ goto done; ++ /* ignore this event if it is not one of the keys we are monitoring */ ++ if (!test_bit(code, kdev->keybit)) ++ goto done; ++ ++ keychord = kdev->keychords; ++ if (!keychord) ++ goto done; ++ ++ /* check to see if the keyboard state matches any keychords */ ++ for (i = 0; i < kdev->keychord_count; i++) { ++ if (check_keychord(kdev, keychord)) { ++ kdev->buff[kdev->head] = keychord->id; ++ kdev->head = (kdev->head + 1) % BUFFER_SIZE; ++ got_chord = 1; ++ break; ++ } ++ /* skip to next keychord */ ++ keychord = NEXT_KEYCHORD(keychord); ++ } ++ ++done: ++ spin_unlock_irqrestore(&kdev->lock, flags); ++ ++ if (got_chord) { ++ pr_info("keychord: got keychord id %d. Any tasks: %d\n", ++ keychord->id, ++ !list_empty_careful(&kdev->waitq.task_list)); ++ wake_up_interruptible(&kdev->waitq); ++ } ++} ++ ++static int keychord_connect(struct input_handler *handler, ++ struct input_dev *dev, ++ const struct input_device_id *id) ++{ ++ int i, ret; ++ struct input_handle *handle; ++ struct keychord_device *kdev = ++ container_of(handler, struct keychord_device, input_handler); ++ ++ /* ++ * ignore this input device if it does not contain any keycodes ++ * that we are monitoring ++ */ ++ for (i = 0; i < KEY_MAX; i++) { ++ if (test_bit(i, kdev->keybit) && test_bit(i, dev->keybit)) ++ break; ++ } ++ if (i == KEY_MAX) ++ return -ENODEV; ++ ++ handle = kzalloc(sizeof(*handle), GFP_KERNEL); ++ if (!handle) ++ return -ENOMEM; ++ ++ handle->dev = dev; ++ handle->handler = handler; ++ handle->name = KEYCHORD_NAME; ++ handle->private = kdev; ++ ++ ret = input_register_handle(handle); ++ if (ret) ++ goto err_input_register_handle; ++ ++ ret = input_open_device(handle); ++ if (ret) ++ goto err_input_open_device; ++ ++ pr_info("keychord: using input dev %s for fevent\n", dev->name); ++ ++ return 0; ++ ++err_input_open_device: ++ input_unregister_handle(handle); ++err_input_register_handle: ++ kfree(handle); ++ return ret; ++} ++ ++static void keychord_disconnect(struct input_handle *handle) ++{ ++ input_close_device(handle); ++ input_unregister_handle(handle); ++ kfree(handle); ++} ++ ++/* ++ * keychord_read is used to read keychord events from the driver ++ */ ++static ssize_t keychord_read(struct file *file, char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ struct keychord_device *kdev = file->private_data; ++ __u16 id; ++ int retval; ++ unsigned long flags; ++ ++ if (count < sizeof(id)) ++ return -EINVAL; ++ count = sizeof(id); ++ ++ if (kdev->head == kdev->tail && (file->f_flags & O_NONBLOCK)) ++ return -EAGAIN; ++ ++ retval = wait_event_interruptible(kdev->waitq, ++ kdev->head != kdev->tail); ++ if (retval) ++ return retval; ++ ++ spin_lock_irqsave(&kdev->lock, flags); ++ /* pop a keychord ID off the queue */ ++ id = kdev->buff[kdev->tail]; ++ kdev->tail = (kdev->tail + 1) % BUFFER_SIZE; ++ spin_unlock_irqrestore(&kdev->lock, flags); ++ ++ if (copy_to_user(buffer, &id, count)) ++ return -EFAULT; ++ ++ return count; ++} ++ ++/* ++ * keychord_write is used to configure the driver ++ */ ++static ssize_t keychord_write(struct file *file, const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ struct keychord_device *kdev = file->private_data; ++ struct input_keychord *keychords = 0; ++ struct input_keychord *keychord, *next, *end; ++ int ret, i, key; ++ unsigned long flags; ++ ++ if (count < sizeof(struct input_keychord)) ++ return -EINVAL; ++ keychords = kzalloc(count, GFP_KERNEL); ++ if (!keychords) ++ return -ENOMEM; ++ ++ /* read list of keychords from userspace */ ++ if (copy_from_user(keychords, buffer, count)) { ++ kfree(keychords); ++ return -EFAULT; ++ } ++ ++ /* unregister handler before changing configuration */ ++ if (kdev->registered) { ++ input_unregister_handler(&kdev->input_handler); ++ kdev->registered = 0; ++ } ++ ++ spin_lock_irqsave(&kdev->lock, flags); ++ /* clear any existing configuration */ ++ kfree(kdev->keychords); ++ kdev->keychords = 0; ++ kdev->keychord_count = 0; ++ kdev->key_down = 0; ++ memset(kdev->keybit, 0, sizeof(kdev->keybit)); ++ memset(kdev->keystate, 0, sizeof(kdev->keystate)); ++ kdev->head = kdev->tail = 0; ++ ++ keychord = keychords; ++ end = (struct input_keychord *)((char *)keychord + count); ++ ++ while (keychord < end) { ++ next = NEXT_KEYCHORD(keychord); ++ if (keychord->count <= 0 || next > end) { ++ pr_err("keychord: invalid keycode count %d\n", ++ keychord->count); ++ goto err_unlock_return; ++ } ++ if (keychord->version != KEYCHORD_VERSION) { ++ pr_err("keychord: unsupported version %d\n", ++ keychord->version); ++ goto err_unlock_return; ++ } ++ ++ /* keep track of the keys we are monitoring in keybit */ ++ for (i = 0; i < keychord->count; i++) { ++ key = keychord->keycodes[i]; ++ if (key < 0 || key >= KEY_CNT) { ++ pr_err("keychord: keycode %d out of range\n", ++ key); ++ goto err_unlock_return; ++ } ++ __set_bit(key, kdev->keybit); ++ } ++ ++ kdev->keychord_count++; ++ keychord = next; ++ } ++ ++ kdev->keychords = keychords; ++ spin_unlock_irqrestore(&kdev->lock, flags); ++ ++ ret = input_register_handler(&kdev->input_handler); ++ if (ret) { ++ kfree(keychords); ++ kdev->keychords = 0; ++ return ret; ++ } ++ kdev->registered = 1; ++ ++ return count; ++ ++err_unlock_return: ++ spin_unlock_irqrestore(&kdev->lock, flags); ++ kfree(keychords); ++ return -EINVAL; ++} ++ ++static unsigned int keychord_poll(struct file *file, poll_table *wait) ++{ ++ struct keychord_device *kdev = file->private_data; ++ ++ poll_wait(file, &kdev->waitq, wait); ++ ++ if (kdev->head != kdev->tail) ++ return POLLIN | POLLRDNORM; ++ ++ return 0; ++} ++ ++static int keychord_open(struct inode *inode, struct file *file) ++{ ++ struct keychord_device *kdev; ++ ++ kdev = kzalloc(sizeof(struct keychord_device), GFP_KERNEL); ++ if (!kdev) ++ return -ENOMEM; ++ ++ spin_lock_init(&kdev->lock); ++ init_waitqueue_head(&kdev->waitq); ++ ++ kdev->input_handler.event = keychord_event; ++ kdev->input_handler.connect = keychord_connect; ++ kdev->input_handler.disconnect = keychord_disconnect; ++ kdev->input_handler.name = KEYCHORD_NAME; ++ kdev->input_handler.id_table = kdev->device_ids; ++ ++ kdev->device_ids[0].flags = INPUT_DEVICE_ID_MATCH_EVBIT; ++ __set_bit(EV_KEY, kdev->device_ids[0].evbit); ++ ++ file->private_data = kdev; ++ ++ return 0; ++} ++ ++static int keychord_release(struct inode *inode, struct file *file) ++{ ++ struct keychord_device *kdev = file->private_data; ++ ++ if (kdev->registered) ++ input_unregister_handler(&kdev->input_handler); ++ kfree(kdev); ++ ++ return 0; ++} ++ ++static const struct file_operations keychord_fops = { ++ .owner = THIS_MODULE, ++ .open = keychord_open, ++ .release = keychord_release, ++ .read = keychord_read, ++ .write = keychord_write, ++ .poll = keychord_poll, ++}; ++ ++static struct miscdevice keychord_misc = { ++ .fops = &keychord_fops, ++ .name = KEYCHORD_NAME, ++ .minor = MISC_DYNAMIC_MINOR, ++}; ++ ++static int __init keychord_init(void) ++{ ++ return misc_register(&keychord_misc); ++} ++ ++static void __exit keychord_exit(void) ++{ ++ misc_deregister(&keychord_misc); ++} ++ ++module_init(keychord_init); ++module_exit(keychord_exit); +diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c +index 38493ff..7aae78e 100644 +--- a/drivers/irqchip/irq-gic.c ++++ b/drivers/irqchip/irq-gic.c +@@ -81,6 +81,23 @@ static DEFINE_RAW_SPINLOCK(irq_controller_lock); + static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly; + + /* ++ *Uesed to process gic sgi interrupt * ++ */ ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++#define DIS_IRQ_CNT 6 ++#else ++#define DIS_IRQ_CNT 2 ++#endif ++struct gic_sgi_handle { ++ unsigned int irq; ++ void (*handle)(unsigned int cpu_intrf, ++ unsigned int irq_num, ++ struct pt_regs *regs); ++}; ++struct gic_sgi_handle dis_irq_handle[DIS_IRQ_CNT]; ++EXPORT_SYMBOL(dis_irq_handle); ++ ++/* + * Supported arch specific GIC irq extension. + * Default make them NULL. + */ +@@ -258,6 +275,22 @@ static int gic_set_wake(struct irq_data *d, unsigned int on) + #else + #define gic_set_wake NULL + #endif ++/* used to process dis irq */ ++int dis_irq_proc(u32 irqnr, u32 irqstat, struct pt_regs *regs) ++{ ++ u32 idx; ++ ++ for (idx = 0; idx < DIS_IRQ_CNT; idx++) { ++ if ((irqnr == dis_irq_handle[idx].irq) ++ && (dis_irq_handle[idx].handle)) { ++ dis_irq_handle[idx].handle(((irqstat >> 10) & 0x7), ++ irqnr, regs); ++ return 1; ++ } ++ } ++ ++ return 0; ++} + + static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) + { +@@ -273,13 +306,19 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) + handle_domain_irq(gic->domain, irqnr, regs); + continue; + } ++ + if (irqnr < 16) { + writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); ++ ++ /*Call dis irq proccess func*/ ++ if (dis_irq_proc(irqnr, irqstat, regs)) ++ continue; + #ifdef CONFIG_SMP + handle_IPI(irqnr, regs); + #endif + continue; + } ++ + break; + } while (1); + } +@@ -366,7 +405,6 @@ static void gic_cpu_if_up(void) + writel_relaxed(bypass | GICC_ENABLE, cpu_base + GIC_CPU_CTRL); + } + +- + static void __init gic_dist_init(struct gic_chip_data *gic) + { + unsigned int i; +@@ -1001,7 +1039,28 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, + } + + gic_chip.flags |= gic_arch_extn.flags; ++#if !defined(CONFIG_ARCH_HI3559) && !defined(CONFIG_ARCH_HI3556) + gic_dist_init(gic); ++#else ++ { ++ /* 0x47444946('G''D''I''F') is abbreviation of GIC_DIST_INIT_FLAG */ ++ /* hi3559/hi3556 runs 2 OS, another OS distributes the IRQ. */ ++ /* when another OS has distributed the IRQ, sysctrl(0x12020130) */ ++ /* register will be set to 0x47444946 */ ++ /* if another OS did not distribute the IRQ, this OS will do it. */ ++ ++#define GIC_DIST_INIT_FLAG 0x47444946 ++#define GIC_DIST_INIT_FLAG_OFFSET 0x0130 ++ struct device_node *np; ++ int gic_dist_init_flag; ++ ++ np = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl"); ++ gic_dist_init_flag = readl(of_iomap(np, 0) + GIC_DIST_INIT_FLAG_OFFSET); ++ ++ if(gic_dist_init_flag != GIC_DIST_INIT_FLAG) ++ gic_dist_init(gic); ++ } ++#endif + gic_cpu_init(gic); + gic_pm_init(gic); + } +diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c +index 08981be..5503e43 100644 +--- a/drivers/md/dm-crypt.c ++++ b/drivers/md/dm-crypt.c +@@ -18,9 +18,11 @@ + #include <linux/slab.h> + #include <linux/crypto.h> + #include <linux/workqueue.h> ++#include <linux/kthread.h> + #include <linux/backing-dev.h> + #include <linux/atomic.h> + #include <linux/scatterlist.h> ++#include <linux/rbtree.h> + #include <asm/page.h> + #include <asm/unaligned.h> + #include <crypto/hash.h> +@@ -58,7 +60,8 @@ struct dm_crypt_io { + atomic_t io_pending; + int error; + sector_t sector; +- struct dm_crypt_io *base_io; ++ ++ struct rb_node rb_node; + } CRYPTO_MINALIGN_ATTR; + + struct dm_crypt_request { +@@ -108,7 +111,8 @@ struct iv_tcw_private { + * Crypt: maps a linear range of a block device + * and encrypts / decrypts at the same time. + */ +-enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID }; ++enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID, ++ DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD }; + + /* + * The fields in here must be read only after initialization. +@@ -121,14 +125,18 @@ struct crypt_config { + * pool for per bio private data, crypto requests and + * encryption requeusts/buffer pages + */ +- mempool_t *io_pool; + mempool_t *req_pool; + mempool_t *page_pool; + struct bio_set *bs; ++ struct mutex bio_alloc_lock; + + struct workqueue_struct *io_queue; + struct workqueue_struct *crypt_queue; + ++ struct task_struct *write_thread; ++ wait_queue_head_t write_thread_wait; ++ struct rb_root write_tree; ++ + char *cipher; + char *cipher_string; + +@@ -172,9 +180,6 @@ struct crypt_config { + }; + + #define MIN_IOS 16 +-#define MIN_POOL_PAGES 32 +- +-static struct kmem_cache *_crypt_io_pool; + + static void clone_init(struct dm_crypt_io *, struct bio *); + static void kcryptd_queue_crypt(struct dm_crypt_io *io); +@@ -223,7 +228,7 @@ static struct crypto_ablkcipher *any_tfm(struct crypt_config *cc) + * + * tcw: Compatible implementation of the block chaining mode used + * by the TrueCrypt device encryption system (prior to version 4.1). +- * For more info see: http://www.truecrypt.org ++ * For more info see: https://gitlab.com/cryptsetup/cryptsetup/wikis/TrueCryptOnDiskFormat + * It operates on full 512 byte sectors and uses CBC + * with an IV derived from initial key and the sector number. + * In addition, whitening value is applied on every sector, whitening +@@ -946,57 +951,70 @@ static int crypt_convert(struct crypt_config *cc, + return 0; + } + ++static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone); ++ + /* + * Generate a new unfragmented bio with the given size + * This should never violate the device limitations +- * May return a smaller bio when running out of pages, indicated by +- * *out_of_pages set to 1. ++ * ++ * This function may be called concurrently. If we allocate from the mempool ++ * concurrently, there is a possibility of deadlock. For example, if we have ++ * mempool of 256 pages, two processes, each wanting 256, pages allocate from ++ * the mempool concurrently, it may deadlock in a situation where both processes ++ * have allocated 128 pages and the mempool is exhausted. ++ * ++ * In order to avoid this scenario we allocate the pages under a mutex. ++ * ++ * In order to not degrade performance with excessive locking, we try ++ * non-blocking allocations without a mutex first but on failure we fallback ++ * to blocking allocations with a mutex. + */ +-static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size, +- unsigned *out_of_pages) ++static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size) + { + struct crypt_config *cc = io->cc; + struct bio *clone; + unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; +- gfp_t gfp_mask = GFP_NOIO | __GFP_HIGHMEM; +- unsigned i, len; ++ gfp_t gfp_mask = GFP_NOWAIT | __GFP_HIGHMEM; ++ unsigned i, len, remaining_size; + struct page *page; ++ struct bio_vec *bvec; ++ ++retry: ++ if (unlikely(gfp_mask & __GFP_WAIT)) ++ mutex_lock(&cc->bio_alloc_lock); + + clone = bio_alloc_bioset(GFP_NOIO, nr_iovecs, cc->bs); + if (!clone) +- return NULL; ++ goto return_clone; + + clone_init(io, clone); +- *out_of_pages = 0; ++ ++ remaining_size = size; + + for (i = 0; i < nr_iovecs; i++) { + page = mempool_alloc(cc->page_pool, gfp_mask); + if (!page) { +- *out_of_pages = 1; +- break; ++ crypt_free_buffer_pages(cc, clone); ++ bio_put(clone); ++ gfp_mask |= __GFP_WAIT; ++ goto retry; + } + +- /* +- * If additional pages cannot be allocated without waiting, +- * return a partially-allocated bio. The caller will then try +- * to allocate more bios while submitting this partial bio. +- */ +- gfp_mask = (gfp_mask | __GFP_NOWARN) & ~__GFP_WAIT; ++ len = (remaining_size > PAGE_SIZE) ? PAGE_SIZE : remaining_size; + +- len = (size > PAGE_SIZE) ? PAGE_SIZE : size; ++ bvec = &clone->bi_io_vec[clone->bi_vcnt++]; ++ bvec->bv_page = page; ++ bvec->bv_len = len; ++ bvec->bv_offset = 0; + +- if (!bio_add_page(clone, page, len, 0)) { +- mempool_free(page, cc->page_pool); +- break; +- } ++ clone->bi_iter.bi_size += len; + +- size -= len; ++ remaining_size -= len; + } + +- if (!clone->bi_iter.bi_size) { +- bio_put(clone); +- return NULL; +- } ++return_clone: ++ if (unlikely(gfp_mask & __GFP_WAIT)) ++ mutex_unlock(&cc->bio_alloc_lock); + + return clone; + } +@@ -1020,7 +1038,6 @@ static void crypt_io_init(struct dm_crypt_io *io, struct crypt_config *cc, + io->base_bio = bio; + io->sector = sector; + io->error = 0; +- io->base_io = NULL; + io->ctx.req = NULL; + atomic_set(&io->io_pending, 0); + } +@@ -1033,13 +1050,11 @@ static void crypt_inc_pending(struct dm_crypt_io *io) + /* + * One of the bios was finished. Check for completion of + * the whole request and correctly clean up the buffer. +- * If base_io is set, wait for the last fragment to complete. + */ + static void crypt_dec_pending(struct dm_crypt_io *io) + { + struct crypt_config *cc = io->cc; + struct bio *base_bio = io->base_bio; +- struct dm_crypt_io *base_io = io->base_io; + int error = io->error; + + if (!atomic_dec_and_test(&io->io_pending)) +@@ -1047,16 +1062,8 @@ static void crypt_dec_pending(struct dm_crypt_io *io) + + if (io->ctx.req) + crypt_free_req(cc, io->ctx.req, base_bio); +- if (io != dm_per_bio_data(base_bio, cc->per_bio_data_size)) +- mempool_free(io, cc->io_pool); +- +- if (likely(!base_io)) +- bio_endio(base_bio, error); +- else { +- if (error && !base_io->error) +- base_io->error = error; +- crypt_dec_pending(base_io); +- } ++ ++ bio_endio(base_bio, error); + } + + /* +@@ -1117,15 +1124,15 @@ static void clone_init(struct dm_crypt_io *io, struct bio *clone) + static int kcryptd_io_read(struct dm_crypt_io *io, gfp_t gfp) + { + struct crypt_config *cc = io->cc; +- struct bio *base_bio = io->base_bio; + struct bio *clone; + + /* +- * The block layer might modify the bvec array, so always +- * copy the required bvecs because we need the original +- * one in order to decrypt the whole bio data *afterwards*. ++ * We need the original biovec array in order to decrypt ++ * the whole bio data *afterwards* -- thanks to immutable ++ * biovecs we don't need to worry about the block layer ++ * modifying the biovec array; so leverage bio_clone_fast(). + */ +- clone = bio_clone_bioset(base_bio, gfp, cc->bs); ++ clone = bio_clone_fast(io->base_bio, gfp, cc->bs); + if (!clone) + return 1; + +@@ -1138,37 +1145,97 @@ static int kcryptd_io_read(struct dm_crypt_io *io, gfp_t gfp) + return 0; + } + ++static void kcryptd_io_read_work(struct work_struct *work) ++{ ++ struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work); ++ ++ crypt_inc_pending(io); ++ if (kcryptd_io_read(io, GFP_NOIO)) ++ io->error = -ENOMEM; ++ crypt_dec_pending(io); ++} ++ ++static void kcryptd_queue_read(struct dm_crypt_io *io) ++{ ++ struct crypt_config *cc = io->cc; ++ ++ INIT_WORK(&io->work, kcryptd_io_read_work); ++ queue_work(cc->io_queue, &io->work); ++} ++ + static void kcryptd_io_write(struct dm_crypt_io *io) + { + struct bio *clone = io->ctx.bio_out; ++ + generic_make_request(clone); + } + +-static void kcryptd_io(struct work_struct *work) ++#define crypt_io_from_node(node) rb_entry((node), struct dm_crypt_io, rb_node) ++ ++static int dmcrypt_write(void *data) + { +- struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work); ++ struct crypt_config *cc = data; ++ struct dm_crypt_io *io; + +- if (bio_data_dir(io->base_bio) == READ) { +- crypt_inc_pending(io); +- if (kcryptd_io_read(io, GFP_NOIO)) +- io->error = -ENOMEM; +- crypt_dec_pending(io); +- } else +- kcryptd_io_write(io); +-} ++ while (1) { ++ struct rb_root write_tree; ++ struct blk_plug plug; + +-static void kcryptd_queue_io(struct dm_crypt_io *io) +-{ +- struct crypt_config *cc = io->cc; ++ DECLARE_WAITQUEUE(wait, current); + +- INIT_WORK(&io->work, kcryptd_io); +- queue_work(cc->io_queue, &io->work); ++ spin_lock_irq(&cc->write_thread_wait.lock); ++continue_locked: ++ ++ if (!RB_EMPTY_ROOT(&cc->write_tree)) ++ goto pop_from_list; ++ ++ __set_current_state(TASK_INTERRUPTIBLE); ++ __add_wait_queue(&cc->write_thread_wait, &wait); ++ ++ spin_unlock_irq(&cc->write_thread_wait.lock); ++ ++ if (unlikely(kthread_should_stop())) { ++ set_task_state(current, TASK_RUNNING); ++ remove_wait_queue(&cc->write_thread_wait, &wait); ++ break; ++ } ++ ++ schedule(); ++ ++ set_task_state(current, TASK_RUNNING); ++ spin_lock_irq(&cc->write_thread_wait.lock); ++ __remove_wait_queue(&cc->write_thread_wait, &wait); ++ goto continue_locked; ++ ++pop_from_list: ++ write_tree = cc->write_tree; ++ cc->write_tree = RB_ROOT; ++ spin_unlock_irq(&cc->write_thread_wait.lock); ++ ++ BUG_ON(rb_parent(write_tree.rb_node)); ++ ++ /* ++ * Note: we cannot walk the tree here with rb_next because ++ * the structures may be freed when kcryptd_io_write is called. ++ */ ++ blk_start_plug(&plug); ++ do { ++ io = crypt_io_from_node(rb_first(&write_tree)); ++ rb_erase(&io->rb_node, &write_tree); ++ kcryptd_io_write(io); ++ } while (!RB_EMPTY_ROOT(&write_tree)); ++ blk_finish_plug(&plug); ++ } ++ return 0; + } + + static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async) + { + struct bio *clone = io->ctx.bio_out; + struct crypt_config *cc = io->cc; ++ unsigned long flags; ++ sector_t sector; ++ struct rb_node **rbp, *parent; + + if (unlikely(io->error < 0)) { + crypt_free_buffer_pages(cc, clone); +@@ -1182,20 +1249,34 @@ static void kcryptd_crypt_write_io_submit(struct dm_crypt_io *io, int async) + + clone->bi_iter.bi_sector = cc->start + io->sector; + +- if (async) +- kcryptd_queue_io(io); +- else ++ if (likely(!async) && test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags)) { + generic_make_request(clone); ++ return; ++ } ++ ++ spin_lock_irqsave(&cc->write_thread_wait.lock, flags); ++ rbp = &cc->write_tree.rb_node; ++ parent = NULL; ++ sector = io->sector; ++ while (*rbp) { ++ parent = *rbp; ++ if (sector < crypt_io_from_node(parent)->sector) ++ rbp = &(*rbp)->rb_left; ++ else ++ rbp = &(*rbp)->rb_right; ++ } ++ rb_link_node(&io->rb_node, parent, rbp); ++ rb_insert_color(&io->rb_node, &cc->write_tree); ++ ++ wake_up_locked(&cc->write_thread_wait); ++ spin_unlock_irqrestore(&cc->write_thread_wait.lock, flags); + } + + static void kcryptd_crypt_write_convert(struct dm_crypt_io *io) + { + struct crypt_config *cc = io->cc; + struct bio *clone; +- struct dm_crypt_io *new_io; + int crypt_finished; +- unsigned out_of_pages = 0; +- unsigned remaining = io->base_bio->bi_iter.bi_size; + sector_t sector = io->sector; + int r; + +@@ -1205,80 +1286,30 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io) + crypt_inc_pending(io); + crypt_convert_init(cc, &io->ctx, NULL, io->base_bio, sector); + +- /* +- * The allocated buffers can be smaller than the whole bio, +- * so repeat the whole process until all the data can be handled. +- */ +- while (remaining) { +- clone = crypt_alloc_buffer(io, remaining, &out_of_pages); +- if (unlikely(!clone)) { +- io->error = -ENOMEM; +- break; +- } +- +- io->ctx.bio_out = clone; +- io->ctx.iter_out = clone->bi_iter; +- +- remaining -= clone->bi_iter.bi_size; +- sector += bio_sectors(clone); +- +- crypt_inc_pending(io); +- +- r = crypt_convert(cc, &io->ctx); +- if (r < 0) +- io->error = -EIO; +- +- crypt_finished = atomic_dec_and_test(&io->ctx.cc_pending); +- +- /* Encryption was already finished, submit io now */ +- if (crypt_finished) { +- kcryptd_crypt_write_io_submit(io, 0); ++ clone = crypt_alloc_buffer(io, io->base_bio->bi_iter.bi_size); ++ if (unlikely(!clone)) { ++ io->error = -EIO; ++ goto dec; ++ } + +- /* +- * If there was an error, do not try next fragments. +- * For async, error is processed in async handler. +- */ +- if (unlikely(r < 0)) +- break; ++ io->ctx.bio_out = clone; ++ io->ctx.iter_out = clone->bi_iter; + +- io->sector = sector; +- } ++ sector += bio_sectors(clone); + +- /* +- * Out of memory -> run queues +- * But don't wait if split was due to the io size restriction +- */ +- if (unlikely(out_of_pages)) +- congestion_wait(BLK_RW_ASYNC, HZ/100); +- +- /* +- * With async crypto it is unsafe to share the crypto context +- * between fragments, so switch to a new dm_crypt_io structure. +- */ +- if (unlikely(!crypt_finished && remaining)) { +- new_io = mempool_alloc(cc->io_pool, GFP_NOIO); +- crypt_io_init(new_io, io->cc, io->base_bio, sector); +- crypt_inc_pending(new_io); +- crypt_convert_init(cc, &new_io->ctx, NULL, +- io->base_bio, sector); +- new_io->ctx.iter_in = io->ctx.iter_in; +- +- /* +- * Fragments after the first use the base_io +- * pending count. +- */ +- if (!io->base_io) +- new_io->base_io = io; +- else { +- new_io->base_io = io->base_io; +- crypt_inc_pending(io->base_io); +- crypt_dec_pending(io); +- } ++ crypt_inc_pending(io); ++ r = crypt_convert(cc, &io->ctx); ++ if (r) ++ io->error = -EIO; ++ crypt_finished = atomic_dec_and_test(&io->ctx.cc_pending); + +- io = new_io; +- } ++ /* Encryption was already finished, submit io now */ ++ if (crypt_finished) { ++ kcryptd_crypt_write_io_submit(io, 0); ++ io->sector = sector; + } + ++dec: + crypt_dec_pending(io); + } + +@@ -1481,6 +1512,9 @@ static void crypt_dtr(struct dm_target *ti) + if (!cc) + return; + ++ if (cc->write_thread) ++ kthread_stop(cc->write_thread); ++ + if (cc->io_queue) + destroy_workqueue(cc->io_queue); + if (cc->crypt_queue) +@@ -1495,8 +1529,6 @@ static void crypt_dtr(struct dm_target *ti) + mempool_destroy(cc->page_pool); + if (cc->req_pool) + mempool_destroy(cc->req_pool); +- if (cc->io_pool) +- mempool_destroy(cc->io_pool); + + if (cc->iv_gen_ops && cc->iv_gen_ops->dtr) + cc->iv_gen_ops->dtr(cc); +@@ -1688,7 +1720,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) + char dummy; + + static struct dm_arg _args[] = { +- {0, 1, "Invalid number of feature args"}, ++ {0, 3, "Invalid number of feature args"}, + }; + + if (argc < 5) { +@@ -1710,13 +1742,6 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) + if (ret < 0) + goto bad; + +- ret = -ENOMEM; +- cc->io_pool = mempool_create_slab_pool(MIN_IOS, _crypt_io_pool); +- if (!cc->io_pool) { +- ti->error = "Cannot allocate crypt io mempool"; +- goto bad; +- } +- + cc->dmreq_start = sizeof(struct ablkcipher_request); + cc->dmreq_start += crypto_ablkcipher_reqsize(any_tfm(cc)); + cc->dmreq_start = ALIGN(cc->dmreq_start, __alignof__(struct dm_crypt_request)); +@@ -1734,6 +1759,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) + iv_size_padding = crypto_ablkcipher_alignmask(any_tfm(cc)); + } + ++ ret = -ENOMEM; + cc->req_pool = mempool_create_kmalloc_pool(MIN_IOS, cc->dmreq_start + + sizeof(struct dm_crypt_request) + iv_size_padding + cc->iv_size); + if (!cc->req_pool) { +@@ -1746,7 +1772,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) + sizeof(struct dm_crypt_request) + iv_size_padding + cc->iv_size, + ARCH_KMALLOC_MINALIGN); + +- cc->page_pool = mempool_create_page_pool(MIN_POOL_PAGES, 0); ++ cc->page_pool = mempool_create_page_pool(BIO_MAX_PAGES, 0); + if (!cc->page_pool) { + ti->error = "Cannot allocate page mempool"; + goto bad; +@@ -1758,6 +1784,8 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) + goto bad; + } + ++ mutex_init(&cc->bio_alloc_lock); ++ + ret = -EINVAL; + if (sscanf(argv[2], "%llu%c", &tmpll, &dummy) != 1) { + ti->error = "Invalid iv_offset sector"; +@@ -1788,15 +1816,27 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) + if (ret) + goto bad; + +- opt_string = dm_shift_arg(&as); ++ ret = -EINVAL; ++ while (opt_params--) { ++ opt_string = dm_shift_arg(&as); ++ if (!opt_string) { ++ ti->error = "Not enough feature arguments"; ++ goto bad; ++ } + +- if (opt_params == 1 && opt_string && +- !strcasecmp(opt_string, "allow_discards")) +- ti->num_discard_bios = 1; +- else if (opt_params) { +- ret = -EINVAL; +- ti->error = "Invalid feature arguments"; +- goto bad; ++ if (!strcasecmp(opt_string, "allow_discards")) ++ ti->num_discard_bios = 1; ++ ++ else if (!strcasecmp(opt_string, "same_cpu_crypt")) ++ set_bit(DM_CRYPT_SAME_CPU, &cc->flags); ++ ++ else if (!strcasecmp(opt_string, "submit_from_crypt_cpus")) ++ set_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags); ++ ++ else { ++ ti->error = "Invalid feature arguments"; ++ goto bad; ++ } + } + } + +@@ -1807,13 +1847,28 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) + goto bad; + } + +- cc->crypt_queue = alloc_workqueue("kcryptd", +- WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM, 1); ++ if (test_bit(DM_CRYPT_SAME_CPU, &cc->flags)) ++ cc->crypt_queue = alloc_workqueue("kcryptd", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM, 1); ++ else ++ cc->crypt_queue = alloc_workqueue("kcryptd", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | WQ_UNBOUND, ++ num_online_cpus()); + if (!cc->crypt_queue) { + ti->error = "Couldn't create kcryptd queue"; + goto bad; + } + ++ init_waitqueue_head(&cc->write_thread_wait); ++ cc->write_tree = RB_ROOT; ++ ++ cc->write_thread = kthread_create(dmcrypt_write, cc, "dmcrypt_write"); ++ if (IS_ERR(cc->write_thread)) { ++ ret = PTR_ERR(cc->write_thread); ++ cc->write_thread = NULL; ++ ti->error = "Couldn't spawn write thread"; ++ goto bad; ++ } ++ wake_up_process(cc->write_thread); ++ + ti->num_flush_bios = 1; + ti->discard_zeroes_data_unsupported = true; + +@@ -1848,7 +1903,7 @@ static int crypt_map(struct dm_target *ti, struct bio *bio) + + if (bio_data_dir(io->base_bio) == READ) { + if (kcryptd_io_read(io, GFP_NOWAIT)) +- kcryptd_queue_io(io); ++ kcryptd_queue_read(io); + } else + kcryptd_queue_crypt(io); + +@@ -1860,6 +1915,7 @@ static void crypt_status(struct dm_target *ti, status_type_t type, + { + struct crypt_config *cc = ti->private; + unsigned i, sz = 0; ++ int num_feature_args = 0; + + switch (type) { + case STATUSTYPE_INFO: +@@ -1878,8 +1934,18 @@ static void crypt_status(struct dm_target *ti, status_type_t type, + DMEMIT(" %llu %s %llu", (unsigned long long)cc->iv_offset, + cc->dev->name, (unsigned long long)cc->start); + +- if (ti->num_discard_bios) +- DMEMIT(" 1 allow_discards"); ++ num_feature_args += !!ti->num_discard_bios; ++ num_feature_args += test_bit(DM_CRYPT_SAME_CPU, &cc->flags); ++ num_feature_args += test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags); ++ if (num_feature_args) { ++ DMEMIT(" %d", num_feature_args); ++ if (ti->num_discard_bios) ++ DMEMIT(" allow_discards"); ++ if (test_bit(DM_CRYPT_SAME_CPU, &cc->flags)) ++ DMEMIT(" same_cpu_crypt"); ++ if (test_bit(DM_CRYPT_NO_OFFLOAD, &cc->flags)) ++ DMEMIT(" submit_from_crypt_cpus"); ++ } + + break; + } +@@ -1976,7 +2042,7 @@ static int crypt_iterate_devices(struct dm_target *ti, + + static struct target_type crypt_target = { + .name = "crypt", +- .version = {1, 13, 0}, ++ .version = {1, 14, 0}, + .module = THIS_MODULE, + .ctr = crypt_ctr, + .dtr = crypt_dtr, +@@ -1994,15 +2060,9 @@ static int __init dm_crypt_init(void) + { + int r; + +- _crypt_io_pool = KMEM_CACHE(dm_crypt_io, 0); +- if (!_crypt_io_pool) +- return -ENOMEM; +- + r = dm_register_target(&crypt_target); +- if (r < 0) { ++ if (r < 0) + DMERR("register failed %d", r); +- kmem_cache_destroy(_crypt_io_pool); +- } + + return r; + } +@@ -2010,7 +2070,6 @@ static int __init dm_crypt_init(void) + static void __exit dm_crypt_exit(void) + { + dm_unregister_target(&crypt_target); +- kmem_cache_destroy(_crypt_io_pool); + } + + module_init(dm_crypt_init); +diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c +index cc9537e..c59f939 100644 +--- a/drivers/media/v4l2-core/videobuf2-core.c ++++ b/drivers/media/v4l2-core/videobuf2-core.c +@@ -3221,7 +3221,6 @@ EXPORT_SYMBOL_GPL(vb2_thread_start); + int vb2_thread_stop(struct vb2_queue *q) + { + struct vb2_threadio_data *threadio = q->threadio; +- struct vb2_fileio_data *fileio = q->fileio; + int err; + + if (threadio == NULL) +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 1456ea7..da983a7 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -223,6 +223,18 @@ config MFD_HI6421_PMIC + menus in order to enable them. + We communicate with the Hi6421 via memory-mapped I/O. + ++config MFD_HISI_FMC ++ tristate "Hisilicon Flash Memory Controller" ++ depends on OF ++ select MFD_CORE ++ select REGMAP_MMIO ++ default y if (HIFMC_SPI_NAND && SPI_HISI_SFC && HIFMC_NAND) ++ help ++ If you need to use SPI nor flash or SPI nand flash at same time, ++ enable this option for swap controller. if your enable ++ HIFMC_SPI_NAND and SPI_HISI_SFC at same time, this function should ++ be select. ++ + config HTC_EGPIO + bool "HTC EGPIO support" + depends on GPIOLIB && ARM +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 8bd54b1..70a68ac 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -174,6 +174,7 @@ obj-$(CONFIG_MFD_STW481X) += stw481x.o + obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o + obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o + obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o ++obj-$(CONFIG_MFD_HISI_FMC) += hisi_fmc.o + + intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o + obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o +diff --git a/drivers/mfd/hisi_fmc.c b/drivers/mfd/hisi_fmc.c +new file mode 100644 +index 0000000..43b771c +--- /dev/null ++++ b/drivers/mfd/hisi_fmc.c +@@ -0,0 +1,114 @@ ++/* HiSilicon Flash Memory Controller Driver ++ * ++ * Copyright (c) 2016 Hisilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/device.h> ++#include <linux/err.h> ++#include <linux/mfd/core.h> ++#include <linux/module.h> ++#include <linux/clk.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/regmap.h> ++#include <linux/mfd/hisi_fmc.h> ++ ++unsigned char hifmc_cs_user[HIFMC_MAX_CHIP_NUM]; ++ ++DEFINE_MUTEX(fmc_switch_mutex); ++EXPORT_SYMBOL_GPL(fmc_switch_mutex); ++ ++/* ------------------------------------------------------------------------ */ ++static const struct mfd_cell hisi_fmc_devs[] = { ++ { ++ .name = "hisi_spi_nor", ++ .of_compatible = "hisilicon,hisi-sfc", ++ }, ++ { ++ .name = "hisi_spi_nand", ++ .of_compatible = "hisilicon,hisi-spi-nand", ++ }, ++ { ++ .name = "hisi_nand", ++ .of_compatible = "hisilicon,hisi-nand", ++ }, ++}; ++ ++static int hisi_fmc_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct hisi_fmc *fmc; ++ struct resource *res; ++ ++ fmc = devm_kzalloc(&pdev->dev, sizeof(*fmc), GFP_KERNEL); ++ if (!fmc) ++ return -ENOMEM; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); ++ fmc->regbase = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(fmc->regbase)) ++ return PTR_ERR(fmc->regbase); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory"); ++ fmc->iobase = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(fmc->iobase)) ++ return PTR_ERR(fmc->iobase); ++ ++ fmc->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(fmc->clk)) ++ return PTR_ERR(fmc->clk); ++ ++ mutex_init(&fmc->lock); ++ ++ platform_set_drvdata(pdev, fmc); ++ ++ ret = mfd_add_devices(&pdev->dev, 0, hisi_fmc_devs, ++ ARRAY_SIZE(hisi_fmc_devs), NULL, 0, NULL); ++ if (ret) { ++ dev_err(&pdev->dev, "add mfd devices failed: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int hisi_fmc_remove(struct platform_device *pdev) ++{ ++ struct hisi_fmc *fmc = platform_get_drvdata(pdev); ++ ++ mfd_remove_devices(&pdev->dev); ++ mutex_destroy(&fmc->lock); ++ ++ return 0; ++} ++ ++static const struct of_device_id hisi_fmc_of_match[] = { ++ { .compatible = "hisilicon,hisi-fmc"}, ++ { } ++}; ++ ++static struct platform_driver hisi_fmc_driver = { ++ .driver = { ++ .name = "hifmc", ++ .of_match_table = hisi_fmc_of_match, ++ }, ++ .probe = hisi_fmc_probe, ++ .remove = hisi_fmc_remove, ++}; ++module_platform_driver(hisi_fmc_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("HiSilicon Flash Memory Controller Driver"); +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index bbeb451..14ba5bc 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -402,6 +402,10 @@ config TI_DAC7512 + This driver can also be built as a module. If so, the module + will be called ti_dac7512. + ++config UID_STAT ++ bool "UID based statistics tracking exported to /proc/uid_stat" ++ default n ++ + config VMWARE_BALLOON + tristate "VMware Balloon Driver" + depends on X86 && HYPERVISOR_GUEST +@@ -515,6 +519,12 @@ config VEXPRESS_SYSCFG + bus. System Configuration interface is one of the possible means + of generating transactions on this bus. + ++config UID_CPUTIME ++ tristate "Per-UID cpu time statistics" ++ depends on PROFILING ++ help ++ Per UID based cpu time statistics exported to /proc/uid_cputime ++ + source "drivers/misc/c2port/Kconfig" + source "drivers/misc/eeprom/Kconfig" + source "drivers/misc/cb710/Kconfig" +@@ -528,4 +538,5 @@ source "drivers/misc/mic/Kconfig" + source "drivers/misc/genwqe/Kconfig" + source "drivers/misc/echo/Kconfig" + source "drivers/misc/cxl/Kconfig" ++source "drivers/misc/hi_reg/Kconfig" + endmenu +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index 7d5c4cd..ecf313b 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -34,6 +34,7 @@ obj-$(CONFIG_ISL29020) += isl29020.o + obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o + obj-$(CONFIG_DS1682) += ds1682.o + obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o ++obj-$(CONFIG_UID_STAT) += uid_stat.o + obj-$(CONFIG_C2PORT) += c2port/ + obj-$(CONFIG_HMC6352) += hmc6352.o + obj-y += eeprom/ +@@ -56,3 +57,5 @@ obj-$(CONFIG_GENWQE) += genwqe/ + obj-$(CONFIG_ECHO) += echo/ + obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o + obj-$(CONFIG_CXL_BASE) += cxl/ ++obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o ++obj-y += hi_reg/ +diff --git a/drivers/misc/hi_reg/Kconfig b/drivers/misc/hi_reg/Kconfig +new file mode 100644 +index 0000000..76c8741 +--- /dev/null ++++ b/drivers/misc/hi_reg/Kconfig +@@ -0,0 +1,13 @@ ++menu "hisi 'himm/himd.l/himc'support" ++ ++config HISI_REG ++ tristate "hisi \"himd\" \"himm\" \"himc\" support" ++ default y if (ARCH_HI3519) ++ default y if (ARCH_HI3519V101) ++ help ++ This selects the Hisilicon io register access support ++ If you want use "himd" "himm" "himc" command, ++ Say Y or M here. ++ ++ Default is Y. ++endmenu +diff --git a/drivers/misc/hi_reg/Makefile b/drivers/misc/hi_reg/Makefile +new file mode 100644 +index 0000000..5c66458 +--- /dev/null ++++ b/drivers/misc/hi_reg/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_HISI_REG) += hi_reg.o +diff --git a/drivers/misc/hi_reg/hi_reg.c b/drivers/misc/hi_reg/hi_reg.c +new file mode 100644 +index 0000000..3dbdfd1 +--- /dev/null ++++ b/drivers/misc/hi_reg/hi_reg.c +@@ -0,0 +1,164 @@ ++#include <mach/io.h> ++#include <linux/io.h> ++ ++#include <asm/uaccess.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/fs.h> ++#include <linux/miscdevice.h> ++ ++#include "hi_reg_user.h" ++ ++static int hi_reg_open(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static int hi_reg_release(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static int hi_reg_read(struct file *file, char __user *buf, ++ size_t count, loff_t *f_pos) ++{ ++ return 0; ++} ++ ++static int hi_reg_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *f_pos) ++{ ++ return 0; ++} ++ ++ ++static long hi_reg_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ void __iomem *virt_addr; ++ struct hi_reg_handle handle; ++ int valid_addr = 0; ++ unsigned int new_value = 0; ++ ++ if (copy_from_user((void *)&handle, (void *)arg, ++ sizeof(struct hi_reg_handle))) { ++ printk(KERN_ERR"hi reg copy from user error!\n"); ++ return -EFAULT; ++ } ++ ++ /* phys addr must be aligned by 4 bytes */ ++ if (!handle.phys_addr || (handle.phys_addr & (sizeof(u32) - 1))) { ++ printk(KERN_ERR"phys addr not aligned by 4 bytes,which is [0x%08x]!\n", ++ handle.phys_addr); ++ return -EFAULT; ++ } ++ ++#ifdef CONFIG_ARCH_HI3516AV200 ++ /* phys addr must be in IO_ADDRESS */ ++ if ((handle.phys_addr >= HI3516AV200_IOCH3_PHYS) ++ && (handle.phys_addr < HI3516AV200_IOCH3_PHYS + HI3516AV200_IOCH3_SIZE)) { ++ valid_addr = 1; ++ } else if ((handle.phys_addr >= HI3516AV200_IOCH2_PHYS) ++ && (handle.phys_addr < HI3516AV200_IOCH2_PHYS + HI3516AV200_IOCH2_SIZE)) { ++ valid_addr = 1; ++ } else if ((handle.phys_addr >= HI3516AV200_IOCH1_PHYS) ++ && (handle.phys_addr < HI3516AV200_IOCH1_PHYS + HI3516AV200_IOCH1_SIZE)) { ++ valid_addr = 1; ++ } ++#endif ++ ++#if defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) ++ /* phys addr must be in IO_ADDRESS */ ++ if ((handle.phys_addr >= HI3519_IOCH3_PHYS) ++ && (handle.phys_addr < HI3519_IOCH3_PHYS + HI3519_IOCH3_SIZE)) { ++ valid_addr = 1; ++ } else if ((handle.phys_addr >= HI3519_IOCH2_PHYS) ++ && (handle.phys_addr < HI3519_IOCH2_PHYS + HI3519_IOCH2_SIZE)) { ++ valid_addr = 1; ++ } else if ((handle.phys_addr >= HI3519_IOCH1_PHYS) ++ && (handle.phys_addr < HI3519_IOCH1_PHYS + HI3519_IOCH1_SIZE)) { ++ valid_addr = 1; ++ } ++#endif ++ ++ if (!valid_addr) { ++ printk(KERN_ERR"invalid addr, which is [0x%08x]!\n", ++ handle.phys_addr); ++ return -EFAULT; ++ } ++ if ('R' == _IOC_TYPE(cmd)) { ++ switch (_IOC_NR(cmd)) { ++ case _IOC_NR(HI_REG_READ): ++ virt_addr = (void *)IO_ADDRESS(handle.phys_addr); ++ new_value = hi_readl(virt_addr); ++ handle.data = (void *)new_value; ++ if (copy_to_user((void *)arg, (void *)&handle, ++ sizeof(struct hi_reg_handle))) { ++ printk(KERN_ERR"hi reg copy to user error!\n"); ++ return -EFAULT; ++ } ++ break; ++ ++ case _IOC_NR(HI_REG_WRITE): ++ virt_addr = (void *)IO_ADDRESS(handle.phys_addr); ++ ++ if (sizeof(u32) == handle.size) { ++ hi_writel((u32)handle.data, virt_addr); ++ } else if (sizeof(u16) == handle.size) { ++ hi_writew(((u32)handle.data & 0xFFFF), virt_addr); ++ } else if (sizeof(u8) == handle.size) { ++ hi_writeb(((u32)handle.data & 0xFF), virt_addr); ++ } else { ++ printk(KERN_ERR"%d, write size should be 1, 2 or 4!\n", __LINE__); ++ } ++ break; ++ ++ default: ++ printk(KERN_ERR"%d, hi reg unknow cmd!\n", __LINE__); ++ break; ++ } ++ } else { ++ printk(KERN_ERR"%d, hi reg unknow cmd!\n", __LINE__); ++ } ++ return 0; ++} ++ ++static const struct file_operations hi_reg_fops = { ++ .owner = THIS_MODULE, ++ .open = hi_reg_open, ++ .release = hi_reg_release, ++ .unlocked_ioctl = hi_reg_ioctl, ++ .write = hi_reg_write, ++ .read = hi_reg_read ++}; ++ ++static struct miscdevice hi_reg_dev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .fops = &hi_reg_fops, ++ .name = "hi_reg" ++}; ++ ++static int __init hi_reg_init(void) ++{ ++ int ret; ++ ret = misc_register(&hi_reg_dev); ++ if (ret) { ++ printk("register hi_reg device failed!"); ++ } ++ return ret; ++} ++ ++void __exit hi_reg_exit(void) ++{ ++ misc_deregister(&hi_reg_dev); ++} ++#ifndef MODULE ++subsys_initcall(hi_reg_init); ++#else ++module_init(hi_reg_init); ++#endif ++module_exit(hi_reg_exit); ++ ++MODULE_DESCRIPTION("Driver for himd/himm/himc"); ++MODULE_AUTHOR("ou jinsong"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/misc/hi_reg/hi_reg_user.h b/drivers/misc/hi_reg/hi_reg_user.h +new file mode 100644 +index 0000000..21e2e76 +--- /dev/null ++++ b/drivers/misc/hi_reg/hi_reg_user.h +@@ -0,0 +1,21 @@ ++#include <linux/ioctl.h> ++ ++#ifndef __HI_REG_USER_H__ ++#define __HI_REG_USER_H__ ++ ++struct hi_reg_handle { ++ unsigned int phys_addr; ++ unsigned int size; ++ void *data; ++ int flags; ++}; ++ ++ ++#define HI_REG_BASE 'R' ++ ++#define HI_REG_READ \ ++ _IOW(HI_REG_BASE, 1, struct hi_reg_handle) ++#define HI_REG_WRITE \ ++ _IOW(HI_REG_BASE, 2, struct hi_reg_handle) ++ ++#endif /* __HI_REG_USER_H__ */ +diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c +new file mode 100644 +index 0000000..43298a4 +--- /dev/null ++++ b/drivers/misc/uid_cputime.c +@@ -0,0 +1,253 @@ ++/* drivers/misc/uid_cputime.c ++ * ++ * Copyright (C) 2014 - 2015 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/atomic.h> ++#include <linux/err.h> ++#include <linux/hashtable.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/list.h> ++#include <linux/proc_fs.h> ++#include <linux/profile.h> ++#include <linux/sched.h> ++#include <linux/seq_file.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++ ++#define UID_HASH_BITS 10 ++DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); ++ ++static DEFINE_MUTEX(uid_lock); ++static struct proc_dir_entry *parent; ++ ++struct uid_entry { ++ uid_t uid; ++ cputime_t utime; ++ cputime_t stime; ++ cputime_t active_utime; ++ cputime_t active_stime; ++ unsigned long long active_power; ++ unsigned long long power; ++ struct hlist_node hash; ++}; ++ ++static struct uid_entry *find_uid_entry(uid_t uid) ++{ ++ struct uid_entry *uid_entry; ++ hash_for_each_possible(hash_table, uid_entry, hash, uid) { ++ if (uid_entry->uid == uid) ++ return uid_entry; ++ } ++ return NULL; ++} ++ ++static struct uid_entry *find_or_register_uid(uid_t uid) ++{ ++ struct uid_entry *uid_entry; ++ ++ uid_entry = find_uid_entry(uid); ++ if (uid_entry) ++ return uid_entry; ++ ++ uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC); ++ if (!uid_entry) ++ return NULL; ++ ++ uid_entry->uid = uid; ++ ++ hash_add(hash_table, &uid_entry->hash, uid); ++ ++ return uid_entry; ++} ++ ++static int uid_stat_show(struct seq_file *m, void *v) ++{ ++ struct uid_entry *uid_entry; ++ struct task_struct *task, *temp; ++ cputime_t utime; ++ cputime_t stime; ++ unsigned long bkt; ++ ++ mutex_lock(&uid_lock); ++ ++ hash_for_each(hash_table, bkt, uid_entry, hash) { ++ uid_entry->active_stime = 0; ++ uid_entry->active_utime = 0; ++ uid_entry->active_power = 0; ++ } ++ ++ read_lock(&tasklist_lock); ++ do_each_thread(temp, task) { ++ uid_entry = find_or_register_uid(from_kuid_munged( ++ current_user_ns(), task_uid(task))); ++ if (!uid_entry) { ++ read_unlock(&tasklist_lock); ++ mutex_unlock(&uid_lock); ++ pr_err("%s: failed to find the uid_entry for uid %d\n", ++ __func__, from_kuid_munged(current_user_ns(), ++ task_uid(task))); ++ return -ENOMEM; ++ } ++ /* if this task is exiting, we have already accounted for the ++ * time and power. ++ */ ++ if (task->cpu_power == ULLONG_MAX) ++ continue; ++ task_cputime_adjusted(task, &utime, &stime); ++ uid_entry->active_utime += utime; ++ uid_entry->active_stime += stime; ++ uid_entry->active_power += task->cpu_power; ++ } while_each_thread(temp, task); ++ read_unlock(&tasklist_lock); ++ ++ hash_for_each(hash_table, bkt, uid_entry, hash) { ++ cputime_t total_utime = uid_entry->utime + ++ uid_entry->active_utime; ++ cputime_t total_stime = uid_entry->stime + ++ uid_entry->active_stime; ++ unsigned long long total_power = uid_entry->power + ++ uid_entry->active_power; ++ seq_printf(m, "%d: %llu %llu %llu\n", uid_entry->uid, ++ (unsigned long long)jiffies_to_msecs( ++ cputime_to_jiffies(total_utime)) * USEC_PER_MSEC, ++ (unsigned long long)jiffies_to_msecs( ++ cputime_to_jiffies(total_stime)) * USEC_PER_MSEC, ++ total_power); ++ } ++ ++ mutex_unlock(&uid_lock); ++ return 0; ++} ++ ++static int uid_stat_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, uid_stat_show, PDE_DATA(inode)); ++} ++ ++static const struct file_operations uid_stat_fops = { ++ .open = uid_stat_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int uid_remove_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, NULL, NULL); ++} ++ ++static ssize_t uid_remove_write(struct file *file, ++ const char __user *buffer, size_t count, loff_t *ppos) ++{ ++ struct uid_entry *uid_entry; ++ struct hlist_node *tmp; ++ char uids[128]; ++ char *start_uid, *end_uid = NULL; ++ long int uid_start = 0, uid_end = 0; ++ ++ if (count >= sizeof(uids)) ++ count = sizeof(uids) - 1; ++ ++ if (copy_from_user(uids, buffer, count)) ++ return -EFAULT; ++ ++ uids[count] = '\0'; ++ end_uid = uids; ++ start_uid = strsep(&end_uid, "-"); ++ ++ if (!start_uid || !end_uid) ++ return -EINVAL; ++ ++ if (kstrtol(start_uid, 10, &uid_start) != 0 || ++ kstrtol(end_uid, 10, &uid_end) != 0) { ++ return -EINVAL; ++ } ++ ++ mutex_lock(&uid_lock); ++ ++ for (; uid_start <= uid_end; uid_start++) { ++ hash_for_each_possible_safe(hash_table, uid_entry, tmp, ++ hash, uid_start) { ++ hash_del(&uid_entry->hash); ++ kfree(uid_entry); ++ } ++ } ++ ++ mutex_unlock(&uid_lock); ++ return count; ++} ++ ++static const struct file_operations uid_remove_fops = { ++ .open = uid_remove_open, ++ .release = single_release, ++ .write = uid_remove_write, ++}; ++ ++static int process_notifier(struct notifier_block *self, ++ unsigned long cmd, void *v) ++{ ++ struct task_struct *task = v; ++ struct uid_entry *uid_entry; ++ cputime_t utime, stime; ++ uid_t uid; ++ ++ if (!task) ++ return NOTIFY_OK; ++ ++ mutex_lock(&uid_lock); ++ uid = from_kuid_munged(current_user_ns(), task_uid(task)); ++ uid_entry = find_or_register_uid(uid); ++ if (!uid_entry) { ++ pr_err("%s: failed to find uid %d\n", __func__, uid); ++ goto exit; ++ } ++ ++ task_cputime_adjusted(task, &utime, &stime); ++ uid_entry->utime += utime; ++ uid_entry->stime += stime; ++ uid_entry->power += task->cpu_power; ++ task->cpu_power = ULLONG_MAX; ++ ++exit: ++ mutex_unlock(&uid_lock); ++ return NOTIFY_OK; ++} ++ ++static struct notifier_block process_notifier_block = { ++ .notifier_call = process_notifier, ++}; ++ ++static int __init proc_uid_cputime_init(void) ++{ ++ hash_init(hash_table); ++ ++ parent = proc_mkdir("uid_cputime", NULL); ++ if (!parent) { ++ pr_err("%s: failed to create proc entry\n", __func__); ++ return -ENOMEM; ++ } ++ ++ proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops, ++ NULL); ++ ++ proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops, ++ NULL); ++ ++ profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block); ++ ++ return 0; ++} ++ ++early_initcall(proc_uid_cputime_init); +diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c +new file mode 100644 +index 0000000..4766c1f +--- /dev/null ++++ b/drivers/misc/uid_stat.c +@@ -0,0 +1,152 @@ ++/* drivers/misc/uid_stat.c ++ * ++ * Copyright (C) 2008 - 2009 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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 <asm/atomic.h> ++ ++#include <linux/err.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/list.h> ++#include <linux/proc_fs.h> ++#include <linux/seq_file.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/stat.h> ++#include <linux/uid_stat.h> ++#include <net/activity_stats.h> ++ ++static DEFINE_SPINLOCK(uid_lock); ++static LIST_HEAD(uid_list); ++static struct proc_dir_entry *parent; ++ ++struct uid_stat { ++ struct list_head link; ++ uid_t uid; ++ atomic_t tcp_rcv; ++ atomic_t tcp_snd; ++}; ++ ++static struct uid_stat *find_uid_stat(uid_t uid) { ++ struct uid_stat *entry; ++ ++ list_for_each_entry(entry, &uid_list, link) { ++ if (entry->uid == uid) { ++ return entry; ++ } ++ } ++ return NULL; ++} ++ ++static int uid_stat_atomic_int_show(struct seq_file *m, void *v) ++{ ++ unsigned int bytes; ++ atomic_t *counter = m->private; ++ ++ bytes = (unsigned int) (atomic_read(counter) + INT_MIN); ++ return seq_printf(m, "%u\n", bytes); ++} ++ ++static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode)); ++} ++ ++static const struct file_operations uid_stat_read_atomic_int_fops = { ++ .open = uid_stat_read_atomic_int_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++ ++/* Create a new entry for tracking the specified uid. */ ++static struct uid_stat *create_stat(uid_t uid) { ++ struct uid_stat *new_uid; ++ /* Create the uid stat struct and append it to the list. */ ++ new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC); ++ if (!new_uid) ++ return NULL; ++ ++ new_uid->uid = uid; ++ /* Counters start at INT_MIN, so we can track 4GB of network traffic. */ ++ atomic_set(&new_uid->tcp_rcv, INT_MIN); ++ atomic_set(&new_uid->tcp_snd, INT_MIN); ++ ++ list_add_tail(&new_uid->link, &uid_list); ++ return new_uid; ++} ++ ++static void create_stat_proc(struct uid_stat *new_uid) ++{ ++ char uid_s[32]; ++ struct proc_dir_entry *entry; ++ sprintf(uid_s, "%d", new_uid->uid); ++ entry = proc_mkdir(uid_s, parent); ++ ++ /* Keep reference to uid_stat so we know what uid to read stats from. */ ++ proc_create_data("tcp_snd", S_IRUGO, entry, ++ &uid_stat_read_atomic_int_fops, &new_uid->tcp_snd); ++ ++ proc_create_data("tcp_rcv", S_IRUGO, entry, ++ &uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv); ++} ++ ++static struct uid_stat *find_or_create_uid_stat(uid_t uid) ++{ ++ struct uid_stat *entry; ++ unsigned long flags; ++ spin_lock_irqsave(&uid_lock, flags); ++ entry = find_uid_stat(uid); ++ if (entry) { ++ spin_unlock_irqrestore(&uid_lock, flags); ++ return entry; ++ } ++ entry = create_stat(uid); ++ spin_unlock_irqrestore(&uid_lock, flags); ++ if (entry) ++ create_stat_proc(entry); ++ return entry; ++} ++ ++int uid_stat_tcp_snd(uid_t uid, int size) { ++ struct uid_stat *entry; ++ activity_stats_update(); ++ entry = find_or_create_uid_stat(uid); ++ if (!entry) ++ return -1; ++ atomic_add(size, &entry->tcp_snd); ++ return 0; ++} ++ ++int uid_stat_tcp_rcv(uid_t uid, int size) { ++ struct uid_stat *entry; ++ activity_stats_update(); ++ entry = find_or_create_uid_stat(uid); ++ if (!entry) ++ return -1; ++ atomic_add(size, &entry->tcp_rcv); ++ return 0; ++} ++ ++static int __init uid_stat_init(void) ++{ ++ parent = proc_mkdir("uid_stat", NULL); ++ if (!parent) { ++ pr_err("uid_stat: failed to create proc entry\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++__initcall(uid_stat_init); +diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig +index 5562308..79d8212 100644 +--- a/drivers/mmc/card/Kconfig ++++ b/drivers/mmc/card/Kconfig +@@ -50,6 +50,15 @@ config MMC_BLOCK_BOUNCE + + If unsure, say Y here. + ++config MMC_BLOCK_DEFERRED_RESUME ++ bool "Deferr MMC layer resume until I/O is requested" ++ depends on MMC_BLOCK ++ default n ++ help ++ Say Y here to enable deferred MMC resume until I/O ++ is requested. This will reduce overall resume latency and ++ save power when theres an SD card inserted but not being used. ++ + config SDIO_UART + tristate "SDIO UART/GPS class support" + depends on TTY +diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c +index 10ecc0a..fd8f4b3 100644 +--- a/drivers/mmc/card/block.c ++++ b/drivers/mmc/card/block.c +@@ -36,6 +36,9 @@ + #include <linux/compat.h> + #include <linux/pm_runtime.h> + ++#define CREATE_TRACE_POINTS ++#include <trace/events/mmc.h> ++ + #include <linux/mmc/ioctl.h> + #include <linux/mmc/card.h> + #include <linux/mmc/host.h> +@@ -136,6 +139,7 @@ static inline int mmc_blk_part_switch(struct mmc_card *card, + struct mmc_blk_data *md); + static int get_card_status(struct mmc_card *card, u32 *status, int retries); + ++#if 0 + static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq) + { + struct mmc_packed *packed = mqrq->packed; +@@ -148,6 +152,7 @@ static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq) + packed->retries = 0; + packed->blocks = 0; + } ++#endif + + static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) + { +@@ -166,11 +171,7 @@ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) + + static inline int mmc_get_devidx(struct gendisk *disk) + { +- int devmaj = MAJOR(disk_devt(disk)); +- int devidx = MINOR(disk_devt(disk)) / perdev_minors; +- +- if (!devmaj) +- devidx = disk->first_minor / perdev_minors; ++ int devidx = disk->first_minor / perdev_minors; + return devidx; + } + +@@ -427,9 +428,11 @@ static int ioctl_do_sanitize(struct mmc_card *card) + pr_debug("%s: %s - SANITIZE IN PROGRESS...\n", + mmc_hostname(card->host), __func__); + ++ trace_mmc_blk_erase_start(EXT_CSD_SANITIZE_START, 0, 0); + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_SANITIZE_START, 1, + MMC_SANITIZE_REQ_TIMEOUT); ++ trace_mmc_blk_erase_end(EXT_CSD_SANITIZE_START, 0, 0); + + if (err) + pr_err("%s: %s - EXT_CSD_SANITIZE_START failed. err=%d\n", +@@ -668,6 +671,7 @@ static inline int mmc_blk_part_switch(struct mmc_card *card, + return 0; + } + ++#if 0 + static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) + { + int err; +@@ -722,6 +726,7 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) + + return result; + } ++#endif + + static int get_card_status(struct mmc_card *card, u32 *status, int retries) + { +@@ -852,18 +857,22 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error, + req->rq_disk->disk_name, "timed out", name, status); + + /* If the status cmd initially failed, retry the r/w cmd */ +- if (!status_valid) ++ if (!status_valid) { ++ pr_err("%s: status not valid, retrying timeout\n", req->rq_disk->disk_name); + return ERR_RETRY; +- ++ } + /* + * If it was a r/w cmd crc error, or illegal command + * (eg, issued in wrong state) then retry - we should + * have corrected the state problem above. + */ +- if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND)) ++ if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND)) { ++ pr_err("%s: command error, retrying timeout\n", req->rq_disk->disk_name); + return ERR_RETRY; ++ } + + /* Otherwise abort the command */ ++ pr_err("%s: not retrying timeout\n", req->rq_disk->disk_name); + return ERR_ABORT; + + default: +@@ -1003,6 +1012,14 @@ static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host, + return -EEXIST; + + md->reset_done |= type; ++ ++#if !defined(CONFIG_ARCH_HI3559) && !defined(CONFIG_ARCH_HI3556) ++ /*Step1: try to software reset*/ ++ if (mmc_sw_reset(host) == 0) ++ return 0; ++#endif ++ ++ /*Step2: try to hardware reset*/ + err = mmc_hw_reset(host); + /* Ensure we switch back to the correct partition */ + if (err != -EOPNOTSUPP) { +@@ -1195,13 +1212,11 @@ static inline void mmc_apply_rel_rw(struct mmc_blk_request *brq, + R1_CC_ERROR | /* Card controller error */ \ + R1_ERROR) /* General/unknown error */ + +-static int mmc_blk_err_check(struct mmc_card *card, +- struct mmc_async_req *areq) ++int mmc_blk_err_check(struct mmc_card *card, ++ struct mmc_req_mrq *req_mrq) + { +- struct mmc_queue_req *mq_mrq = container_of(areq, struct mmc_queue_req, +- mmc_active); +- struct mmc_blk_request *brq = &mq_mrq->brq; +- struct request *req = mq_mrq->req; ++ struct mmc_blk_request *brq = &req_mrq->brq; ++ struct request *req = req_mrq->req; + int ecc_err = 0, gen_err = 0; + + /* +@@ -1287,19 +1302,13 @@ static int mmc_blk_err_check(struct mmc_card *card, + if (!brq->data.bytes_xfered) + return MMC_BLK_RETRY; + +- if (mmc_packed_cmd(mq_mrq->cmd_type)) { +- if (unlikely(brq->data.blocks << 9 != brq->data.bytes_xfered)) +- return MMC_BLK_PARTIAL; +- else +- return MMC_BLK_SUCCESS; +- } +- + if (blk_rq_bytes(req) != brq->data.bytes_xfered) + return MMC_BLK_PARTIAL; + + return MMC_BLK_SUCCESS; + } + ++#if 0 + static int mmc_blk_packed_err_check(struct mmc_card *card, + struct mmc_async_req *areq) + { +@@ -2021,61 +2030,470 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) + + return 0; + } ++#endif ++ ++static int mmc_send_reqs(struct mmc_req_mrq *req_mrq, ++ struct mmc_card *card, ++ struct mmc_queue *mq) ++{ ++ u32 readcmd, writecmd; ++ struct mmc_queue_req *mqrq = mq->mqrq_cur; ++ struct mmc_blk_request *brq; ++ struct request *req = req_mrq->req; ++ struct mmc_blk_data *md = mq->data; ++ bool do_data_tag; ++ int err = 0; ++ ++ /* ++ * Reliable writes are used to implement Forced Unit Access and ++ * REQ_META accesses, and are supported only on MMCs. ++ * ++ * XXX: this really needs a good explanation of why REQ_META ++ * is treated special. ++ */ ++ bool do_rel_wr = ((req->cmd_flags & REQ_FUA) || ++ (req->cmd_flags & REQ_META)) && ++ (rq_data_dir(req) == WRITE) && ++ (md->flags & MMC_BLK_REL_WR); ++ ++ /* ++ * prepare for each request ++ */ ++ mqrq->req = req; ++ brq = &req_mrq->brq; ++ ++ brq->mrq.cmd = &brq->cmd; ++ brq->mrq.data = &brq->data; ++ brq->cmd.data = &brq->data; ++ brq->cmd.mrq = &brq->mrq; ++ ++ brq->cmd.arg = blk_rq_pos(req); ++ if (!mmc_card_blockaddr(card)) ++ brq->cmd.arg <<= 9; ++ brq->cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; ++ brq->cmd.flags |= MMC_CMD_TYPE_RW; ++ brq->data.blksz = 512; ++ brq->stop.opcode = MMC_STOP_TRANSMISSION; ++ brq->stop.arg = 0; ++ brq->data.blocks = blk_rq_sectors(req); ++ ++ /* ++ * The block layer doesn't support all sector count ++ * restrictions, so we need to be prepared for too big ++ * requests. ++ */ ++ if (brq->data.blocks > card->host->max_blk_count) ++ brq->data.blocks = card->host->max_blk_count; ++ ++ if (brq->data.blocks > 1) { ++ /* ++ * Some controllers have HW issues while operating ++ * in multiple I/O mode ++ */ ++ if (card->host->ops->multi_io_quirk) ++ brq->data.blocks = card->host->ops->multi_io_quirk(card, ++ (rq_data_dir(req) == READ) ? ++ MMC_DATA_READ : MMC_DATA_WRITE, ++ brq->data.blocks); ++ } ++ ++ if (brq->data.blocks > 1 || do_rel_wr) { ++ /* SPI multiblock writes terminate using a special ++ * token, not a STOP_TRANSMISSION request. ++ */ ++ if (!mmc_host_is_spi(card->host) || ++ rq_data_dir(req) == READ) ++ brq->mrq.stop = &brq->stop; ++ readcmd = MMC_READ_MULTIPLE_BLOCK; ++ writecmd = MMC_WRITE_MULTIPLE_BLOCK; ++ } else { ++ brq->mrq.stop = NULL; ++ readcmd = MMC_READ_SINGLE_BLOCK; ++ writecmd = MMC_WRITE_BLOCK; ++ } ++ if (rq_data_dir(req) == READ) { ++ brq->cmd.opcode = readcmd; ++ brq->data.flags |= MMC_DATA_READ; ++ if (brq->mrq.stop) ++ brq->stop.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | ++ MMC_CMD_AC; ++ } else { ++ brq->cmd.opcode = writecmd; ++ brq->data.flags |= MMC_DATA_WRITE; ++ if (brq->mrq.stop) ++ brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | ++ MMC_CMD_AC; ++ } ++ ++ if (do_rel_wr) ++ mmc_apply_rel_rw(brq, card, req); ++ /* ++ * When 4KB native sector is enabled, only 8 blocks ++ * multiple read or write is allowed ++ */ ++ if ((brq->data.blocks & 0x07) && ++ (card->ext_csd.data_sector_size == 4096)) { ++ pr_err("%s: Transfer size is not 4KB sector size aligned\n", ++ req->rq_disk->disk_name); ++ err = -EMEDIUMTYPE; ++ goto prep_err; ++ } ++ ++ /* ++ * Data tag is used only during writing meta data to speed ++ * up write and any subsequent read of this meta data ++ */ ++ do_data_tag = (card->ext_csd.data_tag_unit_size) && ++ (req->cmd_flags & REQ_META) && ++ (rq_data_dir(req) == WRITE) && ++ ((brq->data.blocks * brq->data.blksz) >= ++ card->ext_csd.data_tag_unit_size); ++ ++ /* ++ * Pre-defined multi-block transfers are preferable to ++ * open ended-ones (and necessary for reliable writes). ++ * However, it is not sufficient to just send CMD23, ++ * and avoid the final CMD12, as on an error condition ++ * CMD12 (stop) needs to be sent anyway. This, coupled ++ * with Auto-CMD23 enhancements provided by some ++ * hosts, means that the complexity of dealing ++ * with this is best left to the host. If CMD23 is ++ * supported by card and host, we'll fill sbc in and let ++ * the host deal with handling it correctly. This means ++ * that for hosts that don't expose MMC_CAP_CMD23, no ++ * change of behavior will be observed. ++ * ++ * N.B: Some MMC cards experience perf degradation. ++ * We'll avoid using CMD23-bounded multiblock writes for ++ * these, while retaining features like reliable writes. ++ */ ++ if ((md->flags & MMC_BLK_CMD23) && mmc_op_multi(brq->cmd.opcode) && ++ (do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23) || ++ do_data_tag)) { ++ brq->sbc.opcode = MMC_SET_BLOCK_COUNT; ++ brq->sbc.arg = brq->data.blocks | ++ (do_rel_wr ? (1 << 31) : 0) | ++ (do_data_tag ? (1 << 29) : 0); ++ brq->sbc.flags = MMC_RSP_R1 | MMC_CMD_AC; ++ brq->mrq.sbc = &brq->sbc; ++ } ++ ++ mmc_set_data_timeout(&brq->data, card); ++ ++ mqrq->sg = req_mrq->sg; ++ brq->data.sg = req_mrq->sg; ++ brq->data.sg_len = mmc_queue_map_sg(mq, mqrq); ++ ++ /* ++ * Adjust the sg list so it is the same size as the ++ * request. ++ */ ++ if (brq->data.blocks != blk_rq_sectors(req)) { ++ int i, data_size = brq->data.blocks << 9; ++ struct scatterlist *sg; ++ ++ for_each_sg(brq->data.sg, sg, brq->data.sg_len, i) { ++ data_size -= sg->length; ++ if (data_size <= 0) { ++ sg->length += data_size; ++ i++; ++ break; ++ } ++ } ++ brq->data.sg_len = i; ++ } ++ ++ err = __mmc_start_data_req(card->host, &brq->mrq); ++ ++ if (err) { ++ mmc_trace(3, "send mrq fail!\n"); ++ goto prep_err; ++ } else { ++ req_mrq->wr_pos = brq->mrq.wr_pos; ++ mmc_trace(3, "write pos is %d", req_mrq->wr_pos); ++ return 0; ++ } ++prep_err: ++ return err; ++} ++ ++/* ++ * mmc_wait_reqs_done() - wait multi packet intr to wake up ++ * @host: MMC host to prepare the command. ++ * Blocks MMC context till host controller will ack intr of multi-pactet ++ */ ++void mmc_wait_reqs_done(struct mmc_host *host) ++{ ++ struct mmc_context_info *context_info = &host->context_info; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&context_info->lock, flags); ++ context_info->is_waiting_last_req = true; ++ spin_unlock_irqrestore(&context_info->lock, flags); ++ ++ mmc_trace(3, "wait intr..."); ++ wait_event_interruptible(context_info->wait, ++ (context_info->is_done_rcv || ++ context_info->is_new_req)); ++ mmc_trace(3, "intr wake up"); ++ ++ spin_lock_irqsave(&context_info->lock, flags); ++ context_info->is_waiting_last_req = false; ++ context_info->is_done_rcv = false; ++ context_info->is_new_req = false; ++ spin_unlock_irqrestore(&context_info->lock, flags); ++} ++ ++#define MMC_CMD_SUCCESS (0) ++#define MMC_CMD_ABORT (1) ++/* ++ * mmc_req_retrieve() - retrieve all of request that has completed ++ * @mq: request queue that has attached to card. ++ * @host: MMC host to prepare the command. ++ * @drain: retrieve all request if true. ++ * Blocks MMC context till host controller will ack end of data request ++ * execution or new request notification arrives from the block layer. ++ * Handles command retries. ++ * ++ * Returns enum mmc_blk_status of checking errors. ++ */ ++int mmc_blk_retrieve_reqs(struct mmc_queue *mq, ++ struct mmc_host *host, bool drain) ++{ ++ struct mmc_blk_data *md = mq->data; ++ struct mmc_card *card = md->queue.card; ++ struct request *req; ++ struct mmc_blk_request *brq; ++ struct mmc_req_mrq *req_mrq; ++ struct mmc_xmited *xmited = &mq->xmited; ++ enum mmc_blk_status status = MMC_BLK_SUCCESS; ++ bool res = false; ++ int rd_pos, type; ++ unsigned int cmd_flags; ++ ++ while (!mmc_xmited_empty(xmited)) { ++ rd_pos = host->ops->get_rd(host); ++ req_mrq = xmited->rq_buf[xmited->start]; ++ mmc_trace(3, "remain %d, cmd ptr %d, read ptr %d", ++ xmited->used, req_mrq->wr_pos, rd_pos); ++ mmc_trace(3, "status = %d", host->status); ++ if (!host->status && (req_mrq->wr_pos == rd_pos)) { ++ mmc_trace(3, "no new request complete"); ++ if (drain) { ++ if (!(req_mrq->brq.cmd.flags & MMC_CMD_NON_BLOCKING)) ++ mmc_wait_reqs_done(host); ++ continue; ++ } else { ++ break; ++ } ++ } ++ req = req_mrq->req; ++ brq = &req_mrq->brq; ++ ++ if (brq->cmd.resp[0] & CMD_ERRORS || brq->cmd.error || brq->data.error) { ++ pr_info("%s: req failed (CMD%u) cmd %d data %d cmd resp:%x\n", ++ mmc_hostname(host), brq->cmd.opcode, ++ brq->cmd.error, brq->data.error, brq->cmd.resp[0]); ++ status = xmited->err_check(card, req_mrq); ++ } else { ++ host->ops->post_req(host, &brq->mrq, 0); ++ res = blk_end_request(req, 0, brq->data.bytes_xfered); ++ mmc_put_rbuf(xmited, req_mrq); ++ if (res) { ++ pr_info("%s BUG d_totel %d d_xfer %d\n", ++ __func__, blk_rq_bytes(req), ++ brq->data.bytes_xfered); ++ blk_requeue_request(mq->queue, req); ++ } ++ } ++ ++ type = rq_data_dir(req) == READ ? ++ MMC_BLK_READ : MMC_BLK_WRITE; ++ switch (status) { ++ case MMC_BLK_SUCCESS: ++ case MMC_BLK_PARTIAL: ++ /* ++ * A block was successfully transferred. ++ */ ++ mmc_blk_reset_success(md, type); ++ continue; ++ case MMC_BLK_CMD_ERR: ++ if (!mmc_blk_reset(md, card->host, type)) ++ goto cmd_restart; ++ goto cmd_abort; ++ case MMC_BLK_RETRY: ++ ++ /* Fall through */ ++ case MMC_BLK_ABORT: ++ if (!mmc_blk_reset(md, card->host, type) && req->retries++ < 5) ++ goto cmd_restart; ++ goto cmd_abort; ++ case MMC_BLK_ECC_ERR: ++ case MMC_BLK_DATA_ERR: { ++ int error; ++ ++ error = mmc_blk_reset(md, card->host, type); ++ if (!error) ++ goto cmd_restart; ++ if (error == -ENODEV) ++ goto cmd_abort; ++ } ++ /* Fall through */ ++ case MMC_BLK_NOMEDIUM: ++ goto cmd_abort; ++ default: ++ pr_err("%s: Unhandled return value (%d)", ++ req->rq_disk->disk_name, status); ++ goto cmd_abort; ++ } ++ } ++ return MMC_CMD_SUCCESS; ++ ++cmd_abort: ++ pr_err("%s: error, req abort\n", mmc_hostname(card->host)); ++ ++ cmd_flags = mmc_card_removed(card) ? REQ_QUIET : 0; ++ mmc_xmited_abort(xmited, cmd_flags); ++ mmc_xmited_reset(xmited); ++ card->host->status = MMC_HOST_OK; ++ return MMC_CMD_ABORT; ++ ++cmd_restart: ++ /* ++ * putback all incomplete request. ++ * when error fixed, restart ++ */ ++ pr_info("%s: error fixed, restart...\n", mmc_hostname(card->host)); ++ mmc_xmited_requeue(mq->queue, xmited); ++ mmc_xmited_reset(xmited); ++ card->host->status = MMC_HOST_OK; ++ return MMC_CMD_SUCCESS; ++} ++ ++/** ++ * mmc_blk_xmit_reqs - fetch request from queue then transmit it. ++ * @mq: mmc queue ++ * @card: mmc card to attach this queue ++ * @req: new req that should xmited ++ * return: the count of request send this time. ++ * ++ * transmit the request till can't fetch ++ */ ++static int mmc_blk_xmit_reqs(struct mmc_queue *mq, ++ struct mmc_card *card, struct request *req) ++{ ++ struct request *cur; ++ struct mmc_host *host = card->host; ++ struct mmc_req_mrq *req_mrq = NULL; ++ struct request_queue *q = mq->queue; ++ struct mmc_xmited *xmited = &mq->xmited; ++#if !defined(CONFIG_ARCH_HI3559) && !defined(CONFIG_ARCH_HI3556) ++ u32 intr_step = 4; ++#endif ++ /*u32 max_send_reqs = 16;*/ ++ u32 reqs = 0; ++ int ret = 0; ++ unsigned int cmd_flags; ++ ++ do { ++ cur = req; ++ if (cur && mmc_xmited_full(xmited)) { ++ blk_requeue_request(q, cur); ++ return reqs; ++ } ++ /* In case sepecial request(discard, flush), we must be sure ++ * all of request has finished. ++ */ ++ cmd_flags = cur ? cur->cmd_flags : 0; ++ if (cmd_flags & REQ_DISCARD) { ++ /* complete ongoing async transfer ++ * before issuing discard ++ */ ++ if (!mmc_xmited_empty(xmited)) ++ mmc_blk_retrieve_reqs(mq, host, true); ++ if (cur->cmd_flags & REQ_SECURE) ++ ret = mmc_blk_issue_secdiscard_rq(mq, cur); ++ else ++ ret = mmc_blk_issue_discard_rq(mq, cur); ++ } else if (cmd_flags & REQ_FLUSH) { ++ /* complete ongoing async transfer ++ * before issuing flush ++ */ ++ if (!mmc_xmited_empty(xmited)) ++ mmc_blk_retrieve_reqs(mq, host, true); ++ ret = mmc_blk_issue_flush(mq, cur); ++ } else if (cur) { ++ req_mrq = mmc_get_rbuf(xmited); ++ memset(&req_mrq->brq, 0, ++ sizeof(struct mmc_blk_request)); ++ req_mrq->req = cur; ++ reqs++; ++ } ++ ++ /* fetch next request */ ++ spin_lock_irq(q->queue_lock); ++ req = blk_fetch_request(q); ++ spin_unlock_irq(q->queue_lock); ++ ++ if (req_mrq) { ++#if !defined(CONFIG_ARCH_HI3559) && !defined(CONFIG_ARCH_HI3556) ++ if (((reqs % intr_step) != 1) && req) ++ req_mrq->brq.cmd.flags |= MMC_CMD_NON_BLOCKING; ++#endif ++ ++ ret = mmc_send_reqs(req_mrq, card, mq); ++ req_mrq = NULL; ++ if (ret) ++ goto cmd_abort; ++ } ++ } while (req); ++ ++ return reqs; ++ ++cmd_abort: ++ pr_err("%s: send cmd fail. abort...\n", ++ mmc_hostname(card->host)); ++ cmd_flags = mmc_card_removed(card) ? REQ_QUIET : 0; ++ mmc_xmited_abort(xmited, cmd_flags); ++ mmc_xmited_reset(xmited); ++ if (req) { ++ req->cmd_flags |= cmd_flags; ++ blk_end_request_all(req, -EIO); ++ } ++ return 0; ++} + + static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) + { +- int ret; ++ int ret, reqs; + struct mmc_blk_data *md = mq->data; + struct mmc_card *card = md->queue.card; + struct mmc_host *host = card->host; +- unsigned long flags; +- unsigned int cmd_flags = req ? req->cmd_flags : 0; ++ struct mmc_xmited *xmited = &mq->xmited; + +- if (req && !mq->mqrq_prev->req) ++ set_current_state(TASK_RUNNING); ++#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME ++ if (mmc_bus_needs_resume(card->host)) ++ mmc_resume_bus(card->host); ++#endif ++ ++ if (mmc_xmited_empty(xmited)) + /* claim host only for the first request */ + mmc_get_card(card); + + ret = mmc_blk_part_switch(card, md); + if (ret) { +- if (req) { +- blk_end_request_all(req, -EIO); +- } +- ret = 0; + goto out; + } + +- mq->flags &= ~MMC_QUEUE_NEW_REQUEST; +- if (cmd_flags & REQ_DISCARD) { +- /* complete ongoing async transfer before issuing discard */ +- if (card->host->areq) +- mmc_blk_issue_rw_rq(mq, NULL); +- if (req->cmd_flags & REQ_SECURE) +- ret = mmc_blk_issue_secdiscard_rq(mq, req); +- else +- ret = mmc_blk_issue_discard_rq(mq, req); +- } else if (cmd_flags & REQ_FLUSH) { +- /* complete ongoing async transfer before issuing flush */ +- if (card->host->areq) +- mmc_blk_issue_rw_rq(mq, NULL); +- ret = mmc_blk_issue_flush(mq, req); +- } else { +- if (!req && host->areq) { +- spin_lock_irqsave(&host->context_info.lock, flags); +- host->context_info.is_waiting_last_req = true; +- spin_unlock_irqrestore(&host->context_info.lock, flags); +- } +- ret = mmc_blk_issue_rw_rq(mq, req); +- } ++ mmc_blk_retrieve_reqs(mq, card->host, false); + ++ reqs = mmc_blk_xmit_reqs(mq, card, req); ++ if (!reqs && !mmc_xmited_empty(xmited)) ++ mmc_wait_reqs_done(host); + out: +- if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) || +- (cmd_flags & MMC_REQ_SPECIAL_MASK)) +- /* +- * Release host when there are no more requests +- * and after special request(discard, flush) is done. +- * In case sepecial request, there is no reentry to +- * the 'mmc_blk_issue_rq' with 'mqrq_prev->req'. +- */ ++ if (mmc_xmited_empty(xmited)) ++ /* Release host when there are no more requests */ + mmc_put_card(card); + return ret; + } +@@ -2152,6 +2570,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, + md->disk->queue = md->queue.queue; + md->disk->driverfs_dev = parent; + set_disk_ro(md->disk, md->read_only || default_ro); ++ md->disk->flags = GENHD_FL_EXT_DEVT; + if (area_type & (MMC_BLK_DATA_AREA_RPMB | MMC_BLK_DATA_AREA_BOOT)) + md->disk->flags |= GENHD_FL_NO_PART_SCAN; + +@@ -2193,14 +2612,6 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, + blk_queue_flush(md->queue.queue, REQ_FLUSH | REQ_FUA); + } + +- if (mmc_card_mmc(card) && +- (area_type == MMC_BLK_DATA_AREA_MAIN) && +- (md->flags & MMC_BLK_CMD23) && +- card->ext_csd.packed_event_en) { +- if (!mmc_packed_init(&md->queue, card)) +- md->flags |= MMC_BLK_PACKED_CMD; +- } +- + return md; + + err_putdisk: +@@ -2302,8 +2713,6 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md) + */ + card = md->queue.card; + mmc_cleanup_queue(&md->queue); +- if (md->flags & MMC_BLK_PACKED_CMD) +- mmc_packed_clean(&md->queue); + if (md->disk->flags & GENHD_FL_UP) { + device_remove_file(disk_to_dev(md->disk), &md->force_ro); + if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) && +@@ -2469,6 +2878,9 @@ static int mmc_blk_probe(struct mmc_card *card) + + mmc_set_drvdata(card, md); + ++#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME ++ mmc_set_bus_resume_policy(card->host, 1); ++#endif + if (mmc_add_disk(md)) + goto out; + +@@ -2511,6 +2923,9 @@ static void mmc_blk_remove(struct mmc_card *card) + pm_runtime_put_noidle(&card->dev); + mmc_blk_remove_req(md); + mmc_set_drvdata(card, NULL); ++#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME ++ mmc_set_bus_resume_policy(card->host, 0); ++#endif + } + + static int _mmc_blk_suspend(struct mmc_card *card) +diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c +index 7a41a22..1003139 100644 +--- a/drivers/mmc/card/queue.c ++++ b/drivers/mmc/card/queue.c +@@ -23,6 +23,9 @@ + + #define MMC_QUEUE_BOUNCESZ 65536 + ++extern int mmc_blk_err_check(struct mmc_card *card, ++ struct mmc_req_mrq *req_mrq); ++ + /* + * Prepare a MMC request. This just filters out odd stuff. + */ +@@ -50,14 +53,13 @@ static int mmc_queue_thread(void *d) + { + struct mmc_queue *mq = d; + struct request_queue *q = mq->queue; ++ struct mmc_xmited *xmited = &mq->xmited; + + current->flags |= PF_MEMALLOC; + + down(&mq->thread_sem); + do { + struct request *req = NULL; +- struct mmc_queue_req *tmp; +- unsigned int cmd_flags = 0; + + spin_lock_irq(q->queue_lock); + set_current_state(TASK_INTERRUPTIBLE); +@@ -65,30 +67,8 @@ static int mmc_queue_thread(void *d) + mq->mqrq_cur->req = req; + spin_unlock_irq(q->queue_lock); + +- if (req || mq->mqrq_prev->req) { +- set_current_state(TASK_RUNNING); +- cmd_flags = req ? req->cmd_flags : 0; ++ if (req || (!mmc_xmited_empty(xmited))) { + mq->issue_fn(mq, req); +- if (mq->flags & MMC_QUEUE_NEW_REQUEST) { +- mq->flags &= ~MMC_QUEUE_NEW_REQUEST; +- continue; /* fetch again */ +- } +- +- /* +- * Current request becomes previous request +- * and vice versa. +- * In case of special requests, current request +- * has been finished. Do not assign it to previous +- * request. +- */ +- if (cmd_flags & MMC_REQ_SPECIAL_MASK) +- mq->mqrq_cur->req = NULL; +- +- mq->mqrq_prev->brq.mrq.data = NULL; +- mq->mqrq_prev->req = NULL; +- tmp = mq->mqrq_prev; +- mq->mqrq_prev = mq->mqrq_cur; +- mq->mqrq_cur = tmp; + } else { + if (kthread_should_stop()) { + set_current_state(TASK_RUNNING); +@@ -126,7 +106,7 @@ static void mmc_request_fn(struct request_queue *q) + } + + cntx = &mq->card->host->context_info; +- if (!mq->mqrq_cur->req && mq->mqrq_prev->req) { ++ if (!mmc_xmited_empty(&mq->xmited)) { + /* + * New MMC request arrived when MMC thread may be + * blocked on the previous request to be complete +@@ -138,7 +118,7 @@ static void mmc_request_fn(struct request_queue *q) + wake_up_interruptible(&cntx->wait); + } + spin_unlock_irqrestore(&cntx->lock, flags); +- } else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) ++ } else + wake_up_process(mq->thread); + } + +@@ -178,6 +158,14 @@ static void mmc_queue_setup_discard(struct request_queue *q, + queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, q); + } + ++void mmc_xmited_reset(struct mmc_xmited *xmited) ++{ ++ xmited->start = 0; ++ xmited->end = 0; ++ xmited->used = 0; ++ xmited->capacity = MMC_MAX_REQS_SEND_ONCE; ++} ++ + /** + * mmc_init_queue - initialise a queue structure. + * @mq: mmc queue +@@ -191,10 +179,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, + spinlock_t *lock, const char *subname) + { + struct mmc_host *host = card->host; ++ struct mmc_xmited *xmited = &mq->xmited; + u64 limit = BLK_BOUNCE_HIGH; +- int ret; +- struct mmc_queue_req *mqrq_cur = &mq->mqrq[0]; +- struct mmc_queue_req *mqrq_prev = &mq->mqrq[1]; ++ int ret, i; ++ struct mmc_queue_req *mqrq_cur = &mq->mqrq; + + if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) + limit = (u64)dma_max_pfn(mmc_dev(host)) << PAGE_SHIFT; +@@ -205,9 +193,11 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, + return -ENOMEM; + + mq->mqrq_cur = mqrq_cur; +- mq->mqrq_prev = mqrq_prev; + mq->queue->queuedata = mq; + ++ mmc_xmited_reset(xmited); ++ xmited->err_check = mmc_blk_err_check; ++ + blk_queue_prep_rq(mq->queue, mmc_prep_request); + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue); + queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, mq->queue); +@@ -233,57 +223,46 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, + pr_warn("%s: unable to allocate bounce cur buffer\n", + mmc_card_name(card)); + } +- mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); +- if (!mqrq_prev->bounce_buf) { +- pr_warn("%s: unable to allocate bounce prev buffer\n", +- mmc_card_name(card)); +- kfree(mqrq_cur->bounce_buf); +- mqrq_cur->bounce_buf = NULL; +- } + } + +- if (mqrq_cur->bounce_buf && mqrq_prev->bounce_buf) { ++ if (mqrq_cur->bounce_buf) { + blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY); + blk_queue_max_hw_sectors(mq->queue, bouncesz / 512); + blk_queue_max_segments(mq->queue, bouncesz / 512); + blk_queue_max_segment_size(mq->queue, bouncesz); + +- mqrq_cur->sg = mmc_alloc_sg(1, &ret); +- if (ret) +- goto cleanup_queue; +- + mqrq_cur->bounce_sg = + mmc_alloc_sg(bouncesz / 512, &ret); + if (ret) + goto cleanup_queue; +- +- mqrq_prev->sg = mmc_alloc_sg(1, &ret); +- if (ret) +- goto cleanup_queue; +- +- mqrq_prev->bounce_sg = +- mmc_alloc_sg(bouncesz / 512, &ret); +- if (ret) +- goto cleanup_queue; + } + } + #endif + +- if (!mqrq_cur->bounce_buf && !mqrq_prev->bounce_buf) { ++ if (!mqrq_cur->bounce_buf) { + blk_queue_bounce_limit(mq->queue, limit); + blk_queue_max_hw_sectors(mq->queue, + min(host->max_blk_count, host->max_req_size / 512)); + blk_queue_max_segments(mq->queue, host->max_segs); + blk_queue_max_segment_size(mq->queue, host->max_seg_size); + +- mqrq_cur->sg = mmc_alloc_sg(host->max_segs, &ret); +- if (ret) +- goto cleanup_queue; +- ++ memset(xmited->sg_buf, 0, sizeof(struct scatterlist *) ++ * MMC_MAX_REQS_SEND_ONCE); ++ memset(xmited->rq_buf, 0, sizeof(struct mmc_req_mrq *) ++ * MMC_MAX_REQS_SEND_ONCE); + +- mqrq_prev->sg = mmc_alloc_sg(host->max_segs, &ret); +- if (ret) +- goto cleanup_queue; ++ for (i = 0; i < MMC_MAX_REQS_SEND_ONCE; i++) { ++ xmited->sg_buf[i] = mmc_alloc_sg(host->max_segs, &ret); ++ if (ret) ++ goto cleanup_buf; ++ xmited->rq_buf[i] = (struct mmc_req_mrq *) ++ kmalloc(sizeof(struct mmc_req_mrq), GFP_KERNEL); ++ if (!xmited->rq_buf[i]) { ++ kfree(xmited->sg_buf[i]); ++ goto cleanup_buf; ++ } ++ xmited->rq_buf[i]->sg = xmited->sg_buf[i]; ++ } + } + + sema_init(&mq->thread_sem, 1); +@@ -297,23 +276,25 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, + } + + return 0; +- free_bounce_sg: ++free_bounce_sg: + kfree(mqrq_cur->bounce_sg); + mqrq_cur->bounce_sg = NULL; +- kfree(mqrq_prev->bounce_sg); +- mqrq_prev->bounce_sg = NULL; + +- cleanup_queue: +- kfree(mqrq_cur->sg); ++cleanup_buf: ++ if (!mqrq_cur->bounce_buf) { ++ for (i--; i >= 0; i--) { ++ kfree(xmited->sg_buf[i]); ++ xmited->sg_buf[i] = NULL; ++ kfree(xmited->rq_buf[i]); ++ xmited->rq_buf[i] = NULL; ++ } ++ } ++ ++cleanup_queue: + mqrq_cur->sg = NULL; + kfree(mqrq_cur->bounce_buf); + mqrq_cur->bounce_buf = NULL; + +- kfree(mqrq_prev->sg); +- mqrq_prev->sg = NULL; +- kfree(mqrq_prev->bounce_buf); +- mqrq_prev->bounce_buf = NULL; +- + blk_cleanup_queue(mq->queue); + return ret; + } +@@ -321,9 +302,10 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, + void mmc_cleanup_queue(struct mmc_queue *mq) + { + struct request_queue *q = mq->queue; ++ struct mmc_xmited *xmited = &mq->xmited; + unsigned long flags; ++ int i; + struct mmc_queue_req *mqrq_cur = mq->mqrq_cur; +- struct mmc_queue_req *mqrq_prev = mq->mqrq_prev; + + /* Make sure the queue isn't suspended, as that will deadlock */ + mmc_queue_resume(mq); +@@ -340,25 +322,22 @@ void mmc_cleanup_queue(struct mmc_queue *mq) + kfree(mqrq_cur->bounce_sg); + mqrq_cur->bounce_sg = NULL; + +- kfree(mqrq_cur->sg); + mqrq_cur->sg = NULL; + + kfree(mqrq_cur->bounce_buf); + mqrq_cur->bounce_buf = NULL; + +- kfree(mqrq_prev->bounce_sg); +- mqrq_prev->bounce_sg = NULL; +- +- kfree(mqrq_prev->sg); +- mqrq_prev->sg = NULL; +- +- kfree(mqrq_prev->bounce_buf); +- mqrq_prev->bounce_buf = NULL; +- ++ for (i = 0; i < MMC_MAX_REQS_SEND_ONCE; i++) { ++ kfree(xmited->sg_buf[i]); ++ xmited->sg_buf[i] = NULL; ++ kfree(xmited->rq_buf[i]); ++ xmited->rq_buf[i] = NULL; ++ } + mq->card = NULL; + } + EXPORT_SYMBOL(mmc_cleanup_queue); + ++#if 0 + int mmc_packed_init(struct mmc_queue *mq, struct mmc_card *card) + { + struct mmc_queue_req *mqrq_cur = &mq->mqrq[0]; +@@ -401,6 +380,7 @@ void mmc_packed_clean(struct mmc_queue *mq) + kfree(mqrq_prev->packed); + mqrq_prev->packed = NULL; + } ++#endif + + /** + * mmc_queue_suspend - suspend a MMC request queue +@@ -446,6 +426,7 @@ void mmc_queue_resume(struct mmc_queue *mq) + } + } + ++#if 0 + static unsigned int mmc_queue_packed_map_sg(struct mmc_queue *mq, + struct mmc_packed *packed, + struct scatterlist *sg, +@@ -480,6 +461,7 @@ static unsigned int mmc_queue_packed_map_sg(struct mmc_queue *mq, + sg_mark_end(sg + (sg_len - 1)); + return sg_len; + } ++#endif + + /* + * Prepare the sg list(s) to be handed of to the host driver +@@ -495,20 +477,12 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq, struct mmc_queue_req *mqrq) + cmd_type = mqrq->cmd_type; + + if (!mqrq->bounce_buf) { +- if (mmc_packed_cmd(cmd_type)) +- return mmc_queue_packed_map_sg(mq, mqrq->packed, +- mqrq->sg, cmd_type); +- else + return blk_rq_map_sg(mq->queue, mqrq->req, mqrq->sg); + } + + BUG_ON(!mqrq->bounce_sg); + +- if (mmc_packed_cmd(cmd_type)) +- sg_len = mmc_queue_packed_map_sg(mq, mqrq->packed, +- mqrq->bounce_sg, cmd_type); +- else +- sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg); ++ sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg); + + mqrq->bounce_sg_len = sg_len; + +@@ -552,3 +526,69 @@ void mmc_queue_bounce_post(struct mmc_queue_req *mqrq) + sg_copy_from_buffer(mqrq->bounce_sg, mqrq->bounce_sg_len, + mqrq->bounce_buf, mqrq->sg[0].length); + } ++ ++/** ++ * mmc_get_rbuf - get next idle mmc_req_mrq from buf pool. ++ * @xmited: mmc queue pool ++ */ ++struct mmc_req_mrq *mmc_get_rbuf(struct mmc_xmited *xmited) ++{ ++ struct mmc_req_mrq *req_mrq = NULL; ++ ++ if (xmited->used == (xmited->capacity - 2)) { ++ req_mrq = NULL; ++ } else { ++ req_mrq = xmited->rq_buf[xmited->end]; ++ xmited->end++; ++ xmited->end %= xmited->capacity; ++ xmited->used++; ++ } ++ return req_mrq; ++} ++ ++/** ++ * mmc_put_rbuf - release an mmc_req_mrq to buf pool. ++ * @mq: mmc queue ++ * return 0 ok -1 failed 1 confuse ++ */ ++int mmc_put_rbuf(struct mmc_xmited *xmited, struct mmc_req_mrq *req_mrq) ++{ ++ int ret = 0; ++ ++ if (xmited->used <= 0) { ++ ret = -1; ++ } else if (xmited->rq_buf[xmited->start] == req_mrq) { ++ xmited->start++; ++ xmited->start %= xmited->capacity; ++ xmited->used--; ++ ret = 0; ++ } else { ++ ret = 1; ++ } ++ return ret; ++} ++ ++void mmc_xmited_abort(struct mmc_xmited *xmited, unsigned int flags) ++{ ++ struct request *req; ++ struct mmc_req_mrq *req_mrq; ++ ++ while (!mmc_xmited_empty(xmited)) { ++ req_mrq = xmited->rq_buf[xmited->start]; ++ req = req_mrq->req; ++ req->cmd_flags |= flags; ++ blk_end_request_all(req_mrq->req, -EIO); ++ mmc_put_rbuf(xmited, req_mrq); ++ } ++} ++ ++void mmc_xmited_requeue(struct request_queue *q, struct mmc_xmited *xmited) ++{ ++ struct mmc_req_mrq *req_mrq; ++ ++ while (!mmc_xmited_empty(xmited)) { ++ req_mrq = xmited->rq_buf[xmited->start]; ++ blk_requeue_request(q, req_mrq->req); ++ mmc_put_rbuf(xmited, req_mrq); ++ } ++} +diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h +index 99e6521..e40a65d 100644 +--- a/drivers/mmc/card/queue.h ++++ b/drivers/mmc/card/queue.h +@@ -33,7 +33,7 @@ struct mmc_packed { + + struct mmc_queue_req { + struct request *req; +- struct mmc_blk_request brq; ++ struct mmc_blk_request *brq; + struct scatterlist *sg; + char *bounce_buf; + struct scatterlist *bounce_sg; +@@ -43,6 +43,29 @@ struct mmc_queue_req { + struct mmc_packed *packed; + }; + ++#define MMC_REQ_FRESH (0x1<<0) ++#define MMC_REQ_PREP (0x1<<1) ++#define MMC_REQ_SEND (0x1<<2) ++#define MMC_REQ_DONE (0x1<<3) ++#define MMC_REQ_FAIL (0x1<<4) ++struct mmc_req_mrq { ++ struct request *req; ++ struct mmc_blk_request brq; ++ struct scatterlist *sg; ++ u32 wr_pos; ++}; ++ ++#define MMC_MAX_REQS_SEND_ONCE (32) ++struct mmc_xmited { ++ struct scatterlist *sg_buf[MMC_MAX_REQS_SEND_ONCE]; ++ struct mmc_req_mrq *rq_buf[MMC_MAX_REQS_SEND_ONCE]; ++ u32 capacity; ++ u32 used; ++ u32 start; ++ u32 end; ++ int (*err_check)(struct mmc_card *, struct mmc_req_mrq *); ++}; ++ + struct mmc_queue { + struct mmc_card *card; + struct task_struct *thread; +@@ -54,9 +77,9 @@ struct mmc_queue { + int (*issue_fn)(struct mmc_queue *, struct request *); + void *data; + struct request_queue *queue; +- struct mmc_queue_req mqrq[2]; ++ struct mmc_queue_req mqrq; + struct mmc_queue_req *mqrq_cur; +- struct mmc_queue_req *mqrq_prev; ++ struct mmc_xmited xmited; + }; + + extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *, +@@ -75,4 +98,14 @@ extern void mmc_packed_clean(struct mmc_queue *); + + extern int mmc_access_rpmb(struct mmc_queue *); + ++#define mmc_xmited_empty(x) (!((x)->used)) ++#define mmc_xmited_full(x) ((x)->used >= ((x)->capacity-2)) ++ ++void mmc_xmited_reset(struct mmc_xmited *); ++ ++struct mmc_req_mrq *mmc_get_rbuf(struct mmc_xmited *); ++int mmc_put_rbuf(struct mmc_xmited *, struct mmc_req_mrq *); ++ ++void mmc_xmited_requeue(struct request_queue *, struct mmc_xmited *); ++void mmc_xmited_abort(struct mmc_xmited *, unsigned int); + #endif +diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig +index 9ebee72..f771bc3 100644 +--- a/drivers/mmc/core/Kconfig ++++ b/drivers/mmc/core/Kconfig +@@ -11,3 +11,18 @@ config MMC_CLKGATE + support handling this in order for it to be of any use. + + If unsure, say N. ++ ++config MMC_EMBEDDED_SDIO ++ boolean "MMC embedded SDIO device support (EXPERIMENTAL)" ++ help ++ If you say Y here, support will be added for embedded SDIO ++ devices which do not contain the necessary enumeration ++ support in hardware to be properly detected. ++ ++config MMC_PARANOID_SD_INIT ++ bool "Enable paranoid SD card initialization (EXPERIMENTAL)" ++ help ++ If you say Y here, the MMC layer will be extra paranoid ++ about re-trying SD init requests. This can be a useful ++ work-around for buggy controllers and hardware. Enable ++ if you are experiencing issues with SD detection. +diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c +index 297b4f9..b850534 100644 +--- a/drivers/mmc/core/core.c ++++ b/drivers/mmc/core/core.c +@@ -29,6 +29,9 @@ + #include <linux/random.h> + #include <linux/slab.h> + #include <linux/of.h> ++#include <linux/wakelock.h> ++ ++#include <trace/events/mmc.h> + + #include <linux/mmc/card.h> + #include <linux/mmc/host.h> +@@ -55,6 +58,7 @@ + #define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000) /* max time to wait in ms */ + + static struct workqueue_struct *workqueue; ++static struct wake_lock mmc_delayed_work_wake_lock; + static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; + + /* +@@ -71,6 +75,7 @@ module_param(use_spi_crc, bool, 0); + static int mmc_schedule_delayed_work(struct delayed_work *work, + unsigned long delay) + { ++ wake_lock(&mmc_delayed_work_wake_lock); + return queue_delayed_work(workqueue, work, delay); + } + +@@ -158,6 +163,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) + pr_debug("%s: %d bytes transferred: %d\n", + mmc_hostname(host), + mrq->data->bytes_xfered, mrq->data->error); ++ trace_mmc_blk_rw_end(cmd->opcode, cmd->arg, mrq->data); + } + + if (mrq->stop) { +@@ -331,7 +337,7 @@ static void mmc_wait_done(struct mmc_request *mrq) + * Sets the done callback to be called when request is completed by the card. + * Starts data mmc request execution + */ +-static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq) ++int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq) + { + mrq->done = mmc_wait_data_done; + mrq->host = host; +@@ -344,6 +350,7 @@ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq) + + return 0; + } ++EXPORT_SYMBOL(__mmc_start_data_req); + + static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) + { +@@ -358,6 +365,7 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) + return 0; + } + ++#if 0 + /* + * mmc_wait_for_data_req_done() - wait for request completed + * @host: MMC host to prepare the command. +@@ -414,6 +422,7 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host, + } + return err; + } ++#endif + + static void mmc_wait_for_req_done(struct mmc_host *host, + struct mmc_request *mrq) +@@ -454,6 +463,7 @@ static void mmc_wait_for_req_done(struct mmc_host *host, + } + } + ++#if 0 + /** + * mmc_pre_req - Prepare for a new request + * @host: MMC host to prepare command +@@ -474,6 +484,7 @@ static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, + mmc_host_clk_release(host); + } + } ++#endif + + /** + * mmc_post_req - Post process a completed request +@@ -484,7 +495,7 @@ static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq, + * Let the host post process a completed request. Post processing of + * a request may be performed while another reuqest is running. + */ +-static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, ++void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, + int err) + { + if (host->ops->post_req) { +@@ -494,6 +505,7 @@ static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq, + } + } + ++#if 0 + /** + * mmc_start_req - start a non-blocking request + * @host: MMC host to start command +@@ -542,8 +554,12 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, + mmc_start_bkops(host->card, true); + } + +- if (!err && areq) ++ if (!err && areq) { ++ trace_mmc_blk_rw_start(areq->mrq->cmd->opcode, ++ areq->mrq->cmd->arg, ++ areq->mrq->data); + start_err = __mmc_start_data_req(host, areq->mrq); ++ } + + if (host->areq) + mmc_post_req(host, host->areq->mrq, 0); +@@ -562,6 +578,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, + return data; + } + EXPORT_SYMBOL(mmc_start_req); ++#endif + + /** + * mmc_wait_for_req - start a request and wait for completion +@@ -745,6 +762,9 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) + { + unsigned int mult; + ++ if (!card) ++ return; ++ + /* + * SDIO cards only define an upper 1 s limit on access. + */ +@@ -1879,8 +1899,13 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, + struct mmc_command cmd = {0}; + unsigned int qty = 0; + unsigned long timeout; ++ unsigned int fr, nr; + int err; + ++ fr = from; ++ nr = to - from + 1; ++ trace_mmc_blk_erase_start(arg, fr, nr); ++ + /* + * qty is used to calculate the erase timeout which depends on how many + * erase groups (or allocation units in SD terminology) are affected. +@@ -1984,6 +2009,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, + } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || + (R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG)); + out: ++ ++ trace_mmc_blk_erase_end(arg, fr, nr); + return err; + } + +@@ -2295,6 +2322,22 @@ int mmc_hw_reset(struct mmc_host *host) + } + EXPORT_SYMBOL(mmc_hw_reset); + ++int mmc_sw_reset(struct mmc_host *host) ++{ ++ ++ if (!mmc_card_sd(host->card) || !host->ops->sw_reset) ++ return -EOPNOTSUPP; ++ ++ host->ios.bus_width = MMC_BUS_WIDTH_1; ++ host->ios.timing = MMC_TIMING_LEGACY; ++ host->ios.clock = host->f_init; ++ mmc_set_ios(host); ++ host->ops->sw_reset(host); ++ ++ return host->bus_ops->power_restore(host); ++} ++EXPORT_SYMBOL(mmc_sw_reset); ++ + int mmc_hw_reset_check(struct mmc_host *host) + { + return mmc_do_hw_reset(host, 1); +@@ -2411,6 +2454,7 @@ void mmc_rescan(struct work_struct *work) + struct mmc_host *host = + container_of(work, struct mmc_host, detect.work); + int i; ++ bool extend_wakelock = false; + + if (host->trigger_card_event && host->ops->card_event) { + host->ops->card_event(host); +@@ -2437,6 +2481,12 @@ void mmc_rescan(struct work_struct *work) + + host->detect_change = 0; + ++ /* If the card was removed the bus will be marked ++ * as dead - extend the wakelock so userspace ++ * can respond */ ++ if (host->bus_dead) ++ extend_wakelock = 1; ++ + /* + * Let mmc_bus_put() free the bus/bus_ops if we've found that + * the card is no longer present. +@@ -2460,20 +2510,30 @@ void mmc_rescan(struct work_struct *work) + host->ops->get_cd(host) == 0) { + mmc_claim_host(host); + mmc_power_off(host); ++ if (host->ops->card_info_save) ++ host->ops->card_info_save(host); + mmc_release_host(host); + goto out; + } + + mmc_claim_host(host); + for (i = 0; i < ARRAY_SIZE(freqs); i++) { +- if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) ++ if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) { ++ extend_wakelock = true; ++ if (host->ops->card_info_save) ++ host->ops->card_info_save(host); + break; ++ } + if (freqs[i] <= host->f_min) + break; + } + mmc_release_host(host); + + out: ++ if (extend_wakelock) ++ wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2); ++ else ++ wake_unlock(&mmc_delayed_work_wake_lock); + if (host->caps & MMC_CAP_NEEDS_POLL) + mmc_schedule_delayed_work(&host->detect, HZ); + } +@@ -2671,6 +2731,22 @@ void mmc_init_context_info(struct mmc_host *host) + init_waitqueue_head(&host->context_info.wait); + } + ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++void mmc_set_embedded_sdio_data(struct mmc_host *host, ++ struct sdio_cis *cis, ++ struct sdio_cccr *cccr, ++ struct sdio_embedded_func *funcs, ++ int num_funcs) ++{ ++ host->embedded_sdio_data.cis = cis; ++ host->embedded_sdio_data.cccr = cccr; ++ host->embedded_sdio_data.funcs = funcs; ++ host->embedded_sdio_data.num_funcs = num_funcs; ++} ++ ++EXPORT_SYMBOL(mmc_set_embedded_sdio_data); ++#endif ++ + static int __init mmc_init(void) + { + int ret; +@@ -2679,6 +2755,9 @@ static int __init mmc_init(void) + if (!workqueue) + return -ENOMEM; + ++ wake_lock_init(&mmc_delayed_work_wake_lock, WAKE_LOCK_SUSPEND, ++ "mmc_delayed_work"); ++ + ret = mmc_register_bus(); + if (ret) + goto destroy_workqueue; +@@ -2699,6 +2778,7 @@ unregister_bus: + mmc_unregister_bus(); + destroy_workqueue: + destroy_workqueue(workqueue); ++ wake_lock_destroy(&mmc_delayed_work_wake_lock); + + return ret; + } +@@ -2709,6 +2789,7 @@ static void __exit mmc_exit(void) + mmc_unregister_host_class(); + mmc_unregister_bus(); + destroy_workqueue(workqueue); ++ wake_lock_destroy(&mmc_delayed_work_wake_lock); + } + + subsys_initcall(mmc_init); +diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c +index 270d58a..08e11cb 100644 +--- a/drivers/mmc/core/host.c ++++ b/drivers/mmc/core/host.c +@@ -371,7 +371,7 @@ int mmc_of_parse(struct mmc_host *host) + if (ret == -EPROBE_DEFER) + return ret; + if (ret != -ENOENT) { +- dev_err(host->parent, ++ dev_dbg(host->parent, + "Failed to request CD GPIO: %d\n", + ret); + } +@@ -401,7 +401,7 @@ int mmc_of_parse(struct mmc_host *host) + if (ret == -EPROBE_DEFER) + goto out; + if (ret != -ENOENT) { +- dev_err(host->parent, ++ dev_dbg(host->parent, + "Failed to request WP GPIO: %d\n", + ret); + } +@@ -560,7 +560,8 @@ int mmc_add_host(struct mmc_host *host) + mmc_host_clk_sysfs_init(host); + + mmc_start_host(host); +- register_pm_notifier(&host->pm_notify); ++ if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) ++ register_pm_notifier(&host->pm_notify); + + return 0; + } +@@ -577,7 +578,9 @@ EXPORT_SYMBOL(mmc_add_host); + */ + void mmc_remove_host(struct mmc_host *host) + { +- unregister_pm_notifier(&host->pm_notify); ++ if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) ++ unregister_pm_notifier(&host->pm_notify); ++ + mmc_stop_host(host); + + #ifdef CONFIG_DEBUG_FS +diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c +index a301a78..f4b9bf0 100644 +--- a/drivers/mmc/core/mmc.c ++++ b/drivers/mmc/core/mmc.c +@@ -1175,38 +1175,6 @@ bus_speed: + return err; + } + +-const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE] = { +- 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, +- 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, +- 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, +- 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, +- 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, +- 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, +- 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, +- 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +-}; +-EXPORT_SYMBOL(tuning_blk_pattern_4bit); +- +-const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE] = { +- 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, +- 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, +- 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, +- 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, +- 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, +- 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, +- 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, +- 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, +- 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, +- 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, +- 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, +- 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, +- 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, +- 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, +- 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, +- 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +-}; +-EXPORT_SYMBOL(tuning_blk_pattern_8bit); +- + /* + * Execute tuning sequence to seek the proper bus operating + * conditions for HS200 and HS400, which sends CMD21 to the device. +@@ -1239,6 +1207,34 @@ static int mmc_hs200_tuning(struct mmc_card *card) + return err; + } + ++static int mmc_hs400_tuning(struct mmc_card *card) ++{ ++ struct mmc_host *host = card->host; ++ int err = 0; ++ ++ /* ++ * Timing should be adjusted to the HS400 target ++ * operation frequency for tuning process ++ */ ++ if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 && ++ host->ios.bus_width == MMC_BUS_WIDTH_8) ++ if (host->ops->prepare_hs400_tuning) ++ host->ops->prepare_hs400_tuning(host, &host->ios); ++ ++ if (host->ops->execute_tuning) { ++ mmc_host_clk_hold(host); ++ err = host->ops->execute_tuning(host, ++ MMC_SEND_EXT_CSD); ++ mmc_host_clk_release(host); ++ ++ if (err) ++ pr_warn("%s: tuning execution failed\n", ++ mmc_hostname(host)); ++ } ++ ++ return err; ++} ++ + /* + * Handle the detection and initialisation of a card. + * +@@ -1463,6 +1459,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, + err = mmc_select_hs400(card); + if (err) + goto err; ++ ++ if (mmc_card_hs400(card)) { ++ err = mmc_hs400_tuning(card); ++ if (err) ++ goto err; ++ } + } else if (mmc_card_hs(card)) { + /* Select the desired bus width optionally */ + err = mmc_select_bus_width(card); +diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c +index 7911e05..2692021 100644 +--- a/drivers/mmc/core/mmc_ops.c ++++ b/drivers/mmc/core/mmc_ops.c +@@ -23,6 +23,36 @@ + + #define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */ + ++static const u8 tuning_blk_pattern_4bit[] = { ++ 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, ++ 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, ++ 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, ++ 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, ++ 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, ++ 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, ++ 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, ++ 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, ++}; ++ ++static const u8 tuning_blk_pattern_8bit[] = { ++ 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, ++ 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, ++ 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, ++ 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, ++ 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, ++ 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, ++ 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, ++ 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, ++ 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, ++ 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, ++ 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, ++ 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, ++ 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, ++ 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, ++ 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, ++ 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, ++}; ++ + static inline int __mmc_send_status(struct mmc_card *card, u32 *status, + bool ignore_crc) + { +@@ -543,6 +573,93 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, + } + EXPORT_SYMBOL_GPL(mmc_switch); + ++int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error) ++{ ++ struct mmc_request mrq = {NULL}; ++ struct mmc_command cmd = {0}; ++ struct mmc_data data = {0}; ++ struct scatterlist sg; ++ struct mmc_ios *ios = &host->ios; ++ const u8 *tuning_block_pattern; ++ int size, err = 0; ++ u8 *data_buf; ++ ++ if (ios->bus_width == MMC_BUS_WIDTH_8) { ++ tuning_block_pattern = tuning_blk_pattern_8bit; ++ size = sizeof(tuning_blk_pattern_8bit); ++ } else if (ios->bus_width == MMC_BUS_WIDTH_4) { ++ tuning_block_pattern = tuning_blk_pattern_4bit; ++ size = sizeof(tuning_blk_pattern_4bit); ++ } else ++ return -EINVAL; ++ ++ data_buf = kzalloc(size, GFP_KERNEL); ++ if (!data_buf) ++ return -ENOMEM; ++ ++ mrq.cmd = &cmd; ++ mrq.data = &data; ++ ++ cmd.opcode = opcode; ++ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ ++ data.blksz = size; ++ data.blocks = 1; ++ data.flags = MMC_DATA_READ; ++ ++ /* ++ * According to the tuning specs, Tuning process ++ * is normally shorter 40 executions of CMD19, ++ * and timeout value should be shorter than 150 ms ++ */ ++ data.timeout_ns = 150 * NSEC_PER_MSEC; ++ ++ data.sg = &sg; ++ data.sg_len = 1; ++ sg_init_one(&sg, data_buf, size); ++ ++ mmc_wait_for_req(host, &mrq); ++ ++ if (cmd_error) ++ *cmd_error = cmd.error; ++ ++ if (cmd.error) { ++ err = cmd.error; ++ goto out; ++ } ++ ++ if (data.error) { ++ err = data.error; ++ goto out; ++ } ++ ++ if (memcmp(data_buf, tuning_block_pattern, size)) ++ err = -EIO; ++ ++out: ++ kfree(data_buf); ++ return err; ++} ++EXPORT_SYMBOL_GPL(mmc_send_tuning); ++ ++int mmc_send_dll_tuning(struct mmc_host *host) ++{ ++ u8 *ext_csd; ++ int err = 0; ++ ++ ext_csd = kzalloc(512, GFP_KERNEL); ++ if (!ext_csd) ++ return -ENOMEM; ++ ++ err = mmc_send_cxd_data(NULL, host, MMC_SEND_EXT_CSD, ++ ext_csd, 512); ++ ++ kfree(ext_csd); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(mmc_send_dll_tuning); ++ + static int + mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, + u8 len) +diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c +index d90a6de..a9e09ce 100644 +--- a/drivers/mmc/core/sd.c ++++ b/drivers/mmc/core/sd.c +@@ -246,8 +246,10 @@ static int mmc_read_ssr(struct mmc_card *card) + goto out; + } + +- for (i = 0; i < 16; i++) ++ for (i = 0; i < 16; i++) { + ssr[i] = be32_to_cpu(ssr[i]); ++ card->c_ssr[i] = ssr[i]; ++ } + + /* + * UNSTUFF_BITS only works with four u32s so we have to offset the +@@ -813,6 +815,9 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, + bool reinit) + { + int err; ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ int retries; ++#endif + + if (!reinit) { + /* +@@ -839,7 +844,26 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, + /* + * Fetch switch information from card. + */ ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ for (retries = 1; retries <= 3; retries++) { ++ err = mmc_read_switch(card); ++ if (!err) { ++ if (retries > 1) { ++ printk(KERN_WARNING ++ "%s: recovered\n", ++ mmc_hostname(host)); ++ } ++ break; ++ } else { ++ printk(KERN_WARNING ++ "%s: read switch failed (attempt %d)\n", ++ mmc_hostname(host), retries); ++ } ++ } ++#else + err = mmc_read_switch(card); ++#endif ++ + if (err) + return err; + } +@@ -969,6 +993,15 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, + if (err) + goto free_card; + ++ /* check have been set bus speed*/ ++ if (card->sd_bus_speed != 0) { ++ err = mmc_sd_init_uhs_card(card); ++ if (err) ++ goto free_card; ++ ++ goto done; ++ } ++ + /* Initialization sequence for UHS-I cards */ + if (rocr & SD_ROCR_S18A) { + err = mmc_sd_init_uhs_card(card); +@@ -1002,6 +1035,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, + } + } + ++done: + host->card = card; + return 0; + +@@ -1037,7 +1071,10 @@ static int mmc_sd_alive(struct mmc_host *host) + */ + static void mmc_sd_detect(struct mmc_host *host) + { +- int err; ++ int err = 0; ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ int retries = 5; ++#endif + + BUG_ON(!host); + BUG_ON(!host->card); +@@ -1047,7 +1084,23 @@ static void mmc_sd_detect(struct mmc_host *host) + /* + * Just check if our card has been removed. + */ ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ while(retries) { ++ err = mmc_send_status(host->card, NULL); ++ if (err) { ++ retries--; ++ udelay(5); ++ continue; ++ } ++ break; ++ } ++ if (!retries) { ++ printk(KERN_ERR "%s(%s): Unable to re-detect card (%d)\n", ++ __func__, mmc_hostname(host), err); ++ } ++#else + err = _mmc_detect_card_removed(host); ++#endif + + mmc_put_card(host->card); + +@@ -1109,6 +1162,9 @@ static int mmc_sd_suspend(struct mmc_host *host) + static int _mmc_sd_resume(struct mmc_host *host) + { + int err = 0; ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ int retries; ++#endif + + BUG_ON(!host); + BUG_ON(!host->card); +@@ -1119,7 +1175,23 @@ static int _mmc_sd_resume(struct mmc_host *host) + goto out; + + mmc_power_up(host, host->card->ocr); ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ retries = 5; ++ while (retries) { ++ err = mmc_sd_init_card(host, host->card->ocr, host->card); ++ ++ if (err) { ++ printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n", ++ mmc_hostname(host), err, retries); ++ mdelay(5); ++ retries--; ++ continue; ++ } ++ break; ++ } ++#else + err = mmc_sd_init_card(host, host->card->ocr, host->card); ++#endif + mmc_card_clr_suspended(host->card); + + out: +@@ -1210,6 +1282,9 @@ int mmc_attach_sd(struct mmc_host *host) + { + int err; + u32 ocr, rocr; ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ int retries; ++#endif + + BUG_ON(!host); + WARN_ON(!host->claimed); +@@ -1246,9 +1321,27 @@ int mmc_attach_sd(struct mmc_host *host) + /* + * Detect and init the card. + */ ++#ifdef CONFIG_MMC_PARANOID_SD_INIT ++ retries = 5; ++ while (retries) { ++ err = mmc_sd_init_card(host, rocr, NULL); ++ if (err) { ++ retries--; ++ continue; ++ } ++ break; ++ } ++ ++ if (!retries) { ++ printk(KERN_ERR "%s: mmc_sd_init_card() failure (err = %d)\n", ++ mmc_hostname(host), err); ++ goto err; ++ } ++#else + err = mmc_sd_init_card(host, rocr, NULL); + if (err) + goto err; ++#endif + + mmc_release_host(host); + err = mmc_add_card(host->card); +diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c +index 48d0c93..e608af5 100644 +--- a/drivers/mmc/core/sd_ops.c ++++ b/drivers/mmc/core/sd_ops.c +@@ -393,3 +393,4 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr) + + return 0; + } ++EXPORT_SYMBOL(mmc_app_sd_status); +diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c +index 2439e71..7c7e544 100644 +--- a/drivers/mmc/core/sdio.c ++++ b/drivers/mmc/core/sdio.c +@@ -10,6 +10,7 @@ + */ + + #include <linux/err.h> ++#include <linux/module.h> + #include <linux/pm_runtime.h> + + #include <linux/mmc/host.h> +@@ -28,6 +29,10 @@ + #include "sdio_ops.h" + #include "sdio_cis.h" + ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++#include <linux/mmc/sdio_ids.h> ++#endif ++ + static int sdio_read_fbr(struct sdio_func *func) + { + int ret; +@@ -738,19 +743,35 @@ try_again: + goto finish; + } + +- /* +- * Read the common registers. +- */ +- err = sdio_read_cccr(card, ocr); +- if (err) +- goto remove; ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ if (host->embedded_sdio_data.cccr) ++ memcpy(&card->cccr, host->embedded_sdio_data.cccr, sizeof(struct sdio_cccr)); ++ else { ++#endif ++ /* ++ * Read the common registers. ++ */ ++ err = sdio_read_cccr(card, ocr); ++ if (err) ++ goto remove; ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ } ++#endif + +- /* +- * Read the common CIS tuples. +- */ +- err = sdio_read_common_cis(card); +- if (err) +- goto remove; ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ if (host->embedded_sdio_data.cis) ++ memcpy(&card->cis, host->embedded_sdio_data.cis, sizeof(struct sdio_cis)); ++ else { ++#endif ++ /* ++ * Read the common CIS tuples. ++ */ ++ err = sdio_read_common_cis(card); ++ if (err) ++ goto remove; ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ } ++#endif + + if (oldcard) { + int same = (card->cis.vendor == oldcard->cis.vendor && +@@ -1143,14 +1164,36 @@ int mmc_attach_sdio(struct mmc_host *host) + funcs = (ocr & 0x70000000) >> 28; + card->sdio_funcs = 0; + ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ if (host->embedded_sdio_data.funcs) ++ card->sdio_funcs = funcs = host->embedded_sdio_data.num_funcs; ++#endif ++ + /* + * Initialize (but don't add) all present functions. + */ + for (i = 0; i < funcs; i++, card->sdio_funcs++) { +- err = sdio_init_func(host->card, i + 1); +- if (err) +- goto remove; +- ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ if (host->embedded_sdio_data.funcs) { ++ struct sdio_func *tmp; ++ ++ tmp = sdio_alloc_func(host->card); ++ if (IS_ERR(tmp)) ++ goto remove; ++ tmp->num = (i + 1); ++ card->sdio_func[i] = tmp; ++ tmp->class = host->embedded_sdio_data.funcs[i].f_class; ++ tmp->max_blksize = host->embedded_sdio_data.funcs[i].f_maxblksize; ++ tmp->vendor = card->cis.vendor; ++ tmp->device = card->cis.device; ++ } else { ++#endif ++ err = sdio_init_func(host->card, i + 1); ++ if (err) ++ goto remove; ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ } ++#endif + /* + * Enable Runtime PM for this func (if supported) + */ +@@ -1198,3 +1241,40 @@ err: + return err; + } + ++int sdio_reset_comm(struct mmc_card *card) ++{ ++ struct mmc_host *host = card->host; ++ u32 ocr; ++ u32 rocr; ++ int err; ++ ++ printk("%s():\n", __func__); ++ mmc_claim_host(host); ++ ++ mmc_go_idle(host); ++ ++ mmc_set_clock(host, host->f_min); ++ ++ err = mmc_send_io_op_cond(host, 0, &ocr); ++ if (err) ++ goto err; ++ ++ rocr = mmc_select_voltage(host, ocr); ++ if (!rocr) { ++ err = -EINVAL; ++ goto err; ++ } ++ ++ err = mmc_sdio_init_card(host, rocr, card, 0); ++ if (err) ++ goto err; ++ ++ mmc_release_host(host); ++ return 0; ++err: ++ printk("%s: Error resetting SDIO communications (%d)\n", ++ mmc_hostname(host), err); ++ mmc_release_host(host); ++ return err; ++} ++EXPORT_SYMBOL(sdio_reset_comm); +diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c +index 6da97b1..a735f89 100644 +--- a/drivers/mmc/core/sdio_bus.c ++++ b/drivers/mmc/core/sdio_bus.c +@@ -26,6 +26,10 @@ + #include "sdio_cis.h" + #include "sdio_bus.h" + ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++#include <linux/mmc/host.h> ++#endif ++ + /* show configuration fields */ + #define sdio_config_attr(field, format_string) \ + static ssize_t \ +@@ -262,7 +266,14 @@ static void sdio_release_func(struct device *dev) + { + struct sdio_func *func = dev_to_sdio_func(dev); + +- sdio_free_func_cis(func); ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ /* ++ * If this device is embedded then we never allocated ++ * cis tables for this func ++ */ ++ if (!func->card->host->embedded_sdio_data.funcs) ++#endif ++ sdio_free_func_cis(func); + + kfree(func->info); + +diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c +old mode 100644 +new mode 100755 +index 78cb4d5..8fdeb07 +--- a/drivers/mmc/core/sdio_io.c ++++ b/drivers/mmc/core/sdio_io.c +@@ -384,6 +384,39 @@ u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret) + EXPORT_SYMBOL_GPL(sdio_readb); + + /** ++ * sdio_readb_ext - read a single byte from a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * @in: value to add to argument ++ * ++ * Reads a single byte from the address space of a given SDIO ++ * function. If there is a problem reading the address, 0xff ++ * is returned and @err_ret will contain the error code. ++ */ ++unsigned char sdio_readb_ext(struct sdio_func *func, unsigned int addr, ++ int *err_ret, unsigned in) ++{ ++ int ret; ++ unsigned char val; ++ ++ BUG_ON(!func); ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = mmc_io_rw_direct(func->card, 0, func->num, addr, (u8)in, &val); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFF; ++ } ++ ++ return val; ++} ++EXPORT_SYMBOL_GPL(sdio_readb_ext); ++ ++/** + * sdio_writeb - write a single byte to a SDIO function + * @func: SDIO function to access + * @b: byte to write +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 1386065..9629e12 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -748,3 +748,5 @@ config MMC_SUNXI + help + This selects support for the SD/MMC Host Controller on + Allwinner sunxi SoCs. ++ ++source "drivers/mmc/host/himciv200/Kconfig" +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index b09ecfb..8999d29 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -75,3 +75,4 @@ obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o + ifeq ($(CONFIG_CB710_DEBUG),y) + CFLAGS-cb710-mmc += -DDEBUG + endif ++obj-$(CONFIG_HIMCIV200) += himciv200/ +diff --git a/drivers/mmc/host/himciv200/Kconfig b/drivers/mmc/host/himciv200/Kconfig +new file mode 100644 +index 0000000..323a5dd +--- /dev/null ++++ b/drivers/mmc/host/himciv200/Kconfig +@@ -0,0 +1,38 @@ ++# ++# himci v200 device configuration ++# ++menuconfig HIMCIV200 ++ tristate "himci v200 eMMC/SDXC/SDIO device support" ++ depends on (ARCH_HI3516CV300 || ARCH_HI3519 || ARCH_HI3519V101 || ARCH_HI3559 || ARCH_HI3556 || ARCH_HI3516AV200) ++ default y if (ARCH_HI3516CV300) ++ default y if (ARCH_HI3519) ++ default y if (ARCH_HI3519V101) ++ default y if (ARCH_HI3516AV200) ++ default y if (ARCH_HI3559) ++ default y if (ARCH_HI3556) ++ select MMC_UNSAFE_RESUME ++ select MMC_EMBEDDED_SDIO ++ select MMC_BLOCK ++ select MMC_BLOCK_BOUNCE ++ help ++ This selects the Hisilicon Synopsys MultiMedia Card Driver ++ support. If you want use SD/MMC/SDIO driver, ++ Say Y or M here. ++ ++ default is Y. ++ ++config SEND_AUTO_STOP ++ bool "Send Auto Stop to terminate data transfer between host and SD card" ++ depends on (ARCH_HI3516CV300 || ARCH_HI3519 || ARCH_HI3519V101 || ARCH_HI3559 || ARCH_HI3556 || ARCH_HI3516AV200) && HIMCIV200 ++ default y ++ ++config DETECT_CARD_TIME ++ int "The Period to detect whether Card is pluged or unpluged." ++ depends on (ARCH_HI3516CV300 || ARCH_HI3519 || ARCH_HI3519V101 || ARCH_HI3559 || ARCH_HI3556 || ARCH_HI3516AV200) && HIMCIV200 ++ range 10 500 ++ default 200 ++ help ++ Set the Period Time to detect the card, which the unit is ms. ++ Should set it between 10 and 500. ++ ++ Default is 200 here. +diff --git a/drivers/mmc/host/himciv200/Makefile b/drivers/mmc/host/himciv200/Makefile +new file mode 100644 +index 0000000..6ba3ce4 +--- /dev/null ++++ b/drivers/mmc/host/himciv200/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_HIMCIV200) += himciv200.o ++himciv200-objs := himci.o himci_proc.o +diff --git a/drivers/mmc/host/himciv200/himci.c b/drivers/mmc/host/himciv200/himci.c +new file mode 100644 +index 0000000..51b5343 +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci.c +@@ -0,0 +1,2237 @@ ++/* ++ * himci.c - hisilicon MMC Host driver ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#define pr_fmt(fmt) "himci: " fmt ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/mm.h> ++#include <linux/interrupt.h> ++#include <linux/dma-mapping.h> ++#include <linux/scatterlist.h> ++ ++#include <linux/mmc/host.h> ++#include <linux/mmc/mmc.h> ++#include <linux/mmc/card.h> ++#include <linux/mmc/core.h> ++#include <linux/mmc/sd.h> ++#include <linux/slab.h> ++ ++#include <linux/ioport.h> ++#include <linux/device.h> ++#include <linux/spinlock.h> ++ ++#include <linux/delay.h> ++#include <linux/dma-mapping.h> ++#include <linux/kthread.h> ++#include <linux/workqueue.h> ++#include <linux/freezer.h> ++#include <asm/dma.h> ++#include <asm/irq.h> ++#include <linux/sizes.h> ++#include <mach/io.h> ++ ++#include <linux/io.h> ++#include <linux/of.h> ++#include <linux/clk.h> ++#include <linux/clk-provider.h> ++ ++#include "himci_reg.h" ++#include "himci.h" ++#include "himci_dbg.h" ++#include "himci_acl.h" ++#include "himci_proc.h" ++ ++/*************************************************************************/ ++#ifdef CONFIG_ARCH_HI3516CV300 ++#include "himci_hi3516cv300.c" ++#endif ++ ++#ifdef CONFIG_ARCH_HI3519 ++#include "himci_hi3519.c" ++#endif ++ ++#ifdef CONFIG_ARCH_HI3519V101 ++#include "himci_hi3519v101.c" ++#endif ++ ++#ifdef CONFIG_ARCH_HI3516AV200 ++#include "himci_hi3516av200.c" ++#endif ++ ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++#include "himci_hi3559.c" ++#endif ++ ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++ #define PERF_OPT_RATIO 8 ++ #define MAX_SEGS 4096 ++#else ++ #define PERF_OPT_RATIO 1 ++ #define MAX_SEGS 1024 ++#endif ++ ++/*************************************************************************/ ++#define DRIVER_NAME "himci" ++ ++#ifdef CONFIG_DETECT_CARD_TIME ++static unsigned int detect_time = HZ*CONFIG_DETECT_CARD_TIME/1000; ++#else ++static unsigned int detect_time = HI_MCI_DETECT_TIMEOUT; ++#endif ++ ++static unsigned int retry_count = MAX_RETRY_COUNT; ++static unsigned int request_timeout = HI_MCI_REQUEST_TIMEOUT; ++int trace_level = HIMCI_TRACE_LEVEL; ++unsigned int slot_index = 0; ++struct himci_host *mci_host[HIMCI_SLOT_NUM] = {NULL}; ++ ++#ifdef MODULE ++ ++module_param(detect_time, uint, 0600); ++MODULE_PARM_DESC(detect_timer, "card detect time (default:500ms))"); ++ ++module_param(retry_count, uint, 0600); ++MODULE_PARM_DESC(retry_count, "retry count times (default:100))"); ++ ++module_param(request_timeout, uint, 0600); ++MODULE_PARM_DESC(request_timeout, "Request timeout time (default:3s))"); ++ ++module_param(trace_level, int, 0600); ++MODULE_PARM_DESC(trace_level, "HIMCI_TRACE_LEVEL"); ++ ++#endif ++ ++/* reset MMC host controller */ ++static void himciv200_sys_reset(struct himci_host *host) ++{ ++ unsigned int reg_value; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ reg_value = himci_readl(host->base + MCI_BMOD); ++ reg_value |= BMOD_SWR; ++ himci_writel(reg_value, host->base + MCI_BMOD); ++ mdelay(10); ++ ++ reg_value = himci_readl(host->base + MCI_BMOD); ++ reg_value |= BURST_16 | BURST_INCR; ++ himci_writel(reg_value, host->base + MCI_BMOD); ++ ++ reg_value = himci_readl(host->base + MCI_CTRL); ++ reg_value |= CTRL_RESET | FIFO_RESET | DMA_RESET; ++ himci_writel(reg_value, host->base + MCI_CTRL); ++ ++ local_irq_restore(flags); ++} ++ ++static void himci_ctrl_power(struct himci_host *host, ++ unsigned int flag, unsigned int force) ++{ ++ unsigned int port; ++ ++ himci_trace(2, "begin"); ++ ++ port = host->cur_port; ++ ++ if (host->power_status != flag || force == FORCE_ENABLE) { ++ unsigned int reg_value; ++ ++ if (flag == POWER_OFF) { ++ reg_value = himci_readl(host->base + MCI_RESET_N); ++ reg_value &= ~(MMC_RST_N << port); ++ himci_writel(reg_value, host->base + MCI_RESET_N); ++ } ++ ++ reg_value = himci_readl(host->base + MCI_PWREN); ++ if (flag == POWER_OFF) ++ reg_value &= ~(0x1 << port); ++ else ++ reg_value |= (0x1 << port); ++ ++ himci_writel(reg_value, host->base + MCI_PWREN); ++ ++ if (flag == POWER_ON) { ++ reg_value = himci_readl(host->base + MCI_RESET_N); ++ reg_value |= (MMC_RST_N << port); ++ himci_writel(reg_value, host->base + MCI_RESET_N); ++ } ++ ++ if (in_interrupt()) ++ mdelay(100); ++ else ++ msleep(100); ++ ++ host->power_status = flag; ++ } ++} ++ ++static void himciv200_host_power(struct himci_host *host, unsigned int power_on, ++ unsigned int force) ++{ ++ if (host->power_status != power_on || force) { ++ if (!power_on) { ++ himci_writel(0, host->base + MCI_RESET_N); ++ himci_writel(0, host->base + MCI_PWREN); ++ } else { ++ himci_writel(1, host->base + MCI_PWREN); ++ himci_writel(1, host->base + MCI_RESET_N); ++ } ++ ++ mdelay(100); ++ ++ host->power_status = power_on; ++ } ++} ++ ++static void himci_idma_start(struct himci_host *host) ++{ ++ unsigned int tmp; ++ ++ himci_trace(2, "begin"); ++ ++ tmp = himci_readl(host->base + MCI_BMOD); ++ tmp |= BMOD_DMA_EN; ++ himci_writel(tmp, host->base + MCI_BMOD); ++} ++ ++/********************************************** ++ *1: card off ++ *0: card on ++ ***********************************************/ ++static unsigned int himci_sys_card_detect(struct himci_host *host) ++{ ++ unsigned int card_status; ++ ++ card_status = himci_readl(host->base + MCI_CDETECT); ++ ++ /* eMMC need't detect, but we check if eMMC controller available */ ++ if (!(host->mmc->caps & MMC_CAP_SD_HIGHSPEED)) ++ return himci_check_emmc(host); ++ ++ card_status &= HIMCI_CARD0; ++ return card_status; ++} ++ ++/********************************************** ++ *1: card readonly ++ *0: card read/write ++ ***********************************************/ ++static unsigned int himci_ctrl_card_readonly(struct himci_host *host) ++{ ++ unsigned int card_value = himci_readl(host->base + MCI_WRTPRT); ++ unsigned int port = host->cur_port; ++ ++ return card_value & (HIMCI_CARD0 << port); ++} ++ ++static int himci_wait_cmd(struct himci_host *host) ++{ ++ int wait_retry_count = 0; ++ unsigned int reg_data = 0; ++ unsigned long flags; ++ ++ while (1) { ++ /* ++ Check if CMD::start_cmd bit is clear. ++ start_cmd = 0 means MMC Host controller has loaded registers ++ and next command can be loaded in. ++ */ ++ reg_data = himci_readl(host->base + MCI_CMD); ++ if ((reg_data & START_CMD) == 0) ++ return 0; ++ ++ /* Check if Raw_Intr_Status::HLE bit is set. */ ++ spin_lock_irqsave(&host->lock, flags); ++ reg_data = himci_readl(host->base + MCI_RINTSTS); ++ if (reg_data & HLE_INT_STATUS) { ++ reg_data |= HLE_INT_STATUS; ++ himci_writel(reg_data, host->base + MCI_RINTSTS); ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ himci_trace(5, "Other CMD is running," ++ "please operate cmd again!"); ++ return 1; ++ } ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ udelay(100); ++ ++ /* Check if number of retries for this are over. */ ++ wait_retry_count++; ++ if (wait_retry_count >= retry_count) { ++ himci_trace(3, "send cmd is timeout!"); ++ return -1; ++ } ++ } ++} ++ ++static void himci_control_cclk(struct himci_host *host, unsigned int flag) ++{ ++ unsigned int reg; ++ union cmd_arg_u cmd_reg; ++ unsigned int port = host->cur_port; ++ ++ himci_trace(2, "begin"); ++ himci_assert(host); ++ ++ reg = himci_readl(host->base + MCI_CLKENA); ++ if (flag == ENABLE) { ++ reg |= (CCLK_ENABLE << port); ++ reg |= (0x10000 << port); ++ } else { ++ reg &= ~(CCLK_ENABLE << port); ++ reg &= ~(0x10000 << port); ++ } ++ ++ himci_writel(reg, host->base + MCI_CLKENA); ++ cmd_reg.cmd_arg = himci_readl(host->base + MCI_CMD); ++ cmd_reg.bits.start_cmd = 1; ++ cmd_reg.bits.card_number = port; ++ cmd_reg.bits.cmd_index = 0; ++ cmd_reg.bits.data_transfer_expected = 0; ++ cmd_reg.bits.update_clk_reg_only = 1; ++ cmd_reg.bits.response_expect = 0; ++ cmd_reg.bits.send_auto_stop = 0; ++ cmd_reg.bits.wait_prvdata_complete = 0; ++ cmd_reg.bits.check_response_crc = 0; ++ himci_writel(cmd_reg.cmd_arg, host->base + MCI_CMD); ++ if (himci_wait_cmd(host) != 0) ++ himci_trace(3, "disable or enable clk is timeout!"); ++} ++ ++static void himci_set_cclk(struct himci_host *host, unsigned int cclk) ++{ ++ unsigned int reg_value = 0; ++ union cmd_arg_u clk_cmd; ++ unsigned int port = host->cur_port; ++ unsigned int hclk; ++ unsigned int clk_rate; ++ int ret; ++ ++ himci_trace(2, "begin"); ++ himci_assert(host); ++ himci_assert(cclk); ++ ++ clk_rate = cclk > MMC_CRG_MIN ? cclk : MMC_CRG_MIN; ++ ret = clk_set_rate(host->clk, clk_rate); ++ if (ret) ++ dev_warn(mmc_dev(host->mmc), ++ "failed to set rate %uHz\n", clk_rate); ++ ++ hclk = clk_get_rate(host->clk); ++ ++ /* ++ * set card clk divider value, ++ * clk_divider = Fmmcclk/(Fmmc_cclk * 2) ++ */ ++ reg_value = hclk / (cclk * 2); ++ if ((hclk % (cclk * 2)) && (hclk > cclk)) ++ reg_value++; ++ if (reg_value > 0xFF) ++ reg_value = 0xFF; ++ ++ host->hclk = hclk; ++ host->cclk = reg_value ? (hclk / (reg_value * 2)) : hclk; ++ himci_writel(reg_value, host->base + MCI_CLKDIV); ++ clk_cmd.cmd_arg = himci_readl(host->base + MCI_CMD); ++ clk_cmd.bits.start_cmd = 1; ++ clk_cmd.bits.card_number = port; ++ clk_cmd.bits.update_clk_reg_only = 1; ++ clk_cmd.bits.cmd_index = 0; ++ clk_cmd.bits.data_transfer_expected = 0; ++ clk_cmd.bits.response_expect = 0; ++ himci_writel(clk_cmd.cmd_arg, host->base + MCI_CMD); ++ if (himci_wait_cmd(host) != 0) ++ himci_trace(3, "set card clk divider is failed!"); ++} ++ ++static void himci_pre_init_host(struct himci_host *host) ++{ ++#if !defined(CONFIG_ARCH_HI3516CV300) ++ unsigned int shift[] = {13, 5, 21}; ++ unsigned int reg; ++ ++ reg = himci_readl(PERI_CRG49); ++ reg |= 0x1 << shift[host->devid]; ++ himci_writel(reg, PERI_CRG49); ++ ++ if (!(host->mmc->caps & MMC_CAP_SD_HIGHSPEED)) { ++ himci_writel(0x10, EMMC_DLL_CTRL); ++ reg = himci_readl(PERI_CRG49); ++ reg |= 0x1 << 22; ++ himci_writel(reg, PERI_CRG49); ++ } ++#endif ++} ++ ++static void himci_init_host(struct himci_host *host) ++{ ++ unsigned int tmp_reg = 0; ++ struct mmc_host *mmc; ++ ++ himci_trace(3, "begin init card"); ++ himci_assert(host); ++ ++ mmc = host->mmc; ++ host->error_count = 0; ++ /* controller config gpio */ ++ himci_writel(CMD_OUT_EN_FIX_BYPASS | DTO_FIX_BYPASS, ++ host->base + MCI_GPIO); ++ ++ himciv200_sys_reset(host); ++ ++ himci_pre_init_host(host); ++ ++ /* set drv/smpl phase shift */ ++ if (mmc->caps & MMC_CAP_HW_RESET) ++ tmp_reg |= EMMC_SMPL_PHASE_DFLT | EMMC_DRV_PHASE_DFLT; ++ else ++ tmp_reg |= SMPL_PHASE_DFLT | DRV_PHASE_DFLT; ++ ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ ++ /* set card read threshold */ ++ himci_writel(RW_THRESHOLD_SIZE, host->base + MCI_CARDTHRCTL); ++ ++ /* clear MMC host intr */ ++ himci_writel(ALL_INT_CLR, host->base + MCI_RINTSTS); ++ tmp_reg = himci_readl(host->base + MCI_IDSTS); ++ tmp_reg |= ALL_ADMA_INT_CLR; ++ himci_writel(tmp_reg, host->base + MCI_IDSTS); ++ ++ /* MASK MMC all host intr */ ++ tmp_reg = himci_readl(host->base + MCI_INTMASK); ++ tmp_reg &= ~ALL_INT_MASK; ++ himci_writel(tmp_reg, host->base + MCI_INTMASK); ++ ++ /* enable inner DMA mode and close intr of MMC host controller */ ++ tmp_reg = himci_readl(host->base + MCI_CTRL); ++ tmp_reg &= ~INTR_EN; ++ tmp_reg |= USE_INTERNAL_DMA | INTR_EN; ++ himci_writel(tmp_reg, host->base + MCI_CTRL); ++ ++ /* set timeout param */ ++ himci_writel(DATA_TIMEOUT | RESPONSE_TIMEOUT, host->base + MCI_TIMEOUT); ++ ++ /* set FIFO param */ ++ tmp_reg = 0; ++ tmp_reg |= BURST_SIZE | RX_WMARK | TX_WMARK; ++ himci_writel(tmp_reg, host->base + MCI_FIFOTH); ++ ++ /* ADMA3: disable ADMA Func */ ++ himci_writel(0x0, host->base + ADMA_CTRL); ++ ++ /* ADMA3: set entire des addr and quene deepth */ ++ himci_writel(host->entire_paddr, host->base + ADMA_Q_ADDR); /* */ ++ himci_writel(ADMA_QUEUE_DEEPTH, host->base + ADMA_Q_DEEPTH); ++ ++ /* ADMA3: reset queue read/write ptr */ ++ tmp_reg = himci_readl(host->base + ADMA_CTRL); ++ tmp_reg |= RDPTR_MOD_EN; ++ himci_writel(tmp_reg, host->base + ADMA_CTRL); ++ himci_writel(0x0, host->base + ADMA_Q_RDPTR); ++ himci_writel(0x0, host->base + ADMA_Q_WRPTR); ++ tmp_reg = himci_readl(host->base + ADMA_CTRL); ++ tmp_reg &= ~RDPTR_MOD_EN; ++ himci_writel(tmp_reg, host->base + ADMA_CTRL); ++ ++ /* ADMA3: set queue timeout value */ ++ himci_writel(0xffffffff, host->base + ADMA_Q_TO); ++ ++ /* ADMA3: enable ADMA intr */ ++ tmp_reg = (CMD_LOCK_ERR | OWNBIT_ERR | QUEUE_OVERFLOW ++ | RESP_CHECK_ERR | PACKET_INT | PACKET_TO_INT ++ | AUTO_STOP_ERR | QUEUE_FULL | CES | DU | FBE); ++ ++ himci_writel(tmp_reg, host->base + MCI_IDINTEN); ++ /* ADMA3: clear ADMA3 intr */ ++ tmp_reg = (CMD_LOCK_ERR | OWNBIT_ERR | QUEUE_OVERFLOW ++ | RESP_CHECK_ERR | PACKET_INT | PACKET_TO_INT ++ | AUTO_STOP_ERR | QUEUE_FULL | QUEUE_EMPTY | CES ++ | DU | FBE); ++ himci_writel(tmp_reg, host->base + MCI_IDSTS); ++ ++ /* ADMA3: enable ADMA Func */ ++ tmp_reg = PACKET_INT_EN | ADMA3_EN; ++ himci_writel(tmp_reg, host->base + ADMA_CTRL); ++ ++ himci_idma_start(host); ++ host->mmc->status = MMC_HOST_OK; ++} ++ ++void himci_sw_reset(struct mmc_host *mmc) ++{ ++ struct himci_host *host = mmc_priv(mmc); ++ int i; ++ ++ clk_prepare_enable(host->clk); ++ himci_init_host(host); ++ ++ for (i = 0; i < ADMA_QUEUE_DEEPTH ; i++) ++ host->pmrq[i] = NULL; ++} ++ ++static void himci_detect_card(unsigned long arg) ++{ ++ struct himci_host *host = (struct himci_host *)arg; ++ unsigned int i, curr_status, status[3], detect_retry_count = 0; ++ ++ himci_assert(host); ++ ++ while (1) { ++ for (i = 0; i < 3; i++) { ++ status[i] = himci_sys_card_detect(host); ++ udelay(10); ++ } ++ if ((status[0] == status[1]) && (status[0] == status[2])) ++ break; ++ detect_retry_count++; ++ if (detect_retry_count >= retry_count) { ++ himci_error("this is a dithering, card detect error!"); ++ goto err; ++ } ++ } ++ curr_status = status[0]; ++ if (curr_status != host->card_status) { ++ himci_trace(2, "begin card_status = %d\n", host->card_status); ++ host->card_status = curr_status; ++ if (curr_status != CARD_UNPLUGED) { ++ himci_init_host(host); ++ pr_info("card connected!\n"); ++ } else { ++ pr_info("card disconnected!\n"); ++ host->mmc->status = MMC_HOST_ERR; ++ } ++ ++ mmc_detect_change(host->mmc, 0); ++ } ++err: ++ mod_timer(&host->timer, jiffies + detect_time); ++} ++ ++static int himci_prep_data(struct himci_host *host, struct mmc_data *data) ++{ ++ unsigned int sg_phyaddr, sg_length; ++ unsigned int i, ret = 0; ++ unsigned int data_size; ++ unsigned int max_des, des_cnt; ++ struct himci_dma_des *dma_des; ++ struct himci_dma_des *des; ++ unsigned int *dma_paddr; ++ unsigned int dma_dir; ++ ++ himci_trace(2, "begin"); ++ himci_assert(host); ++ himci_assert(data); ++ ++ host->data = data; ++ ++ if (data->flags & MMC_DATA_READ) ++ dma_dir = DMA_FROM_DEVICE; ++ else ++ dma_dir = DMA_TO_DEVICE; ++ ++ host->dma_sg = data->sg; ++ host->dma_sg_num = dma_map_sg(mmc_dev(host->mmc), data->sg, ++ data->sg_len, dma_dir); ++ himci_assert(host->dma_sg_num); ++ himci_trace(2, "sg_num is %d\n", host->dma_sg_num); ++ ++ data_size = data->blksz * data->blocks; ++ if (data_size > (DMA_BUFFER * MAX_DMA_DES)) { ++ himci_error("mci request data_size is too big!\n"); ++ ret = -1; ++ goto out; ++ } ++ ++ himci_trace(2, "host->dma_paddr is 0x%08X,host->dma_addr is 0x%08X\n", ++ (unsigned int)host->dma_paddr, ++ (unsigned int)host->dma_addr); ++ ++ max_des = PERF_OPT_RATIO * PAGE_SIZE / sizeof(struct himci_dma_des) - 1; ++ ++ /* dma descriptor is followed by cmd descriptor */ ++ dma_des = (struct himci_dma_des *)(host->wr_cmd_des + 1); ++ dma_paddr = (unsigned int *)(host->cmd_paddr + PERF_OPT_RATIO * PAGE_SIZE * host->wr_pos ++ + sizeof(struct himci_cmd_des)); ++ ++ des = dma_des; ++ des_cnt = 0; ++ ++ for (i = 0; i < host->dma_sg_num; i++) { ++ sg_length = sg_dma_len(&data->sg[i]); ++ sg_phyaddr = sg_dma_address(&data->sg[i]); ++ himci_trace(2, "sg[%d] sg_length is 0x%08X," ++ " sg_phyaddr is 0x%08X\n", ++ i, ++ (unsigned int)sg_length, (unsigned int)sg_phyaddr); ++ while (sg_length) { ++ des[des_cnt].idmac_des_ctrl = ++ DMA_DES_OWN | DMA_DES_NEXT_DES; ++ des[des_cnt].idmac_des_buf_addr = sg_phyaddr; ++ /* idmac_des_next_addr is paddr for dma */ ++ des[des_cnt].idmac_des_next_addr = (unsigned long) ++ (dma_paddr + (des_cnt + 1) * 4); ++ ++ /* buffer size <= 4k */ ++ if (sg_length >= 0x1000) { ++ des[des_cnt].idmac_des_buf_size = 0x1000; ++ sg_length -= 0x1000; ++ sg_phyaddr += 0x1000; ++ } else { ++ /* data alignment */ ++ des[des_cnt].idmac_des_buf_size = sg_length; ++ sg_length = 0; ++ } ++ himci_trace(2, "des[%d] vaddr is 0x%08X", ++ des_cnt, (unsigned int)&des[des_cnt]); ++ himci_trace(2, "des[%d].idmac_des_ctrl is 0x%08X", ++ des_cnt, (unsigned int) ++ des[des_cnt].idmac_des_ctrl); ++ himci_trace(2, "des[%d].idmac_des_buf_size is 0x%08X", ++ des_cnt, (unsigned int) ++ des[des_cnt].idmac_des_buf_size); ++ himci_trace(2, "des[%d].idmac_des_buf_addr 0x%08X", ++ des_cnt, ++ (unsigned int)des[des_cnt].idmac_des_buf_addr); ++ himci_trace(2, "des[%d].idmac_des_next_addr is 0x%08X", ++ des_cnt, (unsigned int) ++ des[des_cnt].idmac_des_next_addr); ++ des_cnt++; ++ } ++ ++ himci_assert(des_cnt < max_des); ++ } ++ des[0].idmac_des_ctrl |= DMA_DES_FIRST_DES; ++ des[des_cnt - 1].idmac_des_ctrl |= DMA_DES_LAST_DES; ++ des[des_cnt - 1].idmac_des_next_addr = 0; ++ himci_trace(2, "des[%d].idmac_des_ctrl is 0x%08X", ++ des_cnt-1, (unsigned int)des[des_cnt-1].idmac_des_ctrl); ++out: ++ return ret; ++} ++ ++static int himci_prep_cmd(struct himci_host *host, struct mmc_command *cmd, ++ struct mmc_data *data) ++{ ++ volatile union cmd_arg_u cmd_regs; ++ unsigned int port = host->cur_port; ++ struct himci_cmd_des *cmd_des; ++ ++ himci_trace(2, "begin"); ++ himci_assert(host); ++ himci_assert(cmd); ++ ++ host->cmd = cmd; ++ ++ cmd_des = host->wr_cmd_des; ++ cmd_des->arg = cmd->arg; ++ ++ himci_trace(3, "arg_reg 0x%x, val 0x%x\n", MCI_CMDARG, cmd->arg); ++ cmd_regs.cmd_arg = DEFAULT_CMD_VALUE; ++ if (data) { ++ cmd_regs.bits.data_transfer_expected = 1; ++ if (data->flags & (MMC_DATA_WRITE | MMC_DATA_READ)) ++ cmd_regs.bits.transfer_mode = 0; ++ if (data->flags & MMC_DATA_STREAM) ++ cmd_regs.bits.transfer_mode = 1; ++ if (data->flags & MMC_DATA_WRITE) ++ cmd_regs.bits.read_write = 1; ++ else if (data->flags & MMC_DATA_READ) ++ cmd_regs.bits.read_write = 0; ++ } else { ++ cmd_regs.bits.data_transfer_expected = 0; ++ cmd_regs.bits.transfer_mode = 0; ++ cmd_regs.bits.read_write = 0; ++ } ++ ++#ifdef CONFIG_SEND_AUTO_STOP ++ if (host->mrq->stop) ++ cmd_regs.bits.send_auto_stop = 1; ++#endif ++ ++ if (cmd->opcode == MMC_STOP_TRANSMISSION) { ++ cmd_regs.bits.stop_abort_cmd = 1; ++ cmd_regs.bits.wait_prvdata_complete = 0; ++ } else { ++ cmd_regs.bits.stop_abort_cmd = 0; ++ cmd_regs.bits.wait_prvdata_complete = 1; ++ } ++ ++ switch (mmc_resp_type(cmd)) { ++ case MMC_RSP_NONE: ++ cmd_regs.bits.response_expect = 0; ++ cmd_regs.bits.response_length = 0; ++ cmd_regs.bits.check_response_crc = 0; ++ break; ++ case MMC_RSP_R1: ++ case MMC_RSP_R1B: ++ cmd_regs.bits.response_expect = 1; ++ cmd_regs.bits.response_length = 0; ++ cmd_regs.bits.check_response_crc = 1; ++ break; ++ case MMC_RSP_R2: ++ cmd_regs.bits.response_expect = 1; ++ cmd_regs.bits.response_length = 1; ++ cmd_regs.bits.check_response_crc = 1; ++ break; ++ case MMC_RSP_R3: ++ case MMC_RSP_R1 & (~MMC_RSP_CRC): ++ cmd_regs.bits.response_expect = 1; ++ cmd_regs.bits.response_length = 0; ++ cmd_regs.bits.check_response_crc = 0; ++ break; ++ default: ++ host->cmd->error = -EINVAL; ++ himci_error("himci: unhandled response type %02x\n", ++ mmc_resp_type(cmd)); ++ return -EINVAL; ++ } ++ ++ himci_trace(4, "cmd->opcode = %d cmd->arg = 0x%X\n", ++ cmd->opcode, cmd->arg); ++ ++ if (cmd->opcode == SD_SWITCH_VOLTAGE) ++ cmd_regs.bits.volt_switch = 1; ++ else ++ cmd_regs.bits.volt_switch = 0; ++ ++ cmd_regs.bits.send_initialization = 1; ++ cmd_regs.bits.card_number = port; ++ cmd_regs.bits.cmd_index = cmd->opcode; ++ cmd_regs.bits.start_cmd = 1; ++ cmd_regs.bits.update_clk_reg_only = 0; ++ ++ cmd_des->cmd = cmd_regs.cmd_arg; ++ ++ himci_trace(4, "cmd_reg 0x%x, val 0x%x\n", MCI_CMD, cmd_regs.cmd_arg); ++ ++ return 0; ++} ++ ++static void himci_data_done(struct mmc_host *host, ++ struct mmc_request *mrq, int err) ++{ ++ struct mmc_data *data = mrq->data; ++ unsigned int dma_dir; ++ ++ if (!data) ++ return; ++ if (data->flags & MMC_DATA_READ) ++ dma_dir = DMA_FROM_DEVICE; ++ else ++ dma_dir = DMA_TO_DEVICE; ++ dma_unmap_sg(mmc_dev(host), data->sg, data->sg_len, dma_dir); ++ ++ if (!data->error) ++ data->bytes_xfered = data->blocks * data->blksz; ++ else ++ data->bytes_xfered = 0; ++ ++} ++ ++static void himci_finish_request(struct himci_host *host, ++ struct mmc_request *mrq) ++{ ++ himci_trace(2, "begin"); ++ himci_assert(host); ++ himci_assert(mrq); ++ ++ himci_data_done(host->mmc, mrq, 0); ++ mmc_request_done(host->mmc, mrq); ++} ++ ++static void himci_cmd_done(struct mmc_request *mrq, unsigned int stat) ++{ ++ struct mmc_command *cmd = mrq->cmd; ++ ++ himci_trace(2, "begin"); ++ himci_assert(cmd); ++ ++ if (stat & RTO_INT_STATUS) { ++ cmd->error = -ETIMEDOUT; ++ himci_trace(3, "irq cmd status stat = 0x%x is timeout error!", ++ stat); ++ } else if (stat & (RCRC_INT_STATUS | RE_INT_STATUS)) { ++ cmd->error = -EILSEQ; ++ himci_error("irq cmd status stat = 0x%x is response error!", ++ stat); ++ } ++} ++ ++#define CMD_ERRORS \ ++ (R1_OUT_OF_RANGE | /* Command argument out of range */ \ ++ R1_ADDRESS_ERROR | /* Misaligned address */ \ ++ R1_BLOCK_LEN_ERROR | /* Transferred block length incorrect */\ ++ R1_WP_VIOLATION | /* Tried to write to protected block */ \ ++ R1_CC_ERROR | /* Card controller error */ \ ++ R1_ERROR) /* General/unknown error */ ++ ++static void himci_read_response(struct himci_host *host, ++ struct mmc_command *cmd) ++{ ++ /* Read response of the card */ ++ if (cmd->flags & MMC_RSP_PRESENT) { ++ if (cmd->flags & MMC_RSP_136) { ++ cmd->resp[3] = himci_readl(host->base + MCI_RESP0); ++ cmd->resp[2] = himci_readl(host->base + MCI_RESP1); ++ cmd->resp[1] = himci_readl(host->base + MCI_RESP2); ++ cmd->resp[0] = himci_readl(host->base + MCI_RESP3); ++ } else { ++ cmd->resp[0] = himci_readl(host->base + MCI_RESP0); ++ cmd->resp[1] = 0; ++ cmd->resp[2] = 0; ++ cmd->resp[3] = 0; ++ } ++ } ++ if ((cmd->flags & MMC_CMD_TYPE_RW) && (cmd->resp[0] & CMD_ERRORS)) ++ host->error_count++; ++} ++ ++static void himci_reset_host(struct himci_host *host) ++{ ++ unsigned int i = 0, reg_value; ++ ++ /* DMA Controller resets */ ++ reg_value = himci_readl(host->base + ADMA_CTRL); ++ reg_value &= ~(ADMA3_EN); ++ himci_writel(reg_value, host->base + ADMA_CTRL); ++ reg_value = himci_readl(host->base + MCI_BMOD); ++ reg_value |= BMOD_SWR; ++ himci_writel(reg_value, host->base + MCI_BMOD); ++ himci_writel(0xFFFFFFFF, host->base + MCI_IDSTS); ++ ++ /* ADMA3: reset queue R/W ptr */ ++ reg_value = himci_readl(host->base + ADMA_CTRL); ++ reg_value |= (RDPTR_MOD_EN); ++ reg_value &= ~(ADMA3_EN); ++ himci_writel(reg_value, host->base + ADMA_CTRL); ++ himci_writel(0, host->base + ADMA_Q_WRPTR); ++ himci_writel(0, host->base + ADMA_Q_RDPTR); ++ for (i = 0; i < ADMA_QUEUE_DEEPTH ; i++) ++ host->pmrq[i] = NULL; ++ ++ himci_writel(0xFFFFFFFF, host->base + MCI_RINTSTS); ++ udelay(800); ++ ++ reg_value = himci_readl(host->base + ADMA_CTRL); ++ reg_value &= ~RDPTR_MOD_EN; ++ reg_value |= (ADMA3_EN); ++ himci_writel(reg_value, host->base + ADMA_CTRL); ++ ++ /* ADMA3: restart */ ++ reg_value = himci_readl(host->base + ADMA_CTRL); ++ reg_value |= ADMA3_RESTART; ++ himci_writel(reg_value, host->base + ADMA_CTRL); ++ ++ /* dma enable */ ++ himci_idma_start(host); ++} ++ ++static void himci_err_check(struct himci_host *host, ++ unsigned int qstat, unsigned int rstat, ++ unsigned int *cmd_err, unsigned int *data_err) ++{ ++ *cmd_err = 0; ++ *data_err = 0; ++ ++ if (!(qstat & (ADMA_INT_ERR | CES))) ++ return; ++ ++ if (qstat & ADMA_INT_ERR) { ++ himci_trace(4, "ADMA err! qstat:%x rstat:%x\n", qstat, rstat); ++ *cmd_err = -ETIMEDOUT; ++ } ++ ++ if (qstat & CES) { ++ himci_trace(4, "CES err! qstat:%x rstat:%x\n", qstat, rstat); ++ if (rstat & RTO_INT_STATUS) { ++ himci_trace(3, "response timeout error!"); ++ *cmd_err = -ETIMEDOUT; ++ } else if (rstat & (RCRC_INT_STATUS | RE_INT_STATUS)) { ++ himci_trace(3, "response crc error!"); ++ *cmd_err = -EILSEQ; ++ } ++ ++ if (rstat & (HTO_INT_STATUS | DRTO_INT_STATUS)) { ++ himci_trace(3, "data transfer timeout error!"); ++ *data_err = -ETIMEDOUT; ++ } else if (rstat & (EBE_INT_STATUS | SBE_INT_STATUS | ++ FRUN_INT_STATUS | DCRC_INT_STATUS)) { ++ himci_trace(3, "data transfer error!"); ++ *data_err = -EILSEQ; ++ } ++ } ++ ++ if (*cmd_err || *data_err) ++ himci_reset_host(host); ++} ++ ++static int himci_wait_voltage_switch(struct himci_host *host) ++{ ++ unsigned int cmd_retry_count = 0; ++ unsigned long cmd_jiffies_timeout; ++ unsigned int cmd_irq_reg = 0; ++ struct mmc_command *cmd = host->cmd; ++ unsigned long flags; ++ ++ himci_trace(2, "begin"); ++ himci_assert(host); ++ himci_assert(cmd); ++ ++ himci_trace(3, "wait voltage switch..."); ++ cmd_jiffies_timeout = jiffies + request_timeout; ++ while (1) { ++ do { ++ spin_lock_irqsave(&host->lock, flags); ++ cmd_irq_reg = himci_readl(host->base + MCI_RINTSTS); ++ if (cmd_irq_reg & VOLT_SWITCH_INT_STATUS) { ++ himci_writel(VOLT_SWITCH_INT_STATUS, ++ host->base + MCI_RINTSTS); ++ spin_unlock_irqrestore(&host->lock, flags); ++ himci_cmd_done(host->mrq, cmd_irq_reg); ++ goto switch_succeed; ++ } ++ spin_unlock_irqrestore(&host->lock, flags); ++ cmd_retry_count++; ++ } while (cmd_retry_count < retry_count); ++ cmd_retry_count = 0; ++ ++ if (host->card_status == CARD_UNPLUGED) { ++ cmd->error = -ETIMEDOUT; ++ return -1; ++ } ++ ++ if (!time_before(jiffies, cmd_jiffies_timeout)) { ++ unsigned int i = 0; ++ ++ for (i = 0; i < 4; i++) { ++ cmd->resp[i] = himci_readl(host->base ++ + MCI_RESP0 + i * 0x4); ++ } ++ cmd->error = -ETIMEDOUT; ++ himci_trace(3, "wait cmd request complete is timeout!"); ++ return -1; ++ } ++ ++ schedule(); ++ } ++switch_succeed: ++ /* bugfix: synopsys will trigger sdio intr when send cmd11, ++ * it's a bug and must be cleared ++ */ ++ himci_writel(SDIO_INT_STATUS, host->base + MCI_RINTSTS); ++ return 0; ++} ++ ++/* Prepare the entire descriptor */ ++static void himci_prep_entire(struct himci_host *host, ++ struct mmc_request *mrq) ++{ ++ struct himci_entire_des *ent_des; ++ struct himci_cmd_des *cmd_des; ++ unsigned int wr_pos; ++ ++ wr_pos = himci_readl(host->base + ADMA_Q_WRPTR); ++ mrq->wr_pos = wr_pos; /* only for ADMA3 R/W */ ++ host->pmrq[wr_pos] = mrq; /* save send mrq */ ++ host->wr_pos = wr_pos; ++ ++ himci_trace(3, "write des to position %d", wr_pos); ++ /* select entire descriptor */ ++ ent_des = (struct himci_entire_des *)(host->entire_addr); ++ ent_des = ent_des + wr_pos; ++ host->wr_ent_des = ent_des; ++ ++ /* config entire descriptor */ ++ ent_des->cmd_des_addr = host->cmd_paddr + PERF_OPT_RATIO * PAGE_SIZE * wr_pos; ++ ++ if ((host->cmd->flags & MMC_CMD_NON_BLOCKING) ++ || (mrq->cmd->opcode == SD_SWITCH_VOLTAGE)) { ++ /* non-blocking command needn't issue intr */ ++ himci_trace(2, "command is non-blocking"); ++ ent_des->ctrl = 0x80000000; ++ } else { ++ himci_trace(2, "command is block"); ++ ent_des->ctrl = 0xa0000000; ++ } ++ himci_trace(2, "ent des addr: 0x%x", host->entire_paddr ++ + sizeof(struct himci_entire_des) * wr_pos); ++ ++ /* select command descriptor */ ++ cmd_des = (struct himci_cmd_des *)(host->cmd_addr); ++ cmd_des = cmd_des + (PERF_OPT_RATIO * 256 * wr_pos); ++ host->wr_cmd_des = cmd_des; ++} ++ ++static void himci_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct himci_host *host = mmc_priv(mmc); ++ unsigned int byte_cnt = 0; ++ unsigned int status; ++ int ret = 0, i = 0; ++ struct himci_cmd_des *cmd_des; ++ unsigned int rd_pos; ++ ++ himci_trace(2, "begin"); ++ himci_assert(mmc); ++ himci_assert(mrq); ++ himci_assert(host); ++ ++ host->mrq = mrq; ++ host->cmd = mrq->cmd; ++ host->data = mrq->data; ++ ++ /* prepare entire des */ ++ himci_prep_entire(host, mrq); ++ cmd_des = host->wr_cmd_des; ++ ++ /* prepare data des */ ++ if (mrq->data) { ++ ret = himci_prep_data(host, mrq->data); ++ if (ret) { ++ mrq->data->error = ret; ++ himci_trace(3, "data setup is error!"); ++ goto request_done; ++ } ++ ++ byte_cnt = mrq->data->blksz * mrq->data->blocks; ++ cmd_des->blk_sz = mrq->data->blksz; ++ cmd_des->byte_cnt = byte_cnt; ++ himci_trace(3, "blk_sz:%d blk_cnt:%d", ++ mrq->data->blksz, mrq->data->blocks); ++ } else { ++ cmd_des->blk_sz = 0; ++ cmd_des->byte_cnt = 0; ++ } ++ ++ /* prepare command des */ ++ ret = himci_prep_cmd(host, mrq->cmd, mrq->data); ++ if (ret) ++ goto request_done; ++ ++ if (mmc->caps & MMC_CAP_SD_HIGHSPEED) { ++ status = himci_sys_card_detect(host); ++ if (status == CARD_UNPLUGED) { ++ for (i = 0; i < ADMA_QUEUE_DEEPTH ; i++) ++ host->pmrq[i] = NULL; ++ host->cmd->error = -ETIMEDOUT; ++ if (host->cmd->flags & MMC_CMD_TYPE_RW) ++ host->mmc->status = MMC_HOST_ERR; ++ goto request_done; ++ } ++ } ++ ++ /* update des write pointer */ ++ host->wr_pos++; ++ host->wr_pos %= ADMA_QUEUE_DEEPTH; ++ himci_writel(host->wr_pos, host->base + ADMA_Q_WRPTR); ++ ++ if (host->cmd->opcode == SD_SWITCH_VOLTAGE) { ++ ret = himci_wait_voltage_switch(host); ++ if (ret) ++ himci_trace(5, "voltage switch failed!"); ++ rd_pos = host->wr_pos ? host->wr_pos - 1 ++ : ADMA_QUEUE_DEEPTH - 1; ++ host->pmrq[rd_pos] = NULL; ++ goto request_done; ++ } ++ ++ if (!(host->cmd->flags & MMC_CMD_TYPE_RW)) { ++ long time; ++ ++ time = wait_event_timeout(host->intr_wait, host->cmd_done, HZ); ++ if (time <= 0) { ++ unsigned int rstat; ++ unsigned int qstat; ++ unsigned int cmd_err; ++ unsigned int data_err; ++ ++ rstat = himci_readl(host->base + MCI_RINTSTS); ++ qstat = himci_readl(host->base + MCI_IDSTS); ++ himci_err_check(host, qstat, rstat, ++ &cmd_err, &data_err); ++ mrq->cmd->error = -ETIMEDOUT; ++ himci_trace(5, "CMD%u wait event timeout", mrq->cmd->opcode); ++ } ++ ++ host->cmd_done = 0; ++ rd_pos = host->wr_pos ? host->wr_pos - 1 ++ : ADMA_QUEUE_DEEPTH - 1; ++ host->pmrq[rd_pos] = NULL; ++ himci_read_response(host, mrq->cmd); ++ goto request_done; ++ } ++ return; ++ ++request_done: ++ himci_finish_request(host, mrq); ++} ++ ++static int himci_do_voltage_switch(struct himci_host *host, ++ struct mmc_ios *ios) ++{ ++ struct mmc_host *mmc = host->mmc; ++ u32 ctrl; ++ unsigned int port = host->cur_port; ++ /* ++ * We first check whether the request is to set signalling voltage ++ * to 3.3V. If so, we change the voltage to 3.3V and return quickly. ++ */ ++ ctrl = himci_readl(host->base + MCI_UHS_REG); ++ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { ++ /* Set 1.8V Signal Enable in the MCI_UHS_REG to 1 */ ++ himci_trace(3, "switch voltage 330"); ++ ctrl &= ~(HI_SDXC_CTRL_VDD_180 << port); ++ himci_writel(ctrl, host->base + MCI_UHS_REG); ++ ++ /* Wait for 5ms */ ++ usleep_range(5000, 5500); ++ ++ /* 3.3V regulator output should be stable within 5ms */ ++ ctrl = himci_readl(host->base + MCI_UHS_REG); ++ if (!(ctrl & (HI_SDXC_CTRL_VDD_180 << port))) { ++ return 0; ++ } else { ++ himci_error(": Switching to 3.3V "); ++ himci_error("signalling voltage failed\n"); ++ return -EIO; ++ } ++ } else if (!(ctrl & (HI_SDXC_CTRL_VDD_180 << port)) && ++ (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)) { ++ /* Stop SDCLK */ ++ himci_trace(3, "switch voltage 180"); ++ himci_control_cclk(host, DISABLE); ++ ++ /* ++ * Enable 1.8V Signal Enable in the MCI_UHS_REG ++ */ ++ ctrl |= (HI_SDXC_CTRL_VDD_180 << port); ++ himci_writel(ctrl, host->base + MCI_UHS_REG); ++ ++ /* Wait for 8ms */ ++ usleep_range(8000, 8500); ++ ++ ctrl = himci_readl(host->base + MCI_UHS_REG); ++ if (ctrl & (HI_SDXC_CTRL_VDD_180 << port)) { ++ /* Provide SDCLK again and wait for 1ms */ ++ himci_control_cclk(host, ENABLE); ++ usleep_range(1000, 1500); ++ ++ if (mmc->caps2 & MMC_CAP2_HS200) { ++ /* eMMC needn't to check the int status*/ ++ return 0; ++ } ++ /* ++ * If CMD11 return CMD down, then the card ++ * was successfully switched to 1.8V signaling. ++ */ ++ ctrl = himci_readl(host->base + MCI_RINTSTS); ++ if ((ctrl & VOLT_SWITCH_INT_STATUS) ++ && (ctrl & CD_INT_STATUS)) { ++ himci_writel(VOLT_SWITCH_INT_STATUS | CD_INT_STATUS, ++ host->base + MCI_RINTSTS); ++ return 0; ++ } ++ } ++ ++ /* ++ * If we are here, that means the switch to 1.8V signaling ++ * failed. We power cycle the card, and retry initialization ++ * sequence by setting S18R to 0. ++ */ ++ ++ ctrl &= ~(HI_SDXC_CTRL_VDD_180 << port); ++ himci_writel(ctrl, host->base + MCI_UHS_REG); ++ ++ /* Wait for 5ms */ ++ usleep_range(5000, 5500); ++ ++ himci_ctrl_power(host, POWER_OFF, FORCE_DISABLE); ++ /* Wait for 1ms as per the spec */ ++ usleep_range(1000, 1500); ++ himci_ctrl_power(host, POWER_ON, FORCE_DISABLE); ++ ++ himci_control_cclk(host, DISABLE); ++ /* Wait for 1ms as per the spec */ ++ usleep_range(1000, 1500); ++ himci_control_cclk(host, ENABLE); ++ ++ himci_error(": Switching to 1.8V signalling "); ++ himci_error("voltage failed, retrying with S18R set to 0\n"); ++ return -EAGAIN; ++ } else ++ /* No signal voltage switch required */ ++ return 0; ++} ++ ++static int himci_start_signal_voltage_switch(struct mmc_host *mmc, ++ struct mmc_ios *ios) ++{ ++ struct himci_host *host = mmc_priv(mmc); ++ int err; ++ ++ err = himci_do_voltage_switch(host, ios); ++ return err; ++} ++ ++static int himci_send_stop(struct mmc_host *host) ++{ ++ struct mmc_command cmd = {0}; ++ int err; ++ ++ cmd.opcode = MMC_STOP_TRANSMISSION; ++ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; ++ err = mmc_wait_for_cmd(host, &cmd, 0); ++ return err; ++} ++ ++#if defined(CONFIG_ARCH_HI3516CV300) ++static void himci_edge_tuning_enable(struct himci_host *host) ++{ ++ unsigned int val; ++ ++ val = himci_readl(host->base + MCI_TUNING_CTRL); ++ val |= HW_TUNING_EN; ++ himci_writel(val, host->base + MCI_TUNING_CTRL); ++} ++ ++static void himci_edge_tuning_disable(struct himci_host *host) ++{ ++ unsigned int val; ++ ++ val = himci_readl(host->base + MCI_TUNING_CTRL); ++ val &= ~HW_TUNING_EN; ++ himci_writel(val, host->base + MCI_TUNING_CTRL); ++} ++ ++static void himci_set_sap_phase(struct himci_host *host, u32 phase) ++{ ++ unsigned int val; ++ unsigned int phase_a; ++ ++ phase_a = phase >= 2 ? phase - 2 : phase + 6; ++ val = himci_readl(host->base + MCI_UHS_REG_EXT); ++ val &= ~(CLK_SMPL_PHS_MASK | CLK_SMPLA_PHS_MASK); ++ val |= (phase << CLK_SMPL_PHS_SHIFT) | (phase_a << CLK_SMPLA_PHS_SHIFT); ++ himci_writel(val, host->base + MCI_UHS_REG_EXT); ++ val = himci_readl(host->base + MCI_UHS_REG_EXT); ++} ++ ++static int himci_execute_edge_tuning(struct mmc_host *mmc, u32 opcode) ++{ ++ struct himci_host *host = mmc_priv(mmc); ++ unsigned int index, val; ++ unsigned int found = 0, prev_found = 0, prev_point = 0; ++ unsigned int start_point = NOT_FOUND, end_point = NOT_FOUND; ++ unsigned int phase = 0; ++ ++ himci_trace(3, "begin"); ++ ++ himci_edge_tuning_enable(host); ++ ++ for (index = 0; index < HIMCI_PHASE_SCALE; index++) { ++ himci_set_sap_phase(host, index); ++ ++ mmc_send_tuning(mmc, opcode, NULL); ++ ++ himci_send_stop(mmc); ++ ++ val = himci_readl(host->base + MCI_TUNING_CTRL); ++ found = val & FOUND_EDGE; ++ ++ himci_trace(3, "try phase:%02d, found:0x%x\n", index, found); ++ ++ if (prev_found && !found) { ++ end_point = prev_point; ++ } else if (!prev_found && found) { ++ if (index != 0) ++ start_point = index; ++ } ++ if ((start_point != NOT_FOUND) && (end_point != NOT_FOUND)) ++ goto scan_out; ++ ++ prev_point = index; ++ prev_found = found; ++ found = 0; ++ } ++ ++scan_out: ++ if ((start_point == NOT_FOUND) && (end_point == NOT_FOUND)) { ++ himci_trace(5, "%s: no valid phase shift! use default", ++ mmc_hostname(mmc)); ++ return 0; ++ } ++ ++ if (start_point == NOT_FOUND) ++ start_point = end_point; ++ ++ if (end_point == NOT_FOUND) ++ end_point = start_point; ++ ++ pr_info("tuning %s: found edge on (s:%d, e:%d)", ++ mmc_hostname(mmc), start_point, end_point); ++ ++ if (start_point > end_point) ++ end_point += HIMCI_PHASE_SCALE; ++ ++ phase = ((start_point + end_point) / 2) % HIMCI_PHASE_SCALE; ++ ++ phase += HIMCI_PHASE_SCALE / 2; ++ phase %= HIMCI_PHASE_SCALE; ++ ++ himci_set_sap_phase(host, phase); ++ ++ himci_edge_tuning_disable(host); ++ ++ himci_writel(ALL_INT_CLR, host->base + MCI_RINTSTS); ++ ++ pr_info("determing final phase %d\n", phase); ++ ++ return 0; ++} ++#else ++#if defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) || \ ++ defined(CONFIG_ARCH_HI3516AV200) ++static int himci_set_sd_drv_strength(struct mmc_host *mmc, ++ unsigned int drv_type) ++{ ++ struct mmc_request mrq = {NULL}; ++ struct mmc_command cmd = {0}; ++ struct mmc_data data = {0}; ++ struct scatterlist sg; ++ u8 *status; ++ ++ status = kmalloc(64, GFP_KERNEL); ++ if (!status) ++ return -ENOMEM; ++ ++ drv_type &= 0xF; ++ ++ mrq.cmd = &cmd; ++ mrq.data = &data; ++ ++ cmd.opcode = SD_SWITCH; ++ cmd.arg = 0x1 << 31 | 0x00FFFFFF; ++ cmd.arg &= ~(0xF << 8); ++ cmd.arg |= drv_type << 8; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; ++ ++ data.blksz = 64; ++ data.blocks = 1; ++ data.flags = MMC_DATA_READ; ++ data.sg = &sg; ++ data.sg_len = 1; ++ ++ sg_init_one(&sg, status, 64); ++ ++ mmc_wait_for_req(mmc, &mrq); ++ ++ kfree(status); ++ if (cmd.error) ++ return cmd.error; ++ if (data.error) ++ return data.error; ++ ++ return 0; ++} ++ ++static int himci_set_mmc_drv_strength(struct mmc_host *mmc, ++ unsigned int drv_type) ++{ ++ int err; ++ struct mmc_command cmd = {0}; ++ unsigned int value; ++ ++ drv_type &= 0xF; ++ value = EXT_CSD_TIMING_HS200 | (drv_type << 4); ++ ++ cmd.opcode = MMC_SWITCH; ++ cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | ++ (EXT_CSD_HS_TIMING << 16) | ++ (value << 8) | ++ EXT_CSD_CMD_SET_NORMAL; ++ cmd.flags = MMC_CMD_AC | MMC_RSP_SPI_R1B | MMC_RSP_R1B; ++ ++ err = mmc_wait_for_cmd(mmc, &cmd, 0); ++ ++ return err; ++} ++#endif ++static void himci_set_sap_phase(struct himci_host *host, ++ u32 phase, u32 hs400_tuning) ++{ ++ unsigned int reg_value; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ if (hs400_tuning) { ++ phase = (phase < 8) ? (8 - phase) : phase; ++ reg_value = himci_readl(EMMC_DLL_CTRL); ++ reg_value &= ~0xF; ++ reg_value |= phase; ++ himci_writel(reg_value, EMMC_DLL_CTRL); ++ host->dll_phase = phase; ++ } else { ++ reg_value = himci_readl(host->base + MCI_UHS_REG_EXT); ++ reg_value &= ~CLK_SMPL_PHS_MASK; ++ reg_value |= (phase << CLK_SMPL_PHS_SHIFT); ++ himci_writel(reg_value, host->base + MCI_UHS_REG_EXT); ++ host->phase = phase; ++ } ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++/* ++ * The procedure of tuning the phase shift of sampling clock ++ * ++ * 1.Set a phase shift of 0�� on cclk_in_sample ++ * 2.Send the Tuning command to the card ++ * 3.increase the phase shift value of cclk_in_sample until the ++ * correct sampling point is received such that the host does not ++ * see any of the errors. ++ * 4.Mark this phase shift value as the starting point of the sampling ++ * window. ++ * 5.increase the phase shift value of cclk_in_sample until the host ++ * sees the errors starting to come again or the phase shift value ++ * reaches 360��. ++ * 6.Mark the last successful phase shift value as the ending ++ * point of the sampling window. ++ * ++ * A window is established where the tuning block is matched. ++ * For example, for a scenario where the tuning block is received ++ * correctly for a phase shift window of 90��and 180��, then an appropriate ++ * sampling point is established as 135��. Once a sampling point is ++ * established, no errors should be visible in the tuning block. ++ * ++ */ ++static int himci_execute_tuning(struct mmc_host *mmc, u32 opcode) ++{ ++ struct himci_host *host; ++ unsigned int index, count; ++ unsigned int err = 0; ++ unsigned int found = 0; /* identify if we have found a valid phase */ ++ unsigned int start_point; ++ unsigned int end_point; ++ unsigned int prev_err = NOT_FOUND; ++ unsigned int raise_point = NOT_FOUND; ++ unsigned int fall_point = NOT_FOUND; ++ unsigned int hs400_tuning; ++ int phase, ret, send_cmd_num = 1; ++#if defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) || \ ++ defined(CONFIG_ARCH_HI3516AV200) ++ int retry = 0; ++#endif ++ ++ if (opcode == MMC_SEND_EXT_CSD) { ++ start_point = HS400_TUNING_START_PHASE; ++ end_point = HS400_TUNING_END_PHASE; ++ hs400_tuning = 1; ++ } else { ++ start_point = TUNING_START_PHASE; ++ end_point = TUNING_END_PHASE; ++ hs400_tuning = 0; ++ } ++ ++ host = mmc_priv(mmc); ++ ++ himci_trace(3, "start sd3.0 phase tuning..."); ++ ++#if defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) || \ ++ defined(CONFIG_ARCH_HI3516AV200) ++ if (!hs400_tuning && (mmc->caps & MMC_CAP_UHS_SDR104)) ++ himci_set_sd_drv_strength(mmc, SD_STRENGTH_TYPE_C); ++try_again: ++#endif ++ ++ for (index = start_point; index <= end_point; index++) { ++ /* set sample clk phase shift */ ++ himci_set_sap_phase(host, index, hs400_tuning); ++ ++ count = 0; ++ do { ++ if (hs400_tuning) { ++ ret = mmc_send_dll_tuning(mmc); ++ } else { ++ ret = mmc_send_tuning(mmc, opcode, NULL); ++ himci_send_stop(mmc); /* send soft_stop tail */ ++ } ++ ++ if (ret) { ++ himci_trace(3, "send tuning CMD%u fail! phase:%d err:%d\n", ++ opcode, index, ret); ++ err = 1; ++ break; ++ } ++ count++; ++ } while (count < send_cmd_num); ++ ++ if (!err) ++ found = 1; /* found a valid phase */ ++ ++ if (index > start_point) { ++ if (err && !prev_err) ++ fall_point = index - 1; ++ ++ if (!err && prev_err) ++ raise_point = index; ++ } ++ ++ if ((raise_point != NOT_FOUND) && (fall_point != NOT_FOUND)) ++ goto tuning_out; ++ ++ prev_err = err; ++ err = 0; ++ } ++ ++#if defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) || \ ++ defined(CONFIG_ARCH_HI3516AV200) ++ if ((NOT_FOUND == raise_point) && (NOT_FOUND == fall_point) ++ && !hs400_tuning && !retry) { ++ if (mmc->caps & MMC_CAP_UHS_SDR104) ++ himci_set_sd_drv_strength(mmc, SD_STRENGTH_TYPE_D); ++ else if (mmc->caps2 & MMC_CAP2_HS200) ++ himci_set_mmc_drv_strength(mmc, MMC_STRENGTH_TYPE_3); ++ ++ prev_err = NOT_FOUND; ++ send_cmd_num = 40; ++ retry = 1; ++ goto try_again; ++ } ++#endif ++ ++tuning_out: ++ if (!found) { ++ himci_trace(5, "%s: no valid phase shift! use default", ++ mmc_hostname(mmc)); ++ if (!hs400_tuning) ++ himci_set_sap_phase(host, DEFAULT_SMPL_PHASE, hs400_tuning); ++ } else { ++ himci_trace(3, "Tuning finished!!"); ++ ++ if (NOT_FOUND == raise_point) ++ raise_point = start_point; ++ if (NOT_FOUND == fall_point) ++ fall_point = end_point; ++ ++ if (fall_point < raise_point) { ++#ifdef CONFIG_ARCH_HI3519 ++ if (fall_point - start_point > end_point - raise_point) ++ phase = (fall_point + start_point) / 2; ++ else ++ phase = (end_point + raise_point) / 2; ++#else ++ phase = (raise_point + fall_point) / 2; ++ phase = phase - (HIMCI_PHASE_SCALE / 2); ++ phase = (phase < 0) ? (HIMCI_PHASE_SCALE + phase) : phase; ++#endif ++ } else ++ phase = (raise_point + fall_point) / 2; ++ ++ himci_set_sap_phase(host, phase, hs400_tuning); ++ ++ pr_info("%stuning %s: valid phase shift [%d, %d] Final Phase %d\n", ++ hs400_tuning ? "HS400 " : "", mmc_hostname(mmc), ++ raise_point, fall_point, phase); ++ } ++ ++#if defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) || \ ++ defined(CONFIG_ARCH_HI3516AV200) ++ if (retry) { ++ if (mmc->caps & MMC_CAP_UHS_SDR104) ++ himci_set_sd_drv_strength(mmc, SD_STRENGTH_TYPE_C); ++ else if (mmc->caps2 & MMC_CAP2_HS200) ++ himci_set_mmc_drv_strength(mmc, MMC_STRENGTH_TYPE_0); ++ } ++#endif ++ return 0; ++} ++#endif ++ ++static void himci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct himci_host *host = mmc_priv(mmc); ++ unsigned int tmp_reg; ++ unsigned int port = host->cur_port; ++ u32 ctrl = 0; ++ ++ himci_trace(2, "begin"); ++ himci_assert(mmc); ++ himci_assert(ios); ++ himci_assert(host); ++ ++ himci_trace(3, "ios->power_mode = %d ", ios->power_mode); ++ if (!ios->clock) ++ himci_control_cclk(host, DISABLE); ++ ++ switch (ios->power_mode) { ++ case MMC_POWER_OFF: ++ himci_ctrl_power(host, POWER_OFF, FORCE_DISABLE); ++ break; ++ case MMC_POWER_UP: ++ case MMC_POWER_ON: ++ himci_ctrl_power(host, POWER_ON, FORCE_DISABLE); ++ break; ++ } ++ himci_trace(3, "ios->clock = %d ", ios->clock); ++ if (ios->clock) { ++ himci_control_cclk(host, DISABLE); ++ himci_set_cclk(host, ios->clock); ++ himci_control_cclk(host, ENABLE); ++ ++ /* speed mode check ,if it is DDR50 set DDR mode */ ++ if (ios->timing == MMC_TIMING_UHS_DDR50) { ++ ctrl = himci_readl(host->base + MCI_UHS_REG); ++ if (!((HI_SDXC_CTRL_DDR_REG << port) & ctrl)) { ++ ctrl |= (HI_SDXC_CTRL_DDR_REG << port); ++ himci_writel(ctrl, host->base + MCI_UHS_REG); ++ } ++ } ++ ++ if (ios->timing == MMC_TIMING_MMC_HS400) { ++ ctrl = himci_readl(host->base + MCI_EMMC_DDR_REG); ++ if (!(HI_EMMC_HS400_MODE & ctrl)) { ++ ctrl |= HI_EMMC_HS400_MODE; ++ himci_writel(ctrl, ++ host->base + MCI_EMMC_DDR_REG); ++ } ++ } ++ } else { ++ himci_control_cclk(host, DISABLE); ++ if (ios->timing != MMC_TIMING_UHS_DDR50) { ++ ctrl = himci_readl(host->base + MCI_UHS_REG); ++ if ((HI_SDXC_CTRL_DDR_REG << port) & ctrl) { ++ ctrl &= ~(HI_SDXC_CTRL_DDR_REG << port); ++ himci_writel(ctrl, host->base + MCI_UHS_REG); ++ } ++ } ++ ++ if (ios->timing != MMC_TIMING_MMC_HS400) { ++ ctrl = himci_readl(host->base + MCI_EMMC_DDR_REG); ++ if (HI_EMMC_HS400_MODE & ctrl) { ++ ctrl &= ~HI_EMMC_HS400_MODE; ++ himci_writel(ctrl, ++ host->base + MCI_EMMC_DDR_REG); ++ } ++ } ++ } ++ ++ himci_set_drv_cap(host, ios); ++ ++ /* set bus_width */ ++ himci_trace(3, "ios->bus_width = %d ", ios->bus_width); ++ ++ /* clear bus width to 1bit first */ ++ tmp_reg = himci_readl(host->base + MCI_CTYPE); ++ tmp_reg &= ~((CARD_WIDTH_0 | CARD_WIDTH_1) << port); ++ ++ if (ios->bus_width == MMC_BUS_WIDTH_8) { ++ tmp_reg |= (CARD_WIDTH_0 << port); ++ himci_writel(tmp_reg, host->base + MCI_CTYPE); ++ } else if (ios->bus_width == MMC_BUS_WIDTH_4) { ++ tmp_reg |= (CARD_WIDTH_1 << port); ++ himci_writel(tmp_reg, host->base + MCI_CTYPE); ++ } else { ++ himci_writel(tmp_reg, host->base + MCI_CTYPE); ++ } ++} ++ ++static void himci_enable_sdio_irq(struct mmc_host *mmc, int enable) ++{ ++ struct himci_host *host = mmc_priv(mmc); ++ unsigned int reg_value; ++ ++ reg_value = himci_readl(host->base + MCI_INTMASK); ++ if (enable) ++ reg_value |= SDIO_INT_MASK; ++ else ++ reg_value &= ~SDIO_INT_MASK; ++ himci_writel(reg_value, host->base + MCI_INTMASK); ++} ++ ++static int himci_get_card_detect(struct mmc_host *mmc) ++{ ++ unsigned ret; ++ struct himci_host *host = mmc_priv(mmc); ++ ++ himci_trace(2, "begin"); ++ ret = himci_sys_card_detect(host); ++ ++ if (ret) ++ return 0; ++ else ++ return 1; ++} ++ ++static int himci_get_ro(struct mmc_host *mmc) ++{ ++ unsigned ret; ++ struct himci_host *host = mmc_priv(mmc); ++ ++ himci_trace(2, "begin"); ++ himci_assert(mmc); ++ ++ ret = himci_ctrl_card_readonly(host); ++ ++ return ret; ++} ++ ++static unsigned int himci_get_rdptr(struct mmc_host *mmc) ++{ ++ unsigned int cur; ++ struct himci_host *host = mmc_priv(mmc); ++ ++ himci_trace(2, "begin"); ++ himci_assert(mmc); ++ ++ cur = himci_readl(host->base + ADMA_Q_RDPTR); ++ ++ return cur; ++} ++ ++static void himci_hw_reset(struct mmc_host *mmc) ++{ ++ unsigned int reg_value; ++ struct himci_host *host = mmc_priv(mmc); ++ unsigned int port = host->cur_port; ++ ++ reg_value = himci_readl(host->base + MCI_RESET_N); ++ reg_value &= ~(MMC_RST_N << port); ++ himci_writel(reg_value, host->base + MCI_RESET_N); ++ /* For eMMC, minimum is 1us but give it 10us for good measure */ ++ udelay(10); ++ reg_value = himci_readl(host->base + MCI_RESET_N); ++ reg_value |= (MMC_RST_N << port); ++ himci_writel(reg_value, host->base + MCI_RESET_N); ++ /* For eMMC, minimum is 200us but give it 300us for good measure */ ++ usleep_range(300, 1000); ++} ++ ++static int himci_select_drv_strength(unsigned int max_dtr, ++ int host_drv, int card_drv) ++{ ++ if ((host_drv & SD_DRIVER_TYPE_C) && ++ (card_drv & SD_DRIVER_TYPE_C)) ++ return SD_STRENGTH_TYPE_C; ++ else ++ return SD_STRENGTH_TYPE_B; ++} ++ ++static int himci_card_info_save(struct mmc_host *mmc) ++{ ++ struct mmc_card *card = mmc->card; ++ struct himci_host * host= mmc_priv(mmc); ++ struct card_info * c_info = &host->c_info; ++ ++ if (!card) { ++ memset(c_info,0,sizeof(struct card_info)); ++ c_info->card_connect = CARD_DISCONNECT; ++ goto out; ++ } ++ ++ c_info->card_type = card->type; ++ c_info->card_state = card->state; ++ ++ c_info->timing = mmc->ios.timing; ++ c_info->card_support_clock = mmc->ios.clock; ++ ++ c_info->sd_bus_speed = card->sd_bus_speed; ++ ++ memcpy(c_info->ssr, card->c_ssr, ARRAY_SIZE(c_info->ssr)); ++ ++ c_info->card_connect = CARD_CONNECT; ++out: ++ return 0; ++} ++ ++static const struct mmc_host_ops himci_ops = { ++ .request = himci_request, ++ .post_req = himci_data_done, ++ .set_ios = himci_set_ios, ++ .get_rd = himci_get_rdptr, ++ .get_ro = himci_get_ro, ++ .start_signal_voltage_switch = himci_start_signal_voltage_switch, ++#if defined(CONFIG_ARCH_HI3516CV300) ++ .execute_tuning = himci_execute_edge_tuning, ++#else ++ .execute_tuning = himci_execute_tuning, ++#endif ++ .enable_sdio_irq = himci_enable_sdio_irq, ++ .select_drive_strength = himci_select_drv_strength, ++ .hw_reset = himci_hw_reset, ++ .sw_reset = himci_sw_reset, ++ .get_cd = himci_get_card_detect, ++ .card_info_save = himci_card_info_save, ++}; ++ ++static void himci_check_sdio_irq(struct himci_host *host, unsigned int rstat) ++{ ++ u32 mstate; ++ ++ if ((host->mmc->card != NULL) ++ && (host->mmc->card->type == MMC_TYPE_SDIO)) { ++ mstate = himci_readl(host->base + MCI_INTMASK); ++ if ((rstat & SDIO_INT_STATUS) && (mstate & SDIO_INT_MASK)) { ++ spin_lock(&host->lock); ++ himci_writel(SDIO_INT_STATUS, ++ host->base + MCI_RINTSTS); ++ spin_unlock(&host->lock); ++ mmc_signal_sdio_irq(host->mmc); ++ } ++ } ++} ++ ++static unsigned int himci_get_stable_rd_pos(struct himci_host *host, ++ unsigned int qstat, unsigned int rstat) ++{ ++ unsigned int rd_pos; ++ unsigned int fsm_stat; ++ volatile unsigned int new_pos; ++ volatile unsigned int new_fsm; ++ unsigned int count = 0; ++ int try = 1000; ++ ++ rd_pos = himci_readl(host->base + ADMA_Q_RDPTR); ++ fsm_stat = himci_readl(host->base + MCI_IDSTS); ++ while (try > 0) { ++ new_pos = himci_readl(host->base + ADMA_Q_RDPTR); ++ new_fsm = himci_readl(host->base + MCI_IDSTS); ++ if ((rd_pos == new_pos) && (fsm_stat == new_fsm)) { ++ count++; ++ } else { ++ rd_pos = new_pos; ++ fsm_stat = new_fsm; ++ count = 0; ++ } ++ ++ if (count == 3) ++ break; ++ try--; ++ } ++ ++ /* when error occur, whether the read ptr jump next is depend on ++ * host's state machine when CES && ADMA3_FSM=4 && FSM!=0, ++ *rdptr don't jump next ++ */ ++ if (!(qstat & PACKET_TO_INT)) { ++ if (!((qstat & CES) && ++ (((fsm_stat >> ADMA3_FSM_SHIFT) & 0xF) == 4) && ++ ((fsm_stat >> FSM_SHIFT) & 0xF))) ++ rd_pos = rd_pos ? (rd_pos - 1) ++ : (ADMA_QUEUE_DEEPTH - 1); ++ } ++ ++ return rd_pos; ++} ++ ++static irqreturn_t hisd_irq(int irq, void *dev_id) ++{ ++ struct himci_host *host = dev_id; ++ struct mmc_request *mrq; ++ u32 rd_pos = 0; ++ u32 rstat = 0; ++ u32 qstat = 0; ++ int cmd_err = 0; ++ int data_err = 0; ++ ++ rstat = himci_readl(host->base + MCI_RINTSTS); /* read RAW intr */ ++ qstat = himci_readl(host->base + MCI_IDSTS); /* clear ADMA3 intr */ ++ himci_writel(qstat, host->base + MCI_IDSTS); ++ himci_trace(3, "queue state:0x%x raw state:0x%x", qstat, rstat); ++ ++ if (!(qstat & ADMA_INT_ALL) && !(rstat & SDIO_INT_STATUS)) { ++ himci_trace(5, "##### no irq, should never be here! ######"); ++ return IRQ_NONE; ++ } ++ ++ /* bugfix: when send soft stop to SD Card, Host will report ++ sdio interrupt, This situation needs to be avoided */ ++ if (host->mmc->caps & MMC_CAP_SDIO_IRQ) ++ himci_check_sdio_irq(host, rstat); ++ ++ rd_pos = himci_get_stable_rd_pos(host, qstat, rstat); ++ ++ mrq = host->pmrq[rd_pos]; ++ himci_err_check(host, qstat, rstat, &cmd_err, &data_err); ++ ++ host->pmrq[rd_pos] = NULL; ++ if (!mrq || !mrq->cmd) { ++ himci_trace(4, "##################"); ++ himci_trace(4, "queue state:0x%x raw state:0x%x", qstat, rstat); ++ himci_trace(4, "current rd:%d", rd_pos); ++ return IRQ_HANDLED; ++ } ++ ++ if (cmd_err || data_err) { ++ if (mrq->cmd->flags & MMC_CMD_TYPE_RW) { ++ himci_trace(5, "R/W CMD%u error, qstat:0x%x rstat:0x%x", ++ mrq->cmd->opcode, qstat, rstat); ++ host->error_count++; ++ host->mmc->status = MMC_HOST_ERR; ++ } ++ mrq->cmd->error = cmd_err; ++ if (mrq->data) ++ mrq->data->error = data_err; ++ } ++ ++ /* finish each mrq */ ++ if (mrq->cmd->flags & MMC_CMD_TYPE_RW) { ++ himci_read_response(host, mrq->cmd); ++ mmc_request_done(host->mmc, mrq); ++ } else { ++ host->cmd_done = 1; ++ wake_up(&host->intr_wait); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int himci_of_parse(struct device_node *np, struct mmc_host *mmc) ++{ ++ struct himci_host *host = mmc_priv(mmc); ++ int ret = mmc_of_parse(mmc); ++ int len; ++ ++ if (ret) ++ return ret; ++ ++ if (of_property_read_u32(np, "min-frequency", &mmc->f_min)) ++ mmc->f_min = MMC_CCLK_MIN; ++ ++ if (of_property_read_u32(np, "devid", &host->devid)) ++ return -EINVAL; ++ ++ if (of_find_property(np, "cap-mmc-hw-reset", &len)) ++ mmc->caps |= MMC_CAP_HW_RESET; ++ ++ return 0; ++} ++ ++static int __init himci_probe(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc; ++ struct himci_host *host = NULL; ++ struct resource *host_ioaddr_res = NULL; ++ int ret = 0, irq; ++ struct device_node *np = pdev->dev.of_node; ++ ++ himci_trace(2, "begin"); ++ pr_info("mmc host probe\n"); ++ himci_assert(pdev); ++ ++ mmc = mmc_alloc_host(sizeof(struct himci_host), &pdev->dev); ++ if (!mmc) { ++ himci_error("no mem for hi mci host controller!\n"); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ platform_set_drvdata(pdev, mmc); ++ ++ mmc->ops = &himci_ops; ++ ++ host_ioaddr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (NULL == host_ioaddr_res) { ++ himci_error("no ioaddr rescources config!\n"); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ if (himci_of_parse(np, mmc)) { ++ himci_error("failed to parse mmc dts!\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* reload by this controller */ ++ mmc->max_blk_count = 2048; ++ mmc->max_segs = MAX_SEGS; ++ mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count; ++ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; ++ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ++ mmc->status = MMC_HOST_OK; ++ ++ host = mmc_priv(mmc); ++ mci_host[slot_index++] = host; ++ pdev->id = host->devid; ++ host->pdev = pdev; ++ host->mmc = mmc; ++ ++ /* ADMA3 buffer alloc */ ++ host->entire_addr = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, ++ &host->entire_paddr, GFP_KERNEL); ++ if (!host->entire_addr) { ++ himci_error("no mem for himci dma!\n"); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ host->cmd_addr = dma_alloc_coherent(&pdev->dev, ++ PERF_OPT_RATIO * PAGE_SIZE * ADMA_QUEUE_DEEPTH, ++ &host->cmd_paddr, GFP_KERNEL); ++ if (!host->cmd_addr) { ++ himci_error("no mem for himci dma!\n"); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ host->base = devm_ioremap_resource(&pdev->dev, host_ioaddr_res); ++ if (IS_ERR_OR_NULL(host->base)) { ++ himci_error("no mem for himci base!\n"); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ spin_lock_init(&host->lock); ++ ++ host->clk = devm_clk_get(&pdev->dev, "mmc_clk"); ++ if (IS_ERR_OR_NULL(host->clk)) { ++ himci_error("get clock fail.\n"); ++ ret = PTR_ERR(host->clk); ++ goto out; ++ } ++ ++ clk_prepare_enable(host->clk); ++ ++ host->power_status = POWER_OFF; ++ host->cur_port = 0; ++ ++ /* enable card */ ++ himci_init_host(host); ++ host->card_status = himci_sys_card_detect(host); ++ ++ if (mmc->caps & MMC_CAP_SD_HIGHSPEED) { ++ init_timer(&host->timer); ++ host->timer.function = himci_detect_card; ++ host->timer.data = (unsigned long)host; ++ host->timer.expires = jiffies + detect_time; ++ add_timer(&host->timer); ++ } ++ ++ host->cmd_done = 0; ++ init_waitqueue_head(&host->intr_wait); ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ goto out; ++ ++ host->irq = irq; ++ ret = request_irq(irq, hisd_irq, 0, DRIVER_NAME, host); ++ if (ret) ++ goto out; ++ ++ mmc_add_host(mmc); ++ return 0; ++out: ++ if (host) { ++ if (mmc->caps & MMC_CAP_SD_HIGHSPEED) ++ del_timer(&host->timer); ++ ++ if (host->base) ++ devm_iounmap(&pdev->dev, host->base); ++ ++ if (host->entire_addr) ++ dma_free_coherent(&pdev->dev, PAGE_SIZE, ++ host->entire_addr, host->entire_paddr); ++ if (host->cmd_addr) ++ dma_free_coherent(&pdev->dev, ++ PERF_OPT_RATIO * PAGE_SIZE * ADMA_QUEUE_DEEPTH, ++ host->cmd_addr, host->cmd_paddr); ++ } ++ if (mmc) ++ mmc_free_host(mmc); ++ return ret; ++} ++ ++static int __exit himci_remove(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(pdev); ++ ++ himci_trace(2, "begin"); ++ himci_assert(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ if (mmc) { ++ struct himci_host *host = mmc_priv(mmc); ++ ++ mmc_remove_host(mmc); ++ free_irq(host->irq, host); ++ if (mmc->caps & MMC_CAP_SD_HIGHSPEED) ++ del_timer_sync(&host->timer); ++ himci_ctrl_power(host, POWER_OFF, FORCE_DISABLE); ++ himci_control_cclk(host, DISABLE); ++ devm_iounmap(&pdev->dev, host->base); ++ dma_free_coherent(&pdev->dev, PAGE_SIZE, ++ host->entire_addr, host->entire_paddr); ++ dma_free_coherent(&pdev->dev, ++ PERF_OPT_RATIO * PAGE_SIZE * ADMA_QUEUE_DEEPTH, ++ host->cmd_addr, host->cmd_paddr); ++ mmc_free_host(mmc); ++ } ++ return 0; ++} ++ ++static void himci_shutdown(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(pdev); ++ ++ himci_trace(3, "shutdown"); ++ if (mmc) { ++ unsigned int val; ++ struct himci_host *host = mmc_priv(mmc); ++ ++ /* bugfix: host reset can trigger error intr */ ++ himci_writel(0, host->base + MCI_IDINTEN); ++ himci_writel(0, host->base + MCI_INTMASK); ++ ++ val = himci_readl(host->base + MCI_CTRL); ++ val |= CTRL_RESET | FIFO_RESET | DMA_RESET; ++ himci_writel(val, host->base + MCI_CTRL); ++ ++ himciv200_host_power(host, POWER_OFF, FORCE_DISABLE); ++ } ++} ++ ++#ifdef CONFIG_PM ++static int himciv200_pltm_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(pdev); ++ struct himci_host *host; ++ int ret = 0; ++ ++ if (mmc) { ++ host = mmc_priv(mmc); ++ if (mmc->caps & MMC_CAP_SD_HIGHSPEED) ++ del_timer_sync(&host->timer); ++ ++ if (__clk_is_enabled(host->clk)) ++ clk_disable_unprepare(host->clk); ++ } ++ ++ return ret; ++} ++/******************************************************************************/ ++ ++static int himciv200_pltm_resume(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(pdev); ++ struct himci_host *host; ++ int ret = 0; ++ ++ if (mmc) { ++ host = mmc_priv(mmc); ++ ++ if (!__clk_is_enabled(host->clk)) ++ clk_prepare_enable(host->clk); ++ ++ himci_init_host(host); ++ ++ if (mmc->caps & MMC_CAP_SD_HIGHSPEED) ++ add_timer(&host->timer); ++ } ++ ++ return ret; ++} ++#else ++#define himciv200_pltm_suspend NULL ++#define himciv200_pltm_resume NULL ++#endif ++ ++void himci_mmc_rescan(int slot) ++{ ++ struct mmc_host *mmc; ++ struct himci_host *host; ++ ++ host = mci_host[slot]; ++ if (!host || !host->mmc) { ++ himci_trace(5, "mmc%d: invalid slot!\n", slot); ++ return; ++ } ++ ++ mmc = host->mmc; ++ if (mmc->caps & MMC_CAP_SD_HIGHSPEED) ++ del_timer_sync(&host->timer); ++ ++ mmc_remove_host(mmc); ++ ++ mmc_add_host(mmc); ++ ++ if (mmc->caps & MMC_CAP_SD_HIGHSPEED) ++ add_timer(&host->timer); ++} ++EXPORT_SYMBOL(himci_mmc_rescan); ++ ++static const struct of_device_id ++himciv200_match[] __maybe_unused = { ++ {.compatible = "hisilicon,hi3516cv300-himciv200"}, ++ {.compatible = "hisilicon,hi3519-himciv200"}, ++ {.compatible = "hisilicon,hi3516av200-himciv200"}, ++ {.compatible = "hisilicon,hi3559-himciv200"}, ++ {.compatible = "hisilicon,hi3556-himciv200"}, ++ {}, ++}; ++ ++static struct platform_driver himci_driver = { ++ .probe = himci_probe, ++ .remove = himci_remove, ++ .shutdown = himci_shutdown, ++ .suspend = himciv200_pltm_suspend, ++ .resume = himciv200_pltm_resume, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(himciv200_match), ++ }, ++}; ++ ++static int __init himci_init(void) ++{ ++ int ret; ++ ++ himci_trace(2, "begin"); ++ ++ /* ++ * We should register SDIO1 first to make sure that ++ * the eMMC device,which connected to SDIO1 is mmcblk0. ++ */ ++ ++ ret = platform_driver_register(&himci_driver); ++ if (ret) { ++ platform_driver_unregister(&himci_driver); ++ himci_error("Himci driver register failed!"); ++ return ret; ++ } ++ ++ /* device proc entry */ ++ ret = mci_proc_init(HIMCI_SLOT_NUM); ++ if (ret) ++ himci_error("device proc init is failed!"); ++ ++ return ret; ++} ++ ++static void __exit himci_exit(void) ++{ ++ himci_trace(2, "begin"); ++ ++ mci_proc_shutdown(); ++ ++ platform_driver_unregister(&himci_driver); ++} ++ ++module_init(himci_init); ++module_exit(himci_exit); ++ ++#ifdef MODULE ++MODULE_AUTHOR("Hisilicon Drive Group"); ++MODULE_DESCRIPTION("MMC/SD driver for the Hisilicon MMC/SD Host Controller"); ++MODULE_LICENSE("GPL"); ++#endif +diff --git a/drivers/mmc/host/himciv200/himci.h b/drivers/mmc/host/himciv200/himci.h +new file mode 100644 +index 0000000..7b78611 +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci.h +@@ -0,0 +1,192 @@ ++#ifndef _HI_MCI_H_ ++#define _HI_MCI_H_ ++ ++#include <linux/mmc/mmc.h> ++ ++extern int trace_level; ++#define HIMCI_TRACE_LEVEL 5 ++/* ++ 0 - all message ++ 1 - dump all register read/write ++ 2 - flow trace ++ 3 - timeouut err and protocol err ++ */ ++ ++#define HIMCI_TRACE_FMT KERN_INFO ++/* #define HIMCI_TRACE_FMT KERN_DEBUG */ ++ ++#define HS400_TUNING_START_PHASE 1 ++#define HS400_TUNING_END_PHASE 15 ++#define NOT_FOUND -1 ++ ++#define POWER_ON 1 ++#define POWER_OFF 0 ++#define FORCE_ENABLE 1 ++#define FORCE_DISABLE 0 ++ ++#define CARD_UNPLUGED 1 ++#define CARD_PLUGED 0 ++ ++#define ENABLE 1 ++#define DISABLE 0 ++ ++#define SD_STRENGTH_TYPE_B 0 ++#define SD_STRENGTH_TYPE_C 2 ++#define SD_STRENGTH_TYPE_D 3 ++#define MMC_STRENGTH_TYPE_0 0 ++#define MMC_STRENGTH_TYPE_3 3 ++ ++#define HI_MCI_DETECT_TIMEOUT (HZ / 5) ++#define HI_MCI_REQUEST_TIMEOUT (5 * HZ) ++#define MAX_RETRY_COUNT 100 ++ ++#define MMC_CCLK_MIN 100000 ++ ++#define ADMA_QUEUE_DEEPTH (32) ++ ++/* Base address of SD card register */ ++#define HI_MCI_INTR (48+32) ++ ++#define himci_trace(level, msg...) do { \ ++ if ((level) >= trace_level) { \ ++ printk(HIMCI_TRACE_FMT "%s:%d: ", __func__, __LINE__); \ ++ printk(msg); \ ++ printk("\n"); \ ++ } \ ++} while (0) ++ ++#define himci_assert(cond) do { \ ++ if (!(cond)) {\ ++ printk(KERN_ERR "Assert:himci:%s:%d\n", \ ++ __func__, \ ++ __LINE__); \ ++ BUG(); \ ++ } \ ++} while (0) ++ ++#define himci_error(s...) do { \ ++ printk(KERN_ERR "himci:%s:%d: ", __func__, __LINE__); \ ++ printk(s); \ ++ printk("\n"); \ ++} while (0) ++ ++#define himci_readl(addr) ({unsigned int reg = hi_readl(IOMEM(addr)); \ ++ himci_trace(1, "readl(0x%04X) = 0x%08X", (unsigned int)addr, reg); \ ++ reg; }) ++ ++#define himci_writel(v, addr) do { hi_writel(v, IOMEM(addr)); \ ++ himci_trace(1, "writel(0x%04X) = 0x%08X", (unsigned int)addr, \ ++ (unsigned int)(v)); \ ++} while (0) ++ ++struct himci_queue_info { ++ unsigned long end; ++ unsigned long cur; ++ unsigned int status; ++}; ++ ++struct himci_entire_des { ++ unsigned long ctrl; ++ unsigned long cmd_des_addr; ++ unsigned long response; ++ unsigned long reserved; ++}; ++ ++struct himci_cmd_des { ++ unsigned long blk_sz; ++ unsigned long byte_cnt; ++ unsigned long arg; ++ unsigned long cmd; ++}; ++ ++struct himci_dma_des { ++ unsigned long idmac_des_ctrl; ++ unsigned long idmac_des_buf_size; ++ unsigned long idmac_des_buf_addr; ++ unsigned long idmac_des_next_addr; ++}; ++ ++struct card_info { ++ unsigned int card_type; ++ unsigned char timing; ++ unsigned char card_connect; ++#define CARD_CONNECT 1 ++#define CARD_DISCONNECT 0 ++ unsigned int card_support_clock; /* clock rate */ ++ unsigned int card_state; /* (our) card state */ ++ unsigned int sd_bus_speed; ++ unsigned int ssr[16]; ++}; ++ ++struct himci_host { ++ struct mmc_host *mmc; ++ struct platform_device *pdev; ++ spinlock_t lock; ++ struct mmc_request *mrq; ++ struct mmc_command *cmd; ++ struct mmc_data *data; ++ void __iomem *base; ++ void *sys_regmap; ++ struct scatterlist *dma_sg; ++ ++ dma_addr_t entire_paddr; ++ dma_addr_t cmd_paddr; ++ dma_addr_t dma_paddr; ++ unsigned int *entire_addr; ++ unsigned int *cmd_addr; ++ unsigned int *dma_addr; ++ unsigned int dma_sg_num; ++ struct himci_entire_des *wr_ent_des; ++ struct himci_cmd_des *wr_cmd_des; ++ /* save the pointer of send mrq */ ++ struct mmc_request *pmrq[ADMA_QUEUE_DEEPTH]; ++ struct timer_list timer; ++ wait_queue_head_t intr_wait; ++ unsigned int cmd_done; ++ unsigned int irq; ++ unsigned int irq_status; ++ unsigned int power_status; ++ unsigned int card_status; ++ unsigned int devid; ++ unsigned int cur_port; ++ unsigned int wr_pos; ++ unsigned int hclk; ++ unsigned int cclk; ++ unsigned int phase; ++ unsigned int dll_phase; ++ struct clk *clk; ++ unsigned int error_count; ++ struct card_info c_info; ++}; ++ ++union cmd_arg_u { ++ unsigned int cmd_arg; ++ struct cmd_bits_arg { ++ unsigned int cmd_index:6; ++ unsigned int response_expect:1; ++ unsigned int response_length:1; ++ unsigned int check_response_crc:1; ++ unsigned int data_transfer_expected:1; ++ unsigned int read_write:1; ++ unsigned int transfer_mode:1; ++ unsigned int send_auto_stop:1; ++ unsigned int wait_prvdata_complete:1; ++ unsigned int stop_abort_cmd:1; ++ unsigned int send_initialization:1; ++ unsigned int card_number:5; ++ unsigned int update_clk_reg_only:1; /* bit 21 */ ++ unsigned int read_ceata_device:1; ++ unsigned int ccs_expected:1; ++ unsigned int enable_boot:1; ++ unsigned int expect_boot_ack:1; ++ unsigned int disable_boot:1; ++ unsigned int boot_mode:1; ++ unsigned int volt_switch:1; ++ unsigned int use_hold_reg:1; ++ unsigned int reserved:1; ++ unsigned int start_cmd:1; /* HSB */ ++ } bits; ++}; ++ ++struct mmc_host *get_mmchost(int hostid); ++#endif +diff --git a/drivers/mmc/host/himciv200/himci_acl.c b/drivers/mmc/host/himciv200/himci_acl.c +new file mode 100644 +index 0000000..43e8b43 +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci_acl.c +@@ -0,0 +1,139 @@ ++/****************************************************************************** ++ * Copyright (C) 2014 Hisilicon STB Development Dept ++ * All rights reserved. ++ * *** ++ * Create by Czyong ++ * ++******************************************************************************/ ++#define pr_fmt(fmt) "mmcacl: " fmt ++ ++#include <linux/kobject.h> ++#include <linux/slab.h> ++#include <linux/kmod.h> ++#include <linux/vmalloc.h> ++#include <linux/debugfs.h> ++#include <linux/mutex.h> ++#include <linux/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/sched.h> ++#include <linux/cmdline-parser.h> ++#include <linux/delay.h> ++ ++struct part_t { ++ u32 start; ++ u32 end; ++ int part; ++}; ++ ++static int mmcacl_devid = -1; ++extern char *get_blkdevparts(void); ++ ++static struct part_t ro_parts[64]; ++static int nr_ro_parts; ++ ++static int mmcadd_part(int slot, struct cmdline_subpart *subpart, void *param) ++{ ++ u32 start = (u32)(subpart->from >> 9); ++ u32 end = start + (u32)(subpart->size >> 9); ++ struct part_t *part = (struct part_t *)param; ++ ++ if (subpart->flags & PF_RDONLY) { ++ if (nr_ro_parts < 64) { ++ part[nr_ro_parts].start = start; ++ part[nr_ro_parts].end = end; ++ part[nr_ro_parts].part = slot; ++ nr_ro_parts++; ++ } else { ++ pr_err("too many ro partition. please increase ro_parts\n"); ++ } ++ } ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int mmcpart_part(char *cmdline) ++{ ++ struct cmdline_parts *bdev_parts = NULL; ++ ++ if (cmdline_parts_parse(&bdev_parts, cmdline)) ++ return -1; ++ ++ cmdline_parts_set(bdev_parts, (sector_t)0x1000000000ULL, 0, mmcadd_part, ++ (void *)ro_parts); ++ ++ cmdline_parts_free(&bdev_parts); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++int himci_acl_init(int devid) ++{ ++ char *cmdline = NULL; ++ ++ if (mmcacl_devid != -1) ++ return 0; ++ ++ mmcacl_devid = devid; ++ ++ cmdline = get_blkdevparts(); ++ if (!cmdline) ++ return 0; ++ ++ mmcpart_part(cmdline); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++struct part_t *part_find(struct part_t *part, ++ u32 nr_part, u32 ofblk, u32 endblk) ++{ ++ u32 min = 0; ++ u32 max = nr_part; ++ u32 pos = (min + max) >> 1; ++ ++ do { ++ if (endblk <= part[pos].start) { ++ max = pos; ++ goto next; ++ } ++ ++ if (part[pos].end <= ofblk) { ++ min = pos; ++ goto next; ++ } ++ ++ break; ++next: ++ pos = (min + max) >> 1; ++ } while (pos != min && pos != max); ++ ++ return (endblk <= part[pos].start || part[pos].end <= ofblk) ? ++ NULL : &part[pos]; ++} ++/*****************************************************************************/ ++ ++int himci_acl_rw(int devid, int is_write, u32 ofblk, u32 nrblk) ++{ ++ struct part_t *part; ++ ++ if (mmcacl_devid != devid) ++ return 0; ++ ++ if (!is_write) ++ return 0; ++ ++ if (!nr_ro_parts) ++ return 0; ++ ++ part = part_find(ro_parts, nr_ro_parts, ofblk, ofblk + nrblk); ++ if (part) { ++ pr_warn("attempt to write ro area, offset:0x%08x, count:0x%08x.\n", ++ ofblk, nrblk); ++ return 1; ++ } ++ ++ return 0; ++} +diff --git a/drivers/mmc/host/himciv200/himci_acl.h b/drivers/mmc/host/himciv200/himci_acl.h +new file mode 100644 +index 0000000..fee8229 +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci_acl.h +@@ -0,0 +1,15 @@ ++/****************************************************************************** ++ * Copyright (C) 2014 Hisilicon STB Development Dept ++ * All rights reserved. ++ * *** ++ * Create by Czyong ++ * ++******************************************************************************/ ++#ifndef HIMCI_ACL_H ++#define HIMCI_ACL_H ++ ++int himci_acl_init(int devid); ++ ++int himci_acl_rw(int devid, int is_write, u32 ofblk, u32 nrblk); ++ ++#endif +diff --git a/drivers/mmc/host/himciv200/himci_dbg.c b/drivers/mmc/host/himciv200/himci_dbg.c +new file mode 100644 +index 0000000..8fb3f27 +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci_dbg.c +@@ -0,0 +1,662 @@ ++/****************************************************************************** ++ * Copyright (C) 2014 Hisilicon STB Development Dept ++ * All rights reserved. ++ * *** ++ * Create by Czyong ++ * ++******************************************************************************/ ++#define pr_fmt(fmt) "mmcdbg: " fmt ++ ++#include <linux/kobject.h> ++#include <linux/slab.h> ++#include <linux/kmod.h> ++#include <linux/vmalloc.h> ++#include <linux/debugfs.h> ++#include <linux/mutex.h> ++#include <linux/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/sched.h> ++#include <linux/export.h> ++ ++#define MMC_MAX_BLK 0x2000000 /* 32M block, 16G size */ ++#define MMC_CHUNK_SIZE_SHIFT 11 /* 2K blocks, 1M size */ ++ ++#define MMC_CHUNK_SIZE (1 << MMC_CHUNK_SIZE_SHIFT) ++#define MMC_CHUNK_SIZE_MASK (MMC_CHUNK_SIZE - 1) ++#define MMC_NUM_CHUNK (MMC_MAX_BLK / MMC_CHUNK_SIZE) ++ ++#define NUM_PER_LINE 8 ++#define MAX_BUF ((NUM_PER_LINE + 1) * 9 + 24) ++#define MAX_PATH_LEN 260 ++ ++static DEFINE_MUTEX(mutex_rwcount); ++static DEFINE_MUTEX(mutex_notice); ++ ++static volatile unsigned int *rw_chunk[2][MMC_NUM_CHUNK] = { {0}, {0} }; ++ ++#define GET_CHUNK(_rw, _ofblk) rw_chunk[_rw][(_ofblk) >> MMC_CHUNK_SIZE_SHIFT] ++ ++static char *mmcdbg_options_string; ++static char mmcdbg_helper[MAX_PATH_LEN] = {0}; ++static int mmcdbg_rcount_enable; ++static int mmcdbg_wcount_enable; ++static int mmcdbg_devid = -1; ++ ++struct fo_data_t { ++ int is_write; ++ unsigned int ofblk; ++ int state; ++}; ++/******************************************************************************/ ++ ++static int mmcdbg_rw_count(int is_write, unsigned int ofblk, unsigned int nrblk) ++{ ++ int ret = -1; ++ int rw = is_write ? 1 : 0; ++ ++ if (!nrblk) ++ return 0; ++ ++ if (ofblk + nrblk > MMC_MAX_BLK) { ++ pr_err("out of block count.\n"); ++ return -1; ++ } ++ ++ mutex_lock(&mutex_rwcount); ++ ++ while (nrblk-- > 0) { ++ volatile unsigned int *entry = GET_CHUNK(rw, ofblk); ++ ++ if (!entry) { ++ entry = vmalloc(MMC_CHUNK_SIZE << 2); ++ if (!entry) { ++ pr_err("out of memory.\n"); ++ goto fail; ++ } ++ memset((void *)entry, 0, MMC_CHUNK_SIZE << 2); ++ ++ GET_CHUNK(rw, ofblk) = entry; ++ } ++ entry[ofblk & MMC_CHUNK_SIZE_MASK]++; ++ ofblk++; ++ } ++ ret = 0; ++fail: ++ mutex_unlock(&mutex_rwcount); ++ ++ return ret; ++} ++/******************************************************************************/ ++ ++static int mmcdbg_rw_free(int is_write) ++{ ++ unsigned int blk; ++ int rw = is_write ? 1 : 0; ++ ++ mutex_lock(&mutex_rwcount); ++ ++ for (blk = 0; blk < MMC_MAX_BLK; blk += MMC_CHUNK_SIZE) { ++ volatile unsigned int *entry = GET_CHUNK(rw, blk); ++ ++ if (entry) { ++ GET_CHUNK(rw, blk) = NULL; ++ vfree((void *)entry); ++ } ++ } ++ ++ mutex_unlock(&mutex_rwcount); ++ ++ return 0; ++} ++/******************************************************************************/ ++ ++static void dump_line(unsigned int ofblk, unsigned int *value, char **buf, ++ unsigned int *sz_buf, int state) ++{ ++ int ret; ++ ++ if (state == 0 || state == 1) { ++ int ix; ++ unsigned int tmp[NUM_PER_LINE] = {0}; ++ unsigned int *v = (value == NULL ? tmp : value); ++ ++ ret = snprintf(*buf, *sz_buf, "%08x:", ofblk); ++ *buf += ret; ++ *sz_buf -= ret; ++ ++ for (ix = 0; ix < NUM_PER_LINE; ix++) { ++ ret = snprintf(*buf, *sz_buf, " %04x", v[ix]); ++ *buf += ret; ++ *sz_buf -= ret; ++ } ++ ++ } else if (state == 2) { ++ ret = snprintf(*buf, *sz_buf, "..."); ++ *buf += ret; ++ *sz_buf -= ret; ++ ++ } else if (state == 3) { ++ ret = snprintf(*buf, *sz_buf, "%08x:", ofblk); ++ *buf += ret; ++ *sz_buf -= ret; ++ ++ } ++ ++ ret = snprintf(*buf, *sz_buf, "\n"); ++ *buf += ret; ++ *sz_buf -= ret; ++} ++/******************************************************************************/ ++ ++static int mmcdbg_rw_dump_buf(int is_write, unsigned int *ofblk, char *buf, ++ unsigned int sz_buf, int *state) ++{ ++ char *pbuf = buf; ++ unsigned int index; ++ unsigned int blk = *ofblk; ++ int rw = is_write ? 1 : 0; ++ ++ if (*state == 3) ++ return 0; ++ ++ sz_buf--; /* for trailing end '\0' */ ++ ++ while (blk < MMC_MAX_BLK) { ++ unsigned int *entry = (unsigned int *)GET_CHUNK(rw, blk); ++ ++ if (!entry) { ++ if (sz_buf < MAX_BUF) ++ goto no_buf; ++ ++ if (*state == 0) { ++ /* XXXXXXX: 0000 0000 0000 0000 0000 0000 0000 0000 */ ++ *state = 1; ++ dump_line(blk, NULL, &pbuf, &sz_buf, *state); ++ } ++ ++ if (*state == 1) { ++ /* ... */ ++ *state = 2; ++ dump_line(blk, NULL, &pbuf, &sz_buf, *state); ++ } ++ ++ blk += MMC_CHUNK_SIZE; ++ continue; ++ } ++ ++ index = blk & MMC_CHUNK_SIZE_MASK; ++ blk = blk & ~MMC_CHUNK_SIZE_MASK; ++ ++ while (index < MMC_CHUNK_SIZE) { ++ int ix, ret; ++ ++ if (sz_buf < MAX_BUF) { ++ blk += index; ++ goto no_buf; ++ } ++ ++ ret = 0; ++ for (ix = 0; ix < NUM_PER_LINE; ix++) ++ ret |= entry[ix + index]; ++ ++ if (!ret) { ++ if (*state == 0) { ++ *state = 1; ++ dump_line(blk + index, NULL, &pbuf, ++ &sz_buf, *state); ++ } else if (*state == 1) { ++ *state = 2; ++ dump_line(blk + index, NULL, &pbuf, ++ &sz_buf, *state); ++ } ++ } else { ++ if (*state) ++ *state = 0; ++ ++ dump_line(blk + index, &entry[index], ++ &pbuf, &sz_buf, *state); ++ } ++ ++ index += NUM_PER_LINE; ++ } ++ ++ blk += MMC_CHUNK_SIZE; ++ } ++ ++ if (*state == 2) { ++ *state = 3; ++ dump_line(blk, NULL, &pbuf, &sz_buf, *state); ++ } ++ ++no_buf: ++ *ofblk = blk; ++ ++ pbuf[0] = '\0'; ++ ++ return pbuf - buf; ++} ++/******************************************************************************/ ++ ++static int mmcdbg_rw_notice(int is_write, u32 ofblk, u32 nrblk) ++{ ++ int retval = 0; ++ char *argv[2]; ++ struct kobj_uevent_env *env; ++ char *rwstr = is_write ? "write" : "read"; ++ ++ env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL); ++ if (!env) ++ return -ENOMEM; ++ ++ retval = add_uevent_var(env, "RW=%s", rwstr); ++ if (retval) ++ goto exit; ++ ++ retval = add_uevent_var(env, "OFFSET=0x%08x", ofblk); ++ if (retval) ++ goto exit; ++ ++ retval = add_uevent_var(env, "COUNT=0x%08x", nrblk); ++ if (retval) ++ goto exit; ++ ++ retval = add_uevent_var(env, "PID=%d", current->pid); ++ if (retval) ++ goto exit; ++ ++ retval = add_uevent_var(env, "COMM=%s", current->comm); ++ if (retval) ++ goto exit; ++ ++ argv[0] = mmcdbg_helper; ++ argv[1] = NULL; ++ ++ retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin"); ++ if (retval) ++ goto exit; ++ ++ retval = call_usermodehelper(argv[0], argv, ++ env->envp, UMH_WAIT_EXEC); ++exit: ++ kfree(env); ++ return retval; ++} ++/******************************************************************************/ ++ ++static ssize_t fo_dump_rwcount_read(struct file *filp, char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ int ret; ++ char buf[256] = {0}; ++ char __user *pusrbuf = buffer; ++ struct fo_data_t *data = (struct fo_data_t *)filp->private_data; ++ ++ if (*ppos == 2) ++ return 0; ++ ++ if (data->is_write && !mmcdbg_wcount_enable) { ++ *ppos = 2; ++ ++ ret = snprintf(buf, sizeof(buf), "mmc write count is disable.\n" ++ "input \"echo 1 > write\" to enable.\n"); ++ ++ if (copy_to_user(pusrbuf, buf, ret)) ++ return -EFAULT; ++ ++ pusrbuf += ret; ++ ++ return pusrbuf - buffer; ++ } ++ ++ if (!data->is_write && !mmcdbg_rcount_enable) { ++ *ppos = 2; ++ ++ ret = snprintf(buf, sizeof(buf), "mmc read count is disable.\n" ++ "input \"echo 1 > read\" to enable\n"); ++ ++ if (copy_to_user(pusrbuf, buf, ret)) ++ return -EFAULT; ++ ++ pusrbuf += ret; ++ ++ return pusrbuf - buffer; ++ } ++ ++ do { ++ if (count < sizeof(buf)) { ++ *ppos = 1; ++ break; ++ } ++ ++ mutex_lock(&mutex_rwcount); ++ ++ ret = mmcdbg_rw_dump_buf(data->is_write, &data->ofblk, buf, ++ sizeof(buf), &data->state); ++ ++ mutex_unlock(&mutex_rwcount); ++ ++ if (!ret) { ++ *ppos = 2; ++ break; ++ } ++ ++ if (copy_to_user(pusrbuf, buf, ret)) ++ return -EFAULT; ++ ++ pusrbuf += ret; ++ count -= ret; ++ ++ } while (ret); ++ ++ return pusrbuf - buffer; ++} ++/******************************************************************************/ ++ ++static ssize_t fo_dump_rwcount_write(struct file *filp, ++ const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ char *enstr; ++ long enable = 0; ++ char buf[32] = {0}; ++ unsigned int sz_buf = sizeof(buf) - 1; ++ struct fo_data_t *data = (struct fo_data_t *)filp->private_data; ++ int rw = data->is_write ? 1 : 0; ++ ++ if (count < sz_buf) ++ sz_buf = count; ++ ++ if (copy_from_user(buf, buffer, sz_buf)) ++ return -EFAULT; ++ ++ buf[sz_buf] = '\0'; ++ if (kstrtol(buf, 0, &enable)) { ++ pr_err("input error :%s\n", buf); ++ enable = rw ? mmcdbg_wcount_enable : mmcdbg_rcount_enable; ++ } ++ ++ enstr = enable ? "enable" : "disable"; ++ ++ if (rw) { ++ if (enable != mmcdbg_wcount_enable) { ++ mmcdbg_wcount_enable = enable; ++ if (!mmcdbg_wcount_enable) ++ mmcdbg_rw_free(rw); ++ pr_info("%s write count.\n", enstr); ++ } ++ } else { ++ if (enable != mmcdbg_rcount_enable) { ++ mmcdbg_rcount_enable = enable; ++ if (!mmcdbg_rcount_enable) ++ mmcdbg_rw_free(rw); ++ pr_info("%s read count.\n", enstr); ++ } ++ } ++ ++ return count; ++} ++/******************************************************************************/ ++ ++static int fo_dump_rcount_open(struct inode *inode, struct file *file) ++{ ++ struct fo_data_t *data; ++ ++ data = vmalloc(sizeof(struct fo_data_t)); ++ if (!data) { ++ pr_err("out of memory.\n"); ++ return -ENOMEM; ++ } ++ ++ data->is_write = 0; ++ data->ofblk = 0; ++ data->state = 0; ++ ++ file->private_data = (void *)data; ++ ++ return 0; ++} ++/******************************************************************************/ ++ ++static int fo_dump_wcount_open(struct inode *inode, struct file *file) ++{ ++ struct fo_data_t *data; ++ ++ data = vmalloc(sizeof(struct fo_data_t)); ++ if (!data) { ++ pr_err("out of memory.\n"); ++ return -ENOMEM; ++ } ++ ++ data->is_write = 1; ++ data->ofblk = 0; ++ data->state = 0; ++ ++ file->private_data = (void *)data; ++ ++ return 0; ++} ++/******************************************************************************/ ++ ++static int fp_dump_rwcount_release(struct inode *inode, struct file *file) ++{ ++ struct fo_data_t *data = (struct fo_data_t *)file->private_data; ++ ++ if (data) ++ vfree(data); ++ ++ file->private_data = NULL; ++ ++ return 0; ++} ++/******************************************************************************/ ++ ++static ssize_t fo_rw_notice_read(struct file *filp, char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ int ret = 0; ++ char *kbuf; ++ ++ if (*ppos == 1) ++ return 0; ++ ++ *ppos = 1; ++ ++ kbuf = vmalloc(sizeof(mmcdbg_helper) + 4); ++ if (!kbuf) { ++ pr_err("out of memory.\n"); ++ return -ENOMEM; ++ } ++ ++ ret = snprintf(kbuf, sizeof(mmcdbg_helper) + 4, ++ "<%s>\n", mmcdbg_helper); ++ if (copy_to_user(buffer, kbuf, ret)) { ++ vfree(kbuf); ++ return -EFAULT; ++ } ++ ++ vfree(kbuf); ++ ++ return ret; ++} ++/******************************************************************************/ ++ ++static ssize_t fo_rw_notice_write(struct file *filp, const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ char *kbuf; ++ char *pkbuf; ++ int size = count; ++ ++ if (size >= sizeof(mmcdbg_helper)) ++ size = sizeof(mmcdbg_helper) - 1; ++ ++ kbuf = vmalloc(size + 1); ++ if (!kbuf) { ++ pr_err("out of memory.\n"); ++ return -ENOMEM; ++ } ++ ++ if (copy_from_user(kbuf, buffer, size)) { ++ vfree(kbuf); ++ return -EFAULT; ++ } ++ ++ kbuf[size] = '\0'; ++ ++ pkbuf = strim(kbuf); ++ ++ mutex_lock(&mutex_notice); ++ ++ strncpy(mmcdbg_helper, pkbuf, size); ++ ++ mutex_unlock(&mutex_notice); ++ ++ vfree(kbuf); ++ ++ return count; ++} ++/******************************************************************************/ ++ ++static const struct file_operations fo_dump_rcount = { ++ .owner = THIS_MODULE, ++ .open = fo_dump_rcount_open, ++ .read = fo_dump_rwcount_read, ++ .write = fo_dump_rwcount_write, ++ .release = fp_dump_rwcount_release, ++}; ++ ++static const struct file_operations fo_dump_wcount = { ++ .owner = THIS_MODULE, ++ .open = fo_dump_wcount_open, ++ .read = fo_dump_rwcount_read, ++ .write = fo_dump_rwcount_write, ++ .release = fp_dump_rwcount_release, ++}; ++ ++static const struct file_operations fo_rw_notice = { ++ .owner = THIS_MODULE, ++ .read = fo_rw_notice_read, ++ .write = fo_rw_notice_write, ++}; ++/******************************************************************************/ ++ ++static void mmcdbg_options(const char *string) ++{ ++ int enable; ++ const char *pos, *cmd; ++ ++ pos = string; ++ ++ while (*pos) { ++ while (*pos && *pos != '+' && *pos != '-') ++ pos++; ++ ++ switch (*pos++) { ++ case '+': ++ enable = 1; ++ break; ++ case '-': ++ enable = 0; ++ break; ++ default: ++ return; ++ } ++ ++ cmd = pos; ++ ++ while (*pos == '_' || isalpha(*pos)) ++ pos++; ++ ++ if (*cmd && pos > cmd) { ++ int size = pos - cmd; ++ char *enstr = enable ? "enable" : "disable"; ++ ++ if (!strncmp(cmd, "read", size)) { ++ mmcdbg_rcount_enable = enable; ++ pr_info("%s read count.\n", enstr); ++ } else if (!strncmp(cmd, "write", size)) { ++ mmcdbg_wcount_enable = enable; ++ pr_info("%s write count.\n", enstr); ++ } ++ } ++ ++ while (isspace(*pos) || *pos == ',' || *pos == ';') ++ pos++; ++ } ++} ++/******************************************************************************/ ++ ++int himci_dbg_rw(int devid, int is_write, u32 ofblk, u32 nrblk) ++{ ++ if (mmcdbg_devid != devid) ++ return 0; ++ ++ if (is_write && mmcdbg_wcount_enable) ++ mmcdbg_rw_count(is_write, ofblk, nrblk); ++ ++ if (!is_write && mmcdbg_rcount_enable) ++ mmcdbg_rw_count(is_write, ofblk, nrblk); ++ ++ if (mmcdbg_helper[0]) { ++ mutex_lock(&mutex_notice); ++ ++ mmcdbg_rw_notice(is_write, ofblk, nrblk); ++ ++ mutex_unlock(&mutex_notice); ++ } ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++int himci_dbg_init(int devid) ++{ ++ char buf[16]; ++ struct dentry *root = NULL; ++ struct dentry *ret = NULL; ++ unsigned int mode = S_IFREG | S_IRUSR | S_IWUSR; ++ ++ snprintf(buf, sizeof(buf), "himci%d", devid); ++ root = debugfs_create_dir(buf, NULL); ++ if (!root) { ++ pr_err("Can't create '%s' dir.\n", buf); ++ return -ENOENT; ++ } ++ ++ ret = debugfs_create_file("read", mode, root, NULL, &fo_dump_rcount); ++ if (!ret) { ++ pr_err("Can't create 'read' file.\n"); ++ goto fail; ++ } ++ ++ ret = debugfs_create_file("write", mode, root, NULL, &fo_dump_wcount); ++ if (!ret) { ++ pr_err("Can't create 'write' file.\n"); ++ goto fail; ++ } ++ ++ ret = debugfs_create_file("notice", mode, root, NULL, &fo_rw_notice); ++ if (!ret) { ++ pr_err("Can't create 'notice' file.\n"); ++ goto fail; ++ } ++ ++ if (mmcdbg_options_string) ++ mmcdbg_options(mmcdbg_options_string); ++ ++ mmcdbg_devid = devid; ++ ++ return 0; ++fail: ++ debugfs_remove_recursive(root); ++ ++ return -ENOENT; ++} ++/*****************************************************************************/ ++ ++static int __init mmcdbg_options_setup(char *s) ++{ ++ mmcdbg_options_string = s; ++ return 1; ++} ++__setup("mmcdbg=", mmcdbg_options_setup); +diff --git a/drivers/mmc/host/himciv200/himci_dbg.h b/drivers/mmc/host/himciv200/himci_dbg.h +new file mode 100644 +index 0000000..45adb29 +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci_dbg.h +@@ -0,0 +1,15 @@ ++/****************************************************************************** ++ * Copyright (C) 2014 Hisilicon STB Development Dept ++ * All rights reserved. ++ * *** ++ * Create by Czyong ++ * ++******************************************************************************/ ++#ifndef HIMCI_DBG_H ++#define HIMCI_DBG_H ++ ++int himci_dbg_init(int devid); ++ ++int himci_dbg_rw(int devid, int is_write, u32 ofblk, u32 nrblk); ++ ++#endif +diff --git a/drivers/mmc/host/himciv200/himci_hi3516av200.c b/drivers/mmc/host/himciv200/himci_hi3516av200.c +new file mode 100644 +index 0000000..f505bf5 +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci_hi3516av200.c +@@ -0,0 +1,210 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++#include <mach/platform.h> ++ ++#define MMC_CRG_MIN 12500000 ++ ++#define PERI_CRG49 IO_ADDRESS(0x120100c4) ++#define EMMC_DLL_CTRL IO_ADDRESS(0x12030140) ++ ++#define TUNING_START_PHASE 0 ++#define TUNING_END_PHASE 15 ++#define HIMCI_PHASE_SCALE 16 ++#define DRV_PHASE_DFLT (0x6<<23) ++#define SMPL_PHASE_DFLT (0x0<<16) ++ ++#define EMMC_DRV_PHASE_DFLT (0x4<<23) ++#define EMMC_SMPL_PHASE_DFLT SMPL_PHASE_DFLT ++ ++#define REG_PAD_CTRL 0x12040800 ++#define REG_CTRL_SDIO0_CCLK 0xc4 ++#define REG_CTRL_SDIO0_CCMD 0xc8 ++#define REG_CTRL_SDIO0_CDATA0 0xcc ++#define REG_CTRL_SDIO0_CDATA1 0xd0 ++#define REG_CTRL_SDIO0_CDATA2 0xd4 ++#define REG_CTRL_SDIO0_CDATA3 0xd8 ++ ++#define REG_CTRL_SDIO1_CCLK 0xe8 ++#define REG_CTRL_SDIO1_CCMD 0xec ++#define REG_CTRL_SDIO1_CDATA0 0xf0 ++#define REG_CTRL_SDIO1_CDATA1 0xf4 ++#define REG_CTRL_SDIO1_CDATA2 0xf8 ++#define REG_CTRL_SDIO1_CDATA3 0xfc ++ ++#define REG_CTRL_EMMC_CLK 0x1ac ++#define REG_CTRL_EMMC_CMD 0x1c8 ++#define REG_CTRL_EMMC_DATA0 0x1b0 ++#define REG_CTRL_EMMC_DATA1 0x1cc ++#define REG_CTRL_EMMC_DATA2 0x1b4 ++#define REG_CTRL_EMMC_DATA3 0x1bc ++#define REG_CTRL_EMMC_DATA4 0x1c0 ++#define REG_CTRL_EMMC_DATA5 0x1a8 ++#define REG_CTRL_EMMC_DATA6 0x1dc ++#define REG_CTRL_EMMC_DATA7 0x1b8 ++ ++#define EMMC_CLK_DS_3V3 0xd0 ++#define EMMC_CMD_DS_3V3 0xd0 ++#define EMMC_DATA0_DS_3V3 0xd0 ++#define EMMC_DATA1_DS_3V3 0xd0 ++#define EMMC_DATA2_DS_3V3 0xd0 ++#define EMMC_DATA3_DS_3V3 0xd0 ++#define EMMC_DATA4_DS_3V3 0xd0 ++#define EMMC_DATA5_DS_3V3 0xd0 ++#define EMMC_DATA6_DS_3V3 0xd0 ++#define EMMC_DATA7_DS_3V3 0xd0 ++ ++#define EMMC_CLK_DS_1V8 0xc0 ++#define EMMC_CMD_DS_1V8 0xc0 ++#define EMMC_DATA0_DS_1V8 0xc0 ++#define EMMC_DATA1_DS_1V8 0xc0 ++#define EMMC_DATA2_DS_1V8 0xc0 ++#define EMMC_DATA3_DS_1V8 0xc0 ++#define EMMC_DATA4_DS_1V8 0xc0 ++#define EMMC_DATA5_DS_1V8 0xc0 ++#define EMMC_DATA6_DS_1V8 0xc0 ++#define EMMC_DATA7_DS_1V8 0xc0 ++ ++struct sdio_drv_cap { ++ unsigned int reg_addr[2]; ++ unsigned int ds; ++}; ++ ++#define SDIO_DRV_CAP(ofst1, ofst2, v) { \ ++ .reg_addr[0] = (REG_PAD_CTRL + ofst1), \ ++ .reg_addr[1] = (REG_PAD_CTRL + ofst2), \ ++ .ds = v} ++static struct sdio_drv_cap sdio_ds_hs[] = { ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCLK, REG_CTRL_SDIO1_CCLK, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCMD, REG_CTRL_SDIO1_CCMD, 0x1e0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA0, REG_CTRL_SDIO1_CDATA0, 0x1e0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA1, REG_CTRL_SDIO1_CDATA1, 0x1e0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA2, REG_CTRL_SDIO1_CDATA2, 0x1e0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA3, REG_CTRL_SDIO1_CDATA3, 0x1e0) ++}; ++ ++static struct sdio_drv_cap sdio_ds_ddr50[] = { ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCLK, REG_CTRL_SDIO1_CCLK, 0x1b0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCMD, REG_CTRL_SDIO1_CCMD, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA0, REG_CTRL_SDIO1_CDATA0, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA1, REG_CTRL_SDIO1_CDATA1, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA2, REG_CTRL_SDIO1_CDATA2, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA3, REG_CTRL_SDIO1_CDATA3, 0x1c0) ++}; ++ ++static struct sdio_drv_cap sdio_ds_sdr50[] = { ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCLK, REG_CTRL_SDIO1_CCLK, 0x80), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCMD, REG_CTRL_SDIO1_CCMD, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA0, REG_CTRL_SDIO1_CDATA0, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA1, REG_CTRL_SDIO1_CDATA1, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA2, REG_CTRL_SDIO1_CDATA2, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA3, REG_CTRL_SDIO1_CDATA3, 0x1c0) ++}; ++ ++static struct sdio_drv_cap sdio_ds_sdr104[] = { ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCLK, REG_CTRL_SDIO1_CCLK, 0x0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCMD, REG_CTRL_SDIO1_CCMD, 0xc0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA0, REG_CTRL_SDIO1_CDATA0, 0xc0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA1, REG_CTRL_SDIO1_CDATA1, 0xc0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA2, REG_CTRL_SDIO1_CDATA2, 0xc0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA3, REG_CTRL_SDIO1_CDATA3, 0xc0) ++}; ++ ++struct emmc_drv_cap { ++ unsigned int reg_addr; ++ unsigned int ds[2]; ++}; ++ ++#define EMMC_DRV_CAP(ofst, v1, v2) { \ ++ .reg_addr = (REG_PAD_CTRL + ofst), \ ++ .ds[0] = v1, \ ++ .ds[1] = v2} ++static struct emmc_drv_cap emmc_ds[] = { ++ EMMC_DRV_CAP(REG_CTRL_EMMC_CLK, EMMC_CLK_DS_3V3, EMMC_CLK_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_CMD, EMMC_CMD_DS_3V3, EMMC_CMD_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA0, EMMC_DATA0_DS_3V3, EMMC_DATA0_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA1, EMMC_DATA1_DS_3V3, EMMC_DATA1_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA2, EMMC_DATA2_DS_3V3, EMMC_DATA2_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA3, EMMC_DATA3_DS_3V3, EMMC_DATA3_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA4, EMMC_DATA4_DS_3V3, EMMC_DATA4_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA5, EMMC_DATA5_DS_3V3, EMMC_DATA5_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA6, EMMC_DATA6_DS_3V3, EMMC_DATA6_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA7, EMMC_DATA7_DS_3V3, EMMC_DATA7_DS_1V8), ++}; ++ ++static void himci_set_pin_drv_cap(struct himci_host *host, unsigned int flag, ++ unsigned char timing) ++{ ++ struct sdio_drv_cap *sdio_ds; ++ unsigned int i; ++ ++ if (host->devid == 2) { ++ for (i = 0; i < ARRAY_SIZE(emmc_ds); i++) ++ himci_writel(emmc_ds[i].ds[flag], IO_ADDRESS(emmc_ds[i].reg_addr)); ++ } else { ++ if (timing == MMC_TIMING_UHS_SDR104) ++ sdio_ds = sdio_ds_sdr104; ++ else if (timing == MMC_TIMING_UHS_SDR50) ++ sdio_ds = sdio_ds_sdr50; ++ else if (timing == MMC_TIMING_UHS_DDR50) ++ sdio_ds = sdio_ds_ddr50; ++ else ++ sdio_ds = sdio_ds_hs; ++ ++ for (i = 0; i < ARRAY_SIZE(sdio_ds_hs); i++) ++ himci_writel(sdio_ds[i].ds, ++ IO_ADDRESS(sdio_ds[i].reg_addr[host->devid])); ++ } ++} ++ ++static void himci_set_drv_cap(struct himci_host *host, struct mmc_ios *ios) ++{ ++ unsigned int tmp_reg; ++ ++ if (ios->timing == MMC_TIMING_UHS_DDR50) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~CLK_SMPL_PHS_MASK; ++ tmp_reg |= (0x4 << CLK_SMPL_PHS_SHIFT); ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } else if (ios->timing == MMC_TIMING_MMC_HS) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~CLK_SMPL_PHS_MASK; ++ tmp_reg |= SMPL_PHASE_DFLT; ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } else if (ios->timing == MMC_TIMING_MMC_HS400) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~CLK_SMPL_PHS_MASK; ++ tmp_reg |= (host->phase << CLK_SMPL_PHS_SHIFT); ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } ++ ++ /* set pin drive capability */ ++ if (ios->timing == MMC_TIMING_MMC_HS200 || ++ ios->timing == MMC_TIMING_MMC_HS400) ++ himci_set_pin_drv_cap(host, 1, ios->timing); ++ else ++ himci_set_pin_drv_cap(host, 0, ios->timing); ++} ++ ++static int himci_check_emmc(struct himci_host *host) ++{ ++ unsigned int val; ++ ++ val = himci_readl(IO_ADDRESS(SYS_CTRL_BASE + REG_SC_STAT)); ++ ++ return !(GET_SYS_BOOT_MODE(val) == BOOT_FROM_EMMC); ++} +diff --git a/drivers/mmc/host/himciv200/himci_hi3516cv300.c b/drivers/mmc/host/himciv200/himci_hi3516cv300.c +new file mode 100644 +index 0000000..ea35c2b +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci_hi3516cv300.c +@@ -0,0 +1,119 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++#include <linux/mfd/syscon.h> ++#include <linux/regmap.h> ++ ++#define MMC_CRG_MIN 49500000 ++ ++#define TUNING_START_PHASE 0 ++#define TUNING_END_PHASE 7 ++#define HIMCI_PHASE_SCALE 8 ++#define DRV_PHASE_DFLT (0x4<<23) ++#define SMPL_PHASE_DFLT (0x1<<16) ++ ++#define EMMC_DRV_PHASE_DFLT DRV_PHASE_DFLT ++#define EMMC_SMPL_PHASE_DFLT SMPL_PHASE_DFLT ++ ++#define GET_SYS_BOOT_MODE(_reg) (((_reg) >> 0x4) & 0x1) ++#define BOOT_FROM_EMMC 0x1 ++ ++#define REG_PAD_CTRL 0x12040800 ++ ++#define REG_CTRL_EMMC_CLK 0xa0 ++#define REG_CTRL_EMMC_CMD 0x9c ++#define REG_CTRL_EMMC_DATA0 0x90 ++#define REG_CTRL_EMMC_DATA1 0x98 ++#define REG_CTRL_EMMC_DATA2 0xa4 ++#define REG_CTRL_EMMC_DATA3 0x8c ++ ++#define EMMC_CLK_DS_1V8 0xc0 ++#define EMMC_CMD_DS_1V8 0x150 ++#define EMMC_DATA0_DS_1V8 0x1d0 ++#define EMMC_DATA1_DS_1V8 0x1d0 ++#define EMMC_DATA2_DS_1V8 0x1d0 ++#define EMMC_DATA3_DS_1V8 0x1d0 ++ ++struct emmc_drv_cap { ++ unsigned int reg_addr; ++ unsigned int ds; ++}; ++ ++#define EMMC_DRV_CAP(ofst, v) { \ ++ .reg_addr = ofst, \ ++ .ds = v} ++static struct emmc_drv_cap emmc_ds[] = { ++ EMMC_DRV_CAP(REG_CTRL_EMMC_CLK, EMMC_CLK_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_CMD, EMMC_CMD_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA0, EMMC_DATA0_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA1, EMMC_DATA1_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA2, EMMC_DATA2_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA3, EMMC_DATA3_DS_1V8), ++}; ++ ++static void himci_set_drv_cap(struct himci_host *host, struct mmc_ios *ios) ++{ ++ static void *pad_ctrl = NULL; ++ unsigned int i, tmp_reg; ++ ++ if (ios->timing == MMC_TIMING_MMC_DDR52) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~CLK_DRV_PHS_MASK; ++ tmp_reg |= (0x2 << CLK_DRV_PHS_SHIFT); ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } ++ ++ if (ios->timing == MMC_TIMING_MMC_HS200) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~(CLK_DRV_PHS_MASK); ++ tmp_reg |= (0x3 << CLK_DRV_PHS_SHIFT); ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } ++ ++ if (host->mmc->caps & MMC_CAP_SD_HIGHSPEED ++ || ios->timing == MMC_TIMING_LEGACY) ++ return; ++ ++ if (!pad_ctrl) ++ pad_ctrl = ioremap(REG_PAD_CTRL, 0x1000); ++ ++ if (!pad_ctrl) ++ return; ++ ++ for (i = 0; i < ARRAY_SIZE(emmc_ds); i++) ++ himci_writel(emmc_ds[i].ds, pad_ctrl + emmc_ds[i].reg_addr); ++} ++ ++static int himci_check_emmc(struct himci_host *host) ++{ ++ struct device *dev = &(host->pdev->dev); ++ unsigned int val; ++ ++ if (!host->sys_regmap) { ++ host->sys_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "regmap"); ++ if (IS_ERR(host->sys_regmap)) { ++ host->sys_regmap = NULL; ++ return 1; ++ } ++ } ++ ++ if (regmap_read(host->sys_regmap, 0x8c, &val)) ++ return 1; ++ ++ return !(GET_SYS_BOOT_MODE(val) == BOOT_FROM_EMMC); ++} +diff --git a/drivers/mmc/host/himciv200/himci_hi3519.c b/drivers/mmc/host/himciv200/himci_hi3519.c +new file mode 100644 +index 0000000..03bfafe +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci_hi3519.c +@@ -0,0 +1,192 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++#include <mach/platform.h> ++ ++#define MMC_CRG_MIN 12500000 ++ ++#define PERI_CRG49 IO_ADDRESS(0x120100c4) ++#define TUNING_START_PHASE 2 ++#define TUNING_END_PHASE 14 ++ ++#define EMMC_DLL_CTRL IO_ADDRESS(0x12030140) ++#define REG_PAD_CTRL 0x12040800 ++#define DRV_PHASE_DFLT (0x6<<23) ++#define SMPL_PHASE_DFLT (0x0<<16) ++ ++#define EMMC_DRV_PHASE_DFLT DRV_PHASE_DFLT ++#define EMMC_SMPL_PHASE_DFLT SMPL_PHASE_DFLT ++ ++#define REG_CTRL_SDIO0_CCLK IO_ADDRESS(REG_PAD_CTRL + 0x114) ++#define REG_CTRL_SDIO0_CCMD IO_ADDRESS(REG_PAD_CTRL + 0x118) ++#define REG_CTRL_SDIO0_CDATA0 IO_ADDRESS(REG_PAD_CTRL + 0x11c) ++#define REG_CTRL_SDIO0_CDATA1 IO_ADDRESS(REG_PAD_CTRL + 0x120) ++#define REG_CTRL_SDIO0_CDATA2 IO_ADDRESS(REG_PAD_CTRL + 0x124) ++#define REG_CTRL_SDIO0_CDATA3 IO_ADDRESS(REG_PAD_CTRL + 0x128) ++ ++#define REG_CTRL_SDIO1_CCLK IO_ADDRESS(REG_PAD_CTRL + 0x138) ++#define REG_CTRL_SDIO1_CCMD IO_ADDRESS(REG_PAD_CTRL + 0x13c) ++#define REG_CTRL_SDIO1_CDATA0 IO_ADDRESS(REG_PAD_CTRL + 0x140) ++#define REG_CTRL_SDIO1_CDATA1 IO_ADDRESS(REG_PAD_CTRL + 0x144) ++#define REG_CTRL_SDIO1_CDATA2 IO_ADDRESS(REG_PAD_CTRL + 0x148) ++#define REG_CTRL_SDIO1_CDATA3 IO_ADDRESS(REG_PAD_CTRL + 0x14c) ++ ++#define REG_CTRL_EMMC_DS IO_ADDRESS(REG_PAD_CTRL + 0x17c) ++#define REG_CTRL_EMMC_CLK IO_ADDRESS(REG_PAD_CTRL + 0x154) ++#define REG_CTRL_EMMC_CMD IO_ADDRESS(REG_PAD_CTRL + 0x170) ++#define REG_CTRL_EMMC_DATA0 IO_ADDRESS(REG_PAD_CTRL + 0x158) ++#define REG_CTRL_EMMC_DATA1 IO_ADDRESS(REG_PAD_CTRL + 0x174) ++#define REG_CTRL_EMMC_DATA2 IO_ADDRESS(REG_PAD_CTRL + 0x15c) ++#define REG_CTRL_EMMC_DATA3 IO_ADDRESS(REG_PAD_CTRL + 0x164) ++#define REG_CTRL_EMMC_DATA4 IO_ADDRESS(REG_PAD_CTRL + 0x168) ++#define REG_CTRL_EMMC_DATA5 IO_ADDRESS(REG_PAD_CTRL + 0x150) ++#define REG_CTRL_EMMC_DATA6 IO_ADDRESS(REG_PAD_CTRL + 0x184) ++#define REG_CTRL_EMMC_DATA7 IO_ADDRESS(REG_PAD_CTRL + 0x160) ++ ++#define SDIO_CCLK_DS_3V3 0x1c0 ++#define SDIO_CCMD_DS_3V3 0x140 ++#define SDIO_CDATA0_DS_3V3 0x140 ++#define SDIO_CDATA1_DS_3V3 0x140 ++#define SDIO_CDATA2_DS_3V3 0x140 ++#define SDIO_CDATA3_DS_3V3 0x140 ++ ++#define SDIO_CCLK_DS_1V8 0x0 ++#define SDIO_CCMD_DS_1V8 0x120 ++#define SDIO_CDATA0_DS_1V8 0x120 ++#define SDIO_CDATA1_DS_1V8 0x120 ++#define SDIO_CDATA2_DS_1V8 0x120 ++#define SDIO_CDATA3_DS_1V8 0x120 ++ ++#define EMMC_DS_DS_3V3 0x120 ++#define EMMC_CLK_DS_3V3 0x1c0 ++#define EMMC_CMD_DS_3V3 0x1d0 ++#define EMMC_DATA0_DS_3V3 0x1e0 ++#define EMMC_DATA1_DS_3V3 0x1d0 ++#define EMMC_DATA2_DS_3V3 0x1d0 ++#define EMMC_DATA3_DS_3V3 0x1e0 ++#define EMMC_DATA4_DS_3V3 0x1e0 ++#define EMMC_DATA5_DS_3V3 0x1d0 ++#define EMMC_DATA6_DS_3V3 0x1e0 ++#define EMMC_DATA7_DS_3V3 0x1e0 ++ ++#define EMMC_DS_DS_1V8 0x100 ++#define EMMC_CLK_DS_1V8 0x160 ++#define EMMC_CMD_DS_1V8 0x180 ++#define EMMC_DATA0_DS_1V8 0x180 ++#define EMMC_DATA1_DS_1V8 0x180 ++#define EMMC_DATA2_DS_1V8 0x180 ++#define EMMC_DATA3_DS_1V8 0x180 ++#define EMMC_DATA4_DS_1V8 0x180 ++#define EMMC_DATA5_DS_1V8 0x180 ++#define EMMC_DATA6_DS_1V8 0x180 ++#define EMMC_DATA7_DS_1V8 0x180 ++ ++static void himci_set_pin_drv_cap(struct himci_host *host, unsigned int flag) ++{ ++ if (flag) { ++ if (0 == host->devid) { ++ himci_writel(SDIO_CCLK_DS_1V8, REG_CTRL_SDIO0_CCLK); ++ himci_writel(SDIO_CCMD_DS_1V8, REG_CTRL_SDIO0_CCMD); ++ himci_writel(SDIO_CDATA0_DS_1V8, REG_CTRL_SDIO0_CDATA0); ++ himci_writel(SDIO_CDATA1_DS_1V8, REG_CTRL_SDIO0_CDATA1); ++ himci_writel(SDIO_CDATA2_DS_1V8, REG_CTRL_SDIO0_CDATA2); ++ himci_writel(SDIO_CDATA3_DS_1V8, REG_CTRL_SDIO0_CDATA3); ++ } else if (1 == host->devid) { ++ himci_writel(SDIO_CCLK_DS_1V8, REG_CTRL_SDIO1_CCLK); ++ himci_writel(SDIO_CCMD_DS_1V8, REG_CTRL_SDIO1_CCMD); ++ himci_writel(SDIO_CDATA0_DS_1V8, REG_CTRL_SDIO1_CDATA0); ++ himci_writel(SDIO_CDATA1_DS_1V8, REG_CTRL_SDIO1_CDATA1); ++ himci_writel(SDIO_CDATA2_DS_1V8, REG_CTRL_SDIO1_CDATA2); ++ himci_writel(SDIO_CDATA3_DS_1V8, REG_CTRL_SDIO1_CDATA3); ++ } else if (2 == host->devid) { ++ himci_writel(EMMC_DS_DS_1V8, REG_CTRL_EMMC_DS); ++ himci_writel(EMMC_CLK_DS_1V8, REG_CTRL_EMMC_CLK); ++ himci_writel(EMMC_CMD_DS_1V8, REG_CTRL_EMMC_CMD); ++ himci_writel(EMMC_DATA0_DS_1V8, REG_CTRL_EMMC_DATA0); ++ himci_writel(EMMC_DATA1_DS_1V8, REG_CTRL_EMMC_DATA1); ++ himci_writel(EMMC_DATA2_DS_1V8, REG_CTRL_EMMC_DATA2); ++ himci_writel(EMMC_DATA3_DS_1V8, REG_CTRL_EMMC_DATA3); ++ himci_writel(EMMC_DATA4_DS_1V8, REG_CTRL_EMMC_DATA4); ++ himci_writel(EMMC_DATA5_DS_1V8, REG_CTRL_EMMC_DATA5); ++ himci_writel(EMMC_DATA6_DS_1V8, REG_CTRL_EMMC_DATA6); ++ himci_writel(EMMC_DATA7_DS_1V8, REG_CTRL_EMMC_DATA7); ++ } ++ } else { ++ /* config Pin drive capability */ ++ if (0 == host->devid) { ++ himci_writel(SDIO_CCLK_DS_3V3, REG_CTRL_SDIO0_CCLK); ++ himci_writel(SDIO_CCMD_DS_3V3, REG_CTRL_SDIO0_CCMD); ++ himci_writel(SDIO_CDATA0_DS_3V3, REG_CTRL_SDIO0_CDATA0); ++ himci_writel(SDIO_CDATA1_DS_3V3, REG_CTRL_SDIO0_CDATA1); ++ himci_writel(SDIO_CDATA2_DS_3V3, REG_CTRL_SDIO0_CDATA2); ++ himci_writel(SDIO_CDATA3_DS_3V3, REG_CTRL_SDIO0_CDATA3); ++ } else if (1 == host->devid) { ++ himci_writel(SDIO_CCLK_DS_3V3, REG_CTRL_SDIO1_CCLK); ++ himci_writel(SDIO_CCMD_DS_3V3, REG_CTRL_SDIO1_CCMD); ++ himci_writel(SDIO_CDATA0_DS_3V3, REG_CTRL_SDIO1_CDATA0); ++ himci_writel(SDIO_CDATA1_DS_3V3, REG_CTRL_SDIO1_CDATA1); ++ himci_writel(SDIO_CDATA2_DS_3V3, REG_CTRL_SDIO1_CDATA2); ++ himci_writel(SDIO_CDATA3_DS_3V3, REG_CTRL_SDIO1_CDATA3); ++ } else if (2 == host->devid) { ++ himci_writel(EMMC_DS_DS_3V3, REG_CTRL_EMMC_DS); ++ himci_writel(EMMC_CLK_DS_3V3, REG_CTRL_EMMC_CLK); ++ himci_writel(EMMC_CMD_DS_3V3, REG_CTRL_EMMC_CMD); ++ himci_writel(EMMC_DATA0_DS_3V3, REG_CTRL_EMMC_DATA0); ++ himci_writel(EMMC_DATA1_DS_3V3, REG_CTRL_EMMC_DATA1); ++ himci_writel(EMMC_DATA2_DS_3V3, REG_CTRL_EMMC_DATA2); ++ himci_writel(EMMC_DATA3_DS_3V3, REG_CTRL_EMMC_DATA3); ++ himci_writel(EMMC_DATA4_DS_3V3, REG_CTRL_EMMC_DATA4); ++ himci_writel(EMMC_DATA5_DS_3V3, REG_CTRL_EMMC_DATA5); ++ himci_writel(EMMC_DATA6_DS_3V3, REG_CTRL_EMMC_DATA6); ++ himci_writel(EMMC_DATA7_DS_3V3, REG_CTRL_EMMC_DATA7); ++ } ++ } ++} ++ ++static void himci_set_drv_cap(struct himci_host *host, struct mmc_ios *ios) ++{ ++ unsigned int tmp_reg; ++ ++ if (ios->timing == MMC_TIMING_UHS_DDR50) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~CLK_SMPL_PHS_MASK; ++ tmp_reg |= (0x4 << 16); ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } else if (ios->timing == MMC_TIMING_UHS_SDR104) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~CLK_DRV_PHS_MASK; ++ tmp_reg |= (0x4 << 23); ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } ++ ++ /* set pin drive capability */ ++ if (ios->timing == MMC_TIMING_MMC_HS200 || ++ ios->timing == MMC_TIMING_MMC_HS400 || ++ ios->timing == MMC_TIMING_UHS_SDR104) ++ himci_set_pin_drv_cap(host, 1); ++ else ++ himci_set_pin_drv_cap(host, 0); ++} ++ ++static int himci_check_emmc(struct himci_host *host) ++{ ++ unsigned int val; ++ ++ val = himci_readl(IO_ADDRESS(SYS_CTRL_BASE + REG_SC_STAT)); ++ ++ return !(GET_SYS_BOOT_MODE(val) == BOOT_FROM_EMMC); ++} +diff --git a/drivers/mmc/host/himciv200/himci_hi3519v101.c b/drivers/mmc/host/himciv200/himci_hi3519v101.c +new file mode 100644 +index 0000000..f505bf5 +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci_hi3519v101.c +@@ -0,0 +1,210 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++#include <mach/platform.h> ++ ++#define MMC_CRG_MIN 12500000 ++ ++#define PERI_CRG49 IO_ADDRESS(0x120100c4) ++#define EMMC_DLL_CTRL IO_ADDRESS(0x12030140) ++ ++#define TUNING_START_PHASE 0 ++#define TUNING_END_PHASE 15 ++#define HIMCI_PHASE_SCALE 16 ++#define DRV_PHASE_DFLT (0x6<<23) ++#define SMPL_PHASE_DFLT (0x0<<16) ++ ++#define EMMC_DRV_PHASE_DFLT (0x4<<23) ++#define EMMC_SMPL_PHASE_DFLT SMPL_PHASE_DFLT ++ ++#define REG_PAD_CTRL 0x12040800 ++#define REG_CTRL_SDIO0_CCLK 0xc4 ++#define REG_CTRL_SDIO0_CCMD 0xc8 ++#define REG_CTRL_SDIO0_CDATA0 0xcc ++#define REG_CTRL_SDIO0_CDATA1 0xd0 ++#define REG_CTRL_SDIO0_CDATA2 0xd4 ++#define REG_CTRL_SDIO0_CDATA3 0xd8 ++ ++#define REG_CTRL_SDIO1_CCLK 0xe8 ++#define REG_CTRL_SDIO1_CCMD 0xec ++#define REG_CTRL_SDIO1_CDATA0 0xf0 ++#define REG_CTRL_SDIO1_CDATA1 0xf4 ++#define REG_CTRL_SDIO1_CDATA2 0xf8 ++#define REG_CTRL_SDIO1_CDATA3 0xfc ++ ++#define REG_CTRL_EMMC_CLK 0x1ac ++#define REG_CTRL_EMMC_CMD 0x1c8 ++#define REG_CTRL_EMMC_DATA0 0x1b0 ++#define REG_CTRL_EMMC_DATA1 0x1cc ++#define REG_CTRL_EMMC_DATA2 0x1b4 ++#define REG_CTRL_EMMC_DATA3 0x1bc ++#define REG_CTRL_EMMC_DATA4 0x1c0 ++#define REG_CTRL_EMMC_DATA5 0x1a8 ++#define REG_CTRL_EMMC_DATA6 0x1dc ++#define REG_CTRL_EMMC_DATA7 0x1b8 ++ ++#define EMMC_CLK_DS_3V3 0xd0 ++#define EMMC_CMD_DS_3V3 0xd0 ++#define EMMC_DATA0_DS_3V3 0xd0 ++#define EMMC_DATA1_DS_3V3 0xd0 ++#define EMMC_DATA2_DS_3V3 0xd0 ++#define EMMC_DATA3_DS_3V3 0xd0 ++#define EMMC_DATA4_DS_3V3 0xd0 ++#define EMMC_DATA5_DS_3V3 0xd0 ++#define EMMC_DATA6_DS_3V3 0xd0 ++#define EMMC_DATA7_DS_3V3 0xd0 ++ ++#define EMMC_CLK_DS_1V8 0xc0 ++#define EMMC_CMD_DS_1V8 0xc0 ++#define EMMC_DATA0_DS_1V8 0xc0 ++#define EMMC_DATA1_DS_1V8 0xc0 ++#define EMMC_DATA2_DS_1V8 0xc0 ++#define EMMC_DATA3_DS_1V8 0xc0 ++#define EMMC_DATA4_DS_1V8 0xc0 ++#define EMMC_DATA5_DS_1V8 0xc0 ++#define EMMC_DATA6_DS_1V8 0xc0 ++#define EMMC_DATA7_DS_1V8 0xc0 ++ ++struct sdio_drv_cap { ++ unsigned int reg_addr[2]; ++ unsigned int ds; ++}; ++ ++#define SDIO_DRV_CAP(ofst1, ofst2, v) { \ ++ .reg_addr[0] = (REG_PAD_CTRL + ofst1), \ ++ .reg_addr[1] = (REG_PAD_CTRL + ofst2), \ ++ .ds = v} ++static struct sdio_drv_cap sdio_ds_hs[] = { ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCLK, REG_CTRL_SDIO1_CCLK, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCMD, REG_CTRL_SDIO1_CCMD, 0x1e0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA0, REG_CTRL_SDIO1_CDATA0, 0x1e0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA1, REG_CTRL_SDIO1_CDATA1, 0x1e0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA2, REG_CTRL_SDIO1_CDATA2, 0x1e0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA3, REG_CTRL_SDIO1_CDATA3, 0x1e0) ++}; ++ ++static struct sdio_drv_cap sdio_ds_ddr50[] = { ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCLK, REG_CTRL_SDIO1_CCLK, 0x1b0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCMD, REG_CTRL_SDIO1_CCMD, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA0, REG_CTRL_SDIO1_CDATA0, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA1, REG_CTRL_SDIO1_CDATA1, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA2, REG_CTRL_SDIO1_CDATA2, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA3, REG_CTRL_SDIO1_CDATA3, 0x1c0) ++}; ++ ++static struct sdio_drv_cap sdio_ds_sdr50[] = { ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCLK, REG_CTRL_SDIO1_CCLK, 0x80), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCMD, REG_CTRL_SDIO1_CCMD, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA0, REG_CTRL_SDIO1_CDATA0, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA1, REG_CTRL_SDIO1_CDATA1, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA2, REG_CTRL_SDIO1_CDATA2, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA3, REG_CTRL_SDIO1_CDATA3, 0x1c0) ++}; ++ ++static struct sdio_drv_cap sdio_ds_sdr104[] = { ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCLK, REG_CTRL_SDIO1_CCLK, 0x0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCMD, REG_CTRL_SDIO1_CCMD, 0xc0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA0, REG_CTRL_SDIO1_CDATA0, 0xc0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA1, REG_CTRL_SDIO1_CDATA1, 0xc0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA2, REG_CTRL_SDIO1_CDATA2, 0xc0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA3, REG_CTRL_SDIO1_CDATA3, 0xc0) ++}; ++ ++struct emmc_drv_cap { ++ unsigned int reg_addr; ++ unsigned int ds[2]; ++}; ++ ++#define EMMC_DRV_CAP(ofst, v1, v2) { \ ++ .reg_addr = (REG_PAD_CTRL + ofst), \ ++ .ds[0] = v1, \ ++ .ds[1] = v2} ++static struct emmc_drv_cap emmc_ds[] = { ++ EMMC_DRV_CAP(REG_CTRL_EMMC_CLK, EMMC_CLK_DS_3V3, EMMC_CLK_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_CMD, EMMC_CMD_DS_3V3, EMMC_CMD_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA0, EMMC_DATA0_DS_3V3, EMMC_DATA0_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA1, EMMC_DATA1_DS_3V3, EMMC_DATA1_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA2, EMMC_DATA2_DS_3V3, EMMC_DATA2_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA3, EMMC_DATA3_DS_3V3, EMMC_DATA3_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA4, EMMC_DATA4_DS_3V3, EMMC_DATA4_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA5, EMMC_DATA5_DS_3V3, EMMC_DATA5_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA6, EMMC_DATA6_DS_3V3, EMMC_DATA6_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA7, EMMC_DATA7_DS_3V3, EMMC_DATA7_DS_1V8), ++}; ++ ++static void himci_set_pin_drv_cap(struct himci_host *host, unsigned int flag, ++ unsigned char timing) ++{ ++ struct sdio_drv_cap *sdio_ds; ++ unsigned int i; ++ ++ if (host->devid == 2) { ++ for (i = 0; i < ARRAY_SIZE(emmc_ds); i++) ++ himci_writel(emmc_ds[i].ds[flag], IO_ADDRESS(emmc_ds[i].reg_addr)); ++ } else { ++ if (timing == MMC_TIMING_UHS_SDR104) ++ sdio_ds = sdio_ds_sdr104; ++ else if (timing == MMC_TIMING_UHS_SDR50) ++ sdio_ds = sdio_ds_sdr50; ++ else if (timing == MMC_TIMING_UHS_DDR50) ++ sdio_ds = sdio_ds_ddr50; ++ else ++ sdio_ds = sdio_ds_hs; ++ ++ for (i = 0; i < ARRAY_SIZE(sdio_ds_hs); i++) ++ himci_writel(sdio_ds[i].ds, ++ IO_ADDRESS(sdio_ds[i].reg_addr[host->devid])); ++ } ++} ++ ++static void himci_set_drv_cap(struct himci_host *host, struct mmc_ios *ios) ++{ ++ unsigned int tmp_reg; ++ ++ if (ios->timing == MMC_TIMING_UHS_DDR50) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~CLK_SMPL_PHS_MASK; ++ tmp_reg |= (0x4 << CLK_SMPL_PHS_SHIFT); ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } else if (ios->timing == MMC_TIMING_MMC_HS) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~CLK_SMPL_PHS_MASK; ++ tmp_reg |= SMPL_PHASE_DFLT; ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } else if (ios->timing == MMC_TIMING_MMC_HS400) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~CLK_SMPL_PHS_MASK; ++ tmp_reg |= (host->phase << CLK_SMPL_PHS_SHIFT); ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } ++ ++ /* set pin drive capability */ ++ if (ios->timing == MMC_TIMING_MMC_HS200 || ++ ios->timing == MMC_TIMING_MMC_HS400) ++ himci_set_pin_drv_cap(host, 1, ios->timing); ++ else ++ himci_set_pin_drv_cap(host, 0, ios->timing); ++} ++ ++static int himci_check_emmc(struct himci_host *host) ++{ ++ unsigned int val; ++ ++ val = himci_readl(IO_ADDRESS(SYS_CTRL_BASE + REG_SC_STAT)); ++ ++ return !(GET_SYS_BOOT_MODE(val) == BOOT_FROM_EMMC); ++} +diff --git a/drivers/mmc/host/himciv200/himci_hi3559.c b/drivers/mmc/host/himciv200/himci_hi3559.c +new file mode 100644 +index 0000000..f505bf5 +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci_hi3559.c +@@ -0,0 +1,210 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++#include <mach/platform.h> ++ ++#define MMC_CRG_MIN 12500000 ++ ++#define PERI_CRG49 IO_ADDRESS(0x120100c4) ++#define EMMC_DLL_CTRL IO_ADDRESS(0x12030140) ++ ++#define TUNING_START_PHASE 0 ++#define TUNING_END_PHASE 15 ++#define HIMCI_PHASE_SCALE 16 ++#define DRV_PHASE_DFLT (0x6<<23) ++#define SMPL_PHASE_DFLT (0x0<<16) ++ ++#define EMMC_DRV_PHASE_DFLT (0x4<<23) ++#define EMMC_SMPL_PHASE_DFLT SMPL_PHASE_DFLT ++ ++#define REG_PAD_CTRL 0x12040800 ++#define REG_CTRL_SDIO0_CCLK 0xc4 ++#define REG_CTRL_SDIO0_CCMD 0xc8 ++#define REG_CTRL_SDIO0_CDATA0 0xcc ++#define REG_CTRL_SDIO0_CDATA1 0xd0 ++#define REG_CTRL_SDIO0_CDATA2 0xd4 ++#define REG_CTRL_SDIO0_CDATA3 0xd8 ++ ++#define REG_CTRL_SDIO1_CCLK 0xe8 ++#define REG_CTRL_SDIO1_CCMD 0xec ++#define REG_CTRL_SDIO1_CDATA0 0xf0 ++#define REG_CTRL_SDIO1_CDATA1 0xf4 ++#define REG_CTRL_SDIO1_CDATA2 0xf8 ++#define REG_CTRL_SDIO1_CDATA3 0xfc ++ ++#define REG_CTRL_EMMC_CLK 0x1ac ++#define REG_CTRL_EMMC_CMD 0x1c8 ++#define REG_CTRL_EMMC_DATA0 0x1b0 ++#define REG_CTRL_EMMC_DATA1 0x1cc ++#define REG_CTRL_EMMC_DATA2 0x1b4 ++#define REG_CTRL_EMMC_DATA3 0x1bc ++#define REG_CTRL_EMMC_DATA4 0x1c0 ++#define REG_CTRL_EMMC_DATA5 0x1a8 ++#define REG_CTRL_EMMC_DATA6 0x1dc ++#define REG_CTRL_EMMC_DATA7 0x1b8 ++ ++#define EMMC_CLK_DS_3V3 0xd0 ++#define EMMC_CMD_DS_3V3 0xd0 ++#define EMMC_DATA0_DS_3V3 0xd0 ++#define EMMC_DATA1_DS_3V3 0xd0 ++#define EMMC_DATA2_DS_3V3 0xd0 ++#define EMMC_DATA3_DS_3V3 0xd0 ++#define EMMC_DATA4_DS_3V3 0xd0 ++#define EMMC_DATA5_DS_3V3 0xd0 ++#define EMMC_DATA6_DS_3V3 0xd0 ++#define EMMC_DATA7_DS_3V3 0xd0 ++ ++#define EMMC_CLK_DS_1V8 0xc0 ++#define EMMC_CMD_DS_1V8 0xc0 ++#define EMMC_DATA0_DS_1V8 0xc0 ++#define EMMC_DATA1_DS_1V8 0xc0 ++#define EMMC_DATA2_DS_1V8 0xc0 ++#define EMMC_DATA3_DS_1V8 0xc0 ++#define EMMC_DATA4_DS_1V8 0xc0 ++#define EMMC_DATA5_DS_1V8 0xc0 ++#define EMMC_DATA6_DS_1V8 0xc0 ++#define EMMC_DATA7_DS_1V8 0xc0 ++ ++struct sdio_drv_cap { ++ unsigned int reg_addr[2]; ++ unsigned int ds; ++}; ++ ++#define SDIO_DRV_CAP(ofst1, ofst2, v) { \ ++ .reg_addr[0] = (REG_PAD_CTRL + ofst1), \ ++ .reg_addr[1] = (REG_PAD_CTRL + ofst2), \ ++ .ds = v} ++static struct sdio_drv_cap sdio_ds_hs[] = { ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCLK, REG_CTRL_SDIO1_CCLK, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCMD, REG_CTRL_SDIO1_CCMD, 0x1e0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA0, REG_CTRL_SDIO1_CDATA0, 0x1e0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA1, REG_CTRL_SDIO1_CDATA1, 0x1e0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA2, REG_CTRL_SDIO1_CDATA2, 0x1e0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA3, REG_CTRL_SDIO1_CDATA3, 0x1e0) ++}; ++ ++static struct sdio_drv_cap sdio_ds_ddr50[] = { ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCLK, REG_CTRL_SDIO1_CCLK, 0x1b0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCMD, REG_CTRL_SDIO1_CCMD, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA0, REG_CTRL_SDIO1_CDATA0, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA1, REG_CTRL_SDIO1_CDATA1, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA2, REG_CTRL_SDIO1_CDATA2, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA3, REG_CTRL_SDIO1_CDATA3, 0x1c0) ++}; ++ ++static struct sdio_drv_cap sdio_ds_sdr50[] = { ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCLK, REG_CTRL_SDIO1_CCLK, 0x80), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCMD, REG_CTRL_SDIO1_CCMD, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA0, REG_CTRL_SDIO1_CDATA0, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA1, REG_CTRL_SDIO1_CDATA1, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA2, REG_CTRL_SDIO1_CDATA2, 0x1c0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA3, REG_CTRL_SDIO1_CDATA3, 0x1c0) ++}; ++ ++static struct sdio_drv_cap sdio_ds_sdr104[] = { ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCLK, REG_CTRL_SDIO1_CCLK, 0x0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CCMD, REG_CTRL_SDIO1_CCMD, 0xc0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA0, REG_CTRL_SDIO1_CDATA0, 0xc0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA1, REG_CTRL_SDIO1_CDATA1, 0xc0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA2, REG_CTRL_SDIO1_CDATA2, 0xc0), ++ SDIO_DRV_CAP(REG_CTRL_SDIO0_CDATA3, REG_CTRL_SDIO1_CDATA3, 0xc0) ++}; ++ ++struct emmc_drv_cap { ++ unsigned int reg_addr; ++ unsigned int ds[2]; ++}; ++ ++#define EMMC_DRV_CAP(ofst, v1, v2) { \ ++ .reg_addr = (REG_PAD_CTRL + ofst), \ ++ .ds[0] = v1, \ ++ .ds[1] = v2} ++static struct emmc_drv_cap emmc_ds[] = { ++ EMMC_DRV_CAP(REG_CTRL_EMMC_CLK, EMMC_CLK_DS_3V3, EMMC_CLK_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_CMD, EMMC_CMD_DS_3V3, EMMC_CMD_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA0, EMMC_DATA0_DS_3V3, EMMC_DATA0_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA1, EMMC_DATA1_DS_3V3, EMMC_DATA1_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA2, EMMC_DATA2_DS_3V3, EMMC_DATA2_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA3, EMMC_DATA3_DS_3V3, EMMC_DATA3_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA4, EMMC_DATA4_DS_3V3, EMMC_DATA4_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA5, EMMC_DATA5_DS_3V3, EMMC_DATA5_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA6, EMMC_DATA6_DS_3V3, EMMC_DATA6_DS_1V8), ++ EMMC_DRV_CAP(REG_CTRL_EMMC_DATA7, EMMC_DATA7_DS_3V3, EMMC_DATA7_DS_1V8), ++}; ++ ++static void himci_set_pin_drv_cap(struct himci_host *host, unsigned int flag, ++ unsigned char timing) ++{ ++ struct sdio_drv_cap *sdio_ds; ++ unsigned int i; ++ ++ if (host->devid == 2) { ++ for (i = 0; i < ARRAY_SIZE(emmc_ds); i++) ++ himci_writel(emmc_ds[i].ds[flag], IO_ADDRESS(emmc_ds[i].reg_addr)); ++ } else { ++ if (timing == MMC_TIMING_UHS_SDR104) ++ sdio_ds = sdio_ds_sdr104; ++ else if (timing == MMC_TIMING_UHS_SDR50) ++ sdio_ds = sdio_ds_sdr50; ++ else if (timing == MMC_TIMING_UHS_DDR50) ++ sdio_ds = sdio_ds_ddr50; ++ else ++ sdio_ds = sdio_ds_hs; ++ ++ for (i = 0; i < ARRAY_SIZE(sdio_ds_hs); i++) ++ himci_writel(sdio_ds[i].ds, ++ IO_ADDRESS(sdio_ds[i].reg_addr[host->devid])); ++ } ++} ++ ++static void himci_set_drv_cap(struct himci_host *host, struct mmc_ios *ios) ++{ ++ unsigned int tmp_reg; ++ ++ if (ios->timing == MMC_TIMING_UHS_DDR50) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~CLK_SMPL_PHS_MASK; ++ tmp_reg |= (0x4 << CLK_SMPL_PHS_SHIFT); ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } else if (ios->timing == MMC_TIMING_MMC_HS) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~CLK_SMPL_PHS_MASK; ++ tmp_reg |= SMPL_PHASE_DFLT; ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } else if (ios->timing == MMC_TIMING_MMC_HS400) { ++ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); ++ tmp_reg &= ~CLK_SMPL_PHS_MASK; ++ tmp_reg |= (host->phase << CLK_SMPL_PHS_SHIFT); ++ himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); ++ } ++ ++ /* set pin drive capability */ ++ if (ios->timing == MMC_TIMING_MMC_HS200 || ++ ios->timing == MMC_TIMING_MMC_HS400) ++ himci_set_pin_drv_cap(host, 1, ios->timing); ++ else ++ himci_set_pin_drv_cap(host, 0, ios->timing); ++} ++ ++static int himci_check_emmc(struct himci_host *host) ++{ ++ unsigned int val; ++ ++ val = himci_readl(IO_ADDRESS(SYS_CTRL_BASE + REG_SC_STAT)); ++ ++ return !(GET_SYS_BOOT_MODE(val) == BOOT_FROM_EMMC); ++} +diff --git a/drivers/mmc/host/himciv200/himci_proc.c b/drivers/mmc/host/himciv200/himci_proc.c +new file mode 100644 +index 0000000..280f24f +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci_proc.c +@@ -0,0 +1,290 @@ ++/***************************************************************************** ++ * This is the driver for the host mci SOC. ++ * ++ * Copyright (C) Hisilicon. All rights reserved. ++ * ++ ******************************************************************************/ ++ ++#include <linux/proc_fs.h> ++#include <linux/seq_file.h> ++#include <linux/device.h> ++#include <linux/io.h> ++#include <linux/platform_device.h> ++ ++#include <linux/mmc/card.h> ++ ++#include <linux/export.h> ++#include <linux/mmc/host.h> ++#include "himci.h" ++#include "himci_reg.h" ++#include "himci_proc.h" ++ ++#define MCI_PARENT "mci" ++#define MCI_STATS_PROC "mci_info" ++#define MAX_CLOCK_SCALE (4) ++#define UNSTUFF_BITS(resp,start,size) \ ++ ({ \ ++ const int __size = size; \ ++ const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \ ++ const int __off = 3 - ((start) / 32); \ ++ const int __shft = (start) & 31; \ ++ u32 __res; \ ++ \ ++ __res = resp[__off] >> __shft; \ ++ if (__size + __shft > 32) \ ++ __res |= resp[__off-1] << ((32 - __shft) % 32); \ ++ __res & __mask; \ ++ }) ++ ++static struct proc_dir_entry *proc_mci_dir; ++static unsigned int mci_max_connections; ++static char *card_type[MAX_CARD_TYPE + 1] = { ++ "MMC card", ++ "SD card", ++ "SDIO card", ++ "SD combo (IO+mem) card", ++ "unknown" ++}; ++static char *clock_unit[4] = { ++ "Hz", ++ "KHz", ++ "MHz", ++ "GHz" ++}; ++ ++static char *mci_get_card_type(unsigned int sd_type) ++{ ++ if (MAX_CARD_TYPE <= sd_type) ++ return card_type[MAX_CARD_TYPE]; ++ else ++ return card_type[sd_type]; ++} ++ ++static unsigned int analyze_clock_scale(unsigned int clock, ++ unsigned int *clock_val) ++{ ++ unsigned int scale = 0; ++ unsigned int tmp = clock; ++ ++ while (1) { ++ tmp = tmp / 1000; ++ if (0 < tmp) { ++ *clock_val = tmp; ++ scale++; ++ } else { ++ break; ++ } ++ } ++ return scale; ++} ++ ++static inline int is_card_uhs(unsigned char timing) ++{ ++ return timing >= MMC_TIMING_UHS_SDR12 && ++ timing <= MMC_TIMING_UHS_DDR50; ++} ++ ++static inline int is_card_hs(unsigned char timing) ++{ ++ return timing == MMC_TIMING_SD_HS || timing == MMC_TIMING_MMC_HS; ++} ++ ++static void mci_stats_seq_printout(struct seq_file *s) ++{ ++ unsigned int index_mci; ++ unsigned int clock; ++ unsigned int clock_scale; ++ unsigned int clock_value = 0; ++ const char *type; ++ struct himci_host *host; ++ struct card_info * c_info; ++ const char *uhs_bus_speed_mode = ""; ++ u32 speed_class, grade_speed_uhs; ++ static const char *const uhs_speeds[] = { ++ [UHS_SDR12_BUS_SPEED] = "SDR12 ", ++ [UHS_SDR25_BUS_SPEED] = "SDR25 ", ++ [UHS_SDR50_BUS_SPEED] = "SDR50 ", ++ [UHS_SDR104_BUS_SPEED] = "SDR104 ", ++ [UHS_DDR50_BUS_SPEED] = "DDR50 ", ++ }; ++ ++ ++ for (index_mci = 0; index_mci < HIMCI_SLOT_NUM; index_mci++) { ++ host = mci_host[index_mci]; ++ c_info = &host->c_info; ++ if (!host || !host->mmc) { ++ seq_printf(s, "MCI%d: invalid\n", index_mci); ++ continue; ++ } else { ++ seq_printf(s, "MCI%d", index_mci); ++ } ++ ++ if (CARD_PLUGED == host->card_status) { ++ seq_puts(s, ": pluged"); ++ } else { ++ seq_puts(s, ": unplugged"); ++ } ++ ++ if (CARD_CONNECT != c_info->card_connect) { ++ seq_puts(s, "_disconnected\n"); ++ } else { ++ seq_puts(s, "_connected\n"); ++ ++ seq_printf(s, ++ "\tType: %s", ++ mci_get_card_type(c_info->card_type) ++ ); ++ ++ if (c_info->card_state & MMC_STATE_BLOCKADDR) { ++ if (c_info->card_state & MMC_CARD_SDXC) ++ type = "SDXC"; ++ else ++ type = "SDHC"; ++ seq_printf(s, "(%s)\n", type); ++ } ++ ++ if (is_card_uhs(c_info->timing) && ++ c_info->sd_bus_speed < ARRAY_SIZE(uhs_speeds)) ++ uhs_bus_speed_mode = uhs_speeds[c_info->sd_bus_speed]; ++ ++ seq_printf(s, "\tMode: %s%s%s%s\n", ++ is_card_uhs(c_info->timing) ? "UHS " : ++ (is_card_hs(c_info->timing) ? "HS " : ""), ++ c_info->timing == MMC_TIMING_MMC_HS400 ? "HS400 " : ++ (c_info->timing == MMC_TIMING_MMC_HS200 ? "HS200 " : ""), ++ c_info->timing == MMC_TIMING_MMC_DDR52 ? "DDR " : "", ++ uhs_bus_speed_mode); ++ ++ speed_class = UNSTUFF_BITS(c_info->ssr, 440 - 384, 8); ++ grade_speed_uhs = UNSTUFF_BITS(c_info->ssr, 396 - 384, 4); ++ seq_printf(s, "\tSpeed Class: Class %s\n", ++ (0x00 == speed_class) ? "0": ++ (0x01 == speed_class) ? "2": ++ (0x02 == speed_class) ? "4": ++ (0x03 == speed_class) ? "6": ++ (0x04 == speed_class) ? "10": ++ "Reserved"); ++ seq_printf(s, "\tUhs Speed Grade: %s\n", ++ (0x00 == grade_speed_uhs)? ++ "Less than 10MB/sec(0h)" : ++ (0x01 == grade_speed_uhs)? ++ "10MB/sec and above(1h)": ++ "Reserved"); ++ ++ clock = host->hclk; ++ clock_scale = analyze_clock_scale(clock, &clock_value); ++ seq_printf(s, "\tHost work clock: %d%s\n", ++ clock_value, clock_unit[clock_scale]); ++ ++ clock = c_info->card_support_clock; ++ clock_scale = analyze_clock_scale(clock, &clock_value); ++ seq_printf(s, "\tCard support clock: %d%s\n", ++ clock_value, clock_unit[clock_scale]); ++ ++ clock = host->cclk; ++ clock_scale = analyze_clock_scale(clock, &clock_value); ++ seq_printf(s, "\tCard work clock: %d%s\n", ++ clock_value, clock_unit[clock_scale]); ++ /* add card read/write error count */ ++ seq_printf(s, "\tCard error count: %d\n", ++ host->error_count); ++ } ++ } ++} ++ ++/* proc interface setup */ ++static void *mci_seq_start(struct seq_file *s, loff_t *pos) ++{ ++ /* counter is used to tracking multi proc interfaces ++ * We have only one interface so return zero ++ * pointer to start the sequence. ++ */ ++ static unsigned long counter; ++ ++ if (*pos == 0) ++ return &counter; ++ ++ *pos = 0; ++ return NULL; ++} ++ ++/* proc interface next */ ++static void *mci_seq_next(struct seq_file *s, void *v, loff_t *pos) ++{ ++ (*pos)++; ++ if (*pos >= HIMCI_SLOT_NUM) ++ return NULL; ++ ++ return NULL; ++} ++ ++/* define parameters where showed in proc file */ ++static int mci_stats_seq_show(struct seq_file *s, void *v) ++{ ++ mci_stats_seq_printout(s); ++ return 0; ++} ++ ++/* proc interface stop */ ++static void mci_seq_stop(struct seq_file *s, void *v) ++{ ++} ++ ++/* proc interface operation */ ++static const struct seq_operations mci_stats_seq_ops = { ++ .start = mci_seq_start, ++ .next = mci_seq_next, ++ .stop = mci_seq_stop, ++ .show = mci_stats_seq_show ++}; ++ ++/* proc file open*/ ++static int mci_stats_proc_open(struct inode *inode, struct file *file) ++{ ++ return seq_open(file, &mci_stats_seq_ops); ++}; ++ ++/* proc file operation */ ++static const struct file_operations mci_stats_proc_ops = { ++ .owner = THIS_MODULE, ++ .open = mci_stats_proc_open, ++ .read = seq_read, ++ .release = seq_release ++}; ++ ++int mci_proc_init(unsigned int max_connections) ++{ ++ struct proc_dir_entry *proc_stats_entry; ++ ++ mci_max_connections = max_connections; ++ ++ proc_mci_dir = proc_mkdir(MCI_PARENT, NULL); ++ if (!proc_mci_dir) { ++ pr_err("%s: failed to create proc file %s\n", ++ __func__, MCI_PARENT); ++ return 1; ++ } ++ ++ proc_stats_entry = proc_create(MCI_STATS_PROC, ++ 0, proc_mci_dir, &mci_stats_proc_ops); ++ if (!proc_stats_entry) { ++ pr_err("%s: failed to create proc file %s\n", ++ __func__, MCI_STATS_PROC); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++int mci_proc_shutdown(void) ++{ ++ if (proc_mci_dir) { ++ if (mci_max_connections > 0) ++ remove_proc_entry(MCI_STATS_PROC, proc_mci_dir); ++ ++ remove_proc_entry(MCI_PARENT, NULL); ++ proc_mci_dir = NULL; ++ } ++ ++ return 0; ++} +diff --git a/drivers/mmc/host/himciv200/himci_proc.h b/drivers/mmc/host/himciv200/himci_proc.h +new file mode 100644 +index 0000000..989d6e4 +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci_proc.h +@@ -0,0 +1,25 @@ ++/* ++ * MCI connection table manager ++ */ ++#ifndef __MCI_PROC_H__ ++#define __MCI_PROC_H__ ++ ++#include <linux/proc_fs.h> ++ ++#define MAX_CARD_TYPE 4 ++#define MAX_SPEED_MODE 5 ++ ++#ifdef CONFIG_ARCH_HI3516CV300 ++ #define HIMCI_SLOT_NUM 4 ++#endif ++ ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101 || defined CONFIG_ARCH_HI3559 || defined CONFIG_ARCH_HI3556 || \ ++ defined CONFIG_ARCH_HI3516AV200) ++ #define HIMCI_SLOT_NUM 3 ++#endif ++ ++extern struct himci_host *mci_host[HIMCI_SLOT_NUM]; ++int mci_proc_init(unsigned int max_connections); ++int mci_proc_shutdown(void); ++ ++#endif /* __MCI_PROC_H__ */ +diff --git a/drivers/mmc/host/himciv200/himci_reg.h b/drivers/mmc/host/himciv200/himci_reg.h +new file mode 100644 +index 0000000..8ecb3ed +--- /dev/null ++++ b/drivers/mmc/host/himciv200/himci_reg.h +@@ -0,0 +1,252 @@ ++#ifndef _HI_MCI_REG_H_ ++#define _HI_MCI_REG_H_ ++ ++#define HI_MCI_IO_SIZE 0x1000 ++ ++#define MCI_CTRL 0x00 ++#define MCI_PWREN 0x04 ++#define MCI_CLKDIV 0x08 ++#define MCI_CLKSRC 0x0C ++#define MCI_CLKENA 0x10 ++#define MCI_TIMEOUT 0x14 ++#define MCI_CTYPE 0x18 ++#define MCI_BLKSIZ 0x1c ++#define MCI_BYTCNT 0x20 ++#define MCI_INTMASK 0x24 ++#define MCI_CMDARG 0x28 ++#define MCI_CMD 0x2C ++#define MCI_RESP0 0x30 ++#define MCI_RESP1 0x34 ++#define MCI_RESP2 0x38 ++#define MCI_RESP3 0x3C ++#define MCI_MINTSTS 0x40 ++#define MCI_RINTSTS 0x44 ++#define MCI_STATUS 0x48 ++#define MCI_FIFOTH 0x4C ++#define MCI_CDETECT 0x50 ++#define MCI_WRTPRT 0x54 ++#define MCI_GPIO 0x58 ++#define MCI_TCBCNT 0x5C ++#define MCI_TBBCNT 0x60 ++#define MCI_DEBNCE 0x64 ++#define MCI_USRID 0x68 ++#define MCI_VERID 0x6C ++#define MCI_HCON 0x70 ++#define MCI_UHS_REG 0x74 ++#define MCI_RESET_N 0x78 ++#define MCI_BMOD 0x80 ++#define MCI_DBADDR 0x88 ++#define MCI_IDSTS 0x8C ++#define MCI_IDINTEN 0x90 ++#define MCI_DSCADDR 0x94 ++#define MCI_BUFADDR 0x98 ++#define ADMA_CTRL 0xb0 ++#define ADMA_Q_ADDR 0xb4 ++#define ADMA_Q_DEEPTH 0xb8 ++#define ADMA_Q_RDPTR 0xbc ++#define ADMA_Q_WRPTR 0xc0 ++#define ADMA_Q_TO 0xc4 ++#define MCI_CARDTHRCTL 0x100 ++#define MCI_UHS_REG_EXT 0x108 ++#define MCI_EMMC_DDR_REG 0x10c ++#define MCI_TUNING_CTRL 0x118 ++ ++/* MCI_IDSTS(0x8c) detals */ ++#define CMD_LOCK_ERR (0x1<<29) ++#define OWNBIT_ERR (0x1<<28) ++#define QUEUE_OVERFLOW (0x1<<27) ++#define RESP_CHECK_ERR (0x1<<26) ++#define PACKET_INT (0x1<<25) ++#define PACKET_TO_INT (0x1<<24) ++#define AUTO_STOP_ERR (0x1<<23) ++#define QUEUE_FULL (0x1<<22) ++#define QUEUE_EMPTY (0x1<<21) ++#define ADMA3_FSM_SHIFT (17) ++#define FSM_SHIFT (13) ++#define CES (0x1<<5) ++#define DU (0x1<<4) ++#define FBE (0x1<<2) ++ ++#define ADMA_INT_ERR (CMD_LOCK_ERR | OWNBIT_ERR | QUEUE_OVERFLOW \ ++ | AUTO_STOP_ERR | PACKET_TO_INT | DU | FBE) ++ ++#define ADMA_INT_ALL (CMD_LOCK_ERR | OWNBIT_ERR | QUEUE_OVERFLOW \ ++ | RESP_CHECK_ERR | PACKET_INT \ ++ | PACKET_TO_INT | AUTO_STOP_ERR \ ++ | QUEUE_FULL | CES | DU | FBE) ++#define RESP_CHK_EN (0x1<<4) ++#define RDPTR_MOD_EN (0x1<<3) ++#define PACKET_INT_EN (0x1<<2) ++#define ADMA3_RESTART (0x1<<1) ++#define ADMA3_EN (0x1<<0) ++ ++/* GPIO config */ ++#define DTO_FIX_BYPASS (0x1<<23) ++#define CMD_OUT_EN_FIX_BYPASS (0x1<<8) ++ ++/* MCI_UHS_REG(0x74) details */ ++#define HI_EMMC_CTRL_VDD_180 (0x2<<0) ++#define HI_EMMC_CTRL_DDR_REG (0x2<<16) ++#define HI_SDXC_CTRL_VDD_180 (0x1<<0) ++#define HI_SDXC_CTRL_DDR_REG (0x1<<16) ++ ++/* MCI_BMOD(0x80) details */ ++#define BMOD_SWR (0x1<<0) ++#define BURST_INCR (0x1<<1) ++#define BMOD_DMA_EN (0x1<<7) ++#define BURST_8 (0x2<<8) ++#define BURST_16 (0x3<<8) ++ ++/* MCI_CTRL(0x00) details */ ++#define CTRL_RESET (1<<0) ++#define FIFO_RESET (1<<1) ++#define DMA_RESET (1<<2) ++#define INTR_EN (1<<4) ++#define USE_INTERNAL_DMA (1<<25) ++ ++/* IDMAC DEST1 details */ ++#define DMA_BUFFER (0x2000) ++#define MAX_DMA_DES (20480) ++ ++/* IDMAC DEST0 details */ ++#define DMA_DES_OWN (1<<31) ++#define DMA_DES_NEXT_DES (1<<4) ++#define DMA_DES_FIRST_DES (1<<3) ++#define DMA_DES_LAST_DES (1<<2) ++ ++/* MCI_CDETECT(0x50) details */ ++#define HIMCI_CARD0 (0x1<<0) ++#define HIMCI_CARD_MASK (0x3<<0) ++#define HIMCI_CARD_EMMC (0x1<<1) ++#define HIMCI_CARD_SD (0x1<<0) ++ ++/* MCI_TIMEOUT(0x14) details: */ ++/*bit 31-8: data read timeout param */ ++#define DATA_TIMEOUT (0xffffff<<8) ++/* bit 7-0: response timeout param */ ++#define RESPONSE_TIMEOUT 0xff ++ ++/* MCI_CLKENA(0x10) details */ ++#define CCLK_ENABLE (0x1<<0) /* bit 0: enable of card clk*/ ++ ++/* MCI_CTYPE(0x18) details */ ++#define EMMC_CARD_WIDTH_1 (0x2<<0) ++#define EMMC_CARD_WIDTH_0 (0x2<<16) ++#define CARD_WIDTH_1 (0x1<<0) ++#define CARD_WIDTH_0 (0x1<<16) ++ ++/* MCI_CARDTHRCTL(0x100) details */ ++#define RW_THRESHOLD_SIZE (0x2000005) ++ ++/* MCI_SRC(0x0C) details */ ++#define EMMC_CLK_SOURCE (0x1<<2) ++ ++/* MCI_EMMC_DDR_REG(0x10c) details */ ++#define HI_EMMC_HS400_MODE (0x1<<31) ++ ++/* MCI_RESET_N(0x78) details */ ++#define MMC_RST_N (0x1<<0) /* control reset*/ ++ ++/* MCI_INTMASK(0x24) details: ++ bit 16-1: mask MMC host controller each interrupt ++*/ ++#define ALL_INT_MASK 0x1ffff ++#define DTO_INT_MASK (0x1<<3) ++#define SDIO_INT_MASK (0x1<<16) ++ ++/* MCI_UHS_REG_EXT(0x108) details */ ++/* bit[19:16] sampling phase */ ++#define CLK_SMPL_PHS_SHIFT (16) ++#define CLK_SMPL_PHS_MASK (0xF<<16) ++#define CLK_SMPLA_PHS_SHIFT (9) ++#define CLK_SMPLA_PHS_MASK (0x7<<9) ++/* bit[26:23] drv phase */ ++#define CLK_DRV_PHS_SHIFT (23) ++#define CLK_DRV_PHS_MASK (0xF<<23) ++#define DEFAULT_SMPL_PHASE (0x5) ++ ++/* MCI_CMD(0x2c) details: ++ bit 31: cmd execute or load start param of interface clk bit ++*/ ++#define START_CMD (0x1<<31) ++ ++/* MCI_INTSTS(0x44) details */ ++/***************************************************************/ ++/* bit 16: sdio interrupt status */ ++#define SDIO_INT_STATUS (0x1<<16) ++ ++/* bit 15: end-bit error (read)/write no CRC interrupt status */ ++#define EBE_INT_STATUS (0x1<<15) ++ ++/* bit 14: auto command done interrupt status */ ++#define ACD_INT_STATUS (0x1<<14) ++ ++/* bit 13: start bit error interrupt status */ ++#define SBE_INT_STATUS (0x1<<13) ++ ++/* bit 12: hardware locked write error interrupt status */ ++#define HLE_INT_STATUS (0x1<<12) ++ ++/* bit 11: FIFO underrun/overrun error interrupt status */ ++#define FRUN_INT_STATUS (0x1<<11) ++ ++/* bit 10: data starvation-by-host timeout interrupt status */ ++#define HTO_INT_STATUS (0x1<<10) ++ ++/* bit 10: volt_switch to 1.8v for sdxc */ ++#define VOLT_SWITCH_INT_STATUS (0x1<<10) ++ ++/* bit 9: data read timeout interrupt status */ ++#define DRTO_INT_STATUS (0x1<<9) ++ ++/* bit 8: response timeout interrupt status */ ++#define RTO_INT_STATUS (0x1<<8) ++ ++/* bit 7: data CRC error interrupt status */ ++#define DCRC_INT_STATUS (0x1<<7) ++ ++/* bit 6: response CRC error interrupt status */ ++#define RCRC_INT_STATUS (0x1<<6) ++ ++/* bit 5: receive FIFO data request interrupt status */ ++#define RXDR_INT_STATUS (0x1<<5) ++ ++/* bit 4: transmit FIFO data request interrupt status */ ++#define TXDR_INT_STATUS (0x1<<4) ++ ++/* bit 3: data transfer Over interrupt status */ ++#define DTO_INT_STATUS (0x1<<3) ++ ++/* bit 2: command done interrupt status */ ++#define CD_INT_STATUS (0x1<<2) ++ ++/* bit 1: response error interrupt status */ ++#define RE_INT_STATUS (0x1<<1) ++ ++#define CMD_INT_MASK (RTO_INT_STATUS | RCRC_INT_STATUS | RE_INT_STATUS) ++#define DATA_INT_MASK (DCRC_INT_STATUS | SBE_INT_STATUS | EBE_INT_STATUS) ++/***************************************************************/ ++ ++/* MCI_RINTSTS(0x44) details:bit 16-1: clear ++ MMC host controller each interrupt but ++ hardware locked write error interrupt ++*/ ++#define ALL_INT_CLR 0x1efff ++#define ALL_ADMA_INT_CLR (0xffe<<21) ++ ++/* MCI_STATUS(0x48) details */ ++#define DATA_BUSY (0x1<<9) ++ ++/* MCI_FIFOTH(0x4c) details */ ++#define BURST_SIZE (0x6<<28) ++#define RX_WMARK (0x7f<<16) ++#define TX_WMARK (0x80) ++ ++/* MCI_TUNING_CTRL(0x118) details */ ++#define HW_TUNING_EN (0x1 << 0) ++#define EDGE_CTRL (0x1 << 1) ++#define FOUND_EDGE (0x1 << 5) ++ ++#define DEFAULT_CMD_VALUE 0x20000000 ++ ++#endif +diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig +index 94b8210..ff3808f 100644 +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -258,7 +258,7 @@ config INFTL + not use it. + + config RFD_FTL +- tristate "Resident Flash Disk (Flash Translation Layer) support" ++ tristate "Resident Flash Disk (Flash Translation Layer) support" + depends on BLOCK + select MTD_BLKDEVS + ---help--- +@@ -305,10 +305,59 @@ config MTD_SWAP + select MTD_BLKDEVS + help + Provides volatile block device driver on top of mtd partition +- suitable for swapping. The mapping of written blocks is not saved. ++ suitable for swapping. The mapping of written blocks is not saved. + The driver provides wear leveling by storing erase counter into the + OOB. + ++config HIFMC ++ bool "Enable HIFMC - Hisilicon Flash Memory Controller" ++ depends on MTD ++ default y if ARCH_HI3516CV300 ++ default y if ARCH_HI3519 ++ default y if ARCH_HI3519V101 ++ default y if ARCH_HI3516AV200 ++ default y if ARCH_HI3559 ++ default y if ARCH_HI3556 ++ default y if ARCH_HI3536C ++ default y if ARCH_HI3531D ++ default y if ARCH_HI3521D ++ help ++ Hisilicon Flash Memory Controller support SPI Nor SPI Nand and parallel ++ Nand Flash. often used on embedded chip. This option will provide the ++ generic support for FMC drivers to register ++ ++if HIFMC ++ ++config HIFMC_SPI_NAND ++ bool "Enable SPI nand flash Support on HIFMC" ++ depends on ARCH_HI3516CV300 || ARCH_HI3519 || ARCH_HI3519V101 || ARCH_HI3559 || ARCH_HI3556 || ARCH_HI3516AV200 || ARCH_HI3531D || ARCH_HI3521D || ARCH_HI3536C ++ select MTD_NAND ++ select HIFMC100_SPI_NAND ++ default y if ARCH_HI3516CV300 ++ default y if ARCH_HI3519 ++ default y if ARCH_HI3519V101 ++ default y if ARCH_HI3516AV200 ++ default y if ARCH_HI3559 ++ default y if ARCH_HI3556 ++ default y if ARCH_HI3536C ++ default y if ARCH_HI3531D ++ default y if ARCH_HI3521D ++ help ++ Hisilicon Flash Memory Controller is called hifmc for short. ++ This option enable hifmc support SPI nand flash. ++ If your enadle this option, HIFMC100_SPI_NAND should be select ++ ++config HIFMC_NAND ++ bool "Enable Nand flash Support on HIFMC" ++ select MTD_NAND ++ select HIFMC100_NAND ++ help ++ Hisilicon Flash Memory Controller is called hifmc for short. ++ This option enable hifmc support Nand flash. ++ If your choice this option, HIFMC100_NAND should be select ++ ++endif # End of HIFMC ++ + source "drivers/mtd/chips/Kconfig" + + source "drivers/mtd/maps/Kconfig" +diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile +index 99bb9a1..da61523 100644 +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -7,8 +7,8 @@ obj-$(CONFIG_MTD) += mtd.o + mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o + + obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o +-obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o +-obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o ++obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o ++obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o + obj-$(CONFIG_MTD_AFS_PARTS) += afs.o + obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o + obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o +@@ -30,7 +30,7 @@ obj-$(CONFIG_MTD_SWAP) += mtdswap.o + nftl-objs := nftlcore.o nftlmount.o + inftl-objs := inftlcore.o inftlmount.o + ++obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ + obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/ + +-obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ + obj-$(CONFIG_MTD_UBI) += ubi/ +diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile +index f0b0e61..da80a3d 100644 +--- a/drivers/mtd/devices/Makefile ++++ b/drivers/mtd/devices/Makefile +@@ -17,5 +17,4 @@ obj-$(CONFIG_MTD_SST25L) += sst25l.o + obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o + obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o + +- + CFLAGS_docg3.o += -I$(src) +diff --git a/drivers/mtd/devices/spi_ids.h b/drivers/mtd/devices/spi_ids.h +new file mode 100644 +index 0000000..f082969 +--- /dev/null ++++ b/drivers/mtd/devices/spi_ids.h +@@ -0,0 +1,185 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef SPI_IDSH ++#define SPI_IDSH ++ ++/*****************************************************************************/ ++ ++#define _1K (0x400) ++#define _2K (0x800) ++ ++#define _4K (0x1000) ++#define _8K (0x2000) ++#define _16K (0x4000) ++#define _32K (0x8000) ++ ++#define _64K (0x10000) ++#define _128K (0x20000) ++#define _256K (0x40000) ++#define _512K (0x80000) ++ ++#define _1M (0x100000) ++#define _2M (0x200000) ++#define _4M (0x400000) ++#define _8M (0x800000) ++ ++#define _16M (0x1000000) ++#define _32M (0x2000000) ++#define _64M (0x4000000) ++ ++#define INFINITE (0xFFFFFFFF) ++/*****************************************************************************/ ++ ++#define SPI_IF_READ_STD (0x01) ++#define SPI_IF_READ_FAST (0x02) ++#define SPI_IF_READ_DUAL (0x04) ++#define SPI_IF_READ_DUAL_ADDR (0x08) ++#define SPI_IF_READ_QUAD (0x10) ++#define SPI_IF_READ_QUAD_ADDR (0x20) ++ ++#define SPI_IF_WRITE_STD (0x01) ++#define SPI_IF_WRITE_DUAL (0x02) ++#define SPI_IF_WRITE_DUAL_ADDR (0x04) ++#define SPI_IF_WRITE_QUAD (0x08) ++#define SPI_IF_WRITE_QUAD_ADDR (0x10) ++ ++#define SPI_IF_ERASE_SECTOR (0x01) /* sector erase, 64K */ ++#define SPI_IF_ERASE_CHIP (0x02) /* chip erase */ ++#define SPI_IF_ERASE_4K (0x04) /* 4K */ ++#define SPI_IF_ERASE_8K (0x08) /* 8K */ ++ ++#define SPI_IF_ERASE_SECTOR_4K (0x01) /* 4K */ ++#define SPI_IF_ERASE_SECTOR_32K (0x02) /* 32K */ ++#define SPI_IF_ERASE_SECTOR_64K (0x04) /* 64K */ ++#define SPI_IF_ERASE_SECTOR_128K (0x08) /* 128K */ ++#define SPI_IF_ERASE_SECTOR_256K (0x10) /* 256K */ ++/*****************************************************************************/ ++#define SPI_CMD_BRWR (0x17) /*write value to BAR*/ ++#define SPI_EN4B_VALUE (0x80) /*the enable 4Byte addr len value*/ ++#define SPI_EX4B_VALUE (0x00) /*the disable 4Byte addr len value*/ ++#define SPI_4BYTE_ADDR_LEN (4) /*address len 4Byte*/ ++/*****************************************************************************/ ++ ++#define SPI_CMD_WREN 0x06 /* Write Enable */ ++#define SPI_CMD_WRDI 0x04 /* Write Disable */ ++/*****************************************************************************/ ++#define SPI_CMD_SE_4K 0x20 /* 4KB sector Erase */ ++#define SPI_CMD_SE_32K 0x52 /* 32KB sector Erase */ ++ ++#define SPI_CMD_SE_64K 0xD8 /* 64KB sector Erase */ ++#define SPI_CMD_SE_128K 0xD8 /* 128KB sector Erase */ ++#define SPI_CMD_SE_256K 0xD8 /* 256KB sector Erase */ ++ ++#define SPI_CMD_SE 0xD8 /* 64KB Sector Erase */ ++#define SPI_CMD_BE 0xC7 /* chip erase */ ++/*****************************************************************************/ ++#define SPI_CMD_WRSR 0x01 /* Write Status Register */ ++#define SPI_CMD_WRSR2 0x31 /* Write Status Register-2 */ ++#define SPI_CMD_WRSR3 0x11 /* Write Status Register-3 */ ++ ++#define SPI_CMD_RDSR 0x05 /* Read Status Register */ ++#define SPI_CMD_GET_FEATURES 0x0F /* Get Features */ ++#define SPI_CMD_SET_FEATURES 0x1F /* Set Features */ ++#define SPI_CMD_RDSR2 0x35 /* Read Status Register-2 */ ++#define SPI_CMD_RDSR3 0x15 /* Read Status Register-3 */ ++ ++#define SPI_CMD_RDCR 0x35 /* Read Config Register */ ++ ++#define SPI_CMD_RDID 0x9F /* Read Identification */ ++#define SPI_CMD_RESET 0xff /* Reset the device */ ++/*****************************************************************************/ ++#define SPI_CMD_PP 0x02 /* Page Programming */ ++#define SPI_CMD_WRITE_DUAL 0xA2 /* fast program dual input */ ++#define SPI_CMD_WRITE_QUAD 0x32 /* fast program quad input */ ++#define SPI_CMD_WRITE_DUAL_ADDR 0xD2 /* Dual I/O High Performance Write */ ++#define SPI_CMD_WRITE_QUAD_ADDR 0x12 /* Quad I/O High Performance Write */ ++/*****************************************************************************/ ++#define SPI_CMD_PAGE_READ 0x13 /* Page Read to Cache */ ++#define SPI_CMD_READ 0x03 /* Read Data bytes */ ++#define SPI_CMD_FAST_READ 0x0B /* Read Data Bytes at Higher Speed */ ++#define SPI_CMD_READ_DUAL 0x3B /* fast read dual output */ ++#define SPI_CMD_READ_QUAD 0x6B /* fast read quad output */ ++#define SPI_CMD_READ_DUAL_ADDR 0xBB /* Dual I/O High Performance Read */ ++#define SPI_CMD_READ_QUAD_ADDR 0xEB /* Quad I/O High Performance Read */ ++/*****************************************************************************/ ++#define SPI_CMD_SR_WIP 1 /* Write in Progress */ ++#define SPI_CMD_SR_WEL 2 /* Write Enable Latch */ ++#define SPI_CMD_SR_QE (1 << 9) /* quad enable */ ++#define SPI_CMD_SR_XQE (0 << 9) /* quad disable */ ++/*****************************************************************************/ ++#define SPI_CMD_EN4B 0xB7 /* enter 4 bytes mode and set 4 byte bit as '1' */ ++#define SPI_CMD_EX4B 0xE9 /* exit 4 bytes mode and clear 4 byte bit */ ++/*****************************************************************************/ ++ ++/*****************************************************************************/ ++#ifndef DBG_BUG ++#define DBG_BUG(fmt, args...) \ ++ do { \ ++ pr_err("%s(%d): BUG !!! " fmt, __FILE__, \ ++ __LINE__, ##args); \ ++ while (1) \ ++ ; \ ++ } while (0) ++#endif ++ ++/*****************************************************************************/ ++struct spi_operation { ++ unsigned char iftype; ++ unsigned char cmd; ++ unsigned char dummy; ++ unsigned int size; ++ unsigned int clock; ++}; ++ ++struct spi_info { ++ char *name; ++ ++ unsigned char id[8]; ++ unsigned int id_len; ++ ++ unsigned long chipsize; ++ unsigned int erasesize; ++ unsigned int addrcycle; ++ ++#define MAX_SPI_OP (8) ++ struct spi_operation *read[8]; ++ struct spi_operation *write[8]; ++ struct spi_operation *erase[8]; ++#ifndef CONFIG_MTD_HISFC300 ++ struct spi_driver *driver; ++#endif ++}; ++/*****************************************************************************/ ++ ++struct spi_info *spi_serach_ids(unsigned char ids[8]); ++ ++void spi_search_rw(struct spi_info *spiinfo, struct spi_operation *spiop_rw, ++ unsigned int iftype, unsigned int max_dummy, int is_read); ++ ++#ifndef CONFIG_MTD_HISFC300 ++void spi_get_erase(struct spi_info *spiinfo, struct spi_operation *spiop_erase); ++#else ++void spi_get_erase(struct spi_info *spiinfo, struct spi_operation *spiop_erase, ++ unsigned int *erasesize); ++#endif ++/******************************************************************************/ ++ ++extern struct spi_info spi_info_table[]; ++/******************************************************************************/ ++#endif /* SPI_IDSH */ +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index dd10646..be09cf3 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -1,3 +1,10 @@ ++config MTD_NAND_IDS ++ tristate "Include chip ids for known NAND devices." ++ depends on MTD ++ help ++ Useful for NAND drivers that do not use the NAND subsystem but ++ still like to take advantage of the known chip information. ++ + config MTD_NAND_ECC + tristate + +@@ -109,9 +116,6 @@ config MTD_NAND_OMAP_BCH + config MTD_NAND_OMAP_BCH_BUILD + def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH + +-config MTD_NAND_IDS +- tristate +- + config MTD_NAND_RICOH + tristate "Ricoh xD card reader" + default n +@@ -516,4 +520,21 @@ config MTD_NAND_XWAY + Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached + to the External Bus Unit (EBU). + ++config HISI_NAND_FS_MAY_NO_YAFFS2 ++ bool "Remove the restraintion of 16bit ecc type on yaffs2 to HiSilicon" ++ default n ++ help ++ The ecc type: 16bit is limited by the HiSilicon flash memory controller, ++ as the yaffs2 tag of hisi rootfs limits the min size of CTRL len is 28. ++ ++config HISI_NAND_ECC_STATUS_REPORT ++ bool "Report the ecc status to MTD for HiSilicon Nand Driver" ++ default n ++ help ++ Flash Memory Controller V100 reports the ecc status include ECC error ++ and ECC corrected to MTD to monitor the aging of devices. ++ ++source "drivers/mtd/nand/hinfc610/Kconfig" ++source "drivers/mtd/nand/hifmc100_nand/Kconfig" ++source "drivers/mtd/nand/hifmc100/Kconfig" + endif # MTD_NAND +diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile +index 9c847e4..63877fd 100644 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -8,6 +8,9 @@ obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o + obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o + obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o + ++obj-$(CONFIG_MTD_NAND_HINFC610) += hinfc610/ ++obj-$(CONFIG_HIFMC_NAND) += hifmc100_nand/ ++obj-$(CONFIG_HIFMC_SPI_NAND) += hifmc100/ + obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o + obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o + obj-$(CONFIG_MTD_NAND_DENALI) += denali.o +@@ -51,4 +54,4 @@ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ + obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o + obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ + +-nand-objs := nand_base.o nand_bbt.o nand_timings.o ++nand-objs := nand_base.o nand_bbt.o nand_timings.o hinfc_gen.o hinfc_spl_ids.o match_table.o +diff --git a/drivers/mtd/nand/hifmc100/Kconfig b/drivers/mtd/nand/hifmc100/Kconfig +new file mode 100644 +index 0000000..92a7c77 +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100/Kconfig +@@ -0,0 +1,79 @@ ++# ++# hisilicon flash memory controller SPI nand device driver version 100 ++# drivers/mtd/nand/hifmc100/Kconfig ++# add by hisilicon 2015.5.7 ++# ++ ++menuconfig HIFMC100_SPI_NAND ++ tristate "Hisilicon Flash Memory Controller v100 SPI Nand device Support" ++ depends on HIFMC ++ depends on MTD_NAND ++ depends on HIFMC_SPI_NAND ++ default n if ARCH_HI3516CV300 ++ default n if ARCH_HI3519 ++ default n if ARCH_HI3519V101 ++ default n if ARCH_HI3559 ++ default n if ARCH_HI3556 ++ default n if ARCH_HI3536C ++ default n if ARCH_HI3531D ++ default n if ARCH_HI3521D ++ select MISC_FILESYSTEMS ++ select MTD_BLOCK ++ help ++ Hisilicon Flash Memory Controller device version 100 driver Support ++ Hisilicon Flash Memory Controller version 100 is called hifmc100 for ++ short. The controller driver support registers and DMA transfers ++ while reading or writing the SPI nand flash. ++ ++if HIFMC100_SPI_NAND ++ ++config SPI_NAND_MAX_CHIP_NUM ++ int "Support max number of SPI Nand flash chip (1, 2)" ++ default 1 if ARCH_HI3516CV300 ++ default 1 if ARCH_HI3519 ++ default 1 if ARCH_HI3519V101 ++ default 1 if ARCH_HI3559 ++ default 1 if ARCH_HI3556 ++ default 1 if ARCH_HI3536C ++ default 1 if ARCH_HI3531D ++ default 1 if ARCH_HI3521D ++ help ++ flash memory controller v100 device only support 1 or 2 SPI nand flash ++ chip, your should not config other value. ++ ++choice ++ prompt "Page Size and Ecc Type Select" ++ default HIFMC100_AUTO_PAGESIZE_ECC if ARCH_HI3516CV300 ++ default HIFMC100_AUTO_PAGESIZE_ECC if ARCH_HI3519 ++ default HIFMC100_AUTO_PAGESIZE_ECC if ARCH_HI3519V101 ++ default HIFMC100_AUTO_PAGESIZE_ECC if ARCH_HI3559 ++ default HIFMC100_AUTO_PAGESIZE_ECC if ARCH_HI3556 ++ default HIFMC100_AUTO_PAGESIZE_ECC if ARCH_HI3536C ++ default HIFMC100_AUTO_PAGESIZE_ECC if ARCH_HI3531D ++ default HIFMC100_AUTO_PAGESIZE_ECC if ARCH_HI3521D ++ ++config HIFMC100_HARDWARE_PAGESIZE_ECC ++ bool "Hardware" ++ help ++ the configure of page size and ecc type lie on switch on the board. ++ so the page size and ecc type is controlled by Hardware see demo ++ board of SOC. ++ ++config HIFMC100_AUTO_PAGESIZE_ECC ++ bool "Auto" ++ help ++ auto-sensed the page size and ecc type value. driver will try each of ++ page size and ecc type one by one till flash can be read and wrote ++ accurately. so the page size and ecc type is match adaptively without ++ switch on the board ++ ++config HIFMC100_PAGESIZE_AUTO_ECC_NONE ++ bool "Pagesize Auto, Ecc None" ++ help ++ auto-sensed the page size and select ecc none. driver will try each ++ of page size one by one till flash can be read and wrote accurately. ++ so the page size is match adaptively without switch on the board ++ ++endchoice ++ ++endif # End of HIFMC100_SPI_NAND +diff --git a/drivers/mtd/nand/hifmc100/Makefile b/drivers/mtd/nand/hifmc100/Makefile +new file mode 100644 +index 0000000..72f7661 +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100/Makefile +@@ -0,0 +1,26 @@ ++# ++# The Flash Memory Controller v100 Device Driver for hisilicon ++# ++# Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++# ++# This program is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by the ++# Free Software Foundation; either version 2 of the License, or (at your ++# option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see <http://www.gnu.org/licenses/>. ++# ++# ++ ++# ++# drivers/mtd/nand/hifmc100/Makefile ++# ++ ++obj-$(CONFIG_HIFMC_SPI_NAND) += hifmc_spi_nand_ids.o ++obj-$(CONFIG_HIFMC100_SPI_NAND) += hifmc100.o hifmc100_os.o +diff --git a/drivers/mtd/nand/hifmc100/hifmc100.c b/drivers/mtd/nand/hifmc100/hifmc100.c +new file mode 100644 +index 0000000..60bb364 +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100/hifmc100.c +@@ -0,0 +1,1161 @@ ++/* ++ * The Flash Memory Controller v100 Device Driver for hisilicon ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/clk.h> ++#include <linux/io.h> ++#include <linux/errno.h> ++#include <linux/slab.h> ++#include <linux/mtd/nand.h> ++#include <linux/delay.h> ++#include <linux/sched.h> ++#include <asm/setup.h> ++ ++#include "../hinfc_gen.h" ++#include "hifmc100_os.h" ++#include "hifmc100.h" ++#include <linux/mfd/hisi_fmc.h> ++ ++/*****************************************************************************/ ++static void hifmc100_switch_to_spi_nand(struct hifmc_host *host) ++{ ++ int reg; ++ ++ reg = hifmc_readl(host, FMC_CFG); ++ reg &= ~FLASH_TYPE_SEL_MASK; ++ reg |= FMC_CFG_FLASH_SEL(FLASH_TYPE_SPI_NAND); ++ hifmc_writel(host, FMC_CFG, reg); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_operation_config(struct hifmc_host *host, int op) ++{ ++ int ret, clkrate = 0; ++ struct hifmc_spi *spi = host->spi; ++ ++ hifmc100_switch_to_spi_nand(host); ++ clk_prepare_enable(host->clk); ++ switch (op) { ++ case OP_STYPE_WRITE: ++ clkrate = min((u_long)host->clkrate, ++ (u_long)CLK_FMC_TO_CRG_MHZ(spi->write->clock)); ++ break; ++ case OP_STYPE_READ: ++ clkrate = min((u_long)host->clkrate, ++ (u_long)CLK_FMC_TO_CRG_MHZ(spi->read->clock)); ++ break; ++ case OP_STYPE_ERASE: ++ clkrate = min((u_long)host->clkrate, ++ (u_long)CLK_FMC_TO_CRG_MHZ(spi->erase->clock)); ++ break; ++ default: ++ break; ++ } ++ ++ ret = clk_set_rate(host->clk, clkrate); ++ BUG_ON(ret); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_send_cmd_write(struct hifmc_host *host) ++{ ++ unsigned char pages_per_block_shift; ++ unsigned int reg, block_num, block_num_h, page_num; ++ struct hifmc_spi *spi = host->spi; ++ struct nand_chip *chip = host->chip; ++#ifdef HIFMC100_SPI_NAND_SUPPORT_REG_WRITE ++ const char *op = "Reg"; ++#else ++ const char *op = "Dma"; ++#endif ++ ++ if (WR_DBG) ++ pr_info("\n"); ++ FMC_PR(WR_DBG, "*-Start send %s page write command\n", op); ++ ++ mutex_lock(host->lock); ++ hifmc100_operation_config(host, OP_STYPE_WRITE); ++ ++ reg = spi->driver->wait_ready(spi); ++ if (reg) { ++ DB_MSG("Error: %s program wait ready failed! status: %#x\n", ++ op, reg); ++ goto end; ++ } ++ ++ reg = spi->driver->write_enable(spi); ++ if (reg) { ++ DB_MSG("Error: %s program write enable failed! reg: %#x\n", ++ op, reg); ++ goto end; ++ } ++ ++ reg = FMC_INT_CLR_ALL; ++ hifmc_writel(host, FMC_INT_CLR, reg); ++ FMC_PR(WR_DBG, "|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg); ++ ++ reg = OP_CFG_FM_CS(host->cmd_op.cs) ++ | OP_CFG_MEM_IF_TYPE(spi->write->iftype); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ FMC_PR(WR_DBG, "|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ pages_per_block_shift = chip->phys_erase_shift - chip->page_shift; ++ block_num = host->addr_value[1] >> pages_per_block_shift; ++ block_num_h = block_num >> REG_CNT_HIGH_BLOCK_NUM_SHIFT; ++ reg = FMC_ADDRH_SET(block_num_h); ++ hifmc_writel(host, FMC_ADDRH, reg); ++ FMC_PR(WR_DBG, "|-Set ADDRH[%#x]%#x\n", FMC_ADDRH, reg); ++ ++ page_num = host->addr_value[1] - (block_num << pages_per_block_shift); ++ reg = ((block_num & REG_CNT_BLOCK_NUM_MASK) << REG_CNT_BLOCK_NUM_SHIFT) ++ | ((page_num & REG_CNT_PAGE_NUM_MASK) << REG_CNT_PAGE_NUM_SHIFT); ++ hifmc_writel(host, FMC_ADDRL, reg); ++ FMC_PR(WR_DBG, "|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ *host->epm = 0x0000; ++ ++#ifndef HIFMC100_SPI_NAND_SUPPORT_REG_WRITE ++ reg = host->dma_buffer; ++ hifmc_writel(host, FMC_DMA_SADDR_D0, reg); ++ FMC_PR(WR_DBG, "|-Set DMA_SADDR_D[0x40]%#x\n", reg); ++ ++ reg = host->dma_oob; ++ hifmc_writel(host, FMC_DMA_SADDR_OOB, reg); ++ FMC_PR(WR_DBG, "|-Set DMA_SADDR_OOB[%#x]%#x\n", FMC_DMA_SADDR_OOB, reg); ++#endif ++ ++ reg = OP_CTRL_WR_OPCODE(spi->write->cmd) ++#ifdef HIFMC100_SPI_NAND_SUPPORT_REG_WRITE ++ | OP_CTRL_DMA_OP(OP_TYPE_REG) ++#else ++ | OP_CTRL_DMA_OP(OP_TYPE_DMA) ++#endif ++ | OP_CTRL_RW_OP(RW_OP_WRITE) ++ | OP_CTRL_DMA_OP_READY; ++ hifmc_writel(host, FMC_OP_CTRL, reg); ++ FMC_PR(WR_DBG, "|-Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, reg); ++ ++ FMC_DMA_WAIT_INT_FINISH(host); ++ ++end: ++ mutex_unlock(host->lock); ++ FMC_PR(WR_DBG, "*-End %s page program!\n", op); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_send_cmd_status(struct hifmc_host *host) ++{ ++ unsigned char status, addr = STATUS_ADDR; ++ struct hifmc_spi *spi = host->spi; ++ ++ if (host->cmd_op.l_cmd == NAND_CMD_GET_FEATURES) ++ addr = PROTECT_ADDR; ++ ++ status = spi_nand_feature_op(spi, GET_OP, addr, 0); ++ FMC_PR((ER_DBG || WR_DBG), "\t*-Get status[%#x]: %#x\n", addr, status); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_send_cmd_read(struct hifmc_host *host) ++{ ++ unsigned char pages_per_block_shift, only_oob = 0; ++ unsigned short wrap = 0; ++ unsigned int reg, block_num, block_num_h, page_num, addr_of = 0; ++ struct hifmc_spi *spi = host->spi; ++ struct nand_chip *chip = host->chip; ++#ifdef HIFMC100_SPI_NAND_SUPPORT_REG_READ ++ char *op = "Reg"; ++#else ++ char *op = "Dma"; ++#endif ++ ++ if (RD_DBG) ++ pr_info("\n"); ++ FMC_PR(RD_DBG, "\t*-Start %s page read\n", op); ++ ++ if ((host->addr_value[0] == host->cache_addr_value[0]) ++ && (host->addr_value[1] == host->cache_addr_value[1])) { ++ FMC_PR(RD_DBG, "\t*-%s read cache hit, addr[%#x %#x]\n", ++ op, host->addr_value[1], host->addr_value[0]); ++ return; ++ } ++ ++ mutex_lock(host->lock); ++ hifmc100_operation_config(host, OP_STYPE_READ); ++ ++ FMC_PR(RD_DBG, "\t|-Wait ready before %s page read\n", op); ++ reg = spi->driver->wait_ready(spi); ++ if (reg) { ++ DB_MSG("Error: %s read wait ready fail! reg: %#x\n", op, reg); ++ goto end; ++ } ++ ++ reg = FMC_INT_CLR_ALL; ++ hifmc_writel(host, FMC_INT_CLR, reg); ++ FMC_PR(RD_DBG, "\t|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg); ++ ++ if (host->cmd_op.l_cmd == NAND_CMD_READOOB) { ++ only_oob = 1; ++ host->cmd_op.op_cfg = OP_CTRL_RD_OP_SEL(RD_OP_READ_OOB); ++ } else ++ host->cmd_op.op_cfg = OP_CTRL_RD_OP_SEL(RD_OP_READ_ALL_PAGE); ++ ++ reg = OP_CFG_FM_CS(host->cmd_op.cs) ++ | OP_CFG_MEM_IF_TYPE(spi->read->iftype) ++ | OP_CFG_DUMMY_NUM(spi->read->dummy); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ FMC_PR(RD_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ pages_per_block_shift = chip->phys_erase_shift - chip->page_shift; ++ block_num = host->addr_value[1] >> pages_per_block_shift; ++ block_num_h = block_num >> REG_CNT_HIGH_BLOCK_NUM_SHIFT; ++ ++ reg = FMC_ADDRH_SET(block_num_h); ++ hifmc_writel(host, FMC_ADDRH, reg); ++ FMC_PR(RD_DBG, "\t|-Set ADDRH[%#x]%#x\n", FMC_ADDRH, reg); ++ ++ page_num = host->addr_value[1] - (block_num << pages_per_block_shift); ++ if (only_oob) ++ switch (host->ecctype) { ++ case NAND_ECC_8BIT: ++ addr_of = REG_CNT_ECC_8BIT_OFFSET; ++ break; ++ case NAND_ECC_16BIT: ++ addr_of = REG_CNT_ECC_16BIT_OFFSET; ++ break; ++ case NAND_ECC_24BIT: ++ addr_of = REG_CNT_ECC_24BIT_OFFSET; ++ break; ++ case NAND_ECC_0BIT: ++ default: ++ break; ++ } ++ ++ reg = ((block_num & REG_CNT_BLOCK_NUM_MASK) << REG_CNT_BLOCK_NUM_SHIFT) ++ | ((page_num & REG_CNT_PAGE_NUM_MASK) << REG_CNT_PAGE_NUM_SHIFT) ++ | ((wrap & REG_CNT_WRAP_MASK) << REG_CNT_WRAP_SHIFT) ++ | (addr_of & REG_CNT_ECC_OFFSET_MASK); ++ hifmc_writel(host, FMC_ADDRL, reg); ++ FMC_PR(RD_DBG, "\t|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++#ifndef HIFMC100_SPI_NAND_SUPPORT_REG_READ ++ reg = host->dma_buffer; ++ hifmc_writel(host, FMC_DMA_SADDR_D0, reg); ++ FMC_PR(RD_DBG, "\t|-Set DMA_SADDR_D0[%#x]%#x\n", FMC_DMA_SADDR_D0, reg); ++ ++ reg = host->dma_oob; ++ hifmc_writel(host, FMC_DMA_SADDR_OOB, reg); ++ FMC_PR(RD_DBG, "\t|-Set DMA_SADDR_OOB[%#x]%#x\n", FMC_DMA_SADDR_OOB, ++ reg); ++#endif ++ ++ reg = OP_CTRL_RD_OPCODE(spi->read->cmd) | host->cmd_op.op_cfg ++#ifdef HIFMC100_SPI_NAND_SUPPORT_REG_READ ++ | OP_CTRL_DMA_OP(OP_TYPE_REG) ++#else ++ | OP_CTRL_DMA_OP(OP_TYPE_DMA) ++#endif ++ | OP_CTRL_RW_OP(RW_OP_READ) | OP_CTRL_DMA_OP_READY; ++ hifmc_writel(host, FMC_OP_CTRL, reg); ++ FMC_PR(RD_DBG, "\t|-Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, reg); ++ ++ FMC_DMA_WAIT_INT_FINISH(host); ++ ++ host->cache_addr_value[0] = host->addr_value[0]; ++ host->cache_addr_value[1] = host->addr_value[1]; ++ ++end: ++ mutex_unlock(host->lock); ++ FMC_PR(RD_DBG, "\t*-End %s page read\n", op); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_send_cmd_erase(struct hifmc_host *host) ++{ ++ unsigned int reg; ++ struct hifmc_spi *spi = host->spi; ++ ++ if (ER_DBG) ++ pr_info("\n"); ++ FMC_PR(ER_DBG, "\t*-Start send cmd erase!\n"); ++ ++ mutex_lock(host->lock); ++ hifmc100_operation_config(host, OP_STYPE_ERASE); ++ ++ reg = spi->driver->wait_ready(spi); ++ FMC_PR(ER_DBG, "\t|-Erase wait ready, reg: %#x\n", reg); ++ if (reg) { ++ DB_MSG("Error: Erase wait ready fail! status: %#x\n", reg); ++ goto end; ++ } ++ ++ reg = spi->driver->write_enable(spi); ++ if (reg) { ++ DB_MSG("Error: Erase write enable failed! reg: %#x\n", reg); ++ goto end; ++ } ++ ++ reg = FMC_INT_CLR_ALL; ++ hifmc_writel(host, FMC_INT_CLR, reg); ++ FMC_PR(ER_DBG, "\t|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg); ++ ++ reg = spi->erase->cmd; ++ hifmc_writel(host, FMC_CMD, FMC_CMD_CMD1(reg)); ++ FMC_PR(ER_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = FMC_ADDRL_BLOCK_H_MASK(host->addr_value[1]) ++ | FMC_ADDRL_BLOCK_L_MASK(host->addr_value[0]); ++ hifmc_writel(host, FMC_ADDRL, reg); ++ FMC_PR(ER_DBG, "\t|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ reg = OP_CFG_FM_CS(host->cmd_op.cs) ++ | OP_CFG_MEM_IF_TYPE(spi->erase->iftype) ++ | OP_CFG_ADDR_NUM(STD_OP_ADDR_NUM) ++ | OP_CFG_DUMMY_NUM(spi->erase->dummy); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ FMC_PR(ER_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = FMC_OP_CMD1_EN ++ | FMC_OP_ADDR_EN ++ | FMC_OP_REG_OP_START; ++ hifmc_writel(host, FMC_OP, reg); ++ FMC_PR(ER_DBG, "\t|-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ FMC_CMD_WAIT_CPU_FINISH(host); ++ ++end: ++ mutex_unlock(host->lock); ++ FMC_PR(ER_DBG, "\t*-End send cmd erase!\n"); ++} ++ ++/*****************************************************************************/ ++void hifmc100_ecc0_switch(struct hifmc_host *host, unsigned char op) ++{ ++ unsigned int config; ++#if EC_DBG ++ unsigned int cmp_cfg; ++ ++ config = hifmc_readl(host, FMC_CFG); ++ FMC_PR(EC_DBG, "\t *-Get CFG[%#x]%#x\n", FMC_CFG, config); ++ ++ if (op) ++ cmp_cfg = host->fmc_cfg; ++ else ++ cmp_cfg = host->fmc_cfg_ecc0; ++ ++ if (cmp_cfg != config) ++ DB_MSG("Warning: FMC config[%#x] is different.\n", ++ cmp_cfg); ++#endif ++ ++ if (op == ENABLE) ++ config = host->fmc_cfg_ecc0; ++ else if (op == DISABLE) ++ config = host->fmc_cfg; ++ else { ++ DB_MSG("Error: Invalid opcode: %d\n", op); ++ return; ++ } ++ ++ hifmc_writel(host, FMC_CFG, config); ++ FMC_PR(EC_DBG, "\t *-Set CFG[%#x]%#x\n", FMC_CFG, config); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_send_cmd_readid(struct hifmc_host *host) ++{ ++ unsigned int reg; ++ ++ FMC_PR(BT_DBG, "\t|*-Start send cmd read ID\n"); ++ ++ hifmc100_ecc0_switch(host, ENABLE); ++ ++ reg = FMC_CMD_CMD1(SPI_CMD_RDID); ++ hifmc_writel(host, FMC_CMD, reg); ++ FMC_PR(BT_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = READ_ID_ADDR; ++ hifmc_writel(host, FMC_ADDRL, reg); ++ FMC_PR(BT_DBG, "\t||-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ reg = OP_CFG_FM_CS(host->cmd_op.cs) ++ | OP_CFG_ADDR_NUM(READ_ID_ADDR_NUM); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ FMC_PR(BT_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = FMC_DATA_NUM_CNT(MAX_SPI_NAND_ID_LEN); ++ hifmc_writel(host, FMC_DATA_NUM, reg); ++ FMC_PR(BT_DBG, "\t||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg); ++ ++ reg = FMC_OP_CMD1_EN ++ | FMC_OP_ADDR_EN ++ | FMC_OP_READ_DATA_EN ++ | FMC_OP_REG_OP_START; ++ hifmc_writel(host, FMC_OP, reg); ++ FMC_PR(BT_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ host->addr_cycle = 0x0; ++ ++ FMC_CMD_WAIT_CPU_FINISH(host); ++ ++ hifmc100_ecc0_switch(host, DISABLE); ++ ++ FMC_PR(BT_DBG, "\t|*-End read flash ID\n"); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_send_cmd_reset(struct hifmc_host *host) ++{ ++ unsigned int reg; ++ ++ FMC_PR(BT_DBG, "\t|*-Start send cmd reset\n"); ++ ++ reg = FMC_CMD_CMD1(SPI_CMD_RESET); ++ hifmc_writel(host, FMC_CMD, reg); ++ FMC_PR(BT_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = OP_CFG_FM_CS(host->cmd_op.cs); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ FMC_PR(BT_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = FMC_OP_CMD1_EN | FMC_OP_REG_OP_START; ++ hifmc_writel(host, FMC_OP, reg); ++ FMC_PR(BT_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ FMC_CMD_WAIT_CPU_FINISH(host); ++ ++ FMC_PR(BT_DBG, "\t|*-End send cmd reset\n"); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_host_init(struct hifmc_host *host) ++{ ++ unsigned int reg; ++ ++ FMC_PR(BT_DBG, "\t||*-Start SPI Nand host init\n"); ++ ++ reg = hifmc_readl(host, FMC_CFG); ++ if ((reg & FMC_CFG_OP_MODE_MASK) == FMC_CFG_OP_MODE_BOOT) { ++ reg |= FMC_CFG_OP_MODE(FMC_CFG_OP_MODE_NORMAL); ++ hifmc_writel(host, FMC_CFG, reg); ++ FMC_PR(BT_DBG, "\t|||-Set CFG[%#x]%#x\n", FMC_CFG, reg); ++ } ++ ++ host->fmc_cfg = reg; ++ host->fmc_cfg_ecc0 = (reg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT; ++ ++ reg = hifmc_readl(host, FMC_GLOBAL_CFG); ++ if (reg & FMC_GLOBAL_CFG_WP_ENABLE) { ++ reg &= ~FMC_GLOBAL_CFG_WP_ENABLE; ++ hifmc_writel(host, FMC_GLOBAL_CFG, reg); ++ } ++ ++ host->addr_cycle = 0; ++ host->addr_value[0] = 0; ++ host->addr_value[1] = 0; ++ host->cache_addr_value[0] = ~0; ++ host->cache_addr_value[1] = ~0; ++ ++ host->send_cmd_write = hifmc100_send_cmd_write; ++ host->send_cmd_status = hifmc100_send_cmd_status; ++ host->send_cmd_read = hifmc100_send_cmd_read; ++ host->send_cmd_erase = hifmc100_send_cmd_erase; ++ host->send_cmd_readid = hifmc100_send_cmd_readid; ++ host->send_cmd_reset = hifmc100_send_cmd_reset; ++#ifdef CONFIG_PM ++ host->suspend = hifmc100_suspend; ++ host->resume = hifmc100_resume; ++#endif ++ ++ reg = TIMING_CFG_TCSH(CS_HOLD_TIME) ++ | TIMING_CFG_TCSS(CS_SETUP_TIME) ++ | TIMING_CFG_TSHSL(CS_DESELECT_TIME); ++ hifmc_writel(host, FMC_SPI_TIMING_CFG, reg); ++ ++ reg = ALL_BURST_ENABLE; ++ hifmc_writel(host, FMC_DMA_AHB_CTRL, reg); ++ ++ FMC_PR(BT_DBG, "\t||*-End SPI Nand host init\n"); ++} ++ ++/*****************************************************************************/ ++static unsigned char hifmc100_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ unsigned char value, ret_val = 0; ++ ++ if (host->cmd_op.l_cmd == NAND_CMD_READID) { ++ value = hifmc_readb(host->iobase + host->offset); ++ host->offset++; ++ if (host->cmd_op.data_no == host->offset) ++ host->cmd_op.l_cmd = 0; ++ return value; ++ } ++ ++ if (host->cmd_op.cmd == NAND_CMD_STATUS) { ++ value = hifmc_readl(host, FMC_STATUS); ++ if (host->cmd_op.l_cmd == NAND_CMD_GET_FEATURES) { ++ FMC_PR((ER_DBG || WR_DBG), "\t\tRead BP status:%#x\n", ++ value); ++ if (ANY_BP_ENABLE(value)) ++ ret_val |= NAND_STATUS_WP; ++ ++ host->cmd_op.l_cmd = NAND_CMD_STATUS; ++ } ++ ++ if (!(value & STATUS_OIP_MASK)) ++ ret_val |= NAND_STATUS_READY; ++ ++ if (value & STATUS_E_FAIL_MASK) { ++ FMC_PR(ER_DBG, "\t\tGet erase status: %#x\n", value); ++ ret_val |= NAND_STATUS_FAIL; ++ } ++ ++ if (value & STATUS_P_FAIL_MASK) { ++ FMC_PR(WR_DBG, "\t\tGet write status: %#x\n", value); ++ ret_val |= NAND_STATUS_FAIL; ++ } ++ ++ return ret_val; ++ } ++ ++ if (host->cmd_op.l_cmd == NAND_CMD_READOOB) { ++ value = hifmc_readb(host->buffer + host->pagesize + host->offset); ++ host->offset++; ++ return value; ++ } ++ ++ host->offset++; ++ ++ return hifmc_readb(host->buffer + host->column + host->offset - 1); ++} ++ ++/*****************************************************************************/ ++static unsigned short hifmc100_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++ host->offset += 2; ++ return hifmc_readw(host->buffer + host->column + host->offset - 2); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_write_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++#ifdef HIFMC100_SPI_NAND_SUPPORT_REG_WRITE ++ if (buf == chip->oob_poi) ++ memcpy((char *)host->iobase + host->pagesize, buf, len); ++ else ++ memcpy((char *)host->iobase, buf, len); ++#else ++ if (buf == chip->oob_poi) ++ memcpy((char *)(host->buffer + host->pagesize), buf, len); ++ else ++ memcpy((char *)host->buffer, buf, len); ++#endif ++ return; ++} ++ ++/*****************************************************************************/ ++static void hifmc100_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++#ifdef HIFMC100_SPI_NAND_SUPPORT_REG_READ ++ if (buf == chip->oob_poi) ++ memcpy(buf, (char *)host->iobase + host->pagesize, len); ++ else ++ memcpy(buf, (char *)host->iobase, len); ++#else ++ if (buf == chip->oob_poi) ++ memcpy(buf, (char *)host->buffer + host->pagesize, len); ++ else ++ memcpy(buf, (char *)host->buffer, len); ++#endif ++ ++#ifdef CONFIG_HISI_NAND_ECC_STATUS_REPORT ++ if (buf != chip->oob_poi) { ++ u_int reg, ecc_step = host->pagesize >> 10; ++ ++ reg = hifmc_readl(host, HIFMC100_ECC_ERR_NUM0_BUF0); ++ while (ecc_step) { ++ u_char err_num; ++ ++ err_num = GET_ECC_ERR_NUM(--ecc_step, reg); ++ if (err_num == 0xff) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += err_num; ++ } ++ } ++#endif ++ ++ return; ++} ++ ++/*****************************************************************************/ ++static void hifmc100_select_chip(struct mtd_info *mtd, int chipselect) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++ if (chipselect < 0) { ++ mutex_unlock(&fmc_switch_mutex); ++ return; ++ } ++ ++ mutex_lock(&fmc_switch_mutex); ++ ++ if (chipselect > CONFIG_SPI_NAND_MAX_CHIP_NUM) ++ DB_BUG("Error: Invalid chipselect: %d\n", chipselect); ++ ++ if (host->mtd != mtd) { ++ host->mtd = mtd; ++ host->cmd_op.cs = chipselect; ++ } ++ ++ if (!(chip->options & NAND_BROKEN_XD)) { ++ if ((chip->state == FL_ERASING) || (chip->state == FL_WRITING)) ++ host->cmd_op.l_cmd = NAND_CMD_GET_FEATURES; ++ } ++} ++ ++/*****************************************************************************/ ++static void hifmc100_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned ctrl) ++{ ++ unsigned char cmd; ++ int is_cache_invalid = 1; ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++ if (ctrl & NAND_ALE) { ++ unsigned int addr_value = 0; ++ unsigned int addr_offset = 0; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ host->addr_cycle = 0x0; ++ host->addr_value[0] = 0x0; ++ host->addr_value[1] = 0x0; ++ } ++ addr_offset = host->addr_cycle << 3; ++ ++ if (host->addr_cycle >= HIFMC100_ADDR_CYCLE_MASK) { ++ addr_offset = (host->addr_cycle - ++ HIFMC100_ADDR_CYCLE_MASK) << 3; ++ addr_value = 1; ++ } ++ ++ host->addr_value[addr_value] |= ++ ((dat & 0xff) << addr_offset); ++ ++ host->addr_cycle++; ++ } ++ ++ if ((ctrl & NAND_CLE) && (ctrl & NAND_CTRL_CHANGE)) { ++ cmd = dat & 0xff; ++ host->cmd_op.cmd = cmd; ++ switch (cmd) { ++ case NAND_CMD_PAGEPROG: ++ host->offset = 0; ++ host->send_cmd_write(host); ++ break; ++ ++ case NAND_CMD_READSTART: ++ is_cache_invalid = 0; ++ if (host->addr_value[0] == host->pagesize) ++ host->cmd_op.l_cmd = NAND_CMD_READOOB; ++ host->send_cmd_read(host); ++ break; ++ ++ case NAND_CMD_ERASE2: ++ host->send_cmd_erase(host); ++ break; ++ ++ case NAND_CMD_READID: ++ memset((u_char *)(host->iobase), 0, ++ MAX_SPI_NAND_ID_LEN); ++ host->cmd_op.l_cmd = cmd; ++ host->cmd_op.data_no = MAX_SPI_NAND_ID_LEN; ++ host->send_cmd_readid(host); ++ break; ++ ++ case NAND_CMD_STATUS: ++ host->send_cmd_status(host); ++ break; ++ ++ case NAND_CMD_READ0: ++ host->cmd_op.l_cmd = cmd; ++ break; ++ ++ case NAND_CMD_RESET: ++ host->send_cmd_reset(host); ++ break; ++ ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_ERASE1: ++ default: ++ break; ++ } ++ } ++ ++ if ((dat == NAND_CMD_NONE) && host->addr_cycle) { ++ if (host->cmd_op.cmd == NAND_CMD_SEQIN ++ || host->cmd_op.cmd == NAND_CMD_READ0 ++ || host->cmd_op.cmd == NAND_CMD_READID) { ++ host->offset = 0x0; ++ host->column = (host->addr_value[0] & 0xffff); ++ } ++ } ++ ++ if (is_cache_invalid) { ++ host->cache_addr_value[0] = ~0; ++ host->cache_addr_value[1] = ~0; ++ } ++} ++ ++/*****************************************************************************/ ++static int hifmc100_dev_ready(struct mtd_info *mtd) ++{ ++ unsigned int reg; ++ unsigned long deadline = jiffies + FMC_MAX_READY_WAIT_JIFFIES; ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++ do { ++ reg = OP_CFG_FM_CS(host->cmd_op.cs); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ ++ reg = FMC_OP_READ_STATUS_EN | FMC_OP_REG_OP_START; ++ hifmc_writel(host, FMC_OP, reg); ++ ++ FMC_CMD_WAIT_CPU_FINISH(host); ++ ++ reg = hifmc_readl(host, FMC_STATUS); ++ ++ if (!(reg & STATUS_OIP_MASK)) ++ return NAND_STATUS_READY; ++ ++ cond_resched(); ++ ++ } while (!time_after_eq(jiffies, deadline)); ++ ++ if (!(chip->options & NAND_SCAN_SILENT_NODEV)) ++ pr_warn("Wait SPI nand ready timeout, status: %#x\n", reg); ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++/* ++ * 'host->epm' only use the first oobfree[0] field, it looks very simple, But... ++ */ ++static struct nand_ecclayout nand_ecc_default = { ++ .oobfree = {{2, 30} } ++}; ++#ifdef CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 ++static struct nand_ecclayout nand_ecc_2k16bit = { ++ .oobfree = {{2, 6} } ++}; ++ ++static struct nand_ecclayout nand_ecc_4k16bit = { ++ .oobfree = {{2, 14} } ++}; ++#endif ++ ++/*****************************************************************************/ ++static struct nand_config_info hifmc_spi_nand_config_table[] = { ++ {NAND_PAGE_4K, NAND_ECC_24BIT, 200, &nand_ecc_default}, ++#ifdef CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 ++ {NAND_PAGE_4K, NAND_ECC_16BIT, 128, &nand_ecc_4k16bit}, ++#endif ++ {NAND_PAGE_4K, NAND_ECC_8BIT, 88, &nand_ecc_default}, ++ {NAND_PAGE_4K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {NAND_PAGE_2K, NAND_ECC_24BIT, 128, &nand_ecc_default}, ++#ifdef CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 ++ {NAND_PAGE_2K, NAND_ECC_16BIT, 64, &nand_ecc_2k16bit}, ++#endif ++ {NAND_PAGE_2K, NAND_ECC_8BIT, 64, &nand_ecc_default}, ++ {NAND_PAGE_2K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {0, 0, 0, NULL}, ++}; ++ ++/*****************************************************************************/ ++/* used the best correct arithmetic. */ ++static struct nand_config_info *hifmc100_get_best_ecc(struct mtd_info *mtd) ++{ ++ struct nand_config_info *best = NULL; ++ struct nand_config_info *info = hifmc_spi_nand_config_table; ++ ++ for (; info->layout; info++) { ++ if (match_page_type_to_size(info->pagetype) != mtd->writesize) ++ continue; ++ ++ if (mtd->oobsize < info->oobsize) ++ continue; ++ ++ if (!best || (best->ecctype < info->ecctype)) ++ best = info; ++ } ++ ++ if (!best) ++ DB_BUG(ERR_STR_DRIVER "pagesize: %d and oobsize: %d.\n", ++ mtd->writesize, mtd->oobsize); ++ return best; ++} ++ ++#if defined(CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE) \ ++ || defined(CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC) ++/*****************************************************************************/ ++/* force the pagesize and ecctype */ ++static struct nand_config_info *hifmc100_force_ecc(struct mtd_info *mtd, ++ int pagetype, int ecctype, char *cfgmsg, int allow_pagediv) ++{ ++ int pagesize; ++ struct nand_config_info *fit = NULL; ++ struct nand_config_info *info = hifmc_spi_nand_config_table; ++ ++ for (; info->layout; info++) { ++ if (info->pagetype == pagetype && info->ecctype == ecctype) { ++ fit = info; ++ break; ++ } ++ } ++ ++ if (!fit) { ++ DB_MSG("Driver(%s) can't find this configure\n", cfgmsg); ++ DB_BUG(ERR_STR_DRIVER "pagesize: %s, ecctype: %s\n", ++ nand_page_name(pagetype), nand_ecc_name(ecctype)); ++ return NULL; ++ } ++ ++ pagesize = match_page_type_to_size(pagetype); ++ if ((pagesize != mtd->writesize) ++ && (pagesize > mtd->writesize || !allow_pagediv)) { ++ DB_MSG("This SPI Nand Flash pageszie: %d\n", mtd->writesize); ++ DB_BUG("But (%s) configure pagesize: %d" ERR_STR_CHECK "\n", ++ cfgmsg, pagesize); ++ return NULL; ++ } ++ ++ if (fit->oobsize > mtd->oobsize) { ++ DB_MSG("This SPI Nand Flash offer space area is %dB\n", ++ mtd->oobsize); ++ DB_BUG("But (%s) the controller request %dB in ecc %s.", ++ cfgmsg, fit->oobsize, nand_ecc_name(ecctype)); ++ return NULL; ++ } ++ ++ return fit; ++} ++#endif ++ ++/*****************************************************************************/ ++static void hifmc100_chip_init(struct nand_chip *chip) ++{ ++ chip->read_byte = hifmc100_read_byte; ++ chip->read_word = hifmc100_read_word; ++ chip->write_buf = hifmc100_write_buf; ++ chip->read_buf = hifmc100_read_buf; ++ ++ chip->select_chip = hifmc100_select_chip; ++ ++ chip->cmd_ctrl = hifmc100_cmd_ctrl; ++ chip->dev_ready = hifmc100_dev_ready; ++ ++ chip->chip_delay = FMC_CHIP_DELAY; ++ ++ chip->options = NAND_NO_AUTOINCR | NAND_SKIP_BBTSCAN | NAND_BROKEN_XD ++ | NAND_SCAN_SILENT_NODEV; ++ ++ chip->ecc.layout = NULL; ++ chip->ecc.mode = NAND_ECC_NONE; ++} ++ ++/*****************************************************************************/ ++static int hifmc100_ecc_probe(struct mtd_info *mtd, struct nand_chip *chip, ++ struct nand_dev_t *nand_dev) ++{ ++ unsigned char page_reg, pagetype, ecc_reg, ecctype, block_reg = 0; ++ unsigned int reg, page_per_block; ++ char *start_type = "unknown"; ++ struct nand_config_info *best = NULL; ++ struct hifmc_host *host = chip->priv; ++ ++ FMC_PR(BT_DBG, "\t*-Start match PageSize and EccType\n"); ++ ++ reg = hifmc_readl(host, FMC_CFG); ++ FMC_PR(BT_DBG, "\t|-Get FMC_CFG[%#x] config: %#x\n", FMC_CFG, reg); ++ ++#ifdef CONFIG_HIFMC100_AUTO_PAGESIZE_ECC ++ best = hifmc100_get_best_ecc(mtd); ++ if (!best) ++ DB_BUG("Can't found any configure for SPI Nand Flash\n"); ++ ++ start_type = "Auto"; ++ ++ pagetype = best->pagetype; ++ ecctype = best->ecctype; ++ ++ page_reg = match_page_type_to_reg(pagetype); ++ reg &= ~PAGE_SIZE_MASK; ++ reg |= FMC_CFG_PAGE_SIZE(page_reg); ++ FMC_PR(BT_DBG, "\t|-%s Config, PageSize %s EccType %s OobSize %d\n", ++ start_type, nand_page_name(pagetype), ++ nand_ecc_name(ecctype), best->oobsize); ++ ++ ecc_reg = match_ecc_type_to_reg(ecctype); ++ reg &= ~ECC_TYPE_MASK; ++ reg |= FMC_CFG_ECC_TYPE(ecc_reg); ++ FMC_PR(BT_DBG, "\t|-%s Config best EccType: %s\n", start_type, ++ nand_ecc_name(best->ecctype)); ++ ++ page_per_block = mtd->erasesize / match_page_type_to_size(pagetype); ++ switch (page_per_block) { ++ case 64: ++ block_reg = BLOCK_SIZE_64_PAGE; ++ break; ++ case 128: ++ block_reg = BLOCK_SIZE_128_PAGE; ++ break; ++ case 256: ++ block_reg = BLOCK_SIZE_256_PAGE; ++ break; ++ case 512: ++ block_reg = BLOCK_SIZE_512_PAGE; ++ break; ++ default: ++ DB_MSG("Can't support block %#x and page %#x size\n", ++ mtd->erasesize, mtd->writesize); ++ } ++ reg &= ~BLOCK_SIZE_MASK; ++ reg |= FMC_CFG_BLOCK_SIZE(block_reg); ++ ++ hifmc_writel(host, FMC_CFG, reg); ++ FMC_PR(BT_DBG, "\t|-Set FMC_CFG[%#x] config: %#x\n", FMC_CFG, reg); ++#endif ++ ++ host->fmc_cfg = reg; ++ FMC_PR(BT_DBG, "\t|-Save FMC_CFG config: %#x\n", reg); ++ ++#ifdef CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC ++ #ifdef CONFIG_HIFMC100_AUTO_PAGESIZE_ECC ++ #error you SHOULD NOT define CONFIG_HIFMC100_AUTO_PAGESIZE_ECC \ ++ and CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC at the same time ++ #endif ++ ++ page_reg = (host->fmc_cfg & PAGE_SIZE_MASK) >> PAGE_SIZE_SHIFT; ++ pagetype = match_page_reg_to_type(page_reg); ++ FMC_PR(BT_DBG, "\t|-Get Hardware Config PageSize: %s\n", ++ nand_page_name(pagetype)); ++ ++ ecc_reg = (host->fmc_cfg & ECC_TYPE_MASK) >> ECC_TYPE_SHIFT; ++ ecctype = match_ecc_reg_to_type(ecc_reg); ++ FMC_PR(BT_DBG, "\t|-Get Hardware Config EccType: %s\n", ++ match_ecc_type_to_str(ecctype)); ++ ++ FMC_PR(BT_DBG, "\t|-Check FMC_CFG Config with Hardware Config\n"); ++ best = hifmc100_force_ecc(mtd, pagetype, ecctype, ++ "hardware config mode", 0); ++ if (!best) ++ DB_BUG("Can't found any configure for SPI Nand Flash\n"); ++ ++ start_type = "Hardware"; ++#endif ++ ++#ifdef CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE ++ ++ #ifdef CONFIG_HIFMC100_AUTO_PAGESIZE_ECC ++ #error you SHOULD NOT define CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE \ ++ and CONFIG_HIFMC100_AUTO_PAGESIZE_ECC at the same time ++ #endif ++ ++ #ifdef CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC ++ #error you SHOULD NOT define CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE \ ++ and CONFIG_HIFMC100_HARDWARE_PAGESIZE_ECC at the same time ++ #endif ++ ++ pagetype = match_page_size_to_type(mtd->writesize); ++ ++ best = hifmc100_force_ecc(mtd, pagetype, NAND_ECC_0BIT, ++ "force config mode", 0); ++ if (!best) ++ DB_BUG("Can't found any configure for SPI Nand Flash\n"); ++ ++ start_type = "AutoForce"; ++ FMC_PR(BT_DBG, "\t|-Check PageSize %s Config\n", start_type); ++ ++ page_reg = match_page_type_to_reg(best->pagetype); ++ ++ reg &= ~(PAGE_SIZE_MASK | ECC_TYPE_MASK); ++ reg |= FMC_CFG_PAGE_SIZE(page_reg); ++ reg |= FMC_CFG_ECC_TYPE(ECC_TYPE_0BIT); ++ host->fmc_cfg = reg; ++ FMC_PR(BT_DBG, "\t|-Save FMC_CFG config: %#x\n", reg); ++#endif /* End of CONFIG_HIFMC100_PAGESIZE_AUTO_ECC_NONE */ ++ ++ if (!best) ++ DB_BUG("Can't found any configure for SPI Nand Flash\n"); ++ ++ if (best->ecctype != NAND_ECC_0BIT) ++ mtd->oobsize = best->oobsize; ++ ++ chip->ecc.layout = best->layout; ++ ++ host->ecctype = best->ecctype; ++ host->pagesize = match_page_type_to_size(best->pagetype); ++ host->oobsize = mtd->oobsize; ++ ++ FMC_PR(BT_DBG, "\t|-%s Config end, best OOB Size: %d\n", start_type, ++ best->oobsize); ++ ++ host->block_page_mask = ((mtd->erasesize / mtd->writesize) - 1); ++ host->dma_oob = host->dma_buffer + host->pagesize; ++ host->bbm = (u_char *)(host->buffer + host->pagesize ++ + HIFMC_BAD_BLOCK_POS); ++ ++ /* EB bits locate in the bottom two of CTRL(30) */ ++ host->epm = (u_short *)(host->buffer + host->pagesize ++ + chip->ecc.layout->oobfree[0].offset + 28); ++ ++#ifdef CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 ++ if (best->ecctype == NAND_ECC_16BIT) { ++ if (host->pagesize == _2K) { ++ /* EB bits locate in the bottom two of CTRL(4) */ ++ host->epm = (u_short *)(host->buffer + host->pagesize ++ + chip->ecc.layout->oobfree[0].offset + 4); ++ } else if (host->pagesize == _4K) { ++ /* EB bit locate in the bottom two of CTRL(14) */ ++ host->epm = (u_short *)(host->buffer + host->pagesize ++ + chip->ecc.layout->oobfree[0].offset + 12); ++ } ++ } ++#endif ++ ++ host->fmc_cfg_ecc0 = (host->fmc_cfg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT; ++ ++ if (mtd->writesize > SPI_NAND_MAX_PAGESIZE ++ || mtd->oobsize > SPI_NAND_MAX_OOBSIZE) { ++ DB_MSG(ERR_STR_DRIVER "pageszie: %d and oobsize: %d\n", ++ mtd->writesize, mtd->oobsize); ++ DB_BUG("Please increase MAX PAGESIZE and OOBSIZE.\n"); ++ } ++ ++ if (mtd->writesize != host->pagesize) { ++ unsigned int shift = 0; ++ unsigned int writesize = mtd->writesize; ++ while (writesize > host->pagesize) { ++ writesize >>= 1; ++ shift++; ++ } ++ chip->chipsize = chip->chipsize >> shift; ++ mtd->erasesize = mtd->erasesize >> shift; ++ mtd->writesize = host->pagesize; ++ DB_MSG("SPI Nand divide into 1/%u\n", (1 << shift)); ++ } ++ ++ nand_dev->start_type = start_type; ++ nand_dev->oobsize = host->oobsize; ++ nand_dev->ecctype = host->ecctype; ++ ++ FMC_PR(BT_DBG, "\t*-End match PageSize and EccType\n"); ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++int hifmc100_spi_nand_init(struct nand_chip *chip) ++{ ++ struct hifmc_host *host = chip->priv; ++ ++ FMC_PR(BT_DBG, "\t|*-Start hifmc100 SPI Nand init\n"); ++ ++ /* Set system clock and enable controller */ ++ clk_prepare_enable(host->clk); ++ ++ /* Switch SPI type to SPI nand */ ++ hifmc100_switch_to_spi_nand(host); ++ ++ /* Hifmc host init */ ++ hifmc100_host_init(host); ++ host->chip = chip; ++ ++ /* Hifmc nand_chip struct init */ ++ hifmc100_chip_init(chip); ++ ++ hifmc_spi_nand_ids_register(); ++ hinfc_param_adjust = hifmc100_ecc_probe; ++ ++ FMC_PR(BT_DBG, "\t|*-End hifmc100 SPI Nand init\n"); ++ ++ return 0; ++} ++#ifdef CONFIG_PM ++/*****************************************************************************/ ++int hifmc100_suspend(struct platform_device *pltdev, pm_message_t state) ++{ ++ unsigned int ret; ++ struct hifmc_host *host = platform_get_drvdata(pltdev); ++ struct hifmc_spi *spi = host->spi; ++ ++ mutex_lock(host->lock); ++ hifmc100_switch_to_spi_nand(host); ++ ++ ret = spi->driver->wait_ready(spi); ++ if (ret) { ++ DB_MSG("Error: wait ready failed!"); ++ return 0; ++ } ++ ++ clk_disable_unprepare(host->clk); ++ mutex_unlock(host->lock); ++ ++ return 0; ++} ++/*****************************************************************************/ ++int hifmc100_resume(struct platform_device *pltdev) ++{ ++ int cs; ++ struct hifmc_host *host = platform_get_drvdata(pltdev); ++ struct nand_chip *chip = host->chip; ++ ++ mutex_lock(host->lock); ++ hifmc100_switch_to_spi_nand(host); ++ clk_prepare_enable(host->clk); ++ ++ for (cs = 0; cs < chip->numchips; cs++) ++ host->send_cmd_reset(host); ++ ++ hifmc100_spi_nand_config(host); ++ ++ mutex_unlock(host->lock); ++ return 0; ++} ++#endif ++ +diff --git a/drivers/mtd/nand/hifmc100/hifmc100.h b/drivers/mtd/nand/hifmc100/hifmc100.h +new file mode 100644 +index 0000000..7d04635 +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100/hifmc100.h +@@ -0,0 +1,386 @@ ++/* ++ * The Flash Memory Controller v100 Device Driver for hisilicon ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef __HIFMC100_H__ ++#define __HIFMC100_H__ ++ ++/*****************************************************************************/ ++#include <linux/platform_device.h> ++#include <linux/mfd/hisi_fmc.h> ++ ++/*****************************************************************************/ ++#define INFINITE (0xFFFFFFFF) ++ ++/*****************************************************************************/ ++#define SPI_IF_READ_STD (0x01) ++#define SPI_IF_READ_FAST (0x02) ++#define SPI_IF_READ_DUAL (0x04) ++#define SPI_IF_READ_DUAL_ADDR (0x08) ++#define SPI_IF_READ_QUAD (0x10) ++#define SPI_IF_READ_QUAD_ADDR (0x20) ++ ++#define SPI_IF_WRITE_STD (0x01) ++#define SPI_IF_WRITE_DUAL (0x02) ++#define SPI_IF_WRITE_DUAL_ADDR (0x04) ++#define SPI_IF_WRITE_QUAD (0x08) ++#define SPI_IF_WRITE_QUAD_ADDR (0x10) ++ ++#define SPI_IF_ERASE_SECTOR_4K (0x01) ++#define SPI_IF_ERASE_SECTOR_32K (0x02) ++#define SPI_IF_ERASE_SECTOR_64K (0x04) ++#define SPI_IF_ERASE_SECTOR_128K (0x08) ++#define SPI_IF_ERASE_SECTOR_256K (0x10) ++ ++/******************************************************************************/ ++#define HIFMC_SPI_NAND_SUPPORT_READ (SPI_IF_READ_STD \ ++ | SPI_IF_READ_FAST \ ++ | SPI_IF_READ_DUAL \ ++ | SPI_IF_READ_DUAL_ADDR \ ++ | SPI_IF_READ_QUAD \ ++ | SPI_IF_READ_QUAD_ADDR) ++ ++#define HIFMC_SPI_NAND_SUPPORT_WRITE (SPI_IF_WRITE_STD | SPI_IF_WRITE_QUAD) ++ ++#define HIFMC_SPI_NAND_SUPPORT_MAX_DUMMY 8 ++ ++/*****************************************************************************/ ++#define SPI_CMD_READ_STD 0x03 /* Standard read cache */ ++#define SPI_CMD_READ_FAST 0x0B /* Higher speed read cache */ ++#define SPI_CMD_READ_DUAL 0x3B /* 2 IO read cache only date */ ++#define SPI_CMD_READ_DUAL_ADDR 0xBB /* 2 IO read cache date&addr */ ++#define SPI_CMD_READ_QUAD 0x6B /* 4 IO read cache only date */ ++#define SPI_CMD_READ_QUAD_ADDR 0xEB /* 4 IO read cache date&addr */ ++ ++#define SPI_CMD_WRITE_STD 0x02 /* Standard page program */ ++#define SPI_CMD_WRITE_DUAL 0xA2 /* 2 IO program only date */ ++#define SPI_CMD_WRITE_DUAL_ADDR 0xD2 /* 2 IO program date&addr */ ++#define SPI_CMD_WRITE_QUAD 0x32 /* 4 IO program only date */ ++#define SPI_CMD_WRITE_QUAD_ADDR 0x12 /* 4 IO program date&addr */ ++ ++#define SPI_CMD_SE_4K 0x20 /* 4KB sector Erase */ ++#define SPI_CMD_SE_32K 0x52 /* 32KB sector Erase */ ++#define SPI_CMD_SE_64K 0xD8 /* 64KB sector Erase */ ++#define SPI_CMD_SE_128K 0xD8 /* 128KB sector Erase */ ++#define SPI_CMD_SE_256K 0xD8 /* 256KB sector Erase */ ++ ++/*****************************************************************************/ ++#define SET_READ_STD(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_std_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_STD, SPI_CMD_READ_STD, _dummy_, _size_, _clk_ } ++ ++#define SET_READ_FAST(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_fast_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_FAST, SPI_CMD_READ_FAST, _dummy_, _size_, _clk_ } ++ ++#define SET_READ_DUAL(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_dual_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_DUAL, SPI_CMD_READ_DUAL, _dummy_, _size_, _clk_ } ++ ++#define SET_READ_DUAL_ADDR(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_dual_addr_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_DUAL_ADDR, SPI_CMD_READ_DUAL_ADDR, _dummy_, _size_, _clk_ } ++ ++#define SET_READ_QUAD(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_quad_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_QUAD, SPI_CMD_READ_QUAD, _dummy_, _size_, _clk_ } ++ ++#define SET_READ_QUAD_ADDR(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_quad_addr_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_QUAD_ADDR, SPI_CMD_READ_QUAD_ADDR, _dummy_, _size_, _clk_ } ++ ++/*****************************************************************************/ ++#define SET_WRITE_STD(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_std_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_WRITE_STD, SPI_CMD_WRITE_STD, _dummy_, _size_, _clk_ } ++ ++#define SET_WRITE_DUAL(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_dual_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_WRITE_DUAL, SPI_CMD_WRITE_DUAL, _dummy_, _size_, _clk_ } ++ ++#define SET_WRITE_DUAL_ADDR(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_dual_addr_##_dummy_##_size_##_clk_ = { \ ++SPI_IF_WRITE_DUAL_ADDR, SPI_CMD_WRITE_DUAL_ADDR, _dummy_, _size_, _clk_ } ++ ++#define SET_WRITE_QUAD(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_quad_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_WRITE_QUAD, SPI_CMD_WRITE_QUAD, _dummy_, _size_, _clk_ } ++ ++#define SET_WRITE_QUAD_ADDR(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_quad_addr_##_dummy_##_size_##_clk_ = { \ ++SPI_IF_WRITE_QUAD_ADDR, SPI_CMD_WRITE_QUAD_ADDR, _dummy_, _size_, _clk_ } ++ ++/*****************************************************************************/ ++#define SET_ERASE_SECTOR_4K(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_4k_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_4K, SPI_CMD_SE_4K, _dummy_, _size_, _clk_ } ++ ++#define SET_ERASE_SECTOR_32K(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_32k_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_32K, SPI_CMD_SE_32K, _dummy_, _size_, _clk_ } ++ ++#define SET_ERASE_SECTOR_64K(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_64k_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_64K, SPI_CMD_SE_64K, _dummy_, _size_, _clk_ } ++ ++#define SET_ERASE_SECTOR_128K(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_128k_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_128K, SPI_CMD_SE_128K, _dummy_, _size_, _clk_ } ++ ++#define SET_ERASE_SECTOR_256K(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_256k_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_256K, SPI_CMD_SE_256K, _dummy_, _size_, _clk_ } ++ ++/*****************************************************************************/ ++#define READ_STD(_dummy_, _size_, _clk_) read_std_##_dummy_##_size_##_clk_ ++#define READ_FAST(_dummy_, _size_, _clk_) read_fast_##_dummy_##_size_##_clk_ ++#define READ_DUAL(_dummy_, _size_, _clk_) read_dual_##_dummy_##_size_##_clk_ ++#define READ_DUAL_ADDR(_dummy_, _size_, _clk_) \ ++ read_dual_addr_##_dummy_##_size_##_clk_ ++#define READ_QUAD(_dummy_, _size_, _clk_) read_quad_##_dummy_##_size_##_clk_ ++#define READ_QUAD_ADDR(_dummy_, _size_, _clk_) \ ++ read_quad_addr_##_dummy_##_size_##_clk_ ++ ++/*****************************************************************************/ ++#define WRITE_STD(_dummy_, _size_, _clk_) write_std_##_dummy_##_size_##_clk_ ++#define WRITE_DUAL(_dummy_, _size_, _clk_) write_dual_##_dummy_##_size_##_clk_ ++#define WRITE_DUAL_ADDR(_dummy_, _size_, _clk_) \ ++ write_dual_addr_##_dummy_##_size_##_clk_ ++#define WRITE_QUAD(_dummy_, _size_, _clk_) write_quad_##_dummy_##_size_##_clk_ ++#define WRITE_QUAD_ADDR(_dummy_, _size_, _clk_) \ ++ write_quad_addr_##_dummy_##_size_##_clk_ ++ ++/*****************************************************************************/ ++#define ERASE_SECTOR_4K(_dummy_, _size_, _clk_) \ ++ erase_sector_4k_##_dummy_##_size_##_clk_ ++#define ERASE_SECTOR_32K(_dummy_, _size_, _clk_) \ ++ erase_sector_32k_##_dummy_##_size_##_clk_ ++#define ERASE_SECTOR_64K(_dummy_, _size_, _clk_) \ ++ erase_sector_64k_##_dummy_##_size_##_clk_ ++#define ERASE_SECTOR_128K(_dummy_, _size_, _clk_) \ ++ erase_sector_128k_##_dummy_##_size_##_clk_ ++#define ERASE_SECTOR_256K(_dummy_, _size_, _clk_) \ ++ erase_sector_256k_##_dummy_##_size_##_clk_ ++ ++/*****************************************************************************/ ++#define SPI_CMD_WREN 0x06 /* Write Enable */ ++#define SPI_CMD_WRDI 0x04 /* Write Disable */ ++ ++#define SPI_CMD_RDID 0x9F /* Read Identification */ ++ ++/*****************************************************************************/ ++#define SPI_CMD_GET_FEATURES 0x0F /* Get Features */ ++#define SPI_CMD_SET_FEATURE 0x1F /* Set Feature */ ++ ++#define SPI_CMD_PAGE_READ 0x13 /* Page Read to Cache */ ++ ++#define SPI_CMD_RESET 0xff /* Reset the device */ ++ ++/*****************************************************************************/ ++/* These macroes are for debug only, reg option is slower then dma option */ ++#undef HIFMC100_SPI_NAND_SUPPORT_REG_READ ++/* #define HIFMC100_SPI_NAND_SUPPORT_REG_READ */ ++ ++#undef HIFMC100_SPI_NAND_SUPPORT_REG_WRITE ++/* #define HIFMC100_SPI_NAND_SUPPORT_REG_WRITE */ ++ ++#ifdef CONFIG_HISI_NAND_ECC_STATUS_REPORT ++/*****************************************************************************/ ++#define HIFMC100_ECC_ERR_NUM0_BUF0 0xc0 ++ ++#define GET_ECC_ERR_NUM(_i, _reg) (((_reg) >> ((_i) * 8)) & 0xff) ++#endif ++/*****************************************************************************/ ++#define REG_CNT_HIGH_BLOCK_NUM_SHIFT 10 ++ ++#define REG_CNT_BLOCK_NUM_MASK 0x3ff ++#define REG_CNT_BLOCK_NUM_SHIFT 22 ++ ++#define REG_CNT_PAGE_NUM_MASK 0x3f ++#define REG_CNT_PAGE_NUM_SHIFT 16 ++ ++#define REG_CNT_WRAP_MASK 0xf ++#define REG_CNT_WRAP_SHIFT 12 ++ ++#define REG_CNT_ECC_OFFSET_MASK 0xfff ++#define REG_CNT_ECC_8BIT_OFFSET 1054 ++#define REG_CNT_ECC_16BIT_OFFSET 1056 ++#define REG_CNT_ECC_24BIT_OFFSET 1082 ++ ++#define ERR_STR_DRIVER "Driver does not support this configure " ++#define ERR_STR_CHECK "Please make sure the hardware configuration is correct" ++ ++/*****************************************************************************/ ++#define HIFMC100_ADDR_CYCLE_MASK 0x2 ++ ++/*****************************************************************************/ ++#define OP_STYPE_NONE 0x0 ++#define OP_STYPE_READ 0x01 ++#define OP_STYPE_WRITE 0x02 ++#define OP_STYPE_ERASE 0x04 ++#define CLK_FMC_TO_CRG_MHZ(_clk) ((_clk) * 2000000) ++ ++/*****************************************************************************/ ++#define MAX_SPI_OP 8 ++ ++/*****************************************************************************/ ++/* SPI general operation parameter */ ++struct spi_op { ++ unsigned char iftype; ++ unsigned char cmd; ++ unsigned char dummy; ++ unsigned int size; ++ unsigned int clock; ++}; ++ ++struct spi_drv; ++ ++/* SPI interface all operation */ ++struct hifmc_spi { ++ char *name; ++ int chipselect; ++ unsigned long long chipsize; ++ unsigned int erasesize; ++#define SPI_NOR_3BYTE_ADDR_LEN 3 /* address len 3Bytes */ ++#define SPI_NOR_4BYTE_ADDR_LEN 4 /* address len 4Bytes for 32MB */ ++ unsigned int addrcycle; ++ ++ struct spi_op read[1]; ++ struct spi_op write[1]; ++ struct spi_op erase[MAX_SPI_OP]; ++ ++ void *host; ++ ++ struct spi_drv *driver; ++}; ++ ++/* SPI interface special operation function hook */ ++struct spi_drv { ++ int (*wait_ready)(struct hifmc_spi *spi); ++ int (*write_enable)(struct hifmc_spi *spi); ++ int (*qe_enable)(struct hifmc_spi *spi); ++ int (*bus_prepare)(struct hifmc_spi *spi, int op); ++ int (*entry_4addr)(struct hifmc_spi *spi, int en); ++}; ++ ++struct spi_nand_info { ++ char *name; ++ unsigned char id[MAX_SPI_NAND_ID_LEN]; ++ unsigned char id_len; ++ unsigned long long chipsize; ++ unsigned int erasesize; ++ unsigned int pagesize; ++ unsigned int oobsize; ++#define BBP_LAST_PAGE 0x01 ++#define BBP_FIRST_PAGE 0x02 ++ unsigned int badblock_pos; ++ struct spi_op *read[MAX_SPI_OP]; ++ struct spi_op *write[MAX_SPI_OP]; ++ struct spi_op *erase[MAX_SPI_OP]; ++ struct spi_drv *driver; ++}; ++ ++/*****************************************************************************/ ++extern u_char spi_nand_feature_op(struct hifmc_spi *spi, u_char op, u_char addr, ++ u_char val); ++ ++/*****************************************************************************/ ++struct hifmc_host { ++ struct mtd_info *mtd; ++ struct nand_chip *chip; ++ struct hifmc_spi spi[CONFIG_SPI_NAND_MAX_CHIP_NUM]; ++ struct hifmc_cmd_op cmd_op; ++ ++ void __iomem *iobase; ++ void __iomem *regbase; ++ struct clk *clk; ++ u32 clkrate; ++ ++ unsigned int fmc_cfg; ++ unsigned int fmc_cfg_ecc0; ++ ++ unsigned int offset; ++ ++ struct device *dev; ++ struct mutex *lock; ++ ++ /* This is maybe an un-aligment address, only for malloc or free */ ++ char *buforg; ++ char *buffer; ++ ++ unsigned int dma_buffer; ++ unsigned int dma_oob; ++ ++ unsigned int addr_cycle; ++ unsigned int addr_value[2]; ++ unsigned int cache_addr_value[2]; ++ ++ unsigned int column; ++ unsigned int block_page_mask; ++ ++ unsigned int ecctype; ++ unsigned int pagesize; ++ unsigned int oobsize; ++ ++ int add_partition; ++ ++ int need_rr_data; ++#define HIFMC100_READ_RETRY_DATA_LEN 128 ++ char rr_data[HIFMC100_READ_RETRY_DATA_LEN]; ++ struct read_retry_t *read_retry; ++ ++ int version; ++ ++ /* BOOTROM read two bytes to detect the bad block flag */ ++#define HIFMC_BAD_BLOCK_POS 0 ++ unsigned char *bbm; /* nand bad block mark */ ++ unsigned short *epm; /* nand empty page mark */ ++ ++ unsigned int uc_er; ++ ++ void (*send_cmd_write)(struct hifmc_host *host); ++ void (*send_cmd_status)(struct hifmc_host *host); ++ void (*send_cmd_read)(struct hifmc_host *host); ++ void (*send_cmd_erase)(struct hifmc_host *host); ++ void (*send_cmd_readid)(struct hifmc_host *host); ++ void (*send_cmd_reset)(struct hifmc_host *host); ++#ifdef CONFIG_PM ++ int (*suspend)(struct platform_device *pltdev, pm_message_t state); ++ int (*resume)(struct platform_device *pltdev); ++#endif ++}; ++ ++/*****************************************************************************/ ++void hifmc100_ecc0_switch(struct hifmc_host *host, unsigned char op); ++ ++int hifmc100_spi_nand_init(struct nand_chip *chip); ++ ++/*****************************************************************************/ ++extern void hifmc_spi_nand_ids_register(void); ++ ++extern void hifmc_set_nand_system_clock(struct spi_op *op, int clk_en); ++ ++/*****************************************************************************/ ++#ifdef CONFIG_PM ++int hifmc100_suspend(struct platform_device *pltdev, pm_message_t state); ++int hifmc100_resume(struct platform_device *pltdev); ++void hifmc100_spi_nand_config(struct hifmc_host *host); ++#endif ++ ++#endif /* End of __HIFMC100_H__ */ +diff --git a/drivers/mtd/nand/hifmc100/hifmc100_os.c b/drivers/mtd/nand/hifmc100/hifmc100_os.c +new file mode 100644 +index 0000000..f699e8f +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100/hifmc100_os.c +@@ -0,0 +1,330 @@ ++/* ++ * The Flash Memory Controller v100 Device Driver for hisilicon ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/dma-mapping.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/partitions.h> ++#include <linux/of_platform.h> ++#include <linux/mfd/hisi_fmc.h> ++ ++#include <asm/setup.h> ++ ++#include "../../mtdcore.h" ++#include "hifmc100.h" ++#include "hifmc100_os.h" ++ ++/*****************************************************************************/ ++#define MAX_MTD_PARTITIONS (32) ++ ++struct partition_entry { ++ char name[16]; ++ unsigned long long start; ++ unsigned long long length; ++ unsigned int flags; ++}; ++ ++struct partition_info { ++ int parts_num; ++ struct partition_entry entry[MAX_MTD_PARTITIONS]; ++ struct mtd_partition parts[MAX_MTD_PARTITIONS]; ++}; ++ ++static struct partition_info ptn_info = {0}; ++ ++/*****************************************************************************/ ++static int __init parse_nand_partitions(const struct tag *tag) ++{ ++ int i, len; ++ ++ if (tag->hdr.size <= 2) { ++ pr_info("tag->hdr.size <= 2\n"); ++ return 0; ++ } ++ ++ len = sizeof(struct partition_entry) / sizeof(int); ++ ptn_info.parts_num = (tag->hdr.size - 2) / len; ++ ++ len = ptn_info.parts_num * sizeof(struct partition_entry); ++ memcpy(ptn_info.entry, &tag->u, len); ++ ++ for (i = 0; i < ptn_info.parts_num; i++) { ++ ptn_info.parts[i].name = ptn_info.entry[i].name; ++ ptn_info.parts[i].size = (ptn_info.entry[i].length); ++ ptn_info.parts[i].offset = (ptn_info.entry[i].start); ++ ptn_info.parts[i].mask_flags = 0; ++ ptn_info.parts[i].ecclayout = 0; ++ } ++ ++ return 0; ++} ++ ++/* turn to ascii is "HiNp" */ ++__tagtable(0x48694E70, parse_nand_partitions); ++ ++/*****************************************************************************/ ++static int hifmc100_spi_nand_pre_probe(struct nand_chip *chip) ++{ ++ uint8_t nand_maf_id; ++ struct hifmc_host *host = chip->priv; ++ ++ /* Reset the chip first */ ++ host->send_cmd_reset(host); ++ udelay(1000); ++ ++ /* Check the ID */ ++ host->offset = 0; ++ memset((unsigned char *)(chip->IO_ADDR_R), 0, 0x10); ++ host->send_cmd_readid(host); ++ nand_maf_id = hifmc_readb(chip->IO_ADDR_R); ++ ++ if (nand_maf_id == 0x00 || nand_maf_id == 0xff) { ++ printk("Cannot found a valid SPI Nand Device\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++/*****************************************************************************/ ++static int hifmc_nand_scan(struct mtd_info *mtd) ++{ ++ int result = 0; ++ unsigned char cs, chip_num = CONFIG_SPI_NAND_MAX_CHIP_NUM; ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++ for (cs = 0; chip_num && (cs < HIFMC_MAX_CHIP_NUM); cs++) { ++ if (hifmc_cs_user[cs]) { ++ FMC_PR(BT_DBG, "\t\t*-Current CS(%d) is occupied.\n", ++ cs); ++ continue; ++ } ++ ++ host->cmd_op.cs = cs; ++ ++ if (hifmc100_spi_nand_pre_probe(chip)) ++ return -ENODEV; ++ ++ FMC_PR(BT_DBG, "\t\t*-Scan SPI nand flash on CS: %d\n", cs); ++ if (nand_scan(mtd, chip_num)) ++ continue; ++ chip_num--; ++ } ++ ++ if (chip_num == CONFIG_SPI_NAND_MAX_CHIP_NUM) ++ result = -ENXIO; ++ else ++ result = 0; ++ ++ return result; ++} ++ ++/*****************************************************************************/ ++static int hifmc_os_add_paratitions(struct hifmc_host *host) ++{ ++ int ix; ++ int nr_parts = 0; ++ struct mtd_partition *parts = NULL; ++ int ret; ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ static const char *part_probes[] = {"cmdlinepart", NULL, }; ++ nr_parts = parse_mtd_partitions(host->mtd, part_probes, &parts, 0); ++#endif ++ ++ /* (1) if nr_parts > 0, Using commandline partition definition ++ * (2) if nr_parts = 0, Using board partition definition, or parse ++ * the parameter from __tag. ++ */ ++ if (!nr_parts) { ++ nr_parts = ptn_info.parts_num; ++ parts = ptn_info.parts; ++ } ++ ++ if (nr_parts <= 0) ++ return 0; ++ ++ if (BT_DBG) ++ for (ix = 0; ix < nr_parts; ix++) { ++ DB_MSG("partitions[%d] = {.name = %s, ", ++ ix, parts[ix].name); ++ DB_MSG(".offset = 0x%.8x, .size = 0x%08x (%uMB) }\n", ++ (unsigned int)parts[ix].offset, ++ (unsigned int)parts[ix].size, ++ (unsigned int)parts[ix].size / (1024 * 1024)); ++ } ++ ++ host->add_partition = 1; ++ ++ ret = mtd_device_register(host->mtd, parts, nr_parts); ++ ++ return (1 == ret) ? -ENODEV : 0; ++} ++ ++/*****************************************************************************/ ++static int hisi_spi_nand_probe(struct platform_device *pltdev) ++{ ++ int len, result = 0; ++ struct hifmc_host *host; ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ struct device *dev = &pltdev->dev; ++ struct device_node *np = NULL; ++ struct hisi_fmc *fmc = dev_get_drvdata(dev->parent); ++ ++ FMC_PR(BT_DBG, "\t*-Start SPI Nand flash driver probe\n"); ++ ++ len = sizeof(struct hifmc_host) + sizeof(struct nand_chip) ++ + sizeof(struct mtd_info); ++ host = devm_kzalloc(dev, len, GFP_KERNEL); ++ if (!host) ++ return -ENOMEM; ++ memset((char *)host, 0, len); ++ ++ platform_set_drvdata(pltdev, host); ++ host->dev = &pltdev->dev; ++ ++ host->chip = chip = (struct nand_chip *)&host[1]; ++ host->mtd = mtd = (struct mtd_info *)&chip[1]; ++ ++ host->regbase = fmc->regbase; ++ host->iobase = fmc->iobase; ++ host->clk = fmc->clk; ++ host->lock = &fmc->lock; ++ ++ memset((char *)host->iobase, 0xff, SPI_NAND_BUFFER_LEN); ++ chip->IO_ADDR_R = chip->IO_ADDR_W = host->iobase; ++ ++ host->buffer = dmam_alloc_coherent(host->dev, SPI_NAND_BUFFER_LEN, ++ &host->dma_buffer, GFP_KERNEL); ++ if (!host->buffer) { ++ DB_MSG("Error: Can't allocate memory for dma buffer."); ++ result = -EIO; ++ goto fail; ++ } ++ memset(host->buffer, 0xff, SPI_NAND_BUFFER_LEN); ++ ++ chip->priv = host; ++ result = hifmc100_spi_nand_init(chip); ++ if (result) { ++ FMC_PR(BT_DBG, "\t|-SPI Nand init failed, ret: %d\n", result); ++ result = -ENODEV; ++ goto fail; ++ } ++ ++ np = of_get_next_available_child(dev->of_node, NULL); ++ mtd->name = np->name; ++ mtd->type = MTD_NANDFLASH; ++ mtd->priv = chip; ++ mtd->owner = THIS_MODULE; ++ ++ result = of_property_read_u32(np, "spi-max-frequency", &host->clkrate); ++ if (result) ++ goto fail; ++ ++ result = hifmc_nand_scan(mtd); ++ if (result) { ++ FMC_PR(BT_DBG, "\t|-Scan SPI Nand failed.\n"); ++ goto fail; ++ } ++ ++ result = hifmc_os_add_paratitions(host); ++ if (host->add_partition) ++ goto end; ++ ++ if (!add_mtd_device(host->mtd)) { ++ result = 0; ++ goto end; ++ } ++ ++ result = -ENODEV; ++ DB_MSG("Error: MTD partition register failed! result: %d\n", ++ result); ++fail: ++ nand_release(mtd); ++ clk_disable_unprepare(host->clk); ++end: ++ FMC_PR(BT_DBG, "\t*-End driver probe, result: %d\n", result); ++ return result; ++} ++ ++/*****************************************************************************/ ++static int hisi_spi_nand_remove(struct platform_device *pltdev) ++{ ++ struct hifmc_host *host = platform_get_drvdata(pltdev); ++ ++ clk_disable_unprepare(host->clk); ++ nand_release(host->mtd); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++/*****************************************************************************/ ++static int hifmc100_os_suspend(struct platform_device *pltdev, ++ pm_message_t state) ++{ ++ struct hifmc_host *host = platform_get_drvdata(pltdev); ++ ++ if (host && host->suspend) ++ return (host->suspend)(pltdev, state); ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++static int hifmc100_os_resume(struct platform_device *pltdev) ++{ ++ struct hifmc_host *host = platform_get_drvdata(pltdev); ++ ++ if (host && host->resume) ++ return (host->resume)(pltdev); ++ ++ return 0; ++} ++#endif /* End of CONFIG_PM */ ++/*****************************************************************************/ ++static const struct of_device_id hisi_spi_nand_dt_ids[] = { ++ { .compatible = "hisilicon,hisi-spi-nand"}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, hisi_spi_nand_dt_ids); ++ ++static struct platform_driver hisi_spi_nand_driver = { ++ .driver = { ++ .name = "hisi_spi_nand", ++ .of_match_table = hisi_spi_nand_dt_ids, ++ }, ++ .probe = hisi_spi_nand_probe, ++ .remove = hisi_spi_nand_remove, ++#ifdef CONFIG_PM ++ .suspend = hifmc100_os_suspend, ++ .resume = hifmc100_os_resume, ++#endif ++}; ++module_platform_driver(hisi_spi_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("BVT_BSP"); ++MODULE_DESCRIPTION("Hisilicon Flash Memory Controller V100 SPI Nand Driver"); +diff --git a/drivers/mtd/nand/hifmc100/hifmc100_os.h b/drivers/mtd/nand/hifmc100/hifmc100_os.h +new file mode 100644 +index 0000000..9c720b4 +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100/hifmc100_os.h +@@ -0,0 +1,37 @@ ++/* ++ * The Flash Memory Controller v100 Device Driver for hisilicon ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef __HIFMC100_OS_H__ ++#define __HIFMC100_OS_H__ ++ ++/*****************************************************************************/ ++#ifndef CONFIG_SPI_NAND_MAX_CHIP_NUM ++ #define CONFIG_SPI_NAND_MAX_CHIP_NUM (1) ++ #warning NOT config CONFIG_SPI_NAND_MAX_CHIP_NUM, \ ++ used default value, maybe invalid. ++#endif ++ ++/*****************************************************************************/ ++#define SPI_NAND_MAX_PAGESIZE 4096 ++#define SPI_NAND_MAX_OOBSIZE 256 ++ ++#define SPI_NAND_BUFFER_LEN (SPI_NAND_MAX_PAGESIZE + SPI_NAND_MAX_OOBSIZE) ++ ++#endif /* End of __HIFMC100_OS_H__ */ +diff --git a/drivers/mtd/nand/hifmc100/hifmc100_spi_general.c b/drivers/mtd/nand/hifmc100/hifmc100_spi_general.c +new file mode 100644 +index 0000000..6fef356 +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100/hifmc100_spi_general.c +@@ -0,0 +1,260 @@ ++/* ++ * The Flash Memory Controller v100 Device Driver for hisilicon ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++/* ++ Send set/get features command to SPI Nand flash ++*/ ++u_char spi_nand_feature_op(struct hifmc_spi *spi, u_char op, u_char addr, ++ u_char val) ++{ ++ unsigned int reg; ++ const char *str[] = {"Get", "Set"}; ++ struct hifmc_host *host = (struct hifmc_host *)spi->host; ++ ++ if ((GET_OP == op) && (STATUS_ADDR == addr)) { ++ if (SR_DBG) ++ pr_info("\n"); ++ FMC_PR(SR_DBG, "\t\t|*-Start Get Status\n"); ++ ++ reg = OP_CFG_FM_CS(host->cmd_op.cs); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ FMC_PR(SR_DBG, "\t\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = FMC_OP_READ_STATUS_EN | FMC_OP_REG_OP_START; ++ hifmc_writel(host, FMC_OP, reg); ++ FMC_PR(SR_DBG, "\t\t||-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ FMC_CMD_WAIT_CPU_FINISH(host); ++ ++ val = hifmc_readl(host, FMC_STATUS); ++ FMC_PR(SR_DBG, "\t\t|*-End Get Status, result: %#x\n", val); ++ ++ return val; ++ } ++ ++ FMC_PR(FT_DBG, "\t|||*-Start %s feature, addr[%#x]\n", str[op], addr); ++ ++ hifmc100_ecc0_switch(host, ENABLE); ++ ++ reg = FMC_CMD_CMD1(op ? SPI_CMD_SET_FEATURE : SPI_CMD_GET_FEATURES); ++ hifmc_writel(host, FMC_CMD, reg); ++ FMC_PR(FT_DBG, "\t||||-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ hifmc_writel(host, FMC_ADDRL, addr); ++ FMC_PR(FT_DBG, "\t||||-Set ADDRL[%#x]%#x\n", FMC_ADDRL, addr); ++ ++ reg = OP_CFG_FM_CS(host->cmd_op.cs) ++ | OP_CFG_ADDR_NUM(FEATURES_OP_ADDR_NUM); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ FMC_PR(FT_DBG, "\t||||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = FMC_DATA_NUM_CNT(FEATURES_DATA_LEN); ++ hifmc_writel(host, FMC_DATA_NUM, reg); ++ FMC_PR(FT_DBG, "\t||||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg); ++ ++ reg = FMC_OP_CMD1_EN ++ | FMC_OP_ADDR_EN ++ | FMC_OP_REG_OP_START; ++ ++ if (SET_OP == op) { ++ reg |= FMC_OP_WRITE_DATA_EN; ++ hifmc_writeb(val, host->iobase); ++ FMC_PR(FT_DBG, "\t||||-Write IO[%#x]%#x\n", (u_int)host->iobase, ++ *(u_char *)host->iobase); ++ } else ++ reg |= FMC_OP_READ_DATA_EN; ++ ++ hifmc_writel(host, FMC_OP, reg); ++ FMC_PR(FT_DBG, "\t||||-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ FMC_CMD_WAIT_CPU_FINISH(host); ++ ++ if (GET_OP == op) { ++ val = hifmc_readb(host->iobase); ++ FMC_PR(FT_DBG, "\t||||-Read IO[%#x]%#x\n", (u_int)host->iobase, ++ *(u_char *)host->iobase); ++ } ++ ++ hifmc100_ecc0_switch(host, DISABLE); ++ ++ FMC_PR(FT_DBG, "\t|||*-End %s Feature[%#x]:%#x\n", str[op], addr, val); ++ ++ return val; ++} ++ ++/*****************************************************************************/ ++/* ++ Read status[C0H]:[0]bit OIP, judge whether the device is busy or not ++*/ ++static int spi_general_wait_ready(struct hifmc_spi *spi) ++{ ++ unsigned char status; ++ unsigned long deadline = jiffies + FMC_MAX_READY_WAIT_JIFFIES; ++ struct hifmc_host *host = (struct hifmc_host *)spi->host; ++ ++ do { ++ status = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, 0); ++ if (!(status & STATUS_OIP_MASK)) { ++ if ((host->cmd_op.l_cmd == NAND_CMD_ERASE2) ++ && (status & STATUS_E_FAIL_MASK)) ++ return status; ++ if ((host->cmd_op.l_cmd == NAND_CMD_PAGEPROG) ++ && (status & STATUS_P_FAIL_MASK)) ++ return status; ++ return 0; ++ } ++ ++ cond_resched(); ++ ++ } while (!time_after_eq(jiffies, deadline)); ++ ++ DB_MSG("Error: SPI Nand wait ready timeout, status: %#x\n", status); ++ ++ return 1; ++} ++ ++/*****************************************************************************/ ++/* ++ Send write enable cmd to SPI Nand, status[C0H]:[2]bit WEL must be set 1 ++*/ ++static int spi_general_write_enable(struct hifmc_spi *spi) ++{ ++ unsigned int reg; ++ struct hifmc_host *host = (struct hifmc_host *)spi->host; ++ ++ if (WE_DBG) ++ pr_info("\n"); ++ FMC_PR(WE_DBG, "\t|*-Start Write Enable\n"); ++ ++ reg = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, 0); ++ if (reg & STATUS_WEL_MASK) { ++ FMC_PR(WE_DBG, "\t||-Write Enable was opened! reg: %#x\n", ++ reg); ++ return 0; ++ } ++ ++ reg = hifmc_readl(host, FMC_GLOBAL_CFG); ++ FMC_PR(WE_DBG, "\t||-Get GLOBAL_CFG[%#x]%#x\n", FMC_GLOBAL_CFG, reg); ++ if (reg & FMC_GLOBAL_CFG_WP_ENABLE) { ++ reg &= ~FMC_GLOBAL_CFG_WP_ENABLE; ++ hifmc_writel(host, FMC_GLOBAL_CFG, reg); ++ FMC_PR(WE_DBG, "\t||-Set GLOBAL_CFG[%#x]%#x\n", ++ FMC_GLOBAL_CFG, reg); ++ } ++ ++ reg = FMC_CMD_CMD1(SPI_CMD_WREN); ++ hifmc_writel(host, FMC_CMD, reg); ++ FMC_PR(WE_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = OP_CFG_FM_CS(host->cmd_op.cs); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ FMC_PR(WE_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = FMC_OP_CMD1_EN | FMC_OP_REG_OP_START; ++ hifmc_writel(host, FMC_OP, reg); ++ FMC_PR(WE_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ FMC_CMD_WAIT_CPU_FINISH(host); ++ ++#if WE_DBG ++ spi->driver->wait_ready(spi); ++ ++ reg = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, 0); ++ if (reg & STATUS_WEL_MASK) ++ FMC_PR(WE_DBG, "\t||-Write Enable success. reg: %#x\n", reg); ++ else { ++ DB_MSG("Error: Write Enable failed! reg: %#x\n", reg); ++ return reg; ++ } ++#endif ++ ++ FMC_PR(WE_DBG, "\t|*-End Write Enable\n"); ++ return 0; ++} ++ ++/*****************************************************************************/ ++/* ++ judge whether SPI Nand support QUAD read/write or not ++*/ ++static int spi_is_quad(struct hifmc_spi *spi) ++{ ++ const char *if_str[] = {"STD", "DUAL", "DIO", "QUAD", "QIO"}; ++ ++ FMC_PR(QE_DBG, "\t\t|||*-SPI read iftype: %s write iftype: %s\n", ++ if_str[spi->read->iftype], if_str[spi->write->iftype]); ++ ++ if ((IF_TYPE_QUAD == spi->read->iftype) ++ || (IF_TYPE_QIO == spi->read->iftype) ++ || (IF_TYPE_QUAD == spi->write->iftype) ++ || (IF_TYPE_QIO == spi->write->iftype)) ++ return 1; ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++/* ++ Send set features cmd to SPI Nand, feature[B0H]:[0]bit QE would be set ++*/ ++static int spi_general_qe_enable(struct hifmc_spi *spi) ++{ ++ unsigned int reg, op; ++ const char *str[] = {"Disable", "Enable"}; ++ ++ FMC_PR(QE_DBG, "\t||*-Start SPI Nand flash QE\n"); ++ ++ op = spi_is_quad(spi); ++ ++ FMC_PR(QE_DBG, "\t|||*-End Quad check, SPI Nand %s Quad.\n", str[op]); ++ ++ reg = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, 0); ++ FMC_PR(QE_DBG, "\t|||-Get [%#x]feature: %#x\n", FEATURE_ADDR, reg); ++ if ((reg & FEATURE_QE_ENABLE) == op) { ++ FMC_PR(QE_DBG, "\t||*-SPI Nand quad was %sd!\n", str[op]); ++ return op; ++ } ++ ++ if (op == ENABLE) ++ reg |= FEATURE_QE_ENABLE; ++ else ++ reg &= ~FEATURE_QE_ENABLE; ++ ++ spi_nand_feature_op(spi, SET_OP, FEATURE_ADDR, reg); ++ FMC_PR(QE_DBG, "\t|||-SPI Nand %s Quad\n", str[op]); ++ ++ spi->driver->wait_ready(spi); ++ ++ reg = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, 0); ++ if ((reg & FEATURE_QE_ENABLE) == op) ++ FMC_PR(QE_DBG, "\t|||-SPI Nand %s Quad succeed!\n", str[op]); ++ else ++ DB_MSG("Error: %s Quad failed! reg: %#x\n", str[op], reg); ++ ++ FMC_PR(QE_DBG, "\t||*-End SPI Nand %s Quad.\n", str[op]); ++ ++ return op; ++} ++ ++/****************************************************************************/ ++/* some spi nand flash don't QUAD enable */ ++static int spi_do_not_qe_enable(struct hifmc_spi *spi) ++{ ++ return 0; ++} +diff --git a/drivers/mtd/nand/hifmc100/hifmc_spi_nand_ids.c b/drivers/mtd/nand/hifmc100/hifmc_spi_nand_ids.c +new file mode 100644 +index 0000000..de4751f +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100/hifmc_spi_nand_ids.c +@@ -0,0 +1,1335 @@ ++/* ++ * The Flash Memory Controller v100 Device Driver for hisilicon ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <asm/setup.h> ++#include <linux/types.h> ++#include <linux/io.h> ++#include <linux/sched.h> ++#include <linux/printk.h> ++#include <linux/platform_device.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/partitions.h> ++#include <mach/platform.h> ++#include <linux/mfd/hisi_fmc.h> ++ ++#include "../hinfc_gen.h" ++#include "hifmc100.h" ++ ++/*****************************************************************************/ ++SET_READ_STD(1, INFINITE, 24); ++ ++SET_READ_FAST(1, INFINITE, 75); ++SET_READ_FAST(1, INFINITE, 80); ++SET_READ_FAST(1, INFINITE, 104); ++SET_READ_FAST(1, INFINITE, 108); ++SET_READ_FAST(1, INFINITE, 120); ++ ++SET_READ_DUAL(1, INFINITE, 75); ++SET_READ_DUAL(1, INFINITE, 80); ++SET_READ_DUAL(1, INFINITE, 104); ++SET_READ_DUAL(1, INFINITE, 108); ++SET_READ_DUAL(1, INFINITE, 120); ++ ++SET_READ_DUAL_ADDR(1, INFINITE, 75); ++SET_READ_DUAL_ADDR(1, INFINITE, 80); ++SET_READ_DUAL_ADDR(1, INFINITE, 104); ++SET_READ_DUAL_ADDR(1, INFINITE, 108); ++SET_READ_DUAL_ADDR(1, INFINITE, 120); ++ ++SET_READ_QUAD(1, INFINITE, 75); ++SET_READ_QUAD(1, INFINITE, 80); ++SET_READ_QUAD(1, INFINITE, 104); ++SET_READ_QUAD(1, INFINITE, 108); ++SET_READ_QUAD(1, INFINITE, 120); ++ ++SET_READ_QUAD_ADDR(1, INFINITE, 75); ++SET_READ_QUAD_ADDR(2, INFINITE, 75); ++SET_READ_QUAD_ADDR(1, INFINITE, 80); ++SET_READ_QUAD_ADDR(2, INFINITE, 80); ++SET_READ_QUAD_ADDR(2, INFINITE, 104); ++SET_READ_QUAD_ADDR(1, INFINITE, 108); ++SET_READ_QUAD_ADDR(1, INFINITE, 120); ++ ++/*****************************************************************************/ ++SET_WRITE_STD(0, 256, 24); ++SET_WRITE_STD(0, 256, 75); ++SET_WRITE_STD(0, 256, 80); ++SET_WRITE_STD(0, 256, 104); ++ ++SET_WRITE_QUAD(0, 256, 75); ++SET_WRITE_QUAD(0, 256, 80); ++SET_WRITE_QUAD(0, 256, 104); ++SET_WRITE_QUAD(0, 256, 108); ++SET_WRITE_QUAD(0, 256, 120); ++ ++/*****************************************************************************/ ++SET_ERASE_SECTOR_128K(0, _128K, 24); ++SET_ERASE_SECTOR_128K(0, _128K, 75); ++SET_ERASE_SECTOR_128K(0, _128K, 80); ++SET_ERASE_SECTOR_128K(0, _128K, 104); ++ ++SET_ERASE_SECTOR_256K(0, _256K, 24); ++SET_ERASE_SECTOR_256K(0, _256K, 75); ++SET_ERASE_SECTOR_256K(0, _256K, 80); ++SET_ERASE_SECTOR_256K(0, _256K, 104); ++ ++/*****************************************************************************/ ++#include "hifmc100_spi_general.c" ++static struct spi_drv spi_driver_general = { ++ .wait_ready = spi_general_wait_ready, ++ .write_enable = spi_general_write_enable, ++ .qe_enable = spi_general_qe_enable, ++}; ++ ++static struct spi_drv spi_driver_no_qe = { ++ .wait_ready = spi_general_wait_ready, ++ .write_enable = spi_general_write_enable, ++ .qe_enable = spi_do_not_qe_enable, ++}; ++ ++/*****************************************************************************/ ++#define SPI_NAND_ID_TAB_VER "2.4" ++ ++/******* SPI Nand ID Table *************************************************** ++* Version Manufacturer Chip Name Size Operation ++* 1.0 ESMT F50L512M41A 64MB Add 5 chip ++* GD 5F1GQ4UAYIG 128MB ++* GD 5F2GQ4UAYIG 256MB ++* GD 5F4GQ4UAYIG 512MB ++* GD 5F4GQ4UBYIG 512MB ++* 1.1 ESMT F50L1G41A 128MB Add 2 chip ++* Winbond W25N01GV 128MB ++* 1.2 GD 5F1GQ4UBYIG 128MB Add 2 chip ++* GD 5F2GQ4UBYIG 256MB ++* 1.3 ATO ATO25D1GA 128MB Add 1 chip ++* 1.4 MXIC MX35LF1GE4AB 128MB Add 2 chip ++* MXIC MX35LF2GE4AB 256MB (SOP-16Pin) ++* 1.5 Paragon PN26G01A 128MB Add 1 chip ++* 1.6 All-flash AFS1GQ4UAC 128MB Add 1 chip ++* 1.7 TOSHIBA TC58CVG0S3H 128MB Add 2 chip ++* TOSHIBA TC58CVG2S0H 512MB ++* 1.8 ALL-flash AFS2GQ4UAD 256MB Add 2 chip ++* Paragon PN26G02A 256MB ++* 1.9 TOSHIBA TC58CVG1S3H 256MB Add 1 chip ++* 2.0 HeYangTek HYF1GQ4UAACAE 128MB Add 3 chip ++* HeYangTek HYF2GQ4UAACAE 256MB ++* HeYangTek HYF4GQ4UAACBE 512MB ++* 2.1 Micron MT29F1G01ABA 128MB Add 5 chip ++ Paragon 1.8V PN26Q01AWSIUG 128MB ++ TOSHIBA 1.8V TC58CYG0S3H 128MB ++ TOSHIBA 1.8V TC58CYG1S3H 256MB ++ TOSHIBA 1.8V TC58CYG2S0H 512MB ++* 2.2 Micron MT29F2G01ABA 256MB Add 1 chip ++* 2.3 MXIC MX35LF2G14AC 256MB Add 1 chip ++* 2.4 GD 1.8V 5F4GQ4RAYIG 512MB Add 1 chip ++******************************************************************************/ ++struct spi_nand_info hifmc_spi_nand_flash_table[] = { ++ /* Micron MT29F1G01ABA 1GBit */ ++ { ++ .name = "MT29F1G01ABA", ++ .id = {0x2C, 0x14}, ++ .id_len = 2, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 80), ++ &READ_DUAL(1, INFINITE, 80), ++ &READ_DUAL_ADDR(1, INFINITE, 80), ++ &READ_QUAD(1, INFINITE, 80), ++ &READ_QUAD_ADDR(2, INFINITE, 80), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 80), ++ &WRITE_QUAD(0, 256, 80), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 80), ++ 0 ++ }, ++ .driver = &spi_driver_no_qe, ++ }, ++ ++ /* Micron MT29F2G01ABA 2GBit */ ++ { ++ .name = "MT29F2G01ABA", ++ .id = {0x2C, 0x24}, ++ .id_len = 2, ++ .chipsize = _256M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 108), ++ &READ_DUAL(1, INFINITE, 108), ++ &READ_DUAL_ADDR(1, INFINITE, 108), ++ &READ_QUAD(1, INFINITE, 108), ++ &READ_QUAD_ADDR(2, INFINITE, 104), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 80), ++ &WRITE_QUAD(0, 256, 108), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 80), ++ 0 ++ }, ++ .driver = &spi_driver_no_qe, ++ }, ++ ++ /* ESMT F50L512M41A 512Mbit */ ++ { ++ .name = "F50L512M41A", ++ .id = {0xC8, 0x20}, ++ .id_len = 2, ++ .chipsize = _64M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 64, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 104), ++ &READ_DUAL(1, INFINITE, 104), ++ &READ_QUAD(1, INFINITE, 104), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 104), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_no_qe, ++ }, ++ ++ /* ESMT F50L1G41A 1Gbit */ ++ { ++ .name = "F50L1G41A", ++ .id = {0xC8, 0x21}, ++ .id_len = 2, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 64, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 104), ++ &READ_DUAL(1, INFINITE, 104), ++ &READ_QUAD(1, INFINITE, 104), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 104), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_no_qe, ++ }, ++ ++ /* GD 5F1GQ4UAYIG 1Gbit */ ++ { ++ .name = "5F1GQ4UAYIG", ++ .id = {0xc8, 0xf1}, ++ .id_len = 2, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 64, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 120), ++ &READ_DUAL(1, INFINITE, 120), ++ &READ_DUAL_ADDR(1, INFINITE, 120), ++ &READ_QUAD(1, INFINITE, 120), ++ &READ_QUAD_ADDR(1, INFINITE, 120), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 120), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* GD 5F1GQ4UBYIG 1Gbit */ ++ { ++ .name = "5F1GQ4UBYIG", ++ .id = {0xc8, 0xd1}, ++ .id_len = 2, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 120), ++ &READ_DUAL(1, INFINITE, 120), ++ &READ_DUAL_ADDR(1, INFINITE, 120), ++ &READ_QUAD(1, INFINITE, 120), ++ &READ_QUAD_ADDR(1, INFINITE, 120), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 120), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* GD 5F2GQ4UAYIG 2Gbit */ ++ { ++ .name = "5F2GQ4UAYIG", ++ .id = {0xc8, 0xf2}, ++ .id_len = 2, ++ .chipsize = _256M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 64, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 120), ++ &READ_DUAL(1, INFINITE, 120), ++ &READ_DUAL_ADDR(1, INFINITE, 120), ++ &READ_QUAD(1, INFINITE, 120), ++ &READ_QUAD_ADDR(1, INFINITE, 120), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 120), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* GD 5F2GQ4UBYIG 2Gbit */ ++ { ++ .name = "5F2GQ4UBYIG", ++ .id = {0xc8, 0xd2}, ++ .id_len = 2, ++ .chipsize = _256M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 120), ++ &READ_DUAL(1, INFINITE, 120), ++ &READ_DUAL_ADDR(1, INFINITE, 120), ++ &READ_QUAD(1, INFINITE, 120), ++ &READ_QUAD_ADDR(1, INFINITE, 120), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 120), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* GD 5F4GQ4UAYIG 4Gbit */ ++ { ++ .name = "5F4GQ4UAYIG", ++ .id = {0xc8, 0xf4}, ++ .id_len = 2, ++ .chipsize = _512M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 64, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 120), ++ &READ_DUAL(1, INFINITE, 120), ++ &READ_DUAL_ADDR(1, INFINITE, 120), ++ &READ_QUAD(1, INFINITE, 120), ++ &READ_QUAD_ADDR(1, INFINITE, 120), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 120), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* GD 5F4GQ4UBYIG 4Gbit */ ++ { ++ .name = "5F4GQ4UBYIG", ++ .id = {0xc8, 0xd4}, ++ .id_len = 2, ++ .chipsize = _512M, ++ .erasesize = _256K, ++ .pagesize = _4K, ++ .oobsize = 256, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 120), ++ &READ_DUAL(1, INFINITE, 120), ++ &READ_DUAL_ADDR(1, INFINITE, 120), ++ &READ_QUAD(1, INFINITE, 120), ++ &READ_QUAD_ADDR(1, INFINITE, 120), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 120), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_256K(0, _256K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* GD 1.8V 5F4GQ4RAYIG 4Gbit */ ++ { ++ .name = "5F4GQ4RAYIG", ++ .id = {0xc8, 0xe4}, ++ .id_len = 2, ++ .chipsize = _512M, ++ .erasesize = _256K, ++ .pagesize = _4K, ++ .oobsize = 256, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 75), ++ &READ_DUAL(1, INFINITE, 75), ++ &READ_DUAL_ADDR(1, INFINITE, 75), ++ &READ_QUAD(1, INFINITE, 75), ++ &READ_QUAD_ADDR(1, INFINITE, 75), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 75), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_256K(0, _256K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* Winbond W25N01GV 1Gbit */ ++ { ++ .name = "W25N01GV", ++ .id = {0xef, 0xaa, 0x21}, ++ .id_len = 3, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 64, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 104), ++ &READ_DUAL(1, INFINITE, 104), ++ &READ_DUAL_ADDR(1, INFINITE, 104), ++ &READ_QUAD(1, INFINITE, 104), ++ &READ_QUAD_ADDR(2, INFINITE, 104), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 104), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_no_qe, ++ }, ++ ++ /* Winbond W25N01GW 1Gbit 1.8V */ ++ { ++ .name = "W25N01GW", ++ .id = {0xef, 0xba, 0x21}, ++ .id_len = 3, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 64, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 75), ++ &READ_DUAL(1, INFINITE, 75), ++ &READ_DUAL_ADDR(1, INFINITE, 75), ++ &READ_QUAD(1, INFINITE, 75), ++ &READ_QUAD_ADDR(2, INFINITE, 75), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 75), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_no_qe, ++ }, ++ ++ /* ATO ATO25D1GA 1Gbit */ ++ { ++ .name = "ATO25D1GA", ++ .id = {0x9b, 0x12}, ++ .id_len = 2, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 64, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 104), ++ &READ_QUAD(1, INFINITE, 104), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 104), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* MXIC MX35LF1GE4AB 1Gbit */ ++ { ++ .name = "MX35LF1GE4AB", ++ .id = {0xc2, 0x12}, ++ .id_len = 2, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 64, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 104), ++ &READ_QUAD(1, INFINITE, 104), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 104), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* MXIC MX35LF2GE4AB 2Gbit SOP-16Pin */ ++ { ++ .name = "MX35LF2GE4AB", ++ .id = {0xc2, 0x22}, ++ .id_len = 2, ++ .chipsize = _256M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 64, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 104), ++ &READ_QUAD(1, INFINITE, 104), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 104), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* MXIC MX35LF2G14AC 2GBit */ ++ { ++ .name = "MX35LF2G14AC", ++ .id = {0xc2, 0x20}, ++ .id_len = 2, ++ .chipsize = _256M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 64, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 104), ++ &READ_QUAD(1, INFINITE, 104), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 104), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* Paragon PN26G01AWSIUG 1Gbit 1.8V */ ++ { ++ .name = "PN26G01AW", ++ .id = {0xa1, 0xc1}, ++ .id_len = 2, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 75), ++ &READ_DUAL(1, INFINITE, 75), ++ &READ_DUAL_ADDR(1, INFINITE, 75), ++ &READ_QUAD(1, INFINITE, 75), ++ &READ_QUAD_ADDR(1, INFINITE, 75), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 75), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 75), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* Paragon PN26G01A 1Gbit */ ++ { ++ .name = "PN26G01A", ++ .id = {0xa1, 0xe1}, ++ .id_len = 2, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 108), ++ &READ_DUAL(1, INFINITE, 108), ++ &READ_DUAL_ADDR(1, INFINITE, 108), ++ &READ_QUAD(1, INFINITE, 108), ++ &READ_QUAD_ADDR(1, INFINITE, 108), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 108), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* Paragon PN26G02A 2Gbit */ ++ { ++ .name = "PN26G02A", ++ .id = {0xa1, 0xe2}, ++ .id_len = 2, ++ .chipsize = _256M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 108), ++ &READ_DUAL(1, INFINITE, 108), ++ &READ_DUAL_ADDR(1, INFINITE, 108), ++ &READ_QUAD(1, INFINITE, 108), ++ &READ_QUAD_ADDR(1, INFINITE, 108), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 108), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* All-flash AFS1GQ4UAC 1Gbit */ ++ { ++ .name = "AFS1GQ4UAC", ++ .id = {0xc1, 0x51}, ++ .id_len = 2, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 80), ++ &READ_DUAL(1, INFINITE, 80), ++ &READ_DUAL_ADDR(1, INFINITE, 80), ++ &READ_QUAD(1, INFINITE, 80), ++ &READ_QUAD_ADDR(1, INFINITE, 80), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 80), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* All-flash AFS2GQ4UAD 2Gbit */ ++ { ++ .name = "AFS2GQ4UAD", ++ .id = {0xc1, 0x52}, ++ .id_len = 2, ++ .chipsize = _256M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 80), ++ &READ_DUAL(1, INFINITE, 80), ++ &READ_DUAL_ADDR(1, INFINITE, 80), ++ &READ_QUAD(1, INFINITE, 80), ++ &READ_QUAD_ADDR(1, INFINITE, 80), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 24), ++ &WRITE_QUAD(0, 256, 80), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 24), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* TOSHIBA TC58CVG0S3H 1Gbit */ ++ { ++ .name = "TC58CVG0S3H", ++ .id = {0x98, 0xc2}, ++ .id_len = 2, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 104), ++ &READ_DUAL(1, INFINITE, 104), ++ &READ_QUAD(1, INFINITE, 104), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 104), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 104), ++ 0 ++ }, ++ .driver = &spi_driver_no_qe, ++ }, ++ ++ /* TOSHIBA TC58CYG0S3H 1.8V 1Gbit */ ++ { ++ .name = "TC58CYG0S3H", ++ .id = {0x98, 0xb2}, ++ .id_len = 2, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 75), ++ &READ_DUAL(1, INFINITE, 75), ++ &READ_QUAD(1, INFINITE, 75), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 75), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 75), ++ 0 ++ }, ++ .driver = &spi_driver_no_qe, ++ }, ++ ++ /* TOSHIBA TC58CVG1S3H 2Gbit */ ++ { ++ .name = "TC58CVG1S3H", ++ .id = {0x98, 0xcb}, ++ .id_len = 2, ++ .chipsize = _256M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 104), ++ &READ_DUAL(1, INFINITE, 104), ++ &READ_QUAD(1, INFINITE, 104), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 104), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 104), ++ 0 ++ }, ++ .driver = &spi_driver_no_qe, ++ }, ++ ++ /* TOSHIBA TC58CYG1S3H 1.8V 2Gbit */ ++ { ++ .name = "TC58CYG1S3H", ++ .id = {0x98, 0xbb}, ++ .id_len = 2, ++ .chipsize = _256M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 75), ++ &READ_DUAL(1, INFINITE, 75), ++ &READ_QUAD(1, INFINITE, 75), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 75), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 75), ++ 0 ++ }, ++ .driver = &spi_driver_no_qe, ++ }, ++ ++ /* TOSHIBA TC58CVG2S0H 4Gbit */ ++ { ++ .name = "TC58CVG2S0H", ++ .id = {0x98, 0xcd}, ++ .id_len = 2, ++ .chipsize = _512M, ++ .erasesize = _256K, ++ .pagesize = _4K, ++ .oobsize = 256, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 104), ++ &READ_DUAL(1, INFINITE, 104), ++ &READ_QUAD(1, INFINITE, 104), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 104), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_256K(0, _256K, 104), ++ 0 ++ }, ++ .driver = &spi_driver_no_qe, ++ }, ++ ++ /* TOSHIBA TC58CYG2S0H 1.8V 4Gbit */ ++ { ++ .name = "TC58CYG2S0H", ++ .id = {0x98, 0xbd}, ++ .id_len = 2, ++ .chipsize = _512M, ++ .erasesize = _256K, ++ .pagesize = _4K, ++ .oobsize = 256, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 75), ++ &READ_DUAL(1, INFINITE, 75), ++ &READ_QUAD(1, INFINITE, 75), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 75), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_256K(0, _256K, 75), ++ 0 ++ }, ++ .driver = &spi_driver_no_qe, ++ }, ++ ++ /* HeYangTek HYF1GQ4UAACAE 1Gbit */ ++ { ++ .name = "HYF1GQ4UAACAE", ++ .id = {0xc9, 0x51}, ++ .id_len = 2, ++ .chipsize = _128M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 80), ++ &READ_DUAL(1, INFINITE, 80), ++ &READ_DUAL_ADDR(1, INFINITE, 80), ++ &READ_QUAD(1, INFINITE, 80), ++ &READ_QUAD_ADDR(1, INFINITE, 80), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 80), ++ &WRITE_QUAD(0, 256, 80), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 80), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* HeYangTek HYF2GQ4UAACAE 2Gbit */ ++ { ++ .name = "HYF2GQ4UAACAE", ++ .id = {0xc9, 0x52}, ++ .id_len = 2, ++ .chipsize = _256M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = 128, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 80), ++ &READ_DUAL(1, INFINITE, 80), ++ &READ_DUAL_ADDR(1, INFINITE, 80), ++ &READ_QUAD(1, INFINITE, 80), ++ &READ_QUAD_ADDR(1, INFINITE, 80), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 80), ++ &WRITE_QUAD(0, 256, 80), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_128K(0, _128K, 80), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ /* HeYangTek HYF4GQ4UAACBE 4Gbit */ ++ { ++ .name = "HYF4GQ4UAACBE", ++ .id = {0xc9, 0xd4}, ++ .id_len = 2, ++ .chipsize = _512M, ++ .erasesize = _256K, ++ .pagesize = _4K, ++ .oobsize = 256, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &READ_STD(1, INFINITE, 24), ++ &READ_FAST(1, INFINITE, 80), ++ &READ_DUAL(1, INFINITE, 80), ++ &READ_DUAL_ADDR(1, INFINITE, 80), ++ &READ_QUAD(1, INFINITE, 80), ++ &READ_QUAD_ADDR(1, INFINITE, 80), ++ 0 ++ }, ++ .write = { ++ &WRITE_STD(0, 256, 80), ++ &WRITE_QUAD(0, 256, 80), ++ 0 ++ }, ++ .erase = { ++ &ERASE_SECTOR_256K(0, _256K, 80), ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ { .id_len = 0, }, ++}; ++ ++/*****************************************************************************/ ++static void hifmc100_spi_nand_search_rw(struct spi_nand_info *spiinfo, ++ struct spi_op *spiop_rw, u_int iftype, u_int max_dummy, int rw_type) ++{ ++ int ix = 0; ++ struct spi_op **spiop, **fitspiop; ++ ++ for (fitspiop = spiop = (rw_type ? spiinfo->write : spiinfo->read); ++ (*spiop) && ix < MAX_SPI_OP; spiop++, ix++) { ++ if (((*spiop)->iftype & iftype) ++ && ((*spiop)->dummy <= max_dummy) ++ && (*fitspiop)->iftype < (*spiop)->iftype) ++ fitspiop = spiop; ++ } ++ memcpy(spiop_rw, (*fitspiop), sizeof(struct spi_op)); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_spi_nand_get_erase(struct spi_nand_info *spiinfo, ++ struct spi_op *spiop_erase) ++{ ++ int ix; ++ ++ spiop_erase->size = 0; ++ for (ix = 0; ix < MAX_SPI_OP; ix++) { ++ if (spiinfo->erase[ix] == NULL) ++ break; ++ if (spiinfo->erasesize == spiinfo->erase[ix]->size) { ++ memcpy(&spiop_erase[ix], spiinfo->erase[ix], ++ sizeof(struct spi_op)); ++ break; ++ } ++ } ++} ++ ++/*****************************************************************************/ ++static void hifmc100_map_spi_op(struct hifmc_spi *spi) ++{ ++ unsigned char ix; ++ const int iftype_read[] = { ++ SPI_IF_READ_STD, IF_TYPE_STD, ++ SPI_IF_READ_FAST, IF_TYPE_STD, ++ SPI_IF_READ_DUAL, IF_TYPE_DUAL, ++ SPI_IF_READ_DUAL_ADDR, IF_TYPE_DIO, ++ SPI_IF_READ_QUAD, IF_TYPE_QUAD, ++ SPI_IF_READ_QUAD_ADDR, IF_TYPE_QIO, ++ 0, 0, ++ }; ++ const int iftype_write[] = { ++ SPI_IF_WRITE_STD, IF_TYPE_STD, ++ SPI_IF_WRITE_QUAD, IF_TYPE_QUAD, ++ 0, 0, ++ }; ++ const char *if_str[] = {"STD", "DUAL", "DIO", "QUAD", "QIO"}; ++ ++ FMC_PR(BT_DBG, "\t||*-Start Get SPI operation iftype\n"); ++ ++ for (ix = 0; iftype_write[ix]; ix += 2) { ++ if (spi->write->iftype == iftype_write[ix]) { ++ spi->write->iftype = iftype_write[ix + 1]; ++ break; ++ } ++ } ++ FMC_PR(BT_DBG, "\t|||-Get best write iftype: %s \n", ++ if_str[spi->write->iftype]); ++ ++ for (ix = 0; iftype_read[ix]; ix += 2) { ++ if (spi->read->iftype == iftype_read[ix]) { ++ spi->read->iftype = iftype_read[ix + 1]; ++ break; ++ } ++ } ++ FMC_PR(BT_DBG, "\t|||-Get best read iftype: %s \n", ++ if_str[spi->read->iftype]); ++ ++ spi->erase->iftype = IF_TYPE_STD; ++ FMC_PR(BT_DBG, "\t|||-Get best erase iftype: %s \n", ++ if_str[spi->erase->iftype]); ++ ++ FMC_PR(BT_DBG, "\t||*-End Get SPI operation iftype \n"); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_spi_ids_probe(struct mtd_info *mtd, ++ struct spi_nand_info *spi_dev) ++{ ++ unsigned int reg; ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ struct hifmc_spi *spi = host->spi; ++ ++ FMC_PR(BT_DBG, "\t|*-Start match SPI operation & chip init\n"); ++ ++ spi->host = host; ++ spi->name = spi_dev->name; ++ spi->driver = spi_dev->driver; ++ ++ hifmc100_spi_nand_search_rw(spi_dev, spi->read, ++ HIFMC_SPI_NAND_SUPPORT_READ, ++ HIFMC_SPI_NAND_SUPPORT_MAX_DUMMY, RW_OP_READ); ++ FMC_PR(BT_DBG, "\t||-Save spi->read op cmd:%#x\n", spi->read->cmd); ++ ++ hifmc100_spi_nand_search_rw(spi_dev, spi->write, ++ HIFMC_SPI_NAND_SUPPORT_WRITE, ++ HIFMC_SPI_NAND_SUPPORT_MAX_DUMMY, RW_OP_WRITE); ++ FMC_PR(BT_DBG, "\t||-Save spi->write op cmd:%#x\n", spi->write->cmd); ++ ++ hifmc100_spi_nand_get_erase(spi_dev, spi->erase); ++ FMC_PR(BT_DBG, "\t||-Save spi->erase op cmd:%#x\n", spi->erase->cmd); ++ ++ hifmc100_map_spi_op(spi); ++ ++ spi->driver->qe_enable(spi); ++ ++ /* Disable write protection */ ++ reg = spi_nand_feature_op(spi, GET_OP, PROTECT_ADDR, 0); ++ FMC_PR(BT_DBG, "\t||-Get protect status[%#x]: %#x\n", PROTECT_ADDR, ++ reg); ++ if (ANY_BP_ENABLE(reg)) { ++ reg &= ~ALL_BP_MASK; ++ spi_nand_feature_op(spi, SET_OP, PROTECT_ADDR, reg); ++ FMC_PR(BT_DBG, "\t||-Set [%#x]FT %#x\n", PROTECT_ADDR, reg); ++ ++ spi->driver->wait_ready(spi); ++ ++ reg = spi_nand_feature_op(spi, GET_OP, PROTECT_ADDR, 0); ++ FMC_PR(BT_DBG, "\t||-Check BP disable result: %#x\n", reg); ++ if (ANY_BP_ENABLE(reg)) ++ DB_MSG("Error: Write protection disable failed!\n"); ++ } ++ ++ /* Disable chip internal ECC */ ++ reg = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, 0); ++ FMC_PR(BT_DBG, "\t||-Get feature status[%#x]: %#x\n", FEATURE_ADDR, ++ reg); ++ if (reg & FEATURE_ECC_ENABLE) { ++ reg &= ~FEATURE_ECC_ENABLE; ++ spi_nand_feature_op(spi, SET_OP, FEATURE_ADDR, reg); ++ FMC_PR(BT_DBG, "\t||-Set [%#x]FT: %#x\n", FEATURE_ADDR, reg); ++ ++ spi->driver->wait_ready(spi); ++ ++ reg = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, 0); ++ FMC_PR(BT_DBG, "\t||-Check internal ECC disable result: %#x\n", ++ reg); ++ if (reg & FEATURE_ECC_ENABLE) ++ DB_MSG("Error: Chip internal ECC disable failed!\n"); ++ } ++ ++ hifmc_cs_user[host->cmd_op.cs]++; ++ ++ FMC_PR(BT_DBG, "\t|*-End match SPI operation & chip init\n"); ++} ++ ++static struct nand_flash_dev spi_nand_dev; ++/*****************************************************************************/ ++static struct nand_flash_dev *spi_nand_get_flash_info(struct mtd_info *mtd, ++ unsigned char *id) ++{ ++ unsigned char ix, len = 0; ++ char buffer[100]; ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ struct spi_nand_info *spi_dev = hifmc_spi_nand_flash_table; ++ struct nand_flash_dev *type = &spi_nand_dev; ++ ++ FMC_PR(BT_DBG, "\t*-Start find SPI Nand flash\n"); ++ ++ len = sprintf(buffer, "SPI Nand(cs %d) ID: %#x %#x", ++ host->cmd_op.cs, id[0], id[1]); ++ ++ for (; spi_dev->id_len; spi_dev++) { ++ if (memcmp(id, spi_dev->id, spi_dev->id_len)) ++ continue; ++ ++ for (ix = 2; ix < spi_dev->id_len; ix++) ++ len += sprintf(buffer + len, " %#x", id[ix]); ++ pr_info("%s\n", buffer); ++ ++ FMC_PR(BT_DBG, "\t||-CS(%d) found SPI Nand: %s\n", ++ host->cmd_op.cs, spi_dev->name); ++ ++ type->name = spi_dev->name; ++ memcpy(type->id, spi_dev->id, spi_dev->id_len); ++ type->pagesize = spi_dev->pagesize; ++ type->chipsize = spi_dev->chipsize >> 20; ++ type->erasesize = spi_dev->erasesize; ++ type->id_len = spi_dev->id_len; ++ type->oobsize = spi_dev->oobsize; ++ FMC_PR(BT_DBG, "\t|-Save struct nand_flash_dev info\n"); ++ ++ mtd->size = spi_dev->chipsize; ++ ++ hifmc100_spi_ids_probe(mtd, spi_dev); ++ ++ FMC_PR(BT_DBG, "\t*-Found SPI nand: %s\n", spi_dev->name); ++ ++ return type; ++ } ++ ++ FMC_PR(BT_DBG, "\t*-Not found SPI nand flash, %s\n", buffer); ++ ++ return NULL; ++} ++ ++/*****************************************************************************/ ++void hifmc_spi_nand_ids_register(void) ++{ ++ pr_info("SPI Nand ID Table Version %s\n", SPI_NAND_ID_TAB_VER); ++ get_spi_nand_flash_type_hook = spi_nand_get_flash_info; ++} ++ ++#ifdef CONFIG_PM ++/*****************************************************************************/ ++void hifmc100_spi_nand_config(struct hifmc_host *host) ++{ ++ unsigned int reg; ++ struct hifmc_spi *spi = host->spi; ++ static const char const *str[] = {"STD", "DUAL", "DIO", "QUAD", "QIO"}; ++ ++ /* judge whether support QUAD read/write or not, set it if yes */ ++ FMC_PR(PM_DBG, "\t|-SPI read iftype: %s write iftype: %s\n", ++ str[spi->read->iftype], str[spi->write->iftype]); ++ spi->driver->qe_enable(spi); ++ ++ /* Disable write protection */ ++ reg = spi_nand_feature_op(spi, GET_OP, PROTECT_ADDR, 0); ++ FMC_PR(PM_DBG, "\t|-Get protect status[%#x]: %#x\n", PROTECT_ADDR, ++ reg); ++ if (ANY_BP_ENABLE(reg)) { ++ reg &= ~ALL_BP_MASK; ++ spi_nand_feature_op(spi, SET_OP, PROTECT_ADDR, reg); ++ FMC_PR(PM_DBG, "\t|-Set [%#x]FT %#x\n", PROTECT_ADDR, reg); ++ ++ spi->driver->wait_ready(spi); ++ ++ reg = spi_nand_feature_op(spi, GET_OP, PROTECT_ADDR, 0); ++ FMC_PR(PM_DBG, "\t|-Check BP disable result: %#x\n", reg); ++ if (ANY_BP_ENABLE(reg)) ++ DB_MSG("Error: Write protection disable failed!\n"); ++ } ++ ++ /* Disable chip internal ECC */ ++ reg = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, 0); ++ FMC_PR(PM_DBG, "\t|-Get feature status[%#x]: %#x\n", FEATURE_ADDR, ++ reg); ++ if (reg & FEATURE_ECC_ENABLE) { ++ reg &= ~FEATURE_ECC_ENABLE; ++ spi_nand_feature_op(spi, SET_OP, FEATURE_ADDR, reg); ++ FMC_PR(PM_DBG, "\t|-Set [%#x]FT: %#x\n", FEATURE_ADDR, reg); ++ ++ spi->driver->wait_ready(spi); ++ ++ reg = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, 0); ++ FMC_PR(PM_DBG, "\t|-Check internal ECC disable result: %#x\n", ++ reg); ++ if (reg & FEATURE_ECC_ENABLE) ++ DB_MSG("Error: Chip internal ECC disable failed!\n"); ++ } ++} ++/*****************************************************************************/ ++#endif /* CONFIG_PM */ +diff --git a/drivers/mtd/nand/hifmc100_nand/Kconfig b/drivers/mtd/nand/hifmc100_nand/Kconfig +new file mode 100644 +index 0000000..976db6a +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100_nand/Kconfig +@@ -0,0 +1,104 @@ ++# ++# drivers/mtd/nand/hifmc100_nand/Kconfig ++# add by hisilicon 2015.5.7 ++# ++ ++menuconfig HIFMC100_NAND ++ tristate "Hisilicon Flash Memory Controller v100 Nand device Support" ++ depends on HIFMC ++ depends on MTD_NAND ++ default n if ARCH_HI3519 ++ default n if ARCH_HI3519V101 ++ default n if ARCH_HI3559 ++ default n if ARCH_HI3556 ++ select MISC_FILESYSTEMS ++ select MTD_BLOCK ++ help ++ Hisilicon Flash Memory Controller device version 100 driver Support ++ Hisilicon Flash Memory Controller version 100 is called hifmc100 for ++ short. The controller support DMA transfers while reading or writing ++ the Nand flash. ++ ++if HIFMC100_NAND ++ ++config HIFMC100_MAX_NAND_CHIP ++ int "number of Nand flash chip (1, 2)" ++ default 1 if ARCH_HI3519 ++ default 1 if ARCH_HI3519V101 ++ default 1 if ARCH_HI3559 ++ default 1 if ARCH_HI3556 ++ help ++ flash memory controller v100 device only support 1 or 2 Nand flash ++ chip, your should not config other value. ++ ++config HIFMC100_NAND_EDO_MODE ++ bool "the Extended Data Out(EDO) mode" ++ help ++ In Extended data out (EDO), a new data cycle is started while the data ++ output of the previous cycle is still active. This process of cycle ++ overlapping, called pipelining, increases processing speed by about ++ 10 nanoseconds per cycle,increasing computer performance by about 5 ++ percent compared to performance using FMP. ++ ++config RW_H_WIDTH ++ int "the width of Read/Write HIGH Hold Time (0 to 15)" ++ range 0 15 ++ default 10 if ARCH_HI3519 ++ default 10 if ARCH_HI3519V101 ++ default 10 if ARCH_HI3559 ++ default 10 if ARCH_HI3556 ++ help ++ the Read/Write HIGH Hold Time of nand flash ++ ++config R_L_WIDTH ++ int "the Read pulse width (0 to 15)" ++ range 0 15 ++ default 10 if ARCH_HI3519 ++ default 10 if ARCH_HI3519V101 ++ default 10 if ARCH_HI3559 ++ default 10 if ARCH_HI3556 ++ help ++ the Read/Write LOW Hold Time of nand flash ++ ++config W_L_WIDTH ++ int "the Write pulse width (0 to 15)" ++ range 0 15 ++ default 10 if ARCH_HI3519 ++ default 10 if ARCH_HI3519V101 ++ default 10 if ARCH_HI3559 ++ default 10 if ARCH_HI3556 ++ help ++ the Read/Write LOW Hold Time of nand flash ++ ++choice ++ prompt "Page Size and Ecc Type Select" ++ default HIFMC100_NAND_AUTO_PAGESIZE_ECC if ARCH_HI3519 ++ default HIFMC100_NAND_AUTO_PAGESIZE_ECC if ARCH_HI3519V101 ++ default HIFMC100_NAND_AUTO_PAGESIZE_ECC if ARCH_HI3559 ++ default HIFMC100_NAND_AUTO_PAGESIZE_ECC if ARCH_HI3556 ++ ++config HIFMC100_NAND_HARDWARE_PAGESIZE_ECC ++ bool "Hardware" ++ help ++ the configure of page size and ecc type lie on switch on the board. ++ so the page size and ecc type is controlled by Hardware see demo ++ board of SOC. ++ ++config HIFMC100_NAND_AUTO_PAGESIZE_ECC ++ bool "Auto" ++ help ++ auto-sensed the page size and ecc type value. driver will try each of ++ page size and ecc type one by one till flash can be read and wrote ++ accurately. so the page size and ecc type is match adaptively without ++ switch on the board ++ ++config HIFMC100_NAND_PAGESIZE_AUTO_ECC_NONE ++ bool "Pagesize Auto, Ecc None" ++ help ++ auto-sensed the page size and select ecc none. driver will try each ++ of page size one by one till flash can be read and wrote accurately. ++ so the page size is match adaptively without switch on the board ++ ++endchoice ++ ++endif # End of HIFMC100_NAND +diff --git a/drivers/mtd/nand/hifmc100_nand/Makefile b/drivers/mtd/nand/hifmc100_nand/Makefile +new file mode 100644 +index 0000000..b046e8d +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100_nand/Makefile +@@ -0,0 +1,26 @@ ++# ++# The Flash Memory Controller v100 Device Driver for hisilicon ++# ++# Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++# ++# This program is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by the ++# Free Software Foundation; either version 2 of the License, or (at your ++# option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see <http://www.gnu.org/licenses/>. ++# ++# ++ ++# ++# drivers/mtd/nand/hifmc100_nand/Makefile ++# ++ ++obj-$(CONFIG_HIFMC_NAND) += hifmc_nand_spl_ids.o ++obj-$(CONFIG_HIFMC_NAND) += hifmc100_nand.o hifmc100_nand_os.o +diff --git a/drivers/mtd/nand/hifmc100_nand/hifmc100_nand.c b/drivers/mtd/nand/hifmc100_nand/hifmc100_nand.c +new file mode 100644 +index 0000000..d4fa8fe +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100_nand/hifmc100_nand.c +@@ -0,0 +1,1102 @@ ++/* ++ * The Flash Memory Controller v100 Device Driver for hisilicon ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/io.h> ++#include <linux/compiler.h> ++#include <linux/delay.h> ++#include <linux/printk.h> ++#include <linux/clk.h> ++#include <linux/mfd/hisi_fmc.h> ++ ++#include "../hinfc_gen.h" ++#include "hifmc100_nand_os.h" ++#include "hifmc100_nand.h" ++ ++#include <mach/platform.h> ++/*****************************************************************************/ ++static void hifmc100_dma_transfer(struct hifmc_host *host, int todev) ++{ ++ unsigned int reg = (unsigned int)host->dma_buffer; ++ char *op = todev ? "write" : "read"; ++ ++ FMC_PR(DMA_DB, "\t\t *-Start %s page dma transfer\n", op); ++ ++ hifmc_writel(host, FMC_DMA_SADDR_D0, reg); ++ FMC_PR(DMA_DB, "\t\t |-Set ADDR0[%#x]%#x\n", FMC_DMA_SADDR_D0, reg); ++ ++ reg += FMC_DMA_ADDR_OFFSET; ++ hifmc_writel(host, FMC_DMA_SADDR_D1, reg); ++ FMC_PR(DMA_DB, "\t\t |-Set ADDR1[%#x]%#x\n", FMC_DMA_SADDR_D1, reg); ++ ++ reg += FMC_DMA_ADDR_OFFSET; ++ hifmc_writel(host, FMC_DMA_SADDR_D2, reg); ++ FMC_PR(DMA_DB, "\t\t |-Set ADDR2[%#x]%#x\n", FMC_DMA_SADDR_D2, reg); ++ ++ reg += FMC_DMA_ADDR_OFFSET; ++ hifmc_writel(host, FMC_DMA_SADDR_D3, reg); ++ FMC_PR(DMA_DB, "\t\t |-Set ADDR3[%#x]%#x\n", FMC_DMA_SADDR_D3, reg); ++ ++ reg = host->dma_oob; ++ hifmc_writel(host, FMC_DMA_SADDR_OOB, reg); ++ FMC_PR(DMA_DB, "\t\t |-Set OOB[%#x]%#x\n", FMC_DMA_SADDR_OOB, reg); ++ ++ if (host->ecctype == NAND_ECC_0BIT) { ++ hifmc_writel(host, FMC_DMA_LEN, FMC_DMA_LEN_SET(host->oobsize)); ++ FMC_PR(DMA_DB, "\t\t |-Set LEN[%#x]%#x\n", FMC_DMA_LEN, reg); ++ } ++ reg = FMC_OP_READ_DATA_EN | FMC_OP_WRITE_DATA_EN; ++ hifmc_writel(host, FMC_OP, reg); ++ FMC_PR(DMA_DB, "\t\t |-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ reg = FMC_DMA_AHB_CTRL_DMA_PP_EN ++ | FMC_DMA_AHB_CTRL_BURST16_EN ++ | FMC_DMA_AHB_CTRL_BURST8_EN ++ | FMC_DMA_AHB_CTRL_BURST4_EN; ++ hifmc_writel(host, FMC_DMA_AHB_CTRL, reg); ++ FMC_PR(DMA_DB, "\t\t |-Set AHBCTRL[%#x]%#x\n", FMC_DMA_AHB_CTRL, reg); ++ ++ reg = OP_CFG_FM_CS(host->cmd_op.cs) ++ | OP_CFG_ADDR_NUM(host->addr_cycle); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ FMC_PR(DMA_DB, "\t\t |-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = OP_CTRL_DMA_OP_READY; ++ if (todev) ++ reg |= OP_CTRL_RW_OP(todev); ++ hifmc_writel(host, FMC_OP_CTRL, reg); ++ FMC_PR(DMA_DB, "\t\t |-Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, reg); ++ ++ FMC_DMA_WAIT_CPU_FINISH(host); ++ ++ FMC_PR(DMA_DB, "\t\t *-End %s page dma transfer\n", op); ++ ++ return; ++} ++ ++/*****************************************************************************/ ++static void hifmc100_send_cmd_write(struct hifmc_host *host) ++{ ++ unsigned int reg; ++ ++ FMC_PR(WR_DBG, "\t|*-Start send page programme cmd\n"); ++ ++ if (*host->bbm != 0xFF && *host->bbm != 0x00) ++ pr_info("WARNING: attempt to write an invalid bbm. " \ ++ "page: 0x%08x, mark: 0x%02x,\n", ++ GET_PAGE_INDEX(host), *host->bbm); ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ reg = host->addr_value[1]; ++ hifmc_writel(host, FMC_ADDRH, reg); ++ FMC_PR(WR_DBG, "\t||-Set ADDRH[%#x]%#x\n", FMC_ADDRH, reg); ++ ++ reg = host->addr_value[0] & 0xffff0000; ++ hifmc_writel(host, FMC_ADDRL, reg); ++ FMC_PR(WR_DBG, "\t||-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ reg = FMC_CMD_CMD2(NAND_CMD_PAGEPROG) | FMC_CMD_CMD1(NAND_CMD_SEQIN); ++ hifmc_writel(host, FMC_CMD, reg); ++ FMC_PR(WR_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ *host->epm = 0x0000; ++ ++ hifmc100_dma_transfer(host, RW_OP_WRITE); ++ ++ FMC_PR(WR_DBG, "\t|*-End send page read cmd\n"); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_send_cmd_read(struct hifmc_host *host) ++{ ++ unsigned int reg; ++ ++ FMC_PR(RD_DBG, "\t*-Start send page read cmd\n"); ++ ++ if ((host->addr_value[0] == host->cache_addr_value[0]) ++ && (host->addr_value[1] == host->cache_addr_value[1])) { ++ FMC_PR(RD_DBG, "\t*-Cache hit! addr1[%#x], addr0[%#x]\n", ++ host->addr_value[1], host->addr_value[0]); ++ return; ++ } ++ ++ host->page_status = 0; ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ reg = FMC_INT_CLR_ALL; ++ hifmc_writel(host, FMC_INT_CLR, reg); ++ FMC_PR(RD_DBG, "\t|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg); ++ ++ reg = host->nand_cfg; ++ hifmc_writel(host, FMC_CFG, reg); ++ FMC_PR(RD_DBG, "\t|-Set CFG[%#x]%#x\n", FMC_CFG, reg); ++ ++ reg = host->addr_value[1]; ++ hifmc_writel(host, FMC_ADDRH, reg); ++ FMC_PR(RD_DBG, "\t|-Set ADDRH[%#x]%#x\n", FMC_ADDRH, reg); ++ ++ reg = host->addr_value[0] & 0xffff0000; ++ hifmc_writel(host, FMC_ADDRL, reg); ++ FMC_PR(RD_DBG, "\t|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ reg = FMC_CMD_CMD2(NAND_CMD_READSTART) | FMC_CMD_CMD1(NAND_CMD_READ0); ++ hifmc_writel(host, FMC_CMD, reg); ++ FMC_PR(RD_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ hifmc100_dma_transfer(host, RW_OP_READ); ++ ++ if (hifmc_readl(host, FMC_INT) & FMC_INT_ERR_INVALID) ++ host->page_status |= HIFMC100_PS_UC_ECC; ++ ++ host->cache_addr_value[0] = host->addr_value[0]; ++ host->cache_addr_value[1] = host->addr_value[1]; ++ ++ FMC_PR(RD_DBG, "\t*-End send page read cmd\n"); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_send_cmd_erase(struct hifmc_host *host) ++{ ++ unsigned int reg; ++ ++ FMC_PR(ER_DBG, "\t *-Start send cmd erase\n"); ++ ++ /* Don't case the read retry config */ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ reg = host->addr_value[0]; ++ hifmc_writel(host, FMC_ADDRL, reg); ++ FMC_PR(ER_DBG, "\t |-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ reg = FMC_CMD_CMD2(NAND_CMD_ERASE2) | FMC_CMD_CMD1(NAND_CMD_ERASE1); ++ hifmc_writel(host, FMC_CMD, reg); ++ FMC_PR(ER_DBG, "\t |-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = OP_CFG_FM_CS(host->cmd_op.cs) ++ | OP_CFG_ADDR_NUM(host->addr_cycle); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ FMC_PR(ER_DBG, "\t |-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ /* need to config WAIT_READY_EN */ ++ reg = FMC_OP_WAIT_READY_EN ++ | FMC_OP_CMD1_EN ++ | FMC_OP_CMD2_EN ++ | FMC_OP_ADDR_EN ++ | FMC_OP_REG_OP_START; ++ hifmc_writel(host, FMC_OP, reg); ++ FMC_PR(ER_DBG, "\t |-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ FMC_CMD_WAIT_CPU_FINISH(host); ++ ++ FMC_PR(ER_DBG, "\t |*-End send cmd erase\n"); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_ecc_randomizer(struct hifmc_host *host, int ecc_en, ++ int randomizer_en) ++{ ++ unsigned int old_reg, reg, change = 0; ++ char *ecc_op = ecc_en ? "Quit" : "Enter"; ++ char *rand_op = randomizer_en ? "Enable" : "Disable"; ++ ++ if (IS_NAND_RANDOM(host)) { ++ reg = old_reg = hifmc_readl(host, FMC_GLOBAL_CFG); ++ if (randomizer_en) ++ reg |= FMC_GLOBAL_CFG_RANDOMIZER_EN; ++ else ++ reg &= ~FMC_GLOBAL_CFG_RANDOMIZER_EN; ++ ++ if (old_reg != reg) { ++ FMC_PR(EC_DBG, "\t |*-Start %s randomizer\n", rand_op); ++ FMC_PR(EC_DBG, "\t ||-Get global CFG[%#x]%#x\n", ++ FMC_GLOBAL_CFG, old_reg); ++ hifmc_writel(host, FMC_GLOBAL_CFG, reg); ++ FMC_PR(EC_DBG, "\t ||-Set global CFG[%#x]%#x\n", ++ FMC_GLOBAL_CFG, reg); ++ change++; ++ } ++ } ++ ++ old_reg = hifmc_readl(host, FMC_CFG); ++ reg = (ecc_en ? host->nand_cfg : host->nand_cfg_ecc0); ++ ++ if (old_reg != reg) { ++ FMC_PR(EC_DBG, "\t |%s-Start %s ECC0 mode\n", change ? "|":"*", ++ ecc_op); ++ FMC_PR(EC_DBG, "\t ||-Get CFG[%#x]%#x\n", FMC_CFG, old_reg); ++ hifmc_writel(host, FMC_CFG, reg); ++ FMC_PR(EC_DBG, "\t ||-Set CFG[%#x]%#x\n", FMC_CFG, reg); ++ change++; ++ } ++ ++ if (EC_DBG && change) ++ FMC_PR(EC_DBG, "\t |*-End randomizer and ECC0 mode config\n"); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_send_cmd_status(struct hifmc_host *host) ++{ ++ unsigned int regval; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ regval = OP_CFG_FM_CS(host->cmd_op.cs); ++ hifmc_writel(host, FMC_OP_CFG, regval); ++ ++ regval = FMC_OP_READ_STATUS_EN | FMC_OP_REG_OP_START; ++ hifmc_writel(host, FMC_OP, regval); ++ ++ FMC_CMD_WAIT_CPU_FINISH(host); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_send_cmd_readid(struct hifmc_host *host) ++{ ++ unsigned int reg; ++ ++ FMC_PR(BT_DBG, "\t *-Start read nand flash ID\n"); ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ reg = FMC_DATA_NUM_CNT(host->cmd_op.data_no); ++ hifmc_writel(host, FMC_DATA_NUM, reg); ++ FMC_PR(BT_DBG, "\t |-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg); ++ ++ reg = FMC_CMD_CMD1(NAND_CMD_READID); ++ hifmc_writel(host, FMC_CMD, reg); ++ FMC_PR(BT_DBG, "\t |-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = 0; ++ hifmc_writel(host, FMC_ADDRL, reg); ++ FMC_PR(BT_DBG, "\t |-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ reg = OP_CFG_FM_CS(host->cmd_op.cs) ++ | OP_CFG_ADDR_NUM(READ_ID_ADDR_NUM); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ FMC_PR(BT_DBG, "\t |-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = FMC_OP_CMD1_EN ++ | FMC_OP_ADDR_EN ++ | FMC_OP_READ_DATA_EN ++ | FMC_OP_REG_OP_START; ++ hifmc_writel(host, FMC_OP, reg); ++ FMC_PR(BT_DBG, "\t |-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ host->addr_cycle = 0x0; ++ ++ FMC_CMD_WAIT_CPU_FINISH(host); ++ ++ FMC_PR(BT_DBG, "\t *-End read nand flash ID\n"); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_send_cmd_reset(struct hifmc_host *host) ++{ ++ unsigned int reg; ++ ++ FMC_PR(BT_DBG, "\t *-Start reset nand flash\n"); ++ ++ reg = FMC_CMD_CMD1(NAND_CMD_RESET); ++ hifmc_writel(host, FMC_CMD, reg); ++ FMC_PR(BT_DBG, "\t |-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = OP_CFG_FM_CS(host->cmd_op.cs); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ FMC_PR(BT_DBG, "\t |-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = FMC_OP_CMD1_EN ++ | FMC_OP_WAIT_READY_EN ++ | FMC_OP_REG_OP_START; ++ hifmc_writel(host, FMC_OP, reg); ++ FMC_PR(BT_DBG, "\t |-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ FMC_CMD_WAIT_CPU_FINISH(host); ++ ++ FMC_PR(BT_DBG, "\t *-End reset nand flash\n"); ++} ++ ++/*****************************************************************************/ ++static unsigned char hifmc100_read_byte(struct mtd_info *mtd) ++{ ++ unsigned char value = 0; ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++ if (host->cmd_op.l_cmd == NAND_CMD_READID) { ++ value = hifmc_readb((void __iomem *)(chip->IO_ADDR_R + host->offset)); ++ host->offset++; ++ if (host->cmd_op.data_no == host->offset) ++ host->cmd_op.l_cmd = 0; ++ return value; ++ } ++ ++ if (host->cmd_op.cmd == NAND_CMD_STATUS) { ++ value = hifmc_readl(host, FMC_STATUS); ++ if (host->cmd_op.l_cmd == NAND_CMD_ERASE1) ++ FMC_PR(ER_DBG, "\t*-Erase WP status: %#x\n", value); ++ if (host->cmd_op.l_cmd == NAND_CMD_PAGEPROG) ++ FMC_PR(WR_DBG, "\t*-Write WP status: %#x\n", value); ++ return value; ++ } ++ ++ if (host->cmd_op.l_cmd == NAND_CMD_READOOB) { ++ value = hifmc_readb((void __iomem *)(host->buffer + host->pagesize ++ + host->offset)); ++ host->offset++; ++ return value; ++ } ++ ++ host->offset++; ++ ++ return hifmc_readb((void __iomem *)(host->buffer + host->column \ ++ + host->offset - 1)); ++} ++ ++/*****************************************************************************/ ++static unsigned short hifmc100_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++ host->offset += 2; ++ return hifmc_readw(host->buffer + host->column + host->offset - 2); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_write_buf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++#ifdef HIFMC100_NAND_SUPPORT_REG_WRITE ++ if (buf == chip->oob_poi) ++ memcpy((char *)host->iobase + host->pagesize, buf, len); ++ else ++ memcpy((char *)host->iobase, buf, len); ++#else ++ if (buf == chip->oob_poi) ++ memcpy((char *)host->buffer + host->pagesize, buf, len); ++ else ++ memcpy((char *)host->buffer, buf, len); ++#endif ++ return; ++} ++ ++#ifdef CONFIG_HISI_NAND_ECC_STATUS_REPORT ++/*****************************************************************************/ ++static void hifmc100_ecc_err_num_count(struct mtd_info *mtd, ++ u_int ecc_st, u_int reg) ++{ ++ u_char err_num; ++ ++ if (ecc_st > 4) ++ ecc_st = 4; ++ ++ while (ecc_st) { ++ err_num = GET_ECC_ERR_NUM(--ecc_st, reg); ++ if (err_num == 0xff) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += err_num; ++ } ++} ++#endif ++ ++/*****************************************************************************/ ++static void hifmc100_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++#ifdef HIFMC100_NAND_SUPPORT_REG_READ ++ if (buf == chip->oob_poi) ++ memcpy(buf, (char *)host->iobase + host->pagesize, len); ++ else ++ memcpy(buf, (char *)host->iobase, len); ++#else ++ if (buf == chip->oob_poi) ++ memcpy(buf, (char *)host->buffer + host->pagesize, len); ++ else ++ memcpy(buf, (char *)host->buffer, len); ++#endif ++ ++#ifdef CONFIG_HISI_NAND_ECC_STATUS_REPORT ++ if (buf != chip->oob_poi) { ++ u_int reg, ecc_step = host->pagesize >> 10; ++ ++ /* 2K or 4K or 8K(1) or 16K(1-1) pagesize */ ++ reg = hifmc_readl(host, HIFMC100_ECC_ERR_NUM0_BUF0); ++ hifmc100_ecc_err_num_count(mtd, ecc_step, reg); ++ ++ if (ecc_step > 4) { ++ /* 8K(2) or 16K(1-2) pagesize */ ++ reg = hifmc_readl(host, HIFMC100_ECC_ERR_NUM1_BUF0); ++ hifmc100_ecc_err_num_count(mtd, ecc_step, reg); ++ if (ecc_step > 8) { ++ /* 16K(2-1) pagesize */ ++ reg = hifmc_readl(host, ++ HIFMC100_ECC_ERR_NUM0_BUF1); ++ hifmc100_ecc_err_num_count(mtd, ecc_step, reg); ++ /* 16K(2-2) pagesize */ ++ reg = hifmc_readl(host, ++ HIFMC100_ECC_ERR_NUM1_BUF1); ++ hifmc100_ecc_err_num_count(mtd, ecc_step, reg); ++ } ++ } ++ } ++#endif ++ ++ return; ++} ++ ++/*****************************************************************************/ ++static void hifmc100_select_chip(struct mtd_info *mtd, int chipselect) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++ if (chipselect < 0) { ++ mutex_unlock(&fmc_switch_mutex); ++ return; ++ } ++ ++ mutex_lock(&fmc_switch_mutex); ++ ++ if (chipselect > CONFIG_HIFMC100_MAX_NAND_CHIP) ++ DB_BUG("Error: Invalid chip select: %d\n", chipselect); ++ ++ host->cmd_op.cs = chipselect; ++ if (host->mtd != mtd) ++ host->mtd = mtd; ++ ++ switch (chip->state) { ++ case FL_ERASING: ++ host->cmd_op.l_cmd = NAND_CMD_ERASE1; ++ if (ER_DBG) ++ pr_info("\n"); ++ FMC_PR(ER_DBG, "\t*-Erase chip: %d\n", chipselect); ++ break; ++ case FL_WRITING: ++ host->cmd_op.l_cmd = NAND_CMD_PAGEPROG; ++ if (WR_DBG) ++ pr_info("\n"); ++ FMC_PR(WR_DBG, "\t*-Write chip: %d\n", chipselect); ++ break; ++ case FL_READING: ++ host->cmd_op.l_cmd = NAND_CMD_READ0; ++ if (RD_DBG) ++ pr_info("\n"); ++ FMC_PR(RD_DBG, "\t*-Read chip: %d\n", chipselect); ++ break; ++ default: ++ break; ++ } ++} ++ ++/*****************************************************************************/ ++static void hifmc100_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned ctrl) ++{ ++ unsigned char cmd; ++ int is_cache_invalid = 1; ++ struct nand_chip *chip = mtd->priv; ++ struct hifmc_host *host = chip->priv; ++ ++ if (ctrl & NAND_ALE) { ++ unsigned int addr_value = 0; ++ unsigned int addr_offset = 0; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ host->addr_cycle = 0x0; ++ host->addr_value[0] = 0x0; ++ host->addr_value[1] = 0x0; ++ } ++ addr_offset = host->addr_cycle << 3; ++ ++ if (host->addr_cycle >= HIFMC100_ADDR_CYCLE_MASK) { ++ addr_offset = (host->addr_cycle - ++ HIFMC100_ADDR_CYCLE_MASK) << 3; ++ addr_value = 1; ++ } ++ ++ host->addr_value[addr_value] |= ++ ((dat & 0xff) << addr_offset); ++ ++ host->addr_cycle++; ++ } ++ ++ if ((ctrl & NAND_CLE) && (ctrl & NAND_CTRL_CHANGE)) { ++ cmd = dat & 0xff; ++ host->cmd_op.cmd = cmd; ++ switch (cmd) { ++ case NAND_CMD_PAGEPROG: ++ host->offset = 0; ++ host->send_cmd_pageprog(host); ++ break; ++ ++ case NAND_CMD_READSTART: ++ is_cache_invalid = 0; ++ if (host->addr_value[0] == host->pagesize) ++ host->cmd_op.l_cmd = NAND_CMD_READOOB; ++ host->send_cmd_readstart(host); ++ break; ++ ++ case NAND_CMD_ERASE2: ++ host->cmd_op.l_cmd = cmd; ++ host->send_cmd_erase(host); ++ break; ++ ++ case NAND_CMD_READID: ++ memset((u_char *)(chip->IO_ADDR_R), 0, MAX_NAND_ID_LEN); ++ host->cmd_op.l_cmd = cmd; ++ host->cmd_op.data_no = MAX_NAND_ID_LEN; ++ host->send_cmd_readid(host); ++ break; ++ ++ case NAND_CMD_STATUS: ++ host->send_cmd_status(host); ++ break; ++ ++ case NAND_CMD_READ0: ++ host->cmd_op.l_cmd = cmd; ++ break; ++ ++ case NAND_CMD_RESET: ++ host->send_cmd_reset(host); ++ break; ++ ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_ERASE1: ++ default: ++ break; ++ } ++ } ++ ++ /* pass pagesize and ecctype to kernel when startup. */ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ if ((dat == NAND_CMD_NONE) && host->addr_cycle) { ++ if (host->cmd_op.cmd == NAND_CMD_SEQIN ++ || host->cmd_op.cmd == NAND_CMD_READ0 ++ || host->cmd_op.cmd == NAND_CMD_READID) { ++ host->offset = 0x0; ++ host->column = (host->addr_value[0] & 0xffff); ++ } ++ } ++ ++ if (is_cache_invalid) { ++ host->cache_addr_value[0] = ~0; ++ host->cache_addr_value[1] = ~0; ++ } ++} ++ ++/*****************************************************************************/ ++static int hifmc100_dev_ready(struct mtd_info *mtd) ++{ ++ return 0x1; ++} ++ ++/*****************************************************************************/ ++/* ++ * 'host->epm' only use the first oobfree[0] field, it looks very simple, But... ++ */ ++static struct nand_ecclayout nand_ecc_default = { ++ .oobfree = {{2, 30} } ++}; ++ ++#ifdef CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 ++static struct nand_ecclayout nand_ecc_2k16bit = { ++ .oobfree = {{2, 6} } ++}; ++ ++static struct nand_ecclayout nand_ecc_4k16bit = { ++ .oobfree = {{2, 14} } ++}; ++#endif ++/*****************************************************************************/ ++/* ecc/pagesize get from CPU PIN. */ ++static struct nand_config_info hifmc100_nand_hw_pin_config_table[] = { ++ {NAND_PAGE_16K, NAND_ECC_64BIT, 1824/*1824*/, &nand_ecc_default}, ++ {NAND_PAGE_16K, NAND_ECC_40BIT, 1152/*1152*/, &nand_ecc_default}, ++ {NAND_PAGE_16K, NAND_ECC_0BIT, 32 , &nand_ecc_default}, ++ ++ {NAND_PAGE_8K, NAND_ECC_64BIT, 928 /*928*/, &nand_ecc_default}, ++ {NAND_PAGE_8K, NAND_ECC_40BIT, 592 /*592 */, &nand_ecc_default}, ++ {NAND_PAGE_8K, NAND_ECC_24BIT, 368 /*368*/, &nand_ecc_default}, ++ {NAND_PAGE_8K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {NAND_PAGE_4K, NAND_ECC_24BIT, 200 /*200*/, &nand_ecc_default}, ++#ifdef CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 ++ {NAND_PAGE_4K, NAND_ECC_16BIT, 128 /*128*/, &nand_ecc_4k16bit}, ++#endif ++ {NAND_PAGE_4K, NAND_ECC_8BIT, 88 /*88*/, &nand_ecc_default}, ++ {NAND_PAGE_4K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {NAND_PAGE_2K, NAND_ECC_24BIT, 116 /*116*/, &nand_ecc_default}, ++#ifdef CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 ++ {NAND_PAGE_2K, NAND_ECC_16BIT, 64 /*64*/, &nand_ecc_2k16bit}, ++#endif ++ {NAND_PAGE_2K, NAND_ECC_8BIT, 60 /*60*/, &nand_ecc_default}, ++ {NAND_PAGE_2K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {0, 0, 0, NULL}, ++}; ++ ++/*****************************************************************************/ ++/* ecc/pagesize get from NAND controller */ ++static struct nand_config_info hifmc100_nand_hw_auto_config_table[] = { ++ {NAND_PAGE_16K, NAND_ECC_64BIT, 1824/*1824*/, &nand_ecc_default}, ++ {NAND_PAGE_16K, NAND_ECC_40BIT, 1200/*1152*/, &nand_ecc_default}, ++ {NAND_PAGE_16K, NAND_ECC_0BIT, 32 , &nand_ecc_default}, ++ ++ {NAND_PAGE_8K, NAND_ECC_64BIT, 928 /*928*/, &nand_ecc_default}, ++ {NAND_PAGE_8K, NAND_ECC_40BIT, 600 /*592*/, &nand_ecc_default}, ++ {NAND_PAGE_8K, NAND_ECC_24BIT, 368 /*368*/, &nand_ecc_default}, ++ {NAND_PAGE_8K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {NAND_PAGE_4K, NAND_ECC_24BIT, 200 /*200*/, &nand_ecc_default}, ++#ifdef CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 ++ {NAND_PAGE_4K, NAND_ECC_16BIT, 128 /*128*/, &nand_ecc_default}, ++#endif ++ {NAND_PAGE_4K, NAND_ECC_8BIT, 128 /*88*/, &nand_ecc_default}, ++ {NAND_PAGE_4K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {NAND_PAGE_2K, NAND_ECC_24BIT, 128 /*116*/, &nand_ecc_default}, ++#ifdef CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 ++ {NAND_PAGE_2K, NAND_ECC_16BIT, 64 /*64*/, &nand_ecc_default}, ++#endif ++ {NAND_PAGE_2K, NAND_ECC_8BIT, 64 /*60*/, &nand_ecc_default}, ++ {NAND_PAGE_2K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {0, 0, 0, NULL}, ++}; ++ ++/*****************************************************************************/ ++/* ++ * 0 - This NAND NOT support randomizer ++ * 1 - This NAND support randomizer. ++ */ ++static int hifmc100_nand_support_randomizer(u_int pageisze, u_int ecctype) ++{ ++ switch (pageisze) { ++ case _8K: ++ return (ecctype >= NAND_ECC_24BIT && ecctype <= NAND_ECC_80BIT); ++ case _16K: ++ return (ecctype >= NAND_ECC_40BIT && ecctype <= NAND_ECC_80BIT); ++ case _32K: ++ return (ecctype >= NAND_ECC_40BIT && ecctype <= NAND_ECC_80BIT); ++ default: ++ return 0; ++ } ++} ++ ++/*****************************************************************************/ ++/* used the best correct arithmetic. */ ++static struct nand_config_info *hifmc100_get_best_ecc( ++ struct nand_config_info *config, struct mtd_info *mtd) ++{ ++ struct nand_config_info *best = NULL; ++ ++ for (; config->layout; config++) { ++ if (match_page_type_to_size(config->pagetype) != mtd->writesize) ++ continue; ++ ++ if (mtd->oobsize < config->oobsize) ++ continue; ++ ++ if (!best || (best->ecctype < config->ecctype)) ++ best = config; ++ } ++ ++ if (!best) ++ DB_BUG(ERSTR_DRIVER " the pagesize(%d) and oobsize(%d).\n", ++ mtd->writesize, mtd->oobsize); ++ return best; ++} ++ ++/*****************************************************************************/ ++static void debug_register_dump(struct hifmc_host *host) ++{ ++ int ix; ++ ++ pr_info("Register dump:"); ++ for (ix = 0; ix <= 0x98; ix += 0x04) { ++ if (!(ix & 0x0F)) ++ pr_info("\n0x%08X: ", ++ (unsigned int)(host->regbase + ix)); ++ pr_info("%08X ", hifmc_readl(host, ix)); ++ } ++ pr_info("\n"); ++} ++ ++/*****************************************************************************/ ++static void hifmc100_check_config(struct hifmc_host *host, ++ struct nand_dev_t *flash_dev_ex, ++ struct nand_config_info *best, int is_hw_auto) ++{ ++ unsigned char page_reg, pagetype, ecc_reg, ecctype; ++ int isdump = 0; ++ ++ FMC_PR(BT_DBG, "\t *-Start check best and controller auto config\n"); ++ ++ page_reg = (host->nand_cfg & PAGE_SIZE_MASK) >> PAGE_SIZE_SHIFT; ++ pagetype = match_page_reg_to_type(page_reg); ++ ++ ecc_reg = (host->nand_cfg & ECC_TYPE_MASK) >> ECC_TYPE_SHIFT; ++ ecctype = match_ecc_reg_to_type(ecc_reg); ++ ++ if (pagetype != best->pagetype) { ++ if (!isdump++) ++ debug_register_dump(host); ++ if (is_hw_auto) { ++ DB_BUG("!!! warning: Hardware configure pagesize %s" \ ++ ", but the Nand Flash pageszie is %s. Update" \ ++ " fastboot will resolve this problem.\n", ++ match_page_type_to_str(pagetype), ++ match_page_type_to_str(best->pagetype)); ++ } else { ++ DB_BUG("!!! warning: Hardware configure pagesize %s" \ ++ ", but the Nand Flash pageszie is %s. Modify" \ ++ "hardware pin will resolve this problem.\n", ++ match_page_type_to_str(pagetype), ++ match_page_type_to_str(best->pagetype)); ++ } ++ } ++ ++ if (ecctype < best->ecctype) { ++ if (!is_hw_auto) ++ best->ecctype = ecctype; ++ } else if (ecctype > best->ecctype) { ++ DB_BUG("!!! warning: Hardware configure ecctype %s, " \ ++ "but %s is more suitable for this Nand Flash." \ ++ "Modify hardware pin will resolve this problem.\n", ++ match_ecc_type_to_str(ecctype), ++ match_ecc_type_to_str(best->ecctype)); ++ } ++ ++ if (IS_NAND_RANDOM(flash_dev_ex) && !IS_NAND_RANDOM(host)) { ++ if (is_hw_auto) { ++ DB_BUG("Hardware is't configure randomizer, but it "\ ++ "is more suitable for this Nand Flash. Update"\ ++ " fastboot will resolve this problem\n"); ++ } else { ++ DB_BUG("Hardware is't configure randomizer, but it" \ ++ "is more suitable for this Nand Flash. Modify"\ ++ "hardware pin will resolve this problem.\n"); ++ } ++ } ++ ++ FMC_PR(BT_DBG, "\t *-End check, PageSize: %s, EccType: %s\n", ++ match_page_type_to_str(pagetype), match_ecc_type_to_str(ecctype)); ++} ++ ++/*****************************************************************************/ ++static int hifmc100_ecc_probe(struct mtd_info *mtd, struct nand_chip *chip, ++ struct nand_dev_t *dev) ++{ ++ int regval, buffer_len; ++ char *start_type = "unknown"; ++ struct nand_config_info *best = NULL; ++ struct nand_config_info *config = NULL; ++ struct hifmc_host *host = chip->priv; ++ struct nand_dev_t *nand_dev = &g_nand_dev; ++ ++ FMC_PR(BT_DBG, "\t *-Start match PageSize and EccType\n"); ++ ++ if (IS_NANDC_HW_AUTO(host)) { ++ FMC_PR(BT_DBG, "\t |-Get hardware auto config\n"); ++ config = hifmc100_nand_hw_auto_config_table; ++ ++ start_type = "HW-Auto"; ++ best = hifmc100_get_best_ecc(config, mtd); ++ } else { ++ FMC_PR(BT_DBG, "\t |-Get config from CPU PIN\n"); ++ config = hifmc100_nand_hw_pin_config_table; ++ start_type = "HW-Pin"; ++ best = hifmc100_get_best_ecc(config, mtd); ++ hifmc100_check_config(host, nand_dev, best, 0); ++ } ++ ++ nand_dev->flags |= (IS_NANDC_HW_AUTO(host) ++ | IS_NANDC_CONFIG_DONE(host)); ++ ++ if (!best) ++ DB_BUG(ERSTR_HARDWARE ++ "Please configure Nand Flash pagesize and ecctype!\n"); ++ ++ if (best->ecctype != NAND_ECC_0BIT) ++ mtd->oobsize = best->oobsize; ++ mtd->oobavail = HIFMC100_NAND_OOBSIZE_FOR_YAFFS; ++ chip->ecc.layout = best->layout; ++ ++ host->ecctype = best->ecctype; ++ FMC_PR(BT_DBG, "\t |-Save best EccType %d(%s)\n", host->ecctype, ++ match_ecc_type_to_str(best->ecctype)); ++ host->pagesize = match_page_type_to_size(best->pagetype); ++ FMC_PR(BT_DBG, "\t |-Save best PageSize %d(%s)\n", host->pagesize, ++ match_page_type_to_str(best->pagetype)); ++ ++ if (hifmc100_nand_support_randomizer(host->pagesize, host->ecctype)) ++ host->flags |= IS_NAND_RANDOM(nand_dev); ++ ++ host->oobsize = mtd->oobsize; ++ host->block_page_mask = ((mtd->erasesize / mtd->writesize) - 1); ++ ++ buffer_len = host->pagesize + host->oobsize; ++ host->buffer = dmam_alloc_coherent(host->dev, buffer_len, ++ &host->dma_buffer, GFP_KERNEL); ++ if (!host->buffer) { ++ DB_MSG("Error: Can't malloc memory for hifmc100 driver.\n"); ++ return -ENOMEM; ++ } ++ memset(host->buffer, 0xff, buffer_len); ++ host->dma_oob = host->dma_buffer + host->pagesize; ++ ++ host->bbm = (unsigned char *)(host->buffer + host->pagesize ++ + HIFMC100_BAD_BLOCK_POS); ++ ++ host->epm = (unsigned short *)(host->buffer + host->pagesize ++ + chip->ecc.layout->oobfree[0].offset + 28); ++ ++#ifdef CONFIG_HISI_NAND_FS_MAY_NO_YAFFS2 ++ if (best->ecctype == NAND_ECC_16BIT) { ++ if (host->pagesize == _2K) { ++ /* EB bits locate in the bottom two of CTRL(4) */ ++ host->epm = (u_short *)(host->buffer + host->pagesize ++ + chip->ecc.layout->oobfree[0].offset + 4); ++ } else if (host->pagesize == _4K) { ++ /* EB bit locate in the bottom two of CTRL(14) */ ++ host->epm = (u_short *)(host->buffer + host->pagesize ++ + chip->ecc.layout->oobfree[0].offset + 12); ++ } ++ } ++#endif ++ ++ host->nand_cfg &= ~PAGE_SIZE_MASK; ++ host->nand_cfg_ecc0 &= ~PAGE_SIZE_MASK; ++ regval = match_page_type_to_reg(best->pagetype); ++ host->nand_cfg |= FMC_CFG_PAGE_SIZE(regval); ++ host->nand_cfg_ecc0 |= FMC_CFG_PAGE_SIZE(regval); ++ ++ host->nand_cfg &= ~ECC_TYPE_MASK; ++ host->nand_cfg_ecc0 &= ~ECC_TYPE_MASK; ++ regval = match_ecc_type_to_reg(best->ecctype); ++ host->nand_cfg |= FMC_CFG_ECC_TYPE(regval); ++ ++ /* pass pagesize and ecctype to kernel when spiflash startup. */ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ if (mtd->writesize > NAND_MAX_PAGESIZE ++ || mtd->oobsize > NAND_MAX_OOBSIZE) { ++ DB_BUG(ERSTR_DRIVER ++ "Driver does not support this Nand Flash. Please " \ ++ "increase NAND_MAX_PAGESIZE and NAND_MAX_OOBSIZE.\n"); ++ } ++ ++ if (mtd->writesize != host->pagesize) { ++ unsigned int shift = 0; ++ unsigned int writesize = mtd->writesize; ++ ++ while (writesize > host->pagesize) { ++ writesize >>= 1; ++ shift++; ++ } ++ chip->chipsize = chip->chipsize >> shift; ++ mtd->erasesize = mtd->erasesize >> shift; ++ mtd->writesize = host->pagesize; ++ pr_info("Nand divide into 1/%u\n", (1 << shift)); ++ } ++ ++ nand_dev->start_type = start_type; ++ dev->start_type = nand_dev->start_type; ++ nand_dev->ecctype = host->ecctype; ++ dev->ecctype = nand_dev->ecctype; ++ ++ host->read_retry = NULL; ++ ++ /* ++ * If it want to support the 'read retry' feature, the 'randomizer' ++ * feature must be support first. ++ */ ++ if (host->read_retry && !IS_NAND_RANDOM(host)) { ++ DB_BUG(ERSTR_HARDWARE ++ "This Nand flash need to enable 'randomizer' feature. " ++ "Please configure hardware randomizer PIN."); ++ } ++ ++ /* ++ * Check if hardware enable randomizer PIN, But NAND does not need ++ * randomizer. We will notice user. ++ */ ++ if (IS_NAND_RANDOM(host) && ++ !hifmc100_nand_support_randomizer(host->pagesize, host->ecctype)) ++ DB_BUG(ERSTR_HARDWARE ++ "This NAND flash does not support `randomizer`, " ++ "Please don't configure hardware randomizer PIN."); ++ ++ FMC_PR(BT_DBG, "\t *-End match ecctype\n"); ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++static void hifmc100_chip_init(struct nand_chip *chip) ++{ ++ memset((char *)chip->IO_ADDR_R, 0xff, NAND_BUFFER_LEN); ++ ++ chip->read_byte = hifmc100_read_byte; ++ chip->read_word = hifmc100_read_word; ++ chip->write_buf = hifmc100_write_buf; ++ chip->read_buf = hifmc100_read_buf; ++ ++ chip->select_chip = hifmc100_select_chip; ++ ++ chip->cmd_ctrl = hifmc100_cmd_ctrl; ++ chip->dev_ready = hifmc100_dev_ready; ++ ++ chip->chip_delay = FMC_CHIP_DELAY; ++ ++ chip->options = NAND_NO_AUTOINCR | NAND_NEED_READRDY | NAND_BROKEN_XD ++ | NAND_SKIP_BBTSCAN; ++ ++ chip->ecc.layout = NULL; ++ chip->ecc.mode = NAND_ECC_NONE; ++} ++ ++/*****************************************************************************/ ++static int hifmc100_host_init(struct hifmc_host *host) ++{ ++ unsigned int addr, reg, flash_type; ++ ++ FMC_PR(BT_DBG, "\t *-Start nand host init\n"); ++ ++ addr = (unsigned int)host->regbase + FMC_CFG; ++ reg = hifmc_readl(host, FMC_CFG); ++ FMC_PR(BT_DBG, "\t |-Read FMC CFG[%#x]%#x\n", addr, reg); ++ flash_type = GET_SPI_FLASH_TYPE(reg); ++ if (flash_type != FLASH_TYPE_NAND) { ++ DB_MSG("Error: Flash type isn't Nand flash. reg[%#x]\n", reg); ++ reg |= FMC_CFG_FLASH_SEL(FLASH_TYPE_NAND); ++ FMC_PR(BT_DBG, "\t |-Change flash type to Nand flash\n"); ++ } ++ ++ if ((reg & FMC_CFG_OP_MODE_MASK) == FMC_CFG_OP_MODE_BOOT) { ++ reg |= FMC_CFG_OP_MODE(FMC_CFG_OP_MODE_NORMAL); ++ FMC_PR(BT_DBG, "\t |-Controller enter normal mode\n"); ++ } ++ hifmc_writel(host, FMC_CFG, reg); ++ FMC_PR(BT_DBG, "\t |-Set CFG[%#x]%#x\n", addr, reg); ++ ++ host->nand_cfg = reg; ++ host->nand_cfg_ecc0 = (reg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT; ++ ++ addr = (unsigned int)host->regbase + FMC_GLOBAL_CFG; ++ reg = hifmc_readl(host, FMC_GLOBAL_CFG); ++ FMC_PR(BT_DBG, "\t |-Read global CFG[%#x]%#x\n", addr, reg); ++ if (reg & FMC_GLOBAL_CFG_RANDOMIZER_EN) { ++ host->flags &= ~NAND_RANDOMIZER; ++ FMC_PR(BT_DBG, "\t |-Default disable randomizer\n"); ++ reg &= ~FMC_GLOBAL_CFG_RANDOMIZER_EN; ++ hifmc_writel(host, FMC_GLOBAL_CFG, reg); ++ FMC_PR(BT_DBG, "\t |-Set global CFG[%#x]%#x\n", addr, reg); ++ } ++ ++#ifdef CONFIG_HIFMC100_NAND_EDO_MODE ++ /* enable EDO node */ ++ reg = hifmc_readl(host, FMC_GLOBAL_CFG); ++ hifmc_writel(host, FMC_GLOBAL_CFG, SET_NAND_EDO_MODE_EN(reg)); ++#endif ++ ++ host->addr_cycle = 0; ++ host->addr_value[0] = 0; ++ host->addr_value[1] = 0; ++ host->cache_addr_value[0] = ~0; ++ host->cache_addr_value[1] = ~0; ++ ++ host->send_cmd_pageprog = hifmc100_send_cmd_write; ++ host->send_cmd_status = hifmc100_send_cmd_status; ++ host->send_cmd_readstart = hifmc100_send_cmd_read; ++ host->send_cmd_erase = hifmc100_send_cmd_erase; ++ host->send_cmd_readid = hifmc100_send_cmd_readid; ++ host->send_cmd_reset = hifmc100_send_cmd_reset; ++ ++ /* ++ * check if start from nand. ++ * This register REG_SYSSTAT is set in start.S ++ * When start in NAND (Auto), the ECC/PAGESIZE driver don't detect. ++ */ ++ host->flags |= NANDC_HW_AUTO; ++ ++ if (GET_SYS_BOOT_MODE(reg) == BOOT_FROM_NAND) { ++ host->flags |= NANDC_CONFIG_DONE; ++ FMC_PR(BT_DBG, "\t |-Auto config pagesize and ecctype\n"); ++ } ++ ++ host->enable_ecc_randomizer = hifmc100_ecc_randomizer; ++ ++ FMC_PR(BT_DBG, "\t *-End nand host init\n"); ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++int hifmc100_nand_init(struct nand_chip *chip) ++{ ++ struct hifmc_host *host = chip->priv; ++ ++ /* enable and set system clock */ ++ clk_prepare_enable(host->clk); ++ ++ /* fmc ip version check */ ++ host->version = hifmc_readl(host, FMC_VERSION); ++ if (host->version != HIFMC_VER_100) ++ return -EFAULT; ++ pr_info("Found Flash Memory Controller v100 Nand Driver\n"); ++ ++ /* hifmc host init */ ++ if (hifmc100_host_init(host)) { ++ DB_MSG("Error: Nand host init failed!\n"); ++ return -EFAULT; ++ } ++ host->chip = chip; ++ ++ hifmc_writel(host, FMC_PND_PWIDTH_CFG, PWIDTH_CFG_RW_HCNT(CONFIG_RW_H_WIDTH) ++ | PWIDTH_CFG_R_LCNT(CONFIG_R_L_WIDTH) ++ | PWIDTH_CFG_W_LCNT(CONFIG_W_L_WIDTH)); ++ ++ /* hifmc nand_chip struct init */ ++ hifmc100_chip_init(chip); ++ ++ hifmc_spl_ids_register(); ++ hinfc_param_adjust = hifmc100_ecc_probe; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++/*****************************************************************************/ ++void hifmc100_nand_config(struct hifmc_host *host) ++{ ++ /* enable system clock */ ++ clk_prepare_enable(host->clk); ++ FMC_PR(PM_DBG, "\t |-enable system clock\n"); ++} ++#endif /* CONFIG_PM */ +diff --git a/drivers/mtd/nand/hifmc100_nand/hifmc100_nand.h b/drivers/mtd/nand/hifmc100_nand/hifmc100_nand.h +new file mode 100644 +index 0000000..469e562 +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100_nand/hifmc100_nand.h +@@ -0,0 +1,159 @@ ++/* ++ * The Flash Memory Controller v100 Device Driver for hisilicon ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef __HIFMC100_NAND_H__ ++#define __HIFMC100_NAND_H__ ++ ++#include <linux/mfd/hisi_fmc.h> ++ ++/******************************************************************************/ ++/* These macroes are for debug only, reg option is slower then dma option */ ++#undef HIFMC100_NAND_SUPPORT_REG_READ ++/* #define HIFMC100_NAND_SUPPORT_REG_READ */ ++ ++#undef HIFMC100_NAND_SUPPORT_REG_WRITE ++/* #define HIFMC100_NAND_SUPPORT_REG_WRITE */ ++ ++#ifdef CONFIG_HISI_NAND_ECC_STATUS_REPORT ++/*****************************************************************************/ ++#define HIFMC100_ECC_ERR_NUM0_BUF0 0xc0 ++#define HIFMC100_ECC_ERR_NUM1_BUF0 0xc4 ++#define HIFMC100_ECC_ERR_NUM0_BUF1 0xc8 ++#define HIFMC100_ECC_ERR_NUM1_BUF1 0xcc ++ ++#define GET_ECC_ERR_NUM(_i, _reg) (((_reg) >> ((_i) * 8)) & 0xff) ++#endif ++/*****************************************************************************/ ++#define NAND_MAX_PAGESIZE 32768 ++#define NAND_MAX_OOBSIZE 4800 ++ ++#define HIFMC100_NAND_OOBSIZE_FOR_YAFFS 32 ++ ++/*****************************************************************************/ ++#define REG_CNT_HIGH_BLOCK_NUM_SHIFT 10 ++ ++#define REG_CNT_BLOCK_NUM_MASK 0x3ff ++#define REG_CNT_BLOCK_NUM_SHIFT 22 ++ ++#define REG_CNT_PAGE_NUM_MASK 0x3f ++#define REG_CNT_PAGE_NUM_SHIFT 16 ++ ++#define REG_CNT_WRAP_MASK 0xf ++#define REG_CNT_WRAP_SHIFT 12 ++ ++#define REG_CNT_ECC_OFFSET_MASK 0xfff ++#define REG_CNT_ECC_8BIT_OFFSET 1054 ++#define REG_CNT_ECC_16BIT_OFFSET 1056 ++#define REG_CNT_ECC_24BIT_OFFSET 1082 ++ ++/*****************************************************************************/ ++#define HIFMC100_ADDR_CYCLE_MASK 0x4 ++ ++#define NAND_EDO_MODE_SHIFT 9 ++#define NAND_EDO_MODE_MASK (1<<NAND_EDO_MODE_SHIFT) ++#define SET_NAND_EDO_MODE_EN(reg) ((reg) | NAND_EDO_MODE_MASK) ++/*****************************************************************************/ ++struct hifmc_host { ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ ++ struct hifmc_cmd_op cmd_op; ++ void __iomem *regbase; ++ void __iomem *iobase; ++ ++ /* Controller config option nand flash */ ++ unsigned int nand_cfg; ++ unsigned int nand_cfg_ecc0; ++ ++ unsigned int offset; ++ ++ struct device *dev; ++ ++ /* This is maybe an un-aligment address, only for malloc or free */ ++ char *buforg; ++ char *buffer; ++ ++ unsigned int dma_buffer; ++ unsigned int dma_oob; ++ ++ unsigned int addr_cycle; ++ unsigned int addr_value[2]; ++ unsigned int cache_addr_value[2]; ++ ++ unsigned int column; ++ unsigned int block_page_mask; ++ ++ unsigned int ecctype; ++ unsigned int pagesize; ++ unsigned int oobsize; ++ ++ int need_rr_data; ++#define HIFMC100_READ_RETRY_DATA_LEN 128 ++ char rr_data[HIFMC100_READ_RETRY_DATA_LEN]; ++ int version; ++ int add_partition; ++ ++ /* BOOTROM read two bytes to detect the bad block flag */ ++#define HIFMC100_BAD_BLOCK_POS 0 ++ unsigned char *bbm; /* nand bad block mark */ ++ unsigned short *epm; /* nand empty page mark */ ++ unsigned int flags; ++ ++#define HIFMC100_PS_UC_ECC 0x01 /* page has ecc error */ ++#define HIFMC100_PS_BAD_BLOCK 0x02 /* bad block */ ++#define HIFMC100_PS_EMPTY_PAGE 0x04 /* page is empty */ ++#define HIFMC100_PS_EPM_ERROR 0x0100 /* empty page mark word has error. */ ++#define HIFMC100_PS_BBM_ERROR 0x0200 /* bad block mark word has error. */ ++ unsigned int page_status; ++ ++ struct clk *clk; ++ ++ void (*send_cmd_pageprog)(struct hifmc_host *host); ++ void (*send_cmd_status)(struct hifmc_host *host); ++ void (*send_cmd_readstart)(struct hifmc_host *host); ++ void (*send_cmd_erase)(struct hifmc_host *host); ++ void (*send_cmd_readid)(struct hifmc_host *host); ++ void (*send_cmd_reset)(struct hifmc_host *host); ++ void (*enable)(int enable); ++ ++ void (*enable_ecc_randomizer)(struct hifmc_host *host, ++ int ecc_en, int randomizer_en); ++ ++ void (*detect_ecc)(struct hifmc_host *host); ++ ++ struct read_retry_t *read_retry; ++}; ++ ++/*****************************************************************************/ ++extern struct nand_dev_t g_nand_dev; ++ ++/*****************************************************************************/ ++int hifmc100_nand_init(struct nand_chip *chip); ++ ++/*****************************************************************************/ ++extern void hifmc_spl_ids_register(void); ++ ++/*****************************************************************************/ ++#ifdef CONFIG_PM ++void hifmc100_nand_config(struct hifmc_host *host); ++#endif ++/*****************************************************************************/ ++ ++#endif /* End of __HIFMC100_NAND_H__ */ +diff --git a/drivers/mtd/nand/hifmc100_nand/hifmc100_nand_os.c b/drivers/mtd/nand/hifmc100_nand/hifmc100_nand_os.c +new file mode 100644 +index 0000000..b475615 +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100_nand/hifmc100_nand_os.c +@@ -0,0 +1,180 @@ ++/* ++ * The Flash Memory Controller v100 Device Driver for hisilicon ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/of_platform.h> ++ ++#include "hifmc100_nand_os.h" ++#include "hifmc100_nand.h" ++#include <linux/mfd/hisi_fmc.h> ++ ++/*****************************************************************************/ ++static inline int mtd_has_partitions(void) { return 1; } ++ ++/*****************************************************************************/ ++static int hisi_nand_os_probe(struct platform_device *pltdev) ++{ ++ int len, result = 0; ++ struct hifmc_host *host; ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ int nr_parts = 0; ++ struct mtd_partition *parts = NULL; ++ struct device *dev = &pltdev->dev; ++ struct device_node *np = NULL; ++ struct hisi_fmc *fmc = dev_get_drvdata(dev->parent); ++ ++ len = sizeof(struct hifmc_host) + sizeof(struct nand_chip) ++ + sizeof(struct mtd_info); ++ host = devm_kzalloc(dev, len, GFP_KERNEL); ++ if (!host) ++ return -ENOMEM; ++ memset((char *)host, 0, len); ++ platform_set_drvdata(pltdev, host); ++ ++ host->dev = &pltdev->dev; ++ host->chip = chip = (struct nand_chip *)&host[1]; ++ host->mtd = mtd = (struct mtd_info *)&chip[1]; ++ host->regbase = fmc->regbase; ++ host->iobase = fmc->iobase; ++ host->clk = fmc->clk; ++ chip->IO_ADDR_R = chip->IO_ADDR_W = host->iobase; ++ ++ /* hifmc Nand host init */ ++ chip->priv = host; ++ result = hifmc100_nand_init(chip); ++ if (result) { ++ DB_MSG("Error: host init failed! result: %d\n", result); ++ goto fail; ++ } ++ ++ np = of_get_next_available_child(dev->of_node, NULL); ++ mtd->name = np->name; ++ mtd->type = MTD_NANDFLASH; ++ mtd->priv = chip; ++ mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->owner = THIS_MODULE; ++ ++ if (nand_scan(mtd, CONFIG_HIFMC100_MAX_NAND_CHIP)) { ++ result = -ENXIO; ++ goto fail; ++ } ++ ++ if (mtd_has_partitions()) { ++ static char const *part_probes[] = { ++ "cmdlinepart", ++ NULL, ++ }; ++ ++ nr_parts = parse_mtd_partitions(host->mtd, ++ part_probes, &parts, 0); ++ FMC_PR(BT_DBG, "parse mtd partitions: %d\n", nr_parts); ++ if (nr_parts > 0) ++ host->add_partition = 1; ++ } ++ ++ result = mtd_device_register(host->mtd, parts, nr_parts); ++ if (result) { ++ kfree(parts); ++ parts = NULL; ++ } ++ ++ return (1 == result) ? -ENODEV : 0; ++ ++fail: ++ nand_release(mtd); ++ clk_disable_unprepare(host->clk); ++ return result; ++} ++ ++/*****************************************************************************/ ++static int hisi_nand_os_remove(struct platform_device *pltdev) ++{ ++ struct hifmc_host *host = platform_get_drvdata(pltdev); ++ ++ clk_disable_unprepare(host->clk); ++ nand_release(host->mtd); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++/*****************************************************************************/ ++static int hifmc100_nand_os_suspend(struct platform_device *pltdev, ++ pm_message_t state) ++{ ++ struct hifmc_host *host = platform_get_drvdata(pltdev); ++ if (!host) ++ return 0; ++ ++ while ((hifmc_readl(host, FMC_OP) & FMC_OP_REG_OP_START)) ++ _cond_resched(); ++ ++ while ((hifmc_readl(host, FMC_OP_CTRL) & OP_CTRL_DMA_OP_READY)) ++ _cond_resched(); ++ ++ clk_disable_unprepare(host->clk); ++ FMC_PR(PM_DBG, "\t|-disable system clock\n"); ++ return 0; ++} ++ ++/*****************************************************************************/ ++static int hifmc100_nand_os_resume(struct platform_device *pltdev) ++{ ++ int cs; ++ struct hifmc_host *host = platform_get_drvdata(pltdev); ++ struct nand_chip *chip; ++ ++ if (!host) ++ return 0; ++ ++ chip = host->chip; ++ ++ for (cs = 0; cs < chip->numchips; cs++) ++ host->send_cmd_reset(host); ++ ++ hifmc100_nand_config(host); ++ return 0; ++} ++#endif /* CONFIG_PM */ ++ ++/*****************************************************************************/ ++static const struct of_device_id hisi_nand_dt_ids[] = { ++ { .compatible = "hisilicon,hisi-nand" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, hisi_nand_dt_ids); ++ ++static struct platform_driver hisi_nand_driver = { ++ .driver = { ++ .name = "hisi-nand", ++ .of_match_table = hisi_nand_dt_ids, ++ }, ++ .probe = hisi_nand_os_probe, ++ .remove = hisi_nand_os_remove, ++#ifdef CONFIG_PM ++ .suspend = hifmc100_nand_os_suspend, ++ .resume = hifmc100_nand_os_resume, ++#endif ++}; ++module_platform_driver(hisi_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("BVT_BSP"); ++MODULE_DESCRIPTION("Hisilicon Flash Memory Controller V100 Nand Driver"); +diff --git a/drivers/mtd/nand/hifmc100_nand/hifmc100_nand_os.h b/drivers/mtd/nand/hifmc100_nand/hifmc100_nand_os.h +new file mode 100644 +index 0000000..063fef8 +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100_nand/hifmc100_nand_os.h +@@ -0,0 +1,80 @@ ++/* ++ * The Flash Memory Controller v100 Device Driver for hisilicon ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef __HIFMC100_NAND_OS_H__ ++#define __HIFMC100_NAND_OS_H__ ++ ++/*****************************************************************************/ ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/partitions.h> ++#include <linux/delay.h> ++#include <linux/dma-mapping.h> ++#include <linux/sched.h> ++#include <asm/errno.h> ++#include <asm/setup.h> ++#include <linux/io.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++ ++#include <asm/clkdev.h> ++#include <linux/resource.h> ++#include <linux/clk.h> ++#include <linux/clkdev.h> ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 5)) ++ #include "../../mtdcore.h" ++#endif ++ ++/*****************************************************************************/ ++#define DEFAULT_NAND_PAGESIZE 2048 ++#define DEFAULT_NAND_OOBSIZE 64 ++ ++#define NAND_BUFFER_LEN (DEFAULT_NAND_PAGESIZE + DEFAULT_NAND_OOBSIZE) ++ ++/*****************************************************************************/ ++#ifndef CONFIG_HIFMC100_MAX_NAND_CHIP ++ #define CONFIG_HIFMC100_MAX_NAND_CHIP (1) ++ #warning NOT config CONFIG_HIFMC100_MAX_NAND_CHIP, \ ++ used default value, maybe invalid. ++#endif ++ ++#ifndef CONFIG_RW_H_WIDTH ++ #define CONFIG_RW_H_WIDTH (10) ++ #warning NOT config CONFIG_RW_H_WIDTH, used default value, maybe invalid. ++#endif ++ ++#ifndef CONFIG_R_L_WIDTH ++ #define CONFIG_R_L_WIDTH (10) ++ #warning NOT config CONFIG_R_L_WIDTH, used default value, maybe invalid. ++#endif ++ ++#ifndef CONFIG_W_L_WIDTH ++ #define CONFIG_W_L_WIDTH (10) ++ #warning NOT config CONFIG_W_L_WIDTH, used default value, maybe invalid. ++#endif ++ ++extern void hifmc100_nand_controller_enable(int enable); ++ ++#endif /* End of __HIFMC100_NAND_OS_H__ */ +diff --git a/drivers/mtd/nand/hifmc100_nand/hifmc_nand_spl_ids.c b/drivers/mtd/nand/hifmc100_nand/hifmc_nand_spl_ids.c +new file mode 100644 +index 0000000..5b34c02 +--- /dev/null ++++ b/drivers/mtd/nand/hifmc100_nand/hifmc_nand_spl_ids.c +@@ -0,0 +1,925 @@ ++/* ++ * The Flash Memory Controller v100 Device Driver for hisilicon ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <asm/setup.h> ++#include <linux/mtd/nand.h> ++#include <linux/mfd/hisi_fmc.h> ++ ++#include "../hinfc_gen.h" ++#include "hifmc100_nand.h" ++ ++/*****************************************************************************/ ++#define _768K (_256K + _512K) ++ ++/*****************************************************************************/ ++struct nand_flash_special_dev { ++ unsigned char id[8]; ++ int length; /* length of id. */ ++ unsigned long long chipsize; ++ struct nand_flash_dev *(*probe)(unsigned char *id); ++ char *name; ++ ++ unsigned long pagesize; ++ unsigned long erasesize; ++ unsigned long oobsize; ++ unsigned long options; ++ unsigned int read_retry_type; ++ ++#define BBP_LAST_PAGE 0x01 ++#define BBP_FIRST_PAGE 0x02 ++ unsigned int badblock_pos; ++ int flags; ++}; ++ ++/*****************************************************************************/ ++/* this is nand probe function. */ ++/*****************************************************************************/ ++ ++static struct nand_flash_dev *hynix_probe_v02(unsigned char *id) ++{ ++ struct nand_flash_dev *type = &g_nand_dev.flash_dev; ++ ++ int pagesizes[] = {_2K, _4K, _8K, 0}; ++ int oobsizes[] = {128, 224, 448, 0, 0, 0, 0, 0}; ++ int blocksizes[] = {_128K, _256K, _512K, _768K, _1M, _2M, 0, 0}; ++ ++ int blocktype = (((id[3] >> 5) & 0x04) | ((id[3] >> 4) & 0x03)); ++ int oobtype = (((id[3] >> 2) & 0x03) | ((id[3] >> 4) & 0x04)); ++ ++ type->options = 0; ++ type->pagesize = pagesizes[(id[3] & 0x03)]; ++ type->erasesize = blocksizes[blocktype]; ++ type->oobsize = oobsizes[oobtype]; ++ ++ return type; ++} ++ ++/*****************************************************************************/ ++static struct nand_flash_dev *samsung_probe_v02(unsigned char *id) ++{ ++ struct nand_flash_dev *type = &g_nand_dev.flash_dev; ++ ++ int pagesizes[] = {_2K, _4K, _8K, 0}; ++ int oobsizes[] = {0, 128, 218, 400, 436, 0, 0, 0}; ++ int blocksizes[] = {_128K, _256K, _512K, _1M, 0, 0, 0, 0}; ++ ++ int blocktype = (((id[3] >> 5) & 0x04) | ((id[3] >> 4) & 0x03)); ++ int oobtype = (((id[3] >> 4) & 0x04) | ((id[3] >> 2) & 0x03)); ++ ++ type->options = 0; ++ type->pagesize = pagesizes[(id[3] & 0x03)]; ++ type->erasesize = blocksizes[blocktype]; ++ type->oobsize = oobsizes[oobtype]; ++ ++ return type; ++} ++ ++/*****************************************************************************/ ++ ++#define DRV_VERSION "1.38" ++ ++/*****************************************************************************/ ++/* ++ * samsung: 27nm need randomizer, 21nm need read retry; ++ * micron: 25nm need read retry, datasheet will explain read retry. ++ * toshaba 32nm need randomizer, 24nm need read retry. ++ * hynix: 2xnm need read retry. ++ * ++ * The special nand flash ID table version 1.38 ++ * ++ * manufactory | type | name | ecc_type | version_tag ++ * Micron | MLC | MT29F64G08CBABA | 40bit/1k | 1.36 ++ * Micron | MLC | MT29F32G08CBADA | 40bit/1k | ++ * Micron | SLC | MT29F8G08ABxBA | 4bit/512 | ++ * Micron | MLC | MT29F16G08CBABx | 12bit/512 | ++ * Micron | MLC | MT29F16G08CBACA | 24bit/1k | ++ * Micron | MLC | MT29F32G08CBACA | 24bit/1k | ++ * Micron | MLC | MT29F64G08CxxAA | 24bit/1k | ++ * Micron | MLC | MT29F256G08CJAAA | 24bit/1k | 2CE ++ * Micron | MLC | MT29F256G08CMCBB | 24bit/1k | ++ * Micron | SLC | MT29F8G08ABACA | 8bit/512 | ++ * Micron | SLC | MT29F4G08ABAEA | 8bit/512 | ++ * Micron | SLC | MT29F2G08ABAFA | 8bit/512 | ++ * Micron | SLC | MT29F16G08ABACA | 8bit/512 | ++ * Toshiba | MLC | TC58NVG4D2FTA00 | 24bit/1k | ++ * Toshiba | MLC | TH58NVG6D2FTA20 | 24bit/1k | 2CE ++ * Toshiba | MLC | TC58NVG5D2HTA00 | 40bit/1k | ++ * Toshiba | MLC | TC58NVG6D2GTA00 | 40bit/1k | ++ * Toshiba | MLC | TC58NVG6DCJTA00 | | ++ * Toshiba | MLC | TC58TEG5DCJTA00 | | ++ * Toshiba | SLC | TC58NVG0S3HTA00 | 8bit/512 | ++ * Toshiba | SLC | TC58NVG1S3HTA00 | 8bit/512 | ++ * Toshiba | SLC | TC58NVG1S3ETA00 | 4bit/512 | ++ * Toshiba | SLC | TC58NVG3S0FTA00 | 4bit/512 | ++ * Toshiba | SLC | TC58NVG2S0FTA00 | 4bit/512 | ++ * Toshiba | SLC | TH58NVG2S3HTA00 | 4bit/512 | ++ * Toshiba | TLC | TC58NVG5T2JTA00 | 60bit/1k | ++ * Toshiba | TLC | TC58TEG5DCKTAx0 | 60bit/1k | ++ * Toshiba | MLC | Tx58TEGxDDKTAx0 | | ++ * Samsung | MLC | K9LB(HC/PD/MD)G08U0(1)D | 8bit/512B | ++ * Samsung | MLC | K9GAG08U0E | 24bit/1KB | ++ * Samsung | MLC | K9LBG08U0E | 24bit/1KB | ++ * Samsung | MLC | K9G8G08U0C | 24bit/1KB | ++ * Samsung | MLC | K9GAG08U0F | 24bit/1KB | ++ * Samsung | MLC | K9LBG08U0M | | ++ * Samsung | MLC | K9GBG08U0A | 24bit/1KB | ++ * Samsung | MLC | K9GBG08U0B | 40bit/1KB | ++ * Hynix | MLC | H27UAG8T2A | | ++ * Hynix | MLC | H27UAG8T2B | | ++ * Hynix | MLC | H27UBG8T2A | | ++ * Hynix | MLC | H27UBG8T2BTR | 24bit/1KB | ++ * Hynix | MLC | H27UCG8T2A | 40bit/1KB | ++ * Hynix | MLC | H27UBG8T2C | 40bit/1KB | ++ * MISC | MLC | P1UAGA30AT-GCA | 8bit/512 | ++ * MISC | MLC | PSU8GA30AT-GIA/ASU8GA30IT-G30CA | 4bit/512 | ++ * MISC | SLC | PSU2GA30AT | 1bit/512 | 1.36 ++ * Toshiba | SLC | TC58NVG2S0HTA00 | 24bit/1K | 1.37 ++ * Toshiba | SLC | TC58NVG3S0HTA00 | 24bit/1K | 1.37 ++ * Micron | SLC | MT29F2G08ABAEA | 4bit/512 | ++ * Spansion | SLC | S34ML02G200TFI000 | 24bit/1K | ++ * Spansion | SLC | S34ML04G200TFI000 | 24bit/1K | 1.38 ++ * ++ */ ++static struct nand_flash_special_dev nand_flash_special_table[] = { ++ ++ /****************************** Spansion *******************************/ ++ ++ { /* SLC S34ML02G200TFI000 */ ++ .name = "S34ML02G200TFI000", ++ .id = {0x01, 0xDA, 0x90, 0x95, 0x46, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _256M, ++ .probe = NULL, ++ .pagesize = _2K, ++ .erasesize = _128K, ++ .oobsize = 128, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ ++ { /* SLC S34ML04G200TFI000 */ ++ .name = "S34ML04G200TFI000", ++ .id = {0x01, 0xDC, 0x90, 0x95, 0x56, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _512M, ++ .probe = NULL, ++ .pagesize = _2K, ++ .erasesize = _128K, ++ .oobsize = 128, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ ++ /****************************** Micron *******************************/ ++ ++ { /* MLC 40bit/1k */ ++ .name = "MT29F64G08CBABA", ++ .id = {0x2C, 0x64, 0x44, 0x4B, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _8G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _2M, ++ .oobsize = 744, ++ .options = 0, ++ .read_retry_type = NAND_RR_MICRON, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = NAND_RANDOMIZER | NAND_CHIP_MICRON, ++ }, ++ { /* MLC 40bit/1k */ ++ .name = "MT29F32G08CBADA", ++ .id = {0x2C, 0x44, 0x44, 0x4B, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _2M, ++ .oobsize = 744, ++ .options = 0, ++ .read_retry_type = NAND_RR_MICRON, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* SLC 4bit/512 */ ++ .name = "MT29F8G08ABxBA", ++ .id = {0x2C, 0x38, 0x00, 0x26, 0x85, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _1G, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _512K, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 12bit/512 */ ++ .name = "MT29F16G08CBABx", ++ .id = {0x2C, 0x48, 0x04, 0x46, 0x85, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _2G, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _1M, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k */ ++ .name = "MT29F16G08CBACA", ++ .id = {0x2C, 0x48, 0x04, 0x4A, 0xA5, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _2G, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _1M, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k */ ++ .name = "MT29F32G08CBACA", ++ .id = {0x2C, 0x68, 0x04, 0x4A, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _1M, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k */ ++ .name = "MT29F64G08CxxAA", ++ .id = {0x2C, 0x88, 0x04, 0x4B, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _8G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _2M, ++ .oobsize = 448, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* MLC 24bit/1k 2CE */ ++ .name = "MT29F256G08CJAAA", ++ .id = {0x2C, 0xA8, 0x05, 0xCB, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _16G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _2M, ++ .oobsize = 448, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* MLC 40bit/1k */ ++ .name = "MT29F256G08CMCBB", ++ .id = {0x2C, 0x64, 0x44, 0x4B, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _8G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _2M, ++ .oobsize = 744, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 8bit/512 */ ++ .name = "MT29F8G08ABACA", ++ .id = {0x2C, 0xD3, 0x90, 0xA6, 0x64, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _1G, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _256K, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 8bit/512 */ ++ .name = "MT29F4G08ABAEA", ++ .id = {0x2C, 0xDC, 0x90, 0xA6, 0x54, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _512M, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _256K, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 8bit/512 */ ++ .name = "MT29F2G08ABAFA", ++ .id = {0x2C, 0xDA, 0x90, 0x95, 0x04, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _256M, ++ .probe = NULL, ++ .pagesize = _2K, ++ .erasesize = _128K, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC MT29F2G08ABAEA */ ++ .name = "MT29F2G08ABAEA", ++ .id = {0x2C, 0xDA, 0x90, 0x95, 0x06, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _256M, ++ .probe = NULL, ++ .pagesize = _2K, ++ .erasesize = _128K, ++ .oobsize = 64, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 8bit/512 */ ++ .name = "MT29F16G08ABACA", ++ .id = {0x2C, 0x48, 0x00, 0x26, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _2G, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _512K, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ ++ /****************************** Toshaba *******************************/ ++ ++ { /* MLC 24bit/1k 32nm */ ++ .name = "TC58NVG4D2FTA00", ++ .id = {0x98, 0xD5, 0x94, 0x32, 0x76, 0x55, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _2G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _1M, ++ .oobsize = 448, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k 32nm 2CE*/ ++ .name = "TH58NVG6D2FTA20", ++ .id = {0x98, 0xD7, 0x94, 0x32, 0x76, 0x55, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _1M, ++ .oobsize = 448, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 40bit/1k 24nm */ ++ .name = "TC58NVG5D2HTA00 24nm", ++ .id = {0x98, 0xD7, 0x94, 0x32, 0x76, 0x56, 0x08, 0x00}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _1M, ++ .oobsize = 640, ++ .options = 0, ++ .read_retry_type = NAND_RR_TOSHIBA_24nm, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* MLC 40bit/1k */ ++ .name = "TC58NVG6D2GTA00", ++ .id = {0x98, 0xDE, 0x94, 0x82, 0x76, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _8G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _2M, ++ .oobsize = 640, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 19nm */ ++ .name = "TC58NVG6DCJTA00 19nm", ++ .id = {0x98, 0xDE, 0x84, 0x93, 0x72, 0x57, 0x08, 0x04}, ++ .length = 8, ++ .chipsize = _8G, ++ .probe = NULL, ++ .pagesize = _16K, ++ .erasesize = _4M, ++ .oobsize = 1280, ++ .options = 0, ++ .read_retry_type = NAND_RR_TOSHIBA_24nm, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* MLC 19nm */ ++ .name = "TC58TEG5DCJTA00 19nm", ++ .id = {0x98, 0xD7, 0x84, 0x93, 0x72, 0x57, 0x08, 0x04}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _16K, ++ .erasesize = _4M, ++ .oobsize = 1280, ++ .options = 0, ++ .read_retry_type = NAND_RR_TOSHIBA_24nm, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER | NAND_CHIP_TOSHIBA_TOGGLE_10, ++ }, ++ { /* SLC 8bit/512 */ ++ .name = "TC58NVG0S3HTA00", ++ .id = {0x98, 0xF1, 0x80, 0x15, 0x72, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _128M, ++ .probe = NULL, ++ .pagesize = _2K, ++ .erasesize = _128K, ++ .oobsize = 128, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ /* ++ * Datasheet: read one column of any page in each block. If the ++ * data of the column is 00 (Hex), define the block as a bad ++ * block. ++ */ ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 8bit/512 */ ++ .name = "TC58NVG1S3HTA00", ++ .id = {0x98, 0xDA, 0x90, 0x15, 0x76, 0x16, 0x08, 0x00}, ++ .length = 7, ++ .chipsize = _256M, ++ .probe = NULL, ++ .pagesize = _2K, ++ .erasesize = _128K, ++ .oobsize = 128, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 4bit/512 */ ++ .name = "TC58NVG1S3ETA00", ++ .id = {0x98, 0xDA, 0x90, 0x15, 0x76, 0x14, 0x03, 0x00}, ++ .length = 7, ++ .chipsize = _256M, ++ .probe = NULL, ++ .pagesize = _2K, ++ .erasesize = _128K, ++ .oobsize = 64, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 4bit/512 */ ++ .name = "TC58NVG3S0FTA00", ++ .id = {0x98, 0xD3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08}, ++ .length = 8, ++ .chipsize = _1G, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _256K, ++ .oobsize = 232, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 24bit/1k */ ++ .name = "TC58NVG3S0HTA00", ++ .id = {0x98, 0xD3, 0x91, 0x26, 0x76, 0x16, 0x08, 0x00}, ++ .length = 8, ++ .chipsize = _1G, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _256K, ++ .oobsize = 256, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 24bit/1k */ ++ .name = "TC58NVG2S0HTA00", ++ .id = {0x98, 0xDC, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00}, ++ .length = 8, ++ .chipsize = _512M, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _256K, ++ .oobsize = 256, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 4bit/512 */ ++ .name = "TC58NVG2S0FTA00", ++ .id = {0x98, 0xDC, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08}, ++ .length = 8, ++ .chipsize = _512M, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _256K, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 4bit/512 */ ++ .name = "TH58NVG2S3HTA00", ++ .id = {0x98, 0xDC, 0x91, 0x15, 0x76}, ++ .length = 5, ++ .chipsize = _512M, ++ .probe = NULL, ++ .pagesize = _2K, ++ .erasesize = _128K, ++ .oobsize = 128, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* TLC 60bit/1k 19nm */ ++ .name = "TC58NVG5T2JTA00 19nm TLC", ++ /* datasheet says 6 ids id data, but really has 8 ids. */ ++ .id = {0x98, 0xD7, 0x98, 0x92, 0x72, 0x57, 0x08, 0x10}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _4M, ++ .oobsize = 1024, ++ .options = 0, ++ .read_retry_type = NAND_RR_TOSHIBA_24nm, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* TLC 60bit/1k 19nm */ ++ .name = "TC58TEG5DCKTAx0 19nm MLC", ++ /* datasheet says 6 ids id data, but really has 8 ids. */ ++ .id = {0x98, 0xD7, 0x84, 0x93, 0x72, 0x50, 0x08, 0x04}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _16K, ++ .erasesize = _4M, ++ .oobsize = 1280, ++ .options = 0, ++ .read_retry_type = NAND_RR_TOSHIBA_19nm, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { ++ .name = "Tx58TEGxDDKTAx0 19nm MLC", ++ .id = {0x98, 0xDE, 0x94, 0x93, 0x76, 0x50}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _16K, ++ .erasesize = _4M, ++ .oobsize = 1280, ++ .options = 0, ++ .read_retry_type = NAND_RR_TOSHIBA_19nm, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ /******************************* Samsung ******************************/ ++ { /* MLC 8bit/512B */ ++ .name = "K9LB(HC/PD/MD)G08U0(1)D", ++ .id = {0xEC, 0xD7, 0xD5, 0x29, 0x38, 0x41, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = samsung_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1KB */ ++ .name = "K9GAG08U0E", ++ .id = {0xEC, 0xD5, 0x84, 0x72, 0x50, 0x42, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _2G, ++ .probe = samsung_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1KB */ ++ .name = "K9LBG08U0E", ++ .id = {0xEC, 0xD7, 0xC5, 0x72, 0x54, 0x42, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = samsung_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1KB */ ++ .name = "K9G8G08U0C", ++ .id = {0xEC, 0xD3, 0x84, 0x72, 0x50, 0x42, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _1G, ++ .probe = samsung_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k */ ++ .name = "K9GAG08U0F", ++ .id = {0xEC, 0xD5, 0x94, 0x76, 0x54, 0x43, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _2G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _1M, ++ .oobsize = 512, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC */ ++ .name = "K9LBG08U0M", ++ .id = {0xEC, 0xD7, 0x55, 0xB6, 0x78, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _512K, ++ .oobsize = 128, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k */ ++ .name = "K9GBG08U0A 20nm", ++ .id = {0xEC, 0xD7, 0x94, 0x7A, 0x54, 0x43, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _1M, ++ .oobsize = 640, ++ .options = 0, ++ .read_retry_type = NAND_RR_SAMSUNG, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* MLC 40bit/1k */ ++ .name = "K9GBG08U0B", ++ .id = {0xEC, 0xD7, 0x94, 0x7E, 0x64, 0x44, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _1M, ++ .oobsize = 1024, ++ .options = 0, ++ .read_retry_type = NAND_RR_SAMSUNG, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ ++ /*********************************** Hynix ****************************/ ++ { /* MLC */ ++ .name = "H27UAG8T2A", ++ .id = {0xAD, 0xD5, 0x94, 0x25, 0x44, 0x41, }, ++ .length = 6, ++ .chipsize = _2G, ++ .probe = hynix_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC */ ++ .name = "H27UAG8T2B", ++ .id = {0xAD, 0xD5, 0x94, 0x9A, 0x74, 0x42, }, ++ .length = 6, ++ .chipsize = _2G, ++ .probe = hynix_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC */ ++ .name = "H27UBG8T2A", ++ .id = {0xAD, 0xD7, 0x94, 0x9A, 0x74, 0x42, }, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = hynix_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1K, 26nm TODO: Need read retry, chip is EOS */ ++ .name = "H27UBG8T2BTR 26nm", ++ .id = {0xAD, 0xD7, 0x94, 0xDA, 0x74, 0xC3, }, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _2M, ++ .oobsize = 640, ++ .options = 0, ++ .read_retry_type = NAND_RR_HYNIX_BG_BDIE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* MLC 40bit/1k */ ++ .name = "H27UCG8T2A", ++ .id = {0xAD, 0xDE, 0x94, 0xDA, 0x74, 0xC4, }, ++ .length = 6, ++ .chipsize = _8G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _2M, ++ .oobsize = 640, ++ .options = 0, ++ .read_retry_type = NAND_RR_HYNIX_CG_ADIE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* MLC 40bit/1k */ ++ .name = "H27UBG8T2C", ++ .id = {0xAD, 0xD7, 0x94, 0x91, 0x60, 0x44, }, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = _8K, ++ .erasesize = _2M, ++ .oobsize = 640, ++ .options = 0, ++ .read_retry_type = NAND_RR_HYNIX_BG_CDIE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ ++ /********************** MISC ******************************************/ ++ { /* MLC 8bit/512 */ ++ .name = "P1UAGA30AT-GCA", ++ .id = {0xC8, 0xD5, 0x14, 0x29, 0x34, 0x01, }, ++ .length = 6, ++ .chipsize = _2G, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _512K, ++ .oobsize = 218, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 4bit/512 */ ++ /* ++ * PowerFlash ASU8GA30IT-G30CA ID and MIRA PSU8GA30AT-GIA ID are ++ * the same ID ++ */ ++ .name = "PSU8GA30AT-GIA/ASU8GA30IT-G30CA", ++ .id = {0xC8, 0xD3, 0x90, 0x19, 0x34, 0x01, }, ++ .length = 6, ++ .chipsize = _1G, ++ .probe = NULL, ++ .pagesize = _4K, ++ .erasesize = _256K, ++ .oobsize = 218, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 1bit/512 */ ++ .name = "PSU2GA30AT", ++ .id = {0x7F, 0x7F, 0x7F, 0x7F, 0xC8, 0xDA, 0x00, 0x15, }, ++ .length = 8, ++ .chipsize = _256M, ++ .probe = NULL, ++ .pagesize = _2K, ++ .erasesize = _128K, ++ .oobsize = 64, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ {{0}, 0, 0, 0, 0, 0, 0, 0, 0}, ++}; ++ ++struct nand_dev_t g_nand_dev; ++/*****************************************************************************/ ++struct nand_flash_dev *hifmc_get_spl_flash_type(struct mtd_info *mtd, ++ unsigned char *id) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct nand_flash_special_dev *spl_dev = nand_flash_special_table; ++ struct nand_flash_dev *type = &g_nand_dev.flash_dev; ++ struct nand_dev_t *nand_dev = &g_nand_dev; ++ ++ FMC_PR(BT_DBG, "\t *-Start find special nand flash\n"); ++ ++ pr_info("Nand ID: %#X %#X %#X %#X %#X %#X %#X %#X\n", id[0], id[1], ++ id[2], id[3], id[4], id[5], id[6], id[7]); ++ ++ for (; spl_dev->length; spl_dev++) { ++ if (memcmp(id, spl_dev->id, spl_dev->length)) ++ continue; ++ ++ FMC_PR(BT_DBG, "\t |-Found special Nand flash: %s\n", ++ spl_dev->name); ++ ++ if (spl_dev->probe) { ++ type = spl_dev->probe(id); ++ } else { ++ type->options = spl_dev->options; ++ type->pagesize = spl_dev->pagesize; ++ type->erasesize = spl_dev->erasesize; ++ type->oobsize = spl_dev->oobsize; ++ } ++ ++ type->name = spl_dev->name; ++ type->id_len = spl_dev->length; ++ memcpy(type->id, id, type->id_len); ++ type->chipsize = (unsigned int)(spl_dev->chipsize >> 20); ++ FMC_PR(BT_DBG, "\t |-Save struct nand_flash_dev info\n"); ++ ++ memcpy(nand_dev->ids, id, MAX_NAND_ID_LEN); ++ nand_dev->oobsize = type->oobsize; ++ nand_dev->flags = spl_dev->flags; ++ nand_dev->read_retry_type = spl_dev->read_retry_type; ++ FMC_PR(BT_DBG, "\t |-Save struct nand_dev_t information\n"); ++ ++ mtd->size = spl_dev->chipsize; ++ ++ return type; ++ } ++ nand_dev->read_retry_type = NAND_RR_NONE; ++ ++ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); ++ chip->read_byte(mtd); ++ chip->read_byte(mtd); ++ ++ FMC_PR(BT_DBG, "\t *-Not found special nand flash\n"); ++ ++ return NULL; ++} ++ ++/*****************************************************************************/ ++void hifmc_spl_ids_register(void) ++{ ++ pr_info("Special NAND id table Version %s\n", DRV_VERSION); ++ get_spi_nand_flash_type_hook = hifmc_get_spl_flash_type; ++} +diff --git a/drivers/mtd/nand/hinfc610/Kconfig b/drivers/mtd/nand/hinfc610/Kconfig +new file mode 100644 +index 0000000..7d9dbbe +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/Kconfig +@@ -0,0 +1,100 @@ ++menuconfig MTD_NAND_HINFC610 ++ tristate "Hisilicon NAND Controller v610 device Support" ++ depends on MTD_NAND ++ default y if ARCH_HI3531D ++ select YAFFS_FS ++ select MISC_FILESYSTEMS ++ select MTD_BLOCK ++ select YAFFS_YAFFS2 ++ help ++ When the config is set, the kernel will support Hisilicon ++ NAND Controller v610 device. It means that the kernel would ++ control the nand flash with the nand controller v610 device ++ in operation. ++ ++if MTD_NAND_HINFC610 ++ ++config MTD_PARTITION_FROM_DTS ++ bool "get the mtd partition from devicetree" ++ default n ++ help ++ Get the mtd partition info from devicetree list. ++ ++config HINFC610_MAX_CHIP ++ int "number of nand flash chip (1,4)" ++ default 1 ++ help ++ nand controller v610 device only support 1 or 2 nand flash chip, ++ your should not config other value. ++ ++config HINFC610_DBG_NAND_DEBUG ++ bool "Debug: create debug file to control debug type" ++ default y ++ help ++ When the config is set, the kernel will add the "debug" file ++ to control debug type. When the config is set, we could choose ++ the debugging type to display the informations of the nand controller ++ v610 device in operation. ++ ++config HINFC610_DBG_NAND_DUMP ++ bool "Debug: display read/write/erase process nand data" ++ depends on HINFC610_DBG_NAND_DEBUG ++ default y ++ default n if (ARCH_HI3531D) ++ help ++ When the config is set, the kernel will add "dump" file to ++ display all nand operation and data.When the "HINFC610_DBG_NAND_DEBUG" ++ has been set, the nand controller v610 device will display ++ all the operations and data. ++ ++config HINFC610_DBG_NAND_ERASE_COUNT ++ bool "Debug: display last erase count" ++ depends on HINFC610_DBG_NAND_DEBUG ++ default y ++ default n if (ARCH_HI3531D) ++ help ++ When the config is set, the kernel will add "erase_count" file ++ to display last erase count. When the "HINFC610_DBG_NAND_DEBUG" ++ has been set, the nand controller v610 device will display ++ the last erase count. ++ ++config HINFC610_DBG_NAND_ECC_COUNT ++ bool "Debug: display last ecc count." ++ depends on HINFC610_DBG_NAND_DEBUG ++ default y ++ default n if (ARCH_HI3531D) ++ help ++ When the config is set, the kernel will add "ecc_count" ++ to display last ecc count. When the "HINFC610_DBG_NAND_DEBUG" ++ has been set, the nand controller v610 device will display ++ the last ecc count. ++ ++config HINFC610_DBG_NAND_READ_RETRY ++ bool "Debug: display read_retry process" ++ depends on HINFC610_DBG_NAND_DEBUG ++ default y ++ default n if (ARCH_HI3531D) ++ help ++ When the config is set, the kernel will add read_retry file ++ to display read_retry process. ++ ++choice ++ prompt "Pagesize and Ecc Type Select" ++ default HINFC610_AUTO_PAGESIZE_ECC if ARCH_HI3531D ++ ++config HINFC610_AUTO_PAGESIZE_ECC ++ bool "Auto" ++ help ++ When the config is set, pagesize and ecc type will use ++ hardware config. When we replace the flash, the ++ controller will identify the pagesize and ecc type of ++ the flash. ++ ++config HINFC610_PAGESIZE_AUTO_ECC_NONE ++ bool "Pagesize Auto, Ecc None" ++ help ++ select pagesize 2K, ecc none. ++ ++endchoice ++ ++endif # MTD_NAND_HINFC610 +diff --git a/drivers/mtd/nand/hinfc610/Makefile b/drivers/mtd/nand/hinfc610/Makefile +new file mode 100644 +index 0000000..9ef9acd +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/Makefile +@@ -0,0 +1,19 @@ ++ ++obj-$(CONFIG_MTD_NAND_HINFC610) += hinfc610.o hinfc610_os.o hinfc610_gen.o \ ++ hinfc620_gen.o hinfc610_dbg_inf.o \ ++ hinfc610_read_retry_hynix_bg_cdie.o \ ++ hinfc610_read_retry_hynix_bg_bdie.o \ ++ hinfc610_read_retry_hynix_cg_adie.o \ ++ hinfc610_read_retry_micron.o \ ++ hinfc610_read_retry_samsung.o \ ++ hinfc610_read_retry_toshiba.o \ ++ hinfc610_read_retry.o \ ++ hinfc610_sync.o \ ++ hinfc610_sync_onfi_23.o \ ++ hinfc610_sync_toggle.o ++ ++obj-$(CONFIG_HINFC610_DBG_NAND_DEBUG) += hinfc610_dbg.o hinfc610_dbg_ecc_dump.o ++obj-$(CONFIG_HINFC610_DBG_NAND_DUMP) += hinfc610_dbg_dump.o ++obj-$(CONFIG_HINFC610_DBG_NAND_ERASE_COUNT) += hinfc610_dbg_erase_count.o ++obj-$(CONFIG_HINFC610_DBG_NAND_ECC_COUNT) += hinfc610_dbg_ecc_count.o ++obj-$(CONFIG_HINFC610_DBG_NAND_READ_RETRY) += hinfc610_dbg_read_retry.o +diff --git a/drivers/mtd/nand/hinfc610/hinfc610.c b/drivers/mtd/nand/hinfc610/hinfc610.c +new file mode 100644 +index 0000000..4be2451 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610.c +@@ -0,0 +1,1154 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#define pr_fmt(fmt) "hinfc610: " fmt ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++#include "hinfc610_dbg_inf.h" ++#include "hinfc610_gen.h" ++#include "hinfc620_gen.h" ++#include "hinfc610_sync.h" ++#include "hinfc610_read_retry.h" ++ ++/*****************************************************************************/ ++static unsigned int get_8bits(unsigned char byte) ++{ ++ int ix = 0; ++ int num = 0; ++ ++ if (byte == 0xFF) ++ return 8; ++ if (!byte) ++ return 0; ++ ++ while (ix++ < 8) { ++ if ((byte & 1)) ++ num++; ++ byte = (byte >> 1); ++ } ++ return num; ++} ++/*****************************************************************************/ ++ ++static unsigned int get_16bits(unsigned short byte) ++{ ++ int ix = 0; ++ int num = 0; ++ ++ if (byte == 0xFFFF) ++ return 16; ++ if (!byte) ++ return 0; ++ ++ while (ix++ < 16) { ++ if ((byte & 1)) ++ num++; ++ byte = (byte >> 1); ++ } ++ return num; ++} ++/*****************************************************************************/ ++ ++static void hinfc610_dma_transfer(struct hinfc_host *host, int todev) ++{ ++ unsigned long reg_val; ++ unsigned int dma_addr = (unsigned int)host->dma_buffer; ++ ++ hinfc_write(host, dma_addr, HINFC610_DMA_ADDR_DATA); ++ ++ dma_addr += HINFC610_DMA_ADDR_OFFSET; ++ hinfc_write(host, dma_addr, HINFC610_DMA_ADDR_DATA1); ++ ++ dma_addr += HINFC610_DMA_ADDR_OFFSET; ++ hinfc_write(host, dma_addr, HINFC610_DMA_ADDR_DATA2); ++ ++ dma_addr += HINFC610_DMA_ADDR_OFFSET; ++ hinfc_write(host, dma_addr, HINFC610_DMA_ADDR_DATA3); ++ ++ /* 32K PAGESIZE need below. */ ++ dma_addr += HINFC610_DMA_ADDR_OFFSET; ++ hinfc_write(host, dma_addr, HINFC610_DMA_ADDR_DATA4); ++ ++ dma_addr += HINFC610_DMA_ADDR_OFFSET; ++ hinfc_write(host, dma_addr, HINFC610_DMA_ADDR_DATA5); ++ ++ dma_addr += HINFC610_DMA_ADDR_OFFSET; ++ hinfc_write(host, dma_addr, HINFC610_DMA_ADDR_DATA6); ++ ++ dma_addr += HINFC610_DMA_ADDR_OFFSET; ++ hinfc_write(host, dma_addr, HINFC610_DMA_ADDR_DATA7); ++ ++ hinfc_write(host, host->dma_oob, HINFC610_DMA_ADDR_OOB); ++ ++ if (host->ecctype == NAND_ECC_NONE) { ++ hinfc_write(host, ++ ((host->oobsize & HINFC610_DMA_LEN_OOB_MASK) ++ << HINFC610_DMA_LEN_OOB_SHIFT), ++ HINFC610_DMA_LEN); ++ ++ hinfc_write(host, ++ HINFC610_DMA_PARA_DATA_RW_EN ++ | HINFC610_DMA_PARA_OOB_RW_EN, ++ HINFC610_DMA_PARA); ++ } else ++ hinfc_write(host, ++ HINFC610_DMA_PARA_DATA_RW_EN ++ | HINFC610_DMA_PARA_OOB_RW_EN ++ | HINFC610_DMA_PARA_DATA_EDC_EN ++ | HINFC610_DMA_PARA_OOB_EDC_EN, ++ HINFC610_DMA_PARA); ++ ++ reg_val = (HINFC610_DMA_CTRL_DMA_START ++ | HINFC610_DMA_CTRL_BURST4_EN ++ | HINFC610_DMA_CTRL_BURST8_EN ++ | HINFC610_DMA_CTRL_BURST16_EN ++ | ((host->addr_cycle == 4 ? 1 : 0) ++ << HINFC610_DMA_CTRL_ADDR_NUM_SHIFT) ++ | (((unsigned int)host->chipselect & HINFC610_DMA_CTRL_CS_MASK) ++ << HINFC610_DMA_CTRL_CS_SHIFT)); ++ ++ if (todev) ++ reg_val |= HINFC610_DMA_CTRL_WE; ++ ++ hinfc_write(host, reg_val, HINFC610_DMA_CTRL); ++ ++ do { ++ unsigned int timeout = 0xF0000000; ++ ++ while ((hinfc_read(host, HINFC610_DMA_CTRL)) ++ & HINFC610_DMA_CTRL_DMA_START && timeout) { ++ _cond_resched(); ++ timeout--; ++ } ++ if (!timeout) ++ PR_BUG("Wait DMA finish timeout.\n"); ++ } while (0); ++} ++/*****************************************************************************/ ++ ++static void hinfc610_sync_entry(struct hinfc_host *host) ++{ ++ struct nand_sync *sync = host->sync; ++ struct nand_chip *chip = host->chip; ++ ++ if (!sync) { ++ PR_BUG("this NAND not support sync feature.\n"); ++ return; ++ } ++ ++ if (HINFC610_IS_SYNC(host)) { ++ PR_BUG("this NAND not support sync feature.\n"); ++ return; ++ } ++ ++ if (sync->enable) ++ sync->enable(chip); ++ ++ clk_prepare_enable(host->clk); ++ ++ switch (sync->type) { ++ case NAND_TYPE_TOGGLE_10: ++ host->NFC_CON |= HINFC610_CON_NF_MODE_TOGGLE; ++ host->NFC_CON_ECC_NONE |= HINFC610_CON_NF_MODE_TOGGLE; ++ break; ++ ++ case NAND_TYPE_ONFI_23: ++ host->NFC_CON |= HINFC610_CON_NF_MODE_ONFI_23; ++ host->NFC_CON_ECC_NONE |= HINFC610_CON_NF_MODE_ONFI_23; ++ break; ++ ++ case NAND_TYPE_ONFI_30: ++ host->NFC_CON |= HINFC610_CON_NF_MODE_ONFI_30; ++ host->NFC_CON_ECC_NONE |= HINFC610_CON_NF_MODE_ONFI_30; ++ break; ++ ++ default: ++ PR_BUG("Unsupport sync type 0x%08X.\n", sync->type); ++ break; ++ } ++} ++/*****************************************************************************/ ++ ++static void hinfc610_sync_exit(struct hinfc_host *host) ++{ ++ struct nand_sync *sync = host->sync; ++ struct nand_chip *chip = host->chip; ++ ++ if (!HINFC610_IS_SYNC(host)) { ++ PR_BUG("Current already exit from sync feature.\n"); ++ return; ++ } ++ ++ if (sync->disable) ++ sync->disable(chip); ++ ++ host->NFC_CON &= ~HINFC610_CON_NF_MODE_MASK; ++ host->NFC_CON_ECC_NONE &= ~HINFC610_CON_NF_MODE_MASK; ++ ++ clk_disable_unprepare(host->clk); ++} ++/*****************************************************************************/ ++ ++void hinfc610_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) ++{ ++ int is_cache_invalid = 1; ++ struct nand_chip *chip = mtd->priv; ++ struct hinfc_host *host = chip->priv; ++ ++ if (ctrl & NAND_ALE) { ++ unsigned int addr_value = 0; ++ unsigned int addr_offset = 0; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ host->addr_cycle = 0x0; ++ host->addr_value[0] = 0x0; ++ host->addr_value[1] = 0x0; ++ } ++ addr_offset = host->addr_cycle << 3; ++ ++ if (host->addr_cycle >= HINFC610_ADDR_CYCLE_MASK) { ++ addr_offset = ++ (host->addr_cycle - HINFC610_ADDR_CYCLE_MASK) << 3; ++ addr_value = 1; ++ } ++ ++ host->addr_value[addr_value] |= ++ ((dat & 0xff) << addr_offset); ++ ++ host->addr_cycle++; ++ } ++ ++ if ((ctrl & NAND_CLE) && (ctrl & NAND_CTRL_CHANGE)) { ++ host->command = dat & 0xff; ++ switch (host->command) { ++ case NAND_CMD_PAGEPROG: ++ host->send_cmd_pageprog(host); ++ hinfc610_dbg_write(host); ++ break; ++ ++ case NAND_CMD_READSTART: ++ is_cache_invalid = 0; ++ host->send_cmd_readstart(host); ++ hinfc610_dbg_read(host); ++ ++ break; ++ ++ case NAND_CMD_ERASE2: ++ host->send_cmd_erase(host); ++ hinfc610_dbg_erase(host); ++ ++ break; ++ ++ case NAND_CMD_READID: ++ memset((unsigned char *)(chip->IO_ADDR_R), 0, 0x10); ++ host->send_cmd_readid(host); ++ break; ++ ++ case NAND_CMD_STATUS: ++ host->send_cmd_status(host); ++ break; ++ ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_READ0: ++ break; ++ case NAND_CMD_RESET: ++ host->send_cmd_reset(host, host->chipselect); ++ break; ++ ++ default: ++ break; ++ } ++ } ++ ++ if ((dat == NAND_CMD_NONE) && host->addr_cycle) { ++ if (host->command == NAND_CMD_SEQIN || ++ host->command == NAND_CMD_READ0 || ++ host->command == NAND_CMD_READID) { ++ host->offset = 0x0; ++ host->column = (host->addr_value[0] & 0xffff); ++ } ++ } ++ ++ if (is_cache_invalid) { ++ host->cache_addr_value[0] = ~0; ++ host->cache_addr_value[1] = ~0; ++ } ++} ++/*****************************************************************************/ ++ ++static int hinfc610_send_cmd_pageprog(struct hinfc_host *host) ++{ ++ if (*host->bbm != 0xFF && *host->bbm != 0x00) ++ pr_warn("Attempt to write an invalid bbm. page: 0x%08x, mark: 0x%02x, current process(pid): %s(%d).\n", ++ GET_PAGE_INDEX(host), *host->bbm, ++ current->comm, current->pid); ++ ++ if (IS_NAND_SYNC_ASYNC(host)) ++ hinfc610_sync_entry(host); ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ hinfc_write(host, host->addr_value[0] & 0xffff0000, HINFC610_ADDRL); ++ hinfc_write(host, host->addr_value[1], HINFC610_ADDRH); ++ hinfc_write(host, ++ ((NAND_CMD_STATUS << 16) | (NAND_CMD_PAGEPROG << 8) | ++ NAND_CMD_SEQIN), ++ HINFC610_CMD); ++ ++ *host->epm = 0x0000; ++ ++ hinfc610_dma_transfer(host, 1); ++ ++ if (IS_NAND_SYNC_ASYNC(host)) ++ hinfc610_sync_exit(host); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_get_data_status(struct hinfc_host *host) ++{ ++ unsigned int page_status = 0; ++ ++ if (IS_PS_UN_ECC(host)) ++ page_status = HINFC610_PS_UC_ECC; ++ ++ /* this is block start address */ ++ if (!((host->addr_value[0] >> 16) & host->block_page_mask)) { ++ ++ /* it is a bad block */ ++ if (*host->bbm == 0x00) { ++ page_status |= HINFC610_PS_BAD_BLOCK; ++ goto out; ++ } ++ ++ if (*host->bbm != 0xFF) { ++ page_status |= HINFC610_PS_BBM_ERROR; ++ ++ /* ++ * if there are more than 2 bits flipping, it is ++ * maybe a bad block ++ */ ++ if (!IS_PS_UN_ECC(host) || get_8bits(*host->bbm) < 6) { ++ page_status |= HINFC610_PS_BAD_BLOCK; ++ goto out; ++ } ++ } ++ } ++ ++ if (*host->epm == 0x0000) ++ goto out; ++ ++ if (*host->epm == 0xFFFF) { ++ page_status |= HINFC610_PS_EMPTY_PAGE; ++ goto out; ++ } ++ ++ page_status |= HINFC610_PS_EPM_ERROR; ++ ++ if (IS_PS_UN_ECC(host) && get_16bits(*host->epm) > 12) { ++ page_status |= HINFC610_PS_EMPTY_PAGE; ++ goto out; ++ } ++ ++out: ++ return page_status; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_do_read_retry(struct hinfc_host *host) ++{ ++ int ix; ++ ++ for (ix = 1; IS_PS_UN_ECC(host) && ix < host->read_retry->count; ix++) { ++ ++ hinfc_write(host, HINFC610_INTCLR_UE | HINFC610_INTCLR_CE, ++ HINFC610_INTCLR); ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ host->read_retry->set_rr_param(host, ix); ++ ++ /* enable ecc and randomizer */ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ hinfc_write(host, HINFC610_INTCLR_UE | HINFC610_INTCLR_CE, ++ HINFC610_INTCLR); ++ hinfc_write(host, host->NFC_CON, HINFC610_CON); ++ hinfc_write(host, host->addr_value[0] & 0xffff0000, ++ HINFC610_ADDRL); ++ hinfc_write(host, host->addr_value[1], HINFC610_ADDRH); ++ hinfc_write(host, ++ HINFC_CMD_SEQ(NAND_CMD_READ0, NAND_CMD_READSTART), ++ HINFC610_CMD); ++ ++ hinfc610_dma_transfer(host, 0); ++ ++ if (hinfc_read(host, HINFC610_INTS) & HINFC610_INTS_UE) ++ host->page_status |= HINFC610_PS_UC_ECC; ++ else ++ host->page_status &= ~HINFC610_PS_UC_ECC; ++ } ++ ++ host->page_status = hinfc610_get_data_status(host); ++ ++ hinfc610_dbg_read_retry(host, ix); ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ host->read_retry->reset_rr_param(host); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_send_cmd_readstart(struct hinfc_host *host) ++{ ++ if ((host->addr_value[0] == host->cache_addr_value[0]) && ++ (host->addr_value[1] == host->cache_addr_value[1])) ++ return 0; ++ ++ if (IS_NAND_SYNC_ASYNC(host)) ++ hinfc610_sync_entry(host); ++ ++ host->page_status = 0; ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ hinfc_write(host, HINFC610_INTCLR_UE | HINFC610_INTCLR_CE, ++ HINFC610_INTCLR); ++ hinfc_write(host, host->NFC_CON, HINFC610_CON); ++ hinfc_write(host, host->addr_value[0] & 0xffff0000, HINFC610_ADDRL); ++ hinfc_write(host, host->addr_value[1], HINFC610_ADDRH); ++ hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0, ++ HINFC610_CMD); ++ ++ hinfc610_dma_transfer(host, 0); ++ ++ if (hinfc_read(host, HINFC610_INTS) & HINFC610_INTS_UE) ++ host->page_status |= HINFC610_PS_UC_ECC; ++ ++ if (host->read_retry || IS_NAND_RANDOM(host)) { ++ host->page_status |= hinfc610_get_data_status(host); ++ ++ if (IS_PS_EMPTY_PAGE(host)) { ++ /* ++ * oob area used by yaffs2 only 32 bytes, ++ * so we only fill 32 bytes. ++ */ ++ if (IS_NAND_RANDOM(host)) ++ memset(host->buffer, 0xFF, ++ host->pagesize + host->oobsize); ++ ++ } else if (!IS_PS_BAD_BLOCK(host)) { ++ /* if NAND chip support read retry */ ++ if (IS_PS_UN_ECC(host) && host->read_retry) ++ hinfc610_do_read_retry(host); ++ ++ } /* 'else' NAND have a bad block, do nothing. */ ++ } ++ ++ if (IS_NAND_SYNC_ASYNC(host)) ++ hinfc610_sync_exit(host); ++ ++ host->cache_addr_value[0] = host->addr_value[0]; ++ host->cache_addr_value[1] = host->addr_value[1]; ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_send_cmd_erase(struct hinfc_host *host) ++{ ++ unsigned int regval; ++ ++ /* Don't case the read retry config */ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, host->addr_value[0], HINFC610_ADDRL); ++ hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1, ++ HINFC610_CMD); ++ ++ regval = HINFC610_OP_WAIT_READY_EN ++ | HINFC610_OP_CMD2_EN ++ | HINFC610_OP_CMD1_EN ++ | HINFC610_OP_ADDR_EN ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) ++ << HINFC610_OP_NF_CS_SHIFT) ++ | ((host->addr_cycle & HINFC610_OP_ADDR_CYCLE_MASK) ++ << HINFC610_OP_ADDR_CYCLE_SHIFT); ++ ++ hinfc_write(host, regval, HINFC610_OP); ++ ++ WAIT_CONTROLLER_FINISH(); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_send_cmd_sync_readid(struct hinfc_host *host) ++{ ++ unsigned int regval; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, HINFC610_NANDINFO_LEN, HINFC610_DATA_NUM); ++ hinfc_write(host, NAND_CMD_READID, HINFC610_CMD); ++ hinfc_write(host, 0, HINFC610_ADDRL); ++ ++ /* no need to config HINFC610_OP_WAIT_READY_EN, here not config. */ ++ regval = HINFC610_OP_CMD1_EN ++ | HINFC610_OP_ADDR_EN ++ | HINFC610_OP_READ_DATA_EN ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) ++ << HINFC610_OP_NF_CS_SHIFT) ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT) ++ | HINFC610_OP_READID_EN ++ | HINFC610_OP_RW_REG_EN; ++ ++ hinfc_write(host, regval, HINFC610_OP); ++ ++ host->addr_cycle = 0x0; ++ ++ WAIT_CONTROLLER_FINISH(); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_send_cmd_async_readid(struct hinfc_host *host) ++{ ++ unsigned int regval; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, HINFC610_NANDINFO_LEN, HINFC610_DATA_NUM); ++ hinfc_write(host, NAND_CMD_READID, HINFC610_CMD); ++ hinfc_write(host, 0, HINFC610_ADDRL); ++ ++ /* no need to config HINFC610_OP_WAIT_READY_EN, here not config. */ ++ regval = HINFC610_OP_CMD1_EN ++ | HINFC610_OP_ADDR_EN ++ | HINFC610_OP_READ_DATA_EN ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) ++ << HINFC610_OP_NF_CS_SHIFT) ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT); ++ ++ hinfc_write(host, regval, HINFC610_OP); ++ ++ host->addr_cycle = 0x0; ++ ++ WAIT_CONTROLLER_FINISH(); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_send_cmd_readid(struct hinfc_host *host) ++{ ++ if (HINFC610_IS_SYNC(host)) ++ return hinfc610_send_cmd_sync_readid(host); ++ else ++ return hinfc610_send_cmd_async_readid(host); ++} ++/*****************************************************************************/ ++ ++static int hinfc610_enable_ecc_randomizer(struct hinfc_host *host, int ecc_en, ++ int randomizer_en) ++{ ++ unsigned int nfc_con; ++ ++ if (IS_NAND_RANDOM(host)) { ++ if (randomizer_en) { ++ host->NFC_CON |= HINFC610_CON_RANDOMIZER_EN; ++ host->NFC_CON_ECC_NONE |= HINFC610_CON_RANDOMIZER_EN; ++ } else { ++ host->NFC_CON &= ~HINFC610_CON_RANDOMIZER_EN; ++ host->NFC_CON_ECC_NONE &= ~HINFC610_CON_RANDOMIZER_EN; ++ } ++ } ++ ++ nfc_con = (ecc_en ? host->NFC_CON : host->NFC_CON_ECC_NONE); ++ ++ hinfc_write(host, nfc_con, HINFC610_CON); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_send_cmd_status(struct hinfc_host *host) ++{ ++ unsigned int regval; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, HINFC610_NANDINFO_LEN, HINFC610_DATA_NUM); ++ hinfc_write(host, NAND_CMD_STATUS, HINFC610_CMD); ++ ++ /* no need config HINFC610_OP_WAIT_READY_EN, here not config */ ++ regval = HINFC610_OP_CMD1_EN ++ | HINFC610_OP_READ_DATA_EN ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) ++ << HINFC610_OP_NF_CS_SHIFT); ++ ++ hinfc_write(host, regval, HINFC610_OP); ++ ++ WAIT_CONTROLLER_FINISH(); ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++static int hinfc610_send_cmd_async_reset(struct hinfc_host *host, ++ int chipselect) ++{ ++ unsigned int regval; ++ ++ hinfc_write(host, NAND_CMD_RESET, HINFC610_CMD); ++ ++ /* need to config HINFC610_OP_WAIT_READY_EN */ ++ regval = HINFC610_OP_CMD1_EN ++ | ((((unsigned int)chipselect & HINFC610_OP_NF_CS_MASK) ++ << HINFC610_OP_NF_CS_SHIFT) ++ | HINFC610_OP_WAIT_READY_EN); ++ ++ hinfc_write(host, regval, HINFC610_OP); ++ ++ WAIT_CONTROLLER_FINISH(); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_send_cmd_sync_reset(struct hinfc_host *host, ++ int chipselect) ++{ ++ unsigned int regval; ++ ++ /* ++ * Regarding the ONFI chip sync mode, ++ * NAND_CMD_SYNC_RESET make chip remain sync mode. ++ * But NAND_CMD_RESET will change chip mode to async mode. ++ */ ++ hinfc_write(host, NAND_CMD_SYNC_RESET, HINFC610_CMD); ++ ++ /* need to config HINFC610_OP_WAIT_READY_EN */ ++ regval = HINFC610_OP_CMD1_EN ++ | (((unsigned int)chipselect & HINFC610_OP_NF_CS_MASK) ++ << HINFC610_OP_NF_CS_SHIFT) ++ | HINFC610_OP_WAIT_READY_EN; ++ ++ hinfc_write(host, regval, HINFC610_OP); ++ ++ WAIT_CONTROLLER_FINISH(); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_send_cmd_reset(struct hinfc_host *host, int chipselect) ++{ ++ if (HINFC610_IS_SYNC(host)) ++ return hinfc610_send_cmd_sync_reset(host, chipselect); ++ else ++ return hinfc610_send_cmd_async_reset(host, chipselect); ++} ++/*****************************************************************************/ ++ ++int hinfc610_dev_ready(struct mtd_info *mtd) ++{ ++ return 0x1; ++} ++/*****************************************************************************/ ++ ++void hinfc610_select_chip(struct mtd_info *mtd, int chipselect) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hinfc_host *host = chip->priv; ++ ++ if (chipselect < 0) ++ return; ++ ++ if (chipselect > CONFIG_HINFC610_MAX_CHIP) ++ PR_BUG("invalid chipselect: %d\n", chipselect); ++ ++ host->chipselect = chipselect; ++} ++/*****************************************************************************/ ++ ++uint8_t hinfc610_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hinfc_host *host = chip->priv; ++ ++ if (host->command == NAND_CMD_STATUS) ++ return readb(chip->IO_ADDR_R); ++ ++ host->offset++; ++ ++ if (host->command == NAND_CMD_READID) ++ return readb(chip->IO_ADDR_R + host->offset - 1); ++ ++ return readb(host->buffer + host->column + host->offset - 1); ++} ++/*****************************************************************************/ ++ ++u16 hinfc610_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hinfc_host *host = chip->priv; ++ ++ host->offset += 2; ++ return readw(host->buffer + host->column + host->offset - 2); ++} ++/*****************************************************************************/ ++ ++void hinfc610_write_buf(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hinfc_host *host = chip->priv; ++ ++ memcpy(host->buffer + host->column + host->offset, buf, len); ++ host->offset += len; ++} ++/*****************************************************************************/ ++static void hinfc610_ecc_err_num_count(struct mtd_info *mtd, ++ uint8_t ecc_st, int reg) ++{ ++ u_char err_num; ++ ++ if (ecc_st > 4) ++ ecc_st = 4; ++ ++ while (ecc_st) { ++ err_num = GET_ECC_ERR_NUM(--ecc_st, reg); ++ if (err_num == 0xff) ++ mtd->ecc_stats.failed++; ++ else ++ mtd->ecc_stats.corrected += err_num; ++ } ++} ++ ++/*****************************************************************************/ ++ ++void hinfc610_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct hinfc_host *host = chip->priv; ++ int reg; ++ uint8_t ecc_step = host->pagesize >> 10; ++ ++ memcpy(buf, host->buffer + host->column + host->offset, len); ++ host->offset += len; ++ ++ /* 2K or 4K or 8K(1) or 16K(1-1) pagesize */ ++ reg = hinfc_read(host, HINFC_ECC_ERR_NUM0_BUF0); ++ hinfc610_ecc_err_num_count(mtd, ecc_step, reg); ++ ++ if (ecc_step > 4) { ++ /* 8K(2) or 16K(1-2) pagesize */ ++ reg = hinfc_read(host, HINFC_ECC_ERR_NUM1_BUF0); ++ hinfc610_ecc_err_num_count(mtd, ecc_step, reg); ++ if (ecc_step > 8) { ++ /* 16K(2-1) pagesize */ ++ reg = hinfc_read(host, HINFC_ECC_ERR_NUM0_BUF1); ++ hinfc610_ecc_err_num_count(mtd, ecc_step, reg); ++ /* 16K(2-2) pagesize */ ++ reg = hinfc_read(host, HINFC_ECC_ERR_NUM1_BUF1); ++ hinfc610_ecc_err_num_count(mtd, ecc_step, reg); ++ } ++ } ++} ++/*****************************************************************************/ ++/* ++ * 'host->epm' only use the first oobfree[0] field, it looks very simple, But... ++ */ ++static struct nand_ecclayout nand_ecc_default = { ++ .oobfree = {{2, 30} } ++}; ++ ++/*****************************************************************************/ ++ ++static struct nand_config_info hinfc610_soft_auto_config_table[] = { ++ {NAND_PAGE_16K, NAND_ECC_64BIT, 1824/*1824*/, &nand_ecc_default}, ++ {NAND_PAGE_16K, NAND_ECC_40BIT, 1200/*1152*/, &nand_ecc_default}, ++ {NAND_PAGE_16K, NAND_ECC_NONE, 32 , &nand_ecc_default}, ++ ++ {NAND_PAGE_8K, NAND_ECC_64BIT, 928 /*928*/, &nand_ecc_default}, ++ {NAND_PAGE_8K, NAND_ECC_40BIT, 600 /*592*/, &nand_ecc_default}, ++ {NAND_PAGE_8K, NAND_ECC_24BIT, 368 /*368*/, &nand_ecc_default}, ++ {NAND_PAGE_8K, NAND_ECC_NONE, 32, &nand_ecc_default}, ++ ++ {NAND_PAGE_4K, NAND_ECC_24BIT, 200 /*200*/, &nand_ecc_default}, ++ {NAND_PAGE_4K, NAND_ECC_4BIT_512, 128 /*88*/, &nand_ecc_default}, ++ {NAND_PAGE_4K, NAND_ECC_NONE, 32, &nand_ecc_default}, ++ ++ {NAND_PAGE_2K, NAND_ECC_24BIT, 128 /*116*/, &nand_ecc_default}, ++ {NAND_PAGE_2K, NAND_ECC_4BIT_512, 64 /*60*/, &nand_ecc_default}, ++ {NAND_PAGE_2K, NAND_ECC_NONE, 32, &nand_ecc_default}, ++ ++ {0, 0, 0, NULL}, ++}; ++/*****************************************************************************/ ++/* used the best correct arithmetic. */ ++struct nand_config_info *hinfc610_get_best_ecc(struct mtd_info *mtd) ++{ ++ struct nand_config_info *best = NULL; ++ struct nand_config_info *config = hinfc610_soft_auto_config_table; ++ ++ for (; config->layout; config++) { ++ if (nandpage_type2size(config->pagetype) != mtd->writesize) ++ continue; ++ ++ if (mtd->oobsize < config->oobsize) ++ continue; ++ ++ if (!best || (best->ecctype < config->ecctype)) ++ best = config; ++ } ++ ++ if (!best) ++ PR_BUG(ERSTR_DRIVER ++ "Driver does not support the pagesize(%d) " ++ "and oobsize(%d).\n", ++ mtd->writesize, mtd->oobsize); ++ ++ return best; ++} ++/*****************************************************************************/ ++/* force the pagesize and ecctype */ ++struct nand_config_info *hinfc610_force_ecc(struct mtd_info *mtd, int pagetype, ++ int oobsize, char *cfgmsg, ++ int allow_pagediv) ++{ ++ struct nand_config_info *fit = NULL; ++ struct nand_config_info *config = hinfc610_soft_auto_config_table; ++ ++ for (; config->layout; config++) { ++ if (config->pagetype == pagetype ++ && config->oobsize <= oobsize) { ++ fit = config; ++ break; ++ } ++ } ++ ++ if (!fit) { ++ PR_BUG(ERSTR_DRIVER ++ "Driver(%s mode) does not support this Nand Flash " ++ "pagesize:%s, oobsize:%d\n", ++ cfgmsg, ++ nand_page_name(pagetype), ++ oobsize); ++ return NULL; ++ } ++ return fit; ++} ++/*****************************************************************************/ ++static unsigned int nand_otp_len; ++static unsigned char nand_otp[128] = {0}; ++ ++/* Get NAND parameter table. */ ++static int __init parse_nand_param(const struct tag *tag) ++{ ++ if (tag->hdr.size <= 2) ++ return 0; ++ ++ nand_otp_len = ((tag->hdr.size << 2) - sizeof(struct tag_header)); ++ ++ if (nand_otp_len > sizeof(nand_otp)) { ++ pr_warn("%s(%d): Get Nand OTP from tag fail.\n", ++ __func__, __LINE__); ++ return 0; ++ } ++ memcpy(nand_otp, &tag->u, nand_otp_len); ++ return 0; ++} ++/* 0x48694E77 equal to fastoot ATAG_NAND_PARAM */ ++__tagtable(0x48694E77, parse_nand_param); ++ ++/*****************************************************************************/ ++int hinfc610_ecc_type2reg_intf(int type, struct hinfc_host *host) ++{ ++ if (HINFC_VER_620 == host->version) ++ return hinfc620_ecc_type2reg(type); ++ else ++ return hinfc610_ecc_type2reg(type); ++} ++/*****************************************************************************/ ++int hinfc610_ecc_reg2type_intf(int reg, struct hinfc_host *host) ++{ ++ if (HINFC_VER_620 == host->version) ++ return hinfc620_ecc_reg2type(reg); ++ else ++ return hinfc610_ecc_reg2type(reg); ++} ++ ++/*****************************************************************************/ ++static int hinfc610_param_adjust(struct mtd_info *mtd, struct nand_chip *chip, ++ struct nand_dev_t *nand_dev) ++{ ++ int pagetype; ++ int oobsize; ++ int regval; ++ char *start_type = "unknown"; ++ struct nand_config_info *best = NULL; ++ struct hinfc_host *host = chip->priv; ++ ++ if (IS_NANDC_HW_AUTO(host)) ++ start_type = "HW-Auto"; ++ else ++ start_type = "HW-Reg"; ++ ++ if ((mtd->writesize == SZ_8K) ++ || (mtd->writesize == SZ_16K) ++ || (mtd->writesize == SZ_32K)) ++ host->flags |= NAND_RANDOMIZER; ++ ++ pagetype = nandpage_size2type(mtd->writesize); ++ oobsize = mtd->oobsize; ++ ++ best = hinfc610_force_ecc(mtd, pagetype, oobsize, ++ start_type, 0); ++ ++#ifdef CONFIG_HINFC610_PAGESIZE_AUTO_ECC_NONE ++# ifdef CONFIG_HINFC610_AUTO_PAGESIZE_ECC ++# error you SHOULD NOT define CONFIG_HINFC610_PAGESIZE_AUTO_ECC_NONE \ ++ and CONFIG_HINFC610_AUTO_PAGESIZE_ECC at the same time ++# endif ++# ifdef CONFIG_HINFC610_HARDWARE_PAGESIZE_ECC ++# error you SHOULD NOT define CONFIG_HINFC610_PAGESIZE_AUTO_ECC_NONE \ ++ and CONFIG_HINFC610_HARDWARE_PAGESIZE_ECC at the same time ++# endif ++ ++ pagetype = nandpage_size2type(mtd->writesize); ++ oobsize = 32; ++ best = hinfc610_force_ecc(mtd, pagetype, oobsize, ++ "force config", 0); ++ start_type = "AutoForce"; ++ ++#endif /* CONFIG_HINFC610_PAGESIZE_AUTO_ECC_NONE */ ++ ++ if (!best) { ++ PR_BUG(ERSTR_HARDWARE ++ "Please configure Nand Flash pagesize and ecctype!\n"); ++ return -1; ++ } ++ ++ /* only in case fastboot check randomizer failed. ++ * Update fastboot or configure hardware randomizer pin ++ * fix this problem. ++ */ ++ if (IS_NAND_RANDOM(nand_dev) && !(IS_NAND_RANDOM(host))) ++ PR_BUG(ERSTR_HARDWARE ++ "Hardware is not configure randomizer, " ++ "but it is more suitable for this Nand Flash. " ++ "1. Please configure hardware randomizer PIN." ++ "2. Please updata fastboot.\n"); ++ ++ host->flags |= (IS_NAND_RANDOM(nand_dev) | ++ IS_NAND_SYNC_ASYNC(nand_dev) | ++ IS_NAND_ONLY_SYNC(nand_dev) | ++ IS_NAND_ONFI(nand_dev)); ++ ++ /* only for print nand info. */ ++ nand_dev->flags |= (IS_NANDC_HW_AUTO(host) | ++ IS_NANDC_SYNC_BOOT(host)); ++ ++ /* only in case fastboot check sync boot pin failed. ++ * Update fastboot or configure hardware sync boot pin fix this problem. ++ */ ++ if (IS_NANDC_SYNC_BOOT(host)) { ++ /* But NAND do not support sync mode, warning ! */ ++ if (!IS_NAND_ONLY_SYNC(nand_dev)) ++ PR_BUG(ERSTR_HARDWARE ++ "Hardware SYNC BOOT PIN has configured sync mode, " ++ "but the Nand Flash is async mode.\n" ++ "1. DO NOT configure SYNC BOOT PIN. " ++ "2. Update fastboot.\n"); ++ } else { ++ if (IS_NAND_ONLY_SYNC(nand_dev)) ++ PR_BUG(ERSTR_HARDWARE ++ "Hardware SYNC BOOT PIN has configured async mode, " ++ "but the Nand Flash only support sync mode.\n" ++ "1. Please configure SYNC BOOT PIN." ++ "2. Update fastboot.\n"); ++ } ++ ++ if (IS_NAND_SYNC_ASYNC(nand_dev)) ++ hinfc610_get_sync_info(host); ++ ++ if (best->ecctype != NAND_ECC_NONE) ++ mtd->oobsize = best->oobsize; ++ chip->ecc.layout = best->layout; ++ ++ host->ecctype = best->ecctype; ++ host->pagesize = nandpage_type2size(best->pagetype); ++ host->oobsize = mtd->oobsize; ++ host->block_page_mask = ((mtd->erasesize / mtd->writesize) - 1); ++ ++ host->buffer = dma_alloc_coherent(host->dev, ++ (host->pagesize + host->oobsize), ++ &host->dma_buffer, GFP_KERNEL); ++ if (!host->buffer) { ++ PR_BUG("Can't malloc memory for NAND driver."); ++ return -EIO; ++ } ++ memset(host->buffer, 0xff, (host->pagesize + host->oobsize)); ++ ++ host->dma_oob = host->dma_buffer + host->pagesize; ++ host->bbm = (unsigned char *)(host->buffer ++ + host->pagesize + HINFC_BAD_BLOCK_POS); ++ ++ host->epm = (unsigned short *)(host->buffer ++ + host->pagesize + chip->ecc.layout->oobfree[0].offset + 28); ++ ++ regval = ~(HINFC610_CON_PAGESIZE_MASK << HINFC610_CON_PAGEISZE_SHIFT); ++ host->NFC_CON &= regval; ++ host->NFC_CON_ECC_NONE &= regval; ++ regval = (hinfc610_page_type2reg(best->pagetype) ++ & HINFC610_CON_PAGESIZE_MASK) << HINFC610_CON_PAGEISZE_SHIFT; ++ host->NFC_CON |= regval; ++ host->NFC_CON_ECC_NONE |= regval; ++ ++ regval = ~(HINFC610_CON_ECCTYPE_MASK << HINFC610_CON_ECCTYPE_SHIFT); ++ host->NFC_CON &= regval; ++ host->NFC_CON_ECC_NONE &= regval; ++ regval = (hinfc610_ecc_type2reg_intf(best->ecctype, host) ++ & HINFC610_CON_ECCTYPE_MASK) << HINFC610_CON_ECCTYPE_SHIFT; ++ host->NFC_CON |= regval; ++ ++ if (mtd->writesize > NAND_MAX_PAGESIZE || ++ mtd->oobsize > NAND_MAX_OOBSIZE) { ++ PR_BUG(ERSTR_DRIVER ++ "Driver does not support this Nand Flash. " ++ "Please increase NAND_MAX_PAGESIZE and NAND_MAX_OOBSIZE.\n"); ++ } ++ ++ if (mtd->writesize != host->pagesize) { ++ unsigned int shift = 0; ++ unsigned int writesize = mtd->writesize; ++ ++ while (writesize > host->pagesize) { ++ writesize >>= 1; ++ shift++; ++ } ++ chip->chipsize = chip->chipsize >> shift; ++ mtd->erasesize = mtd->erasesize >> shift; ++ mtd->writesize = host->pagesize; ++ PR_MSG("Nand divide into 1/%u\n", (1 << shift)); ++ } ++ ++ nand_dev->start_type = start_type; ++ nand_dev->ecctype = host->ecctype; ++ nand_dev->oobsize = mtd->oobsize; ++ ++ host->read_retry = NULL; ++ if (nand_dev->read_retry_type != NAND_RR_NONE) { ++ host->read_retry ++ = hinfc610_find_read_retry(nand_dev->read_retry_type); ++ if (!host->read_retry) { ++ PR_BUG(ERSTR_DRIVER ++ "This Nand Flash need to enable the " ++ "'read retry' feature. " ++ "but the driver dose not offer the feature"); ++ } ++ ++ if (nand_otp_len) ++ memcpy(host->rr_data, nand_otp, nand_otp_len); ++ } ++ ++ /* ++ * If it want to support the 'read retry' feature, the 'randomizer' ++ * feature must be support first. ++ */ ++ if (host->read_retry && !IS_NAND_RANDOM(host)) { ++ PR_BUG(ERSTR_HARDWARE ++ "This Nand flash need to enable 'randomizer' feature. " ++ "Please configure hardware randomizer PIN."); ++ } ++ ++ hinfc610_dbg_init(host); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++int hinfc610_nand_init(struct hinfc_host *host, struct nand_chip *chip) ++{ ++ unsigned int regval; ++ ++ host->version = hinfc_read(host, HINFC610_VERSION); ++ ++ host->addr_cycle = 0; ++ host->addr_value[0] = 0; ++ host->addr_value[1] = 0; ++ host->cache_addr_value[0] = ~0; ++ host->cache_addr_value[1] = ~0; ++ host->chipselect = 0; ++ ++ host->send_cmd_pageprog = hinfc610_send_cmd_pageprog; ++ host->send_cmd_readstart = hinfc610_send_cmd_readstart; ++ host->send_cmd_erase = hinfc610_send_cmd_erase; ++ host->send_cmd_readid = hinfc610_send_cmd_readid; ++ host->send_cmd_status = hinfc610_send_cmd_status; ++ host->send_cmd_reset = hinfc610_send_cmd_reset; ++ ++ host->flags = 0; ++ ++ regval = hinfc_read(host, HINFC610_CON); ++ ++ host->NFC_CON = (regval ++ | HINFC610_CON_OP_MODE_NORMAL ++ | HINFC610_CON_READY_BUSY_SEL); ++ ++ host->NFC_CON_ECC_NONE = host->NFC_CON ++ & (~(HINFC610_CON_ECCTYPE_MASK ++ << HINFC610_CON_ECCTYPE_SHIFT)) ++ & (~HINFC610_CON_RANDOMIZER_EN); ++ ++ hinfc_write(host, ++ (SET_HINFC610_PWIDTH(CONFIG_HINFC610_W_LATCH, ++ CONFIG_HINFC610_R_LATCH, ++ CONFIG_HINFC610_RW_LATCH)), ++ HINFC610_PWIDTH); ++ ++ host->flags |= NANDC_HW_AUTO; ++ ++ /* check if chip is sync mode. */ ++ if (regval & HINFC610_BOOT_CFG_SYC_NAND_PAD) { ++ host->flags |= NANDC_IS_SYNC_BOOT; ++ ++ /* ++ * NAND default is sync mode, and read id, reset in sync mode. ++ */ ++ host->NFC_CON |= HINFC610_CON_NF_MODE_TOGGLE; ++ host->NFC_CON_ECC_NONE |= HINFC610_CON_NF_MODE_TOGGLE; ++ ++ /* set synchronous clock and timing. */ ++ clk_prepare_enable(host->clk); ++ } ++ ++ memset((char *)chip->IO_ADDR_R, ++ 0xff, HINFC610_BUFFER_BASE_ADDRESS_LEN); ++ ++ host->enable_ecc_randomizer = hinfc610_enable_ecc_randomizer; ++ hinfc_param_adjust = hinfc610_param_adjust; ++ ++ return 0; ++} +diff --git a/drivers/mtd/nand/hinfc610/hinfc610.h b/drivers/mtd/nand/hinfc610/hinfc610.h +new file mode 100644 +index 0000000..18d90fd +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610.h +@@ -0,0 +1,512 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef HINFCV610H ++#define HINFCV610H ++/******************************************************************************/ ++ ++#ifndef CONFIG_HINFC610_W_LATCH ++ #define CONFIG_HINFC610_W_LATCH (5) ++#endif /* CONFIG_HINFC610_W_LATCH */ ++ ++#ifndef CONFIG_HINFC610_R_LATCH ++ #define CONFIG_HINFC610_R_LATCH (7) ++#endif /* CONFIG_HINFC610_R_LATCH */ ++ ++#ifndef CONFIG_HINFC610_RW_LATCH ++ #define CONFIG_HINFC610_RW_LATCH (3) ++#endif /* CONFIG_HINFC610_RW_LATCH */ ++ ++#ifndef CONFIG_HINFC610_MAX_CHIP ++ #define CONFIG_HINFC610_MAX_CHIP (1) ++ #warning NOT config CONFIG_HINFC610_MAX_CHIP, \ ++ used default value, maybe invalid. ++#endif /* CONFIG_HINFC610_MAX_CHIP */ ++/*****************************************************************************/ ++#define HINFC_ECC_ERR_NUM0_BUF0 0xa0 ++#define HINFC_ECC_ERR_NUM1_BUF0 0xa4 ++#define HINFC_ECC_ERR_NUM0_BUF1 0xa8 ++#define HINFC_ECC_ERR_NUM1_BUF1 0xcc ++ ++#define GET_ECC_ERR_NUM(_i, _reg) (((_reg) >> ((_i) * 8)) & 0xff) ++ ++/*****************************************************************************/ ++#define HINFC610_REG_BASE_ADDRESS_LEN (0x100) ++#define HINFC610_BUFFER_BASE_ADDRESS_LEN (2048 + 128) ++ ++#define HINFC610_CHIP_DELAY (25) ++ ++#define HINFC610_ADDR_CYCLE_MASK 0x4 ++#define HINFC610_DMA_ADDR_OFFSET 4096 ++/*****************************************************************************/ ++#define HINFC610_CON 0x00 ++#define HINFC610_CON_OP_MODE_NORMAL (1U << 0) ++#define HINFC610_CON_PAGEISZE_SHIFT (1) ++#define HINFC610_CON_PAGESIZE_MASK (0x07) ++#define HINFC610_CON_BUS_WIDTH (1U << 4) ++#define HINFC610_CON_READY_BUSY_SEL (1U << 8) ++#define HINFC610_CON_ECCTYPE_SHIFT (9) ++#define HINFC610_CON_ECCTYPE_MASK (0x0f) ++#define HINFC610_CON_RANDOMIZER_EN (1 << 14) ++#define HINFC610_CON_NF_MODE_SHIFT 15 ++#define HINFC610_CON_NF_MODE_MASK (3 << HINFC610_CON_NF_MODE_SHIFT) ++#define HINFC610_CON_NF_MODE_TOGGLE (1 << HINFC610_CON_NF_MODE_SHIFT) ++#define HINFC610_CON_NF_MODE_ONFI_23 (2 << HINFC610_CON_NF_MODE_SHIFT) ++#define HINFC610_CON_NF_MODE_ONFI_30 (3 << HINFC610_CON_NF_MODE_SHIFT) ++ ++#define HINFC610_PWIDTH 0x04 ++#define SET_HINFC610_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \ ++ ((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8)) ++ ++#define HINFC610_CMD 0x0C ++#define HINFC610_ADDRL 0x10 ++#define HINFC610_ADDRH 0x14 ++#define HINFC610_DATA_NUM 0x18 ++ ++#define HINFC610_OP 0x1C ++#define HINFC610_OP_READ_STATUS_EN (1U << 0) ++#define HINFC610_OP_READ_DATA_EN (1U << 1) ++#define HINFC610_OP_WAIT_READY_EN (1U << 2) ++#define HINFC610_OP_CMD2_EN (1U << 3) ++#define HINFC610_OP_WRITE_DATA_EN (1U << 4) ++#define HINFC610_OP_ADDR_EN (1U << 5) ++#define HINFC610_OP_CMD1_EN (1U << 6) ++#define HINFC610_OP_NF_CS_SHIFT (7) ++#define HINFC610_OP_NF_CS_MASK (3) ++#define HINFC610_OP_ADDR_CYCLE_SHIFT (9) ++#define HINFC610_OP_ADDR_CYCLE_MASK (7) ++#define HINFC610_OP_READID_EN (1U << 12) ++#define HINFC610_OP_RW_REG_EN (1U << 13) ++ ++#define HINFC610_STATUS 0x20 ++ ++#define HINFC610_INTS 0x28 ++#define HINFC610_INTS_UE (1U << 6) ++#define HINFC610_INTCLR 0x2C ++#define HINFC610_INTCLR_UE (1U << 6) ++#define HINFC610_INTCLR_CE (1U << 5) ++ ++#define HINFC610_DMA_CTRL 0x60 ++#define HINFC610_DMA_CTRL_DMA_START (1U << 0) ++#define HINFC610_DMA_CTRL_WE (1U << 1) ++#define HINFC610_DMA_CTRL_BURST4_EN (1U << 4) ++#define HINFC610_DMA_CTRL_BURST8_EN (1U << 5) ++#define HINFC610_DMA_CTRL_BURST16_EN (1U << 6) ++#define HINFC610_DMA_CTRL_ADDR_NUM_SHIFT (7) ++#define HINFC610_DMA_CTRL_ADDR_NUM_MASK (1) ++#define HINFC610_DMA_CTRL_CS_SHIFT (8) ++#define HINFC610_DMA_CTRL_CS_MASK (0x03) ++ ++#define HINFC610_DMA_ADDR_DATA 0x64 ++#define HINFC610_DMA_ADDR_OOB 0x68 ++#define HINFC610_DMA_ADDR_DATA1 0xB4 ++#define HINFC610_DMA_ADDR_DATA2 0xB8 ++#define HINFC610_DMA_ADDR_DATA3 0xBC ++#define HINFC610_DMA_ADDR_DATA4 0xEC ++#define HINFC610_DMA_ADDR_DATA5 0xF0 ++#define HINFC610_DMA_ADDR_DATA6 0xF4 ++#define HINFC610_DMA_ADDR_DATA7 0xF8 ++ ++#define HINFC610_DMA_LEN 0x6C ++#define HINFC610_DMA_LEN_OOB_SHIFT (16) ++#define HINFC610_DMA_LEN_OOB_MASK (0x1FFF) ++ ++#define HINFC610_DMA_PARA 0x70 ++#define HINFC610_DMA_PARA_DATA_RW_EN (1U << 0) ++#define HINFC610_DMA_PARA_OOB_RW_EN (1U << 1) ++#define HINFC610_DMA_PARA_DATA_EDC_EN (1U << 2) ++#define HINFC610_DMA_PARA_OOB_EDC_EN (1U << 3) ++#define HINFC610_DMA_PARA_EXT_LEN_SHIFT (6) ++#define HINFC610_DMA_PARA_EXT_LEN_MASK (0x03) ++ ++#define HINFC610_VERSION 0x74 ++#define HINFC610_LOG_READ_ADDR 0x7C ++#define HINFC610_LOG_READ_LEN 0x80 ++ ++#define HINFC610_ECC_REG0 0xA0 ++#define HINFC610_ECC_REG1 0xA4 ++#define HINFC610_ECC_REG2 0xA8 ++#define HINFC610_ECC_REG3 0xAC ++ ++#define HINFC610_RANDOMIZER 0xC0 ++#define HINFC610_RANDOMIZER_PAD 0x02 ++#define HINFC610_RANDOMIZER_ENABLE 0x01 ++/* read nand id or nand status, return from nand data length */ ++#define HINFC610_NANDINFO_LEN 0x10 ++ ++#define HINFC610_BOOT_CFG 0xC4 ++#define HINFC610_BOOT_CFG_RANDOMIZER_PAD 0x01 ++#define HINFC610_BOOT_CFG_SAVE_PIN_MODE_SHIFT 13 ++#define HINFC610_BOOT_CFG_SAVE_PIN_MODE \ ++ (1U << HINFC610_BOOT_CFG_SAVE_PIN_MODE_SHIFT) ++#define HINFC610_BOOT_CFG_SYC_NAND_PAD_SHIFT 12 ++#define HINFC610_BOOT_CFG_SYC_NAND_PAD \ ++ (1U << HINFC610_BOOT_CFG_SYC_NAND_PAD_SHIFT) ++ ++#define HINFC610_SYNC_TIMING 0xD0 ++ ++/* ONFI: sync nand timing config */ ++#define HINFC610_SYNC_ONFI_T_CAD (0xF << 24) ++#define HINFC610_SYNC_ONFI_T_DQZ (0xF << 20) ++ ++/* TOGGLE: sync nand timing config */ ++#define HINFC610_SYNC_TOGGLE_PRE_RDQS (0xF << 16) ++#define HINFC610_SYNC_TOGGLE_POST_RDQS (0xF << 12) ++#define HINFC610_SYNC_TOGGLE_PRE_WDQS (0xF << 8) ++#define HINFC610_SYNC_TOGGLE_POST_WDQS (0xF << 4) ++#define HINFC610_SYNC_TOGGLE_RW_PSTH (0xF << 0) ++ ++/*****************************************************************************/ ++/* ++ * This constant declares the max. oobsize / page, which ++ * is supported now. If you add a chip with bigger oobsize/page ++ * adjust this accordingly. ++ */ ++#define NAND_MAX_OOBSIZE 4800 ++#define NAND_MAX_PAGESIZE 32768 ++ ++/* DMA address align with 32 bytes. */ ++#define HINFC610_DMA_ALIGN 64 ++/*****************************************************************************/ ++#include "../hinfc_gen.h" ++ ++#undef READ ++#define READ 1 ++ ++#undef WRITE ++#define WRITE 0 ++ ++#undef FALSE ++#define FALSE 0 ++ ++#undef TRUE ++#define TRUE 1 ++ ++#undef ENABLE ++#define ENABLE 1 ++ ++#undef DISABLE ++#define DISABLE 0 ++/*****************************************************************************/ ++ ++struct hinfc_host { ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ void __iomem *iobase; ++ struct device *dev; ++ ++ unsigned int offset; ++ unsigned int command; ++ ++ int chipselect; ++ ++ unsigned int n24bit_ext_len; ++ int ecctype; ++ ++/* Current system has already gone to sync mode */ ++#define HINFC610_IS_SYNC(_host) ((_host)->NFC_CON & HINFC610_CON_NF_MODE_MASK) ++ unsigned long NFC_CON; ++ unsigned long NFC_CON_ECC_NONE; ++ ++ unsigned int addr_cycle; ++ unsigned int addr_value[2]; ++ unsigned int cache_addr_value[2]; ++ unsigned int column; ++ unsigned int block_page_mask; ++ ++ unsigned int dma_oob; ++ unsigned int dma_buffer; ++ unsigned int pagesize; ++ unsigned int oobsize; ++ /* This is maybe an un-aligment address, only for malloc or free */ ++ char *buforg; ++ char *buffer; ++ ++ int need_rr_data; ++#define HINFC_READ_RETRY_DATA_LEN 128 ++ char rr_data[HINFC_READ_RETRY_DATA_LEN]; ++ int version; ++ int add_partition; ++ ++ /* BOOTROM read two bytes to detect the bad block flag */ ++#define HINFC_BAD_BLOCK_POS 0 ++ unsigned char *bbm; /* nand bad block mark */ ++ unsigned short *epm; /* nand empty page mark */ ++ unsigned int flags; ++ ++#define HINFC610_PS_UC_ECC 0x01 /* page has ecc error */ ++#define HINFC610_PS_BAD_BLOCK 0x02 /* bad block */ ++#define HINFC610_PS_EMPTY_PAGE 0x04 /* page is empty */ ++#define HINFC610_PS_EPM_ERROR 0x0100 /* empty page mark word has ecc error*/ ++#define HINFC610_PS_BBM_ERROR 0x0200 /* bad block mark word has ecc error*/ ++ unsigned int page_status; ++ ++ struct clk *clk; ++ ++ int (*send_cmd_pageprog)(struct hinfc_host *host); ++ int (*send_cmd_status)(struct hinfc_host *host); ++ int (*send_cmd_readstart)(struct hinfc_host *host); ++ int (*send_cmd_erase)(struct hinfc_host *host); ++ int (*send_cmd_readid)(struct hinfc_host *host); ++ int (*send_cmd_reset)(struct hinfc_host *host, int chipselect); ++ int (*enable)(struct hinfc_host *host, int enable); ++ ++ int (*enable_ecc_randomizer)(struct hinfc_host *host, ++ int ecc_en, int randomizer_en); ++ ++ struct read_retry_t *read_retry; ++ struct nand_sync *sync; ++}; ++ ++#define HINFC610_UC_ECC 0x01 ++#define HINFC610_BAD_BLOCK 0x02 ++#define HINFC610_EMPTY_PAGE 0x04 ++ ++#define IS_PS_EMPTY_PAGE(_host) ((_host)->page_status & HINFC610_PS_EMPTY_PAGE) ++#define IS_PS_BAD_BLOCK(_host) ((_host)->page_status & HINFC610_PS_BAD_BLOCK) ++#define IS_PS_UN_ECC(_host) ((_host)->page_status & HINFC610_PS_UC_ECC) ++#define IS_PS_EPM_ERR(_host) ((_host)->page_status & HINFC610_PS_EPM_ERROR) ++#define IS_PS_BBM_ERR(_host) ((_host)->page_status & HINFC610_PS_BBM_ERROR) ++ ++/*****************************************************************************/ ++ ++#define HINFC610_READ_1CMD_0ADD_NODATA \ ++ (HINFC610_OP_CMD1_EN \ ++ | ((host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT)) ++ ++#define HINFC610_READ_1CMD_1ADD_DATA \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_READ_DATA_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_READ_1CMD_1ADD_DATA_WAIT_READY \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_READ_DATA_EN \ ++ | HINFC610_OP_WAIT_READY_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_READ_1CMD_1ADD_DATA_SYNC \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_READ_DATA_EN \ ++ | HINFC610_OP_WAIT_READY_EN \ ++ | HINFC610_OP_RW_REG_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_READ_2CMD_5ADD \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_CMD2_EN \ ++ | HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_READ_DATA_EN \ ++ | HINFC610_OP_WAIT_READY_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (5 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_WRITE_0CMD_1ADD_DATA \ ++ (HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_WRITE_DATA_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_WRITE_0CMD_1ADD_DATA_WAIT_READY \ ++ (HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_WRITE_DATA_EN \ ++ | HINFC610_OP_WAIT_READY_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_WRITE_0CMD_1ADD_DATA_SYNC \ ++ (HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_WRITE_DATA_EN \ ++ | HINFC610_OP_RW_REG_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_WRITE_0CMD_1ADD_DATA_SYNC_WAIT_READY \ ++ (HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_WRITE_DATA_EN \ ++ | HINFC610_OP_RW_REG_EN \ ++ | HINFC610_OP_WAIT_READY_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_WRITE_1CMD_1ADD_DATA \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_WRITE_DATA_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_WRITE_1CMD_1ADD_DATA_WAIT_READY \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_WRITE_DATA_EN \ ++ | HINFC610_OP_WAIT_READY_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_WRITE_1CMD_1ADD_DATA_SYNC \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_WRITE_DATA_EN \ ++ | HINFC610_OP_RW_REG_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_WRITE_1CMD_1ADD_DATA_SYNC_WAIT_READY \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_WRITE_DATA_EN \ ++ | HINFC610_OP_WAIT_READY_EN \ ++ | HINFC610_OP_RW_REG_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (1 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_WRITE_1CMD_2ADD_DATA \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_WRITE_DATA_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (2 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_WRITE_1CMD_2ADD_DATA_SYNC \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_ADDR_EN \ ++ | HINFC610_OP_WRITE_DATA_EN \ ++ | HINFC610_OP_RW_REG_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT) \ ++ | (2 << HINFC610_OP_ADDR_CYCLE_SHIFT)) ++ ++#define HINFC610_WRITE_2CMD_0ADD_NODATA \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_CMD2_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT)) ++ ++#define HINFC610_WRITE_2CMD_0ADD_NODATA_SYNC \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_CMD2_EN \ ++ | HINFC610_OP_RW_REG_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT)) ++ ++#define HINFC610_WRITE_1CMD_0ADD_NODATA \ ++ (HINFC610_OP_CMD1_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT)) ++ ++#define HINFC610_WRITE_1CMD_0ADD_NODATA_WAIT_READY \ ++ (HINFC610_OP_CMD1_EN \ ++ | HINFC610_OP_WAIT_READY_EN \ ++ | (((unsigned int)host->chipselect & HINFC610_OP_NF_CS_MASK) \ ++ << HINFC610_OP_NF_CS_SHIFT)) ++ ++/*****************************************************************************/ ++ ++#define WAIT_CONTROLLER_FINISH() \ ++do { \ ++ unsigned int timeout = 0x800000; \ ++ while ((hinfc_read(host, HINFC610_STATUS) & 0x1) == 0x0 && timeout) \ ++ timeout--; \ ++ if (!timeout) \ ++ PR_ERR("Wait NAND controller finish timeout.\n"); \ ++} while (0) ++ ++/*****************************************************************************/ ++ ++#define hinfc_read(_host, _reg) \ ++ readl((char *)_host->iobase + (_reg)) ++ ++#define hinfc_write(_host, _value, _reg) \ ++ writel((_value), (char *)_host->iobase + (_reg)) ++ ++#define HINFC_CMD_SEQ(_cmd0, _cmd1) \ ++ (((_cmd0) & 0xFF) | ((_cmd1) & 0xFF) << 8) ++/*****************************************************************************/ ++ ++#define GET_PAGE_INDEX(host) \ ++ ((host->addr_value[0] >> 16) | (host->addr_value[1] << 16)) ++ ++/*****************************************************************************/ ++ ++void hinfc610_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl); ++int hinfc610_dev_ready(struct mtd_info *mtd); ++void hinfc610_select_chip(struct mtd_info *mtd, int chipselect); ++uint8_t hinfc610_read_byte(struct mtd_info *mtd); ++u16 hinfc610_read_word(struct mtd_info *mtd); ++void hinfc610_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len); ++void hinfc610_read_buf(struct mtd_info *mtd, uint8_t *buf, int len); ++int hinfc610_nand_init(struct hinfc_host *host, struct nand_chip *chip); ++/******************************************************************************/ ++ ++extern struct nand_sync hinfc610_sync_onfi_23; ++extern struct nand_sync hinfc610_sync_onfi_30; ++extern struct nand_sync hinfc610_sync_toggle_10; ++extern struct read_retry_t hinfc610_hynix_bg_cdie_read_retry; ++extern struct read_retry_t hinfc610_hynix_bg_bdie_read_retry; ++extern struct read_retry_t hinfc610_hynix_cg_adie_read_retry; ++extern struct read_retry_t hinfc610_micron_read_retry; ++extern struct read_retry_t hinfc610_toshiba_24nm_read_retry; ++extern struct read_retry_t hinfc610_samsung_read_retry; ++ ++#if 0 ++#ifdef CONFIG_MTD_PART_CHANGE ++extern int register_mtd_partdev(struct mtd_info *mtd); ++extern int unregister_mtd_partdev(struct mtd_info *mtd); ++#else ++int register_mtd_partdev(struct mtd_info *mtd) ++{ ++ return 0; ++}; ++ ++int unregister_mtd_partdev(struct mtd_info *mtd) ++{ ++ return 0; ++}; ++#endif ++ ++void hinfc610_controller_enable(struct hinfc_host *host, int enable); ++#endif ++ ++extern int hinfc610_dbgfs_debug_init(struct hinfc_host *host); ++ ++#ifdef CONFIG_HINFC610_DBG_NAND_DEBUG ++extern struct hinfc610_dbg_inf_t *hinfc610_dbg_inf[]; ++#endif ++ ++#endif /* HINFCV610H */ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_dbg.c b/drivers/mtd/nand/hinfc610/hinfc610_dbg.c +new file mode 100644 +index 0000000..ee25956 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_dbg.c +@@ -0,0 +1,294 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/moduleparam.h> ++#include <linux/vmalloc.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/debugfs.h> ++#include <linux/uaccess.h> ++#include <linux/ctype.h> ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++#include "hinfc610_dbg.h" ++ ++struct hinfc610_dbg_inf_t *hinfc610_dbg_inf[] = { ++ &hinfc610_dbg_inf_ecc_notice, ++ &hinfc610_dbg_inf_read_retry_notice, ++#ifdef CONFIG_HINFC610_DBG_NAND_DUMP ++ &hinfc610_dbg_inf_dump, ++#endif ++#ifdef CONFIG_HINFC610_DBG_NAND_ERASE_COUNT ++ &hinfc610_dbg_inf_erase_count, ++#endif ++#ifdef CONFIG_HINFC610_DBG_NAND_ECC_COUNT ++ &hinfc610_dbg_inf_ecc_count, ++#endif ++#ifdef CONFIG_HINFC610_DBG_NAND_READ_RETRY ++ &hinfc610_dbg_inf_read_retry, ++#endif ++ NULL, ++}; ++ ++static struct dentry *dbgfs_root; ++static struct hinfc_host *dbgfs_host; ++ ++/*****************************************************************************/ ++static ssize_t dbgfs_debug_read(struct file *filp, char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ char *msg, *p; ++ struct hinfc610_dbg_inf_t **inf; ++ ++ if (*ppos != 0) ++ return 0; ++ ++ msg = (char *)__get_free_page(GFP_TEMPORARY); ++ if (!msg) ++ return -ENOMEM; ++ ++ p = msg; ++ if (count > PAGE_SIZE) ++ count = PAGE_SIZE; ++ ++ for (inf = hinfc610_dbg_inf; *inf; inf++) { ++ if ((p - msg) + MAX_OPTION_SIZE + 2 > count) { ++ PR_ERR("Not enough memory.\n"); ++ break; ++ } ++ p += snprintf(p, (MAX_OPTION_SIZE + 2), "%c%s,", ++ ((*inf)->enable ? '+' : '-'), ++ (*inf)->name); ++ } ++ ++ p += sprintf(p, "\n"); ++ count = (p - msg); ++ if (copy_to_user(buffer, msg, count)) { ++ free_page((unsigned long) msg); ++ return -EFAULT; ++ } ++ ++ free_page((unsigned long) msg); ++ ++ *ppos += count; ++ return count; ++} ++/*****************************************************************************/ ++ ++static void dbgfs_debug_do_cmd(struct hinfc610_dbg_inf_t **dbg_inf, ++ const char *cmd, unsigned int length, int enable) ++{ ++ int ret = 0; ++ struct hinfc610_dbg_inf_t **inf; ++ ++ if (length >= sizeof((*inf)->name)) ++ return; ++ ++ for (inf = dbg_inf; *inf; inf++) { ++ if (!(*inf)->name[length] && ++ !memcmp((*inf)->name, cmd, length)) ++ break; ++ } ++ ++ if (!(*inf) || (*inf)->enable == enable) ++ return; ++ ++ if (enable) { ++ if ((*inf)->init) ++ ret = (*inf)->init(dbgfs_root, dbgfs_host); ++ } else { ++ if ((*inf)->uninit) ++ ret = (*inf)->uninit(); ++ } ++ ++ if (!ret) ++ (*inf)->enable = enable; ++} ++/*****************************************************************************/ ++ ++static void dbgfs_debug_ops(const char *options, ++ struct hinfc610_dbg_inf_t **dbg_inf) ++{ ++ int enable; ++ const char *pos, *cmd; ++ ++ pos = options; ++ ++ while (*pos) { ++ ++ while (*pos && *pos != '+' && *pos != '-') ++ pos++; ++ ++ switch (*pos++) { ++ case '+': ++ enable = 1; ++ break; ++ case '-': ++ enable = 0; ++ break; ++ default: ++ return; ++ } ++ ++ cmd = pos; ++ while (*pos == '_' || isalpha(*pos)) ++ pos++; ++ ++ if (*cmd && pos > cmd) ++ dbgfs_debug_do_cmd(dbg_inf, cmd, (pos - cmd), enable); ++ ++ while (isspace(*pos) || *pos == ',' || *pos == ';') ++ pos++; ++ } ++} ++/*****************************************************************************/ ++/* ++ * echo "+dump, +read_retry, +ecc_count, +erase_count" > debug ++ */ ++static ssize_t dbgfs_debug_write(struct file *filp, const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ char *options; ++ size_t num = count; ++ ++ if (count > PAGE_SIZE) ++ num = (PAGE_SIZE - 1); ++ ++ options = (char *)__get_free_page(GFP_TEMPORARY); ++ if (!options) ++ return -ENOMEM; ++ ++ if (copy_from_user(options, buffer, num)) { ++ free_page((unsigned long) options); ++ return -EFAULT; ++ } ++ ++ options[num] = 0; ++ ++ dbgfs_debug_ops(options, hinfc610_dbg_inf); ++ ++ free_page((unsigned long) options); ++ ++ *ppos += count; ++ return count; ++} ++/*****************************************************************************/ ++ ++static const struct file_operations dbgfs_debug_fops = { ++ .owner = THIS_MODULE, ++ .read = dbgfs_debug_read, ++ .write = dbgfs_debug_write, ++}; ++/*****************************************************************************/ ++ ++int hinfc610_dbgfs_debug_init(struct hinfc_host *host) ++{ ++ struct dentry *dentry; ++ ++ if (dbgfs_root) ++ return 0; ++ ++ dbgfs_root = debugfs_create_dir("nand", NULL); ++ if (!dbgfs_root) { ++ PR_ERR("Can't create 'nand' dir.\n"); ++ return -ENOENT; ++ } ++ ++ dentry = debugfs_create_file("debug", S_IFREG | S_IRUSR | S_IWUSR, ++ dbgfs_root, NULL, &dbgfs_debug_fops); ++ if (!dentry) { ++ PR_ERR("Can't create 'debug' file.\n"); ++ goto fail; ++ } ++ ++ dbgfs_host = host; ++ ++ if (nand_dbgfs_options) ++ dbgfs_debug_ops(nand_dbgfs_options, hinfc610_dbg_inf); ++ ++ return 0; ++ ++fail: ++ debugfs_remove_recursive(dbgfs_root); ++ dbgfs_root = NULL; ++ ++ return -ENOENT; ++} ++/*****************************************************************************/ ++ ++static int dbgfs_read_retry_notice_init(struct dentry *root, ++ struct hinfc_host *host) ++{ ++ if (!host->read_retry) { ++ pr_warn("read_retry_notice: The NAND not support this interface.\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void hinfc610_dbg_read_retry_notice(struct hinfc_host *host, int index) ++{ ++ pr_warn("Page 0x%08x do read retry (%d/%d) %s.\n", ++ GET_PAGE_INDEX(host), index, host->read_retry->count, ++ (IS_PS_UN_ECC(host) ? "Fail" : "Success")); ++} ++ ++struct hinfc610_dbg_inf_t hinfc610_dbg_inf_read_retry_notice = { ++ "read_retry_notice", 0, ++ dbgfs_read_retry_notice_init, ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ hinfc610_dbg_read_retry_notice, ++}; ++/*****************************************************************************/ ++ ++static void hinfc610_dbg_ecc_notice_read(struct hinfc_host *host) ++{ ++ unsigned int pageindex = GET_PAGE_INDEX(host); ++ ++ if (IS_PS_BAD_BLOCK(host) || IS_PS_EMPTY_PAGE(host)) { ++ if (IS_PS_BBM_ERR(host)) ++ pr_warn("page 0x%08x bbm is corruption, bbm: 0x%x.\n", ++ pageindex, *host->bbm); ++ ++ if (IS_PS_EPM_ERR(host)) ++ pr_warn("page 0x%08x epm is corruption, epm: 0x%x.\n", ++ pageindex, *host->epm); ++ ++ return; ++ } ++ ++ if (IS_PS_UN_ECC(host)) ++ pr_warn("page 0x%08x has uncorrect ecc.\n", pageindex); ++} ++ ++struct hinfc610_dbg_inf_t hinfc610_dbg_inf_ecc_notice = { ++ "ecc_notice", 0, ++ NULL, ++ NULL, ++ hinfc610_dbg_ecc_notice_read, ++ NULL, ++ NULL, ++ NULL, ++}; ++/*****************************************************************************/ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_dbg.h b/drivers/mtd/nand/hinfc610/hinfc610_dbg.h +new file mode 100644 +index 0000000..8fd4deb +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_dbg.h +@@ -0,0 +1,63 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++ ++#ifndef HINFC610_DBGH ++#define HINFC610_DBGH ++/******************************************************************************/ ++ ++#define MAX_OPTION_SIZE 20 ++ ++struct hinfc610_dbg_inf_t { ++ const char name[MAX_OPTION_SIZE]; ++ int enable; ++ int (*init)(struct dentry *root, struct hinfc_host *host); ++ int (*uninit)(void); ++ ++ void (*read)(struct hinfc_host *host); ++ void (*write)(struct hinfc_host *host); ++ void (*erase)(struct hinfc_host *host); ++ ++ void (*read_retry)(struct hinfc_host *host, int index); ++}; ++ ++#define CMD_WORD_OFFSET "offset=" ++#define CMD_WORD_LENGTH "length=" ++#define CMD_WORD_CLEAN "clear" ++#define CMD_WORD_ON "on" ++#define CMD_WORD_OFF "off" ++ ++struct hinfc610_ecc_inf_t { ++ int pagesize; ++ int ecctype; ++ int section; ++ void (*ecc_inf)(struct hinfc_host *host, unsigned char ecc[]); ++}; ++ ++struct hinfc610_ecc_inf_t *hinfc610_get_ecc_inf(struct hinfc_host *host, ++ int pagesize, int ecctype); ++ ++extern struct hinfc610_dbg_inf_t hinfc610_dbg_inf_dump; ++extern struct hinfc610_dbg_inf_t hinfc610_dbg_inf_erase_count; ++extern struct hinfc610_dbg_inf_t hinfc610_dbg_inf_ecc_count; ++extern struct hinfc610_dbg_inf_t hinfc610_dbg_inf_read_retry; ++extern struct hinfc610_dbg_inf_t hinfc610_dbg_inf_read_retry_notice; ++extern struct hinfc610_dbg_inf_t hinfc610_dbg_inf_ecc_notice; ++ ++/******************************************************************************/ ++#endif /* HINFC610_DBGH */ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_dbg_dump.c b/drivers/mtd/nand/hinfc610/hinfc610_dbg_dump.c +new file mode 100644 +index 0000000..e330bb5 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_dbg_dump.c +@@ -0,0 +1,448 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/moduleparam.h> ++#include <linux/vmalloc.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/debugfs.h> ++#include <linux/uaccess.h> ++#include <linux/mutex.h> ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++#include "hinfc610_dbg.h" ++ ++#ifndef CONFIG_HINFC610_DBG_NAND_NUM_OF_LOGS ++# define CONFIG_HINFC610_DBG_NAND_NUM_OF_LOGS (80) ++#endif /* CONFIG_HINFC610_DBG_NAND_NUM_OF_LOGS */ ++ ++#ifndef CONFIG_HINFC610_DBG_NAND_LOG_LENGTH ++# define CONFIG_HINFC610_DBG_NAND_LOG_LENGTH (40) ++#endif /* CONFIG_HINFC610_DBG_NAND_LOG_LENGTH */ ++ ++struct hinfc610_dbg_dump_item_t { ++ unsigned short hour; ++ unsigned short min; ++ unsigned short sec; ++ unsigned short msec; ++ ++ unsigned int cycle; ++ ++ unsigned long page; ++ unsigned long offset; ++ unsigned long length; ++ ++ char page_status[4]; ++ char op; ++ ++ unsigned char data[CONFIG_HINFC610_DBG_NAND_LOG_LENGTH]; ++}; ++ ++struct hinfc610_dbg_dump_t { ++ ++ struct dentry *dentry; ++ unsigned int index; /* current logs index */ ++ int count; /* number of logs */ ++ ++ unsigned long offset; ++ unsigned long length; ++ ++ struct hinfc610_dbg_dump_item_t ++ logs[CONFIG_HINFC610_DBG_NAND_NUM_OF_LOGS]; ++ ++ unsigned int read_index; ++}; ++ ++static DEFINE_MUTEX(dbg_dump_mutex); ++static struct hinfc610_dbg_dump_t *dbg_dump; ++ ++/*****************************************************************************/ ++ ++static void do_gettime(unsigned short *hour, unsigned short *min, ++ unsigned short *sec, unsigned short *msec) ++{ ++ long val; ++ struct timeval tv; ++ ++ do_gettimeofday(&tv); ++ val = tv.tv_sec % 86400; /* the second form 0 hour */ ++ ++ if (hour) ++ *hour = val / 3600; ++ val %= 3600; ++ if (min) ++ *min = val / 60; ++ if (sec) ++ *sec = val % 60; ++ if (msec) ++ *msec = tv.tv_usec / 1000; ++} ++/*****************************************************************************/ ++/* ++ * ++# cat ./debugfs/nand/dump ++Print parameter: "offset=0 length=8" ++UTC Clock op cylce page-offset data ++00:00:33.0321 W 5 0x0000258F-0000 31 18 10 06 18 EF FE 11 ++00:00:33.0325 W 5 0x00002740-0000 31 18 10 06 7C D4 B3 0C ++* ++*/ ++static ssize_t dbgfs_dump_read(struct file *filp, char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ int len = 0; ++ char buf[128] = {0}; ++ unsigned int read_index; ++ char __user *pusrbuf = buffer; ++ struct hinfc610_dbg_dump_item_t *logs; ++ ++ if (*ppos == 0) { ++ ++ if (dbg_dump->count ++ < CONFIG_HINFC610_DBG_NAND_NUM_OF_LOGS) ++ dbg_dump->read_index = 0; ++ else ++ dbg_dump->read_index ++ = (dbg_dump->index + 1); ++ ++ len = snprintf(buf, sizeof(buf), ++ "Print parameter: \"offset=%ld length=%ld\"\n", ++ dbg_dump->offset, dbg_dump->length); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ ++ pusrbuf += len; ++ ++ len += snprintf(buf, sizeof(buf), ++ " UTC Clock op cylce page-offset data\n"); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ ++ pusrbuf += len; ++ ++ } else if (dbg_dump->read_index == dbg_dump->index) ++ return 0; ++ ++ for (read_index = dbg_dump->read_index; ++ (read_index != dbg_dump->index); ++ ++read_index) { ++ ++ if (read_index >= CONFIG_HINFC610_DBG_NAND_NUM_OF_LOGS) ++ read_index = 0; ++ ++ logs = &dbg_dump->logs[read_index]; ++ ++ if ((count - (pusrbuf - buffer)) < (50 + logs->length * 3)) ++ break; ++ ++ len = snprintf(buf, sizeof(buf), ++ "%02d:%02d:%02d.%04d %c %-2u 0x%08lX-%04lX", ++ logs->hour, logs->min, logs->sec, logs->msec, ++ logs->op, logs->cycle, ++ logs->page, logs->offset); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ ++ pusrbuf += len; ++ ++ if (logs->op == 'E') { ++ ++ len = snprintf(buf, sizeof(buf), " ---"); ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ ++ } else { ++ ++ int ix; ++ ++ len = snprintf(buf, sizeof(buf), "%s", ++ logs->page_status); ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ ++ for (ix = 0; ix < logs->length; ix++) { ++ if ((ix % 16) == 15) { ++ len = snprintf(buf, sizeof(buf), ++ "%02X-", ++ logs->data[ix]); ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ } else { ++ len = snprintf(buf, sizeof(buf), ++ "%02X ", ++ logs->data[ix]); ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ } ++ } ++ } ++ len = snprintf(buf, sizeof(buf), "\n"); ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ } ++ ++ dbg_dump->read_index = read_index; ++ ++ *ppos += (pusrbuf - buffer); ++ return pusrbuf - buffer; ++} ++/*****************************************************************************/ ++ ++static ssize_t dbgfs_dump_write(struct file *filp, const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ char *p; ++ int ret; ++ unsigned long value = 0; ++ char buf[128] = {0}; ++ unsigned long pos = 0; ++ ++ if (count > sizeof(buf)) ++ count = sizeof(buf); ++ ++ if (copy_from_user(buf, buffer, count)) ++ return -EFAULT; ++ ++ while (pos < count) { ++ ++ while (pos < count ++ && (buf[pos] == ' ' || ++ buf[pos] == ',' || buf[pos] == ';')) ++ pos++; ++ ++ if (pos >= count) ++ break; ++ ++ switch (buf[pos]) { ++ case 'o': ++ if (!memcmp(&buf[pos], CMD_WORD_OFFSET, ++ sizeof(CMD_WORD_OFFSET) - 1)) { ++ ++ pos += sizeof(CMD_WORD_OFFSET) - 1; ++ p = (char *)(buf + pos); ++ ret = kstrtoul(p, 10, &value); ++ if (ret < 0) ++ value = 0; ++ dbg_dump->offset = value; ++ } ++ break; ++ ++ case 'l': ++ if (!memcmp(&buf[pos], CMD_WORD_LENGTH, ++ sizeof(CMD_WORD_LENGTH) - 1)) { ++ ++ pos += sizeof(CMD_WORD_LENGTH) - 1; ++ p = (char *)(buf + pos); ++ ret = kstrtoul(p, 10, &value); ++ if (ret < 0) ++ value = 0; ++ dbg_dump->length = value; ++ } ++ break; ++ } ++ ++ while (pos < count && ++ (buf[pos] != ' ' && buf[pos] != ',' && buf[pos] != ';')) ++ pos++; ++ } ++ ++ *ppos += count; ++ return count; ++} ++/*****************************************************************************/ ++ ++static const struct file_operations dbgfs_dump_fops = { ++ .owner = THIS_MODULE, ++ .read = dbgfs_dump_read, ++ .write = dbgfs_dump_write, ++}; ++/*****************************************************************************/ ++ ++static int dbgfs_dump_init(struct dentry *root, struct hinfc_host *host) ++{ ++ struct hinfc610_dbg_dump_t *dump; ++ ++ if (dbg_dump) ++ return 0; ++ ++ dump = vmalloc(sizeof(struct hinfc610_dbg_dump_t)); ++ if (!dump) { ++ PR_ERR("Can't allocate memory.\n"); ++ return -ENOMEM; ++ } ++ memset(dump, 0, sizeof(struct hinfc610_dbg_dump_t)); ++ ++ dump->dentry = debugfs_create_file("dump", ++ S_IFREG | S_IRUSR | S_IWUSR, ++ root, NULL, &dbgfs_dump_fops); ++ if (!dump->dentry) { ++ PR_ERR("Can't create 'dump' file.\n"); ++ vfree(dump); ++ return -ENOENT; ++ } ++ ++ dump->length = 8; ++ ++ dbg_dump = dump; ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int dbgfs_dump_uninit(void) ++{ ++ if (!dbg_dump) ++ return 0; ++ ++ mutex_lock(&dbg_dump_mutex); ++ ++ debugfs_remove(dbg_dump->dentry); ++ ++ vfree(dbg_dump); ++ dbg_dump = NULL; ++ ++ mutex_unlock(&dbg_dump_mutex); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static void dbg_dump_rw(struct hinfc_host *host, char op) ++{ ++ unsigned long buflen; ++ struct hinfc610_dbg_dump_item_t *logs; ++ ++ mutex_lock(&dbg_dump_mutex); ++ ++ if (!dbg_dump) ++ goto exit; ++ ++ buflen = (host->pagesize + host->oobsize); ++ logs = &dbg_dump->logs[dbg_dump->index]; ++ ++ dbg_dump->count++; ++ ++ do_gettime(&logs->hour, &logs->min, &logs->sec, &logs->msec); ++ ++ memcpy(logs->page_status, "\x20\x20\x20\x00", 4); ++ ++ if (host->page_status) { ++ if (IS_PS_BAD_BLOCK(host)) ++ logs->page_status[0] = 'B'; ++ else if (IS_PS_EMPTY_PAGE(host)) ++ logs->page_status[0] = 'E'; ++ ++ if (IS_PS_UN_ECC(host)) ++ logs->page_status[1] = '*'; ++ ++ if (IS_PS_EPM_ERR(host) || IS_PS_BBM_ERR(host)) ++ logs->page_status[2] = '?'; ++ } ++ ++ logs->op = op; ++ logs->cycle = host->addr_cycle; ++ logs->length = dbg_dump->length; ++ logs->offset = (host->addr_value[0] & 0xFFFF); ++ logs->page = GET_PAGE_INDEX(host); ++ ++ if (!logs->offset) ++ logs->offset = dbg_dump->offset; ++ ++ if (logs->offset >= buflen) ++ logs->offset = 0; ++ ++ if (logs->length > (buflen - logs->offset)) ++ logs->length = (buflen - logs->offset); ++ ++ if (logs->length > CONFIG_HINFC610_DBG_NAND_LOG_LENGTH) ++ logs->length = CONFIG_HINFC610_DBG_NAND_LOG_LENGTH; ++ ++ memcpy(logs->data, (host->buffer + logs->offset), logs->length); ++ ++ if (++dbg_dump->index >= CONFIG_HINFC610_DBG_NAND_NUM_OF_LOGS) ++ dbg_dump->index = 0; ++ ++exit: ++ mutex_unlock(&dbg_dump_mutex); ++} ++/*****************************************************************************/ ++ ++static void dbg_dump_read(struct hinfc_host *host) ++{ ++ dbg_dump_rw(host, 'R'); ++} ++/*****************************************************************************/ ++ ++static void dbg_dump_write(struct hinfc_host *host) ++{ ++ dbg_dump_rw(host, 'W'); ++} ++/*****************************************************************************/ ++ ++static void dbg_dump_erase(struct hinfc_host *host) ++{ ++ struct hinfc610_dbg_dump_item_t *logs; ++ ++ mutex_lock(&dbg_dump_mutex); ++ ++ if (!dbg_dump) ++ goto exit; ++ ++ dbg_dump->count++; ++ logs = &dbg_dump->logs[dbg_dump->index]; ++ ++ do_gettime(&logs->hour, &logs->min, &logs->sec, &logs->msec); ++ ++ memcpy(logs->page_status, "\x20\x20\x20\x00", 4); ++ ++ logs->op = 'E'; ++ logs->cycle = host->addr_cycle; ++ logs->length = dbg_dump->length; ++ ++ logs->offset = 0; ++ logs->page = host->addr_value[0]; ++ logs->length = 0; ++ ++ if (++dbg_dump->index >= CONFIG_HINFC610_DBG_NAND_NUM_OF_LOGS) ++ dbg_dump->index = 0; ++ ++exit: ++ mutex_unlock(&dbg_dump_mutex); ++} ++/*****************************************************************************/ ++ ++struct hinfc610_dbg_inf_t hinfc610_dbg_inf_dump = { ++ "dump", 0, ++ dbgfs_dump_init, ++ dbgfs_dump_uninit, ++ dbg_dump_read, ++ dbg_dump_write, ++ dbg_dump_erase, ++ NULL, ++}; ++/*****************************************************************************/ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_dbg_ecc_count.c b/drivers/mtd/nand/hinfc610/hinfc610_dbg_ecc_count.c +new file mode 100644 +index 0000000..b7174ab +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_dbg_ecc_count.c +@@ -0,0 +1,401 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/moduleparam.h> ++#include <linux/vmalloc.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/debugfs.h> ++#include <linux/uaccess.h> ++#include <linux/mutex.h> ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++#include "hinfc610_dbg.h" ++ ++#ifndef CONFIG_HINFC610_DBG_ECC_COUNT_NUM ++# define CONFIG_HINFC610_DBG_ECC_COUNT_NUM (100) ++#endif /* CONFIG_HINFC610_DBG_ECC_COUNT_NUM */ ++ ++struct hinfc610_dbg_ecc_count_item_t { ++ unsigned int page; ++ unsigned int page_status; /* the same as host->page_status */ ++ unsigned short hour; ++ unsigned short min; ++ unsigned short sec; ++ unsigned short msec; ++ ++ unsigned char ecc[4]; ++}; ++ ++struct hinfc610_dbg_ecc_count_t { ++ ++ struct dentry *dentry; ++ unsigned int index; /* current logs index */ ++ int count; /* number of logs */ ++ ++ struct hinfc610_ecc_inf_t *ecc_inf; ++ unsigned int offset; ++ unsigned int length; ++ unsigned int pagecount; ++ ++ unsigned char *item; ++ ++ unsigned int read_index; ++}; ++ ++#define GET_ITEM(_ecc_count, _index) \ ++ ((struct hinfc610_dbg_ecc_count_item_t *)((_ecc_count)->item + \ ++ ((sizeof(struct hinfc610_dbg_ecc_count_item_t) + \ ++ (_ecc_count)->ecc_inf->section) * (_index)))) ++ ++static DEFINE_MUTEX(dbg_ecc_count_mutex); ++static struct hinfc610_dbg_ecc_count_t *dbg_ecc_count; ++/*****************************************************************************/ ++ ++static void do_gettime(unsigned short *hour, unsigned short *min, ++ unsigned short *sec, unsigned short *msec) ++{ ++ long val; ++ struct timeval tv; ++ ++ do_gettimeofday(&tv); ++ val = tv.tv_sec % 86400; /* the second form 0 hour */ ++ ++ if (hour) ++ *hour = val / 3600; ++ val %= 3600; ++ if (min) ++ *min = val / 60; ++ if (sec) ++ *sec = val % 60; ++ if (msec) ++ *msec = tv.tv_usec / 1000; ++} ++/*****************************************************************************/ ++ ++static ssize_t dbgfs_ecc_count_read(struct file *filp, char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ int len = 0; ++ char buf[128] = {0}; ++ unsigned int read_index; ++ char __user *pusrbuf = buffer; ++ struct hinfc610_dbg_ecc_count_item_t *item; ++ ++ if (*ppos == 0) { ++ ++ if (dbg_ecc_count->count ++ < CONFIG_HINFC610_DBG_ECC_COUNT_NUM) ++ dbg_ecc_count->read_index = 0; ++ else ++ dbg_ecc_count->read_index ++ = (dbg_ecc_count->index + 1); ++ ++ len = snprintf(buf, sizeof(buf), ++ "Print parameter: \"offset=%d length=%d\"\n", ++ dbg_ecc_count->offset, ++ dbg_ecc_count->length); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ ++ pusrbuf += len; ++ ++ len = snprintf(buf, sizeof(buf), ++ " UTC Clock page ecc data\n"); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ ++ pusrbuf += len; ++ ++ } else if (dbg_ecc_count->read_index == dbg_ecc_count->index) ++ return 0; ++ ++ for (read_index = dbg_ecc_count->read_index; ++ (read_index != dbg_ecc_count->index); ++read_index) { ++ ++ if (read_index >= CONFIG_HINFC610_DBG_ECC_COUNT_NUM) ++ read_index = 0; ++ ++ item = GET_ITEM(dbg_ecc_count, read_index); ++ ++ if ((count - (pusrbuf - buffer)) < 80) ++ break; ++ ++ len = snprintf(buf, sizeof(buf), ++ "%02d:%02d:%02d.%04d 0x%08X ", ++ item->hour, item->min, item->sec, item->msec, ++ item->page); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ ++ pusrbuf += len; ++ ++ if (IS_PS_BAD_BLOCK(item) || IS_PS_EMPTY_PAGE(item) || ++ IS_PS_UN_ECC(item)) { ++ char *ptr = buf; ++ ++ if (IS_PS_BAD_BLOCK(item)) ++ ptr += snprintf(ptr, 16, "bb "); ++ else if (IS_PS_EMPTY_PAGE(item)) ++ ptr += snprintf(ptr, 16, "ep "); ++ ++ if (IS_PS_UN_ECC(item)) ++ ptr += snprintf(ptr, 16, "ecc "); ++ ++ if (IS_PS_EPM_ERR(item) || IS_PS_BBM_ERR(item)) ++ ptr += snprintf(ptr, 16, "? "); ++ ++ ptr += snprintf(ptr, 16, "\n"); ++ len = (ptr - buf); ++ ++ } else { ++ int ix; ++ char *ptr = buf; ++ ++ for (ix = 0; ix < dbg_ecc_count->ecc_inf->section; ix++) ++ ptr += snprintf(ptr, 16, "%d/", item->ecc[ix]); ++ ++ if (IS_PS_EPM_ERR(item)) ++ ptr += snprintf(ptr, 16, " ?"); ++ ++ ptr += snprintf(ptr, 16, "\n"); ++ len = (ptr - buf); ++ } ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ } ++ ++ dbg_ecc_count->read_index = read_index; ++ ++ *ppos += (pusrbuf - buffer); ++ return pusrbuf - buffer; ++} ++/******************************************************************************/ ++/* ++ * echo "offset=8192,length=102400" > ecc_count ++ * ++ */ ++static ssize_t dbgfs_ecc_count_write(struct file *filp, ++ const char __user *buffer, size_t count, ++ loff_t *ppos) ++{ ++ char *str; ++ char buf[128] = {0}; ++ int ret; ++ unsigned long value = 0; ++ unsigned long pos = 0; ++ ++ if (count > sizeof(buf)) ++ count = sizeof(buf); ++ ++ if (copy_from_user(buf, buffer, count)) ++ return -EFAULT; ++ ++ while (pos < count) { ++ while (pos < count && ++ (buf[pos] == ' ' || buf[pos] == ',' || buf[pos] == ';')) ++ pos++; ++ ++ if (pos >= count) ++ break; ++ ++ switch (buf[pos]) { ++ ++ case 'o': ++ ++ if (memcmp(&buf[pos], CMD_WORD_OFFSET, ++ sizeof(CMD_WORD_OFFSET) - 1)) ++ break; ++ ++ pos += sizeof(CMD_WORD_OFFSET) - 1; ++ str = (char *)(buf + pos); ++ ret = kstrtoul(str, 10, &value); ++ ++ if (ret < 0) ++ value = 0; ++ if (value >= dbg_ecc_count->pagecount) ++ value = 0; ++ ++ dbg_ecc_count->offset = (value & ~7); ++ ++ break; ++ ++ case 'l': ++ if (memcmp(&buf[pos], CMD_WORD_LENGTH, ++ sizeof(CMD_WORD_LENGTH) - 1)) ++ break; ++ ++ pos += sizeof(CMD_WORD_LENGTH) - 1; ++ str = (char *)(buf + pos); ++ ret = kstrtoul(str, 10, &value); ++ ++ if (ret < 0) ++ value = dbg_ecc_count->pagecount; ++ ++ value = ((value + 7) & ~7); ++ ++ if (dbg_ecc_count->offset + value > ++ dbg_ecc_count->pagecount) ++ value = dbg_ecc_count->pagecount ++ - dbg_ecc_count->offset; ++ ++ dbg_ecc_count->length = value; ++ ++ break; ++ } ++ ++ while (pos < count && ++ (buf[pos] != ' ' && buf[pos] != ',' && buf[pos] != ';')) ++ pos++; ++ } ++ ++ return count; ++} ++/******************************************************************************/ ++ ++static const struct file_operations dbgfs_ecc_count_fops = { ++ .owner = THIS_MODULE, ++ .read = dbgfs_ecc_count_read, ++ .write = dbgfs_ecc_count_write, ++}; ++/*****************************************************************************/ ++ ++static int dbgfs_ecc_count_init(struct dentry *root, struct hinfc_host *host) ++{ ++ unsigned int size; ++ unsigned int pagesize; ++ unsigned int chipsize; ++ struct hinfc610_ecc_inf_t *ecc_inf; ++ struct hinfc610_dbg_ecc_count_t *ecc_count; ++ ++ if (dbg_ecc_count) ++ return 0; ++ ++ ecc_inf = hinfc610_get_ecc_inf(host, host->pagesize, host->ecctype); ++ if (!ecc_inf) { ++ pr_warn("ecc_count: The NAND not support this interface.\n"); ++ return -1; ++ } ++ ++ size = sizeof(struct hinfc610_dbg_ecc_count_t); ++ size += CONFIG_HINFC610_DBG_ECC_COUNT_NUM * ++ (sizeof(struct hinfc610_dbg_ecc_count_item_t) ++ + ecc_inf->section); ++ ++ ecc_count = vmalloc(size); ++ if (!ecc_count) { ++ PR_ERR("Can't allocate memory.\n"); ++ return -ENOMEM; ++ } ++ memset(ecc_count, 0, size); ++ ++ ecc_count->item = (char *)ecc_count + ++ sizeof(struct hinfc610_dbg_ecc_count_t); ++ ecc_count->ecc_inf = ecc_inf; ++ ++ pagesize = (host->pagesize >> 10); ++ chipsize = (unsigned int)(host->chip->chipsize >> 10); ++ ecc_count->pagecount = (chipsize / pagesize); ++ ecc_count->length = ecc_count->pagecount; ++ ++ ecc_count->dentry = debugfs_create_file("ecc_count", ++ S_IFREG | S_IRUSR | S_IWUSR, ++ root, NULL, &dbgfs_ecc_count_fops); ++ if (!ecc_count->dentry) { ++ PR_ERR("Can't create 'ecc_count' file.\n"); ++ vfree(ecc_count); ++ return -ENOENT; ++ } ++ ++ dbg_ecc_count = ecc_count; ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int dbgfs_ecc_count_uninit(void) ++{ ++ if (!dbg_ecc_count) ++ return 0; ++ ++ mutex_lock(&dbg_ecc_count_mutex); ++ ++ debugfs_remove(dbg_ecc_count->dentry); ++ ++ vfree(dbg_ecc_count); ++ dbg_ecc_count = NULL; ++ ++ mutex_unlock(&dbg_ecc_count_mutex); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static void dbg_ecc_count_read(struct hinfc_host *host) ++{ ++ unsigned int page; ++ struct hinfc610_dbg_ecc_count_item_t *item; ++ ++ mutex_lock(&dbg_ecc_count_mutex); ++ ++ if (!dbg_ecc_count) ++ goto exit; ++ ++ page = GET_PAGE_INDEX(host); ++ ++ if (page < dbg_ecc_count->offset || ++ page > (dbg_ecc_count->offset + dbg_ecc_count->length)) ++ goto exit; ++ ++ item = GET_ITEM(dbg_ecc_count, dbg_ecc_count->index); ++ ++ dbg_ecc_count->count++; ++ ++ do_gettime(&item->hour, &item->min, &item->sec, &item->msec); ++ ++ item->page = page; ++ item->page_status = host->page_status; ++ ++ if (!IS_PS_UN_ECC(host)) ++ dbg_ecc_count->ecc_inf->ecc_inf(host, item->ecc); ++ ++ if (++dbg_ecc_count->index >= CONFIG_HINFC610_DBG_ECC_COUNT_NUM) ++ dbg_ecc_count->index = 0; ++ ++exit: ++ mutex_unlock(&dbg_ecc_count_mutex); ++} ++/*****************************************************************************/ ++ ++struct hinfc610_dbg_inf_t hinfc610_dbg_inf_ecc_count = { ++ "ecc_count", 0, ++ dbgfs_ecc_count_init, ++ dbgfs_ecc_count_uninit, ++ dbg_ecc_count_read, ++ NULL, ++ NULL, ++ NULL, ++}; ++/*****************************************************************************/ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_dbg_ecc_dump.c b/drivers/mtd/nand/hinfc610/hinfc610_dbg_ecc_dump.c +new file mode 100644 +index 0000000..a1b7eda +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_dbg_ecc_dump.c +@@ -0,0 +1,141 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/moduleparam.h> ++#include <linux/vmalloc.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/debugfs.h> ++#include <linux/uaccess.h> ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++#include "hinfc610_dbg.h" ++ ++/*****************************************************************************/ ++static inline void hinfc610_detect_ecc(unsigned char ecc[], int begin, ++ int end, unsigned int reg) ++{ ++ while (begin < end) { ++ ecc[begin] = (reg & 0xff); ++ reg = (reg >> 8); ++ begin++; ++ } ++} ++/*****************************************************************************/ ++ ++static void hinfc610_ecc_32k(struct hinfc_host *host, unsigned char ecc[]) ++{ ++ int ix, jx, kx; ++ ++ for (ix = 0, jx = 0; ix < 4; ix ++, jx += 4) ++ hinfc610_detect_ecc(ecc, jx, jx + 4, ++ hinfc_read(host, 0xA0 + jx)); ++ kx = jx; ++ for (ix = 0, jx = 0; ix < 4; ix ++, jx += 4) ++ hinfc610_detect_ecc(ecc, kx, kx + 4, ++ hinfc_read(host, 0xDC + jx)); ++} ++/*****************************************************************************/ ++ ++static void hinfc610_ecc_16k(struct hinfc_host *host, unsigned char ecc[]) ++{ ++ int ix, jx; ++ ++ for (ix = 0, jx = 0; ix < 4; ix ++, jx += 4) ++ hinfc610_detect_ecc(ecc, jx, jx + 4, ++ hinfc_read(host, 0xA0 + jx)); ++} ++/*****************************************************************************/ ++ ++static void hinfc610_ecc_8k(struct hinfc_host *host, unsigned char ecc[]) ++{ ++ int ix, jx; ++ ++ for (ix = 0, jx = 0; ix < 2; ix ++, jx += 4) ++ hinfc610_detect_ecc(ecc, jx, jx + 4, ++ hinfc_read(host, 0xA0 + jx)); ++} ++/*****************************************************************************/ ++ ++static void hinfc610_ecc_4k(struct hinfc_host *host, unsigned char ecc[]) ++{ ++ hinfc610_detect_ecc(ecc, 0, 4, hinfc_read(host, 0xA0)); ++} ++/*****************************************************************************/ ++ ++static void hinfc610_ecc_2k(struct hinfc_host *host, unsigned char ecc[]) ++{ ++ hinfc610_detect_ecc(ecc, 0, 2, hinfc_read(host, 0xA0)); ++} ++/*****************************************************************************/ ++ ++static struct hinfc610_ecc_inf_t hinfc610_ecc_inf[] = { ++ ++ {32768, NAND_ECC_80BIT, 32, hinfc610_ecc_32k}, ++ {32768, NAND_ECC_72BIT, 32, hinfc610_ecc_32k}, ++ {32768, NAND_ECC_60BIT, 32, hinfc610_ecc_32k}, ++ {32768, NAND_ECC_48BIT, 32, hinfc610_ecc_32k}, ++ {32768, NAND_ECC_41BIT, 32, hinfc610_ecc_32k}, ++ ++ {16384, NAND_ECC_80BIT, 16, hinfc610_ecc_16k}, ++ {16384, NAND_ECC_72BIT, 16, hinfc610_ecc_16k}, ++ {16384, NAND_ECC_60BIT, 16, hinfc610_ecc_16k}, ++ {16384, NAND_ECC_48BIT, 16, hinfc610_ecc_16k}, ++ {16384, NAND_ECC_41BIT, 16, hinfc610_ecc_16k}, ++ ++ {8192, NAND_ECC_80BIT, 8, hinfc610_ecc_8k}, ++ {8192, NAND_ECC_72BIT, 8, hinfc610_ecc_8k}, ++ {8192, NAND_ECC_60BIT, 8, hinfc610_ecc_8k}, ++ {8192, NAND_ECC_48BIT, 8, hinfc610_ecc_8k}, ++ {8192, NAND_ECC_41BIT, 8, hinfc610_ecc_8k}, ++ {8192, NAND_ECC_32BIT, 8, hinfc610_ecc_8k}, ++ {8192, NAND_ECC_27BIT, 8, hinfc610_ecc_8k}, ++ {8192, NAND_ECC_24BIT, 8, hinfc610_ecc_8k}, ++ ++ ++ ++ {4096, NAND_ECC_32BIT, 4, hinfc610_ecc_4k}, ++ {4096, NAND_ECC_27BIT, 4, hinfc610_ecc_4k}, ++ {4096, NAND_ECC_24BIT, 4, hinfc610_ecc_4k}, ++ {4096, NAND_ECC_18BIT, 4, hinfc610_ecc_4k}, ++ {4096, NAND_ECC_13BIT, 4, hinfc610_ecc_4k}, ++ {4096, NAND_ECC_8BIT, 4, hinfc610_ecc_4k}, ++ ++ {2048, NAND_ECC_32BIT, 2, hinfc610_ecc_2k}, ++ {2048, NAND_ECC_27BIT, 2, hinfc610_ecc_2k}, ++ {2048, NAND_ECC_24BIT, 2, hinfc610_ecc_2k}, ++ {2048, NAND_ECC_18BIT, 2, hinfc610_ecc_2k}, ++ {2048, NAND_ECC_13BIT, 2, hinfc610_ecc_2k}, ++ {2048, NAND_ECC_8BIT, 2, hinfc610_ecc_2k}, ++ {0, 0, 0}, ++}; ++/*****************************************************************************/ ++ ++struct hinfc610_ecc_inf_t *hinfc610_get_ecc_inf(struct hinfc_host *host, ++ int pagesize, int ecctype) ++{ ++ struct hinfc610_ecc_inf_t *inf; ++ ++ for (inf = hinfc610_ecc_inf; inf->pagesize; inf++) ++ if (inf->pagesize == pagesize && inf->ecctype == ecctype) ++ return inf; ++ ++ return NULL; ++} +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_dbg_erase_count.c b/drivers/mtd/nand/hinfc610/hinfc610_dbg_erase_count.c +new file mode 100644 +index 0000000..eedbd2b +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_dbg_erase_count.c +@@ -0,0 +1,317 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/moduleparam.h> ++#include <linux/vmalloc.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/debugfs.h> ++#include <linux/uaccess.h> ++#include <linux/mutex.h> ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++#include "hinfc610_dbg.h" ++ ++struct hinfc610_dbg_erase_count_t { ++ ++ unsigned int index; /* display pos */ ++ unsigned int offset; /* display offset */ ++ unsigned int length; /* display length */ ++ ++ struct dentry *dentry; ++ ++ unsigned int blocknum; ++ unsigned int page_per_block; ++ ++ unsigned int pe[1]; ++}; ++ ++static DEFINE_MUTEX(dbg_erase_count_mutex); ++static struct hinfc610_dbg_erase_count_t *dbg_erase_count; ++ ++/*****************************************************************************/ ++ ++static int dbgfs_erase_count_read(struct file *filp, char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ int len = 0; ++ int value = 0; ++ unsigned int *pe; ++ unsigned int index; ++ char buf[128] = {0}; ++ char __user *pusrbuf = buffer; ++ ++ if (*ppos == 0) { ++ dbg_erase_count->index = dbg_erase_count->offset; ++ ++ len = snprintf(buf, sizeof(buf), ++ "Print parameter: \"offset=%d length=%d\"\n", ++ dbg_erase_count->offset, ++ dbg_erase_count->length); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ ++ len = snprintf(buf, sizeof(buf), ++ "Block Index ---------------- " ++ "Erase count from system startup ----------------\n"); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ } ++ ++ for (index = dbg_erase_count->index; ++ index < (dbg_erase_count->offset + dbg_erase_count->length) && ++ ((pusrbuf - buffer) < (count - 100)); ++ index += 8) { ++ ++ pe = &dbg_erase_count->pe[index]; ++ ++ len = snprintf(buf, sizeof(buf), ++ "%4d: %8u %8u %8u %8u %8u %8u %8u %8u\n", ++ index, ++ pe[0], pe[1], pe[2], pe[3], ++ pe[4], pe[5], pe[6], pe[7]); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ } ++ ++ dbg_erase_count->index = index; ++ ++ *ppos += (pusrbuf - buffer); ++ value = pusrbuf - buffer; ++ return value; ++} ++/*****************************************************************************/ ++/* ++ * echo "offset=48,length=78" > /sys/kernel/debug/nand/erase_count ++ * echo "clear" > /sys/kernel/debug/nand/erase_count ++ * ++ ++ # cat ./debugfs/nand/erase_count ++ Print parameter: "offset=0 length=1024" ++ Block Index ---------------- Erase count from system startup ---------------- ++ 0: 0 0 0 0 0 0 0 0 ++ 8: 0 0 0 0 0 0 0 0 ++ 16: 0 0 0 0 0 0 0 0 ++ 24: 0 0 0 0 0 0 0 0 ++ 32: 0 0 0 0 0 0 0 0 ++ 40: 0 0 0 0 0 0 0 0 ++ 48: 0 0 0 0 0 0 0 0 ++ 56: 0 0 0 0 0 0 0 0 ++ 64: 0 0 0 0 0 0 0 0 ++ 72: 0 0 0 0 0 0 0 0 ++ 80: 0 0 0 0 0 0 0 0 ++ ++ */ ++static int dbgfs_erase_count_write(struct file *filp, ++ const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ char *str; ++ char buf[128] = {0}; ++ int ret; ++ unsigned long value = 0; ++ unsigned long pos = 0; ++ ++ if (count > sizeof(buf)) ++ count = sizeof(buf); ++ ++ if (copy_from_user(buf, buffer, count)) ++ return -EFAULT; ++ ++ while (pos < count) { ++ while (pos < count && ++ (buf[pos] == ' ' || buf[pos] == ',' || buf[pos] == ';')) ++ pos++; ++ ++ if (pos >= count) ++ break; ++ ++ switch (buf[pos]) { ++ ++ case 'o': ++ ++ if (memcmp(&buf[pos], CMD_WORD_OFFSET, ++ sizeof(CMD_WORD_OFFSET) - 1)) ++ break; ++ ++ pos += sizeof(CMD_WORD_OFFSET) - 1; ++ str = (char *)(buf + pos); ++ ret = kstrtoul(str, 10, &value); ++ if (ret < 0) ++ value = 0; ++ if (value >= dbg_erase_count->blocknum) ++ value = 0; ++ ++ dbg_erase_count->offset = (value & ~7); ++ ++ break; ++ ++ case 'l': ++ if (memcmp(&buf[pos], CMD_WORD_LENGTH, ++ sizeof(CMD_WORD_LENGTH) - 1)) ++ break; ++ ++ pos += sizeof(CMD_WORD_LENGTH) - 1; ++ str = (char *)(buf + pos); ++ ret = kstrtoul(str, 10, &value); ++ if (ret < 0) ++ value = dbg_erase_count->blocknum; ++ ++ value = ((value + 7) & ~7); ++ ++ if (dbg_erase_count->offset + value ++ > dbg_erase_count->blocknum) ++ value = dbg_erase_count->blocknum ++ - dbg_erase_count->offset; ++ ++ dbg_erase_count->length = value; ++ ++ break; ++ ++ case 'c': ++ if (memcmp(&buf[pos], CMD_WORD_CLEAN, ++ sizeof(CMD_WORD_CLEAN) - 1)) ++ break; ++ ++ memset(dbg_erase_count->pe, 0, ++ dbg_erase_count->blocknum * ++ sizeof(struct hinfc610_dbg_erase_count_t)); ++ ++ return count; ++ } ++ ++ while (pos < count && ++ (buf[pos] != ' ' && buf[pos] != ',' && buf[pos] != ';')) ++ pos++; ++ } ++ ++ return count; ++} ++/*****************************************************************************/ ++ ++static const struct file_operations dbgfs_erase_count_fops = { ++ .owner = THIS_MODULE, ++ .read = dbgfs_erase_count_read, ++ .write = dbgfs_erase_count_write, ++}; ++/*****************************************************************************/ ++ ++static int dbgfs_erase_count_init(struct dentry *root, struct hinfc_host *host) ++{ ++ unsigned int size; ++ unsigned int blocknum; ++ unsigned int pagesize; ++ unsigned int blocksize; ++ unsigned int chipsize; ++ struct hinfc610_dbg_erase_count_t *erase_count; ++ ++ if (dbg_erase_count) ++ return 0; ++ ++ pagesize = (host->pagesize >> 10); ++ blocksize = (host->mtd->erasesize >> 10); ++ chipsize = (unsigned int)(host->chip->chipsize >> 10); ++ ++ blocknum = chipsize / blocksize; ++ size = sizeof(int) * blocknum ++ + sizeof(struct hinfc610_dbg_erase_count_t); ++ ++ erase_count = vmalloc(size); ++ if (!erase_count) { ++ PR_ERR("Can't allocate memory.\n"); ++ return -ENOMEM; ++ } ++ memset(erase_count, 0, size); ++ ++ erase_count->blocknum = blocknum; ++ erase_count->page_per_block = blocksize / pagesize; ++ erase_count->length = blocknum; ++ ++ erase_count->dentry = debugfs_create_file("erase_count", ++ S_IFREG | S_IRUSR | S_IWUSR, ++ root, NULL, &dbgfs_erase_count_fops); ++ if (!erase_count->dentry) { ++ PR_ERR("Can't create 'erase_count' file.\n"); ++ vfree(erase_count); ++ return -ENOENT; ++ } ++ ++ dbg_erase_count = erase_count; ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int dbgfs_erase_count_uninit(void) ++{ ++ if (!dbg_erase_count) ++ return 0; ++ ++ mutex_lock(&dbg_erase_count_mutex); ++ ++ debugfs_remove(dbg_erase_count->dentry); ++ ++ vfree(dbg_erase_count); ++ dbg_erase_count = NULL; ++ ++ mutex_unlock(&dbg_erase_count_mutex); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static void dbg_erase_count_erase(struct hinfc_host *host) ++{ ++ unsigned int block_index; ++ ++ mutex_lock(&dbg_erase_count_mutex); ++ ++ if (!dbg_erase_count) ++ goto exit; ++ ++ block_index = (host->addr_value[0] / dbg_erase_count->page_per_block); ++ ++ if (block_index > dbg_erase_count->blocknum) { ++ PR_ERR("Block out of range.\n"); ++ return; ++ } ++ ++ dbg_erase_count->pe[block_index]++; ++ ++exit: ++ mutex_unlock(&dbg_erase_count_mutex); ++} ++/*****************************************************************************/ ++ ++struct hinfc610_dbg_inf_t hinfc610_dbg_inf_erase_count = { ++ "erase_count", 0, ++ dbgfs_erase_count_init, ++ dbgfs_erase_count_uninit, ++ NULL, ++ NULL, ++ dbg_erase_count_erase, ++ NULL, ++}; +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_dbg_inf.c b/drivers/mtd/nand/hinfc610/hinfc610_dbg_inf.c +new file mode 100644 +index 0000000..f3e7140 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_dbg_inf.c +@@ -0,0 +1,81 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/moduleparam.h> ++#include <linux/vmalloc.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/debugfs.h> ++#include <linux/uaccess.h> ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++#include "hinfc610_dbg.h" ++ ++void hinfc610_dbg_write(struct hinfc_host *host) ++{ ++#ifdef CONFIG_HINFC610_DBG_NAND_DEBUG ++ struct hinfc610_dbg_inf_t **inf; ++ ++ for (inf = hinfc610_dbg_inf; *inf; inf++) ++ if ((*inf)->enable && (*inf)->write) ++ (*inf)->write(host); ++#endif ++} ++ ++void hinfc610_dbg_erase(struct hinfc_host *host) ++{ ++#ifdef CONFIG_HINFC610_DBG_NAND_DEBUG ++ struct hinfc610_dbg_inf_t **inf; ++ ++ for (inf = hinfc610_dbg_inf; *inf; inf++) ++ if ((*inf)->enable && (*inf)->erase) ++ (*inf)->erase(host); ++#endif ++} ++ ++void hinfc610_dbg_read(struct hinfc_host *host) ++{ ++#ifdef CONFIG_HINFC610_DBG_NAND_DEBUG ++ struct hinfc610_dbg_inf_t **inf; ++ ++ for (inf = hinfc610_dbg_inf; *inf; inf++) ++ if ((*inf)->enable && (*inf)->read) ++ (*inf)->read(host); ++#endif ++} ++ ++void hinfc610_dbg_read_retry(struct hinfc_host *host, int index) ++{ ++#ifdef CONFIG_HINFC610_DBG_NAND_DEBUG ++ struct hinfc610_dbg_inf_t **inf; ++ ++ for (inf = hinfc610_dbg_inf; *inf; inf++) ++ if ((*inf)->enable && (*inf)->read_retry) ++ (*inf)->read_retry(host, index); ++#endif ++} ++ ++int hinfc610_dbg_init(struct hinfc_host *host) ++{ ++#ifdef CONFIG_HINFC610_DBG_NAND_DEBUG ++ return hinfc610_dbgfs_debug_init(host); ++#endif ++ return 0; ++} +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_dbg_inf.h b/drivers/mtd/nand/hinfc610/hinfc610_dbg_inf.h +new file mode 100644 +index 0000000..10ac797 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_dbg_inf.h +@@ -0,0 +1,34 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef HINFC610_DBG_INFH ++#define HINFC610_DBG_INFH ++/******************************************************************************/ ++ ++int hinfc610_dbg_init(struct hinfc_host *host); ++ ++void hinfc610_dbg_write(struct hinfc_host *host); ++ ++void hinfc610_dbg_erase(struct hinfc_host *host); ++ ++void hinfc610_dbg_read(struct hinfc_host *host); ++ ++void hinfc610_dbg_read_retry(struct hinfc_host *host, int index); ++ ++/******************************************************************************/ ++#endif /* HINFC610_DBG_INFH */ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_dbg_read_retry.c b/drivers/mtd/nand/hinfc610/hinfc610_dbg_read_retry.c +new file mode 100644 +index 0000000..176d9c1 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_dbg_read_retry.c +@@ -0,0 +1,385 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/moduleparam.h> ++#include <linux/vmalloc.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/debugfs.h> ++#include <linux/uaccess.h> ++#include <linux/mutex.h> ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++#include "hinfc610_dbg.h" ++ ++#ifndef CONFIG_HINFC610_DBG_READ_RETRY_NUM ++# define CONFIG_HINFC610_DBG_READ_RETRY_NUM (100) ++#endif /* CONFIG_HINFC610_DBG_READ_RETRY_NUM */ ++ ++struct hinfc610_dbg_read_retry_item_t { ++ unsigned int page; ++ ++ unsigned short hour; ++ unsigned short min; ++ unsigned short sec; ++ unsigned short msec; ++ ++ unsigned short retry; /* success retry */ ++ unsigned short ecc_err; ++}; ++ ++struct hinfc610_dbg_read_retry_t { ++ ++ struct dentry *dentry; ++ unsigned int index; /* current logs index */ ++ int count; /* number of logs */ ++ ++ unsigned int offset; ++ unsigned int length; ++ unsigned int pagecount; ++ ++ unsigned int read_index; ++ ++ unsigned int max_retry; /* the max read retry times */ ++ unsigned int retry[16]; ++ ++ struct hinfc610_dbg_read_retry_item_t ++ item[CONFIG_HINFC610_DBG_READ_RETRY_NUM]; ++}; ++ ++static DEFINE_MUTEX(dbg_read_retry_mutex); ++static struct hinfc610_dbg_read_retry_t *dbg_read_retry; ++/*****************************************************************************/ ++ ++static void do_gettime(unsigned short *hour, unsigned short *min, ++ unsigned short *sec, unsigned short *msec) ++{ ++ long val; ++ struct timeval tv; ++ ++ do_gettimeofday(&tv); ++ val = tv.tv_sec % 86400; /* the second form 0 hour */ ++ ++ if (hour) ++ *hour = val / 3600; ++ val %= 3600; ++ if (min) ++ *min = val / 60; ++ if (sec) ++ *sec = val % 60; ++ if (msec) ++ *msec = tv.tv_usec / 1000; ++} ++/*****************************************************************************/ ++ ++static ssize_t dbgfs_read_retry_read(struct file *filp, char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ int ix; ++ char *ptr; ++ int len = 0; ++ char buf[128] = {0}; ++ unsigned int read_index; ++ char __user *pusrbuf = buffer; ++ struct hinfc610_dbg_read_retry_item_t *item; ++ ++ if (*ppos == 0) { ++ ++ if (dbg_read_retry->count ++ < CONFIG_HINFC610_DBG_READ_RETRY_NUM) ++ dbg_read_retry->read_index = 0; ++ else ++ dbg_read_retry->read_index ++ = (dbg_read_retry->index + 1); ++ ++ len = snprintf(buf, sizeof(buf), ++ "Print parameter: \"offset=%d length=%d\"\n", ++ dbg_read_retry->offset, ++ dbg_read_retry->length); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ ++ len = snprintf(buf, sizeof(buf), ++ " UTC Clock page read retry (max: %d)\n", ++ dbg_read_retry->max_retry); ++ ++ ptr = buf; ++ ptr += sprintf(ptr, "Read retry: "); ++ for (ix = 1; ix <= dbg_read_retry->max_retry; ix++) ++ ptr += sprintf(ptr, "%d, ", dbg_read_retry->retry[ix]); ++ ptr += sprintf(ptr, "\n"); ++ ++ len = (ptr - buf); ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ ++ ++ len = snprintf(buf, sizeof(buf), ++ " UTC Clock page read retry (max: %d)\n", ++ dbg_read_retry->max_retry); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ ++ } else if (dbg_read_retry->read_index == dbg_read_retry->index) ++ return 0; ++ ++ for (read_index = dbg_read_retry->read_index; ++ (read_index != dbg_read_retry->index); ++read_index) { ++ ++ if (read_index >= CONFIG_HINFC610_DBG_READ_RETRY_NUM) ++ read_index = 0; ++ ++ item = &dbg_read_retry->item[read_index]; ++ ++ if ((count - (pusrbuf - buffer)) < 80) ++ break; ++ ++ len = snprintf(buf, sizeof(buf), ++ "%02d:%02d:%02d.%04d 0x%08X ", ++ item->hour, item->min, item->sec, item->msec, ++ item->page); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ ++ if (!item->ecc_err) ++ len = sprintf(buf, "%d\n", item->retry); ++ else ++ len = sprintf(buf, "fail\n"); ++ ++ if (copy_to_user(pusrbuf, buf, len)) ++ return -EFAULT; ++ pusrbuf += len; ++ } ++ ++ dbg_read_retry->read_index = read_index; ++ ++ *ppos += (pusrbuf - buffer); ++ return pusrbuf - buffer; ++} ++/******************************************************************************/ ++/* ++ * echo "offset=8192,length=102400" > read_retry ++ * ++ */ ++static ssize_t dbgfs_read_retry_write(struct file *filp, ++ const char __user *buffer, size_t count, ++ loff_t *ppos) ++{ ++ char *str; ++ char buf[128] = {0}; ++ int ret; ++ unsigned long value = 0; ++ unsigned long pos = 0; ++ ++ if (count > sizeof(buf)) ++ count = sizeof(buf); ++ ++ if (copy_from_user(buf, buffer, count)) ++ return -EFAULT; ++ ++ while (pos < count) { ++ while (pos < count && ++ (buf[pos] == ' ' || buf[pos] == ',' || buf[pos] == ';')) ++ pos++; ++ ++ if (pos >= count) ++ break; ++ ++ switch (buf[pos]) { ++ ++ case 'o': ++ ++ if (memcmp(&buf[pos], CMD_WORD_OFFSET, ++ sizeof(CMD_WORD_OFFSET) - 1)) ++ break; ++ ++ pos += sizeof(CMD_WORD_OFFSET) - 1; ++ str = (char *)(buf + pos); ++ ret = kstrtoul(str, 10, &value); ++ ++ if (ret < 0) ++ value = 0; ++ if (value >= dbg_read_retry->pagecount) ++ value = 0; ++ ++ dbg_read_retry->offset = (value & ~7); ++ ++ break; ++ ++ case 'l': ++ if (memcmp(&buf[pos], CMD_WORD_LENGTH, ++ sizeof(CMD_WORD_LENGTH) - 1)) ++ break; ++ ++ pos += sizeof(CMD_WORD_LENGTH) - 1; ++ str = (char *)(buf + pos); ++ ret = kstrtoul(str, 10, &value); ++ ++ if (ret < 0) ++ value = dbg_read_retry->pagecount; ++ ++ value = ((value + 7) & ~7); ++ ++ if (dbg_read_retry->offset + value > ++ dbg_read_retry->pagecount) ++ value = dbg_read_retry->pagecount ++ - dbg_read_retry->offset; ++ ++ dbg_read_retry->length = value; ++ ++ break; ++ } ++ ++ while (pos < count && ++ (buf[pos] != ' ' && buf[pos] != ',' && buf[pos] != ';')) ++ pos++; ++ } ++ ++ return count; ++} ++/******************************************************************************/ ++ ++static const struct file_operations dbgfs_read_retry_fops = { ++ .owner = THIS_MODULE, ++ .read = dbgfs_read_retry_read, ++ .write = dbgfs_read_retry_write, ++}; ++/*****************************************************************************/ ++ ++static int dbgfs_read_retry_init(struct dentry *root, struct hinfc_host *host) ++{ ++ unsigned int pagesize; ++ unsigned int chipsize; ++ struct hinfc610_dbg_read_retry_t *read_retry; ++ ++ if (dbg_read_retry) ++ return 0; ++ ++ if (!host->read_retry) { ++ pr_warn("read_retry: The NAND not support this interface.\n"); ++ return -1; ++ } ++ ++ read_retry = vmalloc(sizeof(struct hinfc610_dbg_read_retry_t)); ++ if (!read_retry) { ++ PR_ERR("Can't allocate memory.\n"); ++ return -ENOMEM; ++ } ++ memset(read_retry, 0, sizeof(struct hinfc610_dbg_read_retry_t)); ++ ++ pagesize = (host->pagesize >> 10); ++ chipsize = (unsigned int)(host->chip->chipsize >> 10); ++ read_retry->pagecount = (chipsize / pagesize); ++ read_retry->length = read_retry->pagecount; ++ read_retry->max_retry = host->read_retry->count; ++ ++ if (read_retry->max_retry > 16) { ++ vfree(read_retry); ++ PR_ERR("Bug, max_retry too small.\n"); ++ return -EFAULT; ++ } ++ ++ read_retry->dentry = debugfs_create_file("read_retry", ++ S_IFREG | S_IRUSR | S_IWUSR, ++ root, NULL, &dbgfs_read_retry_fops); ++ if (!read_retry->dentry) { ++ PR_ERR("Can't create 'read_retry' file.\n"); ++ vfree(read_retry); ++ return -ENOENT; ++ } ++ ++ dbg_read_retry = read_retry; ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int dbgfs_read_retry_uninit(void) ++{ ++ if (!dbg_read_retry) ++ return 0; ++ ++ mutex_lock(&dbg_read_retry_mutex); ++ ++ debugfs_remove(dbg_read_retry->dentry); ++ ++ vfree(dbg_read_retry); ++ dbg_read_retry = NULL; ++ ++ mutex_unlock(&dbg_read_retry_mutex); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static void hinfc610_dbg_read_retry_rr(struct hinfc_host *host, int index) ++{ ++ unsigned int page; ++ struct hinfc610_dbg_read_retry_item_t *item; ++ ++ mutex_lock(&dbg_read_retry_mutex); ++ ++ if (!dbg_read_retry) ++ goto exit; ++ ++ page = GET_PAGE_INDEX(host); ++ ++ if (page < dbg_read_retry->offset || ++ page > (dbg_read_retry->offset + dbg_read_retry->length)) ++ goto exit; ++ ++ item = &dbg_read_retry->item[dbg_read_retry->index]; ++ ++ dbg_read_retry->count++; ++ ++ do_gettime(&item->hour, &item->min, &item->sec, &item->msec); ++ ++ item->page = page; ++ item->retry = index; ++ ++ item->ecc_err = IS_PS_UN_ECC(host) ? 1 : 0; ++ if (!item->ecc_err) ++ dbg_read_retry->retry[index]++; ++ ++ if (++dbg_read_retry->index >= CONFIG_HINFC610_DBG_READ_RETRY_NUM) ++ dbg_read_retry->index = 0; ++ ++exit: ++ mutex_unlock(&dbg_read_retry_mutex); ++} ++/*****************************************************************************/ ++ ++struct hinfc610_dbg_inf_t hinfc610_dbg_inf_read_retry = { ++ "read_retry", 0, ++ dbgfs_read_retry_init, ++ dbgfs_read_retry_uninit, ++ NULL, ++ NULL, ++ NULL, ++ hinfc610_dbg_read_retry_rr, ++}; ++/*****************************************************************************/ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_gen.c b/drivers/mtd/nand/hinfc610/hinfc610_gen.c +new file mode 100644 +index 0000000..2b43cf9 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_gen.c +@@ -0,0 +1,85 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "../match_table.h" ++#include "hinfc610_gen.h" ++ ++/*****************************************************************************/ ++ ++static struct match_reg_type page_type2reg[] = { ++ { ++ hinfc610_pagesize_2K, NAND_PAGE_2K, ++ }, { ++ hinfc610_pagesize_4K, NAND_PAGE_4K, ++ }, { ++ hinfc610_pagesize_8K, NAND_PAGE_8K, ++ }, { ++ hinfc610_pagesize_16K, NAND_PAGE_16K, ++ }, { ++ hinfc610_pagesize_32K, NAND_PAGE_32K, ++ } ++}; ++ ++enum hinfc610_page_reg hinfc610_page_type2reg(int type) ++{ ++ return type2reg(page_type2reg, ARRAY_SIZE(page_type2reg), type, 0); ++} ++ ++int hinfc610_page_reg2type(enum hinfc610_page_reg reg) ++{ ++ return reg2type(page_type2reg, ARRAY_SIZE(page_type2reg), reg, 0); ++} ++/*****************************************************************************/ ++ ++static struct match_reg_type ecc_type2reg[] = { ++ { ++ hinfc610_ecc_none, NAND_ECC_NONE, ++ }, { ++ hinfc610_ecc_8bit, NAND_ECC_8BIT, ++ }, { ++ hinfc610_ecc_13bit, NAND_ECC_13BIT, ++ }, { ++ hinfc610_ecc_18bit, NAND_ECC_18BIT, ++ }, { ++ hinfc610_ecc_24bit, NAND_ECC_24BIT, ++ }, { ++ hinfc610_ecc_27bit, NAND_ECC_27BIT, ++ }, { ++ hinfc610_ecc_32bit, NAND_ECC_32BIT, ++ }, { ++ hinfc610_ecc_41bit, NAND_ECC_41BIT, ++ }, { ++ hinfc610_ecc_48bit, NAND_ECC_48BIT, ++ }, { ++ hinfc610_ecc_60bit, NAND_ECC_60BIT, ++ }, { ++ hinfc610_ecc_72bit, NAND_ECC_72BIT, ++ }, { ++ hinfc610_ecc_80bit, NAND_ECC_80BIT, ++ } ++}; ++ ++enum hinfc610_ecc_reg hinfc610_ecc_type2reg(int type) ++{ ++ return type2reg(ecc_type2reg, ARRAY_SIZE(ecc_type2reg), type, 0); ++} ++ ++int hinfc610_ecc_reg2type(enum hinfc610_ecc_reg reg) ++{ ++ return reg2type(ecc_type2reg, ARRAY_SIZE(ecc_type2reg), reg, 0); ++} +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_gen.h b/drivers/mtd/nand/hinfc610/hinfc610_gen.h +new file mode 100644 +index 0000000..c41ff6f +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_gen.h +@@ -0,0 +1,57 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef HINFC610_GENH ++#define HINFC610_GENH ++/******************************************************************************/ ++ ++#include "../hinfc_gen.h" ++ ++enum hinfc610_ecc_reg { ++ hinfc610_ecc_none = 0x00, ++ hinfc610_ecc_8bit = 0x01, ++ hinfc610_ecc_13bit = 0x02, ++ hinfc610_ecc_18bit = 0x03, ++ hinfc610_ecc_24bit = 0x04, ++ hinfc610_ecc_27bit = 0x05, ++ hinfc610_ecc_32bit = 0x06, ++ hinfc610_ecc_41bit = 0x07, ++ hinfc610_ecc_48bit = 0x08, ++ hinfc610_ecc_60bit = 0x09, ++ hinfc610_ecc_72bit = 0x0a, ++ hinfc610_ecc_80bit = 0x0b, ++}; ++ ++enum hinfc610_page_reg { ++ hinfc610_pagesize_2K = 0x01, ++ hinfc610_pagesize_4K = 0x02, ++ hinfc610_pagesize_8K = 0x03, ++ hinfc610_pagesize_16K = 0x04, ++ hinfc610_pagesize_32K = 0x05, ++}; ++ ++enum hinfc610_page_reg hinfc610_page_type2reg(int type); ++ ++int hinfc610_page_reg2type(enum hinfc610_page_reg reg); ++ ++enum hinfc610_ecc_reg hinfc610_ecc_type2reg(int type); ++ ++int hinfc610_ecc_reg2type(enum hinfc610_ecc_reg reg); ++ ++/******************************************************************************/ ++#endif /* HINFC610_GENH */ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_os.c b/drivers/mtd/nand/hinfc610/hinfc610_os.c +new file mode 100644 +index 0000000..449ec93 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_os.c +@@ -0,0 +1,394 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/of_platform.h> ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++ ++#ifdef CONFIG_MTD_PARTITION_FROM_DTS ++/*****************************************************************************/ ++static inline int mtd_has_partitions(void) { return 1; } ++#else ++ ++/*****************************************************************************/ ++#define MAX_MTD_PARTITIONS (32) ++ ++struct partition_entry { ++ char name[16]; ++ unsigned long long start; ++ unsigned long long length; ++ unsigned int flags; ++}; ++ ++struct partition_info { ++ int parts_num; ++ struct partition_entry entry[MAX_MTD_PARTITIONS]; ++ struct mtd_partition parts[MAX_MTD_PARTITIONS]; ++}; ++ ++static struct partition_info ptn_info = {0}; ++ ++static int __init parse_nand_partitions(const struct tag *tag) ++{ ++ int i; ++ ++ if (tag->hdr.size <= 2) { ++ PR_BUG("tag->hdr.size <= 2\n"); ++ return 0; ++ } ++ ptn_info.parts_num = (tag->hdr.size - 2) ++ / (sizeof(struct partition_entry)/sizeof(int)); ++ memcpy(ptn_info.entry, ++ &tag->u, ++ ptn_info.parts_num * sizeof(struct partition_entry)); ++ ++ for (i = 0; i < ptn_info.parts_num; i++) { ++ ptn_info.parts[i].name = ptn_info.entry[i].name; ++ ptn_info.parts[i].size = (ptn_info.entry[i].length); ++ ptn_info.parts[i].offset = (ptn_info.entry[i].start); ++ ptn_info.parts[i].mask_flags = 0; ++ ptn_info.parts[i].ecclayout = 0; ++ } ++ ++ return 0; ++} ++ ++/* turn to ascii is "HiNp" */ ++__tagtable(0x48694E70, parse_nand_partitions); ++/*****************************************************************************/ ++static int hinfc_os_add_paratitions(struct hinfc_host *host) ++{ ++ int ix; ++ int nr_parts = 0; ++ struct mtd_partition *parts = NULL; ++ int ret; ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ static const char * const part_probes[] = {"cmdlinepart", NULL, }; ++ ++ nr_parts = parse_mtd_partitions(host->mtd, part_probes, &parts, 0); ++#endif ++ ++ if (!nr_parts) { ++ nr_parts = ptn_info.parts_num; ++ parts = ptn_info.parts; ++ } ++ ++ if (nr_parts <= 0) ++ return 0; ++ ++ for (ix = 0; ix < nr_parts; ix++) { ++ DBG_MSG("partitions[%d] = {.name = %s, .offset = 0x%.8x,", ++ ix, parts[ix].name, ++ (unsigned int)parts[ix].offset); ++ DBG_MSG(".size = 0x%08x (%uKiB) }\n", ++ (unsigned int)parts[ix].size, ++ (unsigned int)parts[ix].size/1024); ++ } ++ ++ host->add_partition = 1; ++ ++ ret = mtd_device_register(host->mtd, parts, nr_parts); ++ ++ kfree(parts); ++ parts = NULL; ++ ++ return (1 == ret) ? -ENODEV : 0; ++} ++/*****************************************************************************/ ++#endif /* CONFIG_MTD_PARTITION_FROM_DTS */ ++ ++static unsigned int nand_otp_len; ++static unsigned char nand_otp[128] = {0}; ++ ++/* Get NAND parameter table. */ ++static int __init parse_nand_param(const struct tag *tag) ++{ ++ if (tag->hdr.size <= 2) ++ return 0; ++ ++ nand_otp_len = ((tag->hdr.size << 2) - sizeof(struct tag_header)); ++ ++ if (nand_otp_len > sizeof(nand_otp)) { ++ PR_BUG("tag->hdr.size <= 2\n"); ++ return 0; ++ } ++ memcpy(nand_otp, &tag->u, nand_otp_len); ++ return 0; ++} ++/* 0x48694E77 equal to fastoot ATAG_NAND_PARAM */ ++__tagtable(0x48694E77, parse_nand_param); ++/*****************************************************************************/ ++ ++static int hinfc610_nand_pre_probe(struct nand_chip *chip) ++{ ++ uint8_t nand_maf_id; ++ struct hinfc_host *host = chip->priv; ++ ++ /* Reset the chip first */ ++ host->send_cmd_reset(host, 0); ++ ++ /* Check the ID */ ++ host->offset = 0; ++ memset((unsigned char *)(chip->IO_ADDR_R), 0, 0x10); ++ host->send_cmd_readid(host); ++ nand_maf_id = readb(chip->IO_ADDR_R); ++ ++ if (nand_maf_id == 0x00 || nand_maf_id == 0xff) { ++ PR_BUG("\nCannot found a valid Nand Device\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_os_probe(struct platform_device *pltdev) ++{ ++ int size; ++ int result = 0; ++ struct hinfc_host *host; ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ struct resource *rs_reg, *rs_io = NULL; ++ struct device *dev = &pltdev->dev; ++ struct device_node *np = NULL; ++#ifdef CONFIG_MTD_PARTITION_FROM_DTS ++ struct mtd_partition *parts = NULL; ++ int nr_parts = 0; ++#endif ++ ++ size = sizeof(struct hinfc_host) + sizeof(struct nand_chip) ++ + sizeof(struct mtd_info); ++ host = kmalloc(size, GFP_KERNEL); ++ if (!host) { ++ PR_BUG("failed to allocate device structure.\n"); ++ return -ENOMEM; ++ } ++ memset((char *)host, 0, size); ++ platform_set_drvdata(pltdev, host); ++ ++ host->dev = dev; ++ host->chip = chip = (struct nand_chip *)&host[1]; ++ host->mtd = mtd = (struct mtd_info *)&chip[1]; ++ ++ host->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(host->clk)) ++ return PTR_ERR(host->clk); ++ ++ /* enable and set system clock */ ++ clk_prepare_enable(host->clk); ++ ++ rs_reg = platform_get_resource_byname(pltdev, IORESOURCE_MEM, ++ "control"); ++ host->iobase = devm_ioremap_resource(dev, rs_reg); ++ if (IS_ERR(host->iobase)) { ++ PR_BUG("Error: Can't get resource for reg address.\n"); ++ result = -EIO; ++ goto fail; ++ } ++ ++ np = of_get_next_available_child(dev->of_node, NULL); ++ ++ mtd->type = MTD_NANDFLASH; ++ mtd->priv = chip; ++ mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->owner = THIS_MODULE; ++ mtd->name = np->name; ++ ++ rs_io = platform_get_resource_byname(pltdev, IORESOURCE_MEM, ++ "memory"); ++ chip->IO_ADDR_R = chip->IO_ADDR_W = devm_ioremap_resource(dev, rs_io); ++ if (IS_ERR(chip->IO_ADDR_R)) { ++ PR_BUG("Error: Can't get resource for buffer address.\n"); ++ result = -EIO; ++ goto fail; ++ } ++ ++ host->buffer = dma_alloc_coherent(host->dev, ++ (NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE), ++ &host->dma_buffer, GFP_KERNEL); ++ if (!host->buffer) { ++ PR_BUG("Can't malloc memory for NAND driver."); ++ result = -EIO; ++ goto fail; ++ } ++ ++ chip->priv = host; ++ host->chip = chip; ++ chip->cmd_ctrl = hinfc610_cmd_ctrl; ++ chip->dev_ready = hinfc610_dev_ready; ++ chip->select_chip = hinfc610_select_chip; ++ chip->read_byte = hinfc610_read_byte; ++ chip->read_word = hinfc610_read_word; ++ chip->write_buf = hinfc610_write_buf; ++ chip->read_buf = hinfc610_read_buf; ++ ++ chip->chip_delay = HINFC610_CHIP_DELAY; ++ chip->options = NAND_NO_AUTOINCR ++ | NAND_NEED_READRDY ++ | NAND_BROKEN_XD ++ | NAND_SKIP_BBTSCAN; ++ chip->ecc.layout = NULL; ++ chip->ecc.mode = NAND_ECC_NONE; ++ ++ if (hinfc610_nand_init(host, chip)) { ++ PR_BUG("failed to allocate device buffer.\n"); ++ result = -EIO; ++ goto fail; ++ } ++ ++ if (hinfc610_nand_pre_probe(chip)) { ++ result = -EXDEV; ++ goto fail; ++ } ++ ++ if (nand_otp_len) { ++ PR_MSG("Copy Nand read retry parameter from boot,"); ++ PR_MSG(" parameter length %d.\n", nand_otp_len); ++ memcpy(host->rr_data, nand_otp, nand_otp_len); ++ } ++ ++ if (nand_scan(mtd, CONFIG_HINFC610_MAX_CHIP)) { ++ result = -ENXIO; ++ goto fail; ++ } ++ ++#ifdef CONFIG_MTD_PARTITION_FROM_DTS ++ if (mtd_has_partitions()) { ++ static char const *part_probes[] = { ++ "cmdlinepart", ++ NULL, ++ }; ++ ++ nr_parts = parse_mtd_partitions(host->mtd, ++ part_probes, &parts, 0); ++ PR_MSG("parse mtd partitions: %d\n", nr_parts); ++ if (nr_parts > 0) ++ host->add_partition = 1; ++ } ++ ++ result = mtd_device_register(host->mtd, parts, nr_parts); ++ if (result) { ++ kfree(parts); ++ parts = NULL; ++ } ++ return (1 == result) ? -ENODEV : 0; ++#else ++ result = hinfc_os_add_paratitions(host); ++ if (host->add_partition) ++ return result; ++ ++ if (!add_mtd_device(host->mtd)) ++ return 0; ++#endif ++ result = -ENODEV; ++fail: ++ if (host->buffer) { ++ dma_free_coherent(host->dev, ++ (NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE), ++ host->buffer, ++ host->dma_buffer); ++ host->buffer = NULL; ++ } ++ nand_release(host->mtd); ++ kfree(host); ++ platform_set_drvdata(pltdev, NULL); ++ ++ return result; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_os_remove(struct platform_device *pltdev) ++{ ++ struct hinfc_host *host = platform_get_drvdata(pltdev); ++ ++ clk_disable_unprepare(host->clk); ++ ++ nand_release(host->mtd); ++ ++ dma_free_coherent(host->dev, ++ (NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE), ++ host->buffer, ++ host->dma_buffer); ++ kfree(host); ++ platform_set_drvdata(pltdev, NULL); ++ ++ return 0; ++} ++/*****************************************************************************/ ++#ifdef CONFIG_PM ++static int hinfc610_os_suspend(struct platform_device *pltdev, ++ pm_message_t state) ++{ ++ struct hinfc_host *host = platform_get_drvdata(pltdev); ++ ++ while ((hinfc_read(host, HINFC610_STATUS) & 0x1) == 0x0) ++ ; ++ ++ while ((hinfc_read(host, HINFC610_DMA_CTRL)) ++ & HINFC610_DMA_CTRL_DMA_START) ++ _cond_resched(); ++ ++ clk_disable_unprepare(host->clk); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_os_resume(struct platform_device *pltdev) ++{ ++ int cs; ++ struct hinfc_host *host = platform_get_drvdata(pltdev); ++ struct nand_chip *chip = host->chip; ++ ++ clk_prepare_enable(host->clk); ++ for (cs = 0; cs < chip->numchips; cs++) ++ host->send_cmd_reset(host, cs); ++ hinfc_write(host, ++ SET_HINFC610_PWIDTH(CONFIG_HINFC610_W_LATCH, ++ CONFIG_HINFC610_R_LATCH, CONFIG_HINFC610_RW_LATCH), ++ HINFC610_PWIDTH); ++ ++ return 0; ++} ++#endif /* CONFIG_PM */ ++/*****************************************************************************/ ++static const struct of_device_id hisi_nand_dt_ids[] = { ++ { .compatible = "hisilicon,hinfc610-nand" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, hisi_nand_dt_ids); ++ ++static struct platform_driver hisi_nand_driver = { ++ .driver = { ++ .name = "hisi-nand", ++ .of_match_table = hisi_nand_dt_ids, ++ }, ++ .probe = hinfc610_os_probe, ++ .remove = hinfc610_os_remove, ++#ifdef CONFIG_PM ++ .suspend = hinfc610_os_suspend, ++ .resume = hinfc610_os_resume, ++#endif ++}; ++module_platform_driver(hisi_nand_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("BVT_BSP"); ++MODULE_DESCRIPTION("Hisilicon Flash Memory Controller NFC610 Nand Driver"); +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_os.h b/drivers/mtd/nand/hinfc610/hinfc610_os.h +new file mode 100644 +index 0000000..1dafd9e +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_os.h +@@ -0,0 +1,78 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++ ++#ifndef HINFC610_OSH ++#define HINFC610_OSH ++/******************************************************************************/ ++ ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/delay.h> ++#include <linux/dma-mapping.h> ++#include <linux/io.h> ++#include <asm/setup.h> ++#include <linux/errno.h> ++#include <linux/platform_device.h> ++#include <linux/mtd/partitions.h> ++#include <linux/clk.h> ++#include <linux/clkdev.h> ++ ++#include "../../mtdcore.h" ++ ++/*****************************************************************************/ ++ ++#define DUMP_DATA(_p, _n) do { \ ++ int ix; \ ++ unsigned char *rr = (unsigned char *)(_p); \ ++ for (ix = 0; ix < _n; ix++) { \ ++ pr_info("%02X ", rr[ix]); \ ++ if (!((ix + 1) % 16)) \ ++ pr_info("\n"); \ ++ } \ ++} while (0) ++ ++#define DBG_OUT(fmt, args...)\ ++ pr_warn("%s(%d): " fmt, __FILE__, __LINE__, ##args) \ ++ ++#if 1 ++# define DBG_MSG(_fmt, arg...) ++#else ++# define DBG_MSG(_fmt, arg...) \ ++ pr_info("%s(%d): " _fmt, __FILE__, __LINE__, ##arg) ++#endif ++ ++#define PR_BUG(fmt, args...) do {\ ++ pr_debug("%s(%d): bug " fmt, __FILE__, __LINE__, ##args); \ ++ asm("b ."); \ ++} while (0) ++ ++#define PR_ERR(fmt, args...)\ ++ pr_err("%s(%d): " fmt, __FILE__, __LINE__, ##args) \ ++ ++#define PR_MSG(_fmt, arg...) \ ++ printk(_fmt, ##arg) ++ ++/******************************************************************************/ ++#endif /* HINFC610_OSH */ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_read_retry.c b/drivers/mtd/nand/hinfc610/hinfc610_read_retry.c +new file mode 100644 +index 0000000..3f1a9f2 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_read_retry.c +@@ -0,0 +1,43 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "hinfc610_gen.h" ++#include "hinfc610.h" ++#include "hinfc610_read_retry.h" ++ ++static struct read_retry_t *read_retry_table[] = { ++ &hinfc610_hynix_bg_bdie_read_retry, ++ &hinfc610_hynix_bg_cdie_read_retry, ++ &hinfc610_hynix_cg_adie_read_retry, ++ &hinfc610_micron_read_retry, ++ &hinfc610_toshiba_24nm_read_retry, ++ &hinfc610_samsung_read_retry, ++ NULL, ++}; ++ ++struct read_retry_t *hinfc610_find_read_retry(int type) ++{ ++ struct read_retry_t **rr; ++ ++ for (rr = read_retry_table; rr; rr++) { ++ if ((*rr)->type == type) ++ return *rr; ++ } ++ ++ return NULL; ++} +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_read_retry.h b/drivers/mtd/nand/hinfc610/hinfc610_read_retry.h +new file mode 100644 +index 0000000..cfd8880 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_read_retry.h +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef HINFC610_READ_RETRY_H ++#define HINFC610_READ_RETRY_H ++ ++struct read_retry_t *hinfc610_find_read_retry(int type); ++ ++#endif /* HINFC610_READ_RETRY_H */ ++ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_read_retry_hynix_bg_bdie.c b/drivers/mtd/nand/hinfc610/hinfc610_read_retry_hynix_bg_bdie.c +new file mode 100644 +index 0000000..b6fb1a8 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_read_retry_hynix_bg_bdie.c +@@ -0,0 +1,136 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++ ++/*****************************************************************************/ ++ ++static int hynix_bg_bdie_rr_org_exist; ++static char hynix_bg_bdie_rr_org[4] = {0}; ++ ++/*****************************************************************************/ ++ ++static int hinfc610_hynix_bg_bdie_set_rr_reg(struct hinfc_host *host, int index) ++{ ++ int ix; ++ char HYNIX_BG_BDIE_RR_REG[4] = {0xA7, 0xAD, 0xAE, 0xAF}; ++ char value_offset[7][4] = { ++ {0x00, 0x00, 0x00, 0x00}, ++ {0x00, 0x06, 0x0A, 0x06}, ++ {0x7F, -0x03, -0x07, -0x08}, ++ {0x7F, -0x06, -0x0D, -0x0F}, ++ {0x7F, -0x09, -0x14, -0x17}, ++ {0x7F, 0x7F, -0x1A, -0x1E}, ++ {0x7F, 0x7F, -0x20, -0x25} ++ }; ++ char *value = &value_offset[index][0]; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ hinfc_write(host, 1, HINFC610_DATA_NUM); ++ ++ if (!hynix_bg_bdie_rr_org_exist) { ++ ++ for (ix = 0; ix < 4; ix++) { ++ ++ memset(host->chip->IO_ADDR_R, 0xff, 32); ++ ++ hinfc_write(host, 0x37, HINFC610_CMD); ++ hinfc_write(host, HYNIX_BG_BDIE_RR_REG[ix], ++ HINFC610_ADDRL); ++ /* ++ * according to hynix doc, no need to config ++ * HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, HINFC610_READ_1CMD_1ADD_DATA, ++ HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ hynix_bg_bdie_rr_org[ix] ++ = (char)(readl(host->chip->IO_ADDR_R) & 0xff); ++ } ++ hynix_bg_bdie_rr_org_exist = 1; ++ } ++ ++ for (ix = 0; ix < 4; ix++) { ++ if (value[ix] == 0x7F) ++ value[ix] = 0x00; ++ else ++ value[ix] += hynix_bg_bdie_rr_org[ix]; ++ } ++ ++ writel(value[0], host->chip->IO_ADDR_W); ++ hinfc_write(host, HYNIX_BG_BDIE_RR_REG[0], HINFC610_ADDRL); ++ hinfc_write(host, 0x36, HINFC610_CMD); ++ /* ++ * according to hynix doc, no need to config HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, HINFC610_WRITE_1CMD_1ADD_DATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ for (ix = 1; ix < 4; ix++) { ++ writel(value[ix], host->chip->IO_ADDR_W); ++ hinfc_write(host, HYNIX_BG_BDIE_RR_REG[ix], HINFC610_ADDRL); ++ /* ++ * according to hynix doc, no need to config ++ * HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ } ++ ++ hinfc_write(host, 0x16, HINFC610_CMD); ++ /* ++ * according to hynix doc, only 1 cmd: 0x16. ++ * And no need to config HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, HINFC610_WRITE_1CMD_0ADD_NODATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_hynix_bg_bdie_set_rr_param(struct hinfc_host *host, ++ int param) ++{ ++ if (!param) ++ return 0; ++ return hinfc610_hynix_bg_bdie_set_rr_reg(host, param); ++} ++/*****************************************************************************/ ++ ++static int hinfc610_hynix_bg_bdie_reset_rr_param(struct hinfc_host *host) ++{ ++ return hinfc610_hynix_bg_bdie_set_rr_param(host, 0); ++} ++/*****************************************************************************/ ++ ++struct read_retry_t hinfc610_hynix_bg_bdie_read_retry = { ++ .type = NAND_RR_HYNIX_BG_BDIE, ++ .count = 7, ++ .set_rr_param = hinfc610_hynix_bg_bdie_set_rr_param, ++ .get_rr_param = NULL, ++ .reset_rr_param = hinfc610_hynix_bg_bdie_reset_rr_param, ++}; +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_read_retry_hynix_bg_cdie.c b/drivers/mtd/nand/hinfc610/hinfc610_read_retry_hynix_bg_cdie.c +new file mode 100644 +index 0000000..fc39d70 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_read_retry_hynix_bg_cdie.c +@@ -0,0 +1,225 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++ ++/*****************************************************************************/ ++ ++static char *hinfc610_hynix_bg_cdie_otp_check(char *otp) ++{ ++ int index = 0; ++ int ix, jx; ++ char *ptr = NULL; ++ int min, cur; ++ char *otp_origin, *otp_inverse; ++ ++ min = 64; ++ for (ix = 0; ix < 8; ix++, otp += 128) { ++ ++ otp_origin = otp; ++ otp_inverse = otp + 64; ++ cur = 0; ++ ++ for (jx = 0; jx < 64; jx++, otp_origin++, otp_inverse++) { ++ if (((*otp_origin) ^ (*otp_inverse)) == 0xFF) ++ continue; ++ cur++; ++ } ++ ++ if (cur < min) { ++ min = cur; ++ index = ix; ++ ptr = otp; ++ if (!cur) ++ break; ++ } ++ } ++ ++ pr_info("RR select parameter %d from %d, error %d\n", ++ index, ix, min); ++ return ptr; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_hynix_bg_cdie_get_rr_param(struct hinfc_host *host) ++{ ++ char *otp; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ /* step1: reset the chip */ ++ host->send_cmd_reset(host, host->chipselect); ++ ++ /* step2: cmd: 0x36, address: 0xAE, data: 0x00 */ ++ hinfc_write(host, 1, HINFC610_DATA_NUM);/* data length 1 */ ++ writel(0x00, host->chip->IO_ADDR_R); /* data: 0x00 */ ++ hinfc_write(host, 0xAE, HINFC610_ADDRL);/* address: 0xAE */ ++ hinfc_write(host, 0x36, HINFC610_CMD); /* cmd: 0x36 */ ++ /* according to hynix doc, no need to config ++ * HINFC610_OP_WAIT_READY_EN */ ++ hinfc_write(host, HINFC610_WRITE_1CMD_1ADD_DATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ /* step3: address: 0xB0, data: 0x4D */ ++ hinfc_write(host, 1, HINFC610_DATA_NUM);/* data length 1 */ ++ writel(0x4D, host->chip->IO_ADDR_R); /* data: 0x4d */ ++ hinfc_write(host, 0xB0, HINFC610_ADDRL);/* address: 0xB0 */ ++ /* only address and data, without cmd */ ++ /* according to hynix doc, no need to config ++ * HINFC610_OP_WAIT_READY_EN */ ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ /* step4: cmd: 0x16, 0x17, 0x04, 0x19 */ ++ hinfc_write(host, 0x17 << 8 | 0x16, HINFC610_CMD); ++ /* according to hynix doc, no need to config ++ * HINFC610_OP_WAIT_READY_EN */ ++ hinfc_write(host, HINFC610_WRITE_2CMD_0ADD_NODATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ hinfc_write(host, 0x19 << 8 | 0x04, HINFC610_CMD); ++ /* according to hynix doc, no need to config ++ * HINFC610_OP_WAIT_READY_EN */ ++ hinfc_write(host, HINFC610_WRITE_2CMD_0ADD_NODATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ /* step5: cmd: 0x00 0x30, address: 0x02 00 00 00 */ ++ hinfc_write(host, 0x2000000, HINFC610_ADDRL); ++ hinfc_write(host, 0x00, HINFC610_ADDRH); ++ hinfc_write(host, 0x30 << 8 | 0x00, HINFC610_CMD); ++ hinfc_write(host, 0x800, HINFC610_DATA_NUM); ++ /* according to hynix doc, need to config ++ * HINFC610_OP_WAIT_READY_EN */ ++ hinfc_write(host, HINFC610_READ_2CMD_5ADD, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ /*step6 save otp read retry table to mem*/ ++ otp = hinfc610_hynix_bg_cdie_otp_check(host->chip->IO_ADDR_R + 2); ++ if (!otp) { ++ pr_err("Read Retry select parameter failed, this Nand Chip maybe invalidation.\n"); ++ return -1; ++ } ++ memcpy(host->rr_data, otp, 64); ++ host->need_rr_data = 1; ++ ++ /* step7: reset the chip */ ++ host->send_cmd_reset(host, host->chipselect); ++ ++ /* step8: cmd: 0x38 */ ++ hinfc_write(host, 0x38, HINFC610_CMD); ++ /* according to hynix doc, need to config HINFC610_OP_WAIT_READY_EN */ ++ hinfc_write(host, HINFC610_WRITE_1CMD_0ADD_NODATA_WAIT_READY, ++ HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ /* get hynix otp table finish */ ++ return 0; ++} ++/*****************************************************************************/ ++static char hinfc610_hynix_bg_cdie_rr_reg[8] = { ++ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7}; ++ ++static int hinfc610_hynix_bg_cdie_set_rr_reg(struct hinfc_host *host, ++ char *val) ++{ ++ int i; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ hinfc_write(host, 1, HINFC610_DATA_NUM);/* data length 1 */ ++ ++ for (i = 0; i <= 8; i++) { ++ switch (i) { ++ case 0: ++ writel(val[i], host->chip->IO_ADDR_R); ++ hinfc_write(host, ++ hinfc610_hynix_bg_cdie_rr_reg[i], ++ HINFC610_ADDRL); ++ hinfc_write(host, ++ 0x36, HINFC610_CMD); ++ /* ++ * no need to config HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, ++ HINFC610_WRITE_1CMD_1ADD_DATA, ++ HINFC610_OP); ++ break; ++ case 8: ++ hinfc_write(host, ++ 0x16, HINFC610_CMD); ++ /* ++ * according to hynix doc, only 1 cmd: 0x16. ++ * And no need to config HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, ++ HINFC610_WRITE_1CMD_0ADD_NODATA, ++ HINFC610_OP); ++ break; ++ default: ++ writel(val[i], host->chip->IO_ADDR_R); ++ hinfc_write(host, ++ hinfc610_hynix_bg_cdie_rr_reg[i], ++ HINFC610_ADDRL); ++ /* ++ * no need to config HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, ++ HINFC610_WRITE_0CMD_1ADD_DATA, ++ HINFC610_OP); ++ break; ++ } ++ WAIT_CONTROLLER_FINISH(); ++ } ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ return 0; ++} ++ ++/*****************************************************************************/ ++ ++static int hinfc610_hynix_bg_cdie_set_rr_param(struct hinfc_host *host, ++ int param) ++{ ++ unsigned char *rr; ++ ++ if (!(host->rr_data[0] | host->rr_data[1] ++ | host->rr_data[2] | host->rr_data[3]) || !param) ++ return -1; ++ ++ rr = (unsigned char *)&host->rr_data[((param & 0x07) << 3)]; ++ ++ /* set the read retry regs to adjust reading level */ ++ return hinfc610_hynix_bg_cdie_set_rr_reg(host, (char *)rr); ++} ++/*****************************************************************************/ ++ ++static int hinfc610_hynix_bg_cdie_reset_rr_param(struct hinfc_host *host) ++{ ++ return hinfc610_hynix_bg_cdie_set_rr_param(host, 0); ++} ++/*****************************************************************************/ ++ ++struct read_retry_t hinfc610_hynix_bg_cdie_read_retry = { ++ .type = NAND_RR_HYNIX_BG_CDIE, ++ .count = 8, ++ .set_rr_param = hinfc610_hynix_bg_cdie_set_rr_param, ++ .get_rr_param = hinfc610_hynix_bg_cdie_get_rr_param, ++ .reset_rr_param = hinfc610_hynix_bg_cdie_reset_rr_param, ++}; +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_read_retry_hynix_cg_adie.c b/drivers/mtd/nand/hinfc610/hinfc610_read_retry_hynix_cg_adie.c +new file mode 100644 +index 0000000..b7444b8 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_read_retry_hynix_cg_adie.c +@@ -0,0 +1,234 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++ ++/*****************************************************************************/ ++static char *hinfc610_hynix_cg_adie_otp_check(char *otp) ++{ ++ int index = 0; ++ int ix, jx; ++ char *ptr = NULL; ++ int min, cur; ++ char *otp_origin, *otp_inverse; ++ ++ min = 64; ++ for (ix = 0; ix < 8; ix++, otp += 128) { ++ ++ otp_origin = otp; ++ otp_inverse = otp + 64; ++ cur = 0; ++ ++ for (jx = 0; jx < 64; jx++, otp_origin++, otp_inverse++) { ++ if (((*otp_origin) ^ (*otp_inverse)) == 0xFF) ++ continue; ++ cur++; ++ } ++ ++ if (cur < min) { ++ min = cur; ++ index = ix; ++ ptr = otp; ++ if (!cur) ++ break; ++ } ++ } ++ ++ pr_info("RR select parameter %d from %d, error %d\n", ++ index, ix, min); ++ return ptr; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_hynix_cg_adie_get_rr_param(struct hinfc_host *host) ++{ ++ char *otp; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ /* step1: reset the chip */ ++ host->send_cmd_reset(host, host->chipselect); ++ ++ /* step2: cmd: 0x36, address: 0xFF, data: 0x40 */ ++ hinfc_write(host, 1, HINFC610_DATA_NUM);/* data length 1 */ ++ writel(0x40, host->chip->IO_ADDR_R); /* data: 0x00 */ ++ hinfc_write(host, 0xFF, HINFC610_ADDRL);/* address: 0xAE */ ++ hinfc_write(host, 0x36, HINFC610_CMD); /* cmd: 0x36 */ ++ /* ++ * no need to config HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, HINFC610_WRITE_1CMD_1ADD_DATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ /* step3: address: 0xCC, data: 0x4D */ ++ hinfc_write(host, 1, HINFC610_DATA_NUM);/* data length 1 */ ++ writel(0x4D, host->chip->IO_ADDR_R); /* data: 0x4d */ ++ hinfc_write(host, 0xCC, HINFC610_ADDRL);/* address: 0xB0 */ ++ /* ++ * no need to config HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ * only address and data, without cmd ++ */ ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ /* step4: cmd: 0x16, 0x17, 0x04, 0x19 */ ++ hinfc_write(host, 0x17 << 8 | 0x16, HINFC610_CMD); ++ /* ++ * no need to config HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, HINFC610_WRITE_2CMD_0ADD_NODATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ hinfc_write(host, 0x19 << 8 | 0x04, HINFC610_CMD); ++ /* ++ * no need to config HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, HINFC610_WRITE_2CMD_0ADD_NODATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ /* step5: cmd: 0x00 0x30, address: 0x02 00 00 00 */ ++ hinfc_write(host, 0x2000000, HINFC610_ADDRL); ++ hinfc_write(host, 0x00, HINFC610_ADDRH); ++ hinfc_write(host, 0x30 << 8 | 0x00, HINFC610_CMD); ++ hinfc_write(host, 0x800, HINFC610_DATA_NUM); ++ /* ++ * need to config HINFC610_OP_WAIT_READY_EN, here config this bit. ++ */ ++ hinfc_write(host, HINFC610_READ_2CMD_5ADD, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ /*step6 save otp read retry table to mem*/ ++ otp = hinfc610_hynix_cg_adie_otp_check(host->chip->IO_ADDR_R + 2); ++ if (!otp) { ++ pr_err("Read Retry select parameter failed, this Nand Chip maybe invalidation.\n"); ++ return -1; ++ } ++ memcpy(host->rr_data, otp, 64); ++ host->need_rr_data = 1; ++ ++ /* step7: reset the chip */ ++ host->send_cmd_reset(host, host->chipselect); ++ ++ /* step8: cmd: 0x38 */ ++ hinfc_write(host, 0x38, HINFC610_CMD); ++ /* ++ * need to config HINFC610_OP_WAIT_READY_EN, here config this bit. ++ */ ++ hinfc_write(host, HINFC610_WRITE_1CMD_0ADD_NODATA_WAIT_READY, ++ HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ /* get hynix otp table finish */ ++ return 0; ++} ++/*****************************************************************************/ ++static char hinfc610_hynix_cg_adie__rr_reg[8] = { ++ 0xCC, 0xBF, 0xAA, 0xAB, 0xCD, 0xAD, 0xAE, 0xAF}; ++ ++static int hinfc610_hynix_cg_adie_set_rr_reg(struct hinfc_host *host, char *val) ++{ ++ int i; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ hinfc_write(host, 1, HINFC610_DATA_NUM);/* data length 1 */ ++ ++ for (i = 0; i <= 8; i++) { ++ switch (i) { ++ case 0: ++ writel(val[i], host->chip->IO_ADDR_R); ++ hinfc_write(host, ++ hinfc610_hynix_cg_adie__rr_reg[i], ++ HINFC610_ADDRL); ++ hinfc_write(host, ++ 0x36, HINFC610_CMD); ++ /* ++ * no need to config HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, ++ HINFC610_WRITE_1CMD_1ADD_DATA, ++ HINFC610_OP); ++ break; ++ case 8: ++ hinfc_write(host, ++ 0x16, HINFC610_CMD); ++ /* ++ * only have 1 cmd: 0x16 ++ * no need to config HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, ++ HINFC610_WRITE_1CMD_0ADD_NODATA, ++ HINFC610_OP); ++ break; ++ default: ++ writel(val[i], host->chip->IO_ADDR_R); ++ hinfc_write(host, ++ hinfc610_hynix_cg_adie__rr_reg[i], ++ HINFC610_ADDRL); ++ /* ++ * no need to config HINFC610_OP_WAIT_READY_EN, ++ * here not config this bit. ++ */ ++ hinfc_write(host, ++ HINFC610_WRITE_0CMD_1ADD_DATA, ++ HINFC610_OP); ++ break; ++ } ++ WAIT_CONTROLLER_FINISH(); ++ } ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ return 0; ++} ++ ++/*****************************************************************************/ ++ ++static int hinfc610_hynix_cg_adie_set_rr_param(struct hinfc_host *host, ++ int param) ++{ ++ unsigned char *rr; ++ ++ if (!(host->rr_data[0] | host->rr_data[1] ++ | host->rr_data[2] | host->rr_data[3]) || !param) ++ return -1; ++ ++ rr = (unsigned char *)&host->rr_data[((param & 0x07) << 3)]; ++ ++ /* set the read retry regs to adjust reading level */ ++ return hinfc610_hynix_cg_adie_set_rr_reg(host, (char *)rr); ++} ++/*****************************************************************************/ ++ ++static int hinfc610_hynix_cg_adie_reset_rr_param(struct hinfc_host *host) ++{ ++ return hinfc610_hynix_cg_adie_set_rr_param(host, 0); ++} ++/*****************************************************************************/ ++ ++struct read_retry_t hinfc610_hynix_cg_adie_read_retry = { ++ .type = NAND_RR_HYNIX_CG_ADIE, ++ .count = 8, ++ .set_rr_param = hinfc610_hynix_cg_adie_set_rr_param, ++ .get_rr_param = hinfc610_hynix_cg_adie_get_rr_param, ++ .reset_rr_param = hinfc610_hynix_cg_adie_reset_rr_param, ++}; +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_read_retry_micron.c b/drivers/mtd/nand/hinfc610/hinfc610_read_retry_micron.c +new file mode 100644 +index 0000000..c4c0716 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_read_retry_micron.c +@@ -0,0 +1,74 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++ ++/*****************************************************************************/ ++ ++#define MICRON_RR_ADDR 0x89 ++ ++static int hinfc610_micron_set_rr_reg(struct hinfc_host *host, int rr) ++{ ++ int regval; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, 1, HINFC610_DATA_NUM); ++ ++ writel(rr, host->chip->IO_ADDR_W); ++ hinfc_write(host, MICRON_RR_ADDR, HINFC610_ADDRL); ++ /* set read retry */ ++ hinfc_write(host, 0xEF, HINFC610_CMD); ++ ++ /* need to config WAIT_READY_EN, here config WAIT_READY_EN bit. */ ++ regval = (HINFC610_IS_SYNC(host) ? ++ HINFC610_WRITE_1CMD_1ADD_DATA_SYNC_WAIT_READY : ++ HINFC610_WRITE_1CMD_1ADD_DATA_WAIT_READY); ++ ++ hinfc_write(host, regval, HINFC610_OP); ++ ++ WAIT_CONTROLLER_FINISH(); ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ return 0; ++} ++/*****************************************************************************/ ++#undef MICRON_RR_ADDR ++/*****************************************************************************/ ++ ++static int hinfc610_micron_set_rr_param(struct hinfc_host *host, int rr_option) ++{ ++ return hinfc610_micron_set_rr_reg(host, rr_option); ++} ++/*****************************************************************************/ ++ ++static int hinfc610_micron_reset_rr_param(struct hinfc_host *host) ++{ ++ return hinfc610_micron_set_rr_reg(host, 0); ++} ++/*****************************************************************************/ ++ ++struct read_retry_t hinfc610_micron_read_retry = { ++ .type = NAND_RR_MICRON, ++ .count = 8, ++ .set_rr_param = hinfc610_micron_set_rr_param, ++ .get_rr_param = NULL, ++ .reset_rr_param = hinfc610_micron_reset_rr_param, ++}; +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_read_retry_samsung.c b/drivers/mtd/nand/hinfc610/hinfc610_read_retry_samsung.c +new file mode 100644 +index 0000000..6d9adcb +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_read_retry_samsung.c +@@ -0,0 +1,109 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++ ++/*****************************************************************************/ ++ ++static int hinfc610_samsung_set_rr_reg(struct hinfc_host *host, int param) ++{ ++#define SAMSUNG_RR_CMD 0xA1 ++ int opval; ++ ++ unsigned char samsung_rr_params[15][4] = { ++ {0x00, 0x00, 0x00, 0x00}, ++ {0x05, 0x0A, 0x00, 0x00}, ++ {0x28, 0x00, 0xEC, 0xD8}, ++ {0xED, 0xF5, 0xED, 0xE6}, ++ {0x0A, 0x0F, 0x05, 0x00}, ++ {0x0F, 0x0A, 0xFB, 0xEC}, ++ {0xE8, 0xEF, 0xE8, 0xDC}, ++ {0xF1, 0xFB, 0xFE, 0xF0}, ++ {0x0A, 0x00, 0xFB, 0xEC}, ++ {0xD0, 0xE2, 0xD0, 0xC2}, ++ {0x14, 0x0F, 0xFB, 0xEC}, ++ {0xE8, 0xFB, 0xE8, 0xDC}, ++ {0x1E, 0x14, 0xFB, 0xEC}, ++ {0xFB, 0xFF, 0xFB, 0xF8}, ++ {0x07, 0x0C, 0x02, 0x00} ++ }; ++ ++ if (param >= 15) ++ param = (param % 15); ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ /* no need to config WAIT_READY_EN, here not config WAIT_READY_EN bit */ ++ opval = (HINFC610_IS_SYNC(host) ? HINFC610_WRITE_1CMD_2ADD_DATA_SYNC ++ : HINFC610_WRITE_1CMD_2ADD_DATA); ++ ++ hinfc_write(host, 1, HINFC610_DATA_NUM); ++ ++ writel(samsung_rr_params[param][0], host->chip->IO_ADDR_R); ++ hinfc_write(host, 0xA700, HINFC610_ADDRL); ++ hinfc_write(host, SAMSUNG_RR_CMD, HINFC610_CMD); ++ hinfc_write(host, opval, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(samsung_rr_params[param][1], host->chip->IO_ADDR_R); ++ hinfc_write(host, 0xA400, HINFC610_ADDRL); ++ hinfc_write(host, SAMSUNG_RR_CMD, HINFC610_CMD); ++ hinfc_write(host, opval, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(samsung_rr_params[param][2], host->chip->IO_ADDR_R); ++ hinfc_write(host, 0xA500, HINFC610_ADDRL); ++ hinfc_write(host, SAMSUNG_RR_CMD, HINFC610_CMD); ++ hinfc_write(host, opval, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(samsung_rr_params[param][3], host->chip->IO_ADDR_R); ++ hinfc_write(host, 0xA600, HINFC610_ADDRL); ++ hinfc_write(host, SAMSUNG_RR_CMD, HINFC610_CMD); ++ hinfc_write(host, opval, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ return 0; ++ ++#undef SAMSUNG_RR_CMD ++} ++/*****************************************************************************/ ++ ++static int hinfc610_samsung_set_rr_param(struct hinfc_host *host, int param) ++{ ++ return hinfc610_samsung_set_rr_reg(host, param); ++} ++/*****************************************************************************/ ++ ++static int hinfc610_samsung_reset_rr_param(struct hinfc_host *host) ++{ ++ return hinfc610_samsung_set_rr_reg(host, 0); ++} ++/*****************************************************************************/ ++ ++struct read_retry_t hinfc610_samsung_read_retry = { ++ .type = NAND_RR_SAMSUNG, ++ .count = 15, ++ .set_rr_param = hinfc610_samsung_set_rr_param, ++ .get_rr_param = NULL, ++ .reset_rr_param = hinfc610_samsung_reset_rr_param, ++}; ++ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_read_retry_toshiba.c b/drivers/mtd/nand/hinfc610/hinfc610_read_retry_toshiba.c +new file mode 100644 +index 0000000..0a1a87e +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_read_retry_toshiba.c +@@ -0,0 +1,113 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++ ++/*****************************************************************************/ ++ ++static int hinfc610_toshiba_24nm_set_rr_reg(struct hinfc_host *host, int param) ++{ ++#define TOSHIBA_RR_CMD 0x55 ++ int opval; ++ static char toshiba_rr_param[] = {0x00, 0x04, 0x7c, 0x78, 0x74, 0x08}; ++ ++ if (!param) { ++ host->send_cmd_reset(host, host->chipselect); ++ return 0; ++ } ++ ++ if (param >= 6) ++ param = (param % 6); ++ ++ /* ++ * no need to config WAIT_READY_EN, here not config WAIT_READY_EN ++ */ ++ opval = (HINFC610_IS_SYNC(host) ? HINFC610_WRITE_1CMD_1ADD_DATA_SYNC ++ : HINFC610_WRITE_1CMD_1ADD_DATA); ++ ++ hinfc_write(host, 1, HINFC610_DATA_NUM); ++ ++ writel(toshiba_rr_param[param], host->chip->IO_ADDR_R); ++ hinfc_write(host, 0x4, HINFC610_ADDRL); ++ hinfc_write(host, TOSHIBA_RR_CMD, HINFC610_CMD); ++ hinfc_write(host, opval, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(toshiba_rr_param[param], host->chip->IO_ADDR_R); ++ hinfc_write(host, 0x5, HINFC610_ADDRL); ++ hinfc_write(host, TOSHIBA_RR_CMD, HINFC610_CMD); ++ hinfc_write(host, opval, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(toshiba_rr_param[param], host->chip->IO_ADDR_R); ++ hinfc_write(host, 0x6, HINFC610_ADDRL); ++ hinfc_write(host, TOSHIBA_RR_CMD, HINFC610_CMD); ++ hinfc_write(host, opval, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(toshiba_rr_param[param], host->chip->IO_ADDR_R); ++ hinfc_write(host, 0x7, HINFC610_ADDRL); ++ hinfc_write(host, TOSHIBA_RR_CMD, HINFC610_CMD); ++ hinfc_write(host, opval, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ return 0; ++ ++#undef TOSHIBA_RR_CMD ++} ++/*****************************************************************************/ ++ ++static int hinfc610_toshiba_24nm_set_rr_param(struct hinfc_host *host, ++ int param) ++{ ++ int opval; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ opval = (HINFC610_IS_SYNC(host) ? HINFC610_WRITE_2CMD_0ADD_NODATA_SYNC ++ : HINFC610_WRITE_2CMD_0ADD_NODATA); ++ ++ hinfc_write(host, HINFC_CMD_SEQ(0x5C, 0xC5), HINFC610_CMD); ++ hinfc_write(host, opval, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ hinfc610_toshiba_24nm_set_rr_reg(host, param); ++ ++ hinfc_write(host, HINFC_CMD_SEQ(0x26, 0x5D), HINFC610_CMD); ++ hinfc_write(host, opval, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_toshiba_24nm_reset_rr_param(struct hinfc_host *host) ++{ ++ return hinfc610_toshiba_24nm_set_rr_reg(host, 0); ++} ++/*****************************************************************************/ ++struct read_retry_t hinfc610_toshiba_24nm_read_retry = { ++ .type = NAND_RR_TOSHIBA_24nm, ++ .count = 6, ++ .set_rr_param = hinfc610_toshiba_24nm_set_rr_param, ++ .get_rr_param = NULL, ++ .reset_rr_param = hinfc610_toshiba_24nm_reset_rr_param, ++}; +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_sync.c b/drivers/mtd/nand/hinfc610/hinfc610_sync.c +new file mode 100644 +index 0000000..c389d98 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_sync.c +@@ -0,0 +1,187 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "hinfc610_os.h" ++#include "hinfc610_gen.h" ++#include "hinfc610.h" ++#include "hinfc610_sync.h" ++ ++static struct nand_sync *nand_sync_table[] = { ++ &hinfc610_sync_onfi_23, ++ &hinfc610_sync_onfi_30, ++ &hinfc610_sync_toggle_10, ++ NULL, ++}; ++ ++static struct nand_sync *hinfc610_find_sync_type(int type) ++{ ++ struct nand_sync **sync; ++ ++ for (sync = nand_sync_table; sync; sync++) { ++ if ((*sync)->type == type) ++ return *sync; ++ } ++ ++ return NULL; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_onfi_support_sync(struct hinfc_host *host) ++{ ++ char buf[6] = {0}; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, sizeof(buf), HINFC610_DATA_NUM); ++ hinfc_write(host, NAND_CMD_READID, HINFC610_CMD); ++ hinfc_write(host, 0x20, HINFC610_ADDRL); ++ hinfc_write(host, HINFC610_READ_1CMD_1ADD_DATA, HINFC610_OP); ++ ++ WAIT_CONTROLLER_FINISH(); ++ memcpy(buf, host->chip->IO_ADDR_R, sizeof(buf)); ++ ++ if (memcmp(buf, "ONFI", 4)) ++ return 0; ++ ++ hinfc_write(host, sizeof(buf), HINFC610_DATA_NUM); ++ hinfc_write(host, NAND_CMD_READID, HINFC610_CMD); ++ hinfc_write(host, 0x40, HINFC610_ADDRL); ++ hinfc_write(host, HINFC610_READ_1CMD_1ADD_DATA, HINFC610_OP); ++ ++ WAIT_CONTROLLER_FINISH(); ++ memcpy(buf, host->chip->IO_ADDR_R, sizeof(buf)); ++ ++ if (memcmp(buf, "JEDEC", 5)) ++ return 0; ++ ++ return (buf[5] == 0x05); ++} ++/*****************************************************************************/ ++ ++static int hinfc610_get_onfi_info(struct hinfc_host *host, int *type) ++{ ++ char buf[6] = {0}; ++ ++ *type = 0; ++ ++ if (!hinfc610_onfi_support_sync(host)) ++ return 0; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, sizeof(buf), HINFC610_DATA_NUM); ++ hinfc_write(host, NAND_CMD_PARAM, HINFC610_CMD); ++ hinfc_write(host, 0x00, HINFC610_ADDRL); ++ hinfc_write(host, HINFC610_READ_1CMD_1ADD_DATA, HINFC610_OP); ++ ++ WAIT_CONTROLLER_FINISH(); ++ memcpy(buf, host->chip->IO_ADDR_R, sizeof(buf)); ++ ++ if (memcmp(buf, "ONFI", 4)) ++ return 0; ++ ++ if (buf[4] & (1 << 6)) ++ *type = NAND_TYPE_ONFI_30; ++ else if (buf[4] & (1 << 5) || ++ buf[4] & (1 << 4) || ++ buf[4] & (1 << 3) || ++ buf[4] & (1 << 2)) ++ *type = NAND_TYPE_ONFI_23; ++ ++ return 1; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_toggle_support_sync(struct hinfc_host *host) ++{ ++ char buf[6] = {0}; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, sizeof(buf), HINFC610_DATA_NUM); ++ hinfc_write(host, NAND_CMD_READID, HINFC610_CMD); ++ hinfc_write(host, 0x40, HINFC610_ADDRL); ++ hinfc_write(host, HINFC610_READ_1CMD_1ADD_DATA, HINFC610_OP); ++ ++ WAIT_CONTROLLER_FINISH(); ++ ++ memcpy(buf, host->chip->IO_ADDR_R, sizeof(buf)); ++ ++ if (memcmp(buf, "JEDEC", 5)) ++ return 0; ++ ++ return 1; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_get_toggle_info(struct hinfc_host *host, int *type) ++{ ++ char buf[8] = {0}; ++ ++ *type = 0; ++ ++ if (!hinfc610_toggle_support_sync(host)) ++ return 0; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, sizeof(buf), HINFC610_DATA_NUM); ++ hinfc_write(host, NAND_CMD_PARAM, HINFC610_CMD); ++ hinfc_write(host, 0x40, HINFC610_ADDRL); ++ hinfc_write(host, HINFC610_READ_1CMD_1ADD_DATA, HINFC610_OP); ++ ++ WAIT_CONTROLLER_FINISH(); ++ ++ memcpy(buf, host->chip->IO_ADDR_R, sizeof(buf)); ++ ++ if (memcmp(buf, "JESD", 4)) ++ return 0; ++ ++ if (buf[4] & (1 << 1)) ++ /* supports revision 1.0 */ ++ *type = NAND_TYPE_TOGGLE_10; ++ else ++ pr_warn("sync NAND has unknown toggle revision.\n"); ++ ++ return 1; ++} ++/*****************************************************************************/ ++ ++int hinfc610_get_sync_info(struct hinfc_host *host) ++{ ++ int type = 0; ++ ++ if (IS_NAND_ONFI(host)) ++ hinfc610_get_onfi_info(host, &type); ++ else ++ hinfc610_get_toggle_info(host, &type); ++ ++ if (!type) { ++ host->flags &= ~NAND_MODE_SYNC_ASYNC; ++ return 0; ++ } ++ ++ host->sync = hinfc610_find_sync_type(type); ++ if (!host->sync) ++ PR_BUG(ERSTR_DRIVER ++ "This Nand Flash need to enable the 'synchronous' feature. " ++ "but the driver dose not offer the feature"); ++ ++ return 0; ++} ++/*****************************************************************************/ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_sync.h b/drivers/mtd/nand/hinfc610/hinfc610_sync.h +new file mode 100644 +index 0000000..906fd10 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_sync.h +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef HINFC610_SYNC_H ++#define HINFC610_SYNC_H ++ ++int hinfc610_get_sync_info(struct hinfc_host *host); ++ ++#endif /* HINFC610_SYNC_H */ ++ +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_sync_onfi_23.c b/drivers/mtd/nand/hinfc610/hinfc610_sync_onfi_23.c +new file mode 100644 +index 0000000..8fba306 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_sync_onfi_23.c +@@ -0,0 +1,107 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/io.h> ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++ ++/*****************************************************************************/ ++ ++static int hinfc610_onfi_enable_sync(struct nand_chip *chip) ++{ ++ struct hinfc_host *host = chip->priv; ++ unsigned char micron_sync_param[4] = { ++ 0x14, /* set sync mode timing */ 0x00, 0x00, 0x00, ++ }; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, 1, HINFC610_DATA_NUM); ++ hinfc_write(host, 0xEF, HINFC610_CMD); ++ hinfc_write(host, 0x01, HINFC610_ADDRL); ++ writel(micron_sync_param[0], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_1CMD_1ADD_DATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(micron_sync_param[1], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(micron_sync_param[2], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ /* need to config WAIT_READY_EN, here config this bit. */ ++ writel(micron_sync_param[3], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA_WAIT_READY, ++ HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++ ++static int hinfc610_onfi_disable_sync(struct nand_chip *chip) ++{ ++ struct hinfc_host *host = chip->priv; ++ unsigned char micron_sync_param[4] = { ++ 0x00, 0x00, 0x00, 0x00, ++ }; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, 1, HINFC610_DATA_NUM); ++ hinfc_write(host, 0xEF, HINFC610_CMD); ++ hinfc_write(host, 0x01, HINFC610_ADDRL); ++ writel(micron_sync_param[0], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_1CMD_1ADD_DATA_SYNC, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(micron_sync_param[1], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA_SYNC, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(micron_sync_param[2], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA_SYNC, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(micron_sync_param[3], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA_SYNC_WAIT_READY, ++ HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++struct nand_sync hinfc610_sync_onfi_23 = { ++ .type = NAND_TYPE_ONFI_23, ++ .enable = hinfc610_onfi_enable_sync, ++ .disable = hinfc610_onfi_disable_sync, ++}; ++ ++struct nand_sync hinfc610_sync_onfi_30 = { ++ .type = NAND_TYPE_ONFI_30, ++ .enable = hinfc610_onfi_enable_sync, ++ .disable = hinfc610_onfi_disable_sync, ++}; +diff --git a/drivers/mtd/nand/hinfc610/hinfc610_sync_toggle.c b/drivers/mtd/nand/hinfc610/hinfc610_sync_toggle.c +new file mode 100644 +index 0000000..918b210 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc610_sync_toggle.c +@@ -0,0 +1,101 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/io.h> ++#include "hinfc610_os.h" ++#include "hinfc610.h" ++ ++/*****************************************************************************/ ++ ++static int hinfc610_toggle_enable_sync(struct nand_chip *chip) ++{ ++ struct hinfc_host *host = chip->priv; ++ unsigned char toshiba_sync_param[4] = { ++ 0x00, 0x00, 0x00, 0x00, ++ }; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, 1, HINFC610_DATA_NUM); ++ hinfc_write(host, 0xEF, HINFC610_CMD); ++ hinfc_write(host, 0x80, HINFC610_ADDRL); ++ writel(toshiba_sync_param[0], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_1CMD_1ADD_DATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(toshiba_sync_param[1], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(toshiba_sync_param[2], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ /* need to config WAIT_READY_EN. */ ++ writel(toshiba_sync_param[3], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA_WAIT_READY, ++ HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++static int hinfc610_toggle_disable_sync(struct nand_chip *chip) ++{ ++ struct hinfc_host *host = chip->priv; ++ unsigned char toshiba_sync_param[4] = { ++ 0x01, 0x00, 0x00, 0x00, ++ }; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ hinfc_write(host, 1, HINFC610_DATA_NUM); ++ hinfc_write(host, 0xEF, HINFC610_CMD); ++ hinfc_write(host, 0x80, HINFC610_ADDRL); ++ writel(toshiba_sync_param[0], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_1CMD_1ADD_DATA_SYNC, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(toshiba_sync_param[1], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA_SYNC, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ writel(toshiba_sync_param[2], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA_SYNC, HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ /* need to config WAIT_READY_EN */ ++ writel(toshiba_sync_param[3], host->chip->IO_ADDR_R); ++ hinfc_write(host, HINFC610_WRITE_0CMD_1ADD_DATA_SYNC_WAIT_READY, ++ HINFC610_OP); ++ WAIT_CONTROLLER_FINISH(); ++ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ return 0; ++} ++/*****************************************************************************/ ++ ++struct nand_sync hinfc610_sync_toggle_10 = { ++ .type = NAND_TYPE_TOGGLE_10, ++ .enable = hinfc610_toggle_enable_sync, ++ .disable = hinfc610_toggle_disable_sync, ++}; +diff --git a/drivers/mtd/nand/hinfc610/hinfc620_gen.c b/drivers/mtd/nand/hinfc610/hinfc620_gen.c +new file mode 100644 +index 0000000..a1e68f8 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc620_gen.c +@@ -0,0 +1,78 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include "../match_table.h" ++#include "hinfc620_gen.h" ++ ++/*****************************************************************************/ ++ ++static struct match_reg_type page_type2reg[] = { ++ { ++ hinfc620_pagesize_2K, NAND_PAGE_2K, ++ }, { ++ hinfc620_pagesize_4K, NAND_PAGE_4K, ++ }, { ++ hinfc620_pagesize_8K, NAND_PAGE_8K, ++ }, { ++ hinfc620_pagesize_16K, NAND_PAGE_16K, ++ }, { ++ hinfc620_pagesize_32K, NAND_PAGE_32K, ++ } ++}; ++ ++enum hinfc620_page_reg hinfc620_page_type2reg(int type) ++{ ++ return type2reg(page_type2reg, ARRAY_SIZE(page_type2reg), type, 0); ++} ++ ++int hinfc620_page_reg2type(enum hinfc620_page_reg reg) ++{ ++ return reg2type(page_type2reg, ARRAY_SIZE(page_type2reg), reg, 0); ++} ++/*****************************************************************************/ ++ ++static struct match_reg_type ecc_type2reg[] = { ++ { ++ hinfc620_ecc_none, NAND_ECC_NONE, ++ }, { ++ hinfc620_ecc_8bit, NAND_ECC_4BIT_512, ++ }, { ++ hinfc620_ecc_16bit, NAND_ECC_8BIT_512, ++ }, { ++ hinfc620_ecc_24bit, NAND_ECC_24BIT, ++ }, { ++ hinfc620_ecc_40bit, NAND_ECC_40BIT, ++ }, { ++ hinfc620_ecc_64bit, NAND_ECC_64BIT, ++ }, { ++ hinfc620_ecc_28bit, NAND_ECC_28BIT, ++ }, { ++ hinfc620_ecc_42bit, NAND_ECC_42BIT, ++ } ++}; ++ ++enum hinfc620_ecc_reg hinfc620_ecc_type2reg(int type) ++{ ++ return type2reg(ecc_type2reg, ARRAY_SIZE(ecc_type2reg), type, 0); ++} ++ ++int hinfc620_ecc_reg2type(enum hinfc620_ecc_reg reg) ++{ ++ return reg2type(ecc_type2reg, ARRAY_SIZE(ecc_type2reg), reg, 0); ++} ++ +diff --git a/drivers/mtd/nand/hinfc610/hinfc620_gen.h b/drivers/mtd/nand/hinfc610/hinfc620_gen.h +new file mode 100644 +index 0000000..88fb1e4 +--- /dev/null ++++ b/drivers/mtd/nand/hinfc610/hinfc620_gen.h +@@ -0,0 +1,53 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef HINFC620_GENH ++#define HINFC620_GENH ++/******************************************************************************/ ++ ++#include "../hinfc_gen.h" ++ ++enum hinfc620_ecc_reg { ++ hinfc620_ecc_none = 0x00, ++ hinfc620_ecc_8bit = 0x02, ++ hinfc620_ecc_16bit = 0x03, ++ hinfc620_ecc_24bit = 0x04, ++ hinfc620_ecc_40bit = 0x05, ++ hinfc620_ecc_64bit = 0x06, ++ hinfc620_ecc_28bit = 0x07, ++ hinfc620_ecc_42bit = 0x08, ++}; ++ ++enum hinfc620_page_reg { ++ hinfc620_pagesize_2K = 0x01, ++ hinfc620_pagesize_4K = 0x02, ++ hinfc620_pagesize_8K = 0x03, ++ hinfc620_pagesize_16K = 0x04, ++ hinfc620_pagesize_32K = 0x05, ++}; ++ ++enum hinfc620_page_reg hinfc620_page_type2reg(int type); ++ ++int hinfc620_page_reg2type(enum hinfc620_page_reg reg); ++ ++enum hinfc620_ecc_reg hinfc620_ecc_type2reg(int type); ++ ++int hinfc620_ecc_reg2type(enum hinfc620_ecc_reg reg); ++ ++/******************************************************************************/ ++#endif /* HINFC620_GENH */ +diff --git a/drivers/mtd/nand/hinfc_gen.c b/drivers/mtd/nand/hinfc_gen.c +new file mode 100644 +index 0000000..7ecfb5a +--- /dev/null ++++ b/drivers/mtd/nand/hinfc_gen.c +@@ -0,0 +1,247 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/mfd/hisi_fmc.h> ++#include "match_table.h" ++#include "hinfc_gen.h" ++ ++/*****************************************************************************/ ++struct nand_flash_dev *(*nand_get_flash_type_func)(struct mtd_info *mtd, ++ struct nand_chip *chip, struct nand_dev_t *spinand_dev_t) = NULL; ++ ++struct nand_flash_dev *(*get_spi_nand_flash_type_hook)(struct mtd_info *mtd, ++ unsigned char *id) = NULL; ++ ++/*****************************************************************************/ ++static struct match_t match_ecc[] = { ++ MATCH_SET_TYPE_DATA(NAND_ECC_NONE, "none"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_0BIT, "none"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_1BIT_512, "1bit/512"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_4BIT, "4bit/512"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_4BIT_512, "4bit/512"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_4BYTE, "4byte/1k"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_8BIT, "4bit/512"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_8BIT_512, "8bit/512"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_8BYTE, "8byte/1k"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_13BIT, "13bit/1k"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_16BIT, "8bit/512"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_18BIT, "18bit/1k"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_24BIT, "24bit/1k"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_27BIT, "27bit/1k"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_32BIT, "32bit/1k"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_40BIT, "40bit/1k"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_41BIT, "41bit/1k"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_48BIT, "48bit/1k"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_60BIT, "60bit/1k"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_72BIT, "72bit/1k"), ++ MATCH_SET_TYPE_DATA(NAND_ECC_80BIT, "80bit/1k"), ++}; ++ ++const char *nand_ecc_name(int type) ++{ ++ return (char *)match_type_to_data(match_ecc, ARRAY_SIZE(match_ecc), ++ type, "unknown"); ++} ++ ++char *get_ecctype_str(enum ecc_type ecctype) ++{ ++ static char *ecctype_string[] = { ++ "None", "1bit/512Byte", "4bits/512Byte", "8bits/512Byte", ++ "24bits/1K", "40bits/1K", "unknown", "unknown"}; ++ return ecctype_string[(ecctype & 0x07)]; ++} ++ ++/*****************************************************************************/ ++static struct match_type_str page2name[] = { ++ { NAND_PAGE_512B, "512" }, ++ { NAND_PAGE_2K, "2K" }, ++ { NAND_PAGE_4K, "4K" }, ++ { NAND_PAGE_8K, "8K" }, ++ { NAND_PAGE_16K, "16K" }, ++ { NAND_PAGE_32K, "32K" }, ++}; ++ ++const char *nand_page_name(int type) ++{ ++ return type2str(page2name, ARRAY_SIZE(page2name), type, "unknown"); ++} ++ ++char *get_pagesize_str(enum page_type pagetype) ++{ ++ static char *pagesize_str[] = { ++ "512", "2K", "4K", "8K", "16K", "unknown", ++ "unknown", "unknown"}; ++ return pagesize_str[(pagetype & 0x07)]; ++} ++ ++/*****************************************************************************/ ++static struct match_reg_type page2size[] = { ++ { _512B, NAND_PAGE_512B }, ++ { _2K, NAND_PAGE_2K }, ++ { _4K, NAND_PAGE_4K }, ++ { _8K, NAND_PAGE_8K }, ++ { _16K, NAND_PAGE_16K }, ++ { _32K, NAND_PAGE_32K }, ++}; ++ ++unsigned int get_pagesize(enum page_type pagetype) ++{ ++ unsigned int pagesize[] = { ++ _512B, _2K, _4K, _8K, _16K, 0, 0, 0}; ++ return pagesize[(pagetype & 0x07)]; ++} ++ ++int nandpage_size2type(int size) ++{ ++ return reg2type(page2size, ARRAY_SIZE(page2size), size, NAND_PAGE_2K); ++} ++ ++int nandpage_type2size(int size) ++{ ++ return type2reg(page2size, ARRAY_SIZE(page2size), size, NAND_PAGE_2K); ++} ++ ++char *nand_dbgfs_options; ++ ++static int __init dbgfs_options_setup(char *s) ++{ ++ nand_dbgfs_options = s; ++ return 1; ++} ++__setup("nanddbgfs=", dbgfs_options_setup); ++ ++/*****************************************************************************/ ++ ++int get_bits(unsigned int n) ++{ ++ int loop; ++ int ret = 0; ++ ++ if (!n) ++ return 0; ++ ++ if (n > 0xFFFF) ++ loop = n > 0xFFFFFF ? 32 : 24; ++ else ++ loop = n > 0xFF ? 16 : 8; ++ ++ while (loop-- > 0 && n) { ++ if (n & 1) ++ ret++; ++ n >>= 1; ++ } ++ return ret; ++} ++ ++/*****************************************************************************/ ++#define et_ecc_none 0x00 ++#define et_ecc_4bit 0x02 ++#define et_ecc_8bit 0x03 ++#define et_ecc_24bit1k 0x04 ++#define et_ecc_40bit1k 0x05 ++#define et_ecc_64bit1k 0x06 ++ ++static struct match_reg_type ecc_yaffs_type_t[] = { ++ {et_ecc_none, NAND_ECC_0BIT}, ++ {et_ecc_4bit, NAND_ECC_8BIT}, ++ {et_ecc_8bit, NAND_ECC_16BIT}, ++ {et_ecc_24bit1k, NAND_ECC_24BIT}, ++ {et_ecc_40bit1k, NAND_ECC_40BIT}, ++ {et_ecc_64bit1k, NAND_ECC_64BIT} ++}; ++ ++unsigned char match_ecc_type_to_yaffs(unsigned char type) ++{ ++ return type2reg(ecc_yaffs_type_t, ARRAY_SIZE(ecc_yaffs_type_t), type, ++ et_ecc_4bit); ++} ++ ++/*****************************************************************************/ ++static struct match_t page_table[] = { ++ {NAND_PAGE_2K, PAGE_SIZE_2KB, "2K"}, ++ {NAND_PAGE_4K, PAGE_SIZE_4KB, "4K"}, ++ {NAND_PAGE_8K, PAGE_SIZE_8KB, "8K"}, ++ {NAND_PAGE_16K, PAGE_SIZE_16KB, "16K"}, ++}; ++ ++unsigned char match_page_reg_to_type(unsigned char reg) ++{ ++ return match_reg_to_type(page_table, ARRAY_SIZE(page_table), reg, ++ NAND_PAGE_2K); ++} ++ ++unsigned char match_page_type_to_reg(unsigned char type) ++{ ++ return match_type_to_reg(page_table, ARRAY_SIZE(page_table), type, ++ PAGE_SIZE_2KB); ++} ++ ++const char *match_page_type_to_str(unsigned char type) ++{ ++ return match_type_to_data(page_table, ARRAY_SIZE(page_table), type, ++ "unknown"); ++} ++ ++/*****************************************************************************/ ++static struct match_t ecc_table[] = { ++ {NAND_ECC_0BIT, ECC_TYPE_0BIT, "none"}, ++ {NAND_ECC_8BIT, ECC_TYPE_8BIT, "4bit/512"}, ++ {NAND_ECC_16BIT, ECC_TYPE_16BIT, "8bit/512"}, ++ {NAND_ECC_24BIT, ECC_TYPE_24BIT, "24bit/1K"}, ++ {NAND_ECC_28BIT, ECC_TYPE_28BIT, "28bit/1K"}, ++ {NAND_ECC_40BIT, ECC_TYPE_40BIT, "40bit/1K"}, ++ {NAND_ECC_64BIT, ECC_TYPE_64BIT, "64bit/1K"}, ++}; ++ ++unsigned char match_ecc_reg_to_type(unsigned char reg) ++{ ++ return match_reg_to_type(ecc_table, ARRAY_SIZE(ecc_table), reg, ++ NAND_ECC_8BIT); ++} ++ ++unsigned char match_ecc_type_to_reg(unsigned char type) ++{ ++ return match_type_to_reg(ecc_table, ARRAY_SIZE(ecc_table), type, ++ ECC_TYPE_8BIT); ++} ++ ++const char *match_ecc_type_to_str(unsigned char type) ++{ ++ return match_type_to_data(ecc_table, ARRAY_SIZE(ecc_table), type, ++ "unknown"); ++} ++ ++/*****************************************************************************/ ++static struct match_t page_type_size_table[] = { ++ {NAND_PAGE_2K, _2K, NULL}, ++ {NAND_PAGE_4K, _4K, NULL}, ++ {NAND_PAGE_8K, _8K, NULL}, ++ {NAND_PAGE_16K, _16K, NULL}, ++}; ++ ++unsigned char match_page_size_to_type(unsigned int size) ++{ ++ return match_reg_to_type(page_type_size_table, ++ ARRAY_SIZE(page_type_size_table), size, NAND_PAGE_2K); ++} ++ ++unsigned int match_page_type_to_size(unsigned char type) ++{ ++ return match_type_to_reg(page_type_size_table, ++ ARRAY_SIZE(page_type_size_table), type, _2K); ++} +diff --git a/drivers/mtd/nand/hinfc_gen.h b/drivers/mtd/nand/hinfc_gen.h +new file mode 100644 +index 0000000..bc3658c +--- /dev/null ++++ b/drivers/mtd/nand/hinfc_gen.h +@@ -0,0 +1,288 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef __HINFC_GEN_H__ ++#define __HINFC_GEN_H__ ++ ++/*****************************************************************************/ ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/string_helpers.h> ++#include <asm/setup.h> ++#include <linux/module.h> ++ ++/*****************************************************************************/ ++#define HINFC_VER_300 (0x300) ++#define HINFC_VER_301 (0x301) ++#define HINFC_VER_310 (0x310) ++#define HINFC_VER_504 (0x504) ++#define HINFC_VER_505 (0x505) ++#define HINFC_VER_600 (0x600) ++#define HINFC_VER_610 (0x610) ++#define HINFC_VER_620 (0x620) ++ ++/*****************************************************************************/ ++#define NAND_PAGE_512B 0 ++#define NAND_PAGE_1K 1 ++#define NAND_PAGE_2K 2 ++#define NAND_PAGE_4K 3 ++#define NAND_PAGE_8K 4 ++#define NAND_PAGE_16K 5 ++#define NAND_PAGE_32K 6 ++ ++/*****************************************************************************/ ++#define NAND_ECC_NONE 0 ++#define NAND_ECC_0BIT 0 ++#define NAND_ECC_1BIT 1 ++#define NAND_ECC_1BIT_512 1 ++#define NAND_ECC_4BIT 2 ++#define NAND_ECC_4BIT_512 2 ++#define NAND_ECC_4BYTE 2 ++#define NAND_ECC_8BIT 2 ++#define NAND_ECC_8BIT_512 3 ++#define NAND_ECC_8BYTE 3 ++#define NAND_ECC_13BIT 4 ++#define NAND_ECC_16BIT 5 ++#define NAND_ECC_18BIT 6 ++#define NAND_ECC_24BIT 7 ++#define NAND_ECC_27BIT 8 ++#define NAND_ECC_28BIT 9 ++#define NAND_ECC_32BIT 10 ++#define NAND_ECC_40BIT 11 ++#define NAND_ECC_41BIT 12 ++#define NAND_ECC_42BIT 13 ++#define NAND_ECC_48BIT 14 ++#define NAND_ECC_60BIT 15 ++#define NAND_ECC_64BIT 16 ++#define NAND_ECC_72BIT 17 ++#define NAND_ECC_80BIT 18 ++ ++enum ecc_type { ++ et_ecc_none = 0x00, ++ et_ecc_1bit = 0x01, ++ et_ecc_4bit = 0x02, ++ et_ecc_8bit = 0x03, ++ et_ecc_24bit1k = 0x04, ++ et_ecc_40bit1k = 0x05, ++ et_ecc_64bit1k = 0x06, ++}; ++ ++enum page_type { ++ pt_pagesize_512 = 0x00, ++ pt_pagesize_2K = 0x01, ++ pt_pagesize_4K = 0x02, ++ pt_pagesize_8K = 0x03, ++ pt_pagesize_16K = 0x04, ++}; ++ ++/*****************************************************************************/ ++struct nand_config_info { ++ unsigned int pagetype; ++ unsigned int ecctype; ++ unsigned int oobsize; ++ struct nand_ecclayout *layout; ++}; ++ ++struct hinfc_host; ++ ++struct nand_sync { ++ ++#define SET_NAND_SYNC_TYPE(_mfr, _onfi, _version) \ ++ ((((_mfr) & 0xFF) << 16) | (((_version) & 0xFF) << 8) \ ++ | ((_onfi) & 0xFF)) ++ ++#define GET_NAND_SYNC_TYPE_MFR(_type) (((_type) >> 16) & 0xFF) ++#define GET_NAND_SYNC_TYPE_VER(_type) (((_type) >> 8) & 0xFF) ++#define GET_NAND_SYNC_TYPE_INF(_type) ((_type) & 0xFF) ++ ++#define NAND_TYPE_ONFI_23_MICRON \ ++ SET_NAND_SYNC_TYPE(NAND_MFR_MICRON, NAND_IS_ONFI, 0x23) ++#define NAND_TYPE_ONFI_30_MICRON \ ++ SET_NAND_SYNC_TYPE(NAND_MFR_MICRON, NAND_IS_ONFI, 0x30) ++#define NAND_TYPE_TOGGLE_TOSHIBA \ ++ SET_NAND_SYNC_TYPE(NAND_MFR_TOSHIBA, 0, 0) ++#define NAND_TYPE_TOGGLE_SAMSUNG \ ++ SET_NAND_SYNC_TYPE(NAND_MFR_SAMSUNG, 0, 0) ++ ++#define NAND_TYPE_TOGGLE_10 SET_NAND_SYNC_TYPE(0, 0, 0x10) ++#define NAND_TYPE_ONFI_30 SET_NAND_SYNC_TYPE(0, NAND_IS_ONFI, 0x30) ++#define NAND_TYPE_ONFI_23 SET_NAND_SYNC_TYPE(0, NAND_IS_ONFI, 0x23) ++ ++ int type; ++ int (*enable)(struct nand_chip *chip); ++ int (*disable)(struct nand_chip *chip); ++}; ++ ++struct read_retry_t { ++ int type; ++ int count; ++ int (*set_rr_param)(struct hinfc_host *host, int param); ++ int (*get_rr_param)(struct hinfc_host *host); ++ int (*reset_rr_param)(struct hinfc_host *host); ++}; ++ ++struct ecc_info_t { ++ int pagesize; ++ int ecctype; ++ int threshold; ++ int section; ++ void (*dump)(struct hinfc_host *host, unsigned char ecc[], ++ int *max_bitsflag); ++}; ++ ++struct nand_dev_t { ++ struct nand_flash_dev flash_dev; ++ ++ char *start_type; ++ unsigned char ids[8]; ++ int oobsize; ++ int ecctype; ++ ++ /* (Controller) support ecc/page detect, driver don't need detect */ ++#define NANDC_HW_AUTO 0x01 ++ /* (Controller) support ecc/page detect, ++ * and current ecc/page config finish */ ++#define NANDC_CONFIG_DONE 0x02 ++ /* (Controller) is sync, default is async */ ++#define NANDC_IS_SYNC_BOOT 0x04 ++ ++/* (NAND) need randomizer */ ++#define NAND_RANDOMIZER 0x10 ++/* (NAND) is ONFI interface, combine with sync/async symble */ ++#define NAND_IS_ONFI 0x20 ++/* (NAND) support async and sync, such micron onfi, toshiba toggle 1.0 */ ++#define NAND_MODE_SYNC_ASYNC 0x40 ++/* (NAND) support only sync, such samsung sync. */ ++#define NAND_MODE_ONLY_SYNC 0x80 ++ ++#define NAND_CHIP_MICRON (NAND_MODE_SYNC_ASYNC | NAND_IS_ONFI) ++/* This NAND is async, or sync/async, default is async mode, ++ * toggle1.0 interface */ ++#define NAND_CHIP_TOSHIBA_TOGGLE_10 (NAND_MODE_SYNC_ASYNC) ++/* This NAND is only sync mode, toggle2.0 interface */ ++#define NAND_CHIP_TOSHIBA_TOGGLE_20 (NAND_MODE_ONLY_SYNC) ++/* This NAND is only sync mode */ ++#define NAND_CHIP_SAMSUNG (NAND_MODE_ONLY_SYNC) ++ ++ unsigned int flags; ++ ++#define NAND_RR_NONE 0x00 ++#define NAND_RR_HYNIX_BG_BDIE 0x10 ++#define NAND_RR_HYNIX_BG_CDIE 0x11 ++#define NAND_RR_HYNIX_CG_ADIE 0x12 ++#define NAND_RR_MICRON 0x20 ++#define NAND_RR_SAMSUNG 0x30 ++#define NAND_RR_TOSHIBA_24nm 0x40 ++#define NAND_RR_TOSHIBA_19nm 0x41 ++ int read_retry_type; ++}; ++ ++/*****************************************************************************/ ++ ++#define IS_NANDC_HW_AUTO(_host) ((_host)->flags & NANDC_HW_AUTO) ++#define IS_NANDC_CONFIG_DONE(_host) ((_host)->flags & NANDC_CONFIG_DONE) ++#define IS_NANDC_SYNC_BOOT(_host) ((_host)->flags & NANDC_IS_SYNC_BOOT) ++ ++#define IS_NAND_RANDOM(_dev) ((_dev)->flags & NAND_RANDOMIZER) ++#define IS_NAND_ONLY_SYNC(_dev) ((_dev)->flags & NAND_MODE_ONLY_SYNC) ++#define IS_NAND_SYNC_ASYNC(_dev) ((_dev)->flags & NAND_MODE_SYNC_ASYNC) ++#define IS_NAND_ONFI(_dev) ((_dev)->flags & NAND_IS_ONFI) ++ ++#define ERSTR_HARDWARE "Hardware configuration error. " ++#define ERSTR_DRIVER "Driver does not support. " ++ ++#define ENABLE 1 ++#define DISABLE 0 ++ ++/*****************************************************************************/ ++ ++char *get_ecctype_str(enum ecc_type ecctype); ++ ++char *get_pagesize_str(enum page_type pagetype); ++ ++unsigned int get_pagesize(enum page_type pagetype); ++ ++const char *nand_ecc_name(int type); ++ ++const char *nand_page_name(int type); ++ ++int nandpage_size2type(int size); ++ ++int nandpage_type2size(int size); ++ ++/*****************************************************************************/ ++extern int (*hinfc_param_adjust)(struct mtd_info *mtd, struct nand_chip *chip, ++ struct nand_dev_t *nand_dev); ++ ++extern struct nand_flash_dev *(*nand_get_flash_type_func)(struct mtd_info *mtd, ++ struct nand_chip *chip, struct nand_dev_t *spinand_dev_t); ++ ++extern struct nand_flash_dev *(*get_spi_nand_flash_type_hook) ++ (struct mtd_info *mtd, unsigned char *id); ++ ++extern int (*hinfc_param_adjust)(struct mtd_info *, ++ struct nand_chip *, struct nand_dev_t *); ++ ++/*****************************************************************************/ ++struct nand_flash_dev *hinfc_get_flash_type(struct mtd_info *mtd, ++ struct nand_chip *chip, u8 *id_data, int *busw); ++ ++extern struct nand_flash_dev *(*get_spi_nand_flash_type_hook) ++ (struct mtd_info *mtd, unsigned char *id); ++ ++void hinfc_nand_param_adjust(struct mtd_info *mtd, struct nand_chip *chip); ++ ++void hinfc_show_info(struct mtd_info *mtd, char *vendor, char *chipname); ++ ++void hinfc_show_chipsize(struct nand_chip *chip); ++ ++int get_bits(unsigned int n); ++ ++/*****************************************************************************/ ++#define hinfc_pr_msg(_fmt, arg...) printk(_fmt, ##arg) ++ ++#define hinfc_pr_bug(fmt, args...) do { \ ++ printk("%s(%d): bug " fmt, __FILE__, __LINE__, ##args); \ ++ while (1) \ ++ ; \ ++} while (0) ++ ++extern char *nand_dbgfs_options; ++/*****************************************************************************/ ++extern unsigned char match_page_reg_to_type(unsigned char reg); ++ ++extern unsigned char match_page_type_to_reg(unsigned char type); ++ ++extern const char *match_page_type_to_str(unsigned char type); ++ ++/*****************************************************************************/ ++extern unsigned char match_ecc_reg_to_type(unsigned char reg); ++ ++extern unsigned char match_ecc_type_to_reg(unsigned char type); ++ ++extern const char *match_ecc_type_to_str(unsigned char type); ++ ++/*****************************************************************************/ ++extern unsigned char match_page_size_to_type(unsigned int size); ++ ++extern unsigned int match_page_type_to_size(unsigned char type); ++ ++const char *nand_ecc_name(int type); ++/*****************************************************************************/ ++ ++#endif /* End of __HINFC_GEN_H__ */ +diff --git a/drivers/mtd/nand/hinfc_spl_ids.c b/drivers/mtd/nand/hinfc_spl_ids.c +new file mode 100644 +index 0000000..ccc15de +--- /dev/null ++++ b/drivers/mtd/nand/hinfc_spl_ids.c +@@ -0,0 +1,979 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/mtd/mtd.h> ++#include <linux/mfd/hisi_fmc.h> ++#include "hinfc_gen.h" ++ ++/*****************************************************************************/ ++ ++struct nand_flash_special_dev { ++ unsigned char id[8]; ++ int length; /* length of id. */ ++ unsigned long long chipsize; ++ struct nand_flash_dev *(*probe)(struct nand_dev_t *nand_dev); ++ char *name; ++ ++ unsigned long pagesize; ++ unsigned long erasesize; ++ unsigned long oobsize; ++ unsigned long options; ++ unsigned int read_retry_type; ++ ++#define BBP_LAST_PAGE 0x01 ++#define BBP_FIRST_PAGE 0x02 ++ unsigned int badblock_pos; ++ unsigned int flags; ++}; ++ ++/*****************************************************************************/ ++/* this is nand probe function. */ ++/*****************************************************************************/ ++ ++static struct nand_flash_dev *hynix_probe_v02( ++ struct nand_dev_t *nand_dev) ++{ ++ unsigned char *id = nand_dev->ids; ++ struct nand_flash_dev *type = &nand_dev->flash_dev; ++ ++ int pagesizes[] = {SZ_2K, SZ_4K, SZ_8K, 0}; ++ int oobsizes[] = {128, 224, 448, 0, 0, 0, 0, 0}; ++ int blocksizes[] = {SZ_128K, SZ_256K, SZ_512K, ++ (SZ_256K + SZ_512K), SZ_1M, SZ_2M, 0, 0}; ++ ++ int blocktype = (((id[3] >> 5) & 0x04) | ((id[3] >> 4) & 0x03)); ++ int oobtype = (((id[3] >> 2) & 0x03) | ((id[3] >> 4) & 0x04)); ++ ++ type->options = 0; ++ type->pagesize = pagesizes[(id[3] & 0x03)]; ++ type->erasesize = blocksizes[blocktype]; ++ nand_dev->oobsize = oobsizes[oobtype]; ++ ++ return type; ++} ++/*****************************************************************************/ ++ ++static struct nand_flash_dev *samsung_probe_v02( ++ struct nand_dev_t *nand_dev) ++{ ++ unsigned char *id = nand_dev->ids; ++ struct nand_flash_dev *type = &nand_dev->flash_dev; ++ ++ int pagesizes[] = {SZ_2K, SZ_4K, SZ_8K, 0}; ++ int oobsizes[] = {0, 128, 218, 400, 436, 0, 0, 0}; ++ int blocksizes[] = {SZ_128K, SZ_256K, SZ_512K, SZ_1M, 0, 0, 0, 0}; ++ ++ int blocktype = (((id[3] >> 5) & 0x04) | ((id[3] >> 4) & 0x03)); ++ int oobtype = (((id[3] >> 4) & 0x04) | ((id[3] >> 2) & 0x03)); ++ ++ type->options = 0; ++ type->pagesize = pagesizes[(id[3] & 0x03)]; ++ type->erasesize = blocksizes[blocktype]; ++ nand_dev->oobsize = oobsizes[oobtype]; ++ ++ return type; ++} ++/*****************************************************************************/ ++ ++#define DRV_VERSION "1.38" ++ ++/*****************************************************************************/ ++/* ++ * samsung: 27nm need randomizer, 21nm need read retry; ++ * micron: 25nm need read retry, datasheet will explain read retry. ++ * toshaba 32nm need randomizer, 24nm need read retry. ++ * hynix: 2xnm need read retry. ++ * ++ * The special nand flash ID table version 1.37 ++ * ++ * manufactory | type | name | ecc_type | version_tag ++ * Micron | MLC | MT29F64G08CBABA | 40bit/1k | 1.36 ++ * Micron | MLC | MT29F32G08CBADA | 40bit/1k | ++ * Micron | SLC | MT29F8G08ABxBA | 4bit/512 | ++ * Micron | MLC | MT29F16G08CBABx | 12bit/512 | ++ * Micron | MLC | MT29F16G08CBACA | 24bit/1k | ++ * Micron | MLC | MT29F32G08CBACA | 24bit/1k | ++ * Micron | MLC | MT29F64G08CxxAA | 24bit/1k | ++ * Micron | MLC | MT29F256G08CJAAA | 24bit/1k | 2CE ++ * Micron | MLC | MT29F256G08CMCBB | 24bit/1k | ++ * Micron | SLC | MT29F8G08ABACA | 8bit/512 | ++ * Micron | SLC | MT29F4G08ABAEA | 8bit/512 | ++ * Micron | SLC | MT29F2G08ABAFA | 8bit/512 | ++ * Micron | SLC | MT29F16G08ABACA | 8bit/512 | ++ * Toshiba | MLC | TC58NVG4D2FTA00 | 24bit/1k | ++ * Toshiba | MLC | TH58NVG6D2FTA20 | 24bit/1k | 2CE ++ * Toshiba | MLC | TC58NVG5D2HTA00 | 40bit/1k | ++ * Toshiba | MLC | TC58NVG6D2GTA00 | 40bit/1k | ++ * Toshiba | MLC | TC58NVG6DCJTA00 | | ++ * Toshiba | MLC | TC58TEG5DCJTA00 | | ++ * Toshiba | SLC | TC58NVG0S3HTA00 | 8bit/512 | ++ * Toshiba | SLC | TC58NVG1S3HTA00 | 8bit/512 | ++ * Toshiba | SLC | TC58NVG1S3ETA00 | 4bit/512 | ++ * Toshiba | SLC | TC58NVG3S0FTA00 | 4bit/512 | ++ * Toshiba | SLC | TC58NVG2S0FTA00 | 4bit/512 | ++ * Toshiba | SLC | TH58NVG2S3HTA00 | 4bit/512 | ++ * Toshiba | TLC | TC58NVG5T2JTA00 | 60bit/1k | ++ * Toshiba | TLC | TC58TEG5DCKTAx0 | 60bit/1k | ++ * Toshiba | MLC | Tx58TEGxDDKTAx0 | | ++ * Samsung | MLC | K9LB(HC/PD/MD)G08U0(1)D | 8bit/512B | ++ * Samsung | MLC | K9GAG08U0E | 24bit/1KB | ++ * Samsung | MLC | K9LBG08U0E | 24bit/1KB | ++ * Samsung | MLC | K9G8G08U0C | 24bit/1KB | ++ * Samsung | MLC | K9GAG08U0F | 24bit/1KB | ++ * Samsung | MLC | K9LBG08U0M | | ++ * Samsung | MLC | K9GBG08U0A | 24bit/1KB | ++ * Samsung | MLC | K9GBG08U0B | 40bit/1KB | ++ * Hynix | MLC | H27UAG8T2A | | ++ * Hynix | MLC | H27UAG8T2B | | ++ * Hynix | MLC | H27UBG8T2A | | ++ * Hynix | MLC | H27UBG8T2BTR | 24bit/1KB | ++ * Hynix | MLC | H27UCG8T2A | 40bit/1KB | ++ * Hynix | MLC | H27UBG8T2C | 40bit/1KB | ++ * MISC | MLC | P1UAGA30AT-GCA | 8bit/512 | ++ * MISC | MLC | PSU8GA30AT-GIA/ASU8GA30IT-G30CA | 4bit/512 | ++ * MISC | SLC | PSU2GA30AT | 1bit/512 | 1.36 ++ * Toshiba | SLC | TC58NVG2S0HTA00 | 24bit/1K | 1.37 ++ * Toshiba | SLC | TC58NVG3S0HTA00 | 24bit/1K | 1.37 ++ * Micron | SLC | MT29F2G08ABAEA | 4bit/512 | ++ * Spansion | SLC | S34ML02G200TFI000 | 24bit/1K | ++ * Spansion | SLC | S34ML04G200TFI000 | 24bit/1K | 1.38 ++ * ++ */ ++ ++static struct nand_flash_special_dev nand_flash_special_dev[] = { ++ ++/****************************** Spansion *******************************/ ++ ++ { /* SLC S34ML02G200TFI000 */ ++ .name = "S34ML02G200TFI000", ++ .id = {0x01, 0xDA, 0x90, 0x95, 0x46, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _256M, ++ .probe = NULL, ++ .pagesize = SZ_2K, ++ .erasesize = SZ_128K, ++ .oobsize = 128, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ ++ { /* SLC S34ML04G200TFI000 */ ++ .name = "S34ML04G200TFI000", ++ .id = {0x01, 0xDC, 0x90, 0x95, 0x56, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _512M, ++ .probe = NULL, ++ .pagesize = SZ_2K, ++ .erasesize = SZ_128K, ++ .oobsize = 128, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ ++/****************************** Micron *******************************/ ++ { /* MLC 40bit/1k */ ++ .name = "MT29F64G08CBABA", ++ .id = {0x2C, 0x64, 0x44, 0x4B, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _8G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_2M, ++ .oobsize = 744, ++ .options = 0, ++ .read_retry_type = NAND_RR_MICRON, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = NAND_RANDOMIZER | NAND_CHIP_MICRON, ++ }, ++ { /* MLC 40bit/1k */ ++ .name = "MT29F32G08CBADA", ++ .id = {0x2C, 0x44, 0x44, 0x4B, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_2M, ++ .oobsize = 744, ++ .options = 0, ++ .read_retry_type = NAND_RR_MICRON, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* SLC 4bit/512 */ ++ .name = "MT29F8G08ABxBA", ++ .id = {0x2C, 0x38, 0x00, 0x26, 0x85, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = SZ_1G, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_512K, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 12bit/512 */ ++ .name = "MT29F16G08CBABx", ++ .id = {0x2C, 0x48, 0x04, 0x46, 0x85, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = SZ_2G, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_1M, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k */ ++ .name = "MT29F16G08CBACA", ++ .id = {0x2C, 0x48, 0x04, 0x4A, 0xA5, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = SZ_2G, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_1M, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k */ ++ .name = "MT29F32G08CBACA", ++ .id = {0x2C, 0x68, 0x04, 0x4A, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_1M, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k */ ++ .name = "MT29F64G08CxxAA", ++ .id = {0x2C, 0x88, 0x04, 0x4B, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _8G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_2M, ++ .oobsize = 448, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k 2CE */ ++ .name = "MT29F256G08CJAAA", ++ .id = {0x2C, 0xA8, 0x05, 0xCB, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _16G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_2M, ++ .oobsize = 448, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 40bit/1k */ ++ .name = "MT29F256G08CMCBB", ++ .id = {0x2C, 0x64, 0x44, 0x4B, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 8, ++ .chipsize = _8G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_2M, ++ .oobsize = 744, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 8bit/512 */ ++ .name = "MT29F8G08ABACA", ++ .id = {0x2C, 0xD3, 0x90, 0xA6, 0x64, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = SZ_1G, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_256K, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 8bit/512 */ ++ .name = "MT29F4G08ABAEA", ++ .id = {0x2C, 0xDC, 0x90, 0xA6, 0x54, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = SZ_512M, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_256K, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 8bit/512 */ ++ .name = "MT29F2G08ABAFA", ++ .id = {0x2C, 0xDA, 0x90, 0x95, 0x04, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = SZ_256M, ++ .probe = NULL, ++ .pagesize = SZ_2K, ++ .erasesize = SZ_128K, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC MT29F2G08ABAEA */ ++ .name = "MT29F2G08ABAEA", ++ .id = {0x2C, 0xDA, 0x90, 0x95, 0x06, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _256M, ++ .probe = NULL, ++ .pagesize = SZ_2K, ++ .erasesize = SZ_128K, ++ .oobsize = 64, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 8bit/512 */ ++ .name = "MT29F16G08ABACA", ++ .id = {0x2C, 0x48, 0x00, 0x26, 0xA9, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = SZ_2G, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_512K, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ ++/****************************** Toshaba *******************************/ ++ ++ { /* MLC 24bit/1k 32nm */ ++ .name = "TC58NVG4D2FTA00", ++ .id = {0x98, 0xD5, 0x94, 0x32, 0x76, 0x55, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = SZ_2G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_1M, ++ .oobsize = 448, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k 32nm 2CE*/ ++ .name = "TH58NVG6D2FTA20", ++ .id = {0x98, 0xD7, 0x94, 0x32, 0x76, 0x55, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_1M, ++ .oobsize = 448, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 40bit/1k 24nm */ ++ .name = "TC58NVG5D2HTA00 24nm", ++ .id = {0x98, 0xD7, 0x94, 0x32, 0x76, 0x56, 0x08, 0x00}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_1M, ++ .oobsize = 640, ++ .options = 0, ++ .read_retry_type = NAND_RR_TOSHIBA_24nm, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* MLC 40bit/1k */ ++ .name = "TC58NVG6D2GTA00", ++ .id = {0x98, 0xDE, 0x94, 0x82, 0x76, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _8G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_2M, ++ .oobsize = 640, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 19nm */ ++ .name = "TC58NVG6DCJTA00 19nm", ++ .id = {0x98, 0xDE, 0x84, 0x93, 0x72, 0x57, 0x08, 0x04}, ++ .length = 8, ++ .chipsize = _8G, ++ .probe = NULL, ++ .pagesize = SZ_16K, ++ .erasesize = SZ_4M, ++ .oobsize = 1280, ++ .options = 0, ++ .read_retry_type = NAND_RR_TOSHIBA_24nm, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* MLC 19nm */ ++ .name = "TC58TEG5DCJTA00 19nm", ++ .id = {0x98, 0xD7, 0x84, 0x93, 0x72, 0x57, 0x08, 0x04}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_16K, ++ .erasesize = SZ_4M, ++ .oobsize = 1280, ++ .options = 0, ++ .read_retry_type = NAND_RR_TOSHIBA_24nm, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER | NAND_CHIP_TOSHIBA_TOGGLE_10, ++ }, ++ { /* SLC 8bit/512 */ ++ .name = "TC58NVG0S3HTA00", ++ .id = {0x98, 0xF1, 0x80, 0x15, 0x72, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = SZ_128M, ++ .probe = NULL, ++ .pagesize = SZ_2K, ++ .erasesize = SZ_128K, ++ .oobsize = 128, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ /* ++ * Datasheet: read one column of any page in each block. If the ++ * data of the column is 00 (Hex), define the block as a bad ++ * block. ++ */ ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 8bit/512 */ ++ .name = "TC58NVG1S3HTA00", ++ .id = {0x98, 0xDA, 0x90, 0x15, 0x76, 0x16, 0x08, 0x00}, ++ .length = 7, ++ .chipsize = SZ_256M, ++ .probe = NULL, ++ .pagesize = SZ_2K, ++ .erasesize = SZ_128K, ++ .oobsize = 128, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 4bit/512 */ ++ .name = "TC58NVG1S3ETA00", ++ .id = {0x98, 0xDA, 0x90, 0x15, 0x76, 0x14, 0x03, 0x00}, ++ .length = 7, ++ .chipsize = SZ_256M, ++ .probe = NULL, ++ .pagesize = SZ_2K, ++ .erasesize = SZ_128K, ++ .oobsize = 64, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 4bit/512 */ ++ .name = "TC58NVG3S0FTA00", ++ .id = {0x98, 0xD3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08}, ++ .length = 8, ++ .chipsize = SZ_1G, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_256K, ++ .oobsize = 232, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 4bit/512 */ ++ .name = "TC58NVG3S0HTA00", ++ .id = {0x98, 0xD3, 0x91, 0x26, 0x76, 0x16, 0x08, 0x00}, ++ .length = 8, ++ .chipsize = SZ_1G, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_256K, ++ .oobsize = 256, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 24bit/1k */ ++ .name = "TC58NVG2S0HTA00", ++ .id = {0x98, 0xDC, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00}, ++ .length = 8, ++ .chipsize = SZ_512M, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_256K, ++ .oobsize = 256, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 4bit/512 */ ++ .name = "TC58NVG2S0FTA00", ++ .id = {0x98, 0xDC, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08}, ++ .length = 8, ++ .chipsize = SZ_512M, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_256K, ++ .oobsize = 224, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 4bit/512 */ ++ .name = "TH58NVG2S3HTA00", ++ .id = {0x98, 0xDC, 0x91, 0x15, 0x76}, ++ .length = 5, ++ .chipsize = SZ_512M, ++ .probe = NULL, ++ .pagesize = SZ_2K, ++ .erasesize = SZ_128K, ++ .oobsize = 128, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .flags = 0, ++ }, ++ { /* TLC 60bit/1k 19nm */ ++ .name = "TC58NVG5T2JTA00 19nm TLC", ++ .id = {0x98, 0xD7, 0x98, 0x92, 0x72, 0x57, 0x08, 0x10}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_4M, ++ .oobsize = 1024, ++ .options = 0, ++ .read_retry_type = NAND_RR_TOSHIBA_24nm, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* TLC 60bit/1k 19nm */ ++ .name = "TC58TEG5DCKTAx0 19nm MLC", ++ /* datasheet says 6 ids id data, but really has 8 ids. */ ++ .id = {0x98, 0xD7, 0x84, 0x93, 0x72, 0x50, 0x08, 0x04}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_16K, ++ .erasesize = SZ_4M, ++ .oobsize = 1280, ++ .options = 0, ++ .read_retry_type = NAND_RR_TOSHIBA_19nm, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { ++ .name = "Tx58TEGxDDKTAx0 19nm MLC", ++ .id = {0x98, 0xDE, 0x94, 0x93, 0x76, 0x50}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_16K, ++ .erasesize = SZ_4M, ++ .oobsize = 1280, ++ .options = 0, ++ .read_retry_type = NAND_RR_TOSHIBA_19nm, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++/******************************* Samsung ******************************/ ++ { /* MLC 8bit/512B */ ++ .name = "K9LB(HC/PD/MD)G08U0(1)D", ++ .id = {0xEC, 0xD7, 0xD5, 0x29, 0x38, 0x41, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = samsung_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1KB */ ++ .name = "K9GAG08U0E", ++ .id = {0xEC, 0xD5, 0x84, 0x72, 0x50, 0x42, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = SZ_2G, ++ .probe = samsung_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1KB */ ++ .name = "K9LBG08U0E", ++ .id = {0xEC, 0xD7, 0xC5, 0x72, 0x54, 0x42, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = samsung_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1KB */ ++ .name = "K9G8G08U0C", ++ .id = {0xEC, 0xD3, 0x84, 0x72, 0x50, 0x42, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = SZ_1G, ++ .probe = samsung_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k */ ++ .name = "K9GAG08U0F", ++ .id = {0xEC, 0xD5, 0x94, 0x76, 0x54, 0x43, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = SZ_2G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_1M, ++ .oobsize = 512, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC */ ++ .name = "K9LBG08U0M", ++ .id = {0xEC, 0xD7, 0x55, 0xB6, 0x78, 0x00, 0x00, 0x00}, ++ .length = 5, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_512K, ++ .oobsize = 128, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1k */ ++ .name = "K9GBG08U0A 20nm", ++ .id = {0xEC, 0xD7, 0x94, 0x7A, 0x54, 0x43, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_1M, ++ .oobsize = 640, ++ .options = 0, ++ .read_retry_type = NAND_RR_SAMSUNG, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* MLC 40bit/1k */ ++ .name = "K9GBG08U0B", ++ .id = {0xEC, 0xD7, 0x94, 0x7E, 0x64, 0x44, 0x00, 0x00}, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_1M, ++ .oobsize = 1024, ++ .options = 0, ++ .read_retry_type = NAND_RR_SAMSUNG, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ ++/*********************************** Hynix ****************************/ ++ { /* MLC */ ++ .name = "H27UAG8T2A", ++ .id = {0xAD, 0xD5, 0x94, 0x25, 0x44, 0x41, }, ++ .length = 6, ++ .chipsize = SZ_2G, ++ .probe = hynix_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC */ ++ .name = "H27UAG8T2B", ++ .id = {0xAD, 0xD5, 0x94, 0x9A, 0x74, 0x42, }, ++ .length = 6, ++ .chipsize = SZ_2G, ++ .probe = hynix_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC */ ++ .name = "H27UBG8T2A", ++ .id = {0xAD, 0xD7, 0x94, 0x9A, 0x74, 0x42, }, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = hynix_probe_v02, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 24bit/1K, 26nm TODO: Need read retry, chip is EOS */ ++ .name = "H27UBG8T2BTR 26nm", ++ .id = {0xAD, 0xD7, 0x94, 0xDA, 0x74, 0xC3, }, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_2M, ++ .oobsize = 640, ++ .options = 0, ++ .read_retry_type = NAND_RR_HYNIX_BG_BDIE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* MLC 40bit/1k */ ++ .name = "H27UCG8T2A", ++ .id = {0xAD, 0xDE, 0x94, 0xDA, 0x74, 0xC4, }, ++ .length = 6, ++ .chipsize = _8G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_2M, ++ .oobsize = 640, ++ .options = 0, ++ .read_retry_type = NAND_RR_HYNIX_CG_ADIE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ { /* MLC 40bit/1k */ ++ .name = "H27UBG8T2C", ++ .id = {0xAD, 0xD7, 0x94, 0x91, 0x60, 0x44, }, ++ .length = 6, ++ .chipsize = _4G, ++ .probe = NULL, ++ .pagesize = SZ_8K, ++ .erasesize = SZ_2M, ++ .oobsize = 640, ++ .options = 0, ++ .read_retry_type = NAND_RR_HYNIX_BG_CDIE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = NAND_RANDOMIZER, ++ }, ++ ++/********************** MISC ******************************************/ ++ { /* MLC 8bit/512 */ ++ .name = "P1UAGA30AT-GCA", ++ .id = {0xC8, 0xD5, 0x14, 0x29, 0x34, 0x01, }, ++ .length = 6, ++ .chipsize = SZ_2G, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_512K, ++ .oobsize = 218, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* MLC 4bit/512 */ ++ /* ++ * PowerFlash ASU8GA30IT-G30CA ID and MIRA PSU8GA30AT-GIA ID are ++ * the same ID ++ */ ++ .name = "PSU8GA30AT-GIA/ASU8GA30IT-G30CA", ++ .id = {0xC8, 0xD3, 0x90, 0x19, 0x34, 0x01, }, ++ .length = 6, ++ .chipsize = SZ_1G, ++ .probe = NULL, ++ .pagesize = SZ_4K, ++ .erasesize = SZ_256K, ++ .oobsize = 218, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ { /* SLC 1bit/512 */ ++ .name = "PSU2GA30AT", ++ .id = {0x7F, 0x7F, 0x7F, 0x7F, 0xC8, 0xDA, 0x00, 0x15, }, ++ .length = 8, ++ .chipsize = SZ_256M, ++ .probe = NULL, ++ .pagesize = SZ_2K, ++ .erasesize = SZ_128K, ++ .oobsize = 64, ++ .options = 0, ++ .read_retry_type = NAND_RR_NONE, ++ .badblock_pos = BBP_FIRST_PAGE | BBP_LAST_PAGE, ++ .flags = 0, ++ }, ++ {{0}, 0, 0, 0, 0, 0, 0, 0, 0}, ++}; ++ ++#define NUM_OF_SPECIAL_DEVICE \ ++ (sizeof(nand_flash_special_dev)/sizeof(struct nand_flash_special_dev)) ++ ++int (*hinfc_param_adjust)(struct mtd_info *, struct nand_chip *, ++ struct nand_dev_t *) = NULL; ++ ++static struct nand_dev_t __nand_dev; ++/*****************************************************************************/ ++ ++static struct nand_flash_dev *hinfc_nand_probe(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ struct nand_dev_t *nand_dev) ++{ ++ struct nand_flash_special_dev *spl_dev; ++ unsigned char *byte = nand_dev->ids; ++ struct nand_flash_dev *type = &nand_dev->flash_dev; ++ ++ hinfc_pr_msg("Nand ID: 0x%02X 0x%02X 0x%02X 0x%02X", ++ byte[0], byte[1], byte[2], byte[3]); ++ hinfc_pr_msg(" 0x%02X 0x%02X 0x%02X 0x%02X\n", ++ byte[4], byte[5], byte[6], byte[7]); ++ ++ for (spl_dev = nand_flash_special_dev; spl_dev->length; spl_dev++) { ++ if (memcmp(byte, spl_dev->id, spl_dev->length)) ++ continue; ++ ++ hinfc_pr_msg("The Special NAND id table Version: %s\n", DRV_VERSION); ++ ++ if (spl_dev->probe) { ++ type = spl_dev->probe(nand_dev); ++ } else { ++ type->options = spl_dev->options; ++ type->pagesize = spl_dev->pagesize; ++ type->erasesize = spl_dev->erasesize; ++ nand_dev->oobsize = spl_dev->oobsize; ++ } ++ ++ nand_dev->read_retry_type = spl_dev->read_retry_type; ++ nand_dev->flags = spl_dev->flags; ++ ++ type->id[1] = byte[1]; ++ type->chipsize = (unsigned long)(spl_dev->chipsize >> 20); ++ type->name = spl_dev->name; ++ return type; ++ } ++ nand_dev->read_retry_type = NAND_RR_NONE; ++ ++ return NULL; ++} ++/*****************************************************************************/ ++ ++struct nand_flash_dev *hinfc_get_flash_type(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ u8 *id_data, int *busw) ++{ ++ struct nand_flash_dev *type; ++ struct nand_dev_t *nand_dev = &__nand_dev; ++ ++ memset(nand_dev, 0, sizeof(struct nand_dev_t)); ++ memcpy(nand_dev->ids, id_data, 8); ++ ++ if (!hinfc_nand_probe(mtd, chip, nand_dev)) ++ return NULL; ++ ++ type = &nand_dev->flash_dev; ++ ++ if (!mtd->name) ++ mtd->name = type->name; ++ ++ chip->chipsize = (uint64_t)type->chipsize << 20; ++ mtd->erasesize = type->erasesize; ++ mtd->writesize = type->pagesize; ++ mtd->oobsize = nand_dev->oobsize; ++ *busw = (type->options & NAND_BUSWIDTH_16); ++ ++ return type; ++} ++/*****************************************************************************/ ++ ++void hinfc_nand_param_adjust(struct mtd_info *mtd, struct nand_chip *chip) ++{ ++ struct nand_dev_t *nand_dev = &__nand_dev; ++ ++ if (!nand_dev->oobsize) ++ nand_dev->oobsize = mtd->oobsize; ++ ++ if (hinfc_param_adjust) ++ hinfc_param_adjust(mtd, chip, nand_dev); ++} ++/*****************************************************************************/ ++ ++void hinfc_show_info(struct mtd_info *mtd, char *vendor, char *chipname) ++{ ++ /* char buf[20]; */ ++ struct nand_dev_t *nand_dev = &__nand_dev; ++ ++ /* hinfc_pr_msg("Nand: %s %s ", vendor, chipname); */ ++ ++ if (IS_NAND_RANDOM(nand_dev)) ++ hinfc_pr_msg("Randomizer \n"); ++ ++ if (nand_dev->read_retry_type != NAND_RR_NONE) ++ hinfc_pr_msg("Read-Retry \n"); ++ ++ if (nand_dev->start_type) ++ hinfc_pr_msg("Nand(%s): ", nand_dev->start_type); ++ else ++ hinfc_pr_msg("Nand: "); ++ ++ hinfc_pr_msg("OOB:%dB ", nand_dev->oobsize); ++ hinfc_pr_msg("ECC:%s ", nand_ecc_name(nand_dev->ecctype)); ++} ++/*****************************************************************************/ ++ ++void hinfc_show_chipsize(struct nand_chip *chip) ++{ ++ /*char buf[20];*/ ++ ++ /*hinfc_pr_msg("Chip:%sB*%d\n", ++ ultohstr(chip->chipsize, buf, sizeof(buf)), ++ chip->numchips);*/ ++} +diff --git a/drivers/mtd/nand/match_table.c b/drivers/mtd/nand/match_table.c +new file mode 100644 +index 0000000..271da05 +--- /dev/null ++++ b/drivers/mtd/nand/match_table.c +@@ -0,0 +1,96 @@ ++/****************************************************************************** ++ * COPYRIGHT (C) Hisilicon.2013 ++ * All rights reserved. ++ * *** ++ * Create by Hisilicon 2013-08-15 ++ * ++ *****************************************************************************/ ++ ++/*****************************************************************************/ ++#include <linux/string.h> ++#include "match_table.h" ++ ++/*****************************************************************************/ ++int reg2type(struct match_reg_type *table, int length, int reg, int def) ++{ ++ while (length-- > 0) { ++ if (table->reg == reg) ++ return table->type; ++ table++; ++ } ++ return def; ++} ++ ++int type2reg(struct match_reg_type *table, int length, int type, int def) ++{ ++ while (length-- > 0) { ++ if (table->type == type) ++ return table->reg; ++ table++; ++ } ++ return def; ++} ++ ++int str2type(struct match_type_str *table, int length, const char *str, ++ int size, int def) ++{ ++ while (length-- > 0) { ++ if (!strncmp(table->str, str, size)) ++ return table->type; ++ table++; ++ } ++ return def; ++} ++ ++const char *type2str(struct match_type_str *table, int length, int type, ++ const char *def) ++{ ++ while (length-- > 0) { ++ if (table->type == type) ++ return table->str; ++ table++; ++ } ++ return def; ++} ++ ++int match_reg_to_type(struct match_t *table, int nr_table, int reg, int def) ++{ ++ while (nr_table-- > 0) { ++ if (table->reg == reg) ++ return table->type; ++ table++; ++ } ++ return def; ++} ++ ++int match_type_to_reg(struct match_t *table, int nr_table, int type, int def) ++{ ++ while (nr_table-- > 0) { ++ if (table->type == type) ++ return table->reg; ++ table++; ++ } ++ return def; ++} ++ ++int match_data_to_type(struct match_t *table, int nr_table, char *data, ++ int size, int def) ++{ ++ while (nr_table-- > 0) { ++ if (!memcmp(table->data, data, size)) ++ return table->type; ++ table++; ++ } ++ return def; ++} ++ ++void *match_type_to_data(struct match_t *table, int nr_table, int type, ++ void *def) ++{ ++ while (nr_table-- > 0) { ++ if (table->type == type) ++ return table->data; ++ table++; ++ } ++ return def; ++} +diff --git a/drivers/mtd/nand/match_table.h b/drivers/mtd/nand/match_table.h +new file mode 100644 +index 0000000..6fb55cf +--- /dev/null ++++ b/drivers/mtd/nand/match_table.h +@@ -0,0 +1,56 @@ ++/****************************************************************************** ++ * COPYRIGHT (C) Hisilicon 2013 ++ * All rights reserved. ++ * *** ++ * Create by Hisilicon 2013-08-15 ++ * ++ *****************************************************************************/ ++#ifndef __MATCH_TABLE_H__ ++#define __MATCH_TABLE_H__ ++ ++/*****************************************************************************/ ++struct match_reg_type { ++ int reg; ++ int type; ++}; ++ ++struct match_type_str { ++ int type; ++ const char *str; ++}; ++ ++struct match_t { ++ int type; ++ int reg; ++ void *data; ++}; ++ ++/*****************************************************************************/ ++#define MATCH_SET_TYPE_REG(_type, _reg) {(_type), (_reg), (void *)0} ++#define MATCH_SET_TYPE_DATA(_type, _data) {(_type), 0, (void *)(_data)} ++#define MATCH_SET(_type, _reg, _data) {(_type), (_reg), (void *)(_data)} ++ ++/*****************************************************************************/ ++int reg2type(struct match_reg_type *table, int length, int reg, int def); ++ ++int type2reg(struct match_reg_type *table, int length, int type, int def); ++ ++int str2type(struct match_type_str *table, int length, const char *str, ++ int size, int def); ++ ++const char *type2str(struct match_type_str *table, int length, int type, ++ const char *def); ++ ++int match_reg_to_type(struct match_t *table, int nr_table, int reg, int def); ++ ++int match_type_to_reg(struct match_t *table, int nr_table, int type, int def); ++ ++int match_data_to_type(struct match_t *table, int nr_table, char *data, ++ int size, int def); ++ ++void *match_type_to_data(struct match_t *table, int nr_table, int type, ++ void *def); ++ ++/*****************************************************************************/ ++ ++#endif /* End of __MATCH_TABLE_H__ */ +diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c +index 5b5c627..7a754d2 100644 +--- a/drivers/mtd/nand/nand_base.c ++++ b/drivers/mtd/nand/nand_base.c +@@ -49,6 +49,8 @@ + #include <linux/io.h> + #include <linux/mtd/partitions.h> + ++#include "hinfc_gen.h" ++ + /* Define default oob placement schemes for large and small page devices */ + static struct nand_ecclayout nand_oob_8 = { + .eccbytes = 3, +@@ -3570,7 +3572,7 @@ static void nand_decode_bbm_options(struct mtd_info *mtd, + maf_id == NAND_MFR_HYNIX || + maf_id == NAND_MFR_TOSHIBA || + maf_id == NAND_MFR_AMD || +- maf_id == NAND_MFR_MACRONIX)) || ++ maf_id == NAND_MFR_MXIC)) || + (mtd->writesize == 2048 && + maf_id == NAND_MFR_MICRON)) + chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; +@@ -3589,7 +3591,13 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, + mtd->erasesize = type->erasesize; + mtd->oobsize = type->oobsize; + ++#ifdef CONFIG_HIFMC100_SPI_NAND ++ /* Hisilicon only support SLC spi nand devices */ ++ chip->bits_per_cell = 1; ++#else + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); ++#endif ++ + chip->chipsize = (uint64_t)type->chipsize << 20; + chip->options |= type->options; + chip->ecc_strength_ds = NAND_ECC_STRENGTH(type); +@@ -3615,7 +3623,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + int *maf_id, int *dev_id, + struct nand_flash_dev *type) + { +- int busw; ++ int busw = 0; + int i, maf_idx; + u8 id_data[8]; + +@@ -3654,6 +3662,15 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + return ERR_PTR(-ENODEV); + } + ++#ifdef CONFIG_HIFMC ++ if (get_spi_nand_flash_type_hook) ++ type = get_spi_nand_flash_type_hook(mtd, id_data); ++#else ++ type = hinfc_get_flash_type(mtd, chip, id_data, &busw); ++ if (type) ++ goto ident_done; ++#endif ++ + if (!type) + type = nand_flash_ids; + +@@ -3704,6 +3721,9 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) + chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; + ident_done: ++#if defined(CONFIG_HIFMC) || defined(CONFIG_MTD_NAND_HINFC610) ++ hinfc_nand_param_adjust(mtd, chip); ++#endif + + /* Try to identify manufacturer */ + for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { +@@ -3765,9 +3785,13 @@ ident_done: + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + type->name); + +- pr_info("%dMiB, %s, page size: %d, OOB size: %d\n", ++ pr_info("%dMiB, %s, page size: %d\n", + (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", +- mtd->writesize, mtd->oobsize); ++ mtd->writesize); ++ ++ /* Print ecc type and ecc mode about hisilicon flash controller */ ++ hinfc_show_info(mtd, nand_manuf_ids[maf_idx].name, type->name); ++ + return type; + } + +@@ -4047,7 +4071,7 @@ int nand_scan_tail(struct mtd_info *mtd) + break; + + case NAND_ECC_NONE: +- pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n"); ++ pr_warn(" ECC provided by Flash Memory Controller\n"); + ecc->read_page = nand_read_page_raw; + ecc->write_page = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; +diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c +index fbde891..59b1fba 100644 +--- a/drivers/mtd/nand/nand_ids.c ++++ b/drivers/mtd/nand/nand_ids.c +@@ -165,19 +165,25 @@ struct nand_flash_dev nand_flash_ids[] = { + + /* Manufacturer IDs */ + struct nand_manufacturers nand_manuf_ids[] = { +- {NAND_MFR_TOSHIBA, "Toshiba"}, +- {NAND_MFR_SAMSUNG, "Samsung"}, +- {NAND_MFR_FUJITSU, "Fujitsu"}, +- {NAND_MFR_NATIONAL, "National"}, +- {NAND_MFR_RENESAS, "Renesas"}, +- {NAND_MFR_STMICRO, "ST Micro"}, +- {NAND_MFR_HYNIX, "Hynix"}, +- {NAND_MFR_MICRON, "Micron"}, +- {NAND_MFR_AMD, "AMD/Spansion"}, +- {NAND_MFR_MACRONIX, "Macronix"}, +- {NAND_MFR_EON, "Eon"}, ++ {NAND_MFR_TOSHIBA, "Toshiba"}, ++ {NAND_MFR_SAMSUNG, "Samsung"}, ++ {NAND_MFR_FUJITSU, "Fujitsu"}, ++ {NAND_MFR_NATIONAL, "National"}, ++ {NAND_MFR_RENESAS, "Renesas"}, ++ {NAND_MFR_ST_MICRO, "ST/Micro"}, ++ {NAND_MFR_HYNIX, "Hynix"}, ++ {NAND_MFR_MICRON, "Micron"}, ++ {NAND_MFR_AMD, "AMD/Spansion"}, ++ {NAND_MFR_GD_ESMT, "GD/ESMT"}, ++ {NAND_MFR_EON, "Eon"}, ++ {NAND_MFR_WINBOND, "Winbond"}, ++ {NAND_MFR_ATO, "ATO"}, ++ {NAND_MFR_MXIC, "MXIC"}, ++ {NAND_MFR_ALL_FLASH, "All-flash"}, ++ {NAND_MFR_PARAGON, "Paragon"}, + {NAND_MFR_SANDISK, "SanDisk"}, + {NAND_MFR_INTEL, "Intel"}, ++ {NAND_MFR_HEYANGTEK, "HeYangTek"}, + {0x0, "Unknown"} + }; + +diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig +index 64a4f0e..3d8abfc 100644 +--- a/drivers/mtd/spi-nor/Kconfig ++++ b/drivers/mtd/spi-nor/Kconfig +@@ -23,9 +23,35 @@ config MTD_SPI_NOR_USE_4K_SECTORS + + config SPI_FSL_QUADSPI + tristate "Freescale Quad SPI controller" +- depends on ARCH_MXC ++ depends on ARCH_MXC || COMPILE_TEST ++ depends on HAS_IOMEM + help + This enables support for the Quad SPI controller in master mode. +- We only connect the NOR to this controller now. ++ This controller does not support generic SPI. It only supports ++ SPI NOR. ++ ++config SPI_HISI_SFC ++ tristate "Hisilicon SPI-NOR Flash Controller(SFC)" ++ depends on ARCH_HISI ++ help ++ This enables support for hisilicon SPI-NOR flash controller. ++ ++config CLOSE_SPI_8PIN_4IO ++ bool "Hisilicon close SPI device Quad SPI mode for some 8PIN chip" ++ default y if ARCH_HISI ++ help ++ SFC support Quad SPI mode and Quad&addr SPI mode. ++ But some 8PIN chip does not support this mode when HOLD/IO3 PIN was ++ used by reset operation. Usually, your should not config this option. ++ ++config HISI_SPI_BLOCK_PROTECT ++ bool "Hisilicon Spi Nor Device BP(Block Protect) Support" ++ default y if ARCH_HISI ++ help ++ HISI SFC supports BP(Block Protect) feature to preestablish a series ++ area to avoid writing and erasing, except to reading. With this macro ++ definition we can get the BP info which was setted before. The ++ BOTTOM/TOP bit is setted to BOTTOM, it means the lock area starts ++ from 0 address. + + endif # MTD_SPI_NOR +diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile +index 6a7ce14..4544966 100644 +--- a/drivers/mtd/spi-nor/Makefile ++++ b/drivers/mtd/spi-nor/Makefile +@@ -1,2 +1,3 @@ + obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o + obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o ++obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o +diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c +index d5269a2..df8d001 100644 +--- a/drivers/mtd/spi-nor/fsl-quadspi.c ++++ b/drivers/mtd/spi-nor/fsl-quadspi.c +@@ -26,6 +26,21 @@ + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> + #include <linux/mtd/spi-nor.h> ++#include <linux/mutex.h> ++#include <linux/pm_qos.h> ++#include <linux/sizes.h> ++ ++/* Controller needs driver to swap endian */ ++#define QUADSPI_QUIRK_SWAP_ENDIAN (1 << 0) ++/* Controller needs 4x internal clock */ ++#define QUADSPI_QUIRK_4X_INT_CLK (1 << 1) ++/* ++ * TKT253890, Controller needs driver to fill txfifo till 16 byte to ++ * trigger data transfer even though extern data will not transferred. ++ */ ++#define QUADSPI_QUIRK_TKT253890 (1 << 2) ++/* Controller cannot wake up from wait mode, TKT245618 */ ++#define QUADSPI_QUIRK_TKT245618 (1 << 3) + + /* The registers */ + #define QUADSPI_MCR 0x00 +@@ -57,7 +72,9 @@ + + #define QUADSPI_BUF3CR 0x1c + #define QUADSPI_BUF3CR_ALLMST_SHIFT 31 +-#define QUADSPI_BUF3CR_ALLMST (1 << QUADSPI_BUF3CR_ALLMST_SHIFT) ++#define QUADSPI_BUF3CR_ALLMST_MASK (1 << QUADSPI_BUF3CR_ALLMST_SHIFT) ++#define QUADSPI_BUF3CR_ADATSZ_SHIFT 8 ++#define QUADSPI_BUF3CR_ADATSZ_MASK (0xFF << QUADSPI_BUF3CR_ADATSZ_SHIFT) + + #define QUADSPI_BFGENCR 0x20 + #define QUADSPI_BFGENCR_PAR_EN_SHIFT 16 +@@ -138,15 +155,15 @@ + #define LUT_MODE 4 + #define LUT_MODE2 5 + #define LUT_MODE4 6 +-#define LUT_READ 7 +-#define LUT_WRITE 8 ++#define LUT_FSL_READ 7 ++#define LUT_FSL_WRITE 8 + #define LUT_JMP_ON_CS 9 + #define LUT_ADDR_DDR 10 + #define LUT_MODE_DDR 11 + #define LUT_MODE2_DDR 12 + #define LUT_MODE4_DDR 13 +-#define LUT_READ_DDR 14 +-#define LUT_WRITE_DDR 15 ++#define LUT_FSL_READ_DDR 14 ++#define LUT_FSL_WRITE_DDR 15 + #define LUT_DATA_LEARN 16 + + /* +@@ -189,36 +206,66 @@ + #define SEQID_EN4B 10 + #define SEQID_BRWR 11 + ++#define QUADSPI_MIN_IOMAP SZ_4M ++ + enum fsl_qspi_devtype { + FSL_QUADSPI_VYBRID, + FSL_QUADSPI_IMX6SX, ++ FSL_QUADSPI_IMX7D, ++ FSL_QUADSPI_IMX6UL, + }; + + struct fsl_qspi_devtype_data { + enum fsl_qspi_devtype devtype; + int rxfifo; + int txfifo; ++ int ahb_buf_size; ++ int driver_data; + }; + + static struct fsl_qspi_devtype_data vybrid_data = { + .devtype = FSL_QUADSPI_VYBRID, + .rxfifo = 128, +- .txfifo = 64 ++ .txfifo = 64, ++ .ahb_buf_size = 1024, ++ .driver_data = QUADSPI_QUIRK_SWAP_ENDIAN, + }; + + static struct fsl_qspi_devtype_data imx6sx_data = { + .devtype = FSL_QUADSPI_IMX6SX, + .rxfifo = 128, +- .txfifo = 512 ++ .txfifo = 512, ++ .ahb_buf_size = 1024, ++ .driver_data = QUADSPI_QUIRK_4X_INT_CLK ++ | QUADSPI_QUIRK_TKT245618, ++}; ++ ++static struct fsl_qspi_devtype_data imx7d_data = { ++ .devtype = FSL_QUADSPI_IMX7D, ++ .rxfifo = 512, ++ .txfifo = 512, ++ .ahb_buf_size = 1024, ++ .driver_data = QUADSPI_QUIRK_TKT253890 ++ | QUADSPI_QUIRK_4X_INT_CLK, ++}; ++ ++static struct fsl_qspi_devtype_data imx6ul_data = { ++ .devtype = FSL_QUADSPI_IMX6UL, ++ .rxfifo = 128, ++ .txfifo = 512, ++ .ahb_buf_size = 1024, ++ .driver_data = QUADSPI_QUIRK_TKT253890 ++ | QUADSPI_QUIRK_4X_INT_CLK, + }; + + #define FSL_QSPI_MAX_CHIP 4 + struct fsl_qspi { +- struct mtd_info mtd[FSL_QSPI_MAX_CHIP]; + struct spi_nor nor[FSL_QSPI_MAX_CHIP]; + void __iomem *iobase; +- void __iomem *ahb_base; /* Used when read from AHB bus */ ++ void __iomem *ahb_addr; + u32 memmap_phy; ++ u32 memmap_offs; ++ u32 memmap_len; + struct clk *clk, *clk_en; + struct device *dev; + struct completion c; +@@ -227,16 +274,29 @@ struct fsl_qspi { + u32 nor_num; + u32 clk_rate; + unsigned int chip_base_addr; /* We may support two chips. */ ++ bool has_second_chip; ++ struct mutex lock; ++ struct pm_qos_request pm_qos_req; + }; + +-static inline int is_vybrid_qspi(struct fsl_qspi *q) ++static inline int needs_swap_endian(struct fsl_qspi *q) + { +- return q->devtype_data->devtype == FSL_QUADSPI_VYBRID; ++ return q->devtype_data->driver_data & QUADSPI_QUIRK_SWAP_ENDIAN; + } + +-static inline int is_imx6sx_qspi(struct fsl_qspi *q) ++static inline int needs_4x_clock(struct fsl_qspi *q) + { +- return q->devtype_data->devtype == FSL_QUADSPI_IMX6SX; ++ return q->devtype_data->driver_data & QUADSPI_QUIRK_4X_INT_CLK; ++} ++ ++static inline int needs_fill_txfifo(struct fsl_qspi *q) ++{ ++ return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT253890; ++} ++ ++static inline int needs_wakeup_wait_mode(struct fsl_qspi *q) ++{ ++ return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT245618; + } + + /* +@@ -245,7 +305,7 @@ static inline int is_imx6sx_qspi(struct fsl_qspi *q) + */ + static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a) + { +- return is_vybrid_qspi(q) ? __swab32(a) : a; ++ return needs_swap_endian(q) ? __swab32(a) : a; + } + + static inline void fsl_qspi_unlock_lut(struct fsl_qspi *q) +@@ -306,7 +366,7 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) + + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); +- writel(LUT0(DUMMY, PAD1, dummy) | LUT1(READ, PAD4, rxfifo), ++ writel(LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo), + base + QUADSPI_LUT(lut_base + 1)); + + /* Write enable */ +@@ -327,24 +387,18 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) + + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); +- writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1)); ++ writel(LUT0(FSL_WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1)); + + /* Read Status */ + lut_base = SEQID_RDSR * 4; +- writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(READ, PAD1, 0x1), ++ writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(FSL_READ, PAD1, 0x1), + base + QUADSPI_LUT(lut_base)); + + /* Erase a sector */ + lut_base = SEQID_SE * 4; + +- if (q->nor_size <= SZ_16M) { +- cmd = SPINOR_OP_SE; +- addrlen = ADDR24BIT; +- } else { +- /* use the 4-byte address */ +- cmd = SPINOR_OP_SE; +- addrlen = ADDR32BIT; +- } ++ cmd = q->nor[0].erase_opcode; ++ addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT; + + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); +@@ -356,17 +410,17 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) + + /* READ ID */ + lut_base = SEQID_RDID * 4; +- writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(READ, PAD1, 0x8), ++ writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(FSL_READ, PAD1, 0x8), + base + QUADSPI_LUT(lut_base)); + + /* Write Register */ + lut_base = SEQID_WRSR * 4; +- writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(WRITE, PAD1, 0x2), ++ writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(FSL_WRITE, PAD1, 0x2), + base + QUADSPI_LUT(lut_base)); + + /* Read Configuration Register */ + lut_base = SEQID_RDCR * 4; +- writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(READ, PAD1, 0x1), ++ writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(FSL_READ, PAD1, 0x1), + base + QUADSPI_LUT(lut_base)); + + /* Write disable */ +@@ -413,6 +467,8 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) + case SPINOR_OP_BRWR: + return SEQID_BRWR; + default: ++ if (cmd == q->nor[0].erase_opcode) ++ return SEQID_SE; + dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd); + break; + } +@@ -454,8 +510,7 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len) + writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR); + + /* Wait for the interrupt. */ +- err = wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000)); +- if (!err) { ++ if (!wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000))) { + dev_err(q->dev, + "cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n", + cmd, addr, readl(base + QUADSPI_FR), +@@ -532,7 +587,7 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, + + /* clear the TX FIFO. */ + tmp = readl(q->iobase + QUADSPI_MCR); +- writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR); ++ writel(tmp | QUADSPI_MCR_CLR_TXF_MASK, q->iobase + QUADSPI_MCR); + + /* fill the TX data to the FIFO */ + for (j = 0, i = ((count + 3) / 4); j < i; j++) { +@@ -541,6 +596,11 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, + txbuf++; + } + ++ /* fill the TXFIFO upto 16 bytes for i.MX7d */ ++ if (needs_fill_txfifo(q)) ++ for (; i < 4; i++) ++ writel(tmp, q->iobase + QUADSPI_TBDR); ++ + /* Trigger it */ + ret = fsl_qspi_runcmd(q, opcode, to, count); + +@@ -583,7 +643,12 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q) + writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR); + writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR); + writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR); +- writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR); ++ /* ++ * Set ADATSZ with the maximum AHB buffer size to improve the ++ * read performance. ++ */ ++ writel(QUADSPI_BUF3CR_ALLMST_MASK | ((q->devtype_data->ahb_buf_size / 8) ++ << QUADSPI_BUF3CR_ADATSZ_SHIFT), base + QUADSPI_BUF3CR); + + /* We only use the buffer3 */ + writel(0, base + QUADSPI_BUF0IND); +@@ -596,6 +661,38 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q) + q->iobase + QUADSPI_BFGENCR); + } + ++/* This function was used to prepare and enable QSPI clock */ ++static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q) ++{ ++ int ret; ++ ++ ret = clk_prepare_enable(q->clk_en); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(q->clk); ++ if (ret) { ++ clk_disable_unprepare(q->clk_en); ++ return ret; ++ } ++ ++ if (needs_wakeup_wait_mode(q)) ++ pm_qos_add_request(&q->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, 0); ++ ++ return 0; ++} ++ ++/* This function was used to disable and unprepare QSPI clock */ ++static void fsl_qspi_clk_disable_unprep(struct fsl_qspi *q) ++{ ++ if (needs_wakeup_wait_mode(q)) ++ pm_qos_remove_request(&q->pm_qos_req); ++ ++ clk_disable_unprepare(q->clk); ++ clk_disable_unprepare(q->clk_en); ++ ++} ++ + /* We use this function to do some basic init for spi_nor_scan(). */ + static int fsl_qspi_nor_setup(struct fsl_qspi *q) + { +@@ -603,11 +700,23 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q) + u32 reg; + int ret; + +- /* the default frequency, we will change it in the future.*/ ++ /* disable and unprepare clock to avoid glitch pass to controller */ ++ fsl_qspi_clk_disable_unprep(q); ++ ++ /* the default frequency, we will change it in the future. */ + ret = clk_set_rate(q->clk, 66000000); + if (ret) + return ret; + ++ ret = fsl_qspi_clk_prep_enable(q); ++ if (ret) ++ return ret; ++ ++ /* Reset the module */ ++ writel(QUADSPI_MCR_SWRSTSD_MASK | QUADSPI_MCR_SWRSTHD_MASK, ++ base + QUADSPI_MCR); ++ udelay(1); ++ + /* Init the LUT table. */ + fsl_qspi_init_lut(q); + +@@ -625,6 +734,9 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q) + writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK, + base + QUADSPI_MCR); + ++ /* clear all interrupt status */ ++ writel(0xffffffff, q->iobase + QUADSPI_FR); ++ + /* enable the interrupt */ + writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER); + +@@ -636,13 +748,20 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q) + unsigned long rate = q->clk_rate; + int ret; + +- if (is_imx6sx_qspi(q)) ++ if (needs_4x_clock(q)) + rate *= 4; + ++ /* disable and unprepare clock to avoid glitch pass to controller */ ++ fsl_qspi_clk_disable_unprep(q); ++ + ret = clk_set_rate(q->clk, rate); + if (ret) + return ret; + ++ ret = fsl_qspi_clk_prep_enable(q); ++ if (ret) ++ return ret; ++ + /* Init the LUT table again. */ + fsl_qspi_init_lut(q); + +@@ -652,9 +771,11 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q) + return 0; + } + +-static struct of_device_id fsl_qspi_dt_ids[] = { ++static const struct of_device_id fsl_qspi_dt_ids[] = { + { .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, }, + { .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, }, ++ { .compatible = "fsl,imx7d-qspi", .data = (void *)&imx7d_data, }, ++ { .compatible = "fsl,imx6ul-qspi", .data = (void *)&imx6ul_data, }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); +@@ -677,8 +798,7 @@ static int fsl_qspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) + return 0; + } + +-static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, +- int write_enable) ++static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) + { + struct fsl_qspi *q = nor->priv; + int ret; +@@ -719,18 +839,45 @@ static int fsl_qspi_read(struct spi_nor *nor, loff_t from, + { + struct fsl_qspi *q = nor->priv; + u8 cmd = nor->read_opcode; +- int ret; + +- dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n", +- cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len); ++ /* if necessary,ioremap buffer before AHB read, */ ++ if (!q->ahb_addr) { ++ q->memmap_offs = q->chip_base_addr + from; ++ q->memmap_len = ++ len > QUADSPI_MIN_IOMAP ? len : QUADSPI_MIN_IOMAP; ++ ++ q->ahb_addr = ioremap_nocache( ++ q->memmap_phy + q->memmap_offs, ++ q->memmap_len); ++ if (!q->ahb_addr) { ++ dev_err(q->dev, "ioremap failed\n"); ++ return -ENOMEM; ++ } ++ /* ioremap if the data requested is out of range */ ++ } else if (q->chip_base_addr + from < q->memmap_offs ++ || q->chip_base_addr + from + len > ++ q->memmap_offs + q->memmap_len) { ++ iounmap(q->ahb_addr); ++ ++ q->memmap_offs = q->chip_base_addr + from; ++ q->memmap_len = ++ len > QUADSPI_MIN_IOMAP ? len : QUADSPI_MIN_IOMAP; ++ q->ahb_addr = ioremap_nocache( ++ q->memmap_phy + q->memmap_offs, ++ q->memmap_len); ++ if (!q->ahb_addr) { ++ dev_err(q->dev, "ioremap failed\n"); ++ return -ENOMEM; ++ } ++ } + +- /* Wait until the previous command is finished. */ +- ret = nor->wait_till_ready(nor); +- if (ret) +- return ret; ++ dev_dbg(q->dev, "cmd [%x],read from %p, len:%zd\n", ++ cmd, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs, ++ len); + + /* Read out the data directly from the AHB buffer.*/ +- memcpy(buf, q->ahb_base + q->chip_base_addr + from, len); ++ memcpy(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs, ++ len); + + *retlen += len; + return 0; +@@ -742,17 +889,7 @@ static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs) + int ret; + + dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n", +- nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs); +- +- /* Wait until finished previous write command. */ +- ret = nor->wait_till_ready(nor); +- if (ret) +- return ret; +- +- /* Send write enable, then erase commands. */ +- ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); +- if (ret) +- return ret; ++ nor->mtd.erasesize / 1024, q->chip_base_addr, (u32)offs); + + ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0); + if (ret) +@@ -767,26 +904,26 @@ static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops) + struct fsl_qspi *q = nor->priv; + int ret; + +- ret = clk_enable(q->clk_en); +- if (ret) +- return ret; ++ mutex_lock(&q->lock); + +- ret = clk_enable(q->clk); +- if (ret) { +- clk_disable(q->clk_en); +- return ret; +- } ++ ret = fsl_qspi_clk_prep_enable(q); ++ if (ret) ++ goto err_mutex; + + fsl_qspi_set_base_addr(q, nor); + return 0; ++ ++err_mutex: ++ mutex_unlock(&q->lock); ++ return ret; + } + + static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) + { + struct fsl_qspi *q = nor->priv; + +- clk_disable(q->clk); +- clk_disable(q->clk_en); ++ fsl_qspi_clk_disable_unprep(q); ++ mutex_unlock(&q->lock); + } + + static int fsl_qspi_probe(struct platform_device *pdev) +@@ -799,7 +936,6 @@ static int fsl_qspi_probe(struct platform_device *pdev) + struct spi_nor *nor; + struct mtd_info *mtd; + int ret, i = 0; +- bool has_second_chip = false; + const struct of_device_id *of_id = + of_match_device(fsl_qspi_dt_ids, &pdev->dev); + +@@ -811,89 +947,76 @@ static int fsl_qspi_probe(struct platform_device *pdev) + if (!q->nor_num || q->nor_num > FSL_QSPI_MAX_CHIP) + return -ENODEV; + ++ q->dev = dev; ++ q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data; ++ platform_set_drvdata(pdev, q); ++ + /* find the resources */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI"); + q->iobase = devm_ioremap_resource(dev, res); +- if (IS_ERR(q->iobase)) { +- ret = PTR_ERR(q->iobase); +- goto map_failed; +- } ++ if (IS_ERR(q->iobase)) ++ return PTR_ERR(q->iobase); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "QuadSPI-memory"); +- q->ahb_base = devm_ioremap_resource(dev, res); +- if (IS_ERR(q->ahb_base)) { +- ret = PTR_ERR(q->ahb_base); +- goto map_failed; ++ if (!devm_request_mem_region(dev, res->start, resource_size(res), ++ res->name)) { ++ dev_err(dev, "can't request region for resource %pR\n", res); ++ return -EBUSY; + } ++ + q->memmap_phy = res->start; + + /* find the clocks */ + q->clk_en = devm_clk_get(dev, "qspi_en"); +- if (IS_ERR(q->clk_en)) { +- ret = PTR_ERR(q->clk_en); +- goto map_failed; +- } ++ if (IS_ERR(q->clk_en)) ++ return PTR_ERR(q->clk_en); + + q->clk = devm_clk_get(dev, "qspi"); +- if (IS_ERR(q->clk)) { +- ret = PTR_ERR(q->clk); +- goto map_failed; +- } +- +- ret = clk_prepare_enable(q->clk_en); +- if (ret) { +- dev_err(dev, "can not enable the qspi_en clock\n"); +- goto map_failed; +- } ++ if (IS_ERR(q->clk)) ++ return PTR_ERR(q->clk); + +- ret = clk_prepare_enable(q->clk); ++ ret = fsl_qspi_clk_prep_enable(q); + if (ret) { +- clk_disable_unprepare(q->clk_en); +- dev_err(dev, "can not enable the qspi clock\n"); +- goto map_failed; ++ dev_err(dev, "can not enable the clock\n"); ++ goto clk_failed; + } + + /* find the irq */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) { +- dev_err(dev, "failed to get the irq\n"); ++ dev_err(dev, "failed to get the irq: %d\n", ret); + goto irq_failed; + } + + ret = devm_request_irq(dev, ret, + fsl_qspi_irq_handler, 0, pdev->name, q); + if (ret) { +- dev_err(dev, "failed to request irq.\n"); ++ dev_err(dev, "failed to request irq: %d\n", ret); + goto irq_failed; + } + +- q->dev = dev; +- q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data; +- platform_set_drvdata(pdev, q); +- + ret = fsl_qspi_nor_setup(q); + if (ret) + goto irq_failed; + + if (of_get_property(np, "fsl,qspi-has-second-chip", NULL)) +- has_second_chip = true; ++ q->has_second_chip = true; ++ ++ mutex_init(&q->lock); + + /* iterate the subnodes. */ + for_each_available_child_of_node(dev->of_node, np) { +- char modalias[40]; +- + /* skip the holes */ +- if (!has_second_chip) ++ if (!q->has_second_chip) + i *= 2; + + nor = &q->nor[i]; +- mtd = &q->mtd[i]; ++ mtd = &nor->mtd; + +- nor->mtd = mtd; + nor->dev = dev; ++ nor->flash_node = np; + nor->priv = q; +- mtd->priv = nor; + + /* fill the hooks */ + nor->read_reg = fsl_qspi_read_reg; +@@ -905,25 +1028,22 @@ static int fsl_qspi_probe(struct platform_device *pdev) + nor->prepare = fsl_qspi_prep; + nor->unprepare = fsl_qspi_unprep; + +- if (of_modalias_node(np, modalias, sizeof(modalias)) < 0) +- goto map_failed; +- + ret = of_property_read_u32(np, "spi-max-frequency", + &q->clk_rate); + if (ret < 0) +- goto map_failed; ++ goto mutex_failed; + + /* set the chip address for READID */ + fsl_qspi_set_base_addr(q, nor); + +- ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD); ++ ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD); + if (ret) +- goto map_failed; ++ goto mutex_failed; + + ppdata.of_node = np; + ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); + if (ret) +- goto map_failed; ++ goto mutex_failed; + + /* Set the correct NOR size now. */ + if (q->nor_size == 0) { +@@ -953,19 +1073,21 @@ static int fsl_qspi_probe(struct platform_device *pdev) + if (ret) + goto last_init_failed; + +- clk_disable(q->clk); +- clk_disable(q->clk_en); +- dev_info(dev, "QuadSPI SPI NOR flash driver\n"); ++ fsl_qspi_clk_disable_unprep(q); + return 0; + + last_init_failed: +- for (i = 0; i < q->nor_num; i++) +- mtd_device_unregister(&q->mtd[i]); +- ++ for (i = 0; i < q->nor_num; i++) { ++ /* skip the holes */ ++ if (!q->has_second_chip) ++ i *= 2; ++ mtd_device_unregister(&q->nor[i].mtd); ++ } ++mutex_failed: ++ mutex_destroy(&q->lock); + irq_failed: +- clk_disable_unprepare(q->clk); +- clk_disable_unprepare(q->clk_en); +-map_failed: ++ fsl_qspi_clk_disable_unprep(q); ++clk_failed: + dev_err(dev, "Freescale QuadSPI probe failed\n"); + return ret; + } +@@ -975,15 +1097,45 @@ static int fsl_qspi_remove(struct platform_device *pdev) + struct fsl_qspi *q = platform_get_drvdata(pdev); + int i; + +- for (i = 0; i < q->nor_num; i++) +- mtd_device_unregister(&q->mtd[i]); ++ for (i = 0; i < q->nor_num; i++) { ++ /* skip the holes */ ++ if (!q->has_second_chip) ++ i *= 2; ++ mtd_device_unregister(&q->nor[i].mtd); ++ } + + /* disable the hardware */ + writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); + writel(0x0, q->iobase + QUADSPI_RSER); + +- clk_unprepare(q->clk); +- clk_unprepare(q->clk_en); ++ mutex_destroy(&q->lock); ++ ++ if (q->ahb_addr) ++ iounmap(q->ahb_addr); ++ ++ return 0; ++} ++ ++static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ return 0; ++} ++ ++static int fsl_qspi_resume(struct platform_device *pdev) ++{ ++ int ret; ++ struct fsl_qspi *q = platform_get_drvdata(pdev); ++ ++ ret = fsl_qspi_clk_prep_enable(q); ++ if (ret) ++ return ret; ++ ++ fsl_qspi_nor_setup(q); ++ fsl_qspi_set_map_addr(q); ++ fsl_qspi_nor_setup_last(q); ++ ++ fsl_qspi_clk_disable_unprep(q); ++ + return 0; + } + +@@ -991,11 +1143,12 @@ static struct platform_driver fsl_qspi_driver = { + .driver = { + .name = "fsl-quadspi", + .bus = &platform_bus_type, +- .owner = THIS_MODULE, + .of_match_table = fsl_qspi_dt_ids, + }, + .probe = fsl_qspi_probe, + .remove = fsl_qspi_remove, ++ .suspend = fsl_qspi_suspend, ++ .resume = fsl_qspi_resume, + }; + module_platform_driver(fsl_qspi_driver); + +diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c +new file mode 100644 +index 0000000..d09e27a +--- /dev/null ++++ b/drivers/mtd/spi-nor/hisi-sfc.c +@@ -0,0 +1,588 @@ ++/* ++ * HiSilicon SPI Nor Flash Controller Driver ++ * ++ * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/clk.h> ++#include <linux/dma-mapping.h> ++#include <linux/mtd/mtd.h> ++#include <linux/of_address.h> ++#include <linux/of_platform.h> ++#include <linux/mtd/spi-nor.h> ++#include <mach/platform.h> ++#include <linux/mfd/hisi_fmc.h> ++ ++#include "../mtdcore.h" ++ ++#define FMC_OP_DMA 0x68 ++ ++struct hifmc_priv { ++ u32 chipselect; ++ u32 clkrate; ++ struct hifmc_host *host; ++}; ++ ++struct hifmc_host { ++ struct device *dev; ++ struct mutex *lock; ++ ++ void __iomem *regbase; ++ void __iomem *iobase; ++ struct clk *clk; ++ void *buffer; ++ dma_addr_t dma_buffer; ++ ++ struct spi_nor *nor[HIFMC_MAX_CHIP_NUM]; ++ struct hifmc_priv priv[HIFMC_MAX_CHIP_NUM]; ++ int num_chip; ++}; ++ ++/******************************************************************************/ ++static inline int wait_op_finish(struct hifmc_host *host) ++{ ++ unsigned int reg, timeout = FMC_WAIT_TIMEOUT; ++ ++ do { ++ reg = hifmc_readl(host, FMC_INT); ++ } while (!(reg & FMC_INT_OP_DONE) && --timeout); ++ ++ if (!timeout) { ++ dev_dbg(host->dev, "wait for operation finish timeout\n"); ++ return -EAGAIN; ++ } ++ ++ return 0; ++} ++ ++static int get_if_type(enum spi_nor_protocol mode) ++{ ++ enum hifmc_iftype if_type; ++ ++ switch (mode) { ++ case SNOR_PROTO_1_1_2: ++ if_type = IF_TYPE_DUAL; ++ break; ++ case SNOR_PROTO_1_2_2: ++ if_type = IF_TYPE_DIO; ++ break; ++ case SNOR_PROTO_1_1_4: ++ if_type = IF_TYPE_QUAD; ++ break; ++ case SNOR_PROTO_1_4_4: ++ if_type = IF_TYPE_QIO; ++ break; ++ case SNOR_PROTO_1_1_1: ++ default: ++ if_type = IF_TYPE_STD; ++ break; ++ } ++ ++ return if_type; ++} ++ ++/******************************************************************************/ ++static void spi_nor_switch_spi_type(struct hifmc_host *host) ++{ ++ unsigned int reg; ++ ++ reg = hifmc_readl(host, FMC_CFG); ++ reg &= ~FLASH_TYPE_SEL_MASK; ++ reg |= FMC_CFG_FLASH_SEL(0); ++ hifmc_writel(host, FMC_CFG, reg); ++} ++ ++/******************************************************************************/ ++static void hisi_spi_nor_init(struct hifmc_host *host) ++{ ++ unsigned int reg; ++ ++ /* switch the flash type to spi nor */ ++ spi_nor_switch_spi_type(host); ++ ++ /* set the boot mode to normal */ ++ reg = hifmc_readl(host, FMC_CFG); ++ if ((reg & FMC_CFG_OP_MODE_MASK) == FMC_CFG_OP_MODE_BOOT) { ++ reg |= FMC_CFG_OP_MODE(FMC_CFG_OP_MODE_NORMAL); ++ hifmc_writel(host, FMC_CFG, reg); ++ } ++ ++ /* set timming */ ++ reg = TIMING_CFG_TCSH(CS_HOLD_TIME) ++ | TIMING_CFG_TCSS(CS_SETUP_TIME) ++ | TIMING_CFG_TSHSL(CS_DESELECT_TIME); ++ hifmc_writel(host, FMC_SPI_TIMING_CFG, reg); ++} ++ ++/******************************************************************************/ ++static int hisi_spi_nor_prep(struct spi_nor *nor, enum spi_nor_ops ops) ++{ ++ int ret; ++ u32 clkrate; ++ struct hifmc_priv *priv = nor->priv; ++ struct hifmc_host *host = priv->host; ++ ++ mutex_lock(&fmc_switch_mutex); ++ mutex_lock(host->lock); ++ ++ ret = clk_prepare_enable(host->clk); ++ if (ret) ++ goto out; ++ ++ clkrate = min_t(u32, priv->clkrate, nor->clkrate); ++ ret = clk_set_rate(host->clk, clkrate); ++ if (ret) ++ goto out; ++ ++ spi_nor_switch_spi_type(host); ++ ++ return 0; ++ ++out: ++ mutex_unlock(host->lock); ++ return ret; ++} ++ ++/******************************************************************************/ ++static void hisi_spi_nor_unprep(struct spi_nor *nor, enum spi_nor_ops ops) ++{ ++ struct hifmc_priv *priv = nor->priv; ++ struct hifmc_host *host = priv->host; ++ ++ clk_disable_unprepare(host->clk); ++ mutex_unlock(host->lock); ++ mutex_unlock(&fmc_switch_mutex); ++} ++ ++/******************************************************************************/ ++static int hisi_spi_nor_op_reg(struct spi_nor *nor, u8 opcode, int len, u8 optype) ++{ ++ struct hifmc_priv *priv = nor->priv; ++ struct hifmc_host *host = priv->host; ++ u32 reg; ++ ++ reg = FMC_CMD_CMD1(opcode); ++ hifmc_writel(host, FMC_CMD, reg); ++ ++ reg = FMC_DATA_NUM_CNT(len); ++ hifmc_writel(host, FMC_DATA_NUM, reg); ++ ++ reg = OP_CFG_FM_CS(priv->chipselect); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ ++ hifmc_writel(host, FMC_INT_CLR, 0xff); ++ reg = FMC_OP_CMD1_EN; ++ if (optype == FMC_OP_READ) ++ reg |= FMC_OP_READ_DATA_EN; ++ else ++ reg |= FMC_OP_WRITE_DATA_EN; ++ reg |= FMC_OP_REG_OP_START; ++ hifmc_writel(host, FMC_OP, reg); ++ ++ return wait_op_finish(host); ++} ++ ++/******************************************************************************/ ++static int hisi_spi_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, ++ int len) ++{ ++ struct hifmc_priv *priv = nor->priv; ++ struct hifmc_host *host = priv->host; ++ int ret; ++ ++ ret = hisi_spi_nor_op_reg(nor, opcode, len, FMC_OP_READ); ++ if (ret) ++ return ret; ++ ++ memcpy_fromio(buf, host->iobase, len); ++ return 0; ++} ++ ++/******************************************************************************/ ++static int hisi_spi_nor_write_reg(struct spi_nor *nor, u8 opcode, ++ u8 *buf, int len) ++{ ++ struct hifmc_priv *priv = nor->priv; ++ struct hifmc_host *host = priv->host; ++ ++ if (len) ++ memcpy_toio(host->iobase, buf, len); ++ ++ return hisi_spi_nor_op_reg(nor, opcode, len, FMC_OP_WRITE); ++} ++ ++/******************************************************************************/ ++static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off, ++ dma_addr_t dma_buf, size_t len, u8 op_type) ++{ ++ struct hifmc_priv *priv = nor->priv; ++ struct hifmc_host *host = priv->host; ++ u8 if_type = 0, dummy = 0; ++ u32 reg; ++ ++ reg = hifmc_readl(host, FMC_CFG); ++ reg &= ~SPI_NOR_ADDR_MODE_MASK; ++ if (nor->addr_width == 4) ++ reg |= SPI_NOR_ADDR_MODE_4BYTES; ++ else ++ reg |= SPI_NOR_ADDR_MODE_3BYTES; ++ hifmc_writel(host, FMC_CFG, reg); ++ hifmc_writel(host, FMC_ADDRL, start_off); ++ hifmc_writel(host, FMC_DMA_SADDR_D0, dma_buf); ++ hifmc_writel(host, FMC_DMA_LEN, FMC_DMA_LEN_SET(len)); ++ ++ reg = OP_CFG_FM_CS(priv->chipselect); ++ if (op_type == FMC_OP_READ) { ++ if_type = get_if_type(nor->read_proto); ++ dummy = nor->read_dummy >> 3; ++ } else { ++ if_type = get_if_type(nor->write_proto); ++ } ++ reg |= OP_CFG_MEM_IF_TYPE(if_type) ++ | OP_CFG_DUMMY_NUM(dummy); ++ hifmc_writel(host, FMC_OP_CFG, reg); ++ ++ hifmc_writel(host, FMC_INT_CLR, 0xff); ++ reg = OP_CTRL_RW_OP(op_type) | OP_CTRL_DMA_OP_READY; ++ if (op_type == FMC_OP_READ) ++ reg |= OP_CTRL_RD_OPCODE(nor->read_opcode); ++ else ++ reg |= OP_CTRL_WR_OPCODE(nor->program_opcode); ++ hifmc_writel(host, FMC_OP_DMA, reg); ++ ++ return wait_op_finish(host); ++} ++ ++/******************************************************************************/ ++static int hisi_spi_nor_read(struct spi_nor *nor, loff_t from, size_t len, ++ size_t *retlen, u_char *read_buf) ++{ ++ struct hifmc_priv *priv = nor->priv; ++ struct hifmc_host *host = priv->host; ++ size_t offset; ++ int ret; ++ ++ for (offset = 0; offset < len; offset += HIFMC_DMA_MAX_LEN) { ++ size_t trans = min_t(size_t, HIFMC_DMA_MAX_LEN, len - offset); ++ ++ ret = hisi_spi_nor_dma_transfer(nor, from + offset, ++ host->dma_buffer, trans, FMC_OP_READ); ++ if (ret) { ++ dev_warn(nor->dev, "DMA read timeout\n"); ++ return ret; ++ } ++ memcpy(read_buf + offset, host->buffer, trans); ++ *retlen += trans; ++ } ++ ++ return 0; ++} ++/******************************************************************************/ ++static void hisi_spi_nor_write(struct spi_nor *nor, loff_t to, ++ size_t len, size_t *retlen, const u_char *write_buf) ++{ ++ struct hifmc_priv *priv = nor->priv; ++ struct hifmc_host *host = priv->host; ++ int ret; ++ ++ memcpy(host->buffer, write_buf, len); ++ ret = hisi_spi_nor_dma_transfer(nor, to, host->dma_buffer, ++ len, FMC_OP_WRITE); ++ if (ret) { ++ dev_warn(nor->dev, "DMA write timeout\n"); ++ return; ++ } ++ *retlen += len; ++} ++ ++/** ++ * parse partitions info and register spi flash device as mtd device. ++ */ ++static int hisi_snor_device_register(struct mtd_info *mtd) ++{ ++ int nr_parts = 0; ++ struct mtd_partition *parts = NULL; ++ static const char *part_probes[] = {"cmdlinepart", NULL, }; ++ ++ /* We do not add the whole spi flash as a mtdblock device, ++ * To avoid the number of nand partition +1. ++ */ ++ nr_parts = parse_mtd_partitions(mtd, part_probes, &parts, 0); ++ ++ return nr_parts ? mtd_device_register(mtd, parts, nr_parts) : nr_parts; ++} ++ ++/** ++ * Get spi flash device information and register it as a mtd device. ++ */ ++static int hisi_spi_nor_register(struct device_node *np, ++ struct hifmc_host *host) ++{ ++ struct device *dev = host->dev; ++ struct spi_nor *nor; ++ struct hifmc_priv *priv = &host->priv[host->num_chip]; ++ struct mtd_info *mtd; ++ int ret; ++ struct spi_nor_modes modes = { ++ .rd_modes = SNOR_MODE_SLOW, ++ .wr_modes = SNOR_MODE_1_1_1, ++ }; ++ ++ nor = devm_kzalloc(dev, sizeof(*nor), GFP_KERNEL); ++ if (!nor) ++ return -ENOMEM; ++ ++ nor->dev = dev; ++ nor->flash_node = np; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ ret = of_property_read_u32(np, "reg", &priv->chipselect); ++ if (ret) { ++ dev_err(dev, "There's no reg property for %s\n", ++ np->full_name); ++ return ret; ++ } ++ ++ if (priv->chipselect != host->num_chip) { ++ dev_warn(dev, " The CS: %d states in device trees isn't real " \ ++ "chipselect on board\n, using CS: %d instead. ", ++ priv->chipselect, host->num_chip); ++ priv->chipselect = host->num_chip; ++ } ++ ++ ret = of_property_read_u32(np, "spi-max-frequency", ++ &priv->clkrate); ++ if (ret) { ++ dev_err(dev, "There's no spi-max-frequency property for %s\n", ++ np->full_name); ++ return ret; ++ } ++ priv->host = host; ++ nor->priv = priv; ++ ++ nor->prepare = hisi_spi_nor_prep; ++ nor->unprepare = hisi_spi_nor_unprep; ++ nor->read_reg = hisi_spi_nor_read_reg; ++ nor->write_reg = hisi_spi_nor_write_reg; ++ nor->read = hisi_spi_nor_read; ++ nor->write = hisi_spi_nor_write; ++ ++ modes.rd_modes |= SNOR_MODE_1_1_1 ++ | SNOR_MODE_1_1_2 ++ | SNOR_MODE_1_2_2; ++#ifndef CONFIG_CLOSE_SPI_8PIN_4IO ++ modes.rd_modes |= SNOR_MODE_1_1_4 | SNOR_MODE_1_4_4; ++ modes.wr_modes |= SNOR_MODE_1_1_4 | SNOR_MODE_1_4_4; ++#endif ++ ret = spi_nor_scan(nor, NULL, &modes); ++ if (ret) ++ return ret; ++ ++ mtd = &nor->mtd; ++ mtd->name = np->name; ++ ret = hisi_snor_device_register(mtd); ++ if (ret) ++ return ret; ++ ++ /* current chipselect has scanned, to detect next chipselect */ ++ hifmc_cs_user[host->num_chip]++; ++ host->nor[host->num_chip] = nor; ++ host->num_chip++; ++ return 0; ++} ++ ++static void hisi_spi_nor_unregister_all(struct hifmc_host *host) ++{ ++ int i; ++ ++ for (i = 0; i < host->num_chip; i++) ++ mtd_device_unregister(&host->nor[i]->mtd); ++} ++ ++static int hisi_spi_nor_register_all(struct hifmc_host *host) ++{ ++ struct device *dev = host->dev; ++ struct device_node *np = NULL; ++ int ret; ++ ++ for_each_available_child_of_node(dev->of_node, np) { ++ if (hifmc_cs_user[host->num_chip]) { ++ dev_warn(dev, "Current CS(%d) is occupied.\n", ++ host->num_chip); ++ continue; ++ } ++ ret = hisi_spi_nor_register(np, host); ++ if (ret) ++ goto fail; ++ ++ if (host->num_chip == HIFMC_MAX_CHIP_NUM) { ++ dev_warn(dev, "Flash device number exceeds the " ++ "maximum chipselect number\n"); ++ break; ++ } ++ } ++ ++ return 0; ++ ++fail: ++ hisi_spi_nor_unregister_all(host); ++ return ret; ++} ++ ++/******************************************************************************/ ++static int hisi_spi_nor_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct hisi_fmc *fmc = dev_get_drvdata(dev->parent); ++ struct hifmc_host *host; ++ int ret; ++ ++ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); ++ if (!host) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, host); ++ host->dev = dev; ++ ++ host->regbase = fmc->regbase; ++ host->iobase = fmc->iobase; ++ host->clk = fmc->clk; ++ host->lock = &fmc->lock; ++ ++ host->buffer = dmam_alloc_coherent(dev, HIFMC_DMA_MAX_LEN, ++ &host->dma_buffer, GFP_KERNEL); ++ if (!host->buffer) ++ return -ENOMEM; ++ ++ clk_prepare_enable(host->clk); ++ hisi_spi_nor_init(host); ++ ret = hisi_spi_nor_register_all(host); ++ if (ret) ++ dev_warn(dev, "spi nor register fail!\n"); ++ ++ clk_disable_unprepare(host->clk); ++ ++ return ret; ++} ++ ++/******************************************************************************/ ++static int hisi_spi_nor_remove(struct platform_device *pdev) ++{ ++ struct hifmc_host *host = platform_get_drvdata(pdev); ++ ++ hisi_spi_nor_unregister_all(host); ++ clk_disable_unprepare(host->clk); ++ return 0; ++} ++ ++/******************************************************************************/ ++static void hisi_spi_nor_driver_shutdown(struct platform_device *pdev) ++{ ++ int i; ++ struct hifmc_host *host = platform_get_drvdata(pdev); ++ ++ if (!host) ++ return; ++ ++ mutex_lock(host->lock); ++ clk_prepare_enable(host->clk); ++ ++ spi_nor_switch_spi_type(host); ++ for (i = 0; i < host->num_chip; i++) ++ spi_nor_driver_shutdown(host->nor[i]); ++ ++ clk_disable_unprepare(host->clk); ++ mutex_unlock(host->lock); ++ dev_dbg(host->dev, "End of driver shutdown\n"); ++} ++ ++#ifdef CONFIG_PM ++/******************************************************************************/ ++static int hisi_spi_nor_driver_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ int i; ++ struct hifmc_host *host = platform_get_drvdata(pdev); ++ ++ if (!host) ++ return 0; ++ ++ mutex_lock(host->lock); ++ clk_prepare_enable(host->clk); ++ ++ spi_nor_switch_spi_type(host); ++ for (i = 0; i < host->num_chip; i++) ++ spi_nor_suspend(host->nor[i], state); ++ ++ clk_disable_unprepare(host->clk); ++ mutex_unlock(host->lock); ++ dev_dbg(host->dev, "End of suspend\n"); ++ ++ return 0; ++} ++ ++/******************************************************************************/ ++static int hisi_spi_nor_driver_resume(struct platform_device *pdev) ++{ ++ int i; ++ struct hifmc_host *host = platform_get_drvdata(pdev); ++ ++ if (!host) ++ return 0; ++ ++ mutex_lock(host->lock); ++ clk_prepare_enable(host->clk); ++ ++ spi_nor_switch_spi_type(host); ++ for (i = 0; i < host->num_chip; i++) ++ spi_nor_resume(host->nor[i]); ++ ++ mutex_unlock(host->lock); ++ dev_dbg(host->dev, "End of resume\n"); ++ ++ return 0; ++} ++#endif /* End of CONFIG_PM */ ++ ++/******************************************************************************/ ++static const struct of_device_id hisi_spi_nor_dt_ids[] = { ++ { .compatible = "hisilicon,hisi-sfc"}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, hisi_spi_nor_dt_ids); ++ ++/******************************************************************************/ ++static struct platform_driver hisi_spi_nor_driver = { ++ .driver = { ++ .name = "hisi-sfc", ++ .of_match_table = hisi_spi_nor_dt_ids, ++ }, ++ .probe = hisi_spi_nor_probe, ++ .remove = hisi_spi_nor_remove, ++ .shutdown = hisi_spi_nor_driver_shutdown, ++#ifdef CONFIG_PM ++ .suspend = hisi_spi_nor_driver_suspend, ++ .resume = hisi_spi_nor_driver_resume, ++#endif ++}; ++module_platform_driver(hisi_spi_nor_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("HiSilicon SPI Nor Flash Controller Driver"); +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index c51ee52..208e2a3 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -16,19 +16,77 @@ + #include <linux/device.h> + #include <linux/mutex.h> + #include <linux/math64.h> ++#include <linux/sizes.h> + +-#include <linux/mtd/cfi.h> + #include <linux/mtd/mtd.h> + #include <linux/of_platform.h> + #include <linux/spi/flash.h> + #include <linux/mtd/spi-nor.h> + + /* Define max times to check status register before we give up. */ +-#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ + +-#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) ++/* ++ * For everything but full-chip erase; probably could be much smaller, but kept ++ * around for safety for now ++ */ ++#define DEFAULT_READY_WAIT_JIFFIES (40UL * HZ) ++ ++/* ++ * For full-chip erase, calibrated to a 2MB flash (M25P16); should be scaled up ++ * for larger flash ++ */ ++#define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ) ++ ++#define SPI_NOR_MAX_ID_LEN 6 ++#define SPI_NOR_MAX_ADDR_WIDTH 4 ++ ++struct flash_info { ++ char *name; ++ ++ /* ++ * This array stores the ID bytes. ++ * The first three bytes are the JEDIC ID. ++ * JEDEC ID zero means "no ID" (mostly older chips). ++ */ ++ u8 id[SPI_NOR_MAX_ID_LEN]; ++ u8 id_len; ++ ++ /* The size listed here is what works with SPINOR_OP_SE, which isn't ++ * necessarily called a "sector" by the vendor. ++ */ ++ unsigned sector_size; ++ u16 n_sectors; ++ ++ u16 page_size; ++ u16 addr_width; ++ ++ u16 flags; ++#define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */ ++#define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */ ++#define SST_WRITE BIT(2) /* use SST byte programming */ ++#define SPI_NOR_NO_FR BIT(3) /* Can't do fastread */ ++#define SECT_4K_PMC BIT(4) /* SPINOR_OP_BE_4K_PMC works uniformly*/ ++#define SPI_NOR_DUAL_READ BIT(5) /* Flash supports Dual Read */ ++#define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */ ++#define USE_FSR BIT(7) /* use flag status register */ ++#define SPI_NOR_HAS_LOCK BIT(8) /* Flash supports lock/unlock via SR */ ++#define SPI_NOR_HAS_TB BIT(9) /* ++ * Flash SR has Top/Bottom (TB) protect ++ * bit. Must be used with ++ * SPI_NOR_HAS_LOCK. ++ */ ++#define SPI_NOR_4B_OPCODES BIT(10) /* ++ * Use dedicated 4byte address op codes ++ * to support memory size above 128Mib. ++ */ ++ ++ const struct spi_nor_basic_flash_parameter *params; ++ u32 clkrate; ++}; ++ ++#define JEDEC_MFR(info) ((info)->id[0]) + +-static const struct spi_device_id *spi_nor_match_id(const char *name); ++static const struct flash_info *spi_nor_match_id(const char *name); + + /* + * Read the status register, returning its value in the location +@@ -88,31 +146,13 @@ static int read_cr(struct spi_nor *nor) + } + + /* +- * Dummy Cycle calculation for different type of read. +- * It can be used to support more commands with +- * different dummy cycle requirements. +- */ +-static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) +-{ +- switch (nor->flash_read) { +- case SPI_NOR_FAST: +- case SPI_NOR_DUAL: +- case SPI_NOR_QUAD: +- return 1; +- case SPI_NOR_NORMAL: +- return 0; +- } +- return 0; +-} +- +-/* + * Write status register 1 byte + * Returns negative if error occurred. + */ + static inline int write_sr(struct spi_nor *nor, u8 val) + { + nor->cmd_buf[0] = val; +- return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); ++ return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1); + } + + /* +@@ -121,7 +161,7 @@ static inline int write_sr(struct spi_nor *nor, u8 val) + */ + static inline int write_enable(struct spi_nor *nor) + { +- return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); ++ return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0); + } + + /* +@@ -129,7 +169,7 @@ static inline int write_enable(struct spi_nor *nor) + */ + static inline int write_disable(struct spi_nor *nor) + { +- return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0); ++ return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0); + } + + static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) +@@ -137,88 +177,186 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) + return mtd->priv; + } + ++struct spi_nor_address_entry { ++ u8 src_opcode; ++ u8 dst_opcode; ++}; ++ ++static u8 spi_nor_convert_opcode(u8 opcode, ++ const struct spi_nor_address_entry *entries, ++ size_t num_entries) ++{ ++ int min, max; ++ ++ min = 0; ++ max = num_entries - 1; ++ while (min <= max) { ++ int mid = (min + max) >> 1; ++ const struct spi_nor_address_entry *entry = &entries[mid]; ++ ++ if (opcode == entry->src_opcode) ++ return entry->dst_opcode; ++ ++ if (opcode < entry->src_opcode) ++ max = mid - 1; ++ else ++ min = mid + 1; ++ } ++ ++ /* No conversion found */ ++ return opcode; ++} ++ ++static u8 spi_nor_3to4_opcode(u8 opcode) ++{ ++ /* MUST be sorted by 3byte opcode */ ++#define ENTRY_3TO4(_opcode) { _opcode, _opcode##_4B } ++ static const struct spi_nor_address_entry spi_nor_3to4_table[] = { ++ ENTRY_3TO4(SPINOR_OP_PP), /* 0x02 */ ++ ENTRY_3TO4(SPINOR_OP_READ), /* 0x03 */ ++ ENTRY_3TO4(SPINOR_OP_READ_FAST), /* 0x0b */ ++ ENTRY_3TO4(SPINOR_OP_BE_4K), /* 0x20 */ ++ ENTRY_3TO4(SPINOR_OP_PP_1_1_4), /* 0x32 */ ++ ENTRY_3TO4(SPINOR_OP_PP_1_4_4), /* 0x38 */ ++ ENTRY_3TO4(SPINOR_OP_READ_1_1_2), /* 0x3b */ ++ ENTRY_3TO4(SPINOR_OP_BE_32K), /* 0x52 */ ++ ENTRY_3TO4(SPINOR_OP_READ_1_1_4), /* 0x6b */ ++ ENTRY_3TO4(SPINOR_OP_READ_1_2_2), /* 0xbb */ ++ ENTRY_3TO4(SPINOR_OP_SE), /* 0xd8 */ ++ ENTRY_3TO4(SPINOR_OP_READ_1_4_4), /* 0xeb */ ++ }; ++#undef ENTRY_3TO4 ++ ++ return spi_nor_convert_opcode(opcode, spi_nor_3to4_table, ++ ARRAY_SIZE(spi_nor_3to4_table)); ++} ++ ++static void spi_nor_set_4byte_opcodes(struct spi_nor *nor, ++ const struct flash_info *info) ++{ ++ /* Do some manufacturer fixups first */ ++ switch (JEDEC_MFR(info)) { ++ case SNOR_MFR_SPANSION: ++ /* No small sector erase for 4-byte command set */ ++ nor->erase_opcode = SPINOR_OP_SE; ++ nor->mtd.erasesize = info->sector_size; ++ break; ++ ++ default: ++ break; ++ } ++ ++ nor->read_opcode = spi_nor_3to4_opcode(nor->read_opcode); ++ nor->program_opcode = spi_nor_3to4_opcode(nor->program_opcode); ++ nor->erase_opcode = spi_nor_3to4_opcode(nor->erase_opcode); ++} ++ + /* Enable/disable 4-byte addressing mode. */ +-static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) ++static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, ++ int enable) + { + int status; + bool need_wren = false; + u8 cmd; + +- switch (JEDEC_MFR(jedec_id)) { +- case CFI_MFR_ST: /* Micron, actually */ ++ switch (JEDEC_MFR(info)) { ++ case SNOR_MFR_MICRON: + /* Some Micron need WREN command; all will accept it */ + need_wren = true; +- case CFI_MFR_MACRONIX: +- case 0xEF /* winbond */: ++ case SNOR_MFR_MACRONIX: + if (need_wren) + write_enable(nor); + + cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; +- status = nor->write_reg(nor, cmd, NULL, 0, 0); ++ status = nor->write_reg(nor, cmd, NULL, 0); ++ + if (need_wren) + write_disable(nor); + + return status; ++ case SNOR_MFR_WINBOND: ++ if (enable) ++ return nor->write_reg(nor, SPINOR_OP_EN4B, NULL, 0); ++ else { ++ /* w25q256fvfg must send reset to disable 4 byte mode */ ++ nor->write_reg(nor, SPINOR_ENABLE_RESET, NULL, 0); ++ nor->write_reg(nor, SPINOR_OP_RESET, NULL, 0); ++ udelay(30); ++ } ++ return 0; + default: + /* Spansion style */ + nor->cmd_buf[0] = enable << 7; +- return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0); ++ return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1); + } + } +- +-static int spi_nor_wait_till_ready(struct spi_nor *nor) ++static inline int spi_nor_sr_ready(struct spi_nor *nor) + { +- unsigned long deadline; +- int sr; ++ int sr = read_sr(nor); + +- deadline = jiffies + MAX_READY_WAIT_JIFFIES; ++ if (sr < 0) ++ return sr; ++ else ++ return !(sr & SR_WIP); ++} + +- do { +- cond_resched(); ++static inline int spi_nor_fsr_ready(struct spi_nor *nor) ++{ ++ int fsr = read_fsr(nor); + +- sr = read_sr(nor); +- if (sr < 0) +- break; +- else if (!(sr & SR_WIP)) +- return 0; +- } while (!time_after_eq(jiffies, deadline)); ++ if (fsr < 0) ++ return fsr; ++ else ++ return fsr & FSR_READY; ++} + +- return -ETIMEDOUT; ++static int spi_nor_ready(struct spi_nor *nor) ++{ ++ int sr, fsr; ++ ++ sr = spi_nor_sr_ready(nor); ++ if (sr < 0) ++ return sr; ++ fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1; ++ if (fsr < 0) ++ return fsr; ++ return sr && fsr; + } + +-static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor) ++/* ++ * Service routine to read status register until ready, or timeout occurs. ++ * Returns non-zero if error. ++ */ ++static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor, ++ unsigned long timeout_jiffies) + { + unsigned long deadline; +- int sr; +- int fsr; ++ int timeout = 0, ret; ++ ++ deadline = jiffies + timeout_jiffies; + +- deadline = jiffies + MAX_READY_WAIT_JIFFIES; ++ while (!timeout) { ++ if (time_after_eq(jiffies, deadline)) ++ timeout = 1; ++ ++ ret = spi_nor_ready(nor); ++ if (ret < 0) ++ return ret; ++ if (ret) ++ return 0; + +- do { + cond_resched(); ++ } + +- sr = read_sr(nor); +- if (sr < 0) { +- break; +- } else if (!(sr & SR_WIP)) { +- fsr = read_fsr(nor); +- if (fsr < 0) +- break; +- if (fsr & FSR_READY) +- return 0; +- } +- } while (!time_after_eq(jiffies, deadline)); ++ dev_err(nor->dev, "flash operation timed out\n"); + + return -ETIMEDOUT; + } + +-/* +- * Service routine to read status register until ready, or timeout occurs. +- * Returns non-zero if error. +- */ +-static int wait_till_ready(struct spi_nor *nor) ++static int spi_nor_wait_till_ready(struct spi_nor *nor) + { +- return nor->wait_till_ready(nor); ++ return spi_nor_wait_till_ready_with_timeout(nor, ++ DEFAULT_READY_WAIT_JIFFIES); + } + + /* +@@ -228,19 +366,9 @@ static int wait_till_ready(struct spi_nor *nor) + */ + static int erase_chip(struct spi_nor *nor) + { +- int ret; +- +- dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10)); +- +- /* Wait until finished previous write command. */ +- ret = wait_till_ready(nor); +- if (ret) +- return ret; +- +- /* Send write enable, then erase commands. */ +- write_enable(nor); ++ dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10)); + +- return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); ++ return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0); + } + + static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) +@@ -268,6 +396,29 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) + } + + /* ++ * Initiate the erasure of a single sector ++ */ ++static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) ++{ ++ u8 buf[SPI_NOR_MAX_ADDR_WIDTH]; ++ int i; ++ ++ if (nor->erase) ++ return nor->erase(nor, addr); ++ ++ /* ++ * Default implementation, if driver doesn't have a specialized HW ++ * control ++ */ ++ for (i = nor->addr_width - 1; i >= 0; i--) { ++ buf[i] = addr & 0xff; ++ addr >>= 8; ++ } ++ ++ return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width); ++} ++ ++/* + * Erase an address range on the nor chip. The address range may extend + * one or more erase sectors. Return an error is there is a problem erasing. + */ +@@ -292,13 +443,36 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) + if (ret) + return ret; + ++#ifdef CONFIG_HISI_SPI_BLOCK_PROTECT ++ if ((nor->level) && (addr < nor->end_addr)) { ++ dev_err(nor->dev, "Error: The erase area was locked\n"); ++ return -EINVAL; ++ } ++#endif + /* whole-chip erase? */ + if (len == mtd->size) { ++ unsigned long timeout; ++ ++ write_enable(nor); ++ + if (erase_chip(nor)) { + ret = -EIO; + goto erase_err; + } + ++ /* ++ * Scale the timeout linearly with the size of the flash, with ++ * a minimum calibrated to an old 2MB flash. We could try to ++ * pull these from CFI/SFDP, but these values should be good ++ * enough for now. ++ */ ++ timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES, ++ CHIP_ERASE_2MB_READY_WAIT_JIFFIES * ++ (unsigned long)(mtd->size / SZ_2M)); ++ ret = spi_nor_wait_till_ready_with_timeout(nor, timeout); ++ if (ret) ++ goto erase_err; ++ + /* REVISIT in some cases we could speed up erasing large regions + * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up + * to use "small sector erase", but that's not always optimal. +@@ -307,276 +481,827 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) + /* "sector"-at-a-time erase */ + } else { + while (len) { +- if (nor->erase(nor, addr)) { +- ret = -EIO; ++ write_enable(nor); ++ ++ ret = spi_nor_erase_sector(nor, addr); ++ if (ret) + goto erase_err; +- } + + addr += mtd->erasesize; + len -= mtd->erasesize; ++ ++ ret = spi_nor_wait_till_ready(nor); ++ if (ret) ++ goto erase_err; + } + } + ++ write_disable(nor); ++ ++erase_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); + +- instr->state = MTD_ERASE_DONE; ++ instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return ret; +- +-erase_err: +- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); +- instr->state = MTD_ERASE_FAILED; +- return ret; + } + +-static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ++static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, ++ uint64_t *len) + { +- struct spi_nor *nor = mtd_to_spi_nor(mtd); +- uint32_t offset = ofs; +- uint8_t status_old, status_new; +- int ret = 0; ++ struct mtd_info *mtd = &nor->mtd; ++ u8 mask = SR_BP2 | SR_BP1 | SR_BP0; ++ int shift = ffs(mask) - 1; ++ int pow; ++ ++ if (!(sr & mask)) { ++ /* No protection */ ++ *ofs = 0; ++ *len = 0; ++ } else { ++ pow = ((sr & mask) ^ mask) >> shift; ++ *len = mtd->size >> pow; ++ if (nor->flags & SNOR_F_HAS_SR_TB && sr & SR_TB) ++ *ofs = 0; ++ else ++ *ofs = mtd->size - *len; ++ } ++} + +- ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK); +- if (ret) +- return ret; ++/* ++ * Return 1 if the entire region is locked (if @locked is true) or unlocked (if ++ * @locked is false); 0 otherwise ++ */ ++static int stm_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, ++ uint64_t len, u8 sr, bool locked) ++{ ++ loff_t lock_offs; ++ uint64_t lock_len; + +- /* Wait until finished previous command */ +- ret = wait_till_ready(nor); +- if (ret) +- goto err; ++ if (!len) ++ return 1; + +- status_old = read_sr(nor); ++ stm_get_locked_range(nor, sr, &lock_offs, &lock_len); + +- if (offset < mtd->size - (mtd->size / 2)) +- status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; +- else if (offset < mtd->size - (mtd->size / 4)) +- status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; +- else if (offset < mtd->size - (mtd->size / 8)) +- status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; +- else if (offset < mtd->size - (mtd->size / 16)) +- status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; +- else if (offset < mtd->size - (mtd->size / 32)) +- status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; +- else if (offset < mtd->size - (mtd->size / 64)) +- status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; ++ if (locked) ++ /* Requested range is a sub-range of locked range */ ++ return (ofs + len <= lock_offs + lock_len) ++ && (ofs >= lock_offs); + else +- status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; ++ /* Requested range does not overlap with locked range */ ++ return (ofs >= lock_offs + lock_len) ++ || (ofs + len <= lock_offs); ++} + +- /* Only modify protection if it will not unlock other areas */ +- if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) > +- (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { +- write_enable(nor); +- ret = write_sr(nor, status_new); +- if (ret) +- goto err; +- } ++static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, ++ u8 sr) ++{ ++ return stm_check_lock_status_sr(nor, ofs, len, sr, true); ++} + +-err: +- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); +- return ret; ++static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, ++ u8 sr) ++{ ++ return stm_check_lock_status_sr(nor, ofs, len, sr, false); + } + +-static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ++/* ++ * Lock a region of the flash. Compatible with ST Micro and similar flash. ++ * Supports the block protection bits BP{0,1,2} in the status register ++ * (SR). Does not support these features found in newer SR bitfields: ++ * - SEC: sector/block protect - only handle SEC=0 (block protect) ++ * - CMP: complement protect - only support CMP=0 (range is not complemented) ++ * ++ * Support for the following is provided conditionally for some flash: ++ * - TB: top/bottom protect ++ * ++ * Sample table portion for 8MB flash (Winbond w25q64fw): ++ * ++ * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion ++ * -------------------------------------------------------------------------- ++ * X | X | 0 | 0 | 0 | NONE | NONE ++ * 0 | 0 | 0 | 0 | 1 | 128 KB | Upper 1/64 ++ * 0 | 0 | 0 | 1 | 0 | 256 KB | Upper 1/32 ++ * 0 | 0 | 0 | 1 | 1 | 512 KB | Upper 1/16 ++ * 0 | 0 | 1 | 0 | 0 | 1 MB | Upper 1/8 ++ * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4 ++ * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2 ++ * X | X | 1 | 1 | 1 | 8 MB | ALL ++ * ------|-------|-------|-------|-------|---------------|------------------- ++ * 0 | 1 | 0 | 0 | 1 | 128 KB | Lower 1/64 ++ * 0 | 1 | 0 | 1 | 0 | 256 KB | Lower 1/32 ++ * 0 | 1 | 0 | 1 | 1 | 512 KB | Lower 1/16 ++ * 0 | 1 | 1 | 0 | 0 | 1 MB | Lower 1/8 ++ * 0 | 1 | 1 | 0 | 1 | 2 MB | Lower 1/4 ++ * 0 | 1 | 1 | 1 | 0 | 4 MB | Lower 1/2 ++ * ++ * Returns negative on errors, 0 on success. ++ */ ++static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) + { +- struct spi_nor *nor = mtd_to_spi_nor(mtd); +- uint32_t offset = ofs; +- uint8_t status_old, status_new; +- int ret = 0; ++ struct mtd_info *mtd = &nor->mtd; ++ int status_old, status_new; ++ u8 mask = SR_BP2 | SR_BP1 | SR_BP0; ++ u8 shift = ffs(mask) - 1, pow, val; ++ loff_t lock_len; ++ bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; ++ bool use_top; ++ int ret; + +- ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK); +- if (ret) +- return ret; ++ status_old = read_sr(nor); ++ if (status_old < 0) ++ return status_old; + +- /* Wait until finished previous command */ +- ret = wait_till_ready(nor); +- if (ret) +- goto err; ++ /* If nothing in our range is unlocked, we don't need to do anything */ ++ if (stm_is_locked_sr(nor, ofs, len, status_old)) ++ return 0; + +- status_old = read_sr(nor); ++ /* If anything below us is unlocked, we can't use 'bottom' protection */ ++ if (!stm_is_locked_sr(nor, 0, ofs, status_old)) ++ can_be_bottom = false; + +- if (offset+len > mtd->size - (mtd->size / 64)) +- status_new = status_old & ~(SR_BP2 | SR_BP1 | SR_BP0); +- else if (offset+len > mtd->size - (mtd->size / 32)) +- status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; +- else if (offset+len > mtd->size - (mtd->size / 16)) +- status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; +- else if (offset+len > mtd->size - (mtd->size / 8)) +- status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; +- else if (offset+len > mtd->size - (mtd->size / 4)) +- status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; +- else if (offset+len > mtd->size - (mtd->size / 2)) +- status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; +- else +- status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; ++ /* If anything above us is unlocked, we can't use 'top' protection */ ++ if (!stm_is_locked_sr(nor, ofs + len, mtd->size - (ofs + len), ++ status_old)) ++ can_be_top = false; + +- /* Only modify protection if it will not lock other areas */ +- if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) < +- (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { +- write_enable(nor); +- ret = write_sr(nor, status_new); +- if (ret) +- goto err; +- } ++ if (!can_be_bottom && !can_be_top) ++ return -EINVAL; + +-err: +- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK); +- return ret; +-} ++ /* Prefer top, if both are valid */ ++ use_top = can_be_top; + +-struct flash_info { +- /* JEDEC id zero means "no ID" (most older chips); otherwise it has +- * a high byte of zero plus three data bytes: the manufacturer id, +- * then a two byte device id. +- */ +- u32 jedec_id; +- u16 ext_id; ++ /* lock_len: length of region that should end up locked */ ++ if (use_top) ++ lock_len = mtd->size - ofs; ++ else ++ lock_len = ofs + len; + +- /* The size listed here is what works with SPINOR_OP_SE, which isn't +- * necessarily called a "sector" by the vendor. ++ /* ++ * Need smallest pow such that: ++ * ++ * 1 / (2^pow) <= (len / size) ++ * ++ * so (assuming power-of-2 size) we do: ++ * ++ * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) + */ +- unsigned sector_size; +- u16 n_sectors; ++ pow = ilog2(mtd->size) - ilog2(lock_len); ++ val = mask - (pow << shift); ++ if (val & ~mask) ++ return -EINVAL; ++ /* Don't "lock" with no region! */ ++ if (!(val & mask)) ++ return -EINVAL; + +- u16 page_size; +- u16 addr_width; ++ status_new = (status_old & ~mask & ~SR_TB) | val; + +- u16 flags; +-#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */ +-#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */ +-#define SST_WRITE 0x04 /* use SST byte programming */ +-#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */ +-#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */ +-#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ +-#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ +-#define USE_FSR 0x80 /* use flag status register */ +-}; ++ /* Disallow further writes if WP pin is asserted */ ++ status_new |= SR_SRWD; + +-#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ +- ((kernel_ulong_t)&(struct flash_info) { \ +- .jedec_id = (_jedec_id), \ +- .ext_id = (_ext_id), \ +- .sector_size = (_sector_size), \ +- .n_sectors = (_n_sectors), \ +- .page_size = 256, \ +- .flags = (_flags), \ +- }) ++ if (!use_top) ++ status_new |= SR_TB; + +-#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ +- ((kernel_ulong_t)&(struct flash_info) { \ +- .sector_size = (_sector_size), \ +- .n_sectors = (_n_sectors), \ +- .page_size = (_page_size), \ +- .addr_width = (_addr_width), \ +- .flags = (_flags), \ +- }) ++ /* Don't bother if they're the same */ ++ if (status_new == status_old) ++ return 0; + +-/* NOTE: double check command sets and memory organization when you add +- * more nor chips. This current list focusses on newer chips, which +- * have been converging on command sets which including JEDEC ID. +- */ +-static const struct spi_device_id spi_nor_ids[] = { +- /* Atmel -- some are (confusingly) marketed as "DataFlash" */ +- { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, +- { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, ++ /* Only modify protection if it will not unlock other areas */ ++ if ((status_new & mask) < (status_old & mask)) ++ return -EINVAL; + +- { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, +- { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, +- { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, ++ write_enable(nor); ++ ret = write_sr(nor, status_new); ++ if (ret) ++ return ret; ++ return spi_nor_wait_till_ready(nor); ++} + +- { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, +- { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, +- { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, +- { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, ++/* ++ * Unlock a region of the flash. See stm_lock() for more info ++ * ++ * Returns negative on errors, 0 on success. ++ */ ++static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) ++{ ++ struct mtd_info *mtd = &nor->mtd; ++ int status_old, status_new; ++ u8 mask = SR_BP2 | SR_BP1 | SR_BP0; ++ u8 shift = ffs(mask) - 1, pow, val; ++ loff_t lock_len; ++ bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; ++ bool use_top; ++ int ret; + +- { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, ++ status_old = read_sr(nor); ++ if (status_old < 0) ++ return status_old; + +- /* EON -- en25xxx */ +- { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, +- { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, +- { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, +- { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, +- { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, +- { "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) }, +- { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, ++ /* If nothing in our range is locked, we don't need to do anything */ ++ if (stm_is_unlocked_sr(nor, ofs, len, status_old)) ++ return 0; + +- /* ESMT */ +- { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) }, ++ /* If anything below us is locked, we can't use 'top' protection */ ++ if (!stm_is_unlocked_sr(nor, 0, ofs, status_old)) ++ can_be_top = false; + +- /* Everspin */ +- { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, +- { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, ++ /* If anything above us is locked, we can't use 'bottom' protection */ ++ if (!stm_is_unlocked_sr(nor, ofs + len, mtd->size - (ofs + len), ++ status_old)) ++ can_be_bottom = false; + +- /* GigaDevice */ +- { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, +- { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, ++ if (!can_be_bottom && !can_be_top) ++ return -EINVAL; + +- /* Intel/Numonyx -- xxxs33b */ +- { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, +- { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, +- { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, ++ /* Prefer top, if both are valid */ ++ use_top = can_be_top; + +- /* Macronix */ +- { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, +- { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, +- { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, +- { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, +- { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, +- { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, +- { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, +- { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, +- { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, +- { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, +- { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, +- { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) }, +- { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, +- +- /* Micron */ +- { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, +- { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, +- { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, +- { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, +- { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, +- { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) }, +- { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) }, ++ /* lock_len: length of region that should remain locked */ ++ if (use_top) ++ lock_len = mtd->size - (ofs + len); ++ else ++ lock_len = ofs; + +- /* PMC */ +- { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, +- { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, +- { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, ++ /* ++ * Need largest pow such that: ++ * ++ * 1 / (2^pow) >= (len / size) ++ * ++ * so (assuming power-of-2 size) we do: ++ * ++ * pow = floor(log2(size / len)) = log2(size) - ceil(log2(len)) ++ */ ++ pow = ilog2(mtd->size) - order_base_2(lock_len); ++ if (lock_len == 0) { ++ val = 0; /* fully unlocked */ ++ } else { ++ val = mask - (pow << shift); ++ /* Some power-of-two sizes are not supported */ ++ if (val & ~mask) ++ return -EINVAL; ++ } ++ ++ status_new = (status_old & ~mask & ~SR_TB) | val; ++ ++ /* Don't protect status register if we're fully unlocked */ ++ if (lock_len == mtd->size) ++ status_new &= ~SR_SRWD; ++ ++ if (!use_top) ++ status_new |= SR_TB; ++ ++ /* Don't bother if they're the same */ ++ if (status_new == status_old) ++ return 0; ++ ++ /* Only modify protection if it will not lock other areas */ ++ if ((status_new & mask) > (status_old & mask)) ++ return -EINVAL; ++ ++ write_enable(nor); ++ ret = write_sr(nor, status_new); ++ if (ret) ++ return ret; ++ return spi_nor_wait_till_ready(nor); ++} ++ ++/* ++ * Check if a region of the flash is (completely) locked. See stm_lock() for ++ * more info. ++ * ++ * Returns 1 if entire region is locked, 0 if any portion is unlocked, and ++ * negative on errors. ++ */ ++static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) ++{ ++ int status; ++ ++ status = read_sr(nor); ++ if (status < 0) ++ return status; ++ ++ return stm_is_locked_sr(nor, ofs, len, status); ++} ++ ++static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ++{ ++ struct spi_nor *nor = mtd_to_spi_nor(mtd); ++ int ret; ++ ++ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK); ++ if (ret) ++ return ret; ++ ++ ret = nor->flash_lock(nor, ofs, len); ++ ++ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK); ++ return ret; ++} ++ ++static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ++{ ++ struct spi_nor *nor = mtd_to_spi_nor(mtd); ++ int ret; ++ ++ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK); ++ if (ret) ++ return ret; ++ ++ ret = nor->flash_unlock(nor, ofs, len); ++ ++ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); ++ return ret; ++} ++ ++static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) ++{ ++ struct spi_nor *nor = mtd_to_spi_nor(mtd); ++ int ret; ++ ++ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK); ++ if (ret) ++ return ret; ++ ++ ret = nor->flash_is_locked(nor, ofs, len); ++ ++ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); ++ return ret; ++} ++ ++#define SNOR_RD_MODES \ ++ (SNOR_MODE_SLOW | \ ++ SNOR_MODE_1_1_1 | \ ++ SNOR_MODE_1_1_2 | \ ++ SNOR_MODE_1_2_2 | \ ++ SNOR_MODE_1_1_4 | \ ++ SNOR_MODE_1_4_4) ++ ++#define SNOR_WR_MODES \ ++ (SNOR_MODE_1_1_1 | \ ++ SNOR_MODE_1_1_4) ++ ++static int spansion_quad_enable(struct spi_nor *nor); ++static int macronix_quad_enable(struct spi_nor *nor); ++static int gd_quad_enable(struct spi_nor *nor); ++ ++#define SNOR_EON_RD_MODES \ ++ (SNOR_MODE_SLOW | \ ++ SNOR_MODE_1_1_1 | \ ++ SNOR_MODE_1_1_2 | \ ++ SNOR_MODE_1_2_2) ++ ++#define SNOR_EON_WR_MODES \ ++ (SNOR_MODE_1_1_1) ++ ++static const struct spi_nor_basic_flash_parameter eon_params = { ++ .rd_modes = SNOR_EON_RD_MODES, ++ .reads[SNOR_MIDX_SLOW] = SNOR_OP_READ(0, 0, SPINOR_OP_READ), ++ .reads[SNOR_MIDX_1_1_1] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_FAST), ++ .reads[SNOR_MIDX_1_1_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_2), ++ .reads[SNOR_MIDX_1_2_2] = SNOR_OP_READ(8, 0, SPINOR_OP_READ_1_2_2), ++ .reads[SNOR_MIDX_1_1_4] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_4), ++ .reads[SNOR_MIDX_1_4_4] = SNOR_OP_READ(8, 16, SPINOR_OP_READ_1_4_4), ++ ++ .wr_modes = SNOR_EON_WR_MODES, ++ .page_programs[SNOR_MIDX_1_1_1] = SPINOR_OP_PP, ++ ++ .erase_types[0] = SNOR_OP_ERASE_64K(SPINOR_OP_SE), ++ ++}; ++ ++static const struct spi_nor_basic_flash_parameter esmt_params = { ++ .rd_modes = SNOR_RD_MODES, ++ .reads[SNOR_MIDX_SLOW] = SNOR_OP_READ(0, 0, SPINOR_OP_READ), ++ .reads[SNOR_MIDX_1_1_1] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_FAST), ++ .reads[SNOR_MIDX_1_1_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_2), ++ .reads[SNOR_MIDX_1_2_2] = SNOR_OP_READ(8, 0, SPINOR_OP_READ_1_2_2), ++ .reads[SNOR_MIDX_1_1_4] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_4), ++ .reads[SNOR_MIDX_1_4_4] = SNOR_OP_READ(8, 16, SPINOR_OP_READ_1_4_4), ++ ++ .wr_modes = SNOR_WR_MODES, ++ .page_programs[SNOR_MIDX_1_1_1] = SPINOR_OP_PP, ++ .page_programs[SNOR_MIDX_1_1_4] = SPINOR_OP_PP_1_1_4, ++ ++ .erase_types[0] = SNOR_OP_ERASE_64K(SPINOR_OP_SE), ++ ++ .enable_quad_io = macronix_quad_enable, ++ ++}; ++ ++#define SNOR_PARAGON_WR_MODES \ ++ (SNOR_MODE_1_1_1) ++ ++static const struct spi_nor_basic_flash_parameter paragon_params = { ++ .rd_modes = SNOR_RD_MODES, ++ .reads[SNOR_MIDX_SLOW] = SNOR_OP_READ(0, 0, SPINOR_OP_READ), ++ .reads[SNOR_MIDX_1_1_1] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_FAST), ++ .reads[SNOR_MIDX_1_1_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_2), ++ .reads[SNOR_MIDX_1_2_2] = SNOR_OP_READ(8, 0, SPINOR_OP_READ_1_2_2), ++ .reads[SNOR_MIDX_1_1_4] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_4), ++ .reads[SNOR_MIDX_1_4_4] = SNOR_OP_READ(8, 16, SPINOR_OP_READ_1_4_4), ++ ++ .wr_modes = SNOR_PARAGON_WR_MODES, ++ .page_programs[SNOR_MIDX_1_1_1] = SPINOR_OP_PP, ++ ++ .erase_types[0] = SNOR_OP_ERASE_64K(SPINOR_OP_SE), ++ ++ .enable_quad_io = spansion_quad_enable, ++ ++}; ++ ++static const struct spi_nor_basic_flash_parameter gd_params = { ++ .rd_modes = SNOR_RD_MODES, ++ .reads[SNOR_MIDX_SLOW] = SNOR_OP_READ(0, 0, SPINOR_OP_READ), ++ .reads[SNOR_MIDX_1_1_1] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_FAST), ++ .reads[SNOR_MIDX_1_1_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_2), ++ .reads[SNOR_MIDX_1_2_2] = SNOR_OP_READ(8, 0, SPINOR_OP_READ_1_2_2), ++ .reads[SNOR_MIDX_1_1_4] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_4), ++ .reads[SNOR_MIDX_1_4_4] = SNOR_OP_READ(8, 16, SPINOR_OP_READ_1_4_4), ++ ++ .wr_modes = SNOR_WR_MODES, ++ .page_programs[SNOR_MIDX_1_1_1] = SPINOR_OP_PP, ++ .page_programs[SNOR_MIDX_1_1_4] = SPINOR_OP_PP_1_1_4, ++ ++ .erase_types[0] = SNOR_OP_ERASE_64K(SPINOR_OP_SE), ++ ++ .enable_quad_io = gd_quad_enable, ++ ++}; ++ ++static const struct spi_nor_basic_flash_parameter winbond_params = { ++ .rd_modes = SNOR_RD_MODES, ++ .reads[SNOR_MIDX_SLOW] = SNOR_OP_READ(0, 0, SPINOR_OP_READ), ++ .reads[SNOR_MIDX_1_1_1] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_FAST), ++ .reads[SNOR_MIDX_1_1_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_2), ++ .reads[SNOR_MIDX_1_2_2] = SNOR_OP_READ(8, 0, SPINOR_OP_READ_1_2_2), ++ .reads[SNOR_MIDX_1_1_4] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_4), ++ .reads[SNOR_MIDX_1_4_4] = SNOR_OP_READ(8, 16, SPINOR_OP_READ_1_4_4), ++ ++ .wr_modes = SNOR_WR_MODES, ++ .page_programs[SNOR_MIDX_1_1_1] = SPINOR_OP_PP, ++ .page_programs[SNOR_MIDX_1_1_4] = SPINOR_OP_PP_1_1_4, ++ ++ .erase_types[0] = SNOR_OP_ERASE_64K(SPINOR_OP_SE), ++ ++ .enable_quad_io = spansion_quad_enable, ++ ++}; ++ ++static const struct spi_nor_basic_flash_parameter spansion_params = { ++ .rd_modes = SNOR_RD_MODES, ++ .reads[SNOR_MIDX_SLOW] = SNOR_OP_READ(0, 0, SPINOR_OP_READ), ++ .reads[SNOR_MIDX_1_1_1] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_FAST), ++ .reads[SNOR_MIDX_1_1_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_2), ++ .reads[SNOR_MIDX_1_2_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_2_2), ++ .reads[SNOR_MIDX_1_1_4] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_4), ++ .reads[SNOR_MIDX_1_4_4] = SNOR_OP_READ(8, 16, SPINOR_OP_READ_1_4_4), ++ ++ .wr_modes = SNOR_WR_MODES, ++ .page_programs[SNOR_MIDX_1_1_1] = SPINOR_OP_PP, ++ .page_programs[SNOR_MIDX_1_1_4] = SPINOR_OP_PP_1_1_4, ++ ++ .erase_types[0] = SNOR_OP_ERASE_64K(SPINOR_OP_SE), ++ ++ .enable_quad_io = spansion_quad_enable, ++ ++}; ++ ++#define SNOR_MXIC_WR_MODES \ ++ (SNOR_MODE_1_1_1 | \ ++ SNOR_MODE_1_4_4) ++ ++static const struct spi_nor_basic_flash_parameter mxic_params = { ++ .rd_modes = SNOR_RD_MODES, ++ .reads[SNOR_MIDX_SLOW] = SNOR_OP_READ(0, 0, SPINOR_OP_READ), ++ .reads[SNOR_MIDX_1_1_1] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_FAST), ++ .reads[SNOR_MIDX_1_1_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_2), ++ .reads[SNOR_MIDX_1_2_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_2_2), ++ .reads[SNOR_MIDX_1_1_4] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_4), ++ .reads[SNOR_MIDX_1_4_4] = SNOR_OP_READ(8, 16, SPINOR_OP_READ_1_4_4), ++ ++ .wr_modes = SNOR_MXIC_WR_MODES, ++ .page_programs[SNOR_MIDX_1_1_1] = SPINOR_OP_PP, ++ .page_programs[SNOR_MIDX_1_4_4] = SPINOR_OP_PP_1_4_4, ++ ++ .erase_types[0] = SNOR_OP_ERASE_64K(SPINOR_OP_SE), ++ ++ .enable_quad_io = macronix_quad_enable, ++ ++}; ++ ++static const struct spi_nor_basic_flash_parameter micron_params = { ++ .rd_modes = SNOR_RD_MODES, ++ .reads[SNOR_MIDX_SLOW] = SNOR_OP_READ(0, 0, SPINOR_OP_READ), ++ .reads[SNOR_MIDX_1_1_1] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_FAST), ++ .reads[SNOR_MIDX_1_1_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_2), ++ .reads[SNOR_MIDX_1_2_2] = SNOR_OP_READ(8, 8, SPINOR_OP_READ_1_2_2), ++ .reads[SNOR_MIDX_1_1_4] = SNOR_OP_READ(1, 7, SPINOR_OP_READ_1_1_4), ++ .reads[SNOR_MIDX_1_4_4] = SNOR_OP_READ(0, 40, SPINOR_OP_READ_1_4_4), ++ ++ .wr_modes = SNOR_WR_MODES, ++ .page_programs[SNOR_MIDX_1_1_1] = SPINOR_OP_PP, ++ .page_programs[SNOR_MIDX_1_1_4] = SPINOR_OP_PP_1_1_4, ++ ++ .erase_types[0] = SNOR_OP_ERASE_64K(SPINOR_OP_SE), ++ ++}; ++ ++static const struct spi_nor_basic_flash_parameter micron_4k_params = { ++ .rd_modes = SNOR_RD_MODES, ++ .reads[SNOR_MIDX_SLOW] = SNOR_OP_READ(0, 0, SPINOR_OP_READ), ++ .reads[SNOR_MIDX_1_1_1] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_FAST), ++ .reads[SNOR_MIDX_1_1_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_2), ++ .reads[SNOR_MIDX_1_2_2] = SNOR_OP_READ(1, 7, SPINOR_OP_READ_1_2_2), ++ .reads[SNOR_MIDX_1_1_4] = SNOR_OP_READ(1, 7, SPINOR_OP_READ_1_1_4), ++ .reads[SNOR_MIDX_1_4_4] = SNOR_OP_READ(1, 9, SPINOR_OP_READ_1_4_4), ++ ++ .wr_modes = SNOR_WR_MODES, ++ .page_programs[SNOR_MIDX_1_1_1] = SPINOR_OP_PP, ++ .page_programs[SNOR_MIDX_1_1_4] = SPINOR_OP_PP_1_1_4, ++ ++ .erase_types[0] = SNOR_OP_ERASE_64K(SPINOR_OP_SE), ++ .erase_types[1] = SNOR_OP_ERASE_4K(SPINOR_OP_BE_4K), ++}; ++ ++#define PARAMS(_name) .params = &_name##_params ++ ++/* Used when the "_ext_id" is two bytes at most */ ++#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ ++ .id = { \ ++ ((_jedec_id) >> 16) & 0xff, \ ++ ((_jedec_id) >> 8) & 0xff, \ ++ (_jedec_id) & 0xff, \ ++ ((_ext_id) >> 8) & 0xff, \ ++ (_ext_id) & 0xff, \ ++ }, \ ++ .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \ ++ .sector_size = (_sector_size), \ ++ .n_sectors = (_n_sectors), \ ++ .page_size = 256, \ ++ .flags = (_flags) ++ ++#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ ++ .id = { \ ++ ((_jedec_id) >> 16) & 0xff, \ ++ ((_jedec_id) >> 8) & 0xff, \ ++ (_jedec_id) & 0xff, \ ++ ((_ext_id) >> 16) & 0xff, \ ++ ((_ext_id) >> 8) & 0xff, \ ++ (_ext_id) & 0xff, \ ++ }, \ ++ .id_len = 6, \ ++ .sector_size = (_sector_size), \ ++ .n_sectors = (_n_sectors), \ ++ .page_size = 256, \ ++ .flags = (_flags), ++ ++#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ ++ .sector_size = (_sector_size), \ ++ .n_sectors = (_n_sectors), \ ++ .page_size = (_page_size), \ ++ .addr_width = (_addr_width), \ ++ .flags = (_flags), ++ ++/* Different from spi-max-frequency in DTS, the clk here stands for the clock ++ * rate on SPI interface, it is half of the FMC CRG configuration */ ++#define CLK_MHZ_2X(clk) .clkrate = (clk * 2000000), ++ ++/* NOTE: double check command sets and memory organization when you add ++ * more nor chips. This current list focusses on newer chips, which ++ * have been converging on command sets which including JEDEC ID. ++ * ++ * All newly added entries should describe *hardware* and should use SECT_4K ++ * (or SECT_4K_PMC) if hardware supports erasing 4 KiB sectors. For usage ++ * scenarios excluding small sectors there is config option that can be ++ * disabled: CONFIG_MTD_SPI_NOR_USE_4K_SECTORS. ++ * For historical (and compatibility) reasons (before we got above config) some ++ * old entries may be missing 4K flag. ++ */ ++ ++/******* SPI Nor ID Table ************************************************** ++ * Version Manufacturer Chip Name Chipsize Block Vol Operation ++ * 1.0 Macronix/MXIC MX25L1606E 2M 64K 3V3 ++ * Macronix/MXIC MX25L6436F 8M 64K 3V3 ++ * Macronix/MXIC MX25R6435F 8M 64K 1V8/3V3 Add 14chips ++ * Macronix/MXIC MX25U6435F 8M 64K 1V8 ++ * Macronix/MXIC MX25U12835F 16M 64K 1V8 ++ * Macronix/MXIC MX25F128XXX 16M 64K 3V3 ++ * Macronix/MXIC MX25U25635F/45G 32M 64K 1V8 25645G-DTR ++ * Macronix/MXIC MX25L(256/257) 32M 64K 3V3 25645G-DTR ++ * Spansion S25FL129P1 16M 64K 3V3 ++ * Spansion S25FL256S 32M 64K 3V3 ++ * Micron N25Q064A 8M 64K 3V3 ++ * Micron N25QL064A 8M 64K 3V3 ++ * Micron N25Q128A11/MT25QU128AB 16M 64K 1V8 ++ * Micron N25QL128A 16M 64K 3V3 ++ * Micron MT25QU256A 32M 64K 1V8 ++ * Micron MT25Q256A 32M 64K 3V3 ++ * Winbond W25Q16(B/C)V/S25FL016K 2M 64K 3V3 ++ * Winbond W25Q32(B/F)V 4M 64K 3V3 ++ * Winbond W25Q32FW 4M 64K 1V8 ++ * Winbond W25Q64FW 8M 64K 1V8 ++ * Winbond W25Q64FV 8M 64K 3V3 ++ * Winbond W25Q128FW 16M 64K 1V8 ++ * Winbond W25Q128(B/F)V 16M 64K 3V3 ++ * Winbond W25Q128JV 16M 64K 3V3 DTR ++ * ESMT/CFEON EN25Q32B 4M 64K 3V3 ++ * ESMT/CFEON EN25Q64 8M 64K 3V3 ++ * ESMT/CFEON EN25Q128 16M 64K 3V3 ++ * ESMT/CFEON F25L64QA 8M 64K 3V3 ++ * GD GD25Q64 8M 64K 3V3 ++ * GD GD25LQ128 16M 64K 1V8 ++ * GD GD25Q128 16M 64K 3V3 ++ * GD GD25LQ64C 8M 64K 1V8 ++ * GD GD25Q32 4M 64K 3V3 ++ * Paragon PN25F16S 2M 64K 3V3 ++ * Paragon PN25F32S 4M 64K 3V3 ++ *****************************************************************************/ ++static const struct flash_info spi_nor_ids[] = { ++ /* Atmel -- some are (confusingly) marketed as "DataFlash" */ ++ { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, ++ { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, ++ ++ { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, ++ { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, ++ { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, ++ ++ { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, ++ { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, ++ { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, ++ { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, ++ ++ { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, ++ ++ /* EON -- en25xxx */ ++ { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, ++ { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, ++ { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, ++ SPI_NOR_QUAD_READ), PARAMS(eon), CLK_MHZ_2X(104) }, ++ { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, ++ { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(eon), CLK_MHZ_2X(104) }, ++ { "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256, ++ SPI_NOR_QUAD_READ), PARAMS(eon), CLK_MHZ_2X(104) }, ++ { "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) }, ++ { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, ++ { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) }, ++ ++ /* ESMT */ ++ { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) }, ++ { "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128, ++ SPI_NOR_QUAD_READ), PARAMS(esmt), CLK_MHZ_2X(84) }, ++ ++ /* Everspin */ ++ { "mr25h256", CAT25_INFO(32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE ++ | SPI_NOR_NO_FR) }, ++ { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE ++ | SPI_NOR_NO_FR) }, ++ ++ /* Fujitsu */ ++ { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) }, ++ ++ /* GigaDevice 3.3V */ ++ { "gd25q16c", INFO(0xc84015, 0, 64 * 1024, 32, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(gd), CLK_MHZ_2X(120) }, ++ { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(gd), CLK_MHZ_2X(120) }, ++ { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(gd), CLK_MHZ_2X(120) }, ++ { "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(gd), CLK_MHZ_2X(80) }, ++ /* GigaDevice 1.8V */ ++ { "gd25lq64", INFO(0xc86017, 0, 64 * 1024, 128, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(gd), CLK_MHZ_2X(133) }, ++ { "gd25lq128", INFO(0xc86018, 0, 64 * 1024, 256, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(gd), CLK_MHZ_2X(133) }, ++ ++ /* Intel/Numonyx -- xxxs33b */ ++ { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, ++ { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, ++ { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, ++ ++ /* ISSI */ ++ { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2, SECT_4K) }, ++ ++ /* Macronix/MXIC 3.3V */ ++ { "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) }, ++ { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, ++ { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, ++ { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, ++ { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K ++ | SPI_NOR_DUAL_READ), CLK_MHZ_2X(80) }, ++ { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, ++ { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, ++ { "mx25l6436f", INFO(0xc22017, 0, 64 * 1024, 128, ++ SPI_NOR_QUAD_READ), PARAMS(mxic), CLK_MHZ_2X(133) }, ++ { "mx25l12835f", INFO(0xc22018, 0, 64 * 1024, 256, ++ SPI_NOR_QUAD_READ), PARAMS(mxic), CLK_MHZ_2X(84) }, ++ { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, ++ { "mx25l25635f", INFO(0xc22019, 0, 64 * 1024, 512, ++ SPI_NOR_QUAD_READ), PARAMS(mxic), CLK_MHZ_2X(84) }, ++ { "mx25l25673g", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_QUAD_READ ++ | SPI_NOR_4B_OPCODES) }, ++ { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, ++ { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ)}, ++ { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ)}, ++ { "mx25v1635f", INFO(0xc22315, 0, 64 * 1024, 32 , ++ SPI_NOR_QUAD_READ), PARAMS(mxic), CLK_MHZ_2X(80) }, ++ /* Macronix/MXIC Wide Voltage Range 1.65~3.6V */ ++ { "mx25r6435f", INFO(0xc22817, 0, 64 * 1024, 128, ++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ), CLK_MHZ_2X(80) }, ++ /* Macronix/MXIC 1.8V */ ++ { "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(mxic), CLK_MHZ_2X(84) }, ++ { "mx25u12835f", INFO(0xc22538, 0, 64 * 1024, 256, ++ SPI_NOR_QUAD_READ), PARAMS(mxic), CLK_MHZ_2X(84) }, ++ { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, ++ SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES), PARAMS(mxic), CLK_MHZ_2X(84) }, ++ ++ /* Micron 3.3V */ ++ { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ), ++ PARAMS(micron), CLK_MHZ_2X(84) }, ++ { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SPI_NOR_QUAD_READ), ++ PARAMS(micron_4k), CLK_MHZ_2X(108) }, ++ { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ), ++ PARAMS(micron), CLK_MHZ_2X(108) }, ++ { "mt25ql256a/n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SPI_NOR_QUAD_READ), ++ PARAMS(micron), CLK_MHZ_2X(108) }, ++ { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR), ++ PARAMS(micron_4k) }, ++ { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR | SPI_NOR_QUAD_READ), ++ PARAMS(micron_4k), CLK_MHZ_2X(80) }, ++ /* Micron 1.8V */ ++ { "n25q032a", INFO(0x20bb16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ), ++ PARAMS(micron), CLK_MHZ_2X(108) }, ++ { "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, SPI_NOR_QUAD_READ), ++ PARAMS(micron), CLK_MHZ_2X(108) }, ++ { "mt25qu128a/n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SPI_NOR_QUAD_READ), ++ PARAMS(micron), CLK_MHZ_2X(108) }, ++ { "mt25qu256a", INFO(0x20bb19, 0, 64 * 1024, 512, ++ SPI_NOR_QUAD_READ), PARAMS(micron), CLK_MHZ_2X(108) }, ++ { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, USE_FSR | SPI_NOR_QUAD_READ), ++ PARAMS(micron_4k), CLK_MHZ_2X(80) }, ++ ++ /* PMC */ ++ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, ++ { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, ++ { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, + + /* Spansion -- single (large) sector size only, at least + * for the chips listed here (without boot sectors). + */ +- { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, +- { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) }, ++ { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, ++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, ++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, +- { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, +- { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, +- { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, ++ { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, ++ SPI_NOR_4B_OPCODES | SPI_NOR_QUAD_READ), PARAMS(spansion), CLK_MHZ_2X(104) }, ++ { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, ++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, ++ { "s25fl127s/129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(spansion), CLK_MHZ_2X(108) }, + { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, + { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, +- { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, +- { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, +- { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, +- { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, +- { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, +- { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, +- { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, +- { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, +- { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, +- { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, ++ { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SECT_4K ++ | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, ++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, ++ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, ++ { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, ++ { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, ++ { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, ++ { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, ++ { "s25fl004k", INFO(0xef4013, 0, 64 * 1024, 8, SECT_4K ++ | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K ++ | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { "w25x16/s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(winbond), CLK_MHZ_2X(84) }, ++ /* { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K ++ | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, */ ++ { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) }, ++ { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) }, ++ { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K ++ | SPI_NOR_DUAL_READ) }, + + /* SST -- large erase sizes are "overlays", "sectors" are 4K */ +- { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, +- { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, +- { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) }, +- { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) }, ++ { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE)}, ++ { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE)}, ++ { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE)}, ++ { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE)}, + { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, +- { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) }, +- { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, +- { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, +- { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, ++ { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE)}, ++ { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE)}, ++ { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE)}, ++ { "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4, SECT_4K) }, ++ { "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K) }, ++ { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE)}, ++ { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE)}, + + /* ST Microelectronics -- newer production may have feature updates */ + { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, +@@ -588,7 +1313,6 @@ static const struct spi_device_id spi_nor_ids[] = { + { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) }, + { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, + { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, +- { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, + + { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) }, + { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) }, +@@ -615,60 +1339,78 @@ static const struct spi_device_id spi_nor_ids[] = { + { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) }, + { "m25px80", INFO(0x207114, 0, 64 * 1024, 16, 0) }, + +- /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ ++ /* Winbond 3.3V-- w25x "blocks" are 64K, "sectors" are 4KiB */ ++ { "w25x05", INFO(0xef3010, 0, 64 * 1024, 1, SECT_4K) }, + { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, + { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) }, + { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, + { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, +- { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, ++ { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K ++ | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, +- { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, +- { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, ++ { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(winbond), CLK_MHZ_2X(80) }, + { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, +- { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, ++ { "w25q64/s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(winbond), CLK_MHZ_2X(80) }, + { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, +- { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, +- { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, ++ { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(winbond), CLK_MHZ_2X(104) }, ++ { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, ++ SECT_4K | SPI_NOR_QUAD_READ), PARAMS(winbond), CLK_MHZ_2X(80) }, ++ /* Winbond 1.8V */ ++ { "w25q32fw", INFO(0xef6016, 0, 64 * 1024, 64, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB), PARAMS(winbond), CLK_MHZ_2X(80) }, ++ { "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB), PARAMS(winbond), CLK_MHZ_2X(80) }, ++ { "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB), PARAMS(winbond), CLK_MHZ_2X(80) }, + + /* Catalyst / On Semiconductor -- non-JEDEC */ +- { "cat25c11", CAT25_INFO( 16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, +- { "cat25c03", CAT25_INFO( 32, 8, 16, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, +- { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, +- { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, +- { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, ++ { "cat25c11", CAT25_INFO(16, 8, 16, 1, SPI_NOR_NO_ERASE ++ | SPI_NOR_NO_FR) }, ++ { "cat25c03", CAT25_INFO(32, 8, 16, 2, SPI_NOR_NO_ERASE ++ | SPI_NOR_NO_FR) }, ++ { "cat25c09", CAT25_INFO(28, 8, 32, 2, SPI_NOR_NO_ERASE ++ | SPI_NOR_NO_FR) }, ++ { "cat25c17", CAT25_INFO(256, 8, 32, 2, SPI_NOR_NO_ERASE ++ | SPI_NOR_NO_FR) }, ++ { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE ++ | SPI_NOR_NO_FR) }, ++ /* Paragon 3.3V */ ++ { "pn25f16s", INFO(0xe04015, 0, 64 * 1024, 32, ++ SPI_NOR_QUAD_READ), PARAMS(paragon), CLK_MHZ_2X(80) }, ++ { "pn25f32s", INFO(0xe04016, 0, 64 * 1024, 64, ++ SPI_NOR_QUAD_READ), PARAMS(paragon), CLK_MHZ_2X(80) }, ++ + { }, + }; + +-static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) ++static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) + { + int tmp; +- u8 id[5]; +- u32 jedec; +- u16 ext_jedec; +- struct flash_info *info; ++ u8 id[SPI_NOR_MAX_ID_LEN]; ++ const struct flash_info *info; + +- tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5); ++ tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); + if (tmp < 0) { + dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); + } +- jedec = id[0]; +- jedec = jedec << 8; +- jedec |= id[1]; +- jedec = jedec << 8; +- jedec |= id[2]; +- +- ext_jedec = id[3] << 8 | id[4]; + + for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { +- info = (void *)spi_nor_ids[tmp].driver_data; +- if (info->jedec_id == jedec) { +- if (info->ext_id == 0 || info->ext_id == ext_jedec) ++ info = &spi_nor_ids[tmp]; ++ if (info->id_len) { ++ if (!memcmp(info->id, id, info->id_len)) + return &spi_nor_ids[tmp]; + } + } +- dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec); ++ dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %02x, %02x\n", ++ id[0], id[1], id[2]); + return ERR_PTR(-ENODEV); + } + +@@ -703,11 +1445,6 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + if (ret) + return ret; + +- /* Wait until finished previous write command. */ +- ret = wait_till_ready(nor); +- if (ret) +- goto time_out; +- + write_enable(nor); + + nor->sst_write_second = false; +@@ -719,7 +1456,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + + /* write one byte. */ + nor->write(nor, to, 1, retlen, buf); +- ret = wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); + if (ret) + goto time_out; + } +@@ -731,7 +1468,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + + /* write two bytes. */ + nor->write(nor, to, 2, retlen, buf + actual); +- ret = wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); + if (ret) + goto time_out; + to += 2; +@@ -740,7 +1477,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + nor->sst_write_second = false; + + write_disable(nor); +- ret = wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); + if (ret) + goto time_out; + +@@ -751,7 +1488,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + nor->program_opcode = SPINOR_OP_BP; + nor->write(nor, to, 1, retlen, buf + actual); + +- ret = wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); + if (ret) + goto time_out; + write_disable(nor); +@@ -779,11 +1516,12 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, + if (ret) + return ret; + +- /* Wait until finished previous write command. */ +- ret = wait_till_ready(nor); +- if (ret) +- goto write_err; +- ++#ifdef CONFIG_HISI_SPI_BLOCK_PROTECT ++ if (nor->level && (to < nor->end_addr)) { ++ dev_err(nor->dev, "Error: The DMA write area was locked\n"); ++ return -EINVAL; ++ } ++#endif + write_enable(nor); + + page_offset = to & (nor->page_size - 1); +@@ -802,16 +1540,20 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, + if (page_size > nor->page_size) + page_size = nor->page_size; + +- wait_till_ready(nor); ++ ret = spi_nor_wait_till_ready(nor); ++ if (ret) ++ goto write_err; ++ + write_enable(nor); + + nor->write(nor, to + i, page_size, retlen, buf + i); + } + } + ++ ret = spi_nor_wait_till_ready(nor); + write_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); +- return 0; ++ return ret; + } + + static int macronix_quad_enable(struct spi_nor *nor) +@@ -819,16 +1561,24 @@ static int macronix_quad_enable(struct spi_nor *nor) + int ret, val; + + val = read_sr(nor); ++ if (val < 0) ++ return val; ++ ++ if ((unsigned int)val & SR_QUAD_EN_MX) ++ return 0; ++ ++ /* Update the Quad Enable bit. */ ++ dev_info(nor->dev, "setting Macronix Quad Enable (non-volatile) bit\n"); ++ + write_enable(nor); + +- nor->cmd_buf[0] = val | SR_QUAD_EN_MX; +- nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); ++ write_sr(nor, (u8)val | SR_QUAD_EN_MX); + +- if (wait_till_ready(nor)) ++ if (spi_nor_wait_till_ready(nor)) + return 1; + + ret = read_sr(nor); +- if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { ++ if (!(ret > 0 && ((unsigned int)ret & SR_QUAD_EN_MX))) { + dev_err(nor->dev, "Macronix Quad bit not set\n"); + return -EINVAL; + } +@@ -847,23 +1597,38 @@ static int write_sr_cr(struct spi_nor *nor, u16 val) + nor->cmd_buf[0] = val & 0xff; + nor->cmd_buf[1] = (val >> 8); + +- return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0); ++ return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2); + } + + static int spansion_quad_enable(struct spi_nor *nor) + { + int ret; +- int quad_en = CR_QUAD_EN_SPAN << 8; ++ u16 val; ++ ++ ret = read_cr(nor); ++ if (ret & CR_QUAD_EN_SPAN) ++ return 0; ++ ++ /* Update the Quad Enable bit. */ ++ dev_info(nor->dev, "setting Quad Enable (non-volatile) bit\n"); ++ ++ val = ((ret & 0xff) | CR_QUAD_EN_SPAN) << 8; ++ ++ ret = read_sr(nor); ++ val |= (ret & 0xff); + + write_enable(nor); + +- ret = write_sr_cr(nor, quad_en); ++ ret = write_sr_cr(nor, val); + if (ret < 0) { + dev_err(nor->dev, + "error while writing configuration register\n"); + return -EINVAL; + } + ++ if (spi_nor_wait_till_ready(nor)) ++ return 1; ++ + /* read back and check it */ + ret = read_cr(nor); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { +@@ -874,18 +1639,128 @@ static int spansion_quad_enable(struct spi_nor *nor) + return 0; + } + +-static int set_quad_mode(struct spi_nor *nor, u32 jedec_id) ++static int micron_quad_enable(struct spi_nor *nor) ++{ ++ int ret; ++ u8 val; ++ ++ ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1); ++ if (ret < 0) { ++ dev_err(nor->dev, "error %d reading EVCR\n", ret); ++ return ret; ++ } ++ ++ write_enable(nor); ++ ++ /* set EVCR, enable quad I/O */ ++ nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON; ++ ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1); ++ if (ret < 0) { ++ dev_err(nor->dev, "error while writing EVCR register\n"); ++ return ret; ++ } ++ ++ ret = spi_nor_wait_till_ready(nor); ++ if (ret) ++ return ret; ++ ++ /* read EVCR and check it */ ++ ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1); ++ if (ret < 0) { ++ dev_err(nor->dev, "error %d reading EVCR\n", ret); ++ return ret; ++ } ++ if (val & EVCR_QUAD_EN_MICRON) { ++ dev_err(nor->dev, "Micron EVCR Quad bit not clear\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int gd_quad_enable(struct spi_nor *nor) ++{ ++ int ret; ++ u16 val; ++ ++ /* First, Quad Enable for 16-Pin GD flash, use WRSR[01h] cmd */ ++ ret = read_cr(nor); ++ val = ((ret & 0xff) | CR_QUAD_EN_SPAN) << 8; ++ ++ ret = read_sr(nor); ++ val |= (ret & 0xff); ++ ++ write_enable(nor); ++ ++ ret = write_sr_cr(nor, val); ++ if (ret < 0) { ++ dev_err(nor->dev, ++ "error while writing config and status register\n"); ++ return -EINVAL; ++ } ++ ++ if (spi_nor_wait_till_ready(nor)) ++ return 1; ++ ++ /* read back and check it */ ++ ret = read_cr(nor); ++ if (ret & CR_QUAD_EN_SPAN) ++ return 0; ++ ++ /* Second, Quad Enable for 8-Pin GD flash, use WRCR[31h] cmd */ ++ ret = read_sr(nor); ++ if (!(ret & SR_WEL)) ++ write_enable(nor); ++ ++ ret = read_cr(nor); ++ nor->cmd_buf[0] = (ret & 0xff) | CR_QUAD_EN_SPAN; ++ ++ ret = nor->write_reg(nor, SPINOR_OP_WRCR, nor->cmd_buf, 1); ++ if (ret < 0) { ++ dev_err(nor->dev, "error while writing config register\n"); ++ return ret; ++ } ++ ++ if (spi_nor_wait_till_ready(nor)) ++ return 1; ++ ++ /* read back and check it */ ++ ret = read_cr(nor); ++ if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { ++ dev_err(nor->dev, "GigaDevice Quad bit not set\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + { + int status; + +- switch (JEDEC_MFR(jedec_id)) { +- case CFI_MFR_MACRONIX: ++ switch (JEDEC_MFR(info)) { ++ case SNOR_MFR_ESMT: ++ case SNOR_MFR_MACRONIX: + status = macronix_quad_enable(nor); + if (status) { + dev_err(nor->dev, "Macronix quad-read not enabled\n"); + return -EINVAL; + } + return status; ++ case SNOR_MFR_MICRON: ++ status = micron_quad_enable(nor); ++ if (status) { ++ dev_err(nor->dev, "Micron quad-read not enabled\n"); ++ return -EINVAL; ++ } ++ return status; ++ case SNOR_MFR_GD: ++ status = gd_quad_enable(nor); ++ if (status) { ++ dev_err(nor->dev, "GD quad-read not enabled\n"); ++ return -EINVAL; ++ } ++ return status; + default: + status = spansion_quad_enable(nor); + if (status) { +@@ -899,26 +1774,362 @@ static int set_quad_mode(struct spi_nor *nor, u32 jedec_id) + static int spi_nor_check(struct spi_nor *nor) + { + if (!nor->dev || !nor->read || !nor->write || +- !nor->read_reg || !nor->write_reg || !nor->erase) { ++ !nor->read_reg || !nor->write_reg) { + pr_err("spi-nor: please fill all the necessary fields!\n"); + return -EINVAL; + } + +- if (!nor->read_id) +- nor->read_id = spi_nor_read_id; +- if (!nor->wait_till_ready) +- nor->wait_till_ready = spi_nor_wait_till_ready; ++ return 0; ++} ++ ++#ifdef CONFIG_HISI_SPI_BLOCK_PROTECT ++static void spi_lock_update_address(struct spi_nor *nor, const struct flash_info *info) ++{ ++ unsigned int lock_level_max, sectorsize, chipsize; ++ ++ if (!nor->level) { ++ nor->end_addr = 0; ++ dev_warn(nor->dev, "all blocks is unlocked.\n"); ++ return; ++ } ++ ++ sectorsize = info->sector_size; ++ chipsize = sectorsize * info->n_sectors; ++ lock_level_max = nor->lock_level_max; ++ ++ switch (JEDEC_MFR(info)) { ++ case SNOR_MFR_MACRONIX: ++ if (chipsize == _2M) { ++ if ((nor->level != lock_level_max) ++ && (nor->level != 1)) ++ nor->end_addr = chipsize - (sectorsize << ++ (lock_level_max - nor->level - 1)); ++ else ++ nor->end_addr = chipsize; ++ return; ++ } ++ ++ if (chipsize != _8M) ++ break; ++ case SNOR_MFR_ESMT: ++ /* this case is for ESMT and MXIC 8M devices */ ++ if (nor->level != lock_level_max) ++ nor->end_addr = chipsize - (sectorsize ++ << (lock_level_max - nor->level)); ++ else ++ nor->end_addr = chipsize; ++ return; ++ case SNOR_MFR_EON: ++ if (nor->level != lock_level_max) ++ nor->end_addr = chipsize - (sectorsize ++ << (nor->level - 1)); ++ else ++ nor->end_addr = chipsize; ++ return; ++ default: ++ break; ++ } ++ ++ /* general case */ ++ nor->end_addr = chipsize >> (lock_level_max - nor->level); ++} ++ ++static unsigned char hisi_bp_to_level(struct spi_nor *nor, ++ const struct flash_info *info, unsigned int bp_num) ++{ ++ int ret; ++ unsigned char val; ++ unsigned char level; ++ unsigned int chipsize; ++ ++ ret = spi_nor_wait_till_ready(nor); ++ BUG_ON(ret); ++ ++ ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1); ++ if (ret < 0) { ++ dev_err(nor->dev, "error %d reading SR\n", ret); ++ return ret; ++ } ++ ++ if (bp_num == BP_NUM_3) ++ level = (val & SPI_NOR_SR_BP_MASK_3) >> SPI_NOR_SR_BP0_SHIFT; ++ else ++ level = (val & SPI_NOR_SR_BP_MASK_4) >> SPI_NOR_SR_BP0_SHIFT; ++ ++ /* dev_info(nor->dev, "the current level[%d]\n", level); */ ++ ++ if (bp_num == BP_NUM_4) { ++ nor->lock_level_max = LOCK_LEVEL_MAX(bp_num) - 5; ++ chipsize = info->sector_size * info->n_sectors; ++ if ((JEDEC_MFR(info) == SNOR_MFR_MACRONIX) ++ && (chipsize == _16M)) ++ nor->lock_level_max--; ++ } else ++ nor->lock_level_max = LOCK_LEVEL_MAX(bp_num); ++ /* dev_info(nor->dev, "Get the max bp level: [%d]\n", ++ * nor->lock_level_max); */ ++ ++ return level; ++} ++ ++static void hisi_get_spi_lock_info(struct spi_nor *nor, const struct flash_info *info) ++{ ++ unsigned int chipsize; ++ struct device *dev = nor->dev; ++ ++ chipsize = info->sector_size * info->n_sectors; ++ ++ /* read the BP bit in RDSR to check whether nor is lock or not */ ++ switch (JEDEC_MFR(info)) { ++ case SNOR_MFR_GD: ++ case SNOR_MFR_ESMT: ++ case SNOR_MFR_EON: ++ case SNOR_MFR_SPANSION: ++ /* BP bit convert to lock level */ ++ nor->level = hisi_bp_to_level(nor, info, BP_NUM_3); ++ break; ++ case SNOR_MFR_WINBOND: ++ /* BP bit convert to lock level */ ++ if (chipsize <= _16M) ++ nor->level = hisi_bp_to_level(nor, info, BP_NUM_3); ++ else ++ nor->level = hisi_bp_to_level(nor, info, BP_NUM_4); ++ break; ++ case SNOR_MFR_MACRONIX: ++ /* BP bit convert to lock level */ ++ if (chipsize <= _8M) ++ nor->level = hisi_bp_to_level(nor, info, BP_NUM_3); ++ else ++ nor->level = hisi_bp_to_level(nor, info, BP_NUM_4); ++ break; ++ default: ++ goto usage; ++ } ++ ++ spi_lock_update_address(nor, info); ++ if (nor->end_addr) ++ dev_info(dev, "Address range [0 => %#x] is locked.\n", ++ nor->end_addr); ++ return; ++usage: ++ dev_err(dev, "The ID: %#x isn't in the BP table," ++ " Current device can't not protect\n", ++ JEDEC_MFR(info)); ++} ++#endif/* CONFIG_HISI_SPI_BLOCK_PROTECT */ ++ ++static int spi_nor_midx2proto(int midx, enum spi_nor_protocol *proto) ++{ ++ switch (midx) { ++ case SNOR_MIDX_SLOW: ++ case SNOR_MIDX_1_1_1: ++ *proto = SNOR_PROTO_1_1_1; ++ break; ++ ++ case SNOR_MIDX_1_1_2: ++ *proto = SNOR_PROTO_1_1_2; ++ break; ++ ++ case SNOR_MIDX_1_2_2: ++ *proto = SNOR_PROTO_1_2_2; ++ break; ++ case SNOR_MIDX_1_1_4: ++ *proto = SNOR_PROTO_1_1_4; ++ break; ++ ++ case SNOR_MIDX_1_4_4: ++ *proto = SNOR_PROTO_1_4_4; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int spi_nor_sr3_to_reset(struct spi_nor *nor) ++{ ++ int ret; ++ unsigned char val; ++ ++ ret = nor->read_reg(nor, SPINOR_OP_RDSR3, &val, 1); ++ if (ret < 0) { ++ dev_err(nor->dev, "error %d reading Status Reg 3.\n", ret); ++ return ret; ++ } ++ ++ if (SPI_NOR_GET_RST(val)) { ++ dev_dbg(nor->dev, "Device has worked on RESET#.\n"); ++ return 0; ++ } ++ ++ dev_dbg(nor->dev, "Start to enable RESET# function.\n"); ++ val = SPI_NOR_SET_RST(val); ++ ++ nor->write_reg(nor, SPINOR_OP_WRSR3, &val, 1); ++ if (ret < 0) { ++ dev_err(nor->dev, "error while writing Status Reg 3.\n"); ++ return ret; ++ } ++ ++ dev_dbg(nor->dev, "Enable RESET# function success.\n"); ++ ++ return 0; ++} ++ ++static int spi_nor_reset_pin_enable(struct spi_nor *nor, ++ const struct flash_info *info) ++{ ++ switch (JEDEC_MFR(info)) { ++ case SNOR_MFR_WINBOND: ++ case SNOR_MFR_GD: ++ return spi_nor_sr3_to_reset(nor); ++ default: ++ return 0; ++ } ++} ++ ++static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info, ++ const struct spi_nor_basic_flash_parameter *params, ++ const struct spi_nor_modes *modes) ++{ ++ bool enable_quad_io; ++ u32 rd_modes, wr_modes; ++ const struct spi_nor_erase_type *erase_type; ++ const struct spi_nor_read_op *read; ++ int rd_midx, wr_midx, err = 0; ++ ++ rd_modes = modes->rd_modes; ++ wr_modes = modes->wr_modes; ++ ++ /* Setup read operation. */ ++ rd_midx = fls(params->rd_modes & rd_modes) - 1; ++ if (spi_nor_midx2proto(rd_midx, &nor->read_proto)) { ++ dev_err(nor->dev, "invalid (fast) read\n"); ++ return -EINVAL; ++ } ++ read = ¶ms->reads[rd_midx]; ++ nor->read_opcode = read->opcode; ++ nor->read_dummy = read->num_mode_clocks + read->num_wait_states; ++ ++ /* Set page program op code and protocol. */ ++ wr_midx = fls(params->wr_modes & wr_modes) - 1; ++ if (spi_nor_midx2proto(wr_midx, &nor->write_proto)) { ++ dev_err(nor->dev, "invalid page program\n"); ++ return -EINVAL; ++ } ++ nor->program_opcode = params->page_programs[wr_midx]; ++ ++ /* Set sector erase op code and size. */ ++ erase_type = ¶ms->erase_types[0]; ++#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS ++ for (i = 1; i < SNOR_MAX_ERASE_TYPES; ++i) ++ if (params->erase_types[i].size == 0x0c) ++ erase_type = ¶ms->erase_types[i]; ++#endif ++ nor->erase_opcode = erase_type->opcode; ++ nor->mtd.erasesize = (1 << erase_type->size); ++ ++ enable_quad_io = (SNOR_PROTO_DATA_FROM_PROTO(nor->read_proto) == 4 || ++ SNOR_PROTO_DATA_FROM_PROTO(nor->write_proto) == 4); ++ ++ /* Enable Quad I/O if needed. */ ++ if (enable_quad_io && params->enable_quad_io) { ++ err = params->enable_quad_io(nor); ++ if (err) { ++ dev_err(nor->dev, ++ "failed to enable the Quad I/O mode\n"); ++ return err; ++ } ++ } ++ ++ /* ++ * Fix erase protocol if needed, read and write protocols should ++ * already be valid. ++ */ ++ nor->erase_proto = SNOR_PROTO_1_1_1; ++ ++ dev_dbg(nor->dev, ++ "(Fast) Read: opcode=%02Xh, protocol=%03x, mode=%u, wait=%u\n", ++ nor->read_opcode, nor->read_proto, ++ read->num_mode_clocks, read->num_wait_states); ++ dev_dbg(nor->dev, ++ "Page Program: opcode=%02Xh, protocol=%03x\n", ++ nor->program_opcode, nor->write_proto); ++ dev_dbg(nor->dev, ++ "Sector Erase: opcode=%02Xh, protocol=%03x, sector size=%zu\n", ++ nor->erase_opcode, nor->erase_proto, nor->mtd.erasesize); ++ ++ return 0; ++} ++ ++static int spi_nor_config(struct spi_nor *nor, const struct flash_info *info, ++ const struct spi_nor_basic_flash_parameter *params, ++ struct spi_nor_modes *modes) ++{ ++ int ret; ++ ++ if (params) { ++ ret = spi_nor_setup(nor, info, params, modes); ++ if (ret) ++ return ret; ++ } else if (modes->rd_modes & SNOR_MODE_1_1_4 && ++ info->flags & SPI_NOR_QUAD_READ) { ++ /* ++ * This branch is spcially for some devices which can ++ * not be stated by params, but only SPI_NOR_QUAD_READ, ++ * it just supports the protocol 1_1_4. ++ */ ++ if (spi_nor_wait_till_ready(nor)) ++ return 1; ++ ++ ret = set_quad_mode(nor, info); ++ if (ret) { ++ dev_err(nor->dev, "quad mode not supported\n"); ++ return ret; ++ } ++ nor->read_proto = SNOR_PROTO_1_1_4; ++ nor->read_opcode = SPINOR_OP_READ_1_1_4; ++ nor->read_dummy = 8; ++ } else if (modes->rd_modes & SNOR_MODE_1_1_2 && ++ info->flags & SPI_NOR_DUAL_READ) { ++ /* ++ * This branch is spcially for some devices which can ++ * not be stated by params, but only SPI_NOR_DUAL_READ, ++ * it just supports the protocol 1_1_2. ++ */ ++ nor->read_proto = SNOR_PROTO_1_1_2; ++ nor->read_opcode = SPINOR_OP_READ_1_1_2; ++ nor->read_dummy = 8; ++ } else { ++ if (modes->rd_modes & SNOR_MODE_1_1_1) { ++ nor->read_opcode = SPINOR_OP_READ_FAST; ++ nor->read_dummy = 8; ++ } else { ++ nor->read_opcode = SPINOR_OP_READ; ++ nor->read_dummy = 0; ++ } ++ } ++ ++ if (!(modes->rd_modes & (SNOR_MODE_1_1_4 | SNOR_MODE_1_4_4))) { ++ ret = spi_nor_reset_pin_enable(nor, info); ++ if (ret < 0) { ++ dev_err(nor->dev, "Enable RESET# fail.\n"); ++ return ret; ++ } ++ } + + return 0; + } + +-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) ++int spi_nor_scan(struct spi_nor *nor, const char *name, ++ struct spi_nor_modes *modes) + { +- const struct spi_device_id *id = NULL; +- struct flash_info *info; ++ const struct spi_nor_basic_flash_parameter *params = NULL; ++ const struct flash_info *info = NULL; + struct device *dev = nor->dev; +- struct mtd_info *mtd = nor->mtd; +- struct device_node *np = dev->of_node; ++ struct mtd_info *mtd = &nor->mtd; ++ struct device_node *np = nor->flash_node; + int ret; + int i; + +@@ -926,19 +2137,30 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + if (ret) + return ret; + +- id = spi_nor_match_id(name); +- if (!id) ++ /* Reset SPI protocol for all commands */ ++ nor->erase_proto = SNOR_PROTO_1_1_1; ++ nor->read_proto = SNOR_PROTO_1_1_1; ++ nor->write_proto = SNOR_PROTO_1_1_1; ++ ++ if (name) ++ info = spi_nor_match_id(name); ++ /* Try to auto-detect if chip name wasn't specified or not found */ ++ if (!info) ++ info = spi_nor_read_id(nor); ++ if (IS_ERR_OR_NULL(info)) + return -ENOENT; + +- info = (void *)id->driver_data; +- +- if (info->jedec_id) { +- const struct spi_device_id *jid; ++ /* ++ * If caller has specified name of flash model that can normally be ++ * detected using JEDEC, let's verify it. ++ */ ++ if (name && info->id_len) { ++ const struct flash_info *jinfo; + +- jid = nor->read_id(nor); +- if (IS_ERR(jid)) { +- return PTR_ERR(jid); +- } else if (jid != id) { ++ jinfo = spi_nor_read_id(nor); ++ if (IS_ERR(jinfo)) { ++ return PTR_ERR(jinfo); ++ } else if (jinfo != info) { + /* + * JEDEC knows better, so overwrite platform ID. We + * can't trust partitions any longer, but we'll let +@@ -947,28 +2169,37 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + * information, even if it's not 100% accurate. + */ + dev_warn(dev, "found %s, expected %s\n", +- jid->name, id->name); +- id = jid; +- info = (void *)jid->driver_data; ++ jinfo->name, info->name); ++ info = jinfo; + } + } ++ if (info->params) ++ params = info->params; + + mutex_init(&nor->lock); + ++#ifdef CONFIG_HISI_SPI_BLOCK_PROTECT ++ /* NOR block protection support */ ++ hisi_get_spi_lock_info(nor, info); ++#else + /* +- * Atmel, SST and Intel/Numonyx serial nor tend to power +- * up with the software protection bits set ++ * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up ++ * with the software protection bits set + */ + +- if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL || +- JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL || +- JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) { ++ if (JEDEC_MFR(info) == SNOR_MFR_ATMEL || ++ JEDEC_MFR(info) == SNOR_MFR_INTEL || ++ JEDEC_MFR(info) == SNOR_MFR_SST || ++ info->flags & SPI_NOR_HAS_LOCK) { + write_enable(nor); + write_sr(nor, 0); ++ spi_nor_wait_till_ready(nor); + } ++#endif + + if (!mtd->name) + mtd->name = dev_name(dev); ++ mtd->priv = nor; + mtd->type = MTD_NORFLASH; + mtd->writesize = 1; + mtd->flags = MTD_CAP_NORFLASH; +@@ -976,10 +2207,19 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + mtd->_erase = spi_nor_erase; + mtd->_read = spi_nor_read; + +- /* nor protection support for STmicro chips */ +- if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { ++ /* NOR protection support for STmicro/Micron chips and similar */ ++ if (JEDEC_MFR(info) == SNOR_MFR_MICRON || ++ JEDEC_MFR(info) == SNOR_MFR_WINBOND || ++ info->flags & SPI_NOR_HAS_LOCK) { ++ nor->flash_lock = stm_lock; ++ nor->flash_unlock = stm_unlock; ++ nor->flash_is_locked = stm_is_locked; ++ } ++ ++ if (nor->flash_lock && nor->flash_unlock && nor->flash_is_locked) { + mtd->_lock = spi_nor_lock; + mtd->_unlock = spi_nor_unlock; ++ mtd->_is_locked = spi_nor_is_locked; + } + + /* sst nor chips use AAI word program */ +@@ -988,9 +2228,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + else + mtd->_write = spi_nor_write; + +- if ((info->flags & USE_FSR) && +- nor->wait_till_ready == spi_nor_wait_till_ready) +- nor->wait_till_ready = spi_nor_wait_till_fsr_ready; ++ if (info->flags & USE_FSR) ++ nor->flags |= SNOR_F_USE_FSR; ++ if (info->flags & SPI_NOR_HAS_TB) ++ nor->flags |= SNOR_F_HAS_SR_TB; + + #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + /* prefer "small sector" erase if possible */ +@@ -1017,85 +2258,58 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + if (np) { + /* If we were instantiated by DT, use it */ + if (of_property_read_bool(np, "m25p,fast-read")) +- nor->flash_read = SPI_NOR_FAST; ++ modes->rd_modes |= SNOR_MODE_1_1_1; + else +- nor->flash_read = SPI_NOR_NORMAL; ++ modes->rd_modes &= ~SNOR_MODE_1_1_1; + } else { + /* If we weren't instantiated by DT, default to fast-read */ +- nor->flash_read = SPI_NOR_FAST; ++ modes->rd_modes |= SNOR_MODE_1_1_1; + } + + /* Some devices cannot do fast-read, no matter what DT tells us */ + if (info->flags & SPI_NOR_NO_FR) +- nor->flash_read = SPI_NOR_NORMAL; +- +- /* Quad/Dual-read mode takes precedence over fast/normal */ +- if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) { +- ret = set_quad_mode(nor, info->jedec_id); +- if (ret) { +- dev_err(dev, "quad mode not supported\n"); +- return ret; +- } +- nor->flash_read = SPI_NOR_QUAD; +- } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) { +- nor->flash_read = SPI_NOR_DUAL; +- } +- +- /* Default commands */ +- switch (nor->flash_read) { +- case SPI_NOR_QUAD: +- nor->read_opcode = SPINOR_OP_READ_1_1_4; +- break; +- case SPI_NOR_DUAL: +- nor->read_opcode = SPINOR_OP_READ_1_1_2; +- break; +- case SPI_NOR_FAST: +- nor->read_opcode = SPINOR_OP_READ_FAST; +- break; +- case SPI_NOR_NORMAL: +- nor->read_opcode = SPINOR_OP_READ; +- break; +- default: +- dev_err(dev, "No Read opcode defined\n"); +- return -EINVAL; +- } ++ modes->rd_modes &= ~SNOR_MODE_1_1_1; + + nor->program_opcode = SPINOR_OP_PP; + ++ /* ++ * Configure the SPI memory: ++ * - select op codes for (Fast) Read, Page Program and Sector Erase. ++ * - set the number of dummy cycles (mode cycles + wait states). ++ * - set the SPI protocols for register and memory accesses. ++ * - set the Quad Enable bit if needed (required by SPI x-y-4 protos). ++ */ ++ ret = spi_nor_config(nor, info, params, modes); ++ if (ret) ++ return ret; ++ + if (info->addr_width) + nor->addr_width = info->addr_width; + else if (mtd->size > 0x1000000) { + /* enable 4-byte addressing if the device exceeds 16MiB */ + nor->addr_width = 4; +- if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { +- /* Dedicated 4-byte command set */ +- switch (nor->flash_read) { +- case SPI_NOR_QUAD: +- nor->read_opcode = SPINOR_OP_READ4_1_1_4; +- break; +- case SPI_NOR_DUAL: +- nor->read_opcode = SPINOR_OP_READ4_1_1_2; +- break; +- case SPI_NOR_FAST: +- nor->read_opcode = SPINOR_OP_READ4_FAST; +- break; +- case SPI_NOR_NORMAL: +- nor->read_opcode = SPINOR_OP_READ4; +- break; +- } +- nor->program_opcode = SPINOR_OP_PP_4B; +- /* No small sector erase for 4-byte command set */ +- nor->erase_opcode = SPINOR_OP_SE_4B; +- mtd->erasesize = info->sector_size; +- } else +- set_4byte(nor, info->jedec_id, 1); ++ if (JEDEC_MFR(info) == SNOR_MFR_SPANSION || ++ info->flags & SPI_NOR_4B_OPCODES) ++ spi_nor_set_4byte_opcodes(nor, info); ++ else ++ set_4byte(nor, info, 1); + } else { + nor->addr_width = 3; + } + +- nor->read_dummy = spi_nor_read_dummy_cycles(nor); ++ /* choose the suitable clockrate */ ++ if (info->clkrate && (modes->rd_modes != SNOR_MODE_1_1_1)) ++ nor->clkrate = info->clkrate; ++ else ++ nor->clkrate = 24000000; ++ ++ if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { ++ dev_err(dev, "address width is too large: %u\n", ++ nor->addr_width); ++ return -EINVAL; ++ } + +- dev_info(dev, "%s (%lld Kbytes)\n", id->name, ++ dev_info(dev, "%s (%lld Kbytes)\n", info->name, + (long long)mtd->size >> 10); + + dev_dbg(dev, +@@ -1118,11 +2332,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + } + EXPORT_SYMBOL_GPL(spi_nor_scan); + +-static const struct spi_device_id *spi_nor_match_id(const char *name) ++static const struct flash_info *spi_nor_match_id(const char *name) + { +- const struct spi_device_id *id = spi_nor_ids; ++ const struct flash_info *id = spi_nor_ids; + +- while (id->name[0]) { ++ while (id->name) { + if (!strcmp(name, id->name)) + return id; + id++; +@@ -1130,6 +2344,64 @@ static const struct spi_device_id *spi_nor_match_id(const char *name) + return NULL; + } + ++/******************************************************************************/ ++void spi_nor_driver_shutdown(struct spi_nor *nor) ++{ ++ /* disable 4-byte addressing if the device exceeds 16MiB */ ++ if (nor->addr_width == 4) { ++ const struct flash_info *info = NULL; ++ ++ info = spi_nor_read_id(nor); ++ set_4byte(nor, info, 0); ++ } ++ return; ++} ++ ++#ifdef CONFIG_PM ++/******************************************************************************/ ++int spi_nor_suspend(struct spi_nor *nor, pm_message_t state) ++{ ++ return spi_nor_wait_till_ready(nor); ++} ++ ++/******************************************************************************/ ++int spi_nor_resume(struct spi_nor *nor) ++{ ++ int ret; ++ const struct flash_info *info = NULL; ++ const struct spi_nor_basic_flash_parameter *params = NULL; ++ struct spi_nor_modes modes = { ++ .rd_modes = SNOR_MODE_SLOW, ++ .wr_modes = SNOR_MODE_1_1_1, ++ }; ++ ++ modes.rd_modes |= SNOR_MODE_1_1_1 ++ | SNOR_MODE_1_1_2 ++ | SNOR_MODE_1_2_2; ++#ifndef CONFIG_CLOSE_SPI_8PIN_4IO ++ modes.rd_modes |= SNOR_MODE_1_1_4 | SNOR_MODE_1_4_4; ++ modes.wr_modes |= SNOR_MODE_1_1_4 | SNOR_MODE_1_4_4; ++#endif ++ ++ if (!info) ++ info = spi_nor_read_id(nor); ++ ++ /* Quad mode takes precedence over fast/normal */ ++ if (info->params) ++ params = info->params; ++ ++ ret = spi_nor_config(nor, info, params, &modes); ++ if (ret) ++ return ret; ++ ++ /* enable 4-byte addressing if the device exceeds 16MiB */ ++ if (nor->addr_width == 4 && JEDEC_MFR(info) != SNOR_MFR_SPANSION) ++ set_4byte(nor, info, 1); ++ ++ return 0; ++} ++#endif /* End of CONFIG_PM */ ++ + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Huang Shijie <shijie8@gmail.com>"); + MODULE_AUTHOR("Mike Lavender"); +diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig +index e942173..1389b6c 100644 +--- a/drivers/net/ethernet/hisilicon/Kconfig ++++ b/drivers/net/ethernet/hisilicon/Kconfig +@@ -24,4 +24,19 @@ config HIX5HD2_GMAC + help + This selects the hix5hd2 mac family network device. + ++config HISI_FEMAC ++ tristate "Hisilicon Fast Ethernet MAC device support" ++ depends on HAS_IOMEM ++ select PHYLIB ++ select RESET_CONTROLLER ++ help ++ This selects the Hisilicon Fast Ethernet MAC device(FEMAC). ++ The FEMAC receives and transmits data over Ethernet ++ ports at 10/100 Mbps in full-duplex or half-duplex mode. ++ The FEMAC exchanges data with the CPU, and supports ++ the energy efficient Ethernet (EEE). ++ ++source "drivers/net/ethernet/hisilicon/higmac/Kconfig" ++source "drivers/net/ethernet/hisilicon/hieth/Kconfig" ++ + endif # NET_VENDOR_HISILICON +diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile +index 9175e84..75126b4 100644 +--- a/drivers/net/ethernet/hisilicon/Makefile ++++ b/drivers/net/ethernet/hisilicon/Makefile +@@ -3,3 +3,6 @@ + # + + obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o ++obj-$(CONFIG_HISI_FEMAC) += hisi_femac.o ++obj-$(CONFIG_HIETH_GMAC) += higmac/ ++obj-$(CONFIG_HIETH_SWITCH_FABRIC) += hieth/ +diff --git a/drivers/net/ethernet/hisilicon/hieth/Kconfig b/drivers/net/ethernet/hisilicon/hieth/Kconfig +new file mode 100644 +index 0000000..2c359ce +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/Kconfig +@@ -0,0 +1,57 @@ ++# ++# hieth family network device configuration ++# ++ ++menuconfig HIETH_SWITCH_FABRIC ++ tristate "hieth(switch fabric) family network device support" ++ select PHYLIB ++ select RESET_CONTROLLER ++ help ++ This selects the hieth family network device. ++ The hieth switch fabric (SF) receives and transmits data over two Ethernet ++ ports at 10/100 Mbit/s in full-duplex or half-duplex mode. ++ The Ethernet port exchanges data with the CPU port, and supports ++ the energy efficient Ethernet (EEE). ++ ++if HIETH_SWITCH_FABRIC ++ ++config HIETH_MAX_RX_POOLS ++ int "hieth max rx pool size" ++ default "1024" ++ help ++ hieth max static rx pool size. ++ ++config TX_FLOW_CTRL_SUPPORT ++ bool "tx flow ctrl supported" ++ default y ++ help ++ Tx flow ctrl supported, default is enabled. ++ When we has no buffer to receive packet, ++ we will send pause frame. ++ Rx flow ctrl default is enabled, cannot be disabled. ++ ++config TX_FLOW_CTRL_ACTIVE_THRESHOLD ++ int "tx flow ctrl active threshold" ++ default "3" ++ range 1 31 ++ help ++ The threshold for activing tx flow ctrl. ++ When the left amount of receive queue descriptors is below this threshold, ++ hardware will send pause frame immediately. ++ We advise this value is set between 1 and 10. Too bigger is not a good choice. ++ This value must be smaller than tx flow ctrl deactive threshold. ++ ++config TX_FLOW_CTRL_DEACTIVE_THRESHOLD ++ int "tx flow ctrl deactive threshold" ++ default "5" ++ range 1 31 ++ help ++ The threshold for deactiving tx flow ctrl. ++ When the left amount of receive queue descriptors is above or equal with this threshold, ++ hardware will exit flow control state. ++ We advise this value is set between 1 and 10. Too bigger is not a good choice. ++ This value must be larger than tx flow ctrl active threshold. ++ ++endif # HIETH_SWITCH_FABRIC ++ ++#vim: set ts=8 sw=8 tw=78: +diff --git a/drivers/net/ethernet/hisilicon/hieth/Makefile b/drivers/net/ethernet/hisilicon/hieth/Makefile +new file mode 100644 +index 0000000..28ddee5 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/Makefile +@@ -0,0 +1,5 @@ ++# ++# Makefile for the hisilicon hieth device drivers. ++# ++ ++obj-$(CONFIG_HIETH_SWITCH_FABRIC) += mdio.o hieth.o phy.o autoeee.o proc.o +diff --git a/drivers/net/ethernet/hisilicon/hieth/autoeee.c b/drivers/net/ethernet/hisilicon/hieth/autoeee.c +new file mode 100644 +index 0000000..970ea04 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/autoeee.c +@@ -0,0 +1,260 @@ ++#include <linux/phy.h> ++#include "phy.h" ++#include "hieth.h" ++ ++/*----------------------------Macro definition-------------------------------*/ ++#define NO_EEE 0 ++#define MAC_EEE 1 ++#define PHY_EEE 2 ++#define PARTNER_EEE 2 ++ ++#define debug(fmt...) ++struct phy_info { ++ char *name; ++ int phy_id; ++ char eee_available;/* eee support by this phy */ ++ int (*eee_init)(struct phy_device *phy_dev); ++}; ++ ++/* GMAC register definition */ ++#define EEE_ENABLE 0x488 ++#define BIT_EEE_ENABLE (1 << 0) ++#define EEE_TIMER 0x48C ++#define EEE_LINK_STATUS 0x490 ++#define BIT_PHY_LINK_STATUS (1 << 0) ++#define EEE_TIME_CLK_CNT 0x494 ++ ++/* ----------------------------phy register-------------------------------*/ ++/* MMD: MDIO Manageable Device */ ++#define MACR 0x0D ++#define MAADR 0x0E ++#define EEE_DEV 0x3 ++#define EEE_CAPABILITY 0x14 ++#define EEELPAR_DEV 0x7 ++#define EEELPAR 0x3D /* EEE link partner ability register */ ++#define EEE_ADVERTISE 0x3c ++#define LP_1000BASE_EEE (1 << 2) ++#define LP_100BASE_EEE (1 << 1) ++ ++static struct phy_info phy_info_table[]; ++ ++static struct phy_info *phy_search_ids(int phy_id) ++{ ++ int i; ++ struct phy_info *fit_info = NULL; ++ ++ for (i = 0; phy_info_table[i].name != NULL; i++) { ++ if (phy_id == phy_info_table[i].phy_id) ++ fit_info = &phy_info_table[i]; ++ } ++ ++ return fit_info; ++} ++ ++static inline int phy_mmd_read(struct phy_device *phy_dev, ++ u32 mmd_device, u32 regnum) ++{ ++ phy_write(phy_dev, MACR, mmd_device);/* function = 00 address */ ++ phy_write(phy_dev, MAADR, regnum); ++ phy_write(phy_dev, MACR, 0x4000 | mmd_device);/* function = 01 data */ ++ ++ return phy_read(phy_dev, MAADR); ++} ++ ++static inline int phy_mmd_write(struct phy_device *phy_dev, ++ u32 mmd_device, u32 regnum, u16 val) ++{ ++ phy_write(phy_dev, MACR, mmd_device);/* function = 00 address */ ++ phy_write(phy_dev, MAADR, regnum); ++ phy_write(phy_dev, MACR, 0x4000 | mmd_device);/* function = 01 data */ ++ ++ return phy_write(phy_dev, MAADR, val); ++} ++ ++static int smsc_lan8740_init(struct phy_device *phy_dev) ++{ ++ static int first_time; ++ int v, eee_type = 0; ++ ++ if (!first_time) { ++ /* Realtek LAN 8740 start to enable eee */ ++ int eee_lan; ++ ++ eee_lan = phy_read(phy_dev, 0x10); ++ eee_lan |= 0x4; ++ phy_write(phy_dev, 0x10, eee_lan); ++ eee_lan = phy_read(phy_dev, 0x10); ++ debug("eee enable bit[45?] :%x\n", eee_lan); ++ /* auto negotiate after enable eee*/ ++ eee_lan = phy_read(phy_dev, 0x0); ++ eee_lan |= 0x200; ++ phy_write(phy_dev, 0x0, eee_lan); ++ first_time = 1; ++ } ++ ++ v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEELPAR); ++ debug("EEELPAR = 0x%x\n", v); ++ ++ if (v & LP_100BASE_EEE) ++ eee_type |= HIETH_P_MAC_PORTSET_SPD_100M; ++ ++ return eee_type; ++} ++ ++#define RTL8211EG_MAC 0 ++#if RTL8211EG_MAC ++static int rtl8211EG_mac_init(struct phy_device *phy_dev) ++{ ++ static int first_time_rtl; ++ /* Realtek 8211EG start reset to change eee to mac */ ++ int v, eee_type = 0; ++ ++ if (!first_time_rtl) { ++ int tmp = 0; ++ ++ phy_write(phy_dev, 0x1f, 0x0); ++ phy_write(phy_dev, MII_BMCR, BMCR_RESET); /* reset phy */ ++ do { /* wait phy restart over */ ++ udelay(1); ++ tmp = phy_read(phy_dev, MII_BMSR); ++ /* no need to wait AN finished */ ++ tmp &= (BMSR_ANEGCOMPLETE | BMSR_ANEGCAPABLE); ++ } while (!tmp); ++ ++ phy_write(phy_dev, 0x1f, 0x7); ++ phy_write(phy_dev, 0x1e, 0x20); ++ phy_write(phy_dev, 0x1b, 0xa03a); ++ phy_write(phy_dev, 0x1f, 0x0); ++ ++ first_time_rtl = 1; ++ } ++ ++ v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEELPAR); ++ ++ if (v & LP_100BASE_EEE) ++ eee_type |= HIETH_P_MAC_PORTSET_SPD_100M; ++ ++ return eee_type; ++} ++#else ++static int rtl8211EG_init(struct phy_device *phy_dev) ++{ ++ int eee_type = 0, v; ++ ++ v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEELPAR); ++ debug("EEELPAR = 0x%x\n", v); ++ ++ if (v & LP_100BASE_EEE) ++ eee_type |= HIETH_P_MAC_PORTSET_SPD_100M; ++ ++ return eee_type; ++} ++#endif ++ ++static int festa_v200_init(struct phy_device *phy_dev) ++{ ++ static int first_time_init; ++ int v, eee_type = 0; ++ ++ if (!first_time_init) { ++ /* EEE_CAPABILITY register: support 100M-BaseT */ ++ v = phy_mmd_read(phy_dev, EEE_DEV, EEE_CAPABILITY); ++ phy_mmd_write(phy_dev, EEE_DEV, EEE_CAPABILITY, v|(1<<1)); ++ ++ /* EEE_ADVERTISEMENT register: advertising 100M-BaseT */ ++ v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEE_ADVERTISE); ++ phy_mmd_write(phy_dev, EEELPAR_DEV, EEE_ADVERTISE, v|(1<<1)); ++ ++ v = phy_read(phy_dev, MII_BMCR); ++ v |= (BMCR_ANENABLE | BMCR_ANRESTART); ++ phy_write(phy_dev, MII_BMCR, v);/* auto-neg restart */ ++ ++ first_time_init = 1; ++ } ++ ++ v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEELPAR); ++ debug("EEELPAR = 0x%x\n", v); ++ ++ if (v & LP_100BASE_EEE) ++ eee_type |= HIETH_P_MAC_PORTSET_SPD_100M; ++ ++ return eee_type; ++} ++ ++static struct phy_info phy_info_table[] = { ++ /* phy_name phy_id eee_available phy_driver */ ++ /* SMSC */ ++ {"SMSC LAN8740", 0x0007c110, MAC_EEE, &smsc_lan8740_init}, ++ /* Realtek */ ++#if RTL8211EG_MAC ++ {"Realtek 8211EG", 0x001cc915, MAC_EEE, &rtl8211EG_mac_init}, ++#else ++ {"Realtek 8211EG", 0x001cc915, PHY_EEE, &rtl8211EG_init}, ++#endif ++ {"Festa V200", HISILICON_PHY_ID_FESTAV200, MAC_EEE, &festa_v200_init}, ++ {"Festa V300", HISILICON_PHY_ID_FESTAV300, MAC_EEE, &festa_v200_init}, ++ {0, 0, 0, 0}, ++}; ++ ++void hieth_autoeee_init(struct hieth_netdev_priv *priv, int link_stat) ++{ ++ int phy_id = priv->phy->phy_id; ++ struct phy_info *phy_info; ++ ++ if (priv->eee_init) ++ goto eee_init; ++ ++ phy_info = phy_search_ids(phy_id); ++ if (phy_info) { ++ int eee_available, lp_eee_capable, v; ++ ++ eee_available = phy_info->eee_available; ++ debug("fit phy_id:0x%x, phy_name:%s, eee:%d\n", ++ phy_info->phy_id, phy_info->name, eee_available); ++ ++ if (!eee_available) ++ goto not_support; ++ ++ if (eee_available == PHY_EEE) { ++ debug("enter phy-EEE mode\n"); ++ v = readl(priv->glb_base + EEE_ENABLE); ++ v &= ~BIT_EEE_ENABLE;/* disable auto-EEE */ ++ writel(v, priv->glb_base + EEE_ENABLE); ++ return; ++ } ++ ++ priv->eee_init = phy_info->eee_init; ++eee_init: ++ lp_eee_capable = priv->eee_init(priv->phy); ++ if (link_stat & HIETH_P_MAC_PORTSET_LINKED) { ++ if (lp_eee_capable & link_stat) { ++ /* EEE_1us: 0x7c for 125M */ ++ writel(0x7c, priv->glb_base + EEE_TIME_CLK_CNT); ++ writel(0x4002710, priv->glb_base + EEE_TIMER); ++ ++ v = readl(priv->glb_base + EEE_LINK_STATUS); ++ v |= 0x3 << 1;/* auto EEE and ... */ ++ v |= BIT_PHY_LINK_STATUS;/* phy linkup */ ++ writel(v, priv->glb_base + EEE_LINK_STATUS); ++ ++ v = readl(priv->glb_base + EEE_ENABLE); ++ v |= BIT_EEE_ENABLE;/* enable EEE */ ++ writel(v, priv->glb_base + EEE_ENABLE); ++ ++ debug("enter auto-EEE mode\n"); ++ } else { ++ debug("link partner not support EEE\n"); ++ } ++ } else { ++ v = readl(priv->glb_base + EEE_LINK_STATUS); ++ v &= ~(BIT_PHY_LINK_STATUS);/* phy linkdown */ ++ writel(v, priv->glb_base + EEE_LINK_STATUS); ++ } ++ ++ return; ++ } ++ ++not_support: ++ priv->eee_init = NULL; ++ debug("non-EEE mode\n"); ++} +diff --git a/drivers/net/ethernet/hisilicon/hieth/hieth.c b/drivers/net/ethernet/hisilicon/hieth/hieth.c +new file mode 100644 +index 0000000..8b8ad4d +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/hieth.c +@@ -0,0 +1,2016 @@ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include <linux/module.h> ++#include <linux/interrupt.h> ++#include <linux/etherdevice.h> ++#include <linux/platform_device.h> ++#include <linux/of_net.h> ++#include <linux/of_mdio.h> ++#include <linux/clk.h> ++#include <linux/reset.h> ++#include <linux/circ_buf.h> ++#include <linux/if_ether.h> ++#include <linux/if_vlan.h> ++#include <linux/ip.h> ++#include <net/ipv6.h> ++#include <linux/tcp.h> ++#include <net/protocol.h> ++#include "hieth.h" ++#include "mdio.h" ++#include "proc.h" ++#include "tso.h" ++ ++#ifdef HIETH_TSO_SUPPORTED ++#define E_MAC_TX_FAIL 2 ++#define E_MAC_SW_GSO 3 ++#endif ++ ++#define FCS_BYTES 4 ++ ++/*----------------------------Global variable-------------------------------*/ ++struct hieth_phy_param_s hieth_phy_param[HIETH_MAX_PORT]; ++ ++#define FC_ACTIVE_MIN 1 ++#define FC_ACTIVE_DEFAULT 3 ++#define FC_ACTIVE_MAX 31 ++#define FC_DEACTIVE_MIN 1 ++#define FC_DEACTIVE_DEFAULT 5 ++#define FC_DEACTIVE_MAX 31 ++ ++/*----------------------------Local variable-------------------------------*/ ++static struct net_device *hieth_devs_save[HIETH_MAX_PORT] = { NULL, NULL }; ++static struct hieth_netdev_priv hieth_priv; ++ ++/* real port count */ ++static int hieth_real_port_cnt; ++/* default, eth enable */ ++static bool hieth_disable; ++/* autoeee, enabled by dts */ ++static bool hieth_enable_autoeee; ++ ++static int __init hieth_noeth(char *str) ++{ ++ hieth_disable = true; ++ ++ return 0; ++} ++ ++early_param("noeth", hieth_noeth); ++ ++#include "pm.c" ++ ++static int hieth_hw_set_macaddress(struct hieth_netdev_priv *priv, ++ unsigned char *mac) ++{ ++ u32 reg; ++ ++ if (priv->port == HIETH_PORT_1) { ++ reg = hieth_readl(priv->glb_base, HIETH_GLB_DN_HOSTMAC_ENA); ++ reg |= HIETH_GLB_DN_HOSTMAC_ENA_BIT; ++ hieth_writel(priv->glb_base, reg, HIETH_GLB_DN_HOSTMAC_ENA); ++ } ++ ++ reg = mac[1] | (mac[0] << 8); ++ if (priv->port == HIETH_PORT_0) ++ hieth_writel(priv->glb_base, reg, HIETH_GLB_HOSTMAC_H16); ++ else ++ hieth_writel(priv->glb_base, reg, HIETH_GLB_DN_HOSTMAC_H16); ++ ++ reg = mac[5] | (mac[4] << 8) | (mac[3] << 16) | (mac[2] << 24); ++ if (priv->port == HIETH_PORT_0) ++ hieth_writel(priv->glb_base, reg, HIETH_GLB_HOSTMAC_L32); ++ else ++ hieth_writel(priv->glb_base, reg, HIETH_GLB_DN_HOSTMAC_L32); ++ ++ return 0; ++} ++ ++static void hieth_irq_enable(struct hieth_netdev_priv *priv, int irqs) ++{ ++ u32 val; ++ ++ local_lock(priv); ++ val = hieth_readl(priv->glb_base, HIETH_GLB_IRQ_ENA); ++ hieth_writel(priv->glb_base, val | irqs, HIETH_GLB_IRQ_ENA); ++ local_unlock(priv); ++} ++ ++static void hieth_irq_disable(struct hieth_netdev_priv *priv, int irqs) ++{ ++ u32 val; ++ ++ local_lock(priv); ++ val = hieth_readl(priv->glb_base, HIETH_GLB_IRQ_ENA); ++ hieth_writel(priv->glb_base, val & (~irqs), HIETH_GLB_IRQ_ENA); ++ local_unlock(priv); ++} ++ ++static void hieth_clear_irqstatus(struct hieth_netdev_priv *priv, int irqs) ++{ ++ local_lock(priv); ++ hieth_writel(priv->glb_base, irqs, HIETH_GLB_IRQ_RAW); ++ local_unlock(priv); ++} ++ ++static void hieth_set_flow_ctrl(struct hieth_netdev_priv *priv) ++{ ++ unsigned int pause_en; ++ unsigned int tx_flow_ctrl; ++ ++ tx_flow_ctrl = hieth_readl(priv->port_base, HIETH_P_GLB_FC_LEVEL); ++ tx_flow_ctrl &= ~BITS_THR_MASK; ++ tx_flow_ctrl |= priv->tx_pause_deactive_thresh; ++ tx_flow_ctrl &= ~(BITS_THR_MASK << BITS_FC_ACTIVE_THR_OFFSET); ++ tx_flow_ctrl |= priv->tx_pause_active_thresh << ++ BITS_FC_ACTIVE_THR_OFFSET; ++ ++ pause_en = hieth_readl(priv->port_base, HIETH_P_MAC_SET); ++ ++ if (priv->tx_pause_en) { ++ tx_flow_ctrl |= BIT_FC_EN; ++ pause_en |= BIT_PAUSE_EN; ++ } else { ++ tx_flow_ctrl &= ~BIT_FC_EN; ++ pause_en &= ~BIT_PAUSE_EN; ++ } ++ ++ hieth_writel(priv->port_base, tx_flow_ctrl, HIETH_P_GLB_FC_LEVEL); ++ ++ hieth_writel(priv->port_base, pause_en, HIETH_P_MAC_SET); ++} ++ ++static int hieth_port_reset(struct hieth_netdev_priv *priv) ++{ ++ u32 rst_bit = 0; ++ u32 val; ++ ++ if (hieth_real_port_cnt == 1) { ++ rst_bit = HIETH_GLB_SOFT_RESET_ALL; ++ } else { ++ if (priv->port == HIETH_PORT_0) ++ rst_bit |= HIETH_GLB_SOFT_RESET_P0; ++ else if (priv->port == HIETH_PORT_1) ++ rst_bit |= HIETH_GLB_SOFT_RESET_P1; ++ else ++ BUG(); ++ } ++ ++ val = hieth_readl(priv->glb_base, HIETH_GLB_SOFT_RESET); ++ ++ val |= rst_bit; ++ hieth_writel(priv->glb_base, val, HIETH_GLB_SOFT_RESET); ++ usleep_range(1000, 10000); ++ val &= ~rst_bit; ++ hieth_writel(priv->glb_base, val, HIETH_GLB_SOFT_RESET); ++ usleep_range(1000, 10000); ++ val |= rst_bit; ++ hieth_writel(priv->glb_base, val, HIETH_GLB_SOFT_RESET); ++ usleep_range(1000, 10000); ++ val &= ~rst_bit; ++ hieth_writel(priv->glb_base, val, HIETH_GLB_SOFT_RESET); ++ ++ return 0; ++} ++ ++static void hieth_port_init(struct hieth_netdev_priv *priv) ++{ ++ u32 val; ++ int phy_intf = (priv->phy_mode == PHY_INTERFACE_MODE_MII ? ++ HIETH_P_MAC_PORTSEL_MII : HIETH_P_MAC_PORTSEL_RMII); ++ ++ /* set little endian */ ++ val = hieth_readl(priv->glb_base, HIETH_GLB_ENDIAN_MOD); ++ val |= HIETH_GLB_ENDIAN_MOD_IN; ++ val |= HIETH_GLB_ENDIAN_MOD_OUT; ++ hieth_writel(priv->glb_base, val, HIETH_GLB_ENDIAN_MOD); ++ ++ /* set stat ctrl to cpuset, and MII or RMII mode */ ++ hieth_writel(priv->port_base, phy_intf | HIETH_P_MAC_PORTSEL_STAT_CPU, ++ HIETH_P_MAC_PORTSEL); ++ ++ /*clear all interrupt status */ ++ hieth_clear_irqstatus(priv, UD_BIT_NAME(HIETH_GLB_IRQ_ENA_BIT)); ++ ++ /*disable interrupts */ ++ hieth_irq_disable(priv, UD_BIT_NAME(HIETH_GLB_IRQ_ENA_BIT) | ++ UD_BIT_NAME(HIETH_GLB_IRQ_ENA_IEN)); ++ ++#ifdef HIETH_TSO_SUPPORTED ++ /* enable TSO debug for error handle */ ++ val = hieth_readl(priv->port_base, HIETH_P_GLB_TSO_DBG_EN); ++ val |= BITS_TSO_DBG_EN; ++ hieth_writel(priv->port_base, val, HIETH_P_GLB_TSO_DBG_EN); ++#endif ++ ++ /* disable vlan, enable UpEther<->CPU */ ++ val = hieth_readl(priv->glb_base, HIETH_GLB_FWCTRL); ++ val &= ~HIETH_GLB_FWCTRL_VLAN_ENABLE; ++ val |= UD_BIT_NAME(HIETH_GLB_FWCTRL_FW2CPU_ENA); ++ val &= ~(UD_BIT_NAME(HIETH_GLB_FWCTRL_FWALL2CPU)); ++ hieth_writel(priv->glb_base, val, HIETH_GLB_FWCTRL); ++ val = hieth_readl(priv->glb_base, HIETH_GLB_MACTCTRL); ++ val |= UD_BIT_NAME(HIETH_GLB_MACTCTRL_BROAD2CPU); ++ val |= UD_BIT_NAME(HIETH_GLB_MACTCTRL_MACT_ENA); ++ hieth_writel(priv->glb_base, val, HIETH_GLB_MACTCTRL); ++ ++ /* set pre count limit */ ++ val = hieth_readl(priv->port_base, HIETH_P_MAC_TX_IPGCTRL); ++ val &= ~HIETH_P_MAC_TX_IPGCTRL_PRE_CNT_LMT_MSK; ++ val |= 0; ++ hieth_writel(priv->port_base, val, HIETH_P_MAC_TX_IPGCTRL); ++ ++ /* set max receive length */ ++ val = hieth_readl(priv->port_base, HIETH_P_MAC_SET); ++ val &= ~HIETH_P_MAC_SET_LEN_MAX_MSK; ++ val |= HIETH_P_MAC_SET_LEN_MAX(HIETH_MAX_RCV_LEN); ++ hieth_writel(priv->port_base, val, HIETH_P_MAC_SET); ++ ++ hieth_set_flow_ctrl(priv); ++} ++ ++static void hieth_set_hwq_depth(struct hieth_netdev_priv *priv) ++{ ++ u32 val; ++ ++ val = hieth_readl(priv->port_base, HIETH_P_GLB_QLEN_SET); ++ val &= ~HIETH_P_GLB_QLEN_SET_TXQ_DEP_MSK; ++ val |= HIETH_P_GLB_QLEN_SET_TXQ_DEP(priv->depth.hw_xmitq); ++ val &= ~HIETH_P_GLB_QLEN_SET_RXQ_DEP_MSK; ++ val |= HIETH_P_GLB_QLEN_SET_RXQ_DEP(HIETH_MAX_QUEUE_DEPTH - ++ priv->depth.hw_xmitq); ++ hieth_writel(priv->port_base, val, HIETH_P_GLB_QLEN_SET); ++} ++ ++static int hieth_hw_xmitq_ready(struct hieth_netdev_priv *priv) ++{ ++ int ret; ++ ++ local_lock(priv); ++ ret = hieth_readl(priv->port_base, HIETH_P_GLB_RO_QUEUE_STAT); ++ ret &= HIETH_P_GLB_RO_QUEUE_STAT_XMITQ_RDY_MSK; ++ local_unlock(priv); ++ ++ return ret; ++} ++ ++#ifdef HIETH_TSO_SUPPORTED ++#ifdef HIETH_TSO_DEBUG ++unsigned int id_send; ++unsigned int id_free; ++struct send_pkt_info pkt_rec[MAX_RECORD]; ++#endif ++#endif ++ ++#ifdef HIETH_TSO_SUPPORTED ++static struct sk_buff *hieth_xmit_release_gso(struct hieth_netdev_priv *priv) ++{ ++ struct sk_buff *skb; ++ struct tx_pkt_info *txq_cur; ++ int pkt_type; ++ ++ txq_cur = priv->txq + priv->txq_tail; ++ ++ skb = txq_cur->skb; ++ ++ if (txq_cur->tx.info.sg_flag) ++ pkt_type = PKT_SG; ++ else ++ pkt_type = PKT_NORMAL; ++ ++ if (pkt_type == PKT_NORMAL) { ++ dma_addr_t dma_addr; ++ ++ dma_addr = txq_cur->tx_addr; ++ dma_unmap_single(priv->dev, dma_addr, skb->len, DMA_TO_DEVICE); ++ } else { /* TSO pkt */ ++ struct dma_tx_desc *desc_cur; ++ unsigned int desc_offset; ++ int nfrags = skb_shinfo(skb)->nr_frags; ++ int i; ++ ++ desc_offset = txq_cur->sg_desc_offset; ++ BUG_ON(desc_offset != priv->sg_tail); ++ desc_cur = priv->dma_tx + desc_offset; ++ ++ dma_unmap_single(priv->dev, desc_cur->linear_addr, ++ desc_cur->linear_len, DMA_TO_DEVICE); ++ for (i = 0; i < nfrags; i++) { ++ dma_unmap_page(priv->dev, desc_cur->frags[i].addr, ++ desc_cur->frags[i].size, DMA_TO_DEVICE); ++ } ++ ++ priv->sg_tail = (priv->sg_tail + 1) % priv->q_size; ++ } ++ ++ txq_cur->skb = NULL; ++ priv->txq_tail = (priv->txq_tail + 1) % priv->q_size; ++ ++#ifdef HIETH_TSO_DEBUG ++ pkt_rec[id_free].status = 0; ++ id_free++; ++ if (id_free == MAX_RECORD) ++ id_free = 0; ++#endif ++ ++ return skb; ++} ++#endif ++ ++static int hieth_xmit_release_skb(struct hieth_netdev_priv *priv) ++{ ++ u32 val; ++ int ret = 0; ++ struct sk_buff *skb; ++ ++ local_lock(priv); ++ ++ val = hieth_readl(priv->port_base, HIETH_P_GLB_RO_QUEUE_STAT) & ++ HIETH_P_GLB_RO_QUEUE_STAT_XMITQ_CNT_INUSE_MSK; ++ while (val < priv->tx_hw_cnt) { ++#ifdef HIETH_TSO_SUPPORTED ++ skb = hieth_xmit_release_gso(priv); ++#else ++ skb = skb_dequeue(&priv->tx_hw); ++#endif ++ ++ if (!skb) { ++ pr_err("hw_xmitq_cnt_inuse=%d, tx_hw_cnt=%d\n", ++ val, priv->tx_hw_cnt); ++ ++ BUG(); ++ ret = -1; ++ goto error_exit; ++ } ++ dev_kfree_skb_any(skb); ++ ++ priv->tx_hw_cnt--; ++ ++ val = hieth_readl(priv->port_base, HIETH_P_GLB_RO_QUEUE_STAT) & ++ HIETH_P_GLB_RO_QUEUE_STAT_XMITQ_CNT_INUSE_MSK; ++ } ++ ++error_exit: ++ local_unlock(priv); ++ return ret; ++} ++ ++#ifdef HIETH_TSO_SUPPORTED ++void hieth_get_tso_err_info(struct hieth_netdev_priv *priv) ++{ ++ unsigned int reg_addr, reg_tx_info, reg_tx_err; ++ unsigned int sg_index; ++ struct dma_tx_desc *sg_desc; ++ int *sg_word; ++ int i; ++ ++ reg_addr = hieth_readl(priv->port_base, HIETH_P_GLB_TSO_DBG_ADDR); ++ reg_tx_info = hieth_readl(priv->port_base, HIETH_P_GLB_TSO_DBG_TX_INFO); ++ reg_tx_err = hieth_readl(priv->port_base, HIETH_P_GLB_TSO_DBG_TX_ERR); ++ ++ WARN(1, "tx err=0x%x, tx_info=0x%x, addr=0x%x\n", ++ reg_tx_err, reg_tx_info, reg_addr); ++ ++ sg_index = (reg_addr - priv->dma_tx_phy) / sizeof(struct dma_tx_desc); ++ sg_desc = priv->dma_tx + sg_index; ++ sg_word = (int *)sg_desc; ++ for (i = 0; i < sizeof(struct dma_tx_desc) / sizeof(int); i++) ++ pr_err("%s,%d: sg_desc word[%d]=0x%x\n", ++ __func__, __LINE__, i, sg_word[i]); ++ ++ /* restart MAC to transmit next packet */ ++ hieth_irq_disable(priv, UD_BIT_NAME(HIETH_INT_TX_ERR)); ++#if 0 ++ hieth_readl(priv, HIETH_P_GLB_TSO_DBG_STATE)); ++ hieth_irq_enable(priv, UD_BIT_NAME(HIETH_INT_TX_ERR)); ++#endif ++} ++ ++static int hieth_xmit_real_send(struct hieth_netdev_priv *priv, ++ struct sk_buff *skb); ++ ++static int hieth_sw_gso(struct hieth_netdev_priv *priv, struct sk_buff *skb) ++{ ++ struct sk_buff *segs, *curr_skb; ++ struct net_device *ndev = hieth_devs_save[priv->port]; ++ int ret; ++ ++ segs = skb_gso_segment(skb, ndev->features & ++ ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_SOFTWARE)); ++ if (IS_ERR(segs)) ++ goto sw_tso_end; ++ ++ do { ++ curr_skb = segs; ++ segs = segs->next; ++ curr_skb->next = NULL; ++ ret = hieth_xmit_real_send(priv, curr_skb); ++ if (ret < 0) { ++ dev_kfree_skb(curr_skb); ++ while (segs != NULL) { ++ curr_skb = segs; ++ segs = segs->next; ++ curr_skb->next = NULL; ++ dev_kfree_skb(curr_skb); ++ } ++ goto sw_tso_end; ++ } ++ } while (segs); ++ ++sw_tso_end: ++ dev_kfree_skb(skb); ++ ++ return 0; ++} ++ ++int hieth_get_pkt_info(struct sk_buff *skb, struct tx_pkt_info *txq_cur) ++{ ++ int nfrags = skb_shinfo(skb)->nr_frags; ++ ++ __be16 l3_proto; /* level 3 protocol */ ++ unsigned int l4_proto = IPPROTO_MAX; ++ unsigned char coe_enable = 0; ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) ++ coe_enable = 1; ++ ++ txq_cur->tx.val = 0; ++ ++ if (skb_is_gso(skb)) { ++ txq_cur->tx.info.tso_flag = 1; ++ txq_cur->tx.info.sg_flag = 1; ++ } else if (nfrags) { ++ txq_cur->tx.info.sg_flag = 1; ++ } ++ ++ /* deal with VLAN flag */ ++ l3_proto = skb->protocol; ++ if (skb->protocol == htons(ETH_P_8021Q)) { ++ struct vlan_hdr *vhdr; ++ ++ vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN); ++ l3_proto = vhdr->h_vlan_encapsulated_proto; ++ txq_cur->tx.info.vlan_flag = 1; ++ } ++ ++ if (l3_proto == htons(ETH_P_IP)) { ++ const struct iphdr *iph; ++ ++ iph = ip_hdr(skb); ++ txq_cur->tx.info.ip_ver = PKT_IPV4; ++ txq_cur->tx.info.ip_hdr_len = iph->ihl; ++ ++ l4_proto = iph->protocol; ++ l4_proto = l4_proto & (MAX_INET_PROTOS - 1); ++ } else if (l3_proto == htons(ETH_P_IPV6)) { ++ const struct ipv6hdr *hdr; ++ ++ txq_cur->tx.info.ip_ver = PKT_IPV6; ++ txq_cur->tx.info.ip_hdr_len = PKT_IPV6_HDR_LEN; ++ hdr = ipv6_hdr(skb); ++ l4_proto = hdr->nexthdr; ++ } else { ++ coe_enable = 0; ++ /* pr_err("unknown level 3 prot=0x%x\n", ntohs(l3_proto)); */ ++ } ++ ++ if (l4_proto == IPPROTO_TCP) { ++ const struct tcphdr *th; ++ ++ th = tcp_hdr(skb); ++ txq_cur->tx.info.prot_type = PKT_TCP; ++ txq_cur->tx.info.prot_hdr_len = th->doff; ++ } else if (l4_proto == IPPROTO_UDP) { ++ txq_cur->tx.info.prot_type = PKT_UDP; ++ txq_cur->tx.info.prot_hdr_len = PKT_UDP_HDR_LEN; ++ } else { ++ coe_enable = 0; ++ /* pr_err("unknown level 4 prot=0x%x\n", l4_proto); */ ++ /* TODO: when l3_proto == ETH_P_IPV6, ++ * we need to deal with IPV6 next header. ++ */ ++ if (l3_proto == htons(ETH_P_IPV6) && ++ skb->ip_summed == CHECKSUM_PARTIAL) ++ return -E_MAC_SW_GSO; ++ } ++ ++ if (skb_is_gso(skb)) ++ txq_cur->tx.info.data_len ++ = skb_shinfo(skb)->gso_size; ++ else ++ txq_cur->tx.info.data_len = skb->len + FCS_BYTES; ++ ++ if (coe_enable) { ++ if (skb_is_gso(skb) && (l4_proto == IPPROTO_UDP)) { ++ /* TODO: self checksum caculate */ ++ int offset; ++ __wsum csum; ++ __sum16 udp_csum; ++ ++ offset = skb_checksum_start_offset(skb); ++ WARN_ON(offset >= skb_headlen(skb)); ++ csum = skb_checksum(skb, offset, skb->len - offset, 0); ++ ++ offset += skb->csum_offset; ++ BUG_ON(offset + sizeof(__sum16) > skb_headlen(skb)); ++ udp_csum = csum_fold(csum); ++ if (udp_csum == 0) ++ udp_csum = CSUM_MANGLED_0; ++ ++ *(__sum16 *)(skb->data + offset) = udp_csum; ++ ++ skb->ip_summed = CHECKSUM_NONE; ++ coe_enable = 0; ++ } ++ } ++ ++ txq_cur->tx.info.coe_flag = coe_enable; ++ txq_cur->tx.info.nfrags_num = nfrags; ++ ++ return 0; ++} ++ ++int hieth_xmit_gso(struct hieth_netdev_priv *priv, struct sk_buff *skb) ++{ ++ int pkt_type = PKT_NORMAL; ++ int nfrags = skb_shinfo(skb)->nr_frags; ++ struct tx_pkt_info *txq_cur; ++ int ret; ++ ++ if (((priv->txq_head + 1) % priv->q_size) == priv->txq_tail) { ++ /* txq full, stop */ ++ pr_err("WARNING: txq full."); ++ return -E_MAC_TX_FAIL; ++ } ++ ++ if (skb_is_gso(skb) || nfrags) { ++ /* TSO pkt or SG pkt */ ++ pkt_type = PKT_SG; ++ } else { /* Normal pkt */ ++ pkt_type = PKT_NORMAL; ++ } ++ ++ txq_cur = priv->txq + priv->txq_head; ++ ++ ret = hieth_get_pkt_info(skb, txq_cur); ++ if (unlikely(ret < 0)) ++ return ret; ++ ++ if (pkt_type == PKT_NORMAL) { ++ txq_cur->tx_addr = dma_map_single(priv->dev, skb->data, ++ skb->len, DMA_TO_DEVICE); ++ } else { /* TSO pkt */ ++ struct dma_tx_desc *desc_cur; ++ int i; ++ ++ if (unlikely(((priv->sg_head + 1) % priv->q_size) == ++ priv->sg_tail)) { ++ /* SG pkt, but sg desc all used */ ++ pr_err("WARNING: sg desc all used."); ++ return -E_MAC_TX_FAIL; ++ } ++ ++ desc_cur = priv->dma_tx + priv->sg_head; ++ ++ /* TODO: deal with ipv6_id */ ++ if (txq_cur->tx.info.tso_flag && ++ txq_cur->tx.info.ip_ver == PKT_IPV6 && ++ txq_cur->tx.info.prot_type == PKT_UDP) { ++ desc_cur->ipv6_id = ntohl(skb_shinfo(skb)->ip6_frag_id); ++ } ++ ++ desc_cur->total_len = skb->len; ++ desc_cur->linear_len = skb_headlen(skb); ++ desc_cur->linear_addr = dma_map_single(priv->dev, skb->data, ++ desc_cur->linear_len, DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(priv->dev, ++ desc_cur->linear_addr))) { ++ pr_err("DMA Mapping fail."); ++ return -E_MAC_TX_FAIL; ++ } ++ ++ for (i = 0; i < nfrags; i++) { ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ int len = frag->size; ++ ++ WARN(len <= 0, "%s,%d: frag %d size %d. desc=%x\n", ++ __func__, __LINE__, i, len, txq_cur->tx.val); ++ ++ desc_cur->frags[i].addr = ++ skb_frag_dma_map(priv->dev, frag, 0, ++ len, DMA_TO_DEVICE); ++ ret = dma_mapping_error(priv->dev, ++ desc_cur->frags[i].addr); ++ if (unlikely(ret)) { ++ pr_err("skb frag DMA Mapping fail."); ++ return -E_MAC_TX_FAIL; ++ } ++ desc_cur->frags[i].size = len; ++ } ++ ++ txq_cur->tx_addr = priv->dma_tx_phy + ++ priv->sg_head * sizeof(struct dma_tx_desc); ++ txq_cur->sg_desc_offset = priv->sg_head; ++ ++ priv->sg_head = (priv->sg_head + 1) % priv->q_size; ++ ++ /* Ensure desc info writen to memory before config hardware */ ++ wmb(); ++ } ++ ++ txq_cur->skb = skb; ++ priv->txq_head = (priv->txq_head + 1) % priv->q_size; ++ ++#ifdef HIETH_TSO_DEBUG ++ pkt_rec[id_send].reg_addr = txq_cur->tx_addr; ++ pkt_rec[id_send].reg_pkt_info = txq_cur->tx.val; ++ pkt_rec[id_send].status = 1; ++ id_send++; ++ if (id_send == MAX_RECORD) ++ id_send = 0; ++#endif ++ hieth_writel(priv->port_base, txq_cur->tx_addr, HIETH_P_GLB_EQ_ADDR); ++ hieth_writel(priv->port_base, txq_cur->tx.val, HIETH_P_GLB_EQFRM_LEN); ++ ++ return 0; ++} ++#endif ++ ++static int hieth_xmit_real_send(struct hieth_netdev_priv *priv, ++ struct sk_buff *skb) ++{ ++ int ret = 0; ++ u32 val; ++ ++ local_lock(priv); ++ ++ val = hieth_readl(priv->port_base, HIETH_P_GLB_RO_QUEUE_STAT); ++ val &= HIETH_P_GLB_RO_QUEUE_STAT_XMITQ_RDY_MSK; ++ if (!val) { ++ pr_err("hw xmit queue is not ready\n"); ++ ret = -1; ++ goto _trans_exit; ++ } ++ ++#ifdef HIETH_TSO_SUPPORTED ++ /* TSO supported */ ++ ret = hieth_xmit_gso(priv, skb); ++ if (unlikely(ret < 0)) { ++ if (ret == -E_MAC_SW_GSO) { ++ local_unlock(priv); ++ return hieth_sw_gso(priv, skb); ++ } ++ ret = -1; ++ goto _trans_exit; ++ } ++#else ++ dma_map_single(priv->dev, skb->data, skb->len, DMA_TO_DEVICE); ++ /* for recalc CRC, 4 bytes more is needed */ ++ hieth_writel(priv->port_base, virt_to_phys(skb->data), ++ HIETH_P_GLB_EQ_ADDR); ++ hieth_writel(priv->port_base, skb->len + FCS_BYTES, ++ HIETH_P_GLB_EQFRM_LEN); ++ ++ skb_queue_tail(&priv->tx_hw, skb); ++#endif ++ ++ priv->tx_hw_cnt++; ++ ++_trans_exit: ++ local_unlock(priv); ++ ++ return ret; ++} ++ ++static struct sk_buff *hieth_platdev_alloc_skb(struct hieth_netdev_priv *priv) ++{ ++ struct sk_buff *skb; ++ ++ skb = priv->rx_pool.sk_pool[priv->rx_pool.next_free_skb++]; ++ ++ if (priv->rx_pool.next_free_skb == CONFIG_HIETH_MAX_RX_POOLS) ++ priv->rx_pool.next_free_skb = 0; ++ ++ /*current skb is used by kernel or other process,find another skb*/ ++ if (skb_shared(skb) || (atomic_read(&(skb_shinfo(skb)->dataref)) > 1)) { ++ int i; ++ ++ for (i = 0; i < CONFIG_HIETH_MAX_RX_POOLS; i++) { ++ skb = priv->rx_pool.sk_pool[priv-> ++ rx_pool.next_free_skb++]; ++ if (priv->rx_pool.next_free_skb == ++ CONFIG_HIETH_MAX_RX_POOLS) ++ priv->rx_pool.next_free_skb = 0; ++ ++ if ((skb_shared(skb) == 0) && ++ (atomic_read(&(skb_shinfo(skb)->dataref)) <= 1)) ++ break; ++ } ++ ++ if (i == CONFIG_HIETH_MAX_RX_POOLS) { ++ priv->stat.rx_pool_dry_times++; ++ pr_debug("%ld: no free skb\n", ++ priv->stat.rx_pool_dry_times); ++ skb = dev_alloc_skb(SKB_SIZE); ++ return skb; ++ } ++ } ++ memset(skb, 0, offsetof(struct sk_buff, tail)); ++ ++ skb->data = skb->head; ++ skb->tail = skb->head; ++ ++ skb_reserve(skb, NET_SKB_PAD); ++ skb->len = 0; ++ skb->data_len = 0; ++ skb->cloned = 0; ++ atomic_inc(&skb->users); ++ return skb; ++} ++ ++static int hieth_feed_hw(struct hieth_netdev_priv *priv) ++{ ++ struct sk_buff *skb; ++ int cnt = 0; ++ int rx_head_len; ++ ++ /* if skb occupied too much, then do not alloc any more. */ ++ rx_head_len = skb_queue_len(&priv->rx_head); ++ if (rx_head_len > HIETH_MAX_RX_HEAD_LEN) ++ return 0; ++ ++ local_lock(priv); ++ ++ while (hieth_readl(priv->port_base, HIETH_P_GLB_RO_QUEUE_STAT) & ++ HIETH_P_GLB_RO_QUEUE_STAT_RECVQ_RDY_MSK) { ++ skb = hieth_platdev_alloc_skb(priv); ++ if (!skb) ++ break; ++ ++ dma_map_single(priv->dev, skb->data, HIETH_MAX_FRAME_SIZE, ++ DMA_FROM_DEVICE); ++ hieth_writel(priv->port_base, virt_to_phys(skb->data + 2), ++ HIETH_P_GLB_IQ_ADDR); ++ skb_queue_tail(&priv->rx_hw, skb); ++ cnt++; ++ } ++ ++ local_unlock(priv); ++ return cnt; ++} ++ ++static int hieth_hw_recv_tryup(struct hieth_netdev_priv *priv) ++{ ++ struct sk_buff *skb; ++ u32 rlen; ++ int cnt = 0; ++ uint32_t rx_pkt_info; ++#ifdef HIETH_RXCSUM_SUPPORTED ++ struct net_device *ndev = hieth_devs_save[priv->port]; ++ int hdr_csum_done, hdr_csum_err; ++ int payload_csum_done, payload_csum_err; ++#endif ++ ++ local_lock(priv); ++ ++ while ((hieth_readl(priv->glb_base, HIETH_GLB_IRQ_RAW) & ++ (UD_BIT_NAME(HIETH_GLB_IRQ_INT_RX_RDY)))) { ++ rx_pkt_info = hieth_readl(priv->port_base, ++ HIETH_P_GLB_RO_IQFRM_DES); ++ rlen = rx_pkt_info & HIETH_P_GLB_RO_IQFRM_DES_FDIN_LEN_MSK; ++ rlen -= 4; /* remove FCS 4Bytes */ ++ ++ /* hw set rx pkg finish */ ++ hieth_writel(priv->glb_base, ++ UD_BIT_NAME(HIETH_GLB_IRQ_INT_RX_RDY), ++ HIETH_GLB_IRQ_RAW); ++ ++ skb = skb_dequeue(&priv->rx_hw); ++ if (!skb) { ++ pr_err("chip told receive pkg, but no packet find!\n"); ++ BUG(); ++ break; ++ } ++ ++ dma_map_single(priv->dev, skb->data, HIETH_MAX_FRAME_SIZE, ++ DMA_FROM_DEVICE); ++ skb_reserve(skb, 2); ++ skb_put(skb, rlen); ++#ifdef HIETH_RXCSUM_SUPPORTED ++ skb->ip_summed = CHECKSUM_NONE; ++ if (ndev->features & NETIF_F_RXCSUM) { ++ hdr_csum_done = ++ (rx_pkt_info >> BITS_HEADER_DONE_OFFSET) & ++ BITS_HEADER_DONE_MASK; ++ payload_csum_done = ++ (rx_pkt_info >> BITS_PAYLOAD_DONE_OFFSET) & ++ BITS_PAYLOAD_DONE_MASK; ++ hdr_csum_err = ++ (rx_pkt_info >> BITS_HEADER_ERR_OFFSET) & ++ BITS_HEADER_ERR_MASK; ++ payload_csum_err = ++ (rx_pkt_info >> BITS_PAYLOAD_ERR_OFFSET) & ++ BITS_PAYLOAD_ERR_MASK; ++ ++ if (hdr_csum_done && payload_csum_done) { ++ if (unlikely(hdr_csum_err || ++ payload_csum_err)) { ++ priv->stats.rx_errors++; ++ priv->stats.rx_crc_errors++; ++ dev_kfree_skb_any(skb); ++ continue; ++ } else { ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ } ++ } ++ } ++#endif ++ skb_queue_tail(&priv->rx_head, skb); ++ cnt++; ++ } ++ ++ local_unlock(priv); ++ ++ /* fill hardware receive queue again */ ++ hieth_feed_hw(priv); ++ return cnt; ++} ++ ++static void hieth_adjust_link(struct net_device *dev) ++{ ++ int stat = 0; ++ struct hieth_netdev_priv *priv = netdev_priv(dev); ++ ++ stat |= (priv->phy->link) ? HIETH_P_MAC_PORTSET_LINKED : 0; ++ stat |= (priv->phy->duplex == DUPLEX_FULL) ? ++ HIETH_P_MAC_PORTSET_DUP_FULL : 0; ++ stat |= (priv->phy->speed == SPEED_100) ? ++ HIETH_P_MAC_PORTSET_SPD_100M : 0; ++ ++ if ((stat != priv->link_stat) && ++ ((stat | priv->link_stat) & HIETH_P_MAC_PORTSET_LINKED)) { ++ hieth_writel(priv->port_base, stat, HIETH_P_MAC_PORTSET); ++ phy_print_status(priv->phy); ++ priv->link_stat = stat; ++ ++ priv->tx_pause_en = priv->phy->pause; ++ hieth_set_flow_ctrl(priv); ++ ++ if (hieth_enable_autoeee) ++ hieth_autoeee_init(priv, stat); ++ } ++} ++ ++static int hieth_init_skb_buffers(struct hieth_netdev_priv *priv) ++{ ++ int i; ++ struct sk_buff *skb; ++ ++ for (i = 0; i < CONFIG_HIETH_MAX_RX_POOLS; i++) { ++ skb = dev_alloc_skb(SKB_SIZE); ++ if (!skb) ++ break; ++ priv->rx_pool.sk_pool[i] = skb; ++ } ++ ++ if (i < CONFIG_HIETH_MAX_RX_POOLS) { ++ pr_err("no mem\n"); ++ for (i--; i > 0; i--) ++ dev_kfree_skb_any(priv->rx_pool.sk_pool[i]); ++ return -ENOMEM; ++ } ++ ++ priv->rx_pool.next_free_skb = 0; ++ priv->stat.rx_pool_dry_times = 0; ++ return 0; ++} ++ ++static void hieth_destroy_skb_buffers(struct hieth_netdev_priv *priv) ++{ ++ int i; ++ ++ for (i = 0; i < CONFIG_HIETH_MAX_RX_POOLS; i++) ++ dev_kfree_skb_any(priv->rx_pool.sk_pool[i]); ++ ++ priv->rx_pool.next_free_skb = 0; ++ priv->stat.rx_pool_dry_times = 0; ++} ++ ++static void hieth_bfproc_recv(unsigned long data) ++{ ++ int ret = 0; ++ struct net_device *dev = (void *)data; ++ struct hieth_netdev_priv *priv = netdev_priv(dev); ++ struct sk_buff *skb; ++ ++ hieth_hw_recv_tryup(priv); ++ ++ while ((skb = skb_dequeue(&priv->rx_head)) != NULL) { ++ skb->protocol = eth_type_trans(skb, dev); ++ if (HIETH_INVALID_RXPKG_LEN(skb->len)) { ++ pr_err("pkg len error\n"); ++ priv->stats.rx_errors++; ++ priv->stats.rx_length_errors++; ++ dev_kfree_skb_any(skb); ++ continue; ++ } ++ ++ priv->stats.rx_packets++; ++ priv->stats.rx_bytes += skb->len; ++ dev->last_rx = jiffies; ++ skb->dev = dev; ++ ++ ret = netif_rx(skb); ++ if (ret) ++ priv->stats.rx_dropped++; ++ } ++} ++ ++static void hieth_net_isr_proc(struct net_device *ndev, int ints) ++{ ++ struct hieth_netdev_priv *priv = netdev_priv(ndev); ++ ++ if ((ints & UD_BIT_NAME(HIETH_GLB_IRQ_INT_MULTI_RXRDY)) && ++ (hieth_hw_recv_tryup(priv) > 0)) { ++ tasklet_schedule(&priv->bf_recv); ++ } ++ ++ if (ints & UD_BIT_NAME(HIETH_GLB_IRQ_INT_TXQUE_RDY)) { ++ hieth_irq_disable(priv, ++ UD_BIT_NAME(HIETH_GLB_IRQ_INT_TXQUE_RDY)); ++ netif_wake_queue(ndev); ++ } ++} ++ ++static irqreturn_t hieth_net_isr(int irq, void *dev_id) ++{ ++ int ints; ++ struct net_device *dev = (struct net_device *)dev_id; ++ struct hieth_netdev_priv *priv = netdev_priv(dev); ++ ++ /*mask the all interrupt */ ++ hieth_irq_disable(priv, HIETH_GLB_IRQ_ENA_IEN_A); ++ ++ ints = hieth_readl(priv->glb_base, HIETH_GLB_IRQ_STAT); ++ ++ if ((HIETH_PORT_0 == priv->port) && ++ likely(ints & HIETH_GLB_IRQ_ENA_BIT_U)) { ++ hieth_net_isr_proc(dev, (ints & HIETH_GLB_IRQ_ENA_BIT_U)); ++ hieth_clear_irqstatus(priv, (ints & HIETH_GLB_IRQ_ENA_BIT_U)); ++ ints &= ~HIETH_GLB_IRQ_ENA_BIT_U; ++ } ++ ++ if ((HIETH_PORT_1 == priv->port) && ++ likely(ints & HIETH_GLB_IRQ_ENA_BIT_D)) { ++ hieth_net_isr_proc(dev, (ints & HIETH_GLB_IRQ_ENA_BIT_D)); ++ hieth_clear_irqstatus(priv, (ints & HIETH_GLB_IRQ_ENA_BIT_D)); ++ ints &= ~HIETH_GLB_IRQ_ENA_BIT_D; ++ } ++#ifdef HIETH_TSO_SUPPORTED ++ if (unlikely(ints & HIETH_INT_TX_ERR_U)) { ++ priv = netdev_priv(hieth_devs_save[HIETH_PORT_0]); ++ ++ hieth_get_tso_err_info(priv); ++ ints &= ~HIETH_INT_TX_ERR_U; ++ } ++ ++ if (unlikely(ints & HIETH_INT_TX_ERR_D)) { ++ priv = netdev_priv(hieth_devs_save[HIETH_PORT_1]); ++ ++ hieth_get_tso_err_info(priv); ++ ints &= ~HIETH_INT_TX_ERR_D; ++ } ++#endif ++ ++ /*unmask the all interrupt */ ++ hieth_irq_enable(priv, HIETH_GLB_IRQ_ENA_IEN_A); ++ ++ return IRQ_HANDLED; ++} ++ ++static void hieth_monitor_func(unsigned long arg) ++{ ++ struct net_device *dev = (struct net_device *)arg; ++ struct hieth_netdev_priv *priv = netdev_priv(dev); ++ ++ if (!priv || !netif_running(dev)) { ++ pr_debug("network driver is stopped.\n"); ++ return; ++ } ++ ++ hieth_feed_hw(priv); ++ hieth_xmit_release_skb(priv); ++ ++ priv->monitor.expires = ++ jiffies + msecs_to_jiffies(HIETH_MONITOR_TIMER); ++ add_timer(&priv->monitor); ++} ++ ++static int hieth_net_open(struct net_device *dev) ++{ ++ int ret = 0; ++ struct cpumask cpumask; ++ struct hieth_netdev_priv *priv = netdev_priv(dev); ++ ++ ret = request_irq(dev->irq, hieth_net_isr, IRQF_SHARED, ++ dev->name, dev); ++ if (ret) { ++ pr_err("request_irq %d failed!\n", dev->irq); ++ return ret; ++ } ++ ++ /* set irq affinity */ ++ if ((num_online_cpus() > 1) && cpu_online(HIETH_IRQ_AFFINITY_CPU)) { ++ cpumask_clear(&cpumask); ++ cpumask_set_cpu(HIETH_IRQ_AFFINITY_CPU, &cpumask); ++ irq_set_affinity(dev->irq, &cpumask); ++ } ++ ++ if (!is_valid_ether_addr(dev->dev_addr)) ++ random_ether_addr(dev->dev_addr); ++ ++ hieth_hw_set_macaddress(priv, dev->dev_addr); ++ ++ /* init tasklet */ ++ priv->bf_recv.next = NULL; ++ priv->bf_recv.state = 0; ++ priv->bf_recv.func = hieth_bfproc_recv; ++ priv->bf_recv.data = (unsigned long)dev; ++ atomic_set(&priv->bf_recv.count, 0); ++ ++ /* setup hardware */ ++ hieth_set_hwq_depth(priv); ++ hieth_clear_irqstatus(priv, UD_BIT_NAME(HIETH_GLB_IRQ_ENA_BIT)); ++ ++ netif_carrier_off(dev); ++ hieth_feed_hw(priv); ++ netif_start_queue(dev); ++ ++ priv->link_stat = 0; ++ if (priv->phy) ++ phy_start(priv->phy); ++ ++ hieth_irq_enable(priv, UD_BIT_NAME(HIETH_GLB_IRQ_INT_MULTI_RXRDY) | ++ UD_BIT_NAME(HIETH_GLB_IRQ_ENA_IEN) | ++ HIETH_GLB_IRQ_ENA_IEN_A); ++#ifdef HIETH_TSO_SUPPORTED ++ hieth_irq_enable(priv, UD_BIT_NAME(HIETH_INT_TX_ERR)); ++#endif ++ ++ priv->monitor.expires = ++ jiffies + msecs_to_jiffies(HIETH_MONITOR_TIMER); ++ add_timer(&priv->monitor); ++ ++ return 0; ++} ++ ++static int hieth_net_close(struct net_device *dev) ++{ ++ struct hieth_netdev_priv *priv = netdev_priv(dev); ++#ifdef HIETH_TSO_SUPPORTED ++ struct sk_buff *skb = NULL; ++#endif ++ ++ hieth_irq_disable(priv, UD_BIT_NAME(HIETH_GLB_IRQ_INT_MULTI_RXRDY)); ++#ifdef HIETH_TSO_SUPPORTED ++ hieth_irq_disable(priv, UD_BIT_NAME(HIETH_INT_TX_ERR)); ++#endif ++ ++ if (priv->phy) ++ phy_stop(priv->phy); ++ ++ del_timer_sync(&priv->monitor); ++ ++ /* delete tasklet */ ++ tasklet_kill(&priv->bf_recv); ++ ++ /* reset and init port */ ++ hieth_port_reset(priv); ++ ++ skb_queue_purge(&priv->rx_head); ++ skb_queue_purge(&priv->rx_hw); ++#ifdef HIETH_TSO_SUPPORTED ++ while (priv->txq_tail != priv->txq_head) { ++ skb = hieth_xmit_release_gso(priv); ++ BUG_ON(skb == NULL); ++ kfree_skb(skb); ++ } ++#else ++ skb_queue_purge(&priv->tx_hw); ++#endif ++ priv->tx_hw_cnt = 0; ++ ++ free_irq(dev->irq, dev); ++ return 0; ++} ++ ++static void hieth_net_timeout(struct net_device *dev) ++{ ++ pr_err("tx timeout\n"); ++} ++ ++static int hieth_net_hard_start_xmit(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ int ret; ++ struct hieth_netdev_priv *priv = netdev_priv(dev); ++ ++ hieth_xmit_release_skb(priv); ++ ++ ret = hieth_xmit_real_send(priv, skb); ++ if (ret < 0) { ++ priv->stats.tx_dropped++; ++ return NETDEV_TX_BUSY; ++ } ++ ++ dev->trans_start = jiffies; ++ priv->stats.tx_packets++; ++ priv->stats.tx_bytes += skb->len; ++ hieth_clear_irqstatus(priv, UD_BIT_NAME(HIETH_GLB_IRQ_INT_TXQUE_RDY)); ++ ++ if (!hieth_hw_xmitq_ready(priv)) { ++ netif_stop_queue(dev); ++ hieth_irq_enable(priv, ++ UD_BIT_NAME(HIETH_GLB_IRQ_INT_TXQUE_RDY)); ++ } ++ ++ return NETDEV_TX_OK; ++} ++ ++static struct net_device_stats *hieth_net_get_stats(struct net_device *dev) ++{ ++ struct hieth_netdev_priv *priv = netdev_priv(dev); ++ ++ return &priv->stats; ++} ++ ++static int hieth_net_set_mac_address(struct net_device *dev, void *p) ++{ ++ struct hieth_netdev_priv *priv = netdev_priv(dev); ++ struct sockaddr *skaddr = p; ++ ++ if (!is_valid_ether_addr(skaddr->sa_data)) ++ return -EADDRNOTAVAIL; ++ ++ memcpy(dev->dev_addr, skaddr->sa_data, dev->addr_len); ++ dev->addr_assign_type &= ~NET_ADDR_RANDOM; ++ ++ hieth_hw_set_macaddress(priv, dev->dev_addr); ++ ++ return 0; ++} ++ ++static inline void hieth_enable_mac_addr_filter(struct hieth_netdev_priv *priv, ++ unsigned int reg_n, int enable) ++{ ++ u32 val; ++ ++ val = hieth_readl(priv->glb_base, GLB_MAC_H16(priv->port, reg_n)); ++ if (enable) ++ val |= UD_BIT_NAME(HIETH_GLB_MACFLT_ENA); ++ else ++ val &= ~(UD_BIT_NAME(HIETH_GLB_MACFLT_ENA)); ++ hieth_writel(priv->glb_base, val, GLB_MAC_H16(priv->port, reg_n)); ++} ++ ++static void hieth_set_mac_addr(struct hieth_netdev_priv *priv, u8 addr[6], ++ unsigned int high, unsigned int low) ++{ ++ u32 val; ++ u32 data; ++ ++ val = hieth_readl(priv->glb_base, high); ++ val |= UD_BIT_NAME(HIETH_GLB_MACFLT_ENA); ++ hieth_writel(priv->glb_base, val, high); ++ ++ val &= ~HIETH_GLB_MACFLT_HI16; ++ val |= ((addr[0] << 8) | addr[1]); ++ hieth_writel(priv->glb_base, val, high); ++ ++ data = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; ++ hieth_writel(priv->glb_base, data, low); ++ ++ val |= UD_BIT_NAME(HIETH_GLB_MACFLT_FW2CPU); ++ hieth_writel(priv->glb_base, val, high); ++} ++ ++static inline void hieth_set_mac_addr_filter(struct hieth_netdev_priv *priv, ++ unsigned char *addr, ++ unsigned int reg_n) ++{ ++ hieth_set_mac_addr(priv, addr, GLB_MAC_H16(priv->port, reg_n), ++ GLB_MAC_L32(priv->port, reg_n)); ++} ++ ++static void hieth_net_set_rx_mode(struct net_device *dev) ++{ ++ u32 val; ++ struct hieth_netdev_priv *priv = netdev_priv(dev); ++ ++ local_lock(priv); ++ ++ val = hieth_readl(priv->glb_base, HIETH_GLB_FWCTRL); ++ if (dev->flags & IFF_PROMISC) { ++ val |= ((priv->port == HIETH_PORT_0) ? ++ HIETH_GLB_FWCTRL_FWALL2CPU_U : ++ HIETH_GLB_FWCTRL_FWALL2CPU_D); ++ hieth_writel(priv->glb_base, val, HIETH_GLB_FWCTRL); ++ } else { ++ val &= ~((priv->port == HIETH_PORT_0) ? ++ HIETH_GLB_FWCTRL_FWALL2CPU_U : ++ HIETH_GLB_FWCTRL_FWALL2CPU_D); ++ hieth_writel(priv->glb_base, val, HIETH_GLB_FWCTRL); ++ ++ val = hieth_readl(priv->glb_base, HIETH_GLB_MACTCTRL); ++ if ((netdev_mc_count(dev) > HIETH_MAX_MULTICAST_ADDRESSES) || ++ (dev->flags & IFF_ALLMULTI)) { ++ val |= UD_BIT_NAME(HIETH_GLB_MACTCTRL_MULTI2CPU); ++ } else { ++ int reg = HIETH_MAX_UNICAST_ADDRESSES; ++ int i; ++ struct netdev_hw_addr *ha; ++ ++ for (i = reg; i < HIETH_MAX_MAC_FILTER_NUM; i++) ++ hieth_enable_mac_addr_filter(priv, i, 0); ++ ++ netdev_for_each_mc_addr(ha, dev) { ++ hieth_set_mac_addr_filter(priv, ha->addr, reg); ++ reg++; ++ } ++ ++ val &= ~(UD_BIT_NAME(HIETH_GLB_MACTCTRL_MULTI2CPU)); ++ } ++ ++ /* Handle multiple unicast addresses (perfect filtering)*/ ++ if (netdev_uc_count(dev) > HIETH_MAX_UNICAST_ADDRESSES) { ++ val |= UD_BIT_NAME(HIETH_GLB_MACTCTRL_UNI2CPU); ++ } else { ++ int reg = 0; ++ int i; ++ struct netdev_hw_addr *ha; ++ ++ for (i = reg; i < HIETH_MAX_UNICAST_ADDRESSES; i++) ++ hieth_enable_mac_addr_filter(priv, i, 0); ++ ++ netdev_for_each_uc_addr(ha, dev) { ++ hieth_set_mac_addr_filter(priv, ha->addr, reg); ++ reg++; ++ } ++ ++ val &= ~(UD_BIT_NAME(HIETH_GLB_MACTCTRL_UNI2CPU)); ++ } ++ hieth_writel(priv->glb_base, val, HIETH_GLB_MACTCTRL); ++ } ++ ++ local_unlock(priv); ++} ++ ++static int hieth_net_ioctl(struct net_device *net_dev, ++ struct ifreq *ifreq, int cmd) ++{ ++ struct hieth_netdev_priv *priv = netdev_priv(net_dev); ++ struct hieth_pm_config pm_config; ++ ++ switch (cmd) { ++ case SIOCSETPM: ++ if (copy_from_user(&pm_config, ifreq->ifr_data, ++ sizeof(pm_config))) ++ return -EFAULT; ++ return hieth_pmt_config(&pm_config); ++ ++ default: ++ if (!netif_running(net_dev)) ++ return -EINVAL; ++ ++ if (!priv->phy) ++ return -EINVAL; ++ ++ return phy_mii_ioctl(priv->phy, ifreq, cmd); ++ } ++ ++ return 0; ++} ++ ++static void hieth_ethtools_get_drvinfo(struct net_device *net_dev, ++ struct ethtool_drvinfo *info) ++{ ++ strcpy(info->driver, "hieth driver"); ++ strcpy(info->version, "v300"); ++ strcpy(info->bus_info, "platform"); ++} ++ ++static u32 hieth_ethtools_get_link(struct net_device *net_dev) ++{ ++ struct hieth_netdev_priv *priv = netdev_priv(net_dev); ++ ++ return ((priv->phy->link) ? HIETH_P_MAC_PORTSET_LINKED : 0); ++} ++ ++static int hieth_ethtools_get_settings(struct net_device *net_dev, ++ struct ethtool_cmd *cmd) ++{ ++ struct hieth_netdev_priv *priv = netdev_priv(net_dev); ++ ++ if (priv->phy) ++ return phy_ethtool_gset(priv->phy, cmd); ++ ++ return -EINVAL; ++} ++ ++static int hieth_ethtools_set_settings(struct net_device *net_dev, ++ struct ethtool_cmd *cmd) ++{ ++ struct hieth_netdev_priv *priv = netdev_priv(net_dev); ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ if (priv->phy) ++ return phy_ethtool_sset(priv->phy, cmd); ++ ++ return -EINVAL; ++} ++ ++static void hieth_get_pauseparam(struct net_device *net_dev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct hieth_netdev_priv *priv = netdev_priv(net_dev); ++ ++ pause->autoneg = priv->phy->autoneg; ++ pause->rx_pause = 1; ++ if (priv->tx_pause_en) ++ pause->tx_pause = 1; ++} ++ ++static int hieth_set_pauseparam(struct net_device *net_dev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct hieth_netdev_priv *priv = netdev_priv(net_dev); ++ struct phy_device *phy = priv->phy; ++ int ret = 0; ++ ++ if (pause->rx_pause == 0) ++ return -EINVAL; ++ ++ if (pause->tx_pause != priv->tx_pause_en) { ++ priv->tx_pause_en = pause->tx_pause; ++ hieth_set_flow_ctrl(priv); ++ } ++ ++ if (phy->autoneg) { ++ if (netif_running(net_dev)) { ++ struct ethtool_cmd cmd; ++ /* auto-negotiation automatically restarted */ ++ cmd.cmd = ETHTOOL_NWAY_RST; ++ cmd.supported = phy->supported; ++ cmd.advertising = phy->advertising; ++ cmd.autoneg = phy->autoneg; ++ cmd.speed = phy->speed; ++ cmd.duplex = phy->duplex; ++ cmd.phy_address = phy->addr; ++ ret = phy_ethtool_sset(phy, &cmd); ++ } ++ } ++ ++ return ret; ++} ++ ++static inline void hieth_enable_rxcsum_drop(struct hieth_netdev_priv *priv, ++ bool drop) ++{ ++ unsigned int val; ++ ++ val = hieth_readl(priv->port_base, HIETH_P_GLB_RX_COE_CTRL); ++ if (drop) ++ val |= COE_ERR_DROP; ++ else ++ val &= ~COE_ERR_DROP; ++ hieth_writel(priv->port_base, val, HIETH_P_GLB_RX_COE_CTRL); ++} ++ ++static int hieth_set_features(struct net_device *dev, ++ netdev_features_t features) ++{ ++ struct hieth_netdev_priv *priv = netdev_priv(dev); ++ netdev_features_t changed = dev->features ^ features; ++ ++ if (changed & NETIF_F_RXCSUM) { ++ if (features & NETIF_F_RXCSUM) ++ hieth_enable_rxcsum_drop(priv, true); ++ else ++ hieth_enable_rxcsum_drop(priv, false); ++ } ++ ++ return 0; ++} ++ ++static struct ethtool_ops hieth_ethtools_ops = { ++ .get_drvinfo = hieth_ethtools_get_drvinfo, ++ .get_link = hieth_ethtools_get_link, ++ .get_settings = hieth_ethtools_get_settings, ++ .set_settings = hieth_ethtools_set_settings, ++ .get_pauseparam = hieth_get_pauseparam, ++ .set_pauseparam = hieth_set_pauseparam, ++}; ++ ++static const struct net_device_ops hieth_netdev_ops = { ++ .ndo_open = hieth_net_open, ++ .ndo_stop = hieth_net_close, ++ .ndo_start_xmit = hieth_net_hard_start_xmit, ++ .ndo_tx_timeout = hieth_net_timeout, ++ .ndo_do_ioctl = hieth_net_ioctl, ++ .ndo_set_mac_address = hieth_net_set_mac_address, ++ .ndo_set_rx_mode = hieth_net_set_rx_mode, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_get_stats = hieth_net_get_stats, ++ .ndo_set_features = hieth_set_features, ++}; ++ ++static void hieth_verify_flow_ctrl_args(struct hieth_netdev_priv *priv) ++{ ++ if (priv->tx_pause_active_thresh < FC_ACTIVE_MIN || ++ priv->tx_pause_active_thresh > FC_ACTIVE_MAX) ++ priv->tx_pause_active_thresh = FC_ACTIVE_DEFAULT; ++ ++ if (priv->tx_pause_deactive_thresh < FC_DEACTIVE_MIN || ++ priv->tx_pause_deactive_thresh > FC_DEACTIVE_MAX) ++ priv->tx_pause_deactive_thresh = FC_DEACTIVE_DEFAULT; ++ ++ if (priv->tx_pause_active_thresh >= priv->tx_pause_deactive_thresh) { ++ priv->tx_pause_active_thresh = FC_ACTIVE_DEFAULT; ++ priv->tx_pause_deactive_thresh = FC_DEACTIVE_DEFAULT; ++ } ++} ++ ++static int hieth_platdev_probe_port(struct platform_device *pdev, ++ struct hieth_netdev_priv *com_priv, ++ int port) ++{ ++ int ret = -1; ++ struct net_device *netdev = NULL; ++ struct device *dev = &pdev->dev; ++ struct hieth_netdev_priv *priv; ++ ++ if ((HIETH_PORT_0 != port) && (HIETH_PORT_1 != port)) { ++ pr_err("port error!\n"); ++ ret = -ENODEV; ++ goto _error_exit; ++ } ++ ++ netdev = alloc_etherdev(sizeof(*priv)); ++ if (netdev == NULL) { ++ pr_err("alloc_etherdev fail!\n"); ++ ret = -ENOMEM; ++ goto _error_exit; ++ } ++ ++ platform_set_drvdata(pdev, netdev); ++ SET_NETDEV_DEV(netdev, &pdev->dev); ++ ++ netdev->irq = com_priv->irq; ++ ++ netdev->watchdog_timeo = 3 * HZ; ++ netdev->netdev_ops = &hieth_netdev_ops; ++ netdev->ethtool_ops = &hieth_ethtools_ops; ++ ++#ifdef HIETH_TSO_SUPPORTED ++ netdev->hw_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | ++ NETIF_F_UFO | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; ++#endif ++#ifdef HIETH_RXCSUM_SUPPORTED ++ netdev->hw_features |= NETIF_F_RXCSUM; ++#endif ++ netdev->features |= netdev->hw_features; ++ netdev->vlan_features |= netdev->features; ++ ++ netdev->priv_flags |= IFF_UNICAST_FLT; ++ ++ if (hieth_phy_param[port].macaddr) ++ ether_addr_copy(netdev->dev_addr, ++ hieth_phy_param[port].macaddr); ++ ++ if (!is_valid_ether_addr(netdev->dev_addr)) ++ eth_hw_addr_random(netdev); ++ ++ /* init hieth_global somethings... */ ++ hieth_devs_save[port] = netdev; ++ ++ /* init hieth_local_driver */ ++ priv = netdev_priv(netdev); ++ memset(priv, 0, sizeof(*priv)); ++ memcpy(priv, com_priv, sizeof(*priv)); ++ ++ local_lock_init(priv); ++ ++ priv->port = port; ++ ++ if (port == HIETH_PORT_0) ++ priv->port_base = priv->glb_base; ++ else ++ priv->port_base = priv->glb_base + 0x2000; ++ ++ priv->dev = dev; ++ ++ init_timer(&priv->monitor); ++ priv->monitor.function = hieth_monitor_func; ++ priv->monitor.data = (unsigned long)netdev; ++ priv->monitor.expires = ++ jiffies + msecs_to_jiffies(HIETH_MONITOR_TIMER); ++ ++ /* wol need */ ++ device_set_wakeup_capable(priv->dev, 1); ++ /* TODO: when we can let phy powerdown? ++ * In forcing fwd mode, we don't want phy powerdown, ++ * so I set wakeup enable all the time ++ */ ++ device_set_wakeup_enable(priv->dev, 1); ++ ++#ifdef CONFIG_TX_FLOW_CTRL_SUPPORT ++ priv->tx_pause_en = 1; ++#endif ++ priv->tx_pause_active_thresh = CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD; ++ priv->tx_pause_deactive_thresh = CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD; ++ ++ hieth_verify_flow_ctrl_args(priv); ++ ++ /* reset and init port */ ++ hieth_port_init(priv); ++ ++#ifdef HIETH_RXCSUM_SUPPORTED ++ hieth_enable_rxcsum_drop(priv, true); ++#endif ++ priv->depth.hw_xmitq = HIETH_HWQ_XMIT_DEPTH; ++ ++ priv->phy = of_phy_connect(netdev, priv->phy_node, ++ hieth_adjust_link, 0, priv->phy_mode); ++ if (!(priv->phy) || IS_ERR(priv->phy)) { ++ pr_info("connect to port[%d] PHY failed!\n", port); ++ priv->phy = NULL; ++ goto _error_phy_connect; ++ } ++ ++ priv->phy->advertising |= ADVERTISED_Pause; ++ priv->phy->supported |= ADVERTISED_Pause; ++ ++ pr_info("attached port %d PHY %d to driver %s, phy_mode=%s\n", ++ port, priv->phy->addr, priv->phy->drv->name, ++ phy_modes(priv->phy_mode)); ++ ++ if (hieth_enable_autoeee) ++ hieth_autoeee_init(priv, 0); ++ ++ skb_queue_head_init(&priv->rx_head); ++ skb_queue_head_init(&priv->rx_hw); ++#ifdef HIETH_TSO_SUPPORTED ++ priv->q_size = 2 * (priv->depth.hw_xmitq); ++ ++ priv->dma_tx = (struct dma_tx_desc *)dma_alloc_coherent(priv->dev, ++ priv->q_size * sizeof(struct dma_tx_desc), ++ &priv->dma_tx_phy, ++ GFP_KERNEL); ++ if (priv->dma_tx == NULL) { ++ pr_err("dma_alloc_coherent fail!\n"); ++ goto _error_alloc_dma_tx; ++ } ++ priv->sg_head = 0; ++ priv->sg_tail = 0; ++ ++ priv->txq = kmalloc_array(priv->q_size, sizeof(struct tx_pkt_info), ++ GFP_KERNEL); ++ if (priv->txq == NULL) ++ goto _error_alloc_txq; ++ priv->txq_head = 0; ++ priv->txq_tail = 0; ++#else ++ skb_queue_head_init(&priv->tx_hw); ++#endif ++ priv->tx_hw_cnt = 0; ++ ++ ret = hieth_init_skb_buffers(priv); ++ if (ret) { ++ pr_err("hieth_init_skb_buffers failed!\n"); ++ goto _error_init_skb_buffers; ++ } ++ ++ ret = register_netdev(netdev); ++ if (ret) { ++ pr_err("register_netdev %s failed!\n", netdev->name); ++ goto _error_register_netdev; ++ } ++ ++ return ret; ++ ++_error_register_netdev: ++ hieth_destroy_skb_buffers(priv); ++ ++_error_init_skb_buffers: ++#ifdef HIETH_TSO_SUPPORTED ++ kfree(priv->txq); ++_error_alloc_txq: ++ dma_free_coherent(priv->dev, ++ priv->q_size * sizeof(struct dma_tx_desc), ++ priv->dma_tx, priv->dma_tx_phy); ++_error_alloc_dma_tx: ++#endif ++ phy_disconnect(priv->phy); ++ priv->phy = NULL; ++ ++_error_phy_connect: ++ local_lock_exit(); ++ hieth_devs_save[port] = NULL; ++ free_netdev(netdev); ++ ++_error_exit: ++ return ret; ++} ++ ++static int hieth_platdev_remove_port(struct platform_device *pdev, int port) ++{ ++ struct net_device *ndev; ++ struct hieth_netdev_priv *priv; ++ ++ ndev = hieth_devs_save[port]; ++ ++ if (!ndev) ++ goto _ndev_exit; ++ ++ priv = netdev_priv(ndev); ++ ++ unregister_netdev(ndev); ++#ifdef HIETH_TSO_SUPPORTED ++ kfree(priv->txq); ++ dma_free_coherent(priv->dev, ++ priv->q_size * sizeof(struct dma_tx_desc), ++ priv->dma_tx, priv->dma_tx_phy); ++#endif ++ hieth_destroy_skb_buffers(priv); ++ ++ phy_disconnect(priv->phy); ++ priv->phy = NULL; ++ ++ iounmap((void *)priv->glb_base); ++ ++ local_lock_exit(); ++ ++ hieth_devs_save[port] = NULL; ++ free_netdev(ndev); ++ ++_ndev_exit: ++ return 0; ++} ++ ++static int hieth_of_get_param(struct device_node *node) ++{ ++ struct device_node *child = NULL; ++ int idx = 0; ++ int data; ++ ++ /* get auto eee */ ++ hieth_enable_autoeee = of_property_read_bool(node, "autoeee"); ++ ++ for_each_available_child_of_node(node, child) { ++ /* get phy-addr */ ++ if (of_property_read_u32(child, "reg", &data)) ++ return -EINVAL; ++ if ((data < 0) || (data >= PHY_MAX_ADDR)) { ++ pr_info("%s has invalid PHY address\n", ++ child->full_name); ++ data = HIETH_INVALID_PHY_ADDR; ++ } ++ ++ hieth_phy_param[idx].phy_addr = data; ++ if (data != HIETH_INVALID_PHY_ADDR) ++ hieth_phy_param[idx].isvalid = true; ++ ++ /* get phy_mode */ ++ hieth_phy_param[idx].phy_mode = of_get_phy_mode(child); ++ ++ /* get mac */ ++ hieth_phy_param[idx].macaddr = of_get_mac_address(child); ++ ++ /* get gpio_base and bit */ ++ of_property_read_u32(child, "phy-gpio-base", ++ (u32 *)(&hieth_phy_param[idx].gpio_base)); ++ of_property_read_u32(child, "phy-gpio-bit", ++ &hieth_phy_param[idx].gpio_bit); ++ ++ /* get internal flag */ ++ hieth_phy_param[idx].isinternal = ++ of_property_read_bool(child, "internal-phy"); ++ ++ if (++idx >= HIETH_MAX_PORT) ++ break; ++ } ++ ++ return 0; ++} ++ ++static void hieth_mac_core_reset(struct hieth_netdev_priv *priv) ++{ ++ /* undo reset */ ++ reset_control_deassert(priv->mac_rst); ++ usleep_range(50, 60); ++ ++ /* soft reset mac port */ ++ reset_control_assert(priv->mac_rst); ++ usleep_range(50, 60); ++ /* undo reset */ ++ reset_control_deassert(priv->mac_rst); ++} ++ ++void hieth_phy_get_reset_controller(struct device *dev) ++{ ++ int i; ++ struct hieth_phy_param_s *phy_param; ++ char *rst_name; ++ ++ for (i = 0; i < HIETH_MAX_PORT; i++) { ++ phy_param = &hieth_phy_param[i]; ++ ++ if (!phy_param->isvalid) ++ continue; ++ ++ if (i == 0) ++ rst_name = HIETH_PHY0_RST_NAME; ++ else ++ rst_name = HIETH_PHY1_RST_NAME; ++ ++ phy_param->phy_rst = devm_reset_control_get(dev, rst_name); ++ if (IS_ERR(phy_param->phy_rst)) ++ phy_param->phy_rst = NULL; ++ } ++} ++ ++static int hieth_plat_driver_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ int irq; ++ struct net_device *ndev = NULL; ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node; ++ struct resource *res; ++ struct hieth_netdev_priv *priv = &hieth_priv; ++ struct device_node *child = NULL; ++ int port = -1; ++ ++ memset(hieth_devs_save, 0, sizeof(hieth_devs_save)); ++ memset(hieth_phy_param, 0, sizeof(hieth_phy_param)); ++ memset(priv, 0, sizeof(*priv)); ++ ++ if (hieth_of_get_param(node)) { ++ pr_err("of get parameter fail\n"); ++ ret = -ENODEV; ++ goto exit; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->glb_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(priv->glb_base)) { ++ ret = PTR_ERR(priv->glb_base); ++ goto exit; ++ } ++ ++ priv->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->clk)) { ++ pr_err("failed to get clk\n"); ++ ret = -ENODEV; ++ goto exit; ++ } ++ ++ ret = clk_prepare_enable(priv->clk); ++ if (ret < 0) { ++ pr_err("failed to enable clk %d\n", ret); ++ goto exit; ++ } ++ ++ priv->mac_rst = devm_reset_control_get(dev, HIETH_MAC_RST_NAME); ++ if (IS_ERR(priv->mac_rst)) { ++ ret = PTR_ERR(priv->mac_rst); ++ goto exit_clk_disable; ++ } ++ ++ hieth_mac_core_reset(priv); ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ pr_err("no IRQ defined!\n"); ++ ret = -ENODEV; ++ goto exit_clk_disable; ++ } ++ priv->irq = irq; ++ ++ hieth_phy_get_reset_controller(dev); ++ hieth_phy_reset(); ++ ++ if (hieth_mdiobus_driver_init(pdev, priv)) { ++ pr_err("mdio bus init error!\n"); ++ ret = -ENODEV; ++ goto exit_clk_disable; ++ } ++ ++ /* phy param */ ++ hieth_phy_register_fixups(); ++ ++ for_each_available_child_of_node(node, child) { ++ if (++port >= HIETH_MAX_PORT) ++ break; ++ ++ if (!hieth_phy_param[port].isvalid) ++ continue; ++ ++ priv->phy_node = of_parse_phandle(node, "phy-handle", port); ++ if (!priv->phy_node) { ++ pr_err("not find phy-handle [%d]\n", port); ++ continue; ++ } ++ ++ priv->phy_mode = hieth_phy_param[port].phy_mode; ++ ++ if (!hieth_platdev_probe_port(pdev, priv, port)) ++ hieth_real_port_cnt++; ++ } ++ ++ if (hieth_devs_save[HIETH_PORT_0]) ++ ndev = hieth_devs_save[HIETH_PORT_0]; ++ else if (hieth_devs_save[HIETH_PORT_1]) ++ ndev = hieth_devs_save[HIETH_PORT_1]; ++ ++ if (!ndev) { ++ pr_err("no dev probed!\n"); ++ ret = -ENODEV; ++ goto exit_mdiobus; ++ } ++ ++ return ret; ++ ++exit_mdiobus: ++ hieth_mdiobus_driver_exit(priv); ++ ++exit_clk_disable: ++ clk_disable_unprepare(priv->clk); ++ ++exit: ++ ++ return ret; ++} ++ ++static int hieth_plat_driver_remove(struct platform_device *pdev) ++{ ++ int i; ++ struct net_device *ndev = NULL; ++ struct hieth_netdev_priv *priv = netdev_priv(ndev); ++ ++ if (hieth_devs_save[HIETH_PORT_0]) ++ ndev = hieth_devs_save[HIETH_PORT_0]; ++ else if (hieth_devs_save[HIETH_PORT_1]) ++ ndev = hieth_devs_save[HIETH_PORT_1]; ++ ++ free_irq(ndev->irq, hieth_devs_save); ++ ++ for (i = 0; i < HIETH_MAX_PORT; i++) ++ hieth_platdev_remove_port(pdev, i); ++ ++ hieth_mdiobus_driver_exit(priv); ++ ++ clk_disable_unprepare(priv->clk); ++ ++ memset(hieth_devs_save, 0, sizeof(hieth_devs_save)); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int hieth_plat_driver_suspend_port(struct platform_device *pdev, ++ pm_message_t state, int port) ++{ ++ struct net_device *ndev = hieth_devs_save[port]; ++ ++ if (ndev) { ++ if (netif_running(ndev)) { ++ hieth_net_close(ndev); ++ netif_device_detach(ndev); ++ } ++ } ++ ++ return 0; ++} ++ ++int hieth_plat_driver_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ int i; ++ bool power_off = true; ++ struct hieth_netdev_priv *priv = NULL; ++ ++ for (i = 0; i < HIETH_MAX_PORT; i++) ++ hieth_plat_driver_suspend_port(pdev, state, i); ++ ++ if (hieth_pmt_enter()) ++ power_off = false; ++ ++ if (power_off) { ++ for (i = 0; i < HIETH_MAX_PORT; i++) { ++ if (hieth_devs_save[i]) { ++ priv = netdev_priv(hieth_devs_save[i]); ++ genphy_suspend(priv->phy);/* power down phy */ ++ } ++ } ++ ++ /* need some time before phy suspend finished. */ ++ usleep_range(1000, 10000); ++ ++ if (priv) ++ clk_disable_unprepare(priv->clk); ++ } ++ ++ return 0; ++} ++ ++static int hieth_plat_driver_resume_port(struct platform_device *pdev, int port) ++{ ++ struct net_device *ndev = hieth_devs_save[port]; ++ struct hieth_netdev_priv *priv = netdev_priv(ndev); ++ ++ if (ndev) { ++ if (netif_running(ndev)) { ++ hieth_port_init(priv); ++ hieth_net_open(ndev); ++ netif_device_attach(ndev); ++ } ++ } ++ ++ return 0; ++} ++ ++int hieth_plat_driver_resume(struct platform_device *pdev) ++{ ++ int i; ++ struct hieth_netdev_priv *priv = &hieth_priv; ++ ++ /* enable clk */ ++ clk_prepare_enable(priv->clk); ++ hieth_phy_reset(); ++ ++ for (i = 0; i < HIETH_MAX_PORT; i++) ++ hieth_plat_driver_resume_port(pdev, i); ++ ++ hieth_pmt_exit(); ++ return 0; ++} ++#else ++# define hieth_plat_driver_suspend NULL ++# define hieth_plat_driver_resume NULL ++#endif ++ ++static const struct of_device_id hieth_of_match[] = { ++ {.compatible = "hisilicon,hieth", }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, hieth_of_match); ++ ++static struct platform_driver hieth_platform_driver = { ++ .probe = hieth_plat_driver_probe, ++ .remove = hieth_plat_driver_remove, ++ .suspend = hieth_plat_driver_suspend, ++ .resume = hieth_plat_driver_resume, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = HIETH_DRIVER_NAME, ++ .bus = &platform_bus_type, ++ .of_match_table = of_match_ptr(hieth_of_match), ++ }, ++}; ++ ++static int hieth_mod_init(void) ++{ ++ int ret = 0; ++ ++ if (hieth_disable) ++ return 0; ++ ++ ret = platform_driver_register(&hieth_platform_driver); ++ if (ret) ++ pr_err("register platform driver failed!\n"); ++ ++ hieth_proc_create(); ++ ++ return ret; ++} ++ ++static void hieth_mod_exit(void) ++{ ++ if (hieth_disable) ++ return; ++ ++ hieth_proc_destroy(); ++ ++ platform_driver_unregister(&hieth_platform_driver); ++} ++ ++module_init(hieth_mod_init); ++module_exit(hieth_mod_exit); ++ ++MODULE_DESCRIPTION("Hisilicon ETH driver whith MDIO support"); ++MODULE_LICENSE("GPL"); ++ ++/* vim: set ts=8 sw=8 tw=78: */ +diff --git a/drivers/net/ethernet/hisilicon/hieth/hieth.h b/drivers/net/ethernet/hisilicon/hieth/hieth.h +new file mode 100644 +index 0000000..88334b9 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/hieth.h +@@ -0,0 +1,418 @@ ++#ifndef __HIETH_H ++#define __HIETH_H ++ ++#include <linux/kernel.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/netdevice.h> ++#include <linux/list.h> ++#include <linux/phy.h> ++#include <linux/io.h> ++ ++#define HIETH_MIIBUS_NAME "himii" ++#define HIETH_DRIVER_NAME "hieth" ++ ++#define HIETH_MAC_RST_NAME "mac_reset" ++#define HIETH_PHY0_RST_NAME "phy0_reset" ++#define HIETH_PHY1_RST_NAME "phy1_reset" ++ ++#define HIETH_TSO_SUPPORTED ++#define HIETH_TSO_DEBUG ++#define HIETH_RXCSUM_SUPPORTED ++ ++#ifdef HIETH_TSO_SUPPORTED ++#include "tso.h" ++#endif ++ ++/* hieth max port */ ++#define HIETH_MAX_PORT 2 ++ ++/* invalid phy addr */ ++#define HIETH_INVALID_PHY_ADDR 31 ++ ++/* hieth monitor timer, 10ms */ ++#define HIETH_MONITOR_TIMER 10 ++ ++/* hieth hardware queue send fifo depth, increase to optimize TX performance. */ ++#define HIETH_HWQ_XMIT_DEPTH 12 ++ ++/* set irq affinity to cpu1 when multi-processor */ ++#define HIETH_IRQ_AFFINITY_CPU 1 ++ ++#define HIETH_MAX_QUEUE_DEPTH 64 ++#define HIETH_MAX_RX_HEAD_LEN (10000) /* max skbs for rx */ ++#define HIETH_MAX_RCV_LEN 1535 /* max receive length */ ++ ++/* mmu should be less than 1600 Bytes ++ */ ++ ++#define HIETH_MAX_FRAME_SIZE (1600) ++#define HIETH_INVALID_RXPKG_LEN(len) (!((len) >= 42 && \ ++ (len) <= HIETH_MAX_FRAME_SIZE)) ++ ++#define HIETH_MAX_MAC_FILTER_NUM 8 ++#define HIETH_MAX_UNICAST_ADDRESSES 2 ++#define HIETH_MAX_MULTICAST_ADDRESSES (HIETH_MAX_MAC_FILTER_NUM - \ ++ HIETH_MAX_UNICAST_ADDRESSES) ++ ++/* Register Definition ++ */ ++/*------------------------- port register -----------------------------------*/ ++/* Mac port sel */ ++#define HIETH_P_MAC_PORTSEL 0x0200 ++#define HIETH_P_MAC_PORTSEL_STAT 0 ++#define HIETH_P_MAC_PORTSEL_STAT_MDIO 0 ++#define HIETH_P_MAC_PORTSEL_STAT_CPU 1 ++#define HIETH_P_MAC_PORTSEL_MII_MODE 1 ++#define HIETH_P_MAC_PORTSEL_MII ~BIT(1) ++#define HIETH_P_MAC_PORTSEL_RMII BIT(1) ++/* Mac ro status */ ++#define HIETH_P_MAC_RO_STAT 0x0204 ++/* Mac port status set */ ++#define HIETH_P_MAC_PORTSET 0x0208 ++#define HIETH_P_MAC_PORTSET_SPD_100M BIT(2) ++#define HIETH_P_MAC_PORTSET_LINKED BIT(1) ++#define HIETH_P_MAC_PORTSET_DUP_FULL BIT(0) ++/* Mac status change */ ++#define HIETH_P_MAC_STAT_CHANGE 0x020C ++/* Mac set */ ++#define HIETH_P_MAC_SET 0x0210 ++#define BIT_PAUSE_EN BIT(18) ++#define HIETH_P_MAC_SET_LEN_MAX(n) ((n) & 0x7FF) ++#define HIETH_P_MAC_SET_LEN_MAX_MSK GENMASK(10, 0) ++ ++#define HIETH_P_MAC_RX_IPGCTRL 0x0214 ++#define HIETH_P_MAC_TX_IPGCTRL 0x0218 ++#define HIETH_P_MAC_TX_IPGCTRL_PRE_CNT_LMT_SHIFT 23 ++#define HIETH_P_MAC_TX_IPGCTRL_PRE_CNT_LMT_MSK GENMASK(25, 23) ++/* queue length set */ ++#define HIETH_P_GLB_QLEN_SET 0x0344 ++#define HIETH_P_GLB_QLEN_SET_TXQ_DEP_MSK GENMASK(5, 0) ++#define HIETH_P_GLB_QLEN_SET_TXQ_DEP(n) ((n) << 0) ++#define HIETH_P_GLB_QLEN_SET_RXQ_DEP_MSK GENMASK(13, 8) ++#define HIETH_P_GLB_QLEN_SET_RXQ_DEP(n) ((n) << 8) ++/* 802.3x flow control register */ ++#define HIETH_P_GLB_FC_LEVEL 0x0348 ++#define BITS_FC_ACTIVE_THR_OFFSET 8 ++#define BIT_FC_EN BIT(14) ++#define BITS_THR_MASK 0x3F ++ ++/* Rx frame start addr */ ++#define HIETH_P_GLB_RXFRM_SADDR 0x0350 ++/* Rx (read only) Queue-ID and LEN */ ++#define HIETH_P_GLB_RO_IQFRM_DES 0x0354 ++#define HIETH_P_GLB_RO_IQFRM_DES_FDIN_LEN_MSK GENMASK(11, 0) ++#define BITS_PAYLOAD_ERR_OFFSET 20 ++#define BITS_PAYLOAD_ERR_MASK 0x1 ++#define BITS_HEADER_ERR_OFFSET 21 ++#define BITS_HEADER_ERR_MASK 0x1 ++#define BITS_PAYLOAD_DONE_OFFSET 22 ++#define BITS_PAYLOAD_DONE_MASK 0x1 ++#define BITS_HEADER_DONE_OFFSET 23 ++#define BITS_HEADER_DONE_MASK 0x1 ++ ++/* Rx ADDR */ ++#define HIETH_P_GLB_IQ_ADDR 0x0358 ++/* Tx ADDR and LEN */ ++#define HIETH_P_GLB_EQ_ADDR 0x0360 ++#define HIETH_P_GLB_EQFRM_LEN 0x0364 ++/* Rx/Tx Queue ID */ ++#define HIETH_P_GLB_RO_QUEUE_ID 0x0368 ++/* Rx/Tx Queue staus */ ++#define HIETH_P_GLB_RO_QUEUE_STAT 0x036C ++/* check this bit to see if we can add a Tx package */ ++#define HIETH_P_GLB_RO_QUEUE_STAT_XMITQ_RDY_MSK BIT(24) ++/* check this bit to see if we can add a Rx addr */ ++#define HIETH_P_GLB_RO_QUEUE_STAT_RECVQ_RDY_MSK BIT(25) ++/* counts in queue, include currently sending */ ++#define HIETH_P_GLB_RO_QUEUE_STAT_XMITQ_CNT_INUSE_MSK GENMASK(5, 0) ++ ++/* Rx COE control */ ++#define HIETH_P_GLB_RX_COE_CTRL 0x0380 ++#define BIT_COE_IPV6_UDP_ZERO_DROP BIT(13) ++#define BIT_COE_PAYLOAD_DROP BIT(14) ++#define BIT_COE_IPHDR_DROP BIT(15) ++#define COE_ERR_DROP (BIT_COE_IPHDR_DROP | BIT_COE_PAYLOAD_DROP | \ ++ BIT_COE_IPV6_UDP_ZERO_DROP) ++ ++#ifdef HIETH_TSO_SUPPORTED ++/* TSO debug enable */ ++#define HIETH_P_GLB_TSO_DBG_EN 0x03A4 ++#define BITS_TSO_DBG_EN BIT(31) ++/* TSO debug state */ ++#define HIETH_P_GLB_TSO_DBG_STATE 0x03A8 ++#define BITS_TSO_DBG_STATE BIT(31) ++/* TSO debug addr */ ++#define HIETH_P_GLB_TSO_DBG_ADDR 0x03AC ++/* TSO debug tx info */ ++#define HIETH_P_GLB_TSO_DBG_TX_INFO 0x03B0 ++/* TSO debug tx err */ ++#define HIETH_P_GLB_TSO_DBG_TX_ERR 0x03B4 ++#endif ++ ++/*------------------------- global register --------------------------------*/ ++/* host mac address */ ++#define HIETH_GLB_HOSTMAC_L32 0x1300 ++#define HIETH_GLB_HOSTMAC_H16 0x1304 ++/* soft reset */ ++#define HIETH_GLB_SOFT_RESET 0x1308 ++#define HIETH_GLB_SOFT_RESET_ALL BIT(0) ++#define HIETH_GLB_SOFT_RESET_P0 BIT(2) ++#define HIETH_GLB_SOFT_RESET_P1 BIT(3) ++/* forward contrl */ ++#define HIETH_GLB_FWCTRL 0x1310 ++#define HIETH_GLB_FWCTRL_VLAN_ENABLE BIT(0) ++#define HIETH_GLB_FWCTRL_FW2CPU_ENA_U BIT(5) ++#define HIETH_GLB_FWCTRL_FW2CPU_ENA_D BIT(9) ++#define HIETH_GLB_FWCTRL_FWALL2CPU_U BIT(7) ++#define HIETH_GLB_FWCTRL_FWALL2CPU_D BIT(11) ++#define HIETH_GLB_FWCTRL_FW2OTHPORT_ENA_U BIT(4) ++#define HIETH_GLB_FWCTRL_FW2OTHPORT_ENA_D BIT(8) ++#define HIETH_GLB_FWCTRL_FW2OTHPORT_FORCE_U BIT(6) ++#define HIETH_GLB_FWCTRL_FW2OTHPORT_FORCE_D BIT(10) ++/* Mac filter table control */ ++#define HIETH_GLB_MACTCTRL 0x1314 ++#define HIETH_GLB_MACTCTRL_MACT_ENA_U BIT(7) ++#define HIETH_GLB_MACTCTRL_MACT_ENA_D BIT(15) ++#define HIETH_GLB_MACTCTRL_BROAD2CPU_U BIT(5) ++#define HIETH_GLB_MACTCTRL_BROAD2CPU_D BIT(13) ++#define HIETH_GLB_MACTCTRL_BROAD2OTHPORT_U BIT(4) ++#define HIETH_GLB_MACTCTRL_BROAD2OTHPORT_D BIT(12) ++#define HIETH_GLB_MACTCTRL_MULTI2CPU_U BIT(3) ++#define HIETH_GLB_MACTCTRL_MULTI2CPU_D BIT(11) ++#define HIETH_GLB_MACTCTRL_MULTI2OTHPORT_U BIT(2) ++#define HIETH_GLB_MACTCTRL_MULTI2OTHPORT_D BIT(10) ++#define HIETH_GLB_MACTCTRL_UNI2CPU_U BIT(1) ++#define HIETH_GLB_MACTCTRL_UNI2CPU_D BIT(9) ++#define HIETH_GLB_MACTCTRL_UNI2OTHPORT_U BIT(0) ++#define HIETH_GLB_MACTCTRL_UNI2OTHPORT_D BIT(8) ++/* Host mac address */ ++#define HIETH_GLB_DN_HOSTMAC_L32 0x1340 ++#define HIETH_GLB_DN_HOSTMAC_H16 0x1344 ++#define HIETH_GLB_DN_HOSTMAC_ENA 0x1348 ++#define HIETH_GLB_DN_HOSTMAC_ENA_BIT BIT(0) ++/* Mac filter */ ++#define HIETH_GLB_MAC_L32_BASE 0x1400 ++#define HIETH_GLB_MAC_H16_BASE 0x1404 ++#define HIETH_GLB_MAC_L32_BASE_D (0x1400 + 16 * 0x8) ++#define HIETH_GLB_MAC_H16_BASE_D (0x1404 + 16 * 0x8) ++#define HIETH_GLB_MACFLT_HI16 GENMASK(15, 0) ++#define HIETH_GLB_MACFLT_FW2CPU_U BIT(21) ++#define HIETH_GLB_MACFLT_FW2CPU_D BIT(19) ++#define HIETH_GLB_MACFLT_FW2PORT_U BIT(20) ++#define HIETH_GLB_MACFLT_FW2PORT_D BIT(18) ++#define HIETH_GLB_MACFLT_ENA_U BIT(17) ++#define HIETH_GLB_MACFLT_ENA_D BIT(16) ++/* ENDIAN */ ++#define HIETH_GLB_ENDIAN_MOD 0x1318 ++#define HIETH_GLB_ENDIAN_MOD_IN BIT(1) ++#define HIETH_GLB_ENDIAN_MOD_OUT BIT(0) ++/* IRQs */ ++#define HIETH_GLB_IRQ_STAT 0x1330 ++#define HIETH_GLB_IRQ_ENA 0x1334 ++#define HIETH_GLB_IRQ_ENA_IEN_A BIT(19) ++#define HIETH_GLB_IRQ_ENA_IEN_U BIT(18) ++#define HIETH_GLB_IRQ_ENA_IEN_D BIT(17) ++#define HIETH_GLB_IRQ_ENA_BIT_U GENMASK(7, 0) ++#define HIETH_GLB_IRQ_ENA_BIT_D GENMASK(27, 20) ++#define HIETH_GLB_IRQ_RAW 0x1338 ++#define HIETH_GLB_IRQ_INT_MULTI_RXRDY_U BIT(7) ++#define HIETH_GLB_IRQ_INT_MULTI_RXRDY_D BIT(27) ++#define HIETH_GLB_IRQ_INT_TXQUE_RDY_U BIT(6) ++#define HIETH_GLB_IRQ_INT_TXQUE_RDY_D BIT(26) ++#define HIETH_GLB_IRQ_INT_RX_RDY_U BIT(0) ++#define HIETH_GLB_IRQ_INT_RX_RDY_D BIT(20) ++#define HIETH_INT_TX_ERR_U BIT(8) ++#define HIETH_INT_TX_ERR_D BIT(28) ++ ++/* *********************************************************** ++* ++* Only for internal used! ++* ++* *********************************************************** ++*/ ++ ++/* read/write IO */ ++#define hieth_readl(base, ofs) \ ++ readl(base + (ofs)) ++#define hieth_writel(base, v, ofs) \ ++ writel(v, base + (ofs)) ++ ++#define hieth_trace_level 8 ++#define hireg_trace(level, msg...) do { \ ++if ((level) >= hieth_trace_level) { \ ++ pr_info("hireg_trace:%s:%d: ", __func__, __LINE__); \ ++ pr_info(msg); \ ++ pr_info("\n"); \ ++} \ ++} while (0) ++ ++#define hireg_readl(base, ofs) ({ unsigned long reg = readl((base) + (ofs)); \ ++ hireg_trace(2, "_readl(0x%04X) = 0x%08lX", (ofs), reg); \ ++ reg; }) ++ ++#define hireg_writel(base, v, ofs) do { writel((v), (base) + (ofs)); \ ++ hireg_trace(2, "_writel(0x%04X) = 0x%08lX", \ ++ (ofs), (unsigned long)(v)); \ ++} while (0) ++ ++#define hieth_dump_buf(buf, len) do {\ ++ int i;\ ++ char *p = (void *)(buf);\ ++ pr_info("%s->%d, buf=0x%.8x, len=%d\n", \ ++ __func__, __LINE__, \ ++ (int)(buf), (int)(len)); \ ++ for (i = 0; i < (len); i++) {\ ++ pr_info("0x%.2x ", *(p+i));\ ++ if (!((i+1) & 0x07)) \ ++ pr_info("\n");\ ++ } \ ++ pr_info("\n");\ ++} while (0) ++ ++/* port */ ++enum hieth_port_e { ++ HIETH_PORT_0 = 0, ++ HIETH_PORT_1, ++ HIETH_PORT_NUM, ++}; ++ ++struct hieth_netdev_priv { ++ void __iomem *glb_base; /* virtual io global addr */ ++ void __iomem *port_base; /* virtual to port addr: ++ * port0-0; port1-0x2000 ++ */ ++ int port; /* 0 => up port, 1 => down port */ ++ int irq; ++ ++ struct device *dev; ++ struct net_device_stats stats; ++ struct phy_device *phy; ++ struct device_node *phy_node; ++ phy_interface_t phy_mode; ++ ++ struct mii_bus *mii_bus; ++ ++ struct sk_buff_head rx_head; /*received pkgs */ ++ struct sk_buff_head rx_hw; /*rx pkgs in hw */ ++#ifdef HIETH_TSO_SUPPORTED ++ struct dma_tx_desc *dma_tx ____cacheline_aligned; ++ dma_addr_t dma_tx_phy; ++ unsigned int sg_head; ++ unsigned int sg_tail; ++#endif ++#ifdef HIETH_TSO_SUPPORTED ++ struct tx_pkt_info *txq; ++ unsigned int txq_head; ++ unsigned int txq_tail; ++ int q_size; ++#else ++ struct sk_buff_head tx_hw; /*tx pkgs in hw */ ++#endif ++ u32 tx_hw_cnt; ++ ++ struct timer_list monitor; ++ ++ struct { ++ int hw_xmitq; ++ } depth; ++ ++ struct { ++ unsigned long rx_pool_dry_times; ++ } stat; ++ ++#define SKB_SIZE (HIETH_MAX_FRAME_SIZE) ++ struct rx_skb_pool { ++ struct sk_buff *sk_pool[CONFIG_HIETH_MAX_RX_POOLS];/*skb pool*/ ++ int next_free_skb; /*next free skb*/ ++ } rx_pool; ++ ++ struct tasklet_struct bf_recv; ++ ++ int link_stat; ++ int (*eee_init)(struct phy_device *phy_dev); ++ ++ spinlock_t lock; /* lock for reg rw */ ++ unsigned long lockflags; ++ ++ spinlock_t mdio_lock; /* lock for mdio reg */ ++ unsigned long mdio_lockflags; ++ ++ struct clk *clk; ++ struct reset_control *mac_rst; ++ struct reset_control *phy_rst; ++ /* 802.3x flow control */ ++ int tx_pause_en; ++ int tx_pause_active_thresh; ++ int tx_pause_deactive_thresh; ++}; ++ ++/* phy parameter */ ++struct hieth_phy_param_s { ++ bool isvalid; /* valid or not */ ++ bool isinternal; /* internal phy or external phy */ ++ int phy_addr; ++ phy_interface_t phy_mode; ++ const char *macaddr; ++ ++ struct reset_control *phy_rst; ++ ++ /* gpio reset pin if has */ ++ void __iomem *gpio_base; ++ u32 gpio_bit; ++}; ++ ++#ifdef HIETH_TSO_DEBUG ++#define MAX_RECORD (100) ++struct send_pkt_info { ++ u32 reg_addr; ++ u32 reg_pkt_info; ++ u32 status; ++}; ++ ++extern unsigned int id_send; ++extern unsigned int id_free; ++extern struct send_pkt_info pkt_rec[MAX_RECORD]; ++#endif ++ ++#define local_lock_init(priv) spin_lock_init(&(priv)->lock) ++#define local_lock_exit(priv) ++#define local_lock(priv) spin_lock_irqsave(&(priv)->lock, \ ++ (priv)->lockflags) ++#define local_unlock(priv) spin_unlock_irqrestore(&(priv)->lock, \ ++ (priv)->lockflags) ++ ++#define hieth_mdio_lock_init(priv) spin_lock_init(&(priv)->mdio_lock) ++#define hieth_mdio_lock_exit(priv) ++#define hieth_mdio_lock(priv) spin_lock_irqsave(&(priv)->mdio_lock, \ ++ (priv)->mdio_lockflags) ++#define hieth_mdio_unlock(priv) spin_unlock_irqrestore(&(priv)->mdio_lock, \ ++ (priv)->mdio_lockflags) ++ ++#define UD_BIT_NAME(name) ((priv->port == HIETH_PORT_0) ? \ ++ name##_U : name##_D) ++ ++#define GLB_MAC_H16(port, reg) ((((port) == HIETH_PORT_0) ? \ ++ HIETH_GLB_MAC_H16_BASE : \ ++ HIETH_GLB_MAC_H16_BASE_D) + (reg * 0x8)) ++#define GLB_MAC_L32(port, reg) ((((port) == HIETH_PORT_0) ? \ ++ HIETH_GLB_MAC_L32_BASE : \ ++ HIETH_GLB_MAC_L32_BASE_D) + (reg * 0x8)) ++ ++#define SIOCGETMODE (SIOCDEVPRIVATE) /* get work mode */ ++#define SIOCSETMODE (SIOCDEVPRIVATE + 1) /* set work mode */ ++#define SIOCGETFWD (SIOCDEVPRIVATE + 2) /* get forcing forward config */ ++#define SIOCSETFWD (SIOCDEVPRIVATE + 3) /* set forcing forward config */ ++#define SIOCSETPM (SIOCDEVPRIVATE + 4) /* set pmt wake up config */ ++#define SIOCSETSUSPEND (SIOCDEVPRIVATE + 5) /* call dev->suspend */ ++#define SIOCSETRESUME (SIOCDEVPRIVATE + 6) /* call dev->resume */ ++ ++extern struct hieth_phy_param_s hieth_phy_param[]; ++ ++void hieth_autoeee_init(struct hieth_netdev_priv *priv, int link_stat); ++void hieth_phy_register_fixups(void); ++void hieth_phy_reset(void); ++ ++#endif ++ ++/* vim: set ts=8 sw=8 tw=78: */ +diff --git a/drivers/net/ethernet/hisilicon/hieth/mdio.c b/drivers/net/ethernet/hisilicon/hieth/mdio.c +new file mode 100644 +index 0000000..bb63593 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/mdio.c +@@ -0,0 +1,180 @@ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/string.h> ++#include <linux/errno.h> ++#include <linux/unistd.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/skbuff.h> ++#include <linux/spinlock.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/version.h> ++#include <linux/mii.h> ++#include <linux/ethtool.h> ++#include <linux/phy.h> ++#include <linux/dma-mapping.h> ++#include <linux/platform_device.h> ++#include <linux/of_mdio.h> ++#include <linux/io.h> ++#include <asm/irq.h> ++#include <linux/uaccess.h> ++ ++#include "hieth.h" ++#include "mdio.h" ++ ++/* MDIO Bus Interface */ ++ ++static void hieth_mdio_init(struct hieth_netdev_priv *priv) ++{ ++ hieth_mdio_lock_init(priv); ++ mdio_reg_reset(priv); ++} ++ ++static void hieth_mdio_exit(struct hieth_netdev_priv *priv) ++{ ++ hieth_mdio_lock_exit(priv); ++} ++ ++static int hieth_wait_mdio_ready(struct hieth_netdev_priv *priv) ++{ ++ int timeout_us = 1000; ++ ++ while (--timeout_us && !mdio_test_ready(priv)) ++ udelay(1); ++ ++ return timeout_us; ++} ++ ++static int hieth_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) ++{ ++ int val = 0; ++ struct hieth_netdev_priv *priv = bus->priv; ++ ++ hieth_mdio_lock(priv); ++ ++ if (!hieth_wait_mdio_ready(priv)) { ++ pr_err("mdio busy\n"); ++ goto error_exit; ++ } ++ ++ mdio_start_phyread(priv, phy_addr, regnum); ++ ++ if (hieth_wait_mdio_ready(priv)) ++ val = mdio_get_phyread_val(priv); ++ else ++ pr_err("read timeout\n"); ++ ++error_exit: ++ ++ hieth_mdio_unlock(priv); ++ ++ pr_debug("phy_addr = %d, regnum = %d, val = 0x%04x\n", ++ phy_addr, regnum, val); ++ ++ return val; ++} ++ ++static int hieth_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum, ++ u16 val) ++{ ++ int ret = 0; ++ struct hieth_netdev_priv *priv = bus->priv; ++ ++ pr_debug("phy_addr = %d, regnum = %d\n", phy_addr, regnum); ++ ++ hieth_mdio_lock(priv); ++ ++ if (!hieth_wait_mdio_ready(priv)) { ++ pr_err("mdio busy\n"); ++ ret = -1; ++ goto error_exit; ++ } ++ ++ mdio_phywrite(priv, phy_addr, regnum, val); ++ ++error_exit: ++ ++ hieth_mdio_unlock(priv); ++ ++ return ret; ++} ++ ++static int hieth_mdiobus_reset(struct mii_bus *bus) ++{ ++ struct hieth_netdev_priv *priv = bus->priv; ++ ++ mdio_reg_reset(priv); ++ return 0; ++} ++ ++int hieth_mdiobus_driver_init(struct platform_device *pdev, ++ struct hieth_netdev_priv *priv) ++{ ++ int phy, ret = 0; ++ struct mii_bus *bus; ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node; ++ ++ hieth_mdio_init(priv); ++ ++ /* register MII bus */ ++ bus = mdiobus_alloc(); ++ if (!bus) { ++ pr_err("get ioresource failed!\n"); ++ ret = -ENOMEM; ++ goto _error_exit; ++ } ++ ++ bus->name = HIETH_MIIBUS_NAME; ++ ++ snprintf(bus->id, MII_BUS_ID_SIZE, "%s", bus->name); ++ bus->read = hieth_mdiobus_read; ++ bus->write = hieth_mdiobus_write; ++ bus->reset = hieth_mdiobus_reset; ++ bus->priv = priv; ++ priv->mii_bus = bus; ++ bus->parent = &pdev->dev; /*for Power Management */ ++ ++ bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); ++ if (!bus->irq) { ++ ret = -ENOMEM; ++ goto _error_free_mdiobus; ++ } ++ ++ for (phy = 0; phy < PHY_MAX_ADDR; phy++) ++ bus->irq[phy] = PHY_POLL; ++ ++ ret = of_mdiobus_register(bus, node); ++ if (ret) { ++ pr_err("failed to register MDIO bus\n"); ++ goto _error_free_mdiobus; ++ } ++ ++ return 0; ++ ++_error_free_mdiobus: ++ kfree(bus->irq); ++ mdiobus_free(bus); ++ ++_error_exit: ++ return ret; ++} ++ ++void hieth_mdiobus_driver_exit(struct hieth_netdev_priv *priv) ++{ ++ struct mii_bus *bus = priv->mii_bus; ++ ++ mdiobus_unregister(bus); ++ kfree(bus->irq); ++ mdiobus_free(bus); ++ hieth_mdio_exit(priv); ++} ++ ++/* vim: set ts=8 sw=8 tw=78: */ +diff --git a/drivers/net/ethernet/hisilicon/hieth/mdio.h b/drivers/net/ethernet/hisilicon/hieth/mdio.h +new file mode 100644 +index 0000000..9bb13fb +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/mdio.h +@@ -0,0 +1,61 @@ ++#ifndef __HIETH_MDIO_H ++#define __HIETH_MDIO_H ++ ++#define HIETH_MDIO_FRQDIV 2 ++ ++#define HIETH_MDIO_RWCTRL 0x1100 ++#define HIETH_MDIO_RO_DATA 0x1104 ++#define HIETH_U_MDIO_PHYADDR 0x0108 ++#define HIETH_D_MDIO_PHYADDR 0x2108 ++#define HIETH_U_MDIO_RO_STAT 0x010C ++#define HIETH_D_MDIO_RO_STAT 0x210C ++#define HIETH_U_MDIO_ANEG_CTRL 0x0110 ++#define HIETH_D_MDIO_ANEG_CTRL 0x2110 ++#define HIETH_U_MDIO_IRQENA 0x0114 ++#define HIETH_D_MDIO_IRQENA 0x2114 ++ ++#define MDIO_MK_RWCTL(cpu_data_in, finish, rw, phy_exaddr, frq_div, phy_regnum)\ ++ (((cpu_data_in) << 16) | \ ++ (((finish) & 0x01) << 15) | \ ++ (((rw) & 0x01) << 13) | \ ++ (((phy_exaddr) & 0x1F) << 8) | \ ++ (((frq_div) & 0x7) << 5) | \ ++ ((phy_regnum) & 0x1F)) ++ ++/* hardware set bit'15 of MDIO_REG(0) if mdio ready */ ++#define mdio_test_ready(priv) (hieth_readl(priv->glb_base, \ ++ HIETH_MDIO_RWCTRL) & (1 << 15)) ++ ++#define mdio_start_phyread(priv, phy_addr, regnum) \ ++ hieth_writel(priv->glb_base, \ ++ MDIO_MK_RWCTL(0, 0, 0, phy_addr, HIETH_MDIO_FRQDIV, \ ++ regnum), \ ++ HIETH_MDIO_RWCTRL) ++ ++#define mdio_get_phyread_val(priv) (hieth_readl(priv->glb_base, \ ++ HIETH_MDIO_RO_DATA) & 0xFFFF) ++ ++#define mdio_phywrite(priv, phy_addr, regnum, val) \ ++ hieth_writel(priv->glb_base, \ ++ MDIO_MK_RWCTL(val, 0, 1, phy_addr, HIETH_MDIO_FRQDIV, \ ++ regnum), \ ++ HIETH_MDIO_RWCTRL) ++ ++/* write mdio registers reset value */ ++#define mdio_reg_reset(priv) do { \ ++ hieth_writel(priv->glb_base, 0x00008000, HIETH_MDIO_RWCTRL); \ ++ hieth_writel(priv->glb_base, 0x00000001, HIETH_U_MDIO_PHYADDR); \ ++ hieth_writel(priv->glb_base, 0x00000001, HIETH_D_MDIO_PHYADDR); \ ++ hieth_writel(priv->glb_base, 0x04631EA9, HIETH_U_MDIO_ANEG_CTRL); \ ++ hieth_writel(priv->glb_base, 0x04631EA9, HIETH_D_MDIO_ANEG_CTRL); \ ++ hieth_writel(priv->glb_base, 0x00000000, HIETH_U_MDIO_IRQENA); \ ++ hieth_writel(priv->glb_base, 0x00000000, HIETH_D_MDIO_IRQENA); \ ++} while (0) ++ ++int hieth_mdiobus_driver_init(struct platform_device *pdev, ++ struct hieth_netdev_priv *priv); ++void hieth_mdiobus_driver_exit(struct hieth_netdev_priv *priv); ++ ++#endif ++ ++/* vim: set ts=8 sw=8 tw=78: */ +diff --git a/drivers/net/ethernet/hisilicon/hieth/phy.c b/drivers/net/ethernet/hisilicon/hieth/phy.c +new file mode 100644 +index 0000000..075a2c3 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/phy.c +@@ -0,0 +1,89 @@ ++#include <linux/platform_device.h> ++#include <linux/reset.h> ++#include "hieth.h" ++#include "mdio.h" ++#include "phy.h" ++ ++static int KSZ8051MNL_phy_fix(struct phy_device *phy_dev) ++{ ++ u32 v; ++ ++ if (phy_dev->interface != PHY_INTERFACE_MODE_RMII) ++ return 0; ++ ++ v = phy_read(phy_dev, 0x1F); ++ v |= (1 << 7); /* set phy RMII 50MHz clk; */ ++ phy_write(phy_dev, 0x1F, v); ++ ++ v = phy_read(phy_dev, 0x16); ++ v |= (1 << 1); /* set phy RMII override; */ ++ phy_write(phy_dev, 0x16, v); ++ ++ return 0; ++} ++ ++static int KSZ8081RNB_phy_fix(struct phy_device *phy_dev) ++{ ++ u32 v; ++ ++ if (phy_dev->interface != PHY_INTERFACE_MODE_RMII) ++ return 0; ++ ++ v = phy_read(phy_dev, 0x1F); ++ v |= (1 << 7); /* set phy RMII 50MHz clk; */ ++ phy_write(phy_dev, 0x1F, v); ++ ++ return 0; ++} ++ ++void hieth_phy_register_fixups(void) ++{ ++ phy_register_fixup_for_uid(PHY_ID_KSZ8051MNL, ++ DEFAULT_PHY_MASK, KSZ8051MNL_phy_fix); ++ phy_register_fixup_for_uid(PHY_ID_KSZ8081RNB, ++ DEFAULT_PHY_MASK, KSZ8081RNB_phy_fix); ++} ++ ++static void hieth_internal_phy_reset(struct hieth_phy_param_s *phy_param) ++{ ++} ++ ++static void hieth_external_phy_reset(struct hieth_phy_param_s *phy_param) ++{ ++ if (phy_param->phy_rst) { ++ /* write 0 to cancel reset */ ++ reset_control_deassert(phy_param->phy_rst); ++ msleep(50); ++ ++ /* RST_BIT, write 1 to reset phy, write 0 to cancel reset */ ++ reset_control_assert(phy_param->phy_rst); ++ ++ /* delay some time to ensure reset ok, ++ * this depends on PHY hardware feature ++ */ ++ msleep(50); ++ ++ /* write 0 to cancel reset */ ++ reset_control_deassert(phy_param->phy_rst); ++ /* delay some time to ensure later MDIO access */ ++ msleep(50); ++ } ++} ++ ++void hieth_phy_reset(void) ++{ ++ int i; ++ struct hieth_phy_param_s *phy_param; ++ ++ for (i = 0; i < HIETH_MAX_PORT; i++) { ++ phy_param = &hieth_phy_param[i]; ++ ++ if (!phy_param->isvalid) ++ continue; ++ ++ if (phy_param->isinternal) ++ hieth_internal_phy_reset(phy_param); ++ else ++ hieth_external_phy_reset(phy_param); ++ } ++} +diff --git a/drivers/net/ethernet/hisilicon/hieth/phy.h b/drivers/net/ethernet/hisilicon/hieth/phy.h +new file mode 100644 +index 0000000..60de1bc +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/phy.h +@@ -0,0 +1,18 @@ ++#ifndef __HIETH_FEPHY_FIX_H ++#define __HIETH_FEPHY_FIX_H ++ ++#define MII_EXPMD 0x1D ++#define MII_EXPMA 0x1E ++ ++#define HISILICON_PHY_ID_FESTAV200 (0x20669823) ++#define HISILICON_PHY_ID_FESTAV300 (0x20669833) ++#define HISILICON_PHY_MASK (0xfffffff0) ++ ++/* the following two copied from phy_quirk() ++ * in "./drivers/net/ethernet/hieth-sf/net.c" ++ */ ++#define PHY_ID_KSZ8051MNL (0x00221550) ++#define PHY_ID_KSZ8081RNB (0x00221560) ++#define DEFAULT_PHY_MASK (0xfffffff0) ++ ++#endif +diff --git a/drivers/net/ethernet/hisilicon/hieth/pm.c b/drivers/net/ethernet/hisilicon/hieth/pm.c +new file mode 100644 +index 0000000..2ace419 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/pm.c +@@ -0,0 +1,323 @@ ++#include <linux/crc16.h> ++ ++#define HIETH_PM_N (31) ++#define HIETH_PM_FILTERS (4) ++ ++struct hieth_pm_config { ++ unsigned char index; /* bit0--eth0 bit1--eth1 */ ++ unsigned char uc_pkts_enable; ++ unsigned char magic_pkts_enable; ++ unsigned char wakeup_pkts_enable; ++ struct { ++ unsigned int mask_bytes : HIETH_PM_N; ++ unsigned int reserved : 1;/* userspace ignore this bit */ ++ unsigned char offset; /* >= 12 */ ++ unsigned char value[HIETH_PM_N];/* byte string */ ++ unsigned char valid; /* valid filter */ ++ } filter[HIETH_PM_FILTERS]; ++}; ++ ++#define HIETH_PMT_CTRL 0x0500 ++#define HIETH_PMT_MASK0 0x0504 ++#define HIETH_PMT_MASK1 0x0508 ++#define HIETH_PMT_MASK2 0x050c ++#define HIETH_PMT_MASK3 0x0510 ++#define HIETH_PMT_CMD 0x0514 ++#define HIETH_PMT_OFFSET 0x0518 ++#define HIETH_PMT_CRC1_0 0x051c ++#define HIETH_PMT_CRC3_2 0x0520 ++ ++static void hieth_init_crc_table(void); ++static unsigned short hieth_compute_crc(char *message, int n_bytes); ++static unsigned short calculate_crc16(char *buf, unsigned int mask) ++{ ++ char data[HIETH_PM_N]; ++ int i, len = 0; ++ ++ memset(data, 0, sizeof(data)); ++ ++ for (i = 0; i < HIETH_PM_N; i++) { ++ if (mask & 0x1) ++ data[len++] = buf[i]; ++ ++ mask >>= 1; ++ } ++ ++ return hieth_compute_crc(data, len); ++} ++ ++#define HIETH_PM_SET (1) ++#define HIETH_PM_CLEAR (0) ++static char pm_state[HIETH_MAX_PORT] = {HIETH_PM_CLEAR, HIETH_PM_CLEAR}; ++ ++int hieth_pmt_config_eth(struct hieth_pm_config *config, ++ struct hieth_netdev_priv *priv) ++{ ++ unsigned int v = 0, cmd = 0, offset = 0; ++ unsigned short crc[HIETH_PM_FILTERS] = {0}; ++ int reg_mask = 0; ++ int i; ++ ++ if (!priv) ++ return -EINVAL; ++ ++ local_lock(priv); ++ if (config->wakeup_pkts_enable) { ++ /* disable wakeup_pkts_enable before reconfig? */ ++ v = hieth_readl(priv->port_base, HIETH_PMT_CTRL); ++ v &= ~(1 << 2); ++ /* any side effect? */ ++ hieth_writel(priv->port_base, v, HIETH_PMT_CTRL); ++ } else { ++ goto config_ctrl; ++ } ++ ++/* filter.valid mask.valid mask_bytes effect ++ * 0 * * no use the filter ++ * 1 0 * all pkts can wake-up(non-exist) ++ * 1 1 0 all pkts can wake-up ++ * 1 1 !0 normal filter ++ */ ++ /* setup filter */ ++ for (i = 0; i < HIETH_PM_FILTERS; i++) { ++ if (config->filter[i].valid) { ++ if (config->filter[i].offset < 12) ++ continue; ++ /* offset and valid bit */ ++ offset |= config->filter[i].offset << (i * 8); ++ cmd |= 1 << (i * 8); /* valid bit */ ++ /* mask */ ++ reg_mask = HIETH_PMT_MASK0 + (i * 4); ++ ++ /* for logic, mask valid bit(bit31) must set to 0, ++ * 0 is enable ++ */ ++ v = config->filter[i].mask_bytes; ++ v &= ~(1 << 31); ++ hieth_writel(priv->port_base, v, reg_mask); ++ ++ /* crc */ ++ crc[i] = calculate_crc16(config->filter[i].value, v); ++ if (i <= 1) {/* for filter0 and filter 1 */ ++ v = hieth_readl(priv->port_base, ++ HIETH_PMT_CRC1_0); ++ v &= ~(0xFFFF << (16 * i)); ++ v |= crc[i] << (16 * i); ++ hieth_writel(priv->port_base, v, ++ HIETH_PMT_CRC1_0); ++ } else {/* filter2 and filter3 */ ++ v = hieth_readl(priv->port_base, ++ HIETH_PMT_CRC3_2); ++ v &= ~(0xFFFF << (16 * (i - 2))); ++ v |= crc[i] << (16 * (i - 2)); ++ hieth_writel(priv->port_base, v, ++ HIETH_PMT_CRC3_2); ++ } ++ } ++ } ++ ++ if (cmd) { ++ hieth_writel(priv->port_base, offset, HIETH_PMT_OFFSET); ++ hieth_writel(priv->port_base, cmd, HIETH_PMT_CMD); ++ } ++ ++config_ctrl: ++ v = 0; ++ if (config->uc_pkts_enable) ++ v |= 1 << 9; /* uc pkts wakeup */ ++ if (config->wakeup_pkts_enable) ++ v |= 1 << 2; /* use filter framework */ ++ if (config->magic_pkts_enable) ++ v |= 1 << 1; /* magic pkts wakeup */ ++ ++ v |= 3 << 5; /* clear irq status */ ++ hieth_writel(priv->port_base, v, HIETH_PMT_CTRL); ++ ++ local_unlock(priv); ++ ++ return 0; ++} ++ ++/* pmt_config will overwrite pre-config */ ++int hieth_pmt_config(struct hieth_pm_config *config) ++{ ++ static int init; ++ int map = config->index, i, ret = -EINVAL; ++ struct hieth_netdev_priv *priv; ++ ++ if (!init) ++ hieth_init_crc_table(); ++ ++ for (i = 0; i < HIETH_MAX_PORT; i++) { ++ if (!hieth_devs_save[i]) ++ continue; ++ ++ priv = netdev_priv(hieth_devs_save[i]); ++ ++ if (map & 0x1) { ++ ret = hieth_pmt_config_eth(config, priv); ++ if (ret) ++ return ret; ++ ++ pm_state[i] = HIETH_PM_SET; ++ device_set_wakeup_enable(priv->dev, 1); ++ } ++ map >>= 1; ++ } ++ ++ return ret; ++} ++ ++inline bool hieth_pmt_enter(void) ++{ ++ int i, v, pm = false; ++ struct hieth_netdev_priv *priv; ++ ++ for (i = 0; i < HIETH_MAX_PORT; i++) { ++ if (!hieth_devs_save[i]) ++ continue; ++ ++ priv = netdev_priv(hieth_devs_save[i]); ++ ++ local_lock(priv); ++ if (pm_state[i] == HIETH_PM_SET) { ++ v = hieth_readl(priv->port_base, HIETH_PMT_CTRL); ++ v |= 1 << 0; /* enter power down */ ++ v |= 1 << 3; /* enable wakeup irq */ ++ v |= 3 << 5; /* clear irq status */ ++ hieth_writel(priv->port_base, v, HIETH_PMT_CTRL); ++ ++ pm_state[i] = HIETH_PM_CLEAR; ++ pm = true; ++ } ++ local_unlock(priv); ++ } ++ return pm; ++} ++ ++inline void hieth_pmt_exit(void) ++{ ++ int i, v; ++ struct hieth_netdev_priv *priv; ++ ++ for (i = 0; i < HIETH_MAX_PORT; i++) { ++ if (!hieth_devs_save[i]) ++ continue; ++ ++ priv = netdev_priv(hieth_devs_save[i]); ++ ++ /* logic auto exit power down mode */ ++ local_lock(priv); ++ ++ v = hieth_readl(priv->port_base, HIETH_PMT_CTRL); ++ v &= ~(1 << 0); /* enter power down */ ++ v &= ~(1 << 3); /* enable wakeup irq */ ++ ++ v |= 3 << 5; /* clear irq status */ ++ hieth_writel(priv->port_base, v, HIETH_PMT_CTRL); ++ ++ local_unlock(priv); ++ } ++ ++ /* device_set_wakeup_enable(priv->dev, 0); */ ++} ++ ++/* ========the following code copy from Synopsys DWC_gmac_crc_example.c====== */ ++#define CRC16 /* Change it to CRC16 for CRC16 Computation*/ ++ ++#define FALSE 0 ++#define TRUE !FALSE ++ ++#if defined(CRC16) ++#define CRC_NAME "CRC-16" ++#define POLYNOMIAL 0x8005 ++#define INITIAL_REMAINDER 0xFFFF ++#define FINAL_XOR_VALUE 0x0000 ++#define REVERSE_DATA TRUE ++#define REVERSE_REMAINDER FALSE ++#endif ++ ++#define WIDTH (8 * sizeof(unsigned short)) ++#define TOPBIT (1 << (WIDTH - 1)) ++ ++#if (REVERSE_DATA) ++#undef REVERSE_DATA ++#define REVERSE_DATA(X) ((unsigned char)reverse((X), 8)) ++#else ++#undef REVERSE_DATA ++#define REVERSE_DATA(X) (X) ++#endif ++ ++#if (REVERSE_REMAINDER) ++#undef REVERSE_REMAINDER ++#define REVERSE_REMAINDER(X) ((unsigned short)reverse((X), WIDTH)) ++#else ++#undef REVERSE_REMAINDER ++#define REVERSE_REMAINDER(X) (X) ++#endif ++ ++static unsigned short crctable[256]; ++ ++/* Reverse the data ++ * ++ * Input1: Data to be reversed ++ * Input2: number of bits in the data ++ * Output: The reversed data ++ */ ++unsigned long reverse(unsigned long data, unsigned char nbits) ++{ ++ unsigned long reversed = 0x00000000; ++ unsigned char bit; ++ ++ /* Reverse the data about the center bit. */ ++ for (bit = 0; bit < nbits; ++bit) { ++ /* If the LSB bit is set, set the reflection of it. */ ++ if (data & 0x01) ++ reversed |= (1 << ((nbits - 1) - bit)); ++ ++ data = (data >> 1); ++ } ++ return reversed; ++} ++ ++/* This Initializes the partial CRC look up table */ ++static void hieth_init_crc_table(void) ++{ ++ unsigned short remainder; ++ int dividend; ++ unsigned char bit; ++ ++ /* Compute the remainder of each possible dividend. */ ++ for (dividend = 0; dividend < 256; ++dividend) { ++ /* Start with the dividend followed by zeros. */ ++ remainder = (unsigned short)(dividend << (WIDTH - 8)); ++ ++ /* Perform modulo-2 division, a bit at a time. */ ++ for (bit = 8; bit > 0; --bit) { ++ /* Try to divide the current data bit. */ ++ if (remainder & TOPBIT) ++ remainder = (remainder << 1) ^ POLYNOMIAL; ++ else ++ remainder = (remainder << 1); ++ } ++ ++ /* Store the result into the table. */ ++ crctable[dividend] = remainder; ++ } ++} ++ ++static unsigned short hieth_compute_crc(char *message, int n_bytes) ++{ ++ unsigned short remainder = INITIAL_REMAINDER; ++ int byte; ++ unsigned char data; ++ ++ /* Divide the message by the polynomial, a byte at a time. */ ++ for (byte = 0; byte < n_bytes; ++byte) { ++ data = REVERSE_DATA(message[byte]) ^ (remainder >> (WIDTH - 8)); ++ remainder = crctable[data] ^ (remainder << 8); ++ } ++ ++ /* The final remainder is the CRC. */ ++ return (REVERSE_REMAINDER(remainder) ^ FINAL_XOR_VALUE); ++} +diff --git a/drivers/net/ethernet/hisilicon/hieth/proc.c b/drivers/net/ethernet/hisilicon/hieth/proc.c +new file mode 100644 +index 0000000..54eea7b +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/proc.c +@@ -0,0 +1,89 @@ ++#include <linux/proc_fs.h> ++ ++#include "hieth.h" ++#include "proc.h" ++ ++static int hieth_tx_info_read(struct seq_file *m, void *v) ++{ ++#ifdef HIETH_TSO_DEBUG ++ { ++ int i; ++ int id_now; ++ ++ if (id_send == 0) ++ id_now = MAX_RECORD-1; ++ else ++ id_now = id_send-1; ++ ++ for (i = id_now; i >= 0; i--) { ++ seq_printf(m, "%x,%x,%x\n", ++ pkt_rec[i].reg_addr, ++ pkt_rec[i].reg_pkt_info, ++ pkt_rec[i].status); ++ } ++ for (i = MAX_RECORD-1; i > id_now; i--) { ++ seq_printf(m, "%x,%x,%x\n", ++ pkt_rec[i].reg_addr, ++ pkt_rec[i].reg_pkt_info, ++ pkt_rec[i].status); ++ } ++ } ++#endif ++ return 0; ++} ++ ++static struct proc_dir_entry *hieth_proc_root; ++ ++#define proc_open(name) \ ++static int proc_open_##name(struct inode *inode, struct file *file) \ ++{ \ ++ return single_open(file, name, PDE_DATA(inode)); \ ++} \ ++ ++proc_open(hieth_tx_info_read); ++ ++static struct proc_file { ++ char *name; ++ const struct file_operations ops; ++} proc_file[] = { ++ { ++ .name = "tx_info", ++ .ops = { ++ .open = proc_open_hieth_tx_info_read, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ }, ++ }, ++}; ++ ++/* /proc/hieth/ ++ * |---tx_info ++ */ ++void hieth_proc_create(void) ++{ ++ int i; ++ ++ hieth_proc_root = proc_mkdir("hieth", NULL); ++ if (!hieth_proc_root) ++ return; ++ ++ for (i = 0; i < ARRAY_SIZE(proc_file); i++) { ++ struct proc_dir_entry *entry; ++ ++ entry = proc_create(proc_file[i].name, 0, hieth_proc_root, ++ &proc_file[i].ops); ++ if (!entry) ++ pr_err("failed to create %s\n", proc_file[i].name); ++ } ++} ++ ++void hieth_proc_destroy(void) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(proc_file); i++) ++ remove_proc_entry(proc_file[i].name, hieth_proc_root); ++ ++ remove_proc_entry("hieth", NULL); ++} +diff --git a/drivers/net/ethernet/hisilicon/hieth/proc.h b/drivers/net/ethernet/hisilicon/hieth/proc.h +new file mode 100644 +index 0000000..20cc8d4 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/proc.h +@@ -0,0 +1,7 @@ ++#ifndef __HIETH_PROC_H ++#define __HIETH_PROC_H ++ ++void hieth_proc_create(void); ++void hieth_proc_destroy(void); ++ ++#endif +diff --git a/drivers/net/ethernet/hisilicon/hieth/tso.h b/drivers/net/ethernet/hisilicon/hieth/tso.h +new file mode 100644 +index 0000000..9929869 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hieth/tso.h +@@ -0,0 +1,76 @@ ++#ifndef __HIETH_TSO_H ++#define __HIETH_TSO_H ++ ++#define TSO_FLAG (1 << 31) ++#define VLAN_FLAG (1 << 30) ++#define IPV6_FLAG (1 << 29) ++#define UDP_FLAG (1 << 28) ++#define TX_CSUM_FLAG (1 << 27) ++#define SG_FLAG (1 << 26) ++ ++#define PKT_IPV6_HDR_LEN (10) ++#define PKT_UDP_HDR_LEN (2) ++#define WORD_TO_BYTE (4) ++ ++enum { ++ PKT_NORMAL, ++ PKT_SG ++}; ++ ++enum { ++ PKT_IPV4, ++ PKT_IPV6 ++}; ++ ++enum { ++ PKT_TCP, ++ PKT_UDP ++}; ++ ++struct frags_info { ++ /* Word(2*i+2) */ ++ u32 addr; ++ /* Word(2*i+3) */ ++ u32 size:16; ++ u32 reserved:16; ++}; ++ ++struct dma_tx_desc { ++ /* Word0 */ ++ u32 total_len:17; ++ u32 reserv:15; ++ /* Word1 */ ++ u32 ipv6_id; ++ /* Word2 */ ++ u32 linear_addr; ++ /* Word3 */ ++ u32 linear_len:16; ++ u32 reserv3:16; ++ /* MAX_SKB_FRAGS = 17 */ ++ struct frags_info frags[30]; ++ /* struct frags_info frags[MAX_SKB_FRAGS]; */ ++}; ++ ++struct tx_pkt_info { ++ union { ++ struct { ++ u32 data_len:11; ++ u32 nfrags_num:5; ++ u32 prot_hdr_len:4; ++ u32 ip_hdr_len:4; ++ u32 reserved:2; ++ u32 sg_flag:1; ++ u32 coe_flag:1; ++ u32 prot_type:1; ++ u32 ip_ver:1; ++ u32 vlan_flag:1; ++ u32 tso_flag:1; ++ } info; ++ u32 val; ++ } tx; ++ u32 tx_addr; /* normal pkt, skb->data */ ++ u32 sg_desc_offset; /* TSO pkt, desc addr */ ++ struct sk_buff *skb; ++}; ++ ++#endif +diff --git a/drivers/net/ethernet/hisilicon/higmac/Kconfig b/drivers/net/ethernet/hisilicon/higmac/Kconfig +new file mode 100644 +index 0000000..bda6f74 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/Kconfig +@@ -0,0 +1,96 @@ ++# ++# higmac family network device configuration ++# ++ ++menuconfig HIETH_GMAC ++ tristate "hieth gmac family network device support" ++ select PHYLIB ++ select RESET_CONTROLLER ++ help ++ This selects the hieth gmac family network device. ++ The gigabit switch fabric (GSF) receives and transmits data over Ethernet ++ ports at 10/100/1000 Mbit/s in full-duplex or half-duplex mode. ++ The Ethernet port exchanges data with the CPU port, and supports ++ the energy efficient Ethernet (EEE) and wake on LAN (WoL) functions. ++ ++if HIETH_GMAC ++ ++config HIGMAC_DESC_4WORD ++ bool "higmac descriptor size is 4 words" ++ default y ++ help ++ This define the size of higmac descriptor structure. ++ In the newest version, descriptor size is 4 words. ++ But in some old version, the size is 8 words. ++ The default value is true. ++ ++config HIGMAC_RXCSUM ++ bool "higmac Receive checksumming offload supported" ++ default y ++ help ++ This indicate MAC support Receive checksumming offload. ++ Support IPv4 and IPv6, tcp and udp. ++ The default value is enabled. ++ If old version MAC does not support, disable this option please. ++ ++config RX_FLOW_CTRL_SUPPORT ++ bool "rx flow ctrl supported" ++ default y ++ help ++ Rx flow ctrl supported, default is enabled. ++ When we received pause frame, ++ we will stop transmiting data frame for some time. ++ The stopping time is the time filled in pause frame. ++ ++config TX_FLOW_CTRL_SUPPORT ++ bool "tx flow ctrl supported" ++ default y ++ help ++ Tx flow ctrl supported, default is enabled. ++ When we has no buffer to receive packet, ++ we will send pause frame. ++ When buffer is available, we will send zero-quanta pause frame. ++ ++config TX_FLOW_CTRL_PAUSE_TIME ++ hex "tx flow ctrl pause time" ++ default "0xFFFF" ++ help ++ The pause time filled in the sending pause frame. ++ The unit is the time for transmiting 512 bit data. ++ This value is 16 bit, so its value is 0x0000~0xFFFF. ++ The default value is 0xFFFF. ++ ++config TX_FLOW_CTRL_PAUSE_INTERVAL ++ hex "tx flow ctrl pause interval" ++ default "0xFFFF" ++ help ++ The interval time for sending pause frame. ++ When the remainint amount of receive queue is below tx flow ctrl active threshold, ++ we will wait this time to transmiting pause frame. ++ The unit is the time for transmiting 512 bit data. ++ This value is 16 bit, so its value is 0x0000~0xFFFF. ++ The default value is 0xFFFF. ++ ++config TX_FLOW_CTRL_ACTIVE_THRESHOLD ++ int "tx flow ctrl active threshold" ++ default "16" ++ range 1 127 ++ help ++ The threshold for activing tx flow ctrl. ++ When the left amount of receive queue descriptors is below this threshold, ++ hardware will send pause frame immediately. ++ We advise this value is set smaller than 64. Too bigger is not a good choice. ++ This value must be smaller than tx flow ctrl deactive threshold. ++ ++config TX_FLOW_CTRL_DEACTIVE_THRESHOLD ++ int "tx flow ctrl deactive threshold" ++ default "32" ++ range 1 127 ++ help ++ The threshold for deactiving tx flow ctrl. ++ When the left amount of receive queue descriptors is above or equal with this threshold, ++ hardware will exit flow control state. ++ We advise this value is set smaller than 64. Too bigger is not a good choice. ++ This value must be larger than tx flow ctrl active threshold. ++ ++endif # HIETH_GMAC +diff --git a/drivers/net/ethernet/hisilicon/higmac/Makefile b/drivers/net/ethernet/hisilicon/higmac/Makefile +new file mode 100644 +index 0000000..e3d9c53 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_HIETH_GMAC) += hieth-gmac.o ++hieth-gmac-objs := board.o higmac.o autoeee/autoeee.o autoeee/phy_id_table.o +diff --git a/drivers/net/ethernet/hisilicon/higmac/autoeee/autoeee.c b/drivers/net/ethernet/hisilicon/higmac/autoeee/autoeee.c +new file mode 100644 +index 0000000..be12244 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/autoeee/autoeee.c +@@ -0,0 +1,125 @@ ++#include <linux/phy.h> ++#include <linux/micrel_phy.h> ++#include "../higmac.h" ++#include "autoeee.h" ++ ++void init_autoeee(struct higmac_netdev_local *ld) ++{ ++ int phy_id = ld->phy->phy_id; ++ struct phy_info *phy_info; ++ ++ if (ld->eee_init) ++ goto eee_init; ++ ++ phy_info = phy_search_ids(phy_id); ++ if (phy_info) { ++ int eee_available, lp_eee_capable, v; ++ u32 link_stat = 0; ++ ++ eee_available = phy_info->eee_available; ++ if (netif_msg_wol(ld)) ++ pr_info("fit phy_id:0x%x, phy_name:%s, eee:%d\n", ++ phy_info->phy_id, phy_info->name, ++ eee_available); ++ ++ if (!eee_available) ++ goto not_support; ++ ++ if (eee_available == PHY_EEE) { ++ if (netif_msg_wol(ld)) ++ pr_info("enter phy-EEE mode\n"); ++ ++ v = readl(ld->gmac_iobase + EEE_ENABLE); ++ v &= ~BIT_EEE_ENABLE; /* disable auto-EEE */ ++ writel(v, ld->gmac_iobase + EEE_ENABLE); ++ return; ++ } ++ ++ ld->eee_init = phy_info->eee_init; ++eee_init: ++ switch (ld->phy->speed) { ++ case SPEED_10: ++ link_stat |= HIGMAC_SPD_10M; ++ break; ++ case SPEED_100: ++ link_stat |= HIGMAC_SPD_100M; ++ break; ++ case SPEED_1000: ++ link_stat |= HIGMAC_SPD_1000M; ++ break; ++ default: ++ break; ++ } ++ ++ lp_eee_capable = ld->eee_init(ld->phy); ++ if (lp_eee_capable < 0) ++ return; ++ ++ if (ld->phy->link) { ++ if (((u32)lp_eee_capable) & link_stat) { ++ if ((phy_id & REALTEK_PHY_MASK) == ++ REALTEK_PHY_ID_8211E) { ++ v = readl(ld->gmac_iobase + EEE_CLK); ++ v &= ~MASK_EEE_CLK; ++ v |= BIT_DISABLE_TX_CLK; ++ writel(v, ld->gmac_iobase + EEE_CLK); ++ } else if ((phy_id & MICREL_PHY_ID_MASK) == ++ PHY_ID_KSZ9031) { ++ v = readl(ld->gmac_iobase + EEE_CLK); ++ v &= ~MASK_EEE_CLK; ++ v |= (BIT_DISABLE_TX_CLK | ++ BIT_PHY_KSZ9031); ++ writel(v, ld->gmac_iobase + EEE_CLK); ++ } ++ ++ /* EEE_1us: 0x7c for 125M */ ++ writel(0x7c, ld->gmac_iobase + ++ EEE_TIME_CLK_CNT); ++ writel(0x1e0400, ld->gmac_iobase + ++ EEE_TIMER);/* FIXME */ ++ ++ v = readl(ld->gmac_iobase + EEE_LINK_STATUS); ++ v |= 0x3 << 1; /* auto EEE and ... */ ++ v |= BIT_PHY_LINK_STATUS; /* phy linkup */ ++ writel(v, ld->gmac_iobase + EEE_LINK_STATUS); ++ ++ v = readl(ld->gmac_iobase + EEE_ENABLE); ++ v |= BIT_EEE_ENABLE; /* enable EEE */ ++ writel(v, ld->gmac_iobase + EEE_ENABLE); ++ ++ if (netif_msg_wol(ld)) ++ pr_info("enter auto-EEE mode\n"); ++ } else { ++ if (netif_msg_wol(ld)) ++ pr_info("link partner not support EEE\n"); ++ } ++ } else { ++ v = readl(ld->gmac_iobase + EEE_LINK_STATUS); ++ v &= ~(BIT_PHY_LINK_STATUS); /* phy linkdown */ ++ writel(v, ld->gmac_iobase + EEE_LINK_STATUS); ++ } ++ ++ return; ++ } ++ ++not_support: ++ ld->eee_init = NULL; ++ if (netif_msg_wol(ld)) ++ pr_info("non-EEE mode\n"); ++} ++ ++void eee_phy_linkdown(struct higmac_netdev_local *ld) ++{ ++ int v = readl(ld->gmac_iobase + EEE_LINK_STATUS); ++ /* update phy link state */ ++ v &= ~BIT_PHY_LINK_STATUS; ++ writel(v, ld->gmac_iobase + EEE_LINK_STATUS); ++} ++ ++void eee_phy_linkup(struct higmac_netdev_local *ld) ++{ ++ int v = readl(ld->gmac_iobase + EEE_LINK_STATUS); ++ /* update phy link state */ ++ v |= BIT_PHY_LINK_STATUS; ++ writel(v, ld->gmac_iobase + EEE_LINK_STATUS); ++} +diff --git a/drivers/net/ethernet/hisilicon/higmac/autoeee/autoeee.h b/drivers/net/ethernet/hisilicon/higmac/autoeee/autoeee.h +new file mode 100644 +index 0000000..8f75a7a +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/autoeee/autoeee.h +@@ -0,0 +1,42 @@ ++#ifndef _AUTO_EEE_H ++ ++#define NO_EEE 0 ++#define MAC_EEE 1 ++#define PHY_EEE 2 ++#define PARTNER_EEE 2 ++ ++struct phy_info { ++ char *name; ++ int phy_id; ++ char eee_available; /* eee support by this phy */ ++ int (*eee_init)(struct phy_device *phy_dev); ++}; ++ ++/* GMAC register definition */ ++#define EEE_CLK 0x800 ++#define MASK_EEE_CLK (0x3 << 20) ++#define BIT_DISABLE_TX_CLK BIT(21) ++#define BIT_PHY_KSZ9031 BIT(20) ++#define EEE_ENABLE 0x808 ++#define BIT_EEE_ENABLE BIT(0) ++#define EEE_TIMER 0x80C ++#define EEE_LINK_STATUS 0x810 ++#define BIT_PHY_LINK_STATUS BIT(0) ++#define EEE_TIME_CLK_CNT 0x814 ++ ++/* ----------------------------phy register-------------------------------*/ ++/* MMD: MDIO Manageable Device */ ++#define MACR 0x0D ++#define MAADR 0x0E ++#define EEE_DEV 0x3 ++#define EEE_CAPABILITY 0x14 ++#define EEELPAR_DEV 0x7 ++#define EEELPAR 0x3D /* EEE link partner ability register */ ++#define EEE_ADVERTISE 0x3c ++#define LP_1000BASE_EEE BIT(2) ++#define LP_100BASE_EEE BIT(1) ++ ++struct phy_info *phy_search_ids(int phy_id); ++void init_autoeee(struct higmac_netdev_local *ld); ++ ++#endif +diff --git a/drivers/net/ethernet/hisilicon/higmac/autoeee/phy_id_table.c b/drivers/net/ethernet/hisilicon/higmac/autoeee/phy_id_table.c +new file mode 100644 +index 0000000..8ffaf2a +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/autoeee/phy_id_table.c +@@ -0,0 +1,177 @@ ++#include <linux/delay.h> ++#include <linux/kernel.h> ++#include <linux/phy.h> ++#include "../higmac.h" ++#include "autoeee.h" ++ ++struct phy_info phy_info_table[]; ++ ++struct phy_info *phy_search_ids(int phy_id) ++{ ++ int i; ++ struct phy_info *fit_info = NULL; ++ ++ for (i = 0; phy_info_table[i].name; i++) { ++ if (phy_id == phy_info_table[i].phy_id) ++ fit_info = &phy_info_table[i]; ++ } ++ ++ return fit_info; ++} ++ ++static inline int phy_mmd_read(struct phy_device *phy_dev, ++ u32 mmd_device, u32 regnum) ++{ ++ phy_write(phy_dev, MACR, mmd_device); /* function = 00 address */ ++ phy_write(phy_dev, MAADR, regnum); ++ phy_write(phy_dev, MACR, 0x4000 | mmd_device); /* function = 01 data */ ++ ++ return phy_read(phy_dev, MAADR); ++} ++ ++static inline int phy_mmd_write(struct phy_device *phy_dev, u32 mmd_device, ++ u32 regnum, u16 val) ++{ ++ phy_write(phy_dev, MACR, mmd_device); /* function = 00 address */ ++ phy_write(phy_dev, MAADR, regnum); ++ phy_write(phy_dev, MACR, 0x4000 | mmd_device); /* function = 01 data */ ++ ++ return phy_write(phy_dev, MAADR, val); ++} ++ ++static int smsc_lan8740_init(struct phy_device *phy_dev) ++{ ++ static int first_time; ++ int v, eee_type = 0; ++ ++ if (!first_time) { ++ /* Realtek LAN 8740 start to enable eee */ ++ int eee_lan; ++ ++ eee_lan = phy_read(phy_dev, 0x10); ++ if (eee_lan < 0) ++ return eee_lan; ++ eee_lan |= 0x4; ++ phy_write(phy_dev, 0x10, eee_lan); ++ eee_lan = phy_read(phy_dev, 0x10); ++ if (eee_lan < 0) ++ return eee_lan; ++ /* auto negotiate after enable eee */ ++ eee_lan = phy_read(phy_dev, 0x0); ++ if (eee_lan < 0) ++ return eee_lan; ++ eee_lan |= 0x200; ++ phy_write(phy_dev, 0x0, eee_lan); ++ first_time = 1; ++ } ++ ++ v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEELPAR); ++ ++ if (v & LP_1000BASE_EEE) ++ eee_type |= HIGMAC_SPD_1000M; ++ if (v & LP_100BASE_EEE) ++ eee_type |= HIGMAC_SPD_100M; ++ ++ return eee_type; ++} ++ ++#define RTL8211EG_MAC 0 ++#if RTL8211EG_MAC ++static int rtl8211EG_mac_init(struct phy_device *phy_dev) ++{ ++ static int first_time; ++ /* Realtek 8211EG start reset to change eee to mac */ ++ int v, eee_type = 0; ++ ++ if (!first_time) { ++ int tmp = 0; ++ ++ phy_write(phy_dev, 0x1f, 0x0); ++ phy_write(phy_dev, MII_BMCR, BMCR_RESET); /* reset phy */ ++ do { /* wait phy restart over */ ++ udelay(1); ++ tmp = phy_read(phy_dev, MII_BMSR); ++ /* no need to wait AN finished */ ++ tmp &= (BMSR_ANEGCOMPLETE | BMSR_ANEGCAPABLE); ++ } while (!tmp); ++ ++ phy_write(phy_dev, 0x1f, 0x7); ++ phy_write(phy_dev, 0x1e, 0x20); ++ phy_write(phy_dev, 0x1b, 0xa03a); ++ phy_write(phy_dev, 0x1f, 0x0); ++ ++ first_time = 1; ++ } ++ ++ v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEELPAR); ++ ++ if (v & LP_1000BASE_EEE) ++ eee_type |= HIGMAC_SPD_1000M; ++ if (v & LP_100BASE_EEE) ++ eee_type |= HIGMAC_SPD_100M; ++ ++ return eee_type; ++} ++#else ++static int rtl8211EG_init(struct phy_device *phy_dev) ++{ ++ int eee_type = 0, v; ++ ++ v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEELPAR); ++ ++ if (v & LP_1000BASE_EEE) ++ eee_type |= HIGMAC_SPD_1000M; ++ if (v & LP_100BASE_EEE) ++ eee_type |= HIGMAC_SPD_100M; ++ ++ return eee_type; ++} ++#endif ++ ++static int festa_v200_init(struct phy_device *phy_dev) ++{ ++ static int first_time_init; ++ int v, eee_type = 0; ++ ++ if (!first_time_init) { ++ /* EEE_CAPABILITY register: support 100M-BaseT */ ++ v = phy_mmd_read(phy_dev, EEE_DEV, EEE_CAPABILITY); ++ phy_mmd_write(phy_dev, EEE_DEV, EEE_CAPABILITY, ++ ((u32)v) | BIT(1)); ++ ++ /* EEE_ADVERTISEMENT register: advertising 100M-BaseT */ ++ v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEE_ADVERTISE); ++ phy_mmd_write(phy_dev, EEELPAR_DEV, EEE_ADVERTISE, ++ ((u32)v) | BIT(1)); ++ ++ v = phy_read(phy_dev, MII_BMCR); ++ if (v < 0) ++ return v; ++ v |= (BMCR_ANENABLE | BMCR_ANRESTART); ++ phy_write(phy_dev, MII_BMCR, v); /* auto-neg restart */ ++ ++ first_time_init = 1; ++ } ++ ++ v = phy_mmd_read(phy_dev, EEELPAR_DEV, EEELPAR); ++ ++ if (v & LP_1000BASE_EEE) ++ eee_type |= HIGMAC_SPD_1000M; ++ if (v & LP_100BASE_EEE) ++ eee_type |= HIGMAC_SPD_100M; ++ ++ return eee_type; ++} ++ ++struct phy_info phy_info_table[] = { ++ /* phy_name phy_id eee_available phy_driver */ ++/* SMSC */ ++ {"SMSC LAN8740", 0x0007c110, MAC_EEE, &smsc_lan8740_init}, ++/* Realtek */ ++#if RTL8211EG_MAC ++ {"Realtek 8211EG", 0x001cc915, MAC_EEE, &rtl8211EG_mac_init}, ++#else ++ {"Realtek 8211EG", 0x001cc915, PHY_EEE, &rtl8211EG_init}, ++#endif ++ {"Festa V200", HISILICON_PHY_ID_FESTAV200, MAC_EEE, &festa_v200_init}, ++}; +diff --git a/drivers/net/ethernet/hisilicon/higmac/board.c b/drivers/net/ethernet/hisilicon/higmac/board.c +new file mode 100644 +index 0000000..89b9d16 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/board.c +@@ -0,0 +1,96 @@ ++#include <linux/clk.h> ++#include <linux/kernel.h> ++#include <linux/reset.h> ++#include "higmac.h" ++ ++void higmac_mac_core_reset(struct higmac_netdev_local *priv) ++{ ++ /* undo reset */ ++ reset_control_deassert(priv->port_rst); ++ usleep_range(50, 60); ++ ++ /* soft reset mac port */ ++ reset_control_assert(priv->port_rst); ++ usleep_range(50, 60); ++ /* undo reset */ ++ reset_control_deassert(priv->port_rst); ++} ++ ++void higmac_hw_internal_phy_reset(struct higmac_netdev_local *priv) ++{ ++} ++ ++void higmac_hw_phy_reset(struct higmac_netdev_local *priv) ++{ ++ if (priv->internal_phy) ++ higmac_hw_internal_phy_reset(priv); ++ else ++ higmac_hw_external_phy_reset(priv); ++} ++ ++void higmac_hw_external_phy_reset(struct higmac_netdev_local *priv) ++{ ++ if (priv->phy_rst) { ++ /* write 0 to cancel reset */ ++ reset_control_deassert(priv->phy_rst); ++ msleep(50); ++ ++ /* HIFONE or 98cv200 use CRG register to reset phy */ ++ /* RST_BIT, write 0 to reset phy, write 1 to cancel reset */ ++ reset_control_assert(priv->phy_rst); ++ ++ /* delay some time to ensure reset ok, ++ * this depends on PHY hardware feature ++ */ ++ msleep(50); ++ ++ /* write 0 to cancel reset */ ++ reset_control_deassert(priv->phy_rst); ++ /* delay some time to ensure later MDIO access */ ++ msleep(50); ++ } ++} ++ ++void higmac_internal_phy_clk_disable(struct higmac_netdev_local *priv) ++{ ++} ++ ++void higmac_internal_phy_clk_enable(struct higmac_netdev_local *priv) ++{ ++} ++ ++void higmac_hw_all_clk_disable(struct higmac_netdev_local *priv) ++{ ++ /* If macif clock is enabled when suspend, we should ++ * disable it here. ++ * Because when resume, PHY will link up again and ++ * macif clock will be enabled too. If we don't disable ++ * macif clock in suspend, macif clock will be enabled twice. ++ */ ++ if (priv->netdev->flags & IFF_UP) ++ clk_disable_unprepare(priv->macif_clk); ++ ++ /* This is called in suspend, when net device is down, ++ * MAC clk is disabled. ++ * So we need to judge whether MAC clk is enabled, ++ * otherwise kernel will WARNING if clk disable twice. ++ */ ++ if (priv->netdev->flags & IFF_UP) ++ clk_disable_unprepare(priv->clk); ++ ++ if (priv->internal_phy) ++ higmac_internal_phy_clk_disable(priv); ++} ++ ++void higmac_hw_all_clk_enable(struct higmac_netdev_local *priv) ++{ ++ if (priv->internal_phy) ++ higmac_internal_phy_clk_enable(priv); ++ ++ if (priv->netdev->flags & IFF_UP) ++ clk_prepare_enable(priv->macif_clk); ++ ++ /* If net device is down when suspend, we should not enable MAC clk. */ ++ if (priv->netdev->flags & IFF_UP) ++ clk_prepare_enable(priv->clk); ++} +diff --git a/drivers/net/ethernet/hisilicon/higmac/higmac.c b/drivers/net/ethernet/hisilicon/higmac/higmac.c +new file mode 100644 +index 0000000..135788f +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/higmac.c +@@ -0,0 +1,3120 @@ ++#include <linux/kernel.h> ++#include <linux/errno.h> ++#include <linux/unistd.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/skbuff.h> ++#include <linux/spinlock.h> ++#include <linux/mm.h> ++#include <linux/mii.h> ++#include <linux/ethtool.h> ++#include <linux/phy.h> ++#include <linux/dma-mapping.h> ++#include <linux/workqueue.h> ++#include <linux/device.h> ++#include <linux/atomic.h> ++#include <linux/platform_device.h> ++#include <linux/capability.h> ++#include <linux/time.h> ++#include <asm/setup.h> ++#include <linux/proc_fs.h> ++#include <linux/module.h> ++ ++#include <linux/circ_buf.h> ++#include <linux/if_vlan.h> ++#include <linux/ip.h> ++#include <linux/ipv6.h> ++#include <net/ipv6.h> ++ ++#include <linux/of_net.h> ++#include <linux/of_mdio.h> ++#include <linux/clk.h> ++#include <linux/reset.h> ++ ++#include "util.h" ++#include "higmac.h" ++#include "autoeee/autoeee.h" ++#include "sockioctl.h" ++ ++#define HAS_TSO_CAP(hw_cap) ((((hw_cap) >> 28) & 0x3) == VER_TSO) ++#define HAS_RXHASH_CAP(hw_cap) ((hw_cap) & BIT(30)) ++#define HAS_RSS_CAP(hw_cap) ((hw_cap) & BIT(31)) ++ ++#define RGMII_SPEED_1000 0x2c ++#define RGMII_SPEED_100 0x2f ++#define RGMII_SPEED_10 0x2d ++#define MII_SPEED_100 0x0f ++#define MII_SPEED_10 0x0d ++#define RMII_SPEED_100 0x8f ++#define RMII_SPEED_10 0x8d ++#define GMAC_FULL_DUPLEX BIT(4) ++ ++static unsigned int flow_ctrl_en = FLOW_OFF; ++static int tx_flow_ctrl_pause_time = CONFIG_TX_FLOW_CTRL_PAUSE_TIME; ++static int tx_flow_ctrl_pause_interval = CONFIG_TX_FLOW_CTRL_PAUSE_INTERVAL; ++static int tx_flow_ctrl_active_threshold = CONFIG_TX_FLOW_CTRL_ACTIVE_THRESHOLD; ++static int tx_flow_ctrl_deactive_threshold = ++ CONFIG_TX_FLOW_CTRL_DEACTIVE_THRESHOLD; ++ ++#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) ++static int debug = -1; ++module_param(debug, int, 0000); ++MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); ++ ++static void higmac_config_port(struct net_device *dev, u32 speed, u32 duplex) ++{ ++ struct higmac_netdev_local *priv = netdev_priv(dev); ++ u32 val; ++ ++ switch (priv->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ if (speed == SPEED_1000) ++ val = RGMII_SPEED_1000; ++ else if (speed == SPEED_100) ++ val = RGMII_SPEED_100; ++ else ++ val = RGMII_SPEED_10; ++ break; ++ case PHY_INTERFACE_MODE_MII: ++ if (speed == SPEED_100) ++ val = MII_SPEED_100; ++ else ++ val = MII_SPEED_10; ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ if (speed == SPEED_100) ++ val = RMII_SPEED_100; ++ else ++ val = RMII_SPEED_10; ++ break; ++ default: ++ netdev_warn(dev, "not supported mode\n"); ++ val = MII_SPEED_10; ++ break; ++ } ++ ++ if (duplex) ++ val |= GMAC_FULL_DUPLEX; ++ ++ reset_control_assert(priv->macif_rst); ++ writel_relaxed(val, priv->macif_base); ++ reset_control_deassert(priv->macif_rst); ++ ++ writel_relaxed(BIT_MODE_CHANGE_EN, priv->gmac_iobase + MODE_CHANGE_EN); ++ if (speed == SPEED_1000) ++ val = GMAC_SPEED_1000; ++ else if (speed == SPEED_100) ++ val = GMAC_SPEED_100; ++ else ++ val = GMAC_SPEED_10; ++ writel_relaxed(val, priv->gmac_iobase + PORT_MODE); ++ writel_relaxed(0, priv->gmac_iobase + MODE_CHANGE_EN); ++ writel_relaxed(duplex, priv->gmac_iobase + MAC_DUPLEX_HALF_CTRL); ++} ++ ++static void higmac_set_desc_depth(struct higmac_netdev_local *priv, ++ u32 rx, u32 tx) ++{ ++ u32 reg; ++ int i; ++ ++ writel(BITS_RX_FQ_DEPTH_EN, priv->gmac_iobase + RX_FQ_REG_EN); ++ writel(rx << DESC_WORD_SHIFT, priv->gmac_iobase + RX_FQ_DEPTH); ++ writel(0, priv->gmac_iobase + RX_FQ_REG_EN); ++ ++ writel(BITS_RX_BQ_DEPTH_EN, priv->gmac_iobase + RX_BQ_REG_EN); ++ writel(rx << DESC_WORD_SHIFT, priv->gmac_iobase + RX_BQ_DEPTH); ++ for (i = 1; i < priv->num_rxqs; i++) { ++ reg = RX_BQ_DEPTH_QUEUE(i); ++ writel(rx << DESC_WORD_SHIFT, priv->gmac_iobase + reg); ++ } ++ writel(0, priv->gmac_iobase + RX_BQ_REG_EN); ++ ++ writel(BITS_TX_BQ_DEPTH_EN, priv->gmac_iobase + TX_BQ_REG_EN); ++ writel(tx << DESC_WORD_SHIFT, priv->gmac_iobase + TX_BQ_DEPTH); ++ writel(0, priv->gmac_iobase + TX_BQ_REG_EN); ++ ++ writel(BITS_TX_RQ_DEPTH_EN, priv->gmac_iobase + TX_RQ_REG_EN); ++ writel(tx << DESC_WORD_SHIFT, priv->gmac_iobase + TX_RQ_DEPTH); ++ writel(0, priv->gmac_iobase + TX_RQ_REG_EN); ++} ++ ++static void higmac_set_rx_fq(struct higmac_netdev_local *priv, ++ dma_addr_t phy_addr) ++{ ++ writel(BITS_RX_FQ_START_ADDR_EN, priv->gmac_iobase + RX_FQ_REG_EN); ++ writel(phy_addr, priv->gmac_iobase + RX_FQ_START_ADDR); ++ writel(0, priv->gmac_iobase + RX_FQ_REG_EN); ++} ++ ++static void higmac_set_rx_bq(struct higmac_netdev_local *priv, ++ dma_addr_t phy_addr) ++{ ++ writel(BITS_RX_BQ_START_ADDR_EN, priv->gmac_iobase + RX_BQ_REG_EN); ++ writel(phy_addr, priv->gmac_iobase + RX_BQ_START_ADDR); ++ writel(0, priv->gmac_iobase + RX_BQ_REG_EN); ++} ++ ++static void higmac_set_tx_bq(struct higmac_netdev_local *priv, ++ dma_addr_t phy_addr) ++{ ++ writel(BITS_TX_BQ_START_ADDR_EN, priv->gmac_iobase + TX_BQ_REG_EN); ++ writel(phy_addr, priv->gmac_iobase + TX_BQ_START_ADDR); ++ writel(0, priv->gmac_iobase + TX_BQ_REG_EN); ++} ++ ++static void higmac_set_tx_rq(struct higmac_netdev_local *priv, ++ dma_addr_t phy_addr) ++{ ++ writel(BITS_TX_RQ_START_ADDR_EN, priv->gmac_iobase + TX_RQ_REG_EN); ++ writel(phy_addr, priv->gmac_iobase + TX_RQ_START_ADDR); ++ writel(0, priv->gmac_iobase + TX_RQ_REG_EN); ++} ++ ++static void higmac_hw_set_desc_addr(struct higmac_netdev_local *priv) ++{ ++ u32 reg; ++ int i; ++ ++ higmac_set_rx_fq(priv, priv->rx_fq.phys_addr); ++ higmac_set_rx_bq(priv, priv->rx_bq.phys_addr); ++ higmac_set_tx_rq(priv, priv->tx_rq.phys_addr); ++ higmac_set_tx_bq(priv, priv->tx_bq.phys_addr); ++ ++ for (i = 1; i < priv->num_rxqs; i++) { ++ reg = RX_BQ_START_ADDR_QUEUE(i); ++ writel(BITS_RX_BQ_START_ADDR_EN, ++ priv->gmac_iobase + RX_BQ_REG_EN); ++ writel(priv->pool[3 + i].phys_addr, priv->gmac_iobase + reg); ++ writel(0, priv->gmac_iobase + RX_BQ_REG_EN); ++ } ++} ++ ++static void higmac_set_rss_cap(struct higmac_netdev_local *priv) ++{ ++ u32 val = 0; ++ ++ if (priv->has_rxhash_cap) ++ val |= BIT_RXHASH_CAP; ++ if (priv->has_rss_cap) ++ val |= BIT_RSS_CAP; ++ writel(val, priv->gmac_iobase + HW_CAP_EN); ++} ++ ++static void higmac_hw_init(struct higmac_netdev_local *priv) ++{ ++ u32 val; ++ u32 reg; ++ int i; ++ ++#if defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) || \ ++ defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) || \ ++ defined(CONFIG_ARCH_HI3516AV200) ++ /* config AXI parameter for better performance. */ ++ val = readl(priv->gmac_iobase + BURST_OUTSTANDING_REG); ++ val >>= BURST_OUTSTANDING_OFFSET; ++ if (!val) ++ writel(BURST4_OUTSTANDING1, priv->gmac_iobase + ++ BURST_OUTSTANDING_REG); ++#elif (defined(CONFIG_ARCH_HI3531D)) ++ /* config AXI parameter for better performance. */ ++ writel(BURST4_OUTSTANDING1, priv->gmac_iobase + ++ BURST_OUTSTANDING_REG); ++#endif ++ ++ /* disable and clear all interrupts */ ++ writel(0, priv->gmac_iobase + ENA_PMU_INT); ++ writel(~0, priv->gmac_iobase + RAW_PMU_INT); ++ ++ for (i = 1; i < priv->num_rxqs; i++) { ++ reg = RSS_ENA_INT_QUEUE(i); ++ writel(0, priv->gmac_iobase + reg); ++ } ++ writel(~0, priv->gmac_iobase + RSS_RAW_PMU_INT); ++ ++ /* enable CRC erro packets filter */ ++ val = readl(priv->gmac_iobase + REC_FILT_CONTROL); ++ val |= BIT_CRC_ERR_PASS; ++ writel(val, priv->gmac_iobase + REC_FILT_CONTROL); ++ ++ /* set tx min packet length */ ++ val = readl(priv->gmac_iobase + CRF_MIN_PACKET); ++ val &= ~BIT_MASK_TX_MIN_LEN; ++ val |= ETH_HLEN << BIT_OFFSET_TX_MIN_LEN; ++ writel(val, priv->gmac_iobase + CRF_MIN_PACKET); ++ ++ /* fix bug for udp and ip error check */ ++ writel(CONTROL_WORD_CONFIG, priv->gmac_iobase + CONTROL_WORD); ++ ++ writel(0, priv->gmac_iobase + COL_SLOT_TIME); ++ ++ writel(DUPLEX_HALF, priv->gmac_iobase + MAC_DUPLEX_HALF_CTRL); ++ ++ /* FIXME: interrupt when rcv packets >= RX_BQ_INT_THRESHOLD */ ++ val = RX_BQ_INT_THRESHOLD | ++ (TX_RQ_INT_THRESHOLD << BITS_OFFSET_TX_RQ_IN_TH); ++ writel(val, priv->gmac_iobase + IN_QUEUE_TH); ++ ++ /* FIXME: rx_bq/tx_rq in timeout threshold */ ++ writel(0x10000, priv->gmac_iobase + RX_BQ_IN_TIMEOUT_TH); ++ ++ writel(0x18000, priv->gmac_iobase + TX_RQ_IN_TIMEOUT_TH); ++ ++ higmac_set_desc_depth(priv, RX_DESC_NUM, TX_DESC_NUM); ++} ++ ++static inline void higmac_irq_enable(struct higmac_netdev_local *ld) ++{ ++ writel(RX_BQ_IN_INT | RX_BQ_IN_TIMEOUT_INT ++ | TX_RQ_IN_INT | TX_RQ_IN_TIMEOUT_INT, ++ ld->gmac_iobase + ENA_PMU_INT); ++} ++ ++static inline void higmac_irq_enable_queue(struct higmac_netdev_local *ld, ++ int rxq_id) ++{ ++ if (rxq_id) { ++ u32 reg; ++ ++ reg = RSS_ENA_INT_QUEUE(rxq_id); ++ writel(~0, ld->gmac_iobase + reg); ++ } else { ++ higmac_irq_enable(ld); ++ } ++} ++ ++static inline void higmac_irq_enable_all_queue(struct higmac_netdev_local *ld) ++{ ++ int i; ++ ++ for (i = 0; i < ld->num_rxqs; i++) ++ higmac_irq_enable_queue(ld, i); ++} ++ ++static inline void higmac_irq_disable(struct higmac_netdev_local *ld) ++{ ++ writel(0, ld->gmac_iobase + ENA_PMU_INT); ++} ++ ++static inline void higmac_irq_disable_queue(struct higmac_netdev_local *ld, ++ int rxq_id) ++{ ++ if (rxq_id) { ++ u32 reg; ++ ++ reg = RSS_ENA_INT_QUEUE(rxq_id); ++ writel(0, ld->gmac_iobase + reg); ++ } else { ++ higmac_irq_disable(ld); ++ } ++} ++ ++static inline void higmac_irq_disable_all_queue(struct higmac_netdev_local *ld) ++{ ++ int i; ++ ++ for (i = 0; i < ld->num_rxqs; i++) ++ higmac_irq_disable_queue(ld, i); ++} ++ ++static inline bool higmac_queue_irq_disabled(struct higmac_netdev_local *ld, ++ int rxq_id) ++{ ++ u32 reg, val; ++ ++ if (rxq_id) ++ reg = RSS_ENA_INT_QUEUE(rxq_id); ++ else ++ reg = ENA_PMU_INT; ++ val = readl(ld->gmac_iobase + reg); ++ ++ return !val; ++} ++ ++static inline void higmac_hw_desc_enable(struct higmac_netdev_local *ld) ++{ ++ writel(0xF, ld->gmac_iobase + DESC_WR_RD_ENA); ++} ++ ++static inline void higmac_hw_desc_disable(struct higmac_netdev_local *ld) ++{ ++ writel(0, ld->gmac_iobase + DESC_WR_RD_ENA); ++} ++ ++static inline void higmac_port_enable(struct higmac_netdev_local *ld) ++{ ++ writel(BITS_TX_EN | BITS_RX_EN, ld->gmac_iobase + PORT_EN); ++} ++ ++static inline void higmac_port_disable(struct higmac_netdev_local *ld) ++{ ++ writel(0, ld->gmac_iobase + PORT_EN); ++} ++ ++void higmac_set_flow_ctrl_params(struct higmac_netdev_local *ld) ++{ ++ unsigned int rx_fq_empty_th; ++ unsigned int rx_fq_full_th; ++ unsigned int rx_bq_empty_th; ++ unsigned int rx_bq_full_th; ++ unsigned int rec_filter; ++ ++ writel(ld->pause, ld->gmac_iobase + FC_TX_TIMER); ++ writel(ld->pause_interval, ld->gmac_iobase + PAUSE_THR); ++ ++ rx_fq_empty_th = readl(ld->gmac_iobase + RX_FQ_ALEMPTY_TH); ++ rx_fq_empty_th &= ~(BITS_Q_PAUSE_TH_MASK << BITS_Q_PAUSE_TH_OFFSET); ++ rx_fq_empty_th |= (ld->flow_ctrl_active_threshold << ++ BITS_Q_PAUSE_TH_OFFSET); ++ writel(rx_fq_empty_th, ld->gmac_iobase + RX_FQ_ALEMPTY_TH); ++ ++ rx_fq_full_th = readl(ld->gmac_iobase + RX_FQ_ALFULL_TH); ++ rx_fq_full_th &= ~(BITS_Q_PAUSE_TH_MASK << BITS_Q_PAUSE_TH_OFFSET); ++ rx_fq_full_th |= (ld->flow_ctrl_deactive_threshold << ++ BITS_Q_PAUSE_TH_OFFSET); ++ writel(rx_fq_full_th, ld->gmac_iobase + RX_FQ_ALFULL_TH); ++ ++ rx_bq_empty_th = readl(ld->gmac_iobase + RX_BQ_ALEMPTY_TH); ++ rx_bq_empty_th &= ~(BITS_Q_PAUSE_TH_MASK << BITS_Q_PAUSE_TH_OFFSET); ++ rx_bq_empty_th |= (ld->flow_ctrl_active_threshold << ++ BITS_Q_PAUSE_TH_OFFSET); ++ writel(rx_bq_empty_th, ld->gmac_iobase + RX_BQ_ALEMPTY_TH); ++ ++ rx_bq_full_th = readl(ld->gmac_iobase + RX_BQ_ALFULL_TH); ++ rx_bq_full_th &= ~(BITS_Q_PAUSE_TH_MASK << BITS_Q_PAUSE_TH_OFFSET); ++ rx_bq_full_th |= (ld->flow_ctrl_deactive_threshold << ++ BITS_Q_PAUSE_TH_OFFSET); ++ writel(rx_bq_full_th, ld->gmac_iobase + RX_BQ_ALFULL_TH); ++ ++ writel(0, ld->gmac_iobase + CRF_TX_PAUSE); ++ ++ rec_filter = readl(ld->gmac_iobase + REC_FILT_CONTROL); ++ rec_filter |= BIT_PAUSE_FRM_PASS; ++ writel(rec_filter, ld->gmac_iobase + REC_FILT_CONTROL); ++} ++ ++void higmac_set_flow_ctrl_state(struct higmac_netdev_local *ld, int pause) ++{ ++ unsigned int flow_rx_q_en; ++ unsigned int flow; ++ ++ flow_rx_q_en = readl(ld->gmac_iobase + RX_PAUSE_EN); ++ flow_rx_q_en &= ~(BIT_RX_FQ_PAUSE_EN | BIT_RX_BQ_PAUSE_EN); ++ if (pause && (ld->flow_ctrl & FLOW_TX)) ++ flow_rx_q_en |= (BIT_RX_FQ_PAUSE_EN | BIT_RX_BQ_PAUSE_EN); ++ writel(flow_rx_q_en, ld->gmac_iobase + RX_PAUSE_EN); ++ ++ flow = readl(ld->gmac_iobase + PAUSE_EN); ++ flow &= ~(BIT_RX_FDFC | BIT_TX_FDFC); ++ if (pause) { ++ if (ld->flow_ctrl & FLOW_RX) ++ flow |= BIT_RX_FDFC; ++ if (ld->flow_ctrl & FLOW_TX) ++ flow |= BIT_TX_FDFC; ++ } ++ writel(flow, ld->gmac_iobase + PAUSE_EN); ++} ++ ++static void higmac_set_flow_ctrl_args(struct higmac_netdev_local *ld) ++{ ++ ld->flow_ctrl = flow_ctrl_en; ++ ld->pause = tx_flow_ctrl_pause_time; ++ ld->pause_interval = tx_flow_ctrl_pause_interval; ++ ld->flow_ctrl_active_threshold = tx_flow_ctrl_active_threshold; ++ ld->flow_ctrl_deactive_threshold = tx_flow_ctrl_deactive_threshold; ++} ++ ++/* set gmac's multicast list, here we setup gmac's mc filter */ ++static void higmac_gmac_multicast_list(struct net_device *dev) ++{ ++ struct higmac_netdev_local *ld = netdev_priv(dev); ++ unsigned int rec_filter; ++ ++ rec_filter = readl(ld->gmac_iobase + REC_FILT_CONTROL); ++ /* when set gmac in promisc mode ++ * a. dev in IFF_PROMISC mode ++ */ ++ if ((dev->flags & IFF_PROMISC)) { ++ /* promisc mode.received all pkgs. */ ++ rec_filter &= ~(BIT_BC_DROP_EN | BIT_MC_MATCH_EN | ++ BIT_UC_MATCH_EN); ++ } else { ++ /* drop uc pkgs with field 'DA' not match our's */ ++ rec_filter |= BIT_UC_MATCH_EN; ++ ++ if (dev->flags & IFF_BROADCAST) /* no broadcast */ ++ rec_filter &= ~BIT_BC_DROP_EN; ++ else ++ rec_filter |= BIT_BC_DROP_EN; ++ ++ if (netdev_mc_empty(dev) || !(dev->flags & IFF_MULTICAST)) { ++ /* haven't join any mc group */ ++ writel(0, ld->gmac_iobase + PORT_MC_ADDR_LOW); ++ writel(0, ld->gmac_iobase + PORT_MC_ADDR_HIGH); ++ rec_filter |= BIT_MC_MATCH_EN; ++ } else if (netdev_mc_count(dev) == 1 && ++ (dev->flags & IFF_MULTICAST)) { ++ struct netdev_hw_addr *ha; ++ unsigned int d = 0; ++ ++ netdev_for_each_mc_addr(ha, dev) { ++ d = (ha->addr[0] << 8) | (ha->addr[1]); ++ writel(d, ld->gmac_iobase + PORT_MC_ADDR_HIGH); ++ ++ d = (ha->addr[2] << 24) | (ha->addr[3] << 16) ++ | (ha->addr[4] << 8) | (ha->addr[5]); ++ writel(d, ld->gmac_iobase + PORT_MC_ADDR_LOW); ++ } ++ rec_filter |= BIT_MC_MATCH_EN; ++ } else { ++ rec_filter &= ~BIT_MC_MATCH_EN; ++ } ++ } ++ writel(rec_filter, ld->gmac_iobase + REC_FILT_CONTROL); ++} ++ ++/* the func stop the hw desc and relaim the software skb resource ++ * before reusing the gmac, you'd better reset the gmac ++ */ ++void higmac_reclaim_rx_tx_resource(struct higmac_netdev_local *ld) ++{ ++ unsigned long rxflags, txflags; ++ int rd_offset, wr_offset; ++ int i; ++ ++ higmac_irq_disable_all_queue(ld); ++ higmac_hw_desc_disable(ld); ++ writel(STOP_RX_TX, ld->gmac_iobase + STOP_CMD); ++ ++ spin_lock_irqsave(&ld->rxlock, rxflags); ++ /* rx_bq: logic write pointer */ ++ wr_offset = readl(ld->gmac_iobase + RX_BQ_WR_ADDR); ++ /* rx_bq: software read pointer */ ++ rd_offset = readl(ld->gmac_iobase + RX_BQ_RD_ADDR); ++ /* FIXME: prevent to reclaim skb in rx bottom half */ ++ writel(wr_offset, ld->gmac_iobase + RX_BQ_RD_ADDR); ++ ++ for (i = 1; i < ld->num_rxqs; i++) { ++ u32 rx_bq_wr_reg, rx_bq_rd_reg; ++ ++ rx_bq_wr_reg = RX_BQ_WR_ADDR_QUEUE(i); ++ rx_bq_rd_reg = RX_BQ_RD_ADDR_QUEUE(i); ++ ++ wr_offset = readl(ld->gmac_iobase + rx_bq_wr_reg); ++ writel(wr_offset, ld->gmac_iobase + rx_bq_rd_reg); ++ } ++ ++ /* rx_fq: software write pointer */ ++ wr_offset = readl(ld->gmac_iobase + RX_FQ_WR_ADDR); ++ /* rx_fq: logic read pointer */ ++ rd_offset = readl(ld->gmac_iobase + RX_FQ_RD_ADDR); ++ if (!rd_offset) ++ rd_offset = (RX_DESC_NUM - 1) << DESC_BYTE_SHIFT; ++ else ++ rd_offset -= DESC_SIZE; ++ /* FIXME: stop to feed hw desc */ ++ writel(rd_offset, ld->gmac_iobase + RX_FQ_WR_ADDR); ++ ++ for (i = 0; i < ld->rx_fq.count; i++) { ++ if (!ld->rx_fq.skb[i]) ++ ld->rx_fq.skb[i] = SKB_MAGIC; ++ } ++ spin_unlock_irqrestore(&ld->rxlock, rxflags); ++ ++ /* no need to wait pkts in tx_rq finish to free all skb, ++ * because higmac_xmit_reclaim is in the tx_lock, ++ */ ++ spin_lock_irqsave(&ld->txlock, txflags); ++ /* tx_rq: logic write */ ++ wr_offset = readl(ld->gmac_iobase + TX_RQ_WR_ADDR); ++ /* tx_rq: software read */ ++ rd_offset = readl(ld->gmac_iobase + TX_RQ_RD_ADDR); ++ /* FIXME: stop to reclaim tx skb */ ++ writel(wr_offset, ld->gmac_iobase + TX_RQ_RD_ADDR); ++ ++ /* tx_bq: logic read */ ++ rd_offset = readl(ld->gmac_iobase + TX_BQ_RD_ADDR); ++ if (!rd_offset) ++ rd_offset = (TX_DESC_NUM - 1) << DESC_BYTE_SHIFT; ++ else ++ rd_offset -= DESC_SIZE; ++ /* FIXME: stop software tx skb */ ++ writel(rd_offset, ld->gmac_iobase + TX_BQ_WR_ADDR); ++ ++ for (i = 0; i < ld->tx_bq.count; i++) { ++ if (!ld->tx_bq.skb[i]) ++ ld->tx_bq.skb[i] = SKB_MAGIC; ++ } ++ spin_unlock_irqrestore(&ld->txlock, txflags); ++} ++ ++static void higmac_monitor_func(unsigned long arg); ++static void higmac_set_multicast_list(struct net_device *dev); ++ ++static void higmac_hw_set_mac_addr(struct net_device *dev) ++{ ++ struct higmac_netdev_local *priv = netdev_priv(dev); ++ unsigned char *mac = dev->dev_addr; ++ u32 val; ++ ++ val = mac[1] | (mac[0] << 8); ++ writel(val, priv->gmac_iobase + STATION_ADDR_HIGH); ++ ++ val = mac[5] | (mac[4] << 8) | (mac[3] << 16) | (mac[2] << 24); ++ writel(val, priv->gmac_iobase + STATION_ADDR_LOW); ++} ++ ++static u32 higmac_rx_refill(struct higmac_netdev_local *priv); ++ ++static void higmac_free_rx_skb(struct higmac_netdev_local *ld) ++{ ++ struct sk_buff *skb = NULL; ++ int i; ++ ++ for (i = 0; i < ld->rx_fq.count; i++) { ++ skb = ld->rx_fq.skb[i]; ++ if (skb) { ++ ld->rx_skb[i] = NULL; ++ ld->rx_fq.skb[i] = NULL; ++ if (skb == SKB_MAGIC) ++ continue; ++ dev_kfree_skb_any(skb); ++ /* TODO: need to unmap the skb here ++ * but there is no way to get the dma_addr here, ++ * and unmap(TO_DEVICE) ops do nothing in fact, ++ * so we ignore to call ++ * dma_unmap_single(dev, dma_addr, skb->len, ++ * DMA_TO_DEVICE) ++ */ ++ } ++ } ++} ++ ++static void higmac_free_tx_skb(struct higmac_netdev_local *ld) ++{ ++ struct sk_buff *skb = NULL; ++ int i; ++ ++ for (i = 0; i < ld->tx_bq.count; i++) { ++ skb = ld->tx_bq.skb[i]; ++ if (skb) { ++ ld->tx_skb[i] = NULL; ++ ld->tx_bq.skb[i] = NULL; ++ if (skb == SKB_MAGIC) ++ continue; ++ dev_kfree_skb_any(skb); ++ /* TODO: unmap the skb */ ++ } ++ } ++} ++ ++/* reset and re-config gmac */ ++void higmac_restart(struct higmac_netdev_local *ld) ++{ ++ unsigned long rxflags, txflags; ++ ++ /* restart hw engine now */ ++ higmac_mac_core_reset(ld); ++ ++ spin_lock_irqsave(&ld->rxlock, rxflags); ++ spin_lock_irqsave(&ld->txlock, txflags); ++ ++ higmac_free_rx_skb(ld); ++ higmac_free_tx_skb(ld); ++ ++ pmt_reg_restore(ld); ++ higmac_hw_init(ld); ++ higmac_hw_set_mac_addr(ld->netdev); ++ higmac_hw_set_desc_addr(ld); ++ ++ /* we don't set macif here, it will be set in adjust_link */ ++ if (ld->netdev->flags & IFF_UP) { ++ /* when resume, only do the following operations ++ * when dev is up before suspend. ++ */ ++ higmac_rx_refill(ld); ++ higmac_set_multicast_list(ld->netdev); ++ ++ higmac_hw_desc_enable(ld); ++ higmac_port_enable(ld); ++ higmac_irq_enable_all_queue(ld); ++ } ++ spin_unlock_irqrestore(&ld->txlock, txflags); ++ spin_unlock_irqrestore(&ld->rxlock, rxflags); ++} ++ ++static int higmac_net_set_mac_address(struct net_device *dev, void *p) ++{ ++ int ret; ++ ++ ret = eth_mac_addr(dev, p); ++ if (!ret) ++ higmac_hw_set_mac_addr(dev); ++ ++ return ret; ++} ++ ++#define HIGMAC_LINK_CHANGE_PROTECT ++#define HIGMAC_MAC_TX_RESET_IN_LINKUP ++ ++#ifdef HIGMAC_LINK_CHANGE_PROTECT ++#define HIGMAC_MS_TO_NS (1000000ULL) ++#define HIGMAC_FLUSH_WAIT_TIME (100*HIGMAC_MS_TO_NS) ++/* protect code */ ++static void higmac_linkup_flush(struct higmac_netdev_local *ld) ++{ ++ int tx_bq_wr_offset, tx_bq_rd_offset; ++ unsigned long long time_limit, time_now; ++ ++ time_now = sched_clock(); ++ time_limit = time_now + HIGMAC_FLUSH_WAIT_TIME; ++ ++ do { ++ tx_bq_wr_offset = readl(ld->gmac_iobase + TX_BQ_WR_ADDR); ++ tx_bq_rd_offset = readl(ld->gmac_iobase + TX_BQ_RD_ADDR); ++ ++ time_now = sched_clock(); ++ if (unlikely((long long)time_now - ++ (long long)time_limit >= 0)) ++ break; ++ } while (tx_bq_rd_offset != tx_bq_wr_offset); ++ ++ mdelay(1); ++} ++#endif ++ ++#ifdef HIGMAC_MAC_TX_RESET_IN_LINKUP ++static void higmac_mac_tx_state_engine_reset(struct higmac_netdev_local *priv) ++{ ++ u32 val; ++ ++ val = readl(priv->gmac_iobase + MAC_CLEAR); ++ val |= BIT_TX_SOFT_RESET; ++ writel(val, priv->gmac_iobase + MAC_CLEAR); ++ ++ mdelay(5); ++ ++ val = readl(priv->gmac_iobase + MAC_CLEAR); ++ val &= ~BIT_TX_SOFT_RESET; ++ writel(val, priv->gmac_iobase + MAC_CLEAR); ++} ++#endif ++ ++static void higmac_adjust_link(struct net_device *dev) ++{ ++ struct higmac_netdev_local *priv = netdev_priv(dev); ++ struct phy_device *phy = priv->phy; ++ bool link_status_changed = false; ++ ++ if (phy->link) { ++ if ((priv->old_speed != phy->speed) || ++ (priv->old_duplex != phy->duplex)) { ++#ifdef HIGMAC_LINK_CHANGE_PROTECT ++ unsigned long txflags; ++ ++ spin_lock_irqsave(&priv->txlock, txflags); ++ ++ higmac_linkup_flush(priv); ++#endif ++ higmac_config_port(dev, phy->speed, phy->duplex); ++#ifdef HIGMAC_MAC_TX_RESET_IN_LINKUP ++ higmac_mac_tx_state_engine_reset(priv); ++#endif ++#ifdef HIGMAC_LINK_CHANGE_PROTECT ++ spin_unlock_irqrestore(&priv->txlock, txflags); ++#endif ++ higmac_set_flow_ctrl_state(priv, phy->pause); ++ ++ if (priv->autoeee) ++ init_autoeee(priv); ++ ++ link_status_changed = true; ++ priv->old_link = 1; ++ priv->old_speed = phy->speed; ++ priv->old_duplex = phy->duplex; ++ } ++ } else if (priv->old_link) { ++ link_status_changed = true; ++ priv->old_link = 0; ++ priv->old_speed = SPEED_UNKNOWN; ++ priv->old_duplex = DUPLEX_UNKNOWN; ++ } ++ ++ if (link_status_changed && netif_msg_link(priv)) ++ phy_print_status(phy); ++} ++ ++int higmac_tx_avail(struct higmac_netdev_local *ld) ++{ ++ int tx_bq_wr_offset, tx_bq_rd_offset; ++ ++ tx_bq_wr_offset = readl(ld->gmac_iobase + TX_BQ_WR_ADDR); ++ tx_bq_rd_offset = readl(ld->gmac_iobase + TX_BQ_RD_ADDR); ++ ++ return (tx_bq_rd_offset >> DESC_BYTE_SHIFT) + TX_DESC_NUM ++ - (tx_bq_wr_offset >> DESC_BYTE_SHIFT) - 1; ++} ++ ++static int higmac_init_sg_desc_queue(struct higmac_netdev_local *ld) ++{ ++ ld->sg_count = ld->tx_bq.count + HIGMAC_SG_DESC_ADD; ++ if (HAS_CAP_CCI(ld->hw_cap)) { ++ ld->dma_sg_desc = kmalloc_array(ld->sg_count, ++ sizeof(struct sg_desc), ++ GFP_KERNEL); ++ if (ld->dma_sg_desc) ++ ld->dma_sg_phy = virt_to_phys(ld->dma_sg_desc); ++ } else { ++ ld->dma_sg_desc = (struct sg_desc *)dma_alloc_coherent(ld->dev, ++ ld->sg_count * sizeof(struct sg_desc), ++ &ld->dma_sg_phy, GFP_KERNEL); ++ } ++ ++ if (!ld->dma_sg_desc) { ++ pr_err("alloc sg desc dma error!\n"); ++ return -ENOMEM; ++ } ++#ifdef HIGMAC_TSO_DEBUG ++ pr_info("Higmac dma_sg_phy: 0x%p\n", (void *)ld->dma_sg_phy); ++#endif ++ ++ ld->sg_head = 0; ++ ld->sg_tail = 0; ++ ++ return 0; ++} ++ ++static void higmac_destroy_sg_desc_queue(struct higmac_netdev_local *ld) ++{ ++ if (ld->dma_sg_desc) { ++ if (HAS_CAP_CCI(ld->hw_cap)) ++ kfree(ld->dma_sg_desc); ++ else ++ dma_free_coherent(ld->dev, ++ ld->sg_count * sizeof(struct sg_desc), ++ ld->dma_sg_desc, ld->dma_sg_phy); ++ ld->dma_sg_desc = NULL; ++ } ++} ++ ++static bool higmac_rx_fq_empty(struct higmac_netdev_local *priv) ++{ ++ u32 start, end; ++ ++ start = readl(priv->gmac_iobase + RX_FQ_WR_ADDR); ++ end = readl(priv->gmac_iobase + RX_FQ_RD_ADDR); ++ ++ if (start == end) ++ return true; ++ else ++ return false; ++} ++ ++static bool higmac_rxq_has_packets(struct higmac_netdev_local *priv, int rxq_id) ++{ ++ u32 rx_bq_rd_reg, rx_bq_wr_reg; ++ u32 start, end; ++ ++ rx_bq_rd_reg = RX_BQ_RD_ADDR_QUEUE(rxq_id); ++ rx_bq_wr_reg = RX_BQ_WR_ADDR_QUEUE(rxq_id); ++ ++ start = readl(priv->gmac_iobase + rx_bq_rd_reg); ++ end = readl(priv->gmac_iobase + rx_bq_wr_reg); ++ ++ if (start == end) ++ return false; ++ else ++ return true; ++} ++ ++static void higmac_monitor_func(unsigned long arg) ++{ ++ struct net_device *dev = (struct net_device *)arg; ++ struct higmac_netdev_local *ld = netdev_priv(dev); ++ u32 refill_cnt; ++ ++ if (!ld || !netif_running(dev)) { ++ higmac_trace(7, "network driver is stopped."); ++ return; ++ } ++ ++ spin_lock(&ld->rxlock); ++ refill_cnt = higmac_rx_refill(ld); ++ if (!refill_cnt && higmac_rx_fq_empty(ld)) { ++ int rxq_id; ++ ++ for (rxq_id = 0; rxq_id < ld->num_rxqs; rxq_id++) { ++ if (higmac_rxq_has_packets(ld, rxq_id)) ++ napi_schedule(&ld->q_napi[rxq_id].napi); ++ } ++ } ++ spin_unlock(&ld->rxlock); ++ ++ ld->monitor.expires = jiffies + HIGMAC_MONITOR_TIMER; ++ mod_timer(&ld->monitor, ld->monitor.expires); ++} ++ ++static u32 higmac_rx_refill(struct higmac_netdev_local *priv) ++{ ++ struct higmac_desc *desc; ++ struct sk_buff *skb; ++ u32 start, end, num, pos, i; ++ u32 len = HIETH_MAX_FRAME_SIZE; ++ dma_addr_t addr; ++ u32 refill_cnt = 0; ++ ++ /* software write pointer */ ++ start = dma_cnt(readl(priv->gmac_iobase + RX_FQ_WR_ADDR)); ++ /* logic read pointer */ ++ end = dma_cnt(readl(priv->gmac_iobase + RX_FQ_RD_ADDR)); ++ num = CIRC_SPACE(start, end, RX_DESC_NUM); ++ ++ for (i = 0, pos = start; i < num; i++) { ++ if (priv->rx_fq.skb[pos] || priv->rx_skb[pos]) ++ break; ++ ++ skb = netdev_alloc_skb_ip_align(priv->netdev, len); ++ if (unlikely(!skb)) ++ break; ++ ++ if (!HAS_CAP_CCI(priv->hw_cap)) { ++ addr = dma_map_single(priv->dev, skb->data, len, ++ DMA_FROM_DEVICE); ++ if (dma_mapping_error(priv->dev, addr)) { ++ dev_kfree_skb_any(skb); ++ break; ++ } ++ } else { ++ addr = virt_to_phys(skb->data); ++ } ++ ++ desc = priv->rx_fq.desc + pos; ++ desc->data_buff_addr = addr; ++ priv->rx_fq.skb[pos] = skb; ++ priv->rx_skb[pos] = skb; ++ ++ desc->buffer_len = len - 1; ++ desc->data_len = 0; ++ desc->fl = 0; ++ desc->descvid = DESC_VLD_FREE; ++ desc->skb_id = pos; ++ ++ refill_cnt++; ++ pos = dma_ring_incr(pos, RX_DESC_NUM); ++ } ++ ++ /* This barrier is important here. It is required to ensure ++ * the ARM CPU flushes it's DMA write buffers before proceeding ++ * to the next instruction, to ensure that GMAC will see ++ * our descriptor changes in memory ++ */ ++ HIGMAC_SYNC_BARRIER(); ++ ++ if (pos != start) ++ writel(dma_byte(pos), priv->gmac_iobase + RX_FQ_WR_ADDR); ++ ++ return refill_cnt; ++} ++ ++static int higmac_rx(struct net_device *dev, int limit, int rxq_id) ++{ ++ struct higmac_netdev_local *ld = netdev_priv(dev); ++ struct sk_buff *skb; ++ struct higmac_desc *desc; ++ dma_addr_t addr; ++ u32 start, end, num, pos, i, len; ++ u32 rx_bq_rd_reg, rx_bq_wr_reg; ++ u16 skb_id; ++ ++ rx_bq_rd_reg = RX_BQ_RD_ADDR_QUEUE(rxq_id); ++ rx_bq_wr_reg = RX_BQ_WR_ADDR_QUEUE(rxq_id); ++ ++ /* software read pointer */ ++ start = dma_cnt(readl(ld->gmac_iobase + rx_bq_rd_reg)); ++ /* logic write pointer */ ++ end = dma_cnt(readl(ld->gmac_iobase + rx_bq_wr_reg)); ++ num = CIRC_CNT(end, start, RX_DESC_NUM); ++ if (num > limit) ++ num = limit; ++ ++ /* ensure get updated desc */ ++ rmb(); ++ for (i = 0, pos = start; i < num; i++) { ++ if (rxq_id) ++ desc = ld->pool[3 + rxq_id].desc + pos; ++ else ++ desc = ld->rx_bq.desc + pos; ++ skb_id = desc->skb_id; ++ ++ spin_lock(&ld->rxlock); ++ skb = ld->rx_skb[skb_id]; ++ if (unlikely(!skb)) { ++ spin_unlock(&ld->rxlock); ++ netdev_err(dev, "inconsistent rx_skb\n"); ++ break; ++ } ++ ++ /* data consistent check */ ++ if (unlikely(skb != ld->rx_fq.skb[skb_id])) { ++ netdev_err(dev, "desc->skb(0x%p),rx_fq.skb[%d](0x%p)\n", ++ skb, skb_id, ld->rx_fq.skb[skb_id]); ++ if (ld->rx_fq.skb[skb_id] == SKB_MAGIC) { ++ spin_unlock(&ld->rxlock); ++ goto next; ++ } ++ WARN_ON(1); ++ } else { ++ ld->rx_fq.skb[skb_id] = NULL; ++ } ++ spin_unlock(&ld->rxlock); ++ ++ len = desc->data_len; ++ ++ if (!HAS_CAP_CCI(ld->hw_cap)) { ++ addr = desc->data_buff_addr; ++ dma_unmap_single(ld->dev, addr, HIETH_MAX_FRAME_SIZE, ++ DMA_FROM_DEVICE); ++ } ++ ++ skb_put(skb, len); ++ if (skb->len > HIETH_MAX_FRAME_SIZE) { ++ netdev_err(dev, "rcv len err, len = %d\n", skb->len); ++ dev->stats.rx_errors++; ++ dev->stats.rx_length_errors++; ++ dev_kfree_skb_any(skb); ++ goto next; ++ } ++ ++ skb->protocol = eth_type_trans(skb, dev); ++ skb->ip_summed = CHECKSUM_NONE; ++#if defined(CONFIG_HIGMAC_RXCSUM) ++ if (dev->features & NETIF_F_RXCSUM) { ++ int hdr_csum_done = ++ desc->header_csum_done; ++ int payload_csum_done = ++ desc->payload_csum_done; ++ int hdr_csum_err = ++ desc->header_csum_err; ++ int payload_csum_err = ++ desc->payload_csum_err; ++ ++ if (hdr_csum_done && payload_csum_done) { ++ if (unlikely(hdr_csum_err || ++ payload_csum_err)) { ++ dev->stats.rx_errors++; ++ dev->stats.rx_crc_errors++; ++ dev_kfree_skb_any(skb); ++ goto next; ++ } else { ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ } ++ } ++ } ++#endif ++ if ((dev->features & NETIF_F_RXHASH) && desc->has_hash) ++ skb_set_hash(skb, desc->rxhash, desc->l3_hash ? ++ PKT_HASH_TYPE_L3 : PKT_HASH_TYPE_L4); ++ ++ skb_record_rx_queue(skb, rxq_id); ++ ++ napi_gro_receive(&ld->q_napi[rxq_id].napi, skb); ++ dev->stats.rx_packets++; ++ dev->stats.rx_bytes += len; ++ dev->last_rx = jiffies; ++next: ++ spin_lock(&ld->rxlock); ++ ld->rx_skb[skb_id] = NULL; ++ spin_unlock(&ld->rxlock); ++ pos = dma_ring_incr(pos, RX_DESC_NUM); ++ } ++ ++ if (pos != start) ++ writel(dma_byte(pos), ld->gmac_iobase + rx_bq_rd_reg); ++ ++ spin_lock(&ld->rxlock); ++ higmac_rx_refill(ld); ++ spin_unlock(&ld->rxlock); ++ ++ return num; ++} ++ ++#ifdef HIGMAC_TSO_DEBUG ++unsigned int id_send; ++unsigned int id_free; ++struct send_pkt_info pkt_rec[MAX_RECORD]; ++#endif ++ ++static int higmac_check_tx_err(struct higmac_netdev_local *ld, ++ struct higmac_tso_desc *tx_bq_desc, ++ unsigned int desc_pos) ++{ ++ unsigned int tx_err = tx_bq_desc->tx_err; ++ ++ if (unlikely(tx_err & ERR_ALL)) { ++ struct sg_desc *desc_cur; ++ int *sg_word; ++ int i; ++ ++ WARN((tx_err & ERR_ALL), ++ "TX ERR: desc1=0x%x, desc2=0x%x, desc5=0x%x\n", ++ tx_bq_desc->data_buff_addr, ++ tx_bq_desc->desc1.val, tx_bq_desc->tx_err); ++ ++ desc_cur = ld->dma_sg_desc + ld->tx_bq.sg_desc_offset[desc_pos]; ++ sg_word = (int *)desc_cur; ++ for (i = 0; i < sizeof(struct sg_desc) / sizeof(int); i++) ++ pr_err("%s,%d: sg_desc word[%d]=0x%x\n", ++ __func__, __LINE__, i, sg_word[i]); ++ ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int higmac_xmit_release_gso(struct higmac_netdev_local *ld, ++ struct higmac_tso_desc *tx_bq_desc, ++ unsigned int desc_pos) ++{ ++ int pkt_type; ++ int nfrags = tx_bq_desc->desc1.tx.nfrags_num; ++ dma_addr_t addr; ++ size_t len; ++ ++ if (unlikely(higmac_check_tx_err(ld, tx_bq_desc, desc_pos) < 0)) { ++ /* dev_close */ ++ higmac_irq_disable_all_queue(ld); ++ higmac_hw_desc_disable(ld); ++ ++ netif_carrier_off(ld->netdev); ++ netif_stop_queue(ld->netdev); ++ ++ phy_stop(ld->phy); ++ del_timer_sync(&ld->monitor); ++ return -1; ++ } ++ ++ if (tx_bq_desc->desc1.tx.tso_flag || nfrags) ++ pkt_type = PKT_SG; ++ else ++ pkt_type = PKT_NORMAL; ++ ++ if (pkt_type == PKT_NORMAL) { ++ if (!HAS_CAP_CCI(ld->hw_cap)) { ++ addr = tx_bq_desc->data_buff_addr; ++ len = tx_bq_desc->desc1.tx.data_len; ++ dma_unmap_single(ld->dev, addr, len, DMA_TO_DEVICE); ++ } ++ } else { ++ if (!HAS_CAP_CCI(ld->hw_cap)) { ++ struct sg_desc *desc_cur; ++ unsigned int desc_offset; ++ int i; ++ ++ desc_offset = ld->tx_bq.sg_desc_offset[desc_pos]; ++ WARN_ON(desc_offset != ld->sg_tail); ++ desc_cur = ld->dma_sg_desc + desc_offset; ++ ++ addr = desc_cur->linear_addr; ++ len = desc_cur->linear_len; ++ dma_unmap_single(ld->dev, addr, len, DMA_TO_DEVICE); ++ for (i = 0; i < nfrags; i++) { ++ addr = desc_cur->frags[i].addr; ++ len = desc_cur->frags[i].size; ++ dma_unmap_page(ld->dev, addr, len, ++ DMA_TO_DEVICE); ++ } ++ } ++ ++ ld->sg_tail = (ld->sg_tail + 1) % ld->sg_count; ++ } ++ ++#ifdef HIGMAC_TSO_DEBUG ++ pkt_rec[id_free].status = 0; ++ id_free++; ++ if (id_free == MAX_RECORD) ++ id_free = 0; ++#endif ++ ++ return 0; ++} ++ ++static void higmac_xmit_reclaim(struct net_device *dev) ++{ ++ struct sk_buff *skb; ++ struct higmac_desc *desc; ++ struct higmac_tso_desc *tso_desc; ++ struct higmac_netdev_local *priv = netdev_priv(dev); ++ unsigned int bytes_compl = 0, pkts_compl = 0; ++ u32 start, end, num, pos, i; ++ dma_addr_t addr; ++ int ret; ++ ++ spin_lock(&priv->txlock); ++ ++ /* software read */ ++ start = dma_cnt(readl(priv->gmac_iobase + TX_RQ_RD_ADDR)); ++ /* logic write */ ++ end = dma_cnt(readl(priv->gmac_iobase + TX_RQ_WR_ADDR)); ++ num = CIRC_CNT(end, start, TX_DESC_NUM); ++ ++ for (i = 0, pos = start; i < num; i++) { ++ skb = priv->tx_skb[pos]; ++ if (unlikely(!skb)) { ++ netdev_err(dev, "inconsistent tx_skb\n"); ++ break; ++ } ++ ++ if (skb != priv->tx_bq.skb[pos]) { ++ netdev_err(dev, "wired, tx skb[%d](%p) != skb(%p)\n", ++ pos, priv->tx_bq.skb[pos], skb); ++ if (priv->tx_bq.skb[pos] == SKB_MAGIC) ++ goto next; ++ } ++ ++ pkts_compl++; ++ bytes_compl += skb->len; ++ desc = priv->tx_rq.desc + pos; ++ if (priv->tso_supported) { ++ tso_desc = (struct higmac_tso_desc *)desc; ++ ret = higmac_xmit_release_gso(priv, tso_desc, pos); ++ if (ret < 0) ++ break; ++ } else if (!HAS_CAP_CCI(priv->hw_cap)) { ++ addr = desc->data_buff_addr; ++ dma_unmap_single(priv->dev, addr, skb->len, ++ DMA_TO_DEVICE); ++ } ++ priv->tx_bq.skb[pos] = NULL; ++next: ++ priv->tx_skb[pos] = NULL; ++ dev_consume_skb_any(skb); ++ pos = dma_ring_incr(pos, TX_DESC_NUM); ++ } ++ ++ if (pos != start) ++ writel(dma_byte(pos), priv->gmac_iobase + TX_RQ_RD_ADDR); ++ ++ if (pkts_compl || bytes_compl) ++ netdev_completed_queue(dev, pkts_compl, bytes_compl); ++ ++ if (unlikely(netif_queue_stopped(priv->netdev)) && pkts_compl) ++ netif_wake_queue(priv->netdev); ++ ++ spin_unlock(&priv->txlock); ++} ++ ++static int higmac_poll(struct napi_struct *napi, int budget) ++{ ++ struct higmac_napi *q_napi = container_of(napi, ++ struct higmac_napi, napi); ++ struct higmac_netdev_local *priv = q_napi->ndev_priv; ++ struct net_device *dev = priv->netdev; ++ int work_done = 0, task = budget; ++ u32 ints, num; ++ u32 raw_int_reg, raw_int_mask; ++ ++ if (q_napi->rxq_id) { ++ raw_int_reg = RSS_RAW_PMU_INT; ++ raw_int_mask = DEF_INT_MASK_QUEUE(q_napi->rxq_id); ++ } else { ++ raw_int_reg = RAW_PMU_INT; ++ raw_int_mask = DEF_INT_MASK; ++ } ++ ++ do { ++ if (!q_napi->rxq_id) ++ higmac_xmit_reclaim(dev); ++ num = higmac_rx(dev, task, q_napi->rxq_id); ++ work_done += num; ++ task -= num; ++ if (work_done >= budget) ++ break; ++ ++ ints = readl(priv->gmac_iobase + raw_int_reg); ++ ints &= raw_int_mask; ++ writel(ints, priv->gmac_iobase + raw_int_reg); ++ } while (ints || higmac_rxq_has_packets(priv, q_napi->rxq_id)); ++ ++ if (work_done < budget) { ++ napi_complete(napi); ++ higmac_irq_enable_queue(priv, q_napi->rxq_id); ++ } ++ ++ return work_done; ++} ++ ++static irqreturn_t higmac_interrupt(int irq, void *dev_id) ++{ ++ struct higmac_napi *q_napi = (struct higmac_napi *)dev_id; ++ struct higmac_netdev_local *ld = q_napi->ndev_priv; ++ u32 ints; ++ u32 raw_int_reg, raw_int_mask; ++ ++ if (higmac_queue_irq_disabled(ld, q_napi->rxq_id)) ++ return IRQ_NONE; ++ ++ if (q_napi->rxq_id) { ++ raw_int_reg = RSS_RAW_PMU_INT; ++ raw_int_mask = DEF_INT_MASK_QUEUE(q_napi->rxq_id); ++ } else { ++ raw_int_reg = RAW_PMU_INT; ++ raw_int_mask = DEF_INT_MASK; ++ } ++ ++ ints = readl(ld->gmac_iobase + raw_int_reg); ++ ints &= raw_int_mask; ++ writel(ints, ld->gmac_iobase + raw_int_reg); ++ ++ if (likely(ints || higmac_rxq_has_packets(ld, q_napi->rxq_id))) { ++ higmac_irq_disable_queue(ld, q_napi->rxq_id); ++ napi_schedule(&q_napi->napi); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static inline __be16 higmac_get_l3_proto(struct sk_buff *skb) ++{ ++ __be16 l3_proto; ++ ++ l3_proto = skb->protocol; ++ if (skb->protocol == htons(ETH_P_8021Q)) ++ l3_proto = vlan_get_protocol(skb); ++ ++ return l3_proto; ++} ++ ++static inline unsigned int higmac_get_l4_proto(struct sk_buff *skb) ++{ ++ __be16 l3_proto; ++ unsigned int l4_proto = IPPROTO_MAX; ++ ++ l3_proto = higmac_get_l3_proto(skb); ++ if (l3_proto == htons(ETH_P_IP)) ++ l4_proto = ip_hdr(skb)->protocol; ++ else if (l3_proto == htons(ETH_P_IPV6)) ++ l4_proto = ipv6_hdr(skb)->nexthdr; ++ ++ return l4_proto; ++} ++ ++static inline bool higmac_skb_is_ipv6(struct sk_buff *skb) ++{ ++ return (higmac_get_l3_proto(skb) == htons(ETH_P_IPV6)); ++} ++ ++static inline bool higmac_skb_is_udp(struct sk_buff *skb) ++{ ++ return (higmac_get_l4_proto(skb) == IPPROTO_UDP); ++} ++ ++static int higmac_check_hw_capability_for_udp(struct sk_buff *skb) ++{ ++ struct ethhdr *eth; ++ ++ /* hardware can't dea with UFO broadcast packet */ ++ eth = (struct ethhdr *)(skb->data); ++ if (skb_is_gso(skb) && is_broadcast_ether_addr(eth->h_dest)) ++ return -ENOTSUPP; ++ ++ return 0; ++} ++ ++static int higmac_check_hw_capability_for_ipv6(struct sk_buff *skb) ++{ ++ unsigned int l4_proto = IPPROTO_MAX; ++ ++ l4_proto = ipv6_hdr(skb)->nexthdr; ++ ++ if ((l4_proto != IPPROTO_TCP) && (l4_proto != IPPROTO_UDP)) { ++ /* when IPv6 next header is not tcp or udp, ++ * it means that IPv6 next header is extension header. ++ * Hardware can't deal with this case, ++ * so do checksumming by software or do GSO by software. ++ */ ++ if (skb_is_gso(skb)) ++ return -ENOTSUPP; ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL && ++ skb_checksum_help(skb)) ++ return -EFAULT; ++ } ++ ++ return 0; ++} ++ ++static inline bool higmac_skb_is_ipv4_with_options(struct sk_buff *skb) ++{ ++ return ((higmac_get_l3_proto(skb) == htons(ETH_P_IP)) && ++ (ip_hdr(skb)->ihl > 5)); ++} ++ ++static int higmac_check_hw_capability(struct sk_buff *skb) ++{ ++ int ret = 0; ++ ++ /* if tcp_mtu_probe() use (2 * tp->mss_cache) as probe_size, ++ * the linear data length will be larger than 2048, ++ * the MAC can't handle it, so let the software do it. ++ */ ++ if (skb_is_gso(skb) && (skb_headlen(skb) > 2048)) ++ return -ENOTSUPP; ++ ++ if (higmac_skb_is_ipv6(skb)) { ++ ret = higmac_check_hw_capability_for_ipv6(skb); ++ if (ret) ++ return ret; ++ } ++ ++ if (higmac_skb_is_udp(skb)) { ++ ret = higmac_check_hw_capability_for_udp(skb); ++ if (ret) ++ return ret; ++ } ++ ++ if (((skb->ip_summed == CHECKSUM_PARTIAL) || skb_is_gso(skb)) && ++ higmac_skb_is_ipv4_with_options(skb)) ++ return -ENOTSUPP; ++ ++ return 0; ++} ++ ++static void higmac_do_udp_checksum(struct sk_buff *skb) ++{ ++ int offset; ++ __wsum csum; ++ __sum16 udp_csum; ++ ++ offset = skb_checksum_start_offset(skb); ++ WARN_ON(offset >= skb_headlen(skb)); ++ csum = skb_checksum(skb, offset, skb->len - offset, 0); ++ ++ offset += skb->csum_offset; ++ WARN_ON(offset + sizeof(__sum16) > skb_headlen(skb)); ++ udp_csum = csum_fold(csum); ++ if (udp_csum == 0) ++ udp_csum = CSUM_MANGLED_0; ++ ++ *(__sum16 *)(skb->data + offset) = udp_csum; ++ ++ skb->ip_summed = CHECKSUM_NONE; ++} ++ ++static void higmac_get_pkt_info(struct higmac_netdev_local *ld, ++ struct sk_buff *skb, ++ struct higmac_tso_desc *tx_bq_desc) ++{ ++ int nfrags = skb_shinfo(skb)->nr_frags; ++ ++ __be16 l3_proto; /* level 3 protocol */ ++ unsigned int l4_proto = IPPROTO_MAX; ++ unsigned int max_mss = ETH_DATA_LEN; ++ unsigned char coe_enable = 0; ++ int max_data_len = skb->len - ETH_HLEN; ++ ++ if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) ++ coe_enable = 1; ++ ++ tx_bq_desc->desc1.val = 0; ++ ++ if (skb_is_gso(skb)) { ++ tx_bq_desc->desc1.tx.tso_flag = 1; ++ tx_bq_desc->desc1.tx.sg_flag = 1; ++ } else if (nfrags) { ++ tx_bq_desc->desc1.tx.sg_flag = 1; ++ } ++ ++ l3_proto = skb->protocol; ++ if (skb->protocol == htons(ETH_P_8021Q)) { ++ l3_proto = vlan_get_protocol(skb); ++ tx_bq_desc->desc1.tx.vlan_flag = 1; ++ max_data_len -= VLAN_HLEN; ++ } ++ ++ if (l3_proto == htons(ETH_P_IP)) { ++ struct iphdr *iph; ++ ++ iph = ip_hdr(skb); ++ tx_bq_desc->desc1.tx.ip_ver = PKT_IPV4; ++ tx_bq_desc->desc1.tx.ip_hdr_len = iph->ihl; ++ ++ if ((max_data_len >= GSO_MAX_SIZE) && ++ (ntohs(iph->tot_len) <= (iph->ihl << 2))) ++ iph->tot_len = htons(GSO_MAX_SIZE - 1); ++ ++ max_mss -= iph->ihl * WORD_TO_BYTE; ++ l4_proto = iph->protocol; ++ } else if (l3_proto == htons(ETH_P_IPV6)) { ++ tx_bq_desc->desc1.tx.ip_ver = PKT_IPV6; ++ tx_bq_desc->desc1.tx.ip_hdr_len = PKT_IPV6_HDR_LEN; ++ max_mss -= PKT_IPV6_HDR_LEN * WORD_TO_BYTE; ++ l4_proto = ipv6_hdr(skb)->nexthdr; ++ } else { ++ coe_enable = 0; ++ } ++ ++ if (l4_proto == IPPROTO_TCP) { ++ tx_bq_desc->desc1.tx.prot_type = PKT_TCP; ++ tx_bq_desc->desc1.tx.prot_hdr_len = tcp_hdr(skb)->doff; ++ max_mss -= tcp_hdr(skb)->doff * WORD_TO_BYTE; ++ } else if (l4_proto == IPPROTO_UDP) { ++ tx_bq_desc->desc1.tx.prot_type = PKT_UDP; ++ tx_bq_desc->desc1.tx.prot_hdr_len = PKT_UDP_HDR_LEN; ++ if (l3_proto == htons(ETH_P_IPV6)) ++ max_mss -= sizeof(struct frag_hdr); ++ } else { ++ coe_enable = 0; ++ } ++ ++ if (skb_is_gso(skb)) ++ tx_bq_desc->desc1.tx.data_len = ++ (skb_shinfo(skb)->gso_size > max_mss) ? max_mss : ++ skb_shinfo(skb)->gso_size; ++ else ++ tx_bq_desc->desc1.tx.data_len = skb->len; ++ ++ if (coe_enable && skb_is_gso(skb) && (l4_proto == IPPROTO_UDP)) ++ higmac_do_udp_checksum(skb); ++ ++ if (coe_enable) ++ tx_bq_desc->desc1.tx.coe_flag = 1; ++ ++ tx_bq_desc->desc1.tx.nfrags_num = nfrags; ++ ++ tx_bq_desc->desc1.tx.hw_own = DESC_VLD_BUSY; ++} ++ ++static int higmac_xmit_gso(struct higmac_netdev_local *ld, struct sk_buff *skb, ++ struct higmac_tso_desc *tx_bq_desc, ++ unsigned int desc_pos) ++{ ++ int pkt_type = PKT_NORMAL; ++ int nfrags = skb_shinfo(skb)->nr_frags; ++ dma_addr_t addr; ++ int ret; ++ ++ if (skb_is_gso(skb) || nfrags) { ++ /* TSO pkt or SG pkt */ ++ pkt_type = PKT_SG; ++ } else { /* Normal pkt */ ++ pkt_type = PKT_NORMAL; ++ } ++ ++ ret = higmac_check_hw_capability(skb); ++ if (unlikely(ret)) ++ return ret; ++ ++ higmac_get_pkt_info(ld, skb, tx_bq_desc); ++ ++ if (pkt_type == PKT_NORMAL) { ++ if (!HAS_CAP_CCI(ld->hw_cap)) { ++ addr = dma_map_single(ld->dev, skb->data, skb->len, ++ DMA_TO_DEVICE); ++ ret = dma_mapping_error(ld->dev, addr); ++ if (unlikely(ret)) { ++ pr_err("Normal Packet DMA Mapping fail.\n"); ++ return -EFAULT; ++ } ++ tx_bq_desc->data_buff_addr = addr; ++ } else { ++ tx_bq_desc->data_buff_addr = virt_to_phys(skb->data); ++ } ++ } else { ++ struct sg_desc *desc_cur; ++ int i; ++ ++ if (unlikely(((ld->sg_head + 1) % ld->sg_count) == ++ ld->sg_tail)) { ++ /* SG pkt, but sg desc all used */ ++ pr_err("WARNING: sg desc all used.\n"); ++ return -EBUSY; ++ } ++ ++ desc_cur = ld->dma_sg_desc + ld->sg_head; ++ ++ /* TODO: deal with ipv6_id */ ++ if (tx_bq_desc->desc1.tx.tso_flag && ++ tx_bq_desc->desc1.tx.ip_ver == PKT_IPV6 && ++ tx_bq_desc->desc1.tx.prot_type == PKT_UDP) { ++ desc_cur->ipv6_id = ntohl(skb_shinfo(skb)->ip6_frag_id); ++ } ++ ++ desc_cur->total_len = skb->len; ++ desc_cur->linear_len = skb_headlen(skb); ++ if (!HAS_CAP_CCI(ld->hw_cap)) { ++ addr = dma_map_single(ld->dev, skb->data, ++ desc_cur->linear_len, ++ DMA_TO_DEVICE); ++ ret = dma_mapping_error(ld->dev, addr); ++ if (unlikely(ret)) { ++ pr_err("DMA Mapping fail."); ++ return -EFAULT; ++ } ++ desc_cur->linear_addr = addr; ++ } else { ++ desc_cur->linear_addr = virt_to_phys(skb->data); ++ } ++ ++ for (i = 0; i < nfrags; i++) { ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ int len = frag->size; ++ ++ if (!HAS_CAP_CCI(ld->hw_cap)) { ++ addr = skb_frag_dma_map(ld->dev, frag, 0, len, ++ DMA_TO_DEVICE); ++ ret = dma_mapping_error(ld->dev, addr); ++ if (unlikely(ret)) { ++ pr_err("skb frag DMA Mapping fail."); ++ return -EFAULT; ++ } ++ desc_cur->frags[i].addr = addr; ++ } else { ++ desc_cur->frags[i].addr = ++ page_to_phys(skb_frag_page(frag)) + ++ frag->page_offset; ++ } ++ desc_cur->frags[i].size = len; ++ } ++ tx_bq_desc->data_buff_addr = ld->dma_sg_phy + ++ ld->sg_head * sizeof(struct sg_desc); ++ ld->tx_bq.sg_desc_offset[desc_pos] = ld->sg_head; ++ ++ ld->sg_head = (ld->sg_head + 1) % ld->sg_count; ++ } ++ ++#ifdef HIGMAC_TSO_DEBUG ++ memcpy(&pkt_rec[id_send].desc, tx_bq_desc, ++ sizeof(struct higmac_tso_desc)); ++ pkt_rec[id_send].status = 1; ++ id_send++; ++ if (id_send == MAX_RECORD) ++ id_send = 0; ++#endif ++ return 0; ++} ++ ++static netdev_tx_t higmac_net_xmit(struct sk_buff *skb, struct net_device *dev); ++ ++static netdev_tx_t higmac_sw_gso(struct higmac_netdev_local *ld, ++ struct sk_buff *skb) ++{ ++ struct sk_buff *segs, *curr_skb; ++ int gso_segs = skb_shinfo(skb)->gso_segs; ++ ++ if (gso_segs == 0 && skb_shinfo(skb)->gso_size != 0) ++ gso_segs = DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size); ++ ++ /* Estimate the number of fragments in the worst case */ ++ if (unlikely(higmac_tx_avail(ld) < gso_segs)) { ++ netif_stop_queue(ld->netdev); ++ if (higmac_tx_avail(ld) < gso_segs) { ++ ld->netdev->stats.tx_dropped++; ++ ld->netdev->stats.tx_fifo_errors++; ++ return NETDEV_TX_BUSY; ++ } ++ ++ netif_wake_queue(ld->netdev); ++ } ++ ++ segs = skb_gso_segment(skb, ld->netdev->features & ~(NETIF_F_ALL_CSUM | ++ NETIF_F_SG | NETIF_F_GSO_SOFTWARE)); ++ ++ if (IS_ERR_OR_NULL(segs)) ++ goto drop; ++ ++ do { ++ curr_skb = segs; ++ segs = segs->next; ++ curr_skb->next = NULL; ++ higmac_net_xmit(curr_skb, ld->netdev); ++ } while (segs); ++ ++ dev_kfree_skb_any(skb); ++ return NETDEV_TX_OK; ++ ++drop: ++ dev_kfree_skb_any(skb); ++ ld->netdev->stats.tx_dropped++; ++ return NETDEV_TX_OK; ++} ++ ++static netdev_tx_t higmac_net_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct higmac_netdev_local *ld = netdev_priv(dev); ++ struct higmac_desc *desc; ++ dma_addr_t addr; ++ unsigned long txflags; ++ int ret; ++ u32 pos; ++ ++ if (skb->len < ETH_HLEN) { ++ dev_kfree_skb_any(skb); ++ dev->stats.tx_errors++; ++ dev->stats.tx_dropped++; ++ return NETDEV_TX_OK; ++ } ++ ++ /* if adding higmac_xmit_reclaim here, iperf tcp client ++ * performance will be affected, from 550M(avg) to 513M~300M ++ */ ++ ++ /* software write pointer */ ++ pos = dma_cnt(readl(ld->gmac_iobase + TX_BQ_WR_ADDR)); ++ ++ spin_lock_irqsave(&ld->txlock, txflags); ++ ++ if (unlikely(ld->tx_skb[pos] || ld->tx_bq.skb[pos])) { ++ dev->stats.tx_dropped++; ++ dev->stats.tx_fifo_errors++; ++ netif_stop_queue(dev); ++ spin_unlock_irqrestore(&ld->txlock, txflags); ++ ++ return NETDEV_TX_BUSY; ++ } ++ ++ ld->tx_bq.skb[pos] = skb; ++ ld->tx_skb[pos] = skb; ++ ++ desc = ld->tx_bq.desc + pos; ++ ++ if (ld->tso_supported) { ++ ret = higmac_xmit_gso(ld, skb, ++ (struct higmac_tso_desc *)desc, ++ pos); ++ if (unlikely(ret < 0)) { ++ ld->tx_skb[pos] = NULL; ++ ld->tx_bq.skb[pos] = NULL; ++ spin_unlock_irqrestore(&ld->txlock, txflags); ++ ++ if (ret == -ENOTSUPP) ++ return higmac_sw_gso(ld, skb); ++ ++ dev_kfree_skb_any(skb); ++ dev->stats.tx_dropped++; ++ return NETDEV_TX_OK; ++ } ++ } else { ++ if (!HAS_CAP_CCI(ld->hw_cap)) { ++ addr = dma_map_single(ld->dev, skb->data, skb->len, ++ DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(ld->dev, addr))) { ++ dev_kfree_skb_any(skb); ++ dev->stats.tx_dropped++; ++ ld->tx_skb[pos] = NULL; ++ ld->tx_bq.skb[pos] = NULL; ++ spin_unlock_irqrestore(&ld->txlock, txflags); ++ return NETDEV_TX_OK; ++ } ++ desc->data_buff_addr = addr; ++ } else { ++ desc->data_buff_addr = virt_to_phys(skb->data); ++ } ++ desc->buffer_len = HIETH_MAX_FRAME_SIZE - 1; ++ desc->data_len = skb->len; ++ desc->fl = DESC_FL_FULL; ++ desc->descvid = DESC_VLD_BUSY; ++ } ++ ++ /* This barrier is important here. It is required to ensure ++ * the ARM CPU flushes it's DMA write buffers before proceeding ++ * to the next instruction, to ensure that GMAC will see ++ * our descriptor changes in memory ++ */ ++ HIGMAC_SYNC_BARRIER(); ++ ++ pos = dma_ring_incr(pos, TX_DESC_NUM); ++ writel(dma_byte(pos), ld->gmac_iobase + TX_BQ_WR_ADDR); ++ ++ dev->trans_start = jiffies; ++ dev->stats.tx_packets++; ++ dev->stats.tx_bytes += skb->len; ++ netdev_sent_queue(dev, skb->len); ++ ++ spin_unlock_irqrestore(&ld->txlock, txflags); ++ ++ return NETDEV_TX_OK; ++} ++ ++void higmac_enable_napi(struct higmac_netdev_local *priv) ++{ ++ struct higmac_napi *q_napi; ++ int i; ++ ++ for (i = 0; i < priv->num_rxqs; i++) { ++ q_napi = &priv->q_napi[i]; ++ napi_enable(&q_napi->napi); ++ } ++} ++ ++void higmac_disable_napi(struct higmac_netdev_local *priv) ++{ ++ struct higmac_napi *q_napi; ++ int i; ++ ++ for (i = 0; i < priv->num_rxqs; i++) { ++ q_napi = &priv->q_napi[i]; ++ napi_disable(&q_napi->napi); ++ } ++} ++ ++static int higmac_net_open(struct net_device *dev) ++{ ++ struct higmac_netdev_local *ld = netdev_priv(dev); ++ unsigned long flags; ++ ++ clk_prepare_enable(ld->macif_clk); ++ clk_prepare_enable(ld->clk); ++ ++ /* If we configure mac address by ++ * "ifconfig ethX hw ether XX:XX:XX:XX:XX:XX", ++ * the ethX must be down state and mac core clock is disabled ++ * which results the mac address has not been configured ++ * in mac core register. ++ * So we must set mac address again here, ++ * because mac core clock is enabled at this time ++ * and we can configure mac address to mac core register. ++ */ ++ higmac_hw_set_mac_addr(dev); ++ ++ /* We should use netif_carrier_off() here, ++ * because the default state should be off. ++ * And this call should before phy_start(). ++ */ ++ netif_carrier_off(dev); ++ higmac_enable_napi(ld); ++ phy_start(ld->phy); ++ ++ higmac_hw_desc_enable(ld); ++ higmac_port_enable(ld); ++ higmac_irq_enable_all_queue(ld); ++ ++ spin_lock_irqsave(&ld->rxlock, flags); ++ higmac_rx_refill(ld); ++ spin_unlock_irqrestore(&ld->rxlock, flags); ++ ++ ld->monitor.expires = jiffies + HIGMAC_MONITOR_TIMER; ++ mod_timer(&ld->monitor, ld->monitor.expires); ++ ++ netif_start_queue(dev); ++ ++ return 0; ++} ++ ++static int higmac_net_close(struct net_device *dev) ++{ ++ struct higmac_netdev_local *ld = netdev_priv(dev); ++ ++ higmac_irq_disable_all_queue(ld); ++ higmac_hw_desc_disable(ld); ++ ++ higmac_disable_napi(ld); ++ ++ netif_carrier_off(dev); ++ netif_stop_queue(dev); ++ ++ phy_stop(ld->phy); ++ del_timer_sync(&ld->monitor); ++ ++ clk_disable_unprepare(ld->clk); ++ clk_disable_unprepare(ld->macif_clk); ++ ++ return 0; ++} ++ ++static void higmac_net_timeout(struct net_device *dev) ++{ ++ dev->stats.tx_errors++; ++ ++ pr_err("tx timeout!\n"); ++} ++ ++static void higmac_set_multicast_list(struct net_device *dev) ++{ ++ higmac_gmac_multicast_list(dev); ++} ++ ++static inline void higmac_enable_rxcsum_drop(struct higmac_netdev_local *ld, ++ bool drop) ++{ ++ unsigned int v; ++ ++ v = readl(ld->gmac_iobase + TSO_COE_CTRL); ++ if (drop) ++ v |= COE_ERR_DROP; ++ else ++ v &= ~COE_ERR_DROP; ++ writel(v, ld->gmac_iobase + TSO_COE_CTRL); ++} ++ ++static int higmac_set_features(struct net_device *dev, ++ netdev_features_t features) ++{ ++ struct higmac_netdev_local *ld = netdev_priv(dev); ++ netdev_features_t changed = dev->features ^ features; ++ ++ if (changed & NETIF_F_RXCSUM) { ++ if (features & NETIF_F_RXCSUM) ++ higmac_enable_rxcsum_drop(ld, true); ++ else ++ higmac_enable_rxcsum_drop(ld, false); ++ } ++ ++ return 0; ++} ++ ++static struct net_device_stats *higmac_net_get_stats(struct net_device *dev) ++{ ++ return &dev->stats; ++} ++ ++static void higmac_get_drvinfo(struct net_device *net_dev, ++ struct ethtool_drvinfo *info) ++{ ++ strncpy(info->driver, "higmac driver", 15); ++ strncpy(info->version, "higmac v200", 15); ++ strncpy(info->bus_info, "platform", 15); ++} ++ ++static unsigned int higmac_get_link(struct net_device *net_dev) ++{ ++ struct higmac_netdev_local *ld = netdev_priv(net_dev); ++ ++ return ld->phy->link ? HIGMAC_LINKED : 0; ++} ++ ++static int higmac_get_settings(struct net_device *net_dev, ++ struct ethtool_cmd *cmd) ++{ ++ struct higmac_netdev_local *ld = netdev_priv(net_dev); ++ ++ if (ld->phy) ++ return phy_ethtool_gset(ld->phy, cmd); ++ ++ return -EINVAL; ++} ++ ++static int higmac_set_settings(struct net_device *net_dev, ++ struct ethtool_cmd *cmd) ++{ ++ struct higmac_netdev_local *ld = netdev_priv(net_dev); ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ if (ld->phy) ++ return phy_ethtool_sset(ld->phy, cmd); ++ ++ return -EINVAL; ++} ++ ++static void higmac_get_pauseparam(struct net_device *net_dev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct higmac_netdev_local *ld = netdev_priv(net_dev); ++ ++ pause->rx_pause = 0; ++ pause->tx_pause = 0; ++ pause->autoneg = ld->phy->autoneg; ++ ++ if (ld->flow_ctrl & FLOW_RX) ++ pause->rx_pause = 1; ++ if (ld->flow_ctrl & FLOW_TX) ++ pause->tx_pause = 1; ++} ++ ++static int higmac_set_pauseparam(struct net_device *net_dev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct higmac_netdev_local *ld = netdev_priv(net_dev); ++ struct phy_device *phy = ld->phy; ++ int new_pause = FLOW_OFF; ++ int ret = 0; ++ ++ if (pause->rx_pause) ++ new_pause |= FLOW_RX; ++ if (pause->tx_pause) ++ new_pause |= FLOW_TX; ++ ++ if (new_pause != ld->flow_ctrl) ++ ld->flow_ctrl = new_pause; ++ ++ higmac_set_flow_ctrl_state(ld, phy->pause); ++ phy->advertising &= ~SUPPORTED_Pause; ++ if (ld->flow_ctrl) ++ phy->advertising |= SUPPORTED_Pause; ++ ++ if (phy->autoneg) { ++ if (netif_running(net_dev)) ++ return phy_start_aneg(phy); ++ } ++ ++ return ret; ++} ++ ++static u32 higmac_ethtool_getmsglevel(struct net_device *ndev) ++{ ++ struct higmac_netdev_local *priv = netdev_priv(ndev); ++ ++ return priv->msg_enable; ++} ++ ++static void higmac_ethtool_setmsglevel(struct net_device *ndev, u32 level) ++{ ++ struct higmac_netdev_local *priv = netdev_priv(ndev); ++ ++ priv->msg_enable = level; ++} ++ ++static u32 higmac_get_rxfh_key_size(struct net_device *ndev) ++{ ++ return RSS_HASH_KEY_SIZE; ++} ++ ++static u32 higmac_get_rxfh_indir_size(struct net_device *ndev) ++{ ++ struct higmac_netdev_local *priv = netdev_priv(ndev); ++ ++ return priv->rss_info.ind_tbl_size; ++} ++ ++static int higmac_get_rxfh(struct net_device *ndev, u32 *indir, u8 *hkey) ++{ ++ struct higmac_netdev_local *priv = netdev_priv(ndev); ++ struct higmac_rss_info *rss = &priv->rss_info; ++ ++ if (hkey) ++ memcpy(hkey, rss->key, RSS_HASH_KEY_SIZE); ++ ++ if (indir) { ++ int i; ++ ++ for (i = 0; i < rss->ind_tbl_size; i++) ++ indir[i] = rss->ind_tbl[i]; ++ } ++ ++ return 0; ++} ++ ++static void higmac_get_rss_key(struct higmac_netdev_local *priv) ++{ ++ struct higmac_rss_info *rss = &priv->rss_info; ++ u32 hkey; ++ ++ hkey = readl(priv->gmac_iobase + RSS_HASH_KEY); ++ *((u32 *)rss->key) = hkey; ++} ++ ++static void higmac_set_rss_key(struct higmac_netdev_local *priv) ++{ ++ struct higmac_rss_info *rss = &priv->rss_info; ++ ++ writel(*((u32 *)rss->key), priv->gmac_iobase + RSS_HASH_KEY); ++} ++ ++static int higmac_wait_rss_ready(struct higmac_netdev_local *priv) ++{ ++ void __iomem *base = priv->gmac_iobase; ++ int i, timeout = 10000; ++ ++ for (i = 0; !(readl(base + RSS_IND_TBL) & BIT_IND_TBL_READY); i++) { ++ if (i == timeout) { ++ netdev_err(priv->netdev, "wait rss ready timeout!\n"); ++ return -ETIMEDOUT; ++ } ++ usleep_range(10, 20); ++ } ++ ++ return 0; ++} ++ ++static void higmac_config_rss(struct higmac_netdev_local *priv) ++{ ++ struct higmac_rss_info *rss = &priv->rss_info; ++ u32 rss_val; ++ int i; ++ ++ for (i = 0; i < rss->ind_tbl_size; i++) { ++ if (higmac_wait_rss_ready(priv)) ++ break; ++ rss_val = BIT_IND_TLB_WR | (rss->ind_tbl[i] << 8) | i; ++ writel(rss_val, priv->gmac_iobase + RSS_IND_TBL); ++ } ++} ++ ++static void higmac_get_rss(struct higmac_netdev_local *priv) ++{ ++ struct higmac_rss_info *rss = &priv->rss_info; ++ u32 rss_val; ++ int i; ++ ++ for (i = 0; i < rss->ind_tbl_size; i++) { ++ if (higmac_wait_rss_ready(priv)) ++ break; ++ writel(i, priv->gmac_iobase + RSS_IND_TBL); ++ if (higmac_wait_rss_ready(priv)) ++ break; ++ rss_val = readl(priv->gmac_iobase + RSS_IND_TBL); ++ rss->ind_tbl[i] = (rss_val >> 10) & 0x3; ++ } ++} ++ ++static int higmac_set_rxfh(struct net_device *ndev, const u32 *indir, ++ const u8 *hkey) ++{ ++ struct higmac_netdev_local *priv = netdev_priv(ndev); ++ struct higmac_rss_info *rss = &priv->rss_info; ++ ++ if (indir) { ++ int i; ++ ++ for (i = 0; i < rss->ind_tbl_size; i++) ++ rss->ind_tbl[i] = indir[i]; ++ } ++ ++ if (hkey) { ++ memcpy(rss->key, hkey, RSS_HASH_KEY_SIZE); ++ higmac_set_rss_key(priv); ++ } ++ ++ higmac_config_rss(priv); ++ ++ return 0; ++} ++ ++static int higmac_get_rss_hash_opts(struct higmac_netdev_local *priv, ++ struct ethtool_rxnfc *info) ++{ ++ u32 hash_cfg = priv->rss_info.hash_cfg; ++ ++ info->data = 0; ++ ++ switch (info->flow_type) { ++ case TCP_V4_FLOW: ++ if (hash_cfg & TCPV4_L3_HASH_EN) ++ info->data |= RXH_IP_SRC | RXH_IP_DST; ++ if (hash_cfg & TCPV4_L4_HASH_EN) ++ info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; ++ if (hash_cfg & TCPV4_VLAN_HASH_EN) ++ info->data |= RXH_VLAN; ++ break; ++ case TCP_V6_FLOW: ++ if (hash_cfg & TCPV6_L3_HASH_EN) ++ info->data |= RXH_IP_SRC | RXH_IP_DST; ++ if (hash_cfg & TCPV6_L4_HASH_EN) ++ info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; ++ if (hash_cfg & TCPV6_VLAN_HASH_EN) ++ info->data |= RXH_VLAN; ++ break; ++ case UDP_V4_FLOW: ++ if (hash_cfg & UDPV4_L3_HASH_EN) ++ info->data |= RXH_IP_SRC | RXH_IP_DST; ++ if (hash_cfg & UDPV4_L4_HASH_EN) ++ info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; ++ if (hash_cfg & UDPV4_VLAN_HASH_EN) ++ info->data |= RXH_VLAN; ++ break; ++ case UDP_V6_FLOW: ++ if (hash_cfg & UDPV6_L3_HASH_EN) ++ info->data |= RXH_IP_SRC | RXH_IP_DST; ++ if (hash_cfg & UDPV6_L4_HASH_EN) ++ info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; ++ if (hash_cfg & UDPV6_VLAN_HASH_EN) ++ info->data |= RXH_VLAN; ++ break; ++ case IPV4_FLOW: ++ if (hash_cfg & IPV4_L3_HASH_EN) ++ info->data |= RXH_IP_SRC | RXH_IP_DST; ++ if (hash_cfg & IPV4_VLAN_HASH_EN) ++ info->data |= RXH_VLAN; ++ break; ++ case IPV6_FLOW: ++ if (hash_cfg & IPV6_L3_HASH_EN) ++ info->data |= RXH_IP_SRC | RXH_IP_DST; ++ if (hash_cfg & IPV6_VLAN_HASH_EN) ++ info->data |= RXH_VLAN; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int higmac_get_rxnfc(struct net_device *ndev, ++ struct ethtool_rxnfc *info, u32 *rules) ++{ ++ struct higmac_netdev_local *priv = netdev_priv(ndev); ++ int ret = -EOPNOTSUPP; ++ ++ switch (info->cmd) { ++ case ETHTOOL_GRXRINGS: ++ info->data = priv->num_rxqs; ++ ret = 0; ++ break; ++ case ETHTOOL_GRXFH: ++ return higmac_get_rss_hash_opts(priv, info); ++ default: ++ break; ++ } ++ return ret; ++} ++ ++static void higmac_config_hash_policy(struct higmac_netdev_local *priv) ++{ ++ writel(priv->rss_info.hash_cfg, priv->gmac_iobase + RSS_HASH_CONFIG); ++} ++ ++static int higmac_set_rss_hash_opts(struct higmac_netdev_local *priv, ++ struct ethtool_rxnfc *info) ++{ ++ u32 hash_cfg = priv->rss_info.hash_cfg; ++ ++ netdev_info(priv->netdev, "Set RSS flow type = %d, data = %lld\n", ++ info->flow_type, info->data); ++ ++ if (!(info->data & RXH_IP_SRC) || !(info->data & RXH_IP_DST)) ++ return -EINVAL; ++ ++ switch (info->flow_type) { ++ case TCP_V4_FLOW: ++ switch (info->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { ++ case 0: ++ hash_cfg &= ~TCPV4_L4_HASH_EN; ++ break; ++ case (RXH_L4_B_0_1 | RXH_L4_B_2_3): ++ hash_cfg |= TCPV4_L4_HASH_EN; ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (info->data & RXH_VLAN) ++ hash_cfg |= TCPV4_VLAN_HASH_EN; ++ else ++ hash_cfg &= ~TCPV4_VLAN_HASH_EN; ++ break; ++ case TCP_V6_FLOW: ++ switch (info->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { ++ case 0: ++ hash_cfg &= ~TCPV6_L4_HASH_EN; ++ break; ++ case (RXH_L4_B_0_1 | RXH_L4_B_2_3): ++ hash_cfg |= TCPV6_L4_HASH_EN; ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (info->data & RXH_VLAN) ++ hash_cfg |= TCPV6_VLAN_HASH_EN; ++ else ++ hash_cfg &= ~TCPV6_VLAN_HASH_EN; ++ break; ++ case UDP_V4_FLOW: ++ switch (info->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { ++ case 0: ++ hash_cfg &= ~UDPV4_L4_HASH_EN; ++ break; ++ case (RXH_L4_B_0_1 | RXH_L4_B_2_3): ++ hash_cfg |= UDPV4_L4_HASH_EN; ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (info->data & RXH_VLAN) ++ hash_cfg |= UDPV4_VLAN_HASH_EN; ++ else ++ hash_cfg &= ~UDPV4_VLAN_HASH_EN; ++ break; ++ case UDP_V6_FLOW: ++ switch (info->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { ++ case 0: ++ hash_cfg &= ~UDPV6_L4_HASH_EN; ++ break; ++ case (RXH_L4_B_0_1 | RXH_L4_B_2_3): ++ hash_cfg |= UDPV6_L4_HASH_EN; ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (info->data & RXH_VLAN) ++ hash_cfg |= UDPV6_VLAN_HASH_EN; ++ else ++ hash_cfg &= ~UDPV6_VLAN_HASH_EN; ++ break; ++ case IPV4_FLOW: ++ if (info->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) ++ return -EINVAL; ++ if (info->data & RXH_VLAN) ++ hash_cfg |= IPV4_VLAN_HASH_EN; ++ else ++ hash_cfg &= ~IPV4_VLAN_HASH_EN; ++ break; ++ case IPV6_FLOW: ++ if (info->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) ++ return -EINVAL; ++ if (info->data & RXH_VLAN) ++ hash_cfg |= IPV6_VLAN_HASH_EN; ++ else ++ hash_cfg &= ~IPV6_VLAN_HASH_EN; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ priv->rss_info.hash_cfg = hash_cfg; ++ higmac_config_hash_policy(priv); ++ ++ return 0; ++} ++ ++static int higmac_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *info) ++{ ++ struct higmac_netdev_local *priv = netdev_priv(ndev); ++ ++ switch (info->cmd) { ++ case ETHTOOL_SRXFH: ++ return higmac_set_rss_hash_opts(priv, info); ++ default: ++ break; ++ } ++ return -EOPNOTSUPP; ++} ++ ++static const struct ethtool_ops hieth_ethtools_ops = { ++ .get_drvinfo = higmac_get_drvinfo, ++ .get_link = higmac_get_link, ++ .get_settings = higmac_get_settings, ++ .set_settings = higmac_set_settings, ++ .get_pauseparam = higmac_get_pauseparam, ++ .set_pauseparam = higmac_set_pauseparam, ++ .get_msglevel = higmac_ethtool_getmsglevel, ++ .set_msglevel = higmac_ethtool_setmsglevel, ++ .get_rxfh_key_size = higmac_get_rxfh_key_size, ++ .get_rxfh_indir_size = higmac_get_rxfh_indir_size, ++ .get_rxfh = higmac_get_rxfh, ++ .set_rxfh = higmac_set_rxfh, ++ .get_rxnfc = higmac_get_rxnfc, ++ .set_rxnfc = higmac_set_rxnfc, ++}; ++ ++static const struct net_device_ops hieth_netdev_ops = { ++ .ndo_open = higmac_net_open, ++ .ndo_stop = higmac_net_close, ++ .ndo_start_xmit = higmac_net_xmit, ++ .ndo_tx_timeout = higmac_net_timeout, ++ .ndo_set_rx_mode = higmac_set_multicast_list, ++ .ndo_set_features = higmac_set_features, ++ .ndo_do_ioctl = higmac_ioctl, ++ .ndo_set_mac_address = higmac_net_set_mac_address, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_get_stats = higmac_net_get_stats, ++}; ++ ++static int higmac_of_get_param(struct higmac_netdev_local *ld, ++ struct device_node *node) ++{ ++ /* get auto eee */ ++ ld->autoeee = of_property_read_bool(node, "autoeee"); ++ /* get internal flag */ ++ ld->internal_phy = ++ of_property_read_bool(node, "internal-phy"); ++ ++ return 0; ++} ++ ++static int KSZ8051MNL_phy_fix(struct phy_device *phy_dev) ++{ ++ u32 v; ++ int ret; ++ ++ if (phy_dev->interface != PHY_INTERFACE_MODE_RMII) ++ return 0; ++ ++ ret = phy_read(phy_dev, 0x1F); ++ if (ret < 0) ++ return ret; ++ v = ret; ++ v |= (1 << 7); /* set phy RMII 50MHz clk; */ ++ phy_write(phy_dev, 0x1F, v); ++ ++ ret = phy_read(phy_dev, 0x16); ++ if (ret < 0) ++ return ret; ++ v = ret; ++ v |= (1 << 1); /* set phy RMII override; */ ++ phy_write(phy_dev, 0x16, v); ++ ++ return 0; ++} ++ ++static int KSZ8081RNB_phy_fix(struct phy_device *phy_dev) ++{ ++ u32 v; ++ int ret; ++ ++ if (phy_dev->interface != PHY_INTERFACE_MODE_RMII) ++ return 0; ++ ++ ret = phy_read(phy_dev, 0x1F); ++ if (ret < 0) ++ return ret; ++ v = ret; ++ v |= (1 << 7); /* set phy RMII 50MHz clk; */ ++ phy_write(phy_dev, 0x1F, v); ++ ++ return 0; ++} ++ ++static int rtl8211e_phy_fix(struct phy_device *phy_dev) ++{ ++ u32 v; ++ int ret; ++ ++ /* select Extension page */ ++ phy_write(phy_dev, 0x1f, 0x7); ++ /* switch ExtPage 164 */ ++ phy_write(phy_dev, 0x1e, 0xa4); ++ ++ /* config RGMII rx pin io driver max */ ++ ret = phy_read(phy_dev, 0x1c); ++ if (ret < 0) ++ return ret; ++ v = ret; ++ v = (v & 0xff03) | 0xfc; ++ phy_write(phy_dev, 0x1c, v); ++ ++ /* select to page 0 */ ++ phy_write(phy_dev, 0x1f, 0); ++ ++ return 0; ++} ++ ++static void phy_register_fixups(void) ++{ ++ phy_register_fixup_for_uid(PHY_ID_KSZ8051MNL, DEFAULT_PHY_MASK, ++ KSZ8051MNL_phy_fix); ++ phy_register_fixup_for_uid(PHY_ID_KSZ8081RNB, DEFAULT_PHY_MASK, ++ KSZ8081RNB_phy_fix); ++ phy_register_fixup_for_uid(REALTEK_PHY_ID_8211E, REALTEK_PHY_MASK, ++ rtl8211e_phy_fix); ++} ++ ++static void phy_unregister_fixups(void) ++{ ++ phy_unregister_fixup_for_uid(PHY_ID_KSZ8051MNL, DEFAULT_PHY_MASK); ++ phy_unregister_fixup_for_uid(PHY_ID_KSZ8081RNB, DEFAULT_PHY_MASK); ++ phy_unregister_fixup_for_uid(REALTEK_PHY_ID_8211E, REALTEK_PHY_MASK); ++} ++ ++static void higmac_verify_flow_ctrl_args(void) ++{ ++#if defined(CONFIG_TX_FLOW_CTRL_SUPPORT) ++ flow_ctrl_en |= FLOW_TX; ++#endif ++#if defined(CONFIG_RX_FLOW_CTRL_SUPPORT) ++ flow_ctrl_en |= FLOW_RX; ++#endif ++ if (tx_flow_ctrl_active_threshold < FC_ACTIVE_MIN || ++ tx_flow_ctrl_active_threshold > FC_ACTIVE_MAX) ++ tx_flow_ctrl_active_threshold = FC_ACTIVE_DEFAULT; ++ ++ if (tx_flow_ctrl_deactive_threshold < FC_DEACTIVE_MIN || ++ tx_flow_ctrl_deactive_threshold > FC_DEACTIVE_MAX) ++ tx_flow_ctrl_deactive_threshold = FC_DEACTIVE_DEFAULT; ++ ++ if (tx_flow_ctrl_active_threshold >= tx_flow_ctrl_deactive_threshold) { ++ tx_flow_ctrl_active_threshold = FC_ACTIVE_DEFAULT; ++ tx_flow_ctrl_deactive_threshold = FC_DEACTIVE_DEFAULT; ++ } ++ ++ if (tx_flow_ctrl_pause_time < 0 || ++ tx_flow_ctrl_pause_time > FC_PAUSE_TIME_MAX) ++ tx_flow_ctrl_pause_time = FC_PAUSE_TIME_DEFAULT; ++ ++ if (tx_flow_ctrl_pause_interval < 0 || ++ tx_flow_ctrl_pause_interval > FC_PAUSE_TIME_MAX) ++ tx_flow_ctrl_pause_interval = FC_PAUSE_INTERVAL_DEFAULT; ++ ++ /* pause interval should not bigger than pause time, ++ * but should not too smaller to avoid sending too many pause frame. ++ */ ++ if ((tx_flow_ctrl_pause_interval > tx_flow_ctrl_pause_time) || ++ (tx_flow_ctrl_pause_interval < (tx_flow_ctrl_pause_time >> 1))) ++ tx_flow_ctrl_pause_interval = tx_flow_ctrl_pause_time; ++} ++ ++static void higmac_destroy_hw_desc_queue(struct higmac_netdev_local *priv) ++{ ++ int i; ++ ++ for (i = 0; i < QUEUE_NUMS + RSS_NUM_RXQS - 1; i++) { ++ if (priv->pool[i].desc) { ++ if (HAS_CAP_CCI(priv->hw_cap)) ++ kfree(priv->pool[i].desc); ++ else ++ dma_free_coherent(priv->dev, priv->pool[i].size, ++ priv->pool[i].desc, ++ priv->pool[i].phys_addr); ++ priv->pool[i].desc = NULL; ++ } ++ } ++ ++ kfree(priv->rx_fq.skb); ++ kfree(priv->tx_bq.skb); ++ priv->rx_fq.skb = NULL; ++ priv->tx_bq.skb = NULL; ++ ++ if (priv->tso_supported) { ++ kfree(priv->tx_bq.sg_desc_offset); ++ priv->tx_bq.sg_desc_offset = NULL; ++ } ++ ++ kfree(priv->tx_skb); ++ priv->tx_skb = NULL; ++ ++ kfree(priv->rx_skb); ++ priv->rx_skb = NULL; ++} ++ ++static int higmac_init_hw_desc_queue(struct higmac_netdev_local *priv) ++{ ++ struct device *dev = priv->dev; ++ struct higmac_desc *virt_addr; ++ dma_addr_t phys_addr = 0; ++ int size, i; ++ ++ priv->rx_fq.count = RX_DESC_NUM; ++ priv->rx_bq.count = RX_DESC_NUM; ++ priv->tx_bq.count = TX_DESC_NUM; ++ priv->tx_rq.count = TX_DESC_NUM; ++ ++ for (i = 1; i < RSS_NUM_RXQS; i++) ++ priv->pool[3 + i].count = RX_DESC_NUM; ++ ++ for (i = 0; i < (QUEUE_NUMS + RSS_NUM_RXQS - 1); i++) { ++ size = priv->pool[i].count * sizeof(struct higmac_desc); ++ if (HAS_CAP_CCI(priv->hw_cap)) { ++ virt_addr = kmalloc(size, GFP_KERNEL); ++ if (virt_addr) ++ phys_addr = virt_to_phys(virt_addr); ++ } else { ++ virt_addr = dma_alloc_coherent(dev, size, &phys_addr, ++ GFP_KERNEL); ++ } ++ if (!virt_addr) ++ goto error_free_pool; ++ ++ memset(virt_addr, 0, size); ++ priv->pool[i].size = size; ++ priv->pool[i].desc = virt_addr; ++ priv->pool[i].phys_addr = phys_addr; ++ } ++ priv->rx_fq.skb = kzalloc(priv->rx_fq.count ++ * sizeof(struct sk_buff *), GFP_KERNEL); ++ if (!priv->rx_fq.skb) ++ goto error_free_pool; ++ ++ priv->rx_skb = kzalloc(priv->rx_fq.count ++ * sizeof(struct sk_buff *), GFP_KERNEL); ++ if (!priv->rx_skb) ++ goto error_free_pool; ++ ++ priv->tx_bq.skb = kzalloc(priv->tx_bq.count ++ * sizeof(struct sk_buff *), GFP_KERNEL); ++ if (!priv->tx_bq.skb) ++ goto error_free_pool; ++ ++ priv->tx_skb = kzalloc(priv->tx_bq.count ++ * sizeof(struct sk_buff *), GFP_KERNEL); ++ if (!priv->tx_skb) ++ goto error_free_pool; ++ ++ if (priv->tso_supported) { ++ priv->tx_bq.sg_desc_offset = kzalloc(priv->tx_bq.count ++ * sizeof(int), GFP_KERNEL); ++ if (!priv->tx_bq.sg_desc_offset) ++ goto error_free_pool; ++ } ++ ++ higmac_hw_set_desc_addr(priv); ++ if (HAS_CAP_CCI(priv->hw_cap)) ++ pr_info("higmac: ETH MAC supporte CCI.\n"); ++ ++ return 0; ++ ++error_free_pool: ++ higmac_destroy_hw_desc_queue(priv); ++ ++ return -ENOMEM; ++} ++ ++void higmac_init_napi(struct higmac_netdev_local *priv) ++{ ++ struct higmac_napi *q_napi; ++ int i; ++ ++ for (i = 0; i < priv->num_rxqs; i++) { ++ q_napi = &priv->q_napi[i]; ++ q_napi->rxq_id = i; ++ q_napi->ndev_priv = priv; ++ netif_napi_add(priv->netdev, &q_napi->napi, higmac_poll, ++ NAPI_POLL_WEIGHT); ++ } ++} ++ ++void higmac_destroy_napi(struct higmac_netdev_local *priv) ++{ ++ struct higmac_napi *q_napi; ++ int i; ++ ++ for (i = 0; i < priv->num_rxqs; i++) { ++ q_napi = &priv->q_napi[i]; ++ netif_napi_del(&q_napi->napi); ++ } ++} ++ ++int higmac_request_irqs(struct platform_device *pdev, ++ struct higmac_netdev_local *priv) ++{ ++ struct device *dev = priv->dev; ++ int ret; ++ int i; ++ ++ for (i = 0; i < priv->num_rxqs; i++) { ++ ret = platform_get_irq(pdev, i); ++ if (ret < 0) { ++ dev_err(dev, "No irq[%d] resource, ret=%d\n", i, ret); ++ return ret; ++ } ++ priv->irq[i] = ret; ++ ++ ret = devm_request_irq(dev, priv->irq[i], higmac_interrupt, ++ IRQF_SHARED, pdev->name, ++ &priv->q_napi[i]); ++ if (ret) { ++ dev_err(dev, "devm_request_irq failed, ret=%d\n", ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int higmac_dev_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node; ++ struct net_device *ndev; ++ struct higmac_netdev_local *priv; ++ struct resource *res; ++ const char *mac_addr; ++ unsigned int hw_cap; ++ int ret; ++ int num_rxqs; ++ ++ higmac_verify_flow_ctrl_args(); ++ ++ if (of_device_is_compatible(node, "hisilicon,higmac-v5")) ++ num_rxqs = RSS_NUM_RXQS; ++ else ++ num_rxqs = 1; ++ ++ ndev = alloc_etherdev_mqs(sizeof(struct higmac_netdev_local), 1, ++ num_rxqs); ++ if (!ndev) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, ndev); ++ SET_NETDEV_DEV(ndev, dev); ++ ++ priv = netdev_priv(ndev); ++ priv->dev = dev; ++ priv->netdev = ndev; ++ priv->num_rxqs = num_rxqs; ++ ++ if (of_device_is_compatible(node, "hisilicon,higmac-v3")) ++ priv->hw_cap |= HW_CAP_CCI; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, MEM_GMAC_IOBASE); ++ priv->gmac_iobase = devm_ioremap_resource(dev, res); ++ if (IS_ERR(priv->gmac_iobase)) { ++ ret = PTR_ERR(priv->gmac_iobase); ++ goto out_free_netdev; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, ++ MEM_MACIF_IOBASE); ++ priv->macif_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(priv->macif_base)) { ++ ret = PTR_ERR(priv->macif_base); ++ goto out_free_netdev; ++ } ++ ++ priv->port_rst = devm_reset_control_get(dev, HIGMAC_PORT_RST_NAME); ++ if (IS_ERR(priv->port_rst)) { ++ ret = PTR_ERR(priv->port_rst); ++ goto out_free_netdev; ++ } ++ ++ priv->macif_rst = devm_reset_control_get(dev, HIGMAC_MACIF_RST_NAME); ++ if (IS_ERR(priv->macif_rst)) { ++ ret = PTR_ERR(priv->macif_rst); ++ goto out_free_netdev; ++ } ++ ++ priv->phy_rst = devm_reset_control_get(dev, HIGMAC_PHY_RST_NAME); ++ if (IS_ERR(priv->phy_rst)) ++ priv->phy_rst = NULL; ++ ++ priv->clk = devm_clk_get(&pdev->dev, HIGMAC_MAC_CLK_NAME); ++ if (IS_ERR(priv->clk)) { ++ netdev_err(ndev, "failed to get clk\n"); ++ ret = -ENODEV; ++ goto out_free_netdev; ++ } ++ ++ ret = clk_prepare_enable(priv->clk); ++ if (ret < 0) { ++ netdev_err(ndev, "failed to enable clk %d\n", ret); ++ goto out_free_netdev; ++ } ++ ++ priv->macif_clk = devm_clk_get(&pdev->dev, HIGMAC_MACIF_CLK_NAME); ++ if (IS_ERR(priv->macif_clk)) ++ priv->macif_clk = NULL; ++ ++ if (priv->macif_clk) { ++ ret = clk_prepare_enable(priv->macif_clk); ++ if (ret < 0) { ++ netdev_err(ndev, "failed enable macif_clk %d\n", ret); ++ goto out_clk_disable; ++ } ++ } ++ ++ higmac_mac_core_reset(priv); ++ ++ /* phy reset, should be early than "of_mdiobus_register". ++ * becausue "of_mdiobus_register" will read PHY register by MDIO. ++ */ ++ higmac_hw_phy_reset(priv); ++ ++ higmac_of_get_param(priv, node); ++ ++ ret = of_get_phy_mode(node); ++ if (ret < 0) { ++ netdev_err(ndev, "not find phy-mode\n"); ++ goto out_macif_clk_disable; ++ } ++ priv->phy_mode = ret; ++ ++ priv->phy_node = of_parse_phandle(node, "phy-handle", 0); ++ if (!priv->phy_node) { ++ netdev_err(ndev, "not find phy-handle\n"); ++ ret = -EINVAL; ++ goto out_macif_clk_disable; ++ } ++ ++ mac_addr = of_get_mac_address(node); ++ if (mac_addr) ++ ether_addr_copy(ndev->dev_addr, mac_addr); ++ if (!is_valid_ether_addr(ndev->dev_addr)) { ++ eth_hw_addr_random(ndev); ++ netdev_warn(ndev, "using random MAC address %pM\n", ++ ndev->dev_addr); ++ } ++ ++ higmac_hw_set_mac_addr(ndev); ++ ++ hw_cap = readl(priv->gmac_iobase + CRF_MIN_PACKET); ++ priv->tso_supported = HAS_TSO_CAP(hw_cap); ++ priv->has_rxhash_cap = HAS_RXHASH_CAP(hw_cap); ++ priv->has_rss_cap = HAS_RSS_CAP(hw_cap); ++ ++ higmac_set_rss_cap(priv); ++ higmac_get_rss_key(priv); ++ if (priv->has_rss_cap) { ++ priv->rss_info.ind_tbl_size = RSS_INDIRECTION_TABLE_SIZE; ++ higmac_get_rss(priv); ++ } ++ ++ if (priv->has_rxhash_cap) { ++ priv->rss_info.hash_cfg = DEF_HASH_CFG; ++ higmac_config_hash_policy(priv); ++ } ++ ++ /* init hw controller */ ++ higmac_hw_init(priv); ++ ++ /* TODO: phy fix here?? other way ??? */ ++ phy_register_fixups(); ++ ++ priv->phy = of_phy_connect(ndev, priv->phy_node, ++ &higmac_adjust_link, 0, priv->phy_mode); ++ if (!priv->phy) { ++ ret = -ENODEV; ++ goto out_phy_node; ++ } ++ ++ /* If the phy_id is mostly Fs, there is no device there */ ++ if ((priv->phy->phy_id & 0x1fffffff) == 0x1fffffff || ++ priv->phy->phy_id == 0) { ++ pr_info("phy %d not found\n", priv->phy->addr); ++ ret = -ENODEV; ++ goto out_phy_disconnect; ++ } ++ ++ pr_info("attached PHY %d to driver %s, PHY_ID=0x%x\n", ++ priv->phy->addr, priv->phy->drv->name, priv->phy->phy_id); ++ ++ /* Stop Advertising 1000BASE Capability if interface is not RGMII */ ++ if ((priv->phy_mode == PHY_INTERFACE_MODE_MII) || ++ (priv->phy_mode == PHY_INTERFACE_MODE_RMII)) { ++ priv->phy->advertising &= ~(SUPPORTED_1000baseT_Half | ++ SUPPORTED_1000baseT_Full); ++ ++ /* Internal FE phy's reg BMSR bit8 is wrong, make the kernel ++ * believe it has the 1000base Capability, so fix it here ++ */ ++ if (priv->phy->phy_id == HISILICON_PHY_ID_FESTAV200) ++ priv->phy->supported &= ~(ADVERTISED_1000baseT_Full | ++ ADVERTISED_1000baseT_Half); ++ } ++ ++ higmac_set_flow_ctrl_args(priv); ++ higmac_set_flow_ctrl_params(priv); ++ priv->phy->supported |= SUPPORTED_Pause; ++ if (priv->flow_ctrl) ++ priv->phy->advertising |= SUPPORTED_Pause; ++ ++ if (priv->autoeee) ++ init_autoeee(priv); ++ ++ ret = higmac_request_irqs(pdev, priv); ++ if (ret) ++ goto out_phy_disconnect; ++ ++ higmac_init_napi(priv); ++ spin_lock_init(&priv->rxlock); ++ spin_lock_init(&priv->txlock); ++ spin_lock_init(&priv->pmtlock); ++ ++ /* init netdevice */ ++ ndev->irq = priv->irq[0]; ++ ndev->watchdog_timeo = 3 * HZ; ++ ndev->netdev_ops = &hieth_netdev_ops; ++ ndev->ethtool_ops = &hieth_ethtools_ops; ++ ++ if (priv->has_rxhash_cap) ++ ndev->hw_features |= NETIF_F_RXHASH; ++ if (priv->has_rss_cap) ++ ndev->hw_features |= NETIF_F_NTUPLE; ++ if (priv->tso_supported) { ++ ndev->hw_features |= NETIF_F_SG | ++ NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_UFO; ++ } ++#if defined(CONFIG_HIGMAC_RXCSUM) ++ ndev->hw_features |= NETIF_F_RXCSUM; ++ higmac_enable_rxcsum_drop(priv, true); ++#endif ++ ++ ndev->features |= ndev->hw_features; ++ ndev->features |= NETIF_F_HIGHDMA | NETIF_F_GSO; ++ ndev->vlan_features |= ndev->features; ++ ++ init_timer(&priv->monitor); ++ priv->monitor.function = higmac_monitor_func; ++ priv->monitor.data = (unsigned long)ndev; ++ priv->monitor.expires = jiffies + HIGMAC_MONITOR_TIMER; ++ ++ device_set_wakeup_capable(priv->dev, 1); ++ /* TODO: when we can let phy powerdown? ++ * In some mode, we don't want phy powerdown, ++ * so I set wakeup enable all the time ++ */ ++ device_set_wakeup_enable(priv->dev, 1); ++ ++ priv->wol_enable = false; ++ ++ priv->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); ++ ++ /* init hw desc queue */ ++ ret = higmac_init_hw_desc_queue(priv); ++ if (ret) ++ goto _error_hw_desc_queue; ++ ++ if (priv->tso_supported) { ++ ret = higmac_init_sg_desc_queue(priv); ++ if (ret) ++ goto _error_sg_desc_queue; ++ } ++ ++ /* register netdevice */ ++ ret = register_netdev(priv->netdev); ++ if (ret) { ++ pr_err("register_ndev failed!"); ++ goto _error_sg_desc_queue; ++ } ++ ++ /* reset queue here to make BQL only reset once. ++ * if we put netdev_reset_queue() in higmac_net_open(), ++ * the BQL will be reset when ifconfig eth0 down and up, ++ * but the tx ring is not cleared before. ++ * As a result, the NAPI poll will call netdev_completed_queue() ++ * and BQL throw a bug. ++ */ ++ netdev_reset_queue(ndev); ++ ++ clk_disable_unprepare(priv->clk); ++ if (priv->macif_clk) ++ clk_disable_unprepare(priv->macif_clk); ++ ++ pr_info("ETH: %s, phy_addr=%d\n", ++ phy_modes(priv->phy_mode), priv->phy->addr); ++ ++ return ret; ++ ++_error_sg_desc_queue: ++ if (priv->tso_supported) ++ higmac_destroy_sg_desc_queue(priv); ++ ++_error_hw_desc_queue: ++ higmac_destroy_hw_desc_queue(priv); ++ higmac_destroy_napi(priv); ++out_phy_disconnect: ++ phy_disconnect(priv->phy); ++out_phy_node: ++ of_node_put(priv->phy_node); ++out_macif_clk_disable: ++ if (priv->macif_clk) ++ clk_disable_unprepare(priv->macif_clk); ++out_clk_disable: ++ clk_disable_unprepare(priv->clk); ++out_free_netdev: ++ free_netdev(ndev); ++ ++ return ret; ++} ++ ++static int higmac_dev_remove(struct platform_device *pdev) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct higmac_netdev_local *priv = netdev_priv(ndev); ++ ++ /* TODO: stop the gmac and free all resource */ ++ del_timer_sync(&priv->monitor); ++ higmac_destroy_napi(priv); ++ ++ unregister_netdev(ndev); ++ ++ higmac_reclaim_rx_tx_resource(priv); ++ higmac_free_rx_skb(priv); ++ higmac_free_tx_skb(priv); ++ ++ if (priv->tso_supported) ++ higmac_destroy_sg_desc_queue(priv); ++ higmac_destroy_hw_desc_queue(priv); ++ ++ phy_disconnect(priv->phy); ++ of_node_put(priv->phy_node); ++ ++ free_netdev(ndev); ++ ++ phy_unregister_fixups(); ++ ++ return 0; ++} ++ ++#include "pm.c" ++#ifdef CONFIG_PM ++ ++static void higmac_disable_irq(struct higmac_netdev_local *priv) ++{ ++ int i; ++ ++ for (i = 0; i < priv->num_rxqs; i++) ++ disable_irq(priv->irq[i]); ++} ++ ++static void higmac_enable_irq(struct higmac_netdev_local *priv) ++{ ++ int i; ++ ++ for (i = 0; i < priv->num_rxqs; i++) ++ enable_irq(priv->irq[i]); ++} ++ ++int higmac_dev_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct higmac_netdev_local *priv = netdev_priv(ndev); ++ ++ higmac_disable_irq(priv); ++ /* If support Wake on LAN, we should not disconnect phy ++ * because it will call phy_suspend to power down phy. ++ */ ++ if (!priv->wol_enable) ++ phy_disconnect(priv->phy); ++ del_timer_sync(&priv->monitor); ++ /* If suspend when netif is not up, the napi_disable will run into ++ * dead loop and dpm_drv_timeout will give warning. ++ */ ++ if (netif_running(ndev)) ++ higmac_disable_napi(priv); ++ netif_device_detach(ndev); ++ ++ netif_carrier_off(ndev); ++ ++ /* If netdev is down, MAC clock is disabled. ++ * So if we want to reclaim MAC rx and tx resource, ++ * we must first enable MAC clock and then disable it. ++ */ ++ if (!(ndev->flags & IFF_UP)) ++ clk_prepare_enable(priv->clk); ++ ++ higmac_reclaim_rx_tx_resource(priv); ++ ++ if (!(ndev->flags & IFF_UP)) ++ clk_disable_unprepare(priv->clk); ++ ++ pmt_enter(priv); ++ ++ if (!priv->wol_enable) { /* if no WOL, then poweroff */ ++ /* pr_info("power off gmac.\n"); */ ++ /* no need to call genphy_resume() in resume, ++ * because we reset everything ++ */ ++ genphy_suspend(priv->phy); /* power down phy */ ++ msleep(20); ++ higmac_hw_all_clk_disable(priv); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(higmac_dev_suspend); ++ ++int higmac_dev_resume(struct platform_device *pdev) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct higmac_netdev_local *priv = netdev_priv(ndev); ++ int ret = 0; ++ ++ /* If we support Wake on LAN, we doesn't call clk_disable. ++ * But when we resume, the uboot may off mac clock and reset phy ++ * by re-write the mac CRG register. ++ * So we first call clk_disable, and then clk_enable. ++ */ ++ if (priv->wol_enable) ++ higmac_hw_all_clk_disable(priv); ++ ++ higmac_hw_all_clk_enable(priv); ++ /* internal FE_PHY: enable clk and reset */ ++ higmac_hw_phy_reset(priv); ++ ++ /* If netdev is down, MAC clock is disabled. ++ * So if we want to restart MAC and re-initialize it, ++ * we must first enable MAC clock and then disable it. ++ */ ++ if (!(ndev->flags & IFF_UP)) ++ clk_prepare_enable(priv->clk); ++ ++ /* power on gmac */ ++ higmac_restart(priv); ++ ++ /* If support WoL, we didn't disconnect phy. ++ * But when we resume, we reset PHY, so we want to ++ * call phy_connect to make phy_fixup excuted. ++ * This is important for internal PHY fix. ++ */ ++ if (priv->wol_enable) ++ phy_disconnect(priv->phy); ++ ++ ret = phy_connect_direct(ndev, priv->phy, higmac_adjust_link, ++ priv->phy_mode); ++ if (ret) ++ return ret; ++ ++ /* If we suspend and resume when net device is down, ++ * some operations are unnecessary. ++ */ ++ if (ndev->flags & IFF_UP) { ++ priv->monitor.expires = jiffies + HIGMAC_MONITOR_TIMER; ++ mod_timer(&priv->monitor, priv->monitor.expires); ++ priv->old_link = 0; ++ priv->old_speed = SPEED_UNKNOWN; ++ priv->old_duplex = DUPLEX_UNKNOWN; ++ } ++ if (netif_running(ndev)) ++ higmac_enable_napi(priv); ++ netif_device_attach(ndev); ++ if (ndev->flags & IFF_UP) ++ phy_start(priv->phy); ++ higmac_enable_irq(priv); ++ ++ pmt_exit(priv); ++ ++ if (!(ndev->flags & IFF_UP)) ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++EXPORT_SYMBOL(higmac_dev_resume); ++#else ++#define higmac_dev_suspend NULL ++#define higmac_dev_resume NULL ++#endif ++ ++static const struct of_device_id higmac_of_match[] = { ++ {.compatible = "hisilicon,higmac",}, ++ {.compatible = "hisilicon,higmac-v1",}, ++ {.compatible = "hisilicon,higmac-v2",}, ++ {.compatible = "hisilicon,higmac-v3",}, ++ {.compatible = "hisilicon,higmac-v4",}, ++ {.compatible = "hisilicon,higmac-v5",}, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, higmac_of_match); ++ ++static struct platform_driver higmac_dev_driver = { ++ .probe = higmac_dev_probe, ++ .remove = higmac_dev_remove, ++ .suspend = higmac_dev_suspend, ++ .resume = higmac_dev_resume, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = HIGMAC_DRIVER_NAME, ++ .of_match_table = higmac_of_match, ++ }, ++}; ++ ++#include "proc-dev.c" ++ ++static int __init higmac_init(void) ++{ ++ int ret = 0; ++ ++ ret = platform_driver_register(&higmac_dev_driver); ++ if (ret) ++ return ret; ++ ++ higmac_proc_create(); ++ ++ return ret; ++} ++ ++static void __exit higmac_exit(void) ++{ ++ platform_driver_unregister(&higmac_dev_driver); ++ ++ higmac_proc_destroy(); ++} ++ ++module_init(higmac_init); ++module_exit(higmac_exit); ++ ++MODULE_AUTHOR("ZMJUN"); ++MODULE_DESCRIPTION("Hisilicon double GMAC driver, base on driver higmacv200 by CHH"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/net/ethernet/hisilicon/higmac/higmac.h b/drivers/net/ethernet/hisilicon/higmac/higmac.h +new file mode 100644 +index 0000000..df0295f +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/higmac.h +@@ -0,0 +1,615 @@ ++#ifndef __HIGMAC_H__ ++#define __HIGMAC_H__ ++ ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/netdevice.h> ++#include <linux/list.h> ++#include <linux/phy.h> ++#include <linux/io.h> ++#include <linux/interrupt.h> ++ ++#define STATION_ADDR_LOW 0x0000 ++#define STATION_ADDR_HIGH 0x0004 ++#define MAC_DUPLEX_HALF_CTRL 0x0008 ++ ++#define PORT_MODE 0x0040 ++ ++#define PORT_EN 0x0044 ++#define BITS_TX_EN BIT(2) ++#define BITS_RX_EN BIT(1) ++ ++#define FC_TX_TIMER 0x001C ++ ++#define PAUSE_THR 0x0038 ++ ++#define PAUSE_EN 0x0048 ++#define BIT_RX_FDFC BIT(0) ++#define BIT_TX_FDFC BIT(1) ++ ++#define RX_PAUSE_EN 0x02A4 ++#define BIT_RX_FQ_PAUSE_EN BIT(0) ++#define BIT_RX_BQ_PAUSE_EN BIT(1) ++ ++#define CRF_TX_PAUSE 0x0340 ++ ++#define BITS_Q_PAUSE_TH_OFFSET 16 ++#define BITS_Q_PAUSE_TH_MASK 0xFFFF ++ ++#define REC_FILT_CONTROL 0x0064 ++#define BIT_CRC_ERR_PASS BIT(5) ++#define BIT_PAUSE_FRM_PASS BIT(4) ++#define BIT_VLAN_DROP_EN BIT(3) ++#define BIT_BC_DROP_EN BIT(2) ++#define BIT_MC_MATCH_EN BIT(1) ++#define BIT_UC_MATCH_EN BIT(0) ++ ++#define PORT_MC_ADDR_LOW 0x0068 ++#define PORT_MC_ADDR_HIGH 0x006C ++#define MAC_CLEAR 0x0070 ++#define BIT_TX_SOFT_RESET BIT(0) ++ ++#define MODE_CHANGE_EN 0x01b4 ++#define BIT_MODE_CHANGE_EN BIT(0) ++ ++#define COL_SLOT_TIME 0x01c0 ++ ++#define CRF_MIN_PACKET 0x0210 ++#define BIT_OFFSET_TX_MIN_LEN 8 ++#define BIT_MASK_TX_MIN_LEN GENMASK(13, 8) ++ ++#define CONTROL_WORD 0x0214 ++#define CONTROL_WORD_CONFIG 0x640 ++ ++#define TSO_COE_CTRL 0x02e8 ++#define BIT_COE_IPHDR_DROP BIT(4) ++#define BIT_COE_PAYLOAD_DROP BIT(5) ++#define BIT_COE_IPV6_UDP_ZERO_DROP BIT(6) ++#define COE_ERR_DROP (BIT_COE_IPHDR_DROP | \ ++ BIT_COE_PAYLOAD_DROP | \ ++ BIT_COE_IPV6_UDP_ZERO_DROP) ++ ++#define RX_FQ_START_ADDR 0x0500 ++#define RX_FQ_DEPTH 0x0504 ++#define RX_FQ_WR_ADDR 0x0508 ++#define BITS_RX_FQ_WR_ADDR MK_BITS(0, 21) ++#define RX_FQ_RD_ADDR 0x050c ++#define BITS_RX_FQ_RD_ADDR MK_BITS(0, 21) ++#define RX_FQ_VLDDESC_CNT 0x0510 ++#define BITS_RX_FQ_VLDDESC_CNT MK_BITS(0, 16) ++#define RX_FQ_ALEMPTY_TH 0x0514 ++#define BITS_RX_FQ_ALEMPTY_TH MK_BITS(0, 16) ++#define RX_FQ_REG_EN 0x0518 ++#define BITS_RX_FQ_START_ADDR_EN BIT(2) ++#define BITS_RX_FQ_DEPTH_EN BIT(1) ++#define BITS_RX_FQ_RD_ADDR_EN MK_BITS(0, 1) ++#define RX_FQ_ALFULL_TH 0x051c ++#define BITS_RX_FQ_ALFULL_TH MK_BITS(0, 16) ++ ++#define RX_BQ_START_ADDR 0x0520 ++#define RX_BQ_DEPTH 0x0524 ++#define RX_BQ_WR_ADDR 0x0528 ++#define RX_BQ_RD_ADDR 0x052c ++#define RX_BQ_FREE_DESC_CNT 0x0530 ++#define BITS_RX_BQ_FREE_DESC_CNT MK_BITS(0, 16) ++#define RX_BQ_ALEMPTY_TH 0x0534 ++#define BITS_RX_BQ_ALEMPTY_TH MK_BITS(0, 16) ++#define RX_BQ_REG_EN 0x0538 ++#define BITS_RX_BQ_START_ADDR_EN BIT(2) ++#define BITS_RX_BQ_DEPTH_EN BIT(1) ++#define BITS_RX_BQ_WR_ADDR_EN MK_BITS(0, 1) ++#define RX_BQ_ALFULL_TH 0x053c ++#define BITS_RX_BQ_ALFULL_TH MK_BITS(0, 16) ++ ++#define TX_BQ_START_ADDR 0x0580 ++#define TX_BQ_DEPTH 0x0584 ++#define TX_BQ_WR_ADDR 0x0588 ++#define BITS_TX_BQ_WR_ADDR MK_BITS(0, 21) ++#define TX_BQ_RD_ADDR 0x058c ++#define BITS_TX_BQ_RD_ADDR MK_BITS(0, 21) ++#define TX_BQ_VLDDESC_CNT 0x0590 ++#define BITS_TX_BQ_VLDDESC_CNT MK_BITS(0, 16) ++#define TX_BQ_ALEMPTY_TH 0x0594 ++#define BITS_TX_BQ_ALEMPTY_TH MK_BITS(0, 16) ++#define TX_BQ_REG_EN 0x0598 ++#define BITS_TX_BQ_START_ADDR_EN BIT(2) ++#define BITS_TX_BQ_DEPTH_EN BIT(1) ++#define BITS_TX_BQ_RD_ADDR_EN MK_BITS(0, 1) ++#define TX_BQ_ALFULL_TH 0x059c ++#define BITS_TX_BQ_ALFULL_TH MK_BITS(0, 16) ++ ++#define TX_RQ_START_ADDR 0x05a0 ++#define TX_RQ_DEPTH 0x05a4 ++#define TX_RQ_WR_ADDR 0x05a8 ++#define BITS_TX_RQ_WR_ADDR MK_BITS(0, 21) ++#define TX_RQ_RD_ADDR 0x05ac ++#define BITS_TX_RQ_RD_ADDR MK_BITS(0, 21) ++#define TX_RQ_FREE_DESC_CNT 0x05b0 ++#define BITS_TX_RQ_FREE_DESC_CNT MK_BITS(0, 16) ++#define TX_RQ_ALEMPTY_TH 0x05b4 ++#define BITS_TX_RQ_ALEMPTY_TH MK_BITS(0, 16) ++#define TX_RQ_REG_EN 0x05b8 ++#define BITS_TX_RQ_START_ADDR_EN BIT(2) ++#define BITS_TX_RQ_DEPTH_EN BIT(1) ++#define BITS_TX_RQ_WR_ADDR_EN MK_BITS(0, 1) ++#define TX_RQ_ALFULL_TH 0x05bc ++#define BITS_TX_RQ_ALFULL_TH MK_BITS(0, 16) ++ ++#define RAW_PMU_INT 0x05c0 ++#define ENA_PMU_INT 0x05c4 ++ ++#define DESC_WR_RD_ENA 0x05CC ++ ++#define IN_QUEUE_TH 0x05d8 ++#define BITS_OFFSET_TX_RQ_IN_TH 16 ++ ++#define RX_BQ_IN_TIMEOUT_TH 0x05E0 ++ ++#define TX_RQ_IN_TIMEOUT_TH 0x05e4 ++ ++#define STOP_CMD 0x05e8 ++#define BITS_TX_STOP_EN BIT(1) ++#define BITS_RX_STOP_EN BIT(0) ++#define STOP_RX_TX (BITS_TX_STOP_EN | BITS_RX_STOP_EN) ++ ++#define HW_CAP_EN 0x0c00 ++#define BIT_RSS_CAP BIT(0) ++#define BIT_RXHASH_CAP BIT(1) ++#define RSS_HASH_KEY 0x0c04 ++#define RSS_HASH_CONFIG 0x0c08 ++#define TCPV4_L3_HASH_EN BIT(0) ++#define TCPV4_L4_HASH_EN BIT(1) ++#define TCPV4_VLAN_HASH_EN BIT(2) ++#define UDPV4_L3_HASH_EN BIT(4) ++#define UDPV4_L4_HASH_EN BIT(5) ++#define UDPV4_VLAN_HASH_EN BIT(6) ++#define IPV4_L3_HASH_EN BIT(8) ++#define IPV4_VLAN_HASH_EN BIT(9) ++#define TCPV6_L3_HASH_EN BIT(12) ++#define TCPV6_L4_HASH_EN BIT(13) ++#define TCPV6_VLAN_HASH_EN BIT(14) ++#define UDPV6_L3_HASH_EN BIT(16) ++#define UDPV6_L4_HASH_EN BIT(17) ++#define UDPV6_VLAN_HASH_EN BIT(18) ++#define IPV6_L3_HASH_EN BIT(20) ++#define IPV6_VLAN_HASH_EN BIT(21) ++#define DEF_HASH_CFG 0x377377 ++ ++#define RSS_IND_TBL 0x0c0c ++#define BIT_IND_TBL_READY BIT(13) ++#define BIT_IND_TLB_WR BIT(12) ++#define RSS_RAW_PMU_INT 0x0c10 ++#define RSS_QUEUE1_START_ADDR 0x0c20 ++#define RX_BQ_START_ADDR_QUEUE(i) (RSS_QUEUE1_START_ADDR + \ ++ ((i) - 1) * 0x10) ++#define RSS_QUEUE1_DEPTH 0x0c24 ++#define RX_BQ_WR_ADDR_QUEUE1 0x0c28 ++#define RX_BQ_RD_ADDR_QUEUE1 0x0c2c ++#define RSS_QUEUE1_ENA_INT 0x0c90 ++#define RSS_ENA_INT_QUEUE(i) (RSS_QUEUE1_ENA_INT + ((i) - 1) * 0x4) ++#define RX_BQ_DEPTH_QUEUE(i) (RSS_QUEUE1_DEPTH + ((i) - 1) * 0x10) ++#define RX_BQ_WR_ADDR_QUEUE(i) ((i) ? (RX_BQ_WR_ADDR_QUEUE1 + \ ++ ((i) - 1) * 0x10) : RX_BQ_WR_ADDR) ++#define RX_BQ_RD_ADDR_QUEUE(i) ((i) ? (RX_BQ_RD_ADDR_QUEUE1 + \ ++ ((i) - 1) * 0x10) : RX_BQ_RD_ADDR) ++ ++#define DEF_INT_MASK_QUEUE(i) (0x3 << (2 * ((i) - 1))) ++ ++/* AXI burst and outstanding config */ ++#define BURST_OUTSTANDING_REG 0x3014 ++#define BURST4_OUTSTANDING1 0x81ff ++#define BURST_OUTSTANDING_OFFSET 16 ++ ++#define GMAC_SPEED_1000 0x05 ++#define GMAC_SPEED_100 0x01 ++#define GMAC_SPEED_10 0x00 ++ ++enum higmac_tx_err { ++ ERR_NONE = 0, ++ ERR_DESC_CFG = (1 << 0), ++ ERR_DATA_LEN = (1 << 1), ++ ERR_DESC_NFRAG_NUM = (1 << 2), ++ ERR_DESC_IP_HDR_LEN = (1 << 3), ++ ERR_DESC_PROT_HDR_LEN = (1 << 4), ++ ERR_DESC_MTU = (1 << 5), ++ ERR_LINK_SGPKT_LEN = (1 << 8), ++ ERR_LINK_TSOPKT_LINEAR = (1 << 9), ++ ERR_LINK_NFRAG_LEN = (1 << 10), ++ ERR_LINK_TOTAL_LEN = (1 << 11), ++ ERR_HDR_TCP_BCMC = (1 << 12), ++ ERR_HDR_UDP_BC = (1 << 13), ++ ERR_HDR_VLAN_IP_TYPE = (1 << 14), ++ ERR_HDR_IP_TYPE = (1 << 15), ++ ERR_HDR_IP_VERSION = (1 << 16), ++ ERR_HDR_IP_HDR_LEN = (1 << 17), ++ ERR_HDR_IP_TOTAL_LEN = (1 << 18), ++ ERR_HDR_IPV6_TTL_PROT = (1 << 19), ++ ERR_HDR_IPV4_OFFSET = (1 << 20), ++ ERR_HDR_IPV4_TTL_PROT = (1 << 21), ++ ERR_HDR_UDP_LEN = (1 << 22), ++ ERR_HDR_TCP_LEN = (1 << 23), ++ ERR_DESC = (ERR_DESC_CFG | ERR_DATA_LEN | ++ ERR_DESC_NFRAG_NUM | ERR_DESC_IP_HDR_LEN | ++ ERR_DESC_PROT_HDR_LEN | ERR_DESC_MTU), ++ ERR_LINK = (ERR_LINK_SGPKT_LEN | ERR_LINK_TSOPKT_LINEAR | ++ ERR_LINK_NFRAG_LEN | ERR_LINK_TOTAL_LEN), ++ ERR_HDR = (ERR_HDR_TCP_BCMC | ERR_HDR_UDP_BC | ++ ERR_HDR_VLAN_IP_TYPE | ERR_HDR_IP_TYPE | ++ ERR_HDR_IP_VERSION | ERR_HDR_IP_HDR_LEN | ++ ERR_HDR_IP_TOTAL_LEN | ERR_HDR_IPV6_TTL_PROT | ++ ERR_HDR_IPV4_OFFSET | ERR_HDR_IPV4_TTL_PROT | ++ ERR_HDR_UDP_LEN | ERR_HDR_TCP_LEN), ++ ERR_ALL = (ERR_DESC | ERR_LINK | ERR_HDR), ++}; ++ ++#define HIGMAC_DRIVER_NAME "hi_gmac_v200" ++ ++#define HIGMAC_MAC_CLK_NAME "higmac_clk" ++#define HIGMAC_MACIF_CLK_NAME "macif_clk" ++ ++#define HIGMAC_PORT_RST_NAME "port_reset" ++#define HIGMAC_MACIF_RST_NAME "macif_reset" ++#define HIGMAC_PHY_RST_NAME "phy_reset" ++ ++#define HIGMAC_TSO_DEBUG ++ ++#include "tso.h" ++ ++#if defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) || \ ++ defined(CONFIG_ARCH_HI3516AV200) ++#ifdef readl ++#undef readl ++#undef readl_relaxed ++#undef writel ++#undef writel_relaxed ++#define readl hi_readl ++#define readl_relaxed hi_readl_relaxed ++#define writel hi_writel ++#define writel_relaxed hi_writel_relaxed ++#endif /* readl */ ++#endif /* defined(CONFIG_ARCH_HI3519) || defined(CONFIG_HI3519V101) */ ++ ++#define HIGMAC_IOSIZE (0x1000) ++#define HIGMAC_OFFSET (HIGMAC_IOSIZE) ++ ++#define RX_BQ_IN_INT BIT(17) ++#define TX_RQ_IN_INT BIT(19) ++#define RX_BQ_IN_TIMEOUT_INT BIT(28) ++#define TX_RQ_IN_TIMEOUT_INT BIT(29) ++ ++#define DEF_INT_MASK (RX_BQ_IN_INT | RX_BQ_IN_TIMEOUT_INT | \ ++ TX_RQ_IN_INT | TX_RQ_IN_TIMEOUT_INT) ++ ++/* write or read descriptor need memory barrier */ ++#define HIGMAC_SYNC_BARRIER() do { isb(); smp_mb(); } while (0) ++ ++#define HISILICON_PHY_ID_FESTAV200 (0x20669823) ++#define PHY_ID_KSZ8051MNL (0x00221550) ++#define PHY_ID_KSZ8081RNB (0x00221560) ++#define DEFAULT_PHY_MASK (0xfffffff0) ++#define REALTEK_PHY_ID_8211E (0x001cc915) ++#define REALTEK_PHY_MASK (0x001fffff) ++ ++enum { ++ GMAC_PORT0, ++ GMAC_PORT1, ++ GMAC_MAX_PORT, ++}; ++ ++enum { ++ MEM_GMAC_IOBASE, ++ MEM_MACIF_IOBASE, ++ MEM_FWD_IOBASE, ++ MEM_CTRL_IOBASE, ++}; ++ ++#define HIGMAC_LINKED BIT(0) ++#define HIGMAC_DUP_FULL BIT(1) ++#define HIGMAC_SPD_10M BIT(2) ++#define HIGMAC_SPD_100M BIT(3) ++#define HIGMAC_SPD_1000M BIT(4) ++/* Flow Control defines */ ++#define FLOW_OFF 0 ++#define FLOW_RX 1 ++#define FLOW_TX 2 ++#define FLOW_AUTO (FLOW_TX | FLOW_RX) ++ ++#define FC_ACTIVE_MIN 1 ++#define FC_ACTIVE_DEFAULT 16 ++#define FC_ACTIVE_MAX 127 ++#define FC_DEACTIVE_MIN 1 ++#define FC_DEACTIVE_DEFAULT 32 ++#define FC_DEACTIVE_MAX 127 ++ ++#define FC_PAUSE_TIME_DEFAULT 0xFFFF ++#define FC_PAUSE_INTERVAL_DEFAULT 0xFFFF ++#define FC_PAUSE_TIME_MAX 0xFFFF ++ ++#define RX_BQ_INT_THRESHOLD 0x40 /* TODO: */ ++#define TX_RQ_INT_THRESHOLD 0x20 /* TODO: */ ++ ++#define HIGMAC_MONITOR_TIMER (msecs_to_jiffies(200)) ++ ++#define HIETH_MAX_FRAME_SIZE (1600 + 128) ++#define SKB_SIZE (HIETH_MAX_FRAME_SIZE) ++ ++#define DESC_VLD_FREE 0 ++#define DESC_VLD_BUSY 1 ++ ++#define DESC_FL_FIRST 2 ++#define DESC_FL_MID 0 ++#define DESC_FL_LAST 1 ++#define DESC_FL_FULL 3 ++ ++#if defined(CONFIG_HIGMAC_DESC_4WORD) ++#define DESC_WORD_SHIFT 2 ++#else ++#define DESC_WORD_SHIFT 3 ++#endif ++#define DESC_BYTE_SHIFT (DESC_WORD_SHIFT + 2) ++#define DESC_WORD_CNT (1 << DESC_WORD_SHIFT) ++#define DESC_SIZE (1 << DESC_BYTE_SHIFT) ++ ++#define RX_DESC_NUM 1024 ++#define TX_DESC_NUM 1024 ++ ++/* DMA descriptor ring helpers */ ++#define dma_ring_incr(n, s) (((n) + 1) & ((s) - 1)) ++#define dma_cnt(n) ((n) >> DESC_BYTE_SHIFT) ++#define dma_byte(n) ((n) << DESC_BYTE_SHIFT) ++ ++#define RSS_HASH_KEY_SIZE 4 ++#define RSS_INDIRECTION_TABLE_SIZE 128 ++#define RSS_NUM_RXQS 4 ++ ++#define HW_CAP_TSO BIT(0) ++#define HW_CAP_RXCSUM BIT(1) ++#define HW_CAP_CCI BIT(2) ++#define HAS_CAP_TSO(hw_cap) ((hw_cap) & HW_CAP_TSO) ++#define HAS_CAP_RXCSUM(hw_cap) ((hw_cap) & HW_CAP_RXCSUM) ++#define HAS_CAP_CCI(hw_cap) ((hw_cap) & HW_CAP_CCI) ++ ++#if defined(CONFIG_HIGMAC_DESC_4WORD) ++struct higmac_desc { ++ unsigned int data_buff_addr; ++ ++ unsigned int buffer_len:11; ++#if defined(CONFIG_HIGMAC_RXCSUM) ++ unsigned int reserve2:1; ++ unsigned int payload_csum_err:1; ++ unsigned int header_csum_err:1; ++ unsigned int payload_csum_done:1; ++ unsigned int header_csum_done:1; ++#else ++ unsigned int reserve2:5; ++#endif ++ unsigned int data_len:11; ++ unsigned int reserve1:2; ++ unsigned int fl:2; ++ unsigned int descvid:1; ++ ++ unsigned int rxhash; ++ unsigned int reserve3:8; ++ unsigned int l3_hash:1; ++ unsigned int has_hash:1; ++ unsigned int skb_id:14; ++ unsigned int reserve31:8; ++}; ++ ++struct higmac_tso_desc { ++ unsigned int data_buff_addr; ++ union { ++ struct { ++ unsigned int prot_hdr_len:4; ++ unsigned int ip_hdr_len:4; ++ unsigned int prot_type:1; ++ unsigned int ip_ver:1; ++ unsigned int vlan_flag:1; ++ unsigned int nfrags_num:5; ++ unsigned int data_len:11; ++ unsigned int reservel:1; ++ unsigned int tso_flag:1; ++ unsigned int coe_flag:1; ++ unsigned int sg_flag:1; ++ unsigned int hw_own:1; ++ } tx; ++ unsigned int val; ++ } desc1; ++ unsigned int reserve_desc2; ++ unsigned int tx_err; ++}; ++#else ++struct higmac_desc { ++ unsigned int data_buff_addr; ++ ++ unsigned int buffer_len:11; ++#if defined(CONFIG_HIGMAC_RXCSUM) ++ unsigned int reserve2:1; ++ unsigned int payload_csum_err:1; ++ unsigned int header_csum_err:1; ++ unsigned int payload_csum_done:1; ++#else ++ unsigned int reserve2:5; ++#endif ++ unsigned int data_len:11; ++ unsigned int reserve1:2; ++ unsigned int fl:2; ++ unsigned int descvid:1; ++ ++ unsigned int rxhash; ++ unsigned int reserve3:8; ++ unsigned int l3_hash:1; ++ unsigned int has_hash:1; ++ unsigned int skb_id:14; ++ unsigned int reserve31:8; ++ ++ unsigned int reserve4; ++ unsigned int reserve5; ++ unsigned int reserve6; ++ unsigned int reserve7; ++}; ++ ++struct higmac_tso_desc { ++ unsigned int data_buff_addr; ++ union { ++ struct { ++ unsigned int prot_hdr_len:4; ++ unsigned int ip_hdr_len:4; ++ unsigned int prot_type:1; ++ unsigned int ip_ver:1; ++ unsigned int vlan_flag:1; ++ unsigned int nfrags_num:5; ++ unsigned int data_len:11; ++ unsigned int reservel:1; ++ unsigned int tso_flag:1; ++ unsigned int coe_flag:1; ++ unsigned int sg_flag:1; ++ unsigned int hw_own:1; ++ } tx; ++ unsigned int val; ++ } desc1; ++ unsigned int reserve_desc2; ++ unsigned int reserve3; ++ ++ unsigned int tx_err; ++ unsigned int reserve5; ++ unsigned int reserve6; ++ unsigned int reserve7; ++}; ++#endif ++ ++#define SKB_MAGIC ((struct sk_buff *)0x5a) ++ ++struct higmac_napi { ++ struct napi_struct napi; ++ struct higmac_netdev_local *ndev_priv; ++ int rxq_id; ++}; ++ ++struct higmac_rss_info { ++ u32 hash_cfg; ++ u32 ind_tbl_size; ++ u8 ind_tbl[RSS_INDIRECTION_TABLE_SIZE]; ++ u8 key[RSS_HASH_KEY_SIZE]; ++}; ++ ++#define QUEUE_NUMS (4) ++struct higmac_netdev_local { ++#define HIGMAC_SG_DESC_ADD (64U) ++ struct sg_desc *dma_sg_desc ____cacheline_aligned; ++ dma_addr_t dma_sg_phy; ++ unsigned int sg_head; ++ unsigned int sg_tail; ++ unsigned int sg_count; ++ ++ void __iomem *gmac_iobase; ++ void __iomem *macif_base; ++ int index; /* 0 -- mac0, 1 -- mac1 */ ++ ++ u32 hw_cap; ++ bool tso_supported; ++ bool has_rxhash_cap; ++ bool has_rss_cap; ++ int num_rxqs; ++ struct higmac_napi q_napi[RSS_NUM_RXQS]; ++ int irq[RSS_NUM_RXQS]; ++ struct higmac_rss_info rss_info; ++ ++ struct reset_control *port_rst; ++ struct reset_control *macif_rst; ++ struct reset_control *phy_rst; ++ ++ struct { ++ struct higmac_desc *desc; ++ dma_addr_t phys_addr; ++ int *sg_desc_offset; ++ ++ /* how many desc in the desc pool */ ++ unsigned int count; ++ struct sk_buff **skb; ++ ++ /* sizeof(desc) * count */ ++ unsigned int size; ++ } pool[QUEUE_NUMS + RSS_NUM_RXQS - 1]; ++#define rx_fq pool[0] ++#define rx_bq pool[1] ++#define tx_bq pool[2] ++#define tx_rq pool[3] ++ ++ struct sk_buff **tx_skb; ++ struct sk_buff **rx_skb; ++ ++ struct device *dev; ++ struct net_device *netdev; ++ struct clk *clk; ++ struct clk *macif_clk; ++ ++ struct higmac_adapter *adapter; ++ ++ struct timer_list monitor; ++ ++ char phy_name[MII_BUS_ID_SIZE]; ++ struct phy_device *phy; ++ struct device_node *phy_node; ++ phy_interface_t phy_mode; ++ bool autoeee; ++ bool internal_phy; ++ int (*eee_init)(struct phy_device *phy_dev); ++ ++ unsigned int flow_ctrl; ++ unsigned int pause; ++ unsigned int pause_interval; ++ unsigned int flow_ctrl_active_threshold; ++ unsigned int flow_ctrl_deactive_threshold; ++ ++ int old_link; ++ int old_speed; ++ int old_duplex; ++ ++ /* receive packet lock */ ++ spinlock_t rxlock; ++ /* transmit packet lock */ ++ spinlock_t txlock; ++ /* power management lock */ ++ spinlock_t pmtlock; ++ ++ int dev_state; /* INIT/OPEN/CLOSE */ ++ char pm_state; ++ bool wol_enable; ++ u32 msg_enable; ++#define INIT (0) /* power off gmac */ ++#define OPEN (1) /* power on gmac */ ++#define CLOSE (2) /* power off gmac */ ++}; ++ ++enum tso_version { ++ VER_NO_TSO = 0x0, ++ VER_BYTE_SPLICE = 0x1, ++ VER_SG_COE = 0x2, ++ VER_TSO = 0x3, ++}; ++ ++#ifdef HIGMAC_TSO_DEBUG ++#define MAX_RECORD (100) ++struct send_pkt_info { ++ struct higmac_tso_desc desc; ++ int status; ++}; ++#endif ++ ++int higmac_tx_avail(struct higmac_netdev_local *ld); ++ ++/* board related func */ ++void higmac_mac_core_reset(struct higmac_netdev_local *priv); ++void higmac_hw_internal_phy_reset(struct higmac_netdev_local *priv); ++void higmac_hw_external_phy_reset(struct higmac_netdev_local *priv); ++void higmac_internal_phy_clk_disable(struct higmac_netdev_local *priv); ++void higmac_internal_phy_clk_enable(struct higmac_netdev_local *priv); ++void higmac_hw_all_clk_disable(struct higmac_netdev_local *priv); ++void higmac_hw_all_clk_enable(struct higmac_netdev_local *priv); ++ ++/* board independent func */ ++void higmac_hw_phy_reset(struct higmac_netdev_local *priv); ++ ++void pmt_reg_restore(struct higmac_netdev_local *ld); ++#endif +diff --git a/drivers/net/ethernet/hisilicon/higmac/pm.c b/drivers/net/ethernet/hisilicon/higmac/pm.c +new file mode 100644 +index 0000000..7620abe +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/pm.c +@@ -0,0 +1,359 @@ ++#include <linux/crc16.h> ++#include "higmac.h" ++ ++#define N (31) ++#define FILTERS (4) ++struct pm_config { ++ unsigned char index; /* bit0--eth0 bit1--eth1 */ ++ unsigned char uc_pkts_enable; ++ unsigned char magic_pkts_enable; ++ unsigned char wakeup_pkts_enable; ++ struct { ++ unsigned int mask_bytes:N; ++ unsigned int reserved:1; /* userspace ignore this bit */ ++ unsigned char offset; /* >= 12 */ ++ unsigned char value[N]; /* byte string */ ++ unsigned char valid; /* valid filter */ ++ } filter[FILTERS]; ++}; ++ ++struct pm_reg_config { ++ unsigned int pmt_ctrl; ++ unsigned int pmt_mask0; ++ unsigned int pmt_mask1; ++ unsigned int pmt_mask2; ++ unsigned int pmt_mask3; ++ unsigned int pmt_cmd; ++ unsigned int pmt_offset; ++ unsigned int pmt_crc1_0; ++ unsigned int pmt_crc3_2; ++}; ++ ++struct pm_reg_config pm_reg_config_backup; ++ ++#define PMT_CTRL 0xa00 ++#define PMT_MASK0 0xa04 ++#define PMT_MASK1 0xa08 ++#define PMT_MASK2 0xa0c ++#define PMT_MASK3 0xa10 ++#define PMT_CMD 0xa14 ++#define PMT_OFFSET 0xa18 ++#define PMT_CRC1_0 0xa1c ++#define PMT_CRC3_2 0xa20 ++#define MASK_INVALID_BIT BIT(31) ++ ++static void init_crc_table(void); ++static unsigned short compute_crc(char *message, int nbytes); ++static unsigned short calculate_crc16(char *buf, unsigned int mask) ++{ ++ char data[N]; ++ int i, len = 0; ++ ++ memset(data, 0, sizeof(data)); ++ ++ for (i = 0; i < N; i++) { ++ if (mask & 0x1) ++ data[len++] = buf[i]; ++ ++ mask >>= 1; ++ } ++ ++ return compute_crc(data, len); ++} ++ ++/* use this func in config pm func */ ++void _pmt_reg_backup(struct higmac_netdev_local *ld) ++{ ++ pm_reg_config_backup.pmt_ctrl = readl(ld->gmac_iobase + PMT_CTRL); ++ pm_reg_config_backup.pmt_mask0 = readl(ld->gmac_iobase + PMT_MASK0); ++ pm_reg_config_backup.pmt_mask1 = readl(ld->gmac_iobase + PMT_MASK1); ++ pm_reg_config_backup.pmt_mask2 = readl(ld->gmac_iobase + PMT_MASK2); ++ pm_reg_config_backup.pmt_mask3 = readl(ld->gmac_iobase + PMT_MASK3); ++ pm_reg_config_backup.pmt_cmd = readl(ld->gmac_iobase + PMT_CMD); ++ pm_reg_config_backup.pmt_offset = readl(ld->gmac_iobase + PMT_OFFSET); ++ pm_reg_config_backup.pmt_crc1_0 = readl(ld->gmac_iobase + PMT_CRC1_0); ++ pm_reg_config_backup.pmt_crc3_2 = readl(ld->gmac_iobase + PMT_CRC3_2); ++} ++ ++#define PM_SET (1) ++#define PM_CLEAR (0) ++ ++int pmt_config_gmac(struct pm_config *config, struct higmac_netdev_local *ld) ++{ ++ unsigned int v = 0, cmd = 0, offset = 0; ++ unsigned short crc[FILTERS] = { 0 }; ++ unsigned long flags; ++ int reg_mask = 0; ++ int i; ++ ++ if (!ld) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&ld->pmtlock, flags); ++ if (config->wakeup_pkts_enable) { ++ /* disable wakeup_pkts_enable before reconfig? */ ++ v = readl(ld->gmac_iobase + PMT_CTRL); ++ v &= ~BIT(2); ++ writel(v, ld->gmac_iobase + PMT_CTRL); /* any side effect? */ ++ } else { ++ goto config_ctrl; ++ } ++ ++/* filter.valid mask.valid mask_bytes effect ++ * 0 * * no use the filter ++ * 1 0 * all pkts can wake-up(non-exist) ++ * 1 1 0 all pkts can wake-up ++ * 1 1 !0 normal filter ++ */ ++ /* setup filter */ ++ for (i = 0; i < FILTERS; i++) { ++ if (config->filter[i].valid) { ++ if (config->filter[i].offset < 12) ++ continue; ++ /* offset and valid bit */ ++ offset |= config->filter[i].offset << (i * 8); ++ cmd |= BIT(i * 8); /* valid bit */ ++ /* mask */ ++ reg_mask = PMT_MASK0 + (i * 4); ++ ++ /* for logic, mask valid bit(bit31) must set to 0, ++ * 0 is enable ++ */ ++ v = config->filter[i].mask_bytes; ++ v &= ~BIT(31); ++ writel(v, ld->gmac_iobase + reg_mask); ++ ++ /* crc */ ++ crc[i] = calculate_crc16(config->filter[i].value, v); ++ if (i <= 1) { /* for filter0 and filter 1 */ ++ v = readl(ld->gmac_iobase + PMT_CRC1_0); ++ v &= ~(0xFFFF << (16 * i)); ++ v |= crc[i] << (16 * i); ++ writel(v, ld->gmac_iobase + PMT_CRC1_0); ++ } else { /* filter2 and filter3 */ ++ v = readl(ld->gmac_iobase + PMT_CRC3_2); ++ v &= ~(0xFFFF << (16 * (i - 2))); ++ v |= crc[i] << (16 * (i - 2)); ++ writel(v, ld->gmac_iobase + PMT_CRC3_2); ++ } ++ } ++ } ++ ++ if (cmd) { ++ writel(offset, ld->gmac_iobase + PMT_OFFSET); ++ writel(cmd, ld->gmac_iobase + PMT_CMD); ++ } ++ ++config_ctrl: ++ v = 0; ++ if (config->uc_pkts_enable) ++ v |= BIT(9); /* uc pkts wakeup */ ++ if (config->wakeup_pkts_enable) ++ v |= BIT(2); /* use filter framework */ ++ if (config->magic_pkts_enable) ++ v |= BIT(1); /* magic pkts wakeup */ ++ ++ v |= 3 << 5; /* clear irq status */ ++ writel(v, ld->gmac_iobase + PMT_CTRL); ++ ++ _pmt_reg_backup(ld); ++ ++ spin_unlock_irqrestore(&ld->pmtlock, flags); ++ ++ return 0; ++} ++ ++/* pmt_config will overwrite pre-config */ ++int pmt_config(struct net_device *ndev, struct pm_config *config) ++{ ++ static int init; ++ int ret = -EINVAL; ++ struct higmac_netdev_local *priv = netdev_priv(ndev); ++ ++ if (!init) ++ init_crc_table(); ++ ++ ret = pmt_config_gmac(config, priv); ++ if (ret) ++ return ret; ++ ++ priv->pm_state = PM_SET; ++ priv->wol_enable = true; ++ device_set_wakeup_enable(priv->dev, 1); ++ ++ return ret; ++} ++ ++inline bool pmt_enter(struct higmac_netdev_local *ld) ++{ ++ int pm = false; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ld->pmtlock, flags); ++ if (ld->pm_state == PM_SET) { ++ int v; ++ ++ v = readl(ld->gmac_iobase + PMT_CTRL); ++ v |= BIT(0); /* enter power down */ ++ v |= BIT(3); /* enable wakeup irq */ ++ v |= 3 << 5; /* clear irq status */ ++ writel(v, ld->gmac_iobase + PMT_CTRL); ++ ++ ld->pm_state = PM_CLEAR; ++ pm = true; ++ } ++ spin_unlock_irqrestore(&ld->pmtlock, flags); ++ return pm; ++} ++ ++inline void pmt_exit(struct higmac_netdev_local *ld) ++{ ++ int v; ++ unsigned long flags; ++ ++ /* logic auto exit power down mode */ ++ spin_lock_irqsave(&ld->pmtlock, flags); ++ ++ v = readl(ld->gmac_iobase + PMT_CTRL); ++ v &= ~BIT(0); /* enter power down */ ++ v &= ~BIT(3); /* enable wakeup irq */ ++ ++ v |= 3 << 5; /* clear irq status */ ++ writel(v, ld->gmac_iobase + PMT_CTRL); ++ ++ spin_unlock_irqrestore(&ld->pmtlock, flags); ++ ++ ld->wol_enable = false; ++ /* device_set_wakeup_enable(ld->dev, 0); */ ++} ++ ++void pmt_reg_restore(struct higmac_netdev_local *ld) ++{ ++ unsigned int v; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ld->pmtlock, flags); ++ v = pm_reg_config_backup.pmt_mask0; ++ writel(v, ld->gmac_iobase + PMT_MASK0); ++ ++ v = pm_reg_config_backup.pmt_mask1; ++ writel(v, ld->gmac_iobase + PMT_MASK1); ++ ++ v = pm_reg_config_backup.pmt_mask2; ++ writel(v, ld->gmac_iobase + PMT_MASK2); ++ ++ v = pm_reg_config_backup.pmt_mask3; ++ writel(v, ld->gmac_iobase + PMT_MASK3); ++ ++ v = pm_reg_config_backup.pmt_cmd; ++ writel(v, ld->gmac_iobase + PMT_CMD); ++ ++ v = pm_reg_config_backup.pmt_offset; ++ writel(v, ld->gmac_iobase + PMT_OFFSET); ++ ++ v = pm_reg_config_backup.pmt_crc1_0; ++ writel(v, ld->gmac_iobase + PMT_CRC1_0); ++ ++ v = pm_reg_config_backup.pmt_crc3_2; ++ writel(v, ld->gmac_iobase + PMT_CRC3_2); ++ ++ v = pm_reg_config_backup.pmt_ctrl; ++ writel(v, ld->gmac_iobase + PMT_CTRL); ++ spin_unlock_irqrestore(&ld->pmtlock, flags); ++} ++ ++/* ========the following code copy from Synopsys DWC_gmac_crc_example.c====== */ ++#define CRC16 /* Change it to CRC16 for CRC16 Computation */ ++ ++#if defined(CRC16) ++#define CRC_NAME "CRC-16" ++#define POLYNOMIAL 0x8005 ++#define INITIAL_REMAINDER 0xFFFF ++#define FINAL_XOR_VALUE 0x0000 ++#define REVERSE_DATA ++#undef REVERSE_REMAINDER ++#endif ++ ++#define WIDTH (8 * sizeof(unsigned short)) ++#define TOPBIT BIT(WIDTH - 1) ++ ++#ifdef REVERSE_DATA ++#undef REVERSE_DATA ++#define REVERSE_DATA(X) ((unsigned char)reverse((X), 8)) ++#else ++#undef REVERSE_DATA ++#define REVERSE_DATA(X) (X) ++#endif ++ ++#ifdef REVERSE_REMAINDER ++#undef REVERSE_REMAINDER ++#define REVERSE_REMAINDER(X) ((unsigned short)reverse((X), WIDTH)) ++#else ++#undef REVERSE_REMAINDER ++#define REVERSE_REMAINDER(X) (X) ++#endif ++ ++static unsigned short crc_table[256]; ++ ++/* Reverse the data ++ * Input1: Data to be reversed ++ * Input2: number of bits in the data ++ * Output: The reversed data ++ */ ++static unsigned int reverse(unsigned int data, unsigned char nbits) ++{ ++ unsigned int reversed = 0x00000000; ++ unsigned char bit; ++ ++ /* Reverse the data about the center bit. */ ++ for (bit = 0; bit < nbits; ++bit) { ++ /* If the LSB bit is set, set the reflection of it. */ ++ if (data & 0x01) ++ reversed |= BIT((nbits - 1) - bit); ++ ++ data = (data >> 1); ++ } ++ return reversed; ++} ++ ++/* This Initializes the partial CRC look up table */ ++static void init_crc_table(void) ++{ ++ unsigned short remainder; ++ int dividend; ++ unsigned char bit; ++ ++ /* Compute the remainder of each possible dividend. */ ++ for (dividend = 0; dividend < 256; ++dividend) { ++ /* Start with the dividend followed by zeros. */ ++ remainder = (unsigned short)(dividend << (WIDTH - 8)); ++ ++ /* Perform modulo-2 division, a bit at a time. */ ++ for (bit = 8; bit > 0; --bit) { ++ /* Try to divide the current data bit. */ ++ if (remainder & TOPBIT) ++ remainder = (remainder << 1) ^ POLYNOMIAL; ++ else ++ remainder = (remainder << 1); ++ } ++ ++ /* Store the result into the table. */ ++ crc_table[dividend] = remainder; ++ } ++} ++ ++static unsigned short compute_crc(char *message, int nbytes) ++{ ++ unsigned short remainder = INITIAL_REMAINDER; ++ int byte; ++ unsigned char data; ++ ++ /* Divide the message by the polynomial, a byte at a time. */ ++ for (byte = 0; byte < nbytes; ++byte) { ++ data = REVERSE_DATA(message[byte]) ^ (remainder >> (WIDTH - 8)); ++ remainder = crc_table[data] ^ (remainder << 8); ++ } ++ ++ /* The final remainder is the CRC. */ ++ return (REVERSE_REMAINDER(remainder) ^ FINAL_XOR_VALUE); ++} +diff --git a/drivers/net/ethernet/hisilicon/higmac/proc-dev.c b/drivers/net/ethernet/hisilicon/higmac/proc-dev.c +new file mode 100644 +index 0000000..d522565 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/proc-dev.c +@@ -0,0 +1,111 @@ ++#include "sockioctl.h" ++ ++/* debug code */ ++static int set_suspend(int eth_n) ++{ ++ return 0; ++} ++ ++/* debug code */ ++static int set_resume(int eth_n) ++{ ++ /* higmac_dev_driver.resume(&higmac_platform_device); */ ++ return 0; ++} ++ ++static int hw_states_read(struct seq_file *m, void *v) ++{ ++ return 0; ++} ++ ++static struct proc_dir_entry *higmac_proc_root; ++ ++#define proc_open(name) \ ++static int proc_open_##name(struct inode *inode, struct file *file) \ ++{ \ ++ return single_open(file, name, PDE_DATA(inode)); \ ++} \ ++ ++proc_open(hw_states_read); ++ ++static struct proc_file { ++ char *name; ++ const struct file_operations ops; ++ ++} proc_file[] = { ++ { ++ .name = "hw_stats", ++ .ops = { ++ .open = proc_open_hw_states_read, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ }, ++ } ++}; ++ ++/* /proc/higmac/ ++ * |---hw_stats ++ * |---skb_pools ++ */ ++void higmac_proc_create(void) ++{ ++ int i; ++ ++ higmac_proc_root = proc_mkdir("higmac", NULL); ++ if (!higmac_proc_root) ++ return; ++ ++ for (i = 0; i < ARRAY_SIZE(proc_file); i++) { ++ struct proc_dir_entry *entry; ++ ++ entry = proc_create(proc_file[i].name, 0000, higmac_proc_root, ++ &proc_file[i].ops); ++ if (!entry) ++ pr_err("failed to create %s\n", proc_file[i].name); ++ } ++} ++ ++void higmac_proc_destroy(void) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(proc_file); i++) ++ remove_proc_entry(proc_file[i].name, higmac_proc_root); ++ ++ remove_proc_entry("higmac", NULL); ++} ++ ++int higmac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) ++{ ++ struct higmac_netdev_local *priv = netdev_priv(ndev); ++ struct pm_config pm_config; ++ int val = 0; ++ ++ switch (cmd) { ++ case SIOCSETPM: ++ if (copy_from_user(&pm_config, rq->ifr_data, sizeof(pm_config))) ++ return -EFAULT; ++ return pmt_config(ndev, &pm_config); ++ ++ case SIOCSETSUSPEND: ++ if (copy_from_user(&val, rq->ifr_data, sizeof(val))) ++ return -EFAULT; ++ return set_suspend(val); ++ ++ case SIOCSETRESUME: ++ if (copy_from_user(&val, rq->ifr_data, sizeof(val))) ++ return -EFAULT; ++ return set_resume(val); ++ ++ default: ++ if (!netif_running(ndev)) ++ return -EINVAL; ++ ++ if (!priv->phy) ++ return -EINVAL; ++ ++ return phy_mii_ioctl(priv->phy, rq, cmd); ++ } ++ return 0; ++} +diff --git a/drivers/net/ethernet/hisilicon/higmac/sockioctl.h b/drivers/net/ethernet/hisilicon/higmac/sockioctl.h +new file mode 100644 +index 0000000..571c71a +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/sockioctl.h +@@ -0,0 +1,12 @@ ++#ifndef _SOCKIOCTL_H_ ++#define _SOCKIOCTL_H_ ++ ++#include <linux/sockios.h> ++ ++#define SIOCSETPM (SIOCDEVPRIVATE + 4) /* set pmt wake up config */ ++#define SIOCSETSUSPEND (SIOCDEVPRIVATE + 5) /* call dev->suspend, debug */ ++#define SIOCSETRESUME (SIOCDEVPRIVATE + 6) /* call dev->resume, debug */ ++ ++int higmac_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd); ++ ++#endif +diff --git a/drivers/net/ethernet/hisilicon/higmac/tso.h b/drivers/net/ethernet/hisilicon/higmac/tso.h +new file mode 100644 +index 0000000..6416eef +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/tso.h +@@ -0,0 +1,53 @@ ++#ifndef __HIETH_TSO_H ++#define __HIETH_TSO_H ++ ++#define SG_FLAG BIT(30) ++#define COE_FLAG BIT(29) ++#define TSO_FLAG BIT(28) ++#define VLAN_FLAG BIT(10) ++#define IPV6_FLAG BIT(9) ++#define UDP_FLAG BIT(8) ++ ++#define PKT_IPV6_HDR_LEN 10 ++#define PKT_UDP_HDR_LEN 2 ++#define WORD_TO_BYTE 4 ++enum { ++ PKT_NORMAL, ++ PKT_SG ++}; ++ ++enum { ++ PKT_IPV4, ++ PKT_IPV6 ++}; ++ ++enum { ++ PKT_TCP, ++ PKT_UDP ++}; ++ ++struct frags_info { ++ /* Word(2*i+2) */ ++ u32 addr; ++ /* Word(2*i+3) */ ++ u32 size:16; ++ u32 reserved:16; ++}; ++ ++struct sg_desc { ++ /* Word0 */ ++ u32 total_len:17; ++ u32 reserv:15; ++ /* Word1 */ ++ u32 ipv6_id; ++ /* Word2 */ ++ u32 linear_addr; ++ /* Word3 */ ++ u32 linear_len:16; ++ u32 reserv3:16; ++ /* MAX_SKB_FRAGS = 17 */ ++ struct frags_info frags[18]; ++ /* struct frags_info frags[MAX_SKB_FRAGS]; */ ++}; ++ ++#endif +diff --git a/drivers/net/ethernet/hisilicon/higmac/util.h b/drivers/net/ethernet/hisilicon/higmac/util.h +new file mode 100644 +index 0000000..f08cbf6 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/higmac/util.h +@@ -0,0 +1,29 @@ ++#ifndef __HIGMAC_UTIL_H__ ++#define __HIGMAC_UTIL_H__ ++ ++#define HIGMAC_TRACE_LEVEL 10 ++ ++#define higmac_trace(level, msg...) do { \ ++ if ((level) >= HIGMAC_TRACE_LEVEL) { \ ++ pr_info("higmac_trace:%s:%d: ", __FILE__, __LINE__); \ ++ printk(msg); \ ++ printk("\n"); \ ++ } \ ++} while (0) ++ ++#define higmac_error(args...) do { \ ++ pr_err("higmac:%s:%d: ", __FILE__, __LINE__); \ ++ printk(args); \ ++ printk("\n"); \ ++} while (0) ++ ++#define higmac_assert(cond) do { \ ++ if (!(cond)) \ ++ pr_alert("Assert:higmac:%s:%d\n", \ ++ __FILE__, \ ++ __LINE__);\ ++} while (0) ++ ++#define MK_BITS(shift, nbits) ((((shift) & 0x1F) << 16) | ((nbits) & 0x3F)) ++ ++#endif +diff --git a/drivers/net/ethernet/hisilicon/hisi_femac.c b/drivers/net/ethernet/hisilicon/hisi_femac.c +new file mode 100644 +index 0000000..2257832 +--- /dev/null ++++ b/drivers/net/ethernet/hisilicon/hisi_femac.c +@@ -0,0 +1,1705 @@ ++/* ++ * Hisilicon Fast Ethernet MAC Driver ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/circ_buf.h> ++#include <linux/clk.h> ++#include <linux/etherdevice.h> ++#include <linux/if_ether.h> ++#include <linux/if_vlan.h> ++#include <linux/ip.h> ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <linux/of_mdio.h> ++#include <linux/of_net.h> ++#include <linux/platform_device.h> ++#include <linux/reset.h> ++#include <linux/tcp.h> ++#include <net/ipv6.h> ++#include <net/protocol.h> ++ ++/* MAC control register list */ ++#define MAC_PORTSEL 0x0200 ++#define MAC_PORTSEL_STAT_CPU BIT(0) ++#define MAC_PORTSEL_RMII BIT(1) ++#define MAC_PORTSET 0x0208 ++#define MAC_PORTSET_DUPLEX_FULL BIT(0) ++#define MAC_PORTSET_LINKED BIT(1) ++#define MAC_PORTSET_SPEED_100M BIT(2) ++#define MAC_SET 0x0210 ++#define MAX_FRAME_SIZE 1600 ++#define MAX_FRAME_SIZE_MASK GENMASK(10, 0) ++#define BIT_PAUSE_EN BIT(18) ++#define RX_COALESCE_SET 0x0340 ++#define RX_COALESCED_FRAME_OFFSET 24 ++#define RX_COALESCED_FRAMES 8 ++#define RX_COALESCED_TIMER 0x74 ++#define QLEN_SET 0x0344 ++#define RX_DEPTH_OFFSET 8 ++#define MAX_HW_FIFO_DEPTH 64 ++#define HW_TX_FIFO_DEPTH 12 ++#define HW_RX_FIFO_DEPTH (MAX_HW_FIFO_DEPTH - HW_TX_FIFO_DEPTH) ++#define FC_LEVEL 0x0348 ++#define BITS_FC_ACTIVE_THR_OFFSET 8 ++#define FC_DEACTIVE_THR_MASK GENMASK(5, 0) ++#define FC_ACTIVE_THR_MASK GENMASK(13, 8) ++#define BIT_FC_EN BIT(14) ++#define IQFRM_DES 0x0354 ++#define RX_FRAME_LEN_MASK GENMASK(11, 0) ++#define BITS_PAYLOAD_ERR_OFFSET 28 ++#define BITS_PAYLOAD_ERR_MASK 0x1 ++#define BITS_HEADER_ERR_OFFSET 29 ++#define BITS_HEADER_ERR_MASK 0x1 ++#define BITS_PAYLOAD_DONE_OFFSET 30 ++#define BITS_PAYLOAD_DONE_MASK 0x1 ++#define BITS_HEADER_DONE_OFFSET 31 ++#define BITS_HEADER_DONE_MASK 0x1 ++#define IQ_ADDR 0x0358 ++#define EQ_ADDR 0x0360 ++#define EQFRM_LEN 0x0364 ++#define ADDRQ_STAT 0x036C ++#define TX_CNT_INUSE_MASK GENMASK(5, 0) ++#define BIT_TX_READY BIT(24) ++#define BIT_RX_READY BIT(25) ++#define RX_COE_CTRL 0x0380 ++#define BIT_COE_IPV6_UDP_ZERO_DROP BIT(13) ++#define BIT_COE_PAYLOAD_DROP BIT(14) ++#define BIT_COE_IPHDR_DROP BIT(15) ++#define COE_ERR_DROP (BIT_COE_IPHDR_DROP | \ ++ BIT_COE_PAYLOAD_DROP | \ ++ BIT_COE_IPV6_UDP_ZERO_DROP) ++#define TSO_DBG_EN 0x03A4 ++#define BITS_TSO_DBG_EN BIT(31) ++#define TSO_DBG_STATE 0x03A8 ++#define TSO_DBG_ADDR 0x03AC ++#define TSO_DBG_TX_INFO 0x03B0 ++#define TSO_DBG_TX_ERR 0x03B4 ++/* global control register list */ ++#define GLB_HOSTMAC_L32 0x0000 ++#define GLB_HOSTMAC_H16 0x0004 ++#define GLB_SOFT_RESET 0x0008 ++#define SOFT_RESET_ALL BIT(0) ++#define GLB_FWCTRL 0x0010 ++#define FWCTRL_VLAN_ENABLE BIT(0) ++#define FWCTRL_FW2CPU_ENA BIT(5) ++#define FWCTRL_FWALL2CPU BIT(7) ++#define GLB_MACTCTRL 0x0014 ++#define MACTCTRL_UNI2CPU BIT(1) ++#define MACTCTRL_MULTI2CPU BIT(3) ++#define MACTCTRL_BROAD2CPU BIT(5) ++#define MACTCTRL_MACT_ENA BIT(7) ++#define GLB_IRQ_STAT 0x0030 ++#define GLB_IRQ_ENA 0x0034 ++#define IRQ_ENA_PORT0_MASK GENMASK(7, 0) ++#define IRQ_ENA_PORT0 BIT(18) ++#define IRQ_ENA_ALL BIT(19) ++#define GLB_IRQ_RAW 0x0038 ++#define IRQ_INT_RX_RDY BIT(0) ++#define IRQ_INT_TX_PER_PACKET BIT(1) ++#define IRQ_INT_TX_FIFO_EMPTY BIT(6) ++#define IRQ_INT_MULTI_RXRDY BIT(7) ++#define INT_TX_ERR BIT(8) ++#define DEF_INT_MASK (IRQ_INT_MULTI_RXRDY | \ ++ IRQ_INT_TX_PER_PACKET | \ ++ IRQ_INT_TX_FIFO_EMPTY) ++#define GLB_MAC_L32_BASE 0x0100 ++#define GLB_MAC_H16_BASE 0x0104 ++#define MACFLT_HI16_MASK GENMASK(15, 0) ++#define BIT_MACFLT_ENA BIT(17) ++#define BIT_MACFLT_FW2CPU BIT(21) ++#define GLB_MAC_H16(reg) (GLB_MAC_H16_BASE + ((reg) * 0x8)) ++#define GLB_MAC_L32(reg) (GLB_MAC_L32_BASE + ((reg) * 0x8)) ++#define MAX_MAC_FILTER_NUM 8 ++#define MAX_UNICAST_ADDRESSES 2 ++#define MAX_MULTICAST_ADDRESSES (MAX_MAC_FILTER_NUM - \ ++ MAX_UNICAST_ADDRESSES) ++/* software tx and rx queue number, should be power of 2 */ ++#define TXQ_NUM 64 ++#define RXQ_NUM 128 ++#define FEMAC_POLL_WEIGHT 16 ++#define HW_CAP_TSO BIT(0) ++#define HW_CAP_RXCSUM BIT(1) ++#define HAS_TSO_CAP(hw_cap) ((hw_cap) & HW_CAP_TSO) ++#define HAS_RXCSUM_CAP(hw_cap) ((hw_cap) & HW_CAP_RXCSUM) ++#define RXBUF_ADDR_ALIGN_SIZE 64UL ++/* UDP header len is 2 word */ ++#define UDP_HDR_LEN 2 ++/* IPv6 header len is 10 word */ ++#define IPV6_HDR_LEN 10 ++#define WORD_TO_BYTE 4 ++ ++#define BIT_OFFSET_NFRAGS_NUM 11 ++#define BIT_OFFSET_PROT_HEADER_LEN 16 ++#define BIT_OFFSET_IP_HEADER_LEN 20 ++#define BIT_FLAG_SG BIT(26) ++#define BIT_FLAG_TXCSUM BIT(27) ++#define BIT_FLAG_UDP BIT(28) ++#define BIT_FLAG_IPV6 BIT(29) ++#define BIT_FLAG_VLAN BIT(30) ++#define BIT_FLAG_TSO BIT(31) ++ ++#define PHY_RESET_DELAYS_PROPERTY "hisilicon,phy-reset-delays-us" ++ ++/* The threshold for activing tx flow ctrl. ++ * When the left amount of receive queue descriptors is below this threshold, ++ * hardware will send pause frame immediately. ++ * We advise this value is set between 1 and 10. ++ * Too bigger is not a good choice. ++ * This value must be smaller than tx flow ctrl deactive threshold. ++ */ ++#define TX_FLOW_CTRL_ACTIVE_THRESHOLD 3 ++/* The threshold for deactiving tx flow ctrl. ++ * When the left amount of receive queue descriptors is ++ * above or equal with this threshold, ++ * hardware will exit flow control state. ++ * We advise this value is set between 1 and 10. ++ * Too bigger is not a good choice. ++ * This value must be larger than tx flow ctrl active threshold. ++ */ ++#define TX_FLOW_CTRL_DEACTIVE_THRESHOLD 5 ++#define FC_ACTIVE_MIN 1 ++#define FC_ACTIVE_DEFAULT 3 ++#define FC_ACTIVE_MAX 31 ++#define FC_DEACTIVE_MIN 1 ++#define FC_DEACTIVE_DEFAULT 5 ++#define FC_DEACTIVE_MAX 31 ++ ++enum phy_reset_delays { ++ PRE_DELAY, ++ PULSE, ++ POST_DELAY, ++ DELAYS_NUM, ++}; ++ ++struct hisi_femac_queue { ++ struct sk_buff **skb; ++ dma_addr_t *dma_phys; ++ int num; ++ unsigned int head; ++ unsigned int tail; ++}; ++ ++struct hisi_femac_tx_desc_ring { ++ struct tx_desc *desc; ++ dma_addr_t dma_phys; ++}; ++ ++struct hisi_femac_priv { ++ void __iomem *port_base; ++ void __iomem *glb_base; ++ struct clk *clk; ++ struct reset_control *mac_rst; ++ struct reset_control *phy_rst; ++ u32 phy_reset_delays[DELAYS_NUM]; ++ u32 link_status; ++ ++ struct device *dev; ++ struct net_device *ndev; ++ ++ u32 hw_cap; ++ struct hisi_femac_queue txq; ++ struct hisi_femac_queue rxq; ++ struct hisi_femac_tx_desc_ring tx_ring; ++ u32 tx_fifo_used_cnt; ++ struct napi_struct napi; ++ ++ /* 802.3x flow control */ ++ bool tx_pause_en; ++ u32 tx_pause_active_thresh; ++ u32 tx_pause_deactive_thresh; ++}; ++ ++struct frags_info { ++ /* Word(2*i+2) */ ++ u32 addr; ++ /* Word(2*i+3) */ ++ u32 size:16; ++ u32 reserved:16; ++}; ++ ++struct tx_desc { ++ /* Word0 */ ++ u32 total_len:17; ++ u32 reserv:15; ++ /* Word1 */ ++ u32 ipv6_id; ++ /* Word2 */ ++ u32 linear_addr; ++ /* Word3 */ ++ u32 linear_len:16; ++ u32 reserv3:16; ++ /* MAX_SKB_FRAGS = 17 */ ++ struct frags_info frags[30]; ++ /* struct frags_info frags[MAX_SKB_FRAGS]; */ ++}; ++ ++static void hisi_femac_irq_enable(struct hisi_femac_priv *priv, u32 irqs) ++{ ++ u32 val; ++ ++ val = readl(priv->glb_base + GLB_IRQ_ENA); ++ writel(val | irqs, priv->glb_base + GLB_IRQ_ENA); ++} ++ ++static void hisi_femac_irq_disable(struct hisi_femac_priv *priv, u32 irqs) ++{ ++ u32 val; ++ ++ val = readl(priv->glb_base + GLB_IRQ_ENA); ++ writel(val & (~irqs), priv->glb_base + GLB_IRQ_ENA); ++} ++ ++static void hisi_femac_set_flow_ctrl(struct hisi_femac_priv *priv) ++{ ++ unsigned int pause_en; ++ unsigned int tx_flow_ctrl; ++ ++ tx_flow_ctrl = readl(priv->port_base + FC_LEVEL); ++ tx_flow_ctrl &= ~FC_DEACTIVE_THR_MASK; ++ tx_flow_ctrl |= priv->tx_pause_deactive_thresh; ++ tx_flow_ctrl &= ~FC_ACTIVE_THR_MASK; ++ tx_flow_ctrl |= priv->tx_pause_active_thresh << ++ BITS_FC_ACTIVE_THR_OFFSET; ++ ++ pause_en = readl(priv->port_base + MAC_SET); ++ ++ if (priv->tx_pause_en) { ++ tx_flow_ctrl |= BIT_FC_EN; ++ pause_en |= BIT_PAUSE_EN; ++ } else { ++ tx_flow_ctrl &= ~BIT_FC_EN; ++ pause_en &= ~BIT_PAUSE_EN; ++ } ++ ++ writel(tx_flow_ctrl, priv->port_base + FC_LEVEL); ++ ++ writel(pause_en, priv->port_base + MAC_SET); ++} ++ ++static void hisi_femac_tx_sg_dma_unmap(struct hisi_femac_priv *priv, ++ struct sk_buff *skb, unsigned int pos) ++{ ++ struct tx_desc *desc_cur; ++ dma_addr_t addr; ++ u32 len; ++ int i; ++ ++ desc_cur = priv->tx_ring.desc + pos; ++ ++ addr = desc_cur->linear_addr; ++ len = desc_cur->linear_len; ++ dma_unmap_single(priv->dev, addr, len, DMA_TO_DEVICE); ++ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ addr = desc_cur->frags[i].addr; ++ len = desc_cur->frags[i].size; ++ dma_unmap_page(priv->dev, addr, len, DMA_TO_DEVICE); ++ } ++} ++ ++static void hisi_femac_tx_dma_unmap(struct hisi_femac_priv *priv, ++ struct sk_buff *skb, unsigned int pos) ++{ ++ if (!(skb_is_gso(skb) || skb_shinfo(skb)->nr_frags)) { ++ dma_addr_t dma_addr; ++ ++ dma_addr = priv->txq.dma_phys[pos]; ++ dma_unmap_single(priv->dev, dma_addr, skb->len, DMA_TO_DEVICE); ++ } else { ++ hisi_femac_tx_sg_dma_unmap(priv, skb, pos); ++ } ++} ++ ++static void hisi_femac_xmit_reclaim(struct net_device *dev) ++{ ++ struct sk_buff *skb; ++ struct hisi_femac_priv *priv = netdev_priv(dev); ++ struct hisi_femac_queue *txq = &priv->txq; ++ unsigned int bytes_compl = 0, pkts_compl = 0; ++ u32 val; ++ ++ netif_tx_lock(dev); ++ ++ val = readl(priv->port_base + ADDRQ_STAT) & TX_CNT_INUSE_MASK; ++ while (val < priv->tx_fifo_used_cnt) { ++ skb = txq->skb[txq->tail]; ++ if (unlikely(!skb)) { ++ netdev_err(dev, "xmitq_cnt_inuse=%d, tx_fifo_used=%d\n", ++ val, priv->tx_fifo_used_cnt); ++ break; ++ } ++ hisi_femac_tx_dma_unmap(priv, skb, txq->tail); ++ pkts_compl++; ++ bytes_compl += skb->len; ++ dev_kfree_skb_any(skb); ++ ++ priv->tx_fifo_used_cnt--; ++ ++ val = readl(priv->port_base + ADDRQ_STAT) & TX_CNT_INUSE_MASK; ++ txq->skb[txq->tail] = NULL; ++ txq->tail = (txq->tail + 1) % txq->num; ++ } ++ ++ netdev_completed_queue(dev, pkts_compl, bytes_compl); ++ ++ if (unlikely(netif_queue_stopped(dev)) && pkts_compl) ++ netif_wake_queue(dev); ++ ++ netif_tx_unlock(dev); ++} ++ ++static void hisi_femac_get_tso_err_info(struct hisi_femac_priv *priv) ++{ ++ unsigned int reg_addr, reg_tx_info, reg_tx_err; ++ unsigned int sg_index; ++ struct tx_desc *sg_desc; ++ int *sg_word; ++ int i; ++ ++ reg_addr = readl(priv->port_base + TSO_DBG_ADDR); ++ reg_tx_info = readl(priv->port_base + TSO_DBG_TX_INFO); ++ reg_tx_err = readl(priv->port_base + TSO_DBG_TX_ERR); ++ ++ WARN(1, "tx err=0x%x, tx_info=0x%x, addr=0x%x\n", ++ reg_tx_err, reg_tx_info, reg_addr); ++ ++ sg_index = (reg_addr - priv->tx_ring.dma_phys) / sizeof(struct tx_desc); ++ sg_desc = priv->tx_ring.desc + sg_index; ++ sg_word = (int *)sg_desc; ++ for (i = 0; i < sizeof(struct tx_desc) / sizeof(int); i++) ++ pr_err("%s,%d: sg_desc word[%d]=0x%x\n", ++ __func__, __LINE__, i, sg_word[i]); ++ ++ /* restart MAC to transmit next packet */ ++ hisi_femac_irq_disable(priv, INT_TX_ERR); ++ /* The following is recovery code, ++ * allow netcard transmit packet again. ++ * But now we disable it for error debug. ++ */ ++#if 0 ++ readl(priv->port_base + TSO_DBG_STATE)); ++ hisi_femac_irq_enable(priv, INT_TX_ERR); ++#endif ++} ++ ++static netdev_tx_t hisi_femac_net_xmit(struct sk_buff *skb, ++ struct net_device *dev); ++ ++static netdev_tx_t hisi_femac_sw_gso(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct sk_buff *segs, *curr_skb; ++ netdev_features_t features = dev->features; ++ ++ features &= ~(NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_UFO); ++ segs = skb_gso_segment(skb, features); ++ if (IS_ERR_OR_NULL(segs)) ++ goto drop; ++ ++ do { ++ curr_skb = segs; ++ segs = segs->next; ++ curr_skb->next = NULL; ++ if (hisi_femac_net_xmit(curr_skb, dev)) { ++ dev_kfree_skb(curr_skb); ++ while (segs) { ++ curr_skb = segs; ++ segs = segs->next; ++ curr_skb->next = NULL; ++ dev_kfree_skb_any(curr_skb); ++ } ++ goto drop; ++ } ++ } while (segs); ++ ++ dev_kfree_skb_any(skb); ++ return NETDEV_TX_OK; ++ ++drop: ++ dev_kfree_skb_any(skb); ++ dev->stats.tx_dropped++; ++ return NETDEV_TX_OK; ++} ++ ++static void hisi_femac_do_udp_checksum(struct sk_buff *skb) ++{ ++ int offset; ++ __wsum csum; ++ __sum16 udp_csum; ++ ++ offset = skb_checksum_start_offset(skb); ++ WARN_ON(offset >= skb_headlen(skb)); ++ csum = skb_checksum(skb, offset, skb->len - offset, 0); ++ ++ offset += skb->csum_offset; ++ WARN_ON(offset + sizeof(__sum16) > skb_headlen(skb)); ++ ++ udp_csum = csum_fold(csum); ++ if (udp_csum == 0) ++ udp_csum = CSUM_MANGLED_0; ++ ++ *(__sum16 *)(skb->data + offset) = udp_csum; ++ ++ skb->ip_summed = CHECKSUM_NONE; ++} ++ ++static inline __be16 hisi_femac_get_l3_proto(struct sk_buff *skb) ++{ ++ __be16 l3_proto; ++ ++ l3_proto = skb->protocol; ++ if (skb->protocol == htons(ETH_P_8021Q)) ++ l3_proto = vlan_get_protocol(skb); ++ ++ return l3_proto; ++} ++ ++static inline bool hisi_femac_skb_is_ipv6(struct sk_buff *skb) ++{ ++ return (hisi_femac_get_l3_proto(skb) == htons(ETH_P_IPV6)); ++} ++ ++static int hisi_femac_check_hw_capability_for_ipv6(struct sk_buff *skb) ++{ ++ unsigned int l4_proto = IPPROTO_MAX; ++ ++ l4_proto = ipv6_hdr(skb)->nexthdr; ++ ++ if ((l4_proto != IPPROTO_TCP) && (l4_proto != IPPROTO_UDP)) { ++ /* when IPv6 next header is not tcp or udp, ++ * it means that IPv6 next header is extension header. ++ * Hardware can't deal with this case, ++ * so do checksumming by software or do GSO by software. ++ */ ++ if (skb_is_gso(skb)) ++ return -ENOTSUPP; ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL && ++ skb_checksum_help(skb)) ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int hisi_femac_check_hw_capability(struct sk_buff *skb) ++{ ++ /* if tcp_mtu_probe() use (2 * tp->mss_cache) as probe_size, ++ * the linear data length will be larger than 2048, ++ * the MAC can't handle it, so let the software do it. ++ */ ++ if (skb_is_gso(skb) && (skb_headlen(skb) > 2048)) ++ return -ENOTSUPP; ++ ++ if (hisi_femac_skb_is_ipv6(skb)) ++ return hisi_femac_check_hw_capability_for_ipv6(skb); ++ ++ return 0; ++} ++ ++static u32 hisi_femac_get_pkt_info(struct sk_buff *skb) ++{ ++ __be16 l3_proto; ++ unsigned int l4_proto = IPPROTO_MAX; ++ bool do_txcsum = false; ++ int max_data_len = skb->len - ETH_HLEN; ++ unsigned int max_mss = ETH_DATA_LEN; ++ u32 pkt_info = 0; ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) ++ do_txcsum = true; ++ ++ l3_proto = skb->protocol; ++ if (skb->protocol == htons(ETH_P_8021Q)) { ++ l3_proto = vlan_get_protocol(skb); ++ max_data_len -= VLAN_HLEN; ++ pkt_info |= BIT_FLAG_VLAN; ++ } ++ ++ if (l3_proto == htons(ETH_P_IP)) { ++ struct iphdr *iph = ip_hdr(skb); ++ ++ if ((max_data_len >= GSO_MAX_SIZE) && ++ (ntohs(iph->tot_len) <= (iph->ihl << 2))) ++ iph->tot_len = htons(GSO_MAX_SIZE - 1); ++ ++ max_mss -= iph->ihl * WORD_TO_BYTE; ++ pkt_info |= (iph->ihl << BIT_OFFSET_IP_HEADER_LEN); ++ l4_proto = iph->protocol; ++ } else if (l3_proto == htons(ETH_P_IPV6)) { ++ max_mss -= IPV6_HDR_LEN * WORD_TO_BYTE; ++ pkt_info |= BIT_FLAG_IPV6; ++ pkt_info |= (IPV6_HDR_LEN << BIT_OFFSET_IP_HEADER_LEN); ++ l4_proto = ipv6_hdr(skb)->nexthdr; ++ } else { ++ do_txcsum = false; ++ } ++ ++ if (l4_proto == IPPROTO_TCP) { ++ max_mss -= tcp_hdr(skb)->doff * WORD_TO_BYTE; ++ pkt_info |= (tcp_hdr(skb)->doff << BIT_OFFSET_PROT_HEADER_LEN); ++ } else if (l4_proto == IPPROTO_UDP) { ++ if (l3_proto == htons(ETH_P_IPV6)) ++ max_mss -= sizeof(struct frag_hdr); ++ pkt_info |= (BIT_FLAG_UDP | ++ (UDP_HDR_LEN << BIT_OFFSET_PROT_HEADER_LEN)); ++ } else { ++ do_txcsum = false; ++ } ++ ++ /* Although netcard support UFO feature, it can't deal with ++ * UDP header checksum. ++ * So the driver will do UDP header checksum and netcard will just ++ * fragment the packet. ++ */ ++ if (do_txcsum && skb_is_gso(skb) && (l4_proto == IPPROTO_UDP)) { ++ hisi_femac_do_udp_checksum(skb); ++ do_txcsum = false; ++ } ++ ++ if (do_txcsum) ++ pkt_info |= BIT_FLAG_TXCSUM; ++ ++ if (skb_is_gso(skb)) ++ pkt_info |= (BIT_FLAG_SG | BIT_FLAG_TSO); ++ else if (skb_shinfo(skb)->nr_frags) ++ pkt_info |= BIT_FLAG_SG; ++ ++ pkt_info |= (skb_shinfo(skb)->nr_frags << BIT_OFFSET_NFRAGS_NUM); ++ pkt_info |= (skb_is_gso(skb) ? ++ ((skb_shinfo(skb)->gso_size > max_mss) ? max_mss : ++ skb_shinfo(skb)->gso_size) : (skb->len + ETH_FCS_LEN)); ++ ++ return pkt_info; ++} ++ ++static int hisi_femac_fill_sg_desc(struct hisi_femac_priv *priv, ++ struct sk_buff *skb, unsigned int pos) ++{ ++ struct tx_desc *desc_cur; ++ dma_addr_t addr; ++ int ret; ++ int i; ++ ++ desc_cur = priv->tx_ring.desc + pos; ++ ++ desc_cur->ipv6_id = ntohl(skb_shinfo(skb)->ip6_frag_id); ++ ++ desc_cur->total_len = skb->len; ++ addr = dma_map_single(priv->dev, skb->data, skb_headlen(skb), ++ DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(priv->dev, addr))) ++ return -EINVAL; ++ desc_cur->linear_addr = addr; ++ desc_cur->linear_len = skb_headlen(skb); ++ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ int len = frag->size; ++ ++ addr = skb_frag_dma_map(priv->dev, frag, 0, len, DMA_TO_DEVICE); ++ ret = dma_mapping_error(priv->dev, addr); ++ if (unlikely(ret)) ++ return -EINVAL; ++ desc_cur->frags[i].addr = addr; ++ desc_cur->frags[i].size = len; ++ } ++ ++ return 0; ++} ++ ++static void hisi_femac_adjust_link(struct net_device *dev) ++{ ++ struct hisi_femac_priv *priv = netdev_priv(dev); ++ struct phy_device *phy = dev->phydev; ++ u32 status = 0; ++ ++ if (phy->link) ++ status |= MAC_PORTSET_LINKED; ++ if (phy->duplex == DUPLEX_FULL) ++ status |= MAC_PORTSET_DUPLEX_FULL; ++ if (phy->speed == SPEED_100) ++ status |= MAC_PORTSET_SPEED_100M; ++ ++ if ((status != priv->link_status) && ++ ((status | priv->link_status) & MAC_PORTSET_LINKED)) { ++ writel(status, priv->port_base + MAC_PORTSET); ++ priv->link_status = status; ++ phy_print_status(phy); ++ ++ priv->tx_pause_en = phy->pause; ++ hisi_femac_set_flow_ctrl(priv); ++ } ++} ++ ++static void hisi_femac_rx_refill(struct hisi_femac_priv *priv) ++{ ++ struct hisi_femac_queue *rxq = &priv->rxq; ++ struct sk_buff *skb; ++ u32 pos; ++ u32 len = MAX_FRAME_SIZE; ++ dma_addr_t addr; ++ u32 alloc_rxbuf_align = 0; ++ int reserve_room = 0; ++ ++ pos = rxq->head; ++ while (readl(priv->port_base + ADDRQ_STAT) & BIT_RX_READY) { ++ if (!CIRC_SPACE(pos, rxq->tail, rxq->num)) ++ break; ++ if (unlikely(rxq->skb[pos])) { ++ netdev_err(priv->ndev, "err skb[%d]=%p\n", ++ pos, rxq->skb[pos]); ++ break; ++ } ++ len = MAX_FRAME_SIZE + RXBUF_ADDR_ALIGN_SIZE; ++ skb = netdev_alloc_skb_ip_align(priv->ndev, len); ++ if (unlikely(!skb)) ++ break; ++ ++ alloc_rxbuf_align = ((unsigned long)skb->data - NET_IP_ALIGN) & ++ (RXBUF_ADDR_ALIGN_SIZE - 1); ++ if (alloc_rxbuf_align) { ++ reserve_room = RXBUF_ADDR_ALIGN_SIZE - alloc_rxbuf_align; ++ len -= reserve_room; ++ skb_reserve(skb, reserve_room); ++ } ++ ++ addr = dma_map_single(priv->dev, skb->data, len, ++ DMA_FROM_DEVICE); ++ if (dma_mapping_error(priv->dev, addr)) { ++ dev_kfree_skb_any(skb); ++ break; ++ } ++ rxq->dma_phys[pos] = addr; ++ rxq->skb[pos] = skb; ++ writel(addr, priv->port_base + IQ_ADDR); ++ pos = (pos + 1) % rxq->num; ++ } ++ rxq->head = pos; ++} ++ ++static u32 hisi_femac_rx(struct net_device *dev, int limit) ++{ ++ struct hisi_femac_priv *priv = netdev_priv(dev); ++ struct hisi_femac_queue *rxq = &priv->rxq; ++ struct sk_buff *skb; ++ dma_addr_t addr; ++ u32 rx_pkt_info, pos, len, rx_pkts_num = 0; ++ int hdr_csum_done, hdr_csum_err; ++ int payload_csum_done, payload_csum_err; ++ ++ pos = rxq->tail; ++ while (readl(priv->glb_base + GLB_IRQ_RAW) & IRQ_INT_RX_RDY) { ++ rx_pkt_info = readl(priv->port_base + IQFRM_DES); ++ len = rx_pkt_info & RX_FRAME_LEN_MASK; ++ len -= ETH_FCS_LEN; ++ ++ /* tell hardware we will deal with this packet */ ++ writel(IRQ_INT_RX_RDY, priv->glb_base + GLB_IRQ_RAW); ++ ++ rx_pkts_num++; ++ ++ skb = rxq->skb[pos]; ++ if (unlikely(!skb)) { ++ netdev_err(dev, "rx skb NULL. pos=%d\n", pos); ++ break; ++ } ++ rxq->skb[pos] = NULL; ++ ++ addr = rxq->dma_phys[pos]; ++ dma_unmap_single(priv->dev, addr, MAX_FRAME_SIZE, ++ DMA_FROM_DEVICE); ++ skb_put(skb, len); ++ if (unlikely(skb->len > MAX_FRAME_SIZE)) { ++ netdev_err(dev, "rcv len err, len = %d\n", skb->len); ++ dev->stats.rx_errors++; ++ dev->stats.rx_length_errors++; ++ dev_kfree_skb_any(skb); ++ goto next; ++ } ++ ++ skb->ip_summed = CHECKSUM_NONE; ++ if (dev->features & NETIF_F_RXCSUM) { ++ hdr_csum_done = ++ (rx_pkt_info >> BITS_HEADER_DONE_OFFSET) & ++ BITS_HEADER_DONE_MASK; ++ payload_csum_done = ++ (rx_pkt_info >> BITS_PAYLOAD_DONE_OFFSET) & ++ BITS_PAYLOAD_DONE_MASK; ++ hdr_csum_err = ++ (rx_pkt_info >> BITS_HEADER_ERR_OFFSET) & ++ BITS_HEADER_ERR_MASK; ++ payload_csum_err = ++ (rx_pkt_info >> BITS_PAYLOAD_ERR_OFFSET) & ++ BITS_PAYLOAD_ERR_MASK; ++ ++ if (hdr_csum_done && payload_csum_done) { ++ if (unlikely(hdr_csum_err)) { ++ dev->stats.rx_errors++; ++ dev->stats.rx_crc_errors++; ++ dev_kfree_skb_any(skb); ++ goto next; ++ } else if (!payload_csum_err) { ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ } ++ } ++ } ++ ++ skb->protocol = eth_type_trans(skb, dev); ++ napi_gro_receive(&priv->napi, skb); ++ dev->stats.rx_packets++; ++ dev->stats.rx_bytes += len; ++next: ++ pos = (pos + 1) % rxq->num; ++ if (rx_pkts_num >= limit) ++ break; ++ } ++ rxq->tail = pos; ++ ++ hisi_femac_rx_refill(priv); ++ ++ return rx_pkts_num; ++} ++ ++static int hisi_femac_poll(struct napi_struct *napi, int budget) ++{ ++ struct hisi_femac_priv *priv = container_of(napi, ++ struct hisi_femac_priv, napi); ++ struct net_device *dev = priv->ndev; ++ int work_done = 0, task = budget; ++ u32 ints, num; ++ ++ do { ++ hisi_femac_xmit_reclaim(dev); ++ num = hisi_femac_rx(dev, task); ++ work_done += num; ++ task -= num; ++ if (work_done >= budget) ++ break; ++ ++ ints = readl(priv->glb_base + GLB_IRQ_RAW); ++ writel(ints & DEF_INT_MASK, ++ priv->glb_base + GLB_IRQ_RAW); ++ } while (ints & DEF_INT_MASK); ++ ++ if (work_done < budget) { ++ napi_complete(napi); ++ hisi_femac_irq_enable(priv, DEF_INT_MASK & ++ (~IRQ_INT_TX_PER_PACKET)); ++ } ++ ++ return work_done; ++} ++ ++static irqreturn_t hisi_femac_interrupt(int irq, void *dev_id) ++{ ++ u32 ints; ++ struct net_device *dev = (struct net_device *)dev_id; ++ struct hisi_femac_priv *priv = netdev_priv(dev); ++ ++ ints = readl(priv->glb_base + GLB_IRQ_RAW); ++ ++ if (likely(ints & DEF_INT_MASK)) { ++ writel(ints & DEF_INT_MASK, ++ priv->glb_base + GLB_IRQ_RAW); ++ hisi_femac_irq_disable(priv, DEF_INT_MASK); ++ napi_schedule(&priv->napi); ++ } ++ ++ if (HAS_TSO_CAP(priv->hw_cap) && ++ unlikely(ints & INT_TX_ERR)) ++ hisi_femac_get_tso_err_info(priv); ++ ++ return IRQ_HANDLED; ++} ++ ++static int hisi_femac_init_tx_descriptor_ring(struct hisi_femac_priv *priv) ++{ ++ priv->tx_ring.desc = (struct tx_desc *)dma_zalloc_coherent(priv->dev, ++ TXQ_NUM * sizeof(struct tx_desc), ++ &priv->tx_ring.dma_phys, ++ GFP_KERNEL); ++ if (!priv->tx_ring.desc) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static void hisi_femac_destroy_tx_descriptor_ring(struct hisi_femac_priv *priv) ++{ ++ if (priv->tx_ring.desc) ++ dma_free_coherent(priv->dev, ++ TXQ_NUM * sizeof(struct tx_desc), ++ priv->tx_ring.desc, priv->tx_ring.dma_phys); ++ priv->tx_ring.desc = NULL; ++} ++ ++static int hisi_femac_init_queue(struct device *dev, ++ struct hisi_femac_queue *queue, ++ unsigned int num) ++{ ++ queue->skb = devm_kcalloc(dev, num, sizeof(struct sk_buff *), ++ GFP_KERNEL); ++ if (!queue->skb) ++ return -ENOMEM; ++ ++ queue->dma_phys = devm_kcalloc(dev, num, sizeof(dma_addr_t), ++ GFP_KERNEL); ++ if (!queue->dma_phys) ++ return -ENOMEM; ++ ++ queue->num = num; ++ queue->head = 0; ++ queue->tail = 0; ++ ++ return 0; ++} ++ ++static int hisi_femac_init_tx_and_rx_queues(struct hisi_femac_priv *priv) ++{ ++ int ret; ++ ++ ret = hisi_femac_init_queue(priv->dev, &priv->txq, TXQ_NUM); ++ if (ret) ++ return ret; ++ ++ ret = hisi_femac_init_queue(priv->dev, &priv->rxq, RXQ_NUM); ++ if (ret) ++ return ret; ++ ++ priv->tx_fifo_used_cnt = 0; ++ ++ return 0; ++} ++ ++static void hisi_femac_free_skb_rings(struct hisi_femac_priv *priv) ++{ ++ struct hisi_femac_queue *txq = &priv->txq; ++ struct hisi_femac_queue *rxq = &priv->rxq; ++ struct sk_buff *skb; ++ dma_addr_t dma_addr; ++ u32 pos; ++ ++ pos = rxq->tail; ++ while (pos != rxq->head) { ++ skb = rxq->skb[pos]; ++ if (unlikely(!skb)) { ++ netdev_err(priv->ndev, "NULL rx skb. pos=%d, head=%d\n", ++ pos, rxq->head); ++ continue; ++ } ++ ++ dma_addr = rxq->dma_phys[pos]; ++ dma_unmap_single(priv->dev, dma_addr, MAX_FRAME_SIZE, ++ DMA_FROM_DEVICE); ++ ++ dev_kfree_skb_any(skb); ++ rxq->skb[pos] = NULL; ++ pos = (pos + 1) % rxq->num; ++ } ++ rxq->tail = pos; ++ ++ pos = txq->tail; ++ while (pos != txq->head) { ++ skb = txq->skb[pos]; ++ if (unlikely(!skb)) { ++ netdev_err(priv->ndev, "NULL tx skb. pos=%d, head=%d\n", ++ pos, txq->head); ++ continue; ++ } ++ hisi_femac_tx_dma_unmap(priv, skb, pos); ++ dev_kfree_skb_any(skb); ++ txq->skb[pos] = NULL; ++ pos = (pos + 1) % txq->num; ++ } ++ txq->tail = pos; ++ priv->tx_fifo_used_cnt = 0; ++} ++ ++static int hisi_femac_set_hw_mac_addr(struct hisi_femac_priv *priv, ++ unsigned char *mac) ++{ ++ u32 reg; ++ ++ reg = mac[1] | (mac[0] << 8); ++ writel(reg, priv->glb_base + GLB_HOSTMAC_H16); ++ ++ reg = mac[5] | (mac[4] << 8) | (mac[3] << 16) | (mac[2] << 24); ++ writel(reg, priv->glb_base + GLB_HOSTMAC_L32); ++ ++ return 0; ++} ++ ++static int hisi_femac_port_reset(struct hisi_femac_priv *priv) ++{ ++ u32 val; ++ ++ val = readl(priv->glb_base + GLB_SOFT_RESET); ++ val |= SOFT_RESET_ALL; ++ writel(val, priv->glb_base + GLB_SOFT_RESET); ++ ++ usleep_range(500, 800); ++ ++ val &= ~SOFT_RESET_ALL; ++ writel(val, priv->glb_base + GLB_SOFT_RESET); ++ ++ return 0; ++} ++ ++static int hisi_femac_net_open(struct net_device *dev) ++{ ++ struct hisi_femac_priv *priv = netdev_priv(dev); ++ ++ hisi_femac_set_hw_mac_addr(priv, dev->dev_addr); ++ /* clear interrupts will drop the first packet MAC have received, ++ * so do it before refill the rx free skbs. ++ */ ++ writel(IRQ_ENA_PORT0_MASK, priv->glb_base + GLB_IRQ_RAW); ++ hisi_femac_rx_refill(priv); ++ ++ netif_carrier_off(dev); ++ netdev_reset_queue(dev); ++ netif_start_queue(dev); ++ napi_enable(&priv->napi); ++ ++ priv->link_status = 0; ++ if (dev->phydev) ++ phy_start(dev->phydev); ++ ++ hisi_femac_irq_enable(priv, IRQ_ENA_ALL | IRQ_ENA_PORT0 | DEF_INT_MASK); ++ if (HAS_TSO_CAP(priv->hw_cap)) ++ hisi_femac_irq_enable(priv, INT_TX_ERR); ++ ++ return 0; ++} ++ ++static void hisi_femac_port_init(struct hisi_femac_priv *priv); ++ ++static int hisi_femac_net_close(struct net_device *dev) ++{ ++ struct hisi_femac_priv *priv = netdev_priv(dev); ++ ++ hisi_femac_irq_disable(priv, IRQ_ENA_PORT0); ++ ++ if (dev->phydev) ++ phy_stop(dev->phydev); ++ ++ netif_stop_queue(dev); ++ napi_disable(&priv->napi); ++ ++ /* reset MAC port first before free skb rings ++ * to prevent potential risk of use-after-free. ++ */ ++ hisi_femac_port_reset(priv); ++ hisi_femac_port_init(priv); ++ ++ priv->tx_pause_en = false; ++ hisi_femac_set_flow_ctrl(priv); ++ hisi_femac_free_skb_rings(priv); ++ ++ return 0; ++} ++ ++static netdev_tx_t hisi_femac_net_xmit(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct hisi_femac_priv *priv = netdev_priv(dev); ++ struct hisi_femac_queue *txq = &priv->txq; ++ dma_addr_t addr; ++ int ret; ++ u32 pkt_info; ++ u32 val; ++ ++ val = readl(priv->port_base + ADDRQ_STAT); ++ val &= BIT_TX_READY; ++ if (!val) { ++ hisi_femac_irq_enable(priv, IRQ_INT_TX_PER_PACKET); ++ dev->stats.tx_dropped++; ++ dev->stats.tx_fifo_errors++; ++ netif_stop_queue(dev); ++ return NETDEV_TX_BUSY; ++ } ++ ++ if (unlikely(!CIRC_SPACE(txq->head, txq->tail, ++ txq->num))) { ++ hisi_femac_irq_enable(priv, IRQ_INT_TX_PER_PACKET); ++ dev->stats.tx_dropped++; ++ dev->stats.tx_fifo_errors++; ++ netif_stop_queue(dev); ++ return NETDEV_TX_BUSY; ++ } ++ ++ ret = hisi_femac_check_hw_capability(skb); ++ if (unlikely(ret)) { ++ if (ret == -ENOTSUPP) ++ return hisi_femac_sw_gso(skb, dev); ++ ++ dev_kfree_skb_any(skb); ++ dev->stats.tx_dropped++; ++ return NETDEV_TX_OK; ++ } ++ ++ pkt_info = hisi_femac_get_pkt_info(skb); ++ ++ if (!(skb_is_gso(skb) || skb_shinfo(skb)->nr_frags)) { ++ addr = dma_map_single(priv->dev, skb->data, ++ skb->len, DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(priv->dev, addr))) { ++ dev_kfree_skb_any(skb); ++ dev->stats.tx_dropped++; ++ return NETDEV_TX_OK; ++ } ++ } else { ++ ret = hisi_femac_fill_sg_desc(priv, skb, txq->head); ++ if (unlikely(ret)) { ++ dev_kfree_skb_any(skb); ++ dev->stats.tx_dropped++; ++ return NETDEV_TX_OK; ++ } ++ ++ addr = priv->tx_ring.dma_phys + ++ txq->head * sizeof(struct tx_desc); ++ ++ /* Ensure desc info writen to memory before config hardware */ ++ wmb(); ++ } ++ txq->dma_phys[txq->head] = addr; ++ ++ txq->skb[txq->head] = skb; ++ txq->head = (txq->head + 1) % txq->num; ++ ++ writel(addr, priv->port_base + EQ_ADDR); ++ writel(pkt_info, priv->port_base + EQFRM_LEN); ++ ++ priv->tx_fifo_used_cnt++; ++ ++ dev->stats.tx_packets++; ++ dev->stats.tx_bytes += skb->len; ++ netdev_sent_queue(dev, skb->len); ++ ++ return NETDEV_TX_OK; ++} ++ ++static int hisi_femac_set_mac_address(struct net_device *dev, void *p) ++{ ++ struct hisi_femac_priv *priv = netdev_priv(dev); ++ struct sockaddr *skaddr = p; ++ ++ if (!is_valid_ether_addr(skaddr->sa_data)) ++ return -EADDRNOTAVAIL; ++ ++ memcpy(dev->dev_addr, skaddr->sa_data, dev->addr_len); ++ dev->addr_assign_type &= ~NET_ADDR_RANDOM; ++ ++ hisi_femac_set_hw_mac_addr(priv, dev->dev_addr); ++ ++ return 0; ++} ++ ++static void hisi_femac_enable_hw_addr_filter(struct hisi_femac_priv *priv, ++ unsigned int reg_n, bool enable) ++{ ++ u32 val; ++ ++ val = readl(priv->glb_base + GLB_MAC_H16(reg_n)); ++ if (enable) ++ val |= BIT_MACFLT_ENA; ++ else ++ val &= ~BIT_MACFLT_ENA; ++ writel(val, priv->glb_base + GLB_MAC_H16(reg_n)); ++} ++ ++static void hisi_femac_set_hw_addr_filter(struct hisi_femac_priv *priv, ++ unsigned char *addr, ++ unsigned int reg_n) ++{ ++ unsigned int high, low; ++ u32 val; ++ ++ high = GLB_MAC_H16(reg_n); ++ low = GLB_MAC_L32(reg_n); ++ ++ val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; ++ writel(val, priv->glb_base + low); ++ ++ val = readl(priv->glb_base + high); ++ val &= ~MACFLT_HI16_MASK; ++ val |= ((addr[0] << 8) | addr[1]); ++ val |= (BIT_MACFLT_ENA | BIT_MACFLT_FW2CPU); ++ writel(val, priv->glb_base + high); ++} ++ ++static void hisi_femac_set_promisc_mode(struct hisi_femac_priv *priv, ++ bool promisc_mode) ++{ ++ u32 val; ++ ++ val = readl(priv->glb_base + GLB_FWCTRL); ++ if (promisc_mode) ++ val |= FWCTRL_FWALL2CPU; ++ else ++ val &= ~FWCTRL_FWALL2CPU; ++ writel(val, priv->glb_base + GLB_FWCTRL); ++} ++ ++/* Handle multiple multicast addresses (perfect filtering)*/ ++static void hisi_femac_set_mc_addr_filter(struct hisi_femac_priv *priv) ++{ ++ struct net_device *dev = priv->ndev; ++ u32 val; ++ ++ val = readl(priv->glb_base + GLB_MACTCTRL); ++ if ((netdev_mc_count(dev) > MAX_MULTICAST_ADDRESSES) || ++ (dev->flags & IFF_ALLMULTI)) { ++ val |= MACTCTRL_MULTI2CPU; ++ } else { ++ int reg = MAX_UNICAST_ADDRESSES; ++ int i; ++ struct netdev_hw_addr *ha; ++ ++ for (i = reg; i < MAX_MAC_FILTER_NUM; i++) ++ hisi_femac_enable_hw_addr_filter(priv, i, false); ++ ++ netdev_for_each_mc_addr(ha, dev) { ++ hisi_femac_set_hw_addr_filter(priv, ha->addr, reg); ++ reg++; ++ } ++ val &= ~MACTCTRL_MULTI2CPU; ++ } ++ writel(val, priv->glb_base + GLB_MACTCTRL); ++} ++ ++/* Handle multiple unicast addresses (perfect filtering)*/ ++static void hisi_femac_set_uc_addr_filter(struct hisi_femac_priv *priv) ++{ ++ struct net_device *dev = priv->ndev; ++ u32 val; ++ ++ val = readl(priv->glb_base + GLB_MACTCTRL); ++ if (netdev_uc_count(dev) > MAX_UNICAST_ADDRESSES) { ++ val |= MACTCTRL_UNI2CPU; ++ } else { ++ int reg = 0; ++ int i; ++ struct netdev_hw_addr *ha; ++ ++ for (i = reg; i < MAX_UNICAST_ADDRESSES; i++) ++ hisi_femac_enable_hw_addr_filter(priv, i, false); ++ ++ netdev_for_each_uc_addr(ha, dev) { ++ hisi_femac_set_hw_addr_filter(priv, ha->addr, reg); ++ reg++; ++ } ++ val &= ~MACTCTRL_UNI2CPU; ++ } ++ writel(val, priv->glb_base + GLB_MACTCTRL); ++} ++ ++static void hisi_femac_net_set_rx_mode(struct net_device *dev) ++{ ++ struct hisi_femac_priv *priv = netdev_priv(dev); ++ ++ if (dev->flags & IFF_PROMISC) { ++ hisi_femac_set_promisc_mode(priv, true); ++ } else { ++ hisi_femac_set_promisc_mode(priv, false); ++ hisi_femac_set_mc_addr_filter(priv); ++ hisi_femac_set_uc_addr_filter(priv); ++ } ++} ++ ++static int hisi_femac_net_ioctl(struct net_device *dev, ++ struct ifreq *ifreq, int cmd) ++{ ++ if (!netif_running(dev)) ++ return -EINVAL; ++ ++ if (!dev->phydev) ++ return -EINVAL; ++ ++ return phy_mii_ioctl(dev->phydev, ifreq, cmd); ++} ++ ++static void hisi_femac_get_pauseparam(struct net_device *dev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct hisi_femac_priv *priv = netdev_priv(dev); ++ ++ pause->autoneg = dev->phydev->autoneg; ++ pause->rx_pause = 1; ++ if (priv->tx_pause_en) ++ pause->tx_pause = 1; ++} ++ ++static int hisi_femac_set_pauseparam(struct net_device *dev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct hisi_femac_priv *priv = netdev_priv(dev); ++ struct phy_device *phy = dev->phydev; ++ int ret = 0; ++ ++ if (pause->rx_pause == 0) ++ return -EINVAL; ++ ++ if (pause->tx_pause != priv->tx_pause_en) { ++ priv->tx_pause_en = pause->tx_pause; ++ hisi_femac_set_flow_ctrl(priv); ++ } ++ ++ if (phy->autoneg) { ++ if (netif_running(dev)) { ++ struct ethtool_cmd cmd; ++ /* auto-negotiation automatically restarted */ ++ cmd.cmd = ETHTOOL_NWAY_RST; ++ cmd.supported = phy->supported; ++ cmd.advertising = phy->advertising; ++ cmd.autoneg = phy->autoneg; ++ cmd.speed = phy->speed; ++ cmd.duplex = phy->duplex; ++ cmd.phy_address = phy->addr; ++ ret = phy_ethtool_sset(phy, &cmd); ++ } ++ } ++ ++ return ret; ++} ++ ++static void hisi_femac_enable_rxcsum_drop(struct hisi_femac_priv *priv, ++ bool drop) ++{ ++ unsigned int val; ++ ++ val = readl(priv->port_base + RX_COE_CTRL); ++ val &= ~COE_ERR_DROP; ++ if (drop) ++ val |= (BIT_COE_IPHDR_DROP | BIT_COE_IPV6_UDP_ZERO_DROP); ++ writel(val, priv->port_base + RX_COE_CTRL); ++} ++ ++static int hisi_femac_set_features(struct net_device *dev, ++ netdev_features_t features) ++{ ++ struct hisi_femac_priv *priv = netdev_priv(dev); ++ netdev_features_t changed = dev->features ^ features; ++ ++ if (changed & NETIF_F_RXCSUM) { ++ if (features & NETIF_F_RXCSUM) ++ hisi_femac_enable_rxcsum_drop(priv, true); ++ else ++ hisi_femac_enable_rxcsum_drop(priv, false); ++ } ++ ++ return 0; ++} ++ ++static int hisi_femac_get_settings(struct net_device *ndev, ++ struct ethtool_cmd *cmd) ++{ ++ if (!ndev->phydev) ++ return -EINVAL; ++ ++ return phy_ethtool_gset(ndev->phydev, cmd); ++} ++ ++static int hisi_femac_set_settings(struct net_device *ndev, ++ struct ethtool_cmd *cmd) ++{ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ if (!ndev->phydev) ++ return -EINVAL; ++ ++ return phy_ethtool_sset(ndev->phydev, cmd); ++} ++ ++static struct ethtool_ops hisi_femac_ethtools_ops = { ++ .get_link = ethtool_op_get_link, ++ .get_settings = hisi_femac_get_settings, ++ .set_settings = hisi_femac_set_settings, ++ .get_pauseparam = hisi_femac_get_pauseparam, ++ .set_pauseparam = hisi_femac_set_pauseparam, ++}; ++ ++static const struct net_device_ops hisi_femac_netdev_ops = { ++ .ndo_open = hisi_femac_net_open, ++ .ndo_stop = hisi_femac_net_close, ++ .ndo_start_xmit = hisi_femac_net_xmit, ++ .ndo_do_ioctl = hisi_femac_net_ioctl, ++ .ndo_set_mac_address = hisi_femac_set_mac_address, ++ .ndo_set_rx_mode = hisi_femac_net_set_rx_mode, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_set_features = hisi_femac_set_features, ++}; ++ ++static void hisi_femac_verify_flow_ctrl_args(struct hisi_femac_priv *priv) ++{ ++ if (priv->tx_pause_active_thresh < FC_ACTIVE_MIN || ++ priv->tx_pause_active_thresh > FC_ACTIVE_MAX) ++ priv->tx_pause_active_thresh = FC_ACTIVE_DEFAULT; ++ ++ if (priv->tx_pause_deactive_thresh < FC_DEACTIVE_MIN || ++ priv->tx_pause_deactive_thresh > FC_DEACTIVE_MAX) ++ priv->tx_pause_deactive_thresh = FC_DEACTIVE_DEFAULT; ++ ++ if (priv->tx_pause_active_thresh >= priv->tx_pause_deactive_thresh) { ++ priv->tx_pause_active_thresh = FC_ACTIVE_DEFAULT; ++ priv->tx_pause_deactive_thresh = FC_DEACTIVE_DEFAULT; ++ } ++} ++ ++static void hisi_femac_core_reset(struct hisi_femac_priv *priv) ++{ ++ reset_control_assert(priv->mac_rst); ++ reset_control_deassert(priv->mac_rst); ++} ++ ++static void hisi_femac_sleep_us(u32 time_us) ++{ ++ u32 time_ms; ++ ++ if (!time_us) ++ return; ++ ++ time_ms = DIV_ROUND_UP(time_us, 1000); ++ if (time_ms < 20) ++ usleep_range(time_us, time_us + 500); ++ else ++ msleep(time_ms); ++} ++ ++static void hisi_femac_phy_reset(struct hisi_femac_priv *priv) ++{ ++ /* To make sure PHY hardware reset success, ++ * we must keep PHY in deassert state first and ++ * then complete the hardware reset operation ++ */ ++ reset_control_deassert(priv->phy_rst); ++ hisi_femac_sleep_us(priv->phy_reset_delays[PRE_DELAY]); ++ ++ reset_control_assert(priv->phy_rst); ++ /* delay some time to ensure reset ok, ++ * this depends on PHY hardware feature ++ */ ++ hisi_femac_sleep_us(priv->phy_reset_delays[PULSE]); ++ reset_control_deassert(priv->phy_rst); ++ /* delay some time to ensure later MDIO access */ ++ hisi_femac_sleep_us(priv->phy_reset_delays[POST_DELAY]); ++} ++ ++static void hisi_femac_port_init(struct hisi_femac_priv *priv) ++{ ++ u32 val; ++ ++ /* MAC gets link status info and phy mode by software config */ ++ val = MAC_PORTSEL_STAT_CPU; ++ if (priv->ndev->phydev->interface == PHY_INTERFACE_MODE_RMII) ++ val |= MAC_PORTSEL_RMII; ++ writel(val, priv->port_base + MAC_PORTSEL); ++ ++ /*clear all interrupt status */ ++ writel(IRQ_ENA_PORT0_MASK, priv->glb_base + GLB_IRQ_RAW); ++ hisi_femac_irq_disable(priv, IRQ_ENA_PORT0_MASK | IRQ_ENA_PORT0); ++ ++ if (HAS_TSO_CAP(priv->hw_cap)) { ++ /* enable TSO debug for error handle */ ++ val = readl(priv->port_base + TSO_DBG_EN); ++ val |= BITS_TSO_DBG_EN; ++ writel(val, priv->port_base + TSO_DBG_EN); ++ } ++ ++ val = readl(priv->glb_base + GLB_FWCTRL); ++ val &= ~(FWCTRL_VLAN_ENABLE | FWCTRL_FWALL2CPU); ++ val |= FWCTRL_FW2CPU_ENA; ++ writel(val, priv->glb_base + GLB_FWCTRL); ++ ++ val = readl(priv->glb_base + GLB_MACTCTRL); ++ val |= (MACTCTRL_BROAD2CPU | MACTCTRL_MACT_ENA); ++ writel(val, priv->glb_base + GLB_MACTCTRL); ++ ++ val = readl(priv->port_base + MAC_SET); ++ val &= ~MAX_FRAME_SIZE_MASK; ++ val |= MAX_FRAME_SIZE; ++ writel(val, priv->port_base + MAC_SET); ++ ++ val = RX_COALESCED_TIMER | ++ (RX_COALESCED_FRAMES << RX_COALESCED_FRAME_OFFSET); ++ writel(val, priv->port_base + RX_COALESCE_SET); ++ ++ val = (HW_RX_FIFO_DEPTH << RX_DEPTH_OFFSET) | HW_TX_FIFO_DEPTH; ++ writel(val, priv->port_base + QLEN_SET); ++ ++ hisi_femac_set_flow_ctrl(priv); ++} ++ ++static int hisi_femac_drv_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node; ++ struct resource *res; ++ struct net_device *ndev; ++ struct hisi_femac_priv *priv; ++ struct phy_device *phy; ++ const char *mac_addr; ++ int ret; ++ ++ ndev = alloc_etherdev(sizeof(*priv)); ++ if (!ndev) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, ndev); ++ ++ priv = netdev_priv(ndev); ++ priv->dev = dev; ++ priv->ndev = ndev; ++ ++ if (of_device_is_compatible(node, "hisilicon,hisi-femac-v2")) ++ priv->hw_cap |= HW_CAP_TSO | HW_CAP_RXCSUM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->port_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(priv->port_base)) { ++ ret = PTR_ERR(priv->port_base); ++ goto out_free_netdev; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ priv->glb_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(priv->glb_base)) { ++ ret = PTR_ERR(priv->glb_base); ++ goto out_free_netdev; ++ } ++ ++ priv->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->clk)) { ++ dev_err(dev, "failed to get clk\n"); ++ ret = -ENODEV; ++ goto out_free_netdev; ++ } ++ ++ ret = clk_prepare_enable(priv->clk); ++ if (ret) { ++ dev_err(dev, "failed to enable clk %d\n", ret); ++ goto out_free_netdev; ++ } ++ ++ priv->mac_rst = devm_reset_control_get(dev, "mac"); ++ if (IS_ERR(priv->mac_rst)) { ++ ret = PTR_ERR(priv->mac_rst); ++ goto out_disable_clk; ++ } ++ hisi_femac_core_reset(priv); ++ ++ priv->phy_rst = devm_reset_control_get(dev, "phy"); ++ if (IS_ERR(priv->phy_rst)) { ++ priv->phy_rst = NULL; ++ } else { ++ ret = of_property_read_u32_array(node, ++ PHY_RESET_DELAYS_PROPERTY, ++ priv->phy_reset_delays, ++ DELAYS_NUM); ++ if (ret) ++ goto out_disable_clk; ++ hisi_femac_phy_reset(priv); ++ } ++ ++ phy = of_phy_get_and_connect(ndev, node, hisi_femac_adjust_link); ++ if (!phy) { ++ dev_err(dev, "connect to PHY failed!\n"); ++ ret = -ENODEV; ++ goto out_disable_clk; ++ } ++ ++ phy->advertising |= ADVERTISED_Pause; ++ phy->supported |= ADVERTISED_Pause; ++ ++ dev_info(dev, "phy_id=0x%.8lx, phy_addr=%d, phy_mode=%s\n", ++ (unsigned long)phy->phy_id, ++ phy->addr, ++ phy_modes(phy->interface)); ++ ++ mac_addr = of_get_mac_address(node); ++ if (mac_addr) ++ ether_addr_copy(ndev->dev_addr, mac_addr); ++ if (!is_valid_ether_addr(ndev->dev_addr)) { ++ eth_hw_addr_random(ndev); ++ dev_warn(dev, "using random MAC address %pM\n", ++ ndev->dev_addr); ++ } ++ ++ ndev->watchdog_timeo = 6 * HZ; ++ ndev->priv_flags |= IFF_UNICAST_FLT; ++ ndev->netdev_ops = &hisi_femac_netdev_ops; ++ ndev->ethtool_ops = &hisi_femac_ethtools_ops; ++ netif_napi_add(ndev, &priv->napi, hisi_femac_poll, FEMAC_POLL_WEIGHT); ++ SET_NETDEV_DEV(ndev, &pdev->dev); ++ ++ if (HAS_TSO_CAP(priv->hw_cap)) ++ ndev->hw_features |= NETIF_F_SG | ++ NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_UFO; ++ ++ if (HAS_RXCSUM_CAP(priv->hw_cap)) ++ ndev->hw_features |= NETIF_F_RXCSUM; ++ ndev->features |= ndev->hw_features; ++ ndev->vlan_features |= ndev->features; ++ ++ device_set_wakeup_capable(priv->dev, true); ++ device_set_wakeup_enable(priv->dev, true); ++ ++ priv->tx_pause_en = true; ++ priv->tx_pause_active_thresh = TX_FLOW_CTRL_ACTIVE_THRESHOLD; ++ priv->tx_pause_deactive_thresh = TX_FLOW_CTRL_DEACTIVE_THRESHOLD; ++ ++ hisi_femac_verify_flow_ctrl_args(priv); ++ ++ hisi_femac_port_init(priv); ++ ++ if (HAS_RXCSUM_CAP(priv->hw_cap)) ++ hisi_femac_enable_rxcsum_drop(priv, true); ++ ++ ret = hisi_femac_init_tx_and_rx_queues(priv); ++ if (ret) ++ goto out_disconnect_phy; ++ ++ if (HAS_TSO_CAP(priv->hw_cap)) { ++ ret = hisi_femac_init_tx_descriptor_ring(priv); ++ if (ret) ++ goto out_disconnect_phy; ++ } ++ ++ ndev->irq = platform_get_irq(pdev, 0); ++ if (ndev->irq <= 0) { ++ dev_err(dev, "No irq resource\n"); ++ ret = -ENODEV; ++ goto out_destroy_descriptor; ++ } ++ ++ ret = devm_request_irq(dev, ndev->irq, hisi_femac_interrupt, ++ IRQF_SHARED, pdev->name, ndev); ++ if (ret) { ++ dev_err(dev, "devm_request_irq %d failed!\n", ndev->irq); ++ goto out_destroy_descriptor; ++ } ++ ++ ret = register_netdev(ndev); ++ if (ret) { ++ dev_err(dev, "register_netdev failed!\n"); ++ goto out_destroy_descriptor; ++ } ++ ++ return ret; ++ ++out_destroy_descriptor: ++ if (HAS_TSO_CAP(priv->hw_cap)) ++ hisi_femac_destroy_tx_descriptor_ring(priv); ++out_disconnect_phy: ++ netif_napi_del(&priv->napi); ++ phy_disconnect(phy); ++out_disable_clk: ++ clk_disable_unprepare(priv->clk); ++out_free_netdev: ++ free_netdev(ndev); ++ ++ return ret; ++} ++ ++static int hisi_femac_drv_remove(struct platform_device *pdev) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct hisi_femac_priv *priv = netdev_priv(ndev); ++ ++ netif_napi_del(&priv->napi); ++ unregister_netdev(ndev); ++ if (HAS_TSO_CAP(priv->hw_cap)) ++ hisi_femac_destroy_tx_descriptor_ring(priv); ++ ++ phy_disconnect(ndev->phydev); ++ clk_disable_unprepare(priv->clk); ++ free_netdev(ndev); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++int hisi_femac_drv_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct hisi_femac_priv *priv = netdev_priv(ndev); ++ ++ disable_irq(ndev->irq); ++ if (netif_running(ndev)) { ++ hisi_femac_net_close(ndev); ++ netif_device_detach(ndev); ++ } ++ ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++ ++int hisi_femac_drv_resume(struct platform_device *pdev) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct hisi_femac_priv *priv = netdev_priv(ndev); ++ ++ clk_prepare_enable(priv->clk); ++ if (priv->phy_rst) ++ hisi_femac_phy_reset(priv); ++ ++ if (netif_running(ndev)) { ++ hisi_femac_port_init(priv); ++ hisi_femac_net_open(ndev); ++ netif_device_attach(ndev); ++ } ++ enable_irq(ndev->irq); ++ ++ return 0; ++} ++#endif ++ ++static const struct of_device_id hisi_femac_match[] = { ++ {.compatible = "hisilicon,hisi-femac-v1",}, ++ {.compatible = "hisilicon,hisi-femac-v2",}, ++ {.compatible = "hisilicon,hi3516cv300-femac",}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, hisi_femac_match); ++ ++static struct platform_driver hisi_femac_driver = { ++ .driver = { ++ .name = "hisi-femac", ++ .of_match_table = hisi_femac_match, ++ }, ++ .probe = hisi_femac_drv_probe, ++ .remove = hisi_femac_drv_remove, ++#ifdef CONFIG_PM ++ .suspend = hisi_femac_drv_suspend, ++ .resume = hisi_femac_drv_resume, ++#endif ++}; ++ ++module_platform_driver(hisi_femac_driver); ++ ++MODULE_DESCRIPTION("Hisilicon Fast Ethernet MAC driver"); ++MODULE_AUTHOR("Dongpo Li <lidongpo@hisilicon.com>"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:hisi-femac"); +diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig +index 75472cf..faa78eb 100644 +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -212,6 +212,20 @@ config MDIO_BCM_UNIMAC + controllers as well as some Broadcom Ethernet switches such as the + Starfighter 2 switches. + ++config MDIO_HISI_FEMAC ++ tristate "Hisilicon FEMAC MDIO bus controller" ++ depends on HAS_IOMEM && OF_MDIO ++ help ++ This module provides a driver for the MDIO busses found in the ++ Hisilicon SoC that have an Fast Ethernet MAC. ++ ++config MDIO_HISI_GEMAC ++ tristate "Hisilicon GEMAC MDIO bus controller" ++ depends on HAS_IOMEM && OF_MDIO ++ help ++ This module provides a driver for the MDIO busses found in the ++ Hisilicon SoC that have an Gigabit Ethernet MAC. ++ + endif # PHYLIB + + config MICREL_KS8995MA +diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile +index eb3b18b..d1cc7c5 100644 +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -35,3 +35,5 @@ obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o + obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o + obj-$(CONFIG_AMD_XGBE_PHY) += amd-xgbe-phy.o + obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o ++obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o ++obj-$(CONFIG_MDIO_HISI_GEMAC) += mdio-hisi-gemac.o +diff --git a/drivers/net/phy/mdio-hisi-femac.c b/drivers/net/phy/mdio-hisi-femac.c +new file mode 100644 +index 0000000..186badd +--- /dev/null ++++ b/drivers/net/phy/mdio-hisi-femac.c +@@ -0,0 +1,166 @@ ++/* ++ * Hisilicon Fast Ethernet MDIO Bus Driver ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/iopoll.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of_address.h> ++#include <linux/of_mdio.h> ++#include <linux/platform_device.h> ++ ++#define MDIO_RWCTRL 0x00 ++#define MDIO_RO_DATA 0x04 ++#define MDIO_WRITE BIT(13) ++#define MDIO_RW_FINISH BIT(15) ++#define BIT_PHY_ADDR_OFFSET 8 ++#define BIT_WR_DATA_OFFSET 16 ++ ++struct hisi_femac_mdio_data { ++ struct clk *clk; ++ void __iomem *membase; ++}; ++ ++static int hisi_femac_mdio_wait_ready(struct hisi_femac_mdio_data *data) ++{ ++ u32 val; ++ ++ return readl_poll_timeout(data->membase + MDIO_RWCTRL, ++ val, val & MDIO_RW_FINISH, 20, 10000); ++} ++ ++static int hisi_femac_mdio_read(struct mii_bus *bus, int mii_id, int regnum) ++{ ++ struct hisi_femac_mdio_data *data = bus->priv; ++ int ret; ++ ++ ret = hisi_femac_mdio_wait_ready(data); ++ if (ret) ++ return ret; ++ ++ writel((mii_id << BIT_PHY_ADDR_OFFSET) | ((u32)regnum), ++ data->membase + MDIO_RWCTRL); ++ ++ ret = hisi_femac_mdio_wait_ready(data); ++ if (ret) ++ return ret; ++ ++ return readl(data->membase + MDIO_RO_DATA) & 0xFFFF; ++} ++ ++static int hisi_femac_mdio_write(struct mii_bus *bus, int mii_id, int regnum, ++ u16 value) ++{ ++ struct hisi_femac_mdio_data *data = bus->priv; ++ int ret; ++ ++ ret = hisi_femac_mdio_wait_ready(data); ++ if (ret) ++ return ret; ++ ++ writel(MDIO_WRITE | (value << BIT_WR_DATA_OFFSET) | ++ (mii_id << BIT_PHY_ADDR_OFFSET) | ((u32)regnum), ++ data->membase + MDIO_RWCTRL); ++ ++ return hisi_femac_mdio_wait_ready(data); ++} ++ ++static int hisi_femac_mdio_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct mii_bus *bus; ++ struct hisi_femac_mdio_data *data; ++ struct resource *res; ++ int ret; ++ ++ bus = mdiobus_alloc_size(sizeof(*data)); ++ if (!bus) ++ return -ENOMEM; ++ ++ bus->name = "hisi_femac_mii_bus"; ++ bus->read = &hisi_femac_mdio_read; ++ bus->write = &hisi_femac_mdio_write; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name); ++ bus->parent = &pdev->dev; ++ ++ data = bus->priv; ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ data->membase = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(data->membase)) { ++ ret = PTR_ERR(data->membase); ++ goto err_out_free_mdiobus; ++ } ++ ++ data->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(data->clk)) { ++ ret = PTR_ERR(data->clk); ++ goto err_out_free_mdiobus; ++ } ++ ++ ret = clk_prepare_enable(data->clk); ++ if (ret) ++ goto err_out_free_mdiobus; ++ ++ ret = of_mdiobus_register(bus, np); ++ if (ret) ++ goto err_out_disable_clk; ++ ++ platform_set_drvdata(pdev, bus); ++ ++ return 0; ++ ++err_out_disable_clk: ++ clk_disable_unprepare(data->clk); ++err_out_free_mdiobus: ++ mdiobus_free(bus); ++ return ret; ++} ++ ++static int hisi_femac_mdio_remove(struct platform_device *pdev) ++{ ++ struct mii_bus *bus = platform_get_drvdata(pdev); ++ struct hisi_femac_mdio_data *data = bus->priv; ++ ++ mdiobus_unregister(bus); ++ clk_disable_unprepare(data->clk); ++ mdiobus_free(bus); ++ ++ return 0; ++} ++ ++static const struct of_device_id hisi_femac_mdio_dt_ids[] = { ++ { .compatible = "hisilicon,hisi-femac-mdio" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, hisi_femac_mdio_dt_ids); ++ ++static struct platform_driver hisi_femac_mdio_driver = { ++ .probe = hisi_femac_mdio_probe, ++ .remove = hisi_femac_mdio_remove, ++ .driver = { ++ .name = "hisi-femac-mdio", ++ .of_match_table = hisi_femac_mdio_dt_ids, ++ }, ++}; ++ ++module_platform_driver(hisi_femac_mdio_driver); ++ ++MODULE_DESCRIPTION("Hisilicon Fast Ethernet MAC MDIO interface driver"); ++MODULE_AUTHOR("Dongpo Li <lidongpo@hisilicon.com>"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/net/phy/mdio-hisi-gemac.c b/drivers/net/phy/mdio-hisi-gemac.c +new file mode 100644 +index 0000000..efe0897 +--- /dev/null ++++ b/drivers/net/phy/mdio-hisi-gemac.c +@@ -0,0 +1,221 @@ ++/* ++ * Hisilicon Gigabit Ethernet MDIO Bus Driver ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/iopoll.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of_address.h> ++#include <linux/of_mdio.h> ++#include <linux/platform_device.h> ++#include <linux/reset.h> ++ ++#if defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3516AV200) ++#ifdef readl ++#undef readl ++#undef writel ++#define readl hi_readl ++#define writel hi_writel ++#endif ++#endif ++ ++#define MDIO_SINGLE_CMD 0x00 ++#define MDIO_SINGLE_DATA 0x04 ++#define MDIO_RDATA_STATUS 0x10 ++#define BIT_PHY_ADDR_OFFSET 8 ++#define MDIO_WRITE BIT(16) ++#define MDIO_READ BIT(17) ++#define MDIO_START BIT(20) ++#define MDIO_START_READ (MDIO_START | MDIO_READ) ++#define MDIO_START_WRITE (MDIO_START | MDIO_WRITE) ++ ++struct hisi_gemac_mdio_data { ++ struct clk *clk; ++ struct reset_control *phy_rst; ++ void __iomem *membase; ++}; ++ ++static int hisi_gemac_mdio_wait_ready(struct hisi_gemac_mdio_data *data) ++{ ++ u32 val; ++ ++ return readl_poll_timeout(data->membase + MDIO_SINGLE_CMD, ++ val, !(val & MDIO_START), 20, 10000); ++} ++ ++static int hisi_gemac_mdio_read(struct mii_bus *bus, int mii_id, int regnum) ++{ ++ struct hisi_gemac_mdio_data *data = bus->priv; ++ int ret; ++ ++ ret = hisi_gemac_mdio_wait_ready(data); ++ if (ret) ++ return ret; ++ ++ writel(MDIO_START_READ | ((u32)mii_id << BIT_PHY_ADDR_OFFSET) | ++ ((u32)regnum), ++ data->membase + MDIO_SINGLE_CMD); ++ ++ ret = hisi_gemac_mdio_wait_ready(data); ++ if (ret) ++ return ret; ++ ++ /* if read data is invalid, we just return 0 instead of -EAGAIN. ++ * This can make MDIO more robust when reading PHY status. ++ */ ++ if (readl(data->membase + MDIO_RDATA_STATUS)) ++ return 0; ++ ++ return readl(data->membase + MDIO_SINGLE_DATA) >> 16; ++} ++ ++static int hisi_gemac_mdio_write(struct mii_bus *bus, int mii_id, int regnum, ++ u16 value) ++{ ++ struct hisi_gemac_mdio_data *data = bus->priv; ++ int ret; ++ ++ ret = hisi_gemac_mdio_wait_ready(data); ++ if (ret) ++ return ret; ++ ++ writel(value, data->membase + MDIO_SINGLE_DATA); ++ writel(MDIO_START_WRITE | ((u32)mii_id << BIT_PHY_ADDR_OFFSET) | ++ ((u32)regnum), ++ data->membase + MDIO_SINGLE_CMD); ++ ++ return hisi_gemac_mdio_wait_ready(data); ++} ++ ++static void hisi_gemac_external_phy_reset(struct hisi_gemac_mdio_data *data) ++{ ++ if (data->phy_rst) { ++ /* write 0 to cancel reset */ ++ reset_control_deassert(data->phy_rst); ++ msleep(50); ++ ++ /* HIFONE or 98cv200 use CRG register to reset phy */ ++ /* RST_BIT, write 0 to reset phy, write 1 to cancel reset */ ++ reset_control_assert(data->phy_rst); ++ ++ /* delay some time to ensure reset ok, ++ * this depends on PHY hardware feature ++ */ ++ msleep(50); ++ ++ /* write 0 to cancel reset */ ++ reset_control_deassert(data->phy_rst); ++ /* delay some time to ensure later MDIO access */ ++ msleep(50); ++ } ++} ++ ++static int hisi_gemac_mdio_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct mii_bus *bus; ++ struct hisi_gemac_mdio_data *data; ++ struct resource *res; ++ int ret; ++ ++ bus = mdiobus_alloc_size(sizeof(*data)); ++ if (!bus) ++ return -ENOMEM; ++ ++ bus->name = "hisi_gemac_mii_bus"; ++ bus->read = &hisi_gemac_mdio_read; ++ bus->write = &hisi_gemac_mdio_write; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name); ++ bus->parent = &pdev->dev; ++ ++ data = bus->priv; ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ ret = -ENXIO; ++ goto err_out_free_mdiobus; ++ } ++ data->membase = devm_ioremap(&pdev->dev, res->start, ++ resource_size(res)); ++ if (!data->membase) { ++ ret = -ENOMEM; ++ goto err_out_free_mdiobus; ++ } ++ ++ data->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(data->clk)) { ++ ret = PTR_ERR(data->clk); ++ goto err_out_free_mdiobus; ++ } ++ ++ ret = clk_prepare_enable(data->clk); ++ if (ret) ++ goto err_out_free_mdiobus; ++ ++ data->phy_rst = devm_reset_control_get(&pdev->dev, "phy_reset"); ++ if (IS_ERR(data->phy_rst)) ++ data->phy_rst = NULL; ++ hisi_gemac_external_phy_reset(data); ++ ++ ret = of_mdiobus_register(bus, np); ++ if (ret) ++ goto err_out_disable_clk; ++ ++ platform_set_drvdata(pdev, bus); ++ ++ return 0; ++ ++err_out_disable_clk: ++ clk_disable_unprepare(data->clk); ++err_out_free_mdiobus: ++ mdiobus_free(bus); ++ return ret; ++} ++ ++static int hisi_gemac_mdio_remove(struct platform_device *pdev) ++{ ++ struct mii_bus *bus = platform_get_drvdata(pdev); ++ struct hisi_gemac_mdio_data *data = bus->priv; ++ ++ mdiobus_unregister(bus); ++ clk_disable_unprepare(data->clk); ++ mdiobus_free(bus); ++ ++ return 0; ++} ++ ++static const struct of_device_id hisi_gemac_mdio_dt_ids[] = { ++ { .compatible = "hisilicon,hisi-gemac-mdio" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, hisi_gemac_mdio_dt_ids); ++ ++static struct platform_driver hisi_gemac_mdio_driver = { ++ .probe = hisi_gemac_mdio_probe, ++ .remove = hisi_gemac_mdio_remove, ++ .driver = { ++ .name = "hisi-gemac-mdio", ++ .of_match_table = hisi_gemac_mdio_dt_ids, ++ }, ++}; ++ ++module_platform_driver(hisi_gemac_mdio_driver); ++ ++MODULE_DESCRIPTION("Hisilicon Gigabit Ethernet MAC MDIO interface driver"); ++MODULE_AUTHOR("Dongpo Li <lidongpo@hisilicon.com>"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index 70a0d88..b599982 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -109,6 +109,53 @@ int phy_register_fixup_for_id(const char *bus_id, + } + EXPORT_SYMBOL(phy_register_fixup_for_id); + ++/** ++ * phy_unregister_fixup - remove a phy_fixup from the list ++ * @bus_id: A string matches fixup->bus_id (or PHY_ANY_ID) in phy_fixup_list ++ * @phy_uid: A phy id matches fixup->phy_id (or PHY_ANY_UID) in phy_fixup_list ++ * @phy_uid_mask: Applied to phy_uid and fixup->phy_uid before comparison ++ */ ++int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask) ++{ ++ struct list_head *pos, *n; ++ struct phy_fixup *fixup; ++ int ret; ++ ++ ret = -ENODEV; ++ ++ mutex_lock(&phy_fixup_lock); ++ list_for_each_safe(pos, n, &phy_fixup_list) { ++ fixup = list_entry(pos, struct phy_fixup, list); ++ ++ if ((!strcmp(fixup->bus_id, bus_id)) && ++ ((fixup->phy_uid & phy_uid_mask) == ++ (phy_uid & phy_uid_mask))) { ++ list_del(&fixup->list); ++ kfree(fixup); ++ ret = 0; ++ break; ++ } ++ } ++ mutex_unlock(&phy_fixup_lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL(phy_unregister_fixup); ++ ++/* Unregisters a fixup of any PHY with the UID in phy_uid */ ++int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask) ++{ ++ return phy_unregister_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask); ++} ++EXPORT_SYMBOL(phy_unregister_fixup_for_uid); ++ ++/* Unregisters a fixup of the PHY with id string bus_id */ ++int phy_unregister_fixup_for_id(const char *bus_id) ++{ ++ return phy_unregister_fixup(bus_id, PHY_ANY_UID, 0xffffffff); ++} ++EXPORT_SYMBOL(phy_unregister_fixup_for_id); ++ + /* Returns 1 if fixup matches phydev in bus_id and phy_uid. + * Fixups can be set to match any in one or more fields. + */ +diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig +index 1373c6d..282aec4 100644 +--- a/drivers/net/ppp/Kconfig ++++ b/drivers/net/ppp/Kconfig +@@ -149,6 +149,23 @@ config PPPOL2TP + tunnels. L2TP is replacing PPTP for VPN uses. + if TTY + ++config PPPOLAC ++ tristate "PPP on L2TP Access Concentrator" ++ depends on PPP && INET ++ help ++ L2TP (RFC 2661) is a tunneling protocol widely used in virtual private ++ networks. This driver handles L2TP data packets between a UDP socket ++ and a PPP channel, but only permits one session per socket. Thus it is ++ fairly simple and suited for clients. ++ ++config PPPOPNS ++ tristate "PPP on PPTP Network Server" ++ depends on PPP && INET ++ help ++ PPTP (RFC 2637) is a tunneling protocol widely used in virtual private ++ networks. This driver handles PPTP data packets between a RAW socket ++ and a PPP channel. It is fairly simple and easy to use. ++ + config PPP_ASYNC + tristate "PPP support for async serial ports" + depends on PPP +diff --git a/drivers/net/ppp/Makefile b/drivers/net/ppp/Makefile +index a6b6297..d283d03 100644 +--- a/drivers/net/ppp/Makefile ++++ b/drivers/net/ppp/Makefile +@@ -11,3 +11,5 @@ obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o + obj-$(CONFIG_PPPOE) += pppox.o pppoe.o + obj-$(CONFIG_PPPOL2TP) += pppox.o + obj-$(CONFIG_PPTP) += pppox.o pptp.o ++obj-$(CONFIG_PPPOLAC) += pppox.o pppolac.o ++obj-$(CONFIG_PPPOPNS) += pppox.o pppopns.o +diff --git a/drivers/net/ppp/pppolac.c b/drivers/net/ppp/pppolac.c +new file mode 100644 +index 0000000..a5d3d63 +--- /dev/null ++++ b/drivers/net/ppp/pppolac.c +@@ -0,0 +1,449 @@ ++/* drivers/net/pppolac.c ++ * ++ * Driver for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661) ++ * ++ * Copyright (C) 2009 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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. ++ */ ++ ++/* This driver handles L2TP data packets between a UDP socket and a PPP channel. ++ * The socket must keep connected, and only one session per socket is permitted. ++ * Sequencing of outgoing packets is controlled by LNS. Incoming packets with ++ * sequences are reordered within a sliding window of one second. Currently ++ * reordering only happens when a packet is received. It is done for simplicity ++ * since no additional locks or threads are required. This driver only works on ++ * IPv4 due to the lack of UDP encapsulation support in IPv6. */ ++ ++#include <linux/module.h> ++#include <linux/jiffies.h> ++#include <linux/workqueue.h> ++#include <linux/skbuff.h> ++#include <linux/file.h> ++#include <linux/netdevice.h> ++#include <linux/net.h> ++#include <linux/udp.h> ++#include <linux/ppp_defs.h> ++#include <linux/if_ppp.h> ++#include <linux/if_pppox.h> ++#include <linux/ppp_channel.h> ++#include <net/tcp_states.h> ++#include <asm/uaccess.h> ++ ++#define L2TP_CONTROL_BIT 0x80 ++#define L2TP_LENGTH_BIT 0x40 ++#define L2TP_SEQUENCE_BIT 0x08 ++#define L2TP_OFFSET_BIT 0x02 ++#define L2TP_VERSION 0x02 ++#define L2TP_VERSION_MASK 0x0F ++ ++#define PPP_ADDR 0xFF ++#define PPP_CTRL 0x03 ++ ++union unaligned { ++ __u32 u32; ++} __attribute__((packed)); ++ ++static inline union unaligned *unaligned(void *ptr) ++{ ++ return (union unaligned *)ptr; ++} ++ ++struct meta { ++ __u32 sequence; ++ __u32 timestamp; ++}; ++ ++static inline struct meta *skb_meta(struct sk_buff *skb) ++{ ++ return (struct meta *)skb->cb; ++} ++ ++/******************************************************************************/ ++ ++static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) ++{ ++ struct sock *sk = (struct sock *)sk_udp->sk_user_data; ++ struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac; ++ struct meta *meta = skb_meta(skb); ++ __u32 now = jiffies; ++ __u8 bits; ++ __u8 *ptr; ++ ++ /* Drop the packet if L2TP header is missing. */ ++ if (skb->len < sizeof(struct udphdr) + 6) ++ goto drop; ++ ++ /* Put it back if it is a control packet. */ ++ if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_BIT) ++ return opt->backlog_rcv(sk_udp, skb); ++ ++ /* Skip UDP header. */ ++ skb_pull(skb, sizeof(struct udphdr)); ++ ++ /* Check the version. */ ++ if ((skb->data[1] & L2TP_VERSION_MASK) != L2TP_VERSION) ++ goto drop; ++ bits = skb->data[0]; ++ ptr = &skb->data[2]; ++ ++ /* Check the length if it is present. */ ++ if (bits & L2TP_LENGTH_BIT) { ++ if ((ptr[0] << 8 | ptr[1]) != skb->len) ++ goto drop; ++ ptr += 2; ++ } ++ ++ /* Skip all fields including optional ones. */ ++ if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_BIT ? 4 : 0) + ++ (bits & L2TP_LENGTH_BIT ? 2 : 0) + ++ (bits & L2TP_OFFSET_BIT ? 2 : 0))) ++ goto drop; ++ ++ /* Skip the offset padding if it is present. */ ++ if (bits & L2TP_OFFSET_BIT && ++ !skb_pull(skb, skb->data[-2] << 8 | skb->data[-1])) ++ goto drop; ++ ++ /* Check the tunnel and the session. */ ++ if (unaligned(ptr)->u32 != opt->local) ++ goto drop; ++ ++ /* Check the sequence if it is present. */ ++ if (bits & L2TP_SEQUENCE_BIT) { ++ meta->sequence = ptr[4] << 8 | ptr[5]; ++ if ((__s16)(meta->sequence - opt->recv_sequence) < 0) ++ goto drop; ++ } ++ ++ /* Skip PPP address and control if they are present. */ ++ if (skb->len >= 2 && skb->data[0] == PPP_ADDR && ++ skb->data[1] == PPP_CTRL) ++ skb_pull(skb, 2); ++ ++ /* Fix PPP protocol if it is compressed. */ ++ if (skb->len >= 1 && skb->data[0] & 1) ++ skb_push(skb, 1)[0] = 0; ++ ++ /* Drop the packet if PPP protocol is missing. */ ++ if (skb->len < 2) ++ goto drop; ++ ++ /* Perform reordering if sequencing is enabled. */ ++ atomic_set(&opt->sequencing, bits & L2TP_SEQUENCE_BIT); ++ if (bits & L2TP_SEQUENCE_BIT) { ++ struct sk_buff *skb1; ++ ++ /* Insert the packet into receive queue in order. */ ++ skb_set_owner_r(skb, sk); ++ skb_queue_walk(&sk->sk_receive_queue, skb1) { ++ struct meta *meta1 = skb_meta(skb1); ++ __s16 order = meta->sequence - meta1->sequence; ++ if (order == 0) ++ goto drop; ++ if (order < 0) { ++ meta->timestamp = meta1->timestamp; ++ skb_insert(skb1, skb, &sk->sk_receive_queue); ++ skb = NULL; ++ break; ++ } ++ } ++ if (skb) { ++ meta->timestamp = now; ++ skb_queue_tail(&sk->sk_receive_queue, skb); ++ } ++ ++ /* Remove packets from receive queue as long as ++ * 1. the receive buffer is full, ++ * 2. they are queued longer than one second, or ++ * 3. there are no missing packets before them. */ ++ skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) { ++ meta = skb_meta(skb); ++ if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && ++ now - meta->timestamp < HZ && ++ meta->sequence != opt->recv_sequence) ++ break; ++ skb_unlink(skb, &sk->sk_receive_queue); ++ opt->recv_sequence = (__u16)(meta->sequence + 1); ++ skb_orphan(skb); ++ ppp_input(&pppox_sk(sk)->chan, skb); ++ } ++ return NET_RX_SUCCESS; ++ } ++ ++ /* Flush receive queue if sequencing is disabled. */ ++ skb_queue_purge(&sk->sk_receive_queue); ++ skb_orphan(skb); ++ ppp_input(&pppox_sk(sk)->chan, skb); ++ return NET_RX_SUCCESS; ++drop: ++ kfree_skb(skb); ++ return NET_RX_DROP; ++} ++ ++static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb) ++{ ++ sock_hold(sk_udp); ++ sk_receive_skb(sk_udp, skb, 0); ++ return 0; ++} ++ ++static struct sk_buff_head delivery_queue; ++ ++static void pppolac_xmit_core(struct work_struct *delivery_work) ++{ ++ mm_segment_t old_fs = get_fs(); ++ struct sk_buff *skb; ++ ++ set_fs(KERNEL_DS); ++ while ((skb = skb_dequeue(&delivery_queue))) { ++ struct sock *sk_udp = skb->sk; ++ struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len}; ++ struct msghdr msg = { ++ .msg_iov = (struct iovec *)&iov, ++ .msg_iovlen = 1, ++ .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, ++ }; ++ sk_udp->sk_prot->sendmsg(NULL, sk_udp, &msg, skb->len); ++ kfree_skb(skb); ++ } ++ set_fs(old_fs); ++} ++ ++static DECLARE_WORK(delivery_work, pppolac_xmit_core); ++ ++static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb) ++{ ++ struct sock *sk_udp = (struct sock *)chan->private; ++ struct pppolac_opt *opt = &pppox_sk(sk_udp->sk_user_data)->proto.lac; ++ ++ /* Install PPP address and control. */ ++ skb_push(skb, 2); ++ skb->data[0] = PPP_ADDR; ++ skb->data[1] = PPP_CTRL; ++ ++ /* Install L2TP header. */ ++ if (atomic_read(&opt->sequencing)) { ++ skb_push(skb, 10); ++ skb->data[0] = L2TP_SEQUENCE_BIT; ++ skb->data[6] = opt->xmit_sequence >> 8; ++ skb->data[7] = opt->xmit_sequence; ++ skb->data[8] = 0; ++ skb->data[9] = 0; ++ opt->xmit_sequence++; ++ } else { ++ skb_push(skb, 6); ++ skb->data[0] = 0; ++ } ++ skb->data[1] = L2TP_VERSION; ++ unaligned(&skb->data[2])->u32 = opt->remote; ++ ++ /* Now send the packet via the delivery queue. */ ++ skb_set_owner_w(skb, sk_udp); ++ skb_queue_tail(&delivery_queue, skb); ++ schedule_work(&delivery_work); ++ return 1; ++} ++ ++/******************************************************************************/ ++ ++static struct ppp_channel_ops pppolac_channel_ops = { ++ .start_xmit = pppolac_xmit, ++}; ++ ++static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr, ++ int addrlen, int flags) ++{ ++ struct sock *sk = sock->sk; ++ struct pppox_sock *po = pppox_sk(sk); ++ struct sockaddr_pppolac *addr = (struct sockaddr_pppolac *)useraddr; ++ struct socket *sock_udp = NULL; ++ struct sock *sk_udp; ++ int error; ++ ++ if (addrlen != sizeof(struct sockaddr_pppolac) || ++ !addr->local.tunnel || !addr->local.session || ++ !addr->remote.tunnel || !addr->remote.session) { ++ return -EINVAL; ++ } ++ ++ lock_sock(sk); ++ error = -EALREADY; ++ if (sk->sk_state != PPPOX_NONE) ++ goto out; ++ ++ sock_udp = sockfd_lookup(addr->udp_socket, &error); ++ if (!sock_udp) ++ goto out; ++ sk_udp = sock_udp->sk; ++ lock_sock(sk_udp); ++ ++ /* Remove this check when IPv6 supports UDP encapsulation. */ ++ error = -EAFNOSUPPORT; ++ if (sk_udp->sk_family != AF_INET) ++ goto out; ++ error = -EPROTONOSUPPORT; ++ if (sk_udp->sk_protocol != IPPROTO_UDP) ++ goto out; ++ error = -EDESTADDRREQ; ++ if (sk_udp->sk_state != TCP_ESTABLISHED) ++ goto out; ++ error = -EBUSY; ++ if (udp_sk(sk_udp)->encap_type || sk_udp->sk_user_data) ++ goto out; ++ if (!sk_udp->sk_bound_dev_if) { ++ struct dst_entry *dst = sk_dst_get(sk_udp); ++ error = -ENODEV; ++ if (!dst) ++ goto out; ++ sk_udp->sk_bound_dev_if = dst->dev->ifindex; ++ dst_release(dst); ++ } ++ ++ po->chan.hdrlen = 12; ++ po->chan.private = sk_udp; ++ po->chan.ops = &pppolac_channel_ops; ++ po->chan.mtu = PPP_MRU - 80; ++ po->proto.lac.local = unaligned(&addr->local)->u32; ++ po->proto.lac.remote = unaligned(&addr->remote)->u32; ++ atomic_set(&po->proto.lac.sequencing, 1); ++ po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv; ++ ++ error = ppp_register_channel(&po->chan); ++ if (error) ++ goto out; ++ ++ sk->sk_state = PPPOX_CONNECTED; ++ udp_sk(sk_udp)->encap_type = UDP_ENCAP_L2TPINUDP; ++ udp_sk(sk_udp)->encap_rcv = pppolac_recv; ++ sk_udp->sk_backlog_rcv = pppolac_recv_core; ++ sk_udp->sk_user_data = sk; ++out: ++ if (sock_udp) { ++ release_sock(sk_udp); ++ if (error) ++ sockfd_put(sock_udp); ++ } ++ release_sock(sk); ++ return error; ++} ++ ++static int pppolac_release(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ if (!sk) ++ return 0; ++ ++ lock_sock(sk); ++ if (sock_flag(sk, SOCK_DEAD)) { ++ release_sock(sk); ++ return -EBADF; ++ } ++ ++ if (sk->sk_state != PPPOX_NONE) { ++ struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private; ++ lock_sock(sk_udp); ++ skb_queue_purge(&sk->sk_receive_queue); ++ pppox_unbind_sock(sk); ++ udp_sk(sk_udp)->encap_type = 0; ++ udp_sk(sk_udp)->encap_rcv = NULL; ++ sk_udp->sk_backlog_rcv = pppox_sk(sk)->proto.lac.backlog_rcv; ++ sk_udp->sk_user_data = NULL; ++ release_sock(sk_udp); ++ sockfd_put(sk_udp->sk_socket); ++ } ++ ++ sock_orphan(sk); ++ sock->sk = NULL; ++ release_sock(sk); ++ sock_put(sk); ++ return 0; ++} ++ ++/******************************************************************************/ ++ ++static struct proto pppolac_proto = { ++ .name = "PPPOLAC", ++ .owner = THIS_MODULE, ++ .obj_size = sizeof(struct pppox_sock), ++}; ++ ++static struct proto_ops pppolac_proto_ops = { ++ .family = PF_PPPOX, ++ .owner = THIS_MODULE, ++ .release = pppolac_release, ++ .bind = sock_no_bind, ++ .connect = pppolac_connect, ++ .socketpair = sock_no_socketpair, ++ .accept = sock_no_accept, ++ .getname = sock_no_getname, ++ .poll = sock_no_poll, ++ .ioctl = pppox_ioctl, ++ .listen = sock_no_listen, ++ .shutdown = sock_no_shutdown, ++ .setsockopt = sock_no_setsockopt, ++ .getsockopt = sock_no_getsockopt, ++ .sendmsg = sock_no_sendmsg, ++ .recvmsg = sock_no_recvmsg, ++ .mmap = sock_no_mmap, ++}; ++ ++static int pppolac_create(struct net *net, struct socket *sock) ++{ ++ struct sock *sk; ++ ++ sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppolac_proto); ++ if (!sk) ++ return -ENOMEM; ++ ++ sock_init_data(sock, sk); ++ sock->state = SS_UNCONNECTED; ++ sock->ops = &pppolac_proto_ops; ++ sk->sk_protocol = PX_PROTO_OLAC; ++ sk->sk_state = PPPOX_NONE; ++ return 0; ++} ++ ++/******************************************************************************/ ++ ++static struct pppox_proto pppolac_pppox_proto = { ++ .create = pppolac_create, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init pppolac_init(void) ++{ ++ int error; ++ ++ error = proto_register(&pppolac_proto, 0); ++ if (error) ++ return error; ++ ++ error = register_pppox_proto(PX_PROTO_OLAC, &pppolac_pppox_proto); ++ if (error) ++ proto_unregister(&pppolac_proto); ++ else ++ skb_queue_head_init(&delivery_queue); ++ return error; ++} ++ ++static void __exit pppolac_exit(void) ++{ ++ unregister_pppox_proto(PX_PROTO_OLAC); ++ proto_unregister(&pppolac_proto); ++} ++ ++module_init(pppolac_init); ++module_exit(pppolac_exit); ++ ++MODULE_DESCRIPTION("PPP on L2TP Access Concentrator (PPPoLAC)"); ++MODULE_AUTHOR("Chia-chi Yeh <chiachi@android.com>"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c +new file mode 100644 +index 0000000..dc15f97 +--- /dev/null ++++ b/drivers/net/ppp/pppopns.c +@@ -0,0 +1,428 @@ ++/* drivers/net/pppopns.c ++ * ++ * Driver for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) ++ * ++ * Copyright (C) 2009 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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. ++ */ ++ ++/* This driver handles PPTP data packets between a RAW socket and a PPP channel. ++ * The socket is created in the kernel space and connected to the same address ++ * of the control socket. Outgoing packets are always sent with sequences but ++ * without acknowledgements. Incoming packets with sequences are reordered ++ * within a sliding window of one second. Currently reordering only happens when ++ * a packet is received. It is done for simplicity since no additional locks or ++ * threads are required. This driver should work on both IPv4 and IPv6. */ ++ ++#include <linux/module.h> ++#include <linux/jiffies.h> ++#include <linux/workqueue.h> ++#include <linux/skbuff.h> ++#include <linux/file.h> ++#include <linux/netdevice.h> ++#include <linux/net.h> ++#include <linux/ppp_defs.h> ++#include <linux/if.h> ++#include <linux/if_ppp.h> ++#include <linux/if_pppox.h> ++#include <linux/ppp_channel.h> ++#include <asm/uaccess.h> ++ ++#define GRE_HEADER_SIZE 8 ++ ++#define PPTP_GRE_BITS htons(0x2001) ++#define PPTP_GRE_BITS_MASK htons(0xEF7F) ++#define PPTP_GRE_SEQ_BIT htons(0x1000) ++#define PPTP_GRE_ACK_BIT htons(0x0080) ++#define PPTP_GRE_TYPE htons(0x880B) ++ ++#define PPP_ADDR 0xFF ++#define PPP_CTRL 0x03 ++ ++struct header { ++ __u16 bits; ++ __u16 type; ++ __u16 length; ++ __u16 call; ++ __u32 sequence; ++} __attribute__((packed)); ++ ++struct meta { ++ __u32 sequence; ++ __u32 timestamp; ++}; ++ ++static inline struct meta *skb_meta(struct sk_buff *skb) ++{ ++ return (struct meta *)skb->cb; ++} ++ ++/******************************************************************************/ ++ ++static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) ++{ ++ struct sock *sk = (struct sock *)sk_raw->sk_user_data; ++ struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns; ++ struct meta *meta = skb_meta(skb); ++ __u32 now = jiffies; ++ struct header *hdr; ++ ++ /* Skip transport header */ ++ skb_pull(skb, skb_transport_header(skb) - skb->data); ++ ++ /* Drop the packet if GRE header is missing. */ ++ if (skb->len < GRE_HEADER_SIZE) ++ goto drop; ++ hdr = (struct header *)skb->data; ++ ++ /* Check the header. */ ++ if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || ++ (hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS) ++ goto drop; ++ ++ /* Skip all fields including optional ones. */ ++ if (!skb_pull(skb, GRE_HEADER_SIZE + ++ (hdr->bits & PPTP_GRE_SEQ_BIT ? 4 : 0) + ++ (hdr->bits & PPTP_GRE_ACK_BIT ? 4 : 0))) ++ goto drop; ++ ++ /* Check the length. */ ++ if (skb->len != ntohs(hdr->length)) ++ goto drop; ++ ++ /* Check the sequence if it is present. */ ++ if (hdr->bits & PPTP_GRE_SEQ_BIT) { ++ meta->sequence = ntohl(hdr->sequence); ++ if ((__s32)(meta->sequence - opt->recv_sequence) < 0) ++ goto drop; ++ } ++ ++ /* Skip PPP address and control if they are present. */ ++ if (skb->len >= 2 && skb->data[0] == PPP_ADDR && ++ skb->data[1] == PPP_CTRL) ++ skb_pull(skb, 2); ++ ++ /* Fix PPP protocol if it is compressed. */ ++ if (skb->len >= 1 && skb->data[0] & 1) ++ skb_push(skb, 1)[0] = 0; ++ ++ /* Drop the packet if PPP protocol is missing. */ ++ if (skb->len < 2) ++ goto drop; ++ ++ /* Perform reordering if sequencing is enabled. */ ++ if (hdr->bits & PPTP_GRE_SEQ_BIT) { ++ struct sk_buff *skb1; ++ ++ /* Insert the packet into receive queue in order. */ ++ skb_set_owner_r(skb, sk); ++ skb_queue_walk(&sk->sk_receive_queue, skb1) { ++ struct meta *meta1 = skb_meta(skb1); ++ __s32 order = meta->sequence - meta1->sequence; ++ if (order == 0) ++ goto drop; ++ if (order < 0) { ++ meta->timestamp = meta1->timestamp; ++ skb_insert(skb1, skb, &sk->sk_receive_queue); ++ skb = NULL; ++ break; ++ } ++ } ++ if (skb) { ++ meta->timestamp = now; ++ skb_queue_tail(&sk->sk_receive_queue, skb); ++ } ++ ++ /* Remove packets from receive queue as long as ++ * 1. the receive buffer is full, ++ * 2. they are queued longer than one second, or ++ * 3. there are no missing packets before them. */ ++ skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) { ++ meta = skb_meta(skb); ++ if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && ++ now - meta->timestamp < HZ && ++ meta->sequence != opt->recv_sequence) ++ break; ++ skb_unlink(skb, &sk->sk_receive_queue); ++ opt->recv_sequence = meta->sequence + 1; ++ skb_orphan(skb); ++ ppp_input(&pppox_sk(sk)->chan, skb); ++ } ++ return NET_RX_SUCCESS; ++ } ++ ++ /* Flush receive queue if sequencing is disabled. */ ++ skb_queue_purge(&sk->sk_receive_queue); ++ skb_orphan(skb); ++ ppp_input(&pppox_sk(sk)->chan, skb); ++ return NET_RX_SUCCESS; ++drop: ++ kfree_skb(skb); ++ return NET_RX_DROP; ++} ++ ++static void pppopns_recv(struct sock *sk_raw) ++{ ++ struct sk_buff *skb; ++ while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) { ++ sock_hold(sk_raw); ++ sk_receive_skb(sk_raw, skb, 0); ++ } ++} ++ ++static struct sk_buff_head delivery_queue; ++ ++static void pppopns_xmit_core(struct work_struct *delivery_work) ++{ ++ mm_segment_t old_fs = get_fs(); ++ struct sk_buff *skb; ++ ++ set_fs(KERNEL_DS); ++ while ((skb = skb_dequeue(&delivery_queue))) { ++ struct sock *sk_raw = skb->sk; ++ struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len}; ++ struct msghdr msg = { ++ .msg_iov = (struct iovec *)&iov, ++ .msg_iovlen = 1, ++ .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, ++ }; ++ sk_raw->sk_prot->sendmsg(NULL, sk_raw, &msg, skb->len); ++ kfree_skb(skb); ++ } ++ set_fs(old_fs); ++} ++ ++static DECLARE_WORK(delivery_work, pppopns_xmit_core); ++ ++static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) ++{ ++ struct sock *sk_raw = (struct sock *)chan->private; ++ struct pppopns_opt *opt = &pppox_sk(sk_raw->sk_user_data)->proto.pns; ++ struct header *hdr; ++ __u16 length; ++ ++ /* Install PPP address and control. */ ++ skb_push(skb, 2); ++ skb->data[0] = PPP_ADDR; ++ skb->data[1] = PPP_CTRL; ++ length = skb->len; ++ ++ /* Install PPTP GRE header. */ ++ hdr = (struct header *)skb_push(skb, 12); ++ hdr->bits = PPTP_GRE_BITS | PPTP_GRE_SEQ_BIT; ++ hdr->type = PPTP_GRE_TYPE; ++ hdr->length = htons(length); ++ hdr->call = opt->remote; ++ hdr->sequence = htonl(opt->xmit_sequence); ++ opt->xmit_sequence++; ++ ++ /* Now send the packet via the delivery queue. */ ++ skb_set_owner_w(skb, sk_raw); ++ skb_queue_tail(&delivery_queue, skb); ++ schedule_work(&delivery_work); ++ return 1; ++} ++ ++/******************************************************************************/ ++ ++static struct ppp_channel_ops pppopns_channel_ops = { ++ .start_xmit = pppopns_xmit, ++}; ++ ++static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, ++ int addrlen, int flags) ++{ ++ struct sock *sk = sock->sk; ++ struct pppox_sock *po = pppox_sk(sk); ++ struct sockaddr_pppopns *addr = (struct sockaddr_pppopns *)useraddr; ++ struct sockaddr_storage ss; ++ struct socket *sock_tcp = NULL; ++ struct socket *sock_raw = NULL; ++ struct sock *sk_tcp; ++ struct sock *sk_raw; ++ int error; ++ ++ if (addrlen != sizeof(struct sockaddr_pppopns)) ++ return -EINVAL; ++ ++ lock_sock(sk); ++ error = -EALREADY; ++ if (sk->sk_state != PPPOX_NONE) ++ goto out; ++ ++ sock_tcp = sockfd_lookup(addr->tcp_socket, &error); ++ if (!sock_tcp) ++ goto out; ++ sk_tcp = sock_tcp->sk; ++ error = -EPROTONOSUPPORT; ++ if (sk_tcp->sk_protocol != IPPROTO_TCP) ++ goto out; ++ addrlen = sizeof(struct sockaddr_storage); ++ error = kernel_getpeername(sock_tcp, (struct sockaddr *)&ss, &addrlen); ++ if (error) ++ goto out; ++ if (!sk_tcp->sk_bound_dev_if) { ++ struct dst_entry *dst = sk_dst_get(sk_tcp); ++ error = -ENODEV; ++ if (!dst) ++ goto out; ++ sk_tcp->sk_bound_dev_if = dst->dev->ifindex; ++ dst_release(dst); ++ } ++ ++ error = sock_create(ss.ss_family, SOCK_RAW, IPPROTO_GRE, &sock_raw); ++ if (error) ++ goto out; ++ sk_raw = sock_raw->sk; ++ sk_raw->sk_bound_dev_if = sk_tcp->sk_bound_dev_if; ++ error = kernel_connect(sock_raw, (struct sockaddr *)&ss, addrlen, 0); ++ if (error) ++ goto out; ++ ++ po->chan.hdrlen = 14; ++ po->chan.private = sk_raw; ++ po->chan.ops = &pppopns_channel_ops; ++ po->chan.mtu = PPP_MRU - 80; ++ po->proto.pns.local = addr->local; ++ po->proto.pns.remote = addr->remote; ++ po->proto.pns.data_ready = sk_raw->sk_data_ready; ++ po->proto.pns.backlog_rcv = sk_raw->sk_backlog_rcv; ++ ++ error = ppp_register_channel(&po->chan); ++ if (error) ++ goto out; ++ ++ sk->sk_state = PPPOX_CONNECTED; ++ lock_sock(sk_raw); ++ sk_raw->sk_data_ready = pppopns_recv; ++ sk_raw->sk_backlog_rcv = pppopns_recv_core; ++ sk_raw->sk_user_data = sk; ++ release_sock(sk_raw); ++out: ++ if (sock_tcp) ++ sockfd_put(sock_tcp); ++ if (error && sock_raw) ++ sock_release(sock_raw); ++ release_sock(sk); ++ return error; ++} ++ ++static int pppopns_release(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ if (!sk) ++ return 0; ++ ++ lock_sock(sk); ++ if (sock_flag(sk, SOCK_DEAD)) { ++ release_sock(sk); ++ return -EBADF; ++ } ++ ++ if (sk->sk_state != PPPOX_NONE) { ++ struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private; ++ lock_sock(sk_raw); ++ skb_queue_purge(&sk->sk_receive_queue); ++ pppox_unbind_sock(sk); ++ sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready; ++ sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv; ++ sk_raw->sk_user_data = NULL; ++ release_sock(sk_raw); ++ sock_release(sk_raw->sk_socket); ++ } ++ ++ sock_orphan(sk); ++ sock->sk = NULL; ++ release_sock(sk); ++ sock_put(sk); ++ return 0; ++} ++ ++/******************************************************************************/ ++ ++static struct proto pppopns_proto = { ++ .name = "PPPOPNS", ++ .owner = THIS_MODULE, ++ .obj_size = sizeof(struct pppox_sock), ++}; ++ ++static struct proto_ops pppopns_proto_ops = { ++ .family = PF_PPPOX, ++ .owner = THIS_MODULE, ++ .release = pppopns_release, ++ .bind = sock_no_bind, ++ .connect = pppopns_connect, ++ .socketpair = sock_no_socketpair, ++ .accept = sock_no_accept, ++ .getname = sock_no_getname, ++ .poll = sock_no_poll, ++ .ioctl = pppox_ioctl, ++ .listen = sock_no_listen, ++ .shutdown = sock_no_shutdown, ++ .setsockopt = sock_no_setsockopt, ++ .getsockopt = sock_no_getsockopt, ++ .sendmsg = sock_no_sendmsg, ++ .recvmsg = sock_no_recvmsg, ++ .mmap = sock_no_mmap, ++}; ++ ++static int pppopns_create(struct net *net, struct socket *sock) ++{ ++ struct sock *sk; ++ ++ sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppopns_proto); ++ if (!sk) ++ return -ENOMEM; ++ ++ sock_init_data(sock, sk); ++ sock->state = SS_UNCONNECTED; ++ sock->ops = &pppopns_proto_ops; ++ sk->sk_protocol = PX_PROTO_OPNS; ++ sk->sk_state = PPPOX_NONE; ++ return 0; ++} ++ ++/******************************************************************************/ ++ ++static struct pppox_proto pppopns_pppox_proto = { ++ .create = pppopns_create, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init pppopns_init(void) ++{ ++ int error; ++ ++ error = proto_register(&pppopns_proto, 0); ++ if (error) ++ return error; ++ ++ error = register_pppox_proto(PX_PROTO_OPNS, &pppopns_pppox_proto); ++ if (error) ++ proto_unregister(&pppopns_proto); ++ else ++ skb_queue_head_init(&delivery_queue); ++ return error; ++} ++ ++static void __exit pppopns_exit(void) ++{ ++ unregister_pppox_proto(PX_PROTO_OPNS); ++ proto_unregister(&pppopns_proto); ++} ++ ++module_init(pppopns_init); ++module_exit(pppopns_exit); ++ ++MODULE_DESCRIPTION("PPP on PPTP Network Server (PPPoPNS)"); ++MODULE_AUTHOR("Chia-chi Yeh <chiachi@android.com>"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/net/tun.c b/drivers/net/tun.c +index 9dd3746..cae87f0 100644 +--- a/drivers/net/tun.c ++++ b/drivers/net/tun.c +@@ -1881,6 +1881,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, + unsigned int ifindex; + int ret; + ++#ifdef CONFIG_ANDROID_PARANOID_NETWORK ++ if (cmd != TUNGETIFF && !capable(CAP_NET_ADMIN)) { ++ return -EPERM; ++ } ++#endif ++ + if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || _IOC_TYPE(cmd) == 0x89) { + if (copy_from_user(&ifr, argp, ifreq_len)) + return -EFAULT; +diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig +index 16604bd..710733f 100644 +--- a/drivers/net/wireless/Kconfig ++++ b/drivers/net/wireless/Kconfig +@@ -265,6 +265,11 @@ config MWL8K + To compile this driver as a module, choose M here: the module + will be called mwl8k. If unsure, say N. + ++config WIFI_CONTROL_FUNC ++ bool "Enable WiFi control function abstraction" ++ help ++ Enables Power/Reset/Carddetect function abstraction ++ + source "drivers/net/wireless/ath/Kconfig" + source "drivers/net/wireless/b43/Kconfig" + source "drivers/net/wireless/b43legacy/Kconfig" +diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c +index d134710..821f6b5 100644 +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -885,36 +885,66 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname, + return 0; + } + ++/* ++ * Convert configs to something easy to use in C code ++ */ ++#if defined(CONFIG_CMDLINE_FORCE) ++static const int overwrite_incoming_cmdline = 1; ++static const int read_dt_cmdline; ++static const int concat_cmdline; ++#elif defined(CONFIG_CMDLINE_EXTEND) ++static const int overwrite_incoming_cmdline; ++static const int read_dt_cmdline = 1; ++static const int concat_cmdline = 1; ++#else /* CMDLINE_FROM_BOOTLOADER */ ++static const int overwrite_incoming_cmdline; ++static const int read_dt_cmdline = 1; ++static const int concat_cmdline; ++#endif ++ ++#ifdef CONFIG_CMDLINE ++static const char *config_cmdline = CONFIG_CMDLINE; ++#else ++static const char *config_cmdline = ""; ++#endif ++ + int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, + int depth, void *data) + { +- int l; +- const char *p; ++ int l = 0; ++ const char *p = NULL; ++ char *cmdline = data; + + pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname); + +- if (depth != 1 || !data || ++ if (depth != 1 || !cmdline || + (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) + return 0; + + early_init_dt_check_for_initrd(node); + +- /* Retrieve command line */ +- p = of_get_flat_dt_prop(node, "bootargs", &l); +- if (p != NULL && l > 0) +- strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); +- +- /* +- * CONFIG_CMDLINE is meant to be a default in case nothing else +- * managed to set the command line, unless CONFIG_CMDLINE_FORCE +- * is set in which case we override whatever was found earlier. +- */ +-#ifdef CONFIG_CMDLINE +-#ifndef CONFIG_CMDLINE_FORCE +- if (!((char *)data)[0]) +-#endif +- strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); +-#endif /* CONFIG_CMDLINE */ ++ /* Put CONFIG_CMDLINE in if forced or if data had nothing in it to start */ ++ if (overwrite_incoming_cmdline || !cmdline[0]) ++ strlcpy(cmdline, config_cmdline, COMMAND_LINE_SIZE); ++ ++ /* Retrieve command line unless forcing */ ++ if (read_dt_cmdline) ++ p = of_get_flat_dt_prop(node, "bootargs", &l); ++ ++ if (p != NULL && l > 0) { ++ if (concat_cmdline) { ++ int cmdline_len; ++ int copy_len; ++ strlcat(cmdline, " ", COMMAND_LINE_SIZE); ++ cmdline_len = strlen(cmdline); ++ copy_len = COMMAND_LINE_SIZE - cmdline_len - 1; ++ copy_len = min((int)l, copy_len); ++ strncpy(cmdline + cmdline_len, p, copy_len); ++ cmdline[cmdline_len + copy_len] = '\0'; ++ } else { ++ strlcpy(cmdline, p, min((int)l, COMMAND_LINE_SIZE)); ++ } ++ } + + pr_debug("Command line is: %s\n", (char*)data); + +diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c +index 1bd4305..a925f29 100644 +--- a/drivers/of/of_mdio.c ++++ b/drivers/of/of_mdio.c +@@ -18,6 +18,7 @@ + #include <linux/of.h> + #include <linux/of_irq.h> + #include <linux/of_mdio.h> ++#include <linux/of_net.h> + #include <linux/module.h> + + MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); +@@ -231,6 +232,41 @@ struct phy_device *of_phy_connect(struct net_device *dev, + EXPORT_SYMBOL(of_phy_connect); + + /** ++ * of_phy_get_and_connect ++ * - Get phy node and connect to the phy described in the device tree ++ * @dev: pointer to net_device claiming the phy ++ * @np: Pointer to device tree node for the net_device claiming the phy ++ * @hndlr: Link state callback for the network device ++ * ++ * If successful, returns a pointer to the phy_device with the embedded ++ * struct device refcount incremented by one, or NULL on failure. The ++ * refcount must be dropped by calling phy_disconnect() or phy_detach(). ++ */ ++struct phy_device *of_phy_get_and_connect(struct net_device *dev, ++ struct device_node *np, ++ void (*hndlr)(struct net_device *)) ++{ ++ phy_interface_t iface; ++ struct device_node *phy_np; ++ struct phy_device *phy; ++ ++ iface = of_get_phy_mode(np); ++ if (iface < 0) ++ return NULL; ++ ++ phy_np = of_parse_phandle(np, "phy-handle", 0); ++ if (!phy_np) ++ return NULL; ++ ++ phy = of_phy_connect(dev, phy_np, hndlr, 0, iface); ++ ++ of_node_put(phy_np); ++ ++ return phy; ++} ++EXPORT_SYMBOL(of_phy_get_and_connect); ++ ++/** + * of_phy_attach - Attach to a PHY without starting the state machine + * @dev: pointer to net_device claiming the phy + * @phy_np: Node pointer for the PHY +diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile +index e04fe2d..0db2477 100644 +--- a/drivers/pci/Makefile ++++ b/drivers/pci/Makefile +@@ -9,6 +9,7 @@ obj-$(CONFIG_PROC_FS) += proc.o + obj-$(CONFIG_SYSFS) += slot.o + + obj-$(CONFIG_PCI_QUIRKS) += quirks.o ++obj-$(CONFIG_HIPCIE) += hipcie/ + + # Build PCI Express stuff if needed + obj-$(CONFIG_PCIEPORTBUS) += pcie/ +diff --git a/drivers/pci/hipcie/Kconfig b/drivers/pci/hipcie/Kconfig +new file mode 100644 +index 0000000..4e5cfc6 +--- /dev/null ++++ b/drivers/pci/hipcie/Kconfig +@@ -0,0 +1,119 @@ ++menuconfig HIPCIE ++ bool "Hisilicon PCI Express support" ++ depends on PCI && (ARCH_HI3519 || ARCH_HI3519V101 || ARCH_HI3559 || ARCH_HI3556 || ARCH_HI3516AV200 || ARCH_HI3531D) ++ default y if PCI ++ default n if !PCI ++ help ++ Hisilicon PCI Express support ++ Choose this selection to support PCI Express uses. ++ ++if HIPCIE ++ ++menu "PCI Express configs" ++ ++config PCIE0_SEL ++ int "PCI Express controller 0 sel" ++ range 0 1 ++ default "0" if !PCI ++ default "1" if PCI ++ depends on PCI ++ help ++ PCI Express controller 0 sel. ++ Set 0 to disable controller 0 as RC. ++ set 1 to enable controller 0 to work at RC mode. ++ ++config PCIE0_DEVICES_MEM_SIZE ++ hex "Total memory size of PCI Express EP devices" ++ range 0x0 0x10000000 if ARCH_HI3519 ++ range 0x0 0x10000000 if ARCH_HI3519V101 ++ range 0x0 0x10000000 if ARCH_HI3516AV200 ++ range 0x0 0x10000000 if ARCH_HI3559 ++ range 0x0 0x10000000 if ARCH_HI3556 ++ range 0x0 0x10000000 if ARCH_HI3531D ++ default "0x8000000" if ARCH_HI3519 ++ default "0x8000000" if ARCH_HI3519V101 ++ default "0x8000000" if ARCH_HI3516AV200 ++ default "0x8000000" if ARCH_HI3559 ++ default "0x8000000" if ARCH_HI3556 ++ default "0x8000000" if ARCH_HI3531D ++ depends on PCI ++ help ++ Memory available for all pcie EP devices in pci subsystem. ++ Hisilicon PCI Express controller provides up to 256MBytes address ++ space for its subordinated devices. ++ No IO address space is reserved, since to support PCI legacy devices ++ which required IO address space. ++ You can change this value as you please. ++ ++config PCIE0_DEVICES_CONFIG_SIZE ++ hex "Sum of configuration header size mapped for all PCIe EP devices" ++ range 0x0 0x10000000 if ARCH_HI3519 ++ range 0x0 0x10000000 if ARCH_HI3519V101 ++ range 0x0 0x10000000 if ARCH_HI3516AV200 ++ range 0x0 0x10000000 if ARCH_HI3559 ++ range 0x0 0x10000000 if ARCH_HI3556 ++ range 0x0 0x10000000 if ARCH_HI3531D ++ default 0x800000 if ARCH_HI3519 ++ default 0x800000 if ARCH_HI3519V101 ++ default 0x800000 if ARCH_HI3516AV200 ++ default 0x800000 if ARCH_HI3559 ++ default 0x800000 if ARCH_HI3556 ++ default 0x8000000 if ARCH_HI3531D ++ depends on PCI ++ help ++ As to the PCIe address space configuration, address space for all EPs ++ is up to 256Mbytes. But, normally people do not used that much. Each ++ EP device will use 4Kbytes virtual address space for PCIe configuration header. ++ Normally people will not use that much(256MB). ++ Enlarge this value will require more system virtual address space. ++ The DEFAULT value(8MB) is enough for most applications. ++ ++config LIMIT_MAX_RD_REQ_SIZE ++ bool "limit pcie max read request size" ++ default y ++ depends on PCI && HIPCIE ++ help ++ The default max read request size of pcie device is 512 Byte. When pcie use ++ the card of pcie-to-sata to connect to the sata disk, with the default max read ++ request size value of 512 byte, would cause the low bandwidth of VDP. If you enable ++ the LIMIT_MAX_RD_REQ_SIZE config, the max read request size of pcie device would be ++ set to 128 byte, and the problem of VDP low band width also be avoided. ++ ++config PCIE1_SEL ++ int "PCI Express controller 1 sel" ++ range 0 1 ++ default "0" ++ depends on (PCI && (ARCH_GODNET || ARCH_HI3531D)) ++ help ++ PCI Express controller 1 sel. ++ Set 0 to disable controller 1, ++ set 1 to enable controller 1 to work at RC mode. ++ ++config PCIE1_DEVICES_MEM_SIZE ++ hex "Total memory size of PCI Express EP devices" ++ range 0x0 0x8000000 ++ default "0x7800000" ++ depends on (PCI && ARCH_HI3531D) ++ help ++ All memory size required by all devices in pci subsystem. ++ Hisilicon PCI Express controller provide up to 128M memory ++ and io size for device. ++ Here we set memory size up to 120M, means that io size has at least 8M ++ You can change this depend on you device connected ++ to Hisilicon PCI Express controller. ++ ++config PCIE1_DEVICES_CONFIG_SIZE ++ hex "Total configuration header size of PCI Express system devices" ++ range 0x0 0x10000000 ++ default 0x800000 ++ depends on (PCI && ARCH_HI3531D) ++ help ++ All configuration size required by devices connnect to ++ Hisilicon PCI Express controller. ++ NOTE: This will alloc memory from kernel, ++ enlarge this will require the same memory. ++ The default value is enough for most applications. ++ ++endmenu ++ ++endif +diff --git a/drivers/pci/hipcie/Makefile b/drivers/pci/hipcie/Makefile +new file mode 100644 +index 0000000..0455420 +--- /dev/null ++++ b/drivers/pci/hipcie/Makefile +@@ -0,0 +1,8 @@ ++ ++obj-$(CONFIG_HIPCIE) += hipcie.o ++ ++hipcie-objs := pcie.o ++ ++ifeq ($(CONFIG_PCI_DEBUG),y) ++ EXTRA_CFLAGS += -DPCIE_DEBUG ++endif +diff --git a/drivers/pci/hipcie/pcie.c b/drivers/pci/hipcie/pcie.c +new file mode 100644 +index 0000000..2f1e9d1 +--- /dev/null ++++ b/drivers/pci/hipcie/pcie.c +@@ -0,0 +1,732 @@ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/pci.h> ++#include <linux/mbus.h> ++#include <asm/irq.h> ++#include <asm/mach/pci.h> ++#include <linux/delay.h> ++#include <linux/spinlock.h> ++#include <linux/version.h> ++#include <asm/siginfo.h> ++ ++#define PCIE_DBG_REG 1 ++#define PCIE_DBG_FUNC 2 ++#define PCIE_DBG_MODULE 3 ++ ++#define PCIE_DEBUG_LEVEL PCIE_DBG_MODULE ++ ++/*#define PCIE_DEBUG*/ ++#ifdef PCIE_DEBUG ++#define pcie_debug(level, str, arg...)\ ++ do {\ ++ if ((level) <= PCIE_DEBUG_LEVEL) {\ ++ pr_debug("%s->%d," str "\n", \ ++ __func__, __LINE__, ##arg);\ ++ } \ ++ } while (0) ++#else ++#define pcie_debug(level, str, arg...) ++#endif ++ ++#define pcie_assert(con)\ ++ do {\ ++ if (!(con)) {\ ++ pr_err("%s->%d,assert fail!\n", \ ++ __func__, __LINE__);\ ++ } \ ++ } while (0) ++ ++#define pcie_error(str, arg...)\ ++ pr_err("%s->%d" str "\n", __func__, __LINE__, ##arg) ++ ++ ++#define __256MB__ 0x10000000 ++#define __128MB__ 0x8000000 ++#define __4KB__ 0x1000 ++#define __8KB__ 0x2000 ++ ++enum pcie_sel { ++ /* ++ * No controller selected. ++ */ ++ pcie_sel_none, ++ /* ++ * PCIE0 selected. ++ */ ++ pcie0_x1_sel, ++ /* ++ * PCIE1 selected. ++ */ ++ pcie1_x1_sel ++}; ++ ++enum pcie_rc_sel { ++ pcie_controller_unselected, ++ pcie_controller_selected ++}; ++ ++enum pcie_controller { ++ pcie_controller_none = -1, ++ pcie_controller_0 = 0, ++ pcie_controller_1 = 1 ++}; ++ ++struct pcie_iatu { ++ unsigned int viewport; /* iATU Viewport Register */ ++ unsigned int region_ctrl_1; /* Region Control 1 Register */ ++ unsigned int region_ctrl_2; /* Region Control 2 Register */ ++ unsigned int lbar; /* Lower Base Address Register */ ++ unsigned int ubar; /* Upper Base Address Register */ ++ unsigned int lar; /* Limit Address Register */ ++ unsigned int ltar; /* Lower Target Address Register */ ++ unsigned int utar; /* Upper Target Address Register */ ++}; ++ ++#define MAX_IATU_PER_CTRLLER (6) ++ ++struct pcie_info { ++ /* ++ * Root bus number ++ */ ++ u8 root_bus_nr; ++ enum pcie_controller controller; ++ ++ /* ++ * Devices configuration space base ++ */ ++ unsigned int base_addr; ++ ++ /* ++ * RC configuration space base ++ */ ++ unsigned int conf_base_addr; ++}; ++ ++static struct pcie_info pcie_info[2]; ++static int pcie_controllers_nr; ++ ++static unsigned int pcie0_sel = pcie_controller_unselected; ++static unsigned int pcie0_mem_space_size = 0x0; ++ ++static unsigned int pcie_errorvalue; ++ ++/* ++ * For number 22 bus err. ++ */ ++static int pcie_fault(unsigned long addr, unsigned int fsr, ++ struct pt_regs *regs) ++{ ++ pcie_errorvalue = 1; ++ return 0; ++} ++ ++static DEFINE_SPINLOCK(cw_lock); ++ ++#define PCIE0_MODE_SEL (1 << 0) ++#define PCIE1_MODE_SEL (1 << 1) ++ ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++#include "pcie_hi3519.c" ++#elif defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++#include "pcie_hi3559.c" ++#elif defined(CONFIG_ARCH_HI3516AV200) ++#include "pcie_hi3516av200.c" ++#elif defined(CONFIG_ARCH_HI3531D) ++#include "pcie_hi3531d.c" ++#else ++#error You must have defined either CONFIG_ARCH_HI3519 or CONFIG_ARCH_HI3559 or CONFIG_ARCH_HI3556 or CONFIG_ARCH_HI3516AV200... ++#endif ++ ++/* ++ * PCIE memory size bootargs: pcie0_mem_size=0xa00000;pcie1_mem_size=0xa00000 ++ */ ++static int __init pcie0_mem_size_parser(char *str) ++{ ++ unsigned int size; ++ ++ if (kstrtoul(str, 16, (long *)&size) < 0) ++ return 0; ++ ++ /* if size >= 256MB, set default 256MB */ ++ if (size >= 0x10000000) ++ size = 0x10000000; ++ pcie0_mem_space_size = size; ++ ++ return 1; ++} ++__setup("pcie0_mem_size=", pcie0_mem_size_parser); ++ ++/* ++ * PCIE sel bootargs: pcie0_sel=x1 pcie1=x1 or pcie1=x2 ++ * Any other value after "pcieX_sel=" prefix ++ * will be treated as none controller selected. ++ * e.g. "pcie0_sel=none" will be treated as no PCIE0 selected. ++ */ ++static int __init pcie0_sel_parser(char *str) ++{ ++ if (strncasecmp(str, "x1", 2) == 0) ++ pcie0_sel = pcie0_x1_sel; ++ else ++ pcie0_sel = pcie_sel_none; ++ ++ return 1; ++} ++__setup("pcie0_sel=", pcie0_sel_parser); ++ ++static void __init pcie_preinit(void) ++{ ++ pcie_debug(PCIE_DBG_FUNC, "!"); ++ __arch_pcie_preinit(); ++} ++ ++static int request_pcie_res(int controller, struct pci_sys_data *sys) ++{ ++ int ret; ++ struct resource *mem = NULL; ++ struct resource *io = NULL; ++ ++ __arch_get_pcie_res(controller, &mem, &io); ++ ++ ret = request_resource(&ioport_resource, io); ++ if (ret) { ++ pcie_error( ++ "request io resource failed,io->start=0x%x,io->end=0x%x", ++ io->start, io->end); ++ return ret; ++ } ++ ++ ret = request_resource(&iomem_resource, mem); ++ if (ret) { ++ pcie_error( ++ "request mem resource failed,mem->start=0x%x,mem->end=0x%x", ++ mem->start, mem->end); ++ ++ release_resource(io); ++ return ret; ++ } ++ ++ pci_add_resource_offset(&sys->resources, io, sys->io_offset); ++ pci_add_resource_offset(&sys->resources, mem, sys->mem_offset); ++ ++ return 0; ++} ++ ++static int __init pcie_setup(int nr, struct pci_sys_data *sys) ++{ ++ struct pcie_info *info; ++ int ret; ++ ++ pcie_debug(PCIE_DBG_FUNC, "nr %d, sys->busnr %d", ++ nr, sys->busnr); ++ if (nr >= pcie_controllers_nr) ++ return 0; ++ ++ info = &pcie_info[nr]; ++ info->root_bus_nr = sys->busnr; ++ sys->mem_offset = 0; ++ ++ /* ++ * Requeset resources for the right controller. ++ */ ++ ret = request_pcie_res(info->controller, sys); ++ if (ret) ++ return ret; ++ ++ __arch_config_iatu_tbl(info, sys); ++ ++ return 1; ++} ++ ++static struct pcie_info *bus_to_info(int busnr) ++{ ++ int i = pcie_controllers_nr - 1; ++ ++ for (; i >= 0; i--) { ++ if (pcie_info[i].controller != pcie_controller_none ++ && pcie_info[i].root_bus_nr <= busnr ++ && pcie_info[i].root_bus_nr != -1) ++ return &pcie_info[i]; ++ } ++ ++ return NULL; ++} ++ ++ ++static int __init pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ struct pcie_info *info = bus_to_info(dev->bus->number); ++ ++ if (unlikely(!info)) { ++ pcie_error( ++ "%s:Cannot find corresponding controller for appointed device!", __func__); ++ BUG(); ++ } ++ ++ pcie_debug(PCIE_DBG_FUNC, ++ "dev->bus->number %d, slot %d, pin %d", ++ dev->bus->number, slot, pin); ++ return __arch_get_int_irq(info, pin); ++ ++} ++ ++#define PCIE_CFG_BUS(busnr) ((busnr & 0xff) << 20) ++#define PCIE_CFG_DEV(devfn) ((devfn & 0xff) << 12) ++#define PCIE_CFG_REG(reg) (reg & 0xffc) /*set dword align*/ ++ ++static inline unsigned int to_pcie_address(struct pci_bus *bus, ++ unsigned int devfn, int where) ++{ ++ struct pcie_info *info = bus_to_info(bus->number); ++ unsigned int address = 0; ++ ++ if (unlikely(!info)) { ++ pcie_error( ++ "%s:Cannot find corresponding controller for appointed device!", __func__); ++ BUG(); ++ } ++ ++ address = info->base_addr + (PCIE_CFG_BUS(bus->number) ++ | PCIE_CFG_DEV(devfn) | PCIE_CFG_REG(where)); ++ ++ ++ return address; ++} ++ ++static inline int is_pcie_link_up(struct pcie_info *info) ++{ ++ int i; ++ ++ for (i = 0; i < 10000; i++) { ++ if (__arch_check_pcie_link(info)) ++ break; ++ udelay(100); ++ } ++ ++ return (i < 10000); ++} ++ ++static int pcie_read_from_device(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *value) ++{ ++ struct pcie_info *info = bus_to_info(bus->number); ++ unsigned int val; ++ void __iomem *addr; ++ int i = 0; ++ ++ if (unlikely(!info)) { ++ pcie_error( ++ "%s:Cannot find corresponding controller for appointed device!", __func__); ++ BUG(); ++ } ++ ++ if (!is_pcie_link_up(info)) { ++ pcie_debug(PCIE_DBG_MODULE, "pcie %d not link up!", ++ info->controller); ++ return -1; ++ } ++ ++ addr = (void __iomem *)to_pcie_address(bus, devfn, where); ++ ++ val = readl(addr); ++ ++ i = 0; ++ while (i < 2000) { ++ __asm__ __volatile__("nop\n"); ++ i++; ++ } ++ ++ if (pcie_errorvalue == 1) { ++ pcie_errorvalue = 0; ++ val = 0xffffffff; ++ } ++ ++ if (size == 1) ++ *value = ((val >> ((where & 0x3) << 3)) & 0xff); ++ else if (size == 2) ++ *value = ((val >> ((where & 0x3) << 3)) & 0xffff); ++ else if (size == 4) ++ *value = val; ++ else{ ++ pcie_error("Unknown size(%d) for read ops", size); ++ BUG(); ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static int pcie_read_from_dbi(struct pcie_info *info, unsigned int devfn, ++ int where, int size, u32 *value) ++{ ++ unsigned int val; ++ ++ /* ++ * For host-side config space read, ignore device func nr. ++ */ ++ if (devfn > 0) ++ return -EIO; ++ ++ val = (u32)readl((void *)(info->conf_base_addr + (where & (~0x3)))); ++ ++ if (1 == size) ++ *value = (val >> ((where & 0x3) << 3)) & 0xff; ++ else if (2 == size) ++ *value = (val >> ((where & 0x3) << 3)) & 0xffff; ++ else if (4 == size) ++ *value = val; ++ else { ++ pcie_error("Unknown size for config read operation!"); ++ BUG(); ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static int pcie_read_conf(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *value) ++{ ++ struct pcie_info *info = bus_to_info(bus->number); ++ int ret; ++ ++ if (unlikely(!info)) { ++ pcie_error( ++ "%s:Cannot find corresponding controller for appointed device!", __func__); ++ BUG(); ++ } ++ ++ if (bus->number == info->root_bus_nr) ++ ret = pcie_read_from_dbi(info, devfn, where, size, value); ++ else ++ ret = pcie_read_from_device(bus, devfn, where, size, value); ++ ++ pcie_debug(PCIE_DBG_REG, ++ "bus %d, devfn %d, where 0x%x, size 0x%x, value 0x%x", ++ bus->number & 0xff, devfn, where, size, *value); ++ ++ return ret; ++} ++ ++static int pcie_write_to_device(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 value) ++{ ++ struct pcie_info *info = bus_to_info(bus->number); ++ void __iomem *addr; ++ unsigned int org; ++ unsigned long flag; ++ ++ if (unlikely(!info)) { ++ pcie_error( ++ "%s:Cannot find corresponding controller for appointed device!", __func__); ++ BUG(); ++ } ++ ++ if (!is_pcie_link_up(info)) { ++ pcie_debug(PCIE_DBG_MODULE, "pcie %d not link up!", ++ info->controller); ++ return -1; ++ } ++ ++ spin_lock_irqsave(&cw_lock, flag); ++ ++ pcie_read_from_device(bus, devfn, where, 4, &org); ++ ++ addr = (void __iomem *)to_pcie_address(bus, devfn, where); ++ ++ if (size == 1) { ++ org &= (~(0xff << ((where & 0x3) << 3))); ++ org |= (value << ((where & 0x3) << 3)); ++ } else if (size == 2) { ++ org &= (~(0xffff << ((where & 0x3) << 3))); ++ org |= (value << ((where & 0x3) << 3)); ++ } else if (size == 4) { ++ org = value; ++ } else { ++ pcie_error("Unknown size(%d) for read ops", size); ++ BUG(); ++ } ++ writel(org, addr); ++ ++ spin_unlock_irqrestore(&cw_lock, flag); ++ ++ return PCIBIOS_SUCCESSFUL; ++ ++} ++ ++static int pcie_write_to_dbi(struct pcie_info *info, unsigned int devfn, ++ int where, int size, u32 value) ++{ ++ unsigned long flag; ++ unsigned int org; ++ ++ spin_lock_irqsave(&cw_lock, flag); ++ ++ if (pcie_read_from_dbi(info, devfn, where, 4, &org)) { ++ pcie_error("Cannot read from dbi! 0x%x:0x%x:0x%x!", ++ 0, devfn, where); ++ spin_unlock_irqrestore(&cw_lock, flag); ++ return -EIO; ++ } ++ if (size == 1) { ++ org &= (~(0xff << ((where & 0x3) << 3))); ++ org |= (value << ((where & 0x3) << 3)); ++ } else if (size == 2) { ++ org &= (~(0xffff << ((where & 0x3) << 3))); ++ org |= (value << ((where & 0x3) << 3)); ++ } else if (size == 4) { ++ org = value; ++ } else { ++ pcie_error("Unknown size(%d) for read ops", size); ++ BUG(); ++ } ++ writel(org, ((void __iomem *)info->conf_base_addr + (where & (~0x3)))); ++ ++ spin_unlock_irqrestore(&cw_lock, flag); ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static int pcie_write_conf(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 value) ++{ ++ struct pcie_info *info = bus_to_info(bus->number); ++ ++ pcie_debug(PCIE_DBG_REG, ++ "bus %d, devfn %d, where 0x%x, size 0x%x, value 0x%x", ++ bus->number & 0xff, devfn, where, size, value); ++ ++ if (unlikely(!info)) { ++ pcie_error( ++ "%s:Cannot find corresponding controller for appointed device!", __func__); ++ BUG(); ++ } ++ ++ if (bus->number == info->root_bus_nr) ++ return pcie_write_to_dbi(info, devfn, where, size, value); ++ else ++ return pcie_write_to_device(bus, devfn, where, size, value); ++} ++ ++static struct pci_ops pcie_ops = { ++ .read = pcie_read_conf, ++ .write = pcie_write_conf, ++}; ++ ++void pci_set_max_rd_req_size(const struct pci_bus *bus) ++{ ++ struct pci_dev *dev; ++ struct pci_bus *child; ++ int pos; ++ unsigned short dev_contrl_reg_val = 0; ++ unsigned int max_rd_req_size = 0; ++ ++ list_for_each_entry(dev, &bus->devices, bus_list) { ++ ++ /* set device max read requset size*/ ++ pos = pci_find_capability(dev, PCI_CAP_ID_EXP); ++ if (pos) { ++ pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, ++ &dev_contrl_reg_val); ++ max_rd_req_size = (dev_contrl_reg_val >> 12) & 0x7; ++ if (max_rd_req_size > 0x0) { ++ dev_contrl_reg_val &= ~(max_rd_req_size << 12); ++ pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, ++ dev_contrl_reg_val); ++ } ++ ++ } ++ } ++ ++ list_for_each_entry(dev, &bus->devices, bus_list) { ++ BUG_ON(!dev->is_added); ++ child = dev->subordinate; ++ if (child) ++ pci_set_max_rd_req_size(child); ++ } ++} ++ ++static struct pci_bus *__init pcie_scan_bus(int nr, struct pci_sys_data *sys) ++{ ++ struct pci_bus *bus; ++ ++ pcie_debug(PCIE_DBG_FUNC, ++ "nr %d, sys->busnr %d, sys->mem_offset 0x%llx, sys->io_offset 0x%lx", ++ nr, sys->busnr, sys->mem_offset, sys->io_offset); ++ if (nr < pcie_controllers_nr) { ++ bus = pci_scan_root_bus(NULL, sys->busnr, &pcie_ops, sys, ++ &sys->resources); ++ } else { ++ bus = NULL; ++ pcie_error("Unknown controller nr :0x%x!", nr); ++ BUG(); ++ } ++ ++#ifdef CONFIG_LIMIT_MAX_RD_REQ_SIZE ++ if (bus) ++ pci_set_max_rd_req_size(bus); ++#endif ++ ++ return bus; ++} ++ ++ ++static struct hw_pci hipcie __initdata = { ++ .nr_controllers = 1, ++ .preinit = pcie_preinit, ++ .swizzle = pci_common_swizzle, ++ .setup = pcie_setup, ++ .scan = pcie_scan_bus, ++ .map_irq = pcie_map_irq, ++}; ++ ++ ++static int __init pcie_init(void) ++{ ++ pcie_info[0].root_bus_nr = -1; ++ pcie_info[1].root_bus_nr = -1; ++ ++ /* ++ * Scene: PCIe host(RC)<--->SWITCH<--->PCIe device(*) ++ * | ++ * |------->NULL SLOT ++ * PCIe will generate a DataAbort to ARM, when scanning NULL SLOT. ++ * Register hook to capture this exception and handle it. ++ */ ++ hook_fault_code(22, pcie_fault, 7, BUS_OBJERR, ++ "external abort on non-linefetch"); ++ ++ if (__arch_pcie_info_setup(pcie_info, &pcie_controllers_nr)) ++ return -EIO; ++ ++ if (__arch_pcie_sys_init(pcie_info)) ++ goto pcie_init_err; ++ hipcie.nr_controllers = pcie_controllers_nr; ++ pr_err("Number of PCIe controllers: %d\n", ++ hipcie.nr_controllers); ++ ++ pci_common_init(&hipcie); ++ ++ return 0; ++pcie_init_err: ++ __arch_pcie_info_release(pcie_info); ++ ++ return -EIO; ++} ++ ++static void __exit pcie_uinit(void) ++{ ++ __arch_pcie_info_release(pcie_info); ++} ++ ++#include <linux/platform_device.h> ++#include <linux/pm.h> ++ ++int hisi_pcie_plat_driver_probe(struct platform_device *pdev) ++{ ++ return 0; ++} ++int hisi_pcie_plat_driver_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++int hisi_pcie_plat_driver_suspend(struct device *dev) ++{ ++ __arch_pcie_sys_exit(); ++ return 0; ++} ++ ++int hisi_pcie_plat_driver_resume(struct device *dev) ++{ ++ return __arch_pcie_sys_init(pcie_info); ++} ++ ++const struct dev_pm_ops hisi_pcie_pm_ops = { ++ .suspend = NULL, ++ .suspend_noirq = hisi_pcie_plat_driver_suspend, ++ .resume = NULL, ++ .resume_noirq = hisi_pcie_plat_driver_resume ++}; ++ ++#define HISI_PCIE_PM_OPS (&hisi_pcie_pm_ops) ++#else ++#define HISI_PCIE_PM_OPS NULL ++#endif ++ ++#define PCIE_RC_DRV_NAME "hisi pcie root complex" ++ ++static struct platform_driver hisi_pcie_platform_driver = { ++ .probe = hisi_pcie_plat_driver_probe, ++ .remove = hisi_pcie_plat_driver_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = PCIE_RC_DRV_NAME, ++ .bus = &platform_bus_type, ++ .pm = HISI_PCIE_PM_OPS ++ }, ++}; ++ ++static void hisi_pcie_platform_device_release(struct device *dev) ++{ ++} ++ ++static struct resource hisi_pcie_resources[] = { ++ [0] = { ++ .start = PCIE_DBI_BASE, ++ .end = PCIE_DBI_BASE + __8KB__ - 1, ++ .flags = IORESOURCE_REG, ++ } ++}; ++ ++static u64 hipcie_dmamask = DMA_BIT_MASK(32); ++ ++static struct platform_device hisi_pcie_platform_device = { ++ .name = PCIE_RC_DRV_NAME, ++ .id = 0, ++ .dev = { ++ .platform_data = NULL, ++ .dma_mask = &hipcie_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ .release = hisi_pcie_platform_device_release, ++ }, ++ .num_resources = ARRAY_SIZE(hisi_pcie_resources), ++ .resource = hisi_pcie_resources, ++}; ++ ++static int __init hisi_pcie_init(void) ++{ ++ int ret; ++ ++ ret = platform_device_register(&hisi_pcie_platform_device); ++ if (ret) ++ goto err_device; ++ ++ ret = platform_driver_register(&hisi_pcie_platform_driver); ++ if (ret) ++ goto err_driver; ++ ++ if (pcie_init()) { ++ pcie_error("pcie sys init failed!"); ++ goto err_init; ++ } ++ ++ return 0; ++err_init: ++ platform_driver_unregister(&hisi_pcie_platform_driver); ++err_driver: ++ platform_device_unregister(&hisi_pcie_platform_device); ++err_device: ++ return -1; ++} ++ ++static void __exit hisi_pcie_exit(void) ++{ ++ pcie_uinit(); ++ platform_device_unregister(&hisi_pcie_platform_device); ++ platform_driver_unregister(&hisi_pcie_platform_driver); ++} ++ ++subsys_initcall(hisi_pcie_init); ++module_exit(hisi_pcie_exit); ++ ++MODULE_DESCRIPTION("Hisilicon PCI-Express Root Complex driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/pci/hipcie/pcie_hi3516av200.c b/drivers/pci/hipcie/pcie_hi3516av200.c +new file mode 100644 +index 0000000..b8b85a7 +--- /dev/null ++++ b/drivers/pci/hipcie/pcie_hi3516av200.c +@@ -0,0 +1,319 @@ ++#include <mach/io.h> ++#include "pcie_hi3516av200.h" ++ ++static void *dbi_base; ++static int __arch_pcie_info_setup(struct pcie_info *info, int *controllers_nr); ++static int __arch_pcie_sys_init(struct pcie_info *info); ++static void __arch_pcie_info_release(struct pcie_info *info); ++ ++struct pcie_iatu iatu_table[] = { ++ { ++ .viewport = 0, ++ .region_ctrl_1 = 0x00000004, ++ .region_ctrl_2 = 0x90000000, ++ .lbar = PCIE_EP_CONF_BASE + (1<<20), ++ .ubar = 0x0, ++ .lar = PCIE_EP_CONF_BASE + (2<<20) - 1, ++ .ltar = 0x01000000, ++ .utar = 0x00000000, ++ }, ++ { ++ .viewport = 1, ++ .region_ctrl_1 = 0x00000005, ++ .region_ctrl_2 = 0x90000000, ++ .lbar = PCIE_EP_CONF_BASE + (2<<20), ++ .ubar = 0x0, ++ .lar = PCIE_EP_CONF_BASE + (__128MB__ - 1), ++ .ltar = 0x02000000, ++ .utar = 0x00000000, ++ }, ++}; ++ ++static void __arch_pcie_preinit(void) ++{ ++ ++} ++ ++static struct resource pcie_mem; ++static struct resource pcie_io; ++static void __arch_get_pcie_res(int controller, ++ struct resource **pmem, ++ struct resource **pio) ++{ ++ *pmem = &pcie_mem; ++ (*pmem)->start = PCIE_MEM_BASE; ++ (*pmem)->end = PCIE_MEM_BASE + __128MB__ - 0x100000 - 1; ++ (*pmem)->flags = IORESOURCE_MEM; ++ (*pmem)->name = "memory"; ++ ++ *pio = &pcie_io; ++ (*pio)->start = PCIE_MEM_BASE + __128MB__ - 0x100000; ++ (*pio)->end = PCIE_MEM_BASE + __128MB__ - 1; ++ (*pio)->flags = IORESOURCE_IO; ++ (*pio)->name = "io"; ++} ++ ++static int __arch_get_int_irq(struct pcie_info *info, u8 pin) ++{ ++ switch (pin) { ++ case PCIE_INTA_PIN: ++ return PCIE_IRQ_INTA; ++ case PCIE_INTB_PIN: ++ return PCIE_IRQ_INTB; ++ case PCIE_INTC_PIN: ++ return PCIE_IRQ_INTC; ++ case PCIE_INTD_PIN: ++ return PCIE_IRQ_INTD; ++ default: ++ pcie_error("Unknown pin for mapping irq!"); ++ return -1; ++ } ++} ++ ++static void __arch_config_iatu_tbl(struct pcie_info *info, ++ struct pci_sys_data *sys) ++{ ++ int i; ++ void __iomem *config_base = (void __iomem *)info->conf_base_addr; ++ struct pcie_iatu *ptable = iatu_table; ++ int table_size = ARRAY_SIZE(iatu_table); ++ ++ for (i = 0; i < table_size; i++) { ++ writel((ptable + i)->viewport, config_base + 0x900); ++ writel((ptable + i)->lbar, config_base + 0x90c); ++ writel((ptable + i)->ubar, config_base + 0x910); ++ writel((ptable + i)->lar, config_base + 0x914); ++ writel((ptable + i)->ltar, config_base + 0x918); ++ writel((ptable + i)->utar, config_base + 0x91c); ++ writel((ptable + i)->region_ctrl_1, config_base + 0x904); ++ writel((ptable + i)->region_ctrl_2, config_base + 0x908); ++ } ++ ++} ++ ++static inline int __arch_check_pcie_link(struct pcie_info *info) ++{ ++ int val; ++ ++ val = readl(dbi_base + PCIE_SYS_STATE0); ++ return ((val & (1 << PCIE_XMLH_LINK_UP)) ++ && (val & (1 << PCIE_RDLH_LINK_UP))) ? 1 : 0; ++} ++ ++/* ++ * ret: ++ */ ++static int __arch_pcie_info_setup(struct pcie_info *info, int *controllers_nr) ++{ ++ unsigned int mem_size = CONFIG_PCIE0_DEVICES_MEM_SIZE; ++ unsigned int cfg_size = CONFIG_PCIE0_DEVICES_CONFIG_SIZE; ++ ++ if ((mem_size > __128MB__) || (cfg_size > __128MB__)) { ++ pcie_error( ++ "Invalid parameter: pcie mem size[0x%x], pcie cfg size[0x%x]!", ++ mem_size, cfg_size); ++ return -EINVAL; ++ } ++ ++ info->controller = 0; ++ ++ /* RC configuration space */ ++ info->conf_base_addr = (unsigned int)ioremap_nocache(PCIE_DBI_BASE, ++ __8KB__); ++ if (!info->conf_base_addr) { ++ pcie_error("Address mapping for RC dbi failed!"); ++ return -EIO; ++ } ++ ++ /* Configuration space for all EPs */ ++ info->base_addr = (unsigned int)ioremap_nocache(PCIE_EP_CONF_BASE, ++ cfg_size); ++ if (!info->base_addr) { ++ iounmap((void *)info->conf_base_addr); ++ pcie_error("Address mapping for EPs cfg failed!"); ++ return -EIO; ++ } ++ ++ *controllers_nr = 1; ++ ++ return 0; ++ ++} ++ ++static void __arch_pcie_info_release(struct pcie_info *info) ++{ ++ if (info->base_addr) ++ iounmap((void *)info->base_addr); ++ ++ if (info->conf_base_addr) ++ iounmap((void *)info->conf_base_addr); ++} ++ ++void set_pcie_para(void *crg_base) ++{ ++ unsigned int val; ++ ++ void * misc_base = (void *)IO_ADDRESS(MISC_CTRL_BASE); ++ ++ val = readl(crg_base + PERI_CRG43); ++ val = val & (~(1 << 0)); ++ val = val | (1 << 1); ++ writel(val,crg_base + PERI_CRG43); ++ ++ writel(0x1506, misc_base + MISC_CTRL33); ++ writel(0x11506, misc_base + MISC_CTRL33); ++ writel(0x1506, misc_base + MISC_CTRL33); ++ writel(0x0, misc_base + MISC_CTRL33); ++ ++ writel(0x108, misc_base + MISC_CTRL33); ++ writel(0x10108, misc_base + MISC_CTRL33); ++ writel(0x108, misc_base + MISC_CTRL33); ++ writel(0x0, misc_base + MISC_CTRL33); ++} ++ ++static int __arch_pcie_sys_init(struct pcie_info *info) ++{ ++ unsigned int val; ++ void *crg_base = (void *)IO_ADDRESS(PERI_CRG_BASE); ++ ++ dbi_base = (void *)info->conf_base_addr; ++ ++#ifdef CONFIG_ARCH_HI3516AV200 ++ /*open pcie pad oe*/ ++ val = readl(crg_base + PERI_CRG44); ++ val &= ~(PCIE_PAD_OE_MASK); ++ writel(val, crg_base + PERI_CRG44); ++ ++ /* refclk output from phy */ ++ writel(PCIE_CLKREQ_FILTER_BYPASS, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL34)); ++ mdelay(1); ++#endif ++ ++ /* ++ * Disable PCIE ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL7); ++ val &= (~(1 << PCIE_APP_LTSSM_ENBALE)); ++ val |= (1 << PCIE_ACCESS_ENABLE); ++ writel(val, dbi_base + PCIE_SYS_CTRL7); ++ ++ /* ++ * Reset ++ */ ++ val = readl(crg_base + PERI_CRG44); ++ val |= (1 << PCIE_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG44); ++ ++ /* ++ * Retreat from the reset state ++ */ ++ udelay(500); ++ val = readl(crg_base + PERI_CRG44); ++ val &= ~(1 << PCIE_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG44); ++ mdelay(10); ++ ++ ++ /* ++ * PCIE RC work mode ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL0); ++ val &= (~(0xf << PCIE_DEVICE_TYPE)); ++ val |= (PCIE_WM_RC << PCIE_DEVICE_TYPE); ++ writel(val, dbi_base + PCIE_SYS_CTRL0); ++ ++ /* ++ * Enable clk ++ */ ++ val = readl(crg_base + PERI_CRG44); ++ val |= ((1 << PCIE_X2_BUS_CKEN) ++ | (1 << PCIE_X2_SYS_CKEN) ++ | (1 << PCIE_X2_PIPE_CKEN) ++ | (1 << PCIE_X2_AUX_CKEN)); ++ writel(val, crg_base + PERI_CRG44); ++ ++ mdelay(10); ++ ++ ++ set_pcie_para(crg_base); ++ mdelay(10); ++ ++ /* ++ * Set PCIE controller class code to be PCI-PCI bridge device ++ */ ++ val = readl(dbi_base + PCI_CLASS_REVISION); ++ val &= ~(0xffffff00); ++ val |= (0x60400 << 8); ++ writel(val, dbi_base + PCI_CLASS_REVISION); ++ udelay(1000); ++ ++#ifdef CONFIG_ARCH_HI3516AV200 ++ /* phy always work at 5Gbps */ ++ writel(COM_PHY_TEST_VAL1, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL33)); ++ writel(COM_PHY_TEST_VAL2, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL33)); ++ writel(COM_PHY_TEST_VAL1, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL33)); ++ writel(0x0, (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL33)); ++ ++ /* default deemphasis to -3.5 dB */ ++ writel(DEEMPHASIS_VAL, (void *)IO_ADDRESS(PCIE_DBI_BASE + DEEMPHASIS_REG)); ++#endif ++ ++ /* ++ * Enable controller ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL7); ++ val |= (1 << PCIE_APP_LTSSM_ENBALE); ++ writel(val, dbi_base + PCIE_SYS_CTRL7); ++ udelay(1000); ++ ++ val = readl(dbi_base + PCI_COMMAND); ++ val |= 7; ++ writel(val, dbi_base + PCI_COMMAND); ++ ++ /* set pcie to gen 1*/ ++#if 0 ++ writel(0x1, dbi_base + 0x8BC); ++ val = readl(dbi_base + 0x7C); ++ val = ((val >> 4) << 4) | 0x1; ++ writel(val, dbi_base + 0x7C); ++#endif ++ return 0; ++} ++ ++static void __arch_pcie_sys_exit(void) ++{ ++ unsigned int val; ++ void *crg_base = (void *)IO_ADDRESS(PERI_CRG_BASE); ++ ++ /* ++ * Disable PCIE ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL7); ++ val &= (~(1 << PCIE_APP_LTSSM_ENBALE)); ++ writel(val, dbi_base + PCIE_SYS_CTRL7); ++ ++ /* ++ * Reset ++ */ ++ val = readl(crg_base + PERI_CRG44); ++ val |= (1 << PCIE_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG44); ++ ++ udelay(1000); ++ ++ /* ++ * Disable clk ++ */ ++ val = readl(crg_base + PERI_CRG44); ++ val &= (~(1 << PCIE_X2_AUX_CKEN)); ++ val &= (~(1 << PCIE_X2_PIPE_CKEN)); ++ val &= (~(1 << PCIE_X2_SYS_CKEN)); ++ val &= (~(1 << PCIE_X2_BUS_CKEN)); ++ writel(val, crg_base + PERI_CRG44); ++ ++ udelay(1000); ++} +diff --git a/drivers/pci/hipcie/pcie_hi3516av200.h b/drivers/pci/hipcie/pcie_hi3516av200.h +new file mode 100644 +index 0000000..e185999 +--- /dev/null ++++ b/drivers/pci/hipcie/pcie_hi3516av200.h +@@ -0,0 +1,55 @@ ++#ifndef __HISI_PCIE_H__ ++#define __HISI_PCIE_H__ ++ ++#define MISC_CTRL_BASE 0x12030000 ++#define PCIE_MEM_BASE 0x28000000 ++#define PCIE_EP_CONF_BASE 0x20000000 ++#define PCIE_DBI_BASE 0x12160000 ++#define PERI_CRG_BASE 0x12010000 ++ ++#define PERI_CRG43 0xAC ++#define PERI_CRG44 0xB0 ++#define PCIE_X2_SRST_REQ 2 ++ ++#define PCIE_X2_AUX_CKEN 7 ++#define PCIE_X2_PIPE_CKEN 6 ++#define PCIE_X2_SYS_CKEN 5 ++#define PCIE_X2_BUS_CKEN 4 ++#define PCIE_PAD_OE_MASK (0x7 << 8) ++ ++#define PCIE_SYS_CTRL0 0x1000 ++#define PCIE_DEVICE_TYPE 28 ++#define PCIE_WM_EP 0x0 ++#define PCIE_WM_LEGACY 0x1 ++#define PCIE_WM_RC 0x4 ++ ++#define PCIE_SYS_CTRL7 0x101C ++#define PCIE_APP_LTSSM_ENBALE 11 ++#define PCIE_ACCESS_ENABLE 13 ++ ++#define PCIE_SYS_STATE0 0x1100 ++#define PCIE_XMLH_LINK_UP 15 ++#define PCIE_RDLH_LINK_UP 5 ++ ++#define PCIE_IRQ_INTA 89 ++#define PCIE_IRQ_INTB 90 ++#define PCIE_IRQ_INTC 91 ++#define PCIE_IRQ_INTD 92 ++#define PCIE_IRQ_EDMA 93 ++#define PCIE_IRQ_MSI 94 ++#define PCIE_IRQ_LINK_DOWN 95 ++ ++#define PCIE_INTA_PIN 1 ++#define PCIE_INTB_PIN 2 ++#define PCIE_INTC_PIN 3 ++#define PCIE_INTD_PIN 4 ++ ++#define MISC_CTRL33 0x84 ++#define MISC_CTRL34 0x88 ++#define DEEMPHASIS_REG 0xa0 ++#define PCIE_CLKREQ_FILTER_BYPASS 0x600 ++#define DEEMPHASIS_VAL 0x42 ++#define COM_PHY_TEST_VAL1 ((0x1 << 3) | (0x1 << 8)) ++#define COM_PHY_TEST_VAL2 ((0x1 << 16) | (0x1 << 3) | (0x1 << 8)) ++ ++#endif +diff --git a/drivers/pci/hipcie/pcie_hi3519.c b/drivers/pci/hipcie/pcie_hi3519.c +new file mode 100644 +index 0000000..fdfdf0a +--- /dev/null ++++ b/drivers/pci/hipcie/pcie_hi3519.c +@@ -0,0 +1,335 @@ ++#include <mach/io.h> ++#include "pcie_hi3519.h" ++ ++static void *dbi_base; ++static int __arch_pcie_info_setup(struct pcie_info *info, int *controllers_nr); ++static int __arch_pcie_sys_init(struct pcie_info *info); ++static void __arch_pcie_info_release(struct pcie_info *info); ++ ++struct pcie_iatu iatu_table[] = { ++ { ++ .viewport = 0, ++ .region_ctrl_1 = 0x00000004, ++ .region_ctrl_2 = 0x90000000, ++ .lbar = PCIE_EP_CONF_BASE + (1<<20), ++ .ubar = 0x0, ++ .lar = PCIE_EP_CONF_BASE + (2<<20) - 1, ++ .ltar = 0x01000000, ++ .utar = 0x00000000, ++ }, ++ { ++ .viewport = 1, ++ .region_ctrl_1 = 0x00000005, ++ .region_ctrl_2 = 0x90000000, ++ .lbar = PCIE_EP_CONF_BASE + (2<<20), ++ .ubar = 0x0, ++ .lar = PCIE_EP_CONF_BASE + (__128MB__ - 1), ++ .ltar = 0x02000000, ++ .utar = 0x00000000, ++ }, ++}; ++ ++static void __arch_pcie_preinit(void) ++{ ++ ++} ++ ++static struct resource pcie_mem; ++static struct resource pcie_io; ++static void __arch_get_pcie_res(int controller, ++ struct resource **pmem, ++ struct resource **pio) ++{ ++ *pmem = &pcie_mem; ++ (*pmem)->start = PCIE_MEM_BASE; ++ (*pmem)->end = PCIE_MEM_BASE + __128MB__ - 0x100000 - 1; ++ (*pmem)->flags = IORESOURCE_MEM; ++ (*pmem)->name = "memory"; ++ ++ *pio = &pcie_io; ++ (*pio)->start = PCIE_MEM_BASE + __128MB__ - 0x100000; ++ (*pio)->end = PCIE_MEM_BASE + __128MB__ - 1; ++ (*pio)->flags = IORESOURCE_IO; ++ (*pio)->name = "io"; ++} ++ ++static int __arch_get_int_irq(struct pcie_info *info, u8 pin) ++{ ++ switch (pin) { ++ case PCIE_INTA_PIN: ++ return PCIE_IRQ_INTA; ++ case PCIE_INTB_PIN: ++ return PCIE_IRQ_INTB; ++ case PCIE_INTC_PIN: ++ return PCIE_IRQ_INTC; ++ case PCIE_INTD_PIN: ++ return PCIE_IRQ_INTD; ++ default: ++ pcie_error("Unknown pin for mapping irq!"); ++ return -1; ++ } ++} ++ ++static void __arch_config_iatu_tbl(struct pcie_info *info, ++ struct pci_sys_data *sys) ++{ ++ int i; ++ void __iomem *config_base = (void __iomem *)info->conf_base_addr; ++ struct pcie_iatu *ptable = iatu_table; ++ int table_size = ARRAY_SIZE(iatu_table); ++ ++ for (i = 0; i < table_size; i++) { ++ writel((ptable + i)->viewport, config_base + 0x900); ++ writel((ptable + i)->lbar, config_base + 0x90c); ++ writel((ptable + i)->ubar, config_base + 0x910); ++ writel((ptable + i)->lar, config_base + 0x914); ++ writel((ptable + i)->ltar, config_base + 0x918); ++ writel((ptable + i)->utar, config_base + 0x91c); ++ writel((ptable + i)->region_ctrl_1, config_base + 0x904); ++ writel((ptable + i)->region_ctrl_2, config_base + 0x908); ++ } ++ ++} ++ ++static inline int __arch_check_pcie_link(struct pcie_info *info) ++{ ++ int val; ++ ++ val = readl(dbi_base + PCIE_SYS_STATE0); ++ return ((val & (1 << PCIE_XMLH_LINK_UP)) ++ && (val & (1 << PCIE_RDLH_LINK_UP))) ? 1 : 0; ++} ++ ++/* ++ * ret: ++ */ ++static int __arch_pcie_info_setup(struct pcie_info *info, int *controllers_nr) ++{ ++ unsigned int mem_size = CONFIG_PCIE0_DEVICES_MEM_SIZE; ++ unsigned int cfg_size = CONFIG_PCIE0_DEVICES_CONFIG_SIZE; ++ ++ if ((mem_size > __128MB__) || (cfg_size > __128MB__)) { ++ pcie_error( ++ "Invalid parameter: pcie mem size[0x%x], pcie cfg size[0x%x]!", ++ mem_size, cfg_size); ++ return -EINVAL; ++ } ++ ++ info->controller = 0; ++ ++ /* RC configuration space */ ++ info->conf_base_addr = (unsigned int)ioremap_nocache(PCIE_DBI_BASE, ++ __8KB__); ++ if (!info->conf_base_addr) { ++ pcie_error("Address mapping for RC dbi failed!"); ++ return -EIO; ++ } ++ ++ /* Configuration space for all EPs */ ++ info->base_addr = (unsigned int)ioremap_nocache(PCIE_EP_CONF_BASE, ++ cfg_size); ++ if (!info->base_addr) { ++ iounmap((void *)info->conf_base_addr); ++ pcie_error("Address mapping for EPs cfg failed!"); ++ return -EIO; ++ } ++ ++ *controllers_nr = 1; ++ ++ return 0; ++ ++} ++ ++static void __arch_pcie_info_release(struct pcie_info *info) ++{ ++ if (info->base_addr) ++ iounmap((void *)info->base_addr); ++ ++ if (info->conf_base_addr) ++ iounmap((void *)info->conf_base_addr); ++} ++ ++void set_pcie_para(void *crg_base) ++{ ++ unsigned int val; ++ ++ void * misc_base = (void *)IO_ADDRESS(MISC_CTRL_BASE); ++ ++ val = readl(crg_base + PERI_CRG43); ++ val = val & (~(1 << 0)); ++ val = val | (1 << 1); ++ writel(val,crg_base + PERI_CRG43); ++ ++#ifdef CONFIG_ARCH_HI3519V101 ++ writel(0x1506, misc_base + MISC_CTRL33); ++ writel(0x11506, misc_base + MISC_CTRL33); ++ writel(0x1506, misc_base + MISC_CTRL33); ++ writel(0x0, misc_base + MISC_CTRL33); ++ ++ writel(0x108, misc_base + MISC_CTRL33); ++ writel(0x10108, misc_base + MISC_CTRL33); ++ writel(0x108, misc_base + MISC_CTRL33); ++ writel(0x0, misc_base + MISC_CTRL33); ++#else ++ writel(0x1a04, misc_base + MISC_CTRL33); ++ writel(0x11a04, misc_base + MISC_CTRL33); ++ writel(0x1a04, misc_base + MISC_CTRL33); ++ writel(0x0, misc_base + MISC_CTRL33); ++ ++ writel(0xc09, misc_base + MISC_CTRL33); ++ writel(0x10c09, misc_base + MISC_CTRL33); ++ writel(0xc09, misc_base + MISC_CTRL33); ++ writel(0x0, misc_base + MISC_CTRL33); ++ ++ writel(0x303, misc_base + MISC_CTRL33); ++ writel(0x10303, misc_base + MISC_CTRL33); ++ writel(0x303, misc_base + MISC_CTRL33); ++ writel(0x0, misc_base + MISC_CTRL33); ++#endif ++} ++ ++static int __arch_pcie_sys_init(struct pcie_info *info) ++{ ++ unsigned int val; ++ void *crg_base = (void *)IO_ADDRESS(PERI_CRG_BASE); ++ ++ dbi_base = (void *)info->conf_base_addr; ++ ++#ifdef CONFIG_ARCH_HI3519V101 ++ /*open pcie pad oe*/ ++ val = readl(crg_base + PERI_CRG44); ++ val &= ~(PCIE_PAD_OE_MASK); ++ writel(val, crg_base + PERI_CRG44); ++ ++ /* refclk output from phy */ ++ writel(PCIE_CLKREQ_FILTER_BYPASS, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL34)); ++ mdelay(1); ++#endif ++ ++ /* ++ * Disable PCIE ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL7); ++ val &= (~(1 << PCIE_APP_LTSSM_ENBALE)); ++ val |= (1 << PCIE_ACCESS_ENABLE); ++ writel(val, dbi_base + PCIE_SYS_CTRL7); ++ ++ /* ++ * Reset ++ */ ++ val = readl(crg_base + PERI_CRG44); ++ val |= (1 << PCIE_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG44); ++ ++ /* ++ * Retreat from the reset state ++ */ ++ udelay(500); ++ val = readl(crg_base + PERI_CRG44); ++ val &= ~(1 << PCIE_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG44); ++ mdelay(10); ++ ++ ++ /* ++ * PCIE RC work mode ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL0); ++ val &= (~(0xf << PCIE_DEVICE_TYPE)); ++ val |= (PCIE_WM_RC << PCIE_DEVICE_TYPE); ++ writel(val, dbi_base + PCIE_SYS_CTRL0); ++ ++ /* ++ * Enable clk ++ */ ++ val = readl(crg_base + PERI_CRG44); ++ val |= ((1 << PCIE_X2_BUS_CKEN) ++ | (1 << PCIE_X2_SYS_CKEN) ++ | (1 << PCIE_X2_PIPE_CKEN) ++ | (1 << PCIE_X2_AUX_CKEN)); ++ writel(val, crg_base + PERI_CRG44); ++ ++ mdelay(10); ++ ++ set_pcie_para(crg_base); ++ mdelay(10); ++ ++ /* ++ * Set PCIE controller class code to be PCI-PCI bridge device ++ */ ++ val = readl(dbi_base + PCI_CLASS_REVISION); ++ val &= ~(0xffffff00); ++ val |= (0x60400 << 8); ++ writel(val, dbi_base + PCI_CLASS_REVISION); ++ udelay(1000); ++ ++#ifdef CONFIG_ARCH_HI3519V101 ++ /* phy always work at 5Gbps */ ++ writel(COM_PHY_TEST_VAL1, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL33)); ++ writel(COM_PHY_TEST_VAL2, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL33)); ++ writel(COM_PHY_TEST_VAL1, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL33)); ++ writel(0x0, (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL33)); ++ ++ /* default deemphasis to -3.5 dB */ ++ writel(DEEMPHASIS_VAL, (void *)IO_ADDRESS(PCIE_DBI_BASE + DEEMPHASIS_REG)); ++#endif ++ ++ /* ++ * Enable controller ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL7); ++ val |= (1 << PCIE_APP_LTSSM_ENBALE); ++ writel(val, dbi_base + PCIE_SYS_CTRL7); ++ udelay(1000); ++ ++ val = readl(dbi_base + PCI_COMMAND); ++ val |= 7; ++ writel(val, dbi_base + PCI_COMMAND); ++ ++ /* set pcie to gen 1*/ ++#if 0 ++ writel(0x1, dbi_base + 0x8BC); ++ val = readl(dbi_base + 0x7C); ++ val = ((val >> 4) << 4) | 0x1; ++ writel(val, dbi_base + 0x7C); ++#endif ++ return 0; ++} ++ ++static void __arch_pcie_sys_exit(void) ++{ ++ unsigned int val; ++ void *crg_base = (void *)IO_ADDRESS(PERI_CRG_BASE); ++ ++ /* ++ * Disable PCIE ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL7); ++ val &= (~(1 << PCIE_APP_LTSSM_ENBALE)); ++ writel(val, dbi_base + PCIE_SYS_CTRL7); ++ ++ /* ++ * Reset ++ */ ++ val = readl(crg_base + PERI_CRG44); ++ val |= (1 << PCIE_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG44); ++ ++ udelay(1000); ++ ++ /* ++ * Disable clk ++ */ ++ val = readl(crg_base + PERI_CRG44); ++ val &= (~(1 << PCIE_X2_AUX_CKEN)); ++ val &= (~(1 << PCIE_X2_PIPE_CKEN)); ++ val &= (~(1 << PCIE_X2_SYS_CKEN)); ++ val &= (~(1 << PCIE_X2_BUS_CKEN)); ++ writel(val, crg_base + PERI_CRG44); ++ ++ udelay(1000); ++} +diff --git a/drivers/pci/hipcie/pcie_hi3519.h b/drivers/pci/hipcie/pcie_hi3519.h +new file mode 100644 +index 0000000..e185999 +--- /dev/null ++++ b/drivers/pci/hipcie/pcie_hi3519.h +@@ -0,0 +1,55 @@ ++#ifndef __HISI_PCIE_H__ ++#define __HISI_PCIE_H__ ++ ++#define MISC_CTRL_BASE 0x12030000 ++#define PCIE_MEM_BASE 0x28000000 ++#define PCIE_EP_CONF_BASE 0x20000000 ++#define PCIE_DBI_BASE 0x12160000 ++#define PERI_CRG_BASE 0x12010000 ++ ++#define PERI_CRG43 0xAC ++#define PERI_CRG44 0xB0 ++#define PCIE_X2_SRST_REQ 2 ++ ++#define PCIE_X2_AUX_CKEN 7 ++#define PCIE_X2_PIPE_CKEN 6 ++#define PCIE_X2_SYS_CKEN 5 ++#define PCIE_X2_BUS_CKEN 4 ++#define PCIE_PAD_OE_MASK (0x7 << 8) ++ ++#define PCIE_SYS_CTRL0 0x1000 ++#define PCIE_DEVICE_TYPE 28 ++#define PCIE_WM_EP 0x0 ++#define PCIE_WM_LEGACY 0x1 ++#define PCIE_WM_RC 0x4 ++ ++#define PCIE_SYS_CTRL7 0x101C ++#define PCIE_APP_LTSSM_ENBALE 11 ++#define PCIE_ACCESS_ENABLE 13 ++ ++#define PCIE_SYS_STATE0 0x1100 ++#define PCIE_XMLH_LINK_UP 15 ++#define PCIE_RDLH_LINK_UP 5 ++ ++#define PCIE_IRQ_INTA 89 ++#define PCIE_IRQ_INTB 90 ++#define PCIE_IRQ_INTC 91 ++#define PCIE_IRQ_INTD 92 ++#define PCIE_IRQ_EDMA 93 ++#define PCIE_IRQ_MSI 94 ++#define PCIE_IRQ_LINK_DOWN 95 ++ ++#define PCIE_INTA_PIN 1 ++#define PCIE_INTB_PIN 2 ++#define PCIE_INTC_PIN 3 ++#define PCIE_INTD_PIN 4 ++ ++#define MISC_CTRL33 0x84 ++#define MISC_CTRL34 0x88 ++#define DEEMPHASIS_REG 0xa0 ++#define PCIE_CLKREQ_FILTER_BYPASS 0x600 ++#define DEEMPHASIS_VAL 0x42 ++#define COM_PHY_TEST_VAL1 ((0x1 << 3) | (0x1 << 8)) ++#define COM_PHY_TEST_VAL2 ((0x1 << 16) | (0x1 << 3) | (0x1 << 8)) ++ ++#endif +diff --git a/drivers/pci/hipcie/pcie_hi3531d.c b/drivers/pci/hipcie/pcie_hi3531d.c +new file mode 100644 +index 0000000..24c3a6d +--- /dev/null ++++ b/drivers/pci/hipcie/pcie_hi3531d.c +@@ -0,0 +1,641 @@ ++ ++#include "pcie_hi3531d.h" ++ ++static void *dbi_base_pcie0; ++static void *dbi_base_pcie1; ++static void *__iomem misc_ctrl_virt; ++static int __arch_pcie_info_setup(struct pcie_info *info, int *controllers_nr); ++static int __arch_pcie_sys_init(struct pcie_info *info); ++static void __arch_pcie_info_release(struct pcie_info *info); ++ ++struct pcie_iatu pcie0_iatu_table[] = { ++ { ++ .viewport = 0, ++ .region_ctrl_1 = 0x00000004, ++ .region_ctrl_2 = 0x90000000, ++ .lbar = PCIE0_EP_CONF_BASE + (1<<20), ++ .ubar = 0x0, ++ .lar = PCIE0_EP_CONF_BASE + (2<<20) - 1, ++ .ltar = 0x01000000, ++ .utar = 0x00000000, ++ }, ++ { ++ .viewport = 1, ++ .region_ctrl_1 = 0x00000005, ++ .region_ctrl_2 = 0x90000000, ++ .lbar = PCIE0_EP_CONF_BASE + (2<<20), ++ .ubar = 0x0, ++ .lar = PCIE0_EP_CONF_BASE + (__128MB__ - 1), ++ .ltar = 0x02000000, ++ .utar = 0x00000000, ++ }, ++}; ++ ++struct pcie_iatu pcie1_iatu_table[] = { ++ { ++ .viewport = 0, ++ .region_ctrl_1 = 0x00000004, ++ .region_ctrl_2 = 0x90000000, ++ .lbar = PCIE1_EP_CONF_BASE, ++ .ubar = 0x0, ++ .lar = PCIE1_EP_CONF_BASE + (1<<20) - 1, ++ .ltar = 0x01000000, ++ .utar = 0x00000000, ++ }, ++ { ++ .viewport = 1, ++ .region_ctrl_1 = 0x00000005, ++ .region_ctrl_2 = 0x90000000, ++ .lbar = PCIE1_EP_CONF_BASE, ++ .ubar = 0x0, ++ .lar = PCIE1_EP_CONF_BASE + (__128MB__ - 1), ++ .ltar = 0x02000000, ++ .utar = 0x00000000, ++ }, ++}; ++ ++static void __arch_pcie_preinit(void) ++{ ++ ++} ++ ++static struct resource pcie_mem[2]; ++static struct resource pcie_io[2]; ++static void __arch_get_pcie_res(int controller, ++ struct resource **pmem, ++ struct resource **pio) ++{ ++ if (controller >= 2) { ++ pcie_error("Pcie controller index(%d) error!", controller); ++ return; ++ } ++ ++ ++ *pmem = &pcie_mem[controller]; ++ *pio = &pcie_io[controller]; ++ ++ if (pcie_controller_0 == controller) { ++ (*pmem)->start = PCIE0_MEM_BASE; ++ (*pmem)->end = PCIE0_MEM_BASE + 0x8000000 - 1; ++ (*pmem)->flags = IORESOURCE_MEM; ++ (*pmem)->name = "memory"; ++ ++ (*pio)->start = 0x0; ++ (*pio)->end = 0x0; ++ (*pio)->flags = IORESOURCE_IO; ++ (*pio)->name = "io"; ++ } ++ ++ if (pcie_controller_1 == controller) { ++ ++ (*pmem)->start = PCIE1_MEM_BASE; ++ (*pmem)->end = PCIE1_MEM_BASE + 0x8000000 - 1; ++ (*pmem)->flags = IORESOURCE_MEM; ++ (*pmem)->name = "memory"; ++ ++ (*pio)->start = 0x1; ++ (*pio)->end = 0x1; ++ (*pio)->flags = IORESOURCE_IO; ++ (*pio)->name = "io"; ++ } ++} ++ ++static int __arch_get_int_irq(struct pcie_info *info, u8 pin) ++{ ++ if (pcie_controller_0 == info->controller) { ++ switch (pin) { ++ case PCIE_INTA_PIN: ++ return PCIE0_IRQ_INTA; ++ case PCIE_INTB_PIN: ++ return PCIE0_IRQ_INTB; ++ case PCIE_INTC_PIN: ++ return PCIE0_IRQ_INTC; ++ case PCIE_INTD_PIN: ++ return PCIE0_IRQ_INTD; ++ default: ++ pcie_error("Unknown pin for mapping irq!"); ++ return -1; ++ } ++ } ++ ++ if (pcie_controller_1 == info->controller) { ++ switch (pin) { ++ case PCIE_INTA_PIN: ++ return PCIE1_IRQ_INTA; ++ case PCIE_INTB_PIN: ++ return PCIE1_IRQ_INTB; ++ case PCIE_INTC_PIN: ++ return PCIE1_IRQ_INTC; ++ case PCIE_INTD_PIN: ++ return PCIE1_IRQ_INTD; ++ default: ++ pcie_error("Unknown pin for mapping irq!"); ++ return -1; ++ } ++ } ++ ++ return -1; ++} ++ ++static void __arch_config_iatu_tbl(struct pcie_info *info, ++ struct pci_sys_data *sys) ++{ ++ int i; ++ void __iomem *config_base; ++ struct pcie_iatu *ptable; ++ int table_size; ++ unsigned int ctl1_lbar_offset; ++ ++ config_base = (void __iomem *)info->conf_base_addr; ++ ++ if (pcie_controller_0 == info->controller) { ++ ptable = pcie0_iatu_table; ++ table_size = ARRAY_SIZE(pcie0_iatu_table); ++ } ++ ++ if (pcie_controller_1 == info->controller) { ++ ptable = pcie1_iatu_table; ++ table_size = ARRAY_SIZE(pcie1_iatu_table); ++ ++ ctl1_lbar_offset = (sys->busnr + 1) << 20; ++ ptable->lbar |= ctl1_lbar_offset; ++ ptable->lar |= ctl1_lbar_offset; ++ ++ ctl1_lbar_offset = (sys->busnr + 2) << 20; ++ (ptable + 1)->lbar |= ctl1_lbar_offset; ++ } ++ ++ for (i = 0; i < table_size; i++) { ++ writel((ptable + i)->viewport, config_base + 0x900); ++ writel((ptable + i)->lbar, config_base + 0x90c); ++ writel((ptable + i)->ubar, config_base + 0x910); ++ writel((ptable + i)->lar, config_base + 0x914); ++ writel((ptable + i)->ltar, config_base + 0x918); ++ writel((ptable + i)->utar, config_base + 0x91c); ++ writel((ptable + i)->region_ctrl_1, config_base + 0x904); ++ writel((ptable + i)->region_ctrl_2, config_base + 0x908); ++ } ++ ++} ++ ++static inline int __arch_check_pcie_link(struct pcie_info *info) ++{ ++ int val; ++ ++ if (pcie_controller_0 == info->controller) { ++ val = readl(dbi_base_pcie0 + PCIE_SYS_STATE0); ++ return ((val & (1 << PCIE_XMLH_LINK_UP)) ++ && (val & (1 << PCIE_RDLH_LINK_UP))) ? 1 : 0; ++ } ++ ++ if (pcie_controller_1 == info->controller) { ++ val = readl(dbi_base_pcie1 + PCIE_SYS_STATE0); ++ return ((val & (1 << PCIE_XMLH_LINK_UP)) ++ && (val & (1 << PCIE_RDLH_LINK_UP))) ? 1 : 0; ++ } ++ ++ return 0; ++} ++ ++/* ++ * ret: ++ */ ++static int __arch_pcie_info_set(struct pcie_info *info, int controller) ++{ ++ unsigned int pcie_mem_size; ++ unsigned int pcie_cfg_size; ++ unsigned int pcie_dbi_base; ++ unsigned int pcie_ep_conf_base; ++ ++#if CONFIG_PCIE0_SEL ++ if (pcie_controller_0 == controller) { ++ pcie_mem_size = CONFIG_PCIE0_DEVICES_MEM_SIZE; ++ pcie_cfg_size = CONFIG_PCIE0_DEVICES_CONFIG_SIZE; ++ pcie_dbi_base = PCIE0_DBI_BASE; ++ pcie_ep_conf_base = PCIE0_EP_CONF_BASE; ++ } ++#endif ++#if CONFIG_PCIE1_SEL ++ if (pcie_controller_1 == controller) { ++ pcie_mem_size = CONFIG_PCIE1_DEVICES_MEM_SIZE; ++ pcie_cfg_size = CONFIG_PCIE1_DEVICES_CONFIG_SIZE; ++ pcie_dbi_base = PCIE1_DBI_BASE; ++ pcie_ep_conf_base = PCIE1_EP_CONF_BASE; ++ } ++#endif ++ ++ if ((pcie_mem_size > __128MB__) || (pcie_cfg_size > __128MB__)) { ++ pcie_error( ++ "Invalid parameter: pcie mem size[0x%x], pcie cfg size[0x%x]!", ++ pcie_mem_size, pcie_cfg_size); ++ return -EINVAL; ++ } ++ ++ info->controller = controller; ++ ++ /* RC configuration space */ ++ info->conf_base_addr = (unsigned int)ioremap_nocache(pcie_dbi_base, ++ __8KB__); ++ if (!info->conf_base_addr) { ++ pcie_error("Address mapping for RC dbi failed!"); ++ return -EIO; ++ } ++ ++ /* Configuration space for all EPs */ ++ info->base_addr = (unsigned int)ioremap_nocache(pcie_ep_conf_base, ++ pcie_cfg_size); ++ if (!info->base_addr) { ++ iounmap((void *)info->conf_base_addr); ++ pcie_error("Address mapping for EPs cfg failed!"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static void __arch_pcie_info_clr(struct pcie_info *info) ++{ ++ if (info->base_addr) ++ iounmap((void *)info->base_addr); ++ ++ if (info->conf_base_addr) ++ iounmap((void *)info->conf_base_addr); ++} ++ ++static int __arch_get_port_nr(void) ++{ ++ unsigned int val, mode; ++ int nr; ++ ++ val = readl((void *)PCIE_SYS_STAT); ++ mode = (val >> 12) & 0xf; ++ switch (mode) { ++ case 0x1: ++ case 0x9: ++ nr = 1; ++ break; ++ ++ case 0x3: ++ case 0xb: ++ nr = 2; ++ break; ++ ++ default: ++ nr = 0; ++ break; ++ } ++ ++ return nr; ++} ++ ++static int __arch_pcie_info_setup(struct pcie_info *info, int *controllers_nr) ++{ ++ int nr; ++ ++ misc_ctrl_virt = (void *)IO_ADDRESS(MISC_CTRL_BASE); ++ ++ nr = __arch_get_port_nr(); ++ if (!nr) { ++ pr_err("Pcie port number: 0\n"); ++ return -EINVAL; ++ } ++ *controllers_nr = 0; ++ ++#if CONFIG_PCIE0_SEL ++ if (__arch_pcie_info_set(&info[*controllers_nr], pcie_controller_0)) ++ return -EIO; ++ (*controllers_nr)++; ++#endif ++#if CONFIG_PCIE1_SEL ++ if (nr < 2) { ++ pr_err("1-PCIE port mode, skip PCIE1 even config pcie1-sel!\n"); ++ return 0; ++ } ++ if (__arch_pcie_info_set(&info[*controllers_nr], pcie_controller_1)) { ++#if CONFIG_PCIE0_SEL ++ __arch_pcie_info_clr(&info[pcie_controller_0]); ++#endif ++ return -EIO; ++ } ++ (*controllers_nr)++; ++#endif ++ ++ return 0; ++} ++ ++static void __arch_pcie_info_release(struct pcie_info *info) ++{ ++ int nr; ++ ++ for (nr = 0; nr < pcie_controllers_nr; nr++) ++ __arch_pcie_info_clr(&info[nr]); ++} ++ ++void set_pcie0_para(void) ++{ ++ unsigned int val; ++ unsigned int flag; ++ ++ val = readl((void *)PCIE_SYS_STAT); ++ flag = (val >> 12) & 0xf; ++ ++ switch(flag) { ++ case 0x1: ++ case 0x3: ++ case 0x9: ++ case 0xb: ++ writel(0x615, misc_ctrl_virt + MISC_CTRL81); ++ writel(0x655, misc_ctrl_virt + MISC_CTRL81); ++ writel(0x615, misc_ctrl_virt + MISC_CTRL81); ++ writel(0x0, misc_ctrl_virt + MISC_CTRL81); ++ break; ++ default: ++ break; ++ ++ } ++} ++ ++void set_pcie1_para(void) ++{ ++ unsigned int val; ++ unsigned int flag; ++ ++ val = readl((void *)PCIE_SYS_STAT); ++ flag = (val >> 12) & 0xf; ++ ++ switch(flag) { ++ case 0x3: ++ case 0xb: ++ writel(0x615, misc_ctrl_virt + MISC_CTRL80); ++ writel(0x655, misc_ctrl_virt + MISC_CTRL80); ++ writel(0x615, misc_ctrl_virt + MISC_CTRL80); ++ writel(0x0, misc_ctrl_virt + MISC_CTRL80); ++ break; ++ default: ++ break; ++ ++ } ++} ++ ++static void __arch_pcie_sys_config(struct pcie_info *info) ++{ ++ static unsigned int loop_count = 0, comp_mode_nr = 0; ++ unsigned int val; ++ void *dbi_base = (void *)info->conf_base_addr; ++ void *crg_base = (void *)IO_ADDRESS(PERI_CRG_BASE); ++ ++ /* set pcie crg */ ++ writel(COMPHY_CLK_REST_VAL, crg_base + PERI_CRG_72); ++ ++ if (comp_mode_nr > 1) { ++ /* PCIE 1*/ ++ /* phy always work at 5Gbps */ ++ /* step1 addr/data enable: 0x901 */ ++ writel((PCIE_COMBPHY_TEST_ADDR_EN | PCIE_COMBPHY_TEST_DATA_EN), ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL80)); ++ /* step2 write enable: 0x941*/ ++ writel((PCIE_COMBPHY_TEST_ADDR_EN ++ | PCIE_COMBPHY_TEST_DATA_EN ++ | PCIE_COMBPHY_TEST_WR_EN), ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL80)); ++ /* step3 write release: 0x901 */ ++ writel((PCIE_COMBPHY_TEST_ADDR_EN | PCIE_COMBPHY_TEST_DATA_EN), ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL80)); ++ /* step4 status recovery */ ++ writel(PCIE_COMBPHY_RECOVER, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL80)); ++ } ++ ++ if (loop_count == 0) { ++ comp_mode_nr = __arch_get_port_nr(); ++ if (comp_mode_nr) { ++ /*PCIE 0*/ ++ /* phy always work at 5Gbps */ ++ /* step1 addr/data enable: 0x901 */ ++ writel((PCIE_COMBPHY_TEST_ADDR_EN | PCIE_COMBPHY_TEST_DATA_EN), ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL81)); ++ /* step2 write enable: 0x941*/ ++ writel((PCIE_COMBPHY_TEST_ADDR_EN ++ | PCIE_COMBPHY_TEST_DATA_EN ++ | PCIE_COMBPHY_TEST_WR_EN), ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL81)); ++ /* step3 write release: 0x901 */ ++ writel((PCIE_COMBPHY_TEST_ADDR_EN | PCIE_COMBPHY_TEST_DATA_EN), ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL81)); ++ /* step4 status recovery */ ++ writel(PCIE_COMBPHY_RECOVER, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL81)); ++ } ++ loop_count++; ++ } ++ ++#if CONFIG_PCIE0_SEL ++ /* refclk output from phy for PCIE0 */ ++ val = readl((void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL3)); ++ /* bit0 = 1: pad output enable; ++ * bit1 = 0: pad input disable; ++ * bit6 = 1: refclk from phy */ ++ val |= PCIE0_REGCLK_OUTPUT_EN | PCIE0_REGCLK_SRC_SEL_PHY; ++ val &= ~PCIE0_REGCLK_INPUT_EN; ++ writel(val, (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL3)); ++ mdelay(1); ++#endif ++ ++#if CONFIG_PCIE1_SEL ++ /* refclk output from phy for PCIE1 */ ++ val = readl((void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL3)); ++ /* bit2 = 1: pad output enable; ++ * bit3 = 0: pad input disable; ++ * bit7 = 1: refclk from phy */ ++ val |= PCIE1_REGCLK_OUTPUT_EN | PCIE1_REGCLK_SRC_SEL_PHY; ++ val &= ~PCIE1_REGCLK_INPUT_EN; ++ writel(val, (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL3)); ++ mdelay(1); ++#endif ++ ++ /* ++ * Disable PCIE ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL7); ++ val &= (~(1 << PCIE_APP_LTSSM_ENBALE)); ++ writel(val, dbi_base + PCIE_SYS_CTRL7); ++ ++ if (pcie_controller_0 == info->controller) { ++ ++ dbi_base_pcie0 = dbi_base; ++ /* ++ * Reset ++ */ ++ val = readl(crg_base + PERI_CRG73); ++ val |= (1 << PCIE0_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG73); ++ ++ /* ++ * Retreat from the reset state ++ */ ++ udelay(500); ++ val = readl(crg_base + PERI_CRG73); ++ val &= ~(1 << PCIE0_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG73); ++ mdelay(10); ++ ++ set_pcie0_para(); ++ mdelay(10); ++ } ++ ++ if (pcie_controller_1 == info->controller) { ++ ++ dbi_base_pcie1 = dbi_base; ++ /* ++ * Reset ++ */ ++ val = readl(crg_base + PERI_CRG73); ++ val |= (1 << PCIE1_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG73); ++ ++ /* ++ * Retreat from the reset state ++ */ ++ udelay(500); ++ val = readl(crg_base + PERI_CRG73); ++ val &= ~(1 << PCIE1_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG73); ++ mdelay(10); ++ ++ set_pcie1_para(); ++ mdelay(10); ++ } ++ ++ /* ++ * PCIE RC work mode ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL0); ++ val &= (~(0xf << PCIE_DEVICE_TYPE)); ++ val |= (PCIE_WM_RC << PCIE_DEVICE_TYPE); ++ writel(val, dbi_base + PCIE_SYS_CTRL0); ++ ++ if (pcie_controller_0 == info->controller) { ++ /* ++ * Enable clk ++ */ ++ val = readl(crg_base + PERI_CRG73); ++ val |= ((1 << PCIE0_X2_BUS_CKEN) ++ | (1 << PCIE0_X2_SYS_CKEN) ++ | (1 << PCIE0_X2_PIPE_CKEN) ++ | (1 << PCIE0_X2_AUX_CKEN)); ++ writel(val, crg_base + PERI_CRG73); ++ } ++ ++ if (pcie_controller_1 == info->controller) { ++ ++ /* ++ * Enable clk ++ */ ++ val = readl(crg_base + PERI_CRG73); ++ val |= ((1 << PCIE1_X2_BUS_CKEN) ++ | (1 << PCIE1_X2_SYS_CKEN) ++ | (1 << PCIE1_X2_PIPE_CKEN) ++ | (1 << PCIE1_X2_AUX_CKEN)); ++ writel(val, crg_base + PERI_CRG73); ++ } ++ ++ mdelay(10); ++ ++ /* ++ * Enable controller ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL7); ++ val |= (1 << PCIE_APP_LTSSM_ENBALE); ++ writel(val, dbi_base + PCIE_SYS_CTRL7); ++ udelay(1000); ++ ++ /* ++ * Set PCIE controller class code to be PCI-PCI bridge device ++ */ ++ val = readl(dbi_base + PCI_CLASS_REVISION); ++ val &= ~(0xffffff00); ++ val |= (0x60400 << 8); ++ writel(val, dbi_base + PCI_CLASS_REVISION); ++ udelay(1000); ++ ++ val = readl(dbi_base + PCI_COMMAND); ++ val |= 7; ++ writel(val, dbi_base + PCI_COMMAND); ++} ++ ++static int __arch_pcie_sys_init(struct pcie_info *info) ++{ ++ int nr; ++ ++ for (nr = 0; nr < pcie_controllers_nr; nr++) ++ __arch_pcie_sys_config(&info[nr]); ++ ++ return 0; ++} ++ ++static void __arch_pcie_sys_exit(void) ++{ ++ void *crg_base = (void *)IO_ADDRESS(PERI_CRG_BASE); ++ unsigned int val; ++ ++#ifdef CONFIG_PCIE0_SEL ++ /* ++ * Disable PCIE ++ */ ++ val = readl(dbi_base_pcie0 + PCIE_SYS_CTRL7); ++ val &= (~(1 << PCIE_APP_LTSSM_ENBALE)); ++ writel(val, dbi_base_pcie0 + PCIE_SYS_CTRL7); ++ ++ /* ++ * Reset ++ */ ++ val = readl(crg_base + PERI_CRG73); ++ val |= (1 << PCIE0_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG73); ++ ++ udelay(1000); ++ ++ /* ++ * Disable clk ++ */ ++ val = readl(crg_base + PERI_CRG73); ++ val &= (~(1 << PCIE0_X2_AUX_CKEN)); ++ val &= (~(1 << PCIE0_X2_PIPE_CKEN)); ++ val &= (~(1 << PCIE0_X2_SYS_CKEN)); ++ val &= (~(1 << PCIE0_X2_BUS_CKEN)); ++ writel(val, crg_base + PERI_CRG73); ++ ++ udelay(1000); ++#endif ++ ++#if CONFIG_PCIE1_SEL ++ /* ++ * Disable PCIE ++ */ ++ val = readl(dbi_base_pcie1 + PCIE_SYS_CTRL7); ++ val &= (~(1 << PCIE_APP_LTSSM_ENBALE)); ++ writel(val, dbi_base_pcie1 + PCIE_SYS_CTRL7); ++ ++ /* ++ * Reset ++ */ ++ val = readl(crg_base + PERI_CRG73); ++ val |= (1 << PCIE1_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG73); ++ ++ udelay(1000); ++ ++ /* ++ * Disable clk ++ */ ++ val = readl(crg_base + PERI_CRG73); ++ val &= (~(1 << PCIE1_X2_AUX_CKEN)); ++ val &= (~(1 << PCIE1_X2_PIPE_CKEN)); ++ val &= (~(1 << PCIE1_X2_SYS_CKEN)); ++ val &= (~(1 << PCIE1_X2_BUS_CKEN)); ++ writel(val, crg_base + PERI_CRG73); ++ ++ udelay(1000); ++#endif ++} ++ +diff --git a/drivers/pci/hipcie/pcie_hi3531d.h b/drivers/pci/hipcie/pcie_hi3531d.h +new file mode 100644 +index 0000000..4c95f07 +--- /dev/null ++++ b/drivers/pci/hipcie/pcie_hi3531d.h +@@ -0,0 +1,98 @@ ++#ifndef __HISI_PCIE_H__ ++#define __HISI_PCIE_H__ ++ ++#include <mach/io.h> ++ ++#define MISC_CTRL_BASE 0x12120000 ++#define PERI_CRG_BASE 0x12040000 ++#define PCIE_SYS_STAT IO_ADDRESS(0x1205008C) ++ ++#define PCIE0_MEM_BASE 0x28000000 ++#define PCIE0_EP_CONF_BASE 0x20000000 ++#define PCIE0_DBI_BASE 0x122F0000 ++#define PCIE_DBI_BASE PCIE0_MEM_BASE ++ ++#define PCIE1_MEM_BASE 0x38000000 ++#define PCIE1_EP_CONF_BASE 0x30000000 ++#define PCIE1_DBI_BASE 0x122F8000 ++ ++#define PERI_CRG_72 0x120 ++#define COMPHY_CLK_REST_VAL ((0x2 << 14) | (0x2 << 12) \ ++ | (0x1 << 9) | (0x1 << 8) \ ++ | (0x2 << 6) | (0x2 << 4) | 0x3) ++ ++#define PERI_CRG73 0x124 ++ ++#define PCIE0_X2_SRST_REQ 6 ++#define PCIE0_X2_AUX_CKEN 3 ++#define PCIE0_X2_PIPE_CKEN 2 ++#define PCIE0_X2_SYS_CKEN 1 ++#define PCIE0_X2_BUS_CKEN 0 ++ ++#define PCIE_PAD_OE_MASK (0x7 << 8) ++ ++#define PCIE1_X2_SRST_REQ 14 ++#define PCIE1_X2_AUX_CKEN 11 ++#define PCIE1_X2_PIPE_CKEN 10 ++#define PCIE1_X2_SYS_CKEN 9 ++#define PCIE1_X2_BUS_CKEN 8 ++ ++#define PCIE_SYS_CTRL0 0x1000 ++#define PCIE_DEVICE_TYPE 28 ++#define PCIE_WM_EP 0x0 ++#define PCIE_WM_LEGACY 0x1 ++#define PCIE_WM_RC 0x4 ++ ++#define PCIE_SYS_CTRL7 0x101C ++#define PCIE_APP_LTSSM_ENBALE 11 ++#define PCIE_ACCESS_ENABLE 13 ++ ++#define PCIE_SYS_STATE0 0x1100 ++#define PCIE_XMLH_LINK_UP 15 ++#define PCIE_RDLH_LINK_UP 5 ++ ++#define PCIE0_IRQ_INTA 94 ++#define PCIE0_IRQ_INTB 95 ++#define PCIE0_IRQ_INTC 96 ++#define PCIE0_IRQ_INTD 97 ++#define PCIE0_IRQ_EDMA 98 ++#define PCIE0_IRQ_MSI 99 ++#define PCIE0_IRQ_LINK_DOWN 100 ++ ++#define PCIE1_IRQ_INTA 101 ++#define PCIE1_IRQ_INTB 102 ++#define PCIE1_IRQ_INTC 103 ++#define PCIE1_IRQ_INTD 104 ++#define PCIE1_IRQ_EDMA 105 ++#define PCIE1_IRQ_MSI 106 ++#define PCIE1_IRQ_LINK_DOWN 107 ++ ++#define PCIE_INTA_PIN 1 ++#define PCIE_INTB_PIN 2 ++#define PCIE_INTC_PIN 3 ++#define PCIE_INTD_PIN 4 ++ ++#define MISC_CTRL3 0xC ++#define MISC_CTRL81 0x144 ++#define MISC_CTRL80 0x140 ++ ++#define PCIE_COMBPHY_TEST_ADDR_EN 0x1 ++#define PCIE_COMBPHY_TEST_DATA_EN (0x9 << 8) ++ ++#define PCIE_COMBPHY_TEST_WR_EN (0x1 << 6) ++#define PCIE_COMBPHY_RECOVER 0x0 ++ ++#define PCIE0_REGCLK_OUTPUT_EN (1 << 0) ++#define PCIE0_REGCLK_INPUT_EN (1 << 1) ++#define PCIE0_REGCLK_SRC_SEL_PHY (1 << 6) ++ ++#define PCIE1_REGCLK_OUTPUT_EN (1 << 2) ++#define PCIE1_REGCLK_INPUT_EN (1 << 3) ++#define PCIE1_REGCLK_SRC_SEL_PHY (1 << 7) ++ ++#define REG_GPIO_15_BASE 0x12240000 ++#define GPIO_15_DATA 0x4 ++#define GPIO_15_DIR 0x400 ++#define GPIO_15_DIR_SET_OUTPUT 1 ++ ++#endif +diff --git a/drivers/pci/hipcie/pcie_hi3559.c b/drivers/pci/hipcie/pcie_hi3559.c +new file mode 100644 +index 0000000..89d4fbe +--- /dev/null ++++ b/drivers/pci/hipcie/pcie_hi3559.c +@@ -0,0 +1,314 @@ ++#include <mach/io.h> ++#include "pcie_hi3559.h" ++ ++static void *dbi_base; ++static int __arch_pcie_info_setup(struct pcie_info *info, int *controllers_nr); ++static int __arch_pcie_sys_init(struct pcie_info *info); ++static void __arch_pcie_info_release(struct pcie_info *info); ++ ++struct pcie_iatu iatu_table[] = { ++ { ++ .viewport = 0, ++ .region_ctrl_1 = 0x00000004, ++ .region_ctrl_2 = 0x90000000, ++ .lbar = PCIE_EP_CONF_BASE + (1<<20), ++ .ubar = 0x0, ++ .lar = PCIE_EP_CONF_BASE + (2<<20) - 1, ++ .ltar = 0x01000000, ++ .utar = 0x00000000, ++ }, ++ { ++ .viewport = 1, ++ .region_ctrl_1 = 0x00000005, ++ .region_ctrl_2 = 0x90000000, ++ .lbar = PCIE_EP_CONF_BASE + (2<<20), ++ .ubar = 0x0, ++ .lar = PCIE_EP_CONF_BASE + (__128MB__ - 1), ++ .ltar = 0x02000000, ++ .utar = 0x00000000, ++ }, ++}; ++ ++static void __arch_pcie_preinit(void) ++{ ++ ++} ++ ++static struct resource pcie_mem; ++static struct resource pcie_io; ++static void __arch_get_pcie_res(int controller, ++ struct resource **pmem, ++ struct resource **pio) ++{ ++ *pmem = &pcie_mem; ++ (*pmem)->start = PCIE_MEM_BASE; ++ (*pmem)->end = PCIE_MEM_BASE + __128MB__ - 0x100000 - 1; ++ (*pmem)->flags = IORESOURCE_MEM; ++ (*pmem)->name = "memory"; ++ ++ *pio = &pcie_io; ++ (*pio)->start = PCIE_MEM_BASE + __128MB__ - 0x100000; ++ (*pio)->end = PCIE_MEM_BASE + __128MB__ - 1; ++ (*pio)->flags = IORESOURCE_IO; ++ (*pio)->name = "io"; ++} ++ ++static int __arch_get_int_irq(struct pcie_info *info, u8 pin) ++{ ++ switch (pin) { ++ case PCIE_INTA_PIN: ++ return PCIE_IRQ_INTA; ++ case PCIE_INTB_PIN: ++ return PCIE_IRQ_INTB; ++ case PCIE_INTC_PIN: ++ return PCIE_IRQ_INTC; ++ case PCIE_INTD_PIN: ++ return PCIE_IRQ_INTD; ++ default: ++ pcie_error("Unknown pin for mapping irq!"); ++ return -1; ++ } ++} ++ ++static void __arch_config_iatu_tbl(struct pcie_info *info, ++ struct pci_sys_data *sys) ++{ ++ int i; ++ void __iomem *config_base = (void __iomem *)info->conf_base_addr; ++ struct pcie_iatu *ptable = iatu_table; ++ int table_size = ARRAY_SIZE(iatu_table); ++ ++ for (i = 0; i < table_size; i++) { ++ writel((ptable + i)->viewport, config_base + 0x900); ++ writel((ptable + i)->lbar, config_base + 0x90c); ++ writel((ptable + i)->ubar, config_base + 0x910); ++ writel((ptable + i)->lar, config_base + 0x914); ++ writel((ptable + i)->ltar, config_base + 0x918); ++ writel((ptable + i)->utar, config_base + 0x91c); ++ writel((ptable + i)->region_ctrl_1, config_base + 0x904); ++ writel((ptable + i)->region_ctrl_2, config_base + 0x908); ++ } ++ ++} ++ ++static inline int __arch_check_pcie_link(struct pcie_info *info) ++{ ++ int val; ++ ++ val = readl(dbi_base + PCIE_SYS_STATE0); ++ return ((val & (1 << PCIE_XMLH_LINK_UP)) ++ && (val & (1 << PCIE_RDLH_LINK_UP))) ? 1 : 0; ++} ++ ++/* ++ * ret: ++ */ ++static int __arch_pcie_info_setup(struct pcie_info *info, int *controllers_nr) ++{ ++ unsigned int mem_size = CONFIG_PCIE0_DEVICES_MEM_SIZE; ++ unsigned int cfg_size = CONFIG_PCIE0_DEVICES_CONFIG_SIZE; ++ ++ if ((mem_size > __128MB__) || (cfg_size > __128MB__)) { ++ pcie_error( ++ "Invalid parameter: pcie mem size[0x%x], pcie cfg size[0x%x]!", ++ mem_size, cfg_size); ++ return -EINVAL; ++ } ++ ++ info->controller = 0; ++ ++ /* RC configuration space */ ++ info->conf_base_addr = (unsigned int)ioremap_nocache(PCIE_DBI_BASE, ++ __8KB__); ++ if (!info->conf_base_addr) { ++ pcie_error("Address mapping for RC dbi failed!"); ++ return -EIO; ++ } ++ ++ /* Configuration space for all EPs */ ++ info->base_addr = (unsigned int)ioremap_nocache(PCIE_EP_CONF_BASE, ++ cfg_size); ++ if (!info->base_addr) { ++ iounmap((void *)info->conf_base_addr); ++ pcie_error("Address mapping for EPs cfg failed!"); ++ return -EIO; ++ } ++ ++ *controllers_nr = 1; ++ ++ return 0; ++ ++} ++ ++static void __arch_pcie_info_release(struct pcie_info *info) ++{ ++ if (info->base_addr) ++ iounmap((void *)info->base_addr); ++ ++ if (info->conf_base_addr) ++ iounmap((void *)info->conf_base_addr); ++} ++ ++void set_pcie_para(void *crg_base) ++{ ++ unsigned int val; ++ ++ void * misc_base = (void *)IO_ADDRESS(MISC_CTRL_BASE); ++ ++ val = readl(crg_base + PERI_CRG43); ++ val = val & (~(1 << 0)); ++ val = val | (1 << 1); ++ writel(val,crg_base + PERI_CRG43); ++ ++ writel(0x1506, misc_base + MISC_CTRL33); ++ writel(0x11506, misc_base + MISC_CTRL33); ++ writel(0x1506, misc_base + MISC_CTRL33); ++ writel(0x0, misc_base + MISC_CTRL33); ++ ++ writel(0x108, misc_base + MISC_CTRL33); ++ writel(0x10108, misc_base + MISC_CTRL33); ++ writel(0x108, misc_base + MISC_CTRL33); ++ writel(0x0, misc_base + MISC_CTRL33); ++} ++ ++static int __arch_pcie_sys_init(struct pcie_info *info) ++{ ++ unsigned int val; ++ void *crg_base = (void *)IO_ADDRESS(PERI_CRG_BASE); ++ ++ dbi_base = (void *)info->conf_base_addr; ++ ++ /*open pcie pad oe*/ ++ val = readl(crg_base + PERI_CRG44); ++ val &= ~(PCIE_PAD_OE_MASK); ++ writel(val, crg_base + PERI_CRG44); ++ ++ /* refclk output from phy */ ++ writel(PCIE_CLKREQ_FILTER_BYPASS, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL34)); ++ mdelay(1); ++ ++ /* ++ * Disable PCIE ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL7); ++ val &= (~(1 << PCIE_APP_LTSSM_ENBALE)); ++ val |= (1 << PCIE_ACCESS_ENABLE); ++ writel(val, dbi_base + PCIE_SYS_CTRL7); ++ ++ /* ++ * Reset ++ */ ++ val = readl(crg_base + PERI_CRG44); ++ val |= (1 << PCIE_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG44); ++ ++ /* ++ * Retreat from the reset state ++ */ ++ udelay(500); ++ val = readl(crg_base + PERI_CRG44); ++ val &= ~(1 << PCIE_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG44); ++ mdelay(10); ++ ++ ++ /* ++ * PCIE RC work mode ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL0); ++ val &= (~(0xf << PCIE_DEVICE_TYPE)); ++ val |= (PCIE_WM_RC << PCIE_DEVICE_TYPE); ++ writel(val, dbi_base + PCIE_SYS_CTRL0); ++ ++ /* ++ * Enable clk ++ */ ++ val = readl(crg_base + PERI_CRG44); ++ val |= ((1 << PCIE_X2_BUS_CKEN) ++ | (1 << PCIE_X2_SYS_CKEN) ++ | (1 << PCIE_X2_PIPE_CKEN) ++ | (1 << PCIE_X2_AUX_CKEN)); ++ writel(val, crg_base + PERI_CRG44); ++ ++ mdelay(10); ++ ++ set_pcie_para(crg_base); ++ mdelay(10); ++ ++ /* ++ * Set PCIE controller class code to be PCI-PCI bridge device ++ */ ++ val = readl(dbi_base + PCI_CLASS_REVISION); ++ val &= ~(0xffffff00); ++ val |= (0x60400 << 8); ++ writel(val, dbi_base + PCI_CLASS_REVISION); ++ udelay(1000); ++ ++ /* phy always work at 5Gbps */ ++ writel(COM_PHY_TEST_VAL1, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL33)); ++ writel(COM_PHY_TEST_VAL2, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL33)); ++ writel(COM_PHY_TEST_VAL1, ++ (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL33)); ++ writel(0x0, (void *)IO_ADDRESS(MISC_CTRL_BASE + MISC_CTRL33)); ++ ++ /* default deemphasis to -3.5 dB */ ++ writel(DEEMPHASIS_VAL, (void *)IO_ADDRESS(PCIE_DBI_BASE + DEEMPHASIS_REG)); ++ ++ /* ++ * Enable controller ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL7); ++ val |= (1 << PCIE_APP_LTSSM_ENBALE); ++ writel(val, dbi_base + PCIE_SYS_CTRL7); ++ udelay(1000); ++ ++ val = readl(dbi_base + PCI_COMMAND); ++ val |= 7; ++ writel(val, dbi_base + PCI_COMMAND); ++ ++ /* set pcie to gen 1*/ ++#if 0 ++ writel(0x1, dbi_base + 0x8BC); ++ val = readl(dbi_base + 0x7C); ++ val = ((val >> 4) << 4) | 0x1; ++ writel(val, dbi_base + 0x7C); ++#endif ++ return 0; ++} ++ ++static void __arch_pcie_sys_exit(void) ++{ ++ unsigned int val; ++ void *crg_base = (void *)IO_ADDRESS(PERI_CRG_BASE); ++ ++ /* ++ * Disable PCIE ++ */ ++ val = readl(dbi_base + PCIE_SYS_CTRL7); ++ val &= (~(1 << PCIE_APP_LTSSM_ENBALE)); ++ writel(val, dbi_base + PCIE_SYS_CTRL7); ++ ++ /* ++ * Reset ++ */ ++ val = readl(crg_base + PERI_CRG44); ++ val |= (1 << PCIE_X2_SRST_REQ); ++ writel(val, crg_base + PERI_CRG44); ++ ++ udelay(1000); ++ ++ /* ++ * Disable clk ++ */ ++ val = readl(crg_base + PERI_CRG44); ++ val &= (~(1 << PCIE_X2_AUX_CKEN)); ++ val &= (~(1 << PCIE_X2_PIPE_CKEN)); ++ val &= (~(1 << PCIE_X2_SYS_CKEN)); ++ val &= (~(1 << PCIE_X2_BUS_CKEN)); ++ writel(val, crg_base + PERI_CRG44); ++ ++ udelay(1000); ++} +diff --git a/drivers/pci/hipcie/pcie_hi3559.h b/drivers/pci/hipcie/pcie_hi3559.h +new file mode 100644 +index 0000000..e185999 +--- /dev/null ++++ b/drivers/pci/hipcie/pcie_hi3559.h +@@ -0,0 +1,55 @@ ++#ifndef __HISI_PCIE_H__ ++#define __HISI_PCIE_H__ ++ ++#define MISC_CTRL_BASE 0x12030000 ++#define PCIE_MEM_BASE 0x28000000 ++#define PCIE_EP_CONF_BASE 0x20000000 ++#define PCIE_DBI_BASE 0x12160000 ++#define PERI_CRG_BASE 0x12010000 ++ ++#define PERI_CRG43 0xAC ++#define PERI_CRG44 0xB0 ++#define PCIE_X2_SRST_REQ 2 ++ ++#define PCIE_X2_AUX_CKEN 7 ++#define PCIE_X2_PIPE_CKEN 6 ++#define PCIE_X2_SYS_CKEN 5 ++#define PCIE_X2_BUS_CKEN 4 ++#define PCIE_PAD_OE_MASK (0x7 << 8) ++ ++#define PCIE_SYS_CTRL0 0x1000 ++#define PCIE_DEVICE_TYPE 28 ++#define PCIE_WM_EP 0x0 ++#define PCIE_WM_LEGACY 0x1 ++#define PCIE_WM_RC 0x4 ++ ++#define PCIE_SYS_CTRL7 0x101C ++#define PCIE_APP_LTSSM_ENBALE 11 ++#define PCIE_ACCESS_ENABLE 13 ++ ++#define PCIE_SYS_STATE0 0x1100 ++#define PCIE_XMLH_LINK_UP 15 ++#define PCIE_RDLH_LINK_UP 5 ++ ++#define PCIE_IRQ_INTA 89 ++#define PCIE_IRQ_INTB 90 ++#define PCIE_IRQ_INTC 91 ++#define PCIE_IRQ_INTD 92 ++#define PCIE_IRQ_EDMA 93 ++#define PCIE_IRQ_MSI 94 ++#define PCIE_IRQ_LINK_DOWN 95 ++ ++#define PCIE_INTA_PIN 1 ++#define PCIE_INTB_PIN 2 ++#define PCIE_INTC_PIN 3 ++#define PCIE_INTD_PIN 4 ++ ++#define MISC_CTRL33 0x84 ++#define MISC_CTRL34 0x88 ++#define DEEMPHASIS_REG 0xa0 ++#define PCIE_CLKREQ_FILTER_BYPASS 0x600 ++#define DEEMPHASIS_VAL 0x42 ++#define COM_PHY_TEST_VAL1 ((0x1 << 3) | (0x1 << 8)) ++#define COM_PHY_TEST_VAL2 ((0x1 << 16) | (0x1 << 3) | (0x1 << 8)) ++ ++#endif +diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig +index 2a436e6..e433939 100644 +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -133,6 +133,30 @@ config PHY_EXYNOS5250_SATA + SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host + port to accept one SATA device. + ++config PHY_HISI_INNO_USB2 ++ tristate "Hisilicon Inno USB2 PHY support" ++ depends on OF && HAS_IOMEM && (USB_EHCI_HCD || HIUSB_DEVICE2_0) ++ select GENERIC_PHY ++ select MFD_SYSCON ++ default n ++ help ++ Support for INNO PHY on Hisilicon Socs. This Phy supports ++ USB 1.5Mb/s, USB 12Mb/s, USB 480Mb/s speeds. It suppots one ++ USB host port to accept one USB device. Support init the phy ++ and adjust phy Eye Diagram. ++ ++config PHY_HI35x1D_INNO_USB2 ++ tristate "Hisilicon 3531D&3521D Inno USB2 PHY support" ++ depends on (ARCH_HI3531D|| ARCH_HI3521D) && OF && HAS_IOMEM && (!PHY_HISI_INNO_USB2) ++ select GENERIC_PHY ++ select MFD_SYSCON ++ default y ++ help ++ Support for INNO PHY on Hisilicon Socs. This Phy supports ++ USB 1.5Mb/s, USB 12Mb/s, USB 480Mb/s speeds. It suppots one ++ USB host port to accept one USB device. Support init the phy ++ and adjust phy Eye Diagram. ++ + config PHY_HIX5HD2_SATA + tristate "HIX5HD2 SATA PHY Driver" + depends on ARCH_HIX5HD2 && OF && HAS_IOMEM +@@ -141,6 +165,28 @@ config PHY_HIX5HD2_SATA + help + Support for SATA PHY on Hisilicon hix5hd2 Soc. + ++config PHY_HISI_USB3 ++ tristate "HISI USB3 PHY Driver" ++ depends on (ARCH_HI3519 || ARCH_HI3519V101 || ARCH_HI3559 || ARCH_HI3556 || ARCH_HI3516AV200) && OF && HAS_IOMEM && USB_XHCI_HCD ++ select GENERIC_PHY ++ select MFD_SYSCON ++ help ++ Support for USB PHY on Hisilicon Soc. ++ Enable this to support the hisi USB 3.0 PHY driver for hisilicon ++ SoCs. This driver provides the interface for USB 3.0 PHY. Support ++ init the phy and adjust phy Eye Diagram. ++ ++config PHY_HI3531D_USB3 ++ tristate "HI3531D USB3 PHY Driver" ++ depends on ARCH_HI3531D && OF && HAS_IOMEM && USB_XHCI_HCD ++ select GENERIC_PHY ++ select MFD_SYSCON ++ help ++ Support for USB PHY on Hisilicon Soc. ++ Enable this to support the hisi USB 3.0 PHY driver for hisilicon ++ SoCs. This driver provides the interface for USB 3.0 PHY. Support ++ init the phy and adjust phy Eye Diagram. ++ + config PHY_SUN4I_USB + tristate "Allwinner sunxi SoC USB PHY driver" + depends on ARCH_SUNXI && HAS_IOMEM && OF +@@ -256,4 +302,31 @@ config PHY_STIH41X_USB + Enable this to support the USB transceiver that is part of + STMicroelectronics STiH41x SoC series. + ++config HI_NANO_PHY_SATA ++ tristate "hisilicon sata nano phy support" ++ depends on ((ARCH_HI3531D || ARCH_HI3536C || ARCH_HI3521D) && OF && HAS_IOMEM) ++ default y if (ARCH_HI3531D || ARCH_HI3536C || ARCH_HI3521D) ++ select GENERIC_PHY ++ help ++ Enable this to support the sata nano phy that is part of ++ sata driver for hisilicon. ++ ++config HI_SATA_PORTS ++ int "hi sata port number" ++ depends on HI_NANO_PHY_SATA ++ range 1 4 if ARCH_HI3531D ++ range 1 2 if (ARCH_HI3536C || ARCH_HI3521D) ++ default "4" if ARCH_HI3531D ++ default "2" if (ARCH_HI3536C || ARCH_HI3521D) ++ help ++ hisilicon sata port number. ++ ++config HI_SATA_MODE ++ int "hi sata interworking speed mode(1.5G:0/3G:1/6G:2)" ++ depends on HI_NANO_PHY_SATA ++ range 0 2 if (ARCH_HI3536C || ARCH_HI3521D || ARCH_HI3531D) ++ default "1" if (ARCH_HI3536C || ARCH_HI3521D || ARCH_HI3531D) ++ help ++ hisilicon interworking speed mode. ++ + endmenu +diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile +index c4590fc..857f284 100644 +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -16,6 +16,15 @@ obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o + obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o + obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o + obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o ++obj-$(CONFIG_HI_NANO_PHY_SATA) += phy-hisi-nano-phy-sata.o ++ifdef CONFIG_ARCH_HI3516CV300 ++obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o ++else ++obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-usb.o ++endif ++obj-$(CONFIG_PHY_HI35x1D_INNO_USB2) += phy-hi35x1d-usb.o ++obj-$(CONFIG_PHY_HI3531D_USB3) += phy-hi3531d-usb3.o ++obj-$(CONFIG_PHY_HISI_USB3) += phy-hisi-usb3.o + obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o + obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o + phy-exynos-usb2-y += phy-samsung-usb2.o +diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c +index 2733112..bfbbf43 100644 +--- a/drivers/phy/phy-core.c ++++ b/drivers/phy/phy-core.c +@@ -586,6 +586,38 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np, + EXPORT_SYMBOL_GPL(devm_of_phy_get); + + /** ++ * devm_of_phy_get_by_index() - lookup and obtain a reference to a phy by index. ++ * @dev: device that requests this phy ++ * @np: node containing the phy ++ * @index: index of the phy ++ * ++ * Gets the phy using _of_phy_get(), and associates a device with it using ++ * devres. On driver detach, release function is invoked on the devres data, ++ * then, devres data is freed. ++ * ++ */ ++struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np, ++ int index) ++{ ++ struct phy **ptr, *phy; ++ ++ ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); ++ if (!ptr) ++ return ERR_PTR(-ENOMEM); ++ ++ phy = _of_phy_get(np, index); ++ if (!IS_ERR(phy)) { ++ *ptr = phy; ++ devres_add(dev, ptr); ++ } else { ++ devres_free(ptr); ++ } ++ ++ return phy; ++} ++EXPORT_SYMBOL_GPL(devm_of_phy_get_by_index); ++ ++/** + * phy_create() - create a new phy + * @dev: device that is creating the new phy + * @node: device node of the phy +diff --git a/drivers/phy/phy-hi3521d-sata.c b/drivers/phy/phy-hi3521d-sata.c +new file mode 100644 +index 0000000..8666605 +--- /dev/null ++++ b/drivers/phy/phy-hi3521d-sata.c +@@ -0,0 +1,415 @@ ++/* ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/mfd/syscon.h> ++#include <linux/module.h> ++#include <linux/phy/phy.h> ++#include <linux/platform_device.h> ++#include <linux/regmap.h> ++#include <mach/io.h> ++#include <mach/platform.h> ++ ++unsigned int sata_port_map; ++ ++enum { ++ HI_SATA_PERI_CTRL = IO_ADDRESS(0x12040000), ++ HI_SATA_PERI_CRG72 = (HI_SATA_PERI_CTRL + 0x120), ++ HI_SATA_PERI_CRG74 = (HI_SATA_PERI_CTRL + 0x128), ++ ++ HI_SATA_PHY0_REFCLK_SEL_MASK = (0x3 << 4), ++ HI_SATA_PHY0_REFCLK_SEL = (0x1 << 4), ++ HI_SATA_PHY1_REFCLK_SEL_MASK = (0x3 << 6), ++ HI_SATA_PHY1_REFCLK_SEL = (0x1 << 6), ++ ++ HI_SATA_PHY0_CLK_EN = (1 << 0), ++ HI_SATA_PHY1_CLK_EN = (1 << 1), ++ ++ HI_SATA_PHY0_RST = (1 << 2), ++ HI_SATA_PHY1_RST = (1 << 3), ++ ++ HI_SATA_PHY_BACK_MASK_ALL = 0xf0, ++ HI_SATA_PHY1_RST_BACK_MASK = (1 << 5), ++ HI_SATA_PHY0_RST_BACK_MASK = (1 << 4), ++ ++ HI_SATA_BUS_CKEN = (1 << 0), ++ HI_SATA_BUS_SRST_REQ = (1 << 8), ++ HI_SATA_CKO_ALIVE_CKEN = (1 << 2), ++ HI_SATA_CKO_ALIVE_SRST_REQ = (1 << 9), ++ HI_SATA_RX0_CKEN = (1 << 1), ++ HI_SATA_TX0_CKEN = (1 << 3), ++ HI_SATA_RX0_SRST_REQ = (1 << 10), ++ HI_SATA0_SRST_REQ = (1 << 11), ++ HI_SATA_RX1_CKEN = (1 << 12), ++ HI_SATA_TX1_CKEN = (1 << 13), ++ HI_SATA_RX1_SRST_REQ = (1 << 14), ++ HI_SATA1_SRST_REQ = (1 << 15), ++ ++ HI_SATA_SYS_CTRL = IO_ADDRESS(0x1205008C), ++}; ++ ++static void hi_sata_poweron(void) ++{ ++ /* msleep(20); */ ++} ++ ++static void hi_sata_poweroff(void) ++{ ++} ++ ++void hisi_sata_reset_rxtx_assert(unsigned int port_no) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG74); ++ ++ if (port_no == 1) { ++ tmp_val |= HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ; ++ } else if (port_no == 0) { ++ tmp_val |= HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ; ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG74); ++} ++EXPORT_SYMBOL(hisi_sata_reset_rxtx_assert); ++ ++void hisi_sata_reset_rxtx_deassert(unsigned int port_no) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG74); ++ ++ if (port_no == 1) { ++ tmp_val &= ~(HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ); ++ } else if (port_no == 0) { ++ tmp_val &= ~(HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ); ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG74); ++} ++EXPORT_SYMBOL(hisi_sata_reset_rxtx_deassert); ++ ++static void hi_sata_reset(void) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG74); ++ ++ tmp_val |= HI_SATA_BUS_SRST_REQ | HI_SATA_CKO_ALIVE_SRST_REQ; ++ if (n_ports == 2) { ++ tmp_val |= HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ ++ | HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ; ++ } else if (n_ports == 1) { ++ tmp_val |= HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ; ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG74); ++} ++ ++static void hi_sata_unreset(void) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG74); ++ ++ tmp_val &= ~(HI_SATA_BUS_SRST_REQ | HI_SATA_CKO_ALIVE_SRST_REQ); ++ if (n_ports == 2) { ++ tmp_val &= ~(HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ ++ | HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ); ++ ++ } else if (n_ports == 1) { ++ tmp_val &= ~(HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ); ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG74); ++} ++ ++static void hi_sata_phy_reset(void) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG72); ++ ++ if (n_ports == 2) { ++ tmp_val |= HI_SATA_PHY0_RST ++ | HI_SATA_PHY1_RST; ++ ++ } else if (n_ports == 1) ++ tmp_val |= HI_SATA_PHY0_RST; ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG72); ++} ++ ++static void hi_sata_phy_unreset(void) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG72); ++ ++ if (n_ports == 2) { ++ tmp_val &= ~(HI_SATA_PHY0_RST ++ | HI_SATA_PHY1_RST); ++ } else if (n_ports == 1) ++ tmp_val &= ~HI_SATA_PHY0_RST; ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG72); ++} ++ ++static void hi_sata_clk_enable(void) ++{ ++ unsigned int tmp_val, tmp_reg; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG72); ++ tmp_reg = readl((void *)HI_SATA_PERI_CRG74); ++ ++ if (n_ports == 2) { ++ tmp_val |= HI_SATA_PHY0_CLK_EN; ++ tmp_val |= HI_SATA_PHY1_CLK_EN; ++ ++ tmp_reg |= HI_SATA_RX0_CKEN ++ | HI_SATA_TX0_CKEN ++ | HI_SATA_RX1_CKEN ++ | HI_SATA_TX1_CKEN; ++ ++ } else if (n_ports == 1) { ++ tmp_val |= HI_SATA_PHY0_CLK_EN; ++ ++ tmp_reg |= HI_SATA_RX0_CKEN ++ | HI_SATA_TX0_CKEN; ++ ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG72); ++ writel(tmp_reg, (void *)HI_SATA_PERI_CRG74); ++ ++} ++ ++static void hi_sata_clk_disable(void) ++{ ++} ++ ++static void hi_sata_clk_reset(void) ++{ ++} ++ ++static void hi_sata_phy_clk_sel(void) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG72); ++ ++ if (n_ports == 2) { ++ tmp_val &= ~HI_SATA_PHY0_REFCLK_SEL_MASK; ++ tmp_val &= ~HI_SATA_PHY1_REFCLK_SEL_MASK; ++ ++ tmp_val |= HI_SATA_PHY0_REFCLK_SEL; ++ tmp_val |= HI_SATA_PHY1_REFCLK_SEL; ++ ++ } else if (n_ports == 1) { ++ tmp_val &= ~HI_SATA_PHY1_REFCLK_SEL_MASK; ++ tmp_val |= HI_SATA_PHY1_REFCLK_SEL; ++ } ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG72); ++} ++ ++void hisata_v200_set_fifo(void *mmio, int n_ports) ++{ ++ int i; ++ ++ for (i = 0; i < n_ports; i++) ++ writel(HI_SATA_FIFOTH_VALUE, (mmio + 0x100 + i*0x80 ++ + HI_SATA_PORT_FIFOTH)); ++} ++ ++void hisata_phy_init(void *mmio, int phy_mode, int n_ports) ++{ ++ unsigned int tmp, phy_config = HI_SATA_PHY_3G; ++ unsigned int phy_sg = HI_SATA_PHY_SG_3G; ++ int i, port_no; ++ ++ if ((n_ports < 1) || (n_ports > 2)) ++ pr_err("ERROR: PORT num you set is WRONG!!!\n"); ++ ++ sata_port_map = (1 << n_ports) - 1; ++ hisata_v200_set_fifo(mmio, n_ports); ++ ++ tmp = readl(mmio + HI_SATA_PHY_CTL1); ++ tmp |= HI_SATA_BIGENDINE; ++ writel(tmp, (mmio + HI_SATA_PHY_CTL1)); ++ tmp = readl(mmio + HI_SATA_PHY_CTL2); ++ tmp |= HI_SATA_BIGENDINE; ++ writel(tmp, (mmio + HI_SATA_PHY_CTL2)); ++ ++ tmp = readl(mmio + HI_SATA_PHY_RST_BACK_MASK); ++ tmp |= HI_SATA_PHY_BACK_MASK_ALL; ++ if (n_ports == 1) ++ tmp &= ~HI_SATA_PHY1_RST_BACK_MASK; ++ else if (n_ports == 2) ++ /* Not need mask any port */ ++ ++ writel(tmp, (mmio + HI_SATA_PHY_RST_BACK_MASK)); ++ ++ if (phy_mode == HI_SATA_PHY_MODE_1_5G) { ++ phy_config = HI_SATA_PHY_1_5G; ++ phy_sg = HI_SATA_PHY_SG_1_5G; ++ } ++ ++ if (phy_mode == HI_SATA_PHY_MODE_3G) { ++ phy_config = HI_SATA_PHY_3G; ++ phy_sg = HI_SATA_PHY_SG_3G; ++ } ++ ++ if (phy_mode == HI_SATA_PHY_MODE_6G) { ++ phy_config = HI_SATA_PHY_6G; ++ phy_sg = HI_SATA_PHY_SG_6G; ++ } ++ ++ for (i = 0; i < n_ports; i++) { ++ port_no = i; ++ writel(phy_config, (mmio + 0x100 + port_no*0x80 ++ + HI_SATA_PORT_PHYCTL)); ++ ++ writel(phy_sg, (mmio + 0x100 + port_no*0x80 ++ + HI_SATA_PORT_PHYCTL1)); ++ } ++} ++ ++static void hi_sata_phy_reg_config(void) ++{ ++ unsigned int i, port_no; ++ ++ for (i = 0; i < n_ports; i++) { ++ port_no = i; ++ ++ if (port_no == 0) { ++ /* PLL always 6G & CDR <= RATE */ ++ writel(0xd01, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0xd41, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0xd01, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ ++ /* disable SSC */ ++ writel(0x803, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x843, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x803, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ ++ /* EQ set b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x049, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x548, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ } else if (port_no == 1) { ++ /* PLL always 6G & CDR <= RATE */ ++ writel(0xd01, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0xd41, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0xd01, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ ++ /* disable SSC */ ++ writel(0x803, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x843, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x803, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ ++ /* EQ set b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x049, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x548, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ } ++ } ++} ++ ++void hi_sata_eq_recovery(unsigned int port_no) ++{ ++ if (port_no == 0) { ++ /* EQ recovery */ ++ writel(0xf09, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0xf49, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0xf09, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ ++ writel(0x308, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x348, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x308, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ } else if (port_no == 1) { ++ /* EQ recovery */ ++ writel(0xf09, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0xf49, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0xf09, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ ++ writel(0x308, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x348, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x308, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ } ++} ++EXPORT_SYMBOL(hi_sata_eq_recovery); ++ ++void hi_sata_set_eq(unsigned int port_no) ++{ ++ if (port_no == 0) { ++ /* EQ set b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x049, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x548, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ } else if (port_no == 1) { ++ /* EQ set b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x049, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x548, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ } ++} ++EXPORT_SYMBOL(hi_sata_set_eq); ++ +diff --git a/drivers/phy/phy-hi3531d-sata.c b/drivers/phy/phy-hi3531d-sata.c +new file mode 100644 +index 0000000..8dc6dd4 +--- /dev/null ++++ b/drivers/phy/phy-hi3531d-sata.c +@@ -0,0 +1,814 @@ ++/* ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/mfd/syscon.h> ++#include <linux/module.h> ++#include <linux/phy/phy.h> ++#include <linux/platform_device.h> ++#include <linux/regmap.h> ++#include <mach/io.h> ++#include <mach/platform.h> ++ ++static unsigned int mplx_port0; ++static unsigned int sata_port_nr; ++ ++enum { ++ HI_SATA_PERI_CTRL = IO_ADDRESS(0x12040000), ++ HI_SATA_PERI_CRG72 = (HI_SATA_PERI_CTRL + 0x120), ++ HI_SATA_PERI_CRG74 = (HI_SATA_PERI_CTRL + 0x128), ++ ++ HI_SATA_PHY0_REFCLK_SEL_MASK = (0x3 << 4), ++ HI_SATA_PHY0_REFCLK_SEL = (0x1 << 4), ++ HI_SATA_PHY1_REFCLK_SEL_MASK = (0x3 << 6), ++ HI_SATA_PHY1_REFCLK_SEL = (0x1 << 6), ++ HI_SATA_PHY2_REFCLK_SEL_MASK = (0x3 << 12), ++ HI_SATA_PHY2_REFCLK_SEL = (0x1 << 12), ++ HI_SATA_PHY3_REFCLK_SEL_MASK = (0x3 << 14), ++ HI_SATA_PHY3_REFCLK_SEL = (0x1 << 14), ++ ++ HI_SATA_PHY0_CLK_EN = (1 << 0), ++ HI_SATA_PHY1_CLK_EN = (1 << 1), ++ HI_SATA_PHY2_CLK_EN = (1 << 8), ++ HI_SATA_PHY3_CLK_EN = (1 << 9), ++ ++ HI_SATA_PHY0_RST = (1 << 2), ++ HI_SATA_PHY1_RST = (1 << 3), ++ HI_SATA_PHY2_RST = (1 << 10), ++ HI_SATA_PHY3_RST = (1 << 11), ++ ++ HI_SATA_PHY3_RST_BACK_MASK = (1 << 7), ++ HI_SATA_PHY2_RST_BACK_MASK = (1 << 6), ++ HI_SATA_PHY1_RST_BACK_MASK = (1 << 5), ++ HI_SATA_PHY0_RST_BACK_MASK = (1 << 4), ++ ++ HI_SATA_BUS_CKEN = (1 << 0), ++ HI_SATA_BUS_SRST_REQ = (1 << 8), ++ HI_SATA_CKO_ALIVE_CKEN = (1 << 2), ++ HI_SATA_CKO_ALIVE_SRST_REQ = (1 << 9), ++ HI_SATA_RX0_CKEN = (1 << 1), ++ HI_SATA_TX0_CKEN = (1 << 3), ++ HI_SATA_RX0_SRST_REQ = (1 << 10), ++ HI_SATA0_SRST_REQ = (1 << 11), ++ HI_SATA_RX1_CKEN = (1 << 12), ++ HI_SATA_TX1_CKEN = (1 << 13), ++ HI_SATA_RX1_SRST_REQ = (1 << 14), ++ HI_SATA1_SRST_REQ = (1 << 15), ++ HI_SATA_RX2_CKEN = (1 << 16), ++ HI_SATA_TX2_CKEN = (1 << 17), ++ HI_SATA_RX2_SRST_REQ = (1 << 18), ++ HI_SATA2_SRST_REQ = (1 << 19), ++ HI_SATA_RX3_CKEN = (1 << 20), ++ HI_SATA_TX3_CKEN = (1 << 21), ++ HI_SATA_RX3_SRST_REQ = (1 << 22), ++ HI_SATA3_SRST_REQ = (1 << 23), ++ ++ HI_SATA_SYS_CTRL = IO_ADDRESS(0x1205008C), ++ HI_SATA_PCIE_MODE = 12, ++}; ++ ++ ++static unsigned int hi_sata_port_nr(void) ++{ ++ unsigned int val, mode, port_nr; ++ ++ val = readl((void *)HI_SATA_SYS_CTRL); ++ ++ mode = (val >> HI_SATA_PCIE_MODE) & 0xf; ++ switch (mode) { ++ case 0x0: ++ port_nr = 4; ++ sata_port_map = 0xf; ++ break; ++ ++ case 0x1: ++ port_nr = 3; ++ sata_port_map = 0x7; ++ break; ++ ++ case 0x3: ++ port_nr = 2; ++ sata_port_map = 0x3; ++ break; ++ ++ case 0x8: ++ port_nr = 3; ++ sata_port_map = 0xe; ++ break; ++ ++ case 0x9: ++ port_nr = 2; ++ sata_port_map = 0x6; ++ break; ++ ++ case 0xb: ++ port_nr = 1; ++ sata_port_map = 0x2; ++ break; ++ ++ default: ++ port_nr = 0; ++ break; ++ } ++ ++ mplx_port0 = (mode & 0x8) ? 1 : 0; ++ sata_port_nr = port_nr; ++ ++ return port_nr; ++} ++ ++static void hi_sata_poweron(void) ++{ ++ /* msleep(20); */ ++} ++ ++static void hi_sata_poweroff(void) ++{ ++} ++ ++void hisi_sata_reset_rxtx_assert(unsigned int port_no) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG74); ++ ++ if (port_no == 0) ++ tmp_val |= HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ; ++ else if (port_no == 1) ++ tmp_val |= HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ; ++ else if (port_no == 2) ++ tmp_val |= HI_SATA_RX2_SRST_REQ ++ | HI_SATA2_SRST_REQ; ++ else if (port_no == 3) ++ tmp_val |= HI_SATA_RX3_SRST_REQ ++ | HI_SATA3_SRST_REQ; ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG74); ++} ++EXPORT_SYMBOL(hisi_sata_reset_rxtx_assert); ++ ++void hisi_sata_reset_rxtx_deassert(unsigned int port_no) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG74); ++ ++ if (port_no == 0) ++ tmp_val &= ~(HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ); ++ else if (port_no == 1) ++ tmp_val &= ~(HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ); ++ else if (port_no == 2) ++ tmp_val &= ~(HI_SATA_RX2_SRST_REQ ++ | HI_SATA2_SRST_REQ); ++ else if (port_no == 3) ++ tmp_val &= ~(HI_SATA_RX3_SRST_REQ ++ | HI_SATA3_SRST_REQ); ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG74); ++} ++EXPORT_SYMBOL(hisi_sata_reset_rxtx_deassert); ++ ++static void hi_sata_reset(void) ++{ ++ unsigned int tmp_val, nport; ++ ++ nport = sata_port_nr; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG74); ++ ++ tmp_val |= HI_SATA_BUS_SRST_REQ | HI_SATA_CKO_ALIVE_SRST_REQ; ++ ++ if (nport == 4) { ++ tmp_val |= HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ ++ | HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ ++ | HI_SATA_RX2_SRST_REQ ++ | HI_SATA2_SRST_REQ ++ | HI_SATA_RX3_SRST_REQ ++ | HI_SATA3_SRST_REQ; ++ } else if (nport == 3) { ++ if (mplx_port0) { ++ tmp_val |= HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ ++ | HI_SATA_RX2_SRST_REQ ++ | HI_SATA2_SRST_REQ ++ | HI_SATA_RX3_SRST_REQ ++ | HI_SATA3_SRST_REQ; ++ } else { ++ tmp_val |= HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ ++ | HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ ++ | HI_SATA_RX2_SRST_REQ ++ | HI_SATA2_SRST_REQ; ++ } ++ } else if (nport == 2) { ++ if (mplx_port0) { ++ tmp_val |= HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ ++ | HI_SATA_RX2_SRST_REQ ++ | HI_SATA2_SRST_REQ; ++ } else { ++ tmp_val |= HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ ++ | HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ; ++ } ++ } else if (nport == 1) { ++ tmp_val |= HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ; ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG74); ++} ++ ++static void hi_sata_unreset(void) ++{ ++ unsigned int tmp_val, nport; ++ ++ nport = sata_port_nr; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG74); ++ ++ tmp_val &= ~(HI_SATA_BUS_SRST_REQ | HI_SATA_CKO_ALIVE_SRST_REQ); ++ ++ if (nport == 4) { ++ tmp_val &= ~(HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ ++ | HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ ++ | HI_SATA_RX2_SRST_REQ ++ | HI_SATA2_SRST_REQ ++ | HI_SATA_RX3_SRST_REQ ++ | HI_SATA3_SRST_REQ); ++ } else if (nport == 3) { ++ if (mplx_port0) { ++ tmp_val &= ~(HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ ++ | HI_SATA_RX2_SRST_REQ ++ | HI_SATA2_SRST_REQ ++ | HI_SATA_RX3_SRST_REQ ++ | HI_SATA3_SRST_REQ); ++ } else { ++ tmp_val &= ~(HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ ++ | HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ ++ | HI_SATA_RX2_SRST_REQ ++ | HI_SATA2_SRST_REQ); ++ } ++ } else if (nport == 2) { ++ if (mplx_port0) { ++ tmp_val &= ~(HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ ++ | HI_SATA_RX2_SRST_REQ ++ | HI_SATA2_SRST_REQ); ++ } else { ++ tmp_val &= ~(HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ ++ | HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ); ++ } ++ } else if (nport == 1) { ++ tmp_val &= ~(HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ); ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG74); ++} ++ ++static void hi_sata_phy_reset(void) ++{ ++ unsigned int tmp_val, nport; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG72); ++ ++ nport = sata_port_nr; ++ ++ if (nport == 4) { ++ tmp_val |= HI_SATA_PHY0_RST ++ | HI_SATA_PHY1_RST ++ | HI_SATA_PHY2_RST ++ | HI_SATA_PHY3_RST; ++ } else if (nport == 3) { ++ if (mplx_port0) { ++ tmp_val |= HI_SATA_PHY1_RST ++ | HI_SATA_PHY2_RST ++ | HI_SATA_PHY3_RST; ++ } else { ++ tmp_val |= HI_SATA_PHY0_RST ++ | HI_SATA_PHY1_RST ++ | HI_SATA_PHY2_RST; ++ } ++ } else if (nport == 2) { ++ if (mplx_port0) { ++ tmp_val |= HI_SATA_PHY1_RST ++ | HI_SATA_PHY2_RST; ++ } else { ++ tmp_val |= HI_SATA_PHY0_RST ++ | HI_SATA_PHY1_RST; ++ } ++ } else if (nport == 1) { ++ tmp_val |= HI_SATA_PHY1_RST; ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG72); ++} ++ ++static void hi_sata_phy_unreset(void) ++{ ++ unsigned int tmp_val, nport; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG72); ++ ++ nport = sata_port_nr; ++ ++ if (nport == 4) { ++ tmp_val &= ~(HI_SATA_PHY0_RST ++ | HI_SATA_PHY1_RST ++ | HI_SATA_PHY2_RST ++ | HI_SATA_PHY3_RST); ++ } else if (nport == 3) { ++ if (mplx_port0) { ++ tmp_val &= ~(HI_SATA_PHY1_RST ++ | HI_SATA_PHY2_RST ++ | HI_SATA_PHY3_RST); ++ } else { ++ tmp_val &= ~(HI_SATA_PHY0_RST ++ | HI_SATA_PHY1_RST ++ | HI_SATA_PHY2_RST); ++ } ++ } else if (nport == 2) { ++ if (mplx_port0) { ++ tmp_val &= ~(HI_SATA_PHY1_RST ++ | HI_SATA_PHY2_RST); ++ } else { ++ tmp_val &= ~(HI_SATA_PHY0_RST ++ | HI_SATA_PHY1_RST); ++ } ++ } else if (nport == 1) { ++ tmp_val &= ~HI_SATA_PHY1_RST; ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG72); ++} ++ ++static void hi_sata_clk_enable(void) ++{ ++ unsigned int tmp_val, tmp_reg, nport; ++ ++ nport = sata_port_nr; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG72); ++ tmp_reg = readl((void *)HI_SATA_PERI_CRG74); ++ ++ tmp_reg |= HI_SATA_BUS_CKEN ++ | HI_SATA_CKO_ALIVE_CKEN; ++ ++ if (nport == 4) { ++ tmp_val |= HI_SATA_PHY0_CLK_EN; ++ tmp_val |= HI_SATA_PHY1_CLK_EN; ++ tmp_val |= HI_SATA_PHY2_CLK_EN; ++ tmp_val |= HI_SATA_PHY3_CLK_EN; ++ ++ tmp_reg |= HI_SATA_RX0_CKEN ++ | HI_SATA_TX0_CKEN ++ | HI_SATA_RX1_CKEN ++ | HI_SATA_TX1_CKEN ++ | HI_SATA_RX2_CKEN ++ | HI_SATA_TX2_CKEN ++ | HI_SATA_RX3_CKEN ++ | HI_SATA_TX3_CKEN; ++ ++ } else if (nport == 3) { ++ if (mplx_port0) { ++ tmp_val |= HI_SATA_PHY1_CLK_EN; ++ tmp_val |= HI_SATA_PHY2_CLK_EN; ++ tmp_val |= HI_SATA_PHY3_CLK_EN; ++ ++ tmp_reg |= HI_SATA_RX1_CKEN ++ | HI_SATA_TX1_CKEN ++ | HI_SATA_RX2_CKEN ++ | HI_SATA_TX2_CKEN ++ | HI_SATA_RX3_CKEN ++ | HI_SATA_TX3_CKEN; ++ } else { ++ tmp_val |= HI_SATA_PHY0_CLK_EN; ++ tmp_val |= HI_SATA_PHY1_CLK_EN; ++ tmp_val |= HI_SATA_PHY2_CLK_EN; ++ ++ tmp_reg |= HI_SATA_RX0_CKEN ++ | HI_SATA_TX0_CKEN ++ | HI_SATA_RX1_CKEN ++ | HI_SATA_TX1_CKEN ++ | HI_SATA_RX2_CKEN ++ | HI_SATA_TX2_CKEN; ++ } ++ } else if (nport == 2) { ++ if (mplx_port0) { ++ tmp_val |= HI_SATA_PHY1_CLK_EN; ++ tmp_val |= HI_SATA_PHY2_CLK_EN; ++ ++ tmp_reg |= HI_SATA_RX1_CKEN ++ | HI_SATA_TX1_CKEN ++ | HI_SATA_RX2_CKEN ++ | HI_SATA_TX2_CKEN; ++ } else { ++ tmp_val |= HI_SATA_PHY0_CLK_EN; ++ tmp_val |= HI_SATA_PHY1_CLK_EN; ++ ++ tmp_reg |= HI_SATA_RX0_CKEN ++ | HI_SATA_TX0_CKEN ++ | HI_SATA_RX1_CKEN ++ | HI_SATA_TX1_CKEN; ++ } ++ } else if (nport == 1) { ++ tmp_val |= HI_SATA_PHY1_CLK_EN; ++ ++ tmp_reg |= HI_SATA_RX1_CKEN ++ | HI_SATA_TX1_CKEN; ++ } else ++ return; ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG72); ++ writel(tmp_reg, (void *)HI_SATA_PERI_CRG74); ++ ++} ++static void hi_sata_clk_disable(void) ++{ ++} ++ ++static void hi_sata_clk_reset(void) ++{ ++} ++ ++static void hi_sata_phy_clk_sel(void) ++{ ++ unsigned int tmp_val, nport; ++ ++ nport = sata_port_nr; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG72); ++ ++ if (nport == 4) { ++ tmp_val &= ~HI_SATA_PHY0_REFCLK_SEL_MASK; ++ tmp_val &= ~HI_SATA_PHY1_REFCLK_SEL_MASK; ++ tmp_val &= ~HI_SATA_PHY2_REFCLK_SEL_MASK; ++ tmp_val &= ~HI_SATA_PHY3_REFCLK_SEL_MASK; ++ ++ tmp_val |= HI_SATA_PHY0_REFCLK_SEL; ++ tmp_val |= HI_SATA_PHY1_REFCLK_SEL; ++ tmp_val |= HI_SATA_PHY2_REFCLK_SEL; ++ tmp_val |= HI_SATA_PHY3_REFCLK_SEL; ++ } else if (nport == 3) { ++ if (mplx_port0) { ++ tmp_val &= ~HI_SATA_PHY1_REFCLK_SEL_MASK; ++ tmp_val &= ~HI_SATA_PHY2_REFCLK_SEL_MASK; ++ tmp_val &= ~HI_SATA_PHY3_REFCLK_SEL_MASK; ++ ++ tmp_val |= HI_SATA_PHY1_REFCLK_SEL; ++ tmp_val |= HI_SATA_PHY2_REFCLK_SEL; ++ tmp_val |= HI_SATA_PHY3_REFCLK_SEL; ++ } else { ++ tmp_val &= ~HI_SATA_PHY0_REFCLK_SEL_MASK; ++ tmp_val &= ~HI_SATA_PHY1_REFCLK_SEL_MASK; ++ tmp_val &= ~HI_SATA_PHY2_REFCLK_SEL_MASK; ++ ++ tmp_val |= HI_SATA_PHY0_REFCLK_SEL; ++ tmp_val |= HI_SATA_PHY1_REFCLK_SEL; ++ tmp_val |= HI_SATA_PHY2_REFCLK_SEL; ++ } ++ } else if (nport == 2) { ++ if (mplx_port0) { ++ tmp_val &= ~HI_SATA_PHY1_REFCLK_SEL_MASK; ++ tmp_val &= ~HI_SATA_PHY2_REFCLK_SEL_MASK; ++ ++ tmp_val |= HI_SATA_PHY1_REFCLK_SEL; ++ tmp_val |= HI_SATA_PHY2_REFCLK_SEL; ++ } else { ++ tmp_val &= ~HI_SATA_PHY0_REFCLK_SEL_MASK; ++ tmp_val &= ~HI_SATA_PHY1_REFCLK_SEL_MASK; ++ ++ tmp_val |= HI_SATA_PHY0_REFCLK_SEL; ++ tmp_val |= HI_SATA_PHY1_REFCLK_SEL; ++ ++ } ++ } else if (nport == 1) { ++ tmp_val &= ~HI_SATA_PHY1_REFCLK_SEL_MASK; ++ tmp_val |= HI_SATA_PHY1_REFCLK_SEL; ++ } else ++ return; ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG72); ++} ++ ++void hisata_v200_set_fifo(void *mmio, int n_ports) ++{ ++ int i, port_no; ++ ++ for (i = 0; i < n_ports; i++) { ++ port_no = i; ++ if (mplx_port0) ++ port_no++; ++ ++ writel(HI_SATA_FIFOTH_VALUE, (mmio + 0x100 + port_no*0x80 ++ + HI_SATA_PORT_FIFOTH)); ++ } ++} ++ ++void hisata_phy_init(void *mmio, int phy_mode, int n_ports) ++{ ++ unsigned int tmp, phy_config = HI_SATA_PHY_3G; ++ unsigned int phy_sg = HI_SATA_PHY_SG_3G; ++ int i, port_no; ++ ++ hisata_v200_set_fifo(mmio, n_ports); ++ ++ tmp = readl(mmio + HI_SATA_PHY_CTL1); ++ tmp |= HI_SATA_BIGENDINE; ++ writel(tmp, (mmio + HI_SATA_PHY_CTL1)); ++ tmp = readl(mmio + HI_SATA_PHY_CTL2); ++ tmp |= HI_SATA_BIGENDINE; ++ writel(tmp, (mmio + HI_SATA_PHY_CTL2)); ++ ++ tmp = readl(mmio + HI_SATA_PHY_RST_BACK_MASK); ++ tmp &= 0xffffff0f; ++ if (n_ports == 1) { ++ tmp |= HI_SATA_PHY0_RST_BACK_MASK ++ | HI_SATA_PHY2_RST_BACK_MASK ++ | HI_SATA_PHY3_RST_BACK_MASK; ++ } else if (n_ports == 2) { ++ if (mplx_port0) { ++ tmp |= HI_SATA_PHY0_RST_BACK_MASK ++ | HI_SATA_PHY3_RST_BACK_MASK; ++ } else { ++ tmp |= HI_SATA_PHY2_RST_BACK_MASK ++ | HI_SATA_PHY3_RST_BACK_MASK; ++ } ++ } else if (n_ports == 3) { ++ if (mplx_port0) ++ tmp |= HI_SATA_PHY0_RST_BACK_MASK; ++ else ++ tmp |= HI_SATA_PHY3_RST_BACK_MASK; ++ } else if (n_ports == 4) { ++ /* Not need mask any port */ ++ } ++ writel(tmp, (mmio + HI_SATA_PHY_RST_BACK_MASK)); ++ ++ if (phy_mode == HI_SATA_PHY_MODE_1_5G) { ++ phy_config = HI_SATA_PHY_1_5G; ++ phy_sg = HI_SATA_PHY_SG_1_5G; ++ } ++ ++ if (phy_mode == HI_SATA_PHY_MODE_3G) { ++ phy_config = HI_SATA_PHY_3G; ++ phy_sg = HI_SATA_PHY_SG_3G; ++ } ++ ++ if (phy_mode == HI_SATA_PHY_MODE_6G) { ++ phy_config = HI_SATA_PHY_6G; ++ phy_sg = HI_SATA_PHY_SG_6G; ++ } ++ ++ for (i = 0; i < n_ports; i++) { ++ port_no = i; ++ if (mplx_port0) ++ port_no++; ++ ++ writel(phy_config, (mmio + 0x100 + port_no*0x80 ++ + HI_SATA_PORT_PHYCTL)); ++ ++ writel(phy_sg, (mmio + 0x100 + port_no*0x80 ++ + HI_SATA_PORT_PHYCTL1)); ++ } ++} ++ ++static void hi_sata_phy_reg_config(void) ++{ ++ unsigned int i, port_no; ++ ++ for (i = 0; i < sata_port_nr; i++) { ++ port_no = i; ++ if (mplx_port0) ++ port_no++; ++ ++ if (port_no == 0) { ++ /* PLL always 6G & CDR <= RATE */ ++ writel(0xd01, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0xd41, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0xd01, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY0); ++ ++ /* disable SSC */ ++ writel(0x803, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x843, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x803, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY0); ++ ++ /* EQ set 6'b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x049, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY0); ++ ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x548, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY0); ++ } else if (port_no == 1) { ++ /* PLL always 6G & CDR <= RATE */ ++ writel(0xd01, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0xd41, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0xd01, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY1); ++ ++ /* disable SSC */ ++ writel(0x803, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x843, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x803, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY1); ++ ++ /* EQ set 6'b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x049, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY1); ++ ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x548, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY1); ++ } else if (port_no == 2) { ++ /* PLL always 6G & CDR <= RATE */ ++ writel(0xd01, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0xd41, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0xd01, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY2); ++ ++ /* disable SSC */ ++ writel(0x803, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x843, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x803, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY2); ++ ++ /* EQ set 6'b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x049, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY2); ++ ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x548, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY2); ++ } else if (port_no == 3) { ++ /* PLL always 6G & CDR <= RATE */ ++ writel(0xd01, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0xd41, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0xd01, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY3); ++ ++ /* disable SSC */ ++ writel(0x803, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x843, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x803, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY3); ++ ++ /* EQ set 6'b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x049, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY3); ++ ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x548, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY3); ++ } ++ } ++} ++ ++void hi_sata_eq_recovery(unsigned int port_no) ++{ ++ if (port_no == 0) { ++ /* auto_eq */ ++ writel(0xf09, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0xf49, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0xf09, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY0); ++ ++ writel(0x308, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x348, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x308, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY0); ++ } else if (port_no == 1) { ++ /* auto_eq */ ++ writel(0xf09, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0xf49, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0xf09, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY1); ++ ++ writel(0x308, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x348, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x308, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY1); ++ } else if (port_no == 2) { ++ /* auto_eq */ ++ writel(0xf09, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0xf49, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0xf09, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY2); ++ ++ writel(0x308, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x348, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x308, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY2); ++ } else if (port_no == 3) { ++ /* auto_eq */ ++ writel(0xf09, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0xf49, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0xf09, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY3); ++ ++ writel(0x308, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x348, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x308, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY3); ++ } ++ ++ return; ++} ++EXPORT_SYMBOL(hi_sata_eq_recovery); ++ ++void hi_sata_set_eq(unsigned int port_no) ++{ ++ if (port_no == 0) { ++ /* EQ set 6'b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x049, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY0); ++ ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x548, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY0); ++ } else if (port_no == 1) { ++ /* EQ set 6'b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x049, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY1); ++ ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x548, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY1); ++ } else if (port_no == 2) { ++ /* EQ set 6'b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x049, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY2); ++ ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x548, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY2); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY2); ++ } else if (port_no == 3) { ++ /* EQ set 6'b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x049, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x009, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY3); ++ ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x548, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x508, (void *)HI_SATA_MISC_COMB_PHY3); ++ writel(0x0, (void *)HI_SATA_MISC_COMB_PHY3); ++ } ++ ++ return; ++} ++EXPORT_SYMBOL(hi_sata_set_eq); +diff --git a/drivers/phy/phy-hi3531d-usb3.c b/drivers/phy/phy-hi3531d-usb3.c +new file mode 100644 +index 0000000..1cc46c1 +--- /dev/null ++++ b/drivers/phy/phy-hi3531d-usb3.c +@@ -0,0 +1,309 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/phy/phy.h> ++#include <linux/platform_device.h> ++#include <linux/of_address.h> ++#include <mach/io.h> ++ ++#define SYS_CTRL 0x12050000 ++#define USB2_PHY1 0x16c ++#define USB2_PHY1_TEST_SRST_REQ BIT(10) ++#define USB2_PHY1_SRST_TREQ BIT(9) ++#define USB2_PHY1_SRST_REQ BIT(8) ++#define USB2_PHY1_REF_CLKEN BIT(0) ++ ++#define HOST_U3_DISABLE (1 << 3) ++#define USB3_U2_PHY 0x12c ++ ++#define USB3_CTRL 0x12c ++#define USB3_VCC_SRST_REQ BIT(13) ++#define USB3_UTMI_CLKEN BIT(12) ++#define USB3_PIPE_CLKEN BIT(11) ++#define USB3_SUSPEND_CLKEN BIT(10) ++#define USB3_REF_CLKEN BIT(9) ++#define USB3_BUS_CLKEN BIT(8) ++ ++#define USB3_COMBPHY 0x120 ++#define COMBPHY1_LANE0_REQ BIT(2) ++ ++#define GTXTHRCFG 0xc108 ++#define GRXTHRCFG 0xc10c ++#define REG_GCTL 0xc110 ++#define U2RSTECN (0x1 << 16) ++ ++#define REG_GUSB2PHYCFG0 0xC200 ++#define BIT_UTMI_ULPI (0x1 << 4) ++#define BIT_UTMI_8_16 (0x1 << 3) ++ ++#define REG_GUSB3PIPECTL0 0xc2c0 ++#define PCS_SSP_SOFT_RESET (0x1 << 31) ++#define TX_MARGIN_MASK (0x7 << 3) ++#define TX_MARGIN_VAL (0x2 << 3) ++ ++#define USB2_PHY0_CTLL 0x80 ++ ++#define USB3_PHY 0x88 ++#define COMBO_PHY_TX_DEEMP_MASK (0x7 << 12) ++#define COMBO_PHY_TX_DEEMP_VAL (0x1 << 12) ++ ++#define USB2_PHY_TEST_REG_ACCESS (1 << 20) ++ ++struct hisi_priv { ++ void __iomem *base; ++ void __iomem *dwc3_ctrl; /* 0x11000000 */ ++ void __iomem *peri_ctrl; /* 0x12040000 */ ++ void __iomem *misc_ctrl; /* 0x12120000 */ ++}; ++ ++static void hisi_usb3_phy_eye(struct phy *phy) ++{ ++ int reg; ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++ /* configuration method */ ++ reg = readl(priv->misc_ctrl + 0x10); ++ reg |= USB2_PHY_TEST_REG_ACCESS; ++ writel(reg, priv->misc_ctrl + 0x10); ++ udelay(100); ++ /* slew rate */ ++ writel(0xc4, priv->misc_ctrl + 0x8020); ++ udelay(20); ++ /* Calibration mode */ ++ writel(0xc1, priv->misc_ctrl + 0x8044); ++ udelay(20); ++ /* disconnect threshold value */ ++ writel(0x1b, priv->misc_ctrl + 0x8028); ++ udelay(20); ++ /* turn on pre-emphasis */ ++ writel(0x1c, priv->misc_ctrl + 0x8000); ++ udelay(20); ++ writel(0x92, priv->misc_ctrl + 0x8014); ++ udelay(20); ++ writel(0xd, priv->misc_ctrl + 0x8018); ++ udelay(20); ++ /* down 100mv 2017/2/13 by h292880*/ ++ writel(0x103, priv->misc_ctrl + 0x134); ++ udelay(20); ++ writel(0x143, priv->misc_ctrl + 0x134); ++ udelay(20); ++} ++ ++static int hisi_usb3_ctrl_phy_config(struct phy *phy) ++{ ++ int reg; ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++ reg = readl(priv->dwc3_ctrl + REG_GUSB3PIPECTL0); ++ reg |= PCS_SSP_SOFT_RESET; ++ writel(reg, priv->dwc3_ctrl + REG_GUSB3PIPECTL0); ++ ++ /*step 3: USB2 PHY chose ulpi 8bit interface */ ++ reg = readl(priv->dwc3_ctrl + REG_GUSB2PHYCFG0); ++ reg &= ~BIT_UTMI_ULPI; ++ reg &= ~(BIT_UTMI_8_16); ++ writel(reg, priv->dwc3_ctrl + REG_GUSB2PHYCFG0); ++ mdelay(20); ++ reg = readl(priv->dwc3_ctrl + REG_GCTL); ++ reg &= ~(0x3<<12); ++ reg |= (0x1<<12); /*[13:12] 01: Host; 10: Device; 11: OTG*/ ++ reg &= ~U2RSTECN; ++ writel(reg, priv->dwc3_ctrl + REG_GCTL); ++ mdelay(20); ++ ++ reg = readl(priv->dwc3_ctrl + REG_GUSB3PIPECTL0); ++ reg &= ~PCS_SSP_SOFT_RESET; ++ reg &= ~(1<<17); /* disable suspend */ ++ writel(reg, priv->dwc3_ctrl + REG_GUSB3PIPECTL0); ++ mdelay(100); ++ ++ writel(0x23100000, priv->dwc3_ctrl + GTXTHRCFG); ++ writel(0x23180000, priv->dwc3_ctrl + GRXTHRCFG); ++ mdelay(20); ++ ++ hisi_usb3_phy_eye(phy); ++ ++ return 0; ++} ++ ++static int hisi_usb3_phy_on(struct phy *phy) ++{ ++ int reg; ++ void __iomem *sys_base; ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++ sys_base = ioremap(SYS_CTRL, 0x1000); ++ reg = readl(sys_base + 0x8c); ++ if ((reg & (0x1 << 15)) != 0) { ++ reg = readl(priv->peri_ctrl + 0x120); ++ reg |= COMBPHY1_LANE0_REQ; ++ writel(reg, priv->peri_ctrl + 0x120); ++ mdelay(10); ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB3_COMBPHY); ++ reg &= ~COMBPHY1_LANE0_REQ; ++ writel_relaxed(reg, priv->peri_ctrl + USB3_COMBPHY); ++ mdelay(10); ++ } else { ++ reg = readl(priv->misc_ctrl + 0x128); ++ reg |= HOST_U3_DISABLE; ++ writel(reg, priv->misc_ctrl + 0x128); ++ mdelay(1); ++ ++ pr_info("COMBPHY IS NOT USB\n"); ++ } ++ iounmap(sys_base); ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB2_PHY1); ++ reg &= ~USB2_PHY1_TEST_SRST_REQ; ++ reg &= ~USB2_PHY1_SRST_TREQ; ++ reg &= ~USB2_PHY1_SRST_REQ; ++ reg |= USB2_PHY1_REF_CLKEN; ++ writel_relaxed(reg, priv->peri_ctrl + USB2_PHY1); ++ mdelay(10); ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB3_CTRL); ++ reg |= USB3_UTMI_CLKEN; ++ reg |= USB3_PIPE_CLKEN; ++ reg |= USB3_SUSPEND_CLKEN; ++ reg |= USB3_REF_CLKEN; ++ reg |= USB3_BUS_CLKEN; ++ reg &= ~USB3_VCC_SRST_REQ; ++ writel_relaxed(reg, priv->peri_ctrl + USB3_CTRL); ++ mdelay(10); ++ ++ hisi_usb3_ctrl_phy_config(phy); ++ ++ return 0; ++} ++ ++static int hisi_usb3_phy_power_off(struct device *dev) ++{ ++ int reg; ++ struct phy *phy = dev_get_drvdata(dev); ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB3_COMBPHY); ++ reg |= COMBPHY1_LANE0_REQ; ++ writel_relaxed(reg, priv->peri_ctrl + USB3_COMBPHY); ++ mdelay(100); ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB3_CTRL); ++ reg |= USB3_VCC_SRST_REQ; ++ writel_relaxed(reg, priv->peri_ctrl + USB3_CTRL); ++ mdelay(10); ++ ++ return 0; ++} ++ ++static int hisi_usb3_phy_power_on(struct device *dev) ++{ ++ struct phy *phy = dev_get_drvdata(dev); ++ ++ hisi_usb3_phy_on(phy); ++ ++ return 0; ++} ++ ++static int hisi_usb3_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct phy *phy; ++ struct hisi_priv *priv; ++ struct device_node *np = pdev->dev.of_node; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->dwc3_ctrl = of_iomap(np, 0); /* 0x11000000 */ ++ if (IS_ERR(priv->dwc3_ctrl)) ++ priv->dwc3_ctrl = NULL; ++ ++ priv->peri_ctrl = of_iomap(np, 1); /* 0x12040000 */ ++ if (IS_ERR(priv->peri_ctrl)) ++ priv->peri_ctrl = NULL; ++ ++ priv->misc_ctrl = of_iomap(np, 2); /* 0x12120000 */ ++ if (IS_ERR(priv->misc_ctrl)) ++ priv->misc_ctrl = NULL; ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, phy); ++ phy_set_drvdata(phy, priv); ++ hisi_usb3_phy_on(phy); ++ ++ return 0; ++} ++ ++static int hisi_usb3_phy_remove(struct platform_device *pdev) ++{ ++ int reg; ++ struct phy *phy = platform_get_drvdata(pdev); ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB3_CTRL); ++ reg &= ~USB3_UTMI_CLKEN; ++ reg &= ~USB3_PIPE_CLKEN; ++ reg &= ~USB3_SUSPEND_CLKEN; ++ reg &= ~USB3_REF_CLKEN; ++ reg &= ~USB3_BUS_CLKEN; ++ reg |= USB3_VCC_SRST_REQ; ++ writel_relaxed(reg, priv->peri_ctrl + USB3_CTRL); ++ mdelay(10); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops hisi_usb3_pmops = { ++ .suspend = hisi_usb3_phy_power_off, ++ .resume = hisi_usb3_phy_power_on, ++#if defined(CONFIG_PM_HIBERNATE) || defined(CONFIG_HISI_SNAPSHOT_BOOT) ++ .freeze = hisi_usb3_phy_power_off, ++ .thaw = hisi_usb3_phy_power_on, ++ .poweroff = hisi_usb3_phy_power_off, ++ .restore = hisi_usb3_phy_power_on, ++#endif ++}; ++ ++static const struct of_device_id hisi_usb3_phy_of_match[] = { ++ {.compatible = "hisilicon,hi3531d-usb3-phy",}, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, hisi_usb3_phy_of_match); ++ ++static struct platform_driver hisi_usb3_phy_driver = { ++ .probe = hisi_usb3_phy_probe, ++ .remove = hisi_usb3_phy_remove, ++ .driver = { ++ .name = "hisi-usb3-phy", ++ .of_match_table = hisi_usb3_phy_of_match, ++ .pm = &hisi_usb3_pmops, ++ } ++}; ++module_platform_driver(hisi_usb3_phy_driver); ++ ++MODULE_AUTHOR("Pengcheng Li <lpc.li@hisilicon.com>"); ++MODULE_DESCRIPTION("HISILICON USB PHY driver"); ++MODULE_ALIAS("platform:hisi-usb3-phy"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-hi3536c-sata.c b/drivers/phy/phy-hi3536c-sata.c +new file mode 100644 +index 0000000..dbfba40 +--- /dev/null ++++ b/drivers/phy/phy-hi3536c-sata.c +@@ -0,0 +1,416 @@ ++/* ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++ ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/mfd/syscon.h> ++#include <linux/module.h> ++#include <linux/phy/phy.h> ++#include <linux/platform_device.h> ++#include <linux/regmap.h> ++#include <mach/io.h> ++#include <mach/platform.h> ++ ++unsigned int sata_port_map; ++ ++enum { ++ HI_SATA_PERI_CTRL = IO_ADDRESS(0x12040000), ++ HI_SATA_PERI_CRG72 = (HI_SATA_PERI_CTRL + 0x120), ++ HI_SATA_PERI_CRG74 = (HI_SATA_PERI_CTRL + 0x128), ++ ++ HI_SATA_PHY0_REFCLK_SEL_MASK = (0x3 << 4), ++ HI_SATA_PHY0_REFCLK_SEL = (0x1 << 4), ++ HI_SATA_PHY1_REFCLK_SEL_MASK = (0x3 << 6), ++ HI_SATA_PHY1_REFCLK_SEL = (0x1 << 6), ++ ++ HI_SATA_PHY0_CLK_EN = (1 << 0), ++ HI_SATA_PHY1_CLK_EN = (1 << 1), ++ ++ HI_SATA_PHY0_RST = (1 << 2), ++ HI_SATA_PHY1_RST = (1 << 3), ++ ++ HI_SATA_PHY_BACK_MASK_ALL = 0xf0, ++ HI_SATA_PHY1_RST_BACK_MASK = (1 << 5), ++ HI_SATA_PHY0_RST_BACK_MASK = (1 << 4), ++ ++ HI_SATA_BUS_CKEN = (1 << 0), ++ HI_SATA_BUS_SRST_REQ = (1 << 8), ++ HI_SATA_CKO_ALIVE_CKEN = (1 << 2), ++ HI_SATA_CKO_ALIVE_SRST_REQ = (1 << 9), ++ HI_SATA_RX0_CKEN = (1 << 1), ++ HI_SATA_TX0_CKEN = (1 << 3), ++ HI_SATA_RX0_SRST_REQ = (1 << 10), ++ HI_SATA0_SRST_REQ = (1 << 11), ++ HI_SATA_RX1_CKEN = (1 << 12), ++ HI_SATA_TX1_CKEN = (1 << 13), ++ HI_SATA_RX1_SRST_REQ = (1 << 14), ++ HI_SATA1_SRST_REQ = (1 << 15), ++ ++ HI_SATA_SYS_CTRL = IO_ADDRESS(0x1205008C), ++}; ++ ++static void hi_sata_poweron(void) ++{ ++ /* msleep(20); */ ++} ++ ++static void hi_sata_poweroff(void) ++{ ++} ++ ++void hisi_sata_reset_rxtx_assert(unsigned int port_no) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG74); ++ ++ if (port_no == 1) { ++ tmp_val |= HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ; ++ } else if (port_no == 0) { ++ tmp_val |= HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ; ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG74); ++} ++EXPORT_SYMBOL(hisi_sata_reset_rxtx_assert); ++ ++void hisi_sata_reset_rxtx_deassert(unsigned int port_no) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG74); ++ ++ if (port_no == 1) { ++ tmp_val &= ~(HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ); ++ } else if (port_no == 0) { ++ tmp_val &= ~(HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ); ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG74); ++} ++EXPORT_SYMBOL(hisi_sata_reset_rxtx_deassert); ++ ++static void hi_sata_reset(void) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG74); ++ ++ tmp_val |= HI_SATA_BUS_SRST_REQ | HI_SATA_CKO_ALIVE_SRST_REQ; ++ if (n_ports == 2) { ++ tmp_val |= HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ ++ | HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ; ++ } else if (n_ports == 1) { ++ tmp_val |= HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ; ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG74); ++} ++ ++static void hi_sata_unreset(void) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG74); ++ ++ tmp_val &= ~(HI_SATA_BUS_SRST_REQ | HI_SATA_CKO_ALIVE_SRST_REQ); ++ if (n_ports == 2) { ++ tmp_val &= ~(HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ ++ | HI_SATA_RX1_SRST_REQ ++ | HI_SATA1_SRST_REQ); ++ ++ } else if (n_ports == 1) { ++ tmp_val &= ~(HI_SATA_RX0_SRST_REQ ++ | HI_SATA0_SRST_REQ); ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG74); ++} ++ ++static void hi_sata_phy_reset(void) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG72); ++ ++ if (n_ports == 2) { ++ tmp_val |= HI_SATA_PHY0_RST ++ | HI_SATA_PHY1_RST; ++ ++ } else if (n_ports == 1) ++ tmp_val |= HI_SATA_PHY0_RST; ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG72); ++} ++ ++static void hi_sata_phy_unreset(void) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG72); ++ ++ if (n_ports == 2) { ++ tmp_val &= ~(HI_SATA_PHY0_RST ++ | HI_SATA_PHY1_RST); ++ } else if (n_ports == 1) ++ tmp_val &= ~HI_SATA_PHY0_RST; ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG72); ++} ++ ++static void hi_sata_clk_enable(void) ++{ ++ unsigned int tmp_val, tmp_reg; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG72); ++ tmp_reg = readl((void *)HI_SATA_PERI_CRG74); ++ ++ if (n_ports == 2) { ++ tmp_val |= HI_SATA_PHY0_CLK_EN; ++ tmp_val |= HI_SATA_PHY1_CLK_EN; ++ ++ tmp_reg |= HI_SATA_RX0_CKEN ++ | HI_SATA_TX0_CKEN ++ | HI_SATA_RX1_CKEN ++ | HI_SATA_TX1_CKEN; ++ ++ } else if (n_ports == 1) { ++ tmp_val |= HI_SATA_PHY0_CLK_EN; ++ ++ tmp_reg |= HI_SATA_RX0_CKEN ++ | HI_SATA_TX0_CKEN; ++ ++ } ++ ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG72); ++ writel(tmp_reg, (void *)HI_SATA_PERI_CRG74); ++ ++} ++ ++static void hi_sata_clk_disable(void) ++{ ++} ++ ++static void hi_sata_clk_reset(void) ++{ ++} ++ ++static void hi_sata_phy_clk_sel(void) ++{ ++ unsigned int tmp_val; ++ ++ tmp_val = readl((void *)HI_SATA_PERI_CRG72); ++ ++ if (n_ports == 2) { ++ tmp_val &= ~HI_SATA_PHY0_REFCLK_SEL_MASK; ++ tmp_val &= ~HI_SATA_PHY1_REFCLK_SEL_MASK; ++ ++ tmp_val |= HI_SATA_PHY0_REFCLK_SEL; ++ tmp_val |= HI_SATA_PHY1_REFCLK_SEL; ++ ++ } else if (n_ports == 1) { ++ tmp_val &= ~HI_SATA_PHY1_REFCLK_SEL_MASK; ++ tmp_val |= HI_SATA_PHY1_REFCLK_SEL; ++ } ++ writel(tmp_val, (void *)HI_SATA_PERI_CRG72); ++} ++ ++void hisata_v200_set_fifo(void *mmio, int n_ports) ++{ ++ int i; ++ ++ for (i = 0; i < n_ports; i++) ++ writel(HI_SATA_FIFOTH_VALUE, (mmio + 0x100 + i*0x80 ++ + HI_SATA_PORT_FIFOTH)); ++} ++ ++void hisata_phy_init(void *mmio, int phy_mode, int n_ports) ++{ ++ unsigned int tmp, phy_config = HI_SATA_PHY_3G; ++ unsigned int phy_sg = HI_SATA_PHY_SG_3G; ++ int i, port_no; ++ ++ if ((n_ports < 1) || (n_ports > 2)) ++ pr_err("ERROR: PORT num you set is WRONG!!!\n"); ++ ++ sata_port_map = (1 << n_ports) - 1; ++ hisata_v200_set_fifo(mmio, n_ports); ++ ++ tmp = readl(mmio + HI_SATA_PHY_CTL1); ++ tmp |= HI_SATA_BIGENDINE; ++ writel(tmp, (mmio + HI_SATA_PHY_CTL1)); ++ tmp = readl(mmio + HI_SATA_PHY_CTL2); ++ tmp |= HI_SATA_BIGENDINE; ++ writel(tmp, (mmio + HI_SATA_PHY_CTL2)); ++ ++ tmp = readl(mmio + HI_SATA_PHY_RST_BACK_MASK); ++ tmp |= HI_SATA_PHY_BACK_MASK_ALL; ++ if (n_ports == 1) ++ tmp &= ~HI_SATA_PHY1_RST_BACK_MASK; ++ else if (n_ports == 2) ++ /* Not need mask any port */ ++ ++ writel(tmp, (mmio + HI_SATA_PHY_RST_BACK_MASK)); ++ ++ if (phy_mode == HI_SATA_PHY_MODE_1_5G) { ++ phy_config = HI_SATA_PHY_1_5G; ++ phy_sg = HI_SATA_PHY_SG_1_5G; ++ } ++ ++ if (phy_mode == HI_SATA_PHY_MODE_3G) { ++ phy_config = HI_SATA_PHY_3G; ++ phy_sg = HI_SATA_PHY_SG_3G; ++ } ++ ++ if (phy_mode == HI_SATA_PHY_MODE_6G) { ++ phy_config = HI_SATA_PHY_6G; ++ phy_sg = HI_SATA_PHY_SG_6G; ++ } ++ ++ for (i = 0; i < n_ports; i++) { ++ port_no = i; ++ writel(phy_config, (mmio + 0x100 + port_no*0x80 ++ + HI_SATA_PORT_PHYCTL)); ++ ++ writel(phy_sg, (mmio + 0x100 + port_no*0x80 ++ + HI_SATA_PORT_PHYCTL1)); ++ } ++} ++ ++static void hi_sata_phy_reg_config(void) ++{ ++ unsigned int i, port_no; ++ ++ for (i = 0; i < n_ports; i++) { ++ port_no = i; ++ ++ if (port_no == 0) { ++ /* PLL always 6G & CDR <= RATE */ ++ writel(0xd01, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0xd41, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0xd01, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ ++ /* disable SSC */ ++ writel(0x803, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x843, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x803, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ ++ /* EQ set b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x049, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x548, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ } else if (port_no == 1) { ++ /* PLL always 6G & CDR <= RATE */ ++ writel(0xd01, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0xd41, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0xd01, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ ++ /* disable SSC */ ++ writel(0x803, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x843, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x803, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ ++ /* EQ set b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x049, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x548, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ } ++ } ++} ++ ++void hi_sata_eq_recovery(unsigned int port_no) ++{ ++ if (port_no == 0) { ++ /* EQ recovery */ ++ writel(0xf09, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0xf49, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0xf09, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ ++ writel(0x308, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x348, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x308, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ } else if (port_no == 1) { ++ /* EQ recovery */ ++ writel(0xf09, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0xf49, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0xf09, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ ++ writel(0x308, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x348, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x308, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ } ++} ++EXPORT_SYMBOL(hi_sata_eq_recovery); ++ ++void hi_sata_set_eq(unsigned int port_no) ++{ ++ if (port_no == 0) { ++ /* EQ set b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x049, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x548, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY0); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY0); ++ } else if (port_no == 1) { ++ /* EQ set b010000 */ ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x049, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x009, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x548, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x508, (void *)HI_SATA_MISC_SATA_PHY1); ++ writel(0x0, (void *)HI_SATA_MISC_SATA_PHY1); ++ } ++} ++EXPORT_SYMBOL(hi_sata_set_eq); ++ +diff --git a/drivers/phy/phy-hi35x1d-usb.c b/drivers/phy/phy-hi35x1d-usb.c +new file mode 100644 +index 0000000..2fdf0ba +--- /dev/null ++++ b/drivers/phy/phy-hi35x1d-usb.c +@@ -0,0 +1,350 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/phy/phy.h> ++#include <linux/platform_device.h> ++#include <linux/of_address.h> ++#include <mach/io.h> ++#include <linux/kthread.h> ++#include <linux/export.h> ++ ++#define USB2_CTRL 0x130 ++#define USB2_BUS_CKEN (1 << 0) ++#define USB2_OHCI48M_CKEN (1 << 1) ++#define USB2_OHCI12M_CKEN (1 << 2) ++#define USB2_HST_PHY_CKEN (1 << 4) ++#define USB2_UTMI0_CKEN (1 << 5) ++#define USB2_UTMI1_CKEN (1 << 6) ++#define USB2_BUS_SRST_REQ (1 << 12) ++#define USB2_UTMI0_SRST_REQ (1 << 13) ++#define USB2_UTMI1_SRST_REQ (1 << 14) ++#define USB2_HST_PHY_SYST_REQ (1 << 16) ++ ++#define REG_USB2_PHY0 0x134 ++#define USB_PHY0_REF_CKEN (1 << 0) ++#define USB_PHY0_SRST_REQ (1 << 8) ++#define USB_PHY0_SRST_TREQ (1 << 9) ++#define USB_PHY1_SRST_TREQ (1 << 10) ++#define USB_PHY0_TEST_SRST_REQ (1 << 11) ++#define USB_PHY0_REFCLK_SEL (1 << 16) ++#ifdef CONFIG_ARCH_HI3531D ++#define USB1_CTRL0 0x90 ++#endif ++#if defined(CONFIG_ARCH_HI3521D) || defined(CONFIG_ARCH_HI3536C) ++#define USB1_CTRL0 0x50 ++#endif ++#define WORDINTERFACE (1 << 0) ++#define SS_BURST4_EN (1 << 7) ++#define SS_BURST8_EN (1 << 8) ++#define SS_BURST16_EN (1 << 9) ++ ++#define USB2_PHY_TEST_REG_ACCESS (1 << 20) ++ ++#define USB2_CTRL1 0x94 ++/* write(0x1 << 5) 0x6 to addr 0x4 */ ++#define CONFIG_CLK ((0x1 << 21) | (0x6 << 8) | (0x4 << 0)) ++ ++extern int otg_usbdev_stat; ++int otg_usbhost_stat; ++EXPORT_SYMBOL(otg_usbhost_stat); ++ ++struct hisi_priv { ++ void __iomem *base; ++ void __iomem *peri_ctrl; /* 0x12040000 */ ++ void __iomem *misc_ctrl; /* 0x12120000 */ ++}; ++ ++static int hisi_usb_phy_on(struct phy *phy) ++{ ++ int reg; ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++ /* reset enable */ ++ reg = readl(priv->peri_ctrl + USB2_CTRL); ++ reg |= (USB2_BUS_SRST_REQ ++ | USB2_UTMI0_SRST_REQ ++ | USB2_HST_PHY_SYST_REQ); ++#if defined(CONFIG_ARCH_HI3521D) || defined(CONFIG_ARCH_HI3536C) ++ reg |= USB2_UTMI1_SRST_REQ; ++#endif ++ ++ writel(reg, priv->peri_ctrl + USB2_CTRL); ++ udelay(200); ++ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg |= (USB_PHY0_SRST_REQ ++ | USB_PHY0_SRST_TREQ); ++#if defined(CONFIG_ARCH_HI3521D) || defined(CONFIG_ARCH_HI3536C) ++ reg |= USB_PHY1_SRST_TREQ; ++#endif ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(200); ++ reg = readl(priv->misc_ctrl + USB1_CTRL0); ++ reg &= ~(WORDINTERFACE); /* 8bit */ ++ reg &= ~(SS_BURST16_EN); /* 16 bit burst disable */ ++ writel(reg, priv->misc_ctrl + USB1_CTRL0); ++ udelay(100); ++ /* for ssk usb storage ok */ ++ msleep(20); ++ ++ /* open ref clock */ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg |= (USB_PHY0_REF_CKEN); ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(100); ++ ++ /* cancel power on reset */ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg &= ~(USB_PHY0_SRST_REQ); ++ reg &= ~(USB_PHY0_TEST_SRST_REQ); ++ writel(reg , priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(300); ++ ++ /* config type */ ++ reg = readl(priv->misc_ctrl + 0x10); ++ reg |= USB2_PHY_TEST_REG_ACCESS; ++ writel(reg, priv->misc_ctrl + 0x10); ++ udelay(2); ++#if defined(CONFIG_ARCH_HI3521D) || defined(CONFIG_ARCH_HI3536C) ++ /* config clock */ ++ writel(0xc, priv->misc_ctrl + 0x8018); ++ mdelay(2); ++ /* port0 IComp default 400 and disconnect 625mv */ ++ writel(0x1, priv->misc_ctrl + 0x8028); ++ udelay(20); ++ /* port1 IComp default400 and disconnect 625mv */ ++ writel(0x1, priv->misc_ctrl + 0x8428); ++ udelay(20); ++ /* comp 262mv */ ++ writel(0xc, priv->misc_ctrl + 0x8018); ++ udelay(20); ++ writel(0x92, priv->misc_ctrl + 0x8014); ++ udelay(20); ++ /* port0 pre-emphasis to adjust for 000 0x9[5:3] */ ++ writel(0x4, priv->misc_ctrl + 0x8024); ++ udelay(20); ++ /* port1 pre-emphasis to adjust for 000 0x9[5:3] */ ++ writel(0x4, priv->misc_ctrl + 0x8424); ++ udelay(20); ++ /* port 1 pre driver 011 */ ++ writel(0xc4, priv->misc_ctrl + 0x8420); ++ udelay(20); ++ /* port0 pre-emphasis to adjust for 000 0x9[5:3] */ ++ writel(0xc4, priv->misc_ctrl + 0x8020); ++ udelay(20); ++#endif ++#ifdef CONFIG_ARCH_HI3531D ++ /* config clock */ ++ writel(0xc, priv->misc_ctrl + 0x4018); ++ mdelay(2); ++ /* slew rate */ ++ writel(0xc4, priv->misc_ctrl + 0x4020); ++ udelay(20); ++ writel(0xc1, priv->misc_ctrl + 0x4044); ++ udelay(20); ++ /* disconnect */ ++ writel(0x1b, priv->misc_ctrl + 0x4028); ++ udelay(20); ++ writel(0x1c, priv->misc_ctrl + 0x4000); ++ udelay(20); ++ writel(0x92, priv->misc_ctrl + 0x4014); ++ udelay(20); ++ writel(0xe, priv->misc_ctrl + 0x4018); ++ udelay(20); ++ writel(0x4, priv->misc_ctrl + 0x4024); ++ udelay(20); ++#endif ++ /* cancel port reset */ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg &= ~(USB_PHY0_SRST_TREQ); ++#if defined(CONFIG_ARCH_HI3521D) || defined(CONFIG_ARCH_HI3536C) ++ reg &= ~(USB_PHY1_SRST_TREQ); ++#endif ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(300); ++ ++ /* cancel control reset */ ++ reg = readl(priv->peri_ctrl + USB2_CTRL); ++ reg &= ~(USB2_BUS_SRST_REQ ++ | USB2_UTMI0_SRST_REQ ++ | USB2_HST_PHY_SYST_REQ); ++#if defined(CONFIG_ARCH_HI3521D) || defined(CONFIG_ARCH_HI3536C) ++ reg &= ~USB2_UTMI1_SRST_REQ; ++#endif ++ ++ reg |= (USB2_BUS_CKEN ++ | USB2_OHCI48M_CKEN ++ | USB2_OHCI12M_CKEN ++ | USB2_HST_PHY_CKEN ++ | USB2_UTMI0_CKEN); ++#if defined(CONFIG_ARCH_HI3521D) || defined(CONFIG_ARCH_HI3536C) ++ reg |= USB2_UTMI1_CKEN; ++#endif ++ writel(reg, priv->peri_ctrl + USB2_CTRL); ++ udelay(200); ++ ++ return 0; ++} ++ ++static int hisi_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct phy *phy; ++ struct hisi_priv *priv; ++ struct device_node *np = pdev->dev.of_node; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->peri_ctrl = of_iomap(np, 0); /* 0x12040000 */ ++ if (IS_ERR(priv->peri_ctrl)) ++ priv->peri_ctrl = NULL; ++ ++ priv->misc_ctrl = of_iomap(np, 1); /* 0x12120000 */ ++ if (IS_ERR(priv->misc_ctrl)) ++ priv->misc_ctrl = NULL; ++ ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ platform_set_drvdata(pdev, phy); ++ phy_set_drvdata(phy, priv); ++ hisi_usb_phy_on(phy); ++ ++ return 0; ++} ++ ++static int hisi_usb_phy_remove(struct platform_device *pdev) ++{ ++#if 1 ++ int reg; ++ struct phy *phy = platform_get_drvdata(pdev); ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg |= (USB_PHY0_SRST_REQ ++ | USB_PHY0_SRST_TREQ); ++#if defined(CONFIG_ARCH_HI3521D) || defined(CONFIG_ARCH_HI3536C) ++ reg |= USB_PHY1_SRST_TREQ; ++#endif ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(100); ++ ++ /* close clock */ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg &= ~(USB_PHY0_REFCLK_SEL ++ | USB_PHY0_REF_CKEN); ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(300); ++ ++ /* close clock */ ++ reg = readl(priv->peri_ctrl + USB2_CTRL); ++ reg &= ~(USB2_BUS_CKEN ++ | USB2_OHCI48M_CKEN ++ | USB2_OHCI12M_CKEN ++ | USB2_HST_PHY_CKEN ++ | USB2_UTMI0_CKEN); ++#if defined(CONFIG_ARCH_HI3521D) || defined(CONFIG_ARCH_HI3536C) ++ reg &= ~USB2_UTMI1_CKEN; ++#endif ++ writel(reg, priv->peri_ctrl + USB2_CTRL); ++ udelay(200); ++#endif ++ return 0; ++} ++ ++static const struct of_device_id hisi_usb_phy_of_match[] = { ++ {.compatible = "hisilicon,hi3531d-usb2-phy",}, ++ {.compatible = "hisilicon,hi3521d-usb2-phy",}, ++ {.compatible = "hisilicon,hi3536c-usb2-phy",}, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, hisi_usb_phy_of_match); ++ ++#ifdef CONFIG_PM_SLEEP ++ ++static int hisi_usb_phy_suspend(struct device *dev) ++{ ++ int reg; ++ struct phy *phy = dev_get_drvdata(dev); ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg |= (USB_PHY0_SRST_REQ ++ | USB_PHY0_SRST_TREQ); ++#if defined(CONFIG_ARCH_HI3521D) || defined(CONFIG_ARCH_HI3536C) ++ reg |= USB_PHY1_SRST_TREQ; ++#endif ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(100); ++ ++ /* close clock */ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg &= ~(USB_PHY0_REFCLK_SEL ++ | USB_PHY0_REF_CKEN); ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(300); ++ ++ /* close clock */ ++ reg = readl(priv->peri_ctrl + USB2_CTRL); ++ reg &= ~(USB2_BUS_CKEN ++ | USB2_OHCI48M_CKEN ++ | USB2_OHCI12M_CKEN ++ | USB2_HST_PHY_CKEN ++ | USB2_UTMI0_CKEN); ++#if defined(CONFIG_ARCH_HI3521D) || defined(CONFIG_ARCH_HI3536C) ++ reg &= ~USB2_UTMI1_CKEN; ++#endif ++ writel(reg, priv->peri_ctrl + USB2_CTRL); ++ udelay(200); ++ ++ return 0; ++} ++ ++static int hisi_usb_phy_resume(struct device *dev) ++{ ++ struct phy *phy = dev_get_drvdata(dev); ++ ++ hisi_usb_phy_on(phy); ++ return 0; ++} ++ ++#endif /* CONFIG_PM_SLEEP */ ++ ++static SIMPLE_DEV_PM_OPS(hisi_usb2_pm_ops, hisi_usb_phy_suspend, ++ hisi_usb_phy_resume); ++ ++static struct platform_driver hisi_usb_phy_driver = { ++ .probe = hisi_usb_phy_probe, ++ .remove = hisi_usb_phy_remove, ++ .driver = { ++ .name = "hisi-usb-phy", ++ .pm = &hisi_usb2_pm_ops, ++ .of_match_table = hisi_usb_phy_of_match, ++ } ++}; ++module_platform_driver(hisi_usb_phy_driver); ++ ++MODULE_AUTHOR("Pengcheng Li <lpc.li@hisilicon.com>"); ++MODULE_DESCRIPTION("HISILICON USB PHY driver"); ++MODULE_ALIAS("platform:hisi-usb-phy"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-hisi-inno-usb2.c b/drivers/phy/phy-hisi-inno-usb2.c +new file mode 100644 +index 0000000..1a50295 +--- /dev/null ++++ b/drivers/phy/phy-hisi-inno-usb2.c +@@ -0,0 +1,489 @@ ++ /* ++ * HiSilicon INNO USB2 PHY Driver. ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/mfd/syscon.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/phy/phy.h> ++#include <linux/io.h> ++#include <linux/regmap.h> ++#include <linux/reset.h> ++#include <linux/kthread.h> ++#include <linux/export.h> ++#include <linux/pm.h> ++#include <linux/of_address.h> ++ ++#define USB2_SWITCH_BASE ioremap_nocache(0x12020150, 0x4) ++#define USB2_OTG_BASE 0x5c ++#define USB2_PHY_SET 0x60 ++#define USB_PHY_REG_ACCESS (0x1 << 16) ++#define DWC_OTG_EN (1 << 31) ++#define USB2_PHY_DPPULL_DOWN (0x3 << 26) ++#define MAX_PORTS 4 ++#define PHY_CLK 0x18 ++ ++#define US2_CRG_BASE 0xb8 ++#define USB2_OHCI48M_CKEN (0x1 << 1) ++#define USB2_OHCI12M_CKEN (0x1 << 2) ++#define USB2_HST_PHY_CKEN (0x1 << 4) ++#define USB2_UTMI0_CKEN (0x1 << 5) ++#define USB2_PHY_CKEN (0x1 << 7) ++#define USB2_BUS_SRST_REQ (0x1 << 8) ++#define USB2_UTMI0_SRST_REQ (0x1 << 9) ++#define USB2_HST_PHY_SRST_REQ (0x1 << 11) ++#define USB2_PHY_REQ (0x1 << 13) ++#define USB2_PHY_PORT0_TREQ (0x1 << 14) ++#define USB2_PHY_CLKSEL (0x1 << 15) ++ ++struct hisi_inno_phy_port { ++ struct clk *utmi_clk; ++ struct reset_control *port_rst; ++ struct reset_control *utmi_rst; ++}; ++ ++struct hisi_inno_phy_priv { ++ void __iomem *peri_ctrl; /* 0x12030000 */ ++ void __iomem *phy_reg; /* 0x120d0000 */ ++ struct clk *ref_clk; ++ struct reset_control *test_rst; ++ struct reset_control *por_rst; ++ struct reg_sequence *reg_seq; ++ u32 reg_num; ++ struct hisi_inno_phy_port *ports; ++ u8 port_num; ++}; ++static struct task_struct *kusbotg_task; ++static int *usb2_switch_base; ++ ++extern void usb2_low_power(int); ++extern int otg_usbdev_stat; ++int otg_usbhost_stat; ++EXPORT_SYMBOL(otg_usbhost_stat); ++static void __iomem *peri_crg; /* 0x12010000 */ ++static void __iomem *sys_ctrl; ++#define SC_SYS_ID 0xEE0 ++ ++void hisi_switch_func(int otg) ++{ ++ int reg; ++ ++ reg = readl(usb2_switch_base); ++ if (otg) { ++ reg |= 0x1; ++ writel(reg, usb2_switch_base); ++ } else { ++ reg &= ~(0x1); ++ writel(reg, usb2_switch_base); ++ } ++} ++EXPORT_SYMBOL(hisi_switch_func); ++ ++void usb2_low_power(int portsc) ++{ ++#ifdef CONFIG_HIUSB_DEVICE2_0 ++ return; ++#else ++ int reg; ++ ++ if (portsc & 0x1) { ++ reg = readl(peri_crg + US2_CRG_BASE); ++ reg |= USB2_PHY_CKEN; ++ writel(reg, peri_crg + US2_CRG_BASE); ++ udelay(100); ++ reg = readl(peri_crg + US2_CRG_BASE); ++ reg &= ~USB2_PHY_REQ; ++ writel(reg, peri_crg + US2_CRG_BASE); ++ mdelay(1); ++ reg = readl(peri_crg + US2_CRG_BASE); ++ reg &= ~USB2_PHY_PORT0_TREQ; ++ writel(reg, peri_crg + US2_CRG_BASE); ++ udelay(10); ++ reg = readl(peri_crg + US2_CRG_BASE); ++ reg &= ~USB2_OHCI48M_CKEN; ++ reg &= ~USB2_OHCI12M_CKEN; ++ reg &= ~USB2_UTMI0_CKEN; ++ writel(reg, peri_crg + US2_CRG_BASE); ++ udelay(10); ++ reg = readl(peri_crg + US2_CRG_BASE); ++ reg &= ~USB2_PHY_CLKSEL; ++ writel(reg, peri_crg + US2_CRG_BASE); ++ udelay(10); ++ reg = readl(peri_crg + US2_CRG_BASE); ++ reg |= USB2_OHCI48M_CKEN; ++ reg |= USB2_OHCI12M_CKEN; ++ reg |= USB2_HST_PHY_CKEN; ++ reg |= USB2_UTMI0_CKEN; ++ writel(reg, peri_crg + US2_CRG_BASE); ++ } else { ++ reg = readl(peri_crg + US2_CRG_BASE); ++ reg &= ~USB2_OHCI48M_CKEN; ++ reg &= ~USB2_OHCI12M_CKEN; ++ reg &= ~USB2_HST_PHY_CKEN; ++ reg &= ~USB2_UTMI0_CKEN; ++ writel(reg, peri_crg + US2_CRG_BASE); ++ reg = readl(peri_crg + US2_CRG_BASE); ++ reg |= USB2_PHY_CLKSEL; ++ writel(reg, peri_crg + US2_CRG_BASE); ++ reg = readl(peri_crg + US2_CRG_BASE); ++ reg |= USB2_OHCI48M_CKEN; ++ reg |= USB2_OHCI12M_CKEN; ++ reg |= USB2_UTMI0_CKEN; ++ writel(reg, peri_crg + US2_CRG_BASE); ++ reg = readl(peri_crg + US2_CRG_BASE); ++ reg |= USB2_PHY_PORT0_TREQ; ++ reg |= USB2_PHY_REQ; ++ reg &= ~USB2_PHY_CKEN; ++ writel(reg, peri_crg + US2_CRG_BASE); ++ } ++#endif ++} ++ ++static int hisi_inno_phy_setup(struct hisi_inno_phy_priv *priv) ++{ ++ int reg; ++ /* config eye */ ++ reg = readl(priv->peri_ctrl + USB2_PHY_SET); ++ reg |= USB_PHY_REG_ACCESS; ++ writel(reg, priv->peri_ctrl + USB2_PHY_SET); ++ writel(0x4, priv->phy_reg + PHY_CLK); ++ mdelay(2); ++ ++ reg = readl(sys_ctrl + SC_SYS_ID); ++ if ((reg >> 24) == 0x4) /* hi3516ev100 */ ++ writel(0x1c, priv->phy_reg); ++ else ++ writel(0x18, priv->phy_reg); ++ udelay(20); ++ writel(0xc4, priv->phy_reg + 0x20); ++ udelay(20); ++ writel(0xc1, priv->phy_reg + 0x44); ++ udelay(20); ++ writel(0x1b, priv->phy_reg + 0x28); ++ udelay(20); ++ ++ return 0; ++} ++ ++static int hisi_inno_port_init(struct hisi_inno_phy_port *port) ++{ ++ int ret = 0; ++ ++ reset_control_deassert(port->port_rst); ++ mdelay(2); ++ ++ ret = clk_prepare_enable(port->utmi_clk); ++ if (ret) ++ return ret; ++ udelay(200); ++ ++ reset_control_deassert(port->utmi_rst); ++ udelay(200); ++ ++ return 0; ++} ++ ++static int hisi_inno_phy_init(struct phy *phy) ++{ ++ struct hisi_inno_phy_priv *priv = phy_get_drvdata(phy); ++ int ret, port; ++ ++ ret = clk_prepare_enable(priv->ref_clk); ++ if (ret) ++ return ret; ++ udelay(100); ++ ++ if (priv->test_rst) { ++ reset_control_deassert(priv->test_rst); ++ udelay(100); ++ } ++ ++ reset_control_deassert(priv->por_rst); ++ udelay(300); ++ ++ /* config phy clk and phy eye diagram */ ++ ret = hisi_inno_phy_setup(priv); ++ if (ret) ++ goto err_disable_ref_clk; ++ ++ for (port = 0; port < priv->port_num; port++) { ++ ret = hisi_inno_port_init(&priv->ports[port]); ++ if (ret) ++ goto err_disable_clks; ++ } ++ ++ return 0; ++ ++err_disable_clks: ++ while (--port >= 0) ++ clk_disable_unprepare(priv->ports[port].utmi_clk); ++err_disable_ref_clk: ++ clk_disable_unprepare(priv->ref_clk); ++ ++ return ret; ++} ++ ++static void hisi_inno_phy_disable(struct phy *phy) ++{ ++ struct hisi_inno_phy_priv *priv = phy_get_drvdata(phy); ++ int i; ++ ++ for (i = 0; i < priv->port_num; i++) ++ clk_disable_unprepare(priv->ports[i].utmi_clk); ++ ++ clk_disable_unprepare(priv->ref_clk); ++} ++ ++static int hisi_inno_phy_of_get_ports(struct device *dev, ++ struct hisi_inno_phy_priv *priv) ++{ ++ struct device_node *node = dev->of_node, *child; ++ int port = 0, ret = 0; ++ ++ priv->port_num = of_get_child_count(node); ++ if (priv->port_num > MAX_PORTS) { ++ dev_err(dev, "too many ports : %d (max = %d)\n", ++ priv->port_num, MAX_PORTS); ++ return -EINVAL; ++ } ++ ++ priv->ports = devm_kcalloc(dev, priv->port_num, ++ sizeof(struct hisi_inno_phy_port), GFP_KERNEL); ++ if (!priv->ports) ++ return -ENOMEM; ++ ++ for_each_child_of_node(node, child) { ++ struct hisi_inno_phy_port *phy_port = &priv->ports[port]; ++ ++ phy_port->utmi_clk = of_clk_get(child, 0); ++ if (IS_ERR(phy_port->utmi_clk)) { ++ ret = PTR_ERR(phy_port->utmi_clk); ++ goto fail; ++ } ++ ++ phy_port->port_rst = of_reset_control_get(child, "port_rst"); ++ if (IS_ERR(phy_port->port_rst)) { ++ ret = PTR_ERR(phy_port->port_rst); ++ clk_put(phy_port->utmi_clk); ++ goto fail; ++ } ++ ++ phy_port->utmi_rst = of_reset_control_get(child, "utmi_rst"); ++ if (IS_ERR(phy_port->utmi_rst)) { ++ ret = PTR_ERR(phy_port->utmi_rst); ++ reset_control_put(phy_port->port_rst); ++ clk_put(phy_port->utmi_clk); ++ goto fail; ++ } ++ port++; ++ } ++ ++ return ret; ++ ++fail: ++ while (--port >= 0) { ++ struct hisi_inno_phy_port *phy_port = &priv->ports[port]; ++ ++ reset_control_put(phy_port->utmi_rst); ++ reset_control_put(phy_port->port_rst); ++ clk_put(phy_port->utmi_clk); ++ } ++ of_node_put(child); ++ ++ return ret; ++} ++ ++/* hiotg run */ ++static void device_to_host(struct hisi_inno_phy_priv *priv) ++{ ++ int reg; ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB2_OTG_BASE); ++ reg |= USB2_PHY_DPPULL_DOWN; ++ reg &= ~DWC_OTG_EN; ++ writel_relaxed(reg, priv->peri_ctrl + USB2_OTG_BASE); ++ ++} ++ ++static void host_to_device(struct hisi_inno_phy_priv *priv) ++{ ++ ++ int reg; ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB2_OTG_BASE); ++ reg &= ~(USB2_PHY_DPPULL_DOWN); ++ reg |= DWC_OTG_EN; ++ writel_relaxed(reg, priv->peri_ctrl + USB2_OTG_BASE); ++ ++} ++ ++int otg_run(struct hisi_inno_phy_priv *priv) ++{ ++ int reg; ++ ++ reg = readl(priv->peri_ctrl + USB2_OTG_BASE); ++ ++ /* device -->host */ ++ if ((reg & DWC_OTG_EN) == DWC_OTG_EN) { ++ ++ if (otg_usbhost_stat == 1) ++ return 0; ++ device_to_host(priv); ++ ++ } else { /* host -->device */ ++ if (otg_usbdev_stat == 1) ++ return 0; ++ ++ host_to_device(priv); ++ } ++ ++ ++ return 0; ++ ++} ++ ++static int usbotg_thread(void *arg) ++{ ++#ifdef CONFIG_USB_AUTO_SWITCH ++ struct hisi_inno_phy_priv *priv = arg; ++ int reg; ++#endif ++ ++ writel(0x0, usb2_switch_base); ++#ifdef CONFIG_USB_AUTO_SWITCH ++ do { ++ reg = readl(usb2_switch_base); ++ if (!(reg & (0x1 << 1))) ++ otg_run(priv); ++ ++ msleep(1000); ++ ++ } while (1); ++#endif ++ ++ return 0; ++} ++ ++ ++static int hisi_inno_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct phy *phy; ++ struct hisi_inno_phy_priv *priv; ++ struct device_node *node = dev->of_node; ++ int ret = 0; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->peri_ctrl = of_iomap(node, 0); /* 0x12030000 */ ++ priv->phy_reg = of_iomap(node, 1); /* 0x120d0000 */ ++ ++#ifdef CONFIG_HIUSB_DEVICE2_0 ++ peri_crg = NULL; ++#else ++ peri_crg = of_iomap(node, 2); /* 0x12010000 */ ++#endif ++ sys_ctrl = of_iomap(node, 3); ++ ++ priv->ref_clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(priv->ref_clk)) ++ return PTR_ERR(priv->ref_clk); ++ ++ priv->por_rst = devm_reset_control_get(dev, "por_rst"); ++ if (IS_ERR(priv->por_rst)) ++ return PTR_ERR(priv->por_rst); ++ ++ priv->test_rst = devm_reset_control_get(dev, "test_rst"); ++ if (IS_ERR(priv->test_rst)) ++ priv->test_rst = NULL; ++ ++ ret = hisi_inno_phy_of_get_ports(dev, priv); ++ if (ret) ++ return ret; ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, phy); ++ phy_set_drvdata(phy, priv); ++ ret = hisi_inno_phy_init(phy); ++ ++ usb2_switch_base = (int *)USB2_SWITCH_BASE; ++ /* run hiotg */ ++ kusbotg_task = kthread_run(usbotg_thread, priv, "kusbotg"); ++ if (IS_ERR(kusbotg_task)) { ++ dev_err(dev, "can't start kusbotg\n"); ++ ++ return ret; ++ } ++ ++ return ret; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int hisi_inno_phy_suspend(struct device *dev) ++{ ++ struct phy *phy = dev_get_drvdata(dev); ++ ++ hisi_inno_phy_disable(phy); ++ ++ return 0; ++} ++ ++static int hisi_inno_phy_resume(struct device *dev) ++{ ++ struct phy *phy = dev_get_drvdata(dev); ++ int ret = 0; ++ ++ ret = hisi_inno_phy_init(phy); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static const struct dev_pm_ops hisi_inno_phy_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(hisi_inno_phy_suspend, hisi_inno_phy_resume) ++}; ++ ++static const struct of_device_id hisi_inno_phy_of_match[] = { ++ {.compatible = "hisilicon,inno_usb2_phy",}, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, hisi_inno_phy_of_match); ++ ++static struct platform_driver hisi_inno_phy_driver = { ++ .probe = hisi_inno_phy_probe, ++ .driver = { ++ .name = "hisi-inno-phy", ++ .of_match_table = hisi_inno_phy_of_match, ++ .pm = &hisi_inno_phy_pm_ops, ++ } ++}; ++module_platform_driver(hisi_inno_phy_driver); ++ ++MODULE_AUTHOR("Pengcheng Li <lpc.li@hisilicon.com>"); ++MODULE_DESCRIPTION("HISILICON USB PHY driver"); ++MODULE_ALIAS("platform:hisi-usb-phy"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-hisi-nano-phy-sata.c b/drivers/phy/phy-hisi-nano-phy-sata.c +new file mode 100644 +index 0000000..811cdbc +--- /dev/null ++++ b/drivers/phy/phy-hisi-nano-phy-sata.c +@@ -0,0 +1,182 @@ ++/* ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/mfd/syscon.h> ++#include <linux/module.h> ++#include <linux/phy/phy.h> ++#include <linux/platform_device.h> ++#include <linux/regmap.h> ++#include <mach/io.h> ++#include <mach/platform.h> ++ ++static int n_ports = CONFIG_HI_SATA_PORTS; ++static int phy_mode = CONFIG_HI_SATA_MODE; ++unsigned int sata_port_map; ++ ++#ifdef MODULE ++module_param(phy_config, uint, 0600); ++MODULE_PARM_DESC(phy_config, "sata phy config (default:0x0e180000)"); ++ ++module_param(n_ports, uint, 0600); ++MODULE_PARM_DESC(n_ports, "sata port number (default:2)"); ++module_param(mode_3g, uint, 0600); ++MODULE_PARM_DESC(phy_mode, "sata phy mode (0:1.5G;1:3G(default);2:6G)"); ++#endif ++ ++#ifdef CONFIG_ARCH_HI3536C ++#include "phy-hi3536c-sata.c" ++#endif ++ ++#ifdef CONFIG_ARCH_HI3521D ++#include "phy-hi3521d-sata.c" ++#endif ++ ++#ifdef CONFIG_ARCH_HI3531D ++#include "phy-hi3531d-sata.c" ++#endif ++ ++static int hisi_sata_phy_init(struct phy *phy) ++{ ++ void __iomem *mmio = phy_get_drvdata(phy); ++#ifdef CONFIG_ARCH_HI3531D ++ int port_num; ++ ++ port_num = hi_sata_port_nr(); ++ if ((port_num < 1) || (port_num > 4)) { ++ pr_err("sata ports number:%d WRONG!!!\n", n_ports); ++ return -EINVAL; ++ } ++ ++ n_ports = port_num; ++#endif ++ ++ hi_sata_poweron(); ++ hi_sata_reset(); ++ hi_sata_phy_reset(); ++ hi_sata_phy_clk_sel(); ++ hi_sata_clk_enable(); ++ msleep(20); ++ hi_sata_phy_unreset(); ++ msleep(20); ++ hi_sata_phy_reg_config(); ++ msleep(20); ++ hi_sata_unreset(); ++ msleep(20); ++ hisata_phy_init(mmio, phy_mode, n_ports); ++ ++ return 0; ++} ++ ++static int hisi_sata_phy_exit(struct phy *phy) ++{ ++ hi_sata_phy_reset(); ++ msleep(20); ++ hi_sata_reset(); ++ msleep(20); ++ hi_sata_clk_reset(); ++ msleep(20); ++ hi_sata_clk_disable(); ++ hi_sata_poweroff(); ++ msleep(20); ++ ++ return 0; ++} ++ ++static struct phy_ops hisi_sata_phy_ops = { ++ .init = hisi_sata_phy_init, ++ .exit = hisi_sata_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int hisi_sata_phy_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *phy_provider; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ struct phy *phy; ++ void __iomem *mmio; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(dev, "failed to get reg base\n"); ++ return -ENOENT; ++ } ++ ++ mmio = devm_ioremap(dev, res->start, resource_size(res)); ++ if (!mmio) ++ return -ENOMEM; ++ ++ phy = devm_phy_create(dev, NULL, &hisi_sata_phy_ops, NULL); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create PHY\n"); ++ return PTR_ERR(phy); ++ } ++ ++ phy_set_drvdata(phy, mmio); ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) ++ return PTR_ERR(phy_provider); ++ ++ return 0; ++} ++ ++static int hisi_sata_phy_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ struct device *dev = &pdev->dev; ++ struct phy *phy = to_phy(dev); ++ ++ hisi_sata_phy_exit(phy); ++ ++ return 0; ++} ++ ++static int hisi_sata_phy_resume(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct phy *phy = to_phy(dev); ++ ++ hisi_sata_phy_init(phy); ++ ++ return 0; ++} ++ ++static const struct of_device_id hisi_sata_phy_of_match[] = { ++ {.compatible = "hisilicon,hisi-sata-nano-phy",}, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, hisi_sata_phy_of_match); ++ ++static struct platform_driver hisi_sata_phy_driver = { ++ .probe = hisi_sata_phy_probe, ++ .suspend = hisi_sata_phy_suspend, ++ .resume = hisi_sata_phy_resume, ++ .driver = { ++ .name = "hisi-sata-nano-phy", ++ .of_match_table = hisi_sata_phy_of_match, ++ } ++}; ++module_platform_driver(hisi_sata_phy_driver); ++ ++MODULE_AUTHOR("HiSilicon BVT"); ++MODULE_DESCRIPTION("HISILICON SATA NANO PHY driver"); ++MODULE_ALIAS("platform:hisi-sata-nano-phy"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-hisi-usb.c b/drivers/phy/phy-hisi-usb.c +new file mode 100644 +index 0000000..7890c80 +--- /dev/null ++++ b/drivers/phy/phy-hisi-usb.c +@@ -0,0 +1,550 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/phy/phy.h> ++#include <linux/platform_device.h> ++#include <linux/of_address.h> ++#include <mach/io.h> ++#include <linux/kthread.h> ++#include <linux/export.h> ++ ++#if defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3516AV200) ++#ifdef readl ++#undef readl ++#undef readl_relaxed ++#undef writel ++#undef writel_relaxed ++#define readl hi_readl ++#define readl_relaxed hi_readl_relaxed ++#define writel hi_writel ++#define writel_relaxed hi_writel_relaxed ++#endif /* readl */ ++#endif /* defined(CONFIG_ARCH_HI3519) || defined(CONFIG_HI3519V101) || defined(CONFIG_ARCH_HI3516AV200)*/ ++ ++#define USB2_SWITCH_BASE ioremap_nocache(0x12020150, 0x4) ++#define USB2_OTG_BASE 0x78 ++#define DWC_OTG_EN (1 << 31) ++#define USB2_PHY_DPPULL_DOWN (0x3 << 26) ++ ++#define USB2_PHY0_RST 0xb4 ++ ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++#define HI3519_USB2_PHY_REQ BIT(1) ++#define HI3519_USB2_PHY_PORT0_TREQ BIT(2) ++#define HI3519_USB2_CTRL_HUB_REQ BIT(4) ++#define HI3519_USB_CKEN BIT(7) ++#endif ++ ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++#define HI3559_USB2_PHY_REQ BIT(1) ++#define HI3559_USB2_PHY_PORT0_TREQ BIT(2) ++#define HI3559_USB2_CTRL_HUB_REQ BIT(4) ++#define HI3559_USB_CKEN BIT(7) ++#endif ++ ++#ifdef CONFIG_ARCH_HI3516AV200 ++#define HI3516AV200_USB2_PHY_REQ BIT(1) ++#define HI3516AV200_USB2_PHY_PORT0_TREQ BIT(2) ++#define HI3516AV200_USB2_CTRL_HUB_REQ BIT(4) ++#define HI3516AV200_USB_CKEN BIT(7) ++#endif ++ ++#define USB2_PHY0_CTLL 0x80 ++ ++/* HI3536C */ ++#define USB2_CTRL 0x130 ++#define USB2_BUS_CKEN (1 << 0) ++#define USB2_OHCI48M_CKEN (1 << 1) ++#define USB2_OHCI12M_CKEN (1 << 2) ++#define USB2_HST_PHY_CKEN (1 << 4) ++#define USB2_UTMI0_CKEN (1 << 5) ++#define USB2_UTMI1_CKEN (1 << 6) ++#define USB2_BUS_SRST_REQ (1 << 12) ++#define USB2_UTMI0_SRST_REQ (1 << 13) ++#define USB2_UTMI1_SRST_REQ (1 << 14) ++#define USB2_HST_PHY_SYST_REQ (1 << 16) ++ ++#define REG_USB2_PHY0 0x134 ++#define USB_PHY0_REF_CKEN (1 << 0) ++#define USB_PHY0_SRST_REQ (1 << 8) ++#define USB_PHY0_SRST_TREQ (1 << 9) ++#define USB_PHY1_SRST_TREQ (1 << 10) ++#define USB_PHY0_TEST_SRST_REQ (1 << 11) ++#define USB_PHY0_REFCLK_SEL (1 << 16) ++#if (defined(CONFIG_ARCH_HI3536C)) ++#define USB1_CTRL0 0x50 ++#endif ++#define WORDINTERFACE (1 << 0) ++#define SS_BURST4_EN (1 << 7) ++#define SS_BURST8_EN (1 << 8) ++#define SS_BURST16_EN (1 << 9) ++ ++#define USB2_PHY_TEST_REG_ACCESS (1 << 20) ++ ++#define USB2_CTRL1 0x94 ++/* write(0x1 << 5) 0x6 to addr 0x4 */ ++#define CONFIG_CLK ((0x1 << 21) | (0x6 << 8) | (0x4 << 0)) ++/* END HI3536C */ ++ ++#if (!defined(CONFIG_ARCH_HI3536C)) ++static struct task_struct *kusbotg_task; ++static int *usb2_switch_base; ++ ++extern int otg_usbdev_stat; ++int otg_usbhost_stat; ++EXPORT_SYMBOL(otg_usbhost_stat); ++ ++void hisi_switch_func(int otg) ++{ ++ int reg; ++ ++ reg = readl(usb2_switch_base); ++ if (otg) { ++ reg |= 0x1; ++ writel(reg, usb2_switch_base); ++ } else { ++ reg &= ~(0x1); ++ writel(reg, usb2_switch_base); ++ } ++} ++EXPORT_SYMBOL(hisi_switch_func); ++#endif ++struct hisi_priv { ++ void __iomem *base; ++ void __iomem *misc_ctrl; /* 0x12030000 */ ++ void __iomem *peri_ctrl; /* 0x12010000 */ ++}; ++ ++#if (!defined(CONFIG_ARCH_HI3536C)) ++static int hisi_usb_phy_config(struct phy *phy) ++{ ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++ writel_relaxed(0x80001c, priv->misc_ctrl + USB2_PHY0_CTLL); ++ writel_relaxed(0xa0001c, priv->misc_ctrl + USB2_PHY0_CTLL); ++ usleep_range(200, 250); ++ ++#if defined(CONFIG_ARCH_HI3519V101) \ ++ || defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) || defined(CONFIG_ARCH_HI3516AV200) ++ writel_relaxed(0x800acb, priv->misc_ctrl + USB2_PHY0_CTLL); ++ writel_relaxed(0xa00acb, priv->misc_ctrl + USB2_PHY0_CTLL); ++ usleep_range(200, 250); ++ ++ writel_relaxed(0x801181, priv->misc_ctrl + USB2_PHY0_CTLL); ++ writel_relaxed(0xa01181, priv->misc_ctrl + USB2_PHY0_CTLL); ++ usleep_range(200, 250); ++ ++ writel_relaxed(0x800592, priv->misc_ctrl + USB2_PHY0_CTLL); ++ writel_relaxed(0xa00592, priv->misc_ctrl + USB2_PHY0_CTLL); ++ usleep_range(200, 250); ++#endif ++ ++ writel_relaxed(0x800904, priv->misc_ctrl + USB2_PHY0_CTLL); ++ writel_relaxed(0xa00904, priv->misc_ctrl + USB2_PHY0_CTLL); ++ usleep_range(200, 250); ++ ++ writel_relaxed(0x80060f, priv->misc_ctrl + USB2_PHY0_CTLL); ++ writel_relaxed(0xa0060f, priv->misc_ctrl + USB2_PHY0_CTLL); ++ usleep_range(200, 250); ++ ++#ifdef CONFIG_ARCH_HI3519 ++ writel_relaxed(0x800a4b, priv->misc_ctrl + USB2_PHY0_CTLL); ++ writel_relaxed(0xa00a4b, priv->misc_ctrl + USB2_PHY0_CTLL); ++ usleep_range(200, 250); ++#endif ++ ++ writel_relaxed(0x801141, priv->misc_ctrl + USB2_PHY0_CTLL); ++ writel_relaxed(0xa01141, priv->misc_ctrl + USB2_PHY0_CTLL); ++ msleep(20); ++ ++ return 0; ++} ++#endif ++ ++static int hisi_usb_phy_on(struct phy *phy) ++{ ++ int reg; ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++#if (defined(CONFIG_ARCH_HI3536C)) ++ reg = readl(priv->peri_ctrl + USB2_CTRL); ++ reg |= (USB2_BUS_SRST_REQ ++ | USB2_UTMI0_SRST_REQ ++ | USB2_HST_PHY_SYST_REQ); ++ reg |= USB2_UTMI1_SRST_REQ; ++ writel(reg, priv->peri_ctrl + USB2_CTRL); ++ udelay(200); ++ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg |= (USB_PHY0_SRST_REQ ++ | USB_PHY0_SRST_TREQ); ++ reg |= USB_PHY1_SRST_TREQ; ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(200); ++ reg = readl(priv->misc_ctrl + USB1_CTRL0); ++ reg &= ~(WORDINTERFACE); /* 8bit */ ++ reg &= ~(SS_BURST16_EN); /* 16 bit burst disable */ ++ writel(reg, priv->misc_ctrl + USB1_CTRL0); ++ udelay(100); ++ /* for ssk usb storage ok */ ++ msleep(20); ++ ++ /* open ref clock */ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg |= (USB_PHY0_REF_CKEN); ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(100); ++ ++ /* cancel power on reset */ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg &= ~(USB_PHY0_SRST_REQ); ++ reg &= ~(USB_PHY0_TEST_SRST_REQ); ++ writel(reg , priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(300); ++ ++ /* config type */ ++ reg = readl(priv->misc_ctrl + 0x10); ++ reg |= USB2_PHY_TEST_REG_ACCESS; ++ writel(reg, priv->misc_ctrl + 0x10); ++ udelay(2); ++ /* config clock */ ++ writel(0xc, priv->misc_ctrl + 0x8018); ++ mdelay(2); ++ /* cancel port reset */ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg &= ~(USB_PHY0_SRST_TREQ); ++ reg &= ~(USB_PHY1_SRST_TREQ); ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(300); ++ ++ /* cancel control reset */ ++ reg = readl(priv->peri_ctrl + USB2_CTRL); ++ reg &= ~(USB2_BUS_SRST_REQ ++ | USB2_UTMI0_SRST_REQ ++ | USB2_HST_PHY_SYST_REQ); ++ reg &= ~USB2_UTMI1_SRST_REQ; ++ ++ reg |= (USB2_BUS_CKEN ++ | USB2_OHCI48M_CKEN ++ | USB2_OHCI12M_CKEN ++ | USB2_HST_PHY_CKEN ++ | USB2_UTMI0_CKEN); ++ reg |= USB2_UTMI1_CKEN; ++ writel(reg, priv->peri_ctrl + USB2_CTRL); ++ udelay(200); ++ ++#else ++ reg = readl_relaxed(priv->peri_ctrl + USB2_PHY0_RST); ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ reg &= ~HI3519_USB2_PHY_REQ; ++ reg &= ~HI3519_USB2_PHY_PORT0_TREQ; ++ reg &= ~HI3519_USB2_CTRL_HUB_REQ; ++ reg |= HI3519_USB_CKEN; ++#endif ++ ++#if defined(CONFIG_ARCH_HI3516AV200) ++ reg &= ~HI3516AV200_USB2_PHY_REQ; ++ reg &= ~HI3516AV200_USB2_PHY_PORT0_TREQ; ++ reg &= ~HI3516AV200_USB2_CTRL_HUB_REQ; ++ reg |= HI3516AV200_USB_CKEN; ++#endif ++ ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++ reg &= ~HI3559_USB2_PHY_REQ; ++ reg &= ~HI3559_USB2_PHY_PORT0_TREQ; ++ reg &= ~HI3559_USB2_CTRL_HUB_REQ; ++ reg |= HI3559_USB_CKEN; ++#endif ++ writel_relaxed(reg, priv->peri_ctrl + USB2_PHY0_RST); ++ mdelay(100); ++ ++ writel_relaxed(0x800000, priv->misc_ctrl + USB2_PHY0_CTLL); ++ mdelay(100); ++ writel_relaxed(0xa0060c, priv->misc_ctrl + USB2_PHY0_CTLL); ++ mdelay(100); ++ ++ hisi_usb_phy_config(phy); ++#endif ++ return 0; ++} ++ ++#if (!defined(CONFIG_ARCH_HI3536C)) ++/* hiotg run */ ++static void device_to_host(struct hisi_priv *priv) ++{ ++ int reg; ++ ++ reg = readl_relaxed(priv->misc_ctrl + USB2_OTG_BASE); ++ reg |= USB2_PHY_DPPULL_DOWN; ++ reg &= ~DWC_OTG_EN; ++ writel_relaxed(reg, priv->misc_ctrl + USB2_OTG_BASE); ++} ++ ++static void host_to_device(struct hisi_priv *priv) ++{ ++ int reg; ++ ++ reg = readl_relaxed(priv->misc_ctrl + USB2_OTG_BASE); ++ reg &= ~(USB2_PHY_DPPULL_DOWN); ++ reg |= DWC_OTG_EN; ++ writel_relaxed(reg, priv->misc_ctrl + USB2_OTG_BASE); ++} ++ ++int otg_run(struct hisi_priv *priv) ++{ ++ int reg; ++ ++ reg = readl(priv->misc_ctrl + USB2_OTG_BASE); ++ ++ /* device -->host */ ++ if ((reg & DWC_OTG_EN) == DWC_OTG_EN) { ++ ++ if (otg_usbhost_stat == 1) ++ return 0; ++ device_to_host(priv); ++ ++ } else { /* host -->device */ ++ if (otg_usbdev_stat == 1) ++ return 0; ++ ++ host_to_device(priv); ++ } ++ ++ ++ return 0; ++ ++} ++ ++static int usbotg_thread(void *arg) ++{ ++#ifdef CONFIG_USB_AUTO_SWITCH ++ struct hisi_priv *priv = arg; ++ int reg; ++#endif ++ ++ ++ writel_relaxed(0x0, usb2_switch_base); ++#ifdef CONFIG_USB_AUTO_SWITCH ++ do { ++ reg = readl_relaxed(usb2_switch_base); ++ if (!(reg & (0x1 << 1))) ++ otg_run(priv); ++ ++ msleep(1000); ++ ++ } while (1); ++#endif ++ ++ return 0; ++} ++#endif ++ ++static int hisi_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct phy *phy; ++ struct hisi_priv *priv; ++ struct device_node *np = pdev->dev.of_node; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->misc_ctrl = of_iomap(np, 0); /* 0x12030000 */ ++ if (IS_ERR(priv->misc_ctrl)) ++ priv->misc_ctrl = NULL; ++ ++ priv->peri_ctrl = of_iomap(np, 1); /* 0x12010000 */ ++ if (IS_ERR(priv->peri_ctrl)) ++ priv->peri_ctrl = NULL; ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ platform_set_drvdata(pdev, phy); ++ phy_set_drvdata(phy, priv); ++ hisi_usb_phy_on(phy); ++ ++#if (!defined(CONFIG_ARCH_HI3536C)) ++ usb2_switch_base = (int *)USB2_SWITCH_BASE; ++ /* run hiotg */ ++ kusbotg_task = kthread_run(usbotg_thread, priv, "kusbotg"); ++ if (IS_ERR(kusbotg_task)) { ++ dev_err(dev, "can't start kusbotg\n"); ++ return -1; ++ } ++#endif ++ return 0; ++} ++ ++static int hisi_usb_phy_remove(struct platform_device *pdev) ++{ ++ int reg; ++ struct phy *phy = platform_get_drvdata(pdev); ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++#if (defined(CONFIG_ARCH_HI3536C)) ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg |= (USB_PHY0_SRST_REQ ++ | USB_PHY0_SRST_TREQ); ++ reg |= USB_PHY1_SRST_TREQ; ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(100); ++ ++ /* close clock */ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg &= ~(USB_PHY0_REFCLK_SEL ++ | USB_PHY0_REF_CKEN); ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(300); ++ ++ /* close clock */ ++ reg = readl(priv->peri_ctrl + USB2_CTRL); ++ reg &= ~(USB2_BUS_CKEN ++ | USB2_OHCI48M_CKEN ++ | USB2_OHCI12M_CKEN ++ | USB2_HST_PHY_CKEN ++ | USB2_UTMI0_CKEN); ++ reg &= ~USB2_UTMI1_CKEN; ++ writel(reg, priv->peri_ctrl + USB2_CTRL); ++ udelay(200); ++#else ++ reg = readl_relaxed(priv->peri_ctrl + USB2_PHY0_RST); ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ reg |= HI3519_USB2_PHY_REQ; ++ reg |= HI3519_USB2_PHY_PORT0_TREQ; ++ reg |= HI3519_USB2_CTRL_HUB_REQ; ++ reg &= ~HI3519_USB_CKEN; ++#endif ++ ++#if defined(CONFIG_ARCH_HI3516AV200) ++ reg |= HI3516AV200_USB2_PHY_REQ; ++ reg |= HI3516AV200_USB2_PHY_PORT0_TREQ; ++ reg |= HI3516AV200_USB2_CTRL_HUB_REQ; ++ reg &= ~HI3516AV200_USB_CKEN; ++#endif ++ ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++ reg |= HI3559_USB2_PHY_REQ; ++ reg |= HI3559_USB2_PHY_PORT0_TREQ; ++ reg |= HI3559_USB2_CTRL_HUB_REQ; ++ reg &= ~HI3559_USB_CKEN; ++#endif ++ writel_relaxed(reg, priv->peri_ctrl + USB2_PHY0_RST); ++ mdelay(100); ++#endif ++ return 0; ++} ++ ++static const struct of_device_id hisi_usb_phy_of_match[] = { ++ {.compatible = "hisilicon,hisi-usb-phy",}, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, hisi_usb_phy_of_match); ++ ++#ifdef CONFIG_PM_SLEEP ++ ++static int hisi_usb_phy_suspend(struct device *dev) ++{ ++ int reg; ++ struct phy *phy = dev_get_drvdata(dev); ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++#if (defined(CONFIG_ARCH_HI3536C)) ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg |= (USB_PHY0_SRST_REQ ++ | USB_PHY0_SRST_TREQ); ++ reg |= USB_PHY1_SRST_TREQ; ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(100); ++ ++ /* close clock */ ++ reg = readl(priv->peri_ctrl + REG_USB2_PHY0); ++ reg &= ~(USB_PHY0_REFCLK_SEL ++ | USB_PHY0_REF_CKEN); ++ writel(reg, priv->peri_ctrl + REG_USB2_PHY0); ++ udelay(300); ++ ++ /* close clock */ ++ reg = readl(priv->peri_ctrl + USB2_CTRL); ++ reg &= ~(USB2_BUS_CKEN ++ | USB2_OHCI48M_CKEN ++ | USB2_OHCI12M_CKEN ++ | USB2_HST_PHY_CKEN ++ | USB2_UTMI0_CKEN); ++ reg &= ~USB2_UTMI1_CKEN; ++ writel(reg, priv->peri_ctrl + USB2_CTRL); ++ udelay(200); ++#else ++ reg = readl_relaxed(priv->peri_ctrl + USB2_PHY0_RST); ++#if (defined CONFIG_ARCH_HI3519 || defined CONFIG_ARCH_HI3519V101) ++ reg |= HI3519_USB2_PHY_REQ; ++ reg |= HI3519_USB2_PHY_PORT0_TREQ; ++ reg |= HI3519_USB2_CTRL_HUB_REQ; ++ reg &= ~HI3519_USB_CKEN; ++#endif ++ ++#if defined(CONFIG_ARCH_HI3516AV200) ++ reg |= HI3516AV200_USB2_PHY_REQ; ++ reg |= HI3516AV200_USB2_PHY_PORT0_TREQ; ++ reg |= HI3516AV200_USB2_CTRL_HUB_REQ; ++ reg &= ~HI3516AV200_USB_CKEN; ++#endif ++ ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++ reg |= HI3559_USB2_PHY_REQ; ++ reg |= HI3559_USB2_PHY_PORT0_TREQ; ++ reg |= HI3559_USB2_CTRL_HUB_REQ; ++ reg &= ~HI3559_USB_CKEN; ++#endif ++ writel_relaxed(reg, priv->peri_ctrl + USB2_PHY0_RST); ++ mdelay(100); ++#endif ++ return 0; ++} ++ ++static int hisi_usb_phy_resume(struct device *dev) ++{ ++ struct phy *phy = dev_get_drvdata(dev); ++ ++ hisi_usb_phy_on(phy); ++ return 0; ++} ++ ++#endif /* CONFIG_PM_SLEEP */ ++ ++static SIMPLE_DEV_PM_OPS(hisi_usb2_pm_ops, hisi_usb_phy_suspend, ++ hisi_usb_phy_resume); ++ ++static struct platform_driver hisi_usb_phy_driver = { ++ .probe = hisi_usb_phy_probe, ++ .remove = hisi_usb_phy_remove, ++ .driver = { ++ .name = "hisi-usb-phy", ++ .pm = &hisi_usb2_pm_ops, ++ .of_match_table = hisi_usb_phy_of_match, ++ } ++}; ++module_platform_driver(hisi_usb_phy_driver); ++ ++MODULE_AUTHOR("Pengcheng Li <lpc.li@hisilicon.com>"); ++MODULE_DESCRIPTION("HISILICON USB PHY driver"); ++MODULE_ALIAS("platform:hisi-usb-phy"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-hisi-usb3.c b/drivers/phy/phy-hisi-usb3.c +new file mode 100644 +index 0000000..f8a6ff2 +--- /dev/null ++++ b/drivers/phy/phy-hisi-usb3.c +@@ -0,0 +1,313 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/phy/phy.h> ++#include <linux/platform_device.h> ++#include <linux/of_address.h> ++#include <mach/io.h> ++ ++#if defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3516AV200) ++#ifdef readl ++#undef readl ++#undef readl_relaxed ++#undef writel ++#undef writel_relaxed ++#define readl hi_readl ++#define readl_relaxed hi_readl_relaxed ++#define writel hi_writel ++#define writel_relaxed hi_writel_relaxed ++#endif /* readl */ ++#endif /* defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3516AV200)*/ ++ ++#define USB3_CTRL 0xb8 ++#define USB3_UTMI_CLKSEL BIT(13) ++#define USB3_UTMI_CLKEN BIT(12) ++#define USB3_PIPE_CLKEN BIT(11) ++#define USB3_SUSPEND_CLKEN BIT(10) ++#define USB3_REF_CLKEN BIT(9) ++#define USB3_BUS_CLKEN BIT(8) ++#define USB3_VCC_SRST_REQ BIT(0) ++ ++#define USB3_COMBPHY 0xac ++#define HI3519_COMBPHY_SRST_REQ BIT(0) ++ ++#define GTXTHRCFG 0xc108 ++#define GRXTHRCFG 0xc10c ++#define REG_GCTL 0xc110 ++#define U2RSTECN (0x1 << 16) ++ ++#define REG_GUSB2PHYCFG0 0xC200 ++#define BIT_UTMI_ULPI (0x1 << 4) ++#define BIT_UTMI_8_16 (0x1 << 3) ++ ++#define REG_GUSB3PIPECTL0 0xc2c0 ++#define PCS_SSP_SOFT_RESET (0x1 << 31) ++#define TX_MARGIN_MASK (0x7 << 3) ++#define TX_MARGIN_VAL (0x2 << 3) ++ ++#define USB2_PHY0_CTLL 0x80 ++ ++#define USB3_PHY 0x88 ++#define COMBO_PHY_TX_DEEMP_MASK (0x7 << 12) ++#define COMBO_PHY_TX_DEEMP_VAL (0x1 << 12) ++#ifdef CONFIG_USB3_DEVICE_GPIO_CTRL ++#define GPIO_MODE __io_address(0x12040000) ++#define GPIO1_DIR __io_address(0x12141400) ++#define GPIO1_IBE __io_address(0x12141408) ++#define GPIO1_IC __io_address(0x1214141c) ++#define GPIO1_IE __io_address(0x12141410) ++#define GPIO1_0_DIR (1<<0) ++#define GPIO1_0_IBE (1<<0) ++#define GPIO1_0_IC (1<<0) ++#define GPIO1_0_IE (1<<0) ++#endif ++struct hisi_priv { ++ void __iomem *base; ++ void __iomem *dwc3_ctrl; /* 0x10180000 */ ++ void __iomem *peri_ctrl; /* 0x12010000 */ ++#if (defined CONFIG_ARCH_HI3519V101 || defined CONFIG_ARCH_HI3559 || defined CONFIG_ARCH_HI3556 || defined CONFIG_ARCH_HI3516AV200) ++ void __iomem *misc_ctrl; /* 0x12030000 */ ++#endif ++}; ++ ++static void hisi_usb3_phy_eye(struct phy *phy) ++{ ++ int reg; ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++#if (defined CONFIG_ARCH_HI3519V101 || defined CONFIG_ARCH_HI3559 || defined CONFIG_ARCH_HI3556 || defined CONFIG_ARCH_HI3516AV200) ++ /* djustment LFPS AM */ ++ reg = readl(priv->dwc3_ctrl + REG_GUSB3PIPECTL0); ++ reg &= ~TX_MARGIN_MASK; ++ reg |= TX_MARGIN_VAL; ++ writel(reg, priv->dwc3_ctrl + REG_GUSB3PIPECTL0); ++ ++ /* de-emphasis -3.5dB */ ++ reg = readl(priv->misc_ctrl + USB3_PHY); ++ reg &= ~COMBO_PHY_TX_DEEMP_MASK; ++ reg |= COMBO_PHY_TX_DEEMP_VAL; ++ writel(reg, priv->misc_ctrl + USB3_PHY); ++#endif ++} ++ ++static int hisi_usb3_ctrl_phy_config(struct phy *phy) ++{ ++ int reg; ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++#ifdef CONFIG_USB3_DEVICE_GPIO_CTRL ++ /* GPIO mode */ ++ hi_writel(0x0, GPIO_MODE); ++ ++ reg = hi_readl(GPIO1_DIR); ++ reg &= ~GPIO1_0_DIR; ++ hi_writel(reg, GPIO1_DIR); ++ mdelay(20); ++ /* falling edge */ ++ reg = hi_readl(GPIO1_IBE); ++ reg &= ~GPIO1_0_IBE; ++ hi_writel(reg, GPIO1_IBE); ++ mdelay(20); ++ /* clear */ ++ reg = hi_readl(GPIO1_IC); ++ reg |= GPIO1_0_IC; ++ hi_writel(reg, GPIO1_IC); ++ mdelay(20); ++ /* unmask */ ++ reg = hi_readl(GPIO1_IE); ++ reg |= GPIO1_0_IE; ++ hi_writel(reg, GPIO1_IE); ++ mdelay(20); ++#endif ++ reg = readl(priv->dwc3_ctrl + REG_GUSB3PIPECTL0); ++ reg |= PCS_SSP_SOFT_RESET; ++ writel(reg, priv->dwc3_ctrl + REG_GUSB3PIPECTL0); ++ ++ /*step 3: USB2 PHY chose ulpi 8bit interface */ ++ reg = readl(priv->dwc3_ctrl + REG_GUSB2PHYCFG0); ++ reg &= ~BIT_UTMI_ULPI; ++ reg &= ~(BIT_UTMI_8_16); ++ writel(reg, priv->dwc3_ctrl + REG_GUSB2PHYCFG0); ++ mdelay(20); ++ ++ reg = readl(priv->dwc3_ctrl + REG_GCTL); ++ reg &= ~(0x3<<12); ++ reg |= (0x1<<12); /*[13:12] 01: Host; 10: Device; 11: OTG*/ ++ reg &= ~U2RSTECN; ++ writel(reg, priv->dwc3_ctrl + REG_GCTL); ++ mdelay(20); ++ ++ reg = readl(priv->dwc3_ctrl + REG_GUSB3PIPECTL0); ++ reg &= ~PCS_SSP_SOFT_RESET; ++ reg &= ~(1<<17); /* disable suspend */ ++ writel(reg, priv->dwc3_ctrl + REG_GUSB3PIPECTL0); ++ mdelay(100); ++ ++ writel(0x23100000, priv->dwc3_ctrl + GTXTHRCFG); ++ writel(0x23180000, priv->dwc3_ctrl + GRXTHRCFG); ++ mdelay(20); ++ ++ hisi_usb3_phy_eye(phy); ++ ++ return 0; ++} ++ ++static int hisi_usb3_phy_on(struct phy *phy) ++{ ++ int reg; ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB3_CTRL); ++ reg |= USB3_UTMI_CLKSEL; ++ reg |= USB3_UTMI_CLKEN; ++ reg |= USB3_PIPE_CLKEN; ++ reg |= USB3_SUSPEND_CLKEN; ++ reg |= USB3_REF_CLKEN; ++ reg |= USB3_BUS_CLKEN; ++ reg &= ~USB3_VCC_SRST_REQ; ++ writel_relaxed(reg, priv->peri_ctrl + USB3_CTRL); ++ mdelay(10); ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB3_COMBPHY); ++ reg &= ~HI3519_COMBPHY_SRST_REQ; ++ writel_relaxed(reg, priv->peri_ctrl + USB3_COMBPHY); ++ mdelay(10); ++ ++ hisi_usb3_ctrl_phy_config(phy); ++ ++ return 0; ++} ++ ++static int hisi_usb3_phy_power_off(struct device *dev) ++{ ++ int reg; ++ struct phy *phy = dev_get_drvdata(dev); ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB3_COMBPHY); ++ reg |= HI3519_COMBPHY_SRST_REQ; ++ writel_relaxed(reg, priv->peri_ctrl + USB3_COMBPHY); ++ mdelay(100); ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB3_CTRL); ++ reg |= USB3_VCC_SRST_REQ; ++ writel_relaxed(reg, priv->peri_ctrl + USB3_CTRL); ++ mdelay(10); ++ ++ return 0; ++} ++ ++static int hisi_usb3_phy_power_on(struct device *dev) ++{ ++ struct phy *phy = dev_get_drvdata(dev); ++ ++ hisi_usb3_phy_on(phy); ++ ++ return 0; ++} ++ ++static int hisi_usb3_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct phy *phy; ++ struct hisi_priv *priv; ++ struct device_node *np = pdev->dev.of_node; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->dwc3_ctrl = of_iomap(np, 0); ++ if (IS_ERR(priv->dwc3_ctrl)) ++ priv->dwc3_ctrl = NULL; ++ ++ priv->peri_ctrl = of_iomap(np, 1); ++ if (IS_ERR(priv->peri_ctrl)) ++ priv->peri_ctrl = NULL; ++ ++#if (defined CONFIG_ARCH_HI3519V101 || defined CONFIG_ARCH_HI3559 || defined CONFIG_ARCH_HI3556 || defined CONFIG_ARCH_HI3516AV200) ++ priv->misc_ctrl = of_iomap(np, 2); ++ if (IS_ERR(priv->misc_ctrl)) ++ priv->misc_ctrl = NULL; ++#endif ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, phy); ++ phy_set_drvdata(phy, priv); ++ hisi_usb3_phy_on(phy); ++ ++ return 0; ++} ++ ++static int hisi_usb3_phy_remove(struct platform_device *pdev) ++{ ++ int reg; ++ struct phy *phy = platform_get_drvdata(pdev); ++ struct hisi_priv *priv = phy_get_drvdata(phy); ++ ++ reg = readl_relaxed(priv->peri_ctrl + USB3_CTRL); ++ reg |= USB3_UTMI_CLKSEL; ++ reg &= ~USB3_UTMI_CLKEN; ++ reg &= ~USB3_PIPE_CLKEN; ++ reg &= ~USB3_SUSPEND_CLKEN; ++ reg &= ~USB3_REF_CLKEN; ++ reg &= ~USB3_BUS_CLKEN; ++ reg |= USB3_VCC_SRST_REQ; ++ writel_relaxed(reg, priv->peri_ctrl + USB3_CTRL); ++ mdelay(10); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops hisi_usb3_pmops = { ++ .suspend = hisi_usb3_phy_power_off, ++ .resume = hisi_usb3_phy_power_on, ++#if defined(CONFIG_PM_HIBERNATE) || defined(CONFIG_HISI_SNAPSHOT_BOOT) ++ .freeze = hisi_usb3_phy_power_off, ++ .thaw = hisi_usb3_phy_power_on, ++ .poweroff = hisi_usb3_phy_power_off, ++ .restore = hisi_usb3_phy_power_on, ++#endif ++}; ++ ++static const struct of_device_id hisi_usb3_phy_of_match[] = { ++ {.compatible = "hisilicon,hisi-usb3-phy",}, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, hisi_usb3_phy_of_match); ++ ++static struct platform_driver hisi_usb3_phy_driver = { ++ .probe = hisi_usb3_phy_probe, ++ .remove = hisi_usb3_phy_remove, ++ .driver = { ++ .name = "hisi-usb3-phy", ++ .of_match_table = hisi_usb3_phy_of_match, ++ .pm = &hisi_usb3_pmops, ++ } ++}; ++module_platform_driver(hisi_usb3_phy_driver); ++ ++MODULE_AUTHOR("Pengcheng Li <lpc.li@hisilicon.com>"); ++MODULE_DESCRIPTION("HISILICON USB PHY driver"); ++MODULE_ALIAS("platform:hisi-usb3-phy"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c +index f94467c..7f21f75 100644 +--- a/drivers/platform/x86/acerhdf.c ++++ b/drivers/platform/x86/acerhdf.c +@@ -330,7 +330,8 @@ static int acerhdf_bind(struct thermal_zone_device *thermal, + return 0; + + if (thermal_zone_bind_cooling_device(thermal, 0, cdev, +- THERMAL_NO_LIMIT, THERMAL_NO_LIMIT)) { ++ THERMAL_NO_LIMIT, THERMAL_NO_LIMIT, ++ THERMAL_WEIGHT_DEFAULT)) { + pr_err("error binding cooling dev\n"); + return -EINVAL; + } +diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c +index 62653f5..87a348c 100644 +--- a/drivers/power/power_supply_sysfs.c ++++ b/drivers/power/power_supply_sysfs.c +@@ -106,7 +106,10 @@ static ssize_t power_supply_show_property(struct device *dev, + else if (off >= POWER_SUPPLY_PROP_MODEL_NAME) + return sprintf(buf, "%s\n", value.strval); + +- return sprintf(buf, "%d\n", value.intval); ++ if (off == POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT) ++ return sprintf(buf, "%lld\n", value.int64val); ++ else ++ return sprintf(buf, "%d\n", value.intval); + } + + static ssize_t power_supply_store_property(struct device *dev, +@@ -197,6 +200,12 @@ static struct device_attribute power_supply_attrs[] = { + POWER_SUPPLY_ATTR(scope), + POWER_SUPPLY_ATTR(charge_term_current), + POWER_SUPPLY_ATTR(calibrate), ++ /* Local extensions */ ++ POWER_SUPPLY_ATTR(usb_hc), ++ POWER_SUPPLY_ATTR(usb_otg), ++ POWER_SUPPLY_ATTR(charge_enabled), ++ /* Local extensions of type int64_t */ ++ POWER_SUPPLY_ATTR(charge_counter_ext), + /* Properties of type `const char *' */ + POWER_SUPPLY_ATTR(model_name), + POWER_SUPPLY_ATTR(manufacturer), +diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig +index ef2dd2e..c6e48b5 100644 +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -110,6 +110,15 @@ config PWM_FSL_FTM + To compile this driver as a module, choose M here: the module + will be called pwm-fsl-ftm. + ++config PWM_HIBVT ++ tristate "HiSilicon BVT PWM support" ++ depends on ARCH_HISI || COMPILE_TEST ++ help ++ Generic PWM framework driver for HiSilicon BVT SoCs. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-hibvt. ++ + config PWM_IMX + tristate "i.MX PWM support" + depends on ARCH_MXC +diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile +index c458606..e90fbbe 100644 +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -8,6 +8,7 @@ obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o + obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o + obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o + obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o ++obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o + obj-$(CONFIG_PWM_IMX) += pwm-imx.o + obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o + obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o +diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c +new file mode 100644 +index 0000000..89e9fe5 +--- /dev/null ++++ b/drivers/pwm/pwm-hibvt.c +@@ -0,0 +1,246 @@ ++/* ++ * PWM Controller Driver for HiSilicon BVT SoCs ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/bitops.h> ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of_device.h> ++#include <linux/platform_device.h> ++#include <linux/pwm.h> ++#include <linux/reset.h> ++ ++#define PWM_CFG0_ADDR(x) (((x) * 0x20) + 0x0) ++#define PWM_CFG1_ADDR(x) (((x) * 0x20) + 0x4) ++#define PWM_CFG2_ADDR(x) (((x) * 0x20) + 0x8) ++#define PWM_CTRL_ADDR(x) (((x) * 0x20) + 0xC) ++ ++#define PWM_ENABLE_SHIFT 0 ++#define PWM_ENABLE_MASK BIT(0) ++ ++#define PWM_POLARITY_SHIFT 1 ++#define PWM_POLARITY_MASK BIT(1) ++ ++#define PWM_KEEP_SHIFT 2 ++#define PWM_KEEP_MASK BIT(2) ++ ++#define PWM_PERIOD_MASK GENMASK(31, 0) ++#define PWM_DUTY_MASK GENMASK(31, 0) ++ ++struct hibvt_pwm_chip { ++ struct pwm_chip chip; ++ struct clk *clk; ++ void __iomem *base; ++ struct reset_control *rstc; ++}; ++ ++struct hibvt_pwm_soc { ++ u32 num_pwms; ++}; ++ ++static const struct hibvt_pwm_soc pwm_soc[2] = { ++ { .num_pwms = 4 }, ++ { .num_pwms = 8 }, ++}; ++ ++static inline struct hibvt_pwm_chip *to_hibvt_pwm_chip(struct pwm_chip *chip) ++{ ++ return container_of(chip, struct hibvt_pwm_chip, chip); ++} ++ ++static void hibvt_pwm_set_bits(void __iomem *base, u32 offset, ++ u32 mask, u32 data) ++{ ++ void __iomem *address = base + offset; ++ u32 value; ++ ++ value = readl(address); ++ value &= ~mask; ++ value |= (data & mask); ++ writel(value, address); ++} ++ ++static int hibvt_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip); ++ ++ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(pwm->hwpwm), ++ PWM_ENABLE_MASK, 0x1); ++ ++ return 0; ++} ++ ++static void hibvt_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip); ++ ++ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(pwm->hwpwm), ++ PWM_ENABLE_MASK, 0x0); ++} ++ ++static int hibvt_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ++ int duty_cycle_ns, int period_ns) ++{ ++ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip); ++ u32 freq, period, duty; ++ ++ freq = div_u64(clk_get_rate(hi_pwm_chip->clk), 1000000); ++ ++ period = div_u64(freq * period_ns, 1000); ++ duty = div_u64(period * duty_cycle_ns, period_ns); ++ ++ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CFG0_ADDR(pwm->hwpwm), ++ PWM_PERIOD_MASK, period); ++ ++ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CFG1_ADDR(pwm->hwpwm), ++ PWM_DUTY_MASK, duty); ++ ++ return 0; ++} ++ ++static int hibvt_pwm_set_polarity(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ enum pwm_polarity polarity) ++{ ++ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip); ++ ++ if (polarity == PWM_POLARITY_INVERSED) ++ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(pwm->hwpwm), ++ PWM_POLARITY_MASK, (0x1 << PWM_POLARITY_SHIFT)); ++ else ++ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(pwm->hwpwm), ++ PWM_POLARITY_MASK, (0x0 << PWM_POLARITY_SHIFT)); ++ ++ return 0; ++} ++ ++static struct pwm_ops hibvt_pwm_ops = { ++ .enable = hibvt_pwm_enable, ++ .disable = hibvt_pwm_disable, ++ .config = hibvt_pwm_config, ++ .set_polarity = hibvt_pwm_set_polarity, ++ ++ .owner = THIS_MODULE, ++}; ++ ++static const struct of_device_id hibvt_pwm_of_match[] = { ++ { .compatible = "hisilicon,hi3516cv300-pwm", .data = &pwm_soc[0] }, ++ { .compatible = "hisilicon,hi3519v100-pwm", .data = &pwm_soc[1] }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, hibvt_pwm_of_match); ++ ++static int hibvt_pwm_probe(struct platform_device *pdev) ++{ ++ const struct hibvt_pwm_soc *soc; ++ const struct of_device_id *of_id = ++ of_match_device(hibvt_pwm_of_match, &pdev->dev); ++ struct hibvt_pwm_chip *pwm_chip; ++ struct resource *res; ++ int ret; ++ int i; ++ ++ if (!of_id) ++ return -ENODEV; ++ ++ pwm_chip = devm_kzalloc(&pdev->dev, sizeof(*pwm_chip), GFP_KERNEL); ++ if (pwm_chip == NULL) ++ return -ENOMEM; ++ ++ pwm_chip->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(pwm_chip->clk)) { ++ dev_err(&pdev->dev, "getting clock failed with %ld\n", ++ PTR_ERR(pwm_chip->clk)); ++ return PTR_ERR(pwm_chip->clk); ++ } ++ ++ soc = of_id->data; ++ ++ pwm_chip->chip.ops = &hibvt_pwm_ops; ++ pwm_chip->chip.dev = &pdev->dev; ++ pwm_chip->chip.base = -1; ++ pwm_chip->chip.npwm = soc->num_pwms; ++ pwm_chip->chip.of_xlate = of_pwm_xlate_with_flags; ++ pwm_chip->chip.of_pwm_n_cells = 3; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ pwm_chip->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(pwm_chip->base)) ++ return PTR_ERR(pwm_chip->base); ++ ++ ret = clk_prepare_enable(pwm_chip->clk); ++ if (ret < 0) ++ return ret; ++ ++ pwm_chip->rstc = devm_reset_control_get(&pdev->dev, NULL); ++ if (IS_ERR(pwm_chip->rstc)) { ++ clk_disable_unprepare(pwm_chip->clk); ++ return PTR_ERR(pwm_chip->rstc); ++ } ++ ++ reset_control_assert(pwm_chip->rstc); ++ msleep(30); ++ reset_control_deassert(pwm_chip->rstc); ++ ++ ret = pwmchip_add(&pwm_chip->chip); ++ if (ret < 0) { ++ clk_disable_unprepare(pwm_chip->clk); ++ return ret; ++ } ++ ++ for (i = 0; i < pwm_chip->chip.npwm; i++) { ++ hibvt_pwm_set_bits(pwm_chip->base, PWM_CTRL_ADDR(i), ++ PWM_KEEP_MASK, (0x1 << PWM_KEEP_SHIFT)); ++ } ++ ++ platform_set_drvdata(pdev, pwm_chip); ++ ++ return 0; ++} ++ ++static int hibvt_pwm_remove(struct platform_device *pdev) ++{ ++ struct hibvt_pwm_chip *pwm_chip; ++ ++ pwm_chip = platform_get_drvdata(pdev); ++ ++ reset_control_assert(pwm_chip->rstc); ++ msleep(30); ++ reset_control_deassert(pwm_chip->rstc); ++ ++ clk_disable_unprepare(pwm_chip->clk); ++ ++ return pwmchip_remove(&pwm_chip->chip); ++} ++ ++static struct platform_driver hibvt_pwm_driver = { ++ .driver = { ++ .name = "hibvt-pwm", ++ .owner = THIS_MODULE, ++ .of_match_table = hibvt_pwm_of_match, ++ }, ++ .probe = hibvt_pwm_probe, ++ .remove = hibvt_pwm_remove, ++}; ++module_platform_driver(hibvt_pwm_driver); ++ ++MODULE_AUTHOR("Jian Yuan"); ++MODULE_DESCRIPTION("HiSilicon BVT SoCs PWM driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c +index bc3d80f..9749ec1 100644 +--- a/drivers/regulator/core.c ++++ b/drivers/regulator/core.c +@@ -1715,6 +1715,8 @@ static void regulator_ena_gpio_free(struct regulator_dev *rdev) + gpiod_put(pin->gpiod); + list_del(&pin->list); + kfree(pin); ++ rdev->ena_pin = NULL; ++ return; + } else { + pin->request_count--; + } +diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig +index 6dd12dd..7c88521 100644 +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -701,6 +701,14 @@ endif # SPI_MASTER + + comment "Platform RTC drivers" + ++config RTC_DRV_HIBVT ++ tristate "HiSilicon BVT RTC support" ++ help ++ Generic RTC framework driver for HiSilicon BVT SoCs. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called rtc-hibvt. ++ + # this 'CMOS' RTC driver is arch dependent because <asm-generic/rtc.h> + # requires <asm/mc146818rtc.h> defining CMOS_READ/CMOS_WRITE, and a + # global rtc_lock ... it's not yet just another platform_device. +diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile +index b188323..632f526 100644 +--- a/drivers/rtc/Makefile ++++ b/drivers/rtc/Makefile +@@ -149,3 +149,4 @@ obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o + obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o + obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o + obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o ++obj-$(CONFIG_RTC_DRV_HIBVT) += rtc-hibvt.o +diff --git a/drivers/rtc/rtc-hibvt.c b/drivers/rtc/rtc-hibvt.c +new file mode 100644 +index 0000000..28d0bae +--- /dev/null ++++ b/drivers/rtc/rtc-hibvt.c +@@ -0,0 +1,581 @@ ++/* ++ * RTC driver for Hisilicon BVT ++ * Copyright (C) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * 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/bcd.h> ++#include <linux/bitops.h> ++#include <linux/log2.h> ++#include <linux/interrupt.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/rtc.h> ++#include <linux/version.h> ++#include <linux/io.h> ++#include <linux/delay.h> ++ ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/platform_device.h> ++ ++union u_spi_rw { ++ struct { ++ unsigned int spi_wdata : 8; /* [7:0] */ ++ unsigned int spi_rdata : 8; /* [15:8] */ ++ unsigned int spi_addr : 7; /* [22:16] */ ++ unsigned int spi_rw : 1; /* [23] */ ++ unsigned int spi_start : 1; /* [24] */ ++ unsigned int reserved : 6; /* [30:25] */ ++ unsigned int spi_busy : 1; /* [31] */ ++ } bits; ++ unsigned int u32; ++}; ++ ++#define SPI_CLK_DIV (0x000) ++#define SPI_RW (0x004) ++ ++#define SPI_WRITE (0) ++#define SPI_READ (1) ++ ++/* RTC REG */ ++#define RTC_10MS_COUN 0x00 ++#define RTC_S_COUNT 0x01 ++#define RTC_M_COUNT 0x02 ++#define RTC_H_COUNT 0x03 ++#define RTC_D_COUNT_L 0x04 ++#define RTC_D_COUNT_H 0x05 ++ ++#define RTC_MR_10MS 0x06 ++#define RTC_MR_S 0x07 ++#define RTC_MR_M 0x08 ++#define RTC_MR_H 0x09 ++#define RTC_MR_D_L 0x0A ++#define RTC_MR_D_H 0x0B ++ ++#define RTC_LR_10MS 0x0C ++#define RTC_LR_S 0x0D ++#define RTC_LR_M 0x0E ++#define RTC_LR_H 0x0F ++#define RTC_LR_D_L 0x10 ++#define RTC_LR_D_H 0x11 ++ ++#define RTC_LORD 0x12 ++ ++#define RTC_IMSC 0x13 ++#define RTC_INT_CLR 0x14 ++#define RTC_INT 0x15 ++#define RTC_INT_RAW 0x16 ++ ++#define RTC_CLK 0x17 ++#define RTC_POR_N 0x18 ++#define RTC_SAR_CTRL 0x1A ++#define RTC_CLK_CFG 0x1B ++ ++#define RTC_FREQ_H 0x51 ++#define RTC_FREQ_L 0x52 ++ ++#define FREQ_H_DEFAULT 0x8 ++#define FREQ_L_DEFAULT 0x1B ++ ++#define LV_CTL_DEFAULT 0x01 ++#define CLK_DIV_DEFAULT 0x4 ++#define INT_RST_DEFAULT 0x0 ++#define INT_MSK_DEFAULT 0x4 ++ ++#define AIE_INT_MASK BIT(0) ++#define LV_INT_MASK BIT(1) ++#define REG_LOAD_STAT BIT(0) ++#define REG_LOCK_STAT BIT(1) ++#define REG_LOCK_BYPASS BIT(2) ++ ++#define RETRY_CNT 500 ++ ++#define DATE_TO_SEC(d, h, m, s) (s + m*60 + h*60*60 + d*24*60*60) ++#define SEC_TO_DAY(s) (s/(60*60*24)) ++ ++struct hibvt_rtc { ++ struct rtc_device *rtc_dev; ++ void __iomem *regs; ++ int rtc_irq; ++}; ++ ++static int hibvt_spi_write(void *spi_reg, unsigned char reg, ++ unsigned char val) ++{ ++ union u_spi_rw w_data, r_data; ++ int cnt = RETRY_CNT; ++ ++ r_data.u32 = 0; ++ w_data.u32 = 0; ++ ++ w_data.bits.spi_wdata = val; ++ w_data.bits.spi_addr = reg; ++ w_data.bits.spi_rw = SPI_WRITE; ++ w_data.bits.spi_start = 0x1; ++ ++ writel(w_data.u32, (spi_reg+SPI_RW)); ++ ++ do ++ r_data.u32 = readl(spi_reg+SPI_RW); ++ while (r_data.bits.spi_busy && (--cnt)); ++ ++ if (r_data.bits.spi_busy) ++ return -EIO; ++ ++ return 0; ++} ++ ++ ++static int hibvt_spi_rtc_write(void *spi_reg, unsigned char reg, ++ unsigned char val) ++{ ++ return hibvt_spi_write(spi_reg, reg, val); ++} ++ ++static int hibvt_spi_read(void *spi_reg, unsigned char reg, ++ unsigned char *val) ++{ ++ union u_spi_rw w_data, r_data; ++ int cnt = RETRY_CNT; ++ ++ r_data.u32 = 0; ++ w_data.u32 = 0; ++ w_data.bits.spi_addr = reg; ++ w_data.bits.spi_rw = SPI_READ; ++ w_data.bits.spi_start = 0x1; ++ ++ writel(w_data.u32, (spi_reg+SPI_RW)); ++ ++ do ++ r_data.u32 = readl(spi_reg+SPI_RW); ++ while (r_data.bits.spi_busy && (--cnt)); ++ ++ if (r_data.bits.spi_busy) ++ return -EIO; ++ ++ *val = r_data.bits.spi_rdata; ++ ++ return 0; ++} ++ ++static int hibvt_spi_rtc_read(void *spi_reg, unsigned char reg, ++ unsigned char *val) ++{ ++ return hibvt_spi_read(spi_reg, reg, val); ++} ++ ++static int hibvt_rtc_read_time(struct device *dev, struct rtc_time *time) ++{ ++ struct hibvt_rtc *rtc = dev_get_drvdata(dev); ++ unsigned char dayl, dayh; ++ unsigned char second, minute, hour; ++ unsigned long seconds = 0; ++ unsigned int day; ++ unsigned char raw_value; ++ int cnt = RETRY_CNT; ++ int ret = 0; ++ ++ ret = hibvt_spi_rtc_read(rtc->regs, RTC_INT_RAW, &raw_value); ++ if (ret) { ++ dev_err(dev, "IO err.\n"); ++ return ret; ++ } ++ ++ if (raw_value & LV_INT_MASK) { ++ dev_err(dev, ++ "low voltage detected, date/time is not reliable.\n"); ++ hibvt_spi_write(rtc->regs, RTC_INT_CLR, 1); ++ return -EINVAL; ++ } ++ ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_LORD, &raw_value); ++ if (raw_value & REG_LOCK_BYPASS) ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_LORD, ++ (~(REG_LOCK_BYPASS)) & raw_value); ++ ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_LORD, &raw_value); ++ /* lock the time */ ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_LORD, ++ (REG_LOCK_STAT) | raw_value); ++ /* wait rtc load flag */ ++ do { ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_LORD, &raw_value); ++ msleep(20); ++ } while ((ret || (raw_value & REG_LOCK_STAT)) && (--cnt)); ++ ++ if (!ret && (raw_value & REG_LOCK_STAT)) ++ return -EBUSY; ++ ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_S_COUNT, &second); ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_M_COUNT, &minute); ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_H_COUNT, &hour); ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_D_COUNT_L, &dayl); ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_D_COUNT_H, &dayh); ++ ++ if (ret) { ++ dev_err(dev, "IO err.\n"); ++ return ret; ++ } ++ ++ day = (dayl | (dayh << 8)); ++ seconds = DATE_TO_SEC(day, hour, minute, second); ++ ++ rtc_time_to_tm(seconds, time); ++ ++ return rtc_valid_tm(time); ++} ++ ++static int hibvt_rtc_set_time(struct device *dev, struct rtc_time *time) ++{ ++ struct hibvt_rtc *rtc = dev_get_drvdata(dev); ++ unsigned char ret = 0; ++ unsigned int days; ++ unsigned long seconds = 0; ++ unsigned int cnt = RETRY_CNT; ++ unsigned char raw_value = 0; ++ ++ ret = rtc_tm_to_time(time, &seconds); ++ if (ret) ++ return ret; ++ days = SEC_TO_DAY(seconds); ++ ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_LR_10MS, 0); ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_LR_S, time->tm_sec); ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_LR_M, time->tm_min); ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_LR_H, time->tm_hour); ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_LR_D_L, (days & 0xFF)); ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_LR_D_H, (days >> 8)); ++ ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_LORD, ++ (raw_value | REG_LOAD_STAT)); ++ /* wait rtc load flag */ ++ do { ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_LORD, &raw_value); ++ msleep(20); ++ } while ((ret || (raw_value & REG_LOAD_STAT)) && (--cnt)); ++ ++ if (!ret && (raw_value & REG_LOAD_STAT)) ++ return -EBUSY; ++ ++ if (ret) ++ dev_err(dev, "IO err.\n"); ++ ++ return ret; ++} ++ ++static int hibvt_rtc_read_alarm(struct device *dev, ++ struct rtc_wkalrm *alrm) ++{ ++ struct hibvt_rtc *rtc = dev_get_drvdata(dev); ++ unsigned char dayl, dayh; ++ unsigned char second, minute, hour; ++ unsigned long seconds = 0; ++ unsigned int day; ++ unsigned char int_state = 0; ++ int ret = 0; ++ ++ memset(alrm, 0, sizeof(struct rtc_wkalrm)); ++ ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_MR_S, &second); ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_MR_M, &minute); ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_MR_H, &hour); ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_MR_D_L, &dayl); ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_MR_D_H, &dayh); ++ ++ day = (unsigned int)(dayl | (dayh << 8)); ++ seconds = DATE_TO_SEC(day, hour, minute, second); ++ ++ rtc_time_to_tm(seconds, &alrm->time); ++ ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_IMSC, &int_state); ++ if (ret) { ++ dev_err(dev, "IO err.\n"); ++ return ret; ++ } ++ ++ alrm->enabled = !!(int_state & AIE_INT_MASK); ++ alrm->pending = alrm->enabled; ++ ++ return 0; ++} ++ ++static int hibvt_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) ++{ ++ struct hibvt_rtc *rtc = dev_get_drvdata(dev); ++ unsigned int days; ++ unsigned long seconds = 0; ++ unsigned char val = 0; ++ int ret = 0; ++ ++ rtc_tm_to_time(&alrm->time, &seconds); ++ ++ days = SEC_TO_DAY(seconds); ++ ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_MR_10MS, 0); ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_MR_S, alrm->time.tm_sec); ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_MR_M, alrm->time.tm_min); ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_MR_H, alrm->time.tm_hour); ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_MR_D_L, (days & 0xFF)); ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_MR_D_H, (days >> 8)); ++ ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_IMSC, &val); ++ if (alrm->enabled) ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_IMSC, ++ val | AIE_INT_MASK); ++ else ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_IMSC, ++ val & ~AIE_INT_MASK); ++ ++ if (ret) { ++ dev_err(dev, "IO err.\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int hibvt_rtc_alarm_irq_enable(struct device *dev, ++ unsigned int enabled) ++{ ++ struct hibvt_rtc *rtc = dev_get_drvdata(dev); ++ unsigned char val = 0; ++ int ret = 0; ++ ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_IMSC, &val); ++ if (enabled) ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_IMSC, ++ val | AIE_INT_MASK); ++ else ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_IMSC, ++ val & ~AIE_INT_MASK); ++ ++ if (ret) { ++ dev_err(dev, "IO err.\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++ ++/* ++ * interrupt function ++ * do nothing. left for future ++ */ ++static irqreturn_t hibvt_rtc_alm_interrupt(int irq, void *data) ++{ ++ struct hibvt_rtc *rtc = (struct hibvt_rtc *)data; ++ unsigned char val = 0; ++ int ret = 0; ++ ++ ret |= hibvt_spi_read(rtc->regs, RTC_INT, &val); ++ ret |= hibvt_spi_write(rtc->regs, RTC_INT_CLR, AIE_INT_MASK); ++ ++ if (ret) { ++ dev_err(&rtc->rtc_dev->dev, "IO err.\n"); ++ return ret; ++ } ++ ++ if (val & AIE_INT_MASK) ++ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); ++ ++ return IRQ_HANDLED; ++} ++ ++#define FREQ_MAX_VAL 3277000 ++#define FREQ_MIN_VAL 3276000 ++ ++static int hibvt_rtc_ioctl(struct device *dev, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct hibvt_rtc *rtc = dev_get_drvdata(dev); ++ int ret = 0; ++ ++ switch (cmd) { ++ case RTC_PLL_SET: ++ { ++ char freq_l, freq_h; ++ struct rtc_pll_info pll_info; ++ ++ if (copy_from_user(&pll_info, (struct rtc_pll_info *)arg, ++ sizeof(struct rtc_pll_info))) ++ return -EFAULT; ++ ++ /* freq = 32700 + (freq /3052)*100 */ ++ if (pll_info.pll_value > FREQ_MAX_VAL ++ || pll_info.pll_value < FREQ_MIN_VAL) ++ return -EINVAL; ++ ++ pll_info.pll_value = (pll_info.pll_value - 3270000) ++ * 3052 / 10000; ++ ++ freq_l = (char)(pll_info.pll_value & 0xff); ++ freq_h = (char)((pll_info.pll_value >> 8) & 0xf); ++ ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_FREQ_H, freq_h); ++ ret |= hibvt_spi_rtc_write(rtc->regs, RTC_FREQ_L, freq_l); ++ ++ if (ret) { ++ dev_err(dev, "IO err.\n"); ++ return ret; ++ } ++ ++ return 0; ++ } ++ case RTC_PLL_GET: ++ { ++ char freq_l, freq_h; ++ struct rtc_pll_info pll_info; ++ ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_FREQ_H, &freq_h); ++ ret |= hibvt_spi_rtc_read(rtc->regs, RTC_FREQ_L, &freq_l); ++ ++ if (ret) { ++ dev_err(dev, "IO err.\n"); ++ return ret; ++ } ++ ++ pll_info.pll_value = ((freq_h & 0xf) << 8) + freq_l; ++ pll_info.pll_value = 3270000 ++ + (pll_info.pll_value * 10000) / 3052; ++ ++ pll_info.pll_max = FREQ_MAX_VAL; ++ pll_info.pll_min = FREQ_MIN_VAL; ++ ++ if (copy_to_user((void __user *)arg, ++ &pll_info, sizeof(struct rtc_pll_info))) ++ return -EFAULT; ++ ++ return 0; ++ } ++ default: ++ return -ENOIOCTLCMD; ++ } ++} ++ ++static const struct rtc_class_ops hibvt_rtc_ops = { ++ .read_time = hibvt_rtc_read_time, ++ .set_time = hibvt_rtc_set_time, ++ .read_alarm = hibvt_rtc_read_alarm, ++ .set_alarm = hibvt_rtc_set_alarm, ++ .alarm_irq_enable = hibvt_rtc_alarm_irq_enable, ++ .ioctl = hibvt_rtc_ioctl, ++}; ++ ++static int hibvt_rtc_init(struct hibvt_rtc *rtc) ++{ ++ void *spi_reg = rtc->regs; ++ int ret = 0; ++ unsigned char val = 0; ++ /* ++ * clk div value = (apb_clk/spi_clk)/2-1, ++ * apb clk = 100MHz, spi_clk = 10MHz,so value= 0x4 ++ */ ++ writel(CLK_DIV_DEFAULT, (spi_reg+SPI_CLK_DIV)); ++ ++ ret |= hibvt_spi_rtc_write(spi_reg, RTC_IMSC, INT_MSK_DEFAULT); ++ ret |= hibvt_spi_rtc_write(spi_reg, RTC_SAR_CTRL, LV_CTL_DEFAULT); ++ ++ ++ ret |= hibvt_spi_rtc_write(spi_reg, RTC_CLK_CFG, 0x01); ++ ++ /* default FREQ COEF */ ++ ret |= hibvt_spi_rtc_write(spi_reg, RTC_FREQ_H, FREQ_H_DEFAULT); ++ ret |= hibvt_spi_rtc_write(spi_reg, RTC_FREQ_L, FREQ_L_DEFAULT); ++ ++ ret |= hibvt_spi_rtc_read(spi_reg, RTC_INT_RAW, &val); ++ //ret |= hibvt_spi_rtc_read(spi_reg, RTC_CLK_CFG, &val2); ++ if (ret) { ++ dev_err(&rtc->rtc_dev->dev, "IO err.\n"); ++ return ret; ++ } ++ ++ if (val & LV_INT_MASK) { ++ dev_err(&rtc->rtc_dev->dev, ++ "low voltage detected, date/time is not reliable.\n"); ++ hibvt_spi_write(rtc->regs, RTC_INT_CLR, 1); ++ } ++ ++ return ret; ++} ++ ++static int hibvt_rtc_probe(struct platform_device *pdev) ++{ ++ struct resource *mem; ++ struct hibvt_rtc *rtc; ++ int ret; ++ ++ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); ++ if (!rtc) ++ return -ENOMEM; ++ ++ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ rtc->regs = devm_ioremap_resource(&pdev->dev, mem); ++ if (IS_ERR((const void *)rtc->regs)) { ++ dev_err(&pdev->dev, "could not map I/O memory\n"); ++ return PTR_ERR((const void *)rtc->regs); ++ } ++ ++ rtc->rtc_irq = platform_get_irq(pdev, 0); ++ ret = devm_request_irq(&pdev->dev, rtc->rtc_irq, ++ hibvt_rtc_alm_interrupt, 0, pdev->name, rtc); ++ if (ret) { ++ dev_err(&pdev->dev, "could not request irq %d\n", rtc->rtc_irq); ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, rtc); ++ rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name, ++ &hibvt_rtc_ops, THIS_MODULE); ++ if (IS_ERR(rtc->rtc_dev)) { ++ dev_err(&pdev->dev, "could not register rtc device\n"); ++ return PTR_ERR(rtc->rtc_dev); ++ } ++ ++ if (hibvt_rtc_init(rtc)) { ++ dev_err(&pdev->dev, "hibvt_rtc_init failed.\n"); ++ return -EIO; ++ } ++ ++ dev_info(&pdev->dev, "RTC driver for hibvt enabled\n"); ++ ++ return 0; ++} ++ ++static int hibvt_rtc_remove(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++static const struct of_device_id hibvt_rtc_match[] = { ++ { .compatible = "hisilicon,hi35xx-rtc" }, ++ {}, ++}; ++ ++static struct platform_driver hibvt_rtc_driver = { ++ .probe = hibvt_rtc_probe, ++ .remove = hibvt_rtc_remove, ++ .driver = { .name = "hibvt_rtc", ++ .of_match_table = hibvt_rtc_match, ++ }, ++}; ++ ++module_platform_driver(hibvt_rtc_driver); ++ ++#define OSDRV_MODULE_VERSION_STRING "HISI_rtc @HiMPP" ++ ++MODULE_AUTHOR("Hisilicon"); ++MODULE_DESCRIPTION("Hisilicon RTC driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_VERSION("HI_VERSION=" OSDRV_MODULE_VERSION_STRING); ++ +diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c +index b1ab509..d0bc88d 100644 +--- a/drivers/scsi/scsi_lib.c ++++ b/drivers/scsi/scsi_lib.c +@@ -1491,7 +1491,7 @@ static inline int scsi_host_queue_ready(struct request_queue *q, + if (scsi_host_in_recovery(shost)) + return 0; + +- busy = atomic_inc_return(&shost->host_busy) - 1; ++ busy = atomic_read(&shost->host_busy); + if (atomic_read(&shost->host_blocked) > 0) { + if (busy) + goto starved; +@@ -1500,7 +1500,7 @@ static inline int scsi_host_queue_ready(struct request_queue *q, + * unblock after host_blocked iterates to zero + */ + if (atomic_dec_return(&shost->host_blocked) > 0) +- goto out_dec; ++ goto out; + + SCSI_LOG_MLQUEUE(3, + shost_printk(KERN_INFO, shost, +@@ -1520,6 +1520,7 @@ static inline int scsi_host_queue_ready(struct request_queue *q, + spin_unlock_irq(shost->host_lock); + } + ++ atomic_inc(&shost->host_busy); + return 1; + + starved: +@@ -1527,8 +1528,7 @@ starved: + if (list_empty(&sdev->starved_entry)) + list_add_tail(&sdev->starved_entry, &shost->starved_list); + spin_unlock_irq(shost->host_lock); +-out_dec: +- atomic_dec(&shost->host_busy); ++out: + return 0; + } + +diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c +index 07b2ea1..b34be2a 100644 +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -592,6 +592,9 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) + sg_io_hdr_t *hp; + unsigned char cmnd[SG_MAX_CDB_SIZE]; + ++ if (unlikely(segment_eq(get_fs(), KERNEL_DS))) ++ return -EINVAL; ++ + if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + return -ENXIO; + SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, +@@ -787,8 +790,14 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, + return k; /* probably out of space --> ENOMEM */ + } + if (atomic_read(&sdp->detaching)) { +- if (srp->bio) ++ if (srp->bio) { ++ if (srp->rq->cmd != srp->rq->__cmd) ++ kfree(srp->rq->cmd); ++ + blk_end_request_all(srp->rq, -EIO); ++ srp->rq = NULL; ++ } ++ + sg_finish_rem_req(srp); + return -ENODEV; + } +@@ -1013,6 +1022,8 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) + result = get_user(val, ip); + if (result) + return result; ++ if (val > SG_MAX_CDB_SIZE) ++ return -ENOMEM; + sfp->next_cmd_len = (val > 0) ? val : 0; + return 0; + case SG_GET_VERSION_NUM: +@@ -1785,6 +1796,9 @@ sg_start_req(Sg_request *srp, unsigned char *cmd) + md->from_user = 0; + } + ++ if (unlikely(iov_count > UIO_MAXIOV)) ++ return -EINVAL; ++ + if (iov_count) { + int len, size = sizeof(struct sg_iovec) * iov_count; + struct iovec *iov; +diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c +index 11e1834..f295a8a 100644 +--- a/drivers/spi/spi-pl022.c ++++ b/drivers/spi/spi-pl022.c +@@ -43,6 +43,7 @@ + #include <linux/gpio.h> + #include <linux/of_gpio.h> + #include <linux/pinctrl/consumer.h> ++#include <linux/of_address.h> + + /* + * This macro is used to define some register default values. +@@ -137,6 +138,13 @@ + #define SSP_CR1_MASK_FBCLKDEL_ST (0x7UL << 13) + + /* ++ * The Hisilicon version of this block adds some bits ++ * in SSP_CR1 ++ */ ++#define SSP_CR1_MASK_BIGEND_HISI (0x1UL << 4) ++#define SSP_CR1_MASK_ALTASENS_HISI (0x1UL << 6) ++ ++/* + * SSP Status Register - SSP_SR + */ + #define SSP_SR_MASK_TFE (0x1UL << 0) /* Transmit FIFO empty */ +@@ -291,6 +299,8 @@ + + #define SPI_POLLING_TIMEOUT 1000 + ++#define PL022_IDS_INDEX_HISI 4 ++ + /* + * The type of reading going on on this chip + */ +@@ -332,6 +342,13 @@ struct vendor_data { + bool internal_cs_ctrl; + }; + ++struct cs_data { ++ struct resource res; ++ void __iomem *virt_addr; ++ unsigned int cs_sb; ++ unsigned int cs_mask_bit; ++}; ++ + /** + * struct pl022 - This is the private SSP driver data structure + * @adev: AMBA device model hookup +@@ -405,6 +422,7 @@ struct pl022 { + #endif + int cur_cs; + int *chipselects; ++ struct cs_data *cs_data; + }; + + /** +@@ -461,13 +479,37 @@ static void null_cs_control(u32 command) + static void internal_cs_control(struct pl022 *pl022, u32 command) + { + u32 tmp; ++ struct amba_device *adev = pl022->adev; ++ struct amba_driver *adrv = container_of(adev->dev.driver, ++ struct amba_driver, drv); ++ ++ if (pl022->vendor->extended_cr && (adev->periphid == ++ adrv->id_table[PL022_IDS_INDEX_HISI].id)) { ++ if (pl022->cs_data) { ++ tmp = readl(pl022->cs_data->virt_addr); ++ tmp &= ~(pl022->cs_data->cs_mask_bit); ++ tmp |= ((u32)pl022->cur_cs) << pl022->cs_data->cs_sb; ++ writel(tmp, pl022->cs_data->virt_addr); ++ } + +- tmp = readw(SSP_CSR(pl022->virtbase)); +- if (command == SSP_CHIP_SELECT) +- tmp &= ~BIT(pl022->cur_cs); +- else +- tmp |= BIT(pl022->cur_cs); +- writew(tmp, SSP_CSR(pl022->virtbase)); ++ if (command == SSP_CHIP_SELECT) ++ /* Enable SSP */ ++ writew((readw(SSP_CR1(pl022->virtbase)) | ++ SSP_CR1_MASK_SSE), ++ SSP_CR1(pl022->virtbase)); ++ else ++ /* disable SSP */ ++ writew((readw(SSP_CR1(pl022->virtbase)) & ++ (~SSP_CR1_MASK_SSE)), ++ SSP_CR1(pl022->virtbase)); ++ } else { ++ tmp = readw(SSP_CSR(pl022->virtbase)); ++ if (command == SSP_CHIP_SELECT) ++ tmp &= ~BIT((u32)pl022->cur_cs); ++ else ++ tmp |= BIT((u32)pl022->cur_cs); ++ writew(tmp, SSP_CSR(pl022->virtbase)); ++ } + } + + static void pl022_cs_control(struct pl022 *pl022, u32 command) +@@ -568,8 +610,12 @@ static int flush(struct pl022 *pl022) + static void restore_state(struct pl022 *pl022) + { + struct chip_data *chip = pl022->cur_chip; ++ struct amba_device *adev = pl022->adev; ++ struct amba_driver *adrv = container_of(adev->dev.driver, ++ struct amba_driver, drv); + +- if (pl022->vendor->extended_cr) ++ if (pl022->vendor->extended_cr && (adev->periphid != ++ adrv->id_table[PL022_IDS_INDEX_HISI].id)) + writel(chip->cr0, SSP_CR0(pl022->virtbase)); + else + writew(chip->cr0, SSP_CR0(pl022->virtbase)); +@@ -642,6 +688,13 @@ static void restore_state(struct pl022 *pl022) + GEN_MASK_BITS(SSP_FEEDBACK_CLK_DELAY_NONE, SSP_CR1_MASK_FBCLKDEL_ST, 13) \ + ) + ++/* Hisilicon versions extend this register to use all 16 bits */ ++#define DEFAULT_SSP_REG_CR1_HISI ( \ ++ DEFAULT_SSP_REG_CR1 | \ ++ GEN_MASK_BITS(SSP_RX_MSB, SSP_CR1_MASK_BIGEND_HISI, 4) | \ ++ GEN_MASK_BITS(0x1, SSP_CR1_MASK_ALTASENS_HISI, 6) \ ++) ++ + #define DEFAULT_SSP_REG_CPSR ( \ + GEN_MASK_BITS(SSP_DEFAULT_PRESCALE, SSP_CPSR_MASK_CPSDVSR, 0) \ + ) +@@ -657,12 +710,24 @@ static void restore_state(struct pl022 *pl022) + */ + static void load_ssp_default_config(struct pl022 *pl022) + { ++ struct amba_device *adev = pl022->adev; ++ struct amba_driver *adrv = container_of(adev->dev.driver, ++ struct amba_driver, drv); ++ + if (pl022->vendor->pl023) { + writel(DEFAULT_SSP_REG_CR0_ST_PL023, SSP_CR0(pl022->virtbase)); + writew(DEFAULT_SSP_REG_CR1_ST_PL023, SSP_CR1(pl022->virtbase)); + } else if (pl022->vendor->extended_cr) { +- writel(DEFAULT_SSP_REG_CR0_ST, SSP_CR0(pl022->virtbase)); +- writew(DEFAULT_SSP_REG_CR1_ST, SSP_CR1(pl022->virtbase)); ++ if (adev->periphid == adrv->id_table[PL022_IDS_INDEX_HISI].id) { ++ writew(DEFAULT_SSP_REG_CR0, SSP_CR0(pl022->virtbase)); ++ writew(DEFAULT_SSP_REG_CR1_HISI, ++ SSP_CR1(pl022->virtbase)); ++ } else { ++ writel(DEFAULT_SSP_REG_CR0_ST, ++ SSP_CR0(pl022->virtbase)); ++ writew(DEFAULT_SSP_REG_CR1_ST, ++ SSP_CR1(pl022->virtbase)); ++ } + } else { + writew(DEFAULT_SSP_REG_CR0, SSP_CR0(pl022->virtbase)); + writew(DEFAULT_SSP_REG_CR1, SSP_CR1(pl022->virtbase)); +@@ -1830,6 +1895,10 @@ static int pl022_setup(struct spi_device *spi) + unsigned int bits = spi->bits_per_word; + u32 tmp; + struct device_node *np = spi->dev.of_node; ++ struct amba_device *adev = pl022->adev; ++ struct amba_driver *adrv = container_of(adev->dev.driver, ++ struct amba_driver, drv); ++ + + if (!spi->max_speed_hz) + return -EINVAL; +@@ -1972,7 +2041,8 @@ static int pl022_setup(struct spi_device *spi) + chip->cpsr = clk_freq.cpsdvsr; + + /* Special setup for the ST micro extended control registers */ +- if (pl022->vendor->extended_cr) { ++ if (pl022->vendor->extended_cr && (adev->periphid != ++ adrv->id_table[PL022_IDS_INDEX_HISI].id)) { + u32 etx; + + if (pl022->vendor->pl023) { +@@ -2006,6 +2076,20 @@ static int pl022_setup(struct spi_device *spi) + SSP_CR1_MASK_RXIFLSEL_ST, 7); + SSP_WRITE_BITS(chip->cr1, chip_info->tx_lev_trig, + SSP_CR1_MASK_TXIFLSEL_ST, 10); ++ } else if (pl022->vendor->extended_cr && (adev->periphid == ++ adrv->id_table[PL022_IDS_INDEX_HISI].id)) { ++ SSP_WRITE_BITS(chip->cr0, bits - 1, ++ SSP_CR0_MASK_DSS, 0); ++ SSP_WRITE_BITS(chip->cr0, chip_info->iface, ++ SSP_CR0_MASK_FRF, 4); ++ ++ if (spi->mode & SPI_LSB_FIRST) ++ tmp = !!SPI_LSB_FIRST; ++ else ++ tmp = !SPI_LSB_FIRST; ++ ++ SSP_WRITE_BITS(chip->cr1, tmp, SSP_CR1_MASK_BIGEND_HISI, 4); ++ SSP_WRITE_BITS(chip->cr1, 0x1, SSP_CR1_MASK_ALTASENS_HISI, 6); + } else { + SSP_WRITE_BITS(chip->cr0, bits - 1, + SSP_CR0_MASK_DSS, 0); +@@ -2037,7 +2121,7 @@ static int pl022_setup(struct spi_device *spi) + } + SSP_WRITE_BITS(chip->cr1, SSP_DISABLED, SSP_CR1_MASK_SSE, 1); + SSP_WRITE_BITS(chip->cr1, chip_info->hierarchy, SSP_CR1_MASK_MS, 2); +- SSP_WRITE_BITS(chip->cr1, chip_info->slave_tx_disable, SSP_CR1_MASK_SOD, ++ SSP_WRITE_BITS(chip->cr1, (unsigned int)chip_info->slave_tx_disable, SSP_CR1_MASK_SOD, + 3); + + /* Save controller_state */ +@@ -2069,7 +2153,7 @@ pl022_platform_data_dt_get(struct device *dev) + { + struct device_node *np = dev->of_node; + struct pl022_ssp_controller *pd; +- u32 tmp; ++ u32 tmp = 0; + + if (!np) { + dev_err(dev, "no dt node defined\n"); +@@ -2094,6 +2178,8 @@ pl022_platform_data_dt_get(struct device *dev) + static int pl022_probe(struct amba_device *adev, const struct amba_id *id) + { + struct device *dev = &adev->dev; ++ struct amba_driver *adrv = container_of(adev->dev.driver, ++ struct amba_driver, drv); + struct pl022_ssp_controller *platform_info = + dev_get_platdata(&adev->dev); + struct spi_master *master; +@@ -2157,6 +2243,41 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) + } else if (pl022->vendor->internal_cs_ctrl) { + for (i = 0; i < num_cs; i++) + pl022->chipselects[i] = i; ++ ++ if ((adev->periphid == adrv->id_table[PL022_IDS_INDEX_HISI].id) ++ && pl022->vendor->extended_cr ++ && (num_cs > 1)) { ++ pl022->cs_data = devm_kzalloc(dev, ++ sizeof(struct cs_data), ++ GFP_KERNEL); ++ if (!pl022->cs_data) { ++ status = -ENOMEM; ++ goto err_no_mem; ++ } ++ ++ if (of_address_to_resource(np, 1, ++ &pl022->cs_data->res)) { ++ status = -EPROBE_DEFER; ++ goto err_no_gpio; ++ } ++ ++ if (of_property_read_u32(np, "hisi,spi_cs_sb", ++ &pl022->cs_data->cs_sb)) { ++ status = -EPROBE_DEFER; ++ goto err_no_gpio; ++ } ++ ++ if (of_property_read_u32(np, "hisi,spi_cs_mask_bit", ++ &pl022->cs_data->cs_mask_bit)) { ++ status = -EPROBE_DEFER; ++ goto err_no_gpio; ++ } ++ ++ pl022->cs_data->virt_addr = devm_ioremap(dev, ++ pl022->cs_data->res.start, ++ resource_size(&adev->res)); ++ } else ++ pl022->cs_data = NULL; + } else if (IS_ENABLED(CONFIG_OF)) { + for (i = 0; i < num_cs; i++) { + int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); +@@ -2279,6 +2400,10 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) + err_no_ioremap: + amba_release_regions(adev); + err_no_ioregion: ++ if (pl022->cs_data) ++ release_mem_region(pl022->cs_data->res.start, ++ resource_size(&pl022->cs_data->res)); ++ + err_no_gpio: + err_no_mem: + spi_master_put(master); +@@ -2305,6 +2430,10 @@ pl022_remove(struct amba_device *adev) + + clk_disable_unprepare(pl022->clk); + amba_release_regions(adev); ++ if (pl022->cs_data) ++ release_mem_region(pl022->cs_data->res.start, ++ resource_size(&pl022->cs_data->res)); ++ + tasklet_disable(&pl022->pump_transfers); + return 0; + } +@@ -2420,6 +2549,16 @@ static struct vendor_data vendor_lsi = { + .internal_cs_ctrl = true, + }; + ++static struct vendor_data vendor_hisi = { ++ .fifodepth = 256, ++ .max_bpw = 16, ++ .unidir = false, ++ .extended_cr = true, ++ .pl023 = false, ++ .loopback = true, ++ .internal_cs_ctrl = true, ++}; ++ + static struct amba_id pl022_ids[] = { + { + /* +@@ -2460,6 +2599,15 @@ static struct amba_id pl022_ids[] = { + .mask = 0x000fffff, + .data = &vendor_lsi, + }, ++ { ++ /* ++ * Hisilicon derivative, this has a 16bit wide ++ * and 256 locations deep TX/RX FIFO ++ */ ++ .id = 0x00800022, ++ .mask = 0xffffffff, ++ .data = &vendor_hisi, ++ }, + { 0, 0 }, + }; + +diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig +index 7a0e288..21bc8ec 100644 +--- a/drivers/staging/android/Kconfig ++++ b/drivers/staging/android/Kconfig +@@ -44,23 +44,6 @@ config ASHMEM + It is, in theory, a good memory allocator for low-memory devices, + because it can discard shared memory units when under memory pressure. + +-config ANDROID_LOGGER +- tristate "Android log driver" +- default n +- ---help--- +- This adds support for system-wide logging using four log buffers. +- +- These are: +- +- 1: main +- 2: events +- 3: radio +- 4: system +- +- Log reading and writing is performed via normal Linux reads and +- optimized writes. This optimization avoids logging having too +- much overhead in the system. +- + config ANDROID_TIMED_OUTPUT + bool "Timed output class driver" + default y +@@ -75,14 +58,14 @@ config ANDROID_LOW_MEMORY_KILLER + ---help--- + Registers processes to be killed when memory is low + +-config ANDROID_INTF_ALARM_DEV +- tristate "Android alarm driver" +- depends on RTC_CLASS +- default n ++config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES ++ bool "Android Low Memory Killer: detect oom_adj values" ++ depends on ANDROID_LOW_MEMORY_KILLER ++ default y + ---help--- +- Provides non-wakeup and rtc backed wakeup alarms based on rtc or +- elapsed realtime, and a non-wakeup alarm on the monotonic clock. +- Also exports the alarm interface to user-space. ++ Detect oom_adj values written to ++ /sys/module/lowmemorykiller/parameters/adj and convert them ++ to oom_score_adj values. + + config SYNC + bool "Synchronization framework" +@@ -114,6 +97,8 @@ config SW_SYNC_USER + + source "drivers/staging/android/ion/Kconfig" + ++source "drivers/staging/android/fiq_debugger/Kconfig" ++ + endif # if ANDROID + + endmenu +diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile +index 517ad5f..b7b9980 100644 +--- a/drivers/staging/android/Makefile ++++ b/drivers/staging/android/Makefile +@@ -1,13 +1,12 @@ + ccflags-y += -I$(src) # needed for trace events + + obj-y += ion/ ++obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger/ + + obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o + obj-$(CONFIG_ASHMEM) += ashmem.o +-obj-$(CONFIG_ANDROID_LOGGER) += logger.o + obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o + obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o + obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o +-obj-$(CONFIG_ANDROID_INTF_ALARM_DEV) += alarm-dev.o + obj-$(CONFIG_SYNC) += sync.o sync_debug.o + obj-$(CONFIG_SW_SYNC) += sw_sync.o +diff --git a/drivers/staging/android/TODO b/drivers/staging/android/TODO +deleted file mode 100644 +index b15fb0d..0000000 +--- a/drivers/staging/android/TODO ++++ /dev/null +@@ -1,10 +0,0 @@ +-TODO: +- - checkpatch.pl cleanups +- - sparse fixes +- - rename files to be not so "generic" +- - make sure things build as modules properly +- - add proper arch dependencies as needed +- - audit userspace interfaces to make sure they are sane +- +-Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc: +-Brian Swetland <swetland@google.com> +diff --git a/drivers/staging/android/alarm-dev.c b/drivers/staging/android/alarm-dev.c +deleted file mode 100644 +index ff4b3e8..0000000 +--- a/drivers/staging/android/alarm-dev.c ++++ /dev/null +@@ -1,446 +0,0 @@ +-/* drivers/rtc/alarm-dev.c +- * +- * Copyright (C) 2007-2009 Google, Inc. +- * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. +- * +- * 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/time.h> +-#include <linux/module.h> +-#include <linux/device.h> +-#include <linux/miscdevice.h> +-#include <linux/fs.h> +-#include <linux/platform_device.h> +-#include <linux/sched.h> +-#include <linux/spinlock.h> +-#include <linux/uaccess.h> +-#include <linux/alarmtimer.h> +-#include "android_alarm.h" +- +-#define ANDROID_ALARM_PRINT_INFO (1U << 0) +-#define ANDROID_ALARM_PRINT_IO (1U << 1) +-#define ANDROID_ALARM_PRINT_INT (1U << 2) +- +-static int debug_mask = ANDROID_ALARM_PRINT_INFO; +-module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); +- +-#define alarm_dbg(debug_level_mask, fmt, ...) \ +-do { \ +- if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) \ +- pr_info(fmt, ##__VA_ARGS__); \ +-} while (0) +- +-#define ANDROID_ALARM_WAKEUP_MASK ( \ +- ANDROID_ALARM_RTC_WAKEUP_MASK | \ +- ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) +- +-static int alarm_opened; +-static DEFINE_SPINLOCK(alarm_slock); +-static struct wakeup_source alarm_wake_lock; +-static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue); +-static uint32_t alarm_pending; +-static uint32_t alarm_enabled; +-static uint32_t wait_pending; +- +-struct devalarm { +- union { +- struct hrtimer hrt; +- struct alarm alrm; +- } u; +- enum android_alarm_type type; +-}; +- +-static struct devalarm alarms[ANDROID_ALARM_TYPE_COUNT]; +- +-/** +- * is_wakeup() - Checks to see if this alarm can wake the device +- * @type: The type of alarm being checked +- * +- * Return: 1 if this is a wakeup alarm, otherwise 0 +- */ +-static int is_wakeup(enum android_alarm_type type) +-{ +- return type == ANDROID_ALARM_RTC_WAKEUP || +- type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP; +-} +- +-static void devalarm_start(struct devalarm *alrm, ktime_t exp) +-{ +- if (is_wakeup(alrm->type)) +- alarm_start(&alrm->u.alrm, exp); +- else +- hrtimer_start(&alrm->u.hrt, exp, HRTIMER_MODE_ABS); +-} +- +-static int devalarm_try_to_cancel(struct devalarm *alrm) +-{ +- if (is_wakeup(alrm->type)) +- return alarm_try_to_cancel(&alrm->u.alrm); +- return hrtimer_try_to_cancel(&alrm->u.hrt); +-} +- +-static void devalarm_cancel(struct devalarm *alrm) +-{ +- if (is_wakeup(alrm->type)) +- alarm_cancel(&alrm->u.alrm); +- else +- hrtimer_cancel(&alrm->u.hrt); +-} +- +-static void alarm_clear(enum android_alarm_type alarm_type) +-{ +- uint32_t alarm_type_mask = 1U << alarm_type; +- unsigned long flags; +- +- spin_lock_irqsave(&alarm_slock, flags); +- alarm_dbg(IO, "alarm %d clear\n", alarm_type); +- devalarm_try_to_cancel(&alarms[alarm_type]); +- if (alarm_pending) { +- alarm_pending &= ~alarm_type_mask; +- if (!alarm_pending && !wait_pending) +- __pm_relax(&alarm_wake_lock); +- } +- alarm_enabled &= ~alarm_type_mask; +- spin_unlock_irqrestore(&alarm_slock, flags); +-} +- +-static void alarm_set(enum android_alarm_type alarm_type, +- struct timespec *ts) +-{ +- uint32_t alarm_type_mask = 1U << alarm_type; +- unsigned long flags; +- +- spin_lock_irqsave(&alarm_slock, flags); +- alarm_dbg(IO, "alarm %d set %ld.%09ld\n", +- alarm_type, ts->tv_sec, ts->tv_nsec); +- alarm_enabled |= alarm_type_mask; +- devalarm_start(&alarms[alarm_type], timespec_to_ktime(*ts)); +- spin_unlock_irqrestore(&alarm_slock, flags); +-} +- +-static int alarm_wait(void) +-{ +- unsigned long flags; +- int rv = 0; +- +- spin_lock_irqsave(&alarm_slock, flags); +- alarm_dbg(IO, "alarm wait\n"); +- if (!alarm_pending && wait_pending) { +- __pm_relax(&alarm_wake_lock); +- wait_pending = 0; +- } +- spin_unlock_irqrestore(&alarm_slock, flags); +- +- rv = wait_event_interruptible(alarm_wait_queue, alarm_pending); +- if (rv) +- return rv; +- +- spin_lock_irqsave(&alarm_slock, flags); +- rv = alarm_pending; +- wait_pending = 1; +- alarm_pending = 0; +- spin_unlock_irqrestore(&alarm_slock, flags); +- +- return rv; +-} +- +-static int alarm_set_rtc(struct timespec *ts) +-{ +- struct rtc_time new_rtc_tm; +- struct rtc_device *rtc_dev; +- unsigned long flags; +- int rv = 0; +- +- rtc_time_to_tm(ts->tv_sec, &new_rtc_tm); +- rtc_dev = alarmtimer_get_rtcdev(); +- rv = do_settimeofday(ts); +- if (rv < 0) +- return rv; +- if (rtc_dev) +- rv = rtc_set_time(rtc_dev, &new_rtc_tm); +- +- spin_lock_irqsave(&alarm_slock, flags); +- alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; +- wake_up(&alarm_wait_queue); +- spin_unlock_irqrestore(&alarm_slock, flags); +- +- return rv; +-} +- +-static int alarm_get_time(enum android_alarm_type alarm_type, +- struct timespec *ts) +-{ +- int rv = 0; +- +- switch (alarm_type) { +- case ANDROID_ALARM_RTC_WAKEUP: +- case ANDROID_ALARM_RTC: +- getnstimeofday(ts); +- break; +- case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP: +- case ANDROID_ALARM_ELAPSED_REALTIME: +- get_monotonic_boottime(ts); +- break; +- case ANDROID_ALARM_SYSTEMTIME: +- ktime_get_ts(ts); +- break; +- default: +- rv = -EINVAL; +- } +- return rv; +-} +- +-static long alarm_do_ioctl(struct file *file, unsigned int cmd, +- struct timespec *ts) +-{ +- int rv = 0; +- unsigned long flags; +- enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd); +- +- if (alarm_type >= ANDROID_ALARM_TYPE_COUNT) +- return -EINVAL; +- +- if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) { +- if ((file->f_flags & O_ACCMODE) == O_RDONLY) +- return -EPERM; +- if (file->private_data == NULL && +- cmd != ANDROID_ALARM_SET_RTC) { +- spin_lock_irqsave(&alarm_slock, flags); +- if (alarm_opened) { +- spin_unlock_irqrestore(&alarm_slock, flags); +- return -EBUSY; +- } +- alarm_opened = 1; +- file->private_data = (void *)1; +- spin_unlock_irqrestore(&alarm_slock, flags); +- } +- } +- +- switch (ANDROID_ALARM_BASE_CMD(cmd)) { +- case ANDROID_ALARM_CLEAR(0): +- alarm_clear(alarm_type); +- break; +- case ANDROID_ALARM_SET(0): +- alarm_set(alarm_type, ts); +- break; +- case ANDROID_ALARM_SET_AND_WAIT(0): +- alarm_set(alarm_type, ts); +- /* fall though */ +- case ANDROID_ALARM_WAIT: +- rv = alarm_wait(); +- break; +- case ANDROID_ALARM_SET_RTC: +- rv = alarm_set_rtc(ts); +- break; +- case ANDROID_ALARM_GET_TIME(0): +- rv = alarm_get_time(alarm_type, ts); +- break; +- +- default: +- rv = -EINVAL; +- } +- return rv; +-} +- +-static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +-{ +- +- struct timespec ts; +- int rv; +- +- switch (ANDROID_ALARM_BASE_CMD(cmd)) { +- case ANDROID_ALARM_SET_AND_WAIT(0): +- case ANDROID_ALARM_SET(0): +- case ANDROID_ALARM_SET_RTC: +- if (copy_from_user(&ts, (void __user *)arg, sizeof(ts))) +- return -EFAULT; +- break; +- } +- +- rv = alarm_do_ioctl(file, cmd, &ts); +- if (rv) +- return rv; +- +- switch (ANDROID_ALARM_BASE_CMD(cmd)) { +- case ANDROID_ALARM_GET_TIME(0): +- if (copy_to_user((void __user *)arg, &ts, sizeof(ts))) +- return -EFAULT; +- break; +- } +- +- return 0; +-} +- +-#ifdef CONFIG_COMPAT +-static long alarm_compat_ioctl(struct file *file, unsigned int cmd, +- unsigned long arg) +-{ +- +- struct timespec ts; +- int rv; +- +- switch (ANDROID_ALARM_BASE_CMD(cmd)) { +- case ANDROID_ALARM_SET_AND_WAIT_COMPAT(0): +- case ANDROID_ALARM_SET_COMPAT(0): +- case ANDROID_ALARM_SET_RTC_COMPAT: +- if (compat_get_timespec(&ts, (void __user *)arg)) +- return -EFAULT; +- /* fall through */ +- case ANDROID_ALARM_GET_TIME_COMPAT(0): +- cmd = ANDROID_ALARM_COMPAT_TO_NORM(cmd); +- break; +- } +- +- rv = alarm_do_ioctl(file, cmd, &ts); +- if (rv) +- return rv; +- +- switch (ANDROID_ALARM_BASE_CMD(cmd)) { +- case ANDROID_ALARM_GET_TIME(0): /* NOTE: we modified cmd above */ +- if (compat_put_timespec(&ts, (void __user *)arg)) +- return -EFAULT; +- break; +- } +- +- return 0; +-} +-#endif +- +-static int alarm_open(struct inode *inode, struct file *file) +-{ +- file->private_data = NULL; +- return 0; +-} +- +-static int alarm_release(struct inode *inode, struct file *file) +-{ +- int i; +- unsigned long flags; +- +- spin_lock_irqsave(&alarm_slock, flags); +- if (file->private_data) { +- for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { +- uint32_t alarm_type_mask = 1U << i; +- +- if (alarm_enabled & alarm_type_mask) { +- alarm_dbg(INFO, +- "%s: clear alarm, pending %d\n", +- __func__, +- !!(alarm_pending & alarm_type_mask)); +- alarm_enabled &= ~alarm_type_mask; +- } +- spin_unlock_irqrestore(&alarm_slock, flags); +- devalarm_cancel(&alarms[i]); +- spin_lock_irqsave(&alarm_slock, flags); +- } +- if (alarm_pending | wait_pending) { +- if (alarm_pending) +- alarm_dbg(INFO, "%s: clear pending alarms %x\n", +- __func__, alarm_pending); +- __pm_relax(&alarm_wake_lock); +- wait_pending = 0; +- alarm_pending = 0; +- } +- alarm_opened = 0; +- } +- spin_unlock_irqrestore(&alarm_slock, flags); +- return 0; +-} +- +-static void devalarm_triggered(struct devalarm *alarm) +-{ +- unsigned long flags; +- uint32_t alarm_type_mask = 1U << alarm->type; +- +- alarm_dbg(INT, "%s: type %d\n", __func__, alarm->type); +- spin_lock_irqsave(&alarm_slock, flags); +- if (alarm_enabled & alarm_type_mask) { +- __pm_wakeup_event(&alarm_wake_lock, 5000); /* 5secs */ +- alarm_enabled &= ~alarm_type_mask; +- alarm_pending |= alarm_type_mask; +- wake_up(&alarm_wait_queue); +- } +- spin_unlock_irqrestore(&alarm_slock, flags); +-} +- +-static enum hrtimer_restart devalarm_hrthandler(struct hrtimer *hrt) +-{ +- struct devalarm *devalrm = container_of(hrt, struct devalarm, u.hrt); +- +- devalarm_triggered(devalrm); +- return HRTIMER_NORESTART; +-} +- +-static enum alarmtimer_restart devalarm_alarmhandler(struct alarm *alrm, +- ktime_t now) +-{ +- struct devalarm *devalrm = container_of(alrm, struct devalarm, u.alrm); +- +- devalarm_triggered(devalrm); +- return ALARMTIMER_NORESTART; +-} +- +- +-static const struct file_operations alarm_fops = { +- .owner = THIS_MODULE, +- .unlocked_ioctl = alarm_ioctl, +- .open = alarm_open, +- .release = alarm_release, +-#ifdef CONFIG_COMPAT +- .compat_ioctl = alarm_compat_ioctl, +-#endif +-}; +- +-static struct miscdevice alarm_device = { +- .minor = MISC_DYNAMIC_MINOR, +- .name = "alarm", +- .fops = &alarm_fops, +-}; +- +-static int __init alarm_dev_init(void) +-{ +- int err; +- int i; +- +- err = misc_register(&alarm_device); +- if (err) +- return err; +- +- alarm_init(&alarms[ANDROID_ALARM_RTC_WAKEUP].u.alrm, +- ALARM_REALTIME, devalarm_alarmhandler); +- hrtimer_init(&alarms[ANDROID_ALARM_RTC].u.hrt, +- CLOCK_REALTIME, HRTIMER_MODE_ABS); +- alarm_init(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].u.alrm, +- ALARM_BOOTTIME, devalarm_alarmhandler); +- hrtimer_init(&alarms[ANDROID_ALARM_ELAPSED_REALTIME].u.hrt, +- CLOCK_BOOTTIME, HRTIMER_MODE_ABS); +- hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].u.hrt, +- CLOCK_MONOTONIC, HRTIMER_MODE_ABS); +- +- for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { +- alarms[i].type = i; +- if (!is_wakeup(i)) +- alarms[i].u.hrt.function = devalarm_hrthandler; +- } +- +- wakeup_source_init(&alarm_wake_lock, "alarm"); +- return 0; +-} +- +-static void __exit alarm_dev_exit(void) +-{ +- misc_deregister(&alarm_device); +- wakeup_source_trash(&alarm_wake_lock); +-} +- +-module_init(alarm_dev_init); +-module_exit(alarm_dev_exit); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/staging/android/android_alarm.h b/drivers/staging/android/android_alarm.h +deleted file mode 100644 +index 495b20c..0000000 +--- a/drivers/staging/android/android_alarm.h ++++ /dev/null +@@ -1,41 +0,0 @@ +-/* include/linux/android_alarm.h +- * +- * Copyright (C) 2006-2007 Google, Inc. +- * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#ifndef _LINUX_ANDROID_ALARM_H +-#define _LINUX_ANDROID_ALARM_H +- +-#include <linux/compat.h> +-#include <linux/ioctl.h> +- +-#include "uapi/android_alarm.h" +- +-#ifdef CONFIG_COMPAT +-#define ANDROID_ALARM_SET_COMPAT(type) ALARM_IOW(2, type, \ +- struct compat_timespec) +-#define ANDROID_ALARM_SET_AND_WAIT_COMPAT(type) ALARM_IOW(3, type, \ +- struct compat_timespec) +-#define ANDROID_ALARM_GET_TIME_COMPAT(type) ALARM_IOW(4, type, \ +- struct compat_timespec) +-#define ANDROID_ALARM_SET_RTC_COMPAT _IOW('a', 5, \ +- struct compat_timespec) +-#define ANDROID_ALARM_IOCTL_NR(cmd) (_IOC_NR(cmd) & ((1<<4)-1)) +-#define ANDROID_ALARM_COMPAT_TO_NORM(cmd) \ +- ALARM_IOW(ANDROID_ALARM_IOCTL_NR(cmd), \ +- ANDROID_ALARM_IOCTL_TO_TYPE(cmd), \ +- struct timespec) +- +-#endif +- +-#endif +diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c +index ad4f579..afee619 100644 +--- a/drivers/staging/android/ashmem.c ++++ b/drivers/staging/android/ashmem.c +@@ -396,22 +396,14 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma) + } + get_file(asma->file); + +- /* +- * XXX - Reworked to use shmem_zero_setup() instead of +- * shmem_set_file while we're in staging. -jstultz +- */ +- if (vma->vm_flags & VM_SHARED) { +- ret = shmem_zero_setup(vma); +- if (ret) { +- fput(asma->file); +- goto out; +- } ++ if (vma->vm_flags & VM_SHARED) ++ shmem_set_file(vma, asma->file); ++ else { ++ if (vma->vm_file) ++ fput(vma->vm_file); ++ vma->vm_file = asma->file; + } + +- if (vma->vm_file) +- fput(vma->vm_file); +- vma->vm_file = asma->file; +- + out: + mutex_unlock(&ashmem_mutex); + return ret; +@@ -441,12 +433,14 @@ ashmem_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) + if (!(sc->gfp_mask & __GFP_FS)) + return SHRINK_STOP; + +- mutex_lock(&ashmem_mutex); ++ if (!mutex_trylock(&ashmem_mutex)) ++ return -1; ++ + list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) { + loff_t start = range->pgstart * PAGE_SIZE; + loff_t end = (range->pgend + 1) * PAGE_SIZE; + +- do_fallocate(range->asma->file, ++ range->asma->file->f_op->fallocate(range->asma->file, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + start, end - start); + range->purged = ASHMEM_WAS_PURGED; +diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c +index c69c40d..710393a 100644 +--- a/drivers/staging/android/binder.c ++++ b/drivers/staging/android/binder.c +@@ -26,6 +26,7 @@ + #include <linux/miscdevice.h> + #include <linux/mm.h> + #include <linux/module.h> ++#include <linux/rtmutex.h> + #include <linux/mutex.h> + #include <linux/nsproxy.h> + #include <linux/poll.h> +@@ -37,11 +38,12 @@ + #include <linux/vmalloc.h> + #include <linux/slab.h> + #include <linux/pid_namespace.h> ++#include <linux/security.h> + + #include "binder.h" + #include "binder_trace.h" + +-static DEFINE_MUTEX(binder_main_lock); ++static DEFINE_RT_MUTEX(binder_main_lock); + static DEFINE_MUTEX(binder_deferred_lock); + static DEFINE_MUTEX(binder_mmap_lock); + +@@ -421,14 +423,14 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd) + static inline void binder_lock(const char *tag) + { + trace_binder_lock(tag); +- mutex_lock(&binder_main_lock); ++ rt_mutex_lock(&binder_main_lock); + trace_binder_locked(tag); + } + + static inline void binder_unlock(const char *tag) + { + trace_binder_unlock(tag); +- mutex_unlock(&binder_main_lock); ++ rt_mutex_unlock(&binder_main_lock); + } + + static void binder_set_nice(long nice) +@@ -1316,6 +1318,7 @@ static void binder_transaction(struct binder_proc *proc, + struct binder_transaction *t; + struct binder_work *tcomplete; + binder_size_t *offp, *off_end; ++ binder_size_t off_min; + struct binder_proc *target_proc; + struct binder_thread *target_thread = NULL; + struct binder_node *target_node = NULL; +@@ -1396,6 +1399,10 @@ static void binder_transaction(struct binder_proc *proc, + return_error = BR_DEAD_REPLY; + goto err_dead_binder; + } ++ if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) { ++ return_error = BR_FAILED_REPLY; ++ goto err_invalid_target_handle; ++ } + if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { + struct binder_transaction *tmp; + +@@ -1512,18 +1519,24 @@ static void binder_transaction(struct binder_proc *proc, + goto err_bad_offset; + } + off_end = (void *)offp + tr->offsets_size; ++ off_min = 0; + for (; offp < off_end; offp++) { + struct flat_binder_object *fp; + + if (*offp > t->buffer->data_size - sizeof(*fp) || ++ *offp < off_min || + t->buffer->data_size < sizeof(*fp) || + !IS_ALIGNED(*offp, sizeof(u32))) { +- binder_user_error("%d:%d got transaction with invalid offset, %lld\n", +- proc->pid, thread->pid, (u64)*offp); ++ binder_user_error("%d:%d got transaction with invalid offset, %lld (min %lld, max %lld)\n", ++ proc->pid, thread->pid, (u64)*offp, ++ (u64)off_min, ++ (u64)(t->buffer->data_size - ++ sizeof(*fp))); + return_error = BR_FAILED_REPLY; + goto err_bad_offset; + } + fp = (struct flat_binder_object *)(t->buffer->data + *offp); ++ off_min = *offp + sizeof(struct flat_binder_object); + switch (fp->type) { + case BINDER_TYPE_BINDER: + case BINDER_TYPE_WEAK_BINDER: { +@@ -1547,6 +1560,10 @@ static void binder_transaction(struct binder_proc *proc, + return_error = BR_FAILED_REPLY; + goto err_binder_get_ref_for_node_failed; + } ++ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { ++ return_error = BR_FAILED_REPLY; ++ goto err_binder_get_ref_for_node_failed; ++ } + ref = binder_get_ref_for_node(target_proc, node); + if (ref == NULL) { + return_error = BR_FAILED_REPLY; +@@ -1577,6 +1594,10 @@ static void binder_transaction(struct binder_proc *proc, + return_error = BR_FAILED_REPLY; + goto err_binder_get_ref_failed; + } ++ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { ++ return_error = BR_FAILED_REPLY; ++ goto err_binder_get_ref_failed; ++ } + if (ref->node->proc == target_proc) { + if (fp->type == BINDER_TYPE_HANDLE) + fp->type = BINDER_TYPE_BINDER; +@@ -1634,6 +1655,11 @@ static void binder_transaction(struct binder_proc *proc, + return_error = BR_FAILED_REPLY; + goto err_fget_failed; + } ++ if (security_binder_transfer_file(proc->tsk, target_proc->tsk, file) < 0) { ++ fput(file); ++ return_error = BR_FAILED_REPLY; ++ goto err_get_unused_fd_failed; ++ } + target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC); + if (target_fd < 0) { + fput(file); +@@ -2736,6 +2762,9 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) + ret = binder_ioctl_set_ctx_mgr(filp); + if (ret) + goto err; ++ ret = security_binder_set_context_mgr(proc->tsk); ++ if (ret < 0) ++ goto err; + break; + case BINDER_THREAD_EXIT: + binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n", +diff --git a/drivers/staging/android/fiq_debugger/Kconfig b/drivers/staging/android/fiq_debugger/Kconfig +new file mode 100644 +index 0000000..56f7f99 +--- /dev/null ++++ b/drivers/staging/android/fiq_debugger/Kconfig +@@ -0,0 +1,49 @@ ++config FIQ_DEBUGGER ++ bool "FIQ Mode Serial Debugger" ++ default n ++ depends on ARM || ARM64 ++ help ++ The FIQ serial debugger can accept commands even when the ++ kernel is unresponsive due to being stuck with interrupts ++ disabled. ++ ++config FIQ_DEBUGGER_NO_SLEEP ++ bool "Keep serial debugger active" ++ depends on FIQ_DEBUGGER ++ default n ++ help ++ Enables the serial debugger at boot. Passing ++ fiq_debugger.no_sleep on the kernel commandline will ++ override this config option. ++ ++config FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON ++ bool "Don't disable wakeup IRQ when debugger is active" ++ depends on FIQ_DEBUGGER ++ default n ++ help ++ Don't disable the wakeup irq when enabling the uart clock. This will ++ cause extra interrupts, but it makes the serial debugger usable with ++ on some MSM radio builds that ignore the uart clock request in power ++ collapse. ++ ++config FIQ_DEBUGGER_CONSOLE ++ bool "Console on FIQ Serial Debugger port" ++ depends on FIQ_DEBUGGER ++ default n ++ help ++ Enables a console so that printk messages are displayed on ++ the debugger serial port as the occur. ++ ++config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE ++ bool "Put the FIQ debugger into console mode by default" ++ depends on FIQ_DEBUGGER_CONSOLE ++ default n ++ help ++ If enabled, this puts the fiq debugger into console mode by default. ++ Otherwise, the fiq debugger will start out in debug mode. ++ ++config FIQ_WATCHDOG ++ bool ++ select FIQ_DEBUGGER ++ select PSTORE_RAM ++ default n +diff --git a/drivers/staging/android/fiq_debugger/Makefile b/drivers/staging/android/fiq_debugger/Makefile +new file mode 100644 +index 0000000..a7ca487 +--- /dev/null ++++ b/drivers/staging/android/fiq_debugger/Makefile +@@ -0,0 +1,4 @@ ++obj-y += fiq_debugger.o ++obj-$(CONFIG_ARM) += fiq_debugger_arm.o ++obj-$(CONFIG_ARM64) += fiq_debugger_arm64.o ++obj-$(CONFIG_FIQ_WATCHDOG) += fiq_watchdog.o +diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c +new file mode 100644 +index 0000000..52f6816 +--- /dev/null ++++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c +@@ -0,0 +1,1212 @@ ++/* ++ * drivers/staging/android/fiq_debugger.c ++ * ++ * Serial Debugger Interface accessed through an FIQ interrupt. ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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 <stdarg.h> ++#include <linux/module.h> ++#include <linux/io.h> ++#include <linux/console.h> ++#include <linux/interrupt.h> ++#include <linux/clk.h> ++#include <linux/platform_device.h> ++#include <linux/kernel_stat.h> ++#include <linux/kmsg_dump.h> ++#include <linux/irq.h> ++#include <linux/delay.h> ++#include <linux/reboot.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/smp.h> ++#include <linux/timer.h> ++#include <linux/tty.h> ++#include <linux/tty_flip.h> ++#include <linux/wakelock.h> ++ ++#ifdef CONFIG_FIQ_GLUE ++#include <asm/fiq_glue.h> ++#endif ++ ++#include <linux/uaccess.h> ++ ++#include "fiq_debugger.h" ++#include "fiq_debugger_priv.h" ++#include "fiq_debugger_ringbuf.h" ++ ++#define DEBUG_MAX 64 ++#define MAX_UNHANDLED_FIQ_COUNT 1000000 ++ ++#define MAX_FIQ_DEBUGGER_PORTS 4 ++ ++struct fiq_debugger_state { ++#ifdef CONFIG_FIQ_GLUE ++ struct fiq_glue_handler handler; ++#endif ++ struct fiq_debugger_output output; ++ ++ int fiq; ++ int uart_irq; ++ int signal_irq; ++ int wakeup_irq; ++ bool wakeup_irq_no_set_wake; ++ struct clk *clk; ++ struct fiq_debugger_pdata *pdata; ++ struct platform_device *pdev; ++ ++ char debug_cmd[DEBUG_MAX]; ++ int debug_busy; ++ int debug_abort; ++ ++ char debug_buf[DEBUG_MAX]; ++ int debug_count; ++ ++ bool no_sleep; ++ bool debug_enable; ++ bool ignore_next_wakeup_irq; ++ struct timer_list sleep_timer; ++ spinlock_t sleep_timer_lock; ++ bool uart_enabled; ++ struct wake_lock debugger_wake_lock; ++ bool console_enable; ++ int current_cpu; ++ atomic_t unhandled_fiq_count; ++ bool in_fiq; ++ ++ struct work_struct work; ++ spinlock_t work_lock; ++ char work_cmd[DEBUG_MAX]; ++ ++#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE ++ spinlock_t console_lock; ++ struct console console; ++ struct tty_port tty_port; ++ struct fiq_debugger_ringbuf *tty_rbuf; ++ bool syslog_dumping; ++#endif ++ ++ unsigned int last_irqs[NR_IRQS]; ++ unsigned int last_local_timer_irqs[NR_CPUS]; ++}; ++ ++#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE ++struct tty_driver *fiq_tty_driver; ++#endif ++ ++#ifdef CONFIG_FIQ_DEBUGGER_NO_SLEEP ++static bool initial_no_sleep = true; ++#else ++static bool initial_no_sleep; ++#endif ++ ++#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE ++static bool initial_debug_enable = true; ++static bool initial_console_enable = true; ++#else ++static bool initial_debug_enable; ++static bool initial_console_enable; ++#endif ++ ++static bool fiq_kgdb_enable; ++ ++module_param_named(no_sleep, initial_no_sleep, bool, 0644); ++module_param_named(debug_enable, initial_debug_enable, bool, 0644); ++module_param_named(console_enable, initial_console_enable, bool, 0644); ++module_param_named(kgdb_enable, fiq_kgdb_enable, bool, 0644); ++ ++#ifdef CONFIG_FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON ++static inline ++void fiq_debugger_enable_wakeup_irq(struct fiq_debugger_state *state) {} ++static inline ++void fiq_debugger_disable_wakeup_irq(struct fiq_debugger_state *state) {} ++#else ++static inline ++void fiq_debugger_enable_wakeup_irq(struct fiq_debugger_state *state) ++{ ++ if (state->wakeup_irq < 0) ++ return; ++ enable_irq(state->wakeup_irq); ++ if (!state->wakeup_irq_no_set_wake) ++ enable_irq_wake(state->wakeup_irq); ++} ++static inline ++void fiq_debugger_disable_wakeup_irq(struct fiq_debugger_state *state) ++{ ++ if (state->wakeup_irq < 0) ++ return; ++ disable_irq_nosync(state->wakeup_irq); ++ if (!state->wakeup_irq_no_set_wake) ++ disable_irq_wake(state->wakeup_irq); ++} ++#endif ++ ++static inline bool fiq_debugger_have_fiq(struct fiq_debugger_state *state) ++{ ++ return (state->fiq >= 0); ++} ++ ++#ifdef CONFIG_FIQ_GLUE ++static void fiq_debugger_force_irq(struct fiq_debugger_state *state) ++{ ++ unsigned int irq = state->signal_irq; ++ ++ if (WARN_ON(!fiq_debugger_have_fiq(state))) ++ return; ++ if (state->pdata->force_irq) { ++ state->pdata->force_irq(state->pdev, irq); ++ } else { ++ struct irq_chip *chip = irq_get_chip(irq); ++ if (chip && chip->irq_retrigger) ++ chip->irq_retrigger(irq_get_irq_data(irq)); ++ } ++} ++#endif ++ ++static void fiq_debugger_uart_enable(struct fiq_debugger_state *state) ++{ ++ if (state->clk) ++ clk_enable(state->clk); ++ if (state->pdata->uart_enable) ++ state->pdata->uart_enable(state->pdev); ++} ++ ++static void fiq_debugger_uart_disable(struct fiq_debugger_state *state) ++{ ++ if (state->pdata->uart_disable) ++ state->pdata->uart_disable(state->pdev); ++ if (state->clk) ++ clk_disable(state->clk); ++} ++ ++static void fiq_debugger_uart_flush(struct fiq_debugger_state *state) ++{ ++ if (state->pdata->uart_flush) ++ state->pdata->uart_flush(state->pdev); ++} ++ ++static void fiq_debugger_putc(struct fiq_debugger_state *state, char c) ++{ ++ state->pdata->uart_putc(state->pdev, c); ++} ++ ++static void fiq_debugger_puts(struct fiq_debugger_state *state, char *s) ++{ ++ unsigned c; ++ while ((c = *s++)) { ++ if (c == '\n') ++ fiq_debugger_putc(state, '\r'); ++ fiq_debugger_putc(state, c); ++ } ++} ++ ++static void fiq_debugger_prompt(struct fiq_debugger_state *state) ++{ ++ fiq_debugger_puts(state, "debug> "); ++} ++ ++static void fiq_debugger_dump_kernel_log(struct fiq_debugger_state *state) ++{ ++ char buf[512]; ++ size_t len; ++ struct kmsg_dumper dumper = { .active = true }; ++ ++ ++ kmsg_dump_rewind_nolock(&dumper); ++ while (kmsg_dump_get_line_nolock(&dumper, true, buf, ++ sizeof(buf) - 1, &len)) { ++ buf[len] = 0; ++ fiq_debugger_puts(state, buf); ++ } ++} ++ ++static void fiq_debugger_printf(struct fiq_debugger_output *output, ++ const char *fmt, ...) ++{ ++ struct fiq_debugger_state *state; ++ char buf[256]; ++ va_list ap; ++ ++ state = container_of(output, struct fiq_debugger_state, output); ++ va_start(ap, fmt); ++ vsnprintf(buf, sizeof(buf), fmt, ap); ++ va_end(ap); ++ ++ fiq_debugger_puts(state, buf); ++} ++ ++/* Safe outside fiq context */ ++static int fiq_debugger_printf_nfiq(void *cookie, const char *fmt, ...) ++{ ++ struct fiq_debugger_state *state = cookie; ++ char buf[256]; ++ va_list ap; ++ unsigned long irq_flags; ++ ++ va_start(ap, fmt); ++ vsnprintf(buf, 128, fmt, ap); ++ va_end(ap); ++ ++ local_irq_save(irq_flags); ++ fiq_debugger_puts(state, buf); ++ fiq_debugger_uart_flush(state); ++ local_irq_restore(irq_flags); ++ return state->debug_abort; ++} ++ ++static void fiq_debugger_dump_irqs(struct fiq_debugger_state *state) ++{ ++ int n; ++ struct irq_desc *desc; ++ ++ fiq_debugger_printf(&state->output, ++ "irqnr total since-last status name\n"); ++ for_each_irq_desc(n, desc) { ++ struct irqaction *act = desc->action; ++ if (!act && !kstat_irqs(n)) ++ continue; ++ fiq_debugger_printf(&state->output, "%5d: %10u %11u %8x %s\n", n, ++ kstat_irqs(n), ++ kstat_irqs(n) - state->last_irqs[n], ++ desc->status_use_accessors, ++ (act && act->name) ? act->name : "???"); ++ state->last_irqs[n] = kstat_irqs(n); ++ } ++} ++ ++static void fiq_debugger_do_ps(struct fiq_debugger_state *state) ++{ ++ struct task_struct *g; ++ struct task_struct *p; ++ unsigned task_state; ++ static const char stat_nam[] = "RSDTtZX"; ++ ++ fiq_debugger_printf(&state->output, "pid ppid prio task pc\n"); ++ read_lock(&tasklist_lock); ++ do_each_thread(g, p) { ++ task_state = p->state ? __ffs(p->state) + 1 : 0; ++ fiq_debugger_printf(&state->output, ++ "%5d %5d %4d ", p->pid, p->parent->pid, p->prio); ++ fiq_debugger_printf(&state->output, "%-13.13s %c", p->comm, ++ task_state >= sizeof(stat_nam) ? '?' : stat_nam[task_state]); ++ if (task_state == TASK_RUNNING) ++ fiq_debugger_printf(&state->output, " running\n"); ++ else ++ fiq_debugger_printf(&state->output, " %08lx\n", ++ thread_saved_pc(p)); ++ } while_each_thread(g, p); ++ read_unlock(&tasklist_lock); ++} ++ ++#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE ++static void fiq_debugger_begin_syslog_dump(struct fiq_debugger_state *state) ++{ ++ state->syslog_dumping = true; ++} ++ ++static void fiq_debugger_end_syslog_dump(struct fiq_debugger_state *state) ++{ ++ state->syslog_dumping = false; ++} ++#else ++extern int do_syslog(int type, char __user *bug, int count); ++static void fiq_debugger_begin_syslog_dump(struct fiq_debugger_state *state) ++{ ++ do_syslog(5 /* clear */, NULL, 0); ++} ++ ++static void fiq_debugger_end_syslog_dump(struct fiq_debugger_state *state) ++{ ++ fiq_debugger_dump_kernel_log(state); ++} ++#endif ++ ++static void fiq_debugger_do_sysrq(struct fiq_debugger_state *state, char rq) ++{ ++ if ((rq == 'g' || rq == 'G') && !fiq_kgdb_enable) { ++ fiq_debugger_printf(&state->output, "sysrq-g blocked\n"); ++ return; ++ } ++ fiq_debugger_begin_syslog_dump(state); ++ handle_sysrq(rq); ++ fiq_debugger_end_syslog_dump(state); ++} ++ ++#ifdef CONFIG_KGDB ++static void fiq_debugger_do_kgdb(struct fiq_debugger_state *state) ++{ ++ if (!fiq_kgdb_enable) { ++ fiq_debugger_printf(&state->output, "kgdb through fiq debugger not enabled\n"); ++ return; ++ } ++ ++ fiq_debugger_printf(&state->output, "enabling console and triggering kgdb\n"); ++ state->console_enable = true; ++ handle_sysrq('g'); ++} ++#endif ++ ++static void fiq_debugger_schedule_work(struct fiq_debugger_state *state, ++ char *cmd) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&state->work_lock, flags); ++ if (state->work_cmd[0] != '\0') { ++ fiq_debugger_printf(&state->output, "work command processor busy\n"); ++ spin_unlock_irqrestore(&state->work_lock, flags); ++ return; ++ } ++ ++ strlcpy(state->work_cmd, cmd, sizeof(state->work_cmd)); ++ spin_unlock_irqrestore(&state->work_lock, flags); ++ ++ schedule_work(&state->work); ++} ++ ++static void fiq_debugger_work(struct work_struct *work) ++{ ++ struct fiq_debugger_state *state; ++ char work_cmd[DEBUG_MAX]; ++ char *cmd; ++ unsigned long flags; ++ ++ state = container_of(work, struct fiq_debugger_state, work); ++ ++ spin_lock_irqsave(&state->work_lock, flags); ++ ++ strlcpy(work_cmd, state->work_cmd, sizeof(work_cmd)); ++ state->work_cmd[0] = '\0'; ++ ++ spin_unlock_irqrestore(&state->work_lock, flags); ++ ++ cmd = work_cmd; ++ if (!strncmp(cmd, "reboot", 6)) { ++ cmd += 6; ++ while (*cmd == ' ') ++ cmd++; ++ if (cmd != '\0') ++ kernel_restart(cmd); ++ else ++ kernel_restart(NULL); ++ } else { ++ fiq_debugger_printf(&state->output, "unknown work command '%s'\n", ++ work_cmd); ++ } ++} ++ ++/* This function CANNOT be called in FIQ context */ ++static void fiq_debugger_irq_exec(struct fiq_debugger_state *state, char *cmd) ++{ ++ if (!strcmp(cmd, "ps")) ++ fiq_debugger_do_ps(state); ++ if (!strcmp(cmd, "sysrq")) ++ fiq_debugger_do_sysrq(state, 'h'); ++ if (!strncmp(cmd, "sysrq ", 6)) ++ fiq_debugger_do_sysrq(state, cmd[6]); ++#ifdef CONFIG_KGDB ++ if (!strcmp(cmd, "kgdb")) ++ fiq_debugger_do_kgdb(state); ++#endif ++ if (!strncmp(cmd, "reboot", 6)) ++ fiq_debugger_schedule_work(state, cmd); ++} ++ ++static void fiq_debugger_help(struct fiq_debugger_state *state) ++{ ++ fiq_debugger_printf(&state->output, ++ "FIQ Debugger commands:\n" ++ " pc PC status\n" ++ " regs Register dump\n" ++ " allregs Extended Register dump\n" ++ " bt Stack trace\n" ++ " reboot [<c>] Reboot with command <c>\n" ++ " reset [<c>] Hard reset with command <c>\n" ++ " irqs Interupt status\n" ++ " kmsg Kernel log\n" ++ " version Kernel version\n"); ++ fiq_debugger_printf(&state->output, ++ " sleep Allow sleep while in FIQ\n" ++ " nosleep Disable sleep while in FIQ\n" ++ " console Switch terminal to console\n" ++ " cpu Current CPU\n" ++ " cpu <number> Switch to CPU<number>\n"); ++ fiq_debugger_printf(&state->output, ++ " ps Process list\n" ++ " sysrq sysrq options\n" ++ " sysrq <param> Execute sysrq with <param>\n"); ++#ifdef CONFIG_KGDB ++ fiq_debugger_printf(&state->output, ++ " kgdb Enter kernel debugger\n"); ++#endif ++} ++ ++static void fiq_debugger_take_affinity(void *info) ++{ ++ struct fiq_debugger_state *state = info; ++ struct cpumask cpumask; ++ ++ cpumask_clear(&cpumask); ++ cpumask_set_cpu(get_cpu(), &cpumask); ++ ++ irq_set_affinity(state->uart_irq, &cpumask); ++} ++ ++static void fiq_debugger_switch_cpu(struct fiq_debugger_state *state, int cpu) ++{ ++ if (!fiq_debugger_have_fiq(state)) ++ smp_call_function_single(cpu, fiq_debugger_take_affinity, state, ++ false); ++ state->current_cpu = cpu; ++} ++ ++static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, ++ const char *cmd, const struct pt_regs *regs, ++ void *svc_sp) ++{ ++ bool signal_helper = false; ++ ++ if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) { ++ fiq_debugger_help(state); ++ } else if (!strcmp(cmd, "pc")) { ++ fiq_debugger_dump_pc(&state->output, regs); ++ } else if (!strcmp(cmd, "regs")) { ++ fiq_debugger_dump_regs(&state->output, regs); ++ } else if (!strcmp(cmd, "allregs")) { ++ fiq_debugger_dump_allregs(&state->output, regs); ++ } else if (!strcmp(cmd, "bt")) { ++ fiq_debugger_dump_stacktrace(&state->output, regs, 100, svc_sp); ++ } else if (!strncmp(cmd, "reset", 5)) { ++ cmd += 5; ++ while (*cmd == ' ') ++ cmd++; ++ if (*cmd) { ++ char tmp_cmd[32]; ++ strlcpy(tmp_cmd, cmd, sizeof(tmp_cmd)); ++ machine_restart(tmp_cmd); ++ } else { ++ machine_restart(NULL); ++ } ++ } else if (!strcmp(cmd, "irqs")) { ++ fiq_debugger_dump_irqs(state); ++ } else if (!strcmp(cmd, "kmsg")) { ++ fiq_debugger_dump_kernel_log(state); ++ } else if (!strcmp(cmd, "version")) { ++ fiq_debugger_printf(&state->output, "%s\n", linux_banner); ++ } else if (!strcmp(cmd, "sleep")) { ++ state->no_sleep = false; ++ fiq_debugger_printf(&state->output, "enabling sleep\n"); ++ } else if (!strcmp(cmd, "nosleep")) { ++ state->no_sleep = true; ++ fiq_debugger_printf(&state->output, "disabling sleep\n"); ++ } else if (!strcmp(cmd, "console")) { ++ fiq_debugger_printf(&state->output, "console mode\n"); ++ fiq_debugger_uart_flush(state); ++ state->console_enable = true; ++ } else if (!strcmp(cmd, "cpu")) { ++ fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu); ++ } else if (!strncmp(cmd, "cpu ", 4)) { ++ unsigned long cpu = 0; ++ if (kstrtoul(cmd + 4, 10, &cpu) == 0) ++ fiq_debugger_switch_cpu(state, cpu); ++ else ++ fiq_debugger_printf(&state->output, "invalid cpu\n"); ++ fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu); ++ } else { ++ if (state->debug_busy) { ++ fiq_debugger_printf(&state->output, ++ "command processor busy. trying to abort.\n"); ++ state->debug_abort = -1; ++ } else { ++ strcpy(state->debug_cmd, cmd); ++ state->debug_busy = 1; ++ } ++ ++ return true; ++ } ++ if (!state->console_enable) ++ fiq_debugger_prompt(state); ++ ++ return signal_helper; ++} ++ ++static void fiq_debugger_sleep_timer_expired(unsigned long data) ++{ ++ struct fiq_debugger_state *state = (struct fiq_debugger_state *)data; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&state->sleep_timer_lock, flags); ++ if (state->uart_enabled && !state->no_sleep) { ++ if (state->debug_enable && !state->console_enable) { ++ state->debug_enable = false; ++ fiq_debugger_printf_nfiq(state, ++ "suspending fiq debugger\n"); ++ } ++ state->ignore_next_wakeup_irq = true; ++ fiq_debugger_uart_disable(state); ++ state->uart_enabled = false; ++ fiq_debugger_enable_wakeup_irq(state); ++ } ++ wake_unlock(&state->debugger_wake_lock); ++ spin_unlock_irqrestore(&state->sleep_timer_lock, flags); ++} ++ ++static void fiq_debugger_handle_wakeup(struct fiq_debugger_state *state) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&state->sleep_timer_lock, flags); ++ if (state->wakeup_irq >= 0 && state->ignore_next_wakeup_irq) { ++ state->ignore_next_wakeup_irq = false; ++ } else if (!state->uart_enabled) { ++ wake_lock(&state->debugger_wake_lock); ++ fiq_debugger_uart_enable(state); ++ state->uart_enabled = true; ++ fiq_debugger_disable_wakeup_irq(state); ++ mod_timer(&state->sleep_timer, jiffies + HZ / 2); ++ } ++ spin_unlock_irqrestore(&state->sleep_timer_lock, flags); ++} ++ ++static irqreturn_t fiq_debugger_wakeup_irq_handler(int irq, void *dev) ++{ ++ struct fiq_debugger_state *state = dev; ++ ++ if (!state->no_sleep) ++ fiq_debugger_puts(state, "WAKEUP\n"); ++ fiq_debugger_handle_wakeup(state); ++ ++ return IRQ_HANDLED; ++} ++ ++static ++void fiq_debugger_handle_console_irq_context(struct fiq_debugger_state *state) ++{ ++#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) ++ if (state->tty_port.ops) { ++ int i; ++ int count = fiq_debugger_ringbuf_level(state->tty_rbuf); ++ for (i = 0; i < count; i++) { ++ int c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0); ++ tty_insert_flip_char(&state->tty_port, c, TTY_NORMAL); ++ if (!fiq_debugger_ringbuf_consume(state->tty_rbuf, 1)) ++ pr_warn("fiq tty failed to consume byte\n"); ++ } ++ tty_flip_buffer_push(&state->tty_port); ++ } ++#endif ++} ++ ++static void fiq_debugger_handle_irq_context(struct fiq_debugger_state *state) ++{ ++ if (!state->no_sleep) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&state->sleep_timer_lock, flags); ++ wake_lock(&state->debugger_wake_lock); ++ mod_timer(&state->sleep_timer, jiffies + HZ * 5); ++ spin_unlock_irqrestore(&state->sleep_timer_lock, flags); ++ } ++ fiq_debugger_handle_console_irq_context(state); ++ if (state->debug_busy) { ++ fiq_debugger_irq_exec(state, state->debug_cmd); ++ if (!state->console_enable) ++ fiq_debugger_prompt(state); ++ state->debug_busy = 0; ++ } ++} ++ ++static int fiq_debugger_getc(struct fiq_debugger_state *state) ++{ ++ return state->pdata->uart_getc(state->pdev); ++} ++ ++static bool fiq_debugger_handle_uart_interrupt(struct fiq_debugger_state *state, ++ int this_cpu, const struct pt_regs *regs, void *svc_sp) ++{ ++ int c; ++ static int last_c; ++ int count = 0; ++ bool signal_helper = false; ++ ++ if (this_cpu != state->current_cpu) { ++ if (state->in_fiq) ++ return false; ++ ++ if (atomic_inc_return(&state->unhandled_fiq_count) != ++ MAX_UNHANDLED_FIQ_COUNT) ++ return false; ++ ++ fiq_debugger_printf(&state->output, ++ "fiq_debugger: cpu %d not responding, " ++ "reverting to cpu %d\n", state->current_cpu, ++ this_cpu); ++ ++ atomic_set(&state->unhandled_fiq_count, 0); ++ fiq_debugger_switch_cpu(state, this_cpu); ++ return false; ++ } ++ ++ state->in_fiq = true; ++ ++ while ((c = fiq_debugger_getc(state)) != FIQ_DEBUGGER_NO_CHAR) { ++ count++; ++ if (!state->debug_enable) { ++ if ((c == 13) || (c == 10)) { ++ state->debug_enable = true; ++ state->debug_count = 0; ++ fiq_debugger_prompt(state); ++ } ++ } else if (c == FIQ_DEBUGGER_BREAK) { ++ state->console_enable = false; ++ fiq_debugger_puts(state, "fiq debugger mode\n"); ++ state->debug_count = 0; ++ fiq_debugger_prompt(state); ++#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE ++ } else if (state->console_enable && state->tty_rbuf) { ++ fiq_debugger_ringbuf_push(state->tty_rbuf, c); ++ signal_helper = true; ++#endif ++ } else if ((c >= ' ') && (c < 127)) { ++ if (state->debug_count < (DEBUG_MAX - 1)) { ++ state->debug_buf[state->debug_count++] = c; ++ fiq_debugger_putc(state, c); ++ } ++ } else if ((c == 8) || (c == 127)) { ++ if (state->debug_count > 0) { ++ state->debug_count--; ++ fiq_debugger_putc(state, 8); ++ fiq_debugger_putc(state, ' '); ++ fiq_debugger_putc(state, 8); ++ } ++ } else if ((c == 13) || (c == 10)) { ++ if (c == '\r' || (c == '\n' && last_c != '\r')) { ++ fiq_debugger_putc(state, '\r'); ++ fiq_debugger_putc(state, '\n'); ++ } ++ if (state->debug_count) { ++ state->debug_buf[state->debug_count] = 0; ++ state->debug_count = 0; ++ signal_helper |= ++ fiq_debugger_fiq_exec(state, ++ state->debug_buf, ++ regs, svc_sp); ++ } else { ++ fiq_debugger_prompt(state); ++ } ++ } ++ last_c = c; ++ } ++ if (!state->console_enable) ++ fiq_debugger_uart_flush(state); ++ if (state->pdata->fiq_ack) ++ state->pdata->fiq_ack(state->pdev, state->fiq); ++ ++ /* poke sleep timer if necessary */ ++ if (state->debug_enable && !state->no_sleep) ++ signal_helper = true; ++ ++ atomic_set(&state->unhandled_fiq_count, 0); ++ state->in_fiq = false; ++ ++ return signal_helper; ++} ++ ++#ifdef CONFIG_FIQ_GLUE ++static void fiq_debugger_fiq(struct fiq_glue_handler *h, ++ const struct pt_regs *regs, void *svc_sp) ++{ ++ struct fiq_debugger_state *state = ++ container_of(h, struct fiq_debugger_state, handler); ++ unsigned int this_cpu = THREAD_INFO(svc_sp)->cpu; ++ bool need_irq; ++ ++ need_irq = fiq_debugger_handle_uart_interrupt(state, this_cpu, regs, ++ svc_sp); ++ if (need_irq) ++ fiq_debugger_force_irq(state); ++} ++#endif ++ ++/* ++ * When not using FIQs, we only use this single interrupt as an entry point. ++ * This just effectively takes over the UART interrupt and does all the work ++ * in this context. ++ */ ++static irqreturn_t fiq_debugger_uart_irq(int irq, void *dev) ++{ ++ struct fiq_debugger_state *state = dev; ++ bool not_done; ++ ++ fiq_debugger_handle_wakeup(state); ++ ++ /* handle the debugger irq in regular context */ ++ not_done = fiq_debugger_handle_uart_interrupt(state, smp_processor_id(), ++ get_irq_regs(), ++ current_thread_info()); ++ if (not_done) ++ fiq_debugger_handle_irq_context(state); ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * If FIQs are used, not everything can happen in fiq context. ++ * FIQ handler does what it can and then signals this interrupt to finish the ++ * job in irq context. ++ */ ++static irqreturn_t fiq_debugger_signal_irq(int irq, void *dev) ++{ ++ struct fiq_debugger_state *state = dev; ++ ++ if (state->pdata->force_irq_ack) ++ state->pdata->force_irq_ack(state->pdev, state->signal_irq); ++ ++ fiq_debugger_handle_irq_context(state); ++ ++ return IRQ_HANDLED; ++} ++ ++#ifdef CONFIG_FIQ_GLUE ++static void fiq_debugger_resume(struct fiq_glue_handler *h) ++{ ++ struct fiq_debugger_state *state = ++ container_of(h, struct fiq_debugger_state, handler); ++ if (state->pdata->uart_resume) ++ state->pdata->uart_resume(state->pdev); ++} ++#endif ++ ++#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) ++struct tty_driver *fiq_debugger_console_device(struct console *co, int *index) ++{ ++ *index = co->index; ++ return fiq_tty_driver; ++} ++ ++static void fiq_debugger_console_write(struct console *co, ++ const char *s, unsigned int count) ++{ ++ struct fiq_debugger_state *state; ++ unsigned long flags; ++ ++ state = container_of(co, struct fiq_debugger_state, console); ++ ++ if (!state->console_enable && !state->syslog_dumping) ++ return; ++ ++ fiq_debugger_uart_enable(state); ++ spin_lock_irqsave(&state->console_lock, flags); ++ while (count--) { ++ if (*s == '\n') ++ fiq_debugger_putc(state, '\r'); ++ fiq_debugger_putc(state, *s++); ++ } ++ fiq_debugger_uart_flush(state); ++ spin_unlock_irqrestore(&state->console_lock, flags); ++ fiq_debugger_uart_disable(state); ++} ++ ++static struct console fiq_debugger_console = { ++ .name = "ttyFIQ", ++ .device = fiq_debugger_console_device, ++ .write = fiq_debugger_console_write, ++ .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED, ++}; ++ ++int fiq_tty_open(struct tty_struct *tty, struct file *filp) ++{ ++ int line = tty->index; ++ struct fiq_debugger_state **states = tty->driver->driver_state; ++ struct fiq_debugger_state *state = states[line]; ++ ++ return tty_port_open(&state->tty_port, tty, filp); ++} ++ ++void fiq_tty_close(struct tty_struct *tty, struct file *filp) ++{ ++ tty_port_close(tty->port, tty, filp); ++} ++ ++int fiq_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) ++{ ++ int i; ++ int line = tty->index; ++ struct fiq_debugger_state **states = tty->driver->driver_state; ++ struct fiq_debugger_state *state = states[line]; ++ ++ if (!state->console_enable) ++ return count; ++ ++ fiq_debugger_uart_enable(state); ++ spin_lock_irq(&state->console_lock); ++ for (i = 0; i < count; i++) ++ fiq_debugger_putc(state, *buf++); ++ spin_unlock_irq(&state->console_lock); ++ fiq_debugger_uart_disable(state); ++ ++ return count; ++} ++ ++int fiq_tty_write_room(struct tty_struct *tty) ++{ ++ return 16; ++} ++ ++#ifdef CONFIG_CONSOLE_POLL ++static int fiq_tty_poll_init(struct tty_driver *driver, int line, char *options) ++{ ++ return 0; ++} ++ ++static int fiq_tty_poll_get_char(struct tty_driver *driver, int line) ++{ ++ struct fiq_debugger_state **states = driver->driver_state; ++ struct fiq_debugger_state *state = states[line]; ++ int c = NO_POLL_CHAR; ++ ++ fiq_debugger_uart_enable(state); ++ if (fiq_debugger_have_fiq(state)) { ++ int count = fiq_debugger_ringbuf_level(state->tty_rbuf); ++ if (count > 0) { ++ c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0); ++ fiq_debugger_ringbuf_consume(state->tty_rbuf, 1); ++ } ++ } else { ++ c = fiq_debugger_getc(state); ++ if (c == FIQ_DEBUGGER_NO_CHAR) ++ c = NO_POLL_CHAR; ++ } ++ fiq_debugger_uart_disable(state); ++ ++ return c; ++} ++ ++static void fiq_tty_poll_put_char(struct tty_driver *driver, int line, char ch) ++{ ++ struct fiq_debugger_state **states = driver->driver_state; ++ struct fiq_debugger_state *state = states[line]; ++ fiq_debugger_uart_enable(state); ++ fiq_debugger_putc(state, ch); ++ fiq_debugger_uart_disable(state); ++} ++#endif ++ ++static const struct tty_port_operations fiq_tty_port_ops; ++ ++static const struct tty_operations fiq_tty_driver_ops = { ++ .write = fiq_tty_write, ++ .write_room = fiq_tty_write_room, ++ .open = fiq_tty_open, ++ .close = fiq_tty_close, ++#ifdef CONFIG_CONSOLE_POLL ++ .poll_init = fiq_tty_poll_init, ++ .poll_get_char = fiq_tty_poll_get_char, ++ .poll_put_char = fiq_tty_poll_put_char, ++#endif ++}; ++ ++static int fiq_debugger_tty_init(void) ++{ ++ int ret; ++ struct fiq_debugger_state **states = NULL; ++ ++ states = kzalloc(sizeof(*states) * MAX_FIQ_DEBUGGER_PORTS, GFP_KERNEL); ++ if (!states) { ++ pr_err("Failed to allocate fiq debugger state structres\n"); ++ return -ENOMEM; ++ } ++ ++ fiq_tty_driver = alloc_tty_driver(MAX_FIQ_DEBUGGER_PORTS); ++ if (!fiq_tty_driver) { ++ pr_err("Failed to allocate fiq debugger tty\n"); ++ ret = -ENOMEM; ++ goto err_free_state; ++ } ++ ++ fiq_tty_driver->owner = THIS_MODULE; ++ fiq_tty_driver->driver_name = "fiq-debugger"; ++ fiq_tty_driver->name = "ttyFIQ"; ++ fiq_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; ++ fiq_tty_driver->subtype = SERIAL_TYPE_NORMAL; ++ fiq_tty_driver->init_termios = tty_std_termios; ++ fiq_tty_driver->flags = TTY_DRIVER_REAL_RAW | ++ TTY_DRIVER_DYNAMIC_DEV; ++ fiq_tty_driver->driver_state = states; ++ ++ fiq_tty_driver->init_termios.c_cflag = ++ B115200 | CS8 | CREAD | HUPCL | CLOCAL; ++ fiq_tty_driver->init_termios.c_ispeed = 115200; ++ fiq_tty_driver->init_termios.c_ospeed = 115200; ++ ++ tty_set_operations(fiq_tty_driver, &fiq_tty_driver_ops); ++ ++ ret = tty_register_driver(fiq_tty_driver); ++ if (ret) { ++ pr_err("Failed to register fiq tty: %d\n", ret); ++ goto err_free_tty; ++ } ++ ++ pr_info("Registered FIQ tty driver\n"); ++ return 0; ++ ++err_free_tty: ++ put_tty_driver(fiq_tty_driver); ++ fiq_tty_driver = NULL; ++err_free_state: ++ kfree(states); ++ return ret; ++} ++ ++static int fiq_debugger_tty_init_one(struct fiq_debugger_state *state) ++{ ++ int ret; ++ struct device *tty_dev; ++ struct fiq_debugger_state **states = fiq_tty_driver->driver_state; ++ ++ states[state->pdev->id] = state; ++ ++ state->tty_rbuf = fiq_debugger_ringbuf_alloc(1024); ++ if (!state->tty_rbuf) { ++ pr_err("Failed to allocate fiq debugger ringbuf\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ tty_port_init(&state->tty_port); ++ state->tty_port.ops = &fiq_tty_port_ops; ++ ++ tty_dev = tty_port_register_device(&state->tty_port, fiq_tty_driver, ++ state->pdev->id, &state->pdev->dev); ++ if (IS_ERR(tty_dev)) { ++ pr_err("Failed to register fiq debugger tty device\n"); ++ ret = PTR_ERR(tty_dev); ++ goto err; ++ } ++ ++ device_set_wakeup_capable(tty_dev, 1); ++ ++ pr_info("Registered fiq debugger ttyFIQ%d\n", state->pdev->id); ++ ++ return 0; ++ ++err: ++ fiq_debugger_ringbuf_free(state->tty_rbuf); ++ state->tty_rbuf = NULL; ++ return ret; ++} ++#endif ++ ++static int fiq_debugger_dev_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct fiq_debugger_state *state = platform_get_drvdata(pdev); ++ ++ if (state->pdata->uart_dev_suspend) ++ return state->pdata->uart_dev_suspend(pdev); ++ return 0; ++} ++ ++static int fiq_debugger_dev_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct fiq_debugger_state *state = platform_get_drvdata(pdev); ++ ++ if (state->pdata->uart_dev_resume) ++ return state->pdata->uart_dev_resume(pdev); ++ return 0; ++} ++ ++static int fiq_debugger_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct fiq_debugger_pdata *pdata = dev_get_platdata(&pdev->dev); ++ struct fiq_debugger_state *state; ++ int fiq; ++ int uart_irq; ++ ++ if (pdev->id >= MAX_FIQ_DEBUGGER_PORTS) ++ return -EINVAL; ++ ++ if (!pdata->uart_getc || !pdata->uart_putc) ++ return -EINVAL; ++ if ((pdata->uart_enable && !pdata->uart_disable) || ++ (!pdata->uart_enable && pdata->uart_disable)) ++ return -EINVAL; ++ ++ fiq = platform_get_irq_byname(pdev, "fiq"); ++ uart_irq = platform_get_irq_byname(pdev, "uart_irq"); ++ ++ /* uart_irq mode and fiq mode are mutually exclusive, but one of them ++ * is required */ ++ if ((uart_irq < 0 && fiq < 0) || (uart_irq >= 0 && fiq >= 0)) ++ return -EINVAL; ++ if (fiq >= 0 && !pdata->fiq_enable) ++ return -EINVAL; ++ ++ state = kzalloc(sizeof(*state), GFP_KERNEL); ++ state->output.printf = fiq_debugger_printf; ++ setup_timer(&state->sleep_timer, fiq_debugger_sleep_timer_expired, ++ (unsigned long)state); ++ state->pdata = pdata; ++ state->pdev = pdev; ++ state->no_sleep = initial_no_sleep; ++ state->debug_enable = initial_debug_enable; ++ state->console_enable = initial_console_enable; ++ ++ state->fiq = fiq; ++ state->uart_irq = uart_irq; ++ state->signal_irq = platform_get_irq_byname(pdev, "signal"); ++ state->wakeup_irq = platform_get_irq_byname(pdev, "wakeup"); ++ ++ INIT_WORK(&state->work, fiq_debugger_work); ++ spin_lock_init(&state->work_lock); ++ ++ platform_set_drvdata(pdev, state); ++ ++ spin_lock_init(&state->sleep_timer_lock); ++ ++ if (state->wakeup_irq < 0 && fiq_debugger_have_fiq(state)) ++ state->no_sleep = true; ++ state->ignore_next_wakeup_irq = !state->no_sleep; ++ ++ wake_lock_init(&state->debugger_wake_lock, ++ WAKE_LOCK_SUSPEND, "serial-debug"); ++ ++ state->clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(state->clk)) ++ state->clk = NULL; ++ ++ /* do not call pdata->uart_enable here since uart_init may still ++ * need to do some initialization before uart_enable can work. ++ * So, only try to manage the clock during init. ++ */ ++ if (state->clk) ++ clk_enable(state->clk); ++ ++ if (pdata->uart_init) { ++ ret = pdata->uart_init(pdev); ++ if (ret) ++ goto err_uart_init; ++ } ++ ++ fiq_debugger_printf_nfiq(state, ++ "<hit enter %sto activate fiq debugger>\n", ++ state->no_sleep ? "" : "twice "); ++ ++#ifdef CONFIG_FIQ_GLUE ++ if (fiq_debugger_have_fiq(state)) { ++ state->handler.fiq = fiq_debugger_fiq; ++ state->handler.resume = fiq_debugger_resume; ++ ret = fiq_glue_register_handler(&state->handler); ++ if (ret) { ++ pr_err("%s: could not install fiq handler\n", __func__); ++ goto err_register_irq; ++ } ++ ++ pdata->fiq_enable(pdev, state->fiq, 1); ++ } else ++#endif ++ { ++ ret = request_irq(state->uart_irq, fiq_debugger_uart_irq, ++ IRQF_NO_SUSPEND, "debug", state); ++ if (ret) { ++ pr_err("%s: could not install irq handler\n", __func__); ++ goto err_register_irq; ++ } ++ ++ /* for irq-only mode, we want this irq to wake us up, if it ++ * can. ++ */ ++ enable_irq_wake(state->uart_irq); ++ } ++ ++ if (state->clk) ++ clk_disable(state->clk); ++ ++ if (state->signal_irq >= 0) { ++ ret = request_irq(state->signal_irq, fiq_debugger_signal_irq, ++ IRQF_TRIGGER_RISING, "debug-signal", state); ++ if (ret) ++ pr_err("serial_debugger: could not install signal_irq"); ++ } ++ ++ if (state->wakeup_irq >= 0) { ++ ret = request_irq(state->wakeup_irq, ++ fiq_debugger_wakeup_irq_handler, ++ IRQF_TRIGGER_FALLING | IRQF_DISABLED, ++ "debug-wakeup", state); ++ if (ret) { ++ pr_err("serial_debugger: " ++ "could not install wakeup irq\n"); ++ state->wakeup_irq = -1; ++ } else { ++ ret = enable_irq_wake(state->wakeup_irq); ++ if (ret) { ++ pr_err("serial_debugger: " ++ "could not enable wakeup\n"); ++ state->wakeup_irq_no_set_wake = true; ++ } ++ } ++ } ++ if (state->no_sleep) ++ fiq_debugger_handle_wakeup(state); ++ ++#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) ++ spin_lock_init(&state->console_lock); ++ state->console = fiq_debugger_console; ++ state->console.index = pdev->id; ++ if (!console_set_on_cmdline) ++ add_preferred_console(state->console.name, ++ state->console.index, NULL); ++ register_console(&state->console); ++ fiq_debugger_tty_init_one(state); ++#endif ++ return 0; ++ ++err_register_irq: ++ if (pdata->uart_free) ++ pdata->uart_free(pdev); ++err_uart_init: ++ if (state->clk) ++ clk_disable(state->clk); ++ if (state->clk) ++ clk_put(state->clk); ++ wake_lock_destroy(&state->debugger_wake_lock); ++ platform_set_drvdata(pdev, NULL); ++ kfree(state); ++ return ret; ++} ++ ++static const struct dev_pm_ops fiq_debugger_dev_pm_ops = { ++ .suspend = fiq_debugger_dev_suspend, ++ .resume = fiq_debugger_dev_resume, ++}; ++ ++static struct platform_driver fiq_debugger_driver = { ++ .probe = fiq_debugger_probe, ++ .driver = { ++ .name = "fiq_debugger", ++ .pm = &fiq_debugger_dev_pm_ops, ++ }, ++}; ++ ++static int __init fiq_debugger_init(void) ++{ ++#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) ++ fiq_debugger_tty_init(); ++#endif ++ return platform_driver_register(&fiq_debugger_driver); ++} ++ ++postcore_initcall(fiq_debugger_init); +diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.h b/drivers/staging/android/fiq_debugger/fiq_debugger.h +new file mode 100644 +index 0000000..c9ec4f8 +--- /dev/null ++++ b/drivers/staging/android/fiq_debugger/fiq_debugger.h +@@ -0,0 +1,64 @@ ++/* ++ * drivers/staging/android/fiq_debugger/fiq_debugger.h ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * Author: Colin Cross <ccross@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_ ++#define _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_ ++ ++#include <linux/serial_core.h> ++ ++#define FIQ_DEBUGGER_NO_CHAR NO_POLL_CHAR ++#define FIQ_DEBUGGER_BREAK 0x00ff0100 ++ ++#define FIQ_DEBUGGER_FIQ_IRQ_NAME "fiq" ++#define FIQ_DEBUGGER_SIGNAL_IRQ_NAME "signal" ++#define FIQ_DEBUGGER_WAKEUP_IRQ_NAME "wakeup" ++ ++/** ++ * struct fiq_debugger_pdata - fiq debugger platform data ++ * @uart_resume: used to restore uart state right before enabling ++ * the fiq. ++ * @uart_enable: Do the work necessary to communicate with the uart ++ * hw (enable clocks, etc.). This must be ref-counted. ++ * @uart_disable: Do the work necessary to disable the uart hw ++ * (disable clocks, etc.). This must be ref-counted. ++ * @uart_dev_suspend: called during PM suspend, generally not needed ++ * for real fiq mode debugger. ++ * @uart_dev_resume: called during PM resume, generally not needed ++ * for real fiq mode debugger. ++ */ ++struct fiq_debugger_pdata { ++ int (*uart_init)(struct platform_device *pdev); ++ void (*uart_free)(struct platform_device *pdev); ++ int (*uart_resume)(struct platform_device *pdev); ++ int (*uart_getc)(struct platform_device *pdev); ++ void (*uart_putc)(struct platform_device *pdev, unsigned int c); ++ void (*uart_flush)(struct platform_device *pdev); ++ void (*uart_enable)(struct platform_device *pdev); ++ void (*uart_disable)(struct platform_device *pdev); ++ ++ int (*uart_dev_suspend)(struct platform_device *pdev); ++ int (*uart_dev_resume)(struct platform_device *pdev); ++ ++ void (*fiq_enable)(struct platform_device *pdev, unsigned int fiq, ++ bool enable); ++ void (*fiq_ack)(struct platform_device *pdev, unsigned int fiq); ++ ++ void (*force_irq)(struct platform_device *pdev, unsigned int irq); ++ void (*force_irq_ack)(struct platform_device *pdev, unsigned int irq); ++}; ++ ++#endif +diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_arm.c b/drivers/staging/android/fiq_debugger/fiq_debugger_arm.c +new file mode 100644 +index 0000000..8b3e013 +--- /dev/null ++++ b/drivers/staging/android/fiq_debugger/fiq_debugger_arm.c +@@ -0,0 +1,240 @@ ++/* ++ * Copyright (C) 2014 Google, Inc. ++ * Author: Colin Cross <ccross@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/ptrace.h> ++#include <linux/uaccess.h> ++ ++#include <asm/stacktrace.h> ++ ++#include "fiq_debugger_priv.h" ++ ++static char *mode_name(unsigned cpsr) ++{ ++ switch (cpsr & MODE_MASK) { ++ case USR_MODE: return "USR"; ++ case FIQ_MODE: return "FIQ"; ++ case IRQ_MODE: return "IRQ"; ++ case SVC_MODE: return "SVC"; ++ case ABT_MODE: return "ABT"; ++ case UND_MODE: return "UND"; ++ case SYSTEM_MODE: return "SYS"; ++ default: return "???"; ++ } ++} ++ ++void fiq_debugger_dump_pc(struct fiq_debugger_output *output, ++ const struct pt_regs *regs) ++{ ++ output->printf(output, " pc %08x cpsr %08x mode %s\n", ++ regs->ARM_pc, regs->ARM_cpsr, mode_name(regs->ARM_cpsr)); ++} ++ ++void fiq_debugger_dump_regs(struct fiq_debugger_output *output, ++ const struct pt_regs *regs) ++{ ++ output->printf(output, ++ " r0 %08x r1 %08x r2 %08x r3 %08x\n", ++ regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); ++ output->printf(output, ++ " r4 %08x r5 %08x r6 %08x r7 %08x\n", ++ regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7); ++ output->printf(output, ++ " r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n", ++ regs->ARM_r8, regs->ARM_r9, regs->ARM_r10, regs->ARM_fp, ++ mode_name(regs->ARM_cpsr)); ++ output->printf(output, ++ " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", ++ regs->ARM_ip, regs->ARM_sp, regs->ARM_lr, regs->ARM_pc, ++ regs->ARM_cpsr); ++} ++ ++struct mode_regs { ++ unsigned long sp_svc; ++ unsigned long lr_svc; ++ unsigned long spsr_svc; ++ ++ unsigned long sp_abt; ++ unsigned long lr_abt; ++ unsigned long spsr_abt; ++ ++ unsigned long sp_und; ++ unsigned long lr_und; ++ unsigned long spsr_und; ++ ++ unsigned long sp_irq; ++ unsigned long lr_irq; ++ unsigned long spsr_irq; ++ ++ unsigned long r8_fiq; ++ unsigned long r9_fiq; ++ unsigned long r10_fiq; ++ unsigned long r11_fiq; ++ unsigned long r12_fiq; ++ unsigned long sp_fiq; ++ unsigned long lr_fiq; ++ unsigned long spsr_fiq; ++}; ++ ++static void __naked get_mode_regs(struct mode_regs *regs) ++{ ++ asm volatile ( ++ "mrs r1, cpsr\n" ++ "msr cpsr_c, #0xd3 @(SVC_MODE | PSR_I_BIT | PSR_F_BIT)\n" ++ "stmia r0!, {r13 - r14}\n" ++ "mrs r2, spsr\n" ++ "msr cpsr_c, #0xd7 @(ABT_MODE | PSR_I_BIT | PSR_F_BIT)\n" ++ "stmia r0!, {r2, r13 - r14}\n" ++ "mrs r2, spsr\n" ++ "msr cpsr_c, #0xdb @(UND_MODE | PSR_I_BIT | PSR_F_BIT)\n" ++ "stmia r0!, {r2, r13 - r14}\n" ++ "mrs r2, spsr\n" ++ "msr cpsr_c, #0xd2 @(IRQ_MODE | PSR_I_BIT | PSR_F_BIT)\n" ++ "stmia r0!, {r2, r13 - r14}\n" ++ "mrs r2, spsr\n" ++ "msr cpsr_c, #0xd1 @(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)\n" ++ "stmia r0!, {r2, r8 - r14}\n" ++ "mrs r2, spsr\n" ++ "stmia r0!, {r2}\n" ++ "msr cpsr_c, r1\n" ++ "bx lr\n"); ++} ++ ++ ++void fiq_debugger_dump_allregs(struct fiq_debugger_output *output, ++ const struct pt_regs *regs) ++{ ++ struct mode_regs mode_regs; ++ unsigned long mode = regs->ARM_cpsr & MODE_MASK; ++ ++ fiq_debugger_dump_regs(output, regs); ++ get_mode_regs(&mode_regs); ++ ++ output->printf(output, ++ "%csvc: sp %08x lr %08x spsr %08x\n", ++ mode == SVC_MODE ? '*' : ' ', ++ mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc); ++ output->printf(output, ++ "%cabt: sp %08x lr %08x spsr %08x\n", ++ mode == ABT_MODE ? '*' : ' ', ++ mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt); ++ output->printf(output, ++ "%cund: sp %08x lr %08x spsr %08x\n", ++ mode == UND_MODE ? '*' : ' ', ++ mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und); ++ output->printf(output, ++ "%cirq: sp %08x lr %08x spsr %08x\n", ++ mode == IRQ_MODE ? '*' : ' ', ++ mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq); ++ output->printf(output, ++ "%cfiq: r8 %08x r9 %08x r10 %08x r11 %08x r12 %08x\n", ++ mode == FIQ_MODE ? '*' : ' ', ++ mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq, ++ mode_regs.r11_fiq, mode_regs.r12_fiq); ++ output->printf(output, ++ " fiq: sp %08x lr %08x spsr %08x\n", ++ mode_regs.sp_fiq, mode_regs.lr_fiq, mode_regs.spsr_fiq); ++} ++ ++struct stacktrace_state { ++ struct fiq_debugger_output *output; ++ unsigned int depth; ++}; ++ ++static int report_trace(struct stackframe *frame, void *d) ++{ ++ struct stacktrace_state *sts = d; ++ ++ if (sts->depth) { ++ sts->output->printf(sts->output, ++ " pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n", ++ frame->pc, frame->pc, frame->lr, frame->lr, ++ frame->sp, frame->fp); ++ sts->depth--; ++ return 0; ++ } ++ sts->output->printf(sts->output, " ...\n"); ++ ++ return sts->depth == 0; ++} ++ ++struct frame_tail { ++ struct frame_tail *fp; ++ unsigned long sp; ++ unsigned long lr; ++} __attribute__((packed)); ++ ++static struct frame_tail *user_backtrace(struct fiq_debugger_output *output, ++ struct frame_tail *tail) ++{ ++ struct frame_tail buftail[2]; ++ ++ /* Also check accessibility of one struct frame_tail beyond */ ++ if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) { ++ output->printf(output, " invalid frame pointer %p\n", ++ tail); ++ return NULL; ++ } ++ if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) { ++ output->printf(output, ++ " failed to copy frame pointer %p\n", tail); ++ return NULL; ++ } ++ ++ output->printf(output, " %p\n", buftail[0].lr); ++ ++ /* frame pointers should strictly progress back up the stack ++ * (towards higher addresses) */ ++ if (tail >= buftail[0].fp) ++ return NULL; ++ ++ return buftail[0].fp-1; ++} ++ ++void fiq_debugger_dump_stacktrace(struct fiq_debugger_output *output, ++ const struct pt_regs *regs, unsigned int depth, void *ssp) ++{ ++ struct frame_tail *tail; ++ struct thread_info *real_thread_info = THREAD_INFO(ssp); ++ struct stacktrace_state sts; ++ ++ sts.depth = depth; ++ sts.output = output; ++ *current_thread_info() = *real_thread_info; ++ ++ if (!current) ++ output->printf(output, "current NULL\n"); ++ else ++ output->printf(output, "pid: %d comm: %s\n", ++ current->pid, current->comm); ++ fiq_debugger_dump_regs(output, regs); ++ ++ if (!user_mode(regs)) { ++ struct stackframe frame; ++ frame.fp = regs->ARM_fp; ++ frame.sp = regs->ARM_sp; ++ frame.lr = regs->ARM_lr; ++ frame.pc = regs->ARM_pc; ++ output->printf(output, ++ " pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n", ++ regs->ARM_pc, regs->ARM_pc, regs->ARM_lr, regs->ARM_lr, ++ regs->ARM_sp, regs->ARM_fp); ++ walk_stackframe(&frame, report_trace, &sts); ++ return; ++ } ++ ++ tail = ((struct frame_tail *) regs->ARM_fp) - 1; ++ while (depth-- && tail && !((unsigned long) tail & 3)) ++ tail = user_backtrace(output, tail); ++} +diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c b/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c +new file mode 100644 +index 0000000..99c6584 +--- /dev/null ++++ b/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c +@@ -0,0 +1,202 @@ ++/* ++ * Copyright (C) 2014 Google, Inc. ++ * Author: Colin Cross <ccross@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/ptrace.h> ++#include <asm/stacktrace.h> ++ ++#include "fiq_debugger_priv.h" ++ ++static char *mode_name(const struct pt_regs *regs) ++{ ++ if (compat_user_mode(regs)) { ++ return "USR"; ++ } else { ++ switch (processor_mode(regs)) { ++ case PSR_MODE_EL0t: return "EL0t"; ++ case PSR_MODE_EL1t: return "EL1t"; ++ case PSR_MODE_EL1h: return "EL1h"; ++ case PSR_MODE_EL2t: return "EL2t"; ++ case PSR_MODE_EL2h: return "EL2h"; ++ default: return "???"; ++ } ++ } ++} ++ ++void fiq_debugger_dump_pc(struct fiq_debugger_output *output, ++ const struct pt_regs *regs) ++{ ++ output->printf(output, " pc %016lx cpsr %08lx mode %s\n", ++ regs->pc, regs->pstate, mode_name(regs)); ++} ++ ++void fiq_debugger_dump_regs_aarch32(struct fiq_debugger_output *output, ++ const struct pt_regs *regs) ++{ ++ output->printf(output, " r0 %08x r1 %08x r2 %08x r3 %08x\n", ++ regs->compat_usr(0), regs->compat_usr(1), ++ regs->compat_usr(2), regs->compat_usr(3)); ++ output->printf(output, " r4 %08x r5 %08x r6 %08x r7 %08x\n", ++ regs->compat_usr(4), regs->compat_usr(5), ++ regs->compat_usr(6), regs->compat_usr(7)); ++ output->printf(output, " r8 %08x r9 %08x r10 %08x r11 %08x\n", ++ regs->compat_usr(8), regs->compat_usr(9), ++ regs->compat_usr(10), regs->compat_usr(11)); ++ output->printf(output, " ip %08x sp %08x lr %08x pc %08x\n", ++ regs->compat_usr(12), regs->compat_sp, ++ regs->compat_lr, regs->pc); ++ output->printf(output, " cpsr %08x (%s)\n", ++ regs->pstate, mode_name(regs)); ++} ++ ++void fiq_debugger_dump_regs_aarch64(struct fiq_debugger_output *output, ++ const struct pt_regs *regs) ++{ ++ ++ output->printf(output, " x0 %016lx x1 %016lx\n", ++ regs->regs[0], regs->regs[1]); ++ output->printf(output, " x2 %016lx x3 %016lx\n", ++ regs->regs[2], regs->regs[3]); ++ output->printf(output, " x4 %016lx x5 %016lx\n", ++ regs->regs[4], regs->regs[5]); ++ output->printf(output, " x6 %016lx x7 %016lx\n", ++ regs->regs[6], regs->regs[7]); ++ output->printf(output, " x8 %016lx x9 %016lx\n", ++ regs->regs[8], regs->regs[9]); ++ output->printf(output, " x10 %016lx x11 %016lx\n", ++ regs->regs[10], regs->regs[11]); ++ output->printf(output, " x12 %016lx x13 %016lx\n", ++ regs->regs[12], regs->regs[13]); ++ output->printf(output, " x14 %016lx x15 %016lx\n", ++ regs->regs[14], regs->regs[15]); ++ output->printf(output, " x16 %016lx x17 %016lx\n", ++ regs->regs[16], regs->regs[17]); ++ output->printf(output, " x18 %016lx x19 %016lx\n", ++ regs->regs[18], regs->regs[19]); ++ output->printf(output, " x20 %016lx x21 %016lx\n", ++ regs->regs[20], regs->regs[21]); ++ output->printf(output, " x22 %016lx x23 %016lx\n", ++ regs->regs[22], regs->regs[23]); ++ output->printf(output, " x24 %016lx x25 %016lx\n", ++ regs->regs[24], regs->regs[25]); ++ output->printf(output, " x26 %016lx x27 %016lx\n", ++ regs->regs[26], regs->regs[27]); ++ output->printf(output, " x28 %016lx x29 %016lx\n", ++ regs->regs[28], regs->regs[29]); ++ output->printf(output, " x30 %016lx sp %016lx\n", ++ regs->regs[30], regs->sp); ++ output->printf(output, " pc %016lx cpsr %08x (%s)\n", ++ regs->pc, regs->pstate, mode_name(regs)); ++} ++ ++void fiq_debugger_dump_regs(struct fiq_debugger_output *output, ++ const struct pt_regs *regs) ++{ ++ if (compat_user_mode(regs)) ++ fiq_debugger_dump_regs_aarch32(output, regs); ++ else ++ fiq_debugger_dump_regs_aarch64(output, regs); ++} ++ ++#define READ_SPECIAL_REG(x) ({ \ ++ u64 val; \ ++ asm volatile ("mrs %0, " # x : "=r"(val)); \ ++ val; \ ++}) ++ ++void fiq_debugger_dump_allregs(struct fiq_debugger_output *output, ++ const struct pt_regs *regs) ++{ ++ u32 pstate = READ_SPECIAL_REG(CurrentEl); ++ bool in_el2 = (pstate & PSR_MODE_MASK) >= PSR_MODE_EL2t; ++ ++ fiq_debugger_dump_regs(output, regs); ++ ++ output->printf(output, " sp_el0 %016lx\n", ++ READ_SPECIAL_REG(sp_el0)); ++ ++ if (in_el2) ++ output->printf(output, " sp_el1 %016lx\n", ++ READ_SPECIAL_REG(sp_el1)); ++ ++ output->printf(output, " elr_el1 %016lx\n", ++ READ_SPECIAL_REG(elr_el1)); ++ ++ output->printf(output, " spsr_el1 %08lx\n", ++ READ_SPECIAL_REG(spsr_el1)); ++ ++ if (in_el2) { ++ output->printf(output, " spsr_irq %08lx\n", ++ READ_SPECIAL_REG(spsr_irq)); ++ output->printf(output, " spsr_abt %08lx\n", ++ READ_SPECIAL_REG(spsr_abt)); ++ output->printf(output, " spsr_und %08lx\n", ++ READ_SPECIAL_REG(spsr_und)); ++ output->printf(output, " spsr_fiq %08lx\n", ++ READ_SPECIAL_REG(spsr_fiq)); ++ output->printf(output, " spsr_el2 %08lx\n", ++ READ_SPECIAL_REG(elr_el2)); ++ output->printf(output, " spsr_el2 %08lx\n", ++ READ_SPECIAL_REG(spsr_el2)); ++ } ++} ++ ++struct stacktrace_state { ++ struct fiq_debugger_output *output; ++ unsigned int depth; ++}; ++ ++static int report_trace(struct stackframe *frame, void *d) ++{ ++ struct stacktrace_state *sts = d; ++ ++ if (sts->depth) { ++ sts->output->printf(sts->output, "%pF:\n", frame->pc); ++ sts->output->printf(sts->output, ++ " pc %016lx sp %016lx fp %016lx\n", ++ frame->pc, frame->sp, frame->fp); ++ sts->depth--; ++ return 0; ++ } ++ sts->output->printf(sts->output, " ...\n"); ++ ++ return sts->depth == 0; ++} ++ ++void fiq_debugger_dump_stacktrace(struct fiq_debugger_output *output, ++ const struct pt_regs *regs, unsigned int depth, void *ssp) ++{ ++ struct thread_info *real_thread_info = THREAD_INFO(ssp); ++ struct stacktrace_state sts; ++ ++ sts.depth = depth; ++ sts.output = output; ++ *current_thread_info() = *real_thread_info; ++ ++ if (!current) ++ output->printf(output, "current NULL\n"); ++ else ++ output->printf(output, "pid: %d comm: %s\n", ++ current->pid, current->comm); ++ fiq_debugger_dump_regs(output, regs); ++ ++ if (!user_mode(regs)) { ++ struct stackframe frame; ++ frame.fp = regs->regs[29]; ++ frame.sp = regs->sp; ++ frame.pc = regs->pc; ++ output->printf(output, "\n"); ++ walk_stackframe(&frame, report_trace, &sts); ++ } ++} +diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_priv.h b/drivers/staging/android/fiq_debugger/fiq_debugger_priv.h +new file mode 100644 +index 0000000..d5d051f +--- /dev/null ++++ b/drivers/staging/android/fiq_debugger/fiq_debugger_priv.h +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (C) 2014 Google, Inc. ++ * Author: Colin Cross <ccross@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _FIQ_DEBUGGER_PRIV_H_ ++#define _FIQ_DEBUGGER_PRIV_H_ ++ ++#define THREAD_INFO(sp) ((struct thread_info *) \ ++ ((unsigned long)(sp) & ~(THREAD_SIZE - 1))) ++ ++struct fiq_debugger_output { ++ void (*printf)(struct fiq_debugger_output *output, const char *fmt, ...); ++}; ++ ++struct pt_regs; ++ ++void fiq_debugger_dump_pc(struct fiq_debugger_output *output, ++ const struct pt_regs *regs); ++void fiq_debugger_dump_regs(struct fiq_debugger_output *output, ++ const struct pt_regs *regs); ++void fiq_debugger_dump_allregs(struct fiq_debugger_output *output, ++ const struct pt_regs *regs); ++void fiq_debugger_dump_stacktrace(struct fiq_debugger_output *output, ++ const struct pt_regs *regs, unsigned int depth, void *ssp); ++ ++#endif +diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_ringbuf.h b/drivers/staging/android/fiq_debugger/fiq_debugger_ringbuf.h +new file mode 100644 +index 0000000..10c3c5d +--- /dev/null ++++ b/drivers/staging/android/fiq_debugger/fiq_debugger_ringbuf.h +@@ -0,0 +1,94 @@ ++/* ++ * drivers/staging/android/fiq_debugger/fiq_debugger_ringbuf.h ++ * ++ * simple lockless ringbuffer ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++ ++struct fiq_debugger_ringbuf { ++ int len; ++ int head; ++ int tail; ++ u8 buf[]; ++}; ++ ++ ++static inline struct fiq_debugger_ringbuf *fiq_debugger_ringbuf_alloc(int len) ++{ ++ struct fiq_debugger_ringbuf *rbuf; ++ ++ rbuf = kzalloc(sizeof(*rbuf) + len, GFP_KERNEL); ++ if (rbuf == NULL) ++ return NULL; ++ ++ rbuf->len = len; ++ rbuf->head = 0; ++ rbuf->tail = 0; ++ smp_mb(); ++ ++ return rbuf; ++} ++ ++static inline void fiq_debugger_ringbuf_free(struct fiq_debugger_ringbuf *rbuf) ++{ ++ kfree(rbuf); ++} ++ ++static inline int fiq_debugger_ringbuf_level(struct fiq_debugger_ringbuf *rbuf) ++{ ++ int level = rbuf->head - rbuf->tail; ++ ++ if (level < 0) ++ level = rbuf->len + level; ++ ++ return level; ++} ++ ++static inline int fiq_debugger_ringbuf_room(struct fiq_debugger_ringbuf *rbuf) ++{ ++ return rbuf->len - fiq_debugger_ringbuf_level(rbuf) - 1; ++} ++ ++static inline u8 ++fiq_debugger_ringbuf_peek(struct fiq_debugger_ringbuf *rbuf, int i) ++{ ++ return rbuf->buf[(rbuf->tail + i) % rbuf->len]; ++} ++ ++static inline int ++fiq_debugger_ringbuf_consume(struct fiq_debugger_ringbuf *rbuf, int count) ++{ ++ count = min(count, fiq_debugger_ringbuf_level(rbuf)); ++ ++ rbuf->tail = (rbuf->tail + count) % rbuf->len; ++ smp_mb(); ++ ++ return count; ++} ++ ++static inline int ++fiq_debugger_ringbuf_push(struct fiq_debugger_ringbuf *rbuf, u8 datum) ++{ ++ if (fiq_debugger_ringbuf_room(rbuf) == 0) ++ return 0; ++ ++ rbuf->buf[rbuf->head] = datum; ++ smp_mb(); ++ rbuf->head = (rbuf->head + 1) % rbuf->len; ++ smp_mb(); ++ ++ return 1; ++} +diff --git a/drivers/staging/android/fiq_debugger/fiq_watchdog.c b/drivers/staging/android/fiq_debugger/fiq_watchdog.c +new file mode 100644 +index 0000000..194b541 +--- /dev/null ++++ b/drivers/staging/android/fiq_debugger/fiq_watchdog.c +@@ -0,0 +1,56 @@ ++/* ++ * Copyright (C) 2014 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/spinlock.h> ++#include <linux/pstore_ram.h> ++ ++#include "fiq_watchdog.h" ++#include "fiq_debugger_priv.h" ++ ++static DEFINE_RAW_SPINLOCK(fiq_watchdog_lock); ++ ++static void fiq_watchdog_printf(struct fiq_debugger_output *output, ++ const char *fmt, ...) ++{ ++ char buf[256]; ++ va_list ap; ++ int len; ++ ++ va_start(ap, fmt); ++ len = vscnprintf(buf, sizeof(buf), fmt, ap); ++ va_end(ap); ++ ++ ramoops_console_write_buf(buf, len); ++} ++ ++struct fiq_debugger_output fiq_watchdog_output = { ++ .printf = fiq_watchdog_printf, ++}; ++ ++void fiq_watchdog_triggered(const struct pt_regs *regs, void *svc_sp) ++{ ++ char msg[24]; ++ int len; ++ ++ raw_spin_lock(&fiq_watchdog_lock); ++ ++ len = scnprintf(msg, sizeof(msg), "watchdog fiq cpu %d\n", ++ THREAD_INFO(svc_sp)->cpu); ++ ramoops_console_write_buf(msg, len); ++ ++ fiq_debugger_dump_stacktrace(&fiq_watchdog_output, regs, 100, svc_sp); ++ ++ raw_spin_unlock(&fiq_watchdog_lock); ++} +diff --git a/drivers/staging/android/fiq_debugger/fiq_watchdog.h b/drivers/staging/android/fiq_debugger/fiq_watchdog.h +new file mode 100644 +index 0000000..c6b507f +--- /dev/null ++++ b/drivers/staging/android/fiq_debugger/fiq_watchdog.h +@@ -0,0 +1,20 @@ ++/* ++ * Copyright (C) 2014 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _FIQ_WATCHDOG_H_ ++#define _FIQ_WATCHDOG_H_ ++ ++void fiq_watchdog_triggered(const struct pt_regs *regs, void *svc_sp); ++ ++#endif +diff --git a/drivers/staging/android/ion/Kconfig b/drivers/staging/android/ion/Kconfig +index 3452346..301948c 100644 +--- a/drivers/staging/android/ion/Kconfig ++++ b/drivers/staging/android/ion/Kconfig +@@ -33,3 +33,10 @@ config ION_TEGRA + help + Choose this option if you wish to use ion on an nVidia Tegra. + ++config ION_POOL_CACHE_POLICY ++ bool "Ion set page pool cache policy" ++ depends on ION ++ default y if X86 ++ help ++ Choose this option if need to explicity set cache policy of the ++ pages in the page pool. +diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c +index 56604f4..8724ef8 100644 +--- a/drivers/staging/android/ion/ion.c ++++ b/drivers/staging/android/ion/ion.c +@@ -250,7 +250,7 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, + our systems the only dma_address space is physical addresses. + Additionally, we can't afford the overhead of invalidating every + allocation via dma_map_sg. The implicit contract here is that +- memory comming from the heaps is ready for dma, ie if it has a ++ memory coming from the heaps is ready for dma, ie if it has a + cached mapping that mapping has been invalidated */ + for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) + sg_dma_address(sg) = sg_phys(sg); +@@ -902,7 +902,7 @@ void ion_pages_sync_for_device(struct device *dev, struct page *page, + sg_set_page(&sg, page, size, 0); + /* + * This is not correct - sg_dma_address needs a dma_addr_t that is valid +- * for the the targeted device, but this works on the currently targeted ++ * for the targeted device, but this works on the currently targeted + * hardware. + */ + sg_dma_address(&sg) = page_to_phys(page); +diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h +index d305bb7..443db84 100644 +--- a/drivers/staging/android/ion/ion.h ++++ b/drivers/staging/android/ion/ion.h +@@ -76,7 +76,7 @@ struct ion_platform_data { + * size + * + * Calls memblock reserve to set aside memory for heaps that are +- * located at specific memory addresses or of specfic sizes not ++ * located at specific memory addresses or of specific sizes not + * managed by the kernel + */ + void ion_reserve(struct ion_platform_data *data); +diff --git a/drivers/staging/android/ion/ion_carveout_heap.c b/drivers/staging/android/ion/ion_carveout_heap.c +old mode 100644 +new mode 100755 +index 9156d82..e702ce6 +--- a/drivers/staging/android/ion/ion_carveout_heap.c ++++ b/drivers/staging/android/ion/ion_carveout_heap.c +@@ -167,7 +167,7 @@ struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) + if (!carveout_heap) + return ERR_PTR(-ENOMEM); + +- carveout_heap->pool = gen_pool_create(12, -1); ++ carveout_heap->pool = gen_pool_create(PAGE_SHIFT, -1); + if (!carveout_heap->pool) { + kfree(carveout_heap); + return ERR_PTR(-ENOMEM); +diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c +index 5864f3d..b021748 100644 +--- a/drivers/staging/android/ion/ion_page_pool.c ++++ b/drivers/staging/android/ion/ion_page_pool.c +@@ -30,6 +30,8 @@ static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool) + + if (!page) + return NULL; ++ ion_page_pool_alloc_set_cache_policy(pool, page); ++ + ion_pages_sync_for_device(NULL, page, PAGE_SIZE << pool->order, + DMA_BIDIRECTIONAL); + return page; +@@ -38,6 +40,7 @@ static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool) + static void ion_page_pool_free_pages(struct ion_page_pool *pool, + struct page *page) + { ++ ion_page_pool_free_set_cache_policy(pool, page); + __free_pages(page, pool->order); + } + +@@ -103,6 +106,11 @@ void ion_page_pool_free(struct ion_page_pool *pool, struct page *page) + ion_page_pool_free_pages(pool, page); + } + ++void ion_page_pool_free_immediate(struct ion_page_pool *pool, struct page *page) ++{ ++ ion_page_pool_free_pages(pool, page); ++} ++ + static int ion_page_pool_total(struct ion_page_pool *pool, bool high) + { + int count = pool->low_count; +@@ -120,7 +128,7 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask, + bool high; + + if (current_is_kswapd()) +- high = 1; ++ high = true; + else + high = !!(gfp_mask & __GFP_HIGHMEM); + +diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h +index c8f0175..0e3b8a6 100644 +--- a/drivers/staging/android/ion/ion_priv.h ++++ b/drivers/staging/android/ion/ion_priv.h +@@ -26,6 +26,9 @@ + #include <linux/sched.h> + #include <linux/shrinker.h> + #include <linux/types.h> ++#ifdef CONFIG_ION_POOL_CACHE_POLICY ++#include <asm/cacheflush.h> ++#endif + + #include "ion.h" + +@@ -345,7 +348,7 @@ void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, + * functions for creating and destroying a heap pool -- allows you + * to keep a pool of pre allocated memory to use from your heap. Keeping + * a pool of memory that is ready for dma, ie any cached mapping have been +- * invalidated from the cache, provides a significant peformance benefit on ++ * invalidated from the cache, provides a significant performance benefit on + * many systems */ + + /** +@@ -362,7 +365,7 @@ void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, + * + * Allows you to keep a pool of pre allocated pages to use from your heap. + * Keeping a pool of pages that is ready for dma, ie any cached mapping have +- * been invalidated from the cache, provides a significant peformance benefit ++ * been invalidated from the cache, provides a significant performance benefit + * on many systems + */ + struct ion_page_pool { +@@ -380,6 +383,37 @@ struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order); + void ion_page_pool_destroy(struct ion_page_pool *); + struct page *ion_page_pool_alloc(struct ion_page_pool *); + void ion_page_pool_free(struct ion_page_pool *, struct page *); ++void ion_page_pool_free_immediate(struct ion_page_pool *, struct page *); ++ ++#ifdef CONFIG_ION_POOL_CACHE_POLICY ++static inline void ion_page_pool_alloc_set_cache_policy ++ (struct ion_page_pool *pool, ++ struct page *page){ ++ void *va = page_address(page); ++ ++ if (va) ++ set_memory_wc((unsigned long)va, 1 << pool->order); ++} ++ ++static inline void ion_page_pool_free_set_cache_policy ++ (struct ion_page_pool *pool, ++ struct page *page){ ++ void *va = page_address(page); ++ ++ if (va) ++ set_memory_wb((unsigned long)va, 1 << pool->order); ++ ++} ++#else ++static inline void ion_page_pool_alloc_set_cache_policy ++ (struct ion_page_pool *pool, ++ struct page *page){ } ++ ++static inline void ion_page_pool_free_set_cache_policy ++ (struct ion_page_pool *pool, ++ struct page *page){ } ++#endif ++ + + /** ion_page_pool_shrink - shrinks the size of the memory cached in the pool + * @pool: the pool +diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c +index da2a63c..1f9feb7 100644 +--- a/drivers/staging/android/ion/ion_system_heap.c ++++ b/drivers/staging/android/ion/ion_system_heap.c +@@ -85,8 +85,10 @@ static void free_buffer_page(struct ion_system_heap *heap, + + if (!cached && !(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE)) { + struct ion_page_pool *pool = heap->pools[order_to_index(order)]; +- +- ion_page_pool_free(pool, page); ++ if (buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE) ++ ion_page_pool_free_immediate(pool, page); ++ else ++ ion_page_pool_free(pool, page); + } else { + __free_pages(page, order); + } +diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c +deleted file mode 100644 +index a673ffa..0000000 +--- a/drivers/staging/android/logger.c ++++ /dev/null +@@ -1,808 +0,0 @@ +-/* +- * drivers/misc/logger.c +- * +- * A Logging Subsystem +- * +- * Copyright (C) 2007-2008 Google, Inc. +- * +- * Robert Love <rlove@google.com> +- * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. +- * +- * 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. +- */ +- +-#define pr_fmt(fmt) "logger: " fmt +- +-#include <linux/sched.h> +-#include <linux/module.h> +-#include <linux/fs.h> +-#include <linux/miscdevice.h> +-#include <linux/uaccess.h> +-#include <linux/poll.h> +-#include <linux/slab.h> +-#include <linux/time.h> +-#include <linux/vmalloc.h> +-#include <linux/aio.h> +-#include "logger.h" +- +-#include <asm/ioctls.h> +- +-/** +- * struct logger_log - represents a specific log, such as 'main' or 'radio' +- * @buffer: The actual ring buffer +- * @misc: The "misc" device representing the log +- * @wq: The wait queue for @readers +- * @readers: This log's readers +- * @mutex: The mutex that protects the @buffer +- * @w_off: The current write head offset +- * @head: The head, or location that readers start reading at. +- * @size: The size of the log +- * @logs: The list of log channels +- * +- * This structure lives from module insertion until module removal, so it does +- * not need additional reference counting. The structure is protected by the +- * mutex 'mutex'. +- */ +-struct logger_log { +- unsigned char *buffer; +- struct miscdevice misc; +- wait_queue_head_t wq; +- struct list_head readers; +- struct mutex mutex; +- size_t w_off; +- size_t head; +- size_t size; +- struct list_head logs; +-}; +- +-static LIST_HEAD(log_list); +- +- +-/** +- * struct logger_reader - a logging device open for reading +- * @log: The associated log +- * @list: The associated entry in @logger_log's list +- * @r_off: The current read head offset. +- * @r_all: Reader can read all entries +- * @r_ver: Reader ABI version +- * +- * This object lives from open to release, so we don't need additional +- * reference counting. The structure is protected by log->mutex. +- */ +-struct logger_reader { +- struct logger_log *log; +- struct list_head list; +- size_t r_off; +- bool r_all; +- int r_ver; +-}; +- +-/* logger_offset - returns index 'n' into the log via (optimized) modulus */ +-static size_t logger_offset(struct logger_log *log, size_t n) +-{ +- return n & (log->size - 1); +-} +- +- +-/* +- * file_get_log - Given a file structure, return the associated log +- * +- * This isn't aesthetic. We have several goals: +- * +- * 1) Need to quickly obtain the associated log during an I/O operation +- * 2) Readers need to maintain state (logger_reader) +- * 3) Writers need to be very fast (open() should be a near no-op) +- * +- * In the reader case, we can trivially go file->logger_reader->logger_log. +- * For a writer, we don't want to maintain a logger_reader, so we just go +- * file->logger_log. Thus what file->private_data points at depends on whether +- * or not the file was opened for reading. This function hides that dirtiness. +- */ +-static inline struct logger_log *file_get_log(struct file *file) +-{ +- if (file->f_mode & FMODE_READ) { +- struct logger_reader *reader = file->private_data; +- +- return reader->log; +- } +- return file->private_data; +-} +- +-/* +- * get_entry_header - returns a pointer to the logger_entry header within +- * 'log' starting at offset 'off'. A temporary logger_entry 'scratch' must +- * be provided. Typically the return value will be a pointer within +- * 'logger->buf'. However, a pointer to 'scratch' may be returned if +- * the log entry spans the end and beginning of the circular buffer. +- */ +-static struct logger_entry *get_entry_header(struct logger_log *log, +- size_t off, struct logger_entry *scratch) +-{ +- size_t len = min(sizeof(struct logger_entry), log->size - off); +- +- if (len != sizeof(struct logger_entry)) { +- memcpy(((void *) scratch), log->buffer + off, len); +- memcpy(((void *) scratch) + len, log->buffer, +- sizeof(struct logger_entry) - len); +- return scratch; +- } +- +- return (struct logger_entry *) (log->buffer + off); +-} +- +-/* +- * get_entry_msg_len - Grabs the length of the message of the entry +- * starting from from 'off'. +- * +- * An entry length is 2 bytes (16 bits) in host endian order. +- * In the log, the length does not include the size of the log entry structure. +- * This function returns the size including the log entry structure. +- * +- * Caller needs to hold log->mutex. +- */ +-static __u32 get_entry_msg_len(struct logger_log *log, size_t off) +-{ +- struct logger_entry scratch; +- struct logger_entry *entry; +- +- entry = get_entry_header(log, off, &scratch); +- return entry->len; +-} +- +-static size_t get_user_hdr_len(int ver) +-{ +- if (ver < 2) +- return sizeof(struct user_logger_entry_compat); +- return sizeof(struct logger_entry); +-} +- +-static ssize_t copy_header_to_user(int ver, struct logger_entry *entry, +- char __user *buf) +-{ +- void *hdr; +- size_t hdr_len; +- struct user_logger_entry_compat v1; +- +- if (ver < 2) { +- v1.len = entry->len; +- v1.__pad = 0; +- v1.pid = entry->pid; +- v1.tid = entry->tid; +- v1.sec = entry->sec; +- v1.nsec = entry->nsec; +- hdr = &v1; +- hdr_len = sizeof(struct user_logger_entry_compat); +- } else { +- hdr = entry; +- hdr_len = sizeof(struct logger_entry); +- } +- +- return copy_to_user(buf, hdr, hdr_len); +-} +- +-/* +- * do_read_log_to_user - reads exactly 'count' bytes from 'log' into the +- * user-space buffer 'buf'. Returns 'count' on success. +- * +- * Caller must hold log->mutex. +- */ +-static ssize_t do_read_log_to_user(struct logger_log *log, +- struct logger_reader *reader, +- char __user *buf, +- size_t count) +-{ +- struct logger_entry scratch; +- struct logger_entry *entry; +- size_t len; +- size_t msg_start; +- +- /* +- * First, copy the header to userspace, using the version of +- * the header requested +- */ +- entry = get_entry_header(log, reader->r_off, &scratch); +- if (copy_header_to_user(reader->r_ver, entry, buf)) +- return -EFAULT; +- +- count -= get_user_hdr_len(reader->r_ver); +- buf += get_user_hdr_len(reader->r_ver); +- msg_start = logger_offset(log, +- reader->r_off + sizeof(struct logger_entry)); +- +- /* +- * We read from the msg in two disjoint operations. First, we read from +- * the current msg head offset up to 'count' bytes or to the end of +- * the log, whichever comes first. +- */ +- len = min(count, log->size - msg_start); +- if (copy_to_user(buf, log->buffer + msg_start, len)) +- return -EFAULT; +- +- /* +- * Second, we read any remaining bytes, starting back at the head of +- * the log. +- */ +- if (count != len) +- if (copy_to_user(buf + len, log->buffer, count - len)) +- return -EFAULT; +- +- reader->r_off = logger_offset(log, reader->r_off + +- sizeof(struct logger_entry) + count); +- +- return count + get_user_hdr_len(reader->r_ver); +-} +- +-/* +- * get_next_entry_by_uid - Starting at 'off', returns an offset into +- * 'log->buffer' which contains the first entry readable by 'euid' +- */ +-static size_t get_next_entry_by_uid(struct logger_log *log, +- size_t off, kuid_t euid) +-{ +- while (off != log->w_off) { +- struct logger_entry *entry; +- struct logger_entry scratch; +- size_t next_len; +- +- entry = get_entry_header(log, off, &scratch); +- +- if (uid_eq(entry->euid, euid)) +- return off; +- +- next_len = sizeof(struct logger_entry) + entry->len; +- off = logger_offset(log, off + next_len); +- } +- +- return off; +-} +- +-/* +- * logger_read - our log's read() method +- * +- * Behavior: +- * +- * - O_NONBLOCK works +- * - If there are no log entries to read, blocks until log is written to +- * - Atomically reads exactly one log entry +- * +- * Will set errno to EINVAL if read +- * buffer is insufficient to hold next entry. +- */ +-static ssize_t logger_read(struct file *file, char __user *buf, +- size_t count, loff_t *pos) +-{ +- struct logger_reader *reader = file->private_data; +- struct logger_log *log = reader->log; +- ssize_t ret; +- DEFINE_WAIT(wait); +- +-start: +- while (1) { +- mutex_lock(&log->mutex); +- +- prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE); +- +- ret = (log->w_off == reader->r_off); +- mutex_unlock(&log->mutex); +- if (!ret) +- break; +- +- if (file->f_flags & O_NONBLOCK) { +- ret = -EAGAIN; +- break; +- } +- +- if (signal_pending(current)) { +- ret = -EINTR; +- break; +- } +- +- schedule(); +- } +- +- finish_wait(&log->wq, &wait); +- if (ret) +- return ret; +- +- mutex_lock(&log->mutex); +- +- if (!reader->r_all) +- reader->r_off = get_next_entry_by_uid(log, +- reader->r_off, current_euid()); +- +- /* is there still something to read or did we race? */ +- if (unlikely(log->w_off == reader->r_off)) { +- mutex_unlock(&log->mutex); +- goto start; +- } +- +- /* get the size of the next entry */ +- ret = get_user_hdr_len(reader->r_ver) + +- get_entry_msg_len(log, reader->r_off); +- if (count < ret) { +- ret = -EINVAL; +- goto out; +- } +- +- /* get exactly one entry from the log */ +- ret = do_read_log_to_user(log, reader, buf, ret); +- +-out: +- mutex_unlock(&log->mutex); +- +- return ret; +-} +- +-/* +- * get_next_entry - return the offset of the first valid entry at least 'len' +- * bytes after 'off'. +- * +- * Caller must hold log->mutex. +- */ +-static size_t get_next_entry(struct logger_log *log, size_t off, size_t len) +-{ +- size_t count = 0; +- +- do { +- size_t nr = sizeof(struct logger_entry) + +- get_entry_msg_len(log, off); +- off = logger_offset(log, off + nr); +- count += nr; +- } while (count < len); +- +- return off; +-} +- +-/* +- * is_between - is a < c < b, accounting for wrapping of a, b, and c +- * positions in the buffer +- * +- * That is, if a<b, check for c between a and b +- * and if a>b, check for c outside (not between) a and b +- * +- * |------- a xxxxxxxx b --------| +- * c^ +- * +- * |xxxxx b --------- a xxxxxxxxx| +- * c^ +- * or c^ +- */ +-static inline int is_between(size_t a, size_t b, size_t c) +-{ +- if (a < b) { +- /* is c between a and b? */ +- if (a < c && c <= b) +- return 1; +- } else { +- /* is c outside of b through a? */ +- if (c <= b || a < c) +- return 1; +- } +- +- return 0; +-} +- +-/* +- * fix_up_readers - walk the list of all readers and "fix up" any who were +- * lapped by the writer; also do the same for the default "start head". +- * We do this by "pulling forward" the readers and start head to the first +- * entry after the new write head. +- * +- * The caller needs to hold log->mutex. +- */ +-static void fix_up_readers(struct logger_log *log, size_t len) +-{ +- size_t old = log->w_off; +- size_t new = logger_offset(log, old + len); +- struct logger_reader *reader; +- +- if (is_between(old, new, log->head)) +- log->head = get_next_entry(log, log->head, len); +- +- list_for_each_entry(reader, &log->readers, list) +- if (is_between(old, new, reader->r_off)) +- reader->r_off = get_next_entry(log, reader->r_off, len); +-} +- +-/* +- * logger_write_iter - our write method, implementing support for write(), +- * writev(), and aio_write(). Writes are our fast path, and we try to optimize +- * them above all else. +- */ +-static ssize_t logger_write_iter(struct kiocb *iocb, struct iov_iter *from) +-{ +- struct logger_log *log = file_get_log(iocb->ki_filp); +- struct logger_entry header; +- struct timespec now; +- size_t len, count, w_off; +- +- count = min_t(size_t, iocb->ki_nbytes, LOGGER_ENTRY_MAX_PAYLOAD); +- +- now = current_kernel_time(); +- +- header.pid = current->tgid; +- header.tid = current->pid; +- header.sec = now.tv_sec; +- header.nsec = now.tv_nsec; +- header.euid = current_euid(); +- header.len = count; +- header.hdr_size = sizeof(struct logger_entry); +- +- /* null writes succeed, return zero */ +- if (unlikely(!header.len)) +- return 0; +- +- mutex_lock(&log->mutex); +- +- /* +- * Fix up any readers, pulling them forward to the first readable +- * entry after (what will be) the new write offset. We do this now +- * because if we partially fail, we can end up with clobbered log +- * entries that encroach on readable buffer. +- */ +- fix_up_readers(log, sizeof(struct logger_entry) + header.len); +- +- len = min(sizeof(header), log->size - log->w_off); +- memcpy(log->buffer + log->w_off, &header, len); +- memcpy(log->buffer, (char *)&header + len, sizeof(header) - len); +- +- /* Work with a copy until we are ready to commit the whole entry */ +- w_off = logger_offset(log, log->w_off + sizeof(struct logger_entry)); +- +- len = min(count, log->size - w_off); +- +- if (copy_from_iter(log->buffer + w_off, len, from) != len) { +- /* +- * Note that by not updating log->w_off, this abandons the +- * portion of the new entry that *was* successfully +- * copied, just above. This is intentional to avoid +- * message corruption from missing fragments. +- */ +- mutex_unlock(&log->mutex); +- return -EFAULT; +- } +- +- if (copy_from_iter(log->buffer, count - len, from) != count - len) { +- mutex_unlock(&log->mutex); +- return -EFAULT; +- } +- +- log->w_off = logger_offset(log, w_off + count); +- mutex_unlock(&log->mutex); +- +- /* wake up any blocked readers */ +- wake_up_interruptible(&log->wq); +- +- return len; +-} +- +-static struct logger_log *get_log_from_minor(int minor) +-{ +- struct logger_log *log; +- +- list_for_each_entry(log, &log_list, logs) +- if (log->misc.minor == minor) +- return log; +- return NULL; +-} +- +-/* +- * logger_open - the log's open() file operation +- * +- * Note how near a no-op this is in the write-only case. Keep it that way! +- */ +-static int logger_open(struct inode *inode, struct file *file) +-{ +- struct logger_log *log; +- int ret; +- +- ret = nonseekable_open(inode, file); +- if (ret) +- return ret; +- +- log = get_log_from_minor(MINOR(inode->i_rdev)); +- if (!log) +- return -ENODEV; +- +- if (file->f_mode & FMODE_READ) { +- struct logger_reader *reader; +- +- reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL); +- if (!reader) +- return -ENOMEM; +- +- reader->log = log; +- reader->r_ver = 1; +- reader->r_all = in_egroup_p(inode->i_gid) || +- capable(CAP_SYSLOG); +- +- INIT_LIST_HEAD(&reader->list); +- +- mutex_lock(&log->mutex); +- reader->r_off = log->head; +- list_add_tail(&reader->list, &log->readers); +- mutex_unlock(&log->mutex); +- +- file->private_data = reader; +- } else +- file->private_data = log; +- +- return 0; +-} +- +-/* +- * logger_release - the log's release file operation +- * +- * Note this is a total no-op in the write-only case. Keep it that way! +- */ +-static int logger_release(struct inode *ignored, struct file *file) +-{ +- if (file->f_mode & FMODE_READ) { +- struct logger_reader *reader = file->private_data; +- struct logger_log *log = reader->log; +- +- mutex_lock(&log->mutex); +- list_del(&reader->list); +- mutex_unlock(&log->mutex); +- +- kfree(reader); +- } +- +- return 0; +-} +- +-/* +- * logger_poll - the log's poll file operation, for poll/select/epoll +- * +- * Note we always return POLLOUT, because you can always write() to the log. +- * Note also that, strictly speaking, a return value of POLLIN does not +- * guarantee that the log is readable without blocking, as there is a small +- * chance that the writer can lap the reader in the interim between poll() +- * returning and the read() request. +- */ +-static unsigned int logger_poll(struct file *file, poll_table *wait) +-{ +- struct logger_reader *reader; +- struct logger_log *log; +- unsigned int ret = POLLOUT | POLLWRNORM; +- +- if (!(file->f_mode & FMODE_READ)) +- return ret; +- +- reader = file->private_data; +- log = reader->log; +- +- poll_wait(file, &log->wq, wait); +- +- mutex_lock(&log->mutex); +- if (!reader->r_all) +- reader->r_off = get_next_entry_by_uid(log, +- reader->r_off, current_euid()); +- +- if (log->w_off != reader->r_off) +- ret |= POLLIN | POLLRDNORM; +- mutex_unlock(&log->mutex); +- +- return ret; +-} +- +-static long logger_set_version(struct logger_reader *reader, void __user *arg) +-{ +- int version; +- +- if (copy_from_user(&version, arg, sizeof(int))) +- return -EFAULT; +- +- if ((version < 1) || (version > 2)) +- return -EINVAL; +- +- reader->r_ver = version; +- return 0; +-} +- +-static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +-{ +- struct logger_log *log = file_get_log(file); +- struct logger_reader *reader; +- long ret = -EINVAL; +- void __user *argp = (void __user *) arg; +- +- mutex_lock(&log->mutex); +- +- switch (cmd) { +- case LOGGER_GET_LOG_BUF_SIZE: +- ret = log->size; +- break; +- case LOGGER_GET_LOG_LEN: +- if (!(file->f_mode & FMODE_READ)) { +- ret = -EBADF; +- break; +- } +- reader = file->private_data; +- if (log->w_off >= reader->r_off) +- ret = log->w_off - reader->r_off; +- else +- ret = (log->size - reader->r_off) + log->w_off; +- break; +- case LOGGER_GET_NEXT_ENTRY_LEN: +- if (!(file->f_mode & FMODE_READ)) { +- ret = -EBADF; +- break; +- } +- reader = file->private_data; +- +- if (!reader->r_all) +- reader->r_off = get_next_entry_by_uid(log, +- reader->r_off, current_euid()); +- +- if (log->w_off != reader->r_off) +- ret = get_user_hdr_len(reader->r_ver) + +- get_entry_msg_len(log, reader->r_off); +- else +- ret = 0; +- break; +- case LOGGER_FLUSH_LOG: +- if (!(file->f_mode & FMODE_WRITE)) { +- ret = -EBADF; +- break; +- } +- if (!(in_egroup_p(file_inode(file)->i_gid) || +- capable(CAP_SYSLOG))) { +- ret = -EPERM; +- break; +- } +- list_for_each_entry(reader, &log->readers, list) +- reader->r_off = log->w_off; +- log->head = log->w_off; +- ret = 0; +- break; +- case LOGGER_GET_VERSION: +- if (!(file->f_mode & FMODE_READ)) { +- ret = -EBADF; +- break; +- } +- reader = file->private_data; +- ret = reader->r_ver; +- break; +- case LOGGER_SET_VERSION: +- if (!(file->f_mode & FMODE_READ)) { +- ret = -EBADF; +- break; +- } +- reader = file->private_data; +- ret = logger_set_version(reader, argp); +- break; +- } +- +- mutex_unlock(&log->mutex); +- +- return ret; +-} +- +-static const struct file_operations logger_fops = { +- .owner = THIS_MODULE, +- .read = logger_read, +- .write_iter = logger_write_iter, +- .poll = logger_poll, +- .unlocked_ioctl = logger_ioctl, +- .compat_ioctl = logger_ioctl, +- .open = logger_open, +- .release = logger_release, +-}; +- +-/* +- * Log size must must be a power of two, and greater than +- * (LOGGER_ENTRY_MAX_PAYLOAD + sizeof(struct logger_entry)). +- */ +-static int __init create_log(char *log_name, int size) +-{ +- int ret = 0; +- struct logger_log *log; +- unsigned char *buffer; +- +- buffer = vmalloc(size); +- if (buffer == NULL) +- return -ENOMEM; +- +- log = kzalloc(sizeof(struct logger_log), GFP_KERNEL); +- if (log == NULL) { +- ret = -ENOMEM; +- goto out_free_buffer; +- } +- log->buffer = buffer; +- +- log->misc.minor = MISC_DYNAMIC_MINOR; +- log->misc.name = kstrdup(log_name, GFP_KERNEL); +- if (log->misc.name == NULL) { +- ret = -ENOMEM; +- goto out_free_log; +- } +- +- log->misc.fops = &logger_fops; +- log->misc.parent = NULL; +- +- init_waitqueue_head(&log->wq); +- INIT_LIST_HEAD(&log->readers); +- mutex_init(&log->mutex); +- log->w_off = 0; +- log->head = 0; +- log->size = size; +- +- INIT_LIST_HEAD(&log->logs); +- list_add_tail(&log->logs, &log_list); +- +- /* finally, initialize the misc device for this log */ +- ret = misc_register(&log->misc); +- if (unlikely(ret)) { +- pr_err("failed to register misc device for log '%s'!\n", +- log->misc.name); +- goto out_free_misc_name; +- } +- +- pr_info("created %luK log '%s'\n", +- (unsigned long) log->size >> 10, log->misc.name); +- +- return 0; +- +-out_free_misc_name: +- kfree(log->misc.name); +- +-out_free_log: +- kfree(log); +- +-out_free_buffer: +- vfree(buffer); +- return ret; +-} +- +-static int __init logger_init(void) +-{ +- int ret; +- +- ret = create_log(LOGGER_LOG_MAIN, 256*1024); +- if (unlikely(ret)) +- goto out; +- +- ret = create_log(LOGGER_LOG_EVENTS, 256*1024); +- if (unlikely(ret)) +- goto out; +- +- ret = create_log(LOGGER_LOG_RADIO, 256*1024); +- if (unlikely(ret)) +- goto out; +- +- ret = create_log(LOGGER_LOG_SYSTEM, 256*1024); +- if (unlikely(ret)) +- goto out; +- +-out: +- return ret; +-} +- +-static void __exit logger_exit(void) +-{ +- struct logger_log *current_log, *next_log; +- +- list_for_each_entry_safe(current_log, next_log, &log_list, logs) { +- /* we have to delete all the entry inside log_list */ +- misc_deregister(¤t_log->misc); +- vfree(current_log->buffer); +- kfree(current_log->misc.name); +- list_del(¤t_log->logs); +- kfree(current_log); +- } +-} +- +- +-device_initcall(logger_init); +-module_exit(logger_exit); +- +-MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Robert Love, <rlove@google.com>"); +-MODULE_DESCRIPTION("Android Logger"); +diff --git a/drivers/staging/android/logger.h b/drivers/staging/android/logger.h +deleted file mode 100644 +index 70af7d8..0000000 +--- a/drivers/staging/android/logger.h ++++ /dev/null +@@ -1,89 +0,0 @@ +-/* include/linux/logger.h +- * +- * Copyright (C) 2007-2008 Google, Inc. +- * Author: Robert Love <rlove@android.com> +- * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#ifndef _LINUX_LOGGER_H +-#define _LINUX_LOGGER_H +- +-#include <linux/types.h> +-#include <linux/ioctl.h> +- +-/** +- * struct user_logger_entry_compat - defines a single entry that is given to a logger +- * @len: The length of the payload +- * @__pad: Two bytes of padding that appear to be required +- * @pid: The generating process' process ID +- * @tid: The generating process' thread ID +- * @sec: The number of seconds that have elapsed since the Epoch +- * @nsec: The number of nanoseconds that have elapsed since @sec +- * @msg: The message that is to be logged +- * +- * The userspace structure for version 1 of the logger_entry ABI. +- * This structure is returned to userspace unless the caller requests +- * an upgrade to a newer ABI version. +- */ +-struct user_logger_entry_compat { +- __u16 len; +- __u16 __pad; +- __s32 pid; +- __s32 tid; +- __s32 sec; +- __s32 nsec; +- char msg[0]; +-}; +- +-/** +- * struct logger_entry - defines a single entry that is given to a logger +- * @len: The length of the payload +- * @hdr_size: sizeof(struct logger_entry_v2) +- * @pid: The generating process' process ID +- * @tid: The generating process' thread ID +- * @sec: The number of seconds that have elapsed since the Epoch +- * @nsec: The number of nanoseconds that have elapsed since @sec +- * @euid: Effective UID of logger +- * @msg: The message that is to be logged +- * +- * The structure for version 2 of the logger_entry ABI. +- * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION) +- * is called with version >= 2 +- */ +-struct logger_entry { +- __u16 len; +- __u16 hdr_size; +- __s32 pid; +- __s32 tid; +- __s32 sec; +- __s32 nsec; +- kuid_t euid; +- char msg[0]; +-}; +- +-#define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */ +-#define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */ +-#define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */ +-#define LOGGER_LOG_MAIN "log_main" /* everything else */ +- +-#define LOGGER_ENTRY_MAX_PAYLOAD 4076 +- +-#define __LOGGERIO 0xAE +- +-#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ +-#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ +-#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ +-#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ +-#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ +-#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ +- +-#endif /* _LINUX_LOGGER_H */ +diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c +index b545d3d..ca92b60 100644 +--- a/drivers/staging/android/lowmemorykiller.c ++++ b/drivers/staging/android/lowmemorykiller.c +@@ -39,7 +39,6 @@ + #include <linux/sched.h> + #include <linux/swap.h> + #include <linux/rcupdate.h> +-#include <linux/profile.h> + #include <linux/notifier.h> + + static uint32_t lowmem_debug_level = 1; +@@ -83,6 +82,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) + int tasksize; + int i; + short min_score_adj = OOM_SCORE_ADJ_MAX + 1; ++ int minfree = 0; + int selected_tasksize = 0; + short selected_oom_score_adj; + int array_size = ARRAY_SIZE(lowmem_adj); +@@ -96,8 +96,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) + if (lowmem_minfree_size < array_size) + array_size = lowmem_minfree_size; + for (i = 0; i < array_size; i++) { +- if (other_free < lowmem_minfree[i] && +- other_file < lowmem_minfree[i]) { ++ minfree = lowmem_minfree[i]; ++ if (other_free < minfree && other_file < minfree) { + min_score_adj = lowmem_adj[i]; + break; + } +@@ -152,13 +152,22 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) + selected = p; + selected_tasksize = tasksize; + selected_oom_score_adj = oom_score_adj; +- lowmem_print(2, "select %d (%s), adj %hd, size %d, to kill\n", +- p->pid, p->comm, oom_score_adj, tasksize); ++ lowmem_print(2, "select '%s' (%d), adj %hd, size %d, to kill\n", ++ p->comm, p->pid, oom_score_adj, tasksize); + } + if (selected) { +- lowmem_print(1, "send sigkill to %d (%s), adj %hd, size %d\n", +- selected->pid, selected->comm, +- selected_oom_score_adj, selected_tasksize); ++ lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \ ++ " to free %ldkB on behalf of '%s' (%d) because\n" \ ++ " cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \ ++ " Free memory is %ldkB above reserved\n", ++ selected->comm, selected->pid, ++ selected_oom_score_adj, ++ selected_tasksize * (long)(PAGE_SIZE / 1024), ++ current->comm, current->pid, ++ other_file * (long)(PAGE_SIZE / 1024), ++ minfree * (long)(PAGE_SIZE / 1024), ++ min_score_adj, ++ other_free * (long)(PAGE_SIZE / 1024)); + lowmem_deathpending_timeout = jiffies + HZ; + set_tsk_thread_flag(selected, TIF_MEMDIE); + send_sig(SIGKILL, selected, 0); +@@ -188,9 +197,92 @@ static void __exit lowmem_exit(void) + unregister_shrinker(&lowmem_shrinker); + } + ++#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES ++static short lowmem_oom_adj_to_oom_score_adj(short oom_adj) ++{ ++ if (oom_adj == OOM_ADJUST_MAX) ++ return OOM_SCORE_ADJ_MAX; ++ else ++ return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE; ++} ++ ++static void lowmem_autodetect_oom_adj_values(void) ++{ ++ int i; ++ short oom_adj; ++ short oom_score_adj; ++ int array_size = ARRAY_SIZE(lowmem_adj); ++ ++ if (lowmem_adj_size < array_size) ++ array_size = lowmem_adj_size; ++ ++ if (array_size <= 0) ++ return; ++ ++ oom_adj = lowmem_adj[array_size - 1]; ++ if (oom_adj > OOM_ADJUST_MAX) ++ return; ++ ++ oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj); ++ if (oom_score_adj <= OOM_ADJUST_MAX) ++ return; ++ ++ lowmem_print(1, "lowmem_shrink: convert oom_adj to oom_score_adj:\n"); ++ for (i = 0; i < array_size; i++) { ++ oom_adj = lowmem_adj[i]; ++ oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj); ++ lowmem_adj[i] = oom_score_adj; ++ lowmem_print(1, "oom_adj %d => oom_score_adj %d\n", ++ oom_adj, oom_score_adj); ++ } ++} ++ ++static int lowmem_adj_array_set(const char *val, const struct kernel_param *kp) ++{ ++ int ret; ++ ++ ret = param_array_ops.set(val, kp); ++ ++ /* HACK: Autodetect oom_adj values in lowmem_adj array */ ++ lowmem_autodetect_oom_adj_values(); ++ ++ return ret; ++} ++ ++static int lowmem_adj_array_get(char *buffer, const struct kernel_param *kp) ++{ ++ return param_array_ops.get(buffer, kp); ++} ++ ++static void lowmem_adj_array_free(void *arg) ++{ ++ param_array_ops.free(arg); ++} ++ ++static struct kernel_param_ops lowmem_adj_array_ops = { ++ .set = lowmem_adj_array_set, ++ .get = lowmem_adj_array_get, ++ .free = lowmem_adj_array_free, ++}; ++ ++static const struct kparam_array __param_arr_adj = { ++ .max = ARRAY_SIZE(lowmem_adj), ++ .num = &lowmem_adj_size, ++ .ops = ¶m_ops_short, ++ .elemsize = sizeof(lowmem_adj[0]), ++ .elem = lowmem_adj, ++}; ++#endif ++ + module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); ++#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES ++module_param_cb(adj, &lowmem_adj_array_ops, ++ .arr = &__param_arr_adj, S_IRUGO | S_IWUSR); ++__MODULE_PARM_TYPE(adj, "array of short"); ++#else + module_param_array_named(adj, lowmem_adj, short, &lowmem_adj_size, + S_IRUGO | S_IWUSR); ++#endif + module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, + S_IRUGO | S_IWUSR); + module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR); +diff --git a/drivers/staging/android/uapi/android_alarm.h b/drivers/staging/android/uapi/android_alarm.h +deleted file mode 100644 +index aa013f6..0000000 +--- a/drivers/staging/android/uapi/android_alarm.h ++++ /dev/null +@@ -1,62 +0,0 @@ +-/* drivers/staging/android/uapi/android_alarm.h +- * +- * Copyright (C) 2006-2007 Google, Inc. +- * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#ifndef _UAPI_LINUX_ANDROID_ALARM_H +-#define _UAPI_LINUX_ANDROID_ALARM_H +- +-#include <linux/ioctl.h> +-#include <linux/time.h> +- +-enum android_alarm_type { +- /* return code bit numbers or set alarm arg */ +- ANDROID_ALARM_RTC_WAKEUP, +- ANDROID_ALARM_RTC, +- ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, +- ANDROID_ALARM_ELAPSED_REALTIME, +- ANDROID_ALARM_SYSTEMTIME, +- +- ANDROID_ALARM_TYPE_COUNT, +- +- /* return code bit numbers */ +- /* ANDROID_ALARM_TIME_CHANGE = 16 */ +-}; +- +-enum android_alarm_return_flags { +- ANDROID_ALARM_RTC_WAKEUP_MASK = 1U << ANDROID_ALARM_RTC_WAKEUP, +- ANDROID_ALARM_RTC_MASK = 1U << ANDROID_ALARM_RTC, +- ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK = +- 1U << ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, +- ANDROID_ALARM_ELAPSED_REALTIME_MASK = +- 1U << ANDROID_ALARM_ELAPSED_REALTIME, +- ANDROID_ALARM_SYSTEMTIME_MASK = 1U << ANDROID_ALARM_SYSTEMTIME, +- ANDROID_ALARM_TIME_CHANGE_MASK = 1U << 16 +-}; +- +-/* Disable alarm */ +-#define ANDROID_ALARM_CLEAR(type) _IO('a', 0 | ((type) << 4)) +- +-/* Ack last alarm and wait for next */ +-#define ANDROID_ALARM_WAIT _IO('a', 1) +- +-#define ALARM_IOW(c, type, size) _IOW('a', (c) | ((type) << 4), size) +-/* Set alarm */ +-#define ANDROID_ALARM_SET(type) ALARM_IOW(2, type, struct timespec) +-#define ANDROID_ALARM_SET_AND_WAIT(type) ALARM_IOW(3, type, struct timespec) +-#define ANDROID_ALARM_GET_TIME(type) ALARM_IOW(4, type, struct timespec) +-#define ANDROID_ALARM_SET_RTC _IOW('a', 5, struct timespec) +-#define ANDROID_ALARM_BASE_CMD(cmd) (cmd & ~(_IOC(0, 0, 0xf0, 0))) +-#define ANDROID_ALARM_IOCTL_TO_TYPE(cmd) (_IOC_NR(cmd) >> 4) +- +-#endif +diff --git a/drivers/staging/android/uapi/ashmem.h b/drivers/staging/android/uapi/ashmem.h +index ba4743c..13df42d 100644 +--- a/drivers/staging/android/uapi/ashmem.h ++++ b/drivers/staging/android/uapi/ashmem.h +@@ -13,6 +13,7 @@ + #define _UAPI_LINUX_ASHMEM_H + + #include <linux/ioctl.h> ++#include <linux/types.h> + + #define ASHMEM_NAME_LEN 256 + +diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c +index f4ca7b7..d9797ba 100644 +--- a/drivers/staging/lustre/lustre/llite/llite_lib.c ++++ b/drivers/staging/lustre/lustre/llite/llite_lib.c +@@ -1434,6 +1434,10 @@ int ll_setattr_raw(struct dentry *dentry, struct iattr *attr, bool hsm_import) + spin_unlock(&lli->lli_lock); + } + ++ rc = setattr_killpriv(dentry, attr); ++ if (rc) ++ return rc; ++ + /* We always do an MDS RPC, even if we're only changing the size; + * only the MDS knows whether truncate() should fail with -ETXTBUSY */ + +diff --git a/drivers/switch/Kconfig b/drivers/switch/Kconfig +new file mode 100644 +index 0000000..19404b6 +--- /dev/null ++++ b/drivers/switch/Kconfig +@@ -0,0 +1,15 @@ ++menuconfig SWITCH ++ tristate "Switch class support" ++ help ++ Say Y here to enable switch class support. This allows ++ monitoring switches by userspace via sysfs and uevent. ++ ++if SWITCH ++ ++config SWITCH_GPIO ++ tristate "GPIO Swith support" ++ depends on GPIOLIB ++ help ++ Say Y here to enable GPIO based switch support. ++ ++endif # SWITCH +diff --git a/drivers/switch/Makefile b/drivers/switch/Makefile +new file mode 100644 +index 0000000..f7606ed +--- /dev/null ++++ b/drivers/switch/Makefile +@@ -0,0 +1,4 @@ ++# Switch Class Driver ++obj-$(CONFIG_SWITCH) += switch_class.o ++obj-$(CONFIG_SWITCH_GPIO) += switch_gpio.o ++ +diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c +new file mode 100644 +index 0000000..e373b62 +--- /dev/null ++++ b/drivers/switch/switch_class.c +@@ -0,0 +1,174 @@ ++/* ++ * drivers/switch/switch_class.c ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/types.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/fs.h> ++#include <linux/err.h> ++#include <linux/switch.h> ++ ++struct class *switch_class; ++static atomic_t device_count; ++ ++static ssize_t state_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct switch_dev *sdev = (struct switch_dev *) ++ dev_get_drvdata(dev); ++ ++ if (sdev->print_state) { ++ int ret = sdev->print_state(sdev, buf); ++ if (ret >= 0) ++ return ret; ++ } ++ return sprintf(buf, "%d\n", sdev->state); ++} ++ ++static ssize_t name_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct switch_dev *sdev = (struct switch_dev *) ++ dev_get_drvdata(dev); ++ ++ if (sdev->print_name) { ++ int ret = sdev->print_name(sdev, buf); ++ if (ret >= 0) ++ return ret; ++ } ++ return sprintf(buf, "%s\n", sdev->name); ++} ++ ++static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); ++static DEVICE_ATTR(name, S_IRUGO, name_show, NULL); ++ ++void switch_set_state(struct switch_dev *sdev, int state) ++{ ++ char name_buf[120]; ++ char state_buf[120]; ++ char *prop_buf; ++ char *envp[3]; ++ int env_offset = 0; ++ int length; ++ ++ if (sdev->state != state) { ++ sdev->state = state; ++ ++ prop_buf = (char *)get_zeroed_page(GFP_KERNEL); ++ if (prop_buf) { ++ length = name_show(sdev->dev, NULL, prop_buf); ++ if (length > 0) { ++ if (prop_buf[length - 1] == '\n') ++ prop_buf[length - 1] = 0; ++ snprintf(name_buf, sizeof(name_buf), ++ "SWITCH_NAME=%s", prop_buf); ++ envp[env_offset++] = name_buf; ++ } ++ length = state_show(sdev->dev, NULL, prop_buf); ++ if (length > 0) { ++ if (prop_buf[length - 1] == '\n') ++ prop_buf[length - 1] = 0; ++ snprintf(state_buf, sizeof(state_buf), ++ "SWITCH_STATE=%s", prop_buf); ++ envp[env_offset++] = state_buf; ++ } ++ envp[env_offset] = NULL; ++ kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp); ++ free_page((unsigned long)prop_buf); ++ } else { ++ printk(KERN_ERR "out of memory in switch_set_state\n"); ++ kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE); ++ } ++ } ++} ++EXPORT_SYMBOL_GPL(switch_set_state); ++ ++static int create_switch_class(void) ++{ ++ if (!switch_class) { ++ switch_class = class_create(THIS_MODULE, "switch"); ++ if (IS_ERR(switch_class)) ++ return PTR_ERR(switch_class); ++ atomic_set(&device_count, 0); ++ } ++ ++ return 0; ++} ++ ++int switch_dev_register(struct switch_dev *sdev) ++{ ++ int ret; ++ ++ if (!switch_class) { ++ ret = create_switch_class(); ++ if (ret < 0) ++ return ret; ++ } ++ ++ sdev->index = atomic_inc_return(&device_count); ++ sdev->dev = device_create(switch_class, NULL, ++ MKDEV(0, sdev->index), NULL, sdev->name); ++ if (IS_ERR(sdev->dev)) ++ return PTR_ERR(sdev->dev); ++ ++ ret = device_create_file(sdev->dev, &dev_attr_state); ++ if (ret < 0) ++ goto err_create_file_1; ++ ret = device_create_file(sdev->dev, &dev_attr_name); ++ if (ret < 0) ++ goto err_create_file_2; ++ ++ dev_set_drvdata(sdev->dev, sdev); ++ sdev->state = 0; ++ return 0; ++ ++err_create_file_2: ++ device_remove_file(sdev->dev, &dev_attr_state); ++err_create_file_1: ++ device_destroy(switch_class, MKDEV(0, sdev->index)); ++ printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(switch_dev_register); ++ ++void switch_dev_unregister(struct switch_dev *sdev) ++{ ++ device_remove_file(sdev->dev, &dev_attr_name); ++ device_remove_file(sdev->dev, &dev_attr_state); ++ device_destroy(switch_class, MKDEV(0, sdev->index)); ++ dev_set_drvdata(sdev->dev, NULL); ++} ++EXPORT_SYMBOL_GPL(switch_dev_unregister); ++ ++static int __init switch_class_init(void) ++{ ++ return create_switch_class(); ++} ++ ++static void __exit switch_class_exit(void) ++{ ++ class_destroy(switch_class); ++} ++ ++module_init(switch_class_init); ++module_exit(switch_class_exit); ++ ++MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>"); ++MODULE_DESCRIPTION("Switch class driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/switch/switch_gpio.c b/drivers/switch/switch_gpio.c +new file mode 100644 +index 0000000..621d62d +--- /dev/null ++++ b/drivers/switch/switch_gpio.c +@@ -0,0 +1,172 @@ ++/* ++ * drivers/switch/switch_gpio.c ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/kernel.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <linux/switch.h> ++#include <linux/workqueue.h> ++#include <linux/gpio.h> ++ ++struct gpio_switch_data { ++ struct switch_dev sdev; ++ unsigned gpio; ++ const char *name_on; ++ const char *name_off; ++ const char *state_on; ++ const char *state_off; ++ int irq; ++ struct work_struct work; ++}; ++ ++static void gpio_switch_work(struct work_struct *work) ++{ ++ int state; ++ struct gpio_switch_data *data = ++ container_of(work, struct gpio_switch_data, work); ++ ++ state = gpio_get_value(data->gpio); ++ switch_set_state(&data->sdev, state); ++} ++ ++static irqreturn_t gpio_irq_handler(int irq, void *dev_id) ++{ ++ struct gpio_switch_data *switch_data = ++ (struct gpio_switch_data *)dev_id; ++ ++ schedule_work(&switch_data->work); ++ return IRQ_HANDLED; ++} ++ ++static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf) ++{ ++ struct gpio_switch_data *switch_data = ++ container_of(sdev, struct gpio_switch_data, sdev); ++ const char *state; ++ if (switch_get_state(sdev)) ++ state = switch_data->state_on; ++ else ++ state = switch_data->state_off; ++ ++ if (state) ++ return sprintf(buf, "%s\n", state); ++ return -1; ++} ++ ++static int gpio_switch_probe(struct platform_device *pdev) ++{ ++ struct gpio_switch_platform_data *pdata = pdev->dev.platform_data; ++ struct gpio_switch_data *switch_data; ++ int ret = 0; ++ ++ if (!pdata) ++ return -EBUSY; ++ ++ switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL); ++ if (!switch_data) ++ return -ENOMEM; ++ ++ switch_data->sdev.name = pdata->name; ++ switch_data->gpio = pdata->gpio; ++ switch_data->name_on = pdata->name_on; ++ switch_data->name_off = pdata->name_off; ++ switch_data->state_on = pdata->state_on; ++ switch_data->state_off = pdata->state_off; ++ switch_data->sdev.print_state = switch_gpio_print_state; ++ ++ ret = switch_dev_register(&switch_data->sdev); ++ if (ret < 0) ++ goto err_switch_dev_register; ++ ++ ret = gpio_request(switch_data->gpio, pdev->name); ++ if (ret < 0) ++ goto err_request_gpio; ++ ++ ret = gpio_direction_input(switch_data->gpio); ++ if (ret < 0) ++ goto err_set_gpio_input; ++ ++ INIT_WORK(&switch_data->work, gpio_switch_work); ++ ++ switch_data->irq = gpio_to_irq(switch_data->gpio); ++ if (switch_data->irq < 0) { ++ ret = switch_data->irq; ++ goto err_detect_irq_num_failed; ++ } ++ ++ ret = request_irq(switch_data->irq, gpio_irq_handler, ++ IRQF_TRIGGER_LOW, pdev->name, switch_data); ++ if (ret < 0) ++ goto err_request_irq; ++ ++ /* Perform initial detection */ ++ gpio_switch_work(&switch_data->work); ++ ++ return 0; ++ ++err_request_irq: ++err_detect_irq_num_failed: ++err_set_gpio_input: ++ gpio_free(switch_data->gpio); ++err_request_gpio: ++ switch_dev_unregister(&switch_data->sdev); ++err_switch_dev_register: ++ kfree(switch_data); ++ ++ return ret; ++} ++ ++static int gpio_switch_remove(struct platform_device *pdev) ++{ ++ struct gpio_switch_data *switch_data = platform_get_drvdata(pdev); ++ ++ cancel_work_sync(&switch_data->work); ++ gpio_free(switch_data->gpio); ++ switch_dev_unregister(&switch_data->sdev); ++ kfree(switch_data); ++ ++ return 0; ++} ++ ++static struct platform_driver gpio_switch_driver = { ++ .probe = gpio_switch_probe, ++ .remove = gpio_switch_remove, ++ .driver = { ++ .name = "switch-gpio", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init gpio_switch_init(void) ++{ ++ return platform_driver_register(&gpio_switch_driver); ++} ++ ++static void __exit gpio_switch_exit(void) ++{ ++ platform_driver_unregister(&gpio_switch_driver); ++} ++ ++module_init(gpio_switch_init); ++module_exit(gpio_switch_exit); ++ ++MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>"); ++MODULE_DESCRIPTION("GPIO Switch driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig +index f554d25..8b7d47f 100644 +--- a/drivers/thermal/Kconfig ++++ b/drivers/thermal/Kconfig +@@ -42,6 +42,17 @@ config THERMAL_OF + Say 'Y' here if you need to build thermal infrastructure + based on device tree. + ++config THERMAL_WRITABLE_TRIPS ++ bool "Enable writable trip points" ++ help ++ This option allows the system integrator to choose whether ++ trip temperatures can be changed from userspace. The ++ writable trips need to be specified when setting up the ++ thermal zone but the choice here takes precedence. ++ ++ Say 'Y' here if you would like to allow userspace tools to ++ change trip temperatures. ++ + choice + prompt "Default Thermal governor" + default THERMAL_DEFAULT_GOV_STEP_WISE +@@ -71,6 +82,14 @@ config THERMAL_DEFAULT_GOV_USER_SPACE + Select this if you want to let the user space manage the + platform thermals. + ++config THERMAL_DEFAULT_GOV_POWER_ALLOCATOR ++ bool "power_allocator" ++ select THERMAL_GOV_POWER_ALLOCATOR ++ help ++ Select this if you want to control temperature based on ++ system and device power allocation. This governor can only ++ operate on cooling devices that implement the power API. ++ + endchoice + + config THERMAL_GOV_FAIR_SHARE +@@ -99,6 +118,13 @@ config THERMAL_GOV_USER_SPACE + help + Enable this to let the user space manage the platform thermals. + ++config THERMAL_GOV_POWER_ALLOCATOR ++ bool "Power allocator thermal governor" ++ select THERMAL_POWER_ACTOR ++ help ++ Enable this to manage platform thermals by dynamically ++ allocating and limiting power to devices. ++ + config CPU_THERMAL + bool "generic cpu cooling support" + depends on CPU_FREQ +@@ -112,6 +138,18 @@ config CPU_THERMAL + + If you want this support, you should say Y here. + ++config CLOCK_THERMAL ++ bool "Generic clock cooling support" ++ depends on COMMON_CLK ++ depends on PM_OPP ++ help ++ This entry implements the generic clock cooling mechanism through ++ frequency clipping. Typically used to cool off co-processors. The ++ device that is configured to use this cooling mechanism will be ++ controlled to reduce clock frequency whenever temperature is high. ++ ++ If you want this support, you should say Y here. ++ + config THERMAL_EMULATION + bool "Thermal emulation mode support" + help +@@ -185,6 +223,16 @@ config ARMADA_THERMAL + Enable this option if you want to have support for thermal management + controller present in Armada 370 and Armada XP SoC. + ++config TEGRA_SOCTHERM ++ tristate "Tegra SOCTHERM thermal management" ++ depends on ARCH_TEGRA ++ help ++ Enable this option for integrated thermal management support on NVIDIA ++ Tegra124 systems-on-chip. The driver supports four thermal zones ++ (CPU, GPU, MEM, PLLX). Cooling devices can be bound to the thermal ++ zones to manage temperatures. This option is also required for the ++ emergency thermal reset (thermtrip) feature to function. ++ + config DB8500_CPUFREQ_COOLING + tristate "DB8500 cpufreq cooling" + depends on ARCH_U8500 +diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile +index 39c4fe8..dae37c0 100644 +--- a/drivers/thermal/Makefile ++++ b/drivers/thermal/Makefile +@@ -14,10 +14,14 @@ thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o + thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG) += gov_bang_bang.o + thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o + thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o ++thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR) += power_allocator.o + + # cpufreq cooling + thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o + ++# clock cooling ++thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o ++ + # platform thermal drivers + obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o + obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o +@@ -34,3 +38,4 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o + obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ + obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ + obj-$(CONFIG_ST_THERMAL) += st/ ++obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o +diff --git a/drivers/thermal/clock_cooling.c b/drivers/thermal/clock_cooling.c +new file mode 100644 +index 0000000..1b4ff0f +--- /dev/null ++++ b/drivers/thermal/clock_cooling.c +@@ -0,0 +1,485 @@ ++/* ++ * drivers/thermal/clock_cooling.c ++ * ++ * Copyright (C) 2014 Eduardo Valentin <edubezval@gmail.com> ++ * ++ * Copyright (C) 2013 Texas Instruments Inc. ++ * Contact: Eduardo Valentin <eduardo.valentin@ti.com> ++ * ++ * Highly based on cpu_cooling.c. ++ * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) ++ * Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org> ++ * ++ * 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; version 2 of the License. ++ * ++ * 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/cpufreq.h> ++#include <linux/device.h> ++#include <linux/err.h> ++#include <linux/idr.h> ++#include <linux/mutex.h> ++#include <linux/pm_opp.h> ++#include <linux/slab.h> ++#include <linux/thermal.h> ++#include <linux/clock_cooling.h> ++ ++/** ++ * struct clock_cooling_device - data for cooling device with clock ++ * @id: unique integer value corresponding to each clock_cooling_device ++ * registered. ++ * @dev: struct device pointer to the device being used to cool off using ++ * clock frequencies. ++ * @cdev: thermal_cooling_device pointer to keep track of the ++ * registered cooling device. ++ * @clk_rate_change_nb: reference to notifier block used to receive clock ++ * rate changes. ++ * @freq_table: frequency table used to keep track of available frequencies. ++ * @clock_state: integer value representing the current state of clock ++ * cooling devices. ++ * @clock_val: integer value representing the absolute value of the clipped ++ * frequency. ++ * @clk: struct clk reference used to enforce clock limits. ++ * @lock: mutex lock to protect this struct. ++ * ++ * This structure is required for keeping information of each ++ * clock_cooling_device registered. In order to prevent corruption of this a ++ * mutex @lock is used. ++ */ ++struct clock_cooling_device { ++ int id; ++ struct device *dev; ++ struct thermal_cooling_device *cdev; ++ struct notifier_block clk_rate_change_nb; ++ struct cpufreq_frequency_table *freq_table; ++ unsigned long clock_state; ++ unsigned long clock_val; ++ struct clk *clk; ++ struct mutex lock; /* lock to protect the content of this struct */ ++}; ++#define to_clock_cooling_device(x) \ ++ container_of(x, struct clock_cooling_device, clk_rate_change_nb) ++static DEFINE_IDR(clock_idr); ++static DEFINE_MUTEX(cooling_clock_lock); ++ ++/** ++ * clock_cooling_get_idr - function to get an unique id. ++ * @id: int * value generated by this function. ++ * ++ * This function will populate @id with an unique ++ * id, using the idr API. ++ * ++ * Return: 0 on success, an error code on failure. ++ */ ++static int clock_cooling_get_idr(int *id) ++{ ++ int ret; ++ ++ mutex_lock(&cooling_clock_lock); ++ ret = idr_alloc(&clock_idr, NULL, 0, 0, GFP_KERNEL); ++ mutex_unlock(&cooling_clock_lock); ++ if (unlikely(ret < 0)) ++ return ret; ++ *id = ret; ++ ++ return 0; ++} ++ ++/** ++ * release_idr - function to free the unique id. ++ * @id: int value representing the unique id. ++ */ ++static void release_idr(int id) ++{ ++ mutex_lock(&cooling_clock_lock); ++ idr_remove(&clock_idr, id); ++ mutex_unlock(&cooling_clock_lock); ++} ++ ++/* Below code defines functions to be used for clock as cooling device */ ++ ++enum clock_cooling_property { ++ GET_LEVEL, ++ GET_FREQ, ++ GET_MAXL, ++}; ++ ++/** ++ * clock_cooling_get_property - fetch a property of interest for a give cpu. ++ * @ccdev: clock cooling device reference ++ * @input: query parameter ++ * @output: query return ++ * @property: type of query (frequency, level, max level) ++ * ++ * This is the common function to ++ * 1. get maximum clock cooling states ++ * 2. translate frequency to cooling state ++ * 3. translate cooling state to frequency ++ * Note that the code may be not in good shape ++ * but it is written in this way in order to: ++ * a) reduce duplicate code as most of the code can be shared. ++ * b) make sure the logic is consistent when translating between ++ * cooling states and frequencies. ++ * ++ * Return: 0 on success, -EINVAL when invalid parameters are passed. ++ */ ++static int clock_cooling_get_property(struct clock_cooling_device *ccdev, ++ unsigned long input, ++ unsigned long *output, ++ enum clock_cooling_property property) ++{ ++ int i; ++ unsigned long max_level = 0, level = 0; ++ unsigned int freq = CPUFREQ_ENTRY_INVALID; ++ int descend = -1; ++ struct cpufreq_frequency_table *pos, *table = ccdev->freq_table; ++ ++ if (!output) ++ return -EINVAL; ++ ++ if (!table) ++ return -EINVAL; ++ ++ cpufreq_for_each_valid_entry(pos, table) { ++ /* ignore duplicate entry */ ++ if (freq == pos->frequency) ++ continue; ++ ++ /* get the frequency order */ ++ if (freq != CPUFREQ_ENTRY_INVALID && descend == -1) ++ descend = freq > pos->frequency; ++ ++ freq = pos->frequency; ++ max_level++; ++ } ++ ++ /* No valid cpu frequency entry */ ++ if (max_level == 0) ++ return -EINVAL; ++ ++ /* max_level is an index, not a counter */ ++ max_level--; ++ ++ /* get max level */ ++ if (property == GET_MAXL) { ++ *output = max_level; ++ return 0; ++ } ++ ++ if (property == GET_FREQ) ++ level = descend ? input : (max_level - input); ++ ++ i = 0; ++ cpufreq_for_each_valid_entry(pos, table) { ++ /* ignore duplicate entry */ ++ if (freq == pos->frequency) ++ continue; ++ ++ /* now we have a valid frequency entry */ ++ freq = pos->frequency; ++ ++ if (property == GET_LEVEL && (unsigned int)input == freq) { ++ /* get level by frequency */ ++ *output = descend ? i : (max_level - i); ++ return 0; ++ } ++ if (property == GET_FREQ && level == i) { ++ /* get frequency by level */ ++ *output = freq; ++ return 0; ++ } ++ i++; ++ } ++ ++ return -EINVAL; ++} ++ ++/** ++ * clock_cooling_get_level - return the cooling level of given clock cooling. ++ * @cdev: reference of a thermal cooling device of used as clock cooling device ++ * @freq: the frequency of interest ++ * ++ * This function will match the cooling level corresponding to the ++ * requested @freq and return it. ++ * ++ * Return: The matched cooling level on success or THERMAL_CSTATE_INVALID ++ * otherwise. ++ */ ++unsigned long clock_cooling_get_level(struct thermal_cooling_device *cdev, ++ unsigned long freq) ++{ ++ struct clock_cooling_device *ccdev = cdev->devdata; ++ unsigned long val; ++ ++ if (clock_cooling_get_property(ccdev, (unsigned long)freq, &val, ++ GET_LEVEL)) ++ return THERMAL_CSTATE_INVALID; ++ ++ return val; ++} ++EXPORT_SYMBOL_GPL(clock_cooling_get_level); ++ ++/** ++ * clock_cooling_get_frequency - get the absolute value of frequency from level. ++ * @ccdev: clock cooling device reference ++ * @level: cooling level ++ * ++ * This function matches cooling level with frequency. Based on a cooling level ++ * of frequency, equals cooling state of cpu cooling device, it will return ++ * the corresponding frequency. ++ * e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc ++ * ++ * Return: 0 on error, the corresponding frequency otherwise. ++ */ ++static unsigned long ++clock_cooling_get_frequency(struct clock_cooling_device *ccdev, ++ unsigned long level) ++{ ++ int ret = 0; ++ unsigned long freq; ++ ++ ret = clock_cooling_get_property(ccdev, level, &freq, GET_FREQ); ++ if (ret) ++ return 0; ++ ++ return freq; ++} ++ ++/** ++ * clock_cooling_apply - function to apply frequency clipping. ++ * @ccdev: clock_cooling_device pointer containing frequency clipping data. ++ * @cooling_state: value of the cooling state. ++ * ++ * Function used to make sure the clock layer is aware of current thermal ++ * limits. The limits are applied by updating the clock rate in case it is ++ * higher than the corresponding frequency based on the requested cooling_state. ++ * ++ * Return: 0 on success, an error code otherwise (-EINVAL in case wrong ++ * cooling state). ++ */ ++static int clock_cooling_apply(struct clock_cooling_device *ccdev, ++ unsigned long cooling_state) ++{ ++ unsigned long clip_freq, cur_freq; ++ int ret = 0; ++ ++ /* Here we write the clipping */ ++ /* Check if the old cooling action is same as new cooling action */ ++ if (ccdev->clock_state == cooling_state) ++ return 0; ++ ++ clip_freq = clock_cooling_get_frequency(ccdev, cooling_state); ++ if (!clip_freq) ++ return -EINVAL; ++ ++ cur_freq = clk_get_rate(ccdev->clk); ++ ++ mutex_lock(&ccdev->lock); ++ ccdev->clock_state = cooling_state; ++ ccdev->clock_val = clip_freq; ++ /* enforce clock level */ ++ if (cur_freq > clip_freq) ++ ret = clk_set_rate(ccdev->clk, clip_freq); ++ mutex_unlock(&ccdev->lock); ++ ++ return ret; ++} ++ ++/** ++ * clock_cooling_clock_notifier - notifier callback on clock rate changes. ++ * @nb: struct notifier_block * with callback info. ++ * @event: value showing clock event for which this function invoked. ++ * @data: callback-specific data ++ * ++ * Callback to hijack the notification on clock transition. ++ * Every time there is a clock change, we intercept all pre change events ++ * and block the transition in case the new rate infringes thermal limits. ++ * ++ * Return: NOTIFY_DONE (success) or NOTIFY_BAD (new_rate > thermal limit). ++ */ ++static int clock_cooling_clock_notifier(struct notifier_block *nb, ++ unsigned long event, void *data) ++{ ++ struct clk_notifier_data *ndata = data; ++ struct clock_cooling_device *ccdev = to_clock_cooling_device(nb); ++ ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ /* ++ * checks on current state ++ * TODO: current method is not best we can find as it ++ * allows possibly voltage transitions, in case DVFS ++ * layer is also hijacking clock pre notifications. ++ */ ++ if (ndata->new_rate > ccdev->clock_val) ++ return NOTIFY_BAD; ++ /* fall through */ ++ case POST_RATE_CHANGE: ++ case ABORT_RATE_CHANGE: ++ default: ++ return NOTIFY_DONE; ++ } ++} ++ ++/* clock cooling device thermal callback functions are defined below */ ++ ++/** ++ * clock_cooling_get_max_state - callback function to get the max cooling state. ++ * @cdev: thermal cooling device pointer. ++ * @state: fill this variable with the max cooling state. ++ * ++ * Callback for the thermal cooling device to return the clock ++ * max cooling state. ++ * ++ * Return: 0 on success, an error code otherwise. ++ */ ++static int clock_cooling_get_max_state(struct thermal_cooling_device *cdev, ++ unsigned long *state) ++{ ++ struct clock_cooling_device *ccdev = cdev->devdata; ++ unsigned long count = 0; ++ int ret; ++ ++ ret = clock_cooling_get_property(ccdev, 0, &count, GET_MAXL); ++ if (!ret) ++ *state = count; ++ ++ return ret; ++} ++ ++/** ++ * clock_cooling_get_cur_state - function to get the current cooling state. ++ * @cdev: thermal cooling device pointer. ++ * @state: fill this variable with the current cooling state. ++ * ++ * Callback for the thermal cooling device to return the clock ++ * current cooling state. ++ * ++ * Return: 0 (success) ++ */ ++static int clock_cooling_get_cur_state(struct thermal_cooling_device *cdev, ++ unsigned long *state) ++{ ++ struct clock_cooling_device *ccdev = cdev->devdata; ++ ++ *state = ccdev->clock_state; ++ ++ return 0; ++} ++ ++/** ++ * clock_cooling_set_cur_state - function to set the current cooling state. ++ * @cdev: thermal cooling device pointer. ++ * @state: set this variable to the current cooling state. ++ * ++ * Callback for the thermal cooling device to change the clock cooling ++ * current cooling state. ++ * ++ * Return: 0 on success, an error code otherwise. ++ */ ++static int clock_cooling_set_cur_state(struct thermal_cooling_device *cdev, ++ unsigned long state) ++{ ++ struct clock_cooling_device *clock_device = cdev->devdata; ++ ++ return clock_cooling_apply(clock_device, state); ++} ++ ++/* Bind clock callbacks to thermal cooling device ops */ ++static struct thermal_cooling_device_ops const clock_cooling_ops = { ++ .get_max_state = clock_cooling_get_max_state, ++ .get_cur_state = clock_cooling_get_cur_state, ++ .set_cur_state = clock_cooling_set_cur_state, ++}; ++ ++/** ++ * clock_cooling_register - function to create clock cooling device. ++ * @dev: struct device pointer to the device used as clock cooling device. ++ * @clock_name: string containing the clock used as cooling mechanism. ++ * ++ * This interface function registers the clock cooling device with the name ++ * "thermal-clock-%x". The cooling device is based on clock frequencies. ++ * The struct device is assumed to be capable of DVFS transitions. ++ * The OPP layer is used to fetch and fill the available frequencies for ++ * the referred device. The ordered frequency table is used to control ++ * the clock cooling device cooling states and to limit clock transitions ++ * based on the cooling state requested by the thermal framework. ++ * ++ * Return: a valid struct thermal_cooling_device pointer on success, ++ * on failure, it returns a corresponding ERR_PTR(). ++ */ ++struct thermal_cooling_device * ++clock_cooling_register(struct device *dev, const char *clock_name) ++{ ++ struct thermal_cooling_device *cdev; ++ struct clock_cooling_device *ccdev = NULL; ++ char dev_name[THERMAL_NAME_LENGTH]; ++ int ret = 0; ++ ++ ccdev = devm_kzalloc(dev, sizeof(*ccdev), GFP_KERNEL); ++ if (!ccdev) ++ return ERR_PTR(-ENOMEM); ++ ++ ccdev->dev = dev; ++ ccdev->clk = devm_clk_get(dev, clock_name); ++ if (IS_ERR(ccdev->clk)) ++ return ERR_CAST(ccdev->clk); ++ ++ ret = clock_cooling_get_idr(&ccdev->id); ++ if (ret) ++ return ERR_PTR(-EINVAL); ++ ++ snprintf(dev_name, sizeof(dev_name), "thermal-clock-%d", ccdev->id); ++ ++ cdev = thermal_cooling_device_register(dev_name, ccdev, ++ &clock_cooling_ops); ++ if (IS_ERR(cdev)) { ++ release_idr(ccdev->id); ++ return ERR_PTR(-EINVAL); ++ } ++ ccdev->cdev = cdev; ++ ccdev->clk_rate_change_nb.notifier_call = clock_cooling_clock_notifier; ++ ++ /* Assuming someone has already filled the opp table for this device */ ++ ret = dev_pm_opp_init_cpufreq_table(dev, &ccdev->freq_table); ++ if (ret) { ++ release_idr(ccdev->id); ++ return ERR_PTR(ret); ++ } ++ ccdev->clock_state = 0; ++ ccdev->clock_val = clock_cooling_get_frequency(ccdev, 0); ++ ++ clk_notifier_register(ccdev->clk, &ccdev->clk_rate_change_nb); ++ ++ return cdev; ++} ++EXPORT_SYMBOL_GPL(clock_cooling_register); ++ ++/** ++ * clock_cooling_unregister - function to remove clock cooling device. ++ * @cdev: thermal cooling device pointer. ++ * ++ * This interface function unregisters the "thermal-clock-%x" cooling device. ++ */ ++void clock_cooling_unregister(struct thermal_cooling_device *cdev) ++{ ++ struct clock_cooling_device *ccdev; ++ ++ if (!cdev) ++ return; ++ ++ ccdev = cdev->devdata; ++ ++ clk_notifier_unregister(ccdev->clk, &ccdev->clk_rate_change_nb); ++ dev_pm_opp_free_cpufreq_table(ccdev->dev, &ccdev->freq_table); ++ ++ thermal_cooling_device_unregister(ccdev->cdev); ++ release_idr(ccdev->id); ++} ++EXPORT_SYMBOL_GPL(clock_cooling_unregister); +diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c +index ad09e51..6509c61 100644 +--- a/drivers/thermal/cpu_cooling.c ++++ b/drivers/thermal/cpu_cooling.c +@@ -4,6 +4,8 @@ + * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) + * Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org> + * ++ * Copyright (C) 2014 Viresh Kumar <viresh.kumar@linaro.org> ++ * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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 +@@ -24,10 +26,40 @@ + #include <linux/thermal.h> + #include <linux/cpufreq.h> + #include <linux/err.h> ++#include <linux/pm_opp.h> + #include <linux/slab.h> + #include <linux/cpu.h> + #include <linux/cpu_cooling.h> + ++#include <trace/events/thermal.h> ++ ++/* ++ * Cooling state <-> CPUFreq frequency ++ * ++ * Cooling states are translated to frequencies throughout this driver and this ++ * is the relation between them. ++ * ++ * Highest cooling state corresponds to lowest possible frequency. ++ * ++ * i.e. ++ * level 0 --> 1st Max Freq ++ * level 1 --> 2nd Max Freq ++ * ... ++ */ ++ ++/** ++ * struct power_table - frequency to power conversion ++ * @frequency: frequency in KHz ++ * @power: power in mW ++ * ++ * This structure is built when the cooling device registers and helps ++ * in translating frequency to power and viceversa. ++ */ ++struct power_table { ++ u32 frequency; ++ u32 power; ++}; ++ + /** + * struct cpufreq_cooling_device - data for cooling device with cpufreq + * @id: unique integer value corresponding to each cpufreq_cooling_device +@@ -38,25 +70,43 @@ + * cooling devices. + * @cpufreq_val: integer value representing the absolute value of the clipped + * frequency. ++ * @max_level: maximum cooling level. One less than total number of valid ++ * cpufreq frequencies. + * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device. ++ * @node: list_head to link all cpufreq_cooling_device together. ++ * @last_load: load measured by the latest call to cpufreq_get_actual_power() ++ * @time_in_idle: previous reading of the absolute time that this cpu was idle ++ * @time_in_idle_timestamp: wall time of the last invocation of ++ * get_cpu_idle_time_us() ++ * @dyn_power_table: array of struct power_table for frequency to power ++ * conversion, sorted in ascending order. ++ * @dyn_power_table_entries: number of entries in the @dyn_power_table array ++ * @cpu_dev: the first cpu_device from @allowed_cpus that has OPPs registered ++ * @plat_get_static_power: callback to calculate the static power + * +- * This structure is required for keeping information of each +- * cpufreq_cooling_device registered. In order to prevent corruption of this a +- * mutex lock cooling_cpufreq_lock is used. ++ * This structure is required for keeping information of each registered ++ * cpufreq_cooling_device. + */ + struct cpufreq_cooling_device { + int id; + struct thermal_cooling_device *cool_dev; + unsigned int cpufreq_state; + unsigned int cpufreq_val; ++ unsigned int max_level; ++ unsigned int *freq_table; /* In descending order */ + struct cpumask allowed_cpus; + struct list_head node; ++ u32 last_load; ++ u64 *time_in_idle; ++ u64 *time_in_idle_timestamp; ++ struct power_table *dyn_power_table; ++ int dyn_power_table_entries; ++ struct device *cpu_dev; ++ get_static_t plat_get_static_power; + }; + static DEFINE_IDR(cpufreq_idr); + static DEFINE_MUTEX(cooling_cpufreq_lock); + +-static unsigned int cpufreq_dev_count; +- + static LIST_HEAD(cpufreq_dev_list); + + /** +@@ -98,120 +148,30 @@ static void release_idr(struct idr *idr, int id) + /* Below code defines functions to be used for cpufreq as cooling device */ + + /** +- * is_cpufreq_valid - function to check frequency transitioning capability. +- * @cpu: cpu for which check is needed. ++ * get_level: Find the level for a particular frequency ++ * @cpufreq_dev: cpufreq_dev for which the property is required ++ * @freq: Frequency + * +- * This function will check the current state of the system if +- * it is capable of changing the frequency for a given @cpu. +- * +- * Return: 0 if the system is not currently capable of changing +- * the frequency of given cpu. !0 in case the frequency is changeable. +- */ +-static int is_cpufreq_valid(int cpu) +-{ +- struct cpufreq_policy policy; +- +- return !cpufreq_get_policy(&policy, cpu); +-} +- +-enum cpufreq_cooling_property { +- GET_LEVEL, +- GET_FREQ, +- GET_MAXL, +-}; +- +-/** +- * get_property - fetch a property of interest for a give cpu. +- * @cpu: cpu for which the property is required +- * @input: query parameter +- * @output: query return +- * @property: type of query (frequency, level, max level) +- * +- * This is the common function to +- * 1. get maximum cpu cooling states +- * 2. translate frequency to cooling state +- * 3. translate cooling state to frequency +- * Note that the code may be not in good shape +- * but it is written in this way in order to: +- * a) reduce duplicate code as most of the code can be shared. +- * b) make sure the logic is consistent when translating between +- * cooling states and frequencies. +- * +- * Return: 0 on success, -EINVAL when invalid parameters are passed. ++ * Return: level on success, THERMAL_CSTATE_INVALID on error. + */ +-static int get_property(unsigned int cpu, unsigned long input, +- unsigned int *output, +- enum cpufreq_cooling_property property) ++static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_dev, ++ unsigned int freq) + { +- int i; +- unsigned long max_level = 0, level = 0; +- unsigned int freq = CPUFREQ_ENTRY_INVALID; +- int descend = -1; +- struct cpufreq_frequency_table *pos, *table = +- cpufreq_frequency_get_table(cpu); +- +- if (!output) +- return -EINVAL; +- +- if (!table) +- return -EINVAL; +- +- cpufreq_for_each_valid_entry(pos, table) { +- /* ignore duplicate entry */ +- if (freq == pos->frequency) +- continue; +- +- /* get the frequency order */ +- if (freq != CPUFREQ_ENTRY_INVALID && descend == -1) +- descend = freq > pos->frequency; +- +- freq = pos->frequency; +- max_level++; +- } +- +- /* No valid cpu frequency entry */ +- if (max_level == 0) +- return -EINVAL; ++ unsigned long level; + +- /* max_level is an index, not a counter */ +- max_level--; +- +- /* get max level */ +- if (property == GET_MAXL) { +- *output = (unsigned int)max_level; +- return 0; +- } +- +- if (property == GET_FREQ) +- level = descend ? input : (max_level - input); +- +- i = 0; +- cpufreq_for_each_valid_entry(pos, table) { +- /* ignore duplicate entry */ +- if (freq == pos->frequency) +- continue; +- +- /* now we have a valid frequency entry */ +- freq = pos->frequency; ++ for (level = 0; level <= cpufreq_dev->max_level; level++) { ++ if (freq == cpufreq_dev->freq_table[level]) ++ return level; + +- if (property == GET_LEVEL && (unsigned int)input == freq) { +- /* get level by frequency */ +- *output = descend ? i : (max_level - i); +- return 0; +- } +- if (property == GET_FREQ && level == i) { +- /* get frequency by level */ +- *output = freq; +- return 0; +- } +- i++; ++ if (freq > cpufreq_dev->freq_table[level]) ++ break; + } + +- return -EINVAL; ++ return THERMAL_CSTATE_INVALID; + } + + /** +- * cpufreq_cooling_get_level - for a give cpu, return the cooling level. ++ * cpufreq_cooling_get_level - for a given cpu, return the cooling level. + * @cpu: cpu for which the level is required + * @freq: the frequency of interest + * +@@ -223,119 +183,272 @@ static int get_property(unsigned int cpu, unsigned long input, + */ + unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq) + { +- unsigned int val; ++ struct cpufreq_cooling_device *cpufreq_dev; + +- if (get_property(cpu, (unsigned long)freq, &val, GET_LEVEL)) +- return THERMAL_CSTATE_INVALID; ++ mutex_lock(&cooling_cpufreq_lock); ++ list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) { ++ if (cpumask_test_cpu(cpu, &cpufreq_dev->allowed_cpus)) { ++ mutex_unlock(&cooling_cpufreq_lock); ++ return get_level(cpufreq_dev, freq); ++ } ++ } ++ mutex_unlock(&cooling_cpufreq_lock); + +- return (unsigned long)val; ++ pr_err("%s: cpu:%d not part of any cooling device\n", __func__, cpu); ++ return THERMAL_CSTATE_INVALID; + } + EXPORT_SYMBOL_GPL(cpufreq_cooling_get_level); + + /** +- * get_cpu_frequency - get the absolute value of frequency from level. +- * @cpu: cpu for which frequency is fetched. +- * @level: cooling level ++ * cpufreq_thermal_notifier - notifier callback for cpufreq policy change. ++ * @nb: struct notifier_block * with callback info. ++ * @event: value showing cpufreq event for which this function invoked. ++ * @data: callback-specific data + * +- * This function matches cooling level with frequency. Based on a cooling level +- * of frequency, equals cooling state of cpu cooling device, it will return +- * the corresponding frequency. +- * e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc ++ * Callback to hijack the notification on cpufreq policy transition. ++ * Every time there is a change in policy, we will intercept and ++ * update the cpufreq policy with thermal constraints. + * +- * Return: 0 on error, the corresponding frequency otherwise. ++ * Return: 0 (success) + */ +-static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level) ++static int cpufreq_thermal_notifier(struct notifier_block *nb, ++ unsigned long event, void *data) + { +- int ret = 0; +- unsigned int freq; ++ struct cpufreq_policy *policy = data; ++ unsigned long max_freq = 0; ++ struct cpufreq_cooling_device *cpufreq_dev; + +- ret = get_property(cpu, level, &freq, GET_FREQ); +- if (ret) +- return 0; ++ switch (event) { ++ ++ case CPUFREQ_ADJUST: ++ mutex_lock(&cooling_cpufreq_lock); ++ list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) { ++ if (!cpumask_test_cpu(policy->cpu, ++ &cpufreq_dev->allowed_cpus)) ++ continue; + +- return freq; ++ max_freq = cpufreq_dev->cpufreq_val; ++ ++ if (policy->max != max_freq) ++ cpufreq_verify_within_limits(policy, 0, ++ max_freq); ++ } ++ mutex_unlock(&cooling_cpufreq_lock); ++ break; ++ default: ++ return NOTIFY_DONE; ++ } ++ ++ return NOTIFY_OK; + } + + /** +- * cpufreq_apply_cooling - function to apply frequency clipping. +- * @cpufreq_device: cpufreq_cooling_device pointer containing frequency +- * clipping data. +- * @cooling_state: value of the cooling state. ++ * build_dyn_power_table() - create a dynamic power to frequency table ++ * @cpufreq_device: the cpufreq cooling device in which to store the table ++ * @capacitance: dynamic power coefficient for these cpus + * +- * Function used to make sure the cpufreq layer is aware of current thermal +- * limits. The limits are applied by updating the cpufreq policy. ++ * Build a dynamic power to frequency table for this cpu and store it ++ * in @cpufreq_device. This table will be used in cpu_power_to_freq() and ++ * cpu_freq_to_power() to convert between power and frequency ++ * efficiently. Power is stored in mW, frequency in KHz. The ++ * resulting table is in ascending order. + * +- * Return: 0 on success, an error code otherwise (-EINVAL in case wrong +- * cooling state). ++ * Return: 0 on success, -E* on error. + */ +-static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device, +- unsigned long cooling_state) ++static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, ++ u32 capacitance) + { +- unsigned int cpuid, clip_freq; +- struct cpumask *mask = &cpufreq_device->allowed_cpus; +- unsigned int cpu = cpumask_any(mask); ++ struct power_table *power_table; ++ struct dev_pm_opp *opp; ++ struct device *dev = NULL; ++ int num_opps = 0, cpu, i, ret = 0; ++ unsigned long freq; ++ ++ rcu_read_lock(); ++ ++ for_each_cpu(cpu, &cpufreq_device->allowed_cpus) { ++ dev = get_cpu_device(cpu); ++ if (!dev) { ++ dev_warn(&cpufreq_device->cool_dev->device, ++ "No cpu device for cpu %d\n", cpu); ++ continue; ++ } + ++ num_opps = dev_pm_opp_get_opp_count(dev); ++ if (num_opps > 0) { ++ break; ++ } else if (num_opps < 0) { ++ ret = num_opps; ++ goto unlock; ++ } ++ } + +- /* Check if the old cooling action is same as new cooling action */ +- if (cpufreq_device->cpufreq_state == cooling_state) +- return 0; ++ if (num_opps == 0) { ++ ret = -EINVAL; ++ goto unlock; ++ } + +- clip_freq = get_cpu_frequency(cpu, cooling_state); +- if (!clip_freq) +- return -EINVAL; ++ power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL); ++ if (!power_table) { ++ ret = -ENOMEM; ++ goto unlock; ++ } + +- cpufreq_device->cpufreq_state = cooling_state; +- cpufreq_device->cpufreq_val = clip_freq; ++ for (freq = 0, i = 0; ++ opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp); ++ freq++, i++) { ++ u32 freq_mhz, voltage_mv; ++ u64 power; ++ ++ freq_mhz = freq / 1000000; ++ voltage_mv = dev_pm_opp_get_voltage(opp) / 1000; ++ ++ /* ++ * Do the multiplication with MHz and millivolt so as ++ * to not overflow. ++ */ ++ power = (u64)capacitance * freq_mhz * voltage_mv * voltage_mv; ++ do_div(power, 1000000000); + +- for_each_cpu(cpuid, mask) { +- if (is_cpufreq_valid(cpuid)) +- cpufreq_update_policy(cpuid); ++ /* frequency is stored in power_table in KHz */ ++ power_table[i].frequency = freq / 1000; ++ ++ /* power is stored in mW */ ++ power_table[i].power = power; + } + +- return 0; ++ if (i == 0) { ++ ret = PTR_ERR(opp); ++ goto unlock; ++ } ++ ++ cpufreq_device->cpu_dev = dev; ++ cpufreq_device->dyn_power_table = power_table; ++ cpufreq_device->dyn_power_table_entries = i; ++ ++unlock: ++ rcu_read_unlock(); ++ return ret; ++} ++ ++static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_device, ++ u32 freq) ++{ ++ int i; ++ struct power_table *pt = cpufreq_device->dyn_power_table; ++ ++ for (i = 1; i < cpufreq_device->dyn_power_table_entries; i++) ++ if (freq < pt[i].frequency) ++ break; ++ ++ return pt[i - 1].power; ++} ++ ++static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_device, ++ u32 power) ++{ ++ int i; ++ struct power_table *pt = cpufreq_device->dyn_power_table; ++ ++ for (i = 1; i < cpufreq_device->dyn_power_table_entries; i++) ++ if (power < pt[i].power) ++ break; ++ ++ return pt[i - 1].frequency; + } + + /** +- * cpufreq_thermal_notifier - notifier callback for cpufreq policy change. +- * @nb: struct notifier_block * with callback info. +- * @event: value showing cpufreq event for which this function invoked. +- * @data: callback-specific data ++ * get_load() - get load for a cpu since last updated ++ * @cpufreq_device: &struct cpufreq_cooling_device for this cpu ++ * @cpu: cpu number + * +- * Callback to hijack the notification on cpufreq policy transition. +- * Every time there is a change in policy, we will intercept and +- * update the cpufreq policy with thermal constraints. +- * +- * Return: 0 (success) ++ * Return: The average load of cpu @cpu in percentage since this ++ * function was last called. + */ +-static int cpufreq_thermal_notifier(struct notifier_block *nb, +- unsigned long event, void *data) ++static u32 get_load(struct cpufreq_cooling_device *cpufreq_device, int cpu) + { +- struct cpufreq_policy *policy = data; +- unsigned long max_freq = 0; +- struct cpufreq_cooling_device *cpufreq_dev; ++ u32 load; ++ u64 now, now_idle, delta_time, delta_idle; ++ ++ now_idle = get_cpu_idle_time(cpu, &now, 0); ++ delta_idle = now_idle - cpufreq_device->time_in_idle[cpu]; ++ delta_time = now - cpufreq_device->time_in_idle_timestamp[cpu]; ++ ++ if (delta_time <= delta_idle) ++ load = 0; ++ else ++ load = div64_u64(100 * (delta_time - delta_idle), delta_time); + +- if (event != CPUFREQ_ADJUST) ++ cpufreq_device->time_in_idle[cpu] = now_idle; ++ cpufreq_device->time_in_idle_timestamp[cpu] = now; ++ ++ return load; ++} ++ ++/** ++ * get_static_power() - calculate the static power consumed by the cpus ++ * @cpufreq_device: struct &cpufreq_cooling_device for this cpu cdev ++ * @tz: thermal zone device in which we're operating ++ * @freq: frequency in KHz ++ * @power: pointer in which to store the calculated static power ++ * ++ * Calculate the static power consumed by the cpus described by ++ * @cpu_actor running at frequency @freq. This function relies on a ++ * platform specific function that should have been provided when the ++ * actor was registered. If it wasn't, the static power is assumed to ++ * be negligible. The calculated static power is stored in @power. ++ * ++ * Return: 0 on success, -E* on failure. ++ */ ++static int get_static_power(struct cpufreq_cooling_device *cpufreq_device, ++ struct thermal_zone_device *tz, unsigned long freq, ++ u32 *power) ++{ ++ struct dev_pm_opp *opp; ++ unsigned long voltage; ++ struct cpumask *cpumask = &cpufreq_device->allowed_cpus; ++ unsigned long freq_hz = freq * 1000; ++ ++ if (!cpufreq_device->plat_get_static_power || ++ !cpufreq_device->cpu_dev) { ++ *power = 0; + return 0; ++ } + +- mutex_lock(&cooling_cpufreq_lock); +- list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) { +- if (!cpumask_test_cpu(policy->cpu, +- &cpufreq_dev->allowed_cpus)) +- continue; ++ rcu_read_lock(); + +- if (!cpufreq_dev->cpufreq_val) +- cpufreq_dev->cpufreq_val = get_cpu_frequency( +- cpumask_any(&cpufreq_dev->allowed_cpus), +- cpufreq_dev->cpufreq_state); ++ opp = dev_pm_opp_find_freq_exact(cpufreq_device->cpu_dev, freq_hz, ++ true); ++ voltage = dev_pm_opp_get_voltage(opp); + +- max_freq = cpufreq_dev->cpufreq_val; ++ rcu_read_unlock(); + +- if (policy->max != max_freq) +- cpufreq_verify_within_limits(policy, 0, max_freq); ++ if (voltage == 0) { ++ dev_warn_ratelimited(cpufreq_device->cpu_dev, ++ "Failed to get voltage for frequency %lu: %ld\n", ++ freq_hz, IS_ERR(opp) ? PTR_ERR(opp) : 0); ++ return -EINVAL; + } +- mutex_unlock(&cooling_cpufreq_lock); + +- return 0; ++ return cpufreq_device->plat_get_static_power(cpumask, tz->passive_delay, ++ voltage, power); ++} ++ ++/** ++ * get_dynamic_power() - calculate the dynamic power ++ * @cpufreq_device: &cpufreq_cooling_device for this cdev ++ * @freq: current frequency ++ * ++ * Return: the dynamic power consumed by the cpus described by ++ * @cpufreq_device. ++ */ ++static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_device, ++ unsigned long freq) ++{ ++ u32 raw_cpu_power; ++ ++ raw_cpu_power = cpu_freq_to_power(cpufreq_device, freq); ++ return (raw_cpu_power * cpufreq_device->last_load) / 100; + } + + /* cpufreq cooling device callback functions are defined below */ +@@ -354,19 +467,9 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) + { + struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; +- struct cpumask *mask = &cpufreq_device->allowed_cpus; +- unsigned int cpu; +- unsigned int count = 0; +- int ret; + +- cpu = cpumask_any(mask); +- +- ret = get_property(cpu, 0, &count, GET_MAXL); +- +- if (count > 0) +- *state = count; +- +- return ret; ++ *state = cpufreq_device->max_level; ++ return 0; + } + + /** +@@ -403,12 +506,225 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) + { + struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; ++ unsigned int cpu = cpumask_any(&cpufreq_device->allowed_cpus); ++ unsigned int clip_freq; ++ ++ /* Request state should be less than max_level */ ++ if (WARN_ON(state > cpufreq_device->max_level)) ++ return -EINVAL; ++ ++ /* Check if the old cooling action is same as new cooling action */ ++ if (cpufreq_device->cpufreq_state == state) ++ return 0; ++ ++ clip_freq = cpufreq_device->freq_table[state]; ++ cpufreq_device->cpufreq_state = state; ++ cpufreq_device->cpufreq_val = clip_freq; ++ ++ cpufreq_update_policy(cpu); ++ ++ return 0; ++} ++ ++/** ++ * cpufreq_get_requested_power() - get the current power ++ * @cdev: &thermal_cooling_device pointer ++ * @tz: a valid thermal zone device pointer ++ * @power: pointer in which to store the resulting power ++ * ++ * Calculate the current power consumption of the cpus in milliwatts ++ * and store it in @power. This function should actually calculate ++ * the requested power, but it's hard to get the frequency that ++ * cpufreq would have assigned if there were no thermal limits. ++ * Instead, we calculate the current power on the assumption that the ++ * immediate future will look like the immediate past. ++ * ++ * We use the current frequency and the average load since this ++ * function was last called. In reality, there could have been ++ * multiple opps since this function was last called and that affects ++ * the load calculation. While it's not perfectly accurate, this ++ * simplification is good enough and works. REVISIT this, as more ++ * complex code may be needed if experiments show that it's not ++ * accurate enough. ++ * ++ * Return: 0 on success, -E* if getting the static power failed. ++ */ ++static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, ++ struct thermal_zone_device *tz, ++ u32 *power) ++{ ++ unsigned long freq; ++ int i = 0, cpu, ret; ++ u32 static_power, dynamic_power, total_load = 0; ++ struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; ++ u32 *load_cpu = NULL; ++ ++ cpu = cpumask_any_and(&cpufreq_device->allowed_cpus, cpu_online_mask); ++ ++ /* ++ * All the CPUs are offline, thus the requested power by ++ * the cdev is 0 ++ */ ++ if (cpu >= nr_cpu_ids) { ++ *power = 0; ++ return 0; ++ } ++ ++ freq = cpufreq_quick_get(cpu); ++ ++ if (trace_thermal_power_cpu_get_power_enabled()) { ++ u32 ncpus = cpumask_weight(&cpufreq_device->allowed_cpus); ++ ++ load_cpu = devm_kcalloc(&cdev->device, ncpus, sizeof(*load_cpu), ++ GFP_KERNEL); ++ } ++ ++ for_each_cpu(cpu, &cpufreq_device->allowed_cpus) { ++ u32 load; ++ ++ if (cpu_online(cpu)) ++ load = get_load(cpufreq_device, cpu); ++ else ++ load = 0; ++ ++ total_load += load; ++ if (trace_thermal_power_cpu_limit_enabled() && load_cpu) ++ load_cpu[i] = load; ++ ++ i++; ++ } ++ ++ cpufreq_device->last_load = total_load; ++ ++ dynamic_power = get_dynamic_power(cpufreq_device, freq); ++ ret = get_static_power(cpufreq_device, tz, freq, &static_power); ++ if (ret) { ++ if (load_cpu) ++ devm_kfree(&cdev->device, load_cpu); ++ return ret; ++ } ++ ++ if (load_cpu) { ++ trace_thermal_power_cpu_get_power( ++ &cpufreq_device->allowed_cpus, ++ freq, load_cpu, i, dynamic_power, static_power); ++ ++ devm_kfree(&cdev->device, load_cpu); ++ } ++ ++ *power = static_power + dynamic_power; ++ return 0; ++} ++ ++/** ++ * cpufreq_state2power() - convert a cpu cdev state to power consumed ++ * @cdev: &thermal_cooling_device pointer ++ * @tz: a valid thermal zone device pointer ++ * @state: cooling device state to be converted ++ * @power: pointer in which to store the resulting power ++ * ++ * Convert cooling device state @state into power consumption in ++ * milliwatts assuming 100% load. Store the calculated power in ++ * @power. ++ * ++ * Return: 0 on success, -EINVAL if the cooling device state could not ++ * be converted into a frequency or other -E* if there was an error ++ * when calculating the static power. ++ */ ++static int cpufreq_state2power(struct thermal_cooling_device *cdev, ++ struct thermal_zone_device *tz, ++ unsigned long state, u32 *power) ++{ ++ unsigned int freq, num_cpus; ++ cpumask_t cpumask; ++ u32 static_power, dynamic_power; ++ int ret; ++ struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; ++ ++ cpumask_and(&cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask); ++ num_cpus = cpumask_weight(&cpumask); ++ ++ /* None of our cpus are online, so no power */ ++ if (num_cpus == 0) { ++ *power = 0; ++ return 0; ++ } ++ ++ freq = cpufreq_device->freq_table[state]; ++ if (!freq) ++ return -EINVAL; + +- return cpufreq_apply_cooling(cpufreq_device, state); ++ dynamic_power = cpu_freq_to_power(cpufreq_device, freq) * num_cpus; ++ ret = get_static_power(cpufreq_device, tz, freq, &static_power); ++ if (ret) ++ return ret; ++ ++ *power = static_power + dynamic_power; ++ return 0; ++} ++ ++/** ++ * cpufreq_power2state() - convert power to a cooling device state ++ * @cdev: &thermal_cooling_device pointer ++ * @tz: a valid thermal zone device pointer ++ * @power: power in milliwatts to be converted ++ * @state: pointer in which to store the resulting state ++ * ++ * Calculate a cooling device state for the cpus described by @cdev ++ * that would allow them to consume at most @power mW and store it in ++ * @state. Note that this calculation depends on external factors ++ * such as the cpu load or the current static power. Calling this ++ * function with the same power as input can yield different cooling ++ * device states depending on those external factors. ++ * ++ * Return: 0 on success, -ENODEV if no cpus are online or -EINVAL if ++ * the calculated frequency could not be converted to a valid state. ++ * The latter should not happen unless the frequencies available to ++ * cpufreq have changed since the initialization of the cpu cooling ++ * device. ++ */ ++static int cpufreq_power2state(struct thermal_cooling_device *cdev, ++ struct thermal_zone_device *tz, u32 power, ++ unsigned long *state) ++{ ++ unsigned int cpu, cur_freq, target_freq; ++ int ret; ++ s32 dyn_power; ++ u32 last_load, normalised_power, static_power; ++ struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; ++ ++ cpu = cpumask_any_and(&cpufreq_device->allowed_cpus, cpu_online_mask); ++ ++ /* None of our cpus are online */ ++ if (cpu >= nr_cpu_ids) ++ return -ENODEV; ++ ++ cur_freq = cpufreq_quick_get(cpu); ++ ret = get_static_power(cpufreq_device, tz, cur_freq, &static_power); ++ if (ret) ++ return ret; ++ ++ dyn_power = power - static_power; ++ dyn_power = dyn_power > 0 ? dyn_power : 0; ++ last_load = cpufreq_device->last_load ?: 1; ++ normalised_power = (dyn_power * 100) / last_load; ++ target_freq = cpu_power_to_freq(cpufreq_device, normalised_power); ++ ++ *state = cpufreq_cooling_get_level(cpu, target_freq); ++ if (*state == THERMAL_CSTATE_INVALID) { ++ dev_warn_ratelimited(&cdev->device, ++ "Failed to convert %dKHz for cpu %d into a cdev state\n", ++ target_freq, cpu); ++ return -EINVAL; ++ } ++ ++ trace_thermal_power_cpu_limit(&cpufreq_device->allowed_cpus, ++ target_freq, *state, power); ++ return 0; + } + + /* Bind cpufreq callbacks to thermal cooling device ops */ +-static struct thermal_cooling_device_ops const cpufreq_cooling_ops = { ++static struct thermal_cooling_device_ops cpufreq_cooling_ops = { + .get_max_state = cpufreq_get_max_state, + .get_cur_state = cpufreq_get_cur_state, + .set_cur_state = cpufreq_set_cur_state, +@@ -419,10 +735,28 @@ static struct notifier_block thermal_cpufreq_notifier_block = { + .notifier_call = cpufreq_thermal_notifier, + }; + ++static unsigned int find_next_max(struct cpufreq_frequency_table *table, ++ unsigned int prev_max) ++{ ++ struct cpufreq_frequency_table *pos; ++ unsigned int max = 0; ++ ++ cpufreq_for_each_valid_entry(pos, table) { ++ if (pos->frequency > max && pos->frequency < prev_max) ++ max = pos->frequency; ++ } ++ ++ return max; ++} ++ + /** + * __cpufreq_cooling_register - helper function to create cpufreq cooling device + * @np: a valid struct device_node to the cooling device device tree node + * @clip_cpus: cpumask of cpus where the frequency constraints will happen. ++ * Normally this should be same as cpufreq policy->related_cpus. ++ * @capacitance: dynamic power coefficient for these cpus ++ * @plat_static_func: function to calculate the static power consumed by these ++ * cpus (optional) + * + * This interface function registers the cpufreq cooling device with the name + * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq +@@ -434,40 +768,77 @@ static struct notifier_block thermal_cpufreq_notifier_block = { + */ + static struct thermal_cooling_device * + __cpufreq_cooling_register(struct device_node *np, +- const struct cpumask *clip_cpus) ++ const struct cpumask *clip_cpus, u32 capacitance, ++ get_static_t plat_static_func) + { + struct thermal_cooling_device *cool_dev; +- struct cpufreq_cooling_device *cpufreq_dev = NULL; +- unsigned int min = 0, max = 0; ++ struct cpufreq_cooling_device *cpufreq_dev; + char dev_name[THERMAL_NAME_LENGTH]; +- int ret = 0, i; +- struct cpufreq_policy policy; ++ struct cpufreq_frequency_table *pos, *table; ++ unsigned int freq, i, num_cpus; ++ int ret; + +- /* Verify that all the clip cpus have same freq_min, freq_max limit */ +- for_each_cpu(i, clip_cpus) { +- /* continue if cpufreq policy not found and not return error */ +- if (!cpufreq_get_policy(&policy, i)) +- continue; +- if (min == 0 && max == 0) { +- min = policy.cpuinfo.min_freq; +- max = policy.cpuinfo.max_freq; +- } else { +- if (min != policy.cpuinfo.min_freq || +- max != policy.cpuinfo.max_freq) +- return ERR_PTR(-EINVAL); +- } ++ table = cpufreq_frequency_get_table(cpumask_first(clip_cpus)); ++ if (!table) { ++ pr_debug("%s: CPUFreq table not found\n", __func__); ++ return ERR_PTR(-EPROBE_DEFER); + } +- cpufreq_dev = kzalloc(sizeof(struct cpufreq_cooling_device), +- GFP_KERNEL); ++ ++ cpufreq_dev = kzalloc(sizeof(*cpufreq_dev), GFP_KERNEL); + if (!cpufreq_dev) + return ERR_PTR(-ENOMEM); + ++ num_cpus = cpumask_weight(clip_cpus); ++ cpufreq_dev->time_in_idle = kcalloc(num_cpus, ++ sizeof(*cpufreq_dev->time_in_idle), ++ GFP_KERNEL); ++ if (!cpufreq_dev->time_in_idle) { ++ cool_dev = ERR_PTR(-ENOMEM); ++ goto free_cdev; ++ } ++ ++ cpufreq_dev->time_in_idle_timestamp = ++ kcalloc(num_cpus, sizeof(*cpufreq_dev->time_in_idle_timestamp), ++ GFP_KERNEL); ++ if (!cpufreq_dev->time_in_idle_timestamp) { ++ cool_dev = ERR_PTR(-ENOMEM); ++ goto free_time_in_idle; ++ } ++ ++ /* Find max levels */ ++ cpufreq_for_each_valid_entry(pos, table) ++ cpufreq_dev->max_level++; ++ ++ cpufreq_dev->freq_table = kmalloc(sizeof(*cpufreq_dev->freq_table) * ++ cpufreq_dev->max_level, GFP_KERNEL); ++ if (!cpufreq_dev->freq_table) { ++ cool_dev = ERR_PTR(-ENOMEM); ++ goto free_time_in_idle_timestamp; ++ } ++ ++ /* max_level is an index, not a counter */ ++ cpufreq_dev->max_level--; ++ + cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus); + ++ if (capacitance) { ++ cpufreq_cooling_ops.get_requested_power = ++ cpufreq_get_requested_power; ++ cpufreq_cooling_ops.state2power = cpufreq_state2power; ++ cpufreq_cooling_ops.power2state = cpufreq_power2state; ++ cpufreq_dev->plat_get_static_power = plat_static_func; ++ ++ ret = build_dyn_power_table(cpufreq_dev, capacitance); ++ if (ret) { ++ cool_dev = ERR_PTR(ret); ++ goto free_table; ++ } ++ } ++ + ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); + if (ret) { +- kfree(cpufreq_dev); +- return ERR_PTR(-EINVAL); ++ cool_dev = ERR_PTR(ret); ++ goto free_table; + } + + snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", +@@ -475,25 +846,48 @@ __cpufreq_cooling_register(struct device_node *np, + + cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev, + &cpufreq_cooling_ops); +- if (IS_ERR(cool_dev)) { +- release_idr(&cpufreq_idr, cpufreq_dev->id); +- kfree(cpufreq_dev); +- return cool_dev; ++ if (IS_ERR(cool_dev)) ++ goto remove_idr; ++ ++ /* Fill freq-table in descending order of frequencies */ ++ for (i = 0, freq = -1; i <= cpufreq_dev->max_level; i++) { ++ freq = find_next_max(table, freq); ++ cpufreq_dev->freq_table[i] = freq; ++ ++ /* Warn for duplicate entries */ ++ if (!freq) ++ pr_warn("%s: table has duplicate entries\n", __func__); ++ else ++ pr_debug("%s: freq:%u KHz\n", __func__, freq); + } ++ ++ cpufreq_dev->cpufreq_val = cpufreq_dev->freq_table[0]; + cpufreq_dev->cool_dev = cool_dev; +- cpufreq_dev->cpufreq_state = 0; ++ + mutex_lock(&cooling_cpufreq_lock); + + /* Register the notifier for first cpufreq cooling device */ +- if (cpufreq_dev_count == 0) ++ if (list_empty(&cpufreq_dev_list)) + cpufreq_register_notifier(&thermal_cpufreq_notifier_block, + CPUFREQ_POLICY_NOTIFIER); +- cpufreq_dev_count++; + list_add(&cpufreq_dev->node, &cpufreq_dev_list); + + mutex_unlock(&cooling_cpufreq_lock); + + return cool_dev; ++ ++remove_idr: ++ release_idr(&cpufreq_idr, cpufreq_dev->id); ++free_table: ++ kfree(cpufreq_dev->freq_table); ++free_time_in_idle_timestamp: ++ kfree(cpufreq_dev->time_in_idle_timestamp); ++free_time_in_idle: ++ kfree(cpufreq_dev->time_in_idle); ++free_cdev: ++ kfree(cpufreq_dev); ++ ++ return cool_dev; + } + + /** +@@ -510,7 +904,7 @@ __cpufreq_cooling_register(struct device_node *np, + struct thermal_cooling_device * + cpufreq_cooling_register(const struct cpumask *clip_cpus) + { +- return __cpufreq_cooling_register(NULL, clip_cpus); ++ return __cpufreq_cooling_register(NULL, clip_cpus, 0, NULL); + } + EXPORT_SYMBOL_GPL(cpufreq_cooling_register); + +@@ -534,11 +928,78 @@ of_cpufreq_cooling_register(struct device_node *np, + if (!np) + return ERR_PTR(-EINVAL); + +- return __cpufreq_cooling_register(np, clip_cpus); ++ return __cpufreq_cooling_register(np, clip_cpus, 0, NULL); + } + EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register); + + /** ++ * cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions ++ * @clip_cpus: cpumask of cpus where the frequency constraints will happen ++ * @capacitance: dynamic power coefficient for these cpus ++ * @plat_static_func: function to calculate the static power consumed by these ++ * cpus (optional) ++ * ++ * This interface function registers the cpufreq cooling device with ++ * the name "thermal-cpufreq-%x". This api can support multiple ++ * instances of cpufreq cooling devices. Using this function, the ++ * cooling device will implement the power extensions by using a ++ * simple cpu power model. The cpus must have registered their OPPs ++ * using the OPP library. ++ * ++ * An optional @plat_static_func may be provided to calculate the ++ * static power consumed by these cpus. If the platform's static ++ * power consumption is unknown or negligible, make it NULL. ++ * ++ * Return: a valid struct thermal_cooling_device pointer on success, ++ * on failure, it returns a corresponding ERR_PTR(). ++ */ ++struct thermal_cooling_device * ++cpufreq_power_cooling_register(const struct cpumask *clip_cpus, u32 capacitance, ++ get_static_t plat_static_func) ++{ ++ return __cpufreq_cooling_register(NULL, clip_cpus, capacitance, ++ plat_static_func); ++} ++EXPORT_SYMBOL(cpufreq_power_cooling_register); ++ ++/** ++ * of_cpufreq_power_cooling_register() - create cpufreq cooling device with power extensions ++ * @np: a valid struct device_node to the cooling device device tree node ++ * @clip_cpus: cpumask of cpus where the frequency constraints will happen ++ * @capacitance: dynamic power coefficient for these cpus ++ * @plat_static_func: function to calculate the static power consumed by these ++ * cpus (optional) ++ * ++ * This interface function registers the cpufreq cooling device with ++ * the name "thermal-cpufreq-%x". This api can support multiple ++ * instances of cpufreq cooling devices. Using this API, the cpufreq ++ * cooling device will be linked to the device tree node provided. ++ * Using this function, the cooling device will implement the power ++ * extensions by using a simple cpu power model. The cpus must have ++ * registered their OPPs using the OPP library. ++ * ++ * An optional @plat_static_func may be provided to calculate the ++ * static power consumed by these cpus. If the platform's static ++ * power consumption is unknown or negligible, make it NULL. ++ * ++ * Return: a valid struct thermal_cooling_device pointer on success, ++ * on failure, it returns a corresponding ERR_PTR(). ++ */ ++struct thermal_cooling_device * ++of_cpufreq_power_cooling_register(struct device_node *np, ++ const struct cpumask *clip_cpus, ++ u32 capacitance, ++ get_static_t plat_static_func) ++{ ++ if (!np) ++ return ERR_PTR(-EINVAL); ++ ++ return __cpufreq_cooling_register(np, clip_cpus, capacitance, ++ plat_static_func); ++} ++EXPORT_SYMBOL(of_cpufreq_power_cooling_register); ++ ++/** + * cpufreq_cooling_unregister - function to remove cpufreq cooling device. + * @cdev: thermal cooling device pointer. + * +@@ -554,16 +1015,18 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) + cpufreq_dev = cdev->devdata; + mutex_lock(&cooling_cpufreq_lock); + list_del(&cpufreq_dev->node); +- cpufreq_dev_count--; + + /* Unregister the notifier for the last cpufreq cooling device */ +- if (cpufreq_dev_count == 0) ++ if (list_empty(&cpufreq_dev_list)) + cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, + CPUFREQ_POLICY_NOTIFIER); + mutex_unlock(&cooling_cpufreq_lock); + + thermal_cooling_device_unregister(cpufreq_dev->cool_dev); + release_idr(&cpufreq_idr, cpufreq_dev->id); ++ kfree(cpufreq_dev->time_in_idle_timestamp); ++ kfree(cpufreq_dev->time_in_idle); ++ kfree(cpufreq_dev->freq_table); + kfree(cpufreq_dev); + } + EXPORT_SYMBOL_GPL(cpufreq_cooling_unregister); +diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c +index 786d192..1ac7ec6 100644 +--- a/drivers/thermal/db8500_cpufreq_cooling.c ++++ b/drivers/thermal/db8500_cpufreq_cooling.c +@@ -18,7 +18,6 @@ + */ + + #include <linux/cpu_cooling.h> +-#include <linux/cpufreq.h> + #include <linux/err.h> + #include <linux/module.h> + #include <linux/of.h> +@@ -30,10 +29,6 @@ static int db8500_cpufreq_cooling_probe(struct platform_device *pdev) + struct thermal_cooling_device *cdev; + struct cpumask mask_val; + +- /* make sure cpufreq driver has been initialized */ +- if (!cpufreq_frequency_get_table(0)) +- return -EPROBE_DEFER; +- + cpumask_set_cpu(0, &mask_val); + cdev = cpufreq_cooling_register(&mask_val); + +diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c +index 1e3b3bf..e3ccc22 100644 +--- a/drivers/thermal/db8500_thermal.c ++++ b/drivers/thermal/db8500_thermal.c +@@ -76,7 +76,7 @@ static int db8500_cdev_bind(struct thermal_zone_device *thermal, + upper = lower = i > max_state ? max_state : i; + + ret = thermal_zone_bind_cooling_device(thermal, i, cdev, +- upper, lower); ++ upper, lower, THERMAL_WEIGHT_DEFAULT); + + dev_info(&cdev->device, "%s bind to %d: %d-%s\n", cdev->type, + i, ret, ret ? "fail" : "succeed"); +diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/fair_share.c +index 6e0a3fb..8c50b8d 100644 +--- a/drivers/thermal/fair_share.c ++++ b/drivers/thermal/fair_share.c +@@ -59,13 +59,13 @@ static int get_trip_level(struct thermal_zone_device *tz) + } + + static long get_target_state(struct thermal_zone_device *tz, +- struct thermal_cooling_device *cdev, int weight, int level) ++ struct thermal_cooling_device *cdev, int percentage, int level) + { + unsigned long max_state; + + cdev->ops->get_max_state(cdev, &max_state); + +- return (long)(weight * level * max_state) / (100 * tz->trips); ++ return (long)(percentage * level * max_state) / (100 * tz->trips); + } + + /** +@@ -77,7 +77,7 @@ static long get_target_state(struct thermal_zone_device *tz, + * + * Parameters used for Throttling: + * P1. max_state: Maximum throttle state exposed by the cooling device. +- * P2. weight[i]/100: ++ * P2. percentage[i]/100: + * How 'effective' the 'i'th device is, in cooling the given zone. + * P3. cur_trip_level/max_no_of_trips: + * This describes the extent to which the devices should be throttled. +@@ -88,28 +88,33 @@ static long get_target_state(struct thermal_zone_device *tz, + */ + static int fair_share_throttle(struct thermal_zone_device *tz, int trip) + { +- const struct thermal_zone_params *tzp; +- struct thermal_cooling_device *cdev; + struct thermal_instance *instance; +- int i; ++ int total_weight = 0; ++ int total_instance = 0; + int cur_trip_level = get_trip_level(tz); + +- if (!tz->tzp || !tz->tzp->tbp) +- return -EINVAL; ++ list_for_each_entry(instance, &tz->thermal_instances, tz_node) { ++ if (instance->trip != trip) ++ continue; ++ ++ total_weight += instance->weight; ++ total_instance++; ++ } + +- tzp = tz->tzp; ++ list_for_each_entry(instance, &tz->thermal_instances, tz_node) { ++ int percentage; ++ struct thermal_cooling_device *cdev = instance->cdev; + +- for (i = 0; i < tzp->num_tbps; i++) { +- if (!tzp->tbp[i].cdev) ++ if (instance->trip != trip) + continue; + +- cdev = tzp->tbp[i].cdev; +- instance = get_thermal_instance(tz, cdev, trip); +- if (!instance) +- continue; ++ if (!total_weight) ++ percentage = 100 / total_instance; ++ else ++ percentage = (instance->weight * 100) / total_weight; + +- instance->target = get_target_state(tz, cdev, +- tzp->tbp[i].weight, cur_trip_level); ++ instance->target = get_target_state(tz, cdev, percentage, ++ cur_trip_level); + + instance->cdev->updated = false; + thermal_cdev_update(cdev); +diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c +index 5a1f107..31ada06 100644 +--- a/drivers/thermal/imx_thermal.c ++++ b/drivers/thermal/imx_thermal.c +@@ -9,7 +9,6 @@ + + #include <linux/clk.h> + #include <linux/cpu_cooling.h> +-#include <linux/cpufreq.h> + #include <linux/delay.h> + #include <linux/device.h> + #include <linux/init.h> +@@ -307,7 +306,8 @@ static int imx_bind(struct thermal_zone_device *tz, + + ret = thermal_zone_bind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev, + THERMAL_NO_LIMIT, +- THERMAL_NO_LIMIT); ++ THERMAL_NO_LIMIT, ++ THERMAL_WEIGHT_DEFAULT); + if (ret) { + dev_err(&tz->device, + "binding zone %s with cdev %s failed:%d\n", +@@ -459,10 +459,6 @@ static int imx_thermal_probe(struct platform_device *pdev) + int measure_freq; + int ret; + +- if (!cpufreq_get_current_driver()) { +- dev_dbg(&pdev->dev, "no cpufreq driver!"); +- return -EPROBE_DEFER; +- } + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; +diff --git a/drivers/thermal/int340x_thermal/int3403_thermal.c b/drivers/thermal/int340x_thermal/int3403_thermal.c +index 6e9fb62..76910d3 100644 +--- a/drivers/thermal/int340x_thermal/int3403_thermal.c ++++ b/drivers/thermal/int340x_thermal/int3403_thermal.c +@@ -471,7 +471,6 @@ static struct platform_driver int3403_driver = { + .remove = int3403_remove, + .driver = { + .name = "int3403 thermal", +- .owner = THIS_MODULE, + .acpi_match_table = int3403_device_ids, + }, + }; +diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c +index 62143ba..b295b2b 100644 +--- a/drivers/thermal/of-thermal.c ++++ b/drivers/thermal/of-thermal.c +@@ -30,27 +30,13 @@ + #include <linux/err.h> + #include <linux/export.h> + #include <linux/string.h> ++#include <linux/thermal.h> + + #include "thermal_core.h" + + /*** Private data structures to represent thermal device tree data ***/ + + /** +- * struct __thermal_trip - representation of a point in temperature domain +- * @np: pointer to struct device_node that this trip point was created from +- * @temperature: temperature value in miliCelsius +- * @hysteresis: relative hysteresis in miliCelsius +- * @type: trip point type +- */ +- +-struct __thermal_trip { +- struct device_node *np; +- unsigned long int temperature; +- unsigned long int hysteresis; +- enum thermal_trip_type type; +-}; +- +-/** + * struct __thermal_bind_param - a match between trip and cooling device + * @cooling_device: a pointer to identify the referred cooling device + * @trip_id: the trip point index +@@ -72,23 +58,26 @@ struct __thermal_bind_params { + * @mode: current thermal zone device mode (enabled/disabled) + * @passive_delay: polling interval while passive cooling is activated + * @polling_delay: zone polling interval ++ * @slope: slope of the temperature adjustment curve ++ * @offset: offset of the temperature adjustment curve + * @ntrips: number of trip points + * @trips: an array of trip points (0..ntrips - 1) + * @num_tbps: number of thermal bind params + * @tbps: an array of thermal bind params (0..num_tbps - 1) + * @sensor_data: sensor private data used while reading temperature and trend +- * @get_temp: sensor callback to read temperature +- * @get_trend: sensor callback to read temperature trend ++ * @ops: set of callbacks to handle the thermal zone based on DT + */ + + struct __thermal_zone { + enum thermal_device_mode mode; + int passive_delay; + int polling_delay; ++ int slope; ++ int offset; + + /* trip data */ + int ntrips; +- struct __thermal_trip *trips; ++ struct thermal_trip *trips; + + /* cooling binding data */ + int num_tbps; +@@ -96,8 +85,7 @@ struct __thermal_zone { + + /* sensor interface */ + void *sensor_data; +- int (*get_temp)(void *, long *); +- int (*get_trend)(void *, long *); ++ const struct thermal_zone_of_device_ops *ops; + }; + + /*** DT thermal zone device callbacks ***/ +@@ -107,10 +95,96 @@ static int of_thermal_get_temp(struct thermal_zone_device *tz, + { + struct __thermal_zone *data = tz->devdata; + +- if (!data->get_temp) ++ if (!data->ops->get_temp) + return -EINVAL; + +- return data->get_temp(data->sensor_data, temp); ++ return data->ops->get_temp(data->sensor_data, temp); ++} ++ ++/** ++ * of_thermal_get_ntrips - function to export number of available trip ++ * points. ++ * @tz: pointer to a thermal zone ++ * ++ * This function is a globally visible wrapper to get number of trip points ++ * stored in the local struct __thermal_zone ++ * ++ * Return: number of available trip points, -ENODEV when data not available ++ */ ++int of_thermal_get_ntrips(struct thermal_zone_device *tz) ++{ ++ struct __thermal_zone *data = tz->devdata; ++ ++ if (!data || IS_ERR(data)) ++ return -ENODEV; ++ ++ return data->ntrips; ++} ++EXPORT_SYMBOL_GPL(of_thermal_get_ntrips); ++ ++/** ++ * of_thermal_is_trip_valid - function to check if trip point is valid ++ * ++ * @tz: pointer to a thermal zone ++ * @trip: trip point to evaluate ++ * ++ * This function is responsible for checking if passed trip point is valid ++ * ++ * Return: true if trip point is valid, false otherwise ++ */ ++bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip) ++{ ++ struct __thermal_zone *data = tz->devdata; ++ ++ if (!data || trip >= data->ntrips || trip < 0) ++ return false; ++ ++ return true; ++} ++EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid); ++ ++/** ++ * of_thermal_get_trip_points - function to get access to a globally exported ++ * trip points ++ * ++ * @tz: pointer to a thermal zone ++ * ++ * This function provides a pointer to trip points table ++ * ++ * Return: pointer to trip points table, NULL otherwise ++ */ ++const struct thermal_trip * ++of_thermal_get_trip_points(struct thermal_zone_device *tz) ++{ ++ struct __thermal_zone *data = tz->devdata; ++ ++ if (!data) ++ return NULL; ++ ++ return data->trips; ++} ++EXPORT_SYMBOL_GPL(of_thermal_get_trip_points); ++ ++/** ++ * of_thermal_set_emul_temp - function to set emulated temperature ++ * ++ * @tz: pointer to a thermal zone ++ * @temp: temperature to set ++ * ++ * This function gives the ability to set emulated value of temperature, ++ * which is handy for debugging ++ * ++ * Return: zero on success, error code otherwise ++ */ ++static int of_thermal_set_emul_temp(struct thermal_zone_device *tz, ++ unsigned long temp) ++{ ++ struct __thermal_zone *data = tz->devdata; ++ ++ if (!data->ops || !data->ops->set_emul_temp) ++ return -EINVAL; ++ ++ return data->ops->set_emul_temp(data->sensor_data, temp); + } + + static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip, +@@ -120,10 +194,10 @@ static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip, + long dev_trend; + int r; + +- if (!data->get_trend) ++ if (!data->ops->get_trend) + return -EINVAL; + +- r = data->get_trend(data->sensor_data, &dev_trend); ++ r = data->ops->get_trend(data->sensor_data, &dev_trend); + if (r) + return r; + +@@ -157,7 +231,8 @@ static int of_thermal_bind(struct thermal_zone_device *thermal, + ret = thermal_zone_bind_cooling_device(thermal, + tbp->trip_id, cdev, + tbp->max, +- tbp->min); ++ tbp->min, ++ tbp->usage); + if (ret) + return ret; + } +@@ -324,8 +399,7 @@ static struct thermal_zone_device_ops of_thermal_ops = { + static struct thermal_zone_device * + thermal_zone_of_add_sensor(struct device_node *zone, + struct device_node *sensor, void *data, +- int (*get_temp)(void *, long *), +- int (*get_trend)(void *, long *)) ++ const struct thermal_zone_of_device_ops *ops) + { + struct thermal_zone_device *tzd; + struct __thermal_zone *tz; +@@ -336,13 +410,16 @@ thermal_zone_of_add_sensor(struct device_node *zone, + + tz = tzd->devdata; + ++ if (!ops) ++ return ERR_PTR(-EINVAL); ++ + mutex_lock(&tzd->lock); +- tz->get_temp = get_temp; +- tz->get_trend = get_trend; ++ tz->ops = ops; + tz->sensor_data = data; + + tzd->ops->get_temp = of_thermal_get_temp; + tzd->ops->get_trend = of_thermal_get_trend; ++ tzd->ops->set_emul_temp = of_thermal_set_emul_temp; + mutex_unlock(&tzd->lock); + + return tzd; +@@ -356,8 +433,7 @@ thermal_zone_of_add_sensor(struct device_node *zone, + * than one sensors + * @data: a private pointer (owned by the caller) that will be passed + * back, when a temperature reading is needed. +- * @get_temp: a pointer to a function that reads the sensor temperature. +- * @get_trend: a pointer to a function that reads the sensor temperature trend. ++ * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp. + * + * This function will search the list of thermal zones described in device + * tree and look for the zone that refer to the sensor device pointed by +@@ -382,9 +458,8 @@ thermal_zone_of_add_sensor(struct device_node *zone, + * check the return value with help of IS_ERR() helper. + */ + struct thermal_zone_device * +-thermal_zone_of_sensor_register(struct device *dev, int sensor_id, +- void *data, int (*get_temp)(void *, long *), +- int (*get_trend)(void *, long *)) ++thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data, ++ const struct thermal_zone_of_device_ops *ops) + { + struct device_node *np, *child, *sensor_np; + struct thermal_zone_device *tzd = ERR_PTR(-ENODEV); +@@ -426,9 +501,10 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, + + if (sensor_specs.np == sensor_np && id == sensor_id) { + tzd = thermal_zone_of_add_sensor(child, sensor_np, +- data, +- get_temp, +- get_trend); ++ data, ops); ++ if (!IS_ERR(tzd)) ++ tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED); ++ + of_node_put(sensor_specs.np); + of_node_put(child); + goto exit; +@@ -475,9 +551,9 @@ void thermal_zone_of_sensor_unregister(struct device *dev, + mutex_lock(&tzd->lock); + tzd->ops->get_temp = NULL; + tzd->ops->get_trend = NULL; ++ tzd->ops->set_emul_temp = NULL; + +- tz->get_temp = NULL; +- tz->get_trend = NULL; ++ tz->ops = NULL; + tz->sensor_data = NULL; + mutex_unlock(&tzd->lock); + } +@@ -501,7 +577,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister); + */ + static int thermal_of_populate_bind_params(struct device_node *np, + struct __thermal_bind_params *__tbp, +- struct __thermal_trip *trips, ++ struct thermal_trip *trips, + int ntrips) + { + struct of_phandle_args cooling_spec; +@@ -510,7 +586,7 @@ static int thermal_of_populate_bind_params(struct device_node *np, + u32 prop; + + /* Default weight. Usage is optional */ +- __tbp->usage = 0; ++ __tbp->usage = THERMAL_WEIGHT_DEFAULT; + ret = of_property_read_u32(np, "contribution", &prop); + if (ret == 0) + __tbp->usage = prop; +@@ -604,7 +680,7 @@ static int thermal_of_get_trip_type(struct device_node *np, + * Return: 0 on success, proper error code otherwise + */ + static int thermal_of_populate_trip(struct device_node *np, +- struct __thermal_trip *trip) ++ struct thermal_trip *trip) + { + int prop; + int ret; +@@ -644,7 +720,7 @@ static int thermal_of_populate_trip(struct device_node *np, + * @np parameter and fills the read data into a __thermal_zone data structure + * and return this pointer. + * +- * TODO: Missing properties to parse: thermal-sensor-names and coefficients ++ * TODO: Missing properties to parse: thermal-sensor-names + * + * Return: On success returns a valid struct __thermal_zone, + * otherwise, it returns a corresponding ERR_PTR(). Caller must +@@ -656,7 +732,7 @@ thermal_of_build_thermal_zone(struct device_node *np) + struct device_node *child = NULL, *gchild; + struct __thermal_zone *tz; + int ret, i; +- u32 prop; ++ u32 prop, coef[2]; + + if (!np) { + pr_err("no thermal zone np\n"); +@@ -681,6 +757,20 @@ thermal_of_build_thermal_zone(struct device_node *np) + } + tz->polling_delay = prop; + ++ /* ++ * REVIST: for now, the thermal framework supports only ++ * one sensor per thermal zone. Thus, we are considering ++ * only the first two values as slope and offset. ++ */ ++ ret = of_property_read_u32_array(np, "coefficients", coef, 2); ++ if (ret == 0) { ++ tz->slope = coef[0]; ++ tz->offset = coef[1]; ++ } else { ++ tz->slope = 1; ++ tz->offset = 0; ++ } ++ + /* trips */ + child = of_get_child_by_name(np, "trips"); + +@@ -794,6 +884,8 @@ int __init of_parse_thermal_zones(void) + for_each_child_of_node(np, child) { + struct thermal_zone_device *zone; + struct thermal_zone_params *tzp; ++ int i, mask = 0; ++ u32 prop; + + /* Check whether child is enabled or not */ + if (!of_device_is_available(child)) +@@ -820,8 +912,18 @@ int __init of_parse_thermal_zones(void) + /* No hwmon because there might be hwmon drivers registering */ + tzp->no_hwmon = true; + ++ if (!of_property_read_u32(child, "sustainable-power", &prop)) ++ tzp->sustainable_power = prop; ++ ++ for (i = 0; i < tz->ntrips; i++) ++ mask |= 1 << i; ++ ++ /* these two are left for temperature drivers to use */ ++ tzp->slope = tz->slope; ++ tzp->offset = tz->offset; ++ + zone = thermal_zone_device_register(child->name, tz->ntrips, +- 0, tz, ++ mask, tz, + ops, tzp, + tz->passive_delay, + tz->polling_delay); +diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c +new file mode 100644 +index 0000000..2516769 +--- /dev/null ++++ b/drivers/thermal/power_allocator.c +@@ -0,0 +1,544 @@ ++/* ++ * A power allocator to manage temperature ++ * ++ * Copyright (C) 2014 ARM Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed "as is" WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#define pr_fmt(fmt) "Power allocator: " fmt ++ ++#include <linux/rculist.h> ++#include <linux/slab.h> ++#include <linux/thermal.h> ++ ++#define CREATE_TRACE_POINTS ++#include <trace/events/thermal_power_allocator.h> ++ ++#include "thermal_core.h" ++ ++#define FRAC_BITS 10 ++#define int_to_frac(x) ((x) << FRAC_BITS) ++#define frac_to_int(x) ((x) >> FRAC_BITS) ++ ++/** ++ * mul_frac() - multiply two fixed-point numbers ++ * @x: first multiplicand ++ * @y: second multiplicand ++ * ++ * Return: the result of multiplying two fixed-point numbers. The ++ * result is also a fixed-point number. ++ */ ++static inline s64 mul_frac(s64 x, s64 y) ++{ ++ return (x * y) >> FRAC_BITS; ++} ++ ++/** ++ * div_frac() - divide two fixed-point numbers ++ * @x: the dividend ++ * @y: the divisor ++ * ++ * Return: the result of dividing two fixed-point numbers. The ++ * result is also a fixed-point number. ++ */ ++static inline s64 div_frac(s64 x, s64 y) ++{ ++ return div_s64(x << FRAC_BITS, y); ++} ++ ++/** ++ * struct power_allocator_params - parameters for the power allocator governor ++ * @err_integral: accumulated error in the PID controller. ++ * @prev_err: error in the previous iteration of the PID controller. ++ * Used to calculate the derivative term. ++ * @trip_switch_on: first passive trip point of the thermal zone. The ++ * governor switches on when this trip point is crossed. ++ * @trip_max_desired_temperature: last passive trip point of the thermal ++ * zone. The temperature we are ++ * controlling for. ++ */ ++struct power_allocator_params { ++ s64 err_integral; ++ s32 prev_err; ++ int trip_switch_on; ++ int trip_max_desired_temperature; ++}; ++ ++/** ++ * pid_controller() - PID controller ++ * @tz: thermal zone we are operating in ++ * @current_temp: the current temperature in millicelsius ++ * @control_temp: the target temperature in millicelsius ++ * @max_allocatable_power: maximum allocatable power for this thermal zone ++ * ++ * This PID controller increases the available power budget so that the ++ * temperature of the thermal zone gets as close as possible to ++ * @control_temp and limits the power if it exceeds it. k_po is the ++ * proportional term when we are overshooting, k_pu is the ++ * proportional term when we are undershooting. integral_cutoff is a ++ * threshold below which we stop accumulating the error. The ++ * accumulated error is only valid if the requested power will make ++ * the system warmer. If the system is mostly idle, there's no point ++ * in accumulating positive error. ++ * ++ * Return: The power budget for the next period. ++ */ ++static u32 pid_controller(struct thermal_zone_device *tz, ++ unsigned long current_temp, ++ unsigned long control_temp, ++ u32 max_allocatable_power) ++{ ++ s64 p, i, d, power_range; ++ s32 err, max_power_frac; ++ struct power_allocator_params *params = tz->governor_data; ++ ++ max_power_frac = int_to_frac(max_allocatable_power); ++ ++ err = ((s32)control_temp - (s32)current_temp); ++ err = int_to_frac(err); ++ ++ /* Calculate the proportional term */ ++ p = mul_frac(err < 0 ? tz->tzp->k_po : tz->tzp->k_pu, err); ++ ++ /* ++ * Calculate the integral term ++ * ++ * if the error is less than cut off allow integration (but ++ * the integral is limited to max power) ++ */ ++ i = mul_frac(tz->tzp->k_i, params->err_integral); ++ ++ if (err < int_to_frac(tz->tzp->integral_cutoff)) { ++ s64 i_next = i + mul_frac(tz->tzp->k_i, err); ++ ++ if (abs64(i_next) < max_power_frac) { ++ i = i_next; ++ params->err_integral += err; ++ } ++ } ++ ++ /* ++ * Calculate the derivative term ++ * ++ * We do err - prev_err, so with a positive k_d, a decreasing ++ * error (i.e. driving closer to the line) results in less ++ * power being applied, slowing down the controller) ++ */ ++ d = mul_frac(tz->tzp->k_d, err - params->prev_err); ++ d = div_frac(d, tz->passive_delay); ++ params->prev_err = err; ++ ++ power_range = p + i + d; ++ ++ /* feed-forward the known sustainable dissipatable power */ ++ power_range = tz->tzp->sustainable_power + frac_to_int(power_range); ++ ++ power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power); ++ ++ trace_thermal_power_allocator_pid(tz, frac_to_int(err), ++ frac_to_int(params->err_integral), ++ frac_to_int(p), frac_to_int(i), ++ frac_to_int(d), power_range); ++ ++ return power_range; ++} ++ ++/** ++ * divvy_up_power() - divvy the allocated power between the actors ++ * @req_power: each actor's requested power ++ * @max_power: each actor's maximum available power ++ * @num_actors: size of the @req_power, @max_power and @granted_power's array ++ * @total_req_power: sum of @req_power ++ * @power_range: total allocated power ++ * @granted_power: output array: each actor's granted power ++ * @extra_actor_power: an appropriately sized array to be used in the ++ * function as temporary storage of the extra power given ++ * to the actors ++ * ++ * This function divides the total allocated power (@power_range) ++ * fairly between the actors. It first tries to give each actor a ++ * share of the @power_range according to how much power it requested ++ * compared to the rest of the actors. For example, if only one actor ++ * requests power, then it receives all the @power_range. If ++ * three actors each requests 1mW, each receives a third of the ++ * @power_range. ++ * ++ * If any actor received more than their maximum power, then that ++ * surplus is re-divvied among the actors based on how far they are ++ * from their respective maximums. ++ * ++ * Granted power for each actor is written to @granted_power, which ++ * should've been allocated by the calling function. ++ */ ++static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors, ++ u32 total_req_power, u32 power_range, ++ u32 *granted_power, u32 *extra_actor_power) ++{ ++ u32 extra_power, capped_extra_power; ++ int i; ++ ++ /* ++ * Prevent division by 0 if none of the actors request power. ++ */ ++ if (!total_req_power) ++ total_req_power = 1; ++ ++ capped_extra_power = 0; ++ extra_power = 0; ++ for (i = 0; i < num_actors; i++) { ++ u64 req_range = req_power[i] * power_range; ++ ++ granted_power[i] = DIV_ROUND_CLOSEST_ULL(req_range, ++ total_req_power); ++ ++ if (granted_power[i] > max_power[i]) { ++ extra_power += granted_power[i] - max_power[i]; ++ granted_power[i] = max_power[i]; ++ } ++ ++ extra_actor_power[i] = max_power[i] - granted_power[i]; ++ capped_extra_power += extra_actor_power[i]; ++ } ++ ++ if (!extra_power) ++ return; ++ ++ /* ++ * Re-divvy the reclaimed extra among actors based on ++ * how far they are from the max ++ */ ++ extra_power = min(extra_power, capped_extra_power); ++ if (capped_extra_power > 0) ++ for (i = 0; i < num_actors; i++) ++ granted_power[i] += (extra_actor_power[i] * ++ extra_power) / capped_extra_power; ++} ++ ++static int allocate_power(struct thermal_zone_device *tz, ++ unsigned long current_temp, ++ unsigned long control_temp) ++{ ++ struct thermal_instance *instance; ++ struct power_allocator_params *params = tz->governor_data; ++ u32 *req_power, *max_power, *granted_power, *extra_actor_power; ++ u32 *weighted_req_power; ++ u32 total_req_power, max_allocatable_power, total_weighted_req_power; ++ u32 total_granted_power, power_range; ++ int i, num_actors, total_weight, ret = 0; ++ int trip_max_desired_temperature = params->trip_max_desired_temperature; ++ ++ mutex_lock(&tz->lock); ++ ++ num_actors = 0; ++ total_weight = 0; ++ list_for_each_entry(instance, &tz->thermal_instances, tz_node) { ++ if ((instance->trip == trip_max_desired_temperature) && ++ cdev_is_power_actor(instance->cdev)) { ++ num_actors++; ++ total_weight += instance->weight; ++ } ++ } ++ ++ /* ++ * We need to allocate five arrays of the same size: ++ * req_power, max_power, granted_power, extra_actor_power and ++ * weighted_req_power. They are going to be needed until this ++ * function returns. Allocate them all in one go to simplify ++ * the allocation and deallocation logic. ++ */ ++ BUILD_BUG_ON(sizeof(*req_power) != sizeof(*max_power)); ++ BUILD_BUG_ON(sizeof(*req_power) != sizeof(*granted_power)); ++ BUILD_BUG_ON(sizeof(*req_power) != sizeof(*extra_actor_power)); ++ BUILD_BUG_ON(sizeof(*req_power) != sizeof(*weighted_req_power)); ++ req_power = kcalloc(num_actors * 5, sizeof(*req_power), GFP_KERNEL); ++ if (!req_power) { ++ ret = -ENOMEM; ++ goto unlock; ++ } ++ ++ max_power = &req_power[num_actors]; ++ granted_power = &req_power[2 * num_actors]; ++ extra_actor_power = &req_power[3 * num_actors]; ++ weighted_req_power = &req_power[4 * num_actors]; ++ ++ i = 0; ++ total_weighted_req_power = 0; ++ total_req_power = 0; ++ max_allocatable_power = 0; ++ ++ list_for_each_entry(instance, &tz->thermal_instances, tz_node) { ++ int weight; ++ struct thermal_cooling_device *cdev = instance->cdev; ++ ++ if (instance->trip != trip_max_desired_temperature) ++ continue; ++ ++ if (!cdev_is_power_actor(cdev)) ++ continue; ++ ++ if (cdev->ops->get_requested_power(cdev, tz, &req_power[i])) ++ continue; ++ ++ if (!total_weight) ++ weight = 1 << FRAC_BITS; ++ else ++ weight = instance->weight; ++ ++ weighted_req_power[i] = frac_to_int(weight * req_power[i]); ++ ++ if (power_actor_get_max_power(cdev, tz, &max_power[i])) ++ continue; ++ ++ total_req_power += req_power[i]; ++ max_allocatable_power += max_power[i]; ++ total_weighted_req_power += weighted_req_power[i]; ++ ++ i++; ++ } ++ ++ power_range = pid_controller(tz, current_temp, control_temp, ++ max_allocatable_power); ++ ++ divvy_up_power(weighted_req_power, max_power, num_actors, ++ total_weighted_req_power, power_range, granted_power, ++ extra_actor_power); ++ ++ total_granted_power = 0; ++ i = 0; ++ list_for_each_entry(instance, &tz->thermal_instances, tz_node) { ++ if (instance->trip != trip_max_desired_temperature) ++ continue; ++ ++ if (!cdev_is_power_actor(instance->cdev)) ++ continue; ++ ++ power_actor_set_power(instance->cdev, instance, ++ granted_power[i]); ++ total_granted_power += granted_power[i]; ++ ++ i++; ++ } ++ ++ trace_thermal_power_allocator(tz, req_power, total_req_power, ++ granted_power, total_granted_power, ++ num_actors, power_range, ++ max_allocatable_power, current_temp, ++ (s32)control_temp - (s32)current_temp); ++ ++ kfree(req_power); ++unlock: ++ mutex_unlock(&tz->lock); ++ ++ return ret; ++} ++ ++static int get_governor_trips(struct thermal_zone_device *tz, ++ struct power_allocator_params *params) ++{ ++ int i, ret, last_passive; ++ bool found_first_passive; ++ ++ found_first_passive = false; ++ last_passive = -1; ++ ret = -EINVAL; ++ ++ for (i = 0; i < tz->trips; i++) { ++ enum thermal_trip_type type; ++ ++ ret = tz->ops->get_trip_type(tz, i, &type); ++ if (ret) ++ return ret; ++ ++ if (!found_first_passive) { ++ if (type == THERMAL_TRIP_PASSIVE) { ++ params->trip_switch_on = i; ++ found_first_passive = true; ++ } ++ } else if (type == THERMAL_TRIP_PASSIVE) { ++ last_passive = i; ++ } else { ++ break; ++ } ++ } ++ ++ if (last_passive != -1) { ++ params->trip_max_desired_temperature = last_passive; ++ ret = 0; ++ } else { ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static void reset_pid_controller(struct power_allocator_params *params) ++{ ++ params->err_integral = 0; ++ params->prev_err = 0; ++} ++ ++static void allow_maximum_power(struct thermal_zone_device *tz) ++{ ++ struct thermal_instance *instance; ++ struct power_allocator_params *params = tz->governor_data; ++ ++ list_for_each_entry(instance, &tz->thermal_instances, tz_node) { ++ if ((instance->trip != params->trip_max_desired_temperature) || ++ (!cdev_is_power_actor(instance->cdev))) ++ continue; ++ ++ instance->target = 0; ++ instance->cdev->updated = false; ++ thermal_cdev_update(instance->cdev); ++ } ++} ++ ++/** ++ * power_allocator_bind() - bind the power_allocator governor to a thermal zone ++ * @tz: thermal zone to bind it to ++ * ++ * Check that the thermal zone is valid for this governor, that is, it ++ * has two thermal trips. If so, initialize the PID controller ++ * parameters and bind it to the thermal zone. ++ * ++ * Return: 0 on success, -EINVAL if the trips were invalid or -ENOMEM ++ * if we ran out of memory. ++ */ ++static int power_allocator_bind(struct thermal_zone_device *tz) ++{ ++ int ret; ++ struct power_allocator_params *params; ++ unsigned long switch_on_temp, control_temp; ++ u32 temperature_threshold; ++ ++ if (!tz->tzp || !tz->tzp->sustainable_power) { ++ dev_err(&tz->device, ++ "power_allocator: missing sustainable_power\n"); ++ return -EINVAL; ++ } ++ ++ params = kzalloc(sizeof(*params), GFP_KERNEL); ++ if (!params) ++ return -ENOMEM; ++ ++ ret = get_governor_trips(tz, params); ++ if (ret) { ++ dev_err(&tz->device, ++ "thermal zone %s has wrong trip setup for power allocator\n", ++ tz->type); ++ goto free; ++ } ++ ++ ret = tz->ops->get_trip_temp(tz, params->trip_switch_on, ++ &switch_on_temp); ++ if (ret) ++ goto free; ++ ++ ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature, ++ &control_temp); ++ if (ret) ++ goto free; ++ ++ temperature_threshold = control_temp - switch_on_temp; ++ ++ tz->tzp->k_po = tz->tzp->k_po ?: ++ int_to_frac(tz->tzp->sustainable_power) / temperature_threshold; ++ tz->tzp->k_pu = tz->tzp->k_pu ?: ++ int_to_frac(2 * tz->tzp->sustainable_power) / ++ temperature_threshold; ++ tz->tzp->k_i = tz->tzp->k_i ?: int_to_frac(10) / 1000; ++ /* ++ * The default for k_d and integral_cutoff is 0, so we can ++ * leave them as they are. ++ */ ++ ++ reset_pid_controller(params); ++ ++ tz->governor_data = params; ++ ++ return 0; ++ ++free: ++ kfree(params); ++ return ret; ++} ++ ++static void power_allocator_unbind(struct thermal_zone_device *tz) ++{ ++ dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id); ++ kfree(tz->governor_data); ++ tz->governor_data = NULL; ++} ++ ++static int power_allocator_throttle(struct thermal_zone_device *tz, int trip) ++{ ++ int ret; ++ unsigned long switch_on_temp, control_temp, current_temp; ++ struct power_allocator_params *params = tz->governor_data; ++ ++ /* ++ * We get called for every trip point but we only need to do ++ * our calculations once ++ */ ++ if (trip != params->trip_max_desired_temperature) ++ return 0; ++ ++ ret = thermal_zone_get_temp(tz, ¤t_temp); ++ if (ret) { ++ dev_warn(&tz->device, "Failed to get temperature: %d\n", ret); ++ return ret; ++ } ++ ++ ret = tz->ops->get_trip_temp(tz, params->trip_switch_on, ++ &switch_on_temp); ++ if (ret) { ++ dev_warn(&tz->device, ++ "Failed to get switch on temperature: %d\n", ret); ++ return ret; ++ } ++ ++ if (current_temp < switch_on_temp) { ++ tz->passive = 0; ++ reset_pid_controller(params); ++ allow_maximum_power(tz); ++ return 0; ++ } ++ ++ tz->passive = 1; ++ ++ ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature, ++ &control_temp); ++ if (ret) { ++ dev_warn(&tz->device, ++ "Failed to get the maximum desired temperature: %d\n", ++ ret); ++ return ret; ++ } ++ ++ return allocate_power(tz, current_temp, control_temp); ++} ++ ++static struct thermal_governor thermal_gov_power_allocator = { ++ .name = "power_allocator", ++ .bind_to_tz = power_allocator_bind, ++ .unbind_from_tz = power_allocator_unbind, ++ .throttle = power_allocator_throttle, ++}; ++ ++int thermal_gov_power_allocator_register(void) ++{ ++ return thermal_register_governor(&thermal_gov_power_allocator); ++} ++ ++void thermal_gov_power_allocator_unregister(void) ++{ ++ thermal_unregister_governor(&thermal_gov_power_allocator); ++} +diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig +index f760389..c43306e 100644 +--- a/drivers/thermal/samsung/Kconfig ++++ b/drivers/thermal/samsung/Kconfig +@@ -1,6 +1,6 @@ + config EXYNOS_THERMAL + tristate "Exynos thermal management unit driver" +- depends on ARCH_HAS_BANDGAP && OF ++ depends on OF + help + If you say yes here you get support for the TMU (Thermal Management + Unit) driver for SAMSUNG EXYNOS series of SoCs. This driver initialises +diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c +index b6be572..7c97db1 100644 +--- a/drivers/thermal/samsung/exynos_thermal_common.c ++++ b/drivers/thermal/samsung/exynos_thermal_common.c +@@ -163,7 +163,7 @@ static int exynos_bind(struct thermal_zone_device *thermal, + case MONITOR_ZONE: + case WARN_ZONE: + if (thermal_zone_bind_cooling_device(thermal, i, cdev, +- level, 0)) { ++ level, 0, THERMAL_WEIGHT_DEFAULT)) { + dev_err(data->dev, + "error unbinding cdev inst=%d\n", i); + ret = -EINVAL; +@@ -371,9 +371,11 @@ int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) + th_zone->cool_dev[th_zone->cool_dev_size] = + cpufreq_cooling_register(&mask_val); + if (IS_ERR(th_zone->cool_dev[th_zone->cool_dev_size])) { +- dev_err(sensor_conf->dev, +- "Failed to register cpufreq cooling device\n"); +- ret = -EINVAL; ++ ret = PTR_ERR(th_zone->cool_dev[th_zone->cool_dev_size]); ++ if (ret != -EPROBE_DEFER) ++ dev_err(sensor_conf->dev, ++ "Failed to register cpufreq cooling device: %d\n", ++ ret); + goto err_unregister; + } + th_zone->cool_dev_size++; +diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c +index 49c0924..2afca9b 100644 +--- a/drivers/thermal/samsung/exynos_tmu.c ++++ b/drivers/thermal/samsung/exynos_tmu.c +@@ -683,7 +683,10 @@ static int exynos_tmu_probe(struct platform_device *pdev) + /* Register the sensor with thermal management interface */ + ret = exynos_register_thermal(sensor_conf); + if (ret) { +- dev_err(&pdev->dev, "Failed to register thermal interface\n"); ++ if (ret != -EPROBE_DEFER) ++ dev_err(&pdev->dev, ++ "Failed to register thermal interface: %d\n", ++ ret); + goto err_clk; + } + data->reg_conf = sensor_conf; +diff --git a/drivers/thermal/tegra_soctherm.c b/drivers/thermal/tegra_soctherm.c +new file mode 100644 +index 0000000..9197fc0 +--- /dev/null ++++ b/drivers/thermal/tegra_soctherm.c +@@ -0,0 +1,476 @@ ++/* ++ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. ++ * ++ * Author: ++ * Mikko Perttunen <mperttunen@nvidia.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/bitops.h> ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/reset.h> ++#include <linux/thermal.h> ++ ++#include <soc/tegra/fuse.h> ++ ++#define SENSOR_CONFIG0 0 ++#define SENSOR_CONFIG0_STOP BIT(0) ++#define SENSOR_CONFIG0_TALL_SHIFT 8 ++#define SENSOR_CONFIG0_TCALC_OVER BIT(4) ++#define SENSOR_CONFIG0_OVER BIT(3) ++#define SENSOR_CONFIG0_CPTR_OVER BIT(2) ++ ++#define SENSOR_CONFIG1 4 ++#define SENSOR_CONFIG1_TSAMPLE_SHIFT 0 ++#define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15 ++#define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24 ++#define SENSOR_CONFIG1_TEMP_ENABLE BIT(31) ++ ++#define SENSOR_CONFIG2 8 ++#define SENSOR_CONFIG2_THERMA_SHIFT 16 ++#define SENSOR_CONFIG2_THERMB_SHIFT 0 ++ ++#define SENSOR_PDIV 0x1c0 ++#define SENSOR_PDIV_T124 0x8888 ++#define SENSOR_HOTSPOT_OFF 0x1c4 ++#define SENSOR_HOTSPOT_OFF_T124 0x00060600 ++#define SENSOR_TEMP1 0x1c8 ++#define SENSOR_TEMP2 0x1cc ++ ++#define SENSOR_TEMP_MASK 0xffff ++#define READBACK_VALUE_MASK 0xff00 ++#define READBACK_VALUE_SHIFT 8 ++#define READBACK_ADD_HALF BIT(7) ++#define READBACK_NEGATE BIT(1) ++ ++#define FUSE_TSENSOR8_CALIB 0x180 ++#define FUSE_SPARE_REALIGNMENT_REG_0 0x1fc ++ ++#define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK 0x1fff ++#define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK (0x1fff << 13) ++#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13 ++ ++#define FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK 0x3ff ++#define FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK (0x7ff << 10) ++#define FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT 10 ++ ++#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_MASK 0x3f ++#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK (0x1f << 21) ++#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT 21 ++ ++#define NOMINAL_CALIB_FT_T124 105 ++#define NOMINAL_CALIB_CP_T124 25 ++ ++struct tegra_tsensor_configuration { ++ u32 tall, tsample, tiddq_en, ten_count, pdiv, tsample_ate, pdiv_ate; ++}; ++ ++struct tegra_tsensor { ++ const struct tegra_tsensor_configuration *config; ++ u32 base, calib_fuse_offset; ++ /* Correction values used to modify values read from calibration fuses */ ++ s32 fuse_corr_alpha, fuse_corr_beta; ++}; ++ ++struct tegra_thermctl_zone { ++ void __iomem *reg; ++ unsigned int shift; ++}; ++ ++static const struct tegra_tsensor_configuration t124_tsensor_config = { ++ .tall = 16300, ++ .tsample = 120, ++ .tiddq_en = 1, ++ .ten_count = 1, ++ .pdiv = 8, ++ .tsample_ate = 480, ++ .pdiv_ate = 8 ++}; ++ ++static const struct tegra_tsensor t124_tsensors[] = { ++ { ++ .config = &t124_tsensor_config, ++ .base = 0xc0, ++ .calib_fuse_offset = 0x098, ++ .fuse_corr_alpha = 1135400, ++ .fuse_corr_beta = -6266900, ++ }, ++ { ++ .config = &t124_tsensor_config, ++ .base = 0xe0, ++ .calib_fuse_offset = 0x084, ++ .fuse_corr_alpha = 1122220, ++ .fuse_corr_beta = -5700700, ++ }, ++ { ++ .config = &t124_tsensor_config, ++ .base = 0x100, ++ .calib_fuse_offset = 0x088, ++ .fuse_corr_alpha = 1127000, ++ .fuse_corr_beta = -6768200, ++ }, ++ { ++ .config = &t124_tsensor_config, ++ .base = 0x120, ++ .calib_fuse_offset = 0x12c, ++ .fuse_corr_alpha = 1110900, ++ .fuse_corr_beta = -6232000, ++ }, ++ { ++ .config = &t124_tsensor_config, ++ .base = 0x140, ++ .calib_fuse_offset = 0x158, ++ .fuse_corr_alpha = 1122300, ++ .fuse_corr_beta = -5936400, ++ }, ++ { ++ .config = &t124_tsensor_config, ++ .base = 0x160, ++ .calib_fuse_offset = 0x15c, ++ .fuse_corr_alpha = 1145700, ++ .fuse_corr_beta = -7124600, ++ }, ++ { ++ .config = &t124_tsensor_config, ++ .base = 0x180, ++ .calib_fuse_offset = 0x154, ++ .fuse_corr_alpha = 1120100, ++ .fuse_corr_beta = -6000500, ++ }, ++ { ++ .config = &t124_tsensor_config, ++ .base = 0x1a0, ++ .calib_fuse_offset = 0x160, ++ .fuse_corr_alpha = 1106500, ++ .fuse_corr_beta = -6729300, ++ }, ++}; ++ ++struct tegra_soctherm { ++ struct reset_control *reset; ++ struct clk *clock_tsensor; ++ struct clk *clock_soctherm; ++ void __iomem *regs; ++ ++ struct thermal_zone_device *thermctl_tzs[4]; ++}; ++ ++struct tsensor_shared_calibration { ++ u32 base_cp, base_ft; ++ u32 actual_temp_cp, actual_temp_ft; ++}; ++ ++static int calculate_shared_calibration(struct tsensor_shared_calibration *r) ++{ ++ u32 val, shifted_cp, shifted_ft; ++ int err; ++ ++ err = tegra_fuse_readl(FUSE_TSENSOR8_CALIB, &val); ++ if (err) ++ return err; ++ r->base_cp = val & FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK; ++ r->base_ft = (val & FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK) ++ >> FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT; ++ val = ((val & FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK) ++ >> FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT); ++ shifted_ft = sign_extend32(val, 4); ++ ++ err = tegra_fuse_readl(FUSE_SPARE_REALIGNMENT_REG_0, &val); ++ if (err) ++ return err; ++ shifted_cp = sign_extend32(val, 5); ++ ++ r->actual_temp_cp = 2 * NOMINAL_CALIB_CP_T124 + shifted_cp; ++ r->actual_temp_ft = 2 * NOMINAL_CALIB_FT_T124 + shifted_ft; ++ ++ return 0; ++} ++ ++static s64 div64_s64_precise(s64 a, s64 b) ++{ ++ s64 r, al; ++ ++ /* Scale up for increased precision division */ ++ al = a << 16; ++ ++ r = div64_s64(al * 2 + 1, 2 * b); ++ return r >> 16; ++} ++ ++static int ++calculate_tsensor_calibration(const struct tegra_tsensor *sensor, ++ const struct tsensor_shared_calibration *shared, ++ u32 *calib) ++{ ++ u32 val; ++ s32 actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp, ++ mult, div; ++ s16 therma, thermb; ++ s64 tmp; ++ int err; ++ ++ err = tegra_fuse_readl(sensor->calib_fuse_offset, &val); ++ if (err) ++ return err; ++ ++ actual_tsensor_cp = (shared->base_cp * 64) + sign_extend32(val, 12); ++ val = (val & FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK) ++ >> FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT; ++ actual_tsensor_ft = (shared->base_ft * 32) + sign_extend32(val, 12); ++ ++ delta_sens = actual_tsensor_ft - actual_tsensor_cp; ++ delta_temp = shared->actual_temp_ft - shared->actual_temp_cp; ++ ++ mult = sensor->config->pdiv * sensor->config->tsample_ate; ++ div = sensor->config->tsample * sensor->config->pdiv_ate; ++ ++ therma = div64_s64_precise((s64) delta_temp * (1LL << 13) * mult, ++ (s64) delta_sens * div); ++ ++ tmp = (s64)actual_tsensor_ft * shared->actual_temp_cp - ++ (s64)actual_tsensor_cp * shared->actual_temp_ft; ++ thermb = div64_s64_precise(tmp, (s64)delta_sens); ++ ++ therma = div64_s64_precise((s64)therma * sensor->fuse_corr_alpha, ++ (s64)1000000LL); ++ thermb = div64_s64_precise((s64)thermb * sensor->fuse_corr_alpha + ++ sensor->fuse_corr_beta, (s64)1000000LL); ++ ++ *calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) | ++ ((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT); ++ ++ return 0; ++} ++ ++static int enable_tsensor(struct tegra_soctherm *tegra, ++ const struct tegra_tsensor *sensor, ++ const struct tsensor_shared_calibration *shared) ++{ ++ void __iomem *base = tegra->regs + sensor->base; ++ unsigned int val; ++ u32 calib; ++ int err; ++ ++ err = calculate_tsensor_calibration(sensor, shared, &calib); ++ if (err) ++ return err; ++ ++ val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT; ++ writel(val, base + SENSOR_CONFIG0); ++ ++ val = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT; ++ val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT; ++ val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT; ++ val |= SENSOR_CONFIG1_TEMP_ENABLE; ++ writel(val, base + SENSOR_CONFIG1); ++ ++ writel(calib, base + SENSOR_CONFIG2); ++ ++ return 0; ++} ++ ++/* ++ * Translate from soctherm readback format to millicelsius. ++ * The soctherm readback format in bits is as follows: ++ * TTTTTTTT H______N ++ * where T's contain the temperature in Celsius, ++ * H denotes an addition of 0.5 Celsius and N denotes negation ++ * of the final value. ++ */ ++static long translate_temp(u16 val) ++{ ++ long t; ++ ++ t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000; ++ if (val & READBACK_ADD_HALF) ++ t += 500; ++ if (val & READBACK_NEGATE) ++ t *= -1; ++ ++ return t; ++} ++ ++static int tegra_thermctl_get_temp(void *data, long *out_temp) ++{ ++ struct tegra_thermctl_zone *zone = data; ++ u32 val; ++ ++ val = (readl(zone->reg) >> zone->shift) & SENSOR_TEMP_MASK; ++ *out_temp = translate_temp(val); ++ ++ return 0; ++} ++ ++static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = { ++ .get_temp = tegra_thermctl_get_temp, ++}; ++ ++static const struct of_device_id tegra_soctherm_of_match[] = { ++ { .compatible = "nvidia,tegra124-soctherm" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, tegra_soctherm_of_match); ++ ++struct thermctl_zone_desc { ++ unsigned int offset; ++ unsigned int shift; ++}; ++ ++static const struct thermctl_zone_desc t124_thermctl_temp_zones[] = { ++ { SENSOR_TEMP1, 16 }, ++ { SENSOR_TEMP2, 16 }, ++ { SENSOR_TEMP1, 0 }, ++ { SENSOR_TEMP2, 0 } ++}; ++ ++static int tegra_soctherm_probe(struct platform_device *pdev) ++{ ++ struct tegra_soctherm *tegra; ++ struct thermal_zone_device *tz; ++ struct tsensor_shared_calibration shared_calib; ++ struct resource *res; ++ unsigned int i; ++ int err; ++ ++ const struct tegra_tsensor *tsensors = t124_tsensors; ++ ++ tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); ++ if (!tegra) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ tegra->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(tegra->regs)) ++ return PTR_ERR(tegra->regs); ++ ++ tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm"); ++ if (IS_ERR(tegra->reset)) { ++ dev_err(&pdev->dev, "can't get soctherm reset\n"); ++ return PTR_ERR(tegra->reset); ++ } ++ ++ tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor"); ++ if (IS_ERR(tegra->clock_tsensor)) { ++ dev_err(&pdev->dev, "can't get tsensor clock\n"); ++ return PTR_ERR(tegra->clock_tsensor); ++ } ++ ++ tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm"); ++ if (IS_ERR(tegra->clock_soctherm)) { ++ dev_err(&pdev->dev, "can't get soctherm clock\n"); ++ return PTR_ERR(tegra->clock_soctherm); ++ } ++ ++ reset_control_assert(tegra->reset); ++ ++ err = clk_prepare_enable(tegra->clock_soctherm); ++ if (err) ++ return err; ++ ++ err = clk_prepare_enable(tegra->clock_tsensor); ++ if (err) { ++ clk_disable_unprepare(tegra->clock_soctherm); ++ return err; ++ } ++ ++ reset_control_deassert(tegra->reset); ++ ++ /* Initialize raw sensors */ ++ ++ err = calculate_shared_calibration(&shared_calib); ++ if (err) ++ goto disable_clocks; ++ ++ for (i = 0; i < ARRAY_SIZE(t124_tsensors); ++i) { ++ err = enable_tsensor(tegra, tsensors + i, &shared_calib); ++ if (err) ++ goto disable_clocks; ++ } ++ ++ writel(SENSOR_PDIV_T124, tegra->regs + SENSOR_PDIV); ++ writel(SENSOR_HOTSPOT_OFF_T124, tegra->regs + SENSOR_HOTSPOT_OFF); ++ ++ /* Initialize thermctl sensors */ ++ ++ for (i = 0; i < ARRAY_SIZE(tegra->thermctl_tzs); ++i) { ++ struct tegra_thermctl_zone *zone = ++ devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL); ++ if (!zone) { ++ err = -ENOMEM; ++ goto unregister_tzs; ++ } ++ ++ zone->reg = tegra->regs + t124_thermctl_temp_zones[i].offset; ++ zone->shift = t124_thermctl_temp_zones[i].shift; ++ ++ tz = thermal_zone_of_sensor_register(&pdev->dev, i, zone, ++ &tegra_of_thermal_ops); ++ if (IS_ERR(tz)) { ++ err = PTR_ERR(tz); ++ dev_err(&pdev->dev, "failed to register sensor: %d\n", ++ err); ++ goto unregister_tzs; ++ } ++ ++ tegra->thermctl_tzs[i] = tz; ++ } ++ ++ return 0; ++ ++unregister_tzs: ++ while (i--) ++ thermal_zone_of_sensor_unregister(&pdev->dev, ++ tegra->thermctl_tzs[i]); ++ ++disable_clocks: ++ clk_disable_unprepare(tegra->clock_tsensor); ++ clk_disable_unprepare(tegra->clock_soctherm); ++ ++ return err; ++} ++ ++static int tegra_soctherm_remove(struct platform_device *pdev) ++{ ++ struct tegra_soctherm *tegra = platform_get_drvdata(pdev); ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(tegra->thermctl_tzs); ++i) { ++ thermal_zone_of_sensor_unregister(&pdev->dev, ++ tegra->thermctl_tzs[i]); ++ } ++ ++ clk_disable_unprepare(tegra->clock_tsensor); ++ clk_disable_unprepare(tegra->clock_soctherm); ++ ++ return 0; ++} ++ ++static struct platform_driver tegra_soctherm_driver = { ++ .probe = tegra_soctherm_probe, ++ .remove = tegra_soctherm_remove, ++ .driver = { ++ .name = "tegra-soctherm", ++ .of_match_table = tegra_soctherm_of_match, ++ }, ++}; ++module_platform_driver(tegra_soctherm_driver); ++ ++MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>"); ++MODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c +index 488e9bf..249b612 100644 +--- a/drivers/thermal/thermal_core.c ++++ b/drivers/thermal/thermal_core.c +@@ -75,6 +75,58 @@ static struct thermal_governor *__find_governor(const char *name) + return NULL; + } + ++/** ++ * bind_previous_governor() - bind the previous governor of the thermal zone ++ * @tz: a valid pointer to a struct thermal_zone_device ++ * @failed_gov_name: the name of the governor that failed to register ++ * ++ * Register the previous governor of the thermal zone after a new ++ * governor has failed to be bound. ++ */ ++static void bind_previous_governor(struct thermal_zone_device *tz, ++ const char *failed_gov_name) ++{ ++ if (tz->governor && tz->governor->bind_to_tz) { ++ if (tz->governor->bind_to_tz(tz)) { ++ dev_err(&tz->device, ++ "governor %s failed to bind and the previous one (%s) failed to bind again, thermal zone %s has no governor\n", ++ failed_gov_name, tz->governor->name, tz->type); ++ tz->governor = NULL; ++ } ++ } ++} ++ ++/** ++ * thermal_set_governor() - Switch to another governor ++ * @tz: a valid pointer to a struct thermal_zone_device ++ * @new_gov: pointer to the new governor ++ * ++ * Change the governor of thermal zone @tz. ++ * ++ * Return: 0 on success, an error if the new governor's bind_to_tz() failed. ++ */ ++static int thermal_set_governor(struct thermal_zone_device *tz, ++ struct thermal_governor *new_gov) ++{ ++ int ret = 0; ++ ++ if (tz->governor && tz->governor->unbind_from_tz) ++ tz->governor->unbind_from_tz(tz); ++ ++ if (new_gov && new_gov->bind_to_tz) { ++ ret = new_gov->bind_to_tz(tz); ++ if (ret) { ++ bind_previous_governor(tz, new_gov->name); ++ ++ return ret; ++ } ++ } ++ ++ tz->governor = new_gov; ++ ++ return ret; ++} ++ + int thermal_register_governor(struct thermal_governor *governor) + { + int err; +@@ -107,8 +159,15 @@ int thermal_register_governor(struct thermal_governor *governor) + + name = pos->tzp->governor_name; + +- if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) +- pos->governor = governor; ++ if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) { ++ int ret; ++ ++ ret = thermal_set_governor(pos, governor); ++ if (ret) ++ dev_err(&pos->device, ++ "Failed to set governor %s for thermal zone %s: %d\n", ++ governor->name, pos->type, ret); ++ } + } + + mutex_unlock(&thermal_list_lock); +@@ -134,7 +193,7 @@ void thermal_unregister_governor(struct thermal_governor *governor) + list_for_each_entry(pos, &thermal_tz_list, node) { + if (!strncasecmp(pos->governor->name, governor->name, + THERMAL_NAME_LENGTH)) +- pos->governor = NULL; ++ thermal_set_governor(pos, NULL); + } + + mutex_unlock(&thermal_list_lock); +@@ -218,7 +277,8 @@ static void print_bind_err_msg(struct thermal_zone_device *tz, + + static void __bind(struct thermal_zone_device *tz, int mask, + struct thermal_cooling_device *cdev, +- unsigned long *limits) ++ unsigned long *limits, ++ unsigned int weight) + { + int i, ret; + +@@ -233,7 +293,8 @@ static void __bind(struct thermal_zone_device *tz, int mask, + upper = limits[i * 2 + 1]; + } + ret = thermal_zone_bind_cooling_device(tz, i, cdev, +- upper, lower); ++ upper, lower, ++ weight); + if (ret) + print_bind_err_msg(tz, cdev, ret); + } +@@ -280,7 +341,8 @@ static void bind_cdev(struct thermal_cooling_device *cdev) + continue; + tzp->tbp[i].cdev = cdev; + __bind(pos, tzp->tbp[i].trip_mask, cdev, +- tzp->tbp[i].binding_limits); ++ tzp->tbp[i].binding_limits, ++ tzp->tbp[i].weight); + } + } + +@@ -319,7 +381,8 @@ static void bind_tz(struct thermal_zone_device *tz) + continue; + tzp->tbp[i].cdev = pos; + __bind(tz, tzp->tbp[i].trip_mask, pos, +- tzp->tbp[i].binding_limits); ++ tzp->tbp[i].binding_limits, ++ tzp->tbp[i].weight); + } + } + exit: +@@ -368,7 +431,7 @@ static void handle_critical_trips(struct thermal_zone_device *tz, + tz->ops->get_trip_temp(tz, trip, &trip_temp); + + /* If we have not crossed the trip_temp, we do not care. */ +- if (tz->temperature < trip_temp) ++ if (trip_temp <= 0 || tz->temperature < trip_temp) + return; + + trace_thermal_zone_trip(tz, trip, trip_type); +@@ -711,7 +774,8 @@ passive_store(struct device *dev, struct device_attribute *attr, + thermal_zone_bind_cooling_device(tz, + THERMAL_TRIPS_NONE, cdev, + THERMAL_NO_LIMIT, +- THERMAL_NO_LIMIT); ++ THERMAL_NO_LIMIT, ++ THERMAL_WEIGHT_DEFAULT); + } + mutex_unlock(&thermal_list_lock); + if (!tz->passive_delay) +@@ -757,15 +821,18 @@ policy_store(struct device *dev, struct device_attribute *attr, + snprintf(name, sizeof(name), "%s", buf); + + mutex_lock(&thermal_governor_lock); ++ mutex_lock(&tz->lock); + + gov = __find_governor(strim(name)); + if (!gov) + goto exit; + +- tz->governor = gov; +- ret = count; ++ ret = thermal_set_governor(tz, gov); ++ if (!ret) ++ ret = count; + + exit: ++ mutex_unlock(&tz->lock); + mutex_unlock(&thermal_governor_lock); + return ret; + } +@@ -806,6 +873,158 @@ emul_temp_store(struct device *dev, struct device_attribute *attr, + static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); + #endif/*CONFIG_THERMAL_EMULATION*/ + ++static ssize_t ++sustainable_power_show(struct device *dev, struct device_attribute *devattr, ++ char *buf) ++{ ++ struct thermal_zone_device *tz = to_thermal_zone(dev); ++ ++ if (tz->tzp) ++ return sprintf(buf, "%u\n", tz->tzp->sustainable_power); ++ else ++ return -EIO; ++} ++ ++static ssize_t ++sustainable_power_store(struct device *dev, struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct thermal_zone_device *tz = to_thermal_zone(dev); ++ u32 sustainable_power; ++ ++ if (!tz->tzp) ++ return -EIO; ++ ++ if (kstrtou32(buf, 10, &sustainable_power)) ++ return -EINVAL; ++ ++ tz->tzp->sustainable_power = sustainable_power; ++ ++ return count; ++} ++static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show, ++ sustainable_power_store); ++ ++#define create_s32_tzp_attr(name) \ ++ static ssize_t \ ++ name##_show(struct device *dev, struct device_attribute *devattr, \ ++ char *buf) \ ++ { \ ++ struct thermal_zone_device *tz = to_thermal_zone(dev); \ ++ \ ++ if (tz->tzp) \ ++ return sprintf(buf, "%u\n", tz->tzp->name); \ ++ else \ ++ return -EIO; \ ++ } \ ++ \ ++ static ssize_t \ ++ name##_store(struct device *dev, struct device_attribute *devattr, \ ++ const char *buf, size_t count) \ ++ { \ ++ struct thermal_zone_device *tz = to_thermal_zone(dev); \ ++ s32 value; \ ++ \ ++ if (!tz->tzp) \ ++ return -EIO; \ ++ \ ++ if (kstrtos32(buf, 10, &value)) \ ++ return -EINVAL; \ ++ \ ++ tz->tzp->name = value; \ ++ \ ++ return count; \ ++ } \ ++ static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, name##_show, name##_store) ++ ++create_s32_tzp_attr(k_po); ++create_s32_tzp_attr(k_pu); ++create_s32_tzp_attr(k_i); ++create_s32_tzp_attr(k_d); ++create_s32_tzp_attr(integral_cutoff); ++create_s32_tzp_attr(slope); ++create_s32_tzp_attr(offset); ++#undef create_s32_tzp_attr ++ ++static struct device_attribute *dev_tzp_attrs[] = { ++ &dev_attr_sustainable_power, ++ &dev_attr_k_po, ++ &dev_attr_k_pu, ++ &dev_attr_k_i, ++ &dev_attr_k_d, ++ &dev_attr_integral_cutoff, ++ &dev_attr_slope, ++ &dev_attr_offset, ++}; ++ ++static int create_tzp_attrs(struct device *dev) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(dev_tzp_attrs); i++) { ++ int ret; ++ struct device_attribute *dev_attr = dev_tzp_attrs[i]; ++ ++ ret = device_create_file(dev, dev_attr); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * power_actor_get_max_power() - get the maximum power that a cdev can consume ++ * @cdev: pointer to &thermal_cooling_device ++ * @tz: a valid thermal zone device pointer ++ * @max_power: pointer in which to store the maximum power ++ * ++ * Calculate the maximum power consumption in milliwats that the ++ * cooling device can currently consume and store it in @max_power. ++ * ++ * Return: 0 on success, -EINVAL if @cdev doesn't support the ++ * power_actor API or -E* on other error. ++ */ ++int power_actor_get_max_power(struct thermal_cooling_device *cdev, ++ struct thermal_zone_device *tz, u32 *max_power) ++{ ++ if (!cdev_is_power_actor(cdev)) ++ return -EINVAL; ++ ++ return cdev->ops->state2power(cdev, tz, 0, max_power); ++} ++ ++/** ++ * power_actor_set_power() - limit the maximum power that a cooling device can consume ++ * @cdev: pointer to &thermal_cooling_device ++ * @instance: thermal instance to update ++ * @power: the power in milliwatts ++ * ++ * Set the cooling device to consume at most @power milliwatts. ++ * ++ * Return: 0 on success, -EINVAL if the cooling device does not ++ * implement the power actor API or -E* for other failures. ++ */ ++int power_actor_set_power(struct thermal_cooling_device *cdev, ++ struct thermal_instance *instance, u32 power) ++{ ++ unsigned long state; ++ int ret; ++ ++ if (!cdev_is_power_actor(cdev)) ++ return -EINVAL; ++ ++ ret = cdev->ops->power2state(cdev, instance->tz, power, &state); ++ if (ret) ++ return ret; ++ ++ instance->target = state; ++ cdev->updated = false; ++ thermal_cdev_update(cdev); ++ ++ return 0; ++} ++ + static DEVICE_ATTR(type, 0444, type_show, NULL); + static DEVICE_ATTR(temp, 0444, temp_show, NULL); + static DEVICE_ATTR(mode, 0644, mode_show, mode_store); +@@ -897,6 +1116,50 @@ thermal_cooling_device_trip_point_show(struct device *dev, + return sprintf(buf, "%d\n", instance->trip); + } + ++static struct attribute *cooling_device_attrs[] = { ++ &dev_attr_cdev_type.attr, ++ &dev_attr_max_state.attr, ++ &dev_attr_cur_state.attr, ++ NULL, ++}; ++ ++static const struct attribute_group cooling_device_attr_group = { ++ .attrs = cooling_device_attrs, ++}; ++ ++static const struct attribute_group *cooling_device_attr_groups[] = { ++ &cooling_device_attr_group, ++ NULL, ++}; ++ ++static ssize_t ++thermal_cooling_device_weight_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct thermal_instance *instance; ++ ++ instance = container_of(attr, struct thermal_instance, weight_attr); ++ ++ return sprintf(buf, "%d\n", instance->weight); ++} ++ ++static ssize_t ++thermal_cooling_device_weight_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct thermal_instance *instance; ++ int ret, weight; ++ ++ ret = kstrtoint(buf, 0, &weight); ++ if (ret) ++ return ret; ++ ++ instance = container_of(attr, struct thermal_instance, weight_attr); ++ instance->weight = weight; ++ ++ return count; ++} + /* Device management */ + + /** +@@ -911,6 +1174,9 @@ thermal_cooling_device_trip_point_show(struct device *dev, + * @lower: the Minimum cooling state can be used for this trip point. + * THERMAL_NO_LIMIT means no lower limit, + * and the cooling device can be in cooling state 0. ++ * @weight: The weight of the cooling device to be bound to the ++ * thermal zone. Use THERMAL_WEIGHT_DEFAULT for the ++ * default value + * + * This interface function bind a thermal cooling device to the certain trip + * point of a thermal zone device. +@@ -921,7 +1187,8 @@ thermal_cooling_device_trip_point_show(struct device *dev, + int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, + int trip, + struct thermal_cooling_device *cdev, +- unsigned long upper, unsigned long lower) ++ unsigned long upper, unsigned long lower, ++ unsigned int weight) + { + struct thermal_instance *dev; + struct thermal_instance *pos; +@@ -964,6 +1231,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, + dev->upper = upper; + dev->lower = lower; + dev->target = THERMAL_NO_TARGET; ++ dev->weight = weight; + + result = get_idr(&tz->idr, &tz->lock, &dev->id); + if (result) +@@ -984,6 +1252,16 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, + if (result) + goto remove_symbol_link; + ++ sprintf(dev->weight_attr_name, "cdev%d_weight", dev->id); ++ sysfs_attr_init(&dev->weight_attr.attr); ++ dev->weight_attr.attr.name = dev->weight_attr_name; ++ dev->weight_attr.attr.mode = S_IWUSR | S_IRUGO; ++ dev->weight_attr.show = thermal_cooling_device_weight_show; ++ dev->weight_attr.store = thermal_cooling_device_weight_store; ++ result = device_create_file(&tz->device, &dev->weight_attr); ++ if (result) ++ goto remove_trip_file; ++ + mutex_lock(&tz->lock); + mutex_lock(&cdev->lock); + list_for_each_entry(pos, &tz->thermal_instances, tz_node) +@@ -1001,6 +1279,8 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, + if (!result) + return 0; + ++ device_remove_file(&tz->device, &dev->weight_attr); ++remove_trip_file: + device_remove_file(&tz->device, &dev->attr); + remove_symbol_link: + sysfs_remove_link(&tz->device.kobj, dev->name); +@@ -1126,6 +1406,7 @@ __thermal_cooling_device_register(struct device_node *np, + cdev->ops = ops; + cdev->updated = false; + cdev->device.class = &thermal_class; ++ cdev->device.groups = cooling_device_attr_groups; + cdev->devdata = devdata; + dev_set_name(&cdev->device, "cooling_device%d", cdev->id); + result = device_register(&cdev->device); +@@ -1135,21 +1416,6 @@ __thermal_cooling_device_register(struct device_node *np, + return ERR_PTR(result); + } + +- /* sys I/F */ +- if (type) { +- result = device_create_file(&cdev->device, &dev_attr_cdev_type); +- if (result) +- goto unregister; +- } +- +- result = device_create_file(&cdev->device, &dev_attr_max_state); +- if (result) +- goto unregister; +- +- result = device_create_file(&cdev->device, &dev_attr_cur_state); +- if (result) +- goto unregister; +- + /* Add 'this' new cdev to the global cdev list */ + mutex_lock(&thermal_list_lock); + list_add(&cdev->node, &thermal_cdev_list); +@@ -1159,11 +1425,6 @@ __thermal_cooling_device_register(struct device_node *np, + bind_cdev(cdev); + + return cdev; +- +-unregister: +- release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); +- device_unregister(&cdev->device); +- return ERR_PTR(result); + } + + /** +@@ -1374,7 +1635,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask) + tz->trip_temp_attrs[indx].name; + tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; + tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; +- if (mask & (1 << indx)) { ++ if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS) && ++ mask & (1 << indx)) { + tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; + tz->trip_temp_attrs[indx].attr.store = + trip_point_temp_store; +@@ -1451,7 +1713,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) + struct thermal_zone_device *thermal_zone_device_register(const char *type, + int trips, int mask, void *devdata, + struct thermal_zone_device_ops *ops, +- const struct thermal_zone_params *tzp, ++ struct thermal_zone_params *tzp, + int passive_delay, int polling_delay) + { + struct thermal_zone_device *tz; +@@ -1459,6 +1721,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, + int result; + int count; + int passive = 0; ++ struct thermal_governor *governor; + + if (type && strlen(type) >= THERMAL_NAME_LENGTH) + return ERR_PTR(-EINVAL); +@@ -1545,13 +1808,24 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, + if (result) + goto unregister; + ++ /* Add thermal zone params */ ++ result = create_tzp_attrs(&tz->device); ++ if (result) ++ goto unregister; ++ + /* Update 'this' zone's governor information */ + mutex_lock(&thermal_governor_lock); + + if (tz->tzp) +- tz->governor = __find_governor(tz->tzp->governor_name); ++ governor = __find_governor(tz->tzp->governor_name); + else +- tz->governor = def_governor; ++ governor = def_governor; ++ ++ result = thermal_set_governor(tz, governor); ++ if (result) { ++ mutex_unlock(&thermal_governor_lock); ++ goto unregister; ++ } + + mutex_unlock(&thermal_governor_lock); + +@@ -1640,7 +1914,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) + device_remove_file(&tz->device, &dev_attr_mode); + device_remove_file(&tz->device, &dev_attr_policy); + remove_trip_attrs(tz); +- tz->governor = NULL; ++ thermal_set_governor(tz, NULL); + + thermal_remove_hwmon_sysfs(tz); + release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); +@@ -1800,7 +2074,11 @@ static int __init thermal_register_governors(void) + if (result) + return result; + +- return thermal_gov_user_space_register(); ++ result = thermal_gov_user_space_register(); ++ if (result) ++ return result; ++ ++ return thermal_gov_power_allocator_register(); + } + + static void thermal_unregister_governors(void) +@@ -1809,6 +2087,7 @@ static void thermal_unregister_governors(void) + thermal_gov_fair_share_unregister(); + thermal_gov_bang_bang_unregister(); + thermal_gov_user_space_unregister(); ++ thermal_gov_power_allocator_unregister(); + } + + static int __init thermal_init(void) +diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h +index d15d243..8a66244 100644 +--- a/drivers/thermal/thermal_core.h ++++ b/drivers/thermal/thermal_core.h +@@ -46,8 +46,11 @@ struct thermal_instance { + unsigned long target; /* expected cooling state */ + char attr_name[THERMAL_NAME_LENGTH]; + struct device_attribute attr; ++ char weight_attr_name[THERMAL_NAME_LENGTH]; ++ struct device_attribute weight_attr; + struct list_head tz_node; /* node in tz->thermal_instances */ + struct list_head cdev_node; /* node in cdev->thermal_instances */ ++ unsigned int weight; /* The weight of the cooling device */ + }; + + int thermal_register_governor(struct thermal_governor *); +@@ -85,13 +88,39 @@ static inline int thermal_gov_user_space_register(void) { return 0; } + static inline void thermal_gov_user_space_unregister(void) {} + #endif /* CONFIG_THERMAL_GOV_USER_SPACE */ + ++#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR ++int thermal_gov_power_allocator_register(void); ++void thermal_gov_power_allocator_unregister(void); ++#else ++static inline int thermal_gov_power_allocator_register(void) { return 0; } ++static inline void thermal_gov_power_allocator_unregister(void) {} ++#endif /* CONFIG_THERMAL_GOV_POWER_ALLOCATOR */ ++ + /* device tree support */ + #ifdef CONFIG_THERMAL_OF + int of_parse_thermal_zones(void); + void of_thermal_destroy_zones(void); ++int of_thermal_get_ntrips(struct thermal_zone_device *); ++bool of_thermal_is_trip_valid(struct thermal_zone_device *, int); ++const struct thermal_trip * ++of_thermal_get_trip_points(struct thermal_zone_device *); + #else + static inline int of_parse_thermal_zones(void) { return 0; } + static inline void of_thermal_destroy_zones(void) { } ++static inline int of_thermal_get_ntrips(struct thermal_zone_device *tz) ++{ ++ return 0; ++} ++static inline bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, ++ int trip) ++{ ++ return 0; ++} ++static inline const struct thermal_trip * ++of_thermal_get_trip_points(struct thermal_zone_device *tz) ++{ ++ return NULL; ++} + #endif + + #endif /* __THERMAL_CORE_H__ */ +diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +index 9eec26d..68f53fc 100644 +--- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c ++++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +@@ -28,7 +28,6 @@ + #include <linux/kernel.h> + #include <linux/workqueue.h> + #include <linux/thermal.h> +-#include <linux/cpufreq.h> + #include <linux/cpumask.h> + #include <linux/cpu_cooling.h> + #include <linux/of.h> +@@ -147,7 +146,8 @@ static int ti_thermal_bind(struct thermal_zone_device *thermal, + return thermal_zone_bind_cooling_device(thermal, 0, cdev, + /* bind with min and max states defined by cpu_cooling */ + THERMAL_NO_LIMIT, +- THERMAL_NO_LIMIT); ++ THERMAL_NO_LIMIT, ++ THERMAL_WEIGHT_DEFAULT); + } + + /* Unbind callback functions for thermal zone */ +@@ -286,6 +286,11 @@ static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal, + return ti_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp); + } + ++static const struct thermal_zone_of_device_ops ti_of_thermal_ops = { ++ .get_temp = __ti_thermal_get_temp, ++ .get_trend = __ti_thermal_get_trend, ++}; ++ + static struct thermal_zone_device_ops ti_thermal_ops = { + .get_temp = ti_thermal_get_temp, + .get_trend = ti_thermal_get_trend, +@@ -333,8 +338,7 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, + + /* in case this is specified by DT */ + data->ti_thermal = thermal_zone_of_sensor_register(bgp->dev, id, +- data, __ti_thermal_get_temp, +- __ti_thermal_get_trend); ++ data, &ti_of_thermal_ops); + if (IS_ERR(data->ti_thermal)) { + /* Create thermal zone */ + data->ti_thermal = thermal_zone_device_register(domain, +@@ -403,11 +407,6 @@ int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id) + if (!data) + return -EINVAL; + +- if (!cpufreq_get_current_driver()) { +- dev_dbg(bgp->dev, "no cpufreq driver yet\n"); +- return -EPROBE_DEFER; +- } +- + /* Register cooling device */ + data->cool_dev = cpufreq_cooling_register(cpu_present_mask); + if (IS_ERR(data->cool_dev)) { +diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c +index a28dee9..ec40c59 100644 +--- a/drivers/tty/serial/serial_core.c ++++ b/drivers/tty/serial/serial_core.c +@@ -95,6 +95,9 @@ static void __uart_start(struct tty_struct *tty) + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; + ++ if (port->ops->wake_peer) ++ port->ops->wake_peer(port); ++ + if (!uart_tx_stopped(port)) + port->ops->start_tx(port); + } +diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c +index 0a0a630..dacf8d5 100644 +--- a/drivers/tty/tty_io.c ++++ b/drivers/tty/tty_io.c +@@ -2594,6 +2594,28 @@ static int tiocsetd(struct tty_struct *tty, int __user *p) + } + + /** ++ * tiocgetd - get line discipline ++ * @tty: tty device ++ * @p: pointer to user data ++ * ++ * Retrieves the line discipline id directly from the ldisc. ++ * ++ * Locking: waits for ldisc reference (in case the line discipline ++ * is changing or the tty is being hungup) ++ */ ++ ++static int tiocgetd(struct tty_struct *tty, int __user *p) ++{ ++ struct tty_ldisc *ld; ++ int ret; ++ ++ ld = tty_ldisc_ref_wait(tty); ++ ret = put_user(ld->ops->num, p); ++ tty_ldisc_deref(ld); ++ return ret; ++} ++ ++/** + * send_break - performed time break + * @tty: device to break on + * @duration: timeout in mS +@@ -2807,7 +2829,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + case TIOCGSID: + return tiocgsid(tty, real_tty, p); + case TIOCGETD: +- return put_user(tty->ldisc->ops->num, (int __user *)p); ++ return tiocgetd(tty, p); + case TIOCSETD: + return tiocsetd(tty, p); + case TIOCVHANGUP: +diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c +index 2d822aa..a72e359 100644 +--- a/drivers/tty/tty_ldisc.c ++++ b/drivers/tty/tty_ldisc.c +@@ -414,6 +414,10 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush); + * they are not on hot paths so a little discipline won't do + * any harm. + * ++ * The line discipline-related tty_struct fields are reset to ++ * prevent the ldisc driver from re-using stale information for ++ * the new ldisc instance. ++ * + * Locking: takes termios_rwsem + */ + +@@ -422,6 +426,9 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num) + down_write(&tty->termios_rwsem); + tty->termios.c_line = num; + up_write(&tty->termios_rwsem); ++ ++ tty->disc_data = NULL; ++ tty->receive_room = 0; + } + + /** +diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c +index b2a540b..062a7c5 100644 +--- a/drivers/usb/core/config.c ++++ b/drivers/usb/core/config.c +@@ -520,15 +520,23 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, + + } else if (header->bDescriptorType == + USB_DT_INTERFACE_ASSOCIATION) { ++ struct usb_interface_assoc_descriptor *d; ++ ++ d = (struct usb_interface_assoc_descriptor *)header; ++ if (d->bLength < USB_DT_INTERFACE_ASSOCIATION_SIZE) { ++ dev_warn(ddev, ++ "config %d has an invalid interface association descriptor of length %d, skipping\n", ++ cfgno, d->bLength); ++ continue; ++ } ++ + if (iad_num == USB_MAXIADS) { + dev_warn(ddev, "found more Interface " + "Association Descriptors " + "than allocated for in " + "configuration %d\n", cfgno); + } else { +- config->intf_assoc[iad_num] = +- (struct usb_interface_assoc_descriptor +- *)header; ++ config->intf_assoc[iad_num] = d; + iad_num++; + } + +@@ -633,18 +641,21 @@ void usb_destroy_configuration(struct usb_device *dev) + return; + + if (dev->rawdescriptors) { +- for (i = 0; i < dev->descriptor.bNumConfigurations; i++) ++ for (i = 0; i < dev->descriptor.bNumConfigurations && ++ i < USB_MAXCONFIG; i++) + kfree(dev->rawdescriptors[i]); + + kfree(dev->rawdescriptors); + dev->rawdescriptors = NULL; + } + +- for (c = 0; c < dev->descriptor.bNumConfigurations; c++) { ++ for (c = 0; c < dev->descriptor.bNumConfigurations && ++ c < USB_MAXCONFIG; c++) { + struct usb_host_config *cf = &dev->config[c]; + + kfree(cf->string); +- for (i = 0; i < cf->desc.bNumInterfaces; i++) { ++ for (i = 0; i < cf->desc.bNumInterfaces && ++ i < USB_MAXINTERFACES; i++) { + if (cf->intf_cache[i]) + kref_put(&cf->intf_cache[i]->ref, + usb_release_interface_cache); +@@ -829,10 +840,12 @@ int usb_get_bos_descriptor(struct usb_device *dev) + for (i = 0; i < num; i++) { + buffer += length; + cap = (struct usb_dev_cap_header *)buffer; +- length = cap->bLength; + +- if (total_len < length) ++ if (total_len < sizeof(*cap) || total_len < cap->bLength) { ++ dev->bos->desc->bNumDeviceCaps = i; + break; ++ } ++ length = cap->bLength; + total_len -= length; + + if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) { +diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c +index a85eadf..dfcb5f8 100644 +--- a/drivers/usb/core/devio.c ++++ b/drivers/usb/core/devio.c +@@ -1202,10 +1202,11 @@ static int proc_getdriver(struct usb_dev_state *ps, void __user *arg) + + static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg) + { +- struct usbdevfs_connectinfo ci = { +- .devnum = ps->dev->devnum, +- .slow = ps->dev->speed == USB_SPEED_LOW +- }; ++ struct usbdevfs_connectinfo ci; ++ ++ memset(&ci, 0, sizeof(ci)); ++ ci.devnum = ps->dev->devnum; ++ ci.slow = ps->dev->speed == USB_SPEED_LOW; + + if (copy_to_user(arg, &ci, sizeof(ci))) + return -EFAULT; +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +index 2222899..68c1112 100644 +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -36,6 +36,9 @@ + #define USB_VENDOR_GENESYS_LOGIC 0x05e3 + #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 + ++int otg_usbdev_stat; ++EXPORT_SYMBOL(otg_usbdev_stat); ++ + /* Protect struct usb_device->state and ->children members + * Note: Both are also protected by ->dev.sem, except that ->state can + * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ +@@ -1030,10 +1033,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) + unsigned delay; + + /* Continue a partial initialization */ +- if (type == HUB_INIT2) +- goto init2; +- if (type == HUB_INIT3) ++ if (type == HUB_INIT2 || type == HUB_INIT3) { ++ device_lock(hub->intfdev); ++ ++ /* Was the hub disconnected while we were waiting? */ ++ if (hub->disconnected) { ++ device_unlock(hub->intfdev); ++ kref_put(&hub->kref, hub_release); ++ return; ++ } ++ if (type == HUB_INIT2) ++ goto init2; + goto init3; ++ } ++ kref_get(&hub->kref); + + /* The superspeed hub except for root hub has to use Hub Depth + * value as an offset into the route string to locate the bits +@@ -1231,6 +1244,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) + queue_delayed_work(system_power_efficient_wq, + &hub->init_work, + msecs_to_jiffies(delay)); ++ device_unlock(hub->intfdev); + return; /* Continues at init3: below */ + } else { + msleep(delay); +@@ -1252,6 +1266,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) + /* Allow autosuspend if it was suppressed */ + if (type <= HUB_INIT3) + usb_autopm_put_interface_async(to_usb_interface(hub->intfdev)); ++ ++ if (type == HUB_INIT2 || type == HUB_INIT3) ++ device_unlock(hub->intfdev); ++ kref_put(&hub->kref, hub_release); + } + + /* Implement the continuations for the delays above */ +@@ -4953,9 +4971,19 @@ static void port_event(struct usb_hub *hub, int port1) + dev_dbg(&port_dev->dev, "do warm reset\n"); + if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION) + || udev->state == USB_STATE_NOTATTACHED) { +- if (hub_port_reset(hub, port1, NULL, +- HUB_BH_RESET_TIME, true) < 0) ++ int ret; ++ ++ ret = hub_port_reset(hub, port1, NULL, ++ HUB_BH_RESET_TIME, true); ++ if (ret < 0) + hub_port_disable(hub, port1, 1); ++ ret = hub_port_status(hub, port1, ++ &portstatus, &portchange); ++ if (ret < 0) ++ return; ++ if ((portstatus & USB_PORT_STAT_CONNECTION) && !udev && ++ portstatus & USB_PORT_STAT_ENABLE) ++ connect_change = 1; + } else + reset_device = 1; + } +@@ -4984,6 +5012,10 @@ static void port_event(struct usb_hub *hub, int port1) + + if (connect_change) + hub_port_connect_change(hub, port1, portstatus, portchange); ++ ++ if (!(portstatus & USB_PORT_STAT_CONNECTION) ++ && (hdev->parent == NULL)) ++ otg_usbdev_stat = 0; + } + + static void hub_event(struct work_struct *work) +@@ -5060,6 +5092,7 @@ static void hub_event(struct work_struct *work) + * (powered-off), we leave it in that state, run + * an abbreviated port_event(), and move on. + */ ++ otg_usbdev_stat = 1; + pm_runtime_get_noresume(&port_dev->dev); + pm_runtime_barrier(&port_dev->dev); + usb_lock_port(port_dev); +diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig +index c4880fc..23814ec 100644 +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -183,13 +183,28 @@ config USB_F_FS + + config USB_F_UAC1 + tristate +- ++ + config USB_F_UAC2 + tristate + + config USB_F_UVC + tristate + ++config USB_F_MTP ++ tristate ++ ++config USB_F_PTP ++ tristate ++ ++config USB_F_AUDIO_SRC ++ tristate ++ ++config USB_F_ACC ++ tristate ++ ++config USB_F_MIDI ++ tristate ++ + choice + tristate "USB Gadget Drivers" + default USB_ETH +@@ -362,6 +377,57 @@ config USB_CONFIGFS_F_FS + implemented in kernel space (for instance Ethernet, serial or + mass storage) and other are implemented in user space. + ++config USB_CONFIGFS_F_MTP ++ boolean "MTP gadget" ++ depends on USB_CONFIGFS ++ select USB_F_MTP ++ help ++ USB gadget MTP support ++ ++config USB_CONFIGFS_F_PTP ++ boolean "PTP gadget" ++ depends on USB_CONFIGFS && USB_CONFIGFS_F_MTP ++ select USB_F_PTP ++ help ++ USB gadget PTP support ++ ++config USB_CONFIGFS_F_ACC ++ boolean "Accessory gadget" ++ depends on USB_CONFIGFS ++ select USB_F_ACC ++ help ++ USB gadget Accessory support ++ ++config USB_CONFIGFS_F_AUDIO_SRC ++ boolean "Audio Source gadget" ++ depends on USB_CONFIGFS && USB_CONFIGFS_F_ACC ++ depends on SND_PCM ++ select USB_F_AUDIO_SRC ++ help ++ USB gadget Audio Source support ++ ++config USB_CONFIGFS_UEVENT ++ boolean "Uevent notification of Gadget state" ++ depends on USB_CONFIGFS ++ help ++ Enable uevent notifications to userspace when the gadget ++ state changes. The gadget can be in any of the following ++ three states: "CONNECTED/DISCONNECTED/CONFIGURED" ++ ++config USB_CONFIGFS_F_MIDI ++ boolean "MIDI function" ++ depends on USB_CONFIGFS ++ depends on SND ++ select USB_LIBCOMPOSITE ++ select SND_RAWMIDI ++ select USB_F_MIDI ++ help ++ The MIDI Function acts as a USB Audio device, with one MIDI ++ input and one MIDI output. These MIDI jacks appear as ++ a sound "card" in the ALSA sound system. Other MIDI ++ connections can then be made on the gadget system, using ++ ALSA's aconnect utility etc. ++ + source "drivers/usb/gadget/legacy/Kconfig" + + endchoice +diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c +index f6a51fd..e50ec80 100644 +--- a/drivers/usb/gadget/composite.c ++++ b/drivers/usb/gadget/composite.c +@@ -17,11 +17,42 @@ + #include <linux/module.h> + #include <linux/device.h> + #include <linux/utsname.h> ++#include <linux/delay.h> ++#ifdef CONFIG_USB3_DEVICE_GPIO_CTRL ++#include <linux/interrupt.h> ++#endif + + #include <linux/usb/composite.h> + #include <asm/unaligned.h> ++#include <mach/io.h> + + #include "u_os_desc.h" ++#ifdef CONFIG_USB3_DEVICE_GPIO_CTRL ++#define GPIO_IRQ_NUM 75 ++#define GPIO1_0_IE (1<<0) ++#define GPIO1_0_IC (1<<0) ++#define GPIO1_IE __io_address(0x12141410) ++#define GPIO1_0_DATA __io_address(0x12141004) ++#define GPIO1_IC __io_address(0x1214141c) ++static int uvc_flag; ++static const char gpio_driver_name[] = "usb_gpio_irq"; ++#endif ++#if defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) \ ++ || defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) \ ++ || defined(CONFIG_ARCH_HI3516CV300) || defined(CONFIG_ARCH_HI3516AV200) ++#define USB2_BASE_REG 0x12030000 ++#define DWC_OTG_EN (1 << 31) ++#define USB2_PHY_DPPULL_DOWN (0x3 << 26) ++#endif ++ ++#ifdef CONFIG_ARCH_HI3516CV300 ++#define USB2_OTG_BASE 0x5c ++#endif ++ ++#if defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) \ ++ || defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) || defined(CONFIG_ARCH_HI3516AV200) ++#define USB2_OTG_BASE 0x78 ++#endif + + /** + * struct usb_os_string - represents OS String to be reported by a gadget +@@ -165,6 +196,8 @@ ep_found: + case USB_ENDPOINT_XFER_ISOC: + /* mult: bits 1:0 of bmAttributes */ + _ep->mult = comp_desc->bmAttributes & 0x3; ++ _ep->maxburst = comp_desc->bMaxBurst; ++ break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + _ep->maxburst = comp_desc->bMaxBurst + 1; +@@ -278,10 +311,10 @@ int usb_function_deactivate(struct usb_function *function) + + spin_lock_irqsave(&cdev->lock, flags); + +- if (cdev->deactivations == 0) ++ if (cdev->deactivations == 0) { + status = usb_gadget_disconnect(cdev->gadget); +- if (status == 0) + cdev->deactivations++; ++ } + + spin_unlock_irqrestore(&cdev->lock, flags); + return status; +@@ -845,7 +878,7 @@ done: + } + EXPORT_SYMBOL_GPL(usb_add_config); + +-static void remove_config(struct usb_composite_dev *cdev, ++static void unbind_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) + { + while (!list_empty(&config->functions)) { +@@ -860,7 +893,6 @@ static void remove_config(struct usb_composite_dev *cdev, + /* may free memory for "f" */ + } + } +- list_del(&config->list); + if (config->unbind) { + DBG(cdev, "unbind config '%s'/%p\n", config->label, config); + config->unbind(config); +@@ -887,9 +919,11 @@ void usb_remove_config(struct usb_composite_dev *cdev, + if (cdev->config == config) + reset_config(cdev); + ++ list_del(&config->list); ++ + spin_unlock_irqrestore(&cdev->lock, flags); + +- remove_config(cdev, config); ++ unbind_config(cdev, config); + } + + /*-------------------------------------------------------------------------*/ +@@ -1780,6 +1814,12 @@ void composite_disconnect(struct usb_gadget *gadget) + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + ++ if (cdev == NULL) { ++ WARN(1, "%s: Calling disconnect on a Gadget that is \ ++ not connected\n", __func__); ++ return; ++ } ++ + /* REVISIT: should we have config and device level + * disconnect callbacks? + */ +@@ -1818,7 +1858,8 @@ static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) + struct usb_configuration *c; + c = list_first_entry(&cdev->configs, + struct usb_configuration, list); +- remove_config(cdev, c); ++ list_del(&c->list); ++ unbind_config(cdev, c); + } + if (cdev->driver->unbind && unbind_driver) + cdev->driver->unbind(cdev); +@@ -1835,6 +1876,35 @@ static void composite_unbind(struct usb_gadget *gadget) + __composite_unbind(gadget, true); + } + ++#ifdef CONFIG_USB3_DEVICE_GPIO_CTRL ++static irqreturn_t dwc_usb3_gpio_irq(int irq, void *dev) ++{ ++ int reg; ++ /* mask */ ++ reg = hi_readl(GPIO1_IE); ++ reg &= ~GPIO1_0_IE; ++ hi_writel(reg, GPIO1_IE); ++ /* GPIO1_0 */ ++ if (0 == (hi_readl(GPIO1_0_DATA) & 0x1)) { ++ /* host */ ++ hi_writel(0x30c11004, __io_address(0x1018c110)); ++ udelay(200); ++ /* device */ ++ hi_writel(0x30c12004, __io_address(0x1018c110)); ++ udelay(200); ++ } ++ /* clear */ ++ reg = hi_readl(GPIO1_IC); ++ reg |= GPIO1_0_IC; ++ hi_writel(reg, GPIO1_IC); ++ /* unmask */ ++ reg = hi_readl(GPIO1_IE); ++ reg |= GPIO1_0_IE; ++ hi_writel(reg, GPIO1_IE); ++ return 0; ++} ++#endif ++ + static void update_unchanged_dev_desc(struct usb_device_descriptor *new, + const struct usb_device_descriptor *old) + { +@@ -1968,6 +2038,10 @@ static int composite_bind(struct usb_gadget *gadget, + struct usb_composite_dev *cdev; + struct usb_composite_driver *composite = to_cdriver(gdriver); + int status = -ENOMEM; ++#ifdef CONFIG_HIUSB_DEVICE2_0 ++ void __iomem *usb2_base_reg; ++ int usb2_reg; ++#endif + + cdev = kzalloc(sizeof *cdev, GFP_KERNEL); + if (!cdev) +@@ -1999,10 +2073,39 @@ static int composite_bind(struct usb_gadget *gadget, + + update_unchanged_dev_desc(&cdev->desc, composite->dev); + ++#ifdef CONFIG_USB3_DEVICE_GPIO_CTRL ++ /* uvc Vendor and Product */ ++ if (((cdev->desc).idVendor == 0x1d6b) ++ && ((cdev->desc).idProduct == 0x102)) { ++ int ret = 0; ++ ++ ret = request_irq(GPIO_IRQ_NUM, dwc_usb3_gpio_irq, ++ IRQF_SHARED | IRQF_DISABLED, ++ gpio_driver_name, cdev->driver); ++ uvc_flag = 1; ++ if (ret) ++ return ret; ++ } ++#endif + /* has userspace failed to provide a serial number? */ + if (composite->needs_serial && !cdev->desc.iSerialNumber) + WARNING(cdev, "userspace failed to provide iSerialNumber\n"); +- ++#ifdef CONFIG_HIUSB_SS_DEVICE ++ writel(0x8000, __io_address(0x12030004)); ++ mdelay(200); ++ writel(0x30c01004, __io_address(0x1018c110)); ++ mdelay(200); ++ writel(0x30c02004, __io_address(0x1018c110)); ++ mdelay(200); ++#endif ++#ifdef CONFIG_HIUSB_DEVICE2_0 ++ usb2_base_reg = ioremap_nocache(USB2_BASE_REG, 0x1000); ++ usb2_reg = readl(usb2_base_reg + USB2_OTG_BASE); ++ usb2_reg &= ~(USB2_PHY_DPPULL_DOWN); ++ usb2_reg |= DWC_OTG_EN; ++ writel(usb2_reg, usb2_base_reg + USB2_OTG_BASE); ++ iounmap(usb2_base_reg); ++#endif + INFO(cdev, "%s ready\n", composite->name); + return 0; + +@@ -2129,7 +2232,26 @@ EXPORT_SYMBOL_GPL(usb_composite_probe); + */ + void usb_composite_unregister(struct usb_composite_driver *driver) + { ++#ifdef CONFIG_HIUSB_DEVICE2_0 ++ void __iomem *usb2_base_reg; ++ int usb2_reg; ++#endif ++ + usb_gadget_unregister_driver(&driver->gadget_driver); ++#ifdef CONFIG_USB3_DEVICE_GPIO_CTRL ++ if (uvc_flag) { ++ free_irq(GPIO_IRQ_NUM, driver); ++ uvc_flag = 0; ++ } ++#endif ++#ifdef CONFIG_HIUSB_DEVICE2_0 ++ usb2_base_reg = ioremap_nocache(USB2_BASE_REG, 0x1000); ++ usb2_reg = readl(usb2_base_reg + USB2_OTG_BASE); ++ usb2_reg |= USB2_PHY_DPPULL_DOWN; ++ usb2_reg &= ~DWC_OTG_EN; ++ writel(usb2_reg, usb2_base_reg + USB2_OTG_BASE); ++ iounmap(usb2_base_reg); ++#endif + } + EXPORT_SYMBOL_GPL(usb_composite_unregister); + +diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c +index a7e1a96..fbf07ff 100644 +--- a/drivers/usb/gadget/configfs.c ++++ b/drivers/usb/gadget/configfs.c +@@ -9,6 +9,31 @@ + #include "u_f.h" + #include "u_os_desc.h" + ++#ifdef CONFIG_USB_CONFIGFS_UEVENT ++#include <linux/platform_device.h> ++#include <linux/kdev_t.h> ++#include <linux/usb/ch9.h> ++ ++#ifdef CONFIG_USB_CONFIGFS_F_ACC ++extern int acc_ctrlrequest(struct usb_composite_dev *cdev, ++ const struct usb_ctrlrequest *ctrl); ++void acc_disconnect(void); ++#endif ++static struct class *android_class; ++static struct device *android_device; ++static int index; ++ ++struct device *create_function_device(char *name) ++{ ++ if (android_device && !IS_ERR(android_device)) ++ return device_create(android_class, android_device, ++ MKDEV(0, index++), NULL, name); ++ else ++ return ERR_PTR(-EINVAL); ++} ++EXPORT_SYMBOL_GPL(create_function_device); ++#endif ++ + int check_user_usb_string(const char *name, + struct usb_gadget_strings *stringtab_dev) + { +@@ -63,6 +88,12 @@ struct gadget_info { + bool use_os_desc; + char b_vendor_code; + char qw_sign[OS_STRING_QW_SIGN_LEN]; ++#ifdef CONFIG_USB_CONFIGFS_UEVENT ++ bool connected; ++ bool sw_connected; ++ struct work_struct work; ++ struct device *dev; ++#endif + }; + + struct config_usb_cfg { +@@ -262,7 +293,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct gadget_info *gi, + + mutex_lock(&gi->lock); + +- if (!strlen(name)) { ++ if (!strlen(name) || strcmp(name, "none") == 0) { + ret = unregister_gadget(gi); + if (ret) + goto err; +@@ -405,6 +436,11 @@ static int config_usb_cfg_link( + } + + f = usb_get_function(fi); ++ if (f == NULL) { ++ /* Are we trying to symlink PTP without MTP function? */ ++ ret = -EINVAL; /* Invalid Configuration */ ++ goto out; ++ } + if (IS_ERR(f)) { + ret = PTR_ERR(f); + goto out; +@@ -1427,6 +1463,60 @@ err_comp_cleanup: + return ret; + } + ++#ifdef CONFIG_USB_CONFIGFS_UEVENT ++static void android_work(struct work_struct *data) ++{ ++ struct gadget_info *gi = container_of(data, struct gadget_info, work); ++ struct usb_composite_dev *cdev = &gi->cdev; ++ char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; ++ char *connected[2] = { "USB_STATE=CONNECTED", NULL }; ++ char *configured[2] = { "USB_STATE=CONFIGURED", NULL }; ++ /* 0-connected 1-configured 2-disconnected*/ ++ bool status[3] = { false, false, false }; ++ unsigned long flags; ++ bool uevent_sent = false; ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ if (cdev->config) ++ status[1] = true; ++ ++ if (gi->connected != gi->sw_connected) { ++ if (gi->connected) ++ status[0] = true; ++ else ++ status[2] = true; ++ gi->sw_connected = gi->connected; ++ } ++ spin_unlock_irqrestore(&cdev->lock, flags); ++ ++ if (status[0]) { ++ kobject_uevent_env(&android_device->kobj, ++ KOBJ_CHANGE, connected); ++ pr_info("%s: sent uevent %s\n", __func__, connected[0]); ++ uevent_sent = true; ++ } ++ ++ if (status[1]) { ++ kobject_uevent_env(&android_device->kobj, ++ KOBJ_CHANGE, configured); ++ pr_info("%s: sent uevent %s\n", __func__, configured[0]); ++ uevent_sent = true; ++ } ++ ++ if (status[2]) { ++ kobject_uevent_env(&android_device->kobj, ++ KOBJ_CHANGE, disconnected); ++ pr_info("%s: sent uevent %s\n", __func__, disconnected[0]); ++ uevent_sent = true; ++ } ++ ++ if (!uevent_sent) { ++ pr_info("%s: did not send uevent (%d %d %p)\n", __func__, ++ gi->connected, gi->sw_connected, cdev->config); ++ } ++} ++#endif ++ + static void configfs_composite_unbind(struct usb_gadget *gadget) + { + struct usb_composite_dev *cdev; +@@ -1444,14 +1534,78 @@ static void configfs_composite_unbind(struct usb_gadget *gadget) + set_gadget_data(gadget, NULL); + } + ++#ifdef CONFIG_USB_CONFIGFS_UEVENT ++static int android_setup(struct usb_gadget *gadget, ++ const struct usb_ctrlrequest *c) ++{ ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ unsigned long flags; ++ struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); ++ int value = -EOPNOTSUPP; ++ struct usb_function_instance *fi; ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ if (!gi->connected) { ++ gi->connected = 1; ++ schedule_work(&gi->work); ++ } ++ spin_unlock_irqrestore(&cdev->lock, flags); ++ list_for_each_entry(fi, &gi->available_func, cfs_list) { ++ if (fi != NULL && fi->f != NULL && fi->f->setup != NULL) { ++ value = fi->f->setup(fi->f, c); ++ if (value >= 0) ++ break; ++ } ++ } ++ ++#ifdef CONFIG_USB_CONFIGFS_F_ACC ++ if (value < 0) ++ value = acc_ctrlrequest(cdev, c); ++#endif ++ ++ if (value < 0) ++ value = composite_setup(gadget, c); ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ if (c->bRequest == USB_REQ_SET_CONFIGURATION && ++ cdev->config) { ++ schedule_work(&gi->work); ++ } ++ spin_unlock_irqrestore(&cdev->lock, flags); ++ ++ return value; ++} ++ ++static void android_disconnect(struct usb_gadget *gadget) ++{ ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); ++ ++ /* accessory HID support can be active while the ++ accessory function is not actually enabled, ++ so we need to inform it when we are disconnected. ++ */ ++ ++#ifdef CONFIG_USB_CONFIGFS_F_ACC ++ acc_disconnect(); ++#endif ++ gi->connected = 0; ++ schedule_work(&gi->work); ++ composite_disconnect(gadget); ++} ++#endif ++ + static const struct usb_gadget_driver configfs_driver_template = { + .bind = configfs_composite_bind, + .unbind = configfs_composite_unbind, +- ++#ifdef CONFIG_USB_CONFIGFS_UEVENT ++ .setup = android_setup, ++ .disconnect = android_disconnect, ++#else + .setup = composite_setup, + .reset = composite_disconnect, + .disconnect = composite_disconnect, +- ++#endif + .max_speed = USB_SPEED_SUPER, + .driver = { + .owner = THIS_MODULE, +@@ -1459,6 +1613,89 @@ static const struct usb_gadget_driver configfs_driver_template = { + }, + }; + ++#ifdef CONFIG_USB_CONFIGFS_UEVENT ++static ssize_t state_show(struct device *pdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct gadget_info *dev = dev_get_drvdata(pdev); ++ struct usb_composite_dev *cdev; ++ char *state = "DISCONNECTED"; ++ unsigned long flags; ++ ++ if (!dev) ++ goto out; ++ ++ cdev = &dev->cdev; ++ ++ if (!cdev) ++ goto out; ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ if (cdev->config) ++ state = "CONFIGURED"; ++ else if (dev->connected) ++ state = "CONNECTED"; ++ spin_unlock_irqrestore(&cdev->lock, flags); ++out: ++ return sprintf(buf, "%s\n", state); ++} ++ ++static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); ++ ++static struct device_attribute *android_usb_attributes[] = { ++ &dev_attr_state, ++ NULL ++}; ++ ++static int android_device_create(struct gadget_info *gi) ++{ ++ struct device_attribute **attrs; ++ struct device_attribute *attr; ++ ++ INIT_WORK(&gi->work, android_work); ++ android_device = device_create(android_class, NULL, ++ MKDEV(0, 0), NULL, "android0"); ++ if (IS_ERR(android_device)) ++ return PTR_ERR(android_device); ++ ++ dev_set_drvdata(android_device, gi); ++ ++ attrs = android_usb_attributes; ++ while ((attr = *attrs++)) { ++ int err; ++ ++ err = device_create_file(android_device, attr); ++ if (err) { ++ device_destroy(android_device->class, ++ android_device->devt); ++ return err; ++ } ++ } ++ ++ return 0; ++} ++ ++static void android_device_destroy(void) ++{ ++ struct device_attribute **attrs; ++ struct device_attribute *attr; ++ ++ attrs = android_usb_attributes; ++ while ((attr = *attrs++)) ++ device_remove_file(android_device, attr); ++ device_destroy(android_device->class, android_device->devt); ++} ++#else ++static inline int android_device_create(struct gadget_info *gi) ++{ ++ return 0; ++} ++ ++static inline void android_device_destroy(void) ++{ ++} ++#endif ++ + static struct config_group *gadgets_make( + struct config_group *group, + const char *name) +@@ -1468,7 +1705,6 @@ static struct config_group *gadgets_make( + gi = kzalloc(sizeof(*gi), GFP_KERNEL); + if (!gi) + return ERR_PTR(-ENOMEM); +- + gi->group.default_groups = gi->default_groups; + gi->group.default_groups[0] = &gi->functions_group; + gi->group.default_groups[1] = &gi->configs_group; +@@ -1507,6 +1743,9 @@ static struct config_group *gadgets_make( + if (!gi->composite.gadget_driver.function) + goto err; + ++ if (android_device_create(gi) < 0) ++ goto err; ++ + #ifdef CONFIG_USB_OTG + gi->otg.bLength = sizeof(struct usb_otg_descriptor); + gi->otg.bDescriptorType = USB_DT_OTG; +@@ -1516,6 +1755,7 @@ static struct config_group *gadgets_make( + config_group_init_type_name(&gi->group, name, + &gadget_root_type); + return &gi->group; ++ + err: + kfree(gi); + return ERR_PTR(-ENOMEM); +@@ -1524,6 +1764,7 @@ err: + static void gadgets_drop(struct config_group *group, struct config_item *item) + { + config_item_put(item); ++ android_device_destroy(); + } + + static struct configfs_group_operations gadgets_ops = { +@@ -1561,6 +1802,13 @@ static int __init gadget_cfs_init(void) + config_group_init(&gadget_subsys.su_group); + + ret = configfs_register_subsystem(&gadget_subsys); ++ ++#ifdef CONFIG_USB_CONFIGFS_UEVENT ++ android_class = class_create(THIS_MODULE, "android_usb"); ++ if (IS_ERR(android_class)) ++ return PTR_ERR(android_class); ++#endif ++ + return ret; + } + module_init(gadget_cfs_init); +@@ -1568,5 +1816,10 @@ module_init(gadget_cfs_init); + static void __exit gadget_cfs_exit(void) + { + configfs_unregister_subsystem(&gadget_subsys); ++#ifdef CONFIG_USB_CONFIGFS_UEVENT ++ if (!IS_ERR(android_class)) ++ class_destroy(android_class); ++#endif ++ + } + module_exit(gadget_cfs_exit); +diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c +index 0567cca..741d75b 100644 +--- a/drivers/usb/gadget/epautoconf.c ++++ b/drivers/usb/gadget/epautoconf.c +@@ -180,8 +180,8 @@ ep_matches ( + int size = ep->maxpacket_limit; + + /* min() doesn't work on bitfields with gcc-3.5 */ +- if (size > 64) +- size = 64; ++ if (size > 512) ++ size = 512; + desc->wMaxPacketSize = cpu_to_le16(size); + } + ep->address = desc->bEndpointAddress; +diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile +index 90701aa..4167a04 100644 +--- a/drivers/usb/gadget/function/Makefile ++++ b/drivers/usb/gadget/function/Makefile +@@ -32,9 +32,21 @@ usb_f_mass_storage-y := f_mass_storage.o storage_common.o + obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o + usb_f_fs-y := f_fs.o + obj-$(CONFIG_USB_F_FS) += usb_f_fs.o +-usb_f_uac1-y := f_uac1.o u_uac1.o ++usb_f_uac1-y := f_uac1.o uac_audio_ex.o uac_queue_ex.o uac_v4l2_ex.o + obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o + usb_f_uac2-y := f_uac2.o + obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o + usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o + obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o ++usb_f_mtp-y := f_mtp.o ++obj-$(CONFIG_USB_F_MTP) += usb_f_mtp.o ++usb_f_ptp-y := f_ptp.o ++obj-$(CONFIG_USB_F_PTP) += usb_f_ptp.o ++usb_f_audio_source-y := f_audio_source.o ++obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o ++usb_f_accessory-y := f_accessory.o ++obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o ++ ++ ++usb_f_midi-y := f_midi.o ++obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o +diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c +new file mode 100644 +index 0000000..95a7bbb +--- /dev/null ++++ b/drivers/usb/gadget/function/f_accessory.c +@@ -0,0 +1,1362 @@ ++/* ++ * Gadget Function Driver for Android USB accessories ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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. ++ * ++ */ ++ ++/* #define DEBUG */ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/poll.h> ++#include <linux/delay.h> ++#include <linux/wait.h> ++#include <linux/err.h> ++#include <linux/interrupt.h> ++#include <linux/kthread.h> ++#include <linux/freezer.h> ++ ++#include <linux/types.h> ++#include <linux/file.h> ++#include <linux/device.h> ++#include <linux/miscdevice.h> ++ ++#include <linux/hid.h> ++#include <linux/hiddev.h> ++#include <linux/usb.h> ++#include <linux/usb/ch9.h> ++#include <linux/usb/f_accessory.h> ++ ++#include <linux/configfs.h> ++#include <linux/usb/composite.h> ++ ++#define MAX_INST_NAME_LEN 40 ++#define BULK_BUFFER_SIZE 16384 ++#define ACC_STRING_SIZE 256 ++ ++#define PROTOCOL_VERSION 2 ++ ++/* String IDs */ ++#define INTERFACE_STRING_INDEX 0 ++ ++/* number of tx and rx requests to allocate */ ++#define TX_REQ_MAX 4 ++#define RX_REQ_MAX 2 ++ ++struct acc_hid_dev { ++ struct list_head list; ++ struct hid_device *hid; ++ struct acc_dev *dev; ++ /* accessory defined ID */ ++ int id; ++ /* HID report descriptor */ ++ u8 *report_desc; ++ /* length of HID report descriptor */ ++ int report_desc_len; ++ /* number of bytes of report_desc we have received so far */ ++ int report_desc_offset; ++}; ++ ++struct acc_dev { ++ struct usb_function function; ++ struct usb_composite_dev *cdev; ++ spinlock_t lock; ++ ++ struct usb_ep *ep_in; ++ struct usb_ep *ep_out; ++ ++ /* set to 1 when we connect */ ++ int online:1; ++ /* Set to 1 when we disconnect. ++ * Not cleared until our file is closed. ++ */ ++ int disconnected:1; ++ ++ /* strings sent by the host */ ++ char manufacturer[ACC_STRING_SIZE]; ++ char model[ACC_STRING_SIZE]; ++ char description[ACC_STRING_SIZE]; ++ char version[ACC_STRING_SIZE]; ++ char uri[ACC_STRING_SIZE]; ++ char serial[ACC_STRING_SIZE]; ++ ++ /* for acc_complete_set_string */ ++ int string_index; ++ ++ /* set to 1 if we have a pending start request */ ++ int start_requested; ++ ++ int audio_mode; ++ ++ /* synchronize access to our device file */ ++ atomic_t open_excl; ++ ++ struct list_head tx_idle; ++ ++ wait_queue_head_t read_wq; ++ wait_queue_head_t write_wq; ++ struct usb_request *rx_req[RX_REQ_MAX]; ++ int rx_done; ++ ++ /* delayed work for handling ACCESSORY_START */ ++ struct delayed_work start_work; ++ ++ /* worker for registering and unregistering hid devices */ ++ struct work_struct hid_work; ++ ++ /* list of active HID devices */ ++ struct list_head hid_list; ++ ++ /* list of new HID devices to register */ ++ struct list_head new_hid_list; ++ ++ /* list of dead HID devices to unregister */ ++ struct list_head dead_hid_list; ++}; ++ ++static struct usb_interface_descriptor acc_interface_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0, ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, ++ .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, ++ .bInterfaceProtocol = 0, ++}; ++ ++static struct usb_endpoint_descriptor acc_highspeed_in_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor acc_highspeed_out_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor acc_fullspeed_in_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor acc_fullspeed_out_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *fs_acc_descs[] = { ++ (struct usb_descriptor_header *) &acc_interface_desc, ++ (struct usb_descriptor_header *) &acc_fullspeed_in_desc, ++ (struct usb_descriptor_header *) &acc_fullspeed_out_desc, ++ NULL, ++}; ++ ++static struct usb_descriptor_header *hs_acc_descs[] = { ++ (struct usb_descriptor_header *) &acc_interface_desc, ++ (struct usb_descriptor_header *) &acc_highspeed_in_desc, ++ (struct usb_descriptor_header *) &acc_highspeed_out_desc, ++ NULL, ++}; ++ ++static struct usb_string acc_string_defs[] = { ++ [INTERFACE_STRING_INDEX].s = "Android Accessory Interface", ++ { }, /* end of list */ ++}; ++ ++static struct usb_gadget_strings acc_string_table = { ++ .language = 0x0409, /* en-US */ ++ .strings = acc_string_defs, ++}; ++ ++static struct usb_gadget_strings *acc_strings[] = { ++ &acc_string_table, ++ NULL, ++}; ++ ++/* temporary variable used between acc_open() and acc_gadget_bind() */ ++static struct acc_dev *_acc_dev; ++ ++struct acc_instance { ++ struct usb_function_instance func_inst; ++ const char *name; ++}; ++ ++static inline struct acc_dev *func_to_dev(struct usb_function *f) ++{ ++ return container_of(f, struct acc_dev, function); ++} ++ ++static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size) ++{ ++ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); ++ if (!req) ++ return NULL; ++ ++ /* now allocate buffers for the requests */ ++ req->buf = kmalloc(buffer_size, GFP_KERNEL); ++ if (!req->buf) { ++ usb_ep_free_request(ep, req); ++ return NULL; ++ } ++ ++ return req; ++} ++ ++static void acc_request_free(struct usb_request *req, struct usb_ep *ep) ++{ ++ if (req) { ++ kfree(req->buf); ++ usb_ep_free_request(ep, req); ++ } ++} ++ ++/* add a request to the tail of a list */ ++static void req_put(struct acc_dev *dev, struct list_head *head, ++ struct usb_request *req) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ list_add_tail(&req->list, head); ++ spin_unlock_irqrestore(&dev->lock, flags); ++} ++ ++/* remove a request from the head of a list */ ++static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) ++{ ++ unsigned long flags; ++ struct usb_request *req; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (list_empty(head)) { ++ req = 0; ++ } else { ++ req = list_first_entry(head, struct usb_request, list); ++ list_del(&req->list); ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ return req; ++} ++ ++static void acc_set_disconnected(struct acc_dev *dev) ++{ ++ dev->online = 0; ++ dev->disconnected = 1; ++} ++ ++static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct acc_dev *dev = _acc_dev; ++ ++ if (req->status == -ESHUTDOWN) { ++ pr_debug("acc_complete_in set disconnected"); ++ acc_set_disconnected(dev); ++ } ++ ++ req_put(dev, &dev->tx_idle, req); ++ ++ wake_up(&dev->write_wq); ++} ++ ++static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct acc_dev *dev = _acc_dev; ++ ++ dev->rx_done = 1; ++ if (req->status == -ESHUTDOWN) { ++ pr_debug("acc_complete_out set disconnected"); ++ acc_set_disconnected(dev); ++ } ++ ++ wake_up(&dev->read_wq); ++} ++ ++static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct acc_dev *dev = ep->driver_data; ++ char *string_dest = NULL; ++ int length = req->actual; ++ ++ if (req->status != 0) { ++ pr_err("acc_complete_set_string, err %d\n", req->status); ++ return; ++ } ++ ++ switch (dev->string_index) { ++ case ACCESSORY_STRING_MANUFACTURER: ++ string_dest = dev->manufacturer; ++ break; ++ case ACCESSORY_STRING_MODEL: ++ string_dest = dev->model; ++ break; ++ case ACCESSORY_STRING_DESCRIPTION: ++ string_dest = dev->description; ++ break; ++ case ACCESSORY_STRING_VERSION: ++ string_dest = dev->version; ++ break; ++ case ACCESSORY_STRING_URI: ++ string_dest = dev->uri; ++ break; ++ case ACCESSORY_STRING_SERIAL: ++ string_dest = dev->serial; ++ break; ++ } ++ if (string_dest) { ++ unsigned long flags; ++ ++ if (length >= ACC_STRING_SIZE) ++ length = ACC_STRING_SIZE - 1; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ memcpy(string_dest, req->buf, length); ++ /* ensure zero termination */ ++ string_dest[length] = 0; ++ spin_unlock_irqrestore(&dev->lock, flags); ++ } else { ++ pr_err("unknown accessory string index %d\n", ++ dev->string_index); ++ } ++} ++ ++static void acc_complete_set_hid_report_desc(struct usb_ep *ep, ++ struct usb_request *req) ++{ ++ struct acc_hid_dev *hid = req->context; ++ struct acc_dev *dev = hid->dev; ++ int length = req->actual; ++ ++ if (req->status != 0) { ++ pr_err("acc_complete_set_hid_report_desc, err %d\n", ++ req->status); ++ return; ++ } ++ ++ memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length); ++ hid->report_desc_offset += length; ++ if (hid->report_desc_offset == hid->report_desc_len) { ++ /* After we have received the entire report descriptor ++ * we schedule work to initialize the HID device ++ */ ++ schedule_work(&dev->hid_work); ++ } ++} ++ ++static void acc_complete_send_hid_event(struct usb_ep *ep, ++ struct usb_request *req) ++{ ++ struct acc_hid_dev *hid = req->context; ++ int length = req->actual; ++ ++ if (req->status != 0) { ++ pr_err("acc_complete_send_hid_event, err %d\n", req->status); ++ return; ++ } ++ ++ hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1); ++} ++ ++static int acc_hid_parse(struct hid_device *hid) ++{ ++ struct acc_hid_dev *hdev = hid->driver_data; ++ ++ hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len); ++ return 0; ++} ++ ++static int acc_hid_start(struct hid_device *hid) ++{ ++ return 0; ++} ++ ++static void acc_hid_stop(struct hid_device *hid) ++{ ++} ++ ++static int acc_hid_open(struct hid_device *hid) ++{ ++ return 0; ++} ++ ++static void acc_hid_close(struct hid_device *hid) ++{ ++} ++ ++static struct hid_ll_driver acc_hid_ll_driver = { ++ .parse = acc_hid_parse, ++ .start = acc_hid_start, ++ .stop = acc_hid_stop, ++ .open = acc_hid_open, ++ .close = acc_hid_close, ++}; ++ ++static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev, ++ int id, int desc_len) ++{ ++ struct acc_hid_dev *hdev; ++ ++ hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC); ++ if (!hdev) ++ return NULL; ++ hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC); ++ if (!hdev->report_desc) { ++ kfree(hdev); ++ return NULL; ++ } ++ hdev->dev = dev; ++ hdev->id = id; ++ hdev->report_desc_len = desc_len; ++ ++ return hdev; ++} ++ ++static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id) ++{ ++ struct acc_hid_dev *hid; ++ ++ list_for_each_entry(hid, list, list) { ++ if (hid->id == id) ++ return hid; ++ } ++ return NULL; ++} ++ ++static int acc_register_hid(struct acc_dev *dev, int id, int desc_length) ++{ ++ struct acc_hid_dev *hid; ++ unsigned long flags; ++ ++ /* report descriptor length must be > 0 */ ++ if (desc_length <= 0) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ /* replace HID if one already exists with this ID */ ++ hid = acc_hid_get(&dev->hid_list, id); ++ if (!hid) ++ hid = acc_hid_get(&dev->new_hid_list, id); ++ if (hid) ++ list_move(&hid->list, &dev->dead_hid_list); ++ ++ hid = acc_hid_new(dev, id, desc_length); ++ if (!hid) { ++ spin_unlock_irqrestore(&dev->lock, flags); ++ return -ENOMEM; ++ } ++ ++ list_add(&hid->list, &dev->new_hid_list); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ /* schedule work to register the HID device */ ++ schedule_work(&dev->hid_work); ++ return 0; ++} ++ ++static int acc_unregister_hid(struct acc_dev *dev, int id) ++{ ++ struct acc_hid_dev *hid; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ hid = acc_hid_get(&dev->hid_list, id); ++ if (!hid) ++ hid = acc_hid_get(&dev->new_hid_list, id); ++ if (!hid) { ++ spin_unlock_irqrestore(&dev->lock, flags); ++ return -EINVAL; ++ } ++ ++ list_move(&hid->list, &dev->dead_hid_list); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ schedule_work(&dev->hid_work); ++ return 0; ++} ++ ++static int create_bulk_endpoints(struct acc_dev *dev, ++ struct usb_endpoint_descriptor *in_desc, ++ struct usb_endpoint_descriptor *out_desc) ++{ ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *req; ++ struct usb_ep *ep; ++ int i; ++ ++ DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); ++ ++ ep = usb_ep_autoconfig(cdev->gadget, in_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_in = ep; ++ ++ ep = usb_ep_autoconfig(cdev->gadget, out_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_out = ep; ++ ++ ep = usb_ep_autoconfig(cdev->gadget, out_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_out = ep; ++ ++ /* now allocate requests for our endpoints */ ++ for (i = 0; i < TX_REQ_MAX; i++) { ++ req = acc_request_new(dev->ep_in, BULK_BUFFER_SIZE); ++ if (!req) ++ goto fail; ++ req->complete = acc_complete_in; ++ req_put(dev, &dev->tx_idle, req); ++ } ++ for (i = 0; i < RX_REQ_MAX; i++) { ++ req = acc_request_new(dev->ep_out, BULK_BUFFER_SIZE); ++ if (!req) ++ goto fail; ++ req->complete = acc_complete_out; ++ dev->rx_req[i] = req; ++ } ++ ++ return 0; ++ ++fail: ++ pr_err("acc_bind() could not allocate requests\n"); ++ while ((req = req_get(dev, &dev->tx_idle))) ++ acc_request_free(req, dev->ep_in); ++ for (i = 0; i < RX_REQ_MAX; i++) ++ acc_request_free(dev->rx_req[i], dev->ep_out); ++ return -1; ++} ++ ++static ssize_t acc_read(struct file *fp, char __user *buf, ++ size_t count, loff_t *pos) ++{ ++ struct acc_dev *dev = fp->private_data; ++ struct usb_request *req; ++ ssize_t r = count; ++ unsigned xfer; ++ int ret = 0; ++ ++ pr_debug("acc_read(%zu)\n", count); ++ ++ if (dev->disconnected) { ++ pr_debug("acc_read disconnected"); ++ return -ENODEV; ++ } ++ ++ if (count > BULK_BUFFER_SIZE) ++ count = BULK_BUFFER_SIZE; ++ ++ /* we will block until we're online */ ++ pr_debug("acc_read: waiting for online\n"); ++ ret = wait_event_interruptible(dev->read_wq, dev->online); ++ if (ret < 0) { ++ r = ret; ++ goto done; ++ } ++ ++ if (dev->rx_done) { ++ // last req cancelled. try to get it. ++ req = dev->rx_req[0]; ++ goto copy_data; ++ } ++ ++requeue_req: ++ /* queue a request */ ++ req = dev->rx_req[0]; ++ req->length = count; ++ dev->rx_done = 0; ++ ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); ++ if (ret < 0) { ++ r = -EIO; ++ goto done; ++ } else { ++ pr_debug("rx %p queue\n", req); ++ } ++ ++ /* wait for a request to complete */ ++ ret = wait_event_interruptible(dev->read_wq, dev->rx_done); ++ if (ret < 0) { ++ r = ret; ++ ret = usb_ep_dequeue(dev->ep_out, req); ++ if (ret != 0) { ++ // cancel failed. There can be a data already received. ++ // it will be retrieved in the next read. ++ pr_debug("acc_read: cancelling failed %d", ret); ++ } ++ goto done; ++ } ++ ++copy_data: ++ dev->rx_done = 0; ++ if (dev->online) { ++ /* If we got a 0-len packet, throw it back and try again. */ ++ if (req->actual == 0) ++ goto requeue_req; ++ ++ pr_debug("rx %p %u\n", req, req->actual); ++ xfer = (req->actual < count) ? req->actual : count; ++ r = xfer; ++ if (copy_to_user(buf, req->buf, xfer)) ++ r = -EFAULT; ++ } else ++ r = -EIO; ++ ++done: ++ pr_debug("acc_read returning %zd\n", r); ++ return r; ++} ++ ++static ssize_t acc_write(struct file *fp, const char __user *buf, ++ size_t count, loff_t *pos) ++{ ++ struct acc_dev *dev = fp->private_data; ++ struct usb_request *req = 0; ++ ssize_t r = count; ++ unsigned xfer; ++ int ret; ++ ++ pr_debug("acc_write(%zu)\n", count); ++ ++ if (!dev->online || dev->disconnected) { ++ pr_debug("acc_write disconnected or not online"); ++ return -ENODEV; ++ } ++ ++ while (count > 0) { ++ if (!dev->online) { ++ pr_debug("acc_write dev->error\n"); ++ r = -EIO; ++ break; ++ } ++ ++ /* get an idle tx request to use */ ++ req = 0; ++ ret = wait_event_interruptible(dev->write_wq, ++ ((req = req_get(dev, &dev->tx_idle)) || !dev->online)); ++ if (!req) { ++ r = ret; ++ break; ++ } ++ ++ if (count > BULK_BUFFER_SIZE) { ++ xfer = BULK_BUFFER_SIZE; ++ /* ZLP, They will be more TX requests so not yet. */ ++ req->zero = 0; ++ } else { ++ xfer = count; ++ /* If the data length is a multple of the ++ * maxpacket size then send a zero length packet(ZLP). ++ */ ++ req->zero = ((xfer % dev->ep_in->maxpacket) == 0); ++ } ++ if (copy_from_user(req->buf, buf, xfer)) { ++ r = -EFAULT; ++ break; ++ } ++ ++ req->length = xfer; ++ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); ++ if (ret < 0) { ++ pr_debug("acc_write: xfer error %d\n", ret); ++ r = -EIO; ++ break; ++ } ++ ++ buf += xfer; ++ count -= xfer; ++ ++ /* zero this so we don't try to free it on error exit */ ++ req = 0; ++ } ++ ++ if (req) ++ req_put(dev, &dev->tx_idle, req); ++ ++ pr_debug("acc_write returning %zd\n", r); ++ return r; ++} ++ ++static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) ++{ ++ struct acc_dev *dev = fp->private_data; ++ char *src = NULL; ++ int ret; ++ ++ switch (code) { ++ case ACCESSORY_GET_STRING_MANUFACTURER: ++ src = dev->manufacturer; ++ break; ++ case ACCESSORY_GET_STRING_MODEL: ++ src = dev->model; ++ break; ++ case ACCESSORY_GET_STRING_DESCRIPTION: ++ src = dev->description; ++ break; ++ case ACCESSORY_GET_STRING_VERSION: ++ src = dev->version; ++ break; ++ case ACCESSORY_GET_STRING_URI: ++ src = dev->uri; ++ break; ++ case ACCESSORY_GET_STRING_SERIAL: ++ src = dev->serial; ++ break; ++ case ACCESSORY_IS_START_REQUESTED: ++ return dev->start_requested; ++ case ACCESSORY_GET_AUDIO_MODE: ++ return dev->audio_mode; ++ } ++ if (!src) ++ return -EINVAL; ++ ++ ret = strlen(src) + 1; ++ if (copy_to_user((void __user *)value, src, ret)) ++ ret = -EFAULT; ++ return ret; ++} ++ ++static int acc_open(struct inode *ip, struct file *fp) ++{ ++ printk(KERN_INFO "acc_open\n"); ++ if (atomic_xchg(&_acc_dev->open_excl, 1)) ++ return -EBUSY; ++ ++ _acc_dev->disconnected = 0; ++ fp->private_data = _acc_dev; ++ return 0; ++} ++ ++static int acc_release(struct inode *ip, struct file *fp) ++{ ++ printk(KERN_INFO "acc_release\n"); ++ ++ WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); ++ _acc_dev->disconnected = 0; ++ return 0; ++} ++ ++/* file operations for /dev/usb_accessory */ ++static const struct file_operations acc_fops = { ++ .owner = THIS_MODULE, ++ .read = acc_read, ++ .write = acc_write, ++ .unlocked_ioctl = acc_ioctl, ++ .open = acc_open, ++ .release = acc_release, ++}; ++ ++static int acc_hid_probe(struct hid_device *hdev, ++ const struct hid_device_id *id) ++{ ++ int ret; ++ ++ ret = hid_parse(hdev); ++ if (ret) ++ return ret; ++ return hid_hw_start(hdev, HID_CONNECT_DEFAULT); ++} ++ ++static struct miscdevice acc_device = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "usb_accessory", ++ .fops = &acc_fops, ++}; ++ ++static const struct hid_device_id acc_hid_table[] = { ++ { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) }, ++ { } ++}; ++ ++static struct hid_driver acc_hid_driver = { ++ .name = "USB accessory", ++ .id_table = acc_hid_table, ++ .probe = acc_hid_probe, ++}; ++ ++int acc_ctrlrequest(struct usb_composite_dev *cdev, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ struct acc_dev *dev = _acc_dev; ++ int value = -EOPNOTSUPP; ++ struct acc_hid_dev *hid; ++ int offset; ++ u8 b_requestType = ctrl->bRequestType; ++ u8 b_request = ctrl->bRequest; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ unsigned long flags; ++ ++/* ++ printk(KERN_INFO "acc_ctrlrequest " ++ "%02x.%02x v%04x i%04x l%u\n", ++ b_requestType, b_request, ++ w_value, w_index, w_length); ++*/ ++ ++ if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { ++ if (b_request == ACCESSORY_START) { ++ dev->start_requested = 1; ++ schedule_delayed_work( ++ &dev->start_work, msecs_to_jiffies(10)); ++ value = 0; ++ } else if (b_request == ACCESSORY_SEND_STRING) { ++ dev->string_index = w_index; ++ cdev->gadget->ep0->driver_data = dev; ++ cdev->req->complete = acc_complete_set_string; ++ value = w_length; ++ } else if (b_request == ACCESSORY_SET_AUDIO_MODE && ++ w_index == 0 && w_length == 0) { ++ dev->audio_mode = w_value; ++ value = 0; ++ } else if (b_request == ACCESSORY_REGISTER_HID) { ++ value = acc_register_hid(dev, w_value, w_index); ++ } else if (b_request == ACCESSORY_UNREGISTER_HID) { ++ value = acc_unregister_hid(dev, w_value); ++ } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) { ++ spin_lock_irqsave(&dev->lock, flags); ++ hid = acc_hid_get(&dev->new_hid_list, w_value); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ if (!hid) { ++ value = -EINVAL; ++ goto err; ++ } ++ offset = w_index; ++ if (offset != hid->report_desc_offset ++ || offset + w_length > hid->report_desc_len) { ++ value = -EINVAL; ++ goto err; ++ } ++ cdev->req->context = hid; ++ cdev->req->complete = acc_complete_set_hid_report_desc; ++ value = w_length; ++ } else if (b_request == ACCESSORY_SEND_HID_EVENT) { ++ spin_lock_irqsave(&dev->lock, flags); ++ hid = acc_hid_get(&dev->hid_list, w_value); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ if (!hid) { ++ value = -EINVAL; ++ goto err; ++ } ++ cdev->req->context = hid; ++ cdev->req->complete = acc_complete_send_hid_event; ++ value = w_length; ++ } ++ } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { ++ if (b_request == ACCESSORY_GET_PROTOCOL) { ++ *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; ++ value = sizeof(u16); ++ ++ /* clear any string left over from a previous session */ ++ memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); ++ memset(dev->model, 0, sizeof(dev->model)); ++ memset(dev->description, 0, sizeof(dev->description)); ++ memset(dev->version, 0, sizeof(dev->version)); ++ memset(dev->uri, 0, sizeof(dev->uri)); ++ memset(dev->serial, 0, sizeof(dev->serial)); ++ dev->start_requested = 0; ++ dev->audio_mode = 0; ++ } ++ } ++ ++ if (value >= 0) { ++ cdev->req->zero = 0; ++ cdev->req->length = value; ++ value = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); ++ if (value < 0) ++ ERROR(cdev, "%s setup response queue error\n", ++ __func__); ++ } ++ ++err: ++ if (value == -EOPNOTSUPP) ++ VDBG(cdev, ++ "unknown class-specific control req " ++ "%02x.%02x v%04x i%04x l%u\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ return value; ++} ++EXPORT_SYMBOL_GPL(acc_ctrlrequest); ++ ++static int ++__acc_function_bind(struct usb_configuration *c, ++ struct usb_function *f, bool configfs) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct acc_dev *dev = func_to_dev(f); ++ int id; ++ int ret; ++ ++ DBG(cdev, "acc_function_bind dev: %p\n", dev); ++ ++ if (configfs) { ++ if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { ++ ret = usb_string_id(c->cdev); ++ if (ret < 0) ++ return ret; ++ acc_string_defs[INTERFACE_STRING_INDEX].id = ret; ++ acc_interface_desc.iInterface = ret; ++ } ++ dev->cdev = c->cdev; ++ } ++ ret = hid_register_driver(&acc_hid_driver); ++ if (ret) ++ return ret; ++ ++ dev->start_requested = 0; ++ ++ /* allocate interface ID(s) */ ++ id = usb_interface_id(c, f); ++ if (id < 0) ++ return id; ++ acc_interface_desc.bInterfaceNumber = id; ++ ++ /* allocate endpoints */ ++ ret = create_bulk_endpoints(dev, &acc_fullspeed_in_desc, ++ &acc_fullspeed_out_desc); ++ if (ret) ++ return ret; ++ ++ /* support high speed hardware */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ acc_highspeed_in_desc.bEndpointAddress = ++ acc_fullspeed_in_desc.bEndpointAddress; ++ acc_highspeed_out_desc.bEndpointAddress = ++ acc_fullspeed_out_desc.bEndpointAddress; ++ } ++ ++ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ f->name, dev->ep_in->name, dev->ep_out->name); ++ return 0; ++} ++ ++static int ++acc_function_bind(struct usb_configuration *c, struct usb_function *f) { ++ return __acc_function_bind(c, f, false); ++} ++ ++static int ++acc_function_bind_configfs(struct usb_configuration *c, ++ struct usb_function *f) { ++ return __acc_function_bind(c, f, true); ++} ++ ++static void ++kill_all_hid_devices(struct acc_dev *dev) ++{ ++ struct acc_hid_dev *hid; ++ struct list_head *entry, *temp; ++ unsigned long flags; ++ ++ /* do nothing if usb accessory device doesn't exist */ ++ if (!dev) ++ return; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ list_for_each_safe(entry, temp, &dev->hid_list) { ++ hid = list_entry(entry, struct acc_hid_dev, list); ++ list_del(&hid->list); ++ list_add(&hid->list, &dev->dead_hid_list); ++ } ++ list_for_each_safe(entry, temp, &dev->new_hid_list) { ++ hid = list_entry(entry, struct acc_hid_dev, list); ++ list_del(&hid->list); ++ list_add(&hid->list, &dev->dead_hid_list); ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ schedule_work(&dev->hid_work); ++} ++ ++static void ++acc_hid_unbind(struct acc_dev *dev) ++{ ++ hid_unregister_driver(&acc_hid_driver); ++ kill_all_hid_devices(dev); ++} ++ ++static void ++acc_function_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct acc_dev *dev = func_to_dev(f); ++ struct usb_request *req; ++ int i; ++ ++ while ((req = req_get(dev, &dev->tx_idle))) ++ acc_request_free(req, dev->ep_in); ++ for (i = 0; i < RX_REQ_MAX; i++) ++ acc_request_free(dev->rx_req[i], dev->ep_out); ++ ++ acc_hid_unbind(dev); ++} ++ ++static void acc_start_work(struct work_struct *data) ++{ ++ char *envp[2] = { "ACCESSORY=START", NULL }; ++ kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); ++} ++ ++static int acc_hid_init(struct acc_hid_dev *hdev) ++{ ++ struct hid_device *hid; ++ int ret; ++ ++ hid = hid_allocate_device(); ++ if (IS_ERR(hid)) ++ return PTR_ERR(hid); ++ ++ hid->ll_driver = &acc_hid_ll_driver; ++ hid->dev.parent = acc_device.this_device; ++ ++ hid->bus = BUS_USB; ++ hid->vendor = HID_ANY_ID; ++ hid->product = HID_ANY_ID; ++ hid->driver_data = hdev; ++ ret = hid_add_device(hid); ++ if (ret) { ++ pr_err("can't add hid device: %d\n", ret); ++ hid_destroy_device(hid); ++ return ret; ++ } ++ ++ hdev->hid = hid; ++ return 0; ++} ++ ++static void acc_hid_delete(struct acc_hid_dev *hid) ++{ ++ kfree(hid->report_desc); ++ kfree(hid); ++} ++ ++static void acc_hid_work(struct work_struct *data) ++{ ++ struct acc_dev *dev = _acc_dev; ++ struct list_head *entry, *temp; ++ struct acc_hid_dev *hid; ++ struct list_head new_list, dead_list; ++ unsigned long flags; ++ ++ INIT_LIST_HEAD(&new_list); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* copy hids that are ready for initialization to new_list */ ++ list_for_each_safe(entry, temp, &dev->new_hid_list) { ++ hid = list_entry(entry, struct acc_hid_dev, list); ++ if (hid->report_desc_offset == hid->report_desc_len) ++ list_move(&hid->list, &new_list); ++ } ++ ++ if (list_empty(&dev->dead_hid_list)) { ++ INIT_LIST_HEAD(&dead_list); ++ } else { ++ /* move all of dev->dead_hid_list to dead_list */ ++ dead_list.prev = dev->dead_hid_list.prev; ++ dead_list.next = dev->dead_hid_list.next; ++ dead_list.next->prev = &dead_list; ++ dead_list.prev->next = &dead_list; ++ INIT_LIST_HEAD(&dev->dead_hid_list); ++ } ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ /* register new HID devices */ ++ list_for_each_safe(entry, temp, &new_list) { ++ hid = list_entry(entry, struct acc_hid_dev, list); ++ if (acc_hid_init(hid)) { ++ pr_err("can't add HID device %p\n", hid); ++ acc_hid_delete(hid); ++ } else { ++ spin_lock_irqsave(&dev->lock, flags); ++ list_move(&hid->list, &dev->hid_list); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ } ++ } ++ ++ /* remove dead HID devices */ ++ list_for_each_safe(entry, temp, &dead_list) { ++ hid = list_entry(entry, struct acc_hid_dev, list); ++ list_del(&hid->list); ++ if (hid->hid) ++ hid_destroy_device(hid->hid); ++ acc_hid_delete(hid); ++ } ++} ++ ++static int acc_function_set_alt(struct usb_function *f, ++ unsigned intf, unsigned alt) ++{ ++ struct acc_dev *dev = func_to_dev(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ int ret; ++ ++ DBG(cdev, "acc_function_set_alt intf: %d alt: %d\n", intf, alt); ++ ++ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); ++ if (ret) ++ return ret; ++ ++ ret = usb_ep_enable(dev->ep_in); ++ if (ret) ++ return ret; ++ ++ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); ++ if (ret) ++ return ret; ++ ++ ret = usb_ep_enable(dev->ep_out); ++ if (ret) { ++ usb_ep_disable(dev->ep_in); ++ return ret; ++ } ++ ++ dev->online = 1; ++ ++ /* readers may be blocked waiting for us to go online */ ++ wake_up(&dev->read_wq); ++ return 0; ++} ++ ++static void acc_function_disable(struct usb_function *f) ++{ ++ struct acc_dev *dev = func_to_dev(f); ++ struct usb_composite_dev *cdev = dev->cdev; ++ ++ DBG(cdev, "acc_function_disable\n"); ++ acc_set_disconnected(dev); ++ usb_ep_disable(dev->ep_in); ++ usb_ep_disable(dev->ep_out); ++ ++ /* readers may be blocked waiting for us to go online */ ++ wake_up(&dev->read_wq); ++ ++ VDBG(cdev, "%s disabled\n", dev->function.name); ++} ++ ++static int __maybe_unused acc_bind_config(struct usb_configuration *c) ++{ ++ struct acc_dev *dev = _acc_dev; ++ int ret; ++ ++ printk(KERN_INFO "acc_bind_config\n"); ++ ++ /* allocate a string ID for our interface */ ++ if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { ++ ret = usb_string_id(c->cdev); ++ if (ret < 0) ++ return ret; ++ acc_string_defs[INTERFACE_STRING_INDEX].id = ret; ++ acc_interface_desc.iInterface = ret; ++ } ++ ++ dev->cdev = c->cdev; ++ dev->function.name = "accessory"; ++ dev->function.strings = acc_strings, ++ dev->function.fs_descriptors = fs_acc_descs; ++ dev->function.hs_descriptors = hs_acc_descs; ++ dev->function.bind = acc_function_bind; ++ dev->function.unbind = acc_function_unbind; ++ dev->function.set_alt = acc_function_set_alt; ++ dev->function.disable = acc_function_disable; ++ ++ return usb_add_function(c, &dev->function); ++} ++ ++static int acc_setup(void) ++{ ++ struct acc_dev *dev; ++ int ret; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ spin_lock_init(&dev->lock); ++ init_waitqueue_head(&dev->read_wq); ++ init_waitqueue_head(&dev->write_wq); ++ atomic_set(&dev->open_excl, 0); ++ INIT_LIST_HEAD(&dev->tx_idle); ++ INIT_LIST_HEAD(&dev->hid_list); ++ INIT_LIST_HEAD(&dev->new_hid_list); ++ INIT_LIST_HEAD(&dev->dead_hid_list); ++ INIT_DELAYED_WORK(&dev->start_work, acc_start_work); ++ INIT_WORK(&dev->hid_work, acc_hid_work); ++ ++ /* _acc_dev must be set before calling usb_gadget_register_driver */ ++ _acc_dev = dev; ++ ++ ret = misc_register(&acc_device); ++ if (ret) ++ goto err; ++ ++ return 0; ++ ++err: ++ kfree(dev); ++ pr_err("USB accessory gadget driver failed to initialize\n"); ++ return ret; ++} ++ ++void acc_disconnect(void) ++{ ++ /* unregister all HID devices if USB is disconnected */ ++ kill_all_hid_devices(_acc_dev); ++} ++EXPORT_SYMBOL_GPL(acc_disconnect); ++ ++static void acc_cleanup(void) ++{ ++ misc_deregister(&acc_device); ++ kfree(_acc_dev); ++ _acc_dev = NULL; ++} ++static struct acc_instance *to_acc_instance(struct config_item *item) ++{ ++ return container_of(to_config_group(item), struct acc_instance, ++ func_inst.group); ++} ++ ++static void acc_attr_release(struct config_item *item) ++{ ++ struct acc_instance *fi_acc = to_acc_instance(item); ++ ++ usb_put_function_instance(&fi_acc->func_inst); ++} ++ ++static struct configfs_item_operations acc_item_ops = { ++ .release = acc_attr_release, ++}; ++ ++static struct config_item_type acc_func_type = { ++ .ct_item_ops = &acc_item_ops, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static struct acc_instance *to_fi_acc(struct usb_function_instance *fi) ++{ ++ return container_of(fi, struct acc_instance, func_inst); ++} ++ ++static int acc_set_inst_name(struct usb_function_instance *fi, const char *name) ++{ ++ struct acc_instance *fi_acc; ++ char *ptr; ++ int name_len; ++ ++ name_len = strlen(name) + 1; ++ if (name_len > MAX_INST_NAME_LEN) ++ return -ENAMETOOLONG; ++ ++ ptr = kstrndup(name, name_len, GFP_KERNEL); ++ if (!ptr) ++ return -ENOMEM; ++ ++ fi_acc = to_fi_acc(fi); ++ fi_acc->name = ptr; ++ return 0; ++} ++ ++static void acc_free_inst(struct usb_function_instance *fi) ++{ ++ struct acc_instance *fi_acc; ++ ++ fi_acc = to_fi_acc(fi); ++ kfree(fi_acc->name); ++ acc_cleanup(); ++} ++ ++static struct usb_function_instance *acc_alloc_inst(void) ++{ ++ struct acc_instance *fi_acc; ++ struct acc_dev *dev; ++ int err; ++ ++ fi_acc = kzalloc(sizeof(*fi_acc), GFP_KERNEL); ++ if (!fi_acc) ++ return ERR_PTR(-ENOMEM); ++ fi_acc->func_inst.set_inst_name = acc_set_inst_name; ++ fi_acc->func_inst.free_func_inst = acc_free_inst; ++ ++ err = acc_setup(); ++ if (err) { ++ kfree(fi_acc); ++ pr_err("Error setting ACCESSORY\n"); ++ return ERR_PTR(err); ++ } ++ ++ config_group_init_type_name(&fi_acc->func_inst.group, ++ "", &acc_func_type); ++ dev = _acc_dev; ++ return &fi_acc->func_inst; ++} ++ ++static void acc_free(struct usb_function *f) ++{ ++/*NO-OP: no function specific resource allocation in mtp_alloc*/ ++} ++ ++int acc_ctrlrequest_configfs(struct usb_function *f, ++ const struct usb_ctrlrequest *ctrl) { ++ if (f->config != NULL && f->config->cdev != NULL) ++ return acc_ctrlrequest(f->config->cdev, ctrl); ++ else ++ return -1; ++} ++ ++static struct usb_function *acc_alloc(struct usb_function_instance *fi) ++{ ++ struct acc_dev *dev = _acc_dev; ++ ++ pr_info("acc_alloc\n"); ++ ++ dev->function.name = "accessory"; ++ dev->function.strings = acc_strings, ++ dev->function.fs_descriptors = fs_acc_descs; ++ dev->function.hs_descriptors = hs_acc_descs; ++ dev->function.bind = acc_function_bind_configfs; ++ dev->function.unbind = acc_function_unbind; ++ dev->function.set_alt = acc_function_set_alt; ++ dev->function.disable = acc_function_disable; ++ dev->function.free_func = acc_free; ++ dev->function.setup = acc_ctrlrequest_configfs; ++ ++ return &dev->function; ++} ++DECLARE_USB_FUNCTION_INIT(accessory, acc_alloc_inst, acc_alloc); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c +new file mode 100644 +index 0000000..39645be +--- /dev/null ++++ b/drivers/usb/gadget/function/f_audio_source.c +@@ -0,0 +1,1054 @@ ++/* ++ * Gadget Function Driver for USB audio source device ++ * ++ * Copyright (C) 2012 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/device.h> ++#include <linux/usb/audio.h> ++#include <linux/wait.h> ++#include <sound/core.h> ++#include <sound/initval.h> ++#include <sound/pcm.h> ++ ++#include <linux/usb.h> ++#include <linux/usb_usual.h> ++#include <linux/usb/ch9.h> ++#include <linux/configfs.h> ++#include <linux/usb/composite.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#define SAMPLE_RATE 44100 ++#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) ++ ++#define IN_EP_MAX_PACKET_SIZE 256 ++ ++/* Number of requests to allocate */ ++#define IN_EP_REQ_COUNT 4 ++ ++#define AUDIO_AC_INTERFACE 0 ++#define AUDIO_AS_INTERFACE 1 ++#define AUDIO_NUM_INTERFACES 2 ++#define MAX_INST_NAME_LEN 40 ++ ++/* B.3.1 Standard AC Interface Descriptor */ ++static struct usb_interface_descriptor ac_interface_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bNumEndpoints = 0, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, ++}; ++ ++DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); ++ ++#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES) ++/* 1 input terminal, 1 output terminal and 1 feature unit */ ++#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ ++ + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \ ++ + UAC_DT_FEATURE_UNIT_SIZE(0)) ++/* B.3.2 Class-Specific AC Interface Descriptor */ ++static struct uac1_ac_header_descriptor_2 ac_header_desc = { ++ .bLength = UAC_DT_AC_HEADER_LENGTH, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_HEADER, ++ .bcdADC = __constant_cpu_to_le16(0x0100), ++ .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), ++ .bInCollection = AUDIO_NUM_INTERFACES, ++ .baInterfaceNr = { ++ [0] = AUDIO_AC_INTERFACE, ++ [1] = AUDIO_AS_INTERFACE, ++ } ++}; ++ ++#define INPUT_TERMINAL_ID 1 ++static struct uac_input_terminal_descriptor input_terminal_desc = { ++ .bLength = UAC_DT_INPUT_TERMINAL_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_INPUT_TERMINAL, ++ .bTerminalID = INPUT_TERMINAL_ID, ++ .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE, ++ .bAssocTerminal = 0, ++ .wChannelConfig = 0x3, ++}; ++ ++DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); ++ ++#define FEATURE_UNIT_ID 2 ++static struct uac_feature_unit_descriptor_0 feature_unit_desc = { ++ .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_FEATURE_UNIT, ++ .bUnitID = FEATURE_UNIT_ID, ++ .bSourceID = INPUT_TERMINAL_ID, ++ .bControlSize = 2, ++}; ++ ++#define OUTPUT_TERMINAL_ID 3 ++static struct uac1_output_terminal_descriptor output_terminal_desc = { ++ .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, ++ .bTerminalID = OUTPUT_TERMINAL_ID, ++ .wTerminalType = UAC_TERMINAL_STREAMING, ++ .bAssocTerminal = FEATURE_UNIT_ID, ++ .bSourceID = FEATURE_UNIT_ID, ++}; ++ ++/* B.4.1 Standard AS Interface Descriptor */ ++static struct usb_interface_descriptor as_interface_alt_0_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 0, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, ++}; ++ ++static struct usb_interface_descriptor as_interface_alt_1_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bAlternateSetting = 1, ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, ++}; ++ ++/* B.4.2 Class-Specific AS Interface Descriptor */ ++static struct uac1_as_header_descriptor as_header_desc = { ++ .bLength = UAC_DT_AS_HEADER_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_AS_GENERAL, ++ .bTerminalLink = INPUT_TERMINAL_ID, ++ .bDelay = 1, ++ .wFormatTag = UAC_FORMAT_TYPE_I_PCM, ++}; ++ ++DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); ++ ++static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { ++ .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_FORMAT_TYPE, ++ .bFormatType = UAC_FORMAT_TYPE_I, ++ .bSubframeSize = 2, ++ .bBitResolution = 16, ++ .bSamFreqType = 1, ++}; ++ ++/* Standard ISO IN Endpoint Descriptor for highspeed */ ++static struct usb_endpoint_descriptor hs_as_in_ep_desc = { ++ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_SYNC_SYNC ++ | USB_ENDPOINT_XFER_ISOC, ++ .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), ++ .bInterval = 4, /* poll 1 per millisecond */ ++}; ++ ++/* Standard ISO IN Endpoint Descriptor for highspeed */ ++static struct usb_endpoint_descriptor fs_as_in_ep_desc = { ++ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_SYNC_SYNC ++ | USB_ENDPOINT_XFER_ISOC, ++ .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), ++ .bInterval = 1, /* poll 1 per millisecond */ ++}; ++ ++/* Class-specific AS ISO OUT Endpoint Descriptor */ ++static struct uac_iso_endpoint_descriptor as_iso_in_desc = { ++ .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, ++ .bDescriptorType = USB_DT_CS_ENDPOINT, ++ .bDescriptorSubtype = UAC_EP_GENERAL, ++ .bmAttributes = 1, ++ .bLockDelayUnits = 1, ++ .wLockDelay = __constant_cpu_to_le16(1), ++}; ++ ++static struct usb_descriptor_header *hs_audio_desc[] = { ++ (struct usb_descriptor_header *)&ac_interface_desc, ++ (struct usb_descriptor_header *)&ac_header_desc, ++ ++ (struct usb_descriptor_header *)&input_terminal_desc, ++ (struct usb_descriptor_header *)&output_terminal_desc, ++ (struct usb_descriptor_header *)&feature_unit_desc, ++ ++ (struct usb_descriptor_header *)&as_interface_alt_0_desc, ++ (struct usb_descriptor_header *)&as_interface_alt_1_desc, ++ (struct usb_descriptor_header *)&as_header_desc, ++ ++ (struct usb_descriptor_header *)&as_type_i_desc, ++ ++ (struct usb_descriptor_header *)&hs_as_in_ep_desc, ++ (struct usb_descriptor_header *)&as_iso_in_desc, ++ NULL, ++}; ++ ++static struct usb_descriptor_header *fs_audio_desc[] = { ++ (struct usb_descriptor_header *)&ac_interface_desc, ++ (struct usb_descriptor_header *)&ac_header_desc, ++ ++ (struct usb_descriptor_header *)&input_terminal_desc, ++ (struct usb_descriptor_header *)&output_terminal_desc, ++ (struct usb_descriptor_header *)&feature_unit_desc, ++ ++ (struct usb_descriptor_header *)&as_interface_alt_0_desc, ++ (struct usb_descriptor_header *)&as_interface_alt_1_desc, ++ (struct usb_descriptor_header *)&as_header_desc, ++ ++ (struct usb_descriptor_header *)&as_type_i_desc, ++ ++ (struct usb_descriptor_header *)&fs_as_in_ep_desc, ++ (struct usb_descriptor_header *)&as_iso_in_desc, ++ NULL, ++}; ++ ++static struct snd_pcm_hardware audio_hw_info = { ++ .info = SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_BATCH | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER, ++ ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ .channels_min = 2, ++ .channels_max = 2, ++ .rate_min = SAMPLE_RATE, ++ .rate_max = SAMPLE_RATE, ++ ++ .buffer_bytes_max = 1024 * 1024, ++ .period_bytes_min = 64, ++ .period_bytes_max = 512 * 1024, ++ .periods_min = 2, ++ .periods_max = 1024, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++struct audio_source_config { ++ int card; ++ int device; ++}; ++ ++struct audio_dev { ++ struct usb_function func; ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ struct snd_pcm_substream *substream; ++ ++ struct list_head idle_reqs; ++ struct usb_ep *in_ep; ++ ++ spinlock_t lock; ++ ++ /* beginning, end and current position in our buffer */ ++ void *buffer_start; ++ void *buffer_end; ++ void *buffer_pos; ++ ++ /* byte size of a "period" */ ++ unsigned int period; ++ /* bytes sent since last call to snd_pcm_period_elapsed */ ++ unsigned int period_offset; ++ /* time we started playing */ ++ ktime_t start_time; ++ /* number of frames sent since start_time */ ++ s64 frames_sent; ++ struct audio_source_config *config; ++}; ++ ++static inline struct audio_dev *func_to_audio(struct usb_function *f) ++{ ++ return container_of(f, struct audio_dev, func); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++struct audio_source_instance { ++ struct usb_function_instance func_inst; ++ const char *name; ++ struct audio_source_config *config; ++ struct device *audio_device; ++}; ++ ++static void audio_source_attr_release(struct config_item *item); ++ ++static struct configfs_item_operations audio_source_item_ops = { ++ .release = audio_source_attr_release, ++}; ++ ++static struct config_item_type audio_source_func_type = { ++ .ct_item_ops = &audio_source_item_ops, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static ssize_t audio_source_pcm_show(struct device *dev, ++ struct device_attribute *attr, char *buf); ++ ++static DEVICE_ATTR(pcm, S_IRUGO, audio_source_pcm_show, NULL); ++ ++static struct device_attribute *audio_source_function_attributes[] = { ++ &dev_attr_pcm, ++ NULL ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size) ++{ ++ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); ++ if (!req) ++ return NULL; ++ ++ req->buf = kmalloc(buffer_size, GFP_KERNEL); ++ if (!req->buf) { ++ usb_ep_free_request(ep, req); ++ return NULL; ++ } ++ req->length = buffer_size; ++ return req; ++} ++ ++static void audio_request_free(struct usb_request *req, struct usb_ep *ep) ++{ ++ if (req) { ++ kfree(req->buf); ++ usb_ep_free_request(ep, req); ++ } ++} ++ ++static void audio_req_put(struct audio_dev *audio, struct usb_request *req) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&audio->lock, flags); ++ list_add_tail(&req->list, &audio->idle_reqs); ++ spin_unlock_irqrestore(&audio->lock, flags); ++} ++ ++static struct usb_request *audio_req_get(struct audio_dev *audio) ++{ ++ unsigned long flags; ++ struct usb_request *req; ++ ++ spin_lock_irqsave(&audio->lock, flags); ++ if (list_empty(&audio->idle_reqs)) { ++ req = 0; ++ } else { ++ req = list_first_entry(&audio->idle_reqs, struct usb_request, ++ list); ++ list_del(&req->list); ++ } ++ spin_unlock_irqrestore(&audio->lock, flags); ++ return req; ++} ++ ++/* send the appropriate number of packets to match our bitrate */ ++static void audio_send(struct audio_dev *audio) ++{ ++ struct snd_pcm_runtime *runtime; ++ struct usb_request *req; ++ int length, length1, length2, ret; ++ s64 msecs; ++ s64 frames; ++ ktime_t now; ++ ++ /* audio->substream will be null if we have been closed */ ++ if (!audio->substream) ++ return; ++ /* audio->buffer_pos will be null if we have been stopped */ ++ if (!audio->buffer_pos) ++ return; ++ ++ runtime = audio->substream->runtime; ++ ++ /* compute number of frames to send */ ++ now = ktime_get(); ++ msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time); ++ do_div(msecs, 1000000); ++ frames = msecs * SAMPLE_RATE; ++ do_div(frames, 1000); ++ ++ /* Readjust our frames_sent if we fall too far behind. ++ * If we get too far behind it is better to drop some frames than ++ * to keep sending data too fast in an attempt to catch up. ++ */ ++ if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC) ++ audio->frames_sent = frames - FRAMES_PER_MSEC; ++ ++ frames -= audio->frames_sent; ++ ++ /* We need to send something to keep the pipeline going */ ++ if (frames <= 0) ++ frames = FRAMES_PER_MSEC; ++ ++ while (frames > 0) { ++ req = audio_req_get(audio); ++ if (!req) ++ break; ++ ++ length = frames_to_bytes(runtime, frames); ++ if (length > IN_EP_MAX_PACKET_SIZE) ++ length = IN_EP_MAX_PACKET_SIZE; ++ ++ if (audio->buffer_pos + length > audio->buffer_end) ++ length1 = audio->buffer_end - audio->buffer_pos; ++ else ++ length1 = length; ++ memcpy(req->buf, audio->buffer_pos, length1); ++ if (length1 < length) { ++ /* Wrap around and copy remaining length ++ * at beginning of buffer. ++ */ ++ length2 = length - length1; ++ memcpy(req->buf + length1, audio->buffer_start, ++ length2); ++ audio->buffer_pos = audio->buffer_start + length2; ++ } else { ++ audio->buffer_pos += length1; ++ if (audio->buffer_pos >= audio->buffer_end) ++ audio->buffer_pos = audio->buffer_start; ++ } ++ ++ req->length = length; ++ ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC); ++ if (ret < 0) { ++ pr_err("usb_ep_queue failed ret: %d\n", ret); ++ audio_req_put(audio, req); ++ break; ++ } ++ ++ frames -= bytes_to_frames(runtime, length); ++ audio->frames_sent += bytes_to_frames(runtime, length); ++ } ++} ++ ++static void audio_control_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ /* nothing to do here */ ++} ++ ++static void audio_data_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct audio_dev *audio = req->context; ++ ++ pr_debug("audio_data_complete req->status %d req->actual %d\n", ++ req->status, req->actual); ++ ++ audio_req_put(audio, req); ++ ++ if (!audio->buffer_start || req->status) ++ return; ++ ++ audio->period_offset += req->actual; ++ if (audio->period_offset >= audio->period) { ++ snd_pcm_period_elapsed(audio->substream); ++ audio->period_offset = 0; ++ } ++ audio_send(audio); ++} ++ ++static int audio_set_endpoint_req(struct usb_function *f, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ int value = -EOPNOTSUPP; ++ u16 ep = le16_to_cpu(ctrl->wIndex); ++ u16 len = le16_to_cpu(ctrl->wLength); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ ++ pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", ++ ctrl->bRequest, w_value, len, ep); ++ ++ switch (ctrl->bRequest) { ++ case UAC_SET_CUR: ++ case UAC_SET_MIN: ++ case UAC_SET_MAX: ++ case UAC_SET_RES: ++ value = len; ++ break; ++ default: ++ break; ++ } ++ ++ return value; ++} ++ ++static int audio_get_endpoint_req(struct usb_function *f, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ struct usb_composite_dev *cdev = f->config->cdev; ++ int value = -EOPNOTSUPP; ++ u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); ++ u16 len = le16_to_cpu(ctrl->wLength); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u8 *buf = cdev->req->buf; ++ ++ pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", ++ ctrl->bRequest, w_value, len, ep); ++ ++ if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) { ++ switch (ctrl->bRequest) { ++ case UAC_GET_CUR: ++ case UAC_GET_MIN: ++ case UAC_GET_MAX: ++ case UAC_GET_RES: ++ /* return our sample rate */ ++ buf[0] = (u8)SAMPLE_RATE; ++ buf[1] = (u8)(SAMPLE_RATE >> 8); ++ buf[2] = (u8)(SAMPLE_RATE >> 16); ++ value = 3; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return value; ++} ++ ++static int ++audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) ++{ ++ struct usb_composite_dev *cdev = f->config->cdev; ++ struct usb_request *req = cdev->req; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ ++ /* composite driver infrastructure handles everything; interface ++ * activation uses set_alt(). ++ */ ++ switch (ctrl->bRequestType) { ++ case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: ++ value = audio_set_endpoint_req(f, ctrl); ++ break; ++ ++ case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: ++ value = audio_get_endpoint_req(f, ctrl); ++ break; ++ } ++ ++ /* respond with data transfer or status phase? */ ++ if (value >= 0) { ++ pr_debug("audio req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ req->zero = 0; ++ req->length = value; ++ req->complete = audio_control_complete; ++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) ++ pr_err("audio response on err %d\n", value); ++ } ++ ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct audio_dev *audio = func_to_audio(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ int ret; ++ ++ pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt); ++ ++ ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep); ++ if (ret) ++ return ret; ++ ++ usb_ep_enable(audio->in_ep); ++ return 0; ++} ++ ++static void audio_disable(struct usb_function *f) ++{ ++ struct audio_dev *audio = func_to_audio(f); ++ ++ pr_debug("audio_disable\n"); ++ usb_ep_disable(audio->in_ep); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void audio_build_desc(struct audio_dev *audio) ++{ ++ u8 *sam_freq; ++ int rate; ++ ++ /* Set channel numbers */ ++ input_terminal_desc.bNrChannels = 2; ++ as_type_i_desc.bNrChannels = 2; ++ ++ /* Set sample rates */ ++ rate = SAMPLE_RATE; ++ sam_freq = as_type_i_desc.tSamFreq[0]; ++ memcpy(sam_freq, &rate, 3); ++} ++ ++ ++static int snd_card_setup(struct usb_configuration *c, ++ struct audio_source_config *config); ++static struct audio_source_instance *to_fi_audio_source( ++ const struct usb_function_instance *fi); ++ ++ ++/* audio function driver setup/binding */ ++static int ++audio_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct audio_dev *audio = func_to_audio(f); ++ int status; ++ struct usb_ep *ep; ++ struct usb_request *req; ++ int i; ++ int err; ++ ++ if (IS_ENABLED(CONFIG_USB_CONFIGFS)) { ++ struct audio_source_instance *fi_audio = ++ to_fi_audio_source(f->fi); ++ struct audio_source_config *config = ++ fi_audio->config; ++ ++ err = snd_card_setup(c, config); ++ if (err) ++ return err; ++ } ++ ++ audio_build_desc(audio); ++ ++ /* allocate instance-specific interface IDs, and patch descriptors */ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ ac_interface_desc.bInterfaceNumber = status; ++ ++ /* AUDIO_AC_INTERFACE */ ++ ac_header_desc.baInterfaceNr[0] = status; ++ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ as_interface_alt_0_desc.bInterfaceNumber = status; ++ as_interface_alt_1_desc.bInterfaceNumber = status; ++ ++ /* AUDIO_AS_INTERFACE */ ++ ac_header_desc.baInterfaceNr[1] = status; ++ ++ status = -ENODEV; ++ ++ /* allocate our endpoint */ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc); ++ if (!ep) ++ goto fail; ++ audio->in_ep = ep; ++ ep->driver_data = audio; /* claim */ ++ ++ if (gadget_is_dualspeed(c->cdev->gadget)) ++ hs_as_in_ep_desc.bEndpointAddress = ++ fs_as_in_ep_desc.bEndpointAddress; ++ ++ f->fs_descriptors = fs_audio_desc; ++ f->hs_descriptors = hs_audio_desc; ++ ++ for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) { ++ req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE); ++ if (req) { ++ req->context = audio; ++ req->complete = audio_data_complete; ++ audio_req_put(audio, req); ++ } else ++ status = -ENOMEM; ++ } ++ ++fail: ++ return status; ++} ++ ++static void ++audio_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct audio_dev *audio = func_to_audio(f); ++ struct usb_request *req; ++ ++ while ((req = audio_req_get(audio))) ++ audio_request_free(req, audio->in_ep); ++ ++ snd_card_free_when_closed(audio->card); ++ audio->card = NULL; ++ audio->pcm = NULL; ++ audio->substream = NULL; ++ audio->in_ep = NULL; ++ ++ if (IS_ENABLED(CONFIG_USB_CONFIGFS)) { ++ struct audio_source_instance *fi_audio = ++ to_fi_audio_source(f->fi); ++ struct audio_source_config *config = ++ fi_audio->config; ++ ++ config->card = -1; ++ config->device = -1; ++ } ++} ++ ++static void audio_pcm_playback_start(struct audio_dev *audio) ++{ ++ audio->start_time = ktime_get(); ++ audio->frames_sent = 0; ++ audio_send(audio); ++} ++ ++static void audio_pcm_playback_stop(struct audio_dev *audio) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&audio->lock, flags); ++ audio->buffer_start = 0; ++ audio->buffer_end = 0; ++ audio->buffer_pos = 0; ++ spin_unlock_irqrestore(&audio->lock, flags); ++} ++ ++static int audio_pcm_open(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct audio_dev *audio = substream->private_data; ++ ++ runtime->private_data = audio; ++ runtime->hw = audio_hw_info; ++ snd_pcm_limit_hw_rates(runtime); ++ runtime->hw.channels_max = 2; ++ ++ audio->substream = substream; ++ return 0; ++} ++ ++static int audio_pcm_close(struct snd_pcm_substream *substream) ++{ ++ struct audio_dev *audio = substream->private_data; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&audio->lock, flags); ++ audio->substream = NULL; ++ spin_unlock_irqrestore(&audio->lock, flags); ++ ++ return 0; ++} ++ ++static int audio_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ unsigned int channels = params_channels(params); ++ unsigned int rate = params_rate(params); ++ ++ if (rate != SAMPLE_RATE) ++ return -EINVAL; ++ if (channels != 2) ++ return -EINVAL; ++ ++ return snd_pcm_lib_alloc_vmalloc_buffer(substream, ++ params_buffer_bytes(params)); ++} ++ ++static int audio_pcm_hw_free(struct snd_pcm_substream *substream) ++{ ++ return snd_pcm_lib_free_vmalloc_buffer(substream); ++} ++ ++static int audio_pcm_prepare(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct audio_dev *audio = runtime->private_data; ++ ++ audio->period = snd_pcm_lib_period_bytes(substream); ++ audio->period_offset = 0; ++ audio->buffer_start = runtime->dma_area; ++ audio->buffer_end = audio->buffer_start ++ + snd_pcm_lib_buffer_bytes(substream); ++ audio->buffer_pos = audio->buffer_start; ++ ++ return 0; ++} ++ ++static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct audio_dev *audio = runtime->private_data; ++ ssize_t bytes = audio->buffer_pos - audio->buffer_start; ++ ++ /* return offset of next frame to fill in our buffer */ ++ return bytes_to_frames(runtime, bytes); ++} ++ ++static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream, ++ int cmd) ++{ ++ struct audio_dev *audio = substream->runtime->private_data; ++ int ret = 0; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ audio_pcm_playback_start(audio); ++ break; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ audio_pcm_playback_stop(audio); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static struct audio_dev _audio_dev = { ++ .func = { ++ .name = "audio_source", ++ .bind = audio_bind, ++ .unbind = audio_unbind, ++ .set_alt = audio_set_alt, ++ .setup = audio_setup, ++ .disable = audio_disable, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock), ++ .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs), ++}; ++ ++static struct snd_pcm_ops audio_playback_ops = { ++ .open = audio_pcm_open, ++ .close = audio_pcm_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = audio_pcm_hw_params, ++ .hw_free = audio_pcm_hw_free, ++ .prepare = audio_pcm_prepare, ++ .trigger = audio_pcm_playback_trigger, ++ .pointer = audio_pcm_pointer, ++}; ++ ++int audio_source_bind_config(struct usb_configuration *c, ++ struct audio_source_config *config) ++{ ++ struct audio_dev *audio; ++ int err; ++ ++ config->card = -1; ++ config->device = -1; ++ ++ audio = &_audio_dev; ++ ++ err = snd_card_setup(c, config); ++ if (err) ++ return err; ++ ++ err = usb_add_function(c, &audio->func); ++ if (err) ++ goto add_fail; ++ ++ return 0; ++ ++add_fail: ++ snd_card_free(audio->card); ++ return err; ++} ++ ++static int snd_card_setup(struct usb_configuration *c, ++ struct audio_source_config *config) ++{ ++ struct audio_dev *audio; ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ int err; ++ ++ audio = &_audio_dev; ++ ++ err = snd_card_new(&c->cdev->gadget->dev, ++ SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, ++ THIS_MODULE, 0, &card); ++ if (err) ++ return err; ++ ++ err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm); ++ if (err) ++ goto pcm_fail; ++ ++ pcm->private_data = audio; ++ pcm->info_flags = 0; ++ audio->pcm = pcm; ++ ++ strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name)); ++ ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops); ++ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, ++ NULL, 0, 64 * 1024); ++ ++ strlcpy(card->driver, "audio_source", sizeof(card->driver)); ++ strlcpy(card->shortname, card->driver, sizeof(card->shortname)); ++ strlcpy(card->longname, "USB accessory audio source", ++ sizeof(card->longname)); ++ ++ err = snd_card_register(card); ++ if (err) ++ goto register_fail; ++ ++ config->card = pcm->card->number; ++ config->device = pcm->device; ++ audio->card = card; ++ return 0; ++ ++register_fail: ++pcm_fail: ++ snd_card_free(audio->card); ++ return err; ++} ++ ++static struct audio_source_instance *to_audio_source_instance( ++ struct config_item *item) ++{ ++ return container_of(to_config_group(item), struct audio_source_instance, ++ func_inst.group); ++} ++ ++static struct audio_source_instance *to_fi_audio_source( ++ const struct usb_function_instance *fi) ++{ ++ return container_of(fi, struct audio_source_instance, func_inst); ++} ++ ++static void audio_source_attr_release(struct config_item *item) ++{ ++ struct audio_source_instance *fi_audio = to_audio_source_instance(item); ++ ++ usb_put_function_instance(&fi_audio->func_inst); ++} ++ ++static int audio_source_set_inst_name(struct usb_function_instance *fi, ++ const char *name) ++{ ++ struct audio_source_instance *fi_audio; ++ char *ptr; ++ int name_len; ++ ++ name_len = strlen(name) + 1; ++ if (name_len > MAX_INST_NAME_LEN) ++ return -ENAMETOOLONG; ++ ++ ptr = kstrndup(name, name_len, GFP_KERNEL); ++ if (!ptr) ++ return -ENOMEM; ++ ++ fi_audio = to_fi_audio_source(fi); ++ fi_audio->name = ptr; ++ ++ return 0; ++} ++ ++static void audio_source_free_inst(struct usb_function_instance *fi) ++{ ++ struct audio_source_instance *fi_audio; ++ ++ fi_audio = to_fi_audio_source(fi); ++ device_destroy(fi_audio->audio_device->class, ++ fi_audio->audio_device->devt); ++ kfree(fi_audio->name); ++ kfree(fi_audio->config); ++} ++ ++static ssize_t audio_source_pcm_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct audio_source_instance *fi_audio = dev_get_drvdata(dev); ++ struct audio_source_config *config = fi_audio->config; ++ ++ /* print PCM card and device numbers */ ++ return sprintf(buf, "%d %d\n", config->card, config->device); ++} ++ ++struct device *create_function_device(char *name); ++ ++static struct usb_function_instance *audio_source_alloc_inst(void) ++{ ++ struct audio_source_instance *fi_audio; ++ struct device_attribute **attrs; ++ struct device_attribute *attr; ++ struct device *dev; ++ void *err_ptr; ++ int err = 0; ++ ++ fi_audio = kzalloc(sizeof(*fi_audio), GFP_KERNEL); ++ if (!fi_audio) ++ return ERR_PTR(-ENOMEM); ++ ++ fi_audio->func_inst.set_inst_name = audio_source_set_inst_name; ++ fi_audio->func_inst.free_func_inst = audio_source_free_inst; ++ ++ fi_audio->config = kzalloc(sizeof(struct audio_source_config), ++ GFP_KERNEL); ++ if (!fi_audio->config) { ++ err_ptr = ERR_PTR(-ENOMEM); ++ goto fail_audio; ++ } ++ ++ config_group_init_type_name(&fi_audio->func_inst.group, "", ++ &audio_source_func_type); ++ dev = create_function_device("f_audio_source"); ++ ++ if (IS_ERR(dev)) { ++ err_ptr = dev; ++ goto fail_audio_config; ++ } ++ ++ fi_audio->config->card = -1; ++ fi_audio->config->device = -1; ++ fi_audio->audio_device = dev; ++ ++ attrs = audio_source_function_attributes; ++ if (attrs) { ++ while ((attr = *attrs++) && !err) ++ err = device_create_file(dev, attr); ++ if (err) { ++ err_ptr = ERR_PTR(-EINVAL); ++ goto fail_device; ++ } ++ } ++ ++ dev_set_drvdata(dev, fi_audio); ++ _audio_dev.config = fi_audio->config; ++ ++ return &fi_audio->func_inst; ++ ++fail_device: ++ device_destroy(dev->class, dev->devt); ++fail_audio_config: ++ kfree(fi_audio->config); ++fail_audio: ++ kfree(fi_audio); ++ return err_ptr; ++ ++} ++ ++static struct usb_function *audio_source_alloc(struct usb_function_instance *fi) ++{ ++ return &_audio_dev.func; ++} ++ ++DECLARE_USB_FUNCTION_INIT(audio_source, audio_source_alloc_inst, ++ audio_source_alloc); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c +index ab9b7ac..45207e2 100644 +--- a/drivers/usb/gadget/function/f_fs.c ++++ b/drivers/usb/gadget/function/f_fs.c +@@ -86,12 +86,15 @@ ffs_setup_state_clear_cancelled(struct ffs_data *ffs) + cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP); + } + ++static void ffs_func_free(struct ffs_function *func); + + static void ffs_func_eps_disable(struct ffs_function *func); + static int __must_check ffs_func_eps_enable(struct ffs_function *func); + + static int ffs_func_bind(struct usb_configuration *, + struct usb_function *); ++static void old_ffs_func_unbind(struct usb_configuration *, ++ struct usb_function *); + static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned); + static void ffs_func_disable(struct usb_function *); + static int ffs_func_setup(struct usb_function *, +@@ -1616,6 +1619,71 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) + kfree(epfiles); + } + ++static int functionfs_bind_config(struct usb_composite_dev *cdev, ++ struct usb_configuration *c, ++ struct ffs_data *ffs) ++{ ++ struct ffs_function *func; ++ int ret; ++ ++ ENTER(); ++ ++ func = kzalloc(sizeof *func, GFP_KERNEL); ++ if (unlikely(!func)) ++ return -ENOMEM; ++ ++ func->function.name = "Function FS Gadget"; ++ func->function.strings = ffs->stringtabs; ++ ++ func->function.bind = ffs_func_bind; ++ func->function.unbind = old_ffs_func_unbind; ++ func->function.set_alt = ffs_func_set_alt; ++ func->function.disable = ffs_func_disable; ++ func->function.setup = ffs_func_setup; ++ func->function.suspend = ffs_func_suspend; ++ func->function.resume = ffs_func_resume; ++ ++ func->conf = c; ++ func->gadget = cdev->gadget; ++ func->ffs = ffs; ++ ffs_data_get(ffs); ++ ++ ret = usb_add_function(c, &func->function); ++ if (unlikely(ret)) ++ ffs_func_free(func); ++ ++ return ret; ++} ++ ++static void ffs_func_free(struct ffs_function *func) ++{ ++ struct ffs_ep *ep = func->eps; ++ unsigned count = func->ffs->eps_count; ++ unsigned long flags; ++ ++ ENTER(); ++ ++ /* cleanup after autoconfig */ ++ spin_lock_irqsave(&func->ffs->eps_lock, flags); ++ do { ++ if (ep->ep && ep->req) ++ usb_ep_free_request(ep->ep, ep->req); ++ ep->req = NULL; ++ ++ep; ++ } while (--count); ++ spin_unlock_irqrestore(&func->ffs->eps_lock, flags); ++ ++ ffs_data_put(func->ffs); ++ ++ kfree(func->eps); ++ /* ++ * eps and interfaces_nums are allocated in the same chunk so ++ * only one free is required. Descriptors are also allocated ++ * in the same chunk. ++ */ ++ ++ kfree(func); ++} + + static void ffs_func_eps_disable(struct ffs_function *func) + { +@@ -2888,6 +2956,24 @@ static int ffs_func_bind(struct usb_configuration *c, + + /* Other USB function hooks *************************************************/ + ++static void old_ffs_func_unbind(struct usb_configuration *c, ++ struct usb_function *f) ++{ ++ struct ffs_function *func = ffs_func_from_usb(f); ++ struct ffs_data *ffs = func->ffs; ++ ++ ENTER(); ++ ++ if (ffs->func == func) { ++ ffs_func_eps_disable(func); ++ ffs->func = NULL; ++ } ++ ++ ffs_event_add(ffs, FUNCTIONFS_UNBIND); ++ ++ ffs_func_free(func); ++} ++ + static int ffs_func_set_alt(struct usb_function *f, + unsigned interface, unsigned alt) + { +diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c +index 59ab62c..6f71a69 100644 +--- a/drivers/usb/gadget/function/f_hid.c ++++ b/drivers/usb/gadget/function/f_hid.c +@@ -373,8 +373,9 @@ static int hidg_setup(struct usb_function *f, + value = __le16_to_cpu(ctrl->wValue); + length = __le16_to_cpu(ctrl->wLength); + +- VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x " +- "Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value); ++ VDBG(cdev, ++ "%s crtl_request : bRequestType:0x%x bRequest:0x%x Value:0x%x\n", ++ __func__, ctrl->bRequestType, ctrl->bRequest, value); + + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 +diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c +index 811929c..e4d3f6a 100644 +--- a/drivers/usb/gadget/function/f_mass_storage.c ++++ b/drivers/usb/gadget/function/f_mass_storage.c +@@ -253,6 +253,7 @@ static struct usb_gadget_strings *fsg_strings_array[] = { + + struct fsg_dev; + struct fsg_common; ++extern void hisi_switch_func(int otg); + + /* Data shared by all the FSG instances. */ + struct fsg_common { +@@ -1935,6 +1936,9 @@ static int do_scsi_command(struct fsg_common *common) + "READ CAPACITY"); + if (reply == 0) + reply = do_read_capacity(common, bh); ++ ++ hisi_switch_func(1); ++ + break; + + case READ_HEADER: +@@ -1978,6 +1982,9 @@ static int do_scsi_command(struct fsg_common *common) + "REQUEST SENSE"); + if (reply == 0) + reply = do_request_sense(common, bh); ++ ++ hisi_switch_func(1); ++ + break; + + case START_STOP: +diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c +index 807b31c..ee1bfc9 100644 +--- a/drivers/usb/gadget/function/f_midi.c ++++ b/drivers/usb/gadget/function/f_midi.c +@@ -20,6 +20,7 @@ + */ + + #include <linux/kernel.h> ++#include <linux/module.h> + #include <linux/slab.h> + #include <linux/device.h> + +@@ -33,6 +34,7 @@ + #include <linux/usb/midi.h> + + #include "u_f.h" ++#include "u_midi.h" + + MODULE_AUTHOR("Ben Williamson"); + MODULE_LICENSE("GPL v2"); +@@ -99,7 +101,7 @@ DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); + DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16); + + /* B.3.1 Standard AC Interface Descriptor */ +-static struct usb_interface_descriptor ac_interface_desc __initdata = { ++static struct usb_interface_descriptor ac_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ +@@ -110,7 +112,7 @@ static struct usb_interface_descriptor ac_interface_desc __initdata = { + }; + + /* B.3.2 Class-Specific AC Interface Descriptor */ +-static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = { ++static struct uac1_ac_header_descriptor_1 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_HEADER, +@@ -121,7 +123,7 @@ static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = { + }; + + /* B.4.1 Standard MS Interface Descriptor */ +-static struct usb_interface_descriptor ms_interface_desc __initdata = { ++static struct usb_interface_descriptor ms_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ +@@ -132,7 +134,7 @@ static struct usb_interface_descriptor ms_interface_desc __initdata = { + }; + + /* B.4.2 Class-Specific MS Interface Descriptor */ +-static struct usb_ms_header_descriptor ms_header_desc __initdata = { ++static struct usb_ms_header_descriptor ms_header_desc = { + .bLength = USB_DT_MS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_HEADER, +@@ -327,6 +329,10 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) + unsigned i; + int err; + ++ /* For Control Device interface we do nothing */ ++ if (intf == 0) ++ return 0; ++ + err = f_midi_start_ep(midi, f, midi->in_ep); + if (err) + return err; +@@ -387,29 +393,6 @@ static void f_midi_disable(struct usb_function *f) + usb_ep_disable(midi->out_ep); + } + +-static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) +-{ +- struct usb_composite_dev *cdev = f->config->cdev; +- struct f_midi *midi = func_to_midi(f); +- struct snd_card *card; +- +- DBG(cdev, "unbind\n"); +- +- /* just to be sure */ +- f_midi_disable(f); +- +- card = midi->card; +- midi->card = NULL; +- if (card) +- snd_card_free(card); +- +- kfree(midi->id); +- midi->id = NULL; +- +- usb_free_all_descriptors(f); +- kfree(midi); +-} +- + static int f_midi_snd_free(struct snd_device *device) + { + return 0; +@@ -541,7 +524,7 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req) + req = midi_alloc_ep_req(ep, midi->buflen); + + if (!req) { +- ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n"); ++ ERROR(midi, "%s: alloc_ep_request failed\n", __func__); + return; + } + req->length = 0; +@@ -654,6 +637,14 @@ static struct snd_rawmidi_ops gmidi_out_ops = { + .trigger = f_midi_out_trigger + }; + ++static inline void f_midi_unregister_card(struct f_midi *midi) ++{ ++ if (midi->card) { ++ snd_card_free(midi->card); ++ midi->card = NULL; ++ } ++} ++ + /* register as a sound "card" */ + static int f_midi_register_card(struct f_midi *midi) + { +@@ -715,17 +706,13 @@ static int f_midi_register_card(struct f_midi *midi) + return 0; + + fail: +- if (midi->card) { +- snd_card_free(midi->card); +- midi->card = NULL; +- } ++ f_midi_unregister_card(midi); + return err; + } + + /* MIDI function driver setup/binding */ + +-static int __init +-f_midi_bind(struct usb_configuration *c, struct usb_function *f) ++static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) + { + struct usb_descriptor_header **midi_function; + struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS]; +@@ -734,15 +721,23 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f) + struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS]; + struct usb_composite_dev *cdev = c->cdev; + struct f_midi *midi = func_to_midi(f); ++ struct usb_string *us; + int status, n, jack = 1, i = 0; + ++ midi->gadget = cdev->gadget; ++ tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); ++ status = f_midi_register_card(midi); ++ if (status < 0) ++ goto fail_register; ++ + /* maybe allocate device-global string ID */ +- if (midi_string_defs[0].id == 0) { +- status = usb_string_id(c->cdev); +- if (status < 0) +- goto fail; +- midi_string_defs[0].id = status; ++ us = usb_gstrings_attach(c->cdev, midi_strings, ++ ARRAY_SIZE(midi_string_defs)); ++ if (IS_ERR(us)) { ++ status = PTR_ERR(us); ++ goto fail; + } ++ ac_interface_desc.iInterface = us[STRING_FUNC_IDX].id; + + /* We have two interfaces, AudioControl and MIDIStreaming */ + status = usb_interface_id(c, f); +@@ -892,6 +887,8 @@ fail_f_midi: + kfree(midi_function); + usb_free_descriptors(f->hs_descriptors); + fail: ++ f_midi_unregister_card(midi); ++fail_register: + /* we might as well release our claims on endpoints */ + if (midi->out_ep) + midi->out_ep->driver_data = NULL; +@@ -903,42 +900,305 @@ fail: + return status; + } + +-/** +- * f_midi_bind_config - add USB MIDI function to a configuration +- * @c: the configuration to supcard the USB audio function +- * @index: the soundcard index to use for the ALSA device creation +- * @id: the soundcard id to use for the ALSA device creation +- * @buflen: the buffer length to use +- * @qlen the number of read requests to pre-allocate +- * Context: single threaded during gadget setup +- * +- * Returns zero on success, else negative errno. +- */ +-int __init f_midi_bind_config(struct usb_configuration *c, +- int index, char *id, +- unsigned int in_ports, +- unsigned int out_ports, +- unsigned int buflen, +- unsigned int qlen) ++static inline struct f_midi_opts *to_f_midi_opts(struct config_item *item) ++{ ++ return container_of(to_config_group(item), struct f_midi_opts, ++ func_inst.group); ++} ++ ++CONFIGFS_ATTR_STRUCT(f_midi_opts); ++CONFIGFS_ATTR_OPS(f_midi_opts); ++ ++static void midi_attr_release(struct config_item *item) ++{ ++ struct f_midi_opts *opts = to_f_midi_opts(item); ++ ++ usb_put_function_instance(&opts->func_inst); ++} ++ ++static struct configfs_item_operations midi_item_ops = { ++ .release = midi_attr_release, ++ .show_attribute = f_midi_opts_attr_show, ++ .store_attribute = f_midi_opts_attr_store, ++}; ++ ++#define F_MIDI_OPT(name, test_limit, limit) \ ++static ssize_t f_midi_opts_##name##_show(struct f_midi_opts *opts, char *page) \ ++{ \ ++ int result; \ ++ \ ++ mutex_lock(&opts->lock); \ ++ result = sprintf(page, "%d\n", opts->name); \ ++ mutex_unlock(&opts->lock); \ ++ \ ++ return result; \ ++} \ ++ \ ++static ssize_t f_midi_opts_##name##_store(struct f_midi_opts *opts, \ ++ const char *page, size_t len) \ ++{ \ ++ int ret; \ ++ u32 num; \ ++ \ ++ mutex_lock(&opts->lock); \ ++ if (opts->refcnt) { \ ++ ret = -EBUSY; \ ++ goto end; \ ++ } \ ++ \ ++ ret = kstrtou32(page, 0, &num); \ ++ if (ret) \ ++ goto end; \ ++ \ ++ if (test_limit && num > limit) { \ ++ ret = -EINVAL; \ ++ goto end; \ ++ } \ ++ opts->name = num; \ ++ ret = len; \ ++ \ ++end: \ ++ mutex_unlock(&opts->lock); \ ++ return ret; \ ++} \ ++ \ ++static struct f_midi_opts_attribute f_midi_opts_##name = \ ++ __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, f_midi_opts_##name##_show, \ ++ f_midi_opts_##name##_store) ++ ++F_MIDI_OPT(index, true, SNDRV_CARDS); ++F_MIDI_OPT(buflen, false, 0); ++F_MIDI_OPT(qlen, false, 0); ++F_MIDI_OPT(in_ports, true, MAX_PORTS); ++F_MIDI_OPT(out_ports, true, MAX_PORTS); ++ ++static ssize_t f_midi_opts_id_show(struct f_midi_opts *opts, char *page) ++{ ++ int result; ++ ++ mutex_lock(&opts->lock); ++ if (opts->id) { ++ result = strlcpy(page, opts->id, PAGE_SIZE); ++ } else { ++ page[0] = 0; ++ result = 0; ++ } ++ ++ mutex_unlock(&opts->lock); ++ ++ return result; ++} ++ ++static ssize_t f_midi_opts_id_store(struct f_midi_opts *opts, ++ const char *page, size_t len) ++{ ++ int ret; ++ char *c; ++ ++ mutex_lock(&opts->lock); ++ if (opts->refcnt) { ++ ret = -EBUSY; ++ goto end; ++ } ++ ++ c = kstrndup(page, len, GFP_KERNEL); ++ if (!c) { ++ ret = -ENOMEM; ++ goto end; ++ } ++ if (opts->id_allocated) ++ kfree(opts->id); ++ opts->id = c; ++ opts->id_allocated = true; ++ ret = len; ++end: ++ mutex_unlock(&opts->lock); ++ return ret; ++} ++ ++static struct f_midi_opts_attribute f_midi_opts_id = ++ __CONFIGFS_ATTR(id, S_IRUGO | S_IWUSR, f_midi_opts_id_show, ++ f_midi_opts_id_store); ++ ++static struct configfs_attribute *midi_attrs[] = { ++ &f_midi_opts_index.attr, ++ &f_midi_opts_buflen.attr, ++ &f_midi_opts_qlen.attr, ++ &f_midi_opts_in_ports.attr, ++ &f_midi_opts_out_ports.attr, ++ &f_midi_opts_id.attr, ++ NULL, ++}; ++ ++static struct config_item_type midi_func_type = { ++ .ct_item_ops = &midi_item_ops, ++ .ct_attrs = midi_attrs, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static void f_midi_free_inst(struct usb_function_instance *f) ++{ ++ struct f_midi_opts *opts; ++ ++ opts = container_of(f, struct f_midi_opts, func_inst); ++ ++ if (opts->id_allocated) ++ kfree(opts->id); ++ ++ kfree(opts); ++} ++ ++#ifdef CONFIG_USB_CONFIGFS_UEVENT ++extern struct device *create_function_device(char *name); ++static ssize_t alsa_show(struct device *dev, ++ struct device_attribute *attr, char *buf) + { ++ struct usb_function_instance *fi_midi = dev_get_drvdata(dev); + struct f_midi *midi; ++ ++ if (!fi_midi->f) ++ dev_warn(dev, "f_midi: function not set\n"); ++ ++ if (fi_midi && fi_midi->f) { ++ midi = func_to_midi(fi_midi->f); ++ if (midi->rmidi && midi->rmidi->card) ++ return sprintf(buf, "%d %d\n", ++ midi->rmidi->card->number, midi->rmidi->device); ++ } ++ ++ /* print PCM card and device numbers */ ++ return sprintf(buf, "%d %d\n", -1, -1); ++} ++ ++static DEVICE_ATTR(alsa, S_IRUGO, alsa_show, NULL); ++ ++static struct device_attribute *alsa_function_attributes[] = { ++ &dev_attr_alsa, ++ NULL ++}; ++ ++static int create_alsa_device(struct usb_function_instance *fi) ++{ ++ struct device *dev; ++ struct device_attribute **attrs; ++ struct device_attribute *attr; ++ int err = 0; ++ ++ dev = create_function_device("f_midi"); ++ if (IS_ERR(dev)) ++ return PTR_ERR(dev); ++ ++ attrs = alsa_function_attributes; ++ if (attrs) { ++ while ((attr = *attrs++) && !err) ++ err = device_create_file(dev, attr); ++ if (err) { ++ device_destroy(dev->class, dev->devt); ++ return -EINVAL; ++ } ++ } ++ dev_set_drvdata(dev, fi); ++ return 0; ++} ++#else ++static int create_alsa_device(struct usb_function_instance *fi) ++{ ++ return 0; ++} ++#endif ++ ++static struct usb_function_instance *f_midi_alloc_inst(void) ++{ ++ struct f_midi_opts *opts; ++ ++ opts = kzalloc(sizeof(*opts), GFP_KERNEL); ++ if (!opts) ++ return ERR_PTR(-ENOMEM); ++ ++ mutex_init(&opts->lock); ++ opts->func_inst.free_func_inst = f_midi_free_inst; ++ opts->index = SNDRV_DEFAULT_IDX1; ++ opts->id = SNDRV_DEFAULT_STR1; ++ opts->buflen = 256; ++ opts->qlen = 32; ++ opts->in_ports = 1; ++ opts->out_ports = 1; ++ ++ if (create_alsa_device(&opts->func_inst)) { ++ kfree(opts); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ config_group_init_type_name(&opts->func_inst.group, "", ++ &midi_func_type); ++ ++ return &opts->func_inst; ++} ++ ++static void f_midi_free(struct usb_function *f) ++{ ++ struct f_midi *midi; ++ struct f_midi_opts *opts; ++ int i; ++ ++ midi = func_to_midi(f); ++ opts = container_of(f->fi, struct f_midi_opts, func_inst); ++ kfree(midi->id); ++ mutex_lock(&opts->lock); ++ for (i = opts->in_ports - 1; i >= 0; --i) ++ kfree(midi->in_port[i]); ++ kfree(midi); ++ --opts->refcnt; ++ mutex_unlock(&opts->lock); ++} ++ ++static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = f->config->cdev; ++ struct f_midi *midi = func_to_midi(f); ++ struct snd_card *card; ++ ++ DBG(cdev, "unbind\n"); ++ ++ /* just to be sure */ ++ f_midi_disable(f); ++ ++ card = midi->card; ++ midi->card = NULL; ++ if (card) ++ snd_card_free(card); ++ ++ usb_free_all_descriptors(f); ++} ++ ++static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) ++{ ++ struct f_midi *midi; ++ struct f_midi_opts *opts; + int status, i; + ++ opts = container_of(fi, struct f_midi_opts, func_inst); ++ ++ mutex_lock(&opts->lock); + /* sanity check */ +- if (in_ports > MAX_PORTS || out_ports > MAX_PORTS) +- return -EINVAL; ++ if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) { ++ mutex_unlock(&opts->lock); ++ return ERR_PTR(-EINVAL); ++ } + + /* allocate and initialize one new instance */ +- midi = kzalloc(sizeof *midi, GFP_KERNEL); ++ midi = kzalloc(sizeof(*midi), GFP_KERNEL); + if (!midi) { +- status = -ENOMEM; +- goto fail; ++ mutex_unlock(&opts->lock); ++ return ERR_PTR(-ENOMEM); + } + +- for (i = 0; i < in_ports; i++) { ++ for (i = 0; i < opts->in_ports; i++) { + struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL); ++ + if (!port) { + status = -ENOMEM; ++ mutex_unlock(&opts->lock); + goto setup_fail; + } + +@@ -948,39 +1208,36 @@ int __init f_midi_bind_config(struct usb_configuration *c, + midi->in_port[i] = port; + } + +- midi->gadget = c->cdev->gadget; +- tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); +- + /* set up ALSA midi devices */ +- midi->in_ports = in_ports; +- midi->out_ports = out_ports; +- status = f_midi_register_card(midi); +- if (status < 0) +- goto setup_fail; +- +- midi->func.name = "gmidi function"; +- midi->func.strings = midi_strings; +- midi->func.bind = f_midi_bind; +- midi->func.unbind = f_midi_unbind; +- midi->func.set_alt = f_midi_set_alt; +- midi->func.disable = f_midi_disable; +- +- midi->id = kstrdup(id, GFP_KERNEL); +- midi->index = index; +- midi->buflen = buflen; +- midi->qlen = qlen; +- +- status = usb_add_function(c, &midi->func); +- if (status) ++ midi->id = kstrdup(opts->id, GFP_KERNEL); ++ if (opts->id && !midi->id) { ++ status = -ENOMEM; ++ mutex_unlock(&opts->lock); + goto setup_fail; +- +- return 0; ++ } ++ midi->in_ports = opts->in_ports; ++ midi->out_ports = opts->out_ports; ++ midi->index = opts->index; ++ midi->buflen = opts->buflen; ++ midi->qlen = opts->qlen; ++ ++opts->refcnt; ++ mutex_unlock(&opts->lock); ++ ++ midi->func.name = "gmidi function"; ++ midi->func.bind = f_midi_bind; ++ midi->func.unbind = f_midi_unbind; ++ midi->func.set_alt = f_midi_set_alt; ++ midi->func.disable = f_midi_disable; ++ midi->func.free_func = f_midi_free; ++ ++ fi->f = &midi->func; ++ return &midi->func; + + setup_fail: + for (--i; i >= 0; i--) + kfree(midi->in_port[i]); + kfree(midi); +-fail: +- return status; ++ return ERR_PTR(status); + } + ++DECLARE_USB_FUNCTION_INIT(midi, f_midi_alloc_inst, f_midi_alloc); +diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c +new file mode 100644 +index 0000000..79053fd +--- /dev/null ++++ b/drivers/usb/gadget/function/f_mtp.c +@@ -0,0 +1,1473 @@ ++/* ++ * Gadget Function Driver for MTP ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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. ++ * ++ */ ++ ++/* #define DEBUG */ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/poll.h> ++#include <linux/delay.h> ++#include <linux/wait.h> ++#include <linux/err.h> ++#include <linux/interrupt.h> ++ ++#include <linux/types.h> ++#include <linux/file.h> ++#include <linux/device.h> ++#include <linux/miscdevice.h> ++ ++#include <linux/usb.h> ++#include <linux/usb_usual.h> ++#include <linux/usb/ch9.h> ++#include <linux/usb/f_mtp.h> ++#include <linux/configfs.h> ++#include <linux/usb/composite.h> ++ ++#include "configfs.h" ++ ++#define MTP_BULK_BUFFER_SIZE 16384 ++#define INTR_BUFFER_SIZE 28 ++#define MAX_INST_NAME_LEN 40 ++ ++/* String IDs */ ++#define INTERFACE_STRING_INDEX 0 ++ ++/* values for mtp_dev.state */ ++#define STATE_OFFLINE 0 /* initial state, disconnected */ ++#define STATE_READY 1 /* ready for userspace calls */ ++#define STATE_BUSY 2 /* processing userspace calls */ ++#define STATE_CANCELED 3 /* transaction canceled by host */ ++#define STATE_ERROR 4 /* error from completion routine */ ++ ++/* number of tx and rx requests to allocate */ ++#define TX_REQ_MAX 4 ++#define RX_REQ_MAX 2 ++#define INTR_REQ_MAX 5 ++ ++/* ID for Microsoft MTP OS String */ ++#define MTP_OS_STRING_ID 0xEE ++ ++/* MTP class reqeusts */ ++#define MTP_REQ_CANCEL 0x64 ++#define MTP_REQ_GET_EXT_EVENT_DATA 0x65 ++#define MTP_REQ_RESET 0x66 ++#define MTP_REQ_GET_DEVICE_STATUS 0x67 ++ ++/* constants for device status */ ++#define MTP_RESPONSE_OK 0x2001 ++#define MTP_RESPONSE_DEVICE_BUSY 0x2019 ++#define DRIVER_NAME "mtp" ++ ++static const char mtp_shortname[] = DRIVER_NAME "_usb"; ++ ++struct mtp_dev { ++ struct usb_function function; ++ struct usb_composite_dev *cdev; ++ spinlock_t lock; ++ ++ struct usb_ep *ep_in; ++ struct usb_ep *ep_out; ++ struct usb_ep *ep_intr; ++ ++ int state; ++ ++ /* synchronize access to our device file */ ++ atomic_t open_excl; ++ /* to enforce only one ioctl at a time */ ++ atomic_t ioctl_excl; ++ ++ struct list_head tx_idle; ++ struct list_head intr_idle; ++ ++ wait_queue_head_t read_wq; ++ wait_queue_head_t write_wq; ++ wait_queue_head_t intr_wq; ++ struct usb_request *rx_req[RX_REQ_MAX]; ++ int rx_done; ++ ++ /* for processing MTP_SEND_FILE, MTP_RECEIVE_FILE and ++ * MTP_SEND_FILE_WITH_HEADER ioctls on a work queue ++ */ ++ struct workqueue_struct *wq; ++ struct work_struct send_file_work; ++ struct work_struct receive_file_work; ++ struct file *xfer_file; ++ loff_t xfer_file_offset; ++ int64_t xfer_file_length; ++ unsigned xfer_send_header; ++ uint16_t xfer_command; ++ uint32_t xfer_transaction_id; ++ int xfer_result; ++}; ++ ++static struct usb_interface_descriptor mtp_interface_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0, ++ .bNumEndpoints = 3, ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, ++ .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, ++ .bInterfaceProtocol = 0, ++}; ++ ++static struct usb_interface_descriptor ptp_interface_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0, ++ .bNumEndpoints = 3, ++ .bInterfaceClass = USB_CLASS_STILL_IMAGE, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 1, ++}; ++ ++static struct usb_endpoint_descriptor mtp_highspeed_in_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor mtp_highspeed_out_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor mtp_fullspeed_in_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor mtp_fullspeed_out_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor mtp_intr_desc = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(INTR_BUFFER_SIZE), ++ .bInterval = 6, ++}; ++ ++static struct usb_descriptor_header *fs_mtp_descs[] = { ++ (struct usb_descriptor_header *) &mtp_interface_desc, ++ (struct usb_descriptor_header *) &mtp_fullspeed_in_desc, ++ (struct usb_descriptor_header *) &mtp_fullspeed_out_desc, ++ (struct usb_descriptor_header *) &mtp_intr_desc, ++ NULL, ++}; ++ ++static struct usb_descriptor_header *hs_mtp_descs[] = { ++ (struct usb_descriptor_header *) &mtp_interface_desc, ++ (struct usb_descriptor_header *) &mtp_highspeed_in_desc, ++ (struct usb_descriptor_header *) &mtp_highspeed_out_desc, ++ (struct usb_descriptor_header *) &mtp_intr_desc, ++ NULL, ++}; ++ ++static struct usb_descriptor_header *fs_ptp_descs[] = { ++ (struct usb_descriptor_header *) &ptp_interface_desc, ++ (struct usb_descriptor_header *) &mtp_fullspeed_in_desc, ++ (struct usb_descriptor_header *) &mtp_fullspeed_out_desc, ++ (struct usb_descriptor_header *) &mtp_intr_desc, ++ NULL, ++}; ++ ++static struct usb_descriptor_header *hs_ptp_descs[] = { ++ (struct usb_descriptor_header *) &ptp_interface_desc, ++ (struct usb_descriptor_header *) &mtp_highspeed_in_desc, ++ (struct usb_descriptor_header *) &mtp_highspeed_out_desc, ++ (struct usb_descriptor_header *) &mtp_intr_desc, ++ NULL, ++}; ++ ++static struct usb_string mtp_string_defs[] = { ++ /* Naming interface "MTP" so libmtp will recognize us */ ++ [INTERFACE_STRING_INDEX].s = "MTP", ++ { }, /* end of list */ ++}; ++ ++static struct usb_gadget_strings mtp_string_table = { ++ .language = 0x0409, /* en-US */ ++ .strings = mtp_string_defs, ++}; ++ ++static struct usb_gadget_strings *mtp_strings[] = { ++ &mtp_string_table, ++ NULL, ++}; ++ ++/* Microsoft MTP OS String */ ++static u8 mtp_os_string[] = { ++ 18, /* sizeof(mtp_os_string) */ ++ USB_DT_STRING, ++ /* Signature field: "MSFT100" */ ++ 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, ++ /* vendor code */ ++ 1, ++ /* padding */ ++ 0 ++}; ++ ++/* Microsoft Extended Configuration Descriptor Header Section */ ++struct mtp_ext_config_desc_header { ++ __le32 dwLength; ++ __u16 bcdVersion; ++ __le16 wIndex; ++ __u8 bCount; ++ __u8 reserved[7]; ++}; ++ ++/* Microsoft Extended Configuration Descriptor Function Section */ ++struct mtp_ext_config_desc_function { ++ __u8 bFirstInterfaceNumber; ++ __u8 bInterfaceCount; ++ __u8 compatibleID[8]; ++ __u8 subCompatibleID[8]; ++ __u8 reserved[6]; ++}; ++ ++/* MTP Extended Configuration Descriptor */ ++struct { ++ struct mtp_ext_config_desc_header header; ++ struct mtp_ext_config_desc_function function; ++} mtp_ext_config_desc = { ++ .header = { ++ .dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)), ++ .bcdVersion = __constant_cpu_to_le16(0x0100), ++ .wIndex = __constant_cpu_to_le16(4), ++ .bCount = __constant_cpu_to_le16(1), ++ }, ++ .function = { ++ .bFirstInterfaceNumber = 0, ++ .bInterfaceCount = 1, ++ .compatibleID = { 'M', 'T', 'P' }, ++ }, ++}; ++ ++struct mtp_device_status { ++ __le16 wLength; ++ __le16 wCode; ++}; ++ ++struct mtp_data_header { ++ /* length of packet, including this header */ ++ __le32 length; ++ /* container type (2 for data packet) */ ++ __le16 type; ++ /* MTP command code */ ++ __le16 command; ++ /* MTP transaction ID */ ++ __le32 transaction_id; ++}; ++ ++struct mtp_instance { ++ struct usb_function_instance func_inst; ++ const char *name; ++ struct mtp_dev *dev; ++}; ++ ++/* temporary variable used between mtp_open() and mtp_gadget_bind() */ ++static struct mtp_dev *_mtp_dev; ++ ++static inline struct mtp_dev *func_to_mtp(struct usb_function *f) ++{ ++ return container_of(f, struct mtp_dev, function); ++} ++ ++static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size) ++{ ++ struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); ++ if (!req) ++ return NULL; ++ ++ /* now allocate buffers for the requests */ ++ req->buf = kmalloc(buffer_size, GFP_KERNEL); ++ if (!req->buf) { ++ usb_ep_free_request(ep, req); ++ return NULL; ++ } ++ ++ return req; ++} ++ ++static void mtp_request_free(struct usb_request *req, struct usb_ep *ep) ++{ ++ if (req) { ++ kfree(req->buf); ++ usb_ep_free_request(ep, req); ++ } ++} ++ ++static inline int mtp_lock(atomic_t *excl) ++{ ++ if (atomic_inc_return(excl) == 1) { ++ return 0; ++ } else { ++ atomic_dec(excl); ++ return -1; ++ } ++} ++ ++static inline void mtp_unlock(atomic_t *excl) ++{ ++ atomic_dec(excl); ++} ++ ++/* add a request to the tail of a list */ ++static void mtp_req_put(struct mtp_dev *dev, struct list_head *head, ++ struct usb_request *req) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ list_add_tail(&req->list, head); ++ spin_unlock_irqrestore(&dev->lock, flags); ++} ++ ++/* remove a request from the head of a list */ ++static struct usb_request ++*mtp_req_get(struct mtp_dev *dev, struct list_head *head) ++{ ++ unsigned long flags; ++ struct usb_request *req; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (list_empty(head)) { ++ req = 0; ++ } else { ++ req = list_first_entry(head, struct usb_request, list); ++ list_del(&req->list); ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ return req; ++} ++ ++static void mtp_complete_in(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct mtp_dev *dev = _mtp_dev; ++ ++ if (req->status != 0) ++ dev->state = STATE_ERROR; ++ ++ mtp_req_put(dev, &dev->tx_idle, req); ++ ++ wake_up(&dev->write_wq); ++} ++ ++static void mtp_complete_out(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct mtp_dev *dev = _mtp_dev; ++ ++ dev->rx_done = 1; ++ if (req->status != 0) ++ dev->state = STATE_ERROR; ++ ++ wake_up(&dev->read_wq); ++} ++ ++static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct mtp_dev *dev = _mtp_dev; ++ ++ if (req->status != 0) ++ dev->state = STATE_ERROR; ++ ++ mtp_req_put(dev, &dev->intr_idle, req); ++ ++ wake_up(&dev->intr_wq); ++} ++ ++static int mtp_create_bulk_endpoints(struct mtp_dev *dev, ++ struct usb_endpoint_descriptor *in_desc, ++ struct usb_endpoint_descriptor *out_desc, ++ struct usb_endpoint_descriptor *intr_desc) ++{ ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *req; ++ struct usb_ep *ep; ++ int i; ++ ++ DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); ++ ++ ep = usb_ep_autoconfig(cdev->gadget, in_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_in = ep; ++ ++ ep = usb_ep_autoconfig(cdev->gadget, out_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_out = ep; ++ ++ ep = usb_ep_autoconfig(cdev->gadget, intr_desc); ++ if (!ep) { ++ DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n"); ++ return -ENODEV; ++ } ++ DBG(cdev, "usb_ep_autoconfig for mtp ep_intr got %s\n", ep->name); ++ ep->driver_data = dev; /* claim the endpoint */ ++ dev->ep_intr = ep; ++ ++ /* now allocate requests for our endpoints */ ++ for (i = 0; i < TX_REQ_MAX; i++) { ++ req = mtp_request_new(dev->ep_in, MTP_BULK_BUFFER_SIZE); ++ if (!req) ++ goto fail; ++ req->complete = mtp_complete_in; ++ mtp_req_put(dev, &dev->tx_idle, req); ++ } ++ for (i = 0; i < RX_REQ_MAX; i++) { ++ req = mtp_request_new(dev->ep_out, MTP_BULK_BUFFER_SIZE); ++ if (!req) ++ goto fail; ++ req->complete = mtp_complete_out; ++ dev->rx_req[i] = req; ++ } ++ for (i = 0; i < INTR_REQ_MAX; i++) { ++ req = mtp_request_new(dev->ep_intr, INTR_BUFFER_SIZE); ++ if (!req) ++ goto fail; ++ req->complete = mtp_complete_intr; ++ mtp_req_put(dev, &dev->intr_idle, req); ++ } ++ ++ return 0; ++ ++fail: ++ pr_err("mtp_bind() could not allocate requests\n"); ++ return -1; ++} ++ ++static ssize_t mtp_read(struct file *fp, char __user *buf, ++ size_t count, loff_t *pos) ++{ ++ struct mtp_dev *dev = fp->private_data; ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *req; ++ ssize_t r = count; ++ unsigned xfer; ++ int ret = 0; ++ ++ DBG(cdev, "mtp_read(%zu)\n", count); ++ ++ if (count > MTP_BULK_BUFFER_SIZE) ++ return -EINVAL; ++ ++ /* we will block until we're online */ ++ DBG(cdev, "mtp_read: waiting for online state\n"); ++ ret = wait_event_interruptible(dev->read_wq, ++ dev->state != STATE_OFFLINE); ++ if (ret < 0) { ++ r = ret; ++ goto done; ++ } ++ spin_lock_irq(&dev->lock); ++ if (dev->state == STATE_CANCELED) { ++ /* report cancelation to userspace */ ++ dev->state = STATE_READY; ++ spin_unlock_irq(&dev->lock); ++ return -ECANCELED; ++ } ++ dev->state = STATE_BUSY; ++ spin_unlock_irq(&dev->lock); ++ ++requeue_req: ++ /* queue a request */ ++ req = dev->rx_req[0]; ++ req->length = count; ++ dev->rx_done = 0; ++ ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); ++ if (ret < 0) { ++ r = -EIO; ++ goto done; ++ } else { ++ DBG(cdev, "rx %p queue\n", req); ++ } ++ ++ /* wait for a request to complete */ ++ ret = wait_event_interruptible(dev->read_wq, dev->rx_done); ++ if (ret < 0) { ++ r = ret; ++ usb_ep_dequeue(dev->ep_out, req); ++ goto done; ++ } ++ if (dev->state == STATE_BUSY) { ++ /* If we got a 0-len packet, throw it back and try again. */ ++ if (req->actual == 0) ++ goto requeue_req; ++ ++ DBG(cdev, "rx %p %d\n", req, req->actual); ++ xfer = (req->actual < count) ? req->actual : count; ++ r = xfer; ++ if (copy_to_user(buf, req->buf, xfer)) ++ r = -EFAULT; ++ } else ++ r = -EIO; ++ ++done: ++ spin_lock_irq(&dev->lock); ++ if (dev->state == STATE_CANCELED) ++ r = -ECANCELED; ++ else if (dev->state != STATE_OFFLINE) ++ dev->state = STATE_READY; ++ spin_unlock_irq(&dev->lock); ++ ++ DBG(cdev, "mtp_read returning %zd\n", r); ++ return r; ++} ++ ++static ssize_t mtp_write(struct file *fp, const char __user *buf, ++ size_t count, loff_t *pos) ++{ ++ struct mtp_dev *dev = fp->private_data; ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *req = 0; ++ ssize_t r = count; ++ unsigned xfer; ++ int sendZLP = 0; ++ int ret; ++ ++ DBG(cdev, "mtp_write(%zu)\n", count); ++ ++ spin_lock_irq(&dev->lock); ++ if (dev->state == STATE_CANCELED) { ++ /* report cancelation to userspace */ ++ dev->state = STATE_READY; ++ spin_unlock_irq(&dev->lock); ++ return -ECANCELED; ++ } ++ if (dev->state == STATE_OFFLINE) { ++ spin_unlock_irq(&dev->lock); ++ return -ENODEV; ++ } ++ dev->state = STATE_BUSY; ++ spin_unlock_irq(&dev->lock); ++ ++ /* we need to send a zero length packet to signal the end of transfer ++ * if the transfer size is aligned to a packet boundary. ++ */ ++ if ((count & (dev->ep_in->maxpacket - 1)) == 0) ++ sendZLP = 1; ++ ++ while (count > 0 || sendZLP) { ++ /* so we exit after sending ZLP */ ++ if (count == 0) ++ sendZLP = 0; ++ ++ if (dev->state != STATE_BUSY) { ++ DBG(cdev, "mtp_write dev->error\n"); ++ r = -EIO; ++ break; ++ } ++ ++ /* get an idle tx request to use */ ++ req = 0; ++ ret = wait_event_interruptible(dev->write_wq, ++ ((req = mtp_req_get(dev, &dev->tx_idle)) ++ || dev->state != STATE_BUSY)); ++ if (!req) { ++ r = ret; ++ break; ++ } ++ ++ if (count > MTP_BULK_BUFFER_SIZE) ++ xfer = MTP_BULK_BUFFER_SIZE; ++ else ++ xfer = count; ++ if (xfer && copy_from_user(req->buf, buf, xfer)) { ++ r = -EFAULT; ++ break; ++ } ++ ++ req->length = xfer; ++ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); ++ if (ret < 0) { ++ DBG(cdev, "mtp_write: xfer error %d\n", ret); ++ r = -EIO; ++ break; ++ } ++ ++ buf += xfer; ++ count -= xfer; ++ ++ /* zero this so we don't try to free it on error exit */ ++ req = 0; ++ } ++ ++ if (req) ++ mtp_req_put(dev, &dev->tx_idle, req); ++ ++ spin_lock_irq(&dev->lock); ++ if (dev->state == STATE_CANCELED) ++ r = -ECANCELED; ++ else if (dev->state != STATE_OFFLINE) ++ dev->state = STATE_READY; ++ spin_unlock_irq(&dev->lock); ++ ++ DBG(cdev, "mtp_write returning %zd\n", r); ++ return r; ++} ++ ++/* read from a local file and write to USB */ ++static void send_file_work(struct work_struct *data) ++{ ++ struct mtp_dev *dev = container_of(data, struct mtp_dev, ++ send_file_work); ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *req = 0; ++ struct mtp_data_header *header; ++ struct file *filp; ++ loff_t offset; ++ int64_t count; ++ int xfer, ret, hdr_size; ++ int r = 0; ++ int sendZLP = 0; ++ ++ /* read our parameters */ ++ smp_rmb(); ++ filp = dev->xfer_file; ++ offset = dev->xfer_file_offset; ++ count = dev->xfer_file_length; ++ ++ DBG(cdev, "send_file_work(%lld %lld)\n", offset, count); ++ ++ if (dev->xfer_send_header) { ++ hdr_size = sizeof(struct mtp_data_header); ++ count += hdr_size; ++ } else { ++ hdr_size = 0; ++ } ++ ++ /* we need to send a zero length packet to signal the end of transfer ++ * if the transfer size is aligned to a packet boundary. ++ */ ++ if ((count & (dev->ep_in->maxpacket - 1)) == 0) ++ sendZLP = 1; ++ ++ while (count > 0 || sendZLP) { ++ /* so we exit after sending ZLP */ ++ if (count == 0) ++ sendZLP = 0; ++ ++ /* get an idle tx request to use */ ++ req = 0; ++ ret = wait_event_interruptible(dev->write_wq, ++ (req = mtp_req_get(dev, &dev->tx_idle)) ++ || dev->state != STATE_BUSY); ++ if (dev->state == STATE_CANCELED) { ++ r = -ECANCELED; ++ break; ++ } ++ if (!req) { ++ r = ret; ++ break; ++ } ++ ++ if (count > MTP_BULK_BUFFER_SIZE) ++ xfer = MTP_BULK_BUFFER_SIZE; ++ else ++ xfer = count; ++ ++ if (hdr_size) { ++ /* prepend MTP data header */ ++ header = (struct mtp_data_header *)req->buf; ++ header->length = __cpu_to_le32(count); ++ header->type = __cpu_to_le16(2); /* data packet */ ++ header->command = __cpu_to_le16(dev->xfer_command); ++ header->transaction_id = ++ __cpu_to_le32(dev->xfer_transaction_id); ++ } ++ ++ ret = vfs_read(filp, req->buf + hdr_size, xfer - hdr_size, ++ &offset); ++ if (ret < 0) { ++ r = ret; ++ break; ++ } ++ xfer = ret + hdr_size; ++ hdr_size = 0; ++ ++ req->length = xfer; ++ ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); ++ if (ret < 0) { ++ DBG(cdev, "send_file_work: xfer error %d\n", ret); ++ dev->state = STATE_ERROR; ++ r = -EIO; ++ break; ++ } ++ ++ count -= xfer; ++ ++ /* zero this so we don't try to free it on error exit */ ++ req = 0; ++ } ++ ++ if (req) ++ mtp_req_put(dev, &dev->tx_idle, req); ++ ++ DBG(cdev, "send_file_work returning %d\n", r); ++ /* write the result */ ++ dev->xfer_result = r; ++ smp_wmb(); ++} ++ ++/* read from USB and write to a local file */ ++static void receive_file_work(struct work_struct *data) ++{ ++ struct mtp_dev *dev = container_of(data, struct mtp_dev, ++ receive_file_work); ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct usb_request *read_req = NULL, *write_req = NULL; ++ struct file *filp; ++ loff_t offset; ++ int64_t count; ++ int ret, cur_buf = 0; ++ int r = 0; ++ ++ /* read our parameters */ ++ smp_rmb(); ++ filp = dev->xfer_file; ++ offset = dev->xfer_file_offset; ++ count = dev->xfer_file_length; ++ ++ DBG(cdev, "receive_file_work(%lld)\n", count); ++ ++ while (count > 0 || write_req) { ++ if (count > 0) { ++ /* queue a request */ ++ read_req = dev->rx_req[cur_buf]; ++ cur_buf = (cur_buf + 1) % RX_REQ_MAX; ++ ++ read_req->length = (count > MTP_BULK_BUFFER_SIZE ++ ? MTP_BULK_BUFFER_SIZE : count); ++ dev->rx_done = 0; ++ ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL); ++ if (ret < 0) { ++ r = -EIO; ++ dev->state = STATE_ERROR; ++ break; ++ } ++ } ++ ++ if (write_req) { ++ DBG(cdev, "rx %p %d\n", write_req, write_req->actual); ++ ret = vfs_write(filp, write_req->buf, write_req->actual, ++ &offset); ++ DBG(cdev, "vfs_write %d\n", ret); ++ if (ret != write_req->actual) { ++ r = -EIO; ++ dev->state = STATE_ERROR; ++ break; ++ } ++ write_req = NULL; ++ } ++ ++ if (read_req) { ++ /* wait for our last read to complete */ ++ ret = wait_event_interruptible(dev->read_wq, ++ dev->rx_done || dev->state != STATE_BUSY); ++ if (dev->state == STATE_CANCELED) { ++ r = -ECANCELED; ++ if (!dev->rx_done) ++ usb_ep_dequeue(dev->ep_out, read_req); ++ break; ++ } ++ /* if xfer_file_length is 0xFFFFFFFF, then we read until ++ * we get a zero length packet ++ */ ++ if (count != 0xFFFFFFFF) ++ count -= read_req->actual; ++ if (read_req->actual < read_req->length) { ++ /* ++ * short packet is used to signal EOF for ++ * sizes > 4 gig ++ */ ++ DBG(cdev, "got short packet\n"); ++ count = 0; ++ } ++ ++ write_req = read_req; ++ read_req = NULL; ++ } ++ } ++ ++ DBG(cdev, "receive_file_work returning %d\n", r); ++ /* write the result */ ++ dev->xfer_result = r; ++ smp_wmb(); ++} ++ ++static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) ++{ ++ struct usb_request *req = NULL; ++ int ret; ++ int length = event->length; ++ ++ DBG(dev->cdev, "mtp_send_event(%zu)\n", event->length); ++ ++ if (length < 0 || length > INTR_BUFFER_SIZE) ++ return -EINVAL; ++ if (dev->state == STATE_OFFLINE) ++ return -ENODEV; ++ ++ ret = wait_event_interruptible_timeout(dev->intr_wq, ++ (req = mtp_req_get(dev, &dev->intr_idle)), ++ msecs_to_jiffies(1000)); ++ if (!req) ++ return -ETIME; ++ ++ if (copy_from_user(req->buf, (void __user *)event->data, length)) { ++ mtp_req_put(dev, &dev->intr_idle, req); ++ return -EFAULT; ++ } ++ req->length = length; ++ ret = usb_ep_queue(dev->ep_intr, req, GFP_KERNEL); ++ if (ret) ++ mtp_req_put(dev, &dev->intr_idle, req); ++ ++ return ret; ++} ++ ++static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) ++{ ++ struct mtp_dev *dev = fp->private_data; ++ struct file *filp = NULL; ++ int ret = -EINVAL; ++ ++ if (mtp_lock(&dev->ioctl_excl)) ++ return -EBUSY; ++ ++ switch (code) { ++ case MTP_SEND_FILE: ++ case MTP_RECEIVE_FILE: ++ case MTP_SEND_FILE_WITH_HEADER: ++ { ++ struct mtp_file_range mfr; ++ struct work_struct *work; ++ ++ spin_lock_irq(&dev->lock); ++ if (dev->state == STATE_CANCELED) { ++ /* report cancelation to userspace */ ++ dev->state = STATE_READY; ++ spin_unlock_irq(&dev->lock); ++ ret = -ECANCELED; ++ goto out; ++ } ++ if (dev->state == STATE_OFFLINE) { ++ spin_unlock_irq(&dev->lock); ++ ret = -ENODEV; ++ goto out; ++ } ++ dev->state = STATE_BUSY; ++ spin_unlock_irq(&dev->lock); ++ ++ if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) { ++ ret = -EFAULT; ++ goto fail; ++ } ++ /* hold a reference to the file while we are working with it */ ++ filp = fget(mfr.fd); ++ if (!filp) { ++ ret = -EBADF; ++ goto fail; ++ } ++ ++ /* write the parameters */ ++ dev->xfer_file = filp; ++ dev->xfer_file_offset = mfr.offset; ++ dev->xfer_file_length = mfr.length; ++ smp_wmb(); ++ ++ if (code == MTP_SEND_FILE_WITH_HEADER) { ++ work = &dev->send_file_work; ++ dev->xfer_send_header = 1; ++ dev->xfer_command = mfr.command; ++ dev->xfer_transaction_id = mfr.transaction_id; ++ } else if (code == MTP_SEND_FILE) { ++ work = &dev->send_file_work; ++ dev->xfer_send_header = 0; ++ } else { ++ work = &dev->receive_file_work; ++ } ++ ++ /* We do the file transfer on a work queue so it will run ++ * in kernel context, which is necessary for vfs_read and ++ * vfs_write to use our buffers in the kernel address space. ++ */ ++ queue_work(dev->wq, work); ++ /* wait for operation to complete */ ++ flush_workqueue(dev->wq); ++ fput(filp); ++ ++ /* read the result */ ++ smp_rmb(); ++ ret = dev->xfer_result; ++ break; ++ } ++ case MTP_SEND_EVENT: ++ { ++ struct mtp_event event; ++ /* return here so we don't change dev->state below, ++ * which would interfere with bulk transfer state. ++ */ ++ if (copy_from_user(&event, (void __user *)value, sizeof(event))) ++ ret = -EFAULT; ++ else ++ ret = mtp_send_event(dev, &event); ++ goto out; ++ } ++ } ++ ++fail: ++ spin_lock_irq(&dev->lock); ++ if (dev->state == STATE_CANCELED) ++ ret = -ECANCELED; ++ else if (dev->state != STATE_OFFLINE) ++ dev->state = STATE_READY; ++ spin_unlock_irq(&dev->lock); ++out: ++ mtp_unlock(&dev->ioctl_excl); ++ DBG(dev->cdev, "ioctl returning %d\n", ret); ++ return ret; ++} ++ ++static int mtp_open(struct inode *ip, struct file *fp) ++{ ++ printk(KERN_INFO "mtp_open\n"); ++ if (mtp_lock(&_mtp_dev->open_excl)) ++ return -EBUSY; ++ ++ /* clear any error condition */ ++ if (_mtp_dev->state != STATE_OFFLINE) ++ _mtp_dev->state = STATE_READY; ++ ++ fp->private_data = _mtp_dev; ++ return 0; ++} ++ ++static int mtp_release(struct inode *ip, struct file *fp) ++{ ++ printk(KERN_INFO "mtp_release\n"); ++ ++ mtp_unlock(&_mtp_dev->open_excl); ++ return 0; ++} ++ ++/* file operations for /dev/mtp_usb */ ++static const struct file_operations mtp_fops = { ++ .owner = THIS_MODULE, ++ .read = mtp_read, ++ .write = mtp_write, ++ .unlocked_ioctl = mtp_ioctl, ++ .open = mtp_open, ++ .release = mtp_release, ++}; ++ ++static struct miscdevice mtp_device = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = mtp_shortname, ++ .fops = &mtp_fops, ++}; ++ ++static int mtp_ctrlrequest(struct usb_composite_dev *cdev, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ struct mtp_dev *dev = _mtp_dev; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ unsigned long flags; ++ ++ VDBG(cdev, "mtp_ctrlrequest " ++ "%02x.%02x v%04x i%04x l%u\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ ++ /* Handle MTP OS string */ ++ if (ctrl->bRequestType == ++ (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) ++ && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR ++ && (w_value >> 8) == USB_DT_STRING ++ && (w_value & 0xFF) == MTP_OS_STRING_ID) { ++ value = (w_length < sizeof(mtp_os_string) ++ ? w_length : sizeof(mtp_os_string)); ++ memcpy(cdev->req->buf, mtp_os_string, value); ++ } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { ++ /* Handle MTP OS descriptor */ ++ DBG(cdev, "vendor request: %d index: %d value: %d length: %d\n", ++ ctrl->bRequest, w_index, w_value, w_length); ++ ++ if (ctrl->bRequest == 1 ++ && (ctrl->bRequestType & USB_DIR_IN) ++ && (w_index == 4 || w_index == 5)) { ++ value = (w_length < sizeof(mtp_ext_config_desc) ? ++ w_length : sizeof(mtp_ext_config_desc)); ++ memcpy(cdev->req->buf, &mtp_ext_config_desc, value); ++ } ++ } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { ++ DBG(cdev, "class request: %d index: %d value: %d length: %d\n", ++ ctrl->bRequest, w_index, w_value, w_length); ++ ++ if (ctrl->bRequest == MTP_REQ_CANCEL && w_index == 0 ++ && w_value == 0) { ++ DBG(cdev, "MTP_REQ_CANCEL\n"); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (dev->state == STATE_BUSY) { ++ dev->state = STATE_CANCELED; ++ wake_up(&dev->read_wq); ++ wake_up(&dev->write_wq); ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ /* We need to queue a request to read the remaining ++ * bytes, but we don't actually need to look at ++ * the contents. ++ */ ++ value = w_length; ++ } else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS ++ && w_index == 0 && w_value == 0) { ++ struct mtp_device_status *status = cdev->req->buf; ++ status->wLength = ++ __constant_cpu_to_le16(sizeof(*status)); ++ ++ DBG(cdev, "MTP_REQ_GET_DEVICE_STATUS\n"); ++ spin_lock_irqsave(&dev->lock, flags); ++ /* device status is "busy" until we report ++ * the cancelation to userspace ++ */ ++ if (dev->state == STATE_CANCELED) ++ status->wCode = ++ __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY); ++ else ++ status->wCode = ++ __cpu_to_le16(MTP_RESPONSE_OK); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ value = sizeof(*status); ++ } ++ } ++ ++ /* respond with data transfer or status phase? */ ++ if (value >= 0) { ++ int rc; ++ cdev->req->zero = value < w_length; ++ cdev->req->length = value; ++ rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); ++ if (rc < 0) ++ ERROR(cdev, "%s: response queue error\n", __func__); ++ } ++ return value; ++} ++ ++static int ++mtp_function_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct mtp_dev *dev = func_to_mtp(f); ++ int id; ++ int ret; ++ ++ dev->cdev = cdev; ++ DBG(cdev, "mtp_function_bind dev: %p\n", dev); ++ ++ /* allocate interface ID(s) */ ++ id = usb_interface_id(c, f); ++ if (id < 0) ++ return id; ++ mtp_interface_desc.bInterfaceNumber = id; ++ ++ if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) { ++ ret = usb_string_id(c->cdev); ++ if (ret < 0) ++ return ret; ++ mtp_string_defs[INTERFACE_STRING_INDEX].id = ret; ++ mtp_interface_desc.iInterface = ret; ++ } ++ /* allocate endpoints */ ++ ret = mtp_create_bulk_endpoints(dev, &mtp_fullspeed_in_desc, ++ &mtp_fullspeed_out_desc, &mtp_intr_desc); ++ if (ret) ++ return ret; ++ ++ /* support high speed hardware */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ mtp_highspeed_in_desc.bEndpointAddress = ++ mtp_fullspeed_in_desc.bEndpointAddress; ++ mtp_highspeed_out_desc.bEndpointAddress = ++ mtp_fullspeed_out_desc.bEndpointAddress; ++ } ++ ++ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ f->name, dev->ep_in->name, dev->ep_out->name); ++ return 0; ++} ++ ++static void ++mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct mtp_dev *dev = func_to_mtp(f); ++ struct usb_request *req; ++ int i; ++ ++ mtp_string_defs[INTERFACE_STRING_INDEX].id = 0; ++ while ((req = mtp_req_get(dev, &dev->tx_idle))) ++ mtp_request_free(req, dev->ep_in); ++ for (i = 0; i < RX_REQ_MAX; i++) ++ mtp_request_free(dev->rx_req[i], dev->ep_out); ++ while ((req = mtp_req_get(dev, &dev->intr_idle))) ++ mtp_request_free(req, dev->ep_intr); ++ dev->state = STATE_OFFLINE; ++} ++ ++static int mtp_function_set_alt(struct usb_function *f, ++ unsigned intf, unsigned alt) ++{ ++ struct mtp_dev *dev = func_to_mtp(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ int ret; ++ ++ DBG(cdev, "mtp_function_set_alt intf: %d alt: %d\n", intf, alt); ++ ++ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); ++ if (ret) ++ return ret; ++ ++ ret = usb_ep_enable(dev->ep_in); ++ if (ret) ++ return ret; ++ ++ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); ++ if (ret) ++ return ret; ++ ++ ret = usb_ep_enable(dev->ep_out); ++ if (ret) { ++ usb_ep_disable(dev->ep_in); ++ return ret; ++ } ++ ++ ret = config_ep_by_speed(cdev->gadget, f, dev->ep_intr); ++ if (ret) ++ return ret; ++ ++ ret = usb_ep_enable(dev->ep_intr); ++ if (ret) { ++ usb_ep_disable(dev->ep_out); ++ usb_ep_disable(dev->ep_in); ++ return ret; ++ } ++ dev->state = STATE_READY; ++ ++ /* readers may be blocked waiting for us to go online */ ++ wake_up(&dev->read_wq); ++ return 0; ++} ++ ++static void mtp_function_disable(struct usb_function *f) ++{ ++ struct mtp_dev *dev = func_to_mtp(f); ++ struct usb_composite_dev *cdev = dev->cdev; ++ ++ DBG(cdev, "mtp_function_disable\n"); ++ dev->state = STATE_OFFLINE; ++ usb_ep_disable(dev->ep_in); ++ usb_ep_disable(dev->ep_out); ++ usb_ep_disable(dev->ep_intr); ++ ++ /* readers may be blocked waiting for us to go online */ ++ wake_up(&dev->read_wq); ++ ++ VDBG(cdev, "%s disabled\n", dev->function.name); ++} ++ ++static int __maybe_unused mtp_bind_config(struct usb_configuration *c, bool ptp_config) ++{ ++ struct mtp_dev *dev = _mtp_dev; ++ int ret = 0; ++ ++ printk(KERN_INFO "mtp_bind_config\n"); ++ ++ /* allocate a string ID for our interface */ ++ if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) { ++ ret = usb_string_id(c->cdev); ++ if (ret < 0) ++ return ret; ++ mtp_string_defs[INTERFACE_STRING_INDEX].id = ret; ++ mtp_interface_desc.iInterface = ret; ++ } ++ ++ dev->cdev = c->cdev; ++ dev->function.name = DRIVER_NAME; ++ dev->function.strings = mtp_strings; ++ if (ptp_config) { ++ dev->function.fs_descriptors = fs_ptp_descs; ++ dev->function.hs_descriptors = hs_ptp_descs; ++ } else { ++ dev->function.fs_descriptors = fs_mtp_descs; ++ dev->function.hs_descriptors = hs_mtp_descs; ++ } ++ dev->function.bind = mtp_function_bind; ++ dev->function.unbind = mtp_function_unbind; ++ dev->function.set_alt = mtp_function_set_alt; ++ dev->function.disable = mtp_function_disable; ++ ++ return usb_add_function(c, &dev->function); ++} ++ ++static int __mtp_setup(struct mtp_instance *fi_mtp) ++{ ++ struct mtp_dev *dev; ++ int ret; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ ++ if (fi_mtp != NULL) ++ fi_mtp->dev = dev; ++ ++ if (!dev) ++ return -ENOMEM; ++ ++ spin_lock_init(&dev->lock); ++ init_waitqueue_head(&dev->read_wq); ++ init_waitqueue_head(&dev->write_wq); ++ init_waitqueue_head(&dev->intr_wq); ++ atomic_set(&dev->open_excl, 0); ++ atomic_set(&dev->ioctl_excl, 0); ++ INIT_LIST_HEAD(&dev->tx_idle); ++ INIT_LIST_HEAD(&dev->intr_idle); ++ ++ dev->wq = create_singlethread_workqueue("f_mtp"); ++ if (!dev->wq) { ++ ret = -ENOMEM; ++ goto err1; ++ } ++ INIT_WORK(&dev->send_file_work, send_file_work); ++ INIT_WORK(&dev->receive_file_work, receive_file_work); ++ ++ _mtp_dev = dev; ++ ++ ret = misc_register(&mtp_device); ++ if (ret) ++ goto err2; ++ ++ return 0; ++ ++err2: ++ destroy_workqueue(dev->wq); ++err1: ++ _mtp_dev = NULL; ++ kfree(dev); ++ printk(KERN_ERR "mtp gadget driver failed to initialize\n"); ++ return ret; ++} ++ ++static int __maybe_unused mtp_setup(void) ++{ ++ return __mtp_setup(NULL); ++} ++ ++static int mtp_setup_configfs(struct mtp_instance *fi_mtp) ++{ ++ return __mtp_setup(fi_mtp); ++} ++ ++ ++static void mtp_cleanup(void) ++{ ++ struct mtp_dev *dev = _mtp_dev; ++ ++ if (!dev) ++ return; ++ ++ misc_deregister(&mtp_device); ++ destroy_workqueue(dev->wq); ++ _mtp_dev = NULL; ++ kfree(dev); ++} ++ ++static struct mtp_instance *to_mtp_instance(struct config_item *item) ++{ ++ return container_of(to_config_group(item), struct mtp_instance, ++ func_inst.group); ++} ++ ++static void mtp_attr_release(struct config_item *item) ++{ ++ struct mtp_instance *fi_mtp = to_mtp_instance(item); ++ usb_put_function_instance(&fi_mtp->func_inst); ++} ++ ++static struct configfs_item_operations mtp_item_ops = { ++ .release = mtp_attr_release, ++}; ++ ++static struct config_item_type mtp_func_type = { ++ .ct_item_ops = &mtp_item_ops, ++ .ct_owner = THIS_MODULE, ++}; ++ ++ ++static struct mtp_instance *to_fi_mtp(struct usb_function_instance *fi) ++{ ++ return container_of(fi, struct mtp_instance, func_inst); ++} ++ ++static int mtp_set_inst_name(struct usb_function_instance *fi, const char *name) ++{ ++ struct mtp_instance *fi_mtp; ++ char *ptr; ++ int name_len; ++ ++ name_len = strlen(name) + 1; ++ if (name_len > MAX_INST_NAME_LEN) ++ return -ENAMETOOLONG; ++ ++ ptr = kstrndup(name, name_len, GFP_KERNEL); ++ if (!ptr) ++ return -ENOMEM; ++ ++ fi_mtp = to_fi_mtp(fi); ++ fi_mtp->name = ptr; ++ ++ return 0; ++} ++ ++static void mtp_free_inst(struct usb_function_instance *fi) ++{ ++ struct mtp_instance *fi_mtp; ++ ++ fi_mtp = to_fi_mtp(fi); ++ kfree(fi_mtp->name); ++ mtp_cleanup(); ++ kfree(fi_mtp); ++} ++ ++struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config) ++{ ++ struct mtp_instance *fi_mtp; ++ int ret = 0; ++ ++ fi_mtp = kzalloc(sizeof(*fi_mtp), GFP_KERNEL); ++ if (!fi_mtp) ++ return ERR_PTR(-ENOMEM); ++ fi_mtp->func_inst.set_inst_name = mtp_set_inst_name; ++ fi_mtp->func_inst.free_func_inst = mtp_free_inst; ++ ++ if (mtp_config) { ++ ret = mtp_setup_configfs(fi_mtp); ++ if (ret) { ++ kfree(fi_mtp); ++ pr_err("Error setting MTP\n"); ++ return ERR_PTR(ret); ++ } ++ } else ++ fi_mtp->dev = _mtp_dev; ++ ++ config_group_init_type_name(&fi_mtp->func_inst.group, ++ "", &mtp_func_type); ++ ++ return &fi_mtp->func_inst; ++} ++EXPORT_SYMBOL_GPL(alloc_inst_mtp_ptp); ++ ++static struct usb_function_instance *mtp_alloc_inst(void) ++{ ++ return alloc_inst_mtp_ptp(true); ++} ++ ++static int mtp_ctrlreq_configfs(struct usb_function *f, ++ const struct usb_ctrlrequest *ctrl) ++{ ++ return mtp_ctrlrequest(f->config->cdev, ctrl); ++} ++ ++static void mtp_free(struct usb_function *f) ++{ ++ /*NO-OP: no function specific resource allocation in mtp_alloc*/ ++} ++ ++struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi, ++ bool mtp_config) ++{ ++ struct mtp_instance *fi_mtp = to_fi_mtp(fi); ++ struct mtp_dev *dev; ++ ++ /* ++ * PTP piggybacks on MTP function so make sure we have ++ * created MTP function before we associate this PTP ++ * function with a gadget configuration. ++ */ ++ if (fi_mtp->dev == NULL) { ++ pr_err("Error: Create MTP function before linking" ++ " PTP function with a gadget configuration\n"); ++ pr_err("\t1: Delete existing PTP function if any\n"); ++ pr_err("\t2: Create MTP function\n"); ++ pr_err("\t3: Create and symlink PTP function" ++ " with a gadget configuration\n"); ++ return NULL; ++ } ++ ++ dev = fi_mtp->dev; ++ dev->function.name = DRIVER_NAME; ++ dev->function.strings = mtp_strings; ++ if (mtp_config) { ++ dev->function.fs_descriptors = fs_mtp_descs; ++ dev->function.hs_descriptors = hs_mtp_descs; ++ } else { ++ dev->function.fs_descriptors = fs_ptp_descs; ++ dev->function.hs_descriptors = hs_ptp_descs; ++ } ++ dev->function.bind = mtp_function_bind; ++ dev->function.unbind = mtp_function_unbind; ++ dev->function.set_alt = mtp_function_set_alt; ++ dev->function.disable = mtp_function_disable; ++ dev->function.setup = mtp_ctrlreq_configfs; ++ dev->function.free_func = mtp_free; ++ ++ return &dev->function; ++} ++EXPORT_SYMBOL_GPL(function_alloc_mtp_ptp); ++ ++static struct usb_function *mtp_alloc(struct usb_function_instance *fi) ++{ ++ return function_alloc_mtp_ptp(fi, true); ++} ++ ++DECLARE_USB_FUNCTION_INIT(mtp, mtp_alloc_inst, mtp_alloc); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/usb/gadget/function/f_mtp.h b/drivers/usb/gadget/function/f_mtp.h +new file mode 100644 +index 0000000..7adb1ff +--- /dev/null ++++ b/drivers/usb/gadget/function/f_mtp.h +@@ -0,0 +1,18 @@ ++/* ++ * Copyright (C) 2014 Google, Inc. ++ * Author: Badhri Jagan Sridharan <badhri@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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. ++ * ++ */ ++ ++extern struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config); ++extern struct usb_function *function_alloc_mtp_ptp( ++ struct usb_function_instance *fi, bool mtp_config); +diff --git a/drivers/usb/gadget/function/f_ptp.c b/drivers/usb/gadget/function/f_ptp.c +new file mode 100644 +index 0000000..da3e4d5 +--- /dev/null ++++ b/drivers/usb/gadget/function/f_ptp.c +@@ -0,0 +1,38 @@ ++/* ++ * Gadget Function Driver for PTP ++ * ++ * Copyright (C) 2014 Google, Inc. ++ * Author: Badhri Jagan Sridharan <badhri@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/types.h> ++ ++#include <linux/configfs.h> ++#include <linux/usb/composite.h> ++ ++#include "f_mtp.h" ++ ++static struct usb_function_instance *ptp_alloc_inst(void) ++{ ++ return alloc_inst_mtp_ptp(false); ++} ++ ++static struct usb_function *ptp_alloc(struct usb_function_instance *fi) ++{ ++ return function_alloc_mtp_ptp(fi, false); ++} ++ ++DECLARE_USB_FUNCTION_INIT(ptp, ptp_alloc_inst, ptp_alloc); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Badhri Jagan Sridharan"); +diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c +index f13fc6a..f4286a4 100644 +--- a/drivers/usb/gadget/function/f_rndis.c ++++ b/drivers/usb/gadget/function/f_rndis.c +@@ -70,6 +70,16 @@ + * - MS-Windows drivers sometimes emit undocumented requests. + */ + ++static unsigned int rndis_dl_max_pkt_per_xfer = 3; ++module_param(rndis_dl_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(rndis_dl_max_pkt_per_xfer, ++ "Maximum packets per transfer for DL aggregation"); ++ ++static unsigned int rndis_ul_max_pkt_per_xfer = 3; ++module_param(rndis_ul_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(rndis_ul_max_pkt_per_xfer, ++ "Maximum packets per transfer for UL aggregation"); ++ + struct f_rndis { + struct gether port; + u8 ctrl_id, data_id; +@@ -378,7 +388,7 @@ static struct sk_buff *rndis_add_header(struct gether *port, + if (skb2) + rndis_add_hdr(skb2); + +- dev_kfree_skb(skb); ++ dev_kfree_skb_irq(skb); + return skb2; + } + +@@ -451,6 +461,7 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) + { + struct f_rndis *rndis = req->context; + int status; ++ rndis_init_msg_type *buf; + + /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ + // spin_lock(&dev->lock); +@@ -458,6 +469,21 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) + if (status < 0) + pr_err("RNDIS command error %d, %d/%d\n", + status, req->actual, req->length); ++ ++ buf = (rndis_init_msg_type *)req->buf; ++ ++ if (buf->MessageType == RNDIS_MSG_INIT) { ++ if (buf->MaxTransferSize > 2048) ++ rndis->port.multi_pkt_xfer = 1; ++ else ++ rndis->port.multi_pkt_xfer = 0; ++ pr_info_once("%s: MaxTransferSize: %d : Multi_pkt_txr: %s\n", ++ __func__, buf->MaxTransferSize, ++ rndis->port.multi_pkt_xfer ? "enabled" : ++ "disabled"); ++ if (rndis_dl_max_pkt_per_xfer <= 1) ++ rndis->port.multi_pkt_xfer = 0; ++ } + // spin_unlock(&dev->lock); + } + +@@ -799,6 +825,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) + + rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); + rndis_set_host_mac(rndis->config, rndis->ethaddr); ++ rndis_set_max_pkt_xfer(rndis->config, rndis_ul_max_pkt_per_xfer); + + if (rndis->manufacturer && rndis->vendorID && + rndis_set_param_vendor(rndis->config, rndis->vendorID, +@@ -843,6 +870,62 @@ fail: + return status; + } + ++static void ++rndis_old_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct f_rndis *rndis = func_to_rndis(f); ++ ++ rndis_deregister(rndis->config); ++ ++ usb_free_all_descriptors(f); ++ ++ kfree(rndis->notify_req->buf); ++ usb_ep_free_request(rndis->notify, rndis->notify_req); ++ ++ kfree(rndis); ++} ++ ++int ++rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], ++ u32 vendorID, const char *manufacturer, struct eth_dev *dev) ++{ ++ struct f_rndis *rndis; ++ int status; ++ ++ /* allocate and initialize one new instance */ ++ status = -ENOMEM; ++ rndis = kzalloc(sizeof *rndis, GFP_KERNEL); ++ if (!rndis) ++ goto fail; ++ ++ memcpy(rndis->ethaddr, ethaddr, ETH_ALEN); ++ rndis->vendorID = vendorID; ++ rndis->manufacturer = manufacturer; ++ ++ rndis->port.ioport = dev; ++ /* RNDIS activates when the host changes this filter */ ++ rndis->port.cdc_filter = 0; ++ ++ /* RNDIS has special (and complex) framing */ ++ rndis->port.header_len = sizeof(struct rndis_packet_msg_type); ++ rndis->port.wrap = rndis_add_header; ++ rndis->port.unwrap = rndis_rm_hdr; ++ ++ rndis->port.func.name = "rndis"; ++ /* descriptors are per-instance copies */ ++ rndis->port.func.bind = rndis_bind; ++ rndis->port.func.unbind = rndis_old_unbind; ++ rndis->port.func.set_alt = rndis_set_alt; ++ rndis->port.func.setup = rndis_setup; ++ rndis->port.func.disable = rndis_disable; ++ ++ status = usb_add_function(c, &rndis->port.func); ++ if (status) ++ kfree(rndis); ++fail: ++ return status; ++} ++ + void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net) + { + struct f_rndis_opts *opts; +@@ -993,6 +1076,8 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) + rndis->port.header_len = sizeof(struct rndis_packet_msg_type); + rndis->port.wrap = rndis_add_header; + rndis->port.unwrap = rndis_rm_hdr; ++ rndis->port.ul_max_pkts_per_xfer = rndis_ul_max_pkt_per_xfer; ++ rndis->port.dl_max_pkts_per_xfer = rndis_dl_max_pkt_per_xfer; + + rndis->port.func.name = "rndis"; + /* descriptors are per-instance copies */ +diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c +index f7b2032..4a4823b 100644 +--- a/drivers/usb/gadget/function/f_uac1.c ++++ b/drivers/usb/gadget/function/f_uac1.c +@@ -1,24 +1,61 @@ + /* +- * f_audio.c -- USB Audio class function driver +- * +- * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> +- * Copyright (C) 2008 Analog Devices, Inc ++ * f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API) + * +- * Enter bugs at http://blackfin.uclinux.org/ ++ * Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com> + * +- * Licensed under the GPL-2 or later. ++ * This driver doesn't expect any real Audio codec to be present ++ * on the device - the audio streams are simply sinked to and ++ * sourced from a virtual ALSA sound card created. ++ * ++ * This file is based on f_uac1.c which is ++ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> ++ * Copyright (C) 2008 Analog Devices, Inc ++ * ++ * 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/slab.h> +-#include <linux/kernel.h> ++#include <linux/usb/audio.h> + #include <linux/module.h> +-#include <linux/device.h> +-#include <linux/atomic.h> + ++#include <media/v4l2-dev.h> ++#include <media/v4l2-event.h> ++#include "uac_ex.h" ++#include "uac_v4l2_ex.h" + #include "u_uac1.h" + +-static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); +-static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); ++struct f_uac1 { ++ struct uac_device uac_dev; ++ u8 ac_intf, as_in_intf, as_out_intf; ++ u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */ ++}; ++ ++struct uac1_format_type_ii_discrete_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; ++ __u8 bFormatType; ++ __le16 wMaxBitRate; ++ __le16 wSamplesPerFrame; ++ __u8 bSamFreqType; ++ __u8 tSamFreq[4][3]; ++} __attribute__((packed)); ++ ++struct uac1_format_type_ii_ac3_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubtype; ++ __le16 wFormatTag; ++ __u8 bmBSID[4]; ++ __u8 bmAC3Features; ++} __attribute__((packed)); ++ ++static inline struct f_uac1 *func_to_uac1(struct usb_function *f) ++{ ++ return container_of(f, struct f_uac1, uac_dev.func); ++} + + /* + * DESCRIPTORS ... most are static, but strings and full +@@ -26,12 +63,28 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); + */ + + /* +- * We have two interfaces- AudioControl and AudioStreaming +- * TODO: only supcard playback currently ++ * We have three interfaces - one AudioControl and two AudioStreaming ++ * ++ * The driver implements a simple UAC_1 topology. ++ * USB-OUT -> IT_1 -> OT_2 -> ALSA_Capture ++ * ALSA_Playback -> IT_3 -> OT_4 -> USB-IN + */ +-#define F_AUDIO_AC_INTERFACE 0 +-#define F_AUDIO_AS_INTERFACE 1 +-#define F_AUDIO_NUM_INTERFACES 2 ++#define F_AUDIO_AC_INTERFACE 0 ++#define F_AUDIO_AS_OUT_INTERFACE 1 ++#define F_AUDIO_AS_IN_INTERFACE 2 ++/* Number of streaming interfaces */ ++#define F_AUDIO_NUM_INTERFACES 1 ++ ++static struct usb_interface_assoc_descriptor uac_iad = { ++ .bLength = sizeof(uac_iad), ++ .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, ++ .bFirstInterface = 0, ++ .bInterfaceCount = 2, ++ .bFunctionClass = USB_CLASS_AUDIO, ++ .bFunctionSubClass = 0x00, ++ .bFunctionProtocol = 0x00, ++ .iFunction = 0, ++}; + + /* B.3.1 Standard AC Interface Descriptor */ + static struct usb_interface_descriptor ac_interface_desc = { +@@ -42,89 +95,96 @@ static struct usb_interface_descriptor ac_interface_desc = { + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + }; + +-DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); ++/* ++ * The number of AudioStreaming and MIDIStreaming interfaces ++ * in the Audio Interface Collection ++ */ ++DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); + + #define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) +-/* 1 input terminal, 1 output terminal and 1 feature unit */ +-#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \ +- + UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0)) ++ ++/* 2 input terminals and 2 output terminals */ ++#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ ++ + 1*UAC_DT_INPUT_TERMINAL_SIZE + 1*UAC_DT_OUTPUT_TERMINAL_SIZE) ++ + /* B.3.2 Class-Specific AC Interface Descriptor */ +-static struct uac1_ac_header_descriptor_2 ac_header_desc = { ++static struct uac1_ac_header_descriptor_1 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_LENGTH, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_HEADER, +- .bcdADC = __constant_cpu_to_le16(0x0100), +- .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), ++ .bcdADC = cpu_to_le16(0x0100), ++ .wTotalLength = cpu_to_le16(UAC_DT_TOTAL_LENGTH), + .bInCollection = F_AUDIO_NUM_INTERFACES, + .baInterfaceNr = { +- [0] = F_AUDIO_AC_INTERFACE, +- [1] = F_AUDIO_AS_INTERFACE, ++ /* Interface number of the AudioStream interfaces */ ++ [0] = 1, + } + }; + +-#define INPUT_TERMINAL_ID 1 +-static struct uac_input_terminal_descriptor input_terminal_desc = { ++#define USB_OUT_IT_ID 1 ++static struct uac_input_terminal_descriptor usb_out_it_desc = { + .bLength = UAC_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, +- .bTerminalID = INPUT_TERMINAL_ID, +- .wTerminalType = UAC_TERMINAL_STREAMING, ++ .bTerminalID = USB_OUT_IT_ID, ++ .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), + .bAssocTerminal = 0, +- .wChannelConfig = 0x3, ++ .wChannelConfig = cpu_to_le16(0x3), + }; + +-DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); +- +-#define FEATURE_UNIT_ID 2 +-static struct uac_feature_unit_descriptor_0 feature_unit_desc = { +- .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), ++#define IO_OUT_OT_ID 2 ++static struct uac1_output_terminal_descriptor io_out_ot_desc = { ++ .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubtype = UAC_FEATURE_UNIT, +- .bUnitID = FEATURE_UNIT_ID, +- .bSourceID = INPUT_TERMINAL_ID, +- .bControlSize = 2, +- .bmaControls[0] = (UAC_FU_MUTE | UAC_FU_VOLUME), ++ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, ++ .bTerminalID = IO_OUT_OT_ID, ++ .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER), ++ .bAssocTerminal = 0, ++ .bSourceID = USB_OUT_IT_ID, + }; + +-static struct usb_audio_control mute_control = { +- .list = LIST_HEAD_INIT(mute_control.list), +- .name = "Mute Control", +- .type = UAC_FU_MUTE, +- /* Todo: add real Mute control code */ +- .set = generic_set_cmd, +- .get = generic_get_cmd, ++#define IO_IN_IT_ID 3 ++static struct uac_input_terminal_descriptor io_in_it_desc = { ++ .bLength = UAC_DT_INPUT_TERMINAL_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_INPUT_TERMINAL, ++ .bTerminalID = IO_IN_IT_ID, ++ .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE), ++ .bAssocTerminal = 0, ++ .wChannelConfig = cpu_to_le16(0x3), + }; + +-static struct usb_audio_control volume_control = { +- .list = LIST_HEAD_INIT(volume_control.list), +- .name = "Volume Control", +- .type = UAC_FU_VOLUME, +- /* Todo: add real Volume control code */ +- .set = generic_set_cmd, +- .get = generic_get_cmd, ++#define USB_IN_OT_ID 4 ++static struct uac1_output_terminal_descriptor usb_in_ot_desc = { ++ .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, ++ .bTerminalID = USB_IN_OT_ID, ++ .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), ++ .bAssocTerminal = 0, ++ .bSourceID = IO_IN_IT_ID, + }; + +-static struct usb_audio_control_selector feature_unit = { +- .list = LIST_HEAD_INIT(feature_unit.list), +- .id = FEATURE_UNIT_ID, +- .name = "Mute & Volume Control", +- .type = UAC_FEATURE_UNIT, +- .desc = (struct usb_descriptor_header *)&feature_unit_desc, ++/* B.4.1 Standard AS Interface Descriptor */ ++static struct usb_interface_descriptor as_out_interface_alt_0_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 0, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, + }; + +-#define OUTPUT_TERMINAL_ID 3 +-static struct uac1_output_terminal_descriptor output_terminal_desc = { +- .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, +- .bDescriptorType = USB_DT_CS_INTERFACE, +- .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, +- .bTerminalID = OUTPUT_TERMINAL_ID, +- .wTerminalType = UAC_OUTPUT_TERMINAL_SPEAKER, +- .bAssocTerminal = FEATURE_UNIT_ID, +- .bSourceID = FEATURE_UNIT_ID, ++static struct usb_interface_descriptor as_out_interface_alt_1_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bAlternateSetting = 1, ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, + }; + +-/* B.4.1 Standard AS Interface Descriptor */ +-static struct usb_interface_descriptor as_interface_alt_0_desc = { ++static struct usb_interface_descriptor as_in_interface_alt_0_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 0, +@@ -133,7 +193,7 @@ static struct usb_interface_descriptor as_interface_alt_0_desc = { + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, + }; + +-static struct usb_interface_descriptor as_interface_alt_1_desc = { ++static struct usb_interface_descriptor as_in_interface_alt_1_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 1, +@@ -142,26 +202,62 @@ static struct usb_interface_descriptor as_interface_alt_1_desc = { + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, + }; + ++static struct usb_interface_descriptor as_in_interface_alt_2_desc = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bAlternateSetting = 2, ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, ++}; ++ + /* B.4.2 Class-Specific AS Interface Descriptor */ +-static struct uac1_as_header_descriptor as_header_desc = { ++static struct uac1_as_header_descriptor as_in_header_desc = { + .bLength = UAC_DT_AS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_AS_GENERAL, +- .bTerminalLink = INPUT_TERMINAL_ID, ++ .bTerminalLink = USB_IN_OT_ID, + .bDelay = 1, +- .wFormatTag = UAC_FORMAT_TYPE_I_PCM, ++ .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM), ++}; ++ ++/* B.4.2 Class-Specific AS Interface Descriptor */ ++static struct uac1_as_header_descriptor as_in_header_2_desc = { ++ .bLength = UAC_DT_AS_HEADER_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_AS_GENERAL, ++ .bTerminalLink = USB_IN_OT_ID, ++ .bDelay = 1, ++ .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_II_AC3), ++}; ++ ++static struct uac1_format_type_ii_discrete_descriptor as_in_type_ii_ac3_desc = { ++ .bLength = sizeof(as_in_type_ii_ac3_desc), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_FORMAT_TYPE, ++ .bFormatType = UAC_FORMAT_TYPE_II, ++ .wMaxBitRate = 2, /*FIXME: how to set MaxBitRate value ?? */ ++ .wSamplesPerFrame = 16, /*FIXME: how to set SamplesPerFrame??*/ ++ .bSamFreqType = 4, ++}; ++ ++static struct uac1_format_type_ii_ac3_descriptor as_in_type_ii_ac3_spec_desc = { ++ .bLength = sizeof(as_in_type_ii_ac3_spec_desc), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_FORMAT_SPECIFIC, ++ .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_II_AC3), + }; + +-DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); ++DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(4); + +-static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { +- .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), ++static struct uac_format_type_i_discrete_descriptor_4 as_out_type_i_desc = { ++ .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(4), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, + .bSubframeSize = 2, + .bBitResolution = 16, +- .bSamFreqType = 1, ++ .bSamFreqType = 4, + }; + + /* Standard ISO OUT Endpoint Descriptor */ +@@ -175,53 +271,86 @@ static struct usb_endpoint_descriptor as_out_ep_desc = { + .bInterval = 4, + }; + ++static struct uac_format_type_i_discrete_descriptor_4 as_in_type_i_desc = { ++ .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(4), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubtype = UAC_FORMAT_TYPE, ++ .bFormatType = UAC_FORMAT_TYPE_I, ++ .bSubframeSize = 2, ++ .bBitResolution = 16, ++ .bSamFreqType = 4, ++}; ++ ++/* Standard ISO OUT Endpoint Descriptor */ ++static struct usb_endpoint_descriptor as_in_ep_desc = { ++ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_SYNC_ASYNC ++ | USB_ENDPOINT_XFER_ISOC, ++ .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), ++ .bInterval = 4, ++}; ++ + /* Class-specific AS ISO OUT Endpoint Descriptor */ +-static struct uac_iso_endpoint_descriptor as_iso_out_desc = { ++static struct uac_iso_endpoint_descriptor as_iso_in_desc = { + .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = UAC_EP_GENERAL, +- .bmAttributes = 1, +- .bLockDelayUnits = 1, +- .wLockDelay = __constant_cpu_to_le16(1), ++ .bmAttributes = 1, ++ .bLockDelayUnits = 0, ++ .wLockDelay = 0, + }; + + static struct usb_descriptor_header *f_audio_desc[] = { ++ (struct usb_descriptor_header *)&uac_iad, + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, +- +- (struct usb_descriptor_header *)&input_terminal_desc, +- (struct usb_descriptor_header *)&output_terminal_desc, +- (struct usb_descriptor_header *)&feature_unit_desc, +- +- (struct usb_descriptor_header *)&as_interface_alt_0_desc, +- (struct usb_descriptor_header *)&as_interface_alt_1_desc, +- (struct usb_descriptor_header *)&as_header_desc, +- +- (struct usb_descriptor_header *)&as_type_i_desc, +- +- (struct usb_descriptor_header *)&as_out_ep_desc, +- (struct usb_descriptor_header *)&as_iso_out_desc, ++ (struct usb_descriptor_header *)&io_in_it_desc, ++ (struct usb_descriptor_header *)&usb_in_ot_desc, ++ (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, ++ (struct usb_descriptor_header *)&as_in_interface_alt_1_desc, ++ (struct usb_descriptor_header *)&as_in_header_desc, ++ (struct usb_descriptor_header *)&as_in_type_i_desc, ++ (struct usb_descriptor_header *)&as_in_ep_desc, ++ (struct usb_descriptor_header *)&as_iso_in_desc, ++ (struct usb_descriptor_header *)&as_in_interface_alt_2_desc, ++ (struct usb_descriptor_header *)&as_in_header_2_desc, ++ (struct usb_descriptor_header *)&as_in_type_ii_ac3_desc, ++ (struct usb_descriptor_header *)&as_in_type_ii_ac3_spec_desc, ++ (struct usb_descriptor_header *)&as_in_ep_desc, ++ (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, + }; + + enum { + STR_AC_IF, +- STR_INPUT_TERMINAL, +- STR_INPUT_TERMINAL_CH_NAMES, +- STR_FEAT_DESC_0, +- STR_OUTPUT_TERMINAL, +- STR_AS_IF_ALT0, +- STR_AS_IF_ALT1, ++ STR_USB_OUT_IT, ++ STR_USB_OUT_IT_CH_NAMES, ++ STR_IO_OUT_OT, ++ STR_IO_IN_IT, ++ STR_IO_IN_IT_CH_NAMES, ++ STR_USB_IN_OT, ++ STR_AS_OUT_IF_ALT0, ++ STR_AS_OUT_IF_ALT1, ++ STR_AS_IN_IF_ALT0, ++ STR_AS_IN_IF_ALT1, ++ STR_AS_IN_IF_ALT2, + }; + + static struct usb_string strings_uac1[] = { +- [STR_AC_IF].s = "AC Interface", +- [STR_INPUT_TERMINAL].s = "Input terminal", +- [STR_INPUT_TERMINAL_CH_NAMES].s = "Channels", +- [STR_FEAT_DESC_0].s = "Volume control & mute", +- [STR_OUTPUT_TERMINAL].s = "Output terminal", +- [STR_AS_IF_ALT0].s = "AS Interface", +- [STR_AS_IF_ALT1].s = "AS Interface", ++ [STR_AC_IF].s = "Hisilicon HD Audio", ++ [STR_USB_OUT_IT].s = "Playback Input terminal", ++ [STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels", ++ [STR_IO_OUT_OT].s = "Playback Output terminal", ++ [STR_IO_IN_IT].s = "Capture Input terminal", ++ [STR_IO_IN_IT_CH_NAMES].s = "Capture Channels", ++ [STR_USB_IN_OT].s = "Capture Output terminal", ++ [STR_AS_OUT_IF_ALT0].s = "Playback Inactive", ++ [STR_AS_OUT_IF_ALT1].s = "Playback Active", ++ [STR_AS_IN_IF_ALT0].s = "Capture Inactive", ++ [STR_AS_IN_IF_ALT1].s = "Capture Active", ++ [STR_AS_IN_IF_ALT2].s = "Capture Active", + { }, + }; + +@@ -239,227 +368,23 @@ static struct usb_gadget_strings *uac1_strings[] = { + * This function is an ALSA sound card following USB Audio Class Spec 1.0. + */ + +-/*-------------------------------------------------------------------------*/ +-struct f_audio_buf { +- u8 *buf; +- int actual; +- struct list_head list; +-}; +- +-static struct f_audio_buf *f_audio_buffer_alloc(int buf_size) +-{ +- struct f_audio_buf *copy_buf; +- +- copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC); +- if (!copy_buf) +- return ERR_PTR(-ENOMEM); +- +- copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC); +- if (!copy_buf->buf) { +- kfree(copy_buf); +- return ERR_PTR(-ENOMEM); +- } +- +- return copy_buf; +-} +- +-static void f_audio_buffer_free(struct f_audio_buf *audio_buf) +-{ +- kfree(audio_buf->buf); +- kfree(audio_buf); +-} +-/*-------------------------------------------------------------------------*/ +- +-struct f_audio { +- struct gaudio card; +- +- /* endpoints handle full and/or high speeds */ +- struct usb_ep *out_ep; +- +- spinlock_t lock; +- struct f_audio_buf *copy_buf; +- struct work_struct playback_work; +- struct list_head play_queue; +- +- /* Control Set command */ +- struct list_head cs; +- u8 set_cmd; +- struct usb_audio_control *set_con; +-}; +- +-static inline struct f_audio *func_to_audio(struct usb_function *f) +-{ +- return container_of(f, struct f_audio, card.func); +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static void f_audio_playback_work(struct work_struct *data) +-{ +- struct f_audio *audio = container_of(data, struct f_audio, +- playback_work); +- struct f_audio_buf *play_buf; +- +- spin_lock_irq(&audio->lock); +- if (list_empty(&audio->play_queue)) { +- spin_unlock_irq(&audio->lock); +- return; +- } +- play_buf = list_first_entry(&audio->play_queue, +- struct f_audio_buf, list); +- list_del(&play_buf->list); +- spin_unlock_irq(&audio->lock); +- +- u_audio_playback(&audio->card, play_buf->buf, play_buf->actual); +- f_audio_buffer_free(play_buf); +-} +- +-static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) +-{ +- struct f_audio *audio = req->context; +- struct usb_composite_dev *cdev = audio->card.func.config->cdev; +- struct f_audio_buf *copy_buf = audio->copy_buf; +- struct f_uac1_opts *opts; +- int audio_buf_size; +- int err; +- +- opts = container_of(audio->card.func.fi, struct f_uac1_opts, +- func_inst); +- audio_buf_size = opts->audio_buf_size; +- +- if (!copy_buf) +- return -EINVAL; +- +- /* Copy buffer is full, add it to the play_queue */ +- if (audio_buf_size - copy_buf->actual < req->actual) { +- list_add_tail(©_buf->list, &audio->play_queue); +- schedule_work(&audio->playback_work); +- copy_buf = f_audio_buffer_alloc(audio_buf_size); +- if (IS_ERR(copy_buf)) +- return -ENOMEM; +- } +- +- memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual); +- copy_buf->actual += req->actual; +- audio->copy_buf = copy_buf; +- +- err = usb_ep_queue(ep, req, GFP_ATOMIC); +- if (err) +- ERROR(cdev, "%s queue req: %d\n", ep->name, err); +- +- return 0; +- +-} +- +-static void f_audio_complete(struct usb_ep *ep, struct usb_request *req) +-{ +- struct f_audio *audio = req->context; +- int status = req->status; +- u32 data = 0; +- struct usb_ep *out_ep = audio->out_ep; +- +- switch (status) { +- +- case 0: /* normal completion? */ +- if (ep == out_ep) +- f_audio_out_ep_complete(ep, req); +- else if (audio->set_con) { +- memcpy(&data, req->buf, req->length); +- audio->set_con->set(audio->set_con, audio->set_cmd, +- le16_to_cpu(data)); +- audio->set_con = NULL; +- } +- break; +- default: +- break; +- } +-} +- +-static int audio_set_intf_req(struct usb_function *f, +- const struct usb_ctrlrequest *ctrl) +-{ +- struct f_audio *audio = func_to_audio(f); +- struct usb_composite_dev *cdev = f->config->cdev; +- struct usb_request *req = cdev->req; +- u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); +- u16 len = le16_to_cpu(ctrl->wLength); +- u16 w_value = le16_to_cpu(ctrl->wValue); +- u8 con_sel = (w_value >> 8) & 0xFF; +- u8 cmd = (ctrl->bRequest & 0x0F); +- struct usb_audio_control_selector *cs; +- struct usb_audio_control *con; +- +- DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", +- ctrl->bRequest, w_value, len, id); +- +- list_for_each_entry(cs, &audio->cs, list) { +- if (cs->id == id) { +- list_for_each_entry(con, &cs->control, list) { +- if (con->type == con_sel) { +- audio->set_con = con; +- break; +- } +- } +- break; +- } +- } +- +- audio->set_cmd = cmd; +- req->context = audio; +- req->complete = f_audio_complete; +- +- return len; +-} +- +-static int audio_get_intf_req(struct usb_function *f, +- const struct usb_ctrlrequest *ctrl) +-{ +- struct f_audio *audio = func_to_audio(f); +- struct usb_composite_dev *cdev = f->config->cdev; +- struct usb_request *req = cdev->req; +- int value = -EOPNOTSUPP; +- u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); +- u16 len = le16_to_cpu(ctrl->wLength); +- u16 w_value = le16_to_cpu(ctrl->wValue); +- u8 con_sel = (w_value >> 8) & 0xFF; +- u8 cmd = (ctrl->bRequest & 0x0F); +- struct usb_audio_control_selector *cs; +- struct usb_audio_control *con; +- +- DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", +- ctrl->bRequest, w_value, len, id); +- +- list_for_each_entry(cs, &audio->cs, list) { +- if (cs->id == id) { +- list_for_each_entry(con, &cs->control, list) { +- if (con->type == con_sel && con->get) { +- value = con->get(con, cmd); +- break; +- } +- } +- break; +- } +- } +- +- req->context = audio; +- req->complete = f_audio_complete; +- len = min_t(size_t, sizeof(value), len); +- memcpy(req->buf, &value, len); +- +- return len; +-} +- + static int audio_set_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) + { +- struct usb_composite_dev *cdev = f->config->cdev; ++ //struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u16 ep = le16_to_cpu(ctrl->wIndex); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + +- DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", +- ctrl->bRequest, w_value, len, ep); ++ printk(KERN_EMERG "%s:bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", ++ __func__, ctrl->bRequest, w_value, len, ep); ++ ++ ++ if ((w_value >>8) != UAC_EP_CS_ATTR_SAMPLE_RATE) { ++ printk("!!!!! value = 0x%x\n", (w_value >>8)); ++ return value; ++ } + + switch (ctrl->bRequest) { + case UAC_SET_CUR: +@@ -489,16 +414,24 @@ static int audio_get_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) + { + struct usb_composite_dev *cdev = f->config->cdev; ++ struct f_uac1 *uac1 = func_to_uac1(f); ++ struct usb_request *req = uac1->uac_dev.control_req; ++ struct uac_device *uac = req->context; + int value = -EOPNOTSUPP; + u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + +- DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", +- ctrl->bRequest, w_value, len, ep); ++ printk(KERN_EMERG "%s:bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", ++ __func__, ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_GET_CUR: ++ if ((w_value >>8) == UAC_EP_CS_ATTR_SAMPLE_RATE) { ++ memcpy(req->buf, &uac->params.p_srate, 3); ++ } ++ value = len; ++ break; + case UAC_GET_MIN: + case UAC_GET_MAX: + case UAC_GET_RES: +@@ -517,41 +450,25 @@ static int + f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) + { + struct usb_composite_dev *cdev = f->config->cdev; +- struct usb_request *req = cdev->req; ++ struct f_uac1 *uac1 = func_to_uac1(f); ++ struct usb_request *req = uac1->uac_dev.control_req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + ++ printk(KERN_EMERG "%s:\n", __func__); ++ + /* composite driver infrastructure handles everything; interface + * activation uses set_alt(). + */ + switch (ctrl->bRequestType) { +- case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: +- value = audio_set_intf_req(f, ctrl); +- break; +- +- case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE: +- value = audio_get_intf_req(f, ctrl); +- break; +- + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: +- value = audio_set_endpoint_req(f, ctrl); +- break; +- +- case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: +- value = audio_get_endpoint_req(f, ctrl); +- break; +- +- default: +- ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", +- ctrl->bRequestType, ctrl->bRequest, +- w_value, w_index, w_length); +- } +- ++ { ++ value = audio_set_endpoint_req(f, ctrl); + /* respond with data transfer or status phase? */ + if (value >= 0) { +- DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n", ++ printk(KERN_EMERG "audio req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; +@@ -559,7 +476,29 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "audio response on err %d\n", value); +- } ++ } ++ } ++ break; ++ ++ case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: ++ value = audio_get_endpoint_req(f, ctrl); ++ if (value >= 0) { ++ printk(KERN_EMERG "audio req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ req->zero = 0; ++ req->length = value; ++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) ++ ERROR(cdev, "audio response on err %d\n", value); ++ } ++ break; ++ ++ default: ++ printk(KERN_EMERG "invalid control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ } + + /* device either stalls (value < 0) or reports success */ + return value; +@@ -567,343 +506,426 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) + + static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) + { +- struct f_audio *audio = func_to_audio(f); +- struct usb_composite_dev *cdev = f->config->cdev; +- struct usb_ep *out_ep = audio->out_ep; +- struct usb_request *req; +- struct f_uac1_opts *opts; +- int req_buf_size, req_count, audio_buf_size; +- int i = 0, err = 0; ++ struct f_uac1 *uac1 = func_to_uac1(f); ++ int ret = 0; + +- DBG(cdev, "intf %d, alt %d\n", intf, alt); ++ printk(KERN_EMERG "%s:intf:%d alt=%d\n", __func__, intf, alt); + +- opts = container_of(f->fi, struct f_uac1_opts, func_inst); +- req_buf_size = opts->req_buf_size; +- req_count = opts->req_count; +- audio_buf_size = opts->audio_buf_size; +- +- if (intf == 1) { +- if (alt == 1) { +- usb_ep_enable(out_ep); +- out_ep->driver_data = audio; +- audio->copy_buf = f_audio_buffer_alloc(audio_buf_size); +- if (IS_ERR(audio->copy_buf)) +- return -ENOMEM; +- +- /* +- * allocate a bunch of read buffers +- * and queue them all at once. +- */ +- for (i = 0; i < req_count && err == 0; i++) { +- req = usb_ep_alloc_request(out_ep, GFP_ATOMIC); +- if (req) { +- req->buf = kzalloc(req_buf_size, +- GFP_ATOMIC); +- if (req->buf) { +- req->length = req_buf_size; +- req->context = audio; +- req->complete = +- f_audio_complete; +- err = usb_ep_queue(out_ep, +- req, GFP_ATOMIC); +- if (err) +- ERROR(cdev, +- "%s queue req: %d\n", +- out_ep->name, err); +- } else +- err = -ENOMEM; +- } else +- err = -ENOMEM; +- } ++ /* No i/f has more than 2 alt settings */ ++ if (alt > 1) { ++ printk(KERN_EMERG "%s:%d Error!\n", __func__, __LINE__); ++ return -EINVAL; ++ } + ++ if (intf == uac1->ac_intf) { ++ /* Control I/f has only 1 AltSetting - 0 */ ++ if (alt) { ++ printk(KERN_EMERG "%s:%d Error!\n", __func__, __LINE__); ++ return -EINVAL; ++ } ++ ++ /*FIXME: notify userpace uac has been connected*/ ++ { ++ struct uac_device *uac_dev = &uac1->uac_dev; ++ struct v4l2_event v4l2_event; ++ ++ memset(&v4l2_event, 0, sizeof(v4l2_event)); ++ v4l2_event.type = UAC_EVENT_CONNECT; ++ v4l2_event_queue(uac_dev->vdev, &v4l2_event); ++ ++ printk(KERN_EMERG "%s: trigger connect\n", __func__); ++ } ++ ++ return 0; ++ } ++ ++ if (intf == uac1->as_out_intf) { ++ uac1->as_out_alt = alt; ++ ++ if (alt) { + } else { +- struct f_audio_buf *copy_buf = audio->copy_buf; +- if (copy_buf) { +- list_add_tail(©_buf->list, +- &audio->play_queue); +- schedule_work(&audio->playback_work); +- } + } ++ } else if (intf == uac1->as_in_intf) { ++ uac1->as_in_alt = alt; ++ ++ if (alt) { ++ /*FIXME: notify userpace to start audio streaming*/ ++ { ++ struct uac_device *uac_dev = &uac1->uac_dev; ++ struct v4l2_event v4l2_event; ++ ++ memset(&v4l2_event, 0, sizeof(v4l2_event)); ++ v4l2_event.type = UAC_EVENT_STREAMON; ++ v4l2_event_queue(uac_dev->vdev, &v4l2_event); ++ ++ printk(KERN_EMERG "%s: trigger UAC_EVENT_STREAMON\n", __func__); ++ } ++ ++ ret = uac_device_start_playback(&uac1->uac_dev); ++ } else { ++ /*FIXME: notify userpace to start audio streaming*/ ++ { ++ struct uac_device *uac_dev = &uac1->uac_dev; ++ struct v4l2_event v4l2_event; ++ ++ memset(&v4l2_event, 0, sizeof(v4l2_event)); ++ v4l2_event.type = UAC_EVENT_STREAMOFF; ++ v4l2_event_queue(uac_dev->vdev, &v4l2_event); ++ ++ printk(KERN_EMERG "%s: trigger UAC_EVENT_STREAMOFF\n", __func__); ++ } ++ ++ uac_device_stop_playback(&uac1->uac_dev); ++ } ++ } else { ++ printk(KERN_EMERG "%s:%d Error!\n", __func__, __LINE__); ++ return -EINVAL; + } + +- return err; ++ return ret; + } + ++static int f_audio_get_alt(struct usb_function *f, unsigned intf) ++{ ++ struct f_uac1 *uac1 = func_to_uac1(f); ++ ++ printk(KERN_EMERG "%s:\n", __func__); ++ ++ if (intf == uac1->ac_intf) { ++ printk(KERN_EMERG "%s:ac_intf = %d\n", __func__, uac1->ac_intf); ++ return uac1->ac_alt; ++ } ++ else if (intf == uac1->as_out_intf) { ++ printk(KERN_EMERG "%s:as_out_intf = %d\n", __func__, uac1->as_out_intf); ++ return uac1->as_out_alt; ++ } ++ else if (intf == uac1->as_in_intf) { ++ printk(KERN_EMERG "%s:as_in_intf = %d\n", __func__, uac1->as_in_intf); ++ return uac1->as_in_alt; ++ } ++ else ++ printk(KERN_EMERG "%s:%d Invalid Interface %d!\n", ++ __func__, __LINE__, intf); ++ ++ return -EINVAL; ++} ++ ++ + static void f_audio_disable(struct usb_function *f) + { +- return; ++ struct f_uac1 *uac1 = func_to_uac1(f); ++ ++ uac1->as_out_alt = 0; ++ uac1->as_in_alt = 0; ++ ++ ++ /*FIXME: notify userpace to disconnect*/ ++ { ++ struct uac_device *uac_dev = &uac1->uac_dev; ++ struct v4l2_event v4l2_event; ++ ++ memset(&v4l2_event, 0, sizeof(v4l2_event)); ++ v4l2_event.type = UAC_EVENT_DISCONNECT; ++ v4l2_event_queue(uac_dev->vdev, &v4l2_event); ++ } + } + + /*-------------------------------------------------------------------------*/ + +-static void f_audio_build_desc(struct f_audio *audio) ++static int ++__uac_register_video(struct usb_composite_dev *dev, struct uac_device *uac_dev) + { +- struct gaudio *card = &audio->card; +- u8 *sam_freq; +- int rate; ++ struct usb_composite_dev *cdev = dev; ++ struct video_device *video; + +- /* Set channel numbers */ +- input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card); +- as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card); ++ /* TODO reference counting. */ ++ video = video_device_alloc(); ++ if (video == NULL) ++ return -ENOMEM; + +- /* Set sample rates */ +- rate = u_audio_get_playback_rate(card); +- sam_freq = as_type_i_desc.tSamFreq[0]; +- memcpy(sam_freq, &rate, 3); ++ video->v4l2_dev = &uac_dev->v4l2_dev; ++ video->fops = &uac_v4l2_fops; ++ video->ioctl_ops = &uac_v4l2_ioctl_ops; ++ video->release = video_device_release; ++ video->vfl_dir = VFL_DIR_TX; ++ strlcpy(video->name, cdev->gadget->name, sizeof(video->name)); ++ ++ uac_dev->vdev = video; ++ video_set_drvdata(video, uac_dev); ++ ++ return video_register_device(video, VFL_TYPE_GRABBER, -1); ++} + +- /* Todo: Set Sample bits and other parameters */ ++static void ++__uac_function_ep0_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct uac_device *uac = req->context; ++ struct v4l2_event v4l2_event; ++ struct uac_event *uac_event = (void *)&v4l2_event.u.data; ++ ++ /*FIXME: notify userpace to set audio resolution */ ++ if (req->actual == 3) ++ { ++ int rate = ((unsigned char*)req->buf)[0] | (((unsigned char*)req->buf)[1] << 8) | (((unsigned char*)req->buf)[2] << 16); ++ ++ uac->params.p_srate = rate; ++ uac->p_pktsize = (rate / 1000) * uac->params.p_ssize * num_channels(uac->params.p_chmask); + +- return; ++ memset(&v4l2_event, 0, sizeof(v4l2_event)); ++ v4l2_event.type = UAC_EVENT_DATA; ++ uac_event->data.length = req->actual; ++ memcpy(&uac_event->data.data, req->buf, req->actual); ++ v4l2_event_queue(uac->vdev, &v4l2_event); ++ ++ printk("%s:rate=%d srate = %d pkt_size=%d\n",__func__, rate, uac->params.p_srate, uac->p_pktsize); ++ } ++} ++ ++static void __print_params(struct uac_params *params) ++{ ++ printk(KERN_EMERG "pamrams: p_chmask=%d p_srate=%d(hz) p_ssize=%d \ ++ req_number=%d\n", ++ params->p_chmask, ++ params->p_srate, ++ params->p_ssize, ++ params->req_number ++ ); + } + + /* audio function driver setup/binding */ +-static int +-f_audio_bind(struct usb_configuration *c, struct usb_function *f) ++static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) + { +- struct usb_composite_dev *cdev = c->cdev; +- struct f_audio *audio = func_to_audio(f); +- struct usb_string *us; +- int status; +- struct usb_ep *ep = NULL; +- struct f_uac1_opts *audio_opts; ++ struct usb_composite_dev *cdev = c->cdev; ++ struct usb_gadget *gadget = cdev->gadget; ++ struct f_uac1 *uac1 = func_to_uac1(f); ++ struct uac_device *uac_dev = &uac1->uac_dev; ++ struct f_uac1_opts *audio_opts; ++ struct usb_ep *ep = NULL; ++ struct usb_string *us; ++ u8 *sam_freq; ++ int rate; ++ int status; + + audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); +- audio->card.gadget = c->cdev->gadget; +- audio_opts->card = &audio->card; +- /* set up ASLA audio devices */ +- if (!audio_opts->bound) { +- status = gaudio_setup(&audio->card); +- if (status < 0) +- return status; +- audio_opts->bound = true; +- } ++ + us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1)); + if (IS_ERR(us)) + return PTR_ERR(us); ++ ++ uac_iad.iFunction = us[STR_AC_IF].id; + ac_interface_desc.iInterface = us[STR_AC_IF].id; +- input_terminal_desc.iTerminal = us[STR_INPUT_TERMINAL].id; +- input_terminal_desc.iChannelNames = us[STR_INPUT_TERMINAL_CH_NAMES].id; +- feature_unit_desc.iFeature = us[STR_FEAT_DESC_0].id; +- output_terminal_desc.iTerminal = us[STR_OUTPUT_TERMINAL].id; +- as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id; +- as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id; ++ usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id; ++ usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id; ++ io_out_ot_desc.iTerminal = us[STR_IO_OUT_OT].id; ++ as_out_interface_alt_0_desc.iInterface = us[STR_AS_OUT_IF_ALT0].id; ++ as_out_interface_alt_1_desc.iInterface = us[STR_AS_OUT_IF_ALT1].id; ++ io_in_it_desc.iTerminal = us[STR_IO_IN_IT].id; ++ io_in_it_desc.iChannelNames = us[STR_IO_IN_IT_CH_NAMES].id; ++ usb_in_ot_desc.iTerminal = us[STR_USB_IN_OT].id; ++ as_in_interface_alt_0_desc.iInterface = us[STR_AS_IN_IF_ALT0].id; ++ as_in_interface_alt_1_desc.iInterface = us[STR_AS_IN_IF_ALT1].id; ++ as_in_interface_alt_2_desc.iInterface = us[STR_AS_IN_IF_ALT2].id; ++ ++ /* Set channel numbers */ ++ usb_out_it_desc.bNrChannels = num_channels(audio_opts->c_chmask); ++ usb_out_it_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask); ++ as_out_type_i_desc.bNrChannels = num_channels(audio_opts->c_chmask); ++ as_out_type_i_desc.bSubframeSize = audio_opts->c_ssize; ++ as_out_type_i_desc.bBitResolution = audio_opts->c_ssize * 8; ++ io_in_it_desc.bNrChannels = num_channels(audio_opts->p_chmask); ++ io_in_it_desc.wChannelConfig = cpu_to_le16(audio_opts->p_chmask); ++ as_in_type_i_desc.bNrChannels = num_channels(audio_opts->p_chmask); ++ as_in_type_i_desc.bSubframeSize = audio_opts->p_ssize; ++ as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8; ++ as_out_ep_desc.wMaxPacketSize = 48*audio_opts->c_ssize * num_channels(audio_opts->c_chmask); ++ /*FIXME: max_resolution * sample * channnel*/ ++ as_in_ep_desc.wMaxPacketSize = 48*audio_opts->p_ssize * num_channels(audio_opts->p_chmask); + + +- f_audio_build_desc(audio); ++ /* Set sample rates */ ++ /*rate = audio_opts->c_srate; ++ sam_freq = as_out_type_i_desc.tSamFreq[0]; ++ memcpy(sam_freq, &rate, 3); ++ rate = audio_opts->p_srate; ++ sam_freq = as_in_type_i_desc.tSamFreq[0]; ++ memcpy(sam_freq, &rate, 3); ++ */ ++ rate = 8000; ++ sam_freq = as_out_type_i_desc.tSamFreq[0]; ++ memcpy(sam_freq, &rate, 3); ++ sam_freq = as_in_type_i_desc.tSamFreq[0]; ++ memcpy(sam_freq, &rate, 3); ++ sam_freq = as_in_type_ii_ac3_desc.tSamFreq[0]; ++ memcpy(sam_freq, &rate, 3); ++ ++ rate = 16000; ++ sam_freq = as_out_type_i_desc.tSamFreq[1]; ++ memcpy(sam_freq, &rate, 3); ++ sam_freq = as_in_type_i_desc.tSamFreq[1]; ++ memcpy(sam_freq, &rate, 3); ++ sam_freq = as_in_type_ii_ac3_desc.tSamFreq[1]; ++ memcpy(sam_freq, &rate, 3); ++ ++ rate = 32000; ++ sam_freq = as_out_type_i_desc.tSamFreq[2]; ++ memcpy(sam_freq, &rate, 3); ++ sam_freq = as_in_type_i_desc.tSamFreq[2]; ++ memcpy(sam_freq, &rate, 3); ++ sam_freq = as_in_type_ii_ac3_desc.tSamFreq[2]; ++ memcpy(sam_freq, &rate, 3); ++ ++ rate = 48000; ++ sam_freq = as_out_type_i_desc.tSamFreq[3]; ++ memcpy(sam_freq, &rate, 3); ++ sam_freq = as_in_type_i_desc.tSamFreq[3]; ++ memcpy(sam_freq, &rate, 3); ++ sam_freq = as_in_type_ii_ac3_desc.tSamFreq[3]; ++ memcpy(sam_freq, &rate, 3); + + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); +- if (status < 0) ++ if (status < 0) { ++ printk(KERN_EMERG "%s:%d\n", __func__, __LINE__); + goto fail; ++ } ++ ++ uac_iad.bFirstInterface = status; + ac_interface_desc.bInterfaceNumber = status; ++ uac1->ac_intf = status; ++ uac1->ac_alt = 0; ++ printk(KERN_EMERG "ac_intf:%d\n", status); + + status = usb_interface_id(c, f); +- if (status < 0) ++ if (status < 0) { ++ printk(KERN_EMERG "%s:%d\n", __func__, __LINE__); + goto fail; +- as_interface_alt_0_desc.bInterfaceNumber = status; +- as_interface_alt_1_desc.bInterfaceNumber = status; ++ } ++ ++ as_in_interface_alt_0_desc.bInterfaceNumber = status; ++ as_in_interface_alt_1_desc.bInterfaceNumber = status; ++ as_in_interface_alt_2_desc.bInterfaceNumber = status; ++ ++ ac_header_desc.baInterfaceNr[0] = status; ++ ++ uac1->as_in_intf = status; ++ uac1->as_in_alt = 0; ++ ++ printk(KERN_EMERG "as_in_intf:%d\n", status); ++ ++ uac_dev->gadget = gadget; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); +- if (!ep) ++ if (!ep) { ++ printk(KERN_EMERG "%s:%d\n", __func__,__LINE__); + goto fail; +- audio->out_ep = ep; +- audio->out_ep->desc = &as_out_ep_desc; +- ep->driver_data = cdev; /* claim */ ++ } + +- status = -ENOMEM; ++ uac_dev->out_ep = ep; ++ uac_dev->out_ep->desc = &as_out_ep_desc; ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc); ++ if (!ep) { ++ printk(KERN_EMERG "%s:%d\n", __func__,__LINE__); ++ goto fail; ++ } ++ uac_dev->in_ep = ep; ++ uac_dev->in_ep->desc = &as_in_ep_desc; + + /* copy descriptors, and track endpoint copies */ + status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL); +- if (status) ++ if (status) { ++ printk(KERN_EMERG "%s:%d\n", __func__,__LINE__); + goto fail; +- return 0; ++ } + +-fail: +- gaudio_cleanup(&audio->card); +- if (ep) +- ep->driver_data = NULL; +- return status; +-} ++ uac_dev->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize); ++ uac_dev->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize); ++ uac_dev->params.c_chmask = audio_opts->c_chmask; ++ uac_dev->params.c_srate = audio_opts->c_srate; ++ uac_dev->params.c_ssize = audio_opts->c_ssize; ++ uac_dev->params.p_chmask = audio_opts->p_chmask; ++ uac_dev->params.p_srate = audio_opts->p_srate; ++ uac_dev->params.p_ssize = audio_opts->p_ssize; ++ uac_dev->params.req_number = audio_opts->req_number; + +-/*-------------------------------------------------------------------------*/ ++ __print_params(&uac_dev->params); + +-static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) +-{ +- con->data[cmd] = value; ++ status = uac_device_setup(uac_dev); ++ if (status) ++ goto err_card_register; ++ ++ /* Preallocate control endpoint request. */ ++ uac_dev->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); ++ uac_dev->control_buf = kmalloc(64, GFP_KERNEL); ++ if (uac_dev->control_req == NULL || uac_dev->control_buf == NULL) { ++ status = -ENOMEM; ++ goto error; ++ } ++ ++ uac_dev->control_req->buf = uac_dev->control_buf; ++ uac_dev->control_req->complete = __uac_function_ep0_complete; ++ uac_dev->control_req->context = uac_dev; ++ ++ /*FIXME: add v4l2 interface*/ ++ { ++ struct uac_device *uac_dev = &uac1->uac_dev; ++ ++ if (uac_dev) { ++ if (v4l2_device_register(&cdev->gadget->dev, &(uac_dev->v4l2_dev))) { ++ printk(KERN_INFO "v4l2_device_register failed\n"); ++ goto error; ++ } + +- return 0; +-} ++ /* Initialise queue buffer. */ ++ status = uac_queue_init(&(uac_dev->queue)); ++ if (status < 0) ++ goto error; + +-static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) +-{ +- return con->data[cmd]; +-} ++ /* Register a V4L2 device. */ ++ status = __uac_register_video(cdev, uac_dev); ++ if (status < 0) { ++ printk(KERN_INFO "Unable to register video device\n"); ++ goto error; ++ } ++ } ++ } + +-/* Todo: add more control selecotor dynamically */ +-static int control_selector_init(struct f_audio *audio) +-{ +- INIT_LIST_HEAD(&audio->cs); +- list_add(&feature_unit.list, &audio->cs); ++ return 0; + +- INIT_LIST_HEAD(&feature_unit.control); +- list_add(&mute_control.list, &feature_unit.control); +- list_add(&volume_control.list, &feature_unit.control); ++error: ++ if (&uac1->uac_dev) { ++ v4l2_device_unregister(&(uac1->uac_dev.v4l2_dev)); ++ if ((uac1->uac_dev.vdev)) ++ video_device_release((uac1->uac_dev.vdev)); ++ } + +- volume_control.data[UAC__CUR] = 0xffc0; +- volume_control.data[UAC__MIN] = 0xe3a0; +- volume_control.data[UAC__MAX] = 0xfff0; +- volume_control.data[UAC__RES] = 0x0030; ++ if (&uac1->uac_dev) { ++ usb_ep_free_request(cdev->gadget->ep0, uac_dev->control_req); ++ kfree(uac_dev->control_buf); ++ } + +- return 0; ++err_card_register: ++ usb_free_all_descriptors(f); ++fail: ++ return status; + } + ++/*-------------------------------------------------------------------------*/ ++ + static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item) + { + return container_of(to_config_group(item), struct f_uac1_opts, +- func_inst.group); +-} +- +-CONFIGFS_ATTR_STRUCT(f_uac1_opts); +-CONFIGFS_ATTR_OPS(f_uac1_opts); +- +-static void f_uac1_attr_release(struct config_item *item) +-{ +- struct f_uac1_opts *opts = to_f_uac1_opts(item); +- +- usb_put_function_instance(&opts->func_inst); ++ func_inst.group); + } + +-static struct configfs_item_operations f_uac1_item_ops = { +- .release = f_uac1_attr_release, +- .show_attribute = f_uac1_opts_attr_show, +- .store_attribute = f_uac1_opts_attr_store, +-}; +- +-#define UAC1_INT_ATTRIBUTE(name) \ +-static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts, \ +- char *page) \ +-{ \ +- int result; \ +- \ +- mutex_lock(&opts->lock); \ +- result = sprintf(page, "%u\n", opts->name); \ +- mutex_unlock(&opts->lock); \ +- \ +- return result; \ +-} \ +- \ +-static ssize_t f_uac1_opts_##name##_store(struct f_uac1_opts *opts, \ +- const char *page, size_t len) \ +-{ \ +- int ret; \ +- u32 num; \ +- \ +- mutex_lock(&opts->lock); \ +- if (opts->refcnt) { \ +- ret = -EBUSY; \ +- goto end; \ +- } \ +- \ +- ret = kstrtou32(page, 0, &num); \ +- if (ret) \ +- goto end; \ +- \ +- opts->name = num; \ +- ret = len; \ +- \ +-end: \ +- mutex_unlock(&opts->lock); \ +- return ret; \ +-} \ +- \ +-static struct f_uac1_opts_attribute f_uac1_opts_##name = \ +- __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ +- f_uac1_opts_##name##_show, \ +- f_uac1_opts_##name##_store) +- +-UAC1_INT_ATTRIBUTE(req_buf_size); +-UAC1_INT_ATTRIBUTE(req_count); +-UAC1_INT_ATTRIBUTE(audio_buf_size); +- +-#define UAC1_STR_ATTRIBUTE(name) \ +-static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts, \ +- char *page) \ +-{ \ +- int result; \ +- \ +- mutex_lock(&opts->lock); \ +- result = sprintf(page, "%s\n", opts->name); \ +- mutex_unlock(&opts->lock); \ +- \ +- return result; \ +-} \ +- \ +-static ssize_t f_uac1_opts_##name##_store(struct f_uac1_opts *opts, \ +- const char *page, size_t len) \ +-{ \ +- int ret = -EBUSY; \ +- char *tmp; \ +- \ +- mutex_lock(&opts->lock); \ +- if (opts->refcnt) \ +- goto end; \ +- \ +- tmp = kstrndup(page, len, GFP_KERNEL); \ +- if (tmp) { \ +- ret = -ENOMEM; \ +- goto end; \ +- } \ +- if (opts->name##_alloc) \ +- kfree(opts->name); \ +- opts->name##_alloc = true; \ +- opts->name = tmp; \ +- ret = len; \ +- \ +-end: \ +- mutex_unlock(&opts->lock); \ +- return ret; \ +-} \ +- \ +-static struct f_uac1_opts_attribute f_uac1_opts_##name = \ +- __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ +- f_uac1_opts_##name##_show, \ +- f_uac1_opts_##name##_store) +- +-UAC1_STR_ATTRIBUTE(fn_play); +-UAC1_STR_ATTRIBUTE(fn_cap); +-UAC1_STR_ATTRIBUTE(fn_cntl); +- +-static struct configfs_attribute *f_uac1_attrs[] = { +- &f_uac1_opts_req_buf_size.attr, +- &f_uac1_opts_req_count.attr, +- &f_uac1_opts_audio_buf_size.attr, +- &f_uac1_opts_fn_play.attr, +- &f_uac1_opts_fn_cap.attr, +- &f_uac1_opts_fn_cntl.attr, +- NULL, +-}; +- +-static struct config_item_type f_uac1_func_type = { +- .ct_item_ops = &f_uac1_item_ops, +- .ct_attrs = f_uac1_attrs, +- .ct_owner = THIS_MODULE, +-}; +- + static void f_audio_free_inst(struct usb_function_instance *f) + { + struct f_uac1_opts *opts; + + opts = container_of(f, struct f_uac1_opts, func_inst); +- gaudio_cleanup(opts->card); +- if (opts->fn_play_alloc) +- kfree(opts->fn_play); +- if (opts->fn_cap_alloc) +- kfree(opts->fn_cap); +- if (opts->fn_cntl_alloc) +- kfree(opts->fn_cntl); + kfree(opts); + } + +@@ -918,68 +940,76 @@ static struct usb_function_instance *f_audio_alloc_inst(void) + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = f_audio_free_inst; + +- config_group_init_type_name(&opts->func_inst.group, "", +- &f_uac1_func_type); +- +- opts->req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE; +- opts->req_count = UAC1_REQ_COUNT; +- opts->audio_buf_size = UAC1_AUDIO_BUF_SIZE; +- opts->fn_play = FILE_PCM_PLAYBACK; +- opts->fn_cap = FILE_PCM_CAPTURE; +- opts->fn_cntl = FILE_CONTROL; ++ opts->c_chmask = UAC1_DEF_CCHMASK; ++ opts->c_srate = UAC1_DEF_CSRATE; ++ opts->c_ssize = UAC1_DEF_CSSIZE; ++ opts->p_chmask = UAC1_DEF_PCHMASK; ++ opts->p_srate = UAC1_DEF_PSRATE; ++ opts->p_ssize = UAC1_DEF_PSSIZE; ++ opts->req_number = UAC1_DEF_REQ_NUM; + return &opts->func_inst; + } + + static void f_audio_free(struct usb_function *f) + { +- struct f_audio *audio = func_to_audio(f); + struct f_uac1_opts *opts; ++ struct f_uac1 *uac1 = func_to_uac1(f); + + opts = container_of(f->fi, struct f_uac1_opts, func_inst); +- kfree(audio); + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); ++ ++ kfree(uac1); + } + + static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f) + { ++ struct f_uac1 *uac1 = func_to_uac1(f); ++ struct uac_device *uac_dev = &uac1->uac_dev; ++ struct usb_composite_dev *cdev = c->cdev; ++ ++ uac_device_cleanup(uac_dev); + usb_free_all_descriptors(f); ++ ++ uac_dev->gadget = NULL; ++ ++ if (uac_dev) { /* release v4l2 device*/ ++ video_unregister_device(uac_dev->vdev); ++ v4l2_device_unregister(&(uac_dev->v4l2_dev)); ++ ++ usb_ep_free_request(cdev->gadget->ep0, uac_dev->control_req); ++ kfree(uac_dev->control_buf); ++ } + } + + static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) + { +- struct f_audio *audio; ++ struct f_uac1 *uac1; + struct f_uac1_opts *opts; + + /* allocate and initialize one new instance */ +- audio = kzalloc(sizeof(*audio), GFP_KERNEL); +- if (!audio) ++ uac1 = kzalloc(sizeof(*uac1), GFP_KERNEL); ++ if (!uac1) + return ERR_PTR(-ENOMEM); + +- audio->card.func.name = "g_audio"; +- + opts = container_of(fi, struct f_uac1_opts, func_inst); + mutex_lock(&opts->lock); + ++opts->refcnt; + mutex_unlock(&opts->lock); +- INIT_LIST_HEAD(&audio->play_queue); +- spin_lock_init(&audio->lock); +- +- audio->card.func.bind = f_audio_bind; +- audio->card.func.unbind = f_audio_unbind; +- audio->card.func.set_alt = f_audio_set_alt; +- audio->card.func.setup = f_audio_setup; +- audio->card.func.disable = f_audio_disable; +- audio->card.func.free_func = f_audio_free; +- +- control_selector_init(audio); + +- INIT_WORK(&audio->playback_work, f_audio_playback_work); ++ uac1->uac_dev.func.name = "uac1_func"; ++ uac1->uac_dev.func.bind = f_audio_bind; ++ uac1->uac_dev.func.unbind = f_audio_unbind; ++ uac1->uac_dev.func.set_alt = f_audio_set_alt; ++ uac1->uac_dev.func.get_alt = f_audio_get_alt; ++ uac1->uac_dev.func.setup = f_audio_setup; ++ uac1->uac_dev.func.disable = f_audio_disable; ++ uac1->uac_dev.func.free_func = f_audio_free; + +- return &audio->card.func; ++ return &uac1->uac_dev.func; + } + + DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc); + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Bryan Wu"); ++MODULE_AUTHOR("Ruslan Bilovol"); +diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c +index 945b3bd..04fb4a9 100644 +--- a/drivers/usb/gadget/function/f_uvc.c ++++ b/drivers/usb/gadget/function/f_uvc.c +@@ -32,6 +32,17 @@ + #include "uvc_video.h" + #include "u_uvc.h" + ++ ++static unsigned int bulk_max_size = 1024; ++module_param(bulk_max_size, uint, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(bulk_max_size, "bulk max size"); ++ ++static bool bulk_streaming_ep; ++module_param(bulk_streaming_ep, bool, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(bulk_streaming_ep, "0 (Use ISOC video streaming ep) / " ++ "1 (Use BULK video streaming ep)"); ++ ++ + unsigned int uvc_gadget_trace_param; + + /* -------------------------------------------------------------------------- +@@ -197,6 +208,85 @@ static const struct usb_descriptor_header * const uvc_ss_streaming[] = { + NULL, + }; + ++static struct usb_interface_descriptor uvc_bulk_streaming_intf_alt0 = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = UVC_INTF_VIDEO_STREAMING, ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = UVC_SC_VIDEOSTREAMING, ++ .bInterfaceProtocol = 0x00, ++ .iInterface = 0, ++}; ++ ++ ++static struct usb_endpoint_descriptor uvc_fs_bulk_streaming_ep = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ /* The wMaxPacketSize and bInterval values will be initialized from ++ * module parameters. ++ */ ++ .wMaxPacketSize = 0, ++ .bInterval = 0, ++}; ++ ++ ++static struct usb_endpoint_descriptor uvc_hs_bulk_streaming_ep = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ /* The wMaxPacketSize and bInterval values will be initialized from ++ * module parameters. ++ */ ++ .wMaxPacketSize = 0, ++ .bInterval = 0, ++}; ++ ++ ++static struct usb_endpoint_descriptor uvc_ss_bulk_streaming_ep = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ /* The wMaxPacketSize and bInterval values will be initialized from ++ * module parameters. ++ */ ++ .wMaxPacketSize = 0, ++ .bInterval = 0, ++}; ++ ++ ++static struct usb_ss_ep_comp_descriptor uvc_ss_bulk_streaming_comp ++ = { ++ .bLength = sizeof(uvc_ss_bulk_streaming_comp), ++ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, ++ /* The following 3 values can be tweaked if necessary. */ ++ .bMaxBurst = 0, ++ .bmAttributes = 0, ++ .wBytesPerInterval = cpu_to_le16(1024), ++}; ++ ++static const struct usb_descriptor_header * const uvc_fs_bulk_streaming[] = { ++ (struct usb_descriptor_header *) &uvc_fs_bulk_streaming_ep, ++ NULL, ++}; ++ ++static const struct usb_descriptor_header * const uvc_hs_bulk_streaming[] = { ++ (struct usb_descriptor_header *) &uvc_hs_bulk_streaming_ep, ++ NULL, ++}; ++ ++static const struct usb_descriptor_header * const uvc_ss_bulk_streaming[] = { ++ (struct usb_descriptor_header *) &uvc_ss_bulk_streaming_ep, ++ (struct usb_descriptor_header *) &uvc_ss_bulk_streaming_comp, ++ NULL, ++}; ++ + void uvc_set_trace_param(unsigned int trace) + { + uvc_gadget_trace_param = trace; +@@ -278,8 +368,20 @@ uvc_function_get_alt(struct usb_function *f, unsigned interface) + return 0; + else if (interface != uvc->streaming_intf) + return -EINVAL; +- else +- return uvc->video.ep->driver_data ? 1 : 0; ++ else { ++ /* ++ * Alt settings in an interface are supported only for ++ * ISOC endpoints as there are different alt-settings for ++ * zero-bandwidth and full-bandwidth cases, but the same ++ * is not true for BULK endpoints, as they have a single ++ * alt-setting. ++ */ ++ if (!bulk_streaming_ep) ++ return uvc->state == UVC_STATE_STREAMING ? 1 : 0; ++ else ++ return 0; ++ } ++ + } + + static int +@@ -325,55 +427,101 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) + if (interface != uvc->streaming_intf) + return -EINVAL; + +- /* TODO +- if (usb_endpoint_xfer_bulk(&uvc->desc.vs_ep)) +- return alt ? -EINVAL : 0; +- */ ++ if (!bulk_streaming_ep) { ++ switch (alt) { ++ case 0: ++ if (uvc->state != UVC_STATE_STREAMING) ++ return 0; ++ ++ if (uvc->video.ep) { ++ usb_ep_disable(uvc->video.ep); ++ uvc->video.ep->driver_data = NULL; ++ } + +- switch (alt) { +- case 0: +- if (uvc->state != UVC_STATE_STREAMING) ++ memset(&v4l2_event, 0, sizeof(v4l2_event)); ++ v4l2_event.type = UVC_EVENT_STREAMOFF; ++ v4l2_event_queue(uvc->vdev, &v4l2_event); ++ ++ uvc->state = UVC_STATE_CONNECTED; + return 0; + +- if (uvc->video.ep) { +- usb_ep_disable(uvc->video.ep); +- uvc->video.ep->driver_data = NULL; +- } ++ case 1: ++ if (uvc->state != UVC_STATE_CONNECTED) ++ return 0; + +- memset(&v4l2_event, 0, sizeof(v4l2_event)); +- v4l2_event.type = UVC_EVENT_STREAMOFF; +- v4l2_event_queue(uvc->vdev, &v4l2_event); ++ if (!uvc->video.ep) ++ return -EINVAL; + +- uvc->state = UVC_STATE_CONNECTED; +- return 0; ++ if (uvc->video.ep->driver_data) { ++ INFO(cdev, "reset UVC\n"); ++ usb_ep_disable(uvc->video.ep); ++ uvc->video.ep->driver_data = NULL; ++ } + +- case 1: +- if (uvc->state != UVC_STATE_CONNECTED) +- return 0; ++ ret = config_ep_by_speed(f->config->cdev->gadget, ++ &(uvc->func), uvc->video.ep); ++ if (ret) ++ return ret; ++ usb_ep_enable(uvc->video.ep); ++ uvc->video.ep->driver_data = uvc; + +- if (!uvc->video.ep) +- return -EINVAL; ++ memset(&v4l2_event, 0, sizeof(v4l2_event)); ++ v4l2_event.type = UVC_EVENT_STREAMON; ++ v4l2_event_queue(uvc->vdev, &v4l2_event); ++ return USB_GADGET_DELAYED_STATUS; + +- if (uvc->video.ep->driver_data) { +- INFO(cdev, "reset UVC\n"); +- usb_ep_disable(uvc->video.ep); +- uvc->video.ep->driver_data = NULL; ++ default: ++ return -EINVAL; + } ++ } else { ++ switch (uvc->state) { ++ case UVC_STATE_CONNECTED: ++ if (!uvc->video.ep->driver_data) { ++ /* ++ * Enable the video streaming endpoint, ++ * but don't change the 'uvc->state'. ++ */ ++ if (uvc->video.ep) { ++ ret = config_ep_by_speed ++ (f->config->cdev->gadget, ++ &(uvc->func), uvc->video.ep); ++ if (ret) ++ return ret; ++ ret = usb_ep_enable(uvc->video.ep); ++ if (ret) ++ return ret; ++ ++ uvc->video.ep->driver_data = uvc; ++ } ++ } else { ++ memset(&v4l2_event, 0, sizeof(v4l2_event)); ++ v4l2_event.type = UVC_EVENT_STREAMON; ++ v4l2_event_queue(uvc->vdev, &v4l2_event); ++ ++ uvc->state = UVC_STATE_STREAMING; ++ } ++ return 0; + +- ret = config_ep_by_speed(f->config->cdev->gadget, +- &(uvc->func), uvc->video.ep); +- if (ret) +- return ret; +- usb_ep_enable(uvc->video.ep); +- uvc->video.ep->driver_data = uvc; ++ case UVC_STATE_STREAMING: ++ if (uvc->video.ep->driver_data) { ++ if (uvc->video.ep) { ++ ret = usb_ep_disable(uvc->video.ep); ++ if (ret) ++ return ret; ++ } ++ } + +- memset(&v4l2_event, 0, sizeof(v4l2_event)); +- v4l2_event.type = UVC_EVENT_STREAMON; +- v4l2_event_queue(uvc->vdev, &v4l2_event); +- return USB_GADGET_DELAYED_STATUS; ++ memset(&v4l2_event, 0, sizeof(v4l2_event)); ++ v4l2_event.type = UVC_EVENT_STREAMOFF; ++ v4l2_event_queue(uvc->vdev, &v4l2_event); ++ uvc->state = UVC_STATE_CONNECTED; ++ INFO(cdev, "uvc_function_set_alt: state to streamoff\n"); + +- default: +- return -EINVAL; ++ return 0; ++ ++ default: ++ return -EINVAL; ++ } + } + } + +@@ -480,6 +628,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) + const struct uvc_descriptor_header * const *uvc_streaming_cls; + const struct usb_descriptor_header * const *uvc_streaming_std; + const struct usb_descriptor_header * const *src; ++ struct usb_interface_descriptor *streaming_intf_alt0; + struct usb_descriptor_header **dst; + struct usb_descriptor_header **hdr; + unsigned int control_size; +@@ -488,6 +637,11 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) + unsigned int bytes; + void *mem; + ++ if (!bulk_streaming_ep) ++ streaming_intf_alt0 = &uvc_streaming_intf_alt0; ++ else ++ streaming_intf_alt0 = &uvc_bulk_streaming_intf_alt0; ++ + switch (speed) { + case USB_SPEED_SUPER: + uvc_control_desc = uvc->desc.ss_control; +@@ -509,6 +663,22 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) + break; + } + ++ if (bulk_streaming_ep) { ++ switch (speed) { ++ case USB_SPEED_SUPER: ++ uvc_streaming_std = uvc_ss_bulk_streaming; ++ break; ++ ++ case USB_SPEED_HIGH: ++ uvc_streaming_std = uvc_hs_bulk_streaming; ++ break; ++ ++ case USB_SPEED_FULL: ++ default: ++ uvc_streaming_std = uvc_fs_bulk_streaming; ++ break; ++ } ++ } + /* Descriptors layout + * + * uvc_iad +@@ -526,8 +696,8 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) + control_size = 0; + streaming_size = 0; + bytes = uvc_iad.bLength + uvc_control_intf.bLength +- + uvc_control_ep.bLength + uvc_control_cs_ep.bLength +- + uvc_streaming_intf_alt0.bLength; ++ + uvc_control_ep.bLength + uvc_control_cs_ep.bLength ++ + streaming_intf_alt0->bLength; + + if (speed == USB_SPEED_SUPER) { + bytes += uvc_ss_control_comp.bLength; +@@ -537,13 +707,13 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) + } + + for (src = (const struct usb_descriptor_header **)uvc_control_desc; +- *src; ++src) { ++ *src; ++src) { + control_size += (*src)->bLength; + bytes += (*src)->bLength; + n_desc++; + } + for (src = (const struct usb_descriptor_header **)uvc_streaming_cls; +- *src; ++src) { ++ *src; ++src) { + streaming_size += (*src)->bLength; + bytes += (*src)->bLength; + n_desc++; +@@ -577,7 +747,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) + UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp); + + UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep); +- UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0); ++ UVC_COPY_DESCRIPTOR(mem, dst, streaming_intf_alt0); + + uvc_streaming_header = mem; + UVC_COPY_DESCRIPTORS(mem, dst, +@@ -605,13 +775,21 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) + + INFO(cdev, "uvc_function_bind\n"); + ++ /* Use Bulk endpoint for video streaming?. */ ++ uvc->video.bulk_streaming_ep = bulk_streaming_ep; ++ uvc->video.bulk_max_size = bulk_max_size; ++ + opts = to_f_uvc_opts(f->fi); + /* Sanity check the streaming endpoint module parameters. + */ +- opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U); +- opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U); +- opts->streaming_maxburst = min(opts->streaming_maxburst, 15U); +- ++ if (!bulk_streaming_ep) { ++ opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U); ++ opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U); ++ opts->streaming_maxburst = min(opts->streaming_maxburst, 15U); ++ } else { ++ opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 1024U); ++ opts->streaming_maxburst = min(opts->streaming_maxburst, 15U); ++ } + /* Fill in the FS/HS/SS Video Streaming specific descriptors from the + * module parameters. + * +@@ -629,21 +807,33 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) + max_packet_size = opts->streaming_maxpacket / 3; + } + +- uvc_fs_streaming_ep.wMaxPacketSize = +- cpu_to_le16(min(opts->streaming_maxpacket, 1023U)); +- uvc_fs_streaming_ep.bInterval = opts->streaming_interval; ++ if (!bulk_streaming_ep) { ++ uvc_fs_streaming_ep.wMaxPacketSize = ++ cpu_to_le16(min(opts->streaming_maxpacket, 1023U)); ++ uvc_fs_streaming_ep.bInterval = opts->streaming_interval; + +- uvc_hs_streaming_ep.wMaxPacketSize = +- cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11)); +- uvc_hs_streaming_ep.bInterval = opts->streaming_interval; ++ uvc_hs_streaming_ep.wMaxPacketSize = ++ cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11)); ++ uvc_hs_streaming_ep.bInterval = opts->streaming_interval; + +- uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size); +- uvc_ss_streaming_ep.bInterval = opts->streaming_interval; +- uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; +- uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; +- uvc_ss_streaming_comp.wBytesPerInterval = +- cpu_to_le16(max_packet_size * max_packet_mult * +- opts->streaming_maxburst); ++ uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size); ++ uvc_ss_streaming_ep.bInterval = opts->streaming_interval; ++ uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; ++ uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; ++ uvc_ss_streaming_comp.wBytesPerInterval = ++ cpu_to_le16(max_packet_size * max_packet_mult * ++ opts->streaming_maxburst); ++ ++ } else { ++ uvc_fs_bulk_streaming_ep.wMaxPacketSize = ++ min(opts->streaming_maxpacket, 64U); ++ ++ uvc_hs_bulk_streaming_ep.wMaxPacketSize = 512; ++ uvc_ss_bulk_streaming_ep.wMaxPacketSize = max_packet_size; ++ uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; ++ uvc_ss_streaming_comp.wBytesPerInterval = ++ max_packet_size * opts->streaming_maxburst; ++ } + + /* Allocate endpoints. */ + ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); +@@ -654,14 +844,31 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) + uvc->control_ep = ep; + ep->driver_data = uvc; + +- if (gadget_is_superspeed(c->cdev->gadget)) +- ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep, +- &uvc_ss_streaming_comp); +- else if (gadget_is_dualspeed(cdev->gadget)) +- ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep); +- else +- ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); +- ++ if (gadget_is_superspeed(c->cdev->gadget)) { ++ if (!bulk_streaming_ep) ++ ep = usb_ep_autoconfig_ss(cdev->gadget, ++ &uvc_ss_streaming_ep, ++ &uvc_ss_streaming_comp); ++ else ++ ep = usb_ep_autoconfig_ss(cdev->gadget, ++ &uvc_ss_bulk_streaming_ep, ++ &uvc_ss_bulk_streaming_comp); ++ ++ } else if (gadget_is_dualspeed(cdev->gadget)) { ++ if (!bulk_streaming_ep) ++ ep = usb_ep_autoconfig(cdev->gadget, ++ &uvc_hs_streaming_ep); ++ else ++ ep = usb_ep_autoconfig(cdev->gadget, ++ &uvc_hs_bulk_streaming_ep); ++ } else { ++ if (!bulk_streaming_ep) ++ ep = usb_ep_autoconfig(cdev->gadget, ++ &uvc_fs_streaming_ep); ++ else ++ ep = usb_ep_autoconfig(cdev->gadget, ++ &uvc_fs_bulk_streaming_ep); ++ } + if (!ep) { + INFO(cdev, "Unable to allocate streaming EP\n"); + goto error; +@@ -669,9 +876,18 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) + uvc->video.ep = ep; + ep->driver_data = uvc; + +- uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address; +- uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; +- uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; ++ if (!bulk_streaming_ep) { ++ uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address; ++ uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; ++ uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; ++ } else { ++ uvc_fs_bulk_streaming_ep.bEndpointAddress = ++ uvc->video.ep->address; ++ uvc_hs_bulk_streaming_ep.bEndpointAddress = ++ uvc->video.ep->address; ++ uvc_ss_bulk_streaming_ep.bEndpointAddress = ++ uvc->video.ep->address; ++ } + + us = usb_gstrings_attach(cdev, uvc_function_strings, + ARRAY_SIZE(uvc_en_us_strings)); +@@ -682,8 +898,13 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) + uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id; + uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id; + ret = us[UVC_STRING_STREAMING_IDX].id; +- uvc_streaming_intf_alt0.iInterface = ret; +- uvc_streaming_intf_alt1.iInterface = ret; ++ ++ if (!bulk_streaming_ep) { ++ uvc_streaming_intf_alt0.iInterface = ret; ++ uvc_streaming_intf_alt1.iInterface = ret; ++ } else { ++ uvc_bulk_streaming_intf_alt0.iInterface = ret; ++ } + + /* Allocate interface IDs. */ + if ((ret = usb_interface_id(c, f)) < 0) +@@ -694,8 +915,15 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) + + if ((ret = usb_interface_id(c, f)) < 0) + goto error; +- uvc_streaming_intf_alt0.bInterfaceNumber = ret; +- uvc_streaming_intf_alt1.bInterfaceNumber = ret; ++ ++ if (!bulk_streaming_ep) { ++ ++ uvc_streaming_intf_alt0.bInterfaceNumber = ret; ++ uvc_streaming_intf_alt1.bInterfaceNumber = ret; ++ } else { ++ uvc_bulk_streaming_intf_alt0.bInterfaceNumber = ret; ++ } ++ + uvc->streaming_intf = ret; + + /* Copy descriptors */ +@@ -720,6 +948,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) + /* Avoid letting this gadget enumerate until the userspace server is + * active. + */ ++ + if ((ret = usb_function_deactivate(f)) < 0) + goto error; + +diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c +index 95d2324..6411667 100644 +--- a/drivers/usb/gadget/function/rndis.c ++++ b/drivers/usb/gadget/function/rndis.c +@@ -41,6 +41,24 @@ + + #include "rndis.h" + ++typedef enum ++{ ++ RNDIS_SKB_IDLE=0, ++ RNDIS_SKB_COMBINING, ++ RNDIS_SKB_COMBINED, ++}rndis_skb_status; ++ ++typedef struct{ ++ rndis_skb_status status; ++ int msgtype; ++ int msglen; ++ int halflen; ++ int datalen; ++ int dataoffset; ++ struct sk_buff *skb; ++}rndis_skb_recombine; ++rndis_skb_recombine g_skb[2]; ++ + + /* The driver for your USB chip needs to support ep0 OUT to work with + * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional). +@@ -59,6 +77,16 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging"); + + #define RNDIS_MAX_CONFIGS 1 + ++int rndis_ul_max_pkt_per_xfer_rcvd; ++module_param(rndis_ul_max_pkt_per_xfer_rcvd, int, S_IRUGO); ++MODULE_PARM_DESC(rndis_ul_max_pkt_per_xfer_rcvd, ++ "Max num of REMOTE_NDIS_PACKET_MSGs received in a single transfer"); ++ ++int rndis_ul_max_xfer_size_rcvd; ++module_param(rndis_ul_max_xfer_size_rcvd, int, S_IRUGO); ++MODULE_PARM_DESC(rndis_ul_max_xfer_size_rcvd, ++ "Max size of bus transfer received"); ++ + + static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS]; + +@@ -585,12 +613,12 @@ static int rndis_init_response(int configNr, rndis_init_msg_type *buf) + resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION); + resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS); + resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3); +- resp->MaxPacketsPerTransfer = cpu_to_le32(1); +- resp->MaxTransferSize = cpu_to_le32( +- params->dev->mtu ++ resp->MaxPacketsPerTransfer = cpu_to_le32(params->max_pkt_per_xfer); ++ resp->MaxTransferSize = cpu_to_le32(params->max_pkt_per_xfer * ++ (params->dev->mtu + + sizeof(struct ethhdr) + + sizeof(struct rndis_packet_msg_type) +- + 22); ++ + 22)); + resp->PacketAlignmentFactor = cpu_to_le32(0); + resp->AFListOffset = cpu_to_le32(0); + resp->AFListSize = cpu_to_le32(0); +@@ -686,6 +714,12 @@ static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf) + rndis_reset_cmplt_type *resp; + rndis_resp_t *r; + struct rndis_params *params = rndis_per_dev_params + configNr; ++ u32 length; ++ u8 *xbuf; ++ ++ /* drain the response queue */ ++ while ((xbuf = rndis_get_next_response(configNr, &length))) ++ rndis_free_response(configNr, xbuf); + + r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type)); + if (!r) +@@ -917,6 +951,8 @@ int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter) + rndis_per_dev_params[configNr].dev = dev; + rndis_per_dev_params[configNr].filter = cdc_filter; + ++ rndis_ul_max_xfer_size_rcvd = 0; ++ rndis_ul_max_pkt_per_xfer_rcvd = 0; + return 0; + } + EXPORT_SYMBOL_GPL(rndis_set_param_dev); +@@ -946,6 +982,13 @@ int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed) + } + EXPORT_SYMBOL_GPL(rndis_set_param_medium); + ++void rndis_set_max_pkt_xfer(u8 configNr, u8 max_pkt_per_xfer) ++{ ++ pr_debug("%s:\n", __func__); ++ ++ rndis_per_dev_params[configNr].max_pkt_per_xfer = max_pkt_per_xfer; ++} ++ + void rndis_add_hdr(struct sk_buff *skb) + { + struct rndis_packet_msg_type *header; +@@ -1021,23 +1064,135 @@ int rndis_rm_hdr(struct gether *port, + struct sk_buff *skb, + struct sk_buff_head *list) + { +- /* tmp points to a struct rndis_packet_msg_type */ +- __le32 *tmp = (void *)skb->data; ++ int num_pkts = 1,i = 0; ++ ++ if (skb->len > rndis_ul_max_xfer_size_rcvd) ++ rndis_ul_max_xfer_size_rcvd = skb->len; ++ ++ while (skb->len) { ++ struct rndis_packet_msg_type *hdr; ++ struct sk_buff *skb2; ++ u32 msg_len, data_offset, data_len; ++ /*recombine broken packet and add to skb process queue*/ ++ int index = 0; ++ for(i=0;i< (sizeof(g_skb)/sizeof(rndis_skb_recombine));i++){ ++ if(RNDIS_SKB_COMBINING == g_skb[i].status){ ++ g_skb[i].status = RNDIS_SKB_COMBINED; ++ memcpy(g_skb[i].skb->data+g_skb[i].halflen,skb->data,g_skb[i].msglen-g_skb[i].halflen); ++ skb_pull(skb, g_skb[i].msglen-g_skb[i].halflen);//update skb ++ g_skb[i].skb->data += g_skb[i].dataoffset+8; ++ g_skb[i].skb->len = g_skb[i].datalen; ++ g_skb[i].skb->tail = g_skb[i].skb->data + g_skb[i].skb->len; ++ index = i + 1; ++ break; ++ } ++ } ++ if(index){ ++ if((g_skb[i].dataoffset + g_skb[i].datalen + 8) > g_skb[i].msglen){ ++ pr_err("invalid rndis message: %d/%d/%d/%d, len:%d\n", ++ g_skb[i].msgtype,g_skb[i].msglen, g_skb[i].dataoffset, g_skb[i].datalen, skb->len); ++ dev_kfree_skb_any(g_skb[i].skb); ++ continue; ++ } ++ if (g_skb[i].msgtype != RNDIS_MSG_PACKET) { ++ pr_err("invalid rndis message: %d/%d/%d/%d, len:%d\n", ++ g_skb[i].msgtype,g_skb[i].msglen, g_skb[i].dataoffset, g_skb[i].datalen, skb->len); ++ dev_kfree_skb_any(g_skb[i].skb); ++ continue; ++ } ++ skb_queue_tail(list, g_skb[i].skb); ++ num_pkts++; ++ continue; ++ } ++ /* some rndis hosts send extra byte to avoid zlp, ignore it */ ++ if (skb->len == 1) { ++ dev_kfree_skb_any(skb); ++ return 0; ++ } + +- /* MessageType, MessageLength */ +- if (cpu_to_le32(RNDIS_MSG_PACKET) +- != get_unaligned(tmp++)) { +- dev_kfree_skb_any(skb); +- return -EINVAL; +- } +- tmp++; ++ if (skb->len < sizeof *hdr) { ++ pr_err("invalid rndis pkt: skblen:%u hdr_len:%u", ++ skb->len, sizeof *hdr); ++ dev_kfree_skb_any(skb); ++ return -EINVAL; ++ } ++ ++ hdr = (void *)skb->data; ++ msg_len = le32_to_cpu(hdr->MessageLength); ++ data_offset = le32_to_cpu(hdr->DataOffset); ++ data_len = le32_to_cpu(hdr->DataLength); ++ /*backup the broken packet for recombining*/ ++ if(skb->len < msg_len){ ++ int size = 1558+20; ++ for(i=0;i< (sizeof(g_skb)/sizeof(rndis_skb_recombine));i++){ ++ if(RNDIS_SKB_IDLE == g_skb[i].status){ ++ g_skb[i].status = RNDIS_SKB_COMBINING; ++ g_skb[i].skb = alloc_skb(size + NET_IP_ALIGN, GFP_ATOMIC); ++ if (g_skb[i].skb == NULL) { ++ g_skb[i].status = RNDIS_SKB_IDLE; ++ return -EINVAL; ++ } ++ skb_reserve(g_skb[i].skb, NET_IP_ALIGN); ++ g_skb[i].msgtype = le32_to_cpu(hdr->MessageType); ++ g_skb[i].msglen = msg_len; ++ g_skb[i].dataoffset = data_offset; ++ g_skb[i].datalen = data_len; ++ g_skb[i].halflen = skb->len; ++ memcpy(g_skb[i].skb->data,skb->data,g_skb[i].halflen); ++ skb_pull(skb,g_skb[i].halflen); ++ break; ++ } ++ } ++ if(!skb->len) ++ break; ++ pr_err("invalid rndis message: %d/%d/%d/%d, len:%d\n", ++ le32_to_cpu(hdr->MessageType), ++ msg_len, data_offset, data_len, skb->len); ++ dev_kfree_skb_any(skb); ++ return -EOVERFLOW; ++ } ++ if((data_offset + data_len + 8) > msg_len){ ++ pr_err("invalid rndis message: %d/%d/%d/%d, len:%d\n", ++ le32_to_cpu(hdr->MessageType), ++ msg_len, data_offset, data_len, skb->len); ++ dev_kfree_skb_any(skb); ++ return -EOVERFLOW; ++ } ++ if (le32_to_cpu(hdr->MessageType) != RNDIS_MSG_PACKET) { ++ int len = skb->len; ++ skb_pull(skb,len); ++ break; ++ } + +- /* DataOffset, DataLength */ +- if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) { +- dev_kfree_skb_any(skb); +- return -EOVERFLOW; ++ skb_pull(skb, data_offset + 8); ++ ++ if (msg_len == skb->len) { ++ skb_trim(skb, data_len); ++ break; ++ } ++ ++ skb2 = skb_clone(skb, GFP_ATOMIC); ++ if (!skb2) { ++ pr_err("%s:skb clone failed\n", __func__); ++ dev_kfree_skb_any(skb); ++ return -ENOMEM; ++ } ++ ++ skb_pull(skb, msg_len - sizeof *hdr); ++ skb_trim(skb2, data_len); ++ skb_queue_tail(list, skb2); ++ ++ num_pkts++; + } +- skb_trim(skb, get_unaligned_le32(tmp++)); ++ ++ if (num_pkts > rndis_ul_max_pkt_per_xfer_rcvd) ++ rndis_ul_max_pkt_per_xfer_rcvd = num_pkts; ++ /*recombinant is finished, update combining status*/ ++ ++ if(RNDIS_SKB_COMBINED == g_skb[0].status) ++ g_skb[0].status = RNDIS_SKB_IDLE; ++ if(RNDIS_SKB_COMBINED == g_skb[1].status) ++ g_skb[1].status = RNDIS_SKB_IDLE; + + skb_queue_tail(list, skb); + return 0; +diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h +index 0f4abb4..145f01b 100644 +--- a/drivers/usb/gadget/function/rndis.h ++++ b/drivers/usb/gadget/function/rndis.h +@@ -190,6 +190,7 @@ typedef struct rndis_params + struct net_device *dev; + + u32 vendorID; ++ u8 max_pkt_per_xfer; + const char *vendorDescr; + void (*resp_avail)(void *v); + void *v; +@@ -205,6 +206,7 @@ int rndis_set_param_dev (u8 configNr, struct net_device *dev, + int rndis_set_param_vendor (u8 configNr, u32 vendorID, + const char *vendorDescr); + int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); ++void rndis_set_max_pkt_xfer(u8 configNr, u8 max_pkt_per_xfer); + void rndis_add_hdr (struct sk_buff *skb); + int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, + struct sk_buff_head *list); +diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c +index 6e6f876..f1fd777 100644 +--- a/drivers/usb/gadget/function/u_ether.c ++++ b/drivers/usb/gadget/function/u_ether.c +@@ -729,9 +729,7 @@ static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len) + if (len < 18) + return -EINVAL; + +- snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x", +- dev_addr[0], dev_addr[1], dev_addr[2], +- dev_addr[3], dev_addr[4], dev_addr[5]); ++ snprintf(str, len, "%pM", dev_addr); + return 18; + } + +diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h +index 334b389..f591f74 100644 +--- a/drivers/usb/gadget/function/u_ether.h ++++ b/drivers/usb/gadget/function/u_ether.h +@@ -75,6 +75,9 @@ struct gether { + bool is_fixed; + u32 fixed_out_len; + u32 fixed_in_len; ++ unsigned ul_max_pkts_per_xfer; ++ unsigned dl_max_pkts_per_xfer; ++ bool multi_pkt_xfer; + bool supports_multi_frame; + struct sk_buff *(*wrap)(struct gether *port, + struct sk_buff *skb); +diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h +new file mode 100644 +index 0000000..2251018 +--- /dev/null ++++ b/drivers/usb/gadget/function/u_midi.h +@@ -0,0 +1,40 @@ ++/* ++ * u_midi.h ++ * ++ * Utility definitions for the midi function ++ * ++ * Copyright (c) 2014 Samsung Electronics Co., Ltd. ++ * http://www.samsung.com ++ * ++ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef U_MIDI_H ++#define U_MIDI_H ++ ++#include <linux/usb/composite.h> ++ ++struct f_midi_opts { ++ struct usb_function_instance func_inst; ++ int index; ++ char *id; ++ bool id_allocated; ++ unsigned int in_ports; ++ unsigned int out_ports; ++ unsigned int buflen; ++ unsigned int qlen; ++ ++ /* ++ * Protect the data form concurrent access by read/write ++ * and create symlink/remove symlink. ++ */ ++ struct mutex lock; ++ int refcnt; ++}; ++ ++#endif /* U_MIDI_H */ ++ +diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c +index 491082a..d9b867a 100644 +--- a/drivers/usb/gadget/function/u_serial.c ++++ b/drivers/usb/gadget/function/u_serial.c +@@ -1122,6 +1122,7 @@ int gserial_alloc_line(unsigned char *line_num) + + tty_dev = tty_port_register_device(&ports[port_num].port->port, + gs_tty_driver, port_num, NULL); ++ + if (IS_ERR(tty_dev)) { + struct gs_port *port; + pr_err("%s: failed to register tty for port %d, err %ld\n", +diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h +index f8b17fe..88bddfe 100644 +--- a/drivers/usb/gadget/function/u_uac1.h ++++ b/drivers/usb/gadget/function/u_uac1.h +@@ -1,85 +1,42 @@ + /* +- * u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities ++ * u_uac1.h - Utility definitions for UAC1 function + * +- * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> +- * Copyright (C) 2008 Analog Devices, Inc ++ * Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com> + * +- * Enter bugs at http://blackfin.uclinux.org/ +- * +- * Licensed under the GPL-2 or later. ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. + */ + +-#ifndef __U_AUDIO_H +-#define __U_AUDIO_H ++#ifndef __U_UAC1_H ++#define __U_UAC1_H + +-#include <linux/device.h> +-#include <linux/err.h> +-#include <linux/usb/audio.h> + #include <linux/usb/composite.h> + +-#include <sound/core.h> +-#include <sound/pcm.h> +-#include <sound/pcm_params.h> +- +-#include "gadget_chips.h" +- +-#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p" +-#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c" +-#define FILE_CONTROL "/dev/snd/controlC0" + ++#define UAC1_DEF_CCHMASK 0x3 ++#define UAC1_DEF_CSRATE 48000 ++#define UAC1_DEF_CSSIZE 2 ++#define UAC1_DEF_PCHMASK 0x1//0x3 ++#define UAC1_DEF_PSRATE 8000//48000 ++#define UAC1_DEF_PSSIZE 2 + #define UAC1_OUT_EP_MAX_PACKET_SIZE 200 +-#define UAC1_REQ_COUNT 256 +-#define UAC1_AUDIO_BUF_SIZE 48000 +- +-/* +- * This represents the USB side of an audio card device, managed by a USB +- * function which provides control and stream interfaces. +- */ +- +-struct gaudio_snd_dev { +- struct gaudio *card; +- struct file *filp; +- struct snd_pcm_substream *substream; +- int access; +- int format; +- int channels; +- int rate; +-}; +- +-struct gaudio { +- struct usb_function func; +- struct usb_gadget *gadget; +- +- /* ALSA sound device interfaces */ +- struct gaudio_snd_dev control; +- struct gaudio_snd_dev playback; +- struct gaudio_snd_dev capture; +- +- /* TODO */ +-}; ++#define UAC1_DEF_REQ_NUM 1 + + struct f_uac1_opts { + struct usb_function_instance func_inst; +- int req_buf_size; +- int req_count; +- int audio_buf_size; +- char *fn_play; +- char *fn_cap; +- char *fn_cntl; ++ int c_chmask; ++ int c_srate; ++ int c_ssize; ++ int p_chmask; ++ int p_srate; ++ int p_ssize; ++ int req_number; ++ int req_buf_size; + unsigned bound:1; +- unsigned fn_play_alloc:1; +- unsigned fn_cap_alloc:1; +- unsigned fn_cntl_alloc:1; +- struct gaudio *card; ++ + struct mutex lock; + int refcnt; + }; + +-int gaudio_setup(struct gaudio *card); +-void gaudio_cleanup(struct gaudio *the_card); +- +-size_t u_audio_playback(struct gaudio *card, void *buf, size_t count); +-int u_audio_get_playback_channels(struct gaudio *card); +-int u_audio_get_playback_rate(struct gaudio *card); +- +-#endif /* __U_AUDIO_H */ ++#endif /* __U_UAC1_H */ +diff --git a/drivers/usb/gadget/function/uac_audio_ex.c b/drivers/usb/gadget/function/uac_audio_ex.c +new file mode 100644 +index 0000000..c7ef83b +--- /dev/null ++++ b/drivers/usb/gadget/function/uac_audio_ex.c +@@ -0,0 +1,275 @@ ++/* ++ * uac_device.c -- interface to USB gadget "ALSA sound card" utilities ++ * ++ * Copyright (C) 2016 ++ * Author: Ruslan Bilovol <ruslan.bilovol@gmail.com> ++ * ++ * Sound card implementation was cut-and-pasted with changes ++ * from f_uac2.c and has: ++ * Copyright (C) 2011 ++ * Yadwinder Singh (yadi.brar01@gmail.com) ++ * Jaswinder Singh (jaswinder.singh@linaro.org) ++ * ++ * 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 <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/pcm_params.h> ++ ++#include "uac_queue_ex.h" ++#include "uac_ex.h" ++ ++static void ++__uac_audio_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct uac_device *uac_dev = req->context; ++ struct uac_queue *queue = &uac_dev->queue; ++ struct uac_buffer *buf; ++ ++ unsigned long flags; ++ int ret; ++ ++ switch (req->status) { ++ case 0: ++ break; ++ ++ case -ESHUTDOWN: /* disconnect from host. */ ++ printk(KERN_DEBUG "AS request cancelled.\n"); ++ uac_queue_cancel(queue, 1); ++ goto err; ++ ++ default: ++ printk(KERN_INFO "AS request completed with status %d.\n", ++ req->status); ++ uac_queue_cancel(queue, 0); ++ goto err; ++ } ++ ++ spin_lock_irqsave(&uac_dev->queue.irqlock, flags); ++ buf = uac_queue_head(&uac_dev->queue); ++ if (buf == NULL) { ++ req->length = 0; ++ goto tran_zero; ++ } ++ ++ { ++ /* ++ * For each IN packet, take the quotient of the current data ++ * rate and the endpoint's interval as the base packet size. ++ * If there is a residue from this division, add it to the ++ * residue accumulator. ++ */ ++ req->length = uac_dev->p_pktsize; ++ uac_dev->p_residue += uac_dev->p_pktsize_residue; ++ ++ /* ++ * Whenever there are more bytes in the accumulator than we ++ * need to add one more sample frame, increase this packet's ++ * size and decrease the accumulator. ++ */ ++ ++ if (uac_dev->p_residue / uac_dev->p_interval >= uac_dev->p_framesize) ++ { ++ req->length += uac_dev->p_framesize; ++ uac_dev->p_residue -= uac_dev->p_framesize * ++ uac_dev->p_interval; ++ } ++ ++ req->actual = req->length; ++ } ++ ++ { ++ unsigned int nbytes; ++ void *mem; ++ ++ /* Copy video data to the USB buffer. */ ++ mem = buf->mem + queue->buf_used; ++ nbytes = min((unsigned int)req->length, buf->bytesused - queue->buf_used); ++ ++ memcpy(req->buf, mem, nbytes); ++ queue->buf_used += nbytes; ++ ++ req->length = nbytes; ++ req->actual = req->length; ++ ++ if (buf->bytesused == uac_dev->queue.buf_used) { ++ uac_dev->queue.buf_used = 0; ++ buf->state = UVC_BUF_STATE_DONE; ++ uac_queue_next_buffer(&uac_dev->queue, buf); ++ } ++ } ++ ++tran_zero: ++ ++ req->actual = req->length; ++ ++ if ((ret = usb_ep_queue(ep, req, GFP_ATOMIC)) < 0) { ++ printk(KERN_INFO "Failed to queue request (%d).\n", ret); ++ usb_ep_set_halt(ep); ++ spin_unlock_irqrestore(&uac_dev->queue.irqlock, flags); ++ uac_queue_cancel(queue, 0); ++ } ++ spin_unlock_irqrestore(&uac_dev->queue.irqlock, flags); ++ ++err: ++ return; ++} ++ ++static inline void free_ep(struct uac_device *dev, struct usb_ep *ep) ++{ ++ struct uac_params *params; ++ int i; ++ ++ if (!dev->ep_enabled) ++ return; ++ ++ dev->ep_enabled = false; ++ params = &dev->params; ++ ++ for (i = 0; i < params->req_number; i++) { ++ if (dev->ureq[i].req) { ++ usb_ep_dequeue(ep, dev->ureq[i].req); ++ usb_ep_free_request(ep, dev->ureq[i].req); ++ dev->ureq[i].req = NULL; ++ } ++ } ++ ++ if (usb_ep_disable(ep)) ++ printk(KERN_EMERG "%s:%d Error!\n", __func__, __LINE__); ++} ++ ++int uac_device_start_playback(struct uac_device *uac_dev) ++{ ++ struct usb_gadget *gadget = uac_dev->gadget; ++ struct usb_request *req; ++ struct usb_ep *ep; ++ struct uac_params *params = &uac_dev->params; ++ unsigned int factor, rate; ++ const struct usb_endpoint_descriptor *ep_desc; ++ int req_len, i; ++ ++ if (uac_dev->ep_enabled) ++ return 0; ++ ++ ep = uac_dev->in_ep; ++ config_ep_by_speed(gadget, &uac_dev->func, ep); ++ ep_desc = ep->desc; ++ ++ /* pre-calculate the playback endpoint's interval */ ++ if (gadget->speed == USB_SPEED_FULL) ++ factor = 1000; ++ else ++ factor = 8000; ++ ++ /* pre-compute some values for iso_complete() */ ++ uac_dev->p_framesize = params->p_ssize * ++ num_channels(params->p_chmask); ++ rate = params->p_srate * uac_dev->p_framesize; ++ uac_dev->p_interval = factor / (1 << (ep_desc->bInterval - 1)); ++ uac_dev->p_pktsize = min_t(unsigned int, rate / uac_dev->p_interval, ++ uac_dev->max_psize); ++ ++ if (uac_dev->p_pktsize < uac_dev->max_psize) ++ uac_dev->p_pktsize_residue = rate % uac_dev->p_interval; ++ else ++ uac_dev->p_pktsize_residue = 0; ++ ++ req_len = uac_dev->p_pktsize; ++ uac_dev->p_residue = 0; ++ ++ uac_dev->ep_enabled = true; ++ usb_ep_enable(ep); ++ ++ printk(KERN_EMERG "p_framesize=%d p_interval=%d p_pktsize=%d \ ++ p_pktsize_residue=%d m=%d\n", ++ uac_dev->p_framesize, ++ uac_dev->p_interval, ++ uac_dev->p_pktsize, ++ uac_dev->p_pktsize_residue, ++ uac_dev->max_psize); ++ ++ for (i = 0; i < params->req_number; i++) { ++ if (!uac_dev->ureq[i].req) { ++ req = usb_ep_alloc_request(ep, GFP_ATOMIC); ++ if (req == NULL) ++ return -ENOMEM; ++ ++ uac_dev->ureq[i].req = req; ++ uac_dev->ureq[i].dev = uac_dev; ++ ++ req->zero = 0; ++ req->context = uac_dev; ++ req->length = req_len; ++ req->complete = __uac_audio_complete; ++ req->buf = uac_dev->rbuf + i * uac_dev->max_psize; ++ } ++ ++ if (usb_ep_queue(ep, uac_dev->ureq[i].req, GFP_ATOMIC)) ++ printk(KERN_EMERG "%s:%d Error!\n", __func__, __LINE__); ++ } ++ ++ return 0; ++} ++ ++void uac_device_stop_playback(struct uac_device *uac_dev) ++{ ++ free_ep(uac_dev, uac_dev->in_ep); ++} ++ ++int uac_device_setup(struct uac_device *uac_dev) ++{ ++ struct uac_params *params; ++ int p_chmask; ++ int err = 0; ++ ++ if (!uac_dev) ++ return -EINVAL; ++ ++ params = &uac_dev->params; ++ p_chmask = params->p_chmask; ++ ++ if (p_chmask) { ++ uac_dev->max_psize = uac_dev->in_ep_maxpsize; ++ uac_dev->ureq = kcalloc(params->req_number, sizeof(struct uac_req), ++ GFP_KERNEL); ++ if (!uac_dev->ureq) { ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ uac_dev->rbuf = kcalloc(params->req_number, uac_dev->max_psize, ++ GFP_KERNEL); ++ if (!uac_dev->rbuf) { ++ uac_dev->max_psize = 0; ++ err = -ENOMEM; ++ goto fail; ++ } ++ } ++ ++ if (!err) ++ return 0; ++fail: ++ kfree(uac_dev->ureq); ++ kfree(uac_dev->rbuf); ++ ++ return err; ++} ++ ++void uac_device_cleanup(struct uac_device *uac_dev) ++{ ++ if (!uac_dev) ++ return; ++ ++ kfree(uac_dev->ureq); ++ kfree(uac_dev->rbuf); ++} +diff --git a/drivers/usb/gadget/function/uac_ex.h b/drivers/usb/gadget/function/uac_ex.h +new file mode 100644 +index 0000000..a62708c +--- /dev/null ++++ b/drivers/usb/gadget/function/uac_ex.h +@@ -0,0 +1,133 @@ ++/* ++ * The file defines UAC API for user space ++ */ ++ ++#ifndef _UAC_EX_H_ ++#define _UAC_EX_H_ ++ ++#include <linux/ioctl.h> ++#include <linux/types.h> ++#include <linux/usb/ch9.h> ++ ++#define UAC_EVENT_FIRST (V4L2_EVENT_PRIVATE_START + 0) ++#define UAC_EVENT_CONNECT (V4L2_EVENT_PRIVATE_START + 0) ++#define UAC_EVENT_DISCONNECT (V4L2_EVENT_PRIVATE_START + 1) ++#define UAC_EVENT_STREAMON (V4L2_EVENT_PRIVATE_START + 2) ++#define UAC_EVENT_STREAMOFF (V4L2_EVENT_PRIVATE_START + 3) ++#define UAC_EVENT_SETUP (V4L2_EVENT_PRIVATE_START + 4) ++#define UAC_EVENT_DATA (V4L2_EVENT_PRIVATE_START + 5) ++#define UAC_EVENT_LAST (V4L2_EVENT_PRIVATE_START + 5) ++ ++#ifdef __KERNEL__ ++ ++#include <linux/usb.h> /* For usb_endpoint_* */ ++#include <linux/usb/composite.h> ++#include <linux/usb/gadget.h> ++#include <linux/videodev2.h> ++#include <linux/version.h> ++#include <media/v4l2-fh.h> ++#include <media/v4l2-device.h> ++ ++#include "uac_queue_ex.h" ++ ++struct uac_request_data ++{ ++ __s32 length; ++ __u8 data[60]; ++}; ++ ++struct uac_event ++{ ++ union { ++ struct uac_request_data data; ++ }; ++}; ++ ++/* ------------------------------------------------------------------------ ++ * Structures ++ */ ++ ++struct uac_params { ++ /* playback */ ++ int p_chmask; /* channel mask */ ++ int p_srate; /* rate in Hz */ ++ int p_ssize; /* sample size */ ++ ++ /* capture */ ++ int c_chmask; /* channel mask */ ++ int c_srate; /* rate in Hz */ ++ int c_ssize; /* sample size */ ++ ++ int req_number; /* number of preallocated requests */ ++}; ++ ++struct uac_req { ++ struct uac_device *dev; /* parent param */ ++ struct usb_request *req; ++}; ++ ++struct uac_device ++{ ++ struct usb_function func; ++ struct usb_gadget *gadget; ++ struct usb_ep *in_ep; ++ struct usb_ep *out_ep; ++ struct uac_params params; ++ struct video_device *vdev; ++ struct v4l2_device v4l2_dev; ++ struct uac_queue queue; ++ struct uac_req *ureq; ++ void *rbuf; ++ struct usb_request *control_req; ++ void *control_buf; ++ ++ unsigned int in_ep_maxpsize; ++ unsigned int out_ep_maxpsize; ++ bool ep_enabled; /* if the ep is enabled */ ++ unsigned max_psize; /* MaxPacketSize of endpoint */ ++ ++ /* timekeeping for the playback endpoint */ ++ unsigned int p_interval; ++ unsigned int p_residue; ++ ++ /* pre-calculated values for playback iso completion */ ++ unsigned int p_pktsize; ++ unsigned int p_pktsize_residue; ++ unsigned int p_framesize; ++}; ++ ++struct uac_file_handle ++{ ++ struct v4l2_fh vfh; ++ struct uac_device *dev; ++}; ++ ++#define to_uac_file_handle(handle) \ ++ container_of(handle, struct uac_file_handle, vfh) ++ ++static inline struct uac_device *func_to_uac_device(struct usb_function *f) ++{ ++ return container_of(f, struct uac_device, func); ++} ++ ++static inline uint num_channels(uint chanmask) ++{ ++ uint num = 0; ++ ++ while (chanmask) { ++ num += (chanmask & 1); ++ chanmask >>= 1; ++ } ++ ++ return num; ++} ++ ++int uac_device_setup(struct uac_device *uac_dev); ++void uac_device_cleanup(struct uac_device *g_audio); ++int uac_device_start_playback(struct uac_device *uac_dev); ++void uac_device_stop_playback(struct uac_device *uac_dev); ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _UAC_EX_H_ */ ++ +diff --git a/drivers/usb/gadget/function/uac_queue_ex.c b/drivers/usb/gadget/function/uac_queue_ex.c +new file mode 100644 +index 0000000..89dcf6b +--- /dev/null ++++ b/drivers/usb/gadget/function/uac_queue_ex.c +@@ -0,0 +1,379 @@ ++/* ++ * uac_queue.c -- USB Video Class driver - Buffers management ++ * ++ * Copyright (C) 2005-2010 ++ * Laurent Pinchart (laurent.pinchart@ideasonboard.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/atomic.h> ++#include <linux/kernel.h> ++#include <linux/mm.h> ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/usb.h> ++#include <linux/videodev2.h> ++#include <linux/vmalloc.h> ++#include <linux/wait.h> ++ ++#include <media/v4l2-common.h> ++#include <media/videobuf2-vmalloc.h> ++ ++#include "uac_ex.h" ++ ++/* ------------------------------------------------------------------------ ++ * Video buffers queue management. ++ * ++ * Video queues is initialized by uac_queue_init(). The function performs ++ * basic initialization of the uac_queue struct and never fails. ++ * ++ * Video buffers are managed by videobuf2. The driver uses a mutex to protect ++ * the videobuf2 queue operations by serializing calls to videobuf2 and a ++ * spinlock to protect the IRQ queue that holds the buffers to be processed by ++ * the driver. ++ */ ++ ++/* ----------------------------------------------------------------------------- ++ * videobuf2 queue operations ++ */ ++ ++static int uac_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++{ ++ if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) ++ *nbuffers = UVC_MAX_VIDEO_BUFFERS; ++ ++ *nplanes = 1; ++ ++ sizes[0] = 1024; ++ ++ return 0; ++} ++ ++static int uac_buffer_prepare(struct vb2_buffer *vb) ++{ ++ struct uac_queue *queue = vb2_get_drv_priv(vb->vb2_queue); ++ struct uac_buffer *buf = container_of(vb, struct uac_buffer, buf); ++ ++ if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT && ++ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { ++ printk(KERN_EMERG "[E] Bytes used out of bounds.\n"); ++ return -EINVAL; ++ } ++ ++ if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED)) ++ return -ENODEV; ++ ++ buf->state = UVC_BUF_STATE_QUEUED; ++ buf->mem = vb2_plane_vaddr(vb, 0); ++ buf->length = vb2_plane_size(vb, 0); ++ if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ buf->bytesused = 0; ++ else ++ buf->bytesused = vb2_get_plane_payload(vb, 0); ++ ++ return 0; ++} ++ ++static void uac_buffer_queue(struct vb2_buffer *vb) ++{ ++ struct uac_queue *queue = vb2_get_drv_priv(vb->vb2_queue); ++ struct uac_buffer *buf = container_of(vb, struct uac_buffer, buf); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&queue->irqlock, flags); ++ ++ if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) { ++ list_add_tail(&buf->queue, &queue->irqqueue); ++ } else { ++ /* If the device is disconnected return the buffer to userspace ++ * directly. The next QBUF call will fail with -ENODEV. ++ */ ++ buf->state = UVC_BUF_STATE_ERROR; ++ vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); ++ } ++ ++ spin_unlock_irqrestore(&queue->irqlock, flags); ++} ++ ++static void uac_wait_prepare(struct vb2_queue *vq) ++{ ++ struct uac_queue *queue = vb2_get_drv_priv(vq); ++ ++ mutex_unlock(&queue->mutex); ++} ++ ++static void uac_wait_finish(struct vb2_queue *vq) ++{ ++ struct uac_queue *queue = vb2_get_drv_priv(vq); ++ ++ mutex_lock(&queue->mutex); ++} ++ ++static void uac_stop_streaming(struct vb2_queue *vq) ++{ ++ struct uac_queue *queue = vb2_get_drv_priv(vq); ++ ++ uac_queue_cancel(queue, 0); ++} ++ ++static struct vb2_ops uac_queue_qops = { ++ .queue_setup = uac_queue_setup, ++ .buf_prepare = uac_buffer_prepare, ++ .buf_queue = uac_buffer_queue, ++ .wait_prepare = uac_wait_prepare, ++ .wait_finish = uac_wait_finish, ++ .stop_streaming = uac_stop_streaming, ++}; ++ ++int uac_queue_init(struct uac_queue *queue) ++{ ++ int ret; ++ ++ queue->queue.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ queue->queue.io_modes = VB2_USERPTR; ++ queue->queue.drv_priv = queue; ++ queue->queue.buf_struct_size = sizeof(struct uac_buffer); ++ queue->queue.ops = &uac_queue_qops; ++ queue->queue.mem_ops = &vb2_vmalloc_memops; ++ queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC ++ | V4L2_BUF_FLAG_TSTAMP_SRC_EOF; ++ ret = vb2_queue_init(&queue->queue); ++ if (ret) ++ return ret; ++ ++ mutex_init(&queue->mutex); ++ spin_lock_init(&queue->irqlock); ++ INIT_LIST_HEAD(&queue->irqqueue); ++ queue->flags = 0; ++ ++ return 0; ++} ++ ++/* ++ * Free the video buffers. ++ */ ++void uac_free_buffers(struct uac_queue *queue) ++{ ++ mutex_lock(&queue->mutex); ++ vb2_queue_release(&queue->queue); ++ mutex_unlock(&queue->mutex); ++} ++ ++/* ++ * Allocate the video buffers. ++ */ ++int uac_alloc_buffers(struct uac_queue *queue, ++ struct v4l2_requestbuffers *rb) ++{ ++ int ret; ++ ++ mutex_lock(&queue->mutex); ++ ret = vb2_reqbufs(&queue->queue, rb); ++ mutex_unlock(&queue->mutex); ++ ++ return ret ? ret : rb->count; ++} ++ ++int uac_query_buffer(struct uac_queue *queue, struct v4l2_buffer *buf) ++{ ++ int ret; ++ ++ mutex_lock(&queue->mutex); ++ ret = vb2_querybuf(&queue->queue, buf); ++ mutex_unlock(&queue->mutex); ++ ++ return ret; ++} ++ ++int uac_queue_buffer(struct uac_queue *queue, struct v4l2_buffer *buf) ++{ ++ unsigned long flags; ++ int ret; ++ ++ mutex_lock(&queue->mutex); ++ ret = vb2_qbuf(&queue->queue, buf); ++ if (ret < 0) ++ goto done; ++ ++ spin_lock_irqsave(&queue->irqlock, flags); ++ ret = (queue->flags & UVC_QUEUE_PAUSED) != 0; ++ queue->flags &= ~UVC_QUEUE_PAUSED; ++ spin_unlock_irqrestore(&queue->irqlock, flags); ++ ++done: ++ mutex_unlock(&queue->mutex); ++ return ret; ++} ++ ++/* ++ * Dequeue a video buffer. If nonblocking is false, block until a buffer is ++ * available. ++ */ ++int uac_dequeue_buffer(struct uac_queue *queue, struct v4l2_buffer *buf, ++ int nonblocking) ++{ ++ int ret; ++ ++ mutex_lock(&queue->mutex); ++ ret = vb2_dqbuf(&queue->queue, buf, nonblocking); ++ mutex_unlock(&queue->mutex); ++ ++ return ret; ++} ++ ++/* ++ * Poll the video queue. ++ * ++ * This function implements video queue polling and is intended to be used by ++ * the device poll handler. ++ */ ++unsigned int uac_queue_poll(struct uac_queue *queue, struct file *file, ++ poll_table *wait) ++{ ++ unsigned int ret; ++ ++ mutex_lock(&queue->mutex); ++ ret = vb2_poll(&queue->queue, file, wait); ++ mutex_unlock(&queue->mutex); ++ ++ return ret; ++} ++ ++/* ++ * Cancel the video buffers queue. ++ * ++ * Cancelling the queue marks all buffers on the irq queue as erroneous, ++ * wakes them up and removes them from the queue. ++ * ++ * If the disconnect parameter is set, further calls to uac_queue_buffer will ++ * fail with -ENODEV. ++ * ++ * This function acquires the irq spinlock and can be called from interrupt ++ * context. ++ */ ++void uac_queue_cancel(struct uac_queue *queue, int disconnect) ++{ ++ struct uac_buffer *buf; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&queue->irqlock, flags); ++ while (!list_empty(&queue->irqqueue)) { ++ buf = list_first_entry(&queue->irqqueue, struct uac_buffer, ++ queue); ++ list_del(&buf->queue); ++ buf->state = UVC_BUF_STATE_ERROR; ++ vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR); ++ } ++ /* This must be protected by the irqlock spinlock to avoid race ++ * conditions between uac_queue_buffer and the disconnection event that ++ * could result in an interruptible wait in uac_dequeue_buffer. Do not ++ * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED ++ * state outside the queue code. ++ */ ++ if (disconnect) ++ queue->flags |= UVC_QUEUE_DISCONNECTED; ++ spin_unlock_irqrestore(&queue->irqlock, flags); ++} ++ ++/* ++ * Enable or disable the video buffers queue. ++ * ++ * The queue must be enabled before starting video acquisition and must be ++ * disabled after stopping it. This ensures that the video buffers queue ++ * state can be properly initialized before buffers are accessed from the ++ * interrupt handler. ++ * ++ * Enabling the video queue initializes parameters (such as sequence number, ++ * sync pattern, ...). If the queue is already enabled, return -EBUSY. ++ * ++ * Disabling the video queue cancels the queue and removes all buffers from ++ * the main queue. ++ * ++ * This function can't be called from interrupt context. Use ++ * uac_queue_cancel() instead. ++ */ ++int uac_queue_enable(struct uac_queue *queue, int enable) ++{ ++ unsigned long flags; ++ int ret = 0; ++ ++ mutex_lock(&queue->mutex); ++ if (enable) { ++ ret = vb2_streamon(&queue->queue, queue->queue.type); ++ if (ret < 0) ++ goto done; ++ ++ queue->sequence = 0; ++ queue->buf_used = 0; ++ } else { ++ ret = vb2_streamoff(&queue->queue, queue->queue.type); ++ if (ret < 0) ++ goto done; ++ ++ spin_lock_irqsave(&queue->irqlock, flags); ++ INIT_LIST_HEAD(&queue->irqqueue); ++ ++ /* ++ * FIXME: We need to clear the DISCONNECTED flag to ensure that ++ * applications will be able to queue buffers for the next ++ * streaming run. However, clearing it here doesn't guarantee ++ * that the device will be reconnected in the meantime. ++ */ ++ queue->flags &= ~UVC_QUEUE_DISCONNECTED; ++ spin_unlock_irqrestore(&queue->irqlock, flags); ++ } ++ ++done: ++ mutex_unlock(&queue->mutex); ++ return ret; ++} ++ ++/* called with &queue_irqlock held.. */ ++struct uac_buffer *uac_queue_next_buffer(struct uac_queue *queue, ++ struct uac_buffer *buf) ++{ ++ struct uac_buffer *nextbuf; ++ ++ if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && ++ buf->length != buf->bytesused) { ++ buf->state = UVC_BUF_STATE_QUEUED; ++ vb2_set_plane_payload(&buf->buf, 0, 0); ++ return buf; ++ } ++ ++ list_del(&buf->queue); ++ if (!list_empty(&queue->irqqueue)) ++ nextbuf = list_first_entry(&queue->irqqueue, struct uac_buffer, ++ queue); ++ else ++ nextbuf = NULL; ++ ++ buf->buf.v4l2_buf.field = V4L2_FIELD_NONE; ++ buf->buf.v4l2_buf.sequence = queue->sequence++; ++ v4l2_get_timestamp(&buf->buf.v4l2_buf.timestamp); ++ ++ vb2_set_plane_payload(&buf->buf, 0, buf->bytesused); ++ vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE); ++ ++ return nextbuf; ++} ++ ++struct uac_buffer *uac_queue_head(struct uac_queue *queue) ++{ ++ struct uac_buffer *buf = NULL; ++ ++ if (!list_empty(&queue->irqqueue)) ++ buf = list_first_entry(&queue->irqqueue, struct uac_buffer, ++ queue); ++ else ++ queue->flags |= UVC_QUEUE_PAUSED; ++ ++ return buf; ++} ++ +diff --git a/drivers/usb/gadget/function/uac_queue_ex.h b/drivers/usb/gadget/function/uac_queue_ex.h +new file mode 100644 +index 0000000..4b5db20 +--- /dev/null ++++ b/drivers/usb/gadget/function/uac_queue_ex.h +@@ -0,0 +1,90 @@ ++#ifndef _UAC_QUEUE_H_ ++#define _UAC_QUEUE_H_ ++ ++#ifdef __KERNEL__ ++ ++#include <linux/kernel.h> ++#include <linux/poll.h> ++#include <linux/videodev2.h> ++#include <media/videobuf2-core.h> ++ ++/* Maximum frame size in bytes, for sanity checking. */ ++#define UVC_MAX_FRAME_SIZE (16*1024*1024) ++/* Maximum number of video buffers. */ ++#define UVC_MAX_VIDEO_BUFFERS 32 ++ ++/* ------------------------------------------------------------------------ ++ * Structures. ++ */ ++ ++enum uac_buffer_state { ++ UVC_BUF_STATE_IDLE = 0, ++ UVC_BUF_STATE_QUEUED = 1, ++ UVC_BUF_STATE_ACTIVE = 2, ++ UVC_BUF_STATE_DONE = 3, ++ UVC_BUF_STATE_ERROR = 4, ++}; ++ ++struct uac_buffer { ++ struct vb2_buffer buf; ++ struct list_head queue; ++ ++ enum uac_buffer_state state; ++ void *mem; ++ unsigned int length; ++ unsigned int bytesused; ++}; ++ ++#define UVC_QUEUE_DISCONNECTED (1 << 0) ++#define UVC_QUEUE_DROP_INCOMPLETE (1 << 1) ++#define UVC_QUEUE_PAUSED (1 << 2) ++ ++struct uac_queue { ++ struct vb2_queue queue; ++ struct mutex mutex; /* Protects queue */ ++ ++ unsigned int flags; ++ __u32 sequence; ++ ++ unsigned int buf_used; ++ ++ spinlock_t irqlock; /* Protects flags and irqqueue */ ++ struct list_head irqqueue; ++}; ++ ++static inline int uac_queue_streaming(struct uac_queue *queue) ++{ ++ return vb2_is_streaming(&queue->queue); ++} ++ ++int uac_queue_init(struct uac_queue *queue); ++ ++void uac_free_buffers(struct uac_queue *queue); ++ ++int uac_alloc_buffers(struct uac_queue *queue, ++ struct v4l2_requestbuffers *rb); ++ ++int uac_query_buffer(struct uac_queue *queue, struct v4l2_buffer *buf); ++ ++int uac_queue_buffer(struct uac_queue *queue, struct v4l2_buffer *buf); ++ ++int uac_dequeue_buffer(struct uac_queue *queue, ++ struct v4l2_buffer *buf, int nonblocking); ++ ++unsigned int uac_queue_poll(struct uac_queue *queue, ++ struct file *file, poll_table *wait); ++ ++ ++void uac_queue_cancel(struct uac_queue *queue, int disconnect); ++ ++int uac_queue_enable(struct uac_queue *queue, int enable); ++ ++struct uac_buffer *uac_queue_next_buffer(struct uac_queue *queue, ++ struct uac_buffer *buf); ++ ++struct uac_buffer *uac_queue_head(struct uac_queue *queue); ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _UAC_QUEUE_H_ */ ++ +diff --git a/drivers/usb/gadget/function/uac_v4l2_ex.c b/drivers/usb/gadget/function/uac_v4l2_ex.c +new file mode 100644 +index 0000000..4570b7a +--- /dev/null ++++ b/drivers/usb/gadget/function/uac_v4l2_ex.c +@@ -0,0 +1,218 @@ ++/* ++ * uac_v4l2.c -- USB Video Class Gadget driver ++ * ++ * Copyright (C) 2009-2010 ++ * Laurent Pinchart (laurent.pinchart@ideasonboard.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/kernel.h> ++#include <linux/device.h> ++#include <linux/errno.h> ++#include <linux/list.h> ++#include <linux/mutex.h> ++#include <linux/videodev2.h> ++#include <linux/vmalloc.h> ++#include <linux/wait.h> ++ ++#include <media/v4l2-dev.h> ++#include <media/v4l2-event.h> ++#include <media/v4l2-ioctl.h> ++ ++#include "uac_ex.h" ++#include "uac_queue_ex.h" ++ ++static int ++uac_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap) ++{ ++ strlcpy(cap->driver, "g_uac", sizeof(cap->driver)); ++ strlcpy(cap->card, "g_uac", sizeof(cap->card)); ++ ++ cap->capabilities = V4L2_CAP_AUDIO; ++ ++ return 0; ++} ++static int ++uac_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ return 0; ++} ++ ++static int ++uac_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ return 0; ++} ++ ++static int ++uac_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct uac_device *uac = video_get_drvdata(vdev); ++ ++ if (b->type != uac->queue.queue.type) ++ return -EINVAL; ++ ++ return uac_alloc_buffers(&uac->queue, b); ++} ++ ++static int ++uac_v4l2_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct uac_device *uac = video_get_drvdata(vdev); ++ ++ return uac_query_buffer(&uac->queue, b); ++} ++ ++static int ++uac_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct uac_device *uac = video_get_drvdata(vdev); ++ ++ return uac_queue_buffer(&uac->queue, b); ++} ++ ++static int ++uac_v4l2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct uac_device *uac = video_get_drvdata(vdev); ++ ++ return uac_dequeue_buffer(&uac->queue, b, file->f_flags & O_NONBLOCK); ++} ++ ++static int ++uac_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct uac_device *uac = video_get_drvdata(vdev); ++ ++ int ret; ++ ++ if (type != uac->queue.queue.type) ++ return -EINVAL; ++ ++ ret = uac_queue_enable(&uac->queue, 1); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int ++uac_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct uac_device *uac = video_get_drvdata(vdev); ++ ++ if (type != uac->queue.queue.type) ++ return -EINVAL; ++ ++ return uac_queue_enable(&uac->queue, 0); ++} ++ ++static int ++uac_v4l2_subscribe_event(struct v4l2_fh *fh, ++ const struct v4l2_event_subscription *sub) ++{ ++ if (sub->type < UAC_EVENT_FIRST || sub->type > UAC_EVENT_LAST) ++ return -EINVAL; ++ ++ return v4l2_event_subscribe(fh, sub, 2, NULL); ++} ++ ++static int ++uac_v4l2_unsubscribe_event(struct v4l2_fh *fh, ++ const struct v4l2_event_subscription *sub) ++{ ++ return v4l2_event_unsubscribe(fh, sub); ++} ++ ++static long ++uac_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio, ++ unsigned int cmd, void *arg) ++{ ++ switch (cmd) { ++ default: ++ return -ENOIOCTLCMD; ++ } ++} ++ ++const struct v4l2_ioctl_ops uac_v4l2_ioctl_ops = { ++ .vidioc_querycap = uac_v4l2_querycap, ++ .vidioc_reqbufs = uac_v4l2_reqbufs, ++ .vidioc_querybuf = uac_v4l2_querybuf, ++ .vidioc_g_fmt_vid_out = uac_v4l2_get_format, ++ .vidioc_s_fmt_vid_out = uac_v4l2_set_format, ++ .vidioc_qbuf = uac_v4l2_qbuf, ++ .vidioc_dqbuf = uac_v4l2_dqbuf, ++ .vidioc_streamon = uac_v4l2_streamon, ++ .vidioc_streamoff = uac_v4l2_streamoff, ++ .vidioc_subscribe_event = uac_v4l2_subscribe_event, ++ .vidioc_unsubscribe_event = uac_v4l2_unsubscribe_event, ++ .vidioc_default = uac_v4l2_ioctl_default, ++}; ++ ++/* -------------------------------------------------------------------------- ++ * V4L2 ++ */ ++ ++static int ++uac_v4l2_open(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct uac_device *uac_dev = video_get_drvdata(vdev); ++ struct uac_file_handle *handle; ++ ++ handle = kzalloc(sizeof(*handle), GFP_KERNEL); ++ if (handle == NULL) ++ return -ENOMEM; ++ ++ v4l2_fh_init(&handle->vfh, vdev); ++ v4l2_fh_add(&handle->vfh); ++ ++ handle->dev = uac_dev; ++ file->private_data = &handle->vfh; ++ ++ return 0; ++} ++ ++static int ++uac_v4l2_release(struct file *file) ++{ ++ struct uac_file_handle *handle = to_uac_file_handle(file->private_data); ++ struct uac_device *uac_dev = handle->dev; ++ ++ uac_queue_enable(&uac_dev->queue, 0); ++ uac_free_buffers(&uac_dev->queue); ++ ++ file->private_data = NULL; ++ v4l2_fh_del(&handle->vfh); ++ v4l2_fh_exit(&handle->vfh); ++ kfree(handle); ++ ++ return 0; ++} ++ ++static unsigned int ++uac_v4l2_poll(struct file *file, poll_table *wait) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct uac_device *uac = video_get_drvdata(vdev); ++ ++ return uac_queue_poll(&uac->queue, file, wait); ++} ++ ++struct v4l2_file_operations uac_v4l2_fops = { ++ .owner = THIS_MODULE, ++ .open = uac_v4l2_open, ++ .release = uac_v4l2_release, ++ .ioctl = video_ioctl2, ++ .poll = uac_v4l2_poll, ++}; +diff --git a/drivers/usb/gadget/function/uac_v4l2_ex.h b/drivers/usb/gadget/function/uac_v4l2_ex.h +new file mode 100644 +index 0000000..9df310f +--- /dev/null ++++ b/drivers/usb/gadget/function/uac_v4l2_ex.h +@@ -0,0 +1,22 @@ ++/* ++ * uvc_v4l2.h -- USB Video Class Gadget driver ++ * ++ * Copyright (C) 2009-2010 ++ * Laurent Pinchart (laurent.pinchart@ideasonboard.com) ++ * ++ * Copyright (c) 2013 Samsung Electronics Co., Ltd. ++ * http://www.samsung.com ++ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef __UAC_V4L2_H__ ++#define __UAC_V4L2_H__ ++ ++extern const struct v4l2_ioctl_ops uac_v4l2_ioctl_ops; ++extern struct v4l2_file_operations uac_v4l2_fops; ++ ++#endif /* __UAC_V4L2_H__ */ +diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h +index f67695c..f057ce3 100644 +--- a/drivers/usb/gadget/function/uvc.h ++++ b/drivers/usb/gadget/function/uvc.h +@@ -96,8 +96,11 @@ extern unsigned int uvc_gadget_trace_param; + /* ------------------------------------------------------------------------ + * Driver specific constants + */ +- +-#define UVC_NUM_REQUESTS 4 ++#ifdef CONFIG_HIUSB_DEVICE2_0 ++#define UVC_NUM_REQUESTS 1 ++#else ++#define UVC_NUM_REQUESTS 32 ++#endif + #define UVC_MAX_REQUEST_SIZE 64 + #define UVC_MAX_EVENTS 4 + +@@ -132,6 +135,8 @@ struct uvc_video + + struct uvc_video_queue queue; + unsigned int fid; ++ bool bulk_streaming_ep; ++ unsigned int bulk_max_size; + }; + + enum uvc_state +diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c +index 5aad7fe..07f9442 100644 +--- a/drivers/usb/gadget/function/uvc_v4l2.c ++++ b/drivers/usb/gadget/function/uvc_v4l2.c +@@ -62,6 +62,7 @@ struct uvc_format + static struct uvc_format uvc_formats[] = { + { 16, V4L2_PIX_FMT_YUYV }, + { 0, V4L2_PIX_FMT_MJPEG }, ++ { 0, V4L2_PIX_FMT_H264 }, + }; + + static int +@@ -200,6 +201,9 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) + if (type != video->queue.queue.type) + return -EINVAL; + ++ if (video->bulk_streaming_ep) ++ uvc_function_connect(uvc); ++ + /* Enable UVC video. */ + ret = uvcg_video_enable(video, 1); + if (ret < 0) +@@ -209,8 +213,15 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) + * Complete the alternate setting selection setup phase now that + * userspace is ready to provide video frames. + */ +- uvc_function_setup_continue(uvc); +- uvc->state = UVC_STATE_STREAMING; ++ if (!video->bulk_streaming_ep) { ++ /* ++ * Complete the alternate setting selection setup ++ * phase now that userspace is ready to provide video ++ * frames. ++ */ ++ uvc_function_setup_continue(uvc); ++ uvc->state = UVC_STATE_STREAMING; ++ } + + return 0; + } +@@ -218,6 +229,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) + static int + uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) + { ++ int code = 0; + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; +@@ -225,7 +237,12 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) + if (type != video->queue.queue.type) + return -EINVAL; + +- return uvcg_video_enable(video, 0); ++ code = uvcg_video_enable(video, 0); ++ ++ if (video->bulk_streaming_ep) ++ uvc_function_disconnect(uvc); ++ ++ return code; + } + + static int +@@ -363,4 +380,3 @@ struct v4l2_file_operations uvc_v4l2_fops = { + .get_unmapped_area = uvcg_v4l2_get_unmapped_area, + #endif + }; +- +diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c +index 9cb86bc..754a321 100644 +--- a/drivers/usb/gadget/function/uvc_video.c ++++ b/drivers/usb/gadget/function/uvc_video.c +@@ -185,13 +185,26 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) + + spin_lock_irqsave(&video->queue.irqlock, flags); + buf = uvcg_queue_head(&video->queue); ++#ifdef CONFIG_ARCH_HISI ++ if (buf == NULL) { ++ if (video->bulk_streaming_ep) { ++ spin_unlock_irqrestore(&video->queue.irqlock, flags); ++ goto requeue; ++ } else { ++ req->length = 0; ++ goto tran_zero; ++ } ++ } ++#else + if (buf == NULL) { + spin_unlock_irqrestore(&video->queue.irqlock, flags); + goto requeue; + } ++#endif + + video->encode(req, video, buf); + ++tran_zero: + if ((ret = usb_ep_queue(ep, req, GFP_ATOMIC)) < 0) { + printk(KERN_INFO "Failed to queue request (%d).\n", ret); + usb_ep_set_halt(ep); +@@ -240,9 +253,13 @@ uvc_video_alloc_requests(struct uvc_video *video) + + BUG_ON(video->req_size); + +- req_size = video->ep->maxpacket +- * max_t(unsigned int, video->ep->maxburst, 1) +- * (video->ep->mult + 1); ++ if (!video->bulk_streaming_ep) ++ req_size = video->ep->maxpacket ++ * max_t(unsigned int, video->ep->maxburst, 1) ++ * (video->ep->mult + 1); ++ else ++ req_size = video->ep->maxpacket ++ * max_t(unsigned int, video->ep->maxburst, 1); + + for (i = 0; i < UVC_NUM_REQUESTS; ++i) { + video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL); +@@ -389,6 +406,10 @@ int uvcg_video_init(struct uvc_video *video) + video->height = 240; + video->imagesize = 320 * 240 * 2; + ++ if (video->bulk_streaming_ep) ++ video->max_payload_size = video->bulk_max_size; ++ ++ + /* Initialize the video buffers queue. */ + uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT); + return 0; +diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c +index b13f839..389c1f3 100644 +--- a/drivers/usb/gadget/functions.c ++++ b/drivers/usb/gadget/functions.c +@@ -58,7 +58,7 @@ struct usb_function *usb_get_function(struct usb_function_instance *fi) + struct usb_function *f; + + f = fi->fd->alloc_func(fi); +- if (IS_ERR(f)) ++ if ((f == NULL) || IS_ERR(f)) + return f; + f->fi = fi; + return f; +diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig +index 24392d2..6b3a349 100644 +--- a/drivers/usb/gadget/legacy/Kconfig ++++ b/drivers/usb/gadget/legacy/Kconfig +@@ -51,11 +51,8 @@ config USB_ZERO_HNPTEST + + config USB_AUDIO + tristate "Audio Gadget" +- depends on SND + select USB_LIBCOMPOSITE +- select SND_PCM + select USB_F_UAC1 if GADGET_UAC1 +- select USB_F_UAC2 if !GADGET_UAC1 + help + This Gadget Audio driver is compatible with USB Audio Class + specification 2.0. It implements 1 AudioControl interface, +@@ -75,6 +72,7 @@ config USB_AUDIO + config GADGET_UAC1 + bool "UAC 1.0 (Legacy)" + depends on USB_AUDIO ++ select USB_F_UAC1 + help + If you instead want older UAC Spec-1.0 driver that also has audio + paths hardwired to the Audio codec chip on-board and doesn't work +@@ -313,6 +311,26 @@ config USB_G_PRINTER + For more information, see Documentation/usb/gadget_printer.txt + which includes sample code for accessing the device file. + ++config USB_G_ANDROID ++ boolean "Android Composite Gadget" ++ select USB_F_ACM ++ select USB_LIBCOMPOSITE ++ select USB_U_SERIAL ++ select USB_F_MASS_STORAGE ++ select SND_PCM ++ help ++ The Android Composite Gadget supports multiple USB ++ functions: adb, acm, mass storage, mtp, accessory ++ and rndis. ++ Each function can be configured and enabled/disabled ++ dynamically from userspace through a sysfs interface. ++ ++config USB_ANDROID_RNDIS_DWORD_ALIGNED ++ boolean "Use double word aligned" ++ depends on USB_G_ANDROID ++ help ++ Provides dword aligned for DMA controller. ++ + if TTY + + config USB_CDC_COMPOSITE +@@ -476,3 +494,20 @@ config USB_G_WEBCAM + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_webcam". ++ ++# put drivers that need isochronous transfer support (for audio ++# or video class gadget drivers), or specific hardware, here. ++config USB_G_WEBCAM_AUDIO ++ tristate "USB Webcam+ Gadget(include audio)" ++ depends on VIDEO_DEV ++ select USB_LIBCOMPOSITE ++ select VIDEOBUF2_VMALLOC ++ select USB_F_UVC ++ help ++ The Webcam+ Gadget acts as a composite USB Audio and Video Class ++ device. It provides a userspace API to process UVC control requests ++ and stream video data to the host. ++ ++ Say "y" to link the driver statically, or "m" to build a ++ dynamically linked module called "g_webcam". ++ +diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile +index 7f485f2..3e309aa 100644 +--- a/drivers/usb/gadget/legacy/Makefile ++++ b/drivers/usb/gadget/legacy/Makefile +@@ -20,8 +20,10 @@ g_hid-y := hid.o + g_dbgp-y := dbgp.o + g_nokia-y := nokia.o + g_webcam-y := webcam.o ++g_webcam_audio-y := webcam_audio.o + g_ncm-y := ncm.o + g_acm_ms-y := acm_ms.o ++g_android-y := android.o + g_tcm_usb_gadget-y := tcm_usb_gadget.o + + obj-$(CONFIG_USB_ZERO) += g_zero.o +@@ -39,6 +41,8 @@ obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o + obj-$(CONFIG_USB_G_MULTI) += g_multi.o + obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o + obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o ++obj-$(CONFIG_USB_G_WEBCAM_AUDIO) += g_webcam_audio.o + obj-$(CONFIG_USB_G_NCM) += g_ncm.o + obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o ++obj-$(CONFIG_USB_G_ANDROID) += g_android.o + obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o +diff --git a/drivers/usb/gadget/legacy/android.c b/drivers/usb/gadget/legacy/android.c +new file mode 100644 +index 0000000..1d38c8d +--- /dev/null ++++ b/drivers/usb/gadget/legacy/android.c +@@ -0,0 +1,1568 @@ ++/* ++ * Gadget Driver for Android ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * Benoit Goby <benoit@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/module.h> ++#include <linux/fs.h> ++#include <linux/delay.h> ++#include <linux/kernel.h> ++#include <linux/utsname.h> ++#include <linux/platform_device.h> ++ ++#include <linux/usb/ch9.h> ++#include <linux/usb/composite.h> ++#include <linux/usb/gadget.h> ++ ++#include "gadget_chips.h" ++ ++#include "f_fs.c" ++#include "f_audio_source.c" ++#include "f_mtp.c" ++#include "f_accessory.c" ++#define USB_ETH_RNDIS y ++#include "f_rndis.c" ++#include "rndis.c" ++#include "u_ether.c" ++ ++USB_ETHERNET_MODULE_PARAMETERS(); ++ ++MODULE_AUTHOR("Mike Lockwood"); ++MODULE_DESCRIPTION("Android Composite USB Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("1.0"); ++ ++static const char longname[] = "Gadget Android"; ++ ++/* Default vendor and product IDs, overridden by userspace */ ++#define VENDOR_ID 0x18D1 ++#define PRODUCT_ID 0x0001 ++ ++struct android_usb_function { ++ char *name; ++ void *config; ++ ++ struct device *dev; ++ char *dev_name; ++ struct device_attribute **attributes; ++ ++ /* for android_dev.enabled_functions */ ++ struct list_head enabled_list; ++ ++ /* Optional: initialization during gadget bind */ ++ int (*init)(struct android_usb_function *, struct usb_composite_dev *); ++ /* Optional: cleanup during gadget unbind */ ++ void (*cleanup)(struct android_usb_function *); ++ /* Optional: called when the function is added the list of ++ * enabled functions */ ++ void (*enable)(struct android_usb_function *); ++ /* Optional: called when it is removed */ ++ void (*disable)(struct android_usb_function *); ++ ++ int (*bind_config)(struct android_usb_function *, ++ struct usb_configuration *); ++ ++ /* Optional: called when the configuration is removed */ ++ void (*unbind_config)(struct android_usb_function *, ++ struct usb_configuration *); ++ /* Optional: handle ctrl requests before the device is configured */ ++ int (*ctrlrequest)(struct android_usb_function *, ++ struct usb_composite_dev *, ++ const struct usb_ctrlrequest *); ++}; ++ ++struct android_dev { ++ struct android_usb_function **functions; ++ struct list_head enabled_functions; ++ struct usb_composite_dev *cdev; ++ struct device *dev; ++ ++ void (*setup_complete)(struct usb_ep *ep, ++ struct usb_request *req); ++ ++ bool enabled; ++ int disable_depth; ++ struct mutex mutex; ++ bool connected; ++ bool sw_connected; ++ struct work_struct work; ++ char ffs_aliases[256]; ++}; ++ ++static struct class *android_class; ++static struct android_dev *_android_dev; ++static int android_bind_config(struct usb_configuration *c); ++static void android_unbind_config(struct usb_configuration *c); ++ ++/* string IDs are assigned dynamically */ ++#define STRING_MANUFACTURER_IDX 0 ++#define STRING_PRODUCT_IDX 1 ++#define STRING_SERIAL_IDX 2 ++ ++static char manufacturer_string[256]; ++static char product_string[256]; ++static char serial_string[256]; ++ ++/*---Copied from configfs.c to let this composite driver build---*/ ++static struct class *android_class; ++static struct device *android_device; ++static int index; ++ ++struct device *create_function_device(char *name) ++{ ++ if (android_device && !IS_ERR(android_device)) ++ return device_create(android_class, android_device, ++ MKDEV(0, index++), NULL, name); ++ else ++ return ERR_PTR(-EINVAL); ++} ++EXPORT_SYMBOL_GPL(create_function_device); ++/*---------------------------------------------------------------*/ ++ ++/* String Table */ ++static struct usb_string strings_dev[] = { ++ [STRING_MANUFACTURER_IDX].s = manufacturer_string, ++ [STRING_PRODUCT_IDX].s = product_string, ++ [STRING_SERIAL_IDX].s = serial_string, ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings stringtab_dev = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_dev, ++}; ++ ++static struct usb_gadget_strings *dev_strings[] = { ++ &stringtab_dev, ++ NULL, ++}; ++ ++static struct usb_device_descriptor device_desc = { ++ .bLength = sizeof(device_desc), ++ .bDescriptorType = USB_DT_DEVICE, ++ .bcdUSB = __constant_cpu_to_le16(0x0200), ++ .bDeviceClass = USB_CLASS_PER_INTERFACE, ++ .idVendor = __constant_cpu_to_le16(VENDOR_ID), ++ .idProduct = __constant_cpu_to_le16(PRODUCT_ID), ++ .bcdDevice = __constant_cpu_to_le16(0xffff), ++ .bNumConfigurations = 1, ++}; ++ ++static struct usb_configuration android_config_driver = { ++ .label = "android", ++ .unbind = android_unbind_config, ++ .bConfigurationValue = 1, ++ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, ++ .MaxPower = 500, /* 500ma */ ++}; ++ ++static void android_work(struct work_struct *data) ++{ ++ struct android_dev *dev = container_of(data, struct android_dev, work); ++ struct usb_composite_dev *cdev = dev->cdev; ++ char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; ++ char *connected[2] = { "USB_STATE=CONNECTED", NULL }; ++ char *configured[2] = { "USB_STATE=CONFIGURED", NULL }; ++ char **uevent_envp = NULL; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ if (cdev->config) ++ uevent_envp = configured; ++ else if (dev->connected != dev->sw_connected) ++ uevent_envp = dev->connected ? connected : disconnected; ++ dev->sw_connected = dev->connected; ++ spin_unlock_irqrestore(&cdev->lock, flags); ++ ++ if (uevent_envp) { ++ kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, uevent_envp); ++ pr_info("%s: sent uevent %s\n", __func__, uevent_envp[0]); ++ } else { ++ pr_info("%s: did not send uevent (%d %d %p)\n", __func__, ++ dev->connected, dev->sw_connected, cdev->config); ++ } ++} ++ ++static void android_enable(struct android_dev *dev) ++{ ++ struct usb_composite_dev *cdev = dev->cdev; ++ ++ if (WARN_ON(!dev->disable_depth)) ++ return; ++ ++ if (--dev->disable_depth == 0) { ++ usb_add_config(cdev, &android_config_driver, ++ android_bind_config); ++ usb_gadget_connect(cdev->gadget); ++ } ++} ++ ++static void android_disable(struct android_dev *dev) ++{ ++ struct usb_composite_dev *cdev = dev->cdev; ++ ++ if (dev->disable_depth++ == 0) { ++ usb_gadget_disconnect(cdev->gadget); ++ /* Cancel pending control requests */ ++ usb_ep_dequeue(cdev->gadget->ep0, cdev->req); ++ usb_remove_config(cdev, &android_config_driver); ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++/* Supported functions initialization */ ++ ++struct functionfs_config { ++ bool opened; ++ bool enabled; ++ struct ffs_data *data; ++}; ++ ++static int ffs_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ f->config = kzalloc(sizeof(struct functionfs_config), GFP_KERNEL); ++ if (!f->config) ++ return -ENOMEM; ++ ++ return functionfs_init(); ++} ++ ++static void ffs_function_cleanup(struct android_usb_function *f) ++{ ++ functionfs_cleanup(); ++ kfree(f->config); ++} ++ ++static void ffs_function_enable(struct android_usb_function *f) ++{ ++ struct android_dev *dev = _android_dev; ++ struct functionfs_config *config = f->config; ++ ++ config->enabled = true; ++ ++ /* Disable the gadget until the function is ready */ ++ if (!config->opened) ++ android_disable(dev); ++} ++ ++static void ffs_function_disable(struct android_usb_function *f) ++{ ++ struct android_dev *dev = _android_dev; ++ struct functionfs_config *config = f->config; ++ ++ config->enabled = false; ++ ++ /* Balance the disable that was called in closed_callback */ ++ if (!config->opened) ++ android_enable(dev); ++} ++ ++static int ffs_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ struct functionfs_config *config = f->config; ++ return functionfs_bind_config(c->cdev, c, config->data); ++} ++ ++static ssize_t ++ffs_aliases_show(struct device *pdev, struct device_attribute *attr, char *buf) ++{ ++ struct android_dev *dev = _android_dev; ++ int ret; ++ ++ mutex_lock(&dev->mutex); ++ ret = sprintf(buf, "%s\n", dev->ffs_aliases); ++ mutex_unlock(&dev->mutex); ++ ++ return ret; ++} ++ ++static ssize_t ++ffs_aliases_store(struct device *pdev, struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct android_dev *dev = _android_dev; ++ char buff[256]; ++ ++ mutex_lock(&dev->mutex); ++ ++ if (dev->enabled) { ++ mutex_unlock(&dev->mutex); ++ return -EBUSY; ++ } ++ ++ strlcpy(buff, buf, sizeof(buff)); ++ strlcpy(dev->ffs_aliases, strim(buff), sizeof(dev->ffs_aliases)); ++ ++ mutex_unlock(&dev->mutex); ++ ++ return size; ++} ++ ++static DEVICE_ATTR(aliases, S_IRUGO | S_IWUSR, ffs_aliases_show, ++ ffs_aliases_store); ++static struct device_attribute *ffs_function_attributes[] = { ++ &dev_attr_aliases, ++ NULL ++}; ++ ++static struct android_usb_function ffs_function = { ++ .name = "ffs", ++ .init = ffs_function_init, ++ .enable = ffs_function_enable, ++ .disable = ffs_function_disable, ++ .cleanup = ffs_function_cleanup, ++ .bind_config = ffs_function_bind_config, ++ .attributes = ffs_function_attributes, ++}; ++ ++static int functionfs_ready_callback(struct ffs_data *ffs) ++{ ++ struct android_dev *dev = _android_dev; ++ struct functionfs_config *config = ffs_function.config; ++ int ret = 0; ++ ++ mutex_lock(&dev->mutex); ++ ++ ret = functionfs_bind(ffs, dev->cdev); ++ if (ret) ++ goto err; ++ ++ config->data = ffs; ++ config->opened = true; ++ ++ if (config->enabled) ++ android_enable(dev); ++ ++err: ++ mutex_unlock(&dev->mutex); ++ return ret; ++} ++ ++static void functionfs_closed_callback(struct ffs_data *ffs) ++{ ++ struct android_dev *dev = _android_dev; ++ struct functionfs_config *config = ffs_function.config; ++ ++ mutex_lock(&dev->mutex); ++ ++ if (config->enabled) ++ android_disable(dev); ++ ++ config->opened = false; ++ config->data = NULL; ++ ++ functionfs_unbind(ffs); ++ ++ mutex_unlock(&dev->mutex); ++} ++ ++static void *functionfs_acquire_dev_callback(const char *dev_name) ++{ ++ return 0; ++} ++ ++static void functionfs_release_dev_callback(struct ffs_data *ffs_data) ++{ ++} ++ ++#define MAX_ACM_INSTANCES 4 ++struct acm_function_config { ++ int instances; ++ int instances_on; ++ struct usb_function *f_acm[MAX_ACM_INSTANCES]; ++ struct usb_function_instance *f_acm_inst[MAX_ACM_INSTANCES]; ++}; ++ ++static int ++acm_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ int i; ++ int ret; ++ struct acm_function_config *config; ++ ++ config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL); ++ if (!config) ++ return -ENOMEM; ++ f->config = config; ++ ++ for (i = 0; i < MAX_ACM_INSTANCES; i++) { ++ config->f_acm_inst[i] = usb_get_function_instance("acm"); ++ if (IS_ERR(config->f_acm_inst[i])) { ++ ret = PTR_ERR(config->f_acm_inst[i]); ++ goto err_usb_get_function_instance; ++ } ++ config->f_acm[i] = usb_get_function(config->f_acm_inst[i]); ++ if (IS_ERR(config->f_acm[i])) { ++ ret = PTR_ERR(config->f_acm[i]); ++ goto err_usb_get_function; ++ } ++ } ++ return 0; ++err_usb_get_function_instance: ++ while (i-- > 0) { ++ usb_put_function(config->f_acm[i]); ++err_usb_get_function: ++ usb_put_function_instance(config->f_acm_inst[i]); ++ } ++ return ret; ++} ++ ++static void acm_function_cleanup(struct android_usb_function *f) ++{ ++ int i; ++ struct acm_function_config *config = f->config; ++ ++ for (i = 0; i < MAX_ACM_INSTANCES; i++) { ++ usb_put_function(config->f_acm[i]); ++ usb_put_function_instance(config->f_acm_inst[i]); ++ } ++ kfree(f->config); ++ f->config = NULL; ++} ++ ++static int ++acm_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ int i; ++ int ret = 0; ++ struct acm_function_config *config = f->config; ++ ++ config->instances_on = config->instances; ++ for (i = 0; i < config->instances_on; i++) { ++ ret = usb_add_function(c, config->f_acm[i]); ++ if (ret) { ++ pr_err("Could not bind acm%u config\n", i); ++ goto err_usb_add_function; ++ } ++ } ++ ++ return 0; ++ ++err_usb_add_function: ++ while (i-- > 0) ++ usb_remove_function(c, config->f_acm[i]); ++ return ret; ++} ++ ++static void acm_function_unbind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ int i; ++ struct acm_function_config *config = f->config; ++ ++ for (i = 0; i < config->instances_on; i++) ++ usb_remove_function(c, config->f_acm[i]); ++} ++ ++static ssize_t acm_instances_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct acm_function_config *config = f->config; ++ return sprintf(buf, "%d\n", config->instances); ++} ++ ++static ssize_t acm_instances_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct acm_function_config *config = f->config; ++ int value; ++ ++ sscanf(buf, "%d", &value); ++ if (value > MAX_ACM_INSTANCES) ++ value = MAX_ACM_INSTANCES; ++ config->instances = value; ++ return size; ++} ++ ++static DEVICE_ATTR(instances, S_IRUGO | S_IWUSR, acm_instances_show, ++ acm_instances_store); ++static struct device_attribute *acm_function_attributes[] = { ++ &dev_attr_instances, ++ NULL ++}; ++ ++static struct android_usb_function acm_function = { ++ .name = "acm", ++ .init = acm_function_init, ++ .cleanup = acm_function_cleanup, ++ .bind_config = acm_function_bind_config, ++ .unbind_config = acm_function_unbind_config, ++ .attributes = acm_function_attributes, ++}; ++ ++ ++static int ++mtp_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ return mtp_setup(); ++} ++ ++static void mtp_function_cleanup(struct android_usb_function *f) ++{ ++ mtp_cleanup(); ++} ++ ++static int ++mtp_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ return mtp_bind_config(c, false); ++} ++ ++static int ++ptp_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ /* nothing to do - initialization is handled by mtp_function_init */ ++ return 0; ++} ++ ++static void ptp_function_cleanup(struct android_usb_function *f) ++{ ++ /* nothing to do - cleanup is handled by mtp_function_cleanup */ ++} ++ ++static int ++ptp_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ return mtp_bind_config(c, true); ++} ++ ++static int mtp_function_ctrlrequest(struct android_usb_function *f, ++ struct usb_composite_dev *cdev, ++ const struct usb_ctrlrequest *c) ++{ ++ return mtp_ctrlrequest(cdev, c); ++} ++ ++static struct android_usb_function mtp_function = { ++ .name = "mtp", ++ .init = mtp_function_init, ++ .cleanup = mtp_function_cleanup, ++ .bind_config = mtp_function_bind_config, ++ .ctrlrequest = mtp_function_ctrlrequest, ++}; ++ ++/* PTP function is same as MTP with slightly different interface descriptor */ ++static struct android_usb_function ptp_function = { ++ .name = "ptp", ++ .init = ptp_function_init, ++ .cleanup = ptp_function_cleanup, ++ .bind_config = ptp_function_bind_config, ++}; ++ ++ ++struct rndis_function_config { ++ u8 ethaddr[ETH_ALEN]; ++ u32 vendorID; ++ char manufacturer[256]; ++ /* "Wireless" RNDIS; auto-detected by Windows */ ++ bool wceis; ++ struct eth_dev *dev; ++}; ++ ++static int ++rndis_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ f->config = kzalloc(sizeof(struct rndis_function_config), GFP_KERNEL); ++ if (!f->config) ++ return -ENOMEM; ++ return 0; ++} ++ ++static void rndis_function_cleanup(struct android_usb_function *f) ++{ ++ kfree(f->config); ++ f->config = NULL; ++} ++ ++static int ++rndis_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ int ret; ++ struct eth_dev *dev; ++ struct rndis_function_config *rndis = f->config; ++ ++ if (!rndis) { ++ pr_err("%s: rndis_pdata\n", __func__); ++ return -1; ++ } ++ ++ pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__, ++ rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], ++ rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); ++ ++ dev = gether_setup_name(c->cdev->gadget,dev_addr, host_addr, rndis->ethaddr, qmult, "rndis"); ++ if (IS_ERR(dev)) { ++ ret = PTR_ERR(dev); ++ pr_err("%s: gether_setup failed\n", __func__); ++ return ret; ++ } ++ rndis->dev = dev; ++ ++ if (rndis->wceis) { ++ /* "Wireless" RNDIS; auto-detected by Windows */ ++ rndis_iad_descriptor.bFunctionClass = ++ USB_CLASS_WIRELESS_CONTROLLER; ++ rndis_iad_descriptor.bFunctionSubClass = 0x01; ++ rndis_iad_descriptor.bFunctionProtocol = 0x03; ++ rndis_control_intf.bInterfaceClass = ++ USB_CLASS_WIRELESS_CONTROLLER; ++ rndis_control_intf.bInterfaceSubClass = 0x01; ++ rndis_control_intf.bInterfaceProtocol = 0x03; ++ } ++ ++ return rndis_bind_config_vendor(c, rndis->ethaddr, rndis->vendorID, ++ rndis->manufacturer, rndis->dev); ++} ++ ++static void rndis_function_unbind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ struct rndis_function_config *rndis = f->config; ++ gether_cleanup(rndis->dev); ++} ++ ++static ssize_t rndis_manufacturer_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *config = f->config; ++ return sprintf(buf, "%s\n", config->manufacturer); ++} ++ ++static ssize_t rndis_manufacturer_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *config = f->config; ++ ++ if (size >= sizeof(config->manufacturer)) ++ return -EINVAL; ++ if (sscanf(buf, "%s", config->manufacturer) == 1) ++ return size; ++ return -1; ++} ++ ++static DEVICE_ATTR(manufacturer, S_IRUGO | S_IWUSR, rndis_manufacturer_show, ++ rndis_manufacturer_store); ++ ++static ssize_t rndis_wceis_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *config = f->config; ++ return sprintf(buf, "%d\n", config->wceis); ++} ++ ++static ssize_t rndis_wceis_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *config = f->config; ++ int value; ++ ++ if (sscanf(buf, "%d", &value) == 1) { ++ config->wceis = value; ++ return size; ++ } ++ return -EINVAL; ++} ++ ++static DEVICE_ATTR(wceis, S_IRUGO | S_IWUSR, rndis_wceis_show, ++ rndis_wceis_store); ++ ++static ssize_t rndis_ethaddr_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *rndis = f->config; ++ return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", ++ rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], ++ rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); ++} ++ ++static ssize_t rndis_ethaddr_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *rndis = f->config; ++ ++ if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", ++ (int *)&rndis->ethaddr[0], (int *)&rndis->ethaddr[1], ++ (int *)&rndis->ethaddr[2], (int *)&rndis->ethaddr[3], ++ (int *)&rndis->ethaddr[4], (int *)&rndis->ethaddr[5]) == 6) ++ return size; ++ return -EINVAL; ++} ++ ++static DEVICE_ATTR(ethaddr, S_IRUGO | S_IWUSR, rndis_ethaddr_show, ++ rndis_ethaddr_store); ++ ++static ssize_t rndis_vendorID_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *config = f->config; ++ return sprintf(buf, "%04x\n", config->vendorID); ++} ++ ++static ssize_t rndis_vendorID_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct rndis_function_config *config = f->config; ++ int value; ++ ++ if (sscanf(buf, "%04x", &value) == 1) { ++ config->vendorID = value; ++ return size; ++ } ++ return -EINVAL; ++} ++ ++static DEVICE_ATTR(vendorID, S_IRUGO | S_IWUSR, rndis_vendorID_show, ++ rndis_vendorID_store); ++ ++static struct device_attribute *rndis_function_attributes[] = { ++ &dev_attr_manufacturer, ++ &dev_attr_wceis, ++ &dev_attr_ethaddr, ++ &dev_attr_vendorID, ++ NULL ++}; ++ ++static struct android_usb_function rndis_function = { ++ .name = "rndis", ++ .init = rndis_function_init, ++ .cleanup = rndis_function_cleanup, ++ .bind_config = rndis_function_bind_config, ++ .unbind_config = rndis_function_unbind_config, ++ .attributes = rndis_function_attributes, ++}; ++ ++ ++#define MAX_MS_INSTANCES 1 ++struct mass_storage_function_config { ++ int instances; ++ int instances_on; ++ struct usb_function *f_ms[MAX_MS_INSTANCES]; ++ struct usb_function_instance *f_ms_inst[MAX_MS_INSTANCES]; ++}; ++ ++static int mass_storage_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ struct mass_storage_function_config *config; ++ int i; ++ int ret; ++ ++ config = kzalloc(sizeof(struct mass_storage_function_config), ++ GFP_KERNEL); ++ if (!config) ++ return -ENOMEM; ++ f->config = config; ++ ++ for (i = 0; i < MAX_MS_INSTANCES; i++) { ++ config->f_ms_inst[i] = usb_get_function_instance("mass_storage"); ++ if (IS_ERR(config->f_ms_inst[i])) { ++ ret = PTR_ERR(config->f_ms_inst[i]); ++ goto err_usb_get_function_instance; ++ } ++ config->f_ms[i] = usb_get_function(config->f_ms_inst[i]); ++ if (IS_ERR(config->f_ms[i])) { ++ ret = PTR_ERR(config->f_ms[i]); ++ goto err_usb_get_function; ++ } ++ } ++ ++ return 0; ++err_usb_get_function_instance: ++ while (i-- > 0) { ++ usb_put_function(config->f_ms[i]); ++err_usb_get_function: ++ usb_put_function_instance(config->f_ms_inst[i]); ++ } ++ return ret; ++} ++ ++static void mass_storage_function_cleanup(struct android_usb_function *f) ++{ ++ struct mass_storage_function_config *config = f->config; ++ int i; ++ ++ for (i = 0; i < MAX_MS_INSTANCES; i++) { ++ usb_put_function(config->f_ms[i]); ++ usb_put_function_instance(config->f_ms_inst[i]); ++ } ++ kfree(f->config); ++ f->config = NULL; ++} ++ ++static int mass_storage_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ struct mass_storage_function_config *config = f->config; ++ int ret = 0; ++ int i; ++ ++ config->instances_on = config->instances; ++ for (i = 0; i < config->instances_on; i++) { ++ ret = usb_add_function(c, config->f_ms[i]); ++ if (ret) { ++ pr_err("Could not bind ms%u config\n", i); ++ goto err_usb_add_function; ++ } ++ } ++ ++ return 0; ++ ++err_usb_add_function: ++ while (i-- > 0) ++ usb_remove_function(c, config->f_ms[i]); ++ return ret; ++} ++ ++static void mass_storage_function_unbind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ int i; ++ struct mass_storage_function_config *config = f->config; ++ ++ for (i = 0; i < config->instances_on; i++) ++ usb_remove_function(c, config->f_ms[i]); ++} ++ ++static ssize_t mass_storage_inquiry_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct mass_storage_function_config *config = f->config; ++ return sprintf(buf, "%d\n", config->instances); ++} ++ ++static ssize_t mass_storage_inquiry_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct android_usb_function *f = dev_get_drvdata(dev); ++ struct mass_storage_function_config *config = f->config; ++ int value; ++ ++ sscanf(buf, "%d", &value); ++ if (value > MAX_MS_INSTANCES) ++ value = MAX_MS_INSTANCES; ++ config->instances = value; ++ return size; ++} ++ ++static DEVICE_ATTR(inquiry_string, S_IRUGO | S_IWUSR, ++ mass_storage_inquiry_show, ++ mass_storage_inquiry_store); ++ ++static struct device_attribute *mass_storage_function_attributes[] = { ++ &dev_attr_inquiry_string, ++ NULL ++}; ++ ++static struct android_usb_function mass_storage_function = { ++ .name = "mass_storage", ++ .init = mass_storage_function_init, ++ .cleanup = mass_storage_function_cleanup, ++ .bind_config = mass_storage_function_bind_config, ++ .attributes = mass_storage_function_attributes, ++}; ++ ++ ++static int accessory_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ return acc_setup(); ++} ++ ++static void accessory_function_cleanup(struct android_usb_function *f) ++{ ++ acc_cleanup(); ++} ++ ++static int accessory_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ return acc_bind_config(c); ++} ++ ++static int accessory_function_ctrlrequest(struct android_usb_function *f, ++ struct usb_composite_dev *cdev, ++ const struct usb_ctrlrequest *c) ++{ ++ return acc_ctrlrequest(cdev, c); ++} ++ ++static struct android_usb_function accessory_function = { ++ .name = "accessory", ++ .init = accessory_function_init, ++ .cleanup = accessory_function_cleanup, ++ .bind_config = accessory_function_bind_config, ++ .ctrlrequest = accessory_function_ctrlrequest, ++}; ++ ++static int audio_source_function_init(struct android_usb_function *f, ++ struct usb_composite_dev *cdev) ++{ ++ struct audio_source_config *config; ++ ++ config = kzalloc(sizeof(struct audio_source_config), GFP_KERNEL); ++ if (!config) ++ return -ENOMEM; ++ config->card = -1; ++ config->device = -1; ++ f->config = config; ++ return 0; ++} ++ ++static void audio_source_function_cleanup(struct android_usb_function *f) ++{ ++ kfree(f->config); ++} ++ ++static int audio_source_function_bind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ struct audio_source_config *config = f->config; ++ ++ return audio_source_bind_config(c, config); ++} ++ ++static void audio_source_function_unbind_config(struct android_usb_function *f, ++ struct usb_configuration *c) ++{ ++ struct audio_source_config *config = f->config; ++ ++ config->card = -1; ++ config->device = -1; ++} ++ ++static struct android_usb_function audio_source_function = { ++ .name = "audio_source", ++ .init = audio_source_function_init, ++ .cleanup = audio_source_function_cleanup, ++ .bind_config = audio_source_function_bind_config, ++ .unbind_config = audio_source_function_unbind_config, ++ .attributes = audio_source_function_attributes, ++}; ++ ++static struct android_usb_function *supported_functions[] = { ++ &ffs_function, ++ &acm_function, ++ &mtp_function, ++ &ptp_function, ++ &rndis_function, ++ &mass_storage_function, ++ &accessory_function, ++ &audio_source_function, ++ NULL ++}; ++ ++ ++static int android_init_functions(struct android_usb_function **functions, ++ struct usb_composite_dev *cdev) ++{ ++ struct android_dev *dev = _android_dev; ++ struct android_usb_function *f; ++ struct device_attribute **attrs; ++ struct device_attribute *attr; ++ int err; ++ int index = 0; ++ ++ for (; (f = *functions++); index++) { ++ f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name); ++ f->dev = device_create(android_class, dev->dev, ++ MKDEV(0, index), f, f->dev_name); ++ if (IS_ERR(f->dev)) { ++ pr_err("%s: Failed to create dev %s", __func__, ++ f->dev_name); ++ err = PTR_ERR(f->dev); ++ goto err_create; ++ } ++ ++ if (f->init) { ++ err = f->init(f, cdev); ++ if (err) { ++ pr_err("%s: Failed to init %s", __func__, ++ f->name); ++ goto err_out; ++ } ++ } ++ ++ attrs = f->attributes; ++ if (attrs) { ++ while ((attr = *attrs++) && !err) ++ err = device_create_file(f->dev, attr); ++ } ++ if (err) { ++ pr_err("%s: Failed to create function %s attributes", ++ __func__, f->name); ++ goto err_out; ++ } ++ } ++ return 0; ++ ++err_out: ++ device_destroy(android_class, f->dev->devt); ++err_create: ++ kfree(f->dev_name); ++ return err; ++} ++ ++static void android_cleanup_functions(struct android_usb_function **functions) ++{ ++ struct android_usb_function *f; ++ ++ while (*functions) { ++ f = *functions++; ++ ++ if (f->dev) { ++ device_destroy(android_class, f->dev->devt); ++ kfree(f->dev_name); ++ } ++ ++ if (f->cleanup) ++ f->cleanup(f); ++ } ++} ++ ++static int ++android_bind_enabled_functions(struct android_dev *dev, ++ struct usb_configuration *c) ++{ ++ struct android_usb_function *f; ++ int ret; ++ ++ list_for_each_entry(f, &dev->enabled_functions, enabled_list) { ++ ret = f->bind_config(f, c); ++ if (ret) { ++ pr_err("%s: %s failed", __func__, f->name); ++ return ret; ++ } ++ } ++ return 0; ++} ++ ++static void ++android_unbind_enabled_functions(struct android_dev *dev, ++ struct usb_configuration *c) ++{ ++ struct android_usb_function *f; ++ ++ list_for_each_entry(f, &dev->enabled_functions, enabled_list) { ++ if (f->unbind_config) ++ f->unbind_config(f, c); ++ } ++} ++ ++static int android_enable_function(struct android_dev *dev, char *name) ++{ ++ struct android_usb_function **functions = dev->functions; ++ struct android_usb_function *f; ++ while ((f = *functions++)) { ++ if (!strcmp(name, f->name)) { ++ list_add_tail(&f->enabled_list, ++ &dev->enabled_functions); ++ return 0; ++ } ++ } ++ return -EINVAL; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/* /sys/class/android_usb/android%d/ interface */ ++ ++static ssize_t ++functions_show(struct device *pdev, struct device_attribute *attr, char *buf) ++{ ++ struct android_dev *dev = dev_get_drvdata(pdev); ++ struct android_usb_function *f; ++ char *buff = buf; ++ ++ mutex_lock(&dev->mutex); ++ ++ list_for_each_entry(f, &dev->enabled_functions, enabled_list) ++ buff += sprintf(buff, "%s,", f->name); ++ ++ mutex_unlock(&dev->mutex); ++ ++ if (buff != buf) ++ *(buff-1) = '\n'; ++ return buff - buf; ++} ++ ++static ssize_t ++functions_store(struct device *pdev, struct device_attribute *attr, ++ const char *buff, size_t size) ++{ ++ struct android_dev *dev = dev_get_drvdata(pdev); ++ char *name; ++ char buf[256], *b; ++ char aliases[256], *a; ++ int err; ++ int is_ffs; ++ int ffs_enabled = 0; ++ ++ mutex_lock(&dev->mutex); ++ ++ if (dev->enabled) { ++ mutex_unlock(&dev->mutex); ++ return -EBUSY; ++ } ++ ++ INIT_LIST_HEAD(&dev->enabled_functions); ++ ++ strlcpy(buf, buff, sizeof(buf)); ++ b = strim(buf); ++ ++ while (b) { ++ name = strsep(&b, ","); ++ if (!name) ++ continue; ++ ++ is_ffs = 0; ++ strlcpy(aliases, dev->ffs_aliases, sizeof(aliases)); ++ a = aliases; ++ ++ while (a) { ++ char *alias = strsep(&a, ","); ++ if (alias && !strcmp(name, alias)) { ++ is_ffs = 1; ++ break; ++ } ++ } ++ ++ if (is_ffs) { ++ if (ffs_enabled) ++ continue; ++ err = android_enable_function(dev, "ffs"); ++ if (err) ++ pr_err("android_usb: Cannot enable ffs (%d)", ++ err); ++ else ++ ffs_enabled = 1; ++ continue; ++ } ++ ++ err = android_enable_function(dev, name); ++ if (err) ++ pr_err("android_usb: Cannot enable '%s' (%d)", ++ name, err); ++ } ++ ++ mutex_unlock(&dev->mutex); ++ ++ return size; ++} ++ ++static ssize_t enable_show(struct device *pdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct android_dev *dev = dev_get_drvdata(pdev); ++ return sprintf(buf, "%d\n", dev->enabled); ++} ++ ++static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, ++ const char *buff, size_t size) ++{ ++ struct android_dev *dev = dev_get_drvdata(pdev); ++ struct usb_composite_dev *cdev = dev->cdev; ++ struct android_usb_function *f; ++ int enabled = 0; ++ ++ ++ if (!cdev) ++ return -ENODEV; ++ ++ mutex_lock(&dev->mutex); ++ ++ sscanf(buff, "%d", &enabled); ++ if (enabled && !dev->enabled) { ++ cdev->next_string_id = 0; ++ /* ++ * Update values in composite driver's copy of ++ * device descriptor. ++ */ ++ cdev->desc.idVendor = device_desc.idVendor; ++ cdev->desc.idProduct = device_desc.idProduct; ++ cdev->desc.bcdDevice = device_desc.bcdDevice; ++ cdev->desc.bDeviceClass = device_desc.bDeviceClass; ++ cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass; ++ cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol; ++ list_for_each_entry(f, &dev->enabled_functions, enabled_list) { ++ if (f->enable) ++ f->enable(f); ++ } ++ android_enable(dev); ++ dev->enabled = true; ++ } else if (!enabled && dev->enabled) { ++ android_disable(dev); ++ list_for_each_entry(f, &dev->enabled_functions, enabled_list) { ++ if (f->disable) ++ f->disable(f); ++ } ++ dev->enabled = false; ++ } else { ++ pr_err("android_usb: already %s\n", ++ dev->enabled ? "enabled" : "disabled"); ++ } ++ ++ mutex_unlock(&dev->mutex); ++ return size; ++} ++ ++static ssize_t state_show(struct device *pdev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct android_dev *dev = dev_get_drvdata(pdev); ++ struct usb_composite_dev *cdev = dev->cdev; ++ char *state = "DISCONNECTED"; ++ unsigned long flags; ++ ++ if (!cdev) ++ goto out; ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ if (cdev->config) ++ state = "CONFIGURED"; ++ else if (dev->connected) ++ state = "CONNECTED"; ++ spin_unlock_irqrestore(&cdev->lock, flags); ++out: ++ return sprintf(buf, "%s\n", state); ++} ++ ++#define DESCRIPTOR_ATTR(field, format_string) \ ++static ssize_t \ ++field ## _show(struct device *dev, struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ return sprintf(buf, format_string, device_desc.field); \ ++} \ ++static ssize_t \ ++field ## _store(struct device *dev, struct device_attribute *attr, \ ++ const char *buf, size_t size) \ ++{ \ ++ int value; \ ++ if (sscanf(buf, format_string, &value) == 1) { \ ++ device_desc.field = value; \ ++ return size; \ ++ } \ ++ return -1; \ ++} \ ++static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store); ++ ++#define DESCRIPTOR_STRING_ATTR(field, buffer) \ ++static ssize_t \ ++field ## _show(struct device *dev, struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ return sprintf(buf, "%s", buffer); \ ++} \ ++static ssize_t \ ++field ## _store(struct device *dev, struct device_attribute *attr, \ ++ const char *buf, size_t size) \ ++{ \ ++ if (size >= sizeof(buffer)) \ ++ return -EINVAL; \ ++ return strlcpy(buffer, buf, sizeof(buffer)); \ ++} \ ++static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store); ++ ++ ++DESCRIPTOR_ATTR(idVendor, "%04x\n") ++DESCRIPTOR_ATTR(idProduct, "%04x\n") ++DESCRIPTOR_ATTR(bcdDevice, "%04x\n") ++DESCRIPTOR_ATTR(bDeviceClass, "%d\n") ++DESCRIPTOR_ATTR(bDeviceSubClass, "%d\n") ++DESCRIPTOR_ATTR(bDeviceProtocol, "%d\n") ++DESCRIPTOR_STRING_ATTR(iManufacturer, manufacturer_string) ++DESCRIPTOR_STRING_ATTR(iProduct, product_string) ++DESCRIPTOR_STRING_ATTR(iSerial, serial_string) ++ ++static DEVICE_ATTR(functions, S_IRUGO | S_IWUSR, functions_show, ++ functions_store); ++static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store); ++static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); ++ ++static struct device_attribute *android_usb_attributes[] = { ++ &dev_attr_idVendor, ++ &dev_attr_idProduct, ++ &dev_attr_bcdDevice, ++ &dev_attr_bDeviceClass, ++ &dev_attr_bDeviceSubClass, ++ &dev_attr_bDeviceProtocol, ++ &dev_attr_iManufacturer, ++ &dev_attr_iProduct, ++ &dev_attr_iSerial, ++ &dev_attr_functions, ++ &dev_attr_enable, ++ &dev_attr_state, ++ NULL ++}; ++ ++/*-------------------------------------------------------------------------*/ ++/* Composite driver */ ++ ++static int android_bind_config(struct usb_configuration *c) ++{ ++ struct android_dev *dev = _android_dev; ++ int ret = 0; ++ ++ ret = android_bind_enabled_functions(dev, c); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void android_unbind_config(struct usb_configuration *c) ++{ ++ struct android_dev *dev = _android_dev; ++ ++ android_unbind_enabled_functions(dev, c); ++} ++ ++static int android_bind(struct usb_composite_dev *cdev) ++{ ++ struct android_dev *dev = _android_dev; ++ struct usb_gadget *gadget = cdev->gadget; ++ int id, ret; ++ ++ /* Save the default handler */ ++ dev->setup_complete = cdev->req->complete; ++ ++ /* ++ * Start disconnected. Userspace will connect the gadget once ++ * it is done configuring the functions. ++ */ ++ usb_gadget_disconnect(gadget); ++ ++ ret = android_init_functions(dev->functions, cdev); ++ if (ret) ++ return ret; ++ ++ /* Allocate string descriptor numbers ... note that string ++ * contents can be overridden by the composite_dev glue. ++ */ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_dev[STRING_MANUFACTURER_IDX].id = id; ++ device_desc.iManufacturer = id; ++ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_dev[STRING_PRODUCT_IDX].id = id; ++ device_desc.iProduct = id; ++ ++ /* Default strings - should be updated by userspace */ ++ strncpy(manufacturer_string, "Android", sizeof(manufacturer_string)-1); ++ strncpy(product_string, "Android", sizeof(product_string) - 1); ++ strncpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1); ++ ++ id = usb_string_id(cdev); ++ if (id < 0) ++ return id; ++ strings_dev[STRING_SERIAL_IDX].id = id; ++ device_desc.iSerialNumber = id; ++ ++ usb_gadget_set_selfpowered(gadget); ++ dev->cdev = cdev; ++ ++ return 0; ++} ++ ++static int android_usb_unbind(struct usb_composite_dev *cdev) ++{ ++ struct android_dev *dev = _android_dev; ++ ++ cancel_work_sync(&dev->work); ++ android_cleanup_functions(dev->functions); ++ return 0; ++} ++ ++/* HACK: android needs to override setup for accessory to work */ ++static int (*composite_setup_func)(struct usb_gadget *gadget, const struct usb_ctrlrequest *c); ++ ++static int ++android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) ++{ ++ struct android_dev *dev = _android_dev; ++ struct usb_composite_dev *cdev = get_gadget_data(gadget); ++ struct usb_request *req = cdev->req; ++ struct android_usb_function *f; ++ int value = -EOPNOTSUPP; ++ unsigned long flags; ++ ++ req->zero = 0; ++ req->length = 0; ++ req->complete = dev->setup_complete; ++ gadget->ep0->driver_data = cdev; ++ ++ list_for_each_entry(f, &dev->enabled_functions, enabled_list) { ++ if (f->ctrlrequest) { ++ value = f->ctrlrequest(f, cdev, c); ++ if (value >= 0) ++ break; ++ } ++ } ++ ++ /* Special case the accessory function. ++ * It needs to handle control requests before it is enabled. ++ */ ++ if (value < 0) ++ value = acc_ctrlrequest(cdev, c); ++ ++ if (value < 0) ++ value = composite_setup_func(gadget, c); ++ ++ spin_lock_irqsave(&cdev->lock, flags); ++ if (!dev->connected) { ++ dev->connected = 1; ++ schedule_work(&dev->work); ++ } else if (c->bRequest == USB_REQ_SET_CONFIGURATION && ++ cdev->config) { ++ schedule_work(&dev->work); ++ } ++ spin_unlock_irqrestore(&cdev->lock, flags); ++ ++ return value; ++} ++ ++static void android_disconnect(struct usb_composite_dev *cdev) ++{ ++ struct android_dev *dev = _android_dev; ++ ++ /* accessory HID support can be active while the ++ accessory function is not actually enabled, ++ so we need to inform it when we are disconnected. ++ */ ++ acc_disconnect(); ++ ++ dev->connected = 0; ++ schedule_work(&dev->work); ++} ++ ++static struct usb_composite_driver android_usb_driver = { ++ .name = "android_usb", ++ .dev = &device_desc, ++ .strings = dev_strings, ++ .bind = android_bind, ++ .unbind = android_usb_unbind, ++ .disconnect = android_disconnect, ++ .max_speed = USB_SPEED_HIGH, ++}; ++ ++static int android_create_device(struct android_dev *dev) ++{ ++ struct device_attribute **attrs = android_usb_attributes; ++ struct device_attribute *attr; ++ int err; ++ ++ dev->dev = device_create(android_class, NULL, ++ MKDEV(0, 0), NULL, "android0"); ++ if (IS_ERR(dev->dev)) ++ return PTR_ERR(dev->dev); ++ ++ dev_set_drvdata(dev->dev, dev); ++ ++ while ((attr = *attrs++)) { ++ err = device_create_file(dev->dev, attr); ++ if (err) { ++ device_destroy(android_class, dev->dev->devt); ++ return err; ++ } ++ } ++ return 0; ++} ++ ++ ++static int __init init(void) ++{ ++ struct android_dev *dev; ++ int err; ++ ++ android_class = class_create(THIS_MODULE, "android_usb"); ++ if (IS_ERR(android_class)) ++ return PTR_ERR(android_class); ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) { ++ err = -ENOMEM; ++ goto err_dev; ++ } ++ ++ dev->disable_depth = 1; ++ dev->functions = supported_functions; ++ INIT_LIST_HEAD(&dev->enabled_functions); ++ INIT_WORK(&dev->work, android_work); ++ mutex_init(&dev->mutex); ++ ++ err = android_create_device(dev); ++ if (err) { ++ pr_err("%s: failed to create android device %d", __func__, err); ++ goto err_create; ++ } ++ ++ _android_dev = dev; ++ ++ err = usb_composite_probe(&android_usb_driver); ++ if (err) { ++ pr_err("%s: failed to probe driver %d", __func__, err); ++ goto err_probe; ++ } ++ ++ /* HACK: exchange composite's setup with ours */ ++ composite_setup_func = android_usb_driver.gadget_driver.setup; ++ android_usb_driver.gadget_driver.setup = android_setup; ++ ++ return 0; ++ ++err_probe: ++ device_destroy(android_class, dev->dev->devt); ++err_create: ++ kfree(dev); ++err_dev: ++ class_destroy(android_class); ++ return err; ++} ++late_initcall(init); ++ ++static void __exit cleanup(void) ++{ ++ usb_composite_unregister(&android_usb_driver); ++ class_destroy(android_class); ++ kfree(_android_dev); ++ _android_dev = NULL; ++} ++module_exit(cleanup); +diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c +index f46a395..1db5181 100644 +--- a/drivers/usb/gadget/legacy/audio.c ++++ b/drivers/usb/gadget/legacy/audio.c +@@ -15,7 +15,6 @@ + #include <linux/module.h> + #include <linux/usb/composite.h> + +-#include "gadget_chips.h" + #define DRIVER_DESC "Linux USB Audio Gadget" + #define DRIVER_VERSION "Feb 2, 2012" + +@@ -54,8 +53,51 @@ static int c_ssize = UAC2_DEF_CSSIZE; + module_param(c_ssize, uint, S_IRUGO); + MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); + #else ++#ifndef CONFIG_GADGET_UAC1_LEGACY + #include "u_uac1.h" + ++/* Playback(USB-IN) Default Stereo - Fl/Fr */ ++static int p_chmask = UAC1_DEF_PCHMASK; ++module_param(p_chmask, uint, S_IRUGO); ++MODULE_PARM_DESC(p_chmask, "Playback Channel Mask"); ++ ++/* Playback Default 48 KHz */ ++static int p_srate = UAC1_DEF_PSRATE; ++module_param(p_srate, uint, S_IRUGO); ++MODULE_PARM_DESC(p_srate, "Playback Sampling Rate"); ++ ++/* Playback Default 16bits/sample */ ++static int p_ssize = UAC1_DEF_PSSIZE; ++module_param(p_ssize, uint, S_IRUGO); ++MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)"); ++ ++/* Capture(USB-OUT) Default Stereo - Fl/Fr */ ++static int c_chmask = UAC1_DEF_CCHMASK; ++module_param(c_chmask, uint, S_IRUGO); ++MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); ++ ++/* Capture Default 48 KHz */ ++static int c_srate = UAC1_DEF_CSRATE; ++module_param(c_srate, uint, S_IRUGO); ++MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); ++ ++/* Capture Default 16bits/sample */ ++static int c_ssize = UAC1_DEF_CSSIZE; ++module_param(c_ssize, uint, S_IRUGO); ++MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); ++ ++/* req number */ ++static int req_num = UAC1_DEF_REQ_NUM; ++module_param(req_num, uint, S_IRUGO); ++MODULE_PARM_DESC(req_num, "req number"); ++ ++static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE; ++module_param(req_buf_size, int, S_IRUGO); ++MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); ++ ++#else /* CONFIG_GADGET_UAC1_LEGACY */ ++#include "u_uac1_legacy.h" ++ + static char *fn_play = FILE_PCM_PLAYBACK; + module_param(fn_play, charp, S_IRUGO); + MODULE_PARM_DESC(fn_play, "Playback PCM device file name"); +@@ -79,6 +121,7 @@ MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); + static int audio_buf_size = UAC1_AUDIO_BUF_SIZE; + module_param(audio_buf_size, int, S_IRUGO); + MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); ++#endif /* CONFIG_GADGET_UAC1_LEGACY */ + #endif + + /* string IDs are assigned dynamically */ +@@ -124,9 +167,9 @@ static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + +- .bcdUSB = __constant_cpu_to_le16(0x200), ++ /* .bcdUSB = DYNAMIC */ + +-#ifdef CONFIG_GADGET_UAC1 ++#ifdef CONFIG_GADGET_UAC1_LEGACY + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, +@@ -141,8 +184,8 @@ static struct usb_device_descriptor device_desc = { + * we support. (As does bNumConfigurations.) These values can + * also be overridden by module parameters. + */ +- .idVendor = __constant_cpu_to_le16(AUDIO_VENDOR_NUM), +- .idProduct = __constant_cpu_to_le16(AUDIO_PRODUCT_NUM), ++ .idVendor = cpu_to_le16(AUDIO_VENDOR_NUM), ++ .idProduct = cpu_to_le16(AUDIO_PRODUCT_NUM), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ +@@ -150,24 +193,11 @@ static struct usb_device_descriptor device_desc = { + .bNumConfigurations = 1, + }; + +-static struct usb_otg_descriptor otg_descriptor = { +- .bLength = sizeof otg_descriptor, +- .bDescriptorType = USB_DT_OTG, +- +- /* REVISIT SRP-only hardware is possible, although +- * it would not be called "OTG" ... +- */ +- .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +-}; +- +-static const struct usb_descriptor_header *otg_desc[] = { +- (struct usb_descriptor_header *) &otg_descriptor, +- NULL, +-}; ++static const struct usb_descriptor_header *otg_desc[2]; + + /*-------------------------------------------------------------------------*/ + +-static int __init audio_do_config(struct usb_configuration *c) ++static int audio_do_config(struct usb_configuration *c) + { + int status; + +@@ -216,12 +246,16 @@ static struct usb_configuration audio_config_driver = { + + /*-------------------------------------------------------------------------*/ + +-static int __init audio_bind(struct usb_composite_dev *cdev) ++static int audio_bind(struct usb_composite_dev *cdev) + { + #ifndef CONFIG_GADGET_UAC1 + struct f_uac2_opts *uac2_opts; + #else ++#ifndef CONFIG_GADGET_UAC1_LEGACY + struct f_uac1_opts *uac1_opts; ++#else ++ struct f_uac1_legacy_opts *uac1_opts; ++#endif + #endif + int status; + +@@ -230,7 +264,11 @@ static int __init audio_bind(struct usb_composite_dev *cdev) + if (IS_ERR(fi_uac2)) + return PTR_ERR(fi_uac2); + #else ++#ifndef CONFIG_GADGET_UAC1_LEGACY + fi_uac1 = usb_get_function_instance("uac1"); ++#else ++ fi_uac1 = usb_get_function_instance("uac1_legacy"); ++#endif + if (IS_ERR(fi_uac1)) + return PTR_ERR(fi_uac1); + #endif +@@ -243,14 +281,29 @@ static int __init audio_bind(struct usb_composite_dev *cdev) + uac2_opts->c_chmask = c_chmask; + uac2_opts->c_srate = c_srate; + uac2_opts->c_ssize = c_ssize; ++ uac2_opts->req_number = UAC2_DEF_REQ_NUM; + #else ++#ifndef CONFIG_GADGET_UAC1_LEGACY + uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst); ++ uac1_opts->p_chmask = p_chmask; ++ uac1_opts->p_srate = p_srate; ++ uac1_opts->p_ssize = p_ssize; ++ uac1_opts->c_chmask = c_chmask; ++ uac1_opts->c_srate = c_srate; ++ uac1_opts->c_ssize = c_ssize; ++ uac1_opts->req_number = UAC1_DEF_REQ_NUM; ++ uac1_opts->req_number = req_num; ++ uac1_opts->req_buf_size = req_buf_size; ++ ++#else /* CONFIG_GADGET_UAC1_LEGACY */ ++ uac1_opts = container_of(fi_uac1, struct f_uac1_legacy_opts, func_inst); + uac1_opts->fn_play = fn_play; + uac1_opts->fn_cap = fn_cap; + uac1_opts->fn_cntl = fn_cntl; + uac1_opts->req_buf_size = req_buf_size; + uac1_opts->req_count = req_count; + uac1_opts->audio_buf_size = audio_buf_size; ++#endif /* CONFIG_GADGET_UAC1_LEGACY */ + #endif + + status = usb_string_ids_tab(cdev, strings_dev); +@@ -259,14 +312,32 @@ static int __init audio_bind(struct usb_composite_dev *cdev) + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; + ++#if 0 ++ if (gadget_is_otg(cdev->gadget) && !otg_desc[0]) { ++ struct usb_descriptor_header *usb_desc; ++ ++ usb_desc = usb_otg_descriptor_alloc(cdev->gadget); ++ if (!usb_desc) ++ goto fail; ++ usb_otg_descriptor_init(cdev->gadget, usb_desc); ++ otg_desc[0] = usb_desc; ++ otg_desc[1] = NULL; ++ } ++#endif ++ ++#if 1 + status = usb_add_config(cdev, &audio_config_driver, audio_do_config); + if (status < 0) +- goto fail; ++ goto fail_otg_desc; + usb_composite_overwrite_options(cdev, &coverwrite); ++#endif + + INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION); + return 0; + ++fail_otg_desc: ++ kfree(otg_desc[0]); ++ otg_desc[0] = NULL; + fail: + #ifndef CONFIG_GADGET_UAC1 + usb_put_function_instance(fi_uac2); +@@ -276,7 +347,7 @@ fail: + return status; + } + +-static int __exit audio_unbind(struct usb_composite_dev *cdev) ++static int audio_unbind(struct usb_composite_dev *cdev) + { + #ifdef CONFIG_GADGET_UAC1 + if (!IS_ERR_OR_NULL(f_uac1)) +@@ -289,16 +360,20 @@ static int __exit audio_unbind(struct usb_composite_dev *cdev) + if (!IS_ERR_OR_NULL(fi_uac2)) + usb_put_function_instance(fi_uac2); + #endif ++ kfree(otg_desc[0]); ++ otg_desc[0] = NULL; ++ + return 0; + } + +-static __refdata struct usb_composite_driver audio_driver = { ++#if 1 ++static struct usb_composite_driver audio_driver = { + .name = "g_audio", + .dev = &device_desc, + .strings = audio_strings, + .max_speed = USB_SPEED_HIGH, + .bind = audio_bind, +- .unbind = __exit_p(audio_unbind), ++ .unbind = audio_unbind, + }; + + module_usb_composite_driver(audio_driver); +@@ -307,3 +382,5 @@ MODULE_DESCRIPTION(DRIVER_DESC); + MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>"); + MODULE_LICENSE("GPL"); + ++#endif ++ +diff --git a/drivers/usb/gadget/legacy/audio.inl b/drivers/usb/gadget/legacy/audio.inl +new file mode 100644 +index 0000000..6f31cbe +--- /dev/null ++++ b/drivers/usb/gadget/legacy/audio.inl +@@ -0,0 +1,328 @@ ++/* ++ * audio.c -- Audio gadget driver ++ * ++ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> ++ * Copyright (C) 2008 Analog Devices, Inc ++ * ++ * Enter bugs at http://blackfin.uclinux.org/ ++ * ++ * Licensed under the GPL-2 or later. ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/usb/composite.h> ++ ++#define DRIVER_DESC "Linux USB Audio Gadget" ++#define DRIVER_VERSION "Feb 2, 2012" ++ ++ ++#ifndef CONFIG_GADGET_UAC1 ++#include "u_uac2.h" ++ ++/* Playback(USB-IN) Default Stereo - Fl/Fr */ ++static int p_chmask = UAC2_DEF_PCHMASK; ++module_param(p_chmask, uint, S_IRUGO); ++MODULE_PARM_DESC(p_chmask, "Playback Channel Mask"); ++ ++/* Playback Default 48 KHz */ ++static int p_srate = UAC2_DEF_PSRATE; ++module_param(p_srate, uint, S_IRUGO); ++MODULE_PARM_DESC(p_srate, "Playback Sampling Rate"); ++ ++/* Playback Default 16bits/sample */ ++static int p_ssize = UAC2_DEF_PSSIZE; ++module_param(p_ssize, uint, S_IRUGO); ++MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)"); ++ ++/* Capture(USB-OUT) Default Stereo - Fl/Fr */ ++static int c_chmask = UAC2_DEF_CCHMASK; ++module_param(c_chmask, uint, S_IRUGO); ++MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); ++ ++/* Capture Default 64 KHz */ ++static int c_srate = UAC2_DEF_CSRATE; ++module_param(c_srate, uint, S_IRUGO); ++MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); ++ ++/* Capture Default 16bits/sample */ ++static int c_ssize = UAC2_DEF_CSSIZE; ++module_param(c_ssize, uint, S_IRUGO); ++MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); ++#else ++#ifndef CONFIG_GADGET_UAC1_LEGACY ++#include "u_uac1.h" ++ ++/* Playback(USB-IN) Default Stereo - Fl/Fr */ ++static int p_chmask = UAC1_DEF_PCHMASK; ++module_param(p_chmask, uint, S_IRUGO); ++MODULE_PARM_DESC(p_chmask, "Playback Channel Mask"); ++ ++/* Playback Default 48 KHz */ ++static int p_srate = UAC1_DEF_PSRATE; ++module_param(p_srate, uint, S_IRUGO); ++MODULE_PARM_DESC(p_srate, "Playback Sampling Rate"); ++ ++/* Playback Default 16bits/sample */ ++static int p_ssize = UAC1_DEF_PSSIZE; ++module_param(p_ssize, uint, S_IRUGO); ++MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)"); ++ ++/* Capture(USB-OUT) Default Stereo - Fl/Fr */ ++static int c_chmask = UAC1_DEF_CCHMASK; ++module_param(c_chmask, uint, S_IRUGO); ++MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); ++ ++/* Capture Default 48 KHz */ ++static int c_srate = UAC1_DEF_CSRATE; ++module_param(c_srate, uint, S_IRUGO); ++MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); ++ ++/* Capture Default 16bits/sample */ ++static int c_ssize = UAC1_DEF_CSSIZE; ++module_param(c_ssize, uint, S_IRUGO); ++MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); ++ ++/* req number */ ++static int req_num = UAC1_DEF_REQ_NUM; ++module_param(req_num, uint, S_IRUGO); ++MODULE_PARM_DESC(req_num, "req number"); ++ ++static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE; ++module_param(req_buf_size, int, S_IRUGO); ++MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); ++ ++#else /* CONFIG_GADGET_UAC1_LEGACY */ ++#include "u_uac1_legacy.h" ++ ++static char *fn_play = FILE_PCM_PLAYBACK; ++module_param(fn_play, charp, S_IRUGO); ++MODULE_PARM_DESC(fn_play, "Playback PCM device file name"); ++ ++static char *fn_cap = FILE_PCM_CAPTURE; ++module_param(fn_cap, charp, S_IRUGO); ++MODULE_PARM_DESC(fn_cap, "Capture PCM device file name"); ++ ++static char *fn_cntl = FILE_CONTROL; ++module_param(fn_cntl, charp, S_IRUGO); ++MODULE_PARM_DESC(fn_cntl, "Control device file name"); ++ ++static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE; ++module_param(req_buf_size, int, S_IRUGO); ++MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); ++ ++static int req_count = UAC1_REQ_COUNT; ++module_param(req_count, int, S_IRUGO); ++MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); ++ ++static int audio_buf_size = UAC1_AUDIO_BUF_SIZE; ++module_param(audio_buf_size, int, S_IRUGO); ++MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); ++#endif /* CONFIG_GADGET_UAC1_LEGACY */ ++#endif ++ ++/* string IDs are assigned dynamically */ ++ ++static struct usb_string strings_dev[] = { ++ [USB_GADGET_MANUFACTURER_IDX].s = "", ++ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, ++ [USB_GADGET_SERIAL_IDX].s = "", ++ { } /* end of list */ ++}; ++ ++ ++ ++#ifndef CONFIG_GADGET_UAC1 ++static struct usb_function_instance *fi_uac2; ++static struct usb_function *f_uac2; ++#else ++static struct usb_function_instance *fi_uac1; ++static struct usb_function *f_uac1; ++#endif ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! ++ * Instead: allocate your own, using normal USB-IF procedures. ++ */ ++ ++/* Thanks to Linux Foundation for donating this product ID. */ ++#define AUDIO_VENDOR_NUM 0x1d6b /* Linux Foundation */ ++#define AUDIO_PRODUCT_NUM 0x0101 /* Linux-USB Audio Gadget */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_device_descriptor device_desc = { ++ .bLength = sizeof device_desc, ++ .bDescriptorType = USB_DT_DEVICE, ++ ++ /* .bcdUSB = DYNAMIC */ ++ ++#ifdef CONFIG_GADGET_UAC1_LEGACY ++ .bDeviceClass = USB_CLASS_PER_INTERFACE, ++ .bDeviceSubClass = 0, ++ .bDeviceProtocol = 0, ++#else ++ .bDeviceClass = USB_CLASS_MISC, ++ .bDeviceSubClass = 0x02, ++ .bDeviceProtocol = 0x01, ++#endif ++ /* .bMaxPacketSize0 = f(hardware) */ ++ ++ /* Vendor and product id defaults change according to what configs ++ * we support. (As does bNumConfigurations.) These values can ++ * also be overridden by module parameters. ++ */ ++ .idVendor = cpu_to_le16(AUDIO_VENDOR_NUM), ++ .idProduct = cpu_to_le16(AUDIO_PRODUCT_NUM), ++ /* .bcdDevice = f(hardware) */ ++ /* .iManufacturer = DYNAMIC */ ++ /* .iProduct = DYNAMIC */ ++ /* NO SERIAL NUMBER */ ++ .bNumConfigurations = 1, ++}; ++ ++static const struct usb_descriptor_header *otg_desc[2]; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int audio_do_config(struct usb_configuration *c) ++{ ++ int status; ++ ++ /* FIXME alloc iConfiguration string, set it in c->strings */ ++ ++ if (gadget_is_otg(c->cdev->gadget)) { ++ c->descriptors = otg_desc; ++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ } ++ ++#ifdef CONFIG_GADGET_UAC1 ++ f_uac1 = usb_get_function(fi_uac1); ++ if (IS_ERR(f_uac1)) { ++ status = PTR_ERR(f_uac1); ++ return status; ++ } ++ ++ status = usb_add_function(c, f_uac1); ++ if (status < 0) { ++ usb_put_function(f_uac1); ++ return status; ++ } ++#else ++ f_uac2 = usb_get_function(fi_uac2); ++ if (IS_ERR(f_uac2)) { ++ status = PTR_ERR(f_uac2); ++ return status; ++ } ++ ++ status = usb_add_function(c, f_uac2); ++ if (status < 0) { ++ usb_put_function(f_uac2); ++ return status; ++ } ++#endif ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int audio_bind(struct usb_composite_dev *cdev) ++{ ++#ifndef CONFIG_GADGET_UAC1 ++ struct f_uac2_opts *uac2_opts; ++#else ++#ifndef CONFIG_GADGET_UAC1_LEGACY ++ struct f_uac1_opts *uac1_opts; ++#else ++ struct f_uac1_legacy_opts *uac1_opts; ++#endif ++#endif ++ int status; ++ ++#ifndef CONFIG_GADGET_UAC1 ++ fi_uac2 = usb_get_function_instance("uac2"); ++ if (IS_ERR(fi_uac2)) ++ return PTR_ERR(fi_uac2); ++#else ++#ifndef CONFIG_GADGET_UAC1_LEGACY ++ fi_uac1 = usb_get_function_instance("uac1"); ++#else ++ fi_uac1 = usb_get_function_instance("uac1_legacy"); ++#endif ++ if (IS_ERR(fi_uac1)) ++ return PTR_ERR(fi_uac1); ++#endif ++ ++#ifndef CONFIG_GADGET_UAC1 ++ uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst); ++ uac2_opts->p_chmask = p_chmask; ++ uac2_opts->p_srate = p_srate; ++ uac2_opts->p_ssize = p_ssize; ++ uac2_opts->c_chmask = c_chmask; ++ uac2_opts->c_srate = c_srate; ++ uac2_opts->c_ssize = c_ssize; ++ uac2_opts->req_number = UAC2_DEF_REQ_NUM; ++#else ++#ifndef CONFIG_GADGET_UAC1_LEGACY ++ uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst); ++ uac1_opts->p_chmask = p_chmask; ++ uac1_opts->p_srate = p_srate; ++ uac1_opts->p_ssize = p_ssize; ++ uac1_opts->c_chmask = c_chmask; ++ uac1_opts->c_srate = c_srate; ++ uac1_opts->c_ssize = c_ssize; ++ uac1_opts->req_number = UAC1_DEF_REQ_NUM; ++ uac1_opts->req_number = req_num; ++ uac1_opts->req_buf_size = req_buf_size; ++ ++#else /* CONFIG_GADGET_UAC1_LEGACY */ ++ uac1_opts = container_of(fi_uac1, struct f_uac1_legacy_opts, func_inst); ++ uac1_opts->fn_play = fn_play; ++ uac1_opts->fn_cap = fn_cap; ++ uac1_opts->fn_cntl = fn_cntl; ++ uac1_opts->req_buf_size = req_buf_size; ++ uac1_opts->req_count = req_count; ++ uac1_opts->audio_buf_size = audio_buf_size; ++#endif /* CONFIG_GADGET_UAC1_LEGACY */ ++#endif ++ ++ status = usb_string_ids_tab(cdev, strings_dev); ++ if (status < 0) ++ goto fail; ++ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; ++ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; ++ ++ INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION); ++ return 0; ++ ++fail: ++#ifndef CONFIG_GADGET_UAC1 ++ usb_put_function_instance(fi_uac2); ++#else ++ usb_put_function_instance(fi_uac1); ++#endif ++ return status; ++} ++ ++static int audio_unbind(struct usb_composite_dev *cdev) ++{ ++#ifdef CONFIG_GADGET_UAC1 ++ if (!IS_ERR_OR_NULL(f_uac1)) ++ usb_put_function(f_uac1); ++ if (!IS_ERR_OR_NULL(fi_uac1)) ++ usb_put_function_instance(fi_uac1); ++#else ++ if (!IS_ERR_OR_NULL(f_uac2)) ++ usb_put_function(f_uac2); ++ if (!IS_ERR_OR_NULL(fi_uac2)) ++ usb_put_function_instance(fi_uac2); ++#endif ++ ++ return 0; ++} ++ ++ +diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c +index 3d696b8..b7c631d 100644 +--- a/drivers/usb/gadget/legacy/gmidi.c ++++ b/drivers/usb/gadget/legacy/gmidi.c +@@ -37,6 +37,7 @@ + + #include "gadget_chips.h" + ++#define USBF_MIDI_INCLUDED + #include "f_midi.c" + + /*-------------------------------------------------------------------------*/ +@@ -115,6 +116,9 @@ static struct usb_gadget_strings *dev_strings[] = { + NULL, + }; + ++static struct usb_function_instance *fi_midi; ++static struct usb_function *f_midi; ++ + static int __exit midi_unbind(struct usb_composite_dev *dev) + { + return 0; +diff --git a/drivers/usb/gadget/legacy/multi.c b/drivers/usb/gadget/legacy/multi.c +index 39d27bb..74b258e 100644 +--- a/drivers/usb/gadget/legacy/multi.c ++++ b/drivers/usb/gadget/legacy/multi.c +@@ -151,7 +151,9 @@ static struct usb_function *f_msg_rndis; + + static __init int rndis_do_config(struct usb_configuration *c) + { ++#if 0 + struct fsg_opts *fsg_opts; ++#endif + int ret; + + if (gadget_is_otg(c->cdev->gadget)) { +@@ -177,6 +179,7 @@ static __init int rndis_do_config(struct usb_configuration *c) + if (ret) + goto err_conf; + ++#if 0 + f_msg_rndis = usb_get_function(fi_msg); + if (IS_ERR(f_msg_rndis)) { + ret = PTR_ERR(f_msg_rndis); +@@ -192,11 +195,14 @@ static __init int rndis_do_config(struct usb_configuration *c) + if (ret) + goto err_run; + ++#endif + return 0; ++#if 0 + err_run: + usb_put_function(f_msg_rndis); + err_fsg: + usb_remove_function(c, f_acm_rndis); ++#endif + err_conf: + usb_put_function(f_acm_rndis); + err_func_acm: +@@ -265,7 +271,7 @@ static __init int cdc_do_config(struct usb_configuration *c) + ret = usb_add_function(c, f_acm_multi); + if (ret) + goto err_conf; +- ++#if 0 + f_msg_multi = usb_get_function(fi_msg); + if (IS_ERR(f_msg_multi)) { + ret = PTR_ERR(f_msg_multi); +@@ -280,6 +286,7 @@ static __init int cdc_do_config(struct usb_configuration *c) + ret = usb_add_function(c, f_msg_multi); + if (ret) + goto err_run; ++#endif + + return 0; + err_run: +@@ -330,8 +337,10 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) + #ifdef USB_ETH_RNDIS + struct f_rndis_opts *rndis_opts; + #endif ++#if 0 + struct fsg_opts *fsg_opts; + struct fsg_config config; ++#endif + int status; + + if (!can_support_ecm(cdev->gadget)) { +@@ -392,7 +401,7 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) + status = PTR_ERR(fi_acm); + goto fail0; + } +- ++#if 0 + /* set up mass storage function */ + fi_msg = usb_get_function_instance("mass_storage"); + if (IS_ERR(fi_msg)) { +@@ -422,7 +431,7 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) + + fsg_common_set_inquiry_string(fsg_opts->common, config.vendor_name, + config.product_name); +- ++#endif + /* allocate string IDs */ + status = usb_string_ids_tab(cdev, strings_dev); + if (unlikely(status < 0)) +@@ -446,6 +455,7 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) + + /* error recovery */ + fail_string_ids: ++#if 0 + fsg_common_remove_luns(fsg_opts->common); + fail_set_cdev: + fsg_common_free_luns(fsg_opts->common); +@@ -455,6 +465,7 @@ fail2: + usb_put_function_instance(fi_msg); + fail1: + usb_put_function_instance(fi_acm); ++#endif + fail0: + #ifdef USB_ETH_RNDIS + usb_put_function_instance(fi_rndis); +@@ -504,7 +515,7 @@ static __refdata struct usb_composite_driver multi_driver = { + .max_speed = USB_SPEED_HIGH, + .bind = multi_bind, + .unbind = __exit_p(multi_unbind), +- .needs_serial = 1, ++ .needs_serial = 0, + }; + + module_usb_composite_driver(multi_driver); +diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c +index 04a3da2..e1fe6b2 100644 +--- a/drivers/usb/gadget/legacy/webcam.c ++++ b/drivers/usb/gadget/legacy/webcam.c +@@ -26,11 +26,11 @@ static unsigned int streaming_interval = 1; + module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); + MODULE_PARM_DESC(streaming_interval, "1 - 16"); + +-static unsigned int streaming_maxpacket = 1024; ++static unsigned int streaming_maxpacket = 3072; + module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); + MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); + +-static unsigned int streaming_maxburst; ++static unsigned int streaming_maxburst = 14; + module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); + MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); + +@@ -49,6 +49,17 @@ static char webcam_vendor_label[] = "Linux Foundation"; + static char webcam_product_label[] = "Webcam gadget"; + static char webcam_config_label[] = "Video"; + ++/* GUID of the UVC H.264 extension unit: ++{A29E7641-DE04-47E3-8B2B-F4341AFF003B} */ ++#define GUID_UVCX_H264_XU {0x41, 0x76, 0x9E, 0xA2, 0x04, 0xDE, 0xE3, 0x47, \ ++ 0x8B, 0x2B, 0xF4, 0x34, 0x1A, 0xFF, 0x00, 0x3B} ++ ++#define UVC_GUID_HI_CAMERA {0x91, 0x72, 0x1e, 0x9a, 0x43, 0x68, 0x83, 0x46, \ ++ 0x6d, 0x92, 0x39, 0xbc, 0x79, 0x06, 0xee, 0x49} ++ ++#define UVC_GUID_FORMAT_H264 {0x48, 0x32, 0x36, 0x34, 0x00, 0x00, 0x10, 0x00, \ ++ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} ++ + /* string IDs are assigned dynamically */ + + #define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX +@@ -97,7 +108,7 @@ static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = { + .bLength = UVC_DT_HEADER_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VC_HEADER, +- .bcdUVC = cpu_to_le16(0x0100), ++ .bcdUVC = cpu_to_le16(0x0110), + .wTotalLength = 0, /* dynamic */ + .dwClockFrequency = cpu_to_le32(48000000), + .bInCollection = 0, /* dynamic */ +@@ -108,7 +119,7 @@ static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = { + .bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VC_INPUT_TERMINAL, +- .bTerminalID = 1, ++ .bTerminalID = 2, + .wTerminalType = cpu_to_le16(0x0201), + .bAssocTerminal = 0, + .iTerminal = 0, +@@ -116,24 +127,56 @@ static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = { + .wObjectiveFocalLengthMax = cpu_to_le16(0), + .wOcularFocalLength = cpu_to_le16(0), + .bControlSize = 3, +- .bmControls[0] = 2, +- .bmControls[1] = 0, ++ .bmControls[0] = 0x1a, ++ .bmControls[1] = 0x00, + .bmControls[2] = 0, + }; + + static const struct uvc_processing_unit_descriptor uvc_processing = { +- .bLength = UVC_DT_PROCESSING_UNIT_SIZE(2), ++ .bLength = sizeof(uvc_processing), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VC_PROCESSING_UNIT, +- .bUnitID = 2, ++ .bUnitID = 5, + .bSourceID = 1, + .wMaxMultiplier = cpu_to_le16(16*1024), + .bControlSize = 2, +- .bmControls[0] = 1, +- .bmControls[1] = 0, ++ .bmControls[0] = 0xff, ++ .bmControls[1] = 0xff, + .iProcessing = 0, + }; + ++DECLARE_UVC_EXTENSION_UNIT_DESCRIPTOR(1, 2); ++ ++static const struct UVC_EXTENSION_UNIT_DESCRIPTOR(1, 2) uvc_xu_h264_desc = { ++ .bLength = UVC_DT_EXTENSION_UNIT_SIZE(1, 2), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VC_EXTENSION_UNIT, ++ .bUnitID = 10, ++ .guidExtensionCode = GUID_UVCX_H264_XU, ++ .bNumControls = 15, ++ .bNrInPins = 1, ++ .baSourceID[0] = 2, ++ .bControlSize = 2, ++ .bmControls[0] = 0xff, ++ .bmControls[1] = 0xff, ++ .iExtension = 0, ++}; ++ ++static const struct UVC_EXTENSION_UNIT_DESCRIPTOR(1, 2) uvc_xu_hicamera_desc = { ++ .bLength = UVC_DT_EXTENSION_UNIT_SIZE(1, 2), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VC_EXTENSION_UNIT, ++ .bUnitID = 0x11, ++ .guidExtensionCode = UVC_GUID_HI_CAMERA, ++ .bNumControls = 15, ++ .bNrInPins = 1, ++ .baSourceID[0] = 10, ++ .bControlSize = 2, ++ .bmControls[0] = 0xff, ++ .bmControls[1] = 0xff, ++ .iExtension = 0, ++}; ++ + static const struct uvc_output_terminal_descriptor uvc_output_terminal = { + .bLength = UVC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, +@@ -141,7 +184,7 @@ static const struct uvc_output_terminal_descriptor uvc_output_terminal = { + .bTerminalID = 3, + .wTerminalType = cpu_to_le16(0x0101), + .bAssocTerminal = 0, +- .bSourceID = 2, ++ .bSourceID = 0x11, + .iTerminal = 0, + }; + +@@ -169,7 +212,7 @@ static const struct uvc_format_uncompressed uvc_format_yuv = { + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED, + .bFormatIndex = 1, +- .bNumFrameDescriptors = 2, ++ .bNumFrameDescriptors = 5, + .guidFormat = + { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}, +@@ -183,39 +226,100 @@ static const struct uvc_format_uncompressed uvc_format_yuv = { + + DECLARE_UVC_FRAME_UNCOMPRESSED(1); + DECLARE_UVC_FRAME_UNCOMPRESSED(3); +- +-static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = { +- .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3), ++static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_144p = { ++ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, + .bFrameIndex = 1, + .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(176), ++ .wHeight = cpu_to_le16(144), ++ .dwMinBitRate = cpu_to_le32(56088), ++ .dwMaxBitRate = cpu_to_le32(56088), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_240p = { ++ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, ++ .bFrameIndex = 2, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(320), ++ .wHeight = cpu_to_le16(240), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_288p = { ++ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, ++ .bFrameIndex = 3, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(352), ++ .wHeight = cpu_to_le16(288), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_480p = { ++ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, ++ .bFrameIndex = 4, ++ .bmCapabilities = 0, + .wWidth = cpu_to_le16(640), +- .wHeight = cpu_to_le16(360), +- .dwMinBitRate = cpu_to_le32(18432000), ++ .wHeight = cpu_to_le16(480), ++ .dwMinBitRate = cpu_to_le32(55296000), + .dwMaxBitRate = cpu_to_le32(55296000), + .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), +- .dwDefaultFrameInterval = cpu_to_le32(666666), +- .bFrameIntervalType = 3, +- .dwFrameInterval[0] = cpu_to_le32(666666), +- .dwFrameInterval[1] = cpu_to_le32(1000000), +- .dwFrameInterval[2] = cpu_to_le32(5000000), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), + }; + + static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = { + .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, +- .bFrameIndex = 2, ++ .bFrameIndex = 5, + .bmCapabilities = 0, + .wWidth = cpu_to_le16(1280), + .wHeight = cpu_to_le16(720), + .dwMinBitRate = cpu_to_le32(29491200), + .dwMaxBitRate = cpu_to_le32(29491200), + .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), +- .dwDefaultFrameInterval = cpu_to_le32(5000000), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_1080p = { ++ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, ++ .bFrameIndex = 6, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(1920), ++ .wHeight = cpu_to_le16(1080), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), + .bFrameIntervalType = 1, +- .dwFrameInterval[0] = cpu_to_le32(5000000), ++ .dwFrameInterval[0] = cpu_to_le32(333333), + }; + + static const struct uvc_format_mjpeg uvc_format_mjpg = { +@@ -223,7 +327,7 @@ static const struct uvc_format_mjpeg uvc_format_mjpg = { + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FORMAT_MJPEG, + .bFormatIndex = 2, +- .bNumFrameDescriptors = 2, ++ .bNumFrameDescriptors = 6, + .bmFlags = 0, + .bDefaultFrameIndex = 1, + .bAspectRatioX = 0, +@@ -235,38 +339,230 @@ static const struct uvc_format_mjpeg uvc_format_mjpg = { + DECLARE_UVC_FRAME_MJPEG(1); + DECLARE_UVC_FRAME_MJPEG(3); + +-static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = { +- .bLength = UVC_DT_FRAME_MJPEG_SIZE(3), ++static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_144p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FRAME_MJPEG, + .bFrameIndex = 1, + .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(176), ++ .wHeight = cpu_to_le16(144), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_240p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_MJPEG, ++ .bFrameIndex = 2, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(320), ++ .wHeight = cpu_to_le16(240), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_288p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_MJPEG, ++ .bFrameIndex = 3, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(352), ++ .wHeight = cpu_to_le16(288), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++ ++static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_480p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_MJPEG, ++ .bFrameIndex = 4, ++ .bmCapabilities = 0, + .wWidth = cpu_to_le16(640), +- .wHeight = cpu_to_le16(360), +- .dwMinBitRate = cpu_to_le32(18432000), ++ .wHeight = cpu_to_le16(480), ++ .dwMinBitRate = cpu_to_le32(55296000), + .dwMaxBitRate = cpu_to_le32(55296000), + .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), +- .dwDefaultFrameInterval = cpu_to_le32(666666), +- .bFrameIntervalType = 3, +- .dwFrameInterval[0] = cpu_to_le32(666666), +- .dwFrameInterval[1] = cpu_to_le32(1000000), +- .dwFrameInterval[2] = cpu_to_le32(5000000), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), + }; + + static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = { + .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FRAME_MJPEG, +- .bFrameIndex = 2, ++ .bFrameIndex = 5, + .bmCapabilities = 0, + .wWidth = cpu_to_le16(1280), + .wHeight = cpu_to_le16(720), + .dwMinBitRate = cpu_to_le32(29491200), + .dwMaxBitRate = cpu_to_le32(29491200), + .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), +- .dwDefaultFrameInterval = cpu_to_le32(5000000), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_1080p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_MJPEG, ++ .bFrameIndex = 6, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(1920), ++ .wHeight = cpu_to_le16(1080), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct uvc_format_h264 uvc_format_h264 = { ++ .bLength = UVC_DT_FORMAT_H264_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FORMAT_H264, ++ .bFormatIndex = 3, ++ .bNumFrameDescriptors = 6, ++ .bmFlags = 0, ++ .bDefaultFrameIndex = 1, ++ .bAspectRatioX = 0, ++ .bAspectRatioY = 0, ++ .bmInterfaceFlags = 0, ++ .bCopyProtect = 0, ++}; ++ ++static const struct uvc_format_h264_base uvc_format_h264_base = { ++ .bLength = sizeof(uvc_format_h264_base), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FORMAT_FRAME_BASED, ++ .bFormatIndex = 3, ++ .bNumFrameDescriptors = 6, ++ .guidFormat = UVC_GUID_FORMAT_H264, ++ .bBitsPerPixel = 16, ++ .bDefaultFrameIndex = 1, ++ .bAspectRatioX = 0, ++ .bAspectRatioY = 0, ++ .bmInterfaceFlags = 0, ++ .bCopyProtect = 0, ++ .bVariableSize = 1, ++}; ++ ++DECLARE_UVC_FRAME_H264_BASE(1); ++DECLARE_UVC_FRAME_H264_BASE(3); ++ ++static const struct UVC_FRAME_H264_BASE(1) uvc_frame_h264_720p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_FRAME_BASED, ++ .bFrameIndex = 5, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(1280), ++ .wHeight = cpu_to_le16(720), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), + .bFrameIntervalType = 1, +- .dwFrameInterval[0] = cpu_to_le32(5000000), ++ .dwBytesPerLine = 0, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_H264_BASE(1) uvc_frame_h264_1080p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_FRAME_BASED, ++ .bFrameIndex = 6, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(1920), ++ .wHeight = cpu_to_le16(1080), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwBytesPerLine = 0, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_H264_BASE(1) uvc_frame_h264_480p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_FRAME_BASED, ++ .bFrameIndex = 4, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(640), ++ .wHeight = cpu_to_le16(480), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwBytesPerLine = 0, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_H264_BASE(1) uvc_frame_h264_144p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_FRAME_BASED, ++ .bFrameIndex = 1, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(176), ++ .wHeight = cpu_to_le16(144), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwBytesPerLine = 0, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_H264_BASE(1) uvc_frame_h264_240p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_FRAME_BASED, ++ .bFrameIndex = 2, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(320), ++ .wHeight = cpu_to_le16(240), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwBytesPerLine = 0, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_H264_BASE(1) uvc_frame_h264_288p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_FRAME_BASED, ++ .bFrameIndex = 3, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(352), ++ .wHeight = cpu_to_le16(288), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwBytesPerLine = 0, ++ .dwFrameInterval[0] = cpu_to_le32(333333), + }; + + static const struct uvc_color_matching_descriptor uvc_color_matching = { +@@ -282,6 +578,8 @@ static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = { + (const struct uvc_descriptor_header *) &uvc_control_header, + (const struct uvc_descriptor_header *) &uvc_camera_terminal, + (const struct uvc_descriptor_header *) &uvc_processing, ++ (const struct uvc_descriptor_header *) &uvc_xu_h264_desc, ++ (const struct uvc_descriptor_header *) &uvc_xu_hicamera_desc, + (const struct uvc_descriptor_header *) &uvc_output_terminal, + NULL, + }; +@@ -290,6 +588,8 @@ static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = { + (const struct uvc_descriptor_header *) &uvc_control_header, + (const struct uvc_descriptor_header *) &uvc_camera_terminal, + (const struct uvc_descriptor_header *) &uvc_processing, ++ (const struct uvc_descriptor_header *) &uvc_xu_h264_desc, ++ (const struct uvc_descriptor_header *) &uvc_xu_hicamera_desc, + (const struct uvc_descriptor_header *) &uvc_output_terminal, + NULL, + }; +@@ -297,11 +597,25 @@ static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = { + static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = { + (const struct uvc_descriptor_header *) &uvc_input_header, + (const struct uvc_descriptor_header *) &uvc_format_yuv, +- (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_288p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_480p, + (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, + (const struct uvc_descriptor_header *) &uvc_format_mjpg, +- (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_288p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_480p, + (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_1080p, ++ (const struct uvc_descriptor_header *) &uvc_format_h264_base, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_720p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_1080p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_480p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_288p, + (const struct uvc_descriptor_header *) &uvc_color_matching, + NULL, + }; +@@ -309,11 +623,25 @@ static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = { + static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = { + (const struct uvc_descriptor_header *) &uvc_input_header, + (const struct uvc_descriptor_header *) &uvc_format_yuv, +- (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_288p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_480p, + (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, + (const struct uvc_descriptor_header *) &uvc_format_mjpg, +- (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_288p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_480p, + (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_1080p, ++ (const struct uvc_descriptor_header *) &uvc_format_h264_base, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_720p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_1080p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_480p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_288p, + (const struct uvc_descriptor_header *) &uvc_color_matching, + NULL, + }; +@@ -321,11 +649,25 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = { + static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = { + (const struct uvc_descriptor_header *) &uvc_input_header, + (const struct uvc_descriptor_header *) &uvc_format_yuv, +- (const struct uvc_descriptor_header *) &uvc_frame_yuv_360p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_288p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_480p, + (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, + (const struct uvc_descriptor_header *) &uvc_format_mjpg, +- (const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_288p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_480p, + (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_1080p, ++ (const struct uvc_descriptor_header *) &uvc_format_h264_base, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_720p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_1080p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_480p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_288p, + (const struct uvc_descriptor_header *) &uvc_color_matching, + NULL, + }; +@@ -346,7 +688,6 @@ webcam_config_bind(struct usb_configuration *c) + status = usb_add_function(c, f_uvc); + if (status < 0) + usb_put_function(f_uvc); +- + return status; + } + +diff --git a/drivers/usb/gadget/legacy/webcam_audio.c b/drivers/usb/gadget/legacy/webcam_audio.c +new file mode 100644 +index 0000000..826c65e +--- /dev/null ++++ b/drivers/usb/gadget/legacy/webcam_audio.c +@@ -0,0 +1,791 @@ ++/* ++ * webcam.c -- USB webcam gadget driver ++ * ++ * Copyright (C) 2009-2010 ++ * Laurent Pinchart (laurent.pinchart@ideasonboard.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/kernel.h> ++#include <linux/device.h> ++#include <linux/module.h> ++#include <linux/usb/video.h> ++ ++#include "u_uvc.h" ++ ++USB_GADGET_COMPOSITE_OPTIONS(); ++ ++#include "audio.inl" ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* module parameters specific to the Video streaming endpoint */ ++static unsigned int streaming_interval = 1; ++module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(streaming_interval, "1 - 16"); ++ ++static unsigned int streaming_maxpacket = 3072; ++module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); ++ ++static unsigned int streaming_maxburst = 14; ++module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); ++ ++static unsigned int trace; ++module_param(trace, uint, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(trace, "Trace level bitmask"); ++/* -------------------------------------------------------------------------- ++ * Device descriptor ++ */ ++ ++#define WEBCAM_VENDOR_ID 0x1d6b /* Linux Foundation */ ++#define WEBCAM_PRODUCT_ID 0x0102 /* Webcam A/V gadget */ ++#define WEBCAM_DEVICE_BCD 0x0010 /* 0.10 */ ++ ++static char webcam_vendor_label[] = "Linux Foundation"; ++static char webcam_product_label[] = "Webcam gadget"; ++static char webcam_config_label[] = "Video"; ++ ++/* GUID of the UVC H.264 extension unit: ++{A29E7641-DE04-47E3-8B2B-F4341AFF003B} */ ++#define GUID_UVCX_H264_XU {0x41, 0x76, 0x9E, 0xA2, 0x04, 0xDE, 0xE3, 0x47, \ ++ 0x8B, 0x2B, 0xF4, 0x34, 0x1A, 0xFF, 0x00, 0x3B} ++ ++#define UVC_GUID_HI_CAMERA {0x91, 0x72, 0x1e, 0x9a, 0x43, 0x68, 0x83, 0x46, \ ++ 0x6d, 0x92, 0x39, 0xbc, 0x79, 0x06, 0xee, 0x49} ++ ++#define UVC_GUID_FORMAT_H264 {0x48, 0x32, 0x36, 0x34, 0x00, 0x00, 0x10, 0x00, \ ++ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} ++ ++/* string IDs are assigned dynamically */ ++ ++#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX ++ ++static struct usb_string webcam_strings[] = { ++ [USB_GADGET_MANUFACTURER_IDX].s = webcam_vendor_label, ++ [USB_GADGET_PRODUCT_IDX].s = webcam_product_label, ++ [USB_GADGET_SERIAL_IDX].s = "", ++ [STRING_DESCRIPTION_IDX].s = webcam_config_label, ++ { } ++}; ++ ++static struct usb_gadget_strings webcam_stringtab = { ++ .language = 0x0409, /* en-us */ ++ .strings = webcam_strings, ++}; ++ ++static struct usb_gadget_strings *webcam_device_strings[] = { ++ &webcam_stringtab, ++ NULL, ++}; ++ ++static struct usb_function_instance *fi_uvc; ++static struct usb_function *f_uvc; ++ ++static struct usb_device_descriptor webcam_device_descriptor = { ++ .bLength = USB_DT_DEVICE_SIZE, ++ .bDescriptorType = USB_DT_DEVICE, ++ .bcdUSB = cpu_to_le16(0x0200), ++ .bDeviceClass = USB_CLASS_MISC, ++ .bDeviceSubClass = 0x02, ++ .bDeviceProtocol = 0x01, ++ .bMaxPacketSize0 = 0, /* dynamic */ ++ .idVendor = cpu_to_le16(WEBCAM_VENDOR_ID), ++ .idProduct = cpu_to_le16(WEBCAM_PRODUCT_ID), ++ .bcdDevice = cpu_to_le16(WEBCAM_DEVICE_BCD), ++ .iManufacturer = 0, /* dynamic */ ++ .iProduct = 0, /* dynamic */ ++ .iSerialNumber = 0, /* dynamic */ ++ .bNumConfigurations = 0, /* dynamic */ ++}; ++ ++DECLARE_UVC_HEADER_DESCRIPTOR(1); ++ ++static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = { ++ .bLength = UVC_DT_HEADER_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VC_HEADER, ++ .bcdUVC = cpu_to_le16(0x0110), ++ .wTotalLength = 0, /* dynamic */ ++ .dwClockFrequency = cpu_to_le32(48000000), ++ .bInCollection = 0, /* dynamic */ ++ .baInterfaceNr[0] = 0, /* dynamic */ ++}; ++ ++static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = { ++ .bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VC_INPUT_TERMINAL, ++ .bTerminalID = 2, ++ .wTerminalType = cpu_to_le16(0x0201), ++ .bAssocTerminal = 0, ++ .iTerminal = 0, ++ .wObjectiveFocalLengthMin = cpu_to_le16(0), ++ .wObjectiveFocalLengthMax = cpu_to_le16(0), ++ .wOcularFocalLength = cpu_to_le16(0), ++ .bControlSize = 3, ++ .bmControls[0] = 0x1a, ++ .bmControls[1] = 0x00, ++ .bmControls[2] = 0, ++}; ++ ++static const struct uvc_processing_unit_descriptor uvc_processing = { ++ .bLength = sizeof(uvc_processing), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VC_PROCESSING_UNIT, ++ .bUnitID = 5, ++ .bSourceID = 1, ++ .wMaxMultiplier = cpu_to_le16(16*1024), ++ .bControlSize = 2, ++ .bmControls[0] = 0xff, ++ .bmControls[1] = 0xff, ++ .iProcessing = 0, ++}; ++ ++DECLARE_UVC_EXTENSION_UNIT_DESCRIPTOR(1, 2); ++ ++static const struct UVC_EXTENSION_UNIT_DESCRIPTOR(1, 2) uvc_xu_h264_desc = { ++ .bLength = UVC_DT_EXTENSION_UNIT_SIZE(1, 2), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VC_EXTENSION_UNIT, ++ .bUnitID = 10, ++ .guidExtensionCode = GUID_UVCX_H264_XU, ++ .bNumControls = 15, ++ .bNrInPins = 1, ++ .baSourceID[0] = 2, ++ .bControlSize = 2, ++ .bmControls[0] = 0xff, ++ .bmControls[1] = 0xff, ++ .iExtension = 0, ++}; ++ ++static const struct UVC_EXTENSION_UNIT_DESCRIPTOR(1, 2) uvc_xu_hicamera_desc = { ++ .bLength = UVC_DT_EXTENSION_UNIT_SIZE(1, 2), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VC_EXTENSION_UNIT, ++ .bUnitID = 0x11, ++ .guidExtensionCode = UVC_GUID_HI_CAMERA, ++ .bNumControls = 15, ++ .bNrInPins = 1, ++ .baSourceID[0] = 10, ++ .bControlSize = 2, ++ .bmControls[0] = 0xff, ++ .bmControls[1] = 0xff, ++ .iExtension = 0, ++}; ++ ++static const struct uvc_output_terminal_descriptor uvc_output_terminal = { ++ .bLength = UVC_DT_OUTPUT_TERMINAL_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL, ++ .bTerminalID = 3, ++ .wTerminalType = cpu_to_le16(0x0101), ++ .bAssocTerminal = 0, ++ .bSourceID = 0x11, ++ .iTerminal = 0, ++}; ++ ++DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 2); ++ ++static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = { ++ .bLength = UVC_DT_INPUT_HEADER_SIZE(1, 2), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_INPUT_HEADER, ++ .bNumFormats = 2, ++ .wTotalLength = 0, /* dynamic */ ++ .bEndpointAddress = 0, /* dynamic */ ++ .bmInfo = 0, ++ .bTerminalLink = 3, ++ .bStillCaptureMethod = 0, ++ .bTriggerSupport = 0, ++ .bTriggerUsage = 0, ++ .bControlSize = 1, ++ .bmaControls[0][0] = 0, ++ .bmaControls[1][0] = 4, ++}; ++ ++static const struct uvc_format_uncompressed uvc_format_yuv = { ++ .bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED, ++ .bFormatIndex = 1, ++ .bNumFrameDescriptors = 5, ++ .guidFormat = ++ { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, ++ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}, ++ .bBitsPerPixel = 16, ++ .bDefaultFrameIndex = 1, ++ .bAspectRatioX = 0, ++ .bAspectRatioY = 0, ++ .bmInterfaceFlags = 0, ++ .bCopyProtect = 0, ++}; ++ ++DECLARE_UVC_FRAME_UNCOMPRESSED(1); ++DECLARE_UVC_FRAME_UNCOMPRESSED(3); ++static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_144p = { ++ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, ++ .bFrameIndex = 1, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(176), ++ .wHeight = cpu_to_le16(144), ++ .dwMinBitRate = cpu_to_le32(56088), ++ .dwMaxBitRate = cpu_to_le32(56088), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_240p = { ++ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, ++ .bFrameIndex = 2, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(320), ++ .wHeight = cpu_to_le16(240), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_288p = { ++ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, ++ .bFrameIndex = 3, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(352), ++ .wHeight = cpu_to_le16(288), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_480p = { ++ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, ++ .bFrameIndex = 4, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(640), ++ .wHeight = cpu_to_le16(480), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = { ++ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, ++ .bFrameIndex = 5, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(1280), ++ .wHeight = cpu_to_le16(720), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_1080p = { ++ .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, ++ .bFrameIndex = 6, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(1920), ++ .wHeight = cpu_to_le16(1080), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct uvc_format_mjpeg uvc_format_mjpg = { ++ .bLength = UVC_DT_FORMAT_MJPEG_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FORMAT_MJPEG, ++ .bFormatIndex = 2, ++ .bNumFrameDescriptors = 6, ++ .bmFlags = 0, ++ .bDefaultFrameIndex = 1, ++ .bAspectRatioX = 0, ++ .bAspectRatioY = 0, ++ .bmInterfaceFlags = 0, ++ .bCopyProtect = 0, ++}; ++ ++DECLARE_UVC_FRAME_MJPEG(1); ++DECLARE_UVC_FRAME_MJPEG(3); ++ ++static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_144p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_MJPEG, ++ .bFrameIndex = 1, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(176), ++ .wHeight = cpu_to_le16(144), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_240p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_MJPEG, ++ .bFrameIndex = 2, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(320), ++ .wHeight = cpu_to_le16(240), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_288p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_MJPEG, ++ .bFrameIndex = 3, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(352), ++ .wHeight = cpu_to_le16(288), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++ ++static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_480p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_MJPEG, ++ .bFrameIndex = 4, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(640), ++ .wHeight = cpu_to_le16(480), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(460800), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_MJPEG, ++ .bFrameIndex = 5, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(1280), ++ .wHeight = cpu_to_le16(720), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_1080p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_MJPEG, ++ .bFrameIndex = 6, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(1920), ++ .wHeight = cpu_to_le16(1080), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwMaxVideoFrameBufferSize = cpu_to_le32(1843200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct uvc_format_h264 uvc_format_h264 = { ++ .bLength = UVC_DT_FORMAT_H264_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FORMAT_H264, ++ .bFormatIndex = 3, ++ .bNumFrameDescriptors = 6, ++ .bmFlags = 0, ++ .bDefaultFrameIndex = 1, ++ .bAspectRatioX = 0, ++ .bAspectRatioY = 0, ++ .bmInterfaceFlags = 0, ++ .bCopyProtect = 0, ++}; ++ ++static const struct uvc_format_h264_base uvc_format_h264_base = { ++ .bLength = sizeof(uvc_format_h264_base), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FORMAT_FRAME_BASED, ++ .bFormatIndex = 3, ++ .bNumFrameDescriptors = 6, ++ .guidFormat = UVC_GUID_FORMAT_H264, ++ .bBitsPerPixel = 16, ++ .bDefaultFrameIndex = 1, ++ .bAspectRatioX = 0, ++ .bAspectRatioY = 0, ++ .bmInterfaceFlags = 0, ++ .bCopyProtect = 0, ++ .bVariableSize = 1, ++}; ++ ++DECLARE_UVC_FRAME_H264_BASE(1); ++DECLARE_UVC_FRAME_H264_BASE(3); ++ ++static const struct UVC_FRAME_H264_BASE(1) uvc_frame_h264_720p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_FRAME_BASED, ++ .bFrameIndex = 5, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(1280), ++ .wHeight = cpu_to_le16(720), ++ .dwMinBitRate = cpu_to_le32(55296000), ++ .dwMaxBitRate = cpu_to_le32(55296000), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwBytesPerLine = 0, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_H264_BASE(1) uvc_frame_h264_1080p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_FRAME_BASED, ++ .bFrameIndex = 6, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(1920), ++ .wHeight = cpu_to_le16(1080), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwBytesPerLine = 0, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_H264_BASE(1) uvc_frame_h264_480p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_FRAME_BASED, ++ .bFrameIndex = 4, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(640), ++ .wHeight = cpu_to_le16(480), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwBytesPerLine = 0, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_H264_BASE(1) uvc_frame_h264_144p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_FRAME_BASED, ++ .bFrameIndex = 1, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(176), ++ .wHeight = cpu_to_le16(144), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwBytesPerLine = 0, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_H264_BASE(1) uvc_frame_h264_240p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_FRAME_BASED, ++ .bFrameIndex = 2, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(320), ++ .wHeight = cpu_to_le16(240), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwBytesPerLine = 0, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct UVC_FRAME_H264_BASE(1) uvc_frame_h264_288p = { ++ .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_FRAME_FRAME_BASED, ++ .bFrameIndex = 3, ++ .bmCapabilities = 0, ++ .wWidth = cpu_to_le16(352), ++ .wHeight = cpu_to_le16(288), ++ .dwMinBitRate = cpu_to_le32(29491200), ++ .dwMaxBitRate = cpu_to_le32(29491200), ++ .dwDefaultFrameInterval = cpu_to_le32(333333), ++ .bFrameIntervalType = 1, ++ .dwBytesPerLine = 0, ++ .dwFrameInterval[0] = cpu_to_le32(333333), ++}; ++ ++static const struct uvc_color_matching_descriptor uvc_color_matching = { ++ .bLength = UVC_DT_COLOR_MATCHING_SIZE, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = UVC_VS_COLORFORMAT, ++ .bColorPrimaries = 1, ++ .bTransferCharacteristics = 1, ++ .bMatrixCoefficients = 4, ++}; ++ ++static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = { ++ (const struct uvc_descriptor_header *) &uvc_control_header, ++ (const struct uvc_descriptor_header *) &uvc_camera_terminal, ++ (const struct uvc_descriptor_header *) &uvc_processing, ++ (const struct uvc_descriptor_header *) &uvc_xu_h264_desc, ++ (const struct uvc_descriptor_header *) &uvc_xu_hicamera_desc, ++ (const struct uvc_descriptor_header *) &uvc_output_terminal, ++ NULL, ++}; ++ ++static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = { ++ (const struct uvc_descriptor_header *) &uvc_control_header, ++ (const struct uvc_descriptor_header *) &uvc_camera_terminal, ++ (const struct uvc_descriptor_header *) &uvc_processing, ++ (const struct uvc_descriptor_header *) &uvc_xu_h264_desc, ++ (const struct uvc_descriptor_header *) &uvc_xu_hicamera_desc, ++ (const struct uvc_descriptor_header *) &uvc_output_terminal, ++ NULL, ++}; ++ ++static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = { ++ (const struct uvc_descriptor_header *) &uvc_input_header, ++ (const struct uvc_descriptor_header *) &uvc_format_yuv, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_288p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_480p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, ++ (const struct uvc_descriptor_header *) &uvc_format_mjpg, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_288p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_480p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_1080p, ++ (const struct uvc_descriptor_header *) &uvc_format_h264_base, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_720p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_1080p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_480p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_288p, ++ (const struct uvc_descriptor_header *) &uvc_color_matching, ++ NULL, ++}; ++ ++static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = { ++ (const struct uvc_descriptor_header *) &uvc_input_header, ++ (const struct uvc_descriptor_header *) &uvc_format_yuv, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_288p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_480p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, ++ (const struct uvc_descriptor_header *) &uvc_format_mjpg, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_288p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_480p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_1080p, ++ (const struct uvc_descriptor_header *) &uvc_format_h264_base, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_720p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_1080p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_480p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_288p, ++ (const struct uvc_descriptor_header *) &uvc_color_matching, ++ NULL, ++}; ++ ++static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = { ++ (const struct uvc_descriptor_header *) &uvc_input_header, ++ (const struct uvc_descriptor_header *) &uvc_format_yuv, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_288p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_480p, ++ (const struct uvc_descriptor_header *) &uvc_frame_yuv_720p, ++ (const struct uvc_descriptor_header *) &uvc_format_mjpg, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_288p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_480p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p, ++ (const struct uvc_descriptor_header *) &uvc_frame_mjpg_1080p, ++ (const struct uvc_descriptor_header *) &uvc_format_h264_base, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_720p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_1080p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_480p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_144p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_240p, ++ (const struct uvc_descriptor_header *) &uvc_frame_h264_288p, ++ (const struct uvc_descriptor_header *) &uvc_color_matching, ++ NULL, ++}; ++ ++/* -------------------------------------------------------------------------- ++ * USB configuration ++ */ ++static int __init ++webcam_config_bind(struct usb_configuration *c) ++{ ++ int status = 0; ++ ++ f_uvc = usb_get_function(fi_uvc); ++ if (IS_ERR(f_uvc)) ++ return PTR_ERR(f_uvc); ++ ++ status = usb_add_function(c, f_uvc); ++ if (status < 0) ++ usb_put_function(f_uvc); ++ ++ /* FIXME: add audio device*/ ++ audio_do_config(c); ++ ++ return status; ++} ++ ++static struct usb_configuration webcam_config_driver = { ++ .label = webcam_config_label, ++ .bConfigurationValue = 1, ++ .iConfiguration = 0, /* dynamic */ ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++ .MaxPower = CONFIG_USB_GADGET_VBUS_DRAW, ++}; ++ ++static int /* __init_or_exit */ ++webcam_unbind(struct usb_composite_dev *cdev) ++{ ++ /* FIXME: add audio device*/ ++ audio_unbind(cdev); ++ ++ if (!IS_ERR_OR_NULL(f_uvc)) ++ usb_put_function(f_uvc); ++ if (!IS_ERR_OR_NULL(fi_uvc)) ++ usb_put_function_instance(fi_uvc); ++ return 0; ++} ++ ++static int __init ++webcam_bind(struct usb_composite_dev *cdev) ++{ ++ struct f_uvc_opts *uvc_opts; ++ int ret; ++ ++ /* FIXME: add audio device*/ ++ audio_bind(cdev); ++ ++ fi_uvc = usb_get_function_instance("uvc"); ++ if (IS_ERR(fi_uvc)) ++ return PTR_ERR(fi_uvc); ++ ++ uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst); ++ ++ uvc_opts->streaming_interval = streaming_interval; ++ uvc_opts->streaming_maxpacket = streaming_maxpacket; ++ uvc_opts->streaming_maxburst = streaming_maxburst; ++ uvc_set_trace_param(trace); ++ ++ uvc_opts->fs_control = uvc_fs_control_cls; ++ uvc_opts->ss_control = uvc_ss_control_cls; ++ uvc_opts->fs_streaming = uvc_fs_streaming_cls; ++ uvc_opts->hs_streaming = uvc_hs_streaming_cls; ++ uvc_opts->ss_streaming = uvc_ss_streaming_cls; ++ ++ /* Allocate string descriptor numbers ... note that string contents ++ * can be overridden by the composite_dev glue. ++ */ ++ ret = usb_string_ids_tab(cdev, webcam_strings); ++ if (ret < 0) ++ goto error; ++ webcam_device_descriptor.iManufacturer = ++ webcam_strings[USB_GADGET_MANUFACTURER_IDX].id; ++ webcam_device_descriptor.iProduct = ++ webcam_strings[USB_GADGET_PRODUCT_IDX].id; ++ webcam_config_driver.iConfiguration = ++ webcam_strings[STRING_DESCRIPTION_IDX].id; ++ ++ /* Register our configuration. */ ++ if ((ret = usb_add_config(cdev, &webcam_config_driver, ++ webcam_config_bind)) < 0) ++ goto error; ++ ++ usb_composite_overwrite_options(cdev, &coverwrite); ++ INFO(cdev, "Webcam Video Gadget\n"); ++ return 0; ++ ++error: ++ usb_put_function_instance(fi_uvc); ++ return ret; ++} ++ ++/* -------------------------------------------------------------------------- ++ * Driver ++ */ ++ ++static __refdata struct usb_composite_driver webcam_driver = { ++ .name = "g_webcam", ++ .dev = &webcam_device_descriptor, ++ .strings = webcam_device_strings, ++ .max_speed = USB_SPEED_SUPER, ++ .bind = webcam_bind, ++ .unbind = webcam_unbind, ++}; ++ ++module_usb_composite_driver(webcam_driver); ++ ++MODULE_AUTHOR("Laurent Pinchart"); ++MODULE_DESCRIPTION("Webcam Video Gadget"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.1.0"); +diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig +index 217365d..ed58012 100644 +--- a/drivers/usb/gadget/udc/Kconfig ++++ b/drivers/usb/gadget/udc/Kconfig +@@ -28,7 +28,7 @@ menu "USB Peripheral Controller" + # + # Integrated controllers + # +- ++#source "drivers/usb/gadget/udc/hiudc3/Kconfig" + config USB_AT91 + tristate "Atmel AT91 USB Device Port" + depends on ARCH_AT91 +@@ -219,6 +219,67 @@ config USB_MV_UDC + USB2.0 OTG controller, which can be configured as high speed or + full speed USB peripheral. + ++menuconfig HIUSB_DEVICE2_0 ++ bool "Hisilicon USB2.0 Device Controller SUPPORT" ++ help ++ This selects the usb(ehci/ohci) family usb device. ++ Say Y to enable hisi usb2.0 controller driver. ++ IF you do not use usb2.0 device in your board, ++ say N to get a smaller uImage. Mostly you need it. ++ ++if HIUSB_DEVICE2_0 ++ config USB_HISI_UDC ++ tristate "hisilicon highspeed device controller version 3.00a driver" ++ help ++ You can select device mode by the option. ++ Enable hisi ehci controller driver. ++ Say Y to enable hisi usb2.0 ehci controller driver. ++ IF you do not use usb2.0 ehci device in your board, say N to get a ++ smaller uImage. Mostly you need it. ++ ++config USB_AUTO_SWITCH ++ bool "Hisilicon USB2.0 Device auto switch" ++ depends on HAS_DMA ++ help ++ Hisilicon Socs include a high speed. The select can auto switch or ++ not. Default is auto switch. If you want to be auto switch ++ host/device, you can set 1 the 1st bit of 0x12020150. ++endif # HI_HS_DEVICE ++ ++menuconfig HIUSB_DEVICE3_0 ++ bool "Hisilicon USB3.0 Device Controller SUPPORT" ++ help ++ This selects the usb(ehci/ohci) family usb device. ++ Say Y to enable hisi usb3.0 controller driver. ++ IF you do not use usb3.0 device in your board, ++ say N to get a smaller uImage. Mostly you need it. ++ ++if HIUSB_DEVICE3_0 ++ config HIUSB_SS_DEVICE ++ boolean "hisilicon susperspeed device controller version 2.50a driver" ++ help ++ You can select device mode by the option. ++ Enable hisi ehci controller driver. ++ Say Y to enable hisi usb3.0 xhci controller driver. ++ IF you do not use usb3.0 xhci device in your board, say N to get a ++ smaller uImage. Mostly you need it. ++ ++config USB_HISI_UDC3 ++ tristate "Hisilicon USB3.0 Device Controller" ++ depends on HAS_DMA ++ help ++ Hisilicon Socs include a high speed ++ USB3.0 Device controller, which can be configured as susperspeed ++ USB peripheral. ++ ++config USB3_DEVICE_GPIO_CTRL ++ tristate "Hisilicon USB3.0 Device Support GPIO CTRL" ++ help ++ USB3.0 Device mode, it support device and host switch. When you ++ pull out the device, it can bring gpio interrupt and notify the sw ++ handle. The sw set the host mode and then set the device mode. ++endif # HI_SS_DEVICE ++ + config USB_MV_U3D + depends on HAS_DMA + tristate "MARVELL PXA2128 USB 3.0 controller" +diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile +index a7f4491..564a046 100644 +--- a/drivers/usb/gadget/udc/Makefile ++++ b/drivers/usb/gadget/udc/Makefile +@@ -1,7 +1,11 @@ + # + # USB peripheral controller drivers + # ++ifndef CONFIG_USB_HISI_UDC ++ifndef CONFIG_USB_HISI_UDC3 + obj-$(CONFIG_USB_GADGET) += udc-core.o ++endif ++endif + obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o + obj-$(CONFIG_USB_NET2272) += net2272.o + obj-$(CONFIG_USB_NET2280) += net2280.o +@@ -30,3 +34,6 @@ obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o + obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o + obj-$(CONFIG_USB_GR_UDC) += gr_udc.o + obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o ++obj-$(CONFIG_USB_HISI_UDC) += hiudc/ ++#obj-$(CONFIG_USB_HISI_UDC) += hiudc_bak/ ++obj-$(CONFIG_USB_HISI_UDC3) += hiudc3/ +diff --git a/drivers/usb/gadget/udc/hiudc/Makefile b/drivers/usb/gadget/udc/hiudc/Makefile +new file mode 100644 +index 0000000..28dd23c +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/Makefile +@@ -0,0 +1,21 @@ ++# ++# USB peripheral controller drivers ++# ++# Use the BUS_INTERFACE variable to compile the software for either ++# PCI(PCI_INTERFACE) or LM(LM_INTERFACE) bus. ++# Use one of the following flags to compile the software in host-only or ++# device-only mode. ++EXTRA_CFLAGS += -DDWC_DEVICE_ONLY ++EXTRA_CFLAGS += -DDWC_LINUX ++EXTRA_CFLAGS += -DLM_INTERFACE ++ ++obj-$(CONFIG_USB_HISI_UDC) += udc-hisi.o ++#obj-y += udc-hisi.o ++udc-hisi-objs := dwc_otg_driver.o dwc_otg_attr.o ++udc-hisi-objs += dwc_otg_cil.o dwc_otg_cil_intr.o ++udc-hisi-objs += dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o ++udc-hisi-objs += dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o ++udc-hisi-objs += dwc_otg_adp.o ++udc-hisi-objs += dwc_cc.o dwc_modpow.o dwc_dh.o \ ++ dwc_crypto.o dwc_notifier.o \ ++ dwc_common_linux.o dwc_mem.o +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_cc.c b/drivers/usb/gadget/udc/hiudc/dwc_cc.c +new file mode 100644 +index 0000000..a757f4f +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_cc.c +@@ -0,0 +1,532 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_cc.c $ ++ * $Revision: #4 $ ++ * $Date: 2010/11/04 $ ++ * $Change: 1621692 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++#ifdef DWC_CCLIB ++ ++#include "dwc_cc.h" ++ ++typedef struct dwc_cc ++{ ++ uint32_t uid; ++ uint8_t chid[16]; ++ uint8_t cdid[16]; ++ uint8_t ck[16]; ++ uint8_t *name; ++ uint8_t length; ++ DWC_CIRCLEQ_ENTRY(dwc_cc) list_entry; ++} dwc_cc_t; ++ ++DWC_CIRCLEQ_HEAD(context_list, dwc_cc); ++ ++/** The main structure for CC management. */ ++struct dwc_cc_if ++{ ++ dwc_mutex_t *mutex; ++ char *filename; ++ ++ unsigned is_host:1; ++ ++ dwc_notifier_t *notifier; ++ ++ struct context_list list; ++}; ++ ++#ifdef DEBUG ++static inline void dump_bytes(char *name, uint8_t *bytes, int len) ++{ ++ int i; ++ DWC_PRINTF("%s: ", name); ++ for (i=0; i<len; i++) { ++ DWC_PRINTF("%02x ", bytes[i]); ++ } ++ DWC_PRINTF("\n"); ++} ++#else ++#define dump_bytes(x...) ++#endif ++ ++static dwc_cc_t *alloc_cc(void *mem_ctx, uint8_t *name, uint32_t length) ++{ ++ dwc_cc_t *cc = dwc_alloc(mem_ctx, sizeof(dwc_cc_t)); ++ if (!cc) { ++ return NULL; ++ } ++ DWC_MEMSET(cc, 0, sizeof(dwc_cc_t)); ++ ++ if (name) { ++ cc->length = length; ++ cc->name = dwc_alloc(mem_ctx, length); ++ if (!cc->name) { ++ dwc_free(mem_ctx, cc); ++ return NULL; ++ } ++ ++ DWC_MEMCPY(cc->name, name, length); ++ } ++ ++ return cc; ++} ++ ++static void free_cc(void *mem_ctx, dwc_cc_t *cc) ++{ ++ if (cc->name) { ++ dwc_free(mem_ctx, cc->name); ++ } ++ dwc_free(mem_ctx, cc); ++} ++ ++static uint32_t next_uid(dwc_cc_if_t *cc_if) ++{ ++ uint32_t uid = 0; ++ dwc_cc_t *cc; ++ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) { ++ if (cc->uid > uid) { ++ uid = cc->uid; ++ } ++ } ++ ++ if (uid == 0) { ++ uid = 255; ++ } ++ ++ return uid + 1; ++} ++ ++static dwc_cc_t *cc_find(dwc_cc_if_t *cc_if, uint32_t uid) ++{ ++ dwc_cc_t *cc; ++ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) { ++ if (cc->uid == uid) { ++ return cc; ++ } ++ } ++ return NULL; ++} ++ ++static unsigned int cc_data_size(dwc_cc_if_t *cc_if) ++{ ++ unsigned int size = 0; ++ dwc_cc_t *cc; ++ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) { ++ size += (48 + 1); ++ if (cc->name) { ++ size += cc->length; ++ } ++ } ++ return size; ++} ++ ++static uint32_t cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid) ++{ ++ uint32_t uid = 0; ++ dwc_cc_t *cc; ++ ++ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) { ++ if (DWC_MEMCMP(cc->chid, chid, 16) == 0) { ++ uid = cc->uid; ++ break; ++ } ++ } ++ return uid; ++} ++static uint32_t cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid) ++{ ++ uint32_t uid = 0; ++ dwc_cc_t *cc; ++ ++ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) { ++ if (DWC_MEMCMP(cc->cdid, cdid, 16) == 0) { ++ uid = cc->uid; ++ break; ++ } ++ } ++ return uid; ++} ++ ++/* Internal cc_add */ ++static int32_t cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid, ++ uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length) ++{ ++ dwc_cc_t *cc; ++ uint32_t uid; ++ ++ if (cc_if->is_host) { ++ uid = cc_match_cdid(cc_if, cdid); ++ } ++ else { ++ uid = cc_match_chid(cc_if, chid); ++ } ++ ++ if (uid) { ++ DWC_DEBUG("Replacing previous connection context id=%d name=%p name_len=%d", uid, name, length); ++ cc = cc_find(cc_if, uid); ++ } ++ else { ++ cc = alloc_cc(mem_ctx, name, length); ++ cc->uid = next_uid(cc_if); ++ DWC_CIRCLEQ_INSERT_TAIL(&cc_if->list, cc, list_entry); ++ } ++ ++ DWC_MEMCPY(&(cc->chid[0]), chid, 16); ++ DWC_MEMCPY(&(cc->cdid[0]), cdid, 16); ++ DWC_MEMCPY(&(cc->ck[0]), ck, 16); ++ ++ DWC_DEBUG("Added connection context id=%d name=%p name_len=%d", cc->uid, name, length); ++ dump_bytes("CHID", cc->chid, 16); ++ dump_bytes("CDID", cc->cdid, 16); ++ dump_bytes("CK", cc->ck, 16); ++ return cc->uid; ++} ++ ++/* Internal cc_clear */ ++static void cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if) ++{ ++ while (!DWC_CIRCLEQ_EMPTY(&cc_if->list)) { ++ dwc_cc_t *cc = DWC_CIRCLEQ_FIRST(&cc_if->list); ++ DWC_CIRCLEQ_REMOVE_INIT(&cc_if->list, cc, list_entry); ++ free_cc(mem_ctx, cc); ++ } ++} ++ ++dwc_cc_if_t *dwc_cc_if_alloc(void *mem_ctx, void *mtx_ctx, ++ dwc_notifier_t *notifier, unsigned is_host) ++{ ++ dwc_cc_if_t *cc_if = NULL; ++ ++ /* Allocate a common_cc_if structure */ ++ cc_if = dwc_alloc(mem_ctx, sizeof(dwc_cc_if_t)); ++ ++ if (!cc_if) ++ return NULL; ++ ++#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)) ++ DWC_MUTEX_ALLOC_LINUX_DEBUG(cc_if->mutex); ++#else ++ cc_if->mutex = dwc_mutex_alloc(mtx_ctx); ++#endif ++ if (!cc_if->mutex) { ++ dwc_free(mem_ctx, cc_if); ++ return NULL; ++ } ++ ++ DWC_CIRCLEQ_INIT(&cc_if->list); ++ cc_if->is_host = is_host; ++ cc_if->notifier = notifier; ++ return cc_if; ++} ++ ++void dwc_cc_if_free(void *mem_ctx, void *mtx_ctx, dwc_cc_if_t *cc_if) ++{ ++#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)) ++ DWC_MUTEX_FREE(cc_if->mutex); ++#else ++ dwc_mutex_free(mtx_ctx, cc_if->mutex); ++#endif ++ cc_clear(mem_ctx, cc_if); ++ dwc_free(mem_ctx, cc_if); ++} ++ ++static void cc_changed(dwc_cc_if_t *cc_if) ++{ ++ if (cc_if->notifier) { ++ dwc_notify(cc_if->notifier, DWC_CC_LIST_CHANGED_NOTIFICATION, cc_if); ++ } ++} ++ ++void dwc_cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if) ++{ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc_clear(mem_ctx, cc_if); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ cc_changed(cc_if); ++} ++ ++int32_t dwc_cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid, ++ uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length) ++{ ++ uint32_t uid; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ uid = cc_add(mem_ctx, cc_if, chid, cdid, ck, name, length); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ cc_changed(cc_if); ++ ++ return uid; ++} ++ ++void dwc_cc_change(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id, uint8_t *chid, ++ uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length) ++{ ++ dwc_cc_t* cc; ++ ++ DWC_DEBUG("Change connection context %d", id); ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc = cc_find(cc_if, id); ++ if (!cc) { ++ DWC_ERROR("Uid %d not found in cc list\n", id); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return; ++ } ++ ++ if (chid) { ++ DWC_MEMCPY(&(cc->chid[0]), chid, 16); ++ } ++ if (cdid) { ++ DWC_MEMCPY(&(cc->cdid[0]), cdid, 16); ++ } ++ if (ck) { ++ DWC_MEMCPY(&(cc->ck[0]), ck, 16); ++ } ++ ++ if (name) { ++ if (cc->name) { ++ dwc_free(mem_ctx, cc->name); ++ } ++ cc->name = dwc_alloc(mem_ctx, length); ++ if (!cc->name) { ++ DWC_ERROR("Out of memory in dwc_cc_change()\n"); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return; ++ } ++ cc->length = length; ++ DWC_MEMCPY(cc->name, name, length); ++ } ++ ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ cc_changed(cc_if); ++ ++ DWC_DEBUG("Changed connection context id=%d\n", id); ++ dump_bytes("New CHID", cc->chid, 16); ++ dump_bytes("New CDID", cc->cdid, 16); ++ dump_bytes("New CK", cc->ck, 16); ++} ++ ++void dwc_cc_remove(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id) ++{ ++ dwc_cc_t *cc; ++ ++ DWC_DEBUG("Removing connection context %d", id); ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc = cc_find(cc_if, id); ++ if (!cc) { ++ DWC_ERROR("Uid %d not found in cc list\n", id); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return; ++ } ++ ++ DWC_CIRCLEQ_REMOVE_INIT(&cc_if->list, cc, list_entry); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ free_cc(mem_ctx, cc); ++ ++ cc_changed(cc_if); ++} ++ ++uint8_t *dwc_cc_data_for_save(void *mem_ctx, dwc_cc_if_t *cc_if, unsigned int *length) ++{ ++ uint8_t *buf, *x; ++ uint8_t zero = 0; ++ dwc_cc_t *cc; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ *length = cc_data_size(cc_if); ++ if (!(*length)) { ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return NULL; ++ } ++ ++ DWC_DEBUG("Creating data for saving (length=%d)", *length); ++ ++ buf = dwc_alloc(mem_ctx, *length); ++ if (!buf) { ++ *length = 0; ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return NULL; ++ } ++ ++ x = buf; ++ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) { ++ DWC_MEMCPY(x, cc->chid, 16); ++ x += 16; ++ DWC_MEMCPY(x, cc->cdid, 16); ++ x += 16; ++ DWC_MEMCPY(x, cc->ck, 16); ++ x += 16; ++ if (cc->name) { ++ DWC_MEMCPY(x, &cc->length, 1); ++ x += 1; ++ DWC_MEMCPY(x, cc->name, cc->length); ++ x += cc->length; ++ } ++ else { ++ DWC_MEMCPY(x, &zero, 1); ++ x += 1; ++ } ++ } ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ return buf; ++} ++ ++void dwc_cc_restore_from_data(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *data, uint32_t length) ++{ ++ uint8_t name_length; ++ uint8_t *name; ++ uint8_t *chid; ++ uint8_t *cdid; ++ uint8_t *ck; ++ uint32_t i = 0; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc_clear(mem_ctx, cc_if); ++ ++ while (i < length) { ++ chid = &data[i]; ++ i += 16; ++ cdid = &data[i]; ++ i += 16; ++ ck = &data[i]; ++ i += 16; ++ ++ name_length = data[i]; ++ i ++; ++ ++ if (name_length) { ++ name = &data[i]; ++ i += name_length; ++ } ++ else { ++ name = NULL; ++ } ++ ++ /* check to see if we haven't overflown the buffer */ ++ if (i > length) { ++ DWC_ERROR("Data format error while attempting to load CCs " ++ "(nlen=%d, iter=%d, buflen=%d).\n", name_length, i, length); ++ break; ++ } ++ ++ cc_add(mem_ctx, cc_if, chid, cdid, ck, name, name_length); ++ } ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ cc_changed(cc_if); ++} ++ ++uint32_t dwc_cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid) ++{ ++ uint32_t uid = 0; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ uid = cc_match_chid(cc_if, chid); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return uid; ++} ++uint32_t dwc_cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid) ++{ ++ uint32_t uid = 0; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ uid = cc_match_cdid(cc_if, cdid); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return uid; ++} ++ ++uint8_t *dwc_cc_ck(dwc_cc_if_t *cc_if, int32_t id) ++{ ++ uint8_t *ck = NULL; ++ dwc_cc_t *cc; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc = cc_find(cc_if, id); ++ if (cc) { ++ ck = cc->ck; ++ } ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ return ck; ++ ++} ++ ++uint8_t *dwc_cc_chid(dwc_cc_if_t *cc_if, int32_t id) ++{ ++ uint8_t *retval = NULL; ++ dwc_cc_t *cc; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc = cc_find(cc_if, id); ++ if (cc) { ++ retval = cc->chid; ++ } ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ return retval; ++} ++ ++uint8_t *dwc_cc_cdid(dwc_cc_if_t *cc_if, int32_t id) ++{ ++ uint8_t *retval = NULL; ++ dwc_cc_t *cc; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc = cc_find(cc_if, id); ++ if (cc) { ++ retval = cc->cdid; ++ } ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ return retval; ++} ++ ++uint8_t *dwc_cc_name(dwc_cc_if_t *cc_if, int32_t id, uint8_t *length) ++{ ++ uint8_t *retval = NULL; ++ dwc_cc_t *cc; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ *length = 0; ++ cc = cc_find(cc_if, id); ++ if (cc) { ++ *length = cc->length; ++ retval = cc->name; ++ } ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ return retval; ++} ++ ++#endif /* DWC_CCLIB */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_cc.h b/drivers/usb/gadget/udc/hiudc/dwc_cc.h +new file mode 100644 +index 0000000..f86e6f2 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_cc.h +@@ -0,0 +1,224 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_cc.h $ ++ * $Revision: #4 $ ++ * $Date: 2010/09/28 $ ++ * $Change: 1596182 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++#ifndef _DWC_CC_H_ ++#define _DWC_CC_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** @file ++ * ++ * This file defines the Context Context library. ++ * ++ * The main data structure is dwc_cc_if_t which is returned by either the ++ * dwc_cc_if_alloc function or returned by the module to the user via a provided ++ * function. The data structure is opaque and should only be manipulated via the ++ * functions provied in this API. ++ * ++ * It manages a list of connection contexts and operations can be performed to ++ * add, remove, query, search, and change, those contexts. Additionally, ++ * a dwc_notifier_t object can be requested from the manager so that ++ * the user can be notified whenever the context list has changed. ++ */ ++ ++#include "dwc_os.h" ++#include "dwc_list.h" ++#include "dwc_notifier.h" ++ ++ ++/* Notifications */ ++#define DWC_CC_LIST_CHANGED_NOTIFICATION "DWC_CC_LIST_CHANGED_NOTIFICATION" ++ ++struct dwc_cc_if; ++typedef struct dwc_cc_if dwc_cc_if_t; ++ ++ ++/** @name Connection Context Operations */ ++/** @{ */ ++ ++/** This function allocates memory for a dwc_cc_if_t structure, initializes ++ * fields to default values, and returns a pointer to the structure or NULL on ++ * error. */ ++extern dwc_cc_if_t *dwc_cc_if_alloc(void *mem_ctx, void *mtx_ctx, ++ dwc_notifier_t *notifier, unsigned is_host); ++ ++/** Frees the memory for the specified CC structure allocated from ++ * dwc_cc_if_alloc(). */ ++extern void dwc_cc_if_free(void *mem_ctx, void *mtx_ctx, dwc_cc_if_t *cc_if); ++ ++/** Removes all contexts from the connection context list */ ++extern void dwc_cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if); ++ ++/** Adds a connection context (CHID, CK, CDID, Name) to the connection context list. ++ * If a CHID already exists, the CK and name are overwritten. Statistics are ++ * not overwritten. ++ * ++ * @param cc_if The cc_if structure. ++ * @param chid A pointer to the 16-byte CHID. This value will be copied. ++ * @param ck A pointer to the 16-byte CK. This value will be copied. ++ * @param cdid A pointer to the 16-byte CDID. This value will be copied. ++ * @param name An optional host friendly name as defined in the association model ++ * spec. Must be a UTF16-LE unicode string. Can be NULL to indicated no name. ++ * @param length The length othe unicode string. ++ * @return A unique identifier used to refer to this context that is valid for ++ * as long as this context is still in the list. */ ++extern int32_t dwc_cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid, ++ uint8_t *cdid, uint8_t *ck, uint8_t *name, ++ uint8_t length); ++ ++/** Changes the CHID, CK, CDID, or Name values of a connection context in the ++ * list, preserving any accumulated statistics. This would typically be called ++ * if the host decideds to change the context with a SET_CONNECTION request. ++ * ++ * @param cc_if The cc_if structure. ++ * @param id The identifier of the connection context. ++ * @param chid A pointer to the 16-byte CHID. This value will be copied. NULL ++ * indicates no change. ++ * @param cdid A pointer to the 16-byte CDID. This value will be copied. NULL ++ * indicates no change. ++ * @param ck A pointer to the 16-byte CK. This value will be copied. NULL ++ * indicates no change. ++ * @param name Host friendly name UTF16-LE. NULL indicates no change. ++ * @param length Length of name. */ ++extern void dwc_cc_change(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id, ++ uint8_t *chid, uint8_t *cdid, uint8_t *ck, ++ uint8_t *name, uint8_t length); ++ ++/** Remove the specified connection context. ++ * @param cc_if The cc_if structure. ++ * @param id The identifier of the connection context to remove. */ ++extern void dwc_cc_remove(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id); ++ ++/** Get a binary block of data for the connection context list and attributes. ++ * This data can be used by the OS specific driver to save the connection ++ * context list into non-volatile memory. ++ * ++ * @param cc_if The cc_if structure. ++ * @param length Return the length of the data buffer. ++ * @return A pointer to the data buffer. The memory for this buffer should be ++ * freed with DWC_FREE() after use. */ ++extern uint8_t *dwc_cc_data_for_save(void *mem_ctx, dwc_cc_if_t *cc_if, ++ unsigned int *length); ++ ++/** Restore the connection context list from the binary data that was previously ++ * returned from a call to dwc_cc_data_for_save. This can be used by the OS specific ++ * driver to load a connection context list from non-volatile memory. ++ * ++ * @param cc_if The cc_if structure. ++ * @param data The data bytes as returned from dwc_cc_data_for_save. ++ * @param length The length of the data. */ ++extern void dwc_cc_restore_from_data(void *mem_ctx, dwc_cc_if_t *cc_if, ++ uint8_t *data, unsigned int length); ++ ++/** Find the connection context from the specified CHID. ++ * ++ * @param cc_if The cc_if structure. ++ * @param chid A pointer to the CHID data. ++ * @return A non-zero identifier of the connection context if the CHID matches. ++ * Otherwise returns 0. */ ++extern uint32_t dwc_cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid); ++ ++/** Find the connection context from the specified CDID. ++ * ++ * @param cc_if The cc_if structure. ++ * @param cdid A pointer to the CDID data. ++ * @return A non-zero identifier of the connection context if the CHID matches. ++ * Otherwise returns 0. */ ++extern uint32_t dwc_cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid); ++ ++/** Retrieve the CK from the specified connection context. ++ * ++ * @param cc_if The cc_if structure. ++ * @param id The identifier of the connection context. ++ * @return A pointer to the CK data. The memory does not need to be freed. */ ++extern uint8_t *dwc_cc_ck(dwc_cc_if_t *cc_if, int32_t id); ++ ++/** Retrieve the CHID from the specified connection context. ++ * ++ * @param cc_if The cc_if structure. ++ * @param id The identifier of the connection context. ++ * @return A pointer to the CHID data. The memory does not need to be freed. */ ++extern uint8_t *dwc_cc_chid(dwc_cc_if_t *cc_if, int32_t id); ++ ++/** Retrieve the CDID from the specified connection context. ++ * ++ * @param cc_if The cc_if structure. ++ * @param id The identifier of the connection context. ++ * @return A pointer to the CDID data. The memory does not need to be freed. */ ++extern uint8_t *dwc_cc_cdid(dwc_cc_if_t *cc_if, int32_t id); ++ ++extern uint8_t *dwc_cc_name(dwc_cc_if_t *cc_if, int32_t id, uint8_t *length); ++ ++/** Checks a buffer for non-zero. ++ * @param id A pointer to a 16 byte buffer. ++ * @return true if the 16 byte value is non-zero. */ ++static inline unsigned dwc_assoc_is_not_zero_id(uint8_t *id) { ++ int i; ++ for (i=0; i<16; i++) { ++ if (id[i]) return 1; ++ } ++ return 0; ++} ++ ++/** Checks a buffer for zero. ++ * @param id A pointer to a 16 byte buffer. ++ * @return true if the 16 byte value is zero. */ ++static inline unsigned dwc_assoc_is_zero_id(uint8_t *id) { ++ return !dwc_assoc_is_not_zero_id(id); ++} ++ ++/** Prints an ASCII representation for the 16-byte chid, cdid, or ck, into ++ * buffer. */ ++static inline int dwc_print_id_string(char *buffer, uint8_t *id) { ++ char *ptr = buffer; ++ int i; ++ for (i=0; i<16; i++) { ++ ptr += DWC_SPRINTF(ptr, "%02x", id[i]); ++ if (i < 15) { ++ ptr += DWC_SPRINTF(ptr, " "); ++ } ++ } ++ return ptr - buffer; ++} ++ ++/** @} */ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_CC_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_common_linux.c b/drivers/usb/gadget/udc/hiudc/dwc_common_linux.c +new file mode 100644 +index 0000000..1484a31 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_common_linux.c +@@ -0,0 +1,1425 @@ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/kthread.h> ++ ++#ifdef DWC_CCLIB ++# include "dwc_cc.h" ++#endif ++ ++#ifdef DWC_CRYPTOLIB ++# include "dwc_modpow.h" ++# include "dwc_dh.h" ++# include "dwc_crypto.h" ++#endif ++ ++#ifdef DWC_NOTIFYLIB ++# include "dwc_notifier.h" ++#endif ++ ++/* OS-Level Implementations */ ++ ++/* This is the Linux kernel implementation of the DWC platform library. */ ++#include <linux/moduleparam.h> ++#include <linux/ctype.h> ++#include <linux/crypto.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/dma-mapping.h> ++#include <linux/cdev.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/jiffies.h> ++#include <linux/list.h> ++#include <linux/pci.h> ++#include <linux/random.h> ++#include <linux/scatterlist.h> ++#include <linux/slab.h> ++#include <linux/stat.h> ++#include <linux/string.h> ++#include <linux/timer.h> ++#include <linux/usb.h> ++ ++#include <linux/version.h> ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) ++# include <linux/usb/gadget.h> ++#else ++# include <linux/usb_gadget.h> ++#endif ++ ++#include <asm/io.h> ++#include <asm/page.h> ++#include <asm/uaccess.h> ++#include <asm/unaligned.h> ++ ++#include "dwc_os.h" ++#include "dwc_list.h" ++ ++ ++/* MISC */ ++ ++void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size) ++{ ++ return memset(dest, byte, size); ++} ++ ++void *DWC_MEMCPY(void *dest, void const *src, uint32_t size) ++{ ++ return memcpy(dest, src, size); ++} ++ ++void *DWC_MEMMOVE(void *dest, void *src, uint32_t size) ++{ ++ return memmove(dest, src, size); ++} ++ ++int DWC_MEMCMP(void *m1, void *m2, uint32_t size) ++{ ++ return memcmp(m1, m2, size); ++} ++ ++int DWC_STRNCMP(void *s1, void *s2, uint32_t size) ++{ ++ return strncmp(s1, s2, size); ++} ++ ++int DWC_STRCMP(void *s1, void *s2) ++{ ++ return strcmp(s1, s2); ++} ++ ++int DWC_STRLEN(char const *str) ++{ ++ return strlen(str); ++} ++ ++char *DWC_STRCPY(char *to, char const *from) ++{ ++ return strcpy(to, from); ++} ++ ++char *DWC_STRDUP(char const *str) ++{ ++ int len = DWC_STRLEN(str) + 1; ++ char *new = DWC_ALLOC_ATOMIC(len); ++ ++ if (!new) { ++ return NULL; ++ } ++ ++ DWC_MEMCPY(new, str, len); ++ return new; ++} ++ ++int DWC_ATOI(const char *str, int32_t *value) ++{ ++ char *end = NULL; ++ ++ *value = simple_strtol(str, &end, 0); ++ if (*end == '\0') { ++ return 0; ++ } ++ ++ return -1; ++} ++ ++int DWC_ATOUI(const char *str, uint32_t *value) ++{ ++ char *end = NULL; ++ ++ *value = simple_strtoul(str, &end, 0); ++ if (*end == '\0') { ++ return 0; ++ } ++ ++ return -1; ++} ++ ++ ++#ifdef DWC_UTFLIB ++/* From usbstring.c */ ++ ++int DWC_UTF8_TO_UTF16LE(uint8_t const *s, uint16_t *cp, unsigned len) ++{ ++ int count = 0; ++ u8 c; ++ u16 uchar; ++ ++ /* this insists on correct encodings, though not minimal ones. ++ * BUT it currently rejects legit 4-byte UTF-8 code points, ++ * which need surrogate pairs. (Unicode 3.1 can use them.) ++ */ ++ while (len != 0 && (c = (u8) *s++) != 0) { ++ if (unlikely(c & 0x80)) { ++ // 2-byte sequence: ++ // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx ++ if ((c & 0xe0) == 0xc0) { ++ uchar = (c & 0x1f) << 6; ++ ++ c = (u8) *s++; ++ if ((c & 0xc0) != 0xc0) ++ goto fail; ++ c &= 0x3f; ++ uchar |= c; ++ ++ // 3-byte sequence (most CJKV characters): ++ // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx ++ } else if ((c & 0xf0) == 0xe0) { ++ uchar = (c & 0x0f) << 12; ++ ++ c = (u8) *s++; ++ if ((c & 0xc0) != 0xc0) ++ goto fail; ++ c &= 0x3f; ++ uchar |= c << 6; ++ ++ c = (u8) *s++; ++ if ((c & 0xc0) != 0xc0) ++ goto fail; ++ c &= 0x3f; ++ uchar |= c; ++ ++ /* no bogus surrogates */ ++ if (0xd800 <= uchar && uchar <= 0xdfff) ++ goto fail; ++ ++ // 4-byte sequence (surrogate pairs, currently rare): ++ // 11101110wwwwzzzzyy + 110111yyyyxxxxxx ++ // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx ++ // (uuuuu = wwww + 1) ++ // FIXME accept the surrogate code points (only) ++ } else ++ goto fail; ++ } else ++ uchar = c; ++ put_unaligned (cpu_to_le16 (uchar), cp++); ++ count++; ++ len--; ++ } ++ return count; ++fail: ++ return -1; ++} ++#endif /* DWC_UTFLIB */ ++ ++ ++/* dwc_debug.h */ ++ ++dwc_bool_t DWC_IN_IRQ(void) ++{ ++ return in_irq(); ++} ++ ++dwc_bool_t DWC_IN_BH(void) ++{ ++ return in_softirq(); ++} ++ ++void DWC_VPRINTF(char *format, va_list args) ++{ ++ vprintk(format, args); ++} ++ ++int DWC_VSNPRINTF(char *str, int size, char *format, va_list args) ++{ ++ return vsnprintf(str, size, format, args); ++} ++ ++void DWC_PRINTF(char *format, ...) ++{ ++ va_list args; ++ ++ va_start(args, format); ++ DWC_VPRINTF(format, args); ++ va_end(args); ++} ++ ++int DWC_SPRINTF(char *buffer, char *format, ...) ++{ ++ int retval; ++ va_list args; ++ ++ va_start(args, format); ++ retval = vsprintf(buffer, format, args); ++ va_end(args); ++ return retval; ++} ++ ++int DWC_SNPRINTF(char *buffer, int size, char *format, ...) ++{ ++ int retval; ++ va_list args; ++ ++ va_start(args, format); ++ retval = vsnprintf(buffer, size, format, args); ++ va_end(args); ++ return retval; ++} ++ ++void __DWC_WARN(char *format, ...) ++{ ++ va_list args; ++ ++ va_start(args, format); ++ DWC_PRINTF(KERN_WARNING); ++ DWC_VPRINTF(format, args); ++ va_end(args); ++} ++ ++void __DWC_ERROR(char *format, ...) ++{ ++ va_list args; ++ ++ va_start(args, format); ++ DWC_PRINTF(KERN_ERR); ++ DWC_VPRINTF(format, args); ++ va_end(args); ++} ++ ++void DWC_EXCEPTION(char *format, ...) ++{ ++ va_list args; ++ ++ va_start(args, format); ++ DWC_PRINTF(KERN_ERR); ++ DWC_VPRINTF(format, args); ++ va_end(args); ++ BUG_ON(1); ++} ++ ++#ifdef DEBUG ++void __DWC_DEBUG(char *format, ...) ++{ ++ va_list args; ++ ++ va_start(args, format); ++ DWC_PRINTF(KERN_DEBUG); ++ DWC_VPRINTF(format, args); ++ va_end(args); ++} ++#endif ++ ++ ++/* dwc_mem.h */ ++ ++#if 0 ++dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size, ++ uint32_t align, ++ uint32_t alloc) ++{ ++ struct dma_pool *pool = dma_pool_create("Pool", NULL, ++ size, align, alloc); ++ return (dwc_pool_t *)pool; ++} ++ ++void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool) ++{ ++ dma_pool_destroy((struct dma_pool *)pool); ++} ++ ++void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr) ++{ ++ return dma_pool_alloc((struct dma_pool *)pool, GFP_KERNEL, dma_addr); ++} ++ ++void *DWC_DMA_POOL_ZALLOC(dwc_pool_t *pool, uint64_t *dma_addr) ++{ ++ void *vaddr = DWC_DMA_POOL_ALLOC(pool, dma_addr); ++ memset(..); ++} ++ ++void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr) ++{ ++ dma_pool_free(pool, vaddr, daddr); ++} ++#endif ++ ++void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr) ++{ ++#ifdef xxCOSIM /* Only works for 32-bit cosim */ ++ void *buf = dma_alloc_coherent(dma_ctx, (size_t)size, dma_addr, GFP_KERNEL); ++#else ++// void *buf = dma_alloc_coherent(dma_ctx, (size_t)size, dma_addr, GFP_KERNEL | GFP_DMA32); ++ void *buf = dma_alloc_coherent(NULL, (size_t)size, dma_addr, GFP_ATOMIC); ++#endif ++ if (!buf) { ++ return NULL; ++ } ++ ++ memset(buf, 0, (size_t)size); ++ return buf; ++} ++ ++void *__DWC_DMA_ALLOC_ATOMIC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr) ++{ ++ void *buf = dma_alloc_coherent(NULL, (size_t)size, dma_addr, GFP_ATOMIC); ++ if (!buf) { ++ return NULL; ++ } ++ memset(buf, 0, (size_t)size); ++ return buf; ++} ++ ++void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr) ++{ ++ dma_free_coherent(dma_ctx, size, virt_addr, dma_addr); ++} ++ ++void *__DWC_ALLOC(void *mem_ctx, uint32_t size) ++{ ++ return kzalloc(size, GFP_KERNEL); ++} ++ ++void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size) ++{ ++ return kzalloc(size, GFP_ATOMIC); ++} ++ ++void __DWC_FREE(void *mem_ctx, void *addr) ++{ ++ kfree(addr); ++} ++ ++ ++#ifdef DWC_CRYPTOLIB ++/* dwc_crypto.h */ ++ ++void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length) ++{ ++ get_random_bytes(buffer, length); ++} ++ ++int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out) ++{ ++ struct crypto_blkcipher *tfm; ++ struct blkcipher_desc desc; ++ struct scatterlist sgd; ++ struct scatterlist sgs; ++ ++ tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); ++ if (tfm == NULL) { ++ printk("failed to load transform for aes CBC\n"); ++ return -1; ++ } ++ ++ crypto_blkcipher_setkey(tfm, key, keylen); ++ crypto_blkcipher_set_iv(tfm, iv, 16); ++ ++ sg_init_one(&sgd, out, messagelen); ++ sg_init_one(&sgs, message, messagelen); ++ ++ desc.tfm = tfm; ++ desc.flags = 0; ++ ++ if (crypto_blkcipher_encrypt(&desc, &sgd, &sgs, messagelen)) { ++ crypto_free_blkcipher(tfm); ++ DWC_ERROR("AES CBC encryption failed"); ++ return -1; ++ } ++ ++ crypto_free_blkcipher(tfm); ++ return 0; ++} ++ ++int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out) ++{ ++ struct crypto_hash *tfm; ++ struct hash_desc desc; ++ struct scatterlist sg; ++ ++ tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC); ++ if (IS_ERR(tfm)) { ++ DWC_ERROR("Failed to load transform for sha256: %ld\n", PTR_ERR(tfm)); ++ return 0; ++ } ++ desc.tfm = tfm; ++ desc.flags = 0; ++ ++ sg_init_one(&sg, message, len); ++ crypto_hash_digest(&desc, &sg, len, out); ++ crypto_free_hash(tfm); ++ ++ return 1; ++} ++ ++int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen, ++ uint8_t *key, uint32_t keylen, uint8_t *out) ++{ ++ struct crypto_hash *tfm; ++ struct hash_desc desc; ++ struct scatterlist sg; ++ ++ tfm = crypto_alloc_hash("hmac(sha256)", 0, CRYPTO_ALG_ASYNC); ++ if (IS_ERR(tfm)) { ++ DWC_ERROR("Failed to load transform for hmac(sha256): %ld\n", PTR_ERR(tfm)); ++ return 0; ++ } ++ desc.tfm = tfm; ++ desc.flags = 0; ++ ++ sg_init_one(&sg, message, messagelen); ++ crypto_hash_setkey(tfm, key, keylen); ++ crypto_hash_digest(&desc, &sg, messagelen, out); ++ crypto_free_hash(tfm); ++ ++ return 1; ++} ++#endif /* DWC_CRYPTOLIB */ ++ ++ ++/* Byte Ordering Conversions */ ++ ++uint32_t DWC_CPU_TO_LE32(uint32_t *p) ++{ ++#ifdef __LITTLE_ENDIAN ++ return *p; ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ ++ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24)); ++#endif ++} ++ ++uint32_t DWC_CPU_TO_BE32(uint32_t *p) ++{ ++#ifdef __BIG_ENDIAN ++ return *p; ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ ++ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24)); ++#endif ++} ++ ++uint32_t DWC_LE32_TO_CPU(uint32_t *p) ++{ ++#ifdef __LITTLE_ENDIAN ++ return *p; ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ ++ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24)); ++#endif ++} ++ ++uint32_t DWC_BE32_TO_CPU(uint32_t *p) ++{ ++#ifdef __BIG_ENDIAN ++ return *p; ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ ++ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24)); ++#endif ++} ++ ++uint16_t DWC_CPU_TO_LE16(uint16_t *p) ++{ ++#ifdef __LITTLE_ENDIAN ++ return *p; ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ return (u_p[1] | (u_p[0] << 8)); ++#endif ++} ++ ++uint16_t DWC_CPU_TO_BE16(uint16_t *p) ++{ ++#ifdef __BIG_ENDIAN ++ return *p; ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ return (u_p[1] | (u_p[0] << 8)); ++#endif ++} ++ ++uint16_t DWC_LE16_TO_CPU(uint16_t *p) ++{ ++#ifdef __LITTLE_ENDIAN ++ return *p; ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ return (u_p[1] | (u_p[0] << 8)); ++#endif ++} ++ ++uint16_t DWC_BE16_TO_CPU(uint16_t *p) ++{ ++#ifdef __BIG_ENDIAN ++ return *p; ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ return (u_p[1] | (u_p[0] << 8)); ++#endif ++} ++ ++ ++/* Registers */ ++ ++uint32_t DWC_READ_REG32(uint32_t volatile *reg) ++{ ++ return readl(reg); ++} ++ ++#if 0 ++uint64_t DWC_READ_REG64(uint64_t volatile *reg) ++{ ++} ++#endif ++ ++void DWC_WRITE_REG32(uint32_t volatile *reg, uint32_t value) ++{ ++ writel(value, reg); ++} ++ ++#if 0 ++void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value) ++{ ++} ++#endif ++ ++void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask) ++{ ++ writel((readl(reg) & ~clear_mask) | set_mask, reg); ++} ++ ++#if 0 ++void DWC_MODIFY_REG64(uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask) ++{ ++} ++#endif ++ ++ ++/* Locking */ ++ ++dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void) ++{ ++ spinlock_t *sl = (spinlock_t *)1; ++ ++#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP) ++ sl = DWC_ALLOC(sizeof(*sl)); ++ if (!sl) { ++ DWC_ERROR("Cannot allocate memory for spinlock\n"); ++ return NULL; ++ } ++ ++ spin_lock_init(sl); ++#endif ++ return (dwc_spinlock_t *)sl; ++} ++ ++void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock) ++{ ++#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP) ++ DWC_FREE(lock); ++#endif ++} ++ ++void DWC_SPINLOCK(dwc_spinlock_t *lock) ++{ ++#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP) ++ spin_lock((spinlock_t *)lock); ++#endif ++} ++ ++void DWC_SPINUNLOCK(dwc_spinlock_t *lock) ++{ ++#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP) ++ spin_unlock((spinlock_t *)lock); ++#endif ++} ++ ++void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags) ++{ ++ dwc_irqflags_t f; ++ ++#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP) ++ spin_lock_irqsave((spinlock_t *)lock, f); ++#else ++ local_irq_save(f); ++#endif ++ *flags = f; ++} ++ ++void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags) ++{ ++#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP) ++ spin_unlock_irqrestore((spinlock_t *)lock, flags); ++#else ++ local_irq_restore(flags); ++#endif ++} ++ ++dwc_mutex_t *DWC_MUTEX_ALLOC(void) ++{ ++ struct mutex *m; ++ dwc_mutex_t *mutex = (dwc_mutex_t *)DWC_ALLOC(sizeof(struct mutex)); ++ ++ if (!mutex) { ++ DWC_ERROR("Cannot allocate memory for mutex\n"); ++ return NULL; ++ } ++ ++ m = (struct mutex *)mutex; ++ mutex_init(m); ++ return mutex; ++} ++ ++#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)) ++#else ++void DWC_MUTEX_FREE(dwc_mutex_t *mutex) ++{ ++ mutex_destroy((struct mutex *)mutex); ++ DWC_FREE(mutex); ++} ++#endif ++ ++void DWC_MUTEX_LOCK(dwc_mutex_t *mutex) ++{ ++ struct mutex *m = (struct mutex *)mutex; ++ mutex_lock(m); ++} ++ ++int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex) ++{ ++ struct mutex *m = (struct mutex *)mutex; ++ return mutex_trylock(m); ++} ++ ++void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex) ++{ ++ struct mutex *m = (struct mutex *)mutex; ++ mutex_unlock(m); ++} ++ ++ ++/* Timing */ ++ ++void DWC_UDELAY(uint32_t usecs) ++{ ++ udelay(usecs); ++} ++ ++void DWC_MDELAY(uint32_t msecs) ++{ ++ if (in_interrupt()) ++ mdelay(msecs); ++ else ++ msleep(msecs); ++} ++ ++void DWC_MSLEEP(uint32_t msecs) ++{ ++ msleep(msecs); ++} ++ ++uint32_t DWC_TIME(void) ++{ ++ return jiffies_to_msecs(jiffies); ++} ++ ++ ++/* Timers */ ++ ++struct dwc_timer { ++ struct timer_list *t; ++ char *name; ++ dwc_timer_callback_t cb; ++ void *data; ++ uint8_t scheduled; ++ dwc_spinlock_t *lock; ++}; ++ ++static void timer_callback(unsigned long data) ++{ ++ dwc_timer_t *timer = (dwc_timer_t *)data; ++ dwc_irqflags_t flags; ++ ++ DWC_SPINLOCK_IRQSAVE(timer->lock, &flags); ++ timer->scheduled = 0; ++ DWC_SPINUNLOCK_IRQRESTORE(timer->lock, flags); ++ DWC_DEBUG("Timer %s callback", timer->name); ++ timer->cb(timer->data); ++} ++ ++dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data) ++{ ++ dwc_timer_t *t = DWC_ALLOC(sizeof(*t)); ++ ++ if (!t) { ++ DWC_ERROR("Cannot allocate memory for timer"); ++ return NULL; ++ } ++ ++ t->t = DWC_ALLOC(sizeof(*t->t)); ++ if (!t->t) { ++ DWC_ERROR("Cannot allocate memory for timer->t"); ++ goto no_timer; ++ } ++ ++ t->name = DWC_STRDUP(name); ++ if (!t->name) { ++ DWC_ERROR("Cannot allocate memory for timer->name"); ++ goto no_name; ++ } ++ ++ t->lock = DWC_SPINLOCK_ALLOC(); ++ if (!t->lock) { ++ DWC_ERROR("Cannot allocate memory for lock"); ++ goto no_lock; ++ } ++ ++ t->scheduled = 0; ++ t->t->base = &boot_tvec_bases; ++ t->t->expires = jiffies; ++ setup_timer(t->t, timer_callback, (unsigned long)t); ++ ++ t->cb = cb; ++ t->data = data; ++ ++ return t; ++ ++ no_lock: ++ DWC_FREE(t->name); ++ no_name: ++ DWC_FREE(t->t); ++ no_timer: ++ DWC_FREE(t); ++ return NULL; ++} ++ ++void DWC_TIMER_FREE(dwc_timer_t *timer) ++{ ++ dwc_irqflags_t flags; ++ ++ DWC_SPINLOCK_IRQSAVE(timer->lock, &flags); ++ ++ if (timer->scheduled) { ++ del_timer(timer->t); ++ timer->scheduled = 0; ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(timer->lock, flags); ++ DWC_SPINLOCK_FREE(timer->lock); ++ DWC_FREE(timer->t); ++ DWC_FREE(timer->name); ++ DWC_FREE(timer); ++} ++ ++void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time) ++{ ++ dwc_irqflags_t flags; ++ ++ DWC_SPINLOCK_IRQSAVE(timer->lock, &flags); ++ ++ if (!timer->scheduled) { ++ timer->scheduled = 1; ++ DWC_DEBUG("Scheduling timer %s to expire in +%d msec", timer->name, time); ++ timer->t->expires = jiffies + msecs_to_jiffies(time); ++ add_timer(timer->t); ++ } else { ++ DWC_DEBUG("Modifying timer %s to expire in +%d msec", timer->name, time); ++ mod_timer(timer->t, jiffies + msecs_to_jiffies(time)); ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(timer->lock, flags); ++} ++ ++void DWC_TIMER_CANCEL(dwc_timer_t *timer) ++{ ++ del_timer(timer->t); ++} ++ ++ ++/* Wait Queues */ ++ ++struct dwc_waitq { ++ wait_queue_head_t queue; ++ int abort; ++}; ++ ++dwc_waitq_t *DWC_WAITQ_ALLOC(void) ++{ ++ dwc_waitq_t *wq = DWC_ALLOC(sizeof(*wq)); ++ ++ if (!wq) { ++ DWC_ERROR("Cannot allocate memory for waitqueue\n"); ++ return NULL; ++ } ++ ++ init_waitqueue_head(&wq->queue); ++ wq->abort = 0; ++ return wq; ++} ++ ++void DWC_WAITQ_FREE(dwc_waitq_t *wq) ++{ ++ DWC_FREE(wq); ++} ++ ++int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data) ++{ ++ int result = wait_event_interruptible(wq->queue, ++ cond(data) || wq->abort); ++ if (result == -ERESTARTSYS) { ++ wq->abort = 0; ++ return -DWC_E_RESTART; ++ } ++ ++ if (wq->abort == 1) { ++ wq->abort = 0; ++ return -DWC_E_ABORT; ++ } ++ ++ wq->abort = 0; ++ ++ if (result == 0) { ++ return 0; ++ } ++ ++ return -DWC_E_UNKNOWN; ++} ++ ++int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, ++ void *data, int32_t msecs) ++{ ++ int32_t tmsecs; ++ int result = wait_event_interruptible_timeout(wq->queue, ++ cond(data) || wq->abort, ++ msecs_to_jiffies(msecs)); ++ if (result == -ERESTARTSYS) { ++ wq->abort = 0; ++ return -DWC_E_RESTART; ++ } ++ ++ if (wq->abort == 1) { ++ wq->abort = 0; ++ return -DWC_E_ABORT; ++ } ++ ++ wq->abort = 0; ++ ++ if (result > 0) { ++ tmsecs = jiffies_to_msecs(result); ++ if (!tmsecs) { ++ return 1; ++ } ++ ++ return tmsecs; ++ } ++ ++ if (result == 0) { ++ return -DWC_E_TIMEOUT; ++ } ++ ++ return -DWC_E_UNKNOWN; ++} ++ ++void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq) ++{ ++ wq->abort = 0; ++ wake_up_interruptible(&wq->queue); ++} ++ ++void DWC_WAITQ_ABORT(dwc_waitq_t *wq) ++{ ++ wq->abort = 1; ++ wake_up_interruptible(&wq->queue); ++} ++ ++ ++/* Threading */ ++ ++dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data) ++{ ++ struct task_struct *thread = kthread_run(func, data, name); ++ ++ if (thread == ERR_PTR(-ENOMEM)) { ++ return NULL; ++ } ++ ++ return (dwc_thread_t *)thread; ++} ++ ++int DWC_THREAD_STOP(dwc_thread_t *thread) ++{ ++ return kthread_stop((struct task_struct *)thread); ++} ++ ++dwc_bool_t DWC_THREAD_SHOULD_STOP(void) ++{ ++ return kthread_should_stop(); ++} ++ ++ ++/* tasklets ++ - run in interrupt context (cannot sleep) ++ - each tasklet runs on a single CPU ++ - different tasklets can be running simultaneously on different CPUs ++ */ ++struct dwc_tasklet { ++ struct tasklet_struct t; ++ dwc_tasklet_callback_t cb; ++ void *data; ++}; ++ ++static void tasklet_callback(unsigned long data) ++{ ++ dwc_tasklet_t *t = (dwc_tasklet_t *)data; ++ t->cb(t->data); ++} ++ ++dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data) ++{ ++ dwc_tasklet_t *t = DWC_ALLOC(sizeof(*t)); ++ ++ if (t) { ++ t->cb = cb; ++ t->data = data; ++ tasklet_init(&t->t, tasklet_callback, (unsigned long)t); ++ } else { ++ DWC_ERROR("Cannot allocate memory for tasklet\n"); ++ } ++ ++ return t; ++} ++ ++void DWC_TASK_FREE(dwc_tasklet_t *task) ++{ ++ DWC_FREE(task); ++} ++ ++void DWC_TASK_SCHEDULE(dwc_tasklet_t *task) ++{ ++ tasklet_schedule(&task->t); ++} ++ ++ ++/* workqueues ++ - run in process context (can sleep) ++ */ ++typedef struct work_container { ++ dwc_work_callback_t cb; ++ void *data; ++ dwc_workq_t *wq; ++ char *name; ++ ++#ifdef DEBUG ++ DWC_CIRCLEQ_ENTRY(work_container) entry; ++#endif ++ struct delayed_work work; ++} work_container_t; ++ ++#ifdef DEBUG ++DWC_CIRCLEQ_HEAD(work_container_queue, work_container); ++#endif ++ ++struct dwc_workq { ++ struct workqueue_struct *wq; ++ dwc_spinlock_t *lock; ++ dwc_waitq_t *waitq; ++ int pending; ++ ++#ifdef DEBUG ++ struct work_container_queue entries; ++#endif ++}; ++ ++static void do_work(struct work_struct *work) ++{ ++ dwc_irqflags_t flags; ++ struct delayed_work *dw = container_of(work, struct delayed_work, work); ++ work_container_t *container = container_of(dw, struct work_container, work); ++ dwc_workq_t *wq = container->wq; ++ ++ container->cb(container->data); ++ ++#ifdef DEBUG ++ DWC_CIRCLEQ_REMOVE(&wq->entries, container, entry); ++#endif ++ DWC_DEBUG("Work done: %s, container=%p", container->name, container); ++ if (container->name) { ++ DWC_FREE(container->name); ++ } ++ DWC_FREE(container); ++ ++ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags); ++ wq->pending--; ++ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags); ++ DWC_WAITQ_TRIGGER(wq->waitq); ++} ++ ++static int work_done(void *data) ++{ ++ dwc_workq_t *workq = (dwc_workq_t *)data; ++ return workq->pending == 0; ++} ++ ++int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout) ++{ ++ return DWC_WAITQ_WAIT_TIMEOUT(workq->waitq, work_done, workq, timeout); ++} ++ ++dwc_workq_t *DWC_WORKQ_ALLOC(char *name) ++{ ++ dwc_workq_t *wq = DWC_ALLOC(sizeof(*wq)); ++ ++ if (!wq) { ++ return NULL; ++ } ++ ++ wq->wq = create_singlethread_workqueue(name); ++ if (!wq->wq) { ++ goto no_wq; ++ } ++ ++ wq->pending = 0; ++ ++ wq->lock = DWC_SPINLOCK_ALLOC(); ++ if (!wq->lock) { ++ goto no_lock; ++ } ++ ++ wq->waitq = DWC_WAITQ_ALLOC(); ++ if (!wq->waitq) { ++ goto no_waitq; ++ } ++ ++#ifdef DEBUG ++ DWC_CIRCLEQ_INIT(&wq->entries); ++#endif ++ return wq; ++ ++ no_waitq: ++ DWC_SPINLOCK_FREE(wq->lock); ++ no_lock: ++ destroy_workqueue(wq->wq); ++ no_wq: ++ DWC_FREE(wq); ++ ++ return NULL; ++} ++ ++void DWC_WORKQ_FREE(dwc_workq_t *wq) ++{ ++#ifdef DEBUG ++ if (wq->pending != 0) { ++ struct work_container *wc; ++ DWC_ERROR("Destroying work queue with pending work"); ++ DWC_CIRCLEQ_FOREACH(wc, &wq->entries, entry) { ++ DWC_ERROR("Work %s still pending", wc->name); ++ } ++ } ++#endif ++ destroy_workqueue(wq->wq); ++ DWC_SPINLOCK_FREE(wq->lock); ++ DWC_WAITQ_FREE(wq->waitq); ++ DWC_FREE(wq); ++} ++ ++void DWC_WORKQ_SCHEDULE(dwc_workq_t *wq, dwc_work_callback_t cb, void *data, ++ char *format, ...) ++{ ++ dwc_irqflags_t flags; ++ work_container_t *container; ++ static char name[128]; ++ va_list args; ++ ++ va_start(args, format); ++ DWC_VSNPRINTF(name, 128, format, args); ++ va_end(args); ++ ++ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags); ++ wq->pending++; ++ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags); ++ DWC_WAITQ_TRIGGER(wq->waitq); ++ ++ container = DWC_ALLOC_ATOMIC(sizeof(*container)); ++ if (!container) { ++ DWC_ERROR("Cannot allocate memory for container\n"); ++ return; ++ } ++ ++ container->name = DWC_STRDUP(name); ++ if (!container->name) { ++ DWC_ERROR("Cannot allocate memory for container->name\n"); ++ DWC_FREE(container); ++ return; ++ } ++ ++ container->cb = cb; ++ container->data = data; ++ container->wq = wq; ++ DWC_DEBUG("Queueing work: %s, container=%p", container->name, container); ++ INIT_WORK(&container->work.work, do_work); ++ ++#ifdef DEBUG ++ DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry); ++#endif ++ queue_work(wq->wq, &container->work.work); ++} ++ ++void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *wq, dwc_work_callback_t cb, ++ void *data, uint32_t time, char *format, ...) ++{ ++ dwc_irqflags_t flags; ++ work_container_t *container; ++ static char name[128]; ++ va_list args; ++ ++ va_start(args, format); ++ DWC_VSNPRINTF(name, 128, format, args); ++ va_end(args); ++ ++ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags); ++ wq->pending++; ++ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags); ++ DWC_WAITQ_TRIGGER(wq->waitq); ++ ++ container = DWC_ALLOC_ATOMIC(sizeof(*container)); ++ if (!container) { ++ DWC_ERROR("Cannot allocate memory for container\n"); ++ return; ++ } ++ ++ container->name = DWC_STRDUP(name); ++ if (!container->name) { ++ DWC_ERROR("Cannot allocate memory for container->name\n"); ++ DWC_FREE(container); ++ return; ++ } ++ ++ container->cb = cb; ++ container->data = data; ++ container->wq = wq; ++ DWC_DEBUG("Queueing work: %s, container=%p", container->name, container); ++ INIT_DELAYED_WORK(&container->work, do_work); ++ ++#ifdef DEBUG ++ DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry); ++#endif ++ queue_delayed_work(wq->wq, &container->work, msecs_to_jiffies(time)); ++} ++ ++int DWC_WORKQ_PENDING(dwc_workq_t *wq) ++{ ++ return wq->pending; ++} ++ ++ ++#ifdef DWC_LIBMODULE ++ ++#ifdef DWC_CCLIB ++/* CC */ ++EXPORT_SYMBOL(dwc_cc_if_alloc); ++EXPORT_SYMBOL(dwc_cc_if_free); ++EXPORT_SYMBOL(dwc_cc_clear); ++EXPORT_SYMBOL(dwc_cc_add); ++EXPORT_SYMBOL(dwc_cc_remove); ++EXPORT_SYMBOL(dwc_cc_change); ++EXPORT_SYMBOL(dwc_cc_data_for_save); ++EXPORT_SYMBOL(dwc_cc_restore_from_data); ++EXPORT_SYMBOL(dwc_cc_match_chid); ++EXPORT_SYMBOL(dwc_cc_match_cdid); ++EXPORT_SYMBOL(dwc_cc_ck); ++EXPORT_SYMBOL(dwc_cc_chid); ++EXPORT_SYMBOL(dwc_cc_cdid); ++EXPORT_SYMBOL(dwc_cc_name); ++#endif /* DWC_CCLIB */ ++ ++#ifdef DWC_CRYPTOLIB ++# ifndef CONFIG_MACH_IPMATE ++/* Modpow */ ++EXPORT_SYMBOL(dwc_modpow); ++ ++/* DH */ ++EXPORT_SYMBOL(dwc_dh_modpow); ++EXPORT_SYMBOL(dwc_dh_derive_keys); ++EXPORT_SYMBOL(dwc_dh_pk); ++# endif /* CONFIG_MACH_IPMATE */ ++ ++/* Crypto */ ++EXPORT_SYMBOL(dwc_wusb_aes_encrypt); ++EXPORT_SYMBOL(dwc_wusb_cmf); ++EXPORT_SYMBOL(dwc_wusb_prf); ++EXPORT_SYMBOL(dwc_wusb_fill_ccm_nonce); ++EXPORT_SYMBOL(dwc_wusb_gen_nonce); ++EXPORT_SYMBOL(dwc_wusb_gen_key); ++EXPORT_SYMBOL(dwc_wusb_gen_mic); ++#endif /* DWC_CRYPTOLIB */ ++ ++/* Notification */ ++#ifdef DWC_NOTIFYLIB ++EXPORT_SYMBOL(dwc_alloc_notification_manager); ++EXPORT_SYMBOL(dwc_free_notification_manager); ++EXPORT_SYMBOL(dwc_register_notifier); ++EXPORT_SYMBOL(dwc_unregister_notifier); ++EXPORT_SYMBOL(dwc_add_observer); ++EXPORT_SYMBOL(dwc_remove_observer); ++EXPORT_SYMBOL(dwc_notify); ++#endif ++ ++/* Memory Debugging Routines */ ++#ifdef DWC_DEBUG_MEMORY ++EXPORT_SYMBOL(dwc_alloc_debug); ++EXPORT_SYMBOL(dwc_alloc_atomic_debug); ++EXPORT_SYMBOL(dwc_free_debug); ++EXPORT_SYMBOL(dwc_dma_alloc_debug); ++EXPORT_SYMBOL(dwc_dma_free_debug); ++#endif ++ ++EXPORT_SYMBOL(DWC_MEMSET); ++EXPORT_SYMBOL(DWC_MEMCPY); ++EXPORT_SYMBOL(DWC_MEMMOVE); ++EXPORT_SYMBOL(DWC_MEMCMP); ++EXPORT_SYMBOL(DWC_STRNCMP); ++EXPORT_SYMBOL(DWC_STRCMP); ++EXPORT_SYMBOL(DWC_STRLEN); ++EXPORT_SYMBOL(DWC_STRCPY); ++EXPORT_SYMBOL(DWC_STRDUP); ++EXPORT_SYMBOL(DWC_ATOI); ++EXPORT_SYMBOL(DWC_ATOUI); ++ ++#ifdef DWC_UTFLIB ++EXPORT_SYMBOL(DWC_UTF8_TO_UTF16LE); ++#endif /* DWC_UTFLIB */ ++ ++EXPORT_SYMBOL(DWC_IN_IRQ); ++EXPORT_SYMBOL(DWC_IN_BH); ++EXPORT_SYMBOL(DWC_VPRINTF); ++EXPORT_SYMBOL(DWC_VSNPRINTF); ++EXPORT_SYMBOL(DWC_PRINTF); ++EXPORT_SYMBOL(DWC_SPRINTF); ++EXPORT_SYMBOL(DWC_SNPRINTF); ++EXPORT_SYMBOL(__DWC_WARN); ++EXPORT_SYMBOL(__DWC_ERROR); ++EXPORT_SYMBOL(DWC_EXCEPTION); ++ ++#ifdef DEBUG ++EXPORT_SYMBOL(__DWC_DEBUG); ++#endif ++ ++EXPORT_SYMBOL(__DWC_DMA_ALLOC); ++EXPORT_SYMBOL(__DWC_DMA_ALLOC_ATOMIC); ++EXPORT_SYMBOL(__DWC_DMA_FREE); ++EXPORT_SYMBOL(__DWC_ALLOC); ++EXPORT_SYMBOL(__DWC_ALLOC_ATOMIC); ++EXPORT_SYMBOL(__DWC_FREE); ++ ++#ifdef DWC_CRYPTOLIB ++EXPORT_SYMBOL(DWC_RANDOM_BYTES); ++EXPORT_SYMBOL(DWC_AES_CBC); ++EXPORT_SYMBOL(DWC_SHA256); ++EXPORT_SYMBOL(DWC_HMAC_SHA256); ++#endif ++ ++EXPORT_SYMBOL(DWC_CPU_TO_LE32); ++EXPORT_SYMBOL(DWC_CPU_TO_BE32); ++EXPORT_SYMBOL(DWC_LE32_TO_CPU); ++EXPORT_SYMBOL(DWC_BE32_TO_CPU); ++EXPORT_SYMBOL(DWC_CPU_TO_LE16); ++EXPORT_SYMBOL(DWC_CPU_TO_BE16); ++EXPORT_SYMBOL(DWC_LE16_TO_CPU); ++EXPORT_SYMBOL(DWC_BE16_TO_CPU); ++EXPORT_SYMBOL(DWC_READ_REG32); ++EXPORT_SYMBOL(DWC_WRITE_REG32); ++EXPORT_SYMBOL(DWC_MODIFY_REG32); ++ ++#if 0 ++EXPORT_SYMBOL(DWC_READ_REG64); ++EXPORT_SYMBOL(DWC_WRITE_REG64); ++EXPORT_SYMBOL(DWC_MODIFY_REG64); ++#endif ++ ++EXPORT_SYMBOL(DWC_SPINLOCK_ALLOC); ++EXPORT_SYMBOL(DWC_SPINLOCK_FREE); ++EXPORT_SYMBOL(DWC_SPINLOCK); ++EXPORT_SYMBOL(DWC_SPINUNLOCK); ++EXPORT_SYMBOL(DWC_SPINLOCK_IRQSAVE); ++EXPORT_SYMBOL(DWC_SPINUNLOCK_IRQRESTORE); ++EXPORT_SYMBOL(DWC_MUTEX_ALLOC); ++ ++#if (!defined(DWC_LINUX) || !defined(CONFIG_DEBUG_MUTEXES)) ++EXPORT_SYMBOL(DWC_MUTEX_FREE); ++#endif ++ ++EXPORT_SYMBOL(DWC_MUTEX_LOCK); ++EXPORT_SYMBOL(DWC_MUTEX_TRYLOCK); ++EXPORT_SYMBOL(DWC_MUTEX_UNLOCK); ++EXPORT_SYMBOL(DWC_UDELAY); ++EXPORT_SYMBOL(DWC_MDELAY); ++EXPORT_SYMBOL(DWC_MSLEEP); ++EXPORT_SYMBOL(DWC_TIME); ++EXPORT_SYMBOL(DWC_TIMER_ALLOC); ++EXPORT_SYMBOL(DWC_TIMER_FREE); ++EXPORT_SYMBOL(DWC_TIMER_SCHEDULE); ++EXPORT_SYMBOL(DWC_TIMER_CANCEL); ++EXPORT_SYMBOL(DWC_WAITQ_ALLOC); ++EXPORT_SYMBOL(DWC_WAITQ_FREE); ++EXPORT_SYMBOL(DWC_WAITQ_WAIT); ++EXPORT_SYMBOL(DWC_WAITQ_WAIT_TIMEOUT); ++EXPORT_SYMBOL(DWC_WAITQ_TRIGGER); ++EXPORT_SYMBOL(DWC_WAITQ_ABORT); ++EXPORT_SYMBOL(DWC_THREAD_RUN); ++EXPORT_SYMBOL(DWC_THREAD_STOP); ++EXPORT_SYMBOL(DWC_THREAD_SHOULD_STOP); ++EXPORT_SYMBOL(DWC_TASK_ALLOC); ++EXPORT_SYMBOL(DWC_TASK_FREE); ++EXPORT_SYMBOL(DWC_TASK_SCHEDULE); ++EXPORT_SYMBOL(DWC_WORKQ_WAIT_WORK_DONE); ++EXPORT_SYMBOL(DWC_WORKQ_ALLOC); ++EXPORT_SYMBOL(DWC_WORKQ_FREE); ++EXPORT_SYMBOL(DWC_WORKQ_SCHEDULE); ++EXPORT_SYMBOL(DWC_WORKQ_SCHEDULE_DELAYED); ++EXPORT_SYMBOL(DWC_WORKQ_PENDING); ++ ++static int dwc_common_port_init_module(void) ++{ ++ int result = 0; ++ ++ printk(KERN_DEBUG "Module dwc_common_port init\n" ); ++ ++#ifdef DWC_DEBUG_MEMORY ++ result = dwc_memory_debug_start(NULL); ++ if (result) { ++ printk(KERN_ERR ++ "dwc_memory_debug_start() failed with error %d\n", ++ result); ++ return result; ++ } ++#endif ++ ++#ifdef DWC_NOTIFYLIB ++ result = dwc_alloc_notification_manager(NULL, NULL); ++ if (result) { ++ printk(KERN_ERR ++ "dwc_alloc_notification_manager() failed with error %d\n", ++ result); ++ return result; ++ } ++#endif ++ return result; ++} ++ ++static void dwc_common_port_exit_module(void) ++{ ++ printk(KERN_DEBUG "Module dwc_common_port exit\n" ); ++ ++#ifdef DWC_NOTIFYLIB ++ dwc_free_notification_manager(); ++#endif ++ ++#ifdef DWC_DEBUG_MEMORY ++ dwc_memory_debug_stop(); ++#endif ++} ++ ++module_init(dwc_common_port_init_module); ++module_exit(dwc_common_port_exit_module); ++ ++MODULE_DESCRIPTION("DWC Common Library - Portable version"); ++MODULE_AUTHOR("Synopsys Inc."); ++MODULE_LICENSE ("GPL"); ++ ++#endif /* DWC_LIBMODULE */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_crypto.c b/drivers/usb/gadget/udc/hiudc/dwc_crypto.c +new file mode 100644 +index 0000000..3b03532 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_crypto.c +@@ -0,0 +1,308 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_crypto.c $ ++ * $Revision: #5 $ ++ * $Date: 2010/09/28 $ ++ * $Change: 1596182 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++ ++/** @file ++ * This file contains the WUSB cryptographic routines. ++ */ ++ ++#ifdef DWC_CRYPTOLIB ++ ++#include "dwc_crypto.h" ++#include "usb.h" ++ ++#ifdef DEBUG ++static inline void dump_bytes(char *name, uint8_t *bytes, int len) ++{ ++ int i; ++ DWC_PRINTF("%s: ", name); ++ for (i=0; i<len; i++) { ++ DWC_PRINTF("%02x ", bytes[i]); ++ } ++ DWC_PRINTF("\n"); ++} ++#else ++#define dump_bytes(x...) ++#endif ++ ++/* Display a block */ ++void show_block(const u8 *blk, const char *prefix, const char *suffix, int a) ++{ ++#ifdef DWC_DEBUG_CRYPTO ++ int i, blksize = 16; ++ ++ DWC_DEBUG("%s", prefix); ++ ++ if (suffix == NULL) { ++ suffix = "\n"; ++ blksize = a; ++ } ++ ++ for (i = 0; i < blksize; i++) ++ DWC_PRINT("%02x%s", *blk++, ((i & 3) == 3) ? " " : " "); ++ DWC_PRINT(suffix); ++#endif ++} ++ ++/** ++ * Encrypts an array of bytes using the AES encryption engine. ++ * If <code>dst</code> == <code>src</code>, then the bytes will be encrypted ++ * in-place. ++ * ++ * @return 0 on success, negative error code on error. ++ */ ++int dwc_wusb_aes_encrypt(u8 *src, u8 *key, u8 *dst) ++{ ++ u8 block_t[16]; ++ DWC_MEMSET(block_t, 0, 16); ++ ++ return DWC_AES_CBC(src, 16, key, 16, block_t, dst); ++} ++ ++/** ++ * The CCM-MAC-FUNCTION described in section 6.5 of the WUSB spec. ++ * This function takes a data string and returns the encrypted CBC ++ * Counter-mode MIC. ++ * ++ * @param key The 128-bit symmetric key. ++ * @param nonce The CCM nonce. ++ * @param label The unique 14-byte ASCII text label. ++ * @param bytes The byte array to be encrypted. ++ * @param len Length of the byte array. ++ * @param result Byte array to receive the 8-byte encrypted MIC. ++ */ ++void dwc_wusb_cmf(u8 *key, u8 *nonce, ++ char *label, u8 *bytes, int len, u8 *result) ++{ ++ u8 block_m[16]; ++ u8 block_x[16]; ++ u8 block_t[8]; ++ int idx, blkNum; ++ u16 la = (u16)(len + 14); ++ ++ /* Set the AES-128 key */ ++ //dwc_aes_setkey(tfm, key, 16); ++ ++ /* Fill block B0 from flags = 0x59, N, and l(m) = 0 */ ++ block_m[0] = 0x59; ++ for (idx = 0; idx < 13; idx++) ++ block_m[idx + 1] = nonce[idx]; ++ block_m[14] = 0; ++ block_m[15] = 0; ++ ++ /* Produce the CBC IV */ ++ dwc_wusb_aes_encrypt(block_m, key, block_x); ++ show_block(block_m, "CBC IV in: ", "\n", 0); ++ show_block(block_x, "CBC IV out:", "\n", 0); ++ ++ /* Fill block B1 from l(a) = Blen + 14, and A */ ++ block_x[0] ^= (u8)(la >> 8); ++ block_x[1] ^= (u8)la; ++ for (idx = 0; idx < 14; idx++) ++ block_x[idx + 2] ^= label[idx]; ++ show_block(block_x, "After xor: ", "b1\n", 16); ++ ++ dwc_wusb_aes_encrypt(block_x, key, block_x); ++ show_block(block_x, "After AES: ", "b1\n", 16); ++ ++ idx = 0; ++ blkNum = 0; ++ ++ /* Fill remaining blocks with B */ ++ while (len-- > 0) { ++ block_x[idx] ^= *bytes++; ++ if (++idx >= 16) { ++ idx = 0; ++ show_block(block_x, "After xor: ", "\n", blkNum); ++ dwc_wusb_aes_encrypt(block_x, key, block_x); ++ show_block(block_x, "After AES: ", "\n", blkNum); ++ blkNum++; ++ } ++ } ++ ++ /* Handle partial last block */ ++ if (idx > 0) { ++ show_block(block_x, "After xor: ", "\n", blkNum); ++ dwc_wusb_aes_encrypt(block_x, key, block_x); ++ show_block(block_x, "After AES: ", "\n", blkNum); ++ } ++ ++ /* Save the MIC tag */ ++ DWC_MEMCPY(block_t, block_x, 8); ++ show_block(block_t, "MIC tag : ", NULL, 8); ++ ++ /* Fill block A0 from flags = 0x01, N, and counter = 0 */ ++ block_m[0] = 0x01; ++ block_m[14] = 0; ++ block_m[15] = 0; ++ ++ /* Encrypt the counter */ ++ dwc_wusb_aes_encrypt(block_m, key, block_x); ++ show_block(block_x, "CTR[MIC] : ", NULL, 8); ++ ++ /* XOR with MIC tag */ ++ for (idx = 0; idx < 8; idx++) { ++ block_t[idx] ^= block_x[idx]; ++ } ++ ++ /* Return result to caller */ ++ DWC_MEMCPY(result, block_t, 8); ++ show_block(result, "CCM-MIC : ", NULL, 8); ++ ++} ++ ++/** ++ * The PRF function described in section 6.5 of the WUSB spec. This function ++ * concatenates MIC values returned from dwc_cmf() to create a value of ++ * the requested length. ++ * ++ * @param prf_len Length of the PRF function in bits (64, 128, or 256). ++ * @param key, nonce, label, bytes, len Same as for dwc_cmf(). ++ * @param result Byte array to receive the result. ++ */ ++void dwc_wusb_prf(int prf_len, u8 *key, ++ u8 *nonce, char *label, u8 *bytes, int len, u8 *result) ++{ ++ int i; ++ ++ nonce[0] = 0; ++ for (i = 0; i < prf_len >> 6; i++, nonce[0]++) { ++ dwc_wusb_cmf(key, nonce, label, bytes, len, result); ++ result += 8; ++ } ++} ++ ++/** ++ * Fills in CCM Nonce per the WUSB spec. ++ * ++ * @param[in] haddr Host address. ++ * @param[in] daddr Device address. ++ * @param[in] tkid Session Key(PTK) identifier. ++ * @param[out] nonce Pointer to where the CCM Nonce output is to be written. ++ */ ++void dwc_wusb_fill_ccm_nonce(uint16_t haddr, uint16_t daddr, uint8_t *tkid, ++ uint8_t *nonce) ++{ ++ ++ DWC_DEBUG("%s %x %x\n", __func__, daddr, haddr); ++ ++ DWC_MEMSET(&nonce[0], 0, 16); ++ ++ DWC_MEMCPY(&nonce[6], tkid, 3); ++ nonce[9] = daddr & 0xFF; ++ nonce[10] = (daddr >> 8) & 0xFF; ++ nonce[11] = haddr & 0xFF; ++ nonce[12] = (haddr >> 8) & 0xFF; ++ ++ dump_bytes("CCM nonce", nonce, 16); ++} ++ ++/** ++ * Generates a 16-byte cryptographic-grade random number for the Host/Device ++ * Nonce. ++ */ ++void dwc_wusb_gen_nonce(uint16_t addr, uint8_t *nonce) ++{ ++ uint8_t inonce[16]; ++ uint32_t temp[4]; ++ ++ /* Fill in the Nonce */ ++ DWC_MEMSET(&inonce[0], 0, sizeof(inonce)); ++ inonce[9] = addr & 0xFF; ++ inonce[10] = (addr >> 8) & 0xFF; ++ inonce[11] = inonce[9]; ++ inonce[12] = inonce[10]; ++ ++ /* Collect "randomness samples" */ ++ DWC_RANDOM_BYTES((uint8_t *)temp, 16); ++ ++ dwc_wusb_prf_128((uint8_t *)temp, nonce, ++ "Random Numbers", (uint8_t *)temp, sizeof(temp), ++ nonce); ++} ++ ++/** ++ * Generates the Session Key (PTK) and Key Confirmation Key (KCK) per the ++ * WUSB spec. ++ * ++ * @param[in] ccm_nonce Pointer to CCM Nonce. ++ * @param[in] mk Master Key to derive the session from ++ * @param[in] hnonce Pointer to Host Nonce. ++ * @param[in] dnonce Pointer to Device Nonce. ++ * @param[out] kck Pointer to where the KCK output is to be written. ++ * @param[out] ptk Pointer to where the PTK output is to be written. ++ */ ++void dwc_wusb_gen_key(uint8_t *ccm_nonce, uint8_t *mk, uint8_t *hnonce, ++ uint8_t *dnonce, uint8_t *kck, uint8_t *ptk) ++{ ++ uint8_t idata[32]; ++ uint8_t odata[32]; ++ ++ dump_bytes("ck", mk, 16); ++ dump_bytes("hnonce", hnonce, 16); ++ dump_bytes("dnonce", dnonce, 16); ++ ++ /* The data is the HNonce and DNonce concatenated */ ++ DWC_MEMCPY(&idata[0], hnonce, 16); ++ DWC_MEMCPY(&idata[16], dnonce, 16); ++ ++ dwc_wusb_prf_256(mk, ccm_nonce, "Pair-wise keys", idata, 32, odata); ++ ++ /* Low 16 bytes of the result is the KCK, high 16 is the PTK */ ++ DWC_MEMCPY(kck, &odata[0], 16); ++ DWC_MEMCPY(ptk, &odata[16], 16); ++ ++ dump_bytes("kck", kck, 16); ++ dump_bytes("ptk", ptk, 16); ++} ++ ++/** ++ * Generates the Message Integrity Code over the Handshake data per the ++ * WUSB spec. ++ * ++ * @param ccm_nonce Pointer to CCM Nonce. ++ * @param kck Pointer to Key Confirmation Key. ++ * @param data Pointer to Handshake data to be checked. ++ * @param mic Pointer to where the MIC output is to be written. ++ */ ++void dwc_wusb_gen_mic(uint8_t *ccm_nonce, uint8_t *kck, ++ uint8_t *data, uint8_t *mic) ++{ ++ ++ dwc_wusb_prf_64(kck, ccm_nonce, "out-of-bandMIC", ++ data, WUSB_HANDSHAKE_LEN_FOR_MIC, mic); ++} ++ ++#endif /* DWC_CRYPTOLIB */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_crypto.h b/drivers/usb/gadget/udc/hiudc/dwc_crypto.h +new file mode 100644 +index 0000000..26fcddc +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_crypto.h +@@ -0,0 +1,111 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_crypto.h $ ++ * $Revision: #3 $ ++ * $Date: 2010/09/28 $ ++ * $Change: 1596182 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++ ++#ifndef _DWC_CRYPTO_H_ ++#define _DWC_CRYPTO_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** @file ++ * ++ * This file contains declarations for the WUSB Cryptographic routines as ++ * defined in the WUSB spec. They are only to be used internally by the DWC UWB ++ * modules. ++ */ ++ ++#include "dwc_os.h" ++ ++int dwc_wusb_aes_encrypt(u8 *src, u8 *key, u8 *dst); ++ ++void dwc_wusb_cmf(u8 *key, u8 *nonce, ++ char *label, u8 *bytes, int len, u8 *result); ++void dwc_wusb_prf(int prf_len, u8 *key, ++ u8 *nonce, char *label, u8 *bytes, int len, u8 *result); ++ ++/** ++ * The PRF-64 function described in section 6.5 of the WUSB spec. ++ * ++ * @param key, nonce, label, bytes, len, result Same as for dwc_prf(). ++ */ ++static inline void dwc_wusb_prf_64(u8 *key, u8 *nonce, ++ char *label, u8 *bytes, int len, u8 *result) ++{ ++ dwc_wusb_prf(64, key, nonce, label, bytes, len, result); ++} ++ ++/** ++ * The PRF-128 function described in section 6.5 of the WUSB spec. ++ * ++ * @param key, nonce, label, bytes, len, result Same as for dwc_prf(). ++ */ ++static inline void dwc_wusb_prf_128(u8 *key, u8 *nonce, ++ char *label, u8 *bytes, int len, u8 *result) ++{ ++ dwc_wusb_prf(128, key, nonce, label, bytes, len, result); ++} ++ ++/** ++ * The PRF-256 function described in section 6.5 of the WUSB spec. ++ * ++ * @param key, nonce, label, bytes, len, result Same as for dwc_prf(). ++ */ ++static inline void dwc_wusb_prf_256(u8 *key, u8 *nonce, ++ char *label, u8 *bytes, int len, u8 *result) ++{ ++ dwc_wusb_prf(256, key, nonce, label, bytes, len, result); ++} ++ ++ ++void dwc_wusb_fill_ccm_nonce(uint16_t haddr, uint16_t daddr, uint8_t *tkid, ++ uint8_t *nonce); ++void dwc_wusb_gen_nonce(uint16_t addr, ++ uint8_t *nonce); ++ ++void dwc_wusb_gen_key(uint8_t *ccm_nonce, uint8_t *mk, ++ uint8_t *hnonce, uint8_t *dnonce, ++ uint8_t *kck, uint8_t *ptk); ++ ++ ++void dwc_wusb_gen_mic(uint8_t *ccm_nonce, uint8_t ++ *kck, uint8_t *data, uint8_t *mic); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_CRYPTO_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_dh.c b/drivers/usb/gadget/udc/hiudc/dwc_dh.c +new file mode 100644 +index 0000000..2b429a3 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_dh.c +@@ -0,0 +1,291 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_dh.c $ ++ * $Revision: #3 $ ++ * $Date: 2010/09/28 $ ++ * $Change: 1596182 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++#ifdef DWC_CRYPTOLIB ++ ++#ifndef CONFIG_MACH_IPMATE ++ ++#include "dwc_dh.h" ++#include "dwc_modpow.h" ++ ++#ifdef DEBUG ++/* This function prints out a buffer in the format described in the Association ++ * Model specification. */ ++static void dh_dump(char *str, void *_num, int len) ++{ ++ uint8_t *num = _num; ++ int i; ++ DWC_PRINTF("%s\n", str); ++ for (i = 0; i < len; i ++) { ++ DWC_PRINTF("%02x", num[i]); ++ if (((i + 1) % 2) == 0) DWC_PRINTF(" "); ++ if (((i + 1) % 26) == 0) DWC_PRINTF("\n"); ++ } ++ ++ DWC_PRINTF("\n"); ++} ++#else ++#define dh_dump(_x...) do {; } while(0) ++#endif ++ ++/* Constant g value */ ++static __u32 dh_g[] = { ++ 0x02000000, ++}; ++ ++/* Constant p value */ ++static __u32 dh_p[] = { ++ 0xFFFFFFFF, 0xFFFFFFFF, 0xA2DA0FC9, 0x34C26821, 0x8B62C6C4, 0xD11CDC80, 0x084E0229, 0x74CC678A, ++ 0xA6BE0B02, 0x229B133B, 0x79084A51, 0xDD04348E, 0xB31995EF, 0x1B433ACD, 0x6D0A2B30, 0x37145FF2, ++ 0x6D35E14F, 0x45C2516D, 0x76B585E4, 0xC67E5E62, 0xE9424CF4, 0x6BED37A6, 0xB65CFF0B, 0xEDB706F4, ++ 0xFB6B38EE, 0xA59F895A, 0x11249FAE, 0xE61F4B7C, 0x51662849, 0x3D5BE4EC, 0xB87C00C2, 0x05BF63A1, ++ 0x3648DA98, 0x9AD3551C, 0xA83F1669, 0x5FCF24FD, 0x235D6583, 0x96ADA3DC, 0x56F3621C, 0xBB528520, ++ 0x0729D59E, 0x6D969670, 0x4E350C67, 0x0498BC4A, 0x086C74F1, 0x7C2118CA, 0x465E9032, 0x3BCE362E, ++ 0x2C779EE3, 0x03860E18, 0xA283279B, 0x8FA207EC, 0xF05DC5B5, 0xC9524C6F, 0xF6CB2BDE, 0x18175895, ++ 0x7C499539, 0xE56A95EA, 0x1826D215, 0x1005FA98, 0x5A8E7215, 0x2DC4AA8A, 0x0D1733AD, 0x337A5004, ++ 0xAB2155A8, 0x64BA1CDF, 0x0485FBEC, 0x0AEFDB58, 0x5771EA8A, 0x7D0C065D, 0x850F97B3, 0xC7E4E1A6, ++ 0x8CAEF5AB, 0xD73309DB, 0xE0948C1E, 0x9D61254A, 0x26D2E3CE, 0x6BEED21A, 0x06FA2FF1, 0x64088AD9, ++ 0x730276D8, 0x646AC83E, 0x182B1F52, 0x0C207B17, 0x5717E1BB, 0x6C5D617A, 0xC0880977, 0xE246D9BA, ++ 0xA04FE208, 0x31ABE574, 0xFC5BDB43, 0x8E10FDE0, 0x20D1824B, 0xCAD23AA9, 0xFFFFFFFF, 0xFFFFFFFF, ++}; ++ ++static void dh_swap_bytes(void *_in, void *_out, uint32_t len) ++{ ++ uint8_t *in = _in; ++ uint8_t *out = _out; ++ int i; ++ for (i=0; i<len; i++) { ++ out[i] = in[len-1-i]; ++ } ++} ++ ++/* Computes the modular exponentiation (num^exp % mod). num, exp, and mod are ++ * big endian numbers of size len, in bytes. Each len value must be a multiple ++ * of 4. */ ++int dwc_dh_modpow(void *mem_ctx, void *num, uint32_t num_len, ++ void *exp, uint32_t exp_len, ++ void *mod, uint32_t mod_len, ++ void *out) ++{ ++ /* modpow() takes little endian numbers. AM uses big-endian. This ++ * function swaps bytes of numbers before passing onto modpow. */ ++ ++ int retval = 0; ++ uint32_t *result; ++ ++ uint32_t *bignum_num = dwc_alloc(mem_ctx, num_len + 4); ++ uint32_t *bignum_exp = dwc_alloc(mem_ctx, exp_len + 4); ++ uint32_t *bignum_mod = dwc_alloc(mem_ctx, mod_len + 4); ++ ++ dh_swap_bytes(num, &bignum_num[1], num_len); ++ bignum_num[0] = num_len / 4; ++ ++ dh_swap_bytes(exp, &bignum_exp[1], exp_len); ++ bignum_exp[0] = exp_len / 4; ++ ++ dh_swap_bytes(mod, &bignum_mod[1], mod_len); ++ bignum_mod[0] = mod_len / 4; ++ ++ result = dwc_modpow(mem_ctx, bignum_num, bignum_exp, bignum_mod); ++ if (!result) { ++ retval = -1; ++ goto dh_modpow_nomem; ++ } ++ ++ dh_swap_bytes(&result[1], out, result[0] * 4); ++ dwc_free(mem_ctx, result); ++ ++ dh_modpow_nomem: ++ dwc_free(mem_ctx, bignum_num); ++ dwc_free(mem_ctx, bignum_exp); ++ dwc_free(mem_ctx, bignum_mod); ++ return retval; ++} ++ ++ ++int dwc_dh_pk(void *mem_ctx, uint8_t nd, uint8_t *exp, uint8_t *pk, uint8_t *hash) ++{ ++ int retval; ++ uint8_t m3[385]; ++ ++#ifndef DH_TEST_VECTORS ++ DWC_RANDOM_BYTES(exp, 32); ++#endif ++ ++ /* Compute the pkd */ ++ if ((retval = dwc_dh_modpow(mem_ctx, dh_g, 4, ++ exp, 32, ++ dh_p, 384, pk))) { ++ return retval; ++ } ++ ++ m3[384] = nd; ++ DWC_MEMCPY(&m3[0], pk, 384); ++ DWC_SHA256(m3, 385, hash); ++ ++ dh_dump("PK", pk, 384); ++ dh_dump("SHA-256(M3)", hash, 32); ++ return 0; ++} ++ ++int dwc_dh_derive_keys(void *mem_ctx, uint8_t nd, uint8_t *pkh, uint8_t *pkd, ++ uint8_t *exp, int is_host, ++ char *dd, uint8_t *ck, uint8_t *kdk) ++{ ++ int retval; ++ uint8_t mv[784]; ++ uint8_t sha_result[32]; ++ uint8_t dhkey[384]; ++ uint8_t shared_secret[384]; ++ char *message; ++ uint32_t vd; ++ ++ uint8_t *pk; ++ ++ if (is_host) { ++ pk = pkd; ++ } ++ else { ++ pk = pkh; ++ } ++ ++ if ((retval = dwc_dh_modpow(mem_ctx, pk, 384, ++ exp, 32, ++ dh_p, 384, shared_secret))) { ++ return retval; ++ } ++ dh_dump("Shared Secret", shared_secret, 384); ++ ++ DWC_SHA256(shared_secret, 384, dhkey); ++ dh_dump("DHKEY", dhkey, 384); ++ ++ DWC_MEMCPY(&mv[0], pkd, 384); ++ DWC_MEMCPY(&mv[384], pkh, 384); ++ DWC_MEMCPY(&mv[768], "displayed digest", 16); ++ dh_dump("MV", mv, 784); ++ ++ DWC_SHA256(mv, 784, sha_result); ++ dh_dump("SHA-256(MV)", sha_result, 32); ++ dh_dump("First 32-bits of SHA-256(MV)", sha_result, 4); ++ ++ dh_swap_bytes(sha_result, &vd, 4); ++#ifdef DEBUG ++ DWC_PRINTF("Vd (decimal) = %d\n", vd); ++#endif ++ ++ switch (nd) { ++ case 2: ++ vd = vd % 100; ++ DWC_SPRINTF(dd, "%02d", vd); ++ break; ++ case 3: ++ vd = vd % 1000; ++ DWC_SPRINTF(dd, "%03d", vd); ++ break; ++ case 4: ++ vd = vd % 10000; ++ DWC_SPRINTF(dd, "%04d", vd); ++ break; ++ } ++#ifdef DEBUG ++ DWC_PRINTF("Display Digits: %s\n", dd); ++#endif ++ ++ message = "connection key"; ++ DWC_HMAC_SHA256(message, DWC_STRLEN(message), dhkey, 32, sha_result); ++ dh_dump("HMAC(SHA-256, DHKey, connection key)", sha_result, 32); ++ DWC_MEMCPY(ck, sha_result, 16); ++ ++ message = "key derivation key"; ++ DWC_HMAC_SHA256(message, DWC_STRLEN(message), dhkey, 32, sha_result); ++ dh_dump("HMAC(SHA-256, DHKey, key derivation key)", sha_result, 32); ++ DWC_MEMCPY(kdk, sha_result, 32); ++ ++ return 0; ++} ++ ++ ++#ifdef DH_TEST_VECTORS ++ ++static __u8 dh_a[] = { ++ 0x44, 0x00, 0x51, 0xd6, ++ 0xf0, 0xb5, 0x5e, 0xa9, ++ 0x67, 0xab, 0x31, 0xc6, ++ 0x8a, 0x8b, 0x5e, 0x37, ++ 0xd9, 0x10, 0xda, 0xe0, ++ 0xe2, 0xd4, 0x59, 0xa4, ++ 0x86, 0x45, 0x9c, 0xaa, ++ 0xdf, 0x36, 0x75, 0x16, ++}; ++ ++static __u8 dh_b[] = { ++ 0x5d, 0xae, 0xc7, 0x86, ++ 0x79, 0x80, 0xa3, 0x24, ++ 0x8c, 0xe3, 0x57, 0x8f, ++ 0xc7, 0x5f, 0x1b, 0x0f, ++ 0x2d, 0xf8, 0x9d, 0x30, ++ 0x6f, 0xa4, 0x52, 0xcd, ++ 0xe0, 0x7a, 0x04, 0x8a, ++ 0xde, 0xd9, 0x26, 0x56, ++}; ++ ++void dwc_run_dh_test_vectors(void *mem_ctx) ++{ ++ uint8_t pkd[384]; ++ uint8_t pkh[384]; ++ uint8_t hashd[32]; ++ uint8_t hashh[32]; ++ uint8_t ck[16]; ++ uint8_t kdk[32]; ++ char dd[5]; ++ ++ DWC_PRINTF("\n\n\nDH_TEST_VECTORS\n\n"); ++ ++ /* compute the PKd and SHA-256(PKd || Nd) */ ++ DWC_PRINTF("Computing PKd\n"); ++ dwc_dh_pk(mem_ctx, 2, dh_a, pkd, hashd); ++ ++ /* compute the PKd and SHA-256(PKh || Nd) */ ++ DWC_PRINTF("Computing PKh\n"); ++ dwc_dh_pk(mem_ctx, 2, dh_b, pkh, hashh); ++ ++ /* compute the dhkey */ ++ dwc_dh_derive_keys(mem_ctx, 2, pkh, pkd, dh_a, 0, dd, ck, kdk); ++} ++#endif /* DH_TEST_VECTORS */ ++ ++#endif /* !CONFIG_MACH_IPMATE */ ++ ++#endif /* DWC_CRYPTOLIB */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_dh.h b/drivers/usb/gadget/udc/hiudc/dwc_dh.h +new file mode 100644 +index 0000000..25c1cc0 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_dh.h +@@ -0,0 +1,106 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_dh.h $ ++ * $Revision: #4 $ ++ * $Date: 2010/09/28 $ ++ * $Change: 1596182 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++#ifndef _DWC_DH_H_ ++#define _DWC_DH_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "dwc_os.h" ++ ++/** @file ++ * ++ * This file defines the common functions on device and host for performing ++ * numeric association as defined in the WUSB spec. They are only to be ++ * used internally by the DWC UWB modules. */ ++ ++extern int dwc_dh_sha256(uint8_t *message, uint32_t len, uint8_t *out); ++extern int dwc_dh_hmac_sha256(uint8_t *message, uint32_t messagelen, ++ uint8_t *key, uint32_t keylen, ++ uint8_t *out); ++extern int dwc_dh_modpow(void *mem_ctx, void *num, uint32_t num_len, ++ void *exp, uint32_t exp_len, ++ void *mod, uint32_t mod_len, ++ void *out); ++ ++/** Computes PKD or PKH, and SHA-256(PKd || Nd) ++ * ++ * PK = g^exp mod p. ++ * ++ * Input: ++ * Nd = Number of digits on the device. ++ * ++ * Output: ++ * exp = A 32-byte buffer to be filled with a randomly generated number. ++ * used as either A or B. ++ * pk = A 384-byte buffer to be filled with the PKH or PKD. ++ * hash = A 32-byte buffer to be filled with SHA-256(PK || ND). ++ */ ++extern int dwc_dh_pk(void *mem_ctx, uint8_t nd, uint8_t *exp, uint8_t *pkd, uint8_t *hash); ++ ++/** Computes the DHKEY, and VD. ++ * ++ * If called from host, then it will comput DHKEY=PKD^exp % p. ++ * If called from device, then it will comput DHKEY=PKH^exp % p. ++ * ++ * Input: ++ * pkd = The PKD value. ++ * pkh = The PKH value. ++ * exp = The A value (if device) or B value (if host) generated in dwc_wudev_dh_pk. ++ * is_host = Set to non zero if a WUSB host is calling this function. ++ * ++ * Output: ++ ++ * dd = A pointer to an buffer to be set to the displayed digits string to be shown ++ * to the user. This buffer should be at 5 bytes long to hold 4 digits plus a ++ * null termination character. This buffer can be used directly for display. ++ * ck = A 16-byte buffer to be filled with the CK. ++ * kdk = A 32-byte buffer to be filled with the KDK. ++ */ ++extern int dwc_dh_derive_keys(void *mem_ctx, uint8_t nd, uint8_t *pkh, uint8_t *pkd, ++ uint8_t *exp, int is_host, ++ char *dd, uint8_t *ck, uint8_t *kdk); ++ ++#ifdef DH_TEST_VECTORS ++extern void dwc_run_dh_test_vectors(void); ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_DH_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_list.h b/drivers/usb/gadget/udc/hiudc/dwc_list.h +new file mode 100644 +index 0000000..89cc325 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_list.h +@@ -0,0 +1,594 @@ ++/* $OpenBSD: queue.h,v 1.26 2004/05/04 16:59:32 grange Exp $ */ ++/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ ++ ++/* ++ * Copyright (c) 1991, 1993 ++ * The Regents of the University of California. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the University nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * @(#)queue.h 8.5 (Berkeley) 8/20/94 ++ */ ++ ++#ifndef _DWC_LIST_H_ ++#define _DWC_LIST_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** @file ++ * ++ * This file defines linked list operations. It is derived from BSD with ++ * only the MACRO names being prefixed with DWC_. This is because a few of ++ * these names conflict with those on Linux. For documentation on use, see the ++ * inline comments in the source code. The original license for this source ++ * code applies and is preserved in the dwc_list.h source file. ++ */ ++ ++/* ++ * This file defines five types of data structures: singly-linked lists, ++ * lists, simple queues, tail queues, and circular queues. ++ * ++ * ++ * A singly-linked list is headed by a single forward pointer. The elements ++ * are singly linked for minimum space and pointer manipulation overhead at ++ * the expense of O(n) removal for arbitrary elements. New elements can be ++ * added to the list after an existing element or at the head of the list. ++ * Elements being removed from the head of the list should use the explicit ++ * macro for this purpose for optimum efficiency. A singly-linked list may ++ * only be traversed in the forward direction. Singly-linked lists are ideal ++ * for applications with large datasets and few or no removals or for ++ * implementing a LIFO queue. ++ * ++ * A list is headed by a single forward pointer (or an array of forward ++ * pointers for a hash table header). The elements are doubly linked ++ * so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before ++ * or after an existing element or at the head of the list. A list ++ * may only be traversed in the forward direction. ++ * ++ * A simple queue is headed by a pair of pointers, one the head of the ++ * list and the other to the tail of the list. The elements are singly ++ * linked to save space, so elements can only be removed from the ++ * head of the list. New elements can be added to the list before or after ++ * an existing element, at the head of the list, or at the end of the ++ * list. A simple queue may only be traversed in the forward direction. ++ * ++ * A tail queue is headed by a pair of pointers, one to the head of the ++ * list and the other to the tail of the list. The elements are doubly ++ * linked so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before or ++ * after an existing element, at the head of the list, or at the end of ++ * the list. A tail queue may be traversed in either direction. ++ * ++ * A circle queue is headed by a pair of pointers, one to the head of the ++ * list and the other to the tail of the list. The elements are doubly ++ * linked so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before or after ++ * an existing element, at the head of the list, or at the end of the list. ++ * A circle queue may be traversed in either direction, but has a more ++ * complex end of list detection. ++ * ++ * For details on the use of these macros, see the queue(3) manual page. ++ */ ++ ++/* ++ * Double-linked List. ++ */ ++ ++typedef struct dwc_list_link { ++ struct dwc_list_link *next; ++ struct dwc_list_link *prev; ++} dwc_list_link_t; ++ ++#define DWC_LIST_INIT(link) do { \ ++ (link)->next = (link); \ ++ (link)->prev = (link); \ ++} while (0) ++ ++#define DWC_LIST_FIRST(link) ((link)->next) ++#define DWC_LIST_LAST(link) ((link)->prev) ++#define DWC_LIST_END(link) (link) ++#define DWC_LIST_NEXT(link) ((link)->next) ++#define DWC_LIST_PREV(link) ((link)->prev) ++#define DWC_LIST_EMPTY(link) \ ++ (DWC_LIST_FIRST(link) == DWC_LIST_END(link)) ++#define DWC_LIST_ENTRY(link, type, field) \ ++ (type *)((uint8_t *)(link) - (size_t)(&((type *)0)->field)) ++ ++#if 0 ++#define DWC_LIST_INSERT_HEAD(list, link) do { \ ++ (link)->next = (list)->next; \ ++ (link)->prev = (list); \ ++ (list)->next->prev = (link); \ ++ (list)->next = (link); \ ++} while (0) ++ ++#define DWC_LIST_INSERT_TAIL(list, link) do { \ ++ (link)->next = (list); \ ++ (link)->prev = (list)->prev; \ ++ (list)->prev->next = (link); \ ++ (list)->prev = (link); \ ++} while (0) ++#else ++#define DWC_LIST_INSERT_HEAD(list, link) do { \ ++ dwc_list_link_t *__next__ = (list)->next; \ ++ __next__->prev = (link); \ ++ (link)->next = __next__; \ ++ (link)->prev = (list); \ ++ (list)->next = (link); \ ++} while (0) ++ ++#define DWC_LIST_INSERT_TAIL(list, link) do { \ ++ dwc_list_link_t *__prev__ = (list)->prev; \ ++ (list)->prev = (link); \ ++ (link)->next = (list); \ ++ (link)->prev = __prev__; \ ++ __prev__->next = (link); \ ++} while (0) ++#endif ++ ++#if 0 ++static inline void __list_add(struct list_head *new, ++ struct list_head *prev, ++ struct list_head *next) ++{ ++ next->prev = new; ++ new->next = next; ++ new->prev = prev; ++ prev->next = new; ++} ++ ++static inline void list_add(struct list_head *new, struct list_head *head) ++{ ++ __list_add(new, head, head->next); ++} ++ ++static inline void list_add_tail(struct list_head *new, struct list_head *head) ++{ ++ __list_add(new, head->prev, head); ++} ++ ++static inline void __list_del(struct list_head * prev, struct list_head * next) ++{ ++ next->prev = prev; ++ prev->next = next; ++} ++ ++static inline void list_del(struct list_head *entry) ++{ ++ __list_del(entry->prev, entry->next); ++ entry->next = LIST_POISON1; ++ entry->prev = LIST_POISON2; ++} ++#endif ++ ++#define DWC_LIST_REMOVE(link) do { \ ++ (link)->next->prev = (link)->prev; \ ++ (link)->prev->next = (link)->next; \ ++} while (0) ++ ++#define DWC_LIST_REMOVE_INIT(link) do { \ ++ DWC_LIST_REMOVE(link); \ ++ DWC_LIST_INIT(link); \ ++} while (0) ++ ++#define DWC_LIST_MOVE_HEAD(list, link) do { \ ++ DWC_LIST_REMOVE(link); \ ++ DWC_LIST_INSERT_HEAD(list, link); \ ++} while (0) ++ ++#define DWC_LIST_MOVE_TAIL(list, link) do { \ ++ DWC_LIST_REMOVE(link); \ ++ DWC_LIST_INSERT_TAIL(list, link); \ ++} while (0) ++ ++#define DWC_LIST_FOREACH(var, list) \ ++ for((var) = DWC_LIST_FIRST(list); \ ++ (var) != DWC_LIST_END(list); \ ++ (var) = DWC_LIST_NEXT(var)) ++ ++#define DWC_LIST_FOREACH_SAFE(var, var2, list) \ ++ for((var) = DWC_LIST_FIRST(list), (var2) = DWC_LIST_NEXT(var); \ ++ (var) != DWC_LIST_END(list); \ ++ (var) = (var2), (var2) = DWC_LIST_NEXT(var2)) ++ ++#define DWC_LIST_FOREACH_REVERSE(var, list) \ ++ for((var) = DWC_LIST_LAST(list); \ ++ (var) != DWC_LIST_END(list); \ ++ (var) = DWC_LIST_PREV(var)) ++ ++/* ++ * Singly-linked List definitions. ++ */ ++#define DWC_SLIST_HEAD(name, type) \ ++struct name { \ ++ struct type *slh_first; /* first element */ \ ++} ++ ++#define DWC_SLIST_HEAD_INITIALIZER(head) \ ++ { NULL } ++ ++#define DWC_SLIST_ENTRY(type) \ ++struct { \ ++ struct type *sle_next; /* next element */ \ ++} ++ ++/* ++ * Singly-linked List access methods. ++ */ ++#define DWC_SLIST_FIRST(head) ((head)->slh_first) ++#define DWC_SLIST_END(head) NULL ++#define DWC_SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) ++#define DWC_SLIST_NEXT(elm, field) ((elm)->field.sle_next) ++ ++#define DWC_SLIST_FOREACH(var, head, field) \ ++ for((var) = SLIST_FIRST(head); \ ++ (var) != SLIST_END(head); \ ++ (var) = SLIST_NEXT(var, field)) ++ ++#define DWC_SLIST_FOREACH_PREVPTR(var, varp, head, field) \ ++ for((varp) = &SLIST_FIRST((head)); \ ++ ((var) = *(varp)) != SLIST_END(head); \ ++ (varp) = &SLIST_NEXT((var), field)) ++ ++/* ++ * Singly-linked List functions. ++ */ ++#define DWC_SLIST_INIT(head) { \ ++ SLIST_FIRST(head) = SLIST_END(head); \ ++} ++ ++#define DWC_SLIST_INSERT_AFTER(slistelm, elm, field) do { \ ++ (elm)->field.sle_next = (slistelm)->field.sle_next; \ ++ (slistelm)->field.sle_next = (elm); \ ++} while (0) ++ ++#define DWC_SLIST_INSERT_HEAD(head, elm, field) do { \ ++ (elm)->field.sle_next = (head)->slh_first; \ ++ (head)->slh_first = (elm); \ ++} while (0) ++ ++#define DWC_SLIST_REMOVE_NEXT(head, elm, field) do { \ ++ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ ++} while (0) ++ ++#define DWC_SLIST_REMOVE_HEAD(head, field) do { \ ++ (head)->slh_first = (head)->slh_first->field.sle_next; \ ++} while (0) ++ ++#define DWC_SLIST_REMOVE(head, elm, type, field) do { \ ++ if ((head)->slh_first == (elm)) { \ ++ SLIST_REMOVE_HEAD((head), field); \ ++ } \ ++ else { \ ++ struct type *curelm = (head)->slh_first; \ ++ while( curelm->field.sle_next != (elm) ) \ ++ curelm = curelm->field.sle_next; \ ++ curelm->field.sle_next = \ ++ curelm->field.sle_next->field.sle_next; \ ++ } \ ++} while (0) ++ ++/* ++ * Simple queue definitions. ++ */ ++#define DWC_SIMPLEQ_HEAD(name, type) \ ++struct name { \ ++ struct type *sqh_first; /* first element */ \ ++ struct type **sqh_last; /* addr of last next element */ \ ++} ++ ++#define DWC_SIMPLEQ_HEAD_INITIALIZER(head) \ ++ { NULL, &(head).sqh_first } ++ ++#define DWC_SIMPLEQ_ENTRY(type) \ ++struct { \ ++ struct type *sqe_next; /* next element */ \ ++} ++ ++/* ++ * Simple queue access methods. ++ */ ++#define DWC_SIMPLEQ_FIRST(head) ((head)->sqh_first) ++#define DWC_SIMPLEQ_END(head) NULL ++#define DWC_SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) ++#define DWC_SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) ++ ++#define DWC_SIMPLEQ_FOREACH(var, head, field) \ ++ for((var) = SIMPLEQ_FIRST(head); \ ++ (var) != SIMPLEQ_END(head); \ ++ (var) = SIMPLEQ_NEXT(var, field)) ++ ++/* ++ * Simple queue functions. ++ */ ++#define DWC_SIMPLEQ_INIT(head) do { \ ++ (head)->sqh_first = NULL; \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++} while (0) ++ ++#define DWC_SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ (head)->sqh_first = (elm); \ ++} while (0) ++ ++#define DWC_SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.sqe_next = NULL; \ ++ *(head)->sqh_last = (elm); \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++} while (0) ++ ++#define DWC_SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ (listelm)->field.sqe_next = (elm); \ ++} while (0) ++ ++#define DWC_SIMPLEQ_REMOVE_HEAD(head, field) do { \ ++ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++} while (0) ++ ++/* ++ * Tail queue definitions. ++ */ ++#define DWC_TAILQ_HEAD(name, type) \ ++struct name { \ ++ struct type *tqh_first; /* first element */ \ ++ struct type **tqh_last; /* addr of last next element */ \ ++} ++ ++#define DWC_TAILQ_HEAD_INITIALIZER(head) \ ++ { NULL, &(head).tqh_first } ++ ++#define DWC_TAILQ_ENTRY(type) \ ++struct { \ ++ struct type *tqe_next; /* next element */ \ ++ struct type **tqe_prev; /* address of previous next element */ \ ++} ++ ++/* ++ * tail queue access methods ++ */ ++#define DWC_TAILQ_FIRST(head) ((head)->tqh_first) ++#define DWC_TAILQ_END(head) NULL ++#define DWC_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) ++#define DWC_TAILQ_LAST(head, headname) \ ++ (*(((struct headname *)((head)->tqh_last))->tqh_last)) ++/* XXX */ ++#define DWC_TAILQ_PREV(elm, headname, field) \ ++ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) ++#define DWC_TAILQ_EMPTY(head) \ ++ (TAILQ_FIRST(head) == TAILQ_END(head)) ++ ++#define DWC_TAILQ_FOREACH(var, head, field) \ ++ for((var) = TAILQ_FIRST(head); \ ++ (var) != TAILQ_END(head); \ ++ (var) = TAILQ_NEXT(var, field)) ++ ++#define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \ ++ for((var) = TAILQ_LAST(head, headname); \ ++ (var) != TAILQ_END(head); \ ++ (var) = TAILQ_PREV(var, headname, field)) ++ ++/* ++ * Tail queue functions. ++ */ ++#define DWC_TAILQ_INIT(head) do { \ ++ (head)->tqh_first = NULL; \ ++ (head)->tqh_last = &(head)->tqh_first; \ ++} while (0) ++ ++#define DWC_TAILQ_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ ++ (head)->tqh_first->field.tqe_prev = \ ++ &(elm)->field.tqe_next; \ ++ else \ ++ (head)->tqh_last = &(elm)->field.tqe_next; \ ++ (head)->tqh_first = (elm); \ ++ (elm)->field.tqe_prev = &(head)->tqh_first; \ ++} while (0) ++ ++#define DWC_TAILQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.tqe_next = NULL; \ ++ (elm)->field.tqe_prev = (head)->tqh_last; \ ++ *(head)->tqh_last = (elm); \ ++ (head)->tqh_last = &(elm)->field.tqe_next; \ ++} while (0) ++ ++#define DWC_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ ++ (elm)->field.tqe_next->field.tqe_prev = \ ++ &(elm)->field.tqe_next; \ ++ else \ ++ (head)->tqh_last = &(elm)->field.tqe_next; \ ++ (listelm)->field.tqe_next = (elm); \ ++ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ ++} while (0) ++ ++#define DWC_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ ++ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ ++ (elm)->field.tqe_next = (listelm); \ ++ *(listelm)->field.tqe_prev = (elm); \ ++ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ ++} while (0) ++ ++#define DWC_TAILQ_REMOVE(head, elm, field) do { \ ++ if (((elm)->field.tqe_next) != NULL) \ ++ (elm)->field.tqe_next->field.tqe_prev = \ ++ (elm)->field.tqe_prev; \ ++ else \ ++ (head)->tqh_last = (elm)->field.tqe_prev; \ ++ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ ++} while (0) ++ ++#define DWC_TAILQ_REPLACE(head, elm, elm2, field) do { \ ++ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ ++ (elm2)->field.tqe_next->field.tqe_prev = \ ++ &(elm2)->field.tqe_next; \ ++ else \ ++ (head)->tqh_last = &(elm2)->field.tqe_next; \ ++ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ ++ *(elm2)->field.tqe_prev = (elm2); \ ++} while (0) ++ ++/* ++ * Circular queue definitions. ++ */ ++#define DWC_CIRCLEQ_HEAD(name, type) \ ++struct name { \ ++ struct type *cqh_first; /* first element */ \ ++ struct type *cqh_last; /* last element */ \ ++} ++ ++#define DWC_CIRCLEQ_HEAD_INITIALIZER(head) \ ++ { DWC_CIRCLEQ_END(&head), DWC_CIRCLEQ_END(&head) } ++ ++#define DWC_CIRCLEQ_ENTRY(type) \ ++struct { \ ++ struct type *cqe_next; /* next element */ \ ++ struct type *cqe_prev; /* previous element */ \ ++} ++ ++/* ++ * Circular queue access methods ++ */ ++#define DWC_CIRCLEQ_FIRST(head) ((head)->cqh_first) ++#define DWC_CIRCLEQ_LAST(head) ((head)->cqh_last) ++#define DWC_CIRCLEQ_END(head) ((void *)(head)) ++#define DWC_CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) ++#define DWC_CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) ++#define DWC_CIRCLEQ_EMPTY(head) \ ++ (DWC_CIRCLEQ_FIRST(head) == DWC_CIRCLEQ_END(head)) ++ ++#define DWC_CIRCLEQ_EMPTY_ENTRY(elm, field) (((elm)->field.cqe_next == NULL) && ((elm)->field.cqe_prev == NULL)) ++ ++#define DWC_CIRCLEQ_FOREACH(var, head, field) \ ++ for((var) = DWC_CIRCLEQ_FIRST(head); \ ++ (var) != DWC_CIRCLEQ_END(head); \ ++ (var) = DWC_CIRCLEQ_NEXT(var, field)) ++ ++#define DWC_CIRCLEQ_FOREACH_SAFE(var, var2, head, field) \ ++ for((var) = DWC_CIRCLEQ_FIRST(head), var2 = DWC_CIRCLEQ_NEXT(var, field); \ ++ (var) != DWC_CIRCLEQ_END(head); \ ++ (var) = var2, var2 = DWC_CIRCLEQ_NEXT(var, field)) ++ ++#define DWC_CIRCLEQ_FOREACH_REVERSE(var, head, field) \ ++ for((var) = DWC_CIRCLEQ_LAST(head); \ ++ (var) != DWC_CIRCLEQ_END(head); \ ++ (var) = DWC_CIRCLEQ_PREV(var, field)) ++ ++/* ++ * Circular queue functions. ++ */ ++#define DWC_CIRCLEQ_INIT(head) do { \ ++ (head)->cqh_first = DWC_CIRCLEQ_END(head); \ ++ (head)->cqh_last = DWC_CIRCLEQ_END(head); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INIT_ENTRY(elm, field) do { \ ++ (elm)->field.cqe_next = NULL; \ ++ (elm)->field.cqe_prev = NULL; \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ ++ (elm)->field.cqe_prev = (listelm); \ ++ if ((listelm)->field.cqe_next == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_last = (elm); \ ++ else \ ++ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ ++ (listelm)->field.cqe_next = (elm); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ ++ (elm)->field.cqe_next = (listelm); \ ++ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ ++ if ((listelm)->field.cqe_prev == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_first = (elm); \ ++ else \ ++ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ ++ (listelm)->field.cqe_prev = (elm); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ ++ (elm)->field.cqe_next = (head)->cqh_first; \ ++ (elm)->field.cqe_prev = DWC_CIRCLEQ_END(head); \ ++ if ((head)->cqh_last == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_last = (elm); \ ++ else \ ++ (head)->cqh_first->field.cqe_prev = (elm); \ ++ (head)->cqh_first = (elm); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.cqe_next = DWC_CIRCLEQ_END(head); \ ++ (elm)->field.cqe_prev = (head)->cqh_last; \ ++ if ((head)->cqh_first == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_first = (elm); \ ++ else \ ++ (head)->cqh_last->field.cqe_next = (elm); \ ++ (head)->cqh_last = (elm); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_REMOVE(head, elm, field) do { \ ++ if ((elm)->field.cqe_next == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_last = (elm)->field.cqe_prev; \ ++ else \ ++ (elm)->field.cqe_next->field.cqe_prev = \ ++ (elm)->field.cqe_prev; \ ++ if ((elm)->field.cqe_prev == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_first = (elm)->field.cqe_next; \ ++ else \ ++ (elm)->field.cqe_prev->field.cqe_next = \ ++ (elm)->field.cqe_next; \ ++} while (0) ++ ++#define DWC_CIRCLEQ_REMOVE_INIT(head, elm, field) do { \ ++ DWC_CIRCLEQ_REMOVE(head, elm, field); \ ++ DWC_CIRCLEQ_INIT_ENTRY(elm, field); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ ++ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ ++ DWC_CIRCLEQ_END(head)) \ ++ (head).cqh_last = (elm2); \ ++ else \ ++ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ ++ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ ++ DWC_CIRCLEQ_END(head)) \ ++ (head).cqh_first = (elm2); \ ++ else \ ++ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ ++} while (0) ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_LIST_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_mem.c b/drivers/usb/gadget/udc/hiudc/dwc_mem.c +new file mode 100644 +index 0000000..ad645ff +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_mem.c +@@ -0,0 +1,245 @@ ++/* Memory Debugging */ ++#ifdef DWC_DEBUG_MEMORY ++ ++#include "dwc_os.h" ++#include "dwc_list.h" ++ ++struct allocation { ++ void *addr; ++ void *ctx; ++ char *func; ++ int line; ++ uint32_t size; ++ int dma; ++ DWC_CIRCLEQ_ENTRY(allocation) entry; ++}; ++ ++DWC_CIRCLEQ_HEAD(allocation_queue, allocation); ++ ++struct allocation_manager { ++ void *mem_ctx; ++ struct allocation_queue allocations; ++ ++ /* statistics */ ++ int num; ++ int num_freed; ++ int num_active; ++ uint32_t total; ++ uint32_t cur; ++ uint32_t max; ++}; ++ ++static struct allocation_manager *manager = NULL; ++ ++static int add_allocation(void *ctx, uint32_t size, char const *func, int line, void *addr, ++ int dma) ++{ ++ struct allocation *a; ++ ++ DWC_ASSERT(manager != NULL, "manager not allocated"); ++ ++ a = __DWC_ALLOC_ATOMIC(manager->mem_ctx, sizeof(*a)); ++ if (!a) { ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ a->func = __DWC_ALLOC_ATOMIC(manager->mem_ctx, DWC_STRLEN(func) + 1); ++ if (!a->func) { ++ __DWC_FREE(manager->mem_ctx, a); ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ DWC_MEMCPY(a->func, func, DWC_STRLEN(func) + 1); ++ a->addr = addr; ++ a->ctx = ctx; ++ a->line = line; ++ a->size = size; ++ a->dma = dma; ++ DWC_CIRCLEQ_INSERT_TAIL(&manager->allocations, a, entry); ++ ++ /* Update stats */ ++ manager->num++; ++ manager->num_active++; ++ manager->total += size; ++ manager->cur += size; ++ ++ if (manager->max < manager->cur) { ++ manager->max = manager->cur; ++ } ++ ++ return 0; ++} ++ ++static struct allocation *find_allocation(void *ctx, void *addr) ++{ ++ struct allocation *a; ++ ++ DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) { ++ if (a->ctx == ctx && a->addr == addr) { ++ return a; ++ } ++ } ++ ++ return NULL; ++} ++ ++static void free_allocation(void *ctx, void *addr, char const *func, int line) ++{ ++ struct allocation *a = find_allocation(ctx, addr); ++ ++ if (!a) { ++ DWC_ASSERT(0, ++ "Free of address %p that was never allocated or already freed %s:%d", ++ addr, func, line); ++ return; ++ } ++ ++ DWC_CIRCLEQ_REMOVE(&manager->allocations, a, entry); ++ ++ manager->num_active--; ++ manager->num_freed++; ++ manager->cur -= a->size; ++ __DWC_FREE(manager->mem_ctx, a->func); ++ __DWC_FREE(manager->mem_ctx, a); ++} ++ ++int dwc_memory_debug_start(void *mem_ctx) ++{ ++ DWC_ASSERT(manager == NULL, "Memory debugging has already started\n"); ++ ++ if (manager) { ++ return -DWC_E_BUSY; ++ } ++ ++ manager = __DWC_ALLOC(mem_ctx, sizeof(*manager)); ++ if (!manager) { ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ DWC_CIRCLEQ_INIT(&manager->allocations); ++ manager->mem_ctx = mem_ctx; ++ manager->num = 0; ++ manager->num_freed = 0; ++ manager->num_active = 0; ++ manager->total = 0; ++ manager->cur = 0; ++ manager->max = 0; ++ ++ return 0; ++} ++ ++void dwc_memory_debug_stop(void) ++{ ++ struct allocation *a; ++ ++ dwc_memory_debug_report(); ++ ++ DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) { ++ DWC_ERROR("Memory leaked from %s:%d\n", a->func, a->line); ++ free_allocation(a->ctx, a->addr, NULL, -1); ++ } ++ ++ __DWC_FREE(manager->mem_ctx, manager); ++} ++ ++void dwc_memory_debug_report(void) ++{ ++ struct allocation *a; ++ ++ DWC_PRINTF("\n\n\n----------------- Memory Debugging Report -----------------\n\n"); ++ DWC_PRINTF("Num Allocations = %d\n", manager->num); ++ DWC_PRINTF("Freed = %d\n", manager->num_freed); ++ DWC_PRINTF("Active = %d\n", manager->num_active); ++ DWC_PRINTF("Current Memory Used = %d\n", manager->cur); ++ DWC_PRINTF("Total Memory Used = %d\n", manager->total); ++ DWC_PRINTF("Maximum Memory Used at Once = %d\n", manager->max); ++ DWC_PRINTF("Unfreed allocations:\n"); ++ ++ DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) { ++ DWC_PRINTF(" addr=%p, size=%d from %s:%d, DMA=%d\n", ++ a->addr, a->size, a->func, a->line, a->dma); ++ } ++} ++ ++/* The replacement functions */ ++void *dwc_alloc_debug(void *mem_ctx, uint32_t size, char const *func, int line) ++{ ++ void *addr = __DWC_ALLOC(mem_ctx, size); ++ ++ if (!addr) { ++ return NULL; ++ } ++ ++ if (add_allocation(mem_ctx, size, func, line, addr, 0)) { ++ __DWC_FREE(mem_ctx, addr); ++ return NULL; ++ } ++ ++ return addr; ++} ++ ++void *dwc_alloc_atomic_debug(void *mem_ctx, uint32_t size, char const *func, ++ int line) ++{ ++ void *addr = __DWC_ALLOC_ATOMIC(mem_ctx, size); ++ ++ if (!addr) { ++ return NULL; ++ } ++ ++ if (add_allocation(mem_ctx, size, func, line, addr, 0)) { ++ __DWC_FREE(mem_ctx, addr); ++ return NULL; ++ } ++ ++ return addr; ++} ++ ++void dwc_free_debug(void *mem_ctx, void *addr, char const *func, int line) ++{ ++ free_allocation(mem_ctx, addr, func, line); ++ __DWC_FREE(mem_ctx, addr); ++} ++ ++void *dwc_dma_alloc_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr, ++ char const *func, int line) ++{ ++ void *addr = __DWC_DMA_ALLOC(dma_ctx, size, dma_addr); ++ ++ if (!addr) { ++ return NULL; ++ } ++ ++ if (add_allocation(dma_ctx, size, func, line, addr, 1)) { ++ __DWC_DMA_FREE(dma_ctx, size, addr, *dma_addr); ++ return NULL; ++ } ++ ++ return addr; ++} ++ ++void *dwc_dma_alloc_atomic_debug(void *dma_ctx, uint32_t size, ++ dwc_dma_t *dma_addr, char const *func, int line) ++{ ++ void *addr = __DWC_DMA_ALLOC_ATOMIC(dma_ctx, size, dma_addr); ++ ++ if (!addr) { ++ return NULL; ++ } ++ ++ if (add_allocation(dma_ctx, size, func, line, addr, 1)) { ++ __DWC_DMA_FREE(dma_ctx, size, addr, *dma_addr); ++ return NULL; ++ } ++ ++ return addr; ++} ++ ++void dwc_dma_free_debug(void *dma_ctx, uint32_t size, void *virt_addr, ++ dwc_dma_t dma_addr, char const *func, int line) ++{ ++ free_allocation(dma_ctx, virt_addr, func, line); ++ __DWC_DMA_FREE(dma_ctx, size, virt_addr, dma_addr); ++} ++ ++#endif /* DWC_DEBUG_MEMORY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_modpow.c b/drivers/usb/gadget/udc/hiudc/dwc_modpow.c +new file mode 100644 +index 0000000..307dbdf +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_modpow.c +@@ -0,0 +1,633 @@ ++/* Bignum routines adapted from PUTTY sources. PuTTY copyright notice follows. ++ * ++ * PuTTY is copyright 1997-2007 Simon Tatham. ++ * ++ * Portions copyright Robert de Bath, Joris van Rantwijk, Delian ++ * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, ++ * Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus ++ * Kuhn, and CORE SDI S.A. ++ * ++ * 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 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. ++ * ++ */ ++#ifdef DWC_CRYPTOLIB ++ ++#ifndef CONFIG_MACH_IPMATE ++ ++#include "dwc_modpow.h" ++ ++#define BIGNUM_INT_MASK 0xFFFFFFFFUL ++#define BIGNUM_TOP_BIT 0x80000000UL ++#define BIGNUM_INT_BITS 32 ++ ++ ++static void *snmalloc(void *mem_ctx, size_t n, size_t size) ++{ ++ void *p; ++ size *= n; ++ if (size == 0) size = 1; ++ p = dwc_alloc(mem_ctx, size); ++ return p; ++} ++ ++#define snewn(ctx, n, type) ((type *)snmalloc((ctx), (n), sizeof(type))) ++#define sfree dwc_free ++ ++/* ++ * Usage notes: ++ * * Do not call the DIVMOD_WORD macro with expressions such as array ++ * subscripts, as some implementations object to this (see below). ++ * * Note that none of the division methods below will cope if the ++ * quotient won't fit into BIGNUM_INT_BITS. Callers should be careful ++ * to avoid this case. ++ * If this condition occurs, in the case of the x86 DIV instruction, ++ * an overflow exception will occur, which (according to a correspondent) ++ * will manifest on Windows as something like ++ * 0xC0000095: Integer overflow ++ * The C variant won't give the right answer, either. ++ */ ++ ++#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2) ++ ++#if defined __GNUC__ && defined __i386__ ++#define DIVMOD_WORD(q, r, hi, lo, w) \ ++ __asm__("div %2" : \ ++ "=d" (r), "=a" (q) : \ ++ "r" (w), "d" (hi), "a" (lo)) ++#else ++#define DIVMOD_WORD(q, r, hi, lo, w) do { \ ++ BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \ ++ q = n / w; \ ++ r = n % w; \ ++} while (0) ++#endif ++ ++#define BIGNUM_INT_BYTES (BIGNUM_INT_BITS / 8) ++ ++#define BIGNUM_INTERNAL ++ ++static Bignum newbn(void *mem_ctx, int length) ++{ ++ Bignum b = snewn(mem_ctx, length + 1, BignumInt); ++ //if (!b) ++ //abort(); /* FIXME */ ++ DWC_MEMSET(b, 0, (length + 1) * sizeof(*b)); ++ b[0] = length; ++ return b; ++} ++ ++void freebn(void *mem_ctx, Bignum b) ++{ ++ /* ++ * Burn the evidence, just in case. ++ */ ++ DWC_MEMSET(b, 0, sizeof(b[0]) * (b[0] + 1)); ++ sfree(mem_ctx, b); ++} ++ ++/* ++ * Compute c = a * b. ++ * Input is in the first len words of a and b. ++ * Result is returned in the first 2*len words of c. ++ */ ++static void internal_mul(BignumInt *a, BignumInt *b, ++ BignumInt *c, int len) ++{ ++ int i, j; ++ BignumDblInt t; ++ ++ for (j = 0; j < 2 * len; j++) ++ c[j] = 0; ++ ++ for (i = len - 1; i >= 0; i--) { ++ t = 0; ++ for (j = len - 1; j >= 0; j--) { ++ t += MUL_WORD(a[i], (BignumDblInt) b[j]); ++ t += (BignumDblInt) c[i + j + 1]; ++ c[i + j + 1] = (BignumInt) t; ++ t = t >> BIGNUM_INT_BITS; ++ } ++ c[i] = (BignumInt) t; ++ } ++} ++ ++static void internal_add_shifted(BignumInt *number, ++ unsigned n, int shift) ++{ ++ int word = 1 + (shift / BIGNUM_INT_BITS); ++ int bshift = shift % BIGNUM_INT_BITS; ++ BignumDblInt addend; ++ ++ addend = (BignumDblInt)n << bshift; ++ ++ while (addend) { ++ addend += number[word]; ++ number[word] = (BignumInt) addend & BIGNUM_INT_MASK; ++ addend >>= BIGNUM_INT_BITS; ++ word++; ++ } ++} ++ ++/* ++ * Compute a = a % m. ++ * Input in first alen words of a and first mlen words of m. ++ * Output in first alen words of a ++ * (of which first alen-mlen words will be zero). ++ * The MSW of m MUST have its high bit set. ++ * Quotient is accumulated in the `quotient' array, which is a Bignum ++ * rather than the internal bigendian format. Quotient parts are shifted ++ * left by `qshift' before adding into quot. ++ */ ++static void internal_mod(BignumInt *a, int alen, ++ BignumInt *m, int mlen, ++ BignumInt *quot, int qshift) ++{ ++ BignumInt m0, m1; ++ unsigned int h; ++ int i, k; ++ ++ m0 = m[0]; ++ if (mlen > 1) ++ m1 = m[1]; ++ else ++ m1 = 0; ++ ++ for (i = 0; i <= alen - mlen; i++) { ++ BignumDblInt t; ++ unsigned int q, r, c, ai1; ++ ++ if (i == 0) { ++ h = 0; ++ } else { ++ h = a[i - 1]; ++ a[i - 1] = 0; ++ } ++ ++ if (i == alen - 1) ++ ai1 = 0; ++ else ++ ai1 = a[i + 1]; ++ ++ /* Find q = h:a[i] / m0 */ ++ if (h >= m0) { ++ /* ++ * Special case. ++ * ++ * To illustrate it, suppose a BignumInt is 8 bits, and ++ * we are dividing (say) A1:23:45:67 by A1:B2:C3. Then ++ * our initial division will be 0xA123 / 0xA1, which ++ * will give a quotient of 0x100 and a divide overflow. ++ * However, the invariants in this division algorithm ++ * are not violated, since the full number A1:23:... is ++ * _less_ than the quotient prefix A1:B2:... and so the ++ * following correction loop would have sorted it out. ++ * ++ * In this situation we set q to be the largest ++ * quotient we _can_ stomach (0xFF, of course). ++ */ ++ q = BIGNUM_INT_MASK; ++ } else { ++ /* Macro doesn't want an array subscript expression passed ++ * into it (see definition), so use a temporary. */ ++ BignumInt tmplo = a[i]; ++ DIVMOD_WORD(q, r, h, tmplo, m0); ++ ++ /* Refine our estimate of q by looking at ++ h:a[i]:a[i+1] / m0:m1 */ ++ t = MUL_WORD(m1, q); ++ if (t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) { ++ q--; ++ t -= m1; ++ r = (r + m0) & BIGNUM_INT_MASK; /* overflow? */ ++ if (r >= (BignumDblInt) m0 && ++ t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) q--; ++ } ++ } ++ ++ /* Subtract q * m from a[i...] */ ++ c = 0; ++ for (k = mlen - 1; k >= 0; k--) { ++ t = MUL_WORD(q, m[k]); ++ t += c; ++ c = (unsigned)(t >> BIGNUM_INT_BITS); ++ if ((BignumInt) t > a[i + k]) ++ c++; ++ a[i + k] -= (BignumInt) t; ++ } ++ ++ /* Add back m in case of borrow */ ++ if (c != h) { ++ t = 0; ++ for (k = mlen - 1; k >= 0; k--) { ++ t += m[k]; ++ t += a[i + k]; ++ a[i + k] = (BignumInt) t; ++ t = t >> BIGNUM_INT_BITS; ++ } ++ q--; ++ } ++ if (quot) ++ internal_add_shifted(quot, q, qshift + BIGNUM_INT_BITS * (alen - mlen - i)); ++ } ++} ++ ++/* ++ * Compute p % mod. ++ * The most significant word of mod MUST be non-zero. ++ * We assume that the result array is the same size as the mod array. ++ * We optionally write out a quotient if `quotient' is non-NULL. ++ * We can avoid writing out the result if `result' is NULL. ++ */ ++void bigdivmod(void *mem_ctx, Bignum p, Bignum mod, Bignum result, Bignum quotient) ++{ ++ BignumInt *n, *m; ++ int mshift; ++ int plen, mlen, i, j; ++ ++ /* Allocate m of size mlen, copy mod to m */ ++ /* We use big endian internally */ ++ mlen = mod[0]; ++ m = snewn(mem_ctx, mlen, BignumInt); ++ //if (!m) ++ //abort(); /* FIXME */ ++ for (j = 0; j < mlen; j++) ++ m[j] = mod[mod[0] - j]; ++ ++ /* Shift m left to make msb bit set */ ++ for (mshift = 0; mshift < BIGNUM_INT_BITS-1; mshift++) ++ if ((m[0] << mshift) & BIGNUM_TOP_BIT) ++ break; ++ if (mshift) { ++ for (i = 0; i < mlen - 1; i++) ++ m[i] = (m[i] << mshift) | (m[i + 1] >> (BIGNUM_INT_BITS - mshift)); ++ m[mlen - 1] = m[mlen - 1] << mshift; ++ } ++ ++ plen = p[0]; ++ /* Ensure plen > mlen */ ++ if (plen <= mlen) ++ plen = mlen + 1; ++ ++ /* Allocate n of size plen, copy p to n */ ++ n = snewn(mem_ctx, plen, BignumInt); ++ //if (!n) ++ //abort(); /* FIXME */ ++ for (j = 0; j < plen; j++) ++ n[j] = 0; ++ for (j = 1; j <= (int)p[0]; j++) ++ n[plen - j] = p[j]; ++ ++ /* Main computation */ ++ internal_mod(n, plen, m, mlen, quotient, mshift); ++ ++ /* Fixup result in case the modulus was shifted */ ++ if (mshift) { ++ for (i = plen - mlen - 1; i < plen - 1; i++) ++ n[i] = (n[i] << mshift) | (n[i + 1] >> (BIGNUM_INT_BITS - mshift)); ++ n[plen - 1] = n[plen - 1] << mshift; ++ internal_mod(n, plen, m, mlen, quotient, 0); ++ for (i = plen - 1; i >= plen - mlen; i--) ++ n[i] = (n[i] >> mshift) | (n[i - 1] << (BIGNUM_INT_BITS - mshift)); ++ } ++ ++ /* Copy result to buffer */ ++ if (result) { ++ for (i = 1; i <= (int)result[0]; i++) { ++ int j = plen - i; ++ result[i] = j >= 0 ? n[j] : 0; ++ } ++ } ++ ++ /* Free temporary arrays */ ++ for (i = 0; i < mlen; i++) ++ m[i] = 0; ++ sfree(mem_ctx, m); ++ for (i = 0; i < plen; i++) ++ n[i] = 0; ++ sfree(mem_ctx, n); ++} ++ ++/* ++ * Simple remainder. ++ */ ++Bignum bigmod(void *mem_ctx, Bignum a, Bignum b) ++{ ++ Bignum r = newbn(mem_ctx, b[0]); ++ bigdivmod(mem_ctx, a, b, r, NULL); ++ return r; ++} ++ ++/* ++ * Compute (base ^ exp) % mod. ++ */ ++Bignum dwc_modpow(void *mem_ctx, Bignum base_in, Bignum exp, Bignum mod) ++{ ++ BignumInt *a, *b, *n, *m; ++ int mshift; ++ int mlen, i, j; ++ Bignum base, result; ++ ++ /* ++ * The most significant word of mod needs to be non-zero. It ++ * should already be, but let's make sure. ++ */ ++ //assert(mod[mod[0]] != 0); ++ ++ /* ++ * Make sure the base is smaller than the modulus, by reducing ++ * it modulo the modulus if not. ++ */ ++ base = bigmod(mem_ctx, base_in, mod); ++ ++ /* Allocate m of size mlen, copy mod to m */ ++ /* We use big endian internally */ ++ mlen = mod[0]; ++ m = snewn(mem_ctx, mlen, BignumInt); ++ //if (!m) ++ //abort(); /* FIXME */ ++ for (j = 0; j < mlen; j++) ++ m[j] = mod[mod[0] - j]; ++ ++ /* Shift m left to make msb bit set */ ++ for (mshift = 0; mshift < BIGNUM_INT_BITS - 1; mshift++) ++ if ((m[0] << mshift) & BIGNUM_TOP_BIT) ++ break; ++ if (mshift) { ++ for (i = 0; i < mlen - 1; i++) ++ m[i] = ++ (m[i] << mshift) | (m[i + 1] >> ++ (BIGNUM_INT_BITS - mshift)); ++ m[mlen - 1] = m[mlen - 1] << mshift; ++ } ++ ++ /* Allocate n of size mlen, copy base to n */ ++ n = snewn(mem_ctx, mlen, BignumInt); ++ //if (!n) ++ //abort(); /* FIXME */ ++ i = mlen - base[0]; ++ for (j = 0; j < i; j++) ++ n[j] = 0; ++ for (j = 0; j < base[0]; j++) ++ n[i + j] = base[base[0] - j]; ++ ++ /* Allocate a and b of size 2*mlen. Set a = 1 */ ++ a = snewn(mem_ctx, 2 * mlen, BignumInt); ++ //if (!a) ++ //abort(); /* FIXME */ ++ b = snewn(mem_ctx, 2 * mlen, BignumInt); ++ //if (!b) ++ //abort(); /* FIXME */ ++ for (i = 0; i < 2 * mlen; i++) ++ a[i] = 0; ++ a[2 * mlen - 1] = 1; ++ ++ /* Skip leading zero bits of exp. */ ++ i = 0; ++ j = BIGNUM_INT_BITS - 1; ++ while (i < exp[0] && (exp[exp[0] - i] & (1 << j)) == 0) { ++ j--; ++ if (j < 0) { ++ i++; ++ j = BIGNUM_INT_BITS - 1; ++ } ++ } ++ ++ /* Main computation */ ++ while (i < exp[0]) { ++ while (j >= 0) { ++ internal_mul(a + mlen, a + mlen, b, mlen); ++ internal_mod(b, mlen * 2, m, mlen, NULL, 0); ++ if ((exp[exp[0] - i] & (1 << j)) != 0) { ++ internal_mul(b + mlen, n, a, mlen); ++ internal_mod(a, mlen * 2, m, mlen, NULL, 0); ++ } else { ++ BignumInt *t; ++ t = a; ++ a = b; ++ b = t; ++ } ++ j--; ++ } ++ i++; ++ j = BIGNUM_INT_BITS - 1; ++ } ++ ++ /* Fixup result in case the modulus was shifted */ ++ if (mshift) { ++ for (i = mlen - 1; i < 2 * mlen - 1; i++) ++ a[i] = ++ (a[i] << mshift) | (a[i + 1] >> ++ (BIGNUM_INT_BITS - mshift)); ++ a[2 * mlen - 1] = a[2 * mlen - 1] << mshift; ++ internal_mod(a, mlen * 2, m, mlen, NULL, 0); ++ for (i = 2 * mlen - 1; i >= mlen; i--) ++ a[i] = ++ (a[i] >> mshift) | (a[i - 1] << ++ (BIGNUM_INT_BITS - mshift)); ++ } ++ ++ /* Copy result to buffer */ ++ result = newbn(mem_ctx, mod[0]); ++ for (i = 0; i < mlen; i++) ++ result[result[0] - i] = a[i + mlen]; ++ while (result[0] > 1 && result[result[0]] == 0) ++ result[0]--; ++ ++ /* Free temporary arrays */ ++ for (i = 0; i < 2 * mlen; i++) ++ a[i] = 0; ++ sfree(mem_ctx, a); ++ for (i = 0; i < 2 * mlen; i++) ++ b[i] = 0; ++ sfree(mem_ctx, b); ++ for (i = 0; i < mlen; i++) ++ m[i] = 0; ++ sfree(mem_ctx, m); ++ for (i = 0; i < mlen; i++) ++ n[i] = 0; ++ sfree(mem_ctx, n); ++ ++ freebn(mem_ctx, base); ++ ++ return result; ++} ++ ++ ++#ifdef UNITTEST ++ ++static __u32 dh_p[] = { ++ 96, ++ 0xFFFFFFFF, ++ 0xFFFFFFFF, ++ 0xA93AD2CA, ++ 0x4B82D120, ++ 0xE0FD108E, ++ 0x43DB5BFC, ++ 0x74E5AB31, ++ 0x08E24FA0, ++ 0xBAD946E2, ++ 0x770988C0, ++ 0x7A615D6C, ++ 0xBBE11757, ++ 0x177B200C, ++ 0x521F2B18, ++ 0x3EC86A64, ++ 0xD8760273, ++ 0xD98A0864, ++ 0xF12FFA06, ++ 0x1AD2EE6B, ++ 0xCEE3D226, ++ 0x4A25619D, ++ 0x1E8C94E0, ++ 0xDB0933D7, ++ 0xABF5AE8C, ++ 0xA6E1E4C7, ++ 0xB3970F85, ++ 0x5D060C7D, ++ 0x8AEA7157, ++ 0x58DBEF0A, ++ 0xECFB8504, ++ 0xDF1CBA64, ++ 0xA85521AB, ++ 0x04507A33, ++ 0xAD33170D, ++ 0x8AAAC42D, ++ 0x15728E5A, ++ 0x98FA0510, ++ 0x15D22618, ++ 0xEA956AE5, ++ 0x3995497C, ++ 0x95581718, ++ 0xDE2BCBF6, ++ 0x6F4C52C9, ++ 0xB5C55DF0, ++ 0xEC07A28F, ++ 0x9B2783A2, ++ 0x180E8603, ++ 0xE39E772C, ++ 0x2E36CE3B, ++ 0x32905E46, ++ 0xCA18217C, ++ 0xF1746C08, ++ 0x4ABC9804, ++ 0x670C354E, ++ 0x7096966D, ++ 0x9ED52907, ++ 0x208552BB, ++ 0x1C62F356, ++ 0xDCA3AD96, ++ 0x83655D23, ++ 0xFD24CF5F, ++ 0x69163FA8, ++ 0x1C55D39A, ++ 0x98DA4836, ++ 0xA163BF05, ++ 0xC2007CB8, ++ 0xECE45B3D, ++ 0x49286651, ++ 0x7C4B1FE6, ++ 0xAE9F2411, ++ 0x5A899FA5, ++ 0xEE386BFB, ++ 0xF406B7ED, ++ 0x0BFF5CB6, ++ 0xA637ED6B, ++ 0xF44C42E9, ++ 0x625E7EC6, ++ 0xE485B576, ++ 0x6D51C245, ++ 0x4FE1356D, ++ 0xF25F1437, ++ 0x302B0A6D, ++ 0xCD3A431B, ++ 0xEF9519B3, ++ 0x8E3404DD, ++ 0x514A0879, ++ 0x3B139B22, ++ 0x020BBEA6, ++ 0x8A67CC74, ++ 0x29024E08, ++ 0x80DC1CD1, ++ 0xC4C6628B, ++ 0x2168C234, ++ 0xC90FDAA2, ++ 0xFFFFFFFF, ++ 0xFFFFFFFF, ++}; ++ ++static __u32 dh_a[] = { ++ 8, ++ 0xdf367516, ++ 0x86459caa, ++ 0xe2d459a4, ++ 0xd910dae0, ++ 0x8a8b5e37, ++ 0x67ab31c6, ++ 0xf0b55ea9, ++ 0x440051d6, ++}; ++ ++static __u32 dh_b[] = { ++ 8, ++ 0xded92656, ++ 0xe07a048a, ++ 0x6fa452cd, ++ 0x2df89d30, ++ 0xc75f1b0f, ++ 0x8ce3578f, ++ 0x7980a324, ++ 0x5daec786, ++}; ++ ++static __u32 dh_g[] = { ++ 1, ++ 2, ++}; ++ ++int main(void) ++{ ++ int i; ++ __u32 *k; ++ k = dwc_modpow(NULL, dh_g, dh_a, dh_p); ++ ++ printf("\n\n"); ++ for (i=0; i<k[0]; i++) { ++ __u32 word32 = k[k[0] - i]; ++ __u16 l = word32 & 0xffff; ++ __u16 m = (word32 & 0xffff0000) >> 16; ++ printf("%04x %04x ", m, l); ++ if (!((i + 1)%13)) printf("\n"); ++ } ++ printf("\n\n"); ++ ++ if ((k[0] == 0x60) && (k[1] == 0x28e490e5) && (k[0x60] == 0x5a0d3d4e)) { ++ printf("PASS\n\n"); ++ } ++ else { ++ printf("FAIL\n\n"); ++ } ++ ++} ++ ++#endif /* UNITTEST */ ++ ++#endif /* CONFIG_MACH_IPMATE */ ++ ++#endif /*DWC_CRYPTOLIB */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_modpow.h b/drivers/usb/gadget/udc/hiudc/dwc_modpow.h +new file mode 100644 +index 0000000..64f00c2 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_modpow.h +@@ -0,0 +1,34 @@ ++/* ++ * dwc_modpow.h ++ * See dwc_modpow.c for license and changes ++ */ ++#ifndef _DWC_MODPOW_H ++#define _DWC_MODPOW_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "dwc_os.h" ++ ++/** @file ++ * ++ * This file defines the module exponentiation function which is only used ++ * internally by the DWC UWB modules for calculation of PKs during numeric ++ * association. The routine is taken from the PUTTY, an open source terminal ++ * emulator. The PUTTY License is preserved in the dwc_modpow.c file. ++ * ++ */ ++ ++typedef uint32_t BignumInt; ++typedef uint64_t BignumDblInt; ++typedef BignumInt *Bignum; ++ ++/* Compute modular exponentiaion */ ++extern Bignum dwc_modpow(void *mem_ctx, Bignum base_in, Bignum exp, Bignum mod); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _LINUX_BIGNUM_H */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_notifier.c b/drivers/usb/gadget/udc/hiudc/dwc_notifier.c +new file mode 100644 +index 0000000..d3dadce +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_notifier.c +@@ -0,0 +1,319 @@ ++#ifdef DWC_NOTIFYLIB ++ ++#include "dwc_notifier.h" ++#include "dwc_list.h" ++ ++typedef struct dwc_observer { ++ void *observer; ++ dwc_notifier_callback_t callback; ++ void *data; ++ char *notification; ++ DWC_CIRCLEQ_ENTRY(dwc_observer) list_entry; ++} observer_t; ++ ++DWC_CIRCLEQ_HEAD(observer_queue, dwc_observer); ++ ++typedef struct dwc_notifier { ++ void *mem_ctx; ++ void *object; ++ struct observer_queue observers; ++ DWC_CIRCLEQ_ENTRY(dwc_notifier) list_entry; ++} notifier_t; ++ ++DWC_CIRCLEQ_HEAD(notifier_queue, dwc_notifier); ++ ++typedef struct manager { ++ void *mem_ctx; ++ void *wkq_ctx; ++ dwc_workq_t *wq; ++// dwc_mutex_t *mutex; ++ struct notifier_queue notifiers; ++} manager_t; ++ ++static manager_t *manager = NULL; ++ ++static int create_manager(void *mem_ctx, void *wkq_ctx) ++{ ++ manager = dwc_alloc(mem_ctx, sizeof(manager_t)); ++ if (!manager) { ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ DWC_CIRCLEQ_INIT(&manager->notifiers); ++ ++ manager->wq = dwc_workq_alloc(wkq_ctx, "DWC Notification WorkQ"); ++ if (!manager->wq) { ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ return 0; ++} ++ ++static void free_manager(void) ++{ ++ dwc_workq_free(manager->wq); ++ ++ /* All notifiers must have unregistered themselves before this module ++ * can be removed. Hitting this assertion indicates a programmer ++ * error. */ ++ DWC_ASSERT(DWC_CIRCLEQ_EMPTY(&manager->notifiers), ++ "Notification manager being freed before all notifiers have been removed"); ++ dwc_free(manager->mem_ctx, manager); ++} ++ ++#ifdef DEBUG ++static void dump_manager(void) ++{ ++ notifier_t *n; ++ observer_t *o; ++ ++ DWC_ASSERT(manager, "Notification manager not found"); ++ ++ DWC_DEBUG("List of all notifiers and observers:\n"); ++ DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) { ++ DWC_DEBUG("Notifier %p has observers:\n", n->object); ++ DWC_CIRCLEQ_FOREACH(o, &n->observers, list_entry) { ++ DWC_DEBUG(" %p watching %s\n", o->observer, o->notification); ++ } ++ } ++} ++#else ++#define dump_manager(...) ++#endif ++ ++static observer_t *alloc_observer(void *mem_ctx, void *observer, char *notification, ++ dwc_notifier_callback_t callback, void *data) ++{ ++ observer_t *new_observer = dwc_alloc(mem_ctx, sizeof(observer_t)); ++ ++ if (!new_observer) { ++ return NULL; ++ } ++ ++ DWC_CIRCLEQ_INIT_ENTRY(new_observer, list_entry); ++ new_observer->observer = observer; ++ new_observer->notification = notification; ++ new_observer->callback = callback; ++ new_observer->data = data; ++ return new_observer; ++} ++ ++static void free_observer(void *mem_ctx, observer_t *observer) ++{ ++ dwc_free(mem_ctx, observer); ++} ++ ++static notifier_t *alloc_notifier(void *mem_ctx, void *object) ++{ ++ notifier_t *notifier; ++ ++ if (!object) { ++ return NULL; ++ } ++ ++ notifier = dwc_alloc(mem_ctx, sizeof(notifier_t)); ++ if (!notifier) { ++ return NULL; ++ } ++ ++ DWC_CIRCLEQ_INIT(¬ifier->observers); ++ DWC_CIRCLEQ_INIT_ENTRY(notifier, list_entry); ++ ++ notifier->mem_ctx = mem_ctx; ++ notifier->object = object; ++ return notifier; ++} ++ ++static void free_notifier(notifier_t *notifier) ++{ ++ observer_t *observer; ++ ++ DWC_CIRCLEQ_FOREACH(observer, ¬ifier->observers, list_entry) { ++ free_observer(notifier->mem_ctx, observer); ++ } ++ ++ dwc_free(notifier->mem_ctx, notifier); ++} ++ ++static notifier_t *find_notifier(void *object) ++{ ++ notifier_t *notifier; ++ ++ DWC_ASSERT(manager, "Notification manager not found"); ++ ++ if (!object) { ++ return NULL; ++ } ++ ++ DWC_CIRCLEQ_FOREACH(notifier, &manager->notifiers, list_entry) { ++ if (notifier->object == object) { ++ return notifier; ++ } ++ } ++ ++ return NULL; ++} ++ ++int dwc_alloc_notification_manager(void *mem_ctx, void *wkq_ctx) ++{ ++ return create_manager(mem_ctx, wkq_ctx); ++} ++ ++void dwc_free_notification_manager(void) ++{ ++ free_manager(); ++} ++ ++dwc_notifier_t *dwc_register_notifier(void *mem_ctx, void *object) ++{ ++ notifier_t *notifier; ++ ++ DWC_ASSERT(manager, "Notification manager not found"); ++ ++ notifier = find_notifier(object); ++ if (notifier) { ++ DWC_ERROR("Notifier %p is already registered\n", object); ++ return NULL; ++ } ++ ++ notifier = alloc_notifier(mem_ctx, object); ++ if (!notifier) { ++ return NULL; ++ } ++ ++ DWC_CIRCLEQ_INSERT_TAIL(&manager->notifiers, notifier, list_entry); ++ ++ DWC_INFO("Notifier %p registered", object); ++ dump_manager(); ++ ++ return notifier; ++} ++ ++void dwc_unregister_notifier(dwc_notifier_t *notifier) ++{ ++ DWC_ASSERT(manager, "Notification manager not found"); ++ ++ if (!DWC_CIRCLEQ_EMPTY(¬ifier->observers)) { ++ observer_t *o; ++ ++ DWC_ERROR("Notifier %p has active observers when removing\n", notifier->object); ++ DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) { ++ DWC_DEBUG(" %p watching %s\n", o->observer, o->notification); ++ } ++ ++ DWC_ASSERT(DWC_CIRCLEQ_EMPTY(¬ifier->observers), ++ "Notifier %p has active observers when removing", notifier); ++ } ++ ++ DWC_CIRCLEQ_REMOVE_INIT(&manager->notifiers, notifier, list_entry); ++ free_notifier(notifier); ++ ++ DWC_INFO("Notifier unregistered"); ++ dump_manager(); ++} ++ ++/* Add an observer to observe the notifier for a particular state, event, or notification. */ ++int dwc_add_observer(void *observer, void *object, char *notification, ++ dwc_notifier_callback_t callback, void *data) ++{ ++ notifier_t *notifier = find_notifier(object); ++ observer_t *new_observer; ++ ++ if (!notifier) { ++ DWC_ERROR("Notifier %p is not found when adding observer\n", object); ++ return -DWC_E_INVALID; ++ } ++ ++ new_observer = alloc_observer(notifier->mem_ctx, observer, notification, callback, data); ++ if (!new_observer) { ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ DWC_CIRCLEQ_INSERT_TAIL(¬ifier->observers, new_observer, list_entry); ++ ++ DWC_INFO("Added observer %p to notifier %p observing notification %s, callback=%p, data=%p", ++ observer, object, notification, callback, data); ++ ++ dump_manager(); ++ return 0; ++} ++ ++int dwc_remove_observer(void *observer) ++{ ++ notifier_t *n; ++ ++ DWC_ASSERT(manager, "Notification manager not found"); ++ ++ DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) { ++ observer_t *o; ++ observer_t *o2; ++ ++ DWC_CIRCLEQ_FOREACH_SAFE(o, o2, &n->observers, list_entry) { ++ if (o->observer == observer) { ++ DWC_CIRCLEQ_REMOVE_INIT(&n->observers, o, list_entry); ++ DWC_INFO("Removing observer %p from notifier %p watching notification %s:", ++ o->observer, n->object, o->notification); ++ free_observer(n->mem_ctx, o); ++ } ++ } ++ } ++ ++ dump_manager(); ++ return 0; ++} ++ ++typedef struct callback_data { ++ void *mem_ctx; ++ dwc_notifier_callback_t cb; ++ void *observer; ++ void *data; ++ void *object; ++ char *notification; ++ void *notification_data; ++} cb_data_t; ++ ++static void cb_task(void *data) ++{ ++ cb_data_t *cb = (cb_data_t *)data; ++ ++ cb->cb(cb->object, cb->notification, cb->observer, cb->notification_data, cb->data); ++ dwc_free(cb->mem_ctx, cb); ++} ++ ++void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data) ++{ ++ observer_t *o; ++ ++ DWC_ASSERT(manager, "Notification manager not found"); ++ ++ DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) { ++ int len = DWC_STRLEN(notification); ++ ++ if (DWC_STRLEN(o->notification) != len) { ++ continue; ++ } ++ ++ if (DWC_STRNCMP(o->notification, notification, len) == 0) { ++ cb_data_t *cb_data = dwc_alloc(notifier->mem_ctx, sizeof(cb_data_t)); ++ ++ if (!cb_data) { ++ DWC_ERROR("Failed to allocate callback data\n"); ++ return; ++ } ++ ++ cb_data->mem_ctx = notifier->mem_ctx; ++ cb_data->cb = o->callback; ++ cb_data->observer = o->observer; ++ cb_data->data = o->data; ++ cb_data->object = notifier->object; ++ cb_data->notification = notification; ++ cb_data->notification_data = notification_data; ++ DWC_DEBUG("Observer found %p for notification %s\n", o->observer, notification); ++ DWC_WORKQ_SCHEDULE(manager->wq, cb_task, cb_data, ++ "Notify callback from %p for Notification %s, to observer %p", ++ cb_data->object, notification, cb_data->observer); ++ } ++ } ++} ++ ++#endif /* DWC_NOTIFYLIB */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_notifier.h b/drivers/usb/gadget/udc/hiudc/dwc_notifier.h +new file mode 100644 +index 0000000..4a8cdfe +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_notifier.h +@@ -0,0 +1,122 @@ ++ ++#ifndef __DWC_NOTIFIER_H__ ++#define __DWC_NOTIFIER_H__ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "dwc_os.h" ++ ++/** @file ++ * ++ * A simple implementation of the Observer pattern. Any "module" can ++ * register as an observer or notifier. The notion of "module" is abstract and ++ * can mean anything used to identify either an observer or notifier. Usually ++ * it will be a pointer to a data structure which contains some state, ie an ++ * object. ++ * ++ * Before any notifiers can be added, the global notification manager must be ++ * brought up with dwc_alloc_notification_manager(). ++ * dwc_free_notification_manager() will bring it down and free all resources. ++ * These would typically be called upon module load and unload. The ++ * notification manager is a single global instance that handles all registered ++ * observable modules and observers so this should be done only once. ++ * ++ * A module can be observable by using Notifications to publicize some general ++ * information about it's state or operation. It does not care who listens, or ++ * even if anyone listens, or what they do with the information. The observable ++ * modules do not need to know any information about it's observers or their ++ * interface, or their state or data. ++ * ++ * Any module can register to emit Notifications. It should publish a list of ++ * notifications that it can emit and their behavior, such as when they will get ++ * triggered, and what information will be provided to the observer. Then it ++ * should register itself as an observable module. See dwc_register_notifier(). ++ * ++ * Any module can observe any observable, registered module, provided it has a ++ * handle to the other module and knows what notifications to observe. See ++ * dwc_add_observer(). ++ * ++ * A function of type dwc_notifier_callback_t is called whenever a notification ++ * is triggered with one or more observers observing it. This function is ++ * called in it's own process so it may sleep or block if needed. It is ++ * guaranteed to be called sometime after the notification has occurred and will ++ * be called once per each time the notification is triggered. It will NOT be ++ * called in the same process context used to trigger the notification. ++ * ++ * @section Limitiations ++ * ++ * Keep in mind that Notifications that can be triggered in rapid sucession may ++ * schedule too many processes too handle. Be aware of this limitation when ++ * designing to use notifications, and only add notifications for appropriate ++ * observable information. ++ * ++ * Also Notification callbacks are not synchronous. If you need to synchronize ++ * the behavior between module/observer you must use other means. And perhaps ++ * that will mean Notifications are not the proper solution. ++ */ ++ ++struct dwc_notifier; ++typedef struct dwc_notifier dwc_notifier_t; ++ ++/** The callback function must be of this type. ++ * ++ * @param object This is the object that is being observed. ++ * @param notification This is the notification that was triggered. ++ * @param observer This is the observer ++ * @param notification_data This is notification-specific data that the notifier ++ * has included in this notification. The value of this should be published in ++ * the documentation of the observable module with the notifications. ++ * @param user_data This is any custom data that the observer provided when ++ * adding itself as an observer to the notification. */ ++typedef void (*dwc_notifier_callback_t)(void *object, char *notification, void *observer, ++ void *notification_data, void *user_data); ++ ++/** Brings up the notification manager. */ ++extern int dwc_alloc_notification_manager(void *mem_ctx, void *wkq_ctx); ++/** Brings down the notification manager. */ ++extern void dwc_free_notification_manager(void); ++ ++/** This function registers an observable module. A dwc_notifier_t object is ++ * returned to the observable module. This is an opaque object that is used by ++ * the observable module to trigger notifications. This object should only be ++ * accessible to functions that are authorized to trigger notifications for this ++ * module. Observers do not need this object. */ ++extern dwc_notifier_t *dwc_register_notifier(void *mem_ctx, void *object); ++ ++/** This function unregisters an observable module. All observers have to be ++ * removed prior to unregistration. */ ++extern void dwc_unregister_notifier(dwc_notifier_t *notifier); ++ ++/** Add a module as an observer to the observable module. The observable module ++ * needs to have previously registered with the notification manager. ++ * ++ * @param observer The observer module ++ * @param object The module to observe ++ * @param notification The notification to observe ++ * @param callback The callback function to call ++ * @param user_data Any additional user data to pass into the callback function */ ++extern int dwc_add_observer(void *observer, void *object, char *notification, ++ dwc_notifier_callback_t callback, void *user_data); ++ ++/** Removes the specified observer from all notifications that it is currently ++ * observing. */ ++extern int dwc_remove_observer(void *observer); ++ ++/** This function triggers a Notification. It should be called by the ++ * observable module, or any module or library which the observable module ++ * allows to trigger notification on it's behalf. Such as the dwc_cc_t. ++ * ++ * dwc_notify is a non-blocking function. Callbacks are scheduled called in ++ * their own process context for each trigger. Callbacks can be blocking. ++ * dwc_notify can be called from interrupt context if needed. ++ * ++ */ ++void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* __DWC_NOTIFIER_H__ */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_os.h b/drivers/usb/gadget/udc/hiudc/dwc_os.h +new file mode 100644 +index 0000000..a64c49a +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_os.h +@@ -0,0 +1,1237 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_os.h $ ++ * $Revision: #14 $ ++ * $Date: 2010/11/04 $ ++ * $Change: 1621695 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++#ifndef _DWC_OS_H_ ++#define _DWC_OS_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** @file ++ * ++ * DWC portability library, low level os-wrapper functions ++ * ++ */ ++ ++/* These basic types need to be defined by some OS header file or custom header ++ * file for your specific target architecture. ++ * ++ * uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t ++ * ++ * Any custom or alternate header file must be added and enabled here. ++ */ ++ ++#ifdef DWC_LINUX ++# include <linux/types.h> ++# ifdef CONFIG_DEBUG_MUTEXES ++# include <linux/mutex.h> ++# endif ++# include <linux/errno.h> ++# include <stdarg.h> ++#endif ++ ++#if defined(DWC_FREEBSD) || defined(DWC_NETBSD) ++# include <os_dep.h> ++#endif ++ ++ ++/** @name Primitive Types and Values */ ++ ++/** We define a boolean type for consistency. Can be either YES or NO */ ++typedef uint8_t dwc_bool_t; ++#define YES 1 ++#define NO 0 ++ ++#ifdef DWC_LINUX ++ ++/** @name Error Codes */ ++#define DWC_E_INVALID EINVAL ++#define DWC_E_NO_MEMORY ENOMEM ++#define DWC_E_NO_DEVICE ENODEV ++#define DWC_E_NOT_SUPPORTED EOPNOTSUPP ++#define DWC_E_TIMEOUT ETIMEDOUT ++#define DWC_E_BUSY EBUSY ++#define DWC_E_AGAIN EAGAIN ++#define DWC_E_RESTART ERESTART ++#define DWC_E_ABORT ECONNABORTED ++#define DWC_E_SHUTDOWN ESHUTDOWN ++#define DWC_E_NO_DATA ENODATA ++#define DWC_E_DISCONNECT ECONNRESET ++#define DWC_E_UNKNOWN EINVAL ++#define DWC_E_NO_STREAM_RES ENOSR ++#define DWC_E_COMMUNICATION ECOMM ++#define DWC_E_OVERFLOW EOVERFLOW ++#define DWC_E_PROTOCOL EPROTO ++#define DWC_E_IN_PROGRESS EINPROGRESS ++#define DWC_E_PIPE EPIPE ++#define DWC_E_IO EIO ++#define DWC_E_NO_SPACE ENOSPC ++ ++#else ++ ++/** @name Error Codes */ ++#define DWC_E_INVALID 1001 ++#define DWC_E_NO_MEMORY 1002 ++#define DWC_E_NO_DEVICE 1003 ++#define DWC_E_NOT_SUPPORTED 1004 ++#define DWC_E_TIMEOUT 1005 ++#define DWC_E_BUSY 1006 ++#define DWC_E_AGAIN 1007 ++#define DWC_E_RESTART 1008 ++#define DWC_E_ABORT 1009 ++#define DWC_E_SHUTDOWN 1010 ++#define DWC_E_NO_DATA 1011 ++#define DWC_E_DISCONNECT 2000 ++#define DWC_E_UNKNOWN 3000 ++#define DWC_E_NO_STREAM_RES 4001 ++#define DWC_E_COMMUNICATION 4002 ++#define DWC_E_OVERFLOW 4003 ++#define DWC_E_PROTOCOL 4004 ++#define DWC_E_IN_PROGRESS 4005 ++#define DWC_E_PIPE 4006 ++#define DWC_E_IO 4007 ++#define DWC_E_NO_SPACE 4008 ++ ++#endif ++ ++ ++/** @name Tracing/Logging Functions ++ * ++ * These function provide the capability to add tracing, debugging, and error ++ * messages, as well exceptions as assertions. The WUDEV uses these ++ * extensively. These could be logged to the main console, the serial port, an ++ * internal buffer, etc. These functions could also be no-op if they are too ++ * expensive on your system. By default undefining the DEBUG macro already ++ * no-ops some of these functions. */ ++ ++/** Returns non-zero if in interrupt context. */ ++extern dwc_bool_t DWC_IN_IRQ(void); ++#define dwc_in_irq DWC_IN_IRQ ++ ++/** Returns "IRQ" if DWC_IN_IRQ is true. */ ++static inline char *dwc_irq(void) { ++ return DWC_IN_IRQ() ? "IRQ" : ""; ++} ++ ++/** Returns non-zero if in bottom-half context. */ ++extern dwc_bool_t DWC_IN_BH(void); ++#define dwc_in_bh DWC_IN_BH ++ ++/** Returns "BH" if DWC_IN_BH is true. */ ++static inline char *dwc_bh(void) { ++ return DWC_IN_BH() ? "BH" : ""; ++} ++ ++/** ++ * A vprintf() clone. Just call vprintf if you've got it. ++ */ ++extern void DWC_VPRINTF(char *format, va_list args); ++#define dwc_vprintf DWC_VPRINTF ++ ++/** ++ * A vsnprintf() clone. Just call vprintf if you've got it. ++ */ ++extern int DWC_VSNPRINTF(char *str, int size, char *format, va_list args); ++#define dwc_vsnprintf DWC_VSNPRINTF ++ ++/** ++ * printf() clone. Just call printf if you've go it. ++ */ ++extern void DWC_PRINTF(char *format, ...) ++/* This provides compiler level static checking of the parameters if you're ++ * using GCC. */ ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 1, 2))); ++#else ++ ; ++#endif ++#define dwc_printf DWC_PRINTF ++ ++/** ++ * sprintf() clone. Just call sprintf if you've got it. ++ */ ++extern int DWC_SPRINTF(char *string, char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 2, 3))); ++#else ++ ; ++#endif ++#define dwc_sprintf DWC_SPRINTF ++ ++/** ++ * snprintf() clone. Just call snprintf if you've got it. ++ */ ++extern int DWC_SNPRINTF(char *string, int size, char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 3, 4))); ++#else ++ ; ++#endif ++#define dwc_snprintf DWC_SNPRINTF ++ ++/** ++ * Prints a WARNING message. On systems that don't differentiate between ++ * warnings and regular log messages, just print it. Indicates that something ++ * may be wrong with the driver. Works like printf(). ++ * ++ * Use the DWC_WARN macro to call this function. ++ */ ++extern void __DWC_WARN(char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 1, 2))); ++#else ++ ; ++#endif ++ ++/** ++ * Prints an error message. On systems that don't differentiate between errors ++ * and regular log messages, just print it. Indicates that something went wrong ++ * with the driver. Works like printf(). ++ * ++ * Use the DWC_ERROR macro to call this function. ++ */ ++extern void __DWC_ERROR(char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 1, 2))); ++#else ++ ; ++#endif ++ ++/** ++ * Prints an exception error message and takes some user-defined action such as ++ * print out a backtrace or trigger a breakpoint. Indicates that something went ++ * abnormally wrong with the driver such as programmer error, or other ++ * exceptional condition. It should not be ignored so even on systems without ++ * printing capability, some action should be taken to notify the developer of ++ * it. Works like printf(). ++ */ ++extern void DWC_EXCEPTION(char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 1, 2))); ++#else ++ ; ++#endif ++#define dwc_exception DWC_EXCEPTION ++ ++#ifdef DEBUG ++/** ++ * Prints out a debug message. Used for logging/trace messages. ++ * ++ * Use the DWC_DEBUG macro to call this function ++ */ ++extern void __DWC_DEBUG(char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 1, 2))); ++#else ++ ; ++#endif ++#else ++#define __DWC_DEBUG(...) ++#endif ++ ++/** ++ * Prints out a Debug message. ++ */ ++#define DWC_DEBUG(_format, _args...) __DWC_DEBUG("DEBUG:%s:%s: " _format "\n", \ ++ __func__, dwc_irq(), ## _args) ++#define dwc_debug DWC_DEBUG ++/** ++ * Prints out an informative message. ++ */ ++#define DWC_INFO(_format, _args...) DWC_PRINTF("INFO:%s: " _format "\n", \ ++ dwc_irq(), ## _args) ++#define dwc_info DWC_INFO ++/** ++ * Prints out a warning message. ++ */ ++#define DWC_WARN(_format, _args...) __DWC_WARN("WARN:%s:%s:%d: " _format "\n", \ ++ dwc_irq(), __func__, __LINE__, ## _args) ++#define dwc_warn DWC_WARN ++/** ++ * Prints out an error message. ++ */ ++#define DWC_ERROR(_format, _args...) __DWC_ERROR("ERROR:%s:%s:%d: " _format "\n", \ ++ dwc_irq(), __func__, __LINE__, ## _args) ++#define dwc_error DWC_ERROR ++ ++#define DWC_PROTO_ERROR(_format, _args...) __DWC_WARN("ERROR:%s:%s:%d: " _format "\n", \ ++ dwc_irq(), __func__, __LINE__, ## _args) ++#define dwc_proto_error DWC_PROTO_ERROR ++ ++#ifdef DEBUG ++/** Prints out a exception error message if the _expr expression fails. Disabled ++ * if DEBUG is not enabled. */ ++#define DWC_ASSERT(_expr, _format, _args...) do { \ ++ if (!(_expr)) { DWC_EXCEPTION("%s:%s:%d: " _format "\n", dwc_irq(), \ ++ __FILE__, __LINE__, ## _args); } \ ++ } while (0) ++#else ++#define DWC_ASSERT(_x...) ++#endif ++#define dwc_assert DWC_ASSERT ++ ++ ++/** @name Byte Ordering ++ * The following functions are for conversions between processor's byte ordering ++ * and specific ordering you want. ++ */ ++ ++/** Converts 32 bit data in CPU byte ordering to little endian. */ ++extern uint32_t DWC_CPU_TO_LE32(uint32_t *p); ++#define dwc_cpu_to_le32 DWC_CPU_TO_LE32 ++ ++/** Converts 32 bit data in CPU byte orderint to big endian. */ ++extern uint32_t DWC_CPU_TO_BE32(uint32_t *p); ++#define dwc_cpu_to_be32 DWC_CPU_TO_BE32 ++ ++/** Converts 32 bit little endian data to CPU byte ordering. */ ++extern uint32_t DWC_LE32_TO_CPU(uint32_t *p); ++#define dwc_le32_to_cpu DWC_LE32_TO_CPU ++ ++/** Converts 32 bit big endian data to CPU byte ordering. */ ++extern uint32_t DWC_BE32_TO_CPU(uint32_t *p); ++#define dwc_be32_to_cpu DWC_BE32_TO_CPU ++ ++/** Converts 16 bit data in CPU byte ordering to little endian. */ ++extern uint16_t DWC_CPU_TO_LE16(uint16_t *p); ++#define dwc_cpu_to_le16 DWC_CPU_TO_LE16 ++ ++/** Converts 16 bit data in CPU byte orderint to big endian. */ ++extern uint16_t DWC_CPU_TO_BE16(uint16_t *p); ++#define dwc_cpu_to_be16 DWC_CPU_TO_BE16 ++ ++/** Converts 16 bit little endian data to CPU byte ordering. */ ++extern uint16_t DWC_LE16_TO_CPU(uint16_t *p); ++#define dwc_le16_to_cpu DWC_LE16_TO_CPU ++ ++/** Converts 16 bit bi endian data to CPU byte ordering. */ ++extern uint16_t DWC_BE16_TO_CPU(uint16_t *p); ++#define dwc_be16_to_cpu DWC_BE16_TO_CPU ++ ++ ++/** @name Register Read/Write ++ * ++ * The following six functions should be implemented to read/write registers of ++ * 32-bit and 64-bit sizes. All modules use this to read/write register values. ++ * The reg value is a pointer to the register calculated from the void *base ++ * variable passed into the driver when it is started. */ ++ ++#ifdef DWC_LINUX ++/* Linux doesn't need any extra parameters for register read/write, so we ++ * just throw away the IO context parameter. ++ */ ++/** Reads the content of a 32-bit register. */ ++extern uint32_t DWC_READ_REG32(uint32_t volatile *reg); ++#define dwc_read_reg32(_ctx_,_reg_) DWC_READ_REG32(_reg_) ++ ++/** Reads the content of a 64-bit register. */ ++extern uint64_t DWC_READ_REG64(uint64_t volatile *reg); ++#define dwc_read_reg64(_ctx_,_reg_) DWC_READ_REG64(_reg_) ++ ++/** Writes to a 32-bit register. */ ++extern void DWC_WRITE_REG32(uint32_t volatile *reg, uint32_t value); ++#define dwc_write_reg32(_ctx_,_reg_,_val_) DWC_WRITE_REG32(_reg_, _val_) ++ ++/** Writes to a 64-bit register. */ ++extern void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value); ++#define dwc_write_reg64(_ctx_,_reg_,_val_) DWC_WRITE_REG64(_reg_, _val_) ++ ++/** ++ * Modify bit values in a register. Using the ++ * algorithm: (reg_contents & ~clear_mask) | set_mask. ++ */ ++extern void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask); ++#define dwc_modify_reg32(_ctx_,_reg_,_cmsk_,_smsk_) DWC_MODIFY_REG32(_reg_,_cmsk_,_smsk_) ++extern void DWC_MODIFY_REG64(uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask); ++#define dwc_modify_reg64(_ctx_,_reg_,_cmsk_,_smsk_) DWC_MODIFY_REG64(_reg_,_cmsk_,_smsk_) ++ ++#endif /* DWC_LINUX */ ++ ++#if defined(DWC_FREEBSD) || defined(DWC_NETBSD) ++typedef struct dwc_ioctx { ++ struct device *dev; ++ bus_space_tag_t iot; ++ bus_space_handle_t ioh; ++} dwc_ioctx_t; ++ ++/** BSD needs two extra parameters for register read/write, so we pass ++ * them in using the IO context parameter. ++ */ ++/** Reads the content of a 32-bit register. */ ++extern uint32_t DWC_READ_REG32(void *io_ctx, uint32_t volatile *reg); ++#define dwc_read_reg32 DWC_READ_REG32 ++ ++/** Reads the content of a 64-bit register. */ ++extern uint64_t DWC_READ_REG64(void *io_ctx, uint64_t volatile *reg); ++#define dwc_read_reg64 DWC_READ_REG64 ++ ++/** Writes to a 32-bit register. */ ++extern void DWC_WRITE_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t value); ++#define dwc_write_reg32 DWC_WRITE_REG32 ++ ++/** Writes to a 64-bit register. */ ++extern void DWC_WRITE_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t value); ++#define dwc_write_reg64 DWC_WRITE_REG64 ++ ++/** ++ * Modify bit values in a register. Using the ++ * algorithm: (reg_contents & ~clear_mask) | set_mask. ++ */ ++extern void DWC_MODIFY_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask); ++#define dwc_modify_reg32 DWC_MODIFY_REG32 ++extern void DWC_MODIFY_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask); ++#define dwc_modify_reg64 DWC_MODIFY_REG64 ++ ++#endif /* DWC_FREEBSD || DWC_NETBSD */ ++ ++/** @cond */ ++ ++/** @name Some convenience MACROS used internally. Define DWC_DEBUG_REGS to log the ++ * register writes. */ ++ ++#ifdef DWC_LINUX ++ ++# ifdef DWC_DEBUG_REGS ++ ++#define dwc_define_read_write_reg_n(_reg,_container_type) \ ++static inline uint32_t dwc_read_##_reg##_n(_container_type *container, int num) { \ ++ return DWC_READ_REG32(&container->regs->_reg[num]); \ ++} \ ++static inline void dwc_write_##_reg##_n(_container_type *container, int num, uint32_t data) { \ ++ DWC_DEBUG("WRITING %8s[%d]: %p: %08x", #_reg, num, \ ++ &(((uint32_t*)container->regs->_reg)[num]), data); \ ++ DWC_WRITE_REG32(&(((uint32_t*)container->regs->_reg)[num]), data); \ ++} ++ ++#define dwc_define_read_write_reg(_reg,_container_type) \ ++static inline uint32_t dwc_read_##_reg(_container_type *container) { \ ++ return DWC_READ_REG32(&container->regs->_reg); \ ++} \ ++static inline void dwc_write_##_reg(_container_type *container, uint32_t data) { \ ++ DWC_DEBUG("WRITING %11s: %p: %08x", #_reg, &container->regs->_reg, data); \ ++ DWC_WRITE_REG32(&container->regs->_reg, data); \ ++} ++ ++# else /* DWC_DEBUG_REGS */ ++ ++#define dwc_define_read_write_reg_n(_reg,_container_type) \ ++static inline uint32_t dwc_read_##_reg##_n(_container_type *container, int num) { \ ++ return DWC_READ_REG32(&container->regs->_reg[num]); \ ++} \ ++static inline void dwc_write_##_reg##_n(_container_type *container, int num, uint32_t data) { \ ++ DWC_WRITE_REG32(&(((uint32_t*)container->regs->_reg)[num]), data); \ ++} ++ ++#define dwc_define_read_write_reg(_reg,_container_type) \ ++static inline uint32_t dwc_read_##_reg(_container_type *container) { \ ++ return DWC_READ_REG32(&container->regs->_reg); \ ++} \ ++static inline void dwc_write_##_reg(_container_type *container, uint32_t data) { \ ++ DWC_WRITE_REG32(&container->regs->_reg, data); \ ++} ++ ++# endif /* DWC_DEBUG_REGS */ ++ ++#endif /* DWC_LINUX */ ++ ++#if defined(DWC_FREEBSD) || defined(DWC_NETBSD) ++ ++# ifdef DWC_DEBUG_REGS ++ ++#define dwc_define_read_write_reg_n(_reg,_container_type) \ ++static inline uint32_t dwc_read_##_reg##_n(void *io_ctx, _container_type *container, int num) { \ ++ return DWC_READ_REG32(io_ctx, &container->regs->_reg[num]); \ ++} \ ++static inline void dwc_write_##_reg##_n(void *io_ctx, _container_type *container, int num, uint32_t data) { \ ++ DWC_DEBUG("WRITING %8s[%d]: %p: %08x", #_reg, num, \ ++ &(((uint32_t*)container->regs->_reg)[num]), data); \ ++ DWC_WRITE_REG32(io_ctx, &(((uint32_t*)container->regs->_reg)[num]), data); \ ++} ++ ++#define dwc_define_read_write_reg(_reg,_container_type) \ ++static inline uint32_t dwc_read_##_reg(void *io_ctx, _container_type *container) { \ ++ return DWC_READ_REG32(io_ctx, &container->regs->_reg); \ ++} \ ++static inline void dwc_write_##_reg(void *io_ctx, _container_type *container, uint32_t data) { \ ++ DWC_DEBUG("WRITING %11s: %p: %08x", #_reg, &container->regs->_reg, data); \ ++ DWC_WRITE_REG32(io_ctx, &container->regs->_reg, data); \ ++} ++ ++# else /* DWC_DEBUG_REGS */ ++ ++#define dwc_define_read_write_reg_n(_reg,_container_type) \ ++static inline uint32_t dwc_read_##_reg##_n(void *io_ctx, _container_type *container, int num) { \ ++ return DWC_READ_REG32(io_ctx, &container->regs->_reg[num]); \ ++} \ ++static inline void dwc_write_##_reg##_n(void *io_ctx, _container_type *container, int num, uint32_t data) { \ ++ DWC_WRITE_REG32(io_ctx, &(((uint32_t*)container->regs->_reg)[num]), data); \ ++} ++ ++#define dwc_define_read_write_reg(_reg,_container_type) \ ++static inline uint32_t dwc_read_##_reg(void *io_ctx, _container_type *container) { \ ++ return DWC_READ_REG32(io_ctx, &container->regs->_reg); \ ++} \ ++static inline void dwc_write_##_reg(void *io_ctx, _container_type *container, uint32_t data) { \ ++ DWC_WRITE_REG32(io_ctx, &container->regs->_reg, data); \ ++} ++ ++# endif /* DWC_DEBUG_REGS */ ++ ++#endif /* DWC_FREEBSD || DWC_NETBSD */ ++ ++/** @endcond */ ++ ++ ++#ifdef DWC_CRYPTOLIB ++/** @name Crypto Functions ++ * ++ * These are the low-level cryptographic functions used by the driver. */ ++ ++/** Perform AES CBC */ ++extern int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out); ++#define dwc_aes_cbc DWC_AES_CBC ++ ++/** Fill the provided buffer with random bytes. These should be cryptographic grade random numbers. */ ++extern void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length); ++#define dwc_random_bytes DWC_RANDOM_BYTES ++ ++/** Perform the SHA-256 hash function */ ++extern int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out); ++#define dwc_sha256 DWC_SHA256 ++ ++/** Calculated the HMAC-SHA256 */ ++extern int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t *out); ++#define dwc_hmac_sha256 DWC_HMAC_SHA256 ++ ++#endif /* DWC_CRYPTOLIB */ ++ ++ ++/** @name Memory Allocation ++ * ++ * These function provide access to memory allocation. There are only 2 DMA ++ * functions and 3 Regular memory functions that need to be implemented. None ++ * of the memory debugging routines need to be implemented. The allocation ++ * routines all ZERO the contents of the memory. ++ * ++ * Defining DWC_DEBUG_MEMORY turns on memory debugging and statistic gathering. ++ * This checks for memory leaks, keeping track of alloc/free pairs. It also ++ * keeps track of how much memory the driver is using at any given time. */ ++ ++#define DWC_PAGE_SIZE 4096 ++#define DWC_PAGE_OFFSET(addr) (((uint32_t)addr) & 0xfff) ++#define DWC_PAGE_ALIGNED(addr) ((((uint32_t)addr) & 0xfff) == 0) ++ ++#define DWC_INVALID_DMA_ADDR 0x0 ++ ++#ifdef DWC_LINUX ++/** Type for a DMA address */ ++typedef dma_addr_t dwc_dma_t; ++#endif ++ ++#if defined(DWC_FREEBSD) || defined(DWC_NETBSD) ++typedef bus_addr_t dwc_dma_t; ++#endif ++ ++#ifdef DWC_FREEBSD ++typedef struct dwc_dmactx { ++ struct device *dev; ++ bus_dma_tag_t dma_tag; ++ bus_dmamap_t dma_map; ++ bus_addr_t dma_paddr; ++ void *dma_vaddr; ++} dwc_dmactx_t; ++#endif ++ ++#ifdef DWC_NETBSD ++typedef struct dwc_dmactx { ++ struct device *dev; ++ bus_dma_tag_t dma_tag; ++ bus_dmamap_t dma_map; ++ bus_dma_segment_t segs[1]; ++ int nsegs; ++ bus_addr_t dma_paddr; ++ void *dma_vaddr; ++} dwc_dmactx_t; ++#endif ++ ++/* @todo these functions will be added in the future */ ++#if 0 ++/** ++ * Creates a DMA pool from which you can allocate DMA buffers. Buffers ++ * allocated from this pool will be guaranteed to meet the size, alignment, and ++ * boundary requirements specified. ++ * ++ * @param[in] size Specifies the size of the buffers that will be allocated from ++ * this pool. ++ * @param[in] align Specifies the byte alignment requirements of the buffers ++ * allocated from this pool. Must be a power of 2. ++ * @param[in] boundary Specifies the N-byte boundary that buffers allocated from ++ * this pool must not cross. ++ * ++ * @returns A pointer to an internal opaque structure which is not to be ++ * accessed outside of these library functions. Use this handle to specify ++ * which pools to allocate/free DMA buffers from and also to destroy the pool, ++ * when you are done with it. ++ */ ++extern dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size, uint32_t align, uint32_t boundary); ++ ++/** ++ * Destroy a DMA pool. All buffers allocated from that pool must be freed first. ++ */ ++extern void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool); ++ ++/** ++ * Allocate a buffer from the specified DMA pool and zeros its contents. ++ */ ++extern void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr); ++ ++/** ++ * Free a previously allocated buffer from the DMA pool. ++ */ ++extern void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr); ++#endif ++ ++/** Allocates a DMA capable buffer and zeroes its contents. */ ++extern void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr); ++ ++/** Allocates a DMA capable buffer and zeroes its contents in atomic contest */ ++extern void *__DWC_DMA_ALLOC_ATOMIC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr); ++ ++/** Frees a previously allocated buffer. */ ++extern void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr); ++ ++/** Allocates a block of memory and zeroes its contents. */ ++extern void *__DWC_ALLOC(void *mem_ctx, uint32_t size); ++ ++/** Allocates a block of memory and zeroes its contents, in an atomic manner ++ * which can be used inside interrupt context. The size should be sufficiently ++ * small, a few KB at most, such that failures are not likely to occur. Can just call ++ * __DWC_ALLOC if it is atomic. */ ++extern void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size); ++ ++/** Frees a previously allocated buffer. */ ++extern void __DWC_FREE(void *mem_ctx, void *addr); ++ ++#ifndef DWC_DEBUG_MEMORY ++ ++#define DWC_ALLOC(_size_) __DWC_ALLOC(NULL, _size_) ++#define DWC_ALLOC_ATOMIC(_size_) __DWC_ALLOC_ATOMIC(NULL, _size_) ++#define DWC_FREE(_addr_) __DWC_FREE(NULL, _addr_) ++ ++# ifdef DWC_LINUX ++#define DWC_DMA_ALLOC(_size_,_dma_) __DWC_DMA_ALLOC(NULL, _size_, _dma_) ++#define DWC_DMA_ALLOC_ATOMIC(_size_,_dma_) __DWC_DMA_ALLOC_ATOMIC(NULL, _size_,_dma_) ++#define DWC_DMA_FREE(_size_,_virt_,_dma_) __DWC_DMA_FREE(NULL, _size_, _virt_, _dma_) ++# endif ++ ++# if defined(DWC_FREEBSD) || defined(DWC_NETBSD) ++#define DWC_DMA_ALLOC __DWC_DMA_ALLOC ++#define DWC_DMA_FREE __DWC_DMA_FREE ++# endif ++ ++#else /* DWC_DEBUG_MEMORY */ ++ ++extern void *dwc_alloc_debug(void *mem_ctx, uint32_t size, char const *func, int line); ++extern void *dwc_alloc_atomic_debug(void *mem_ctx, uint32_t size, char const *func, int line); ++extern void dwc_free_debug(void *mem_ctx, void *addr, char const *func, int line); ++extern void *dwc_dma_alloc_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr, ++ char const *func, int line); ++extern void *dwc_dma_alloc_atomic_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr, ++ char const *func, int line); ++extern void dwc_dma_free_debug(void *dma_ctx, uint32_t size, void *virt_addr, ++ dwc_dma_t dma_addr, char const *func, int line); ++ ++extern int dwc_memory_debug_start(void *mem_ctx); ++extern void dwc_memory_debug_stop(void); ++extern void dwc_memory_debug_report(void); ++ ++#define DWC_ALLOC(_size_) dwc_alloc_debug(NULL, _size_, __func__, __LINE__) ++#define DWC_ALLOC_ATOMIC(_size_) dwc_alloc_atomic_debug(NULL, _size_, \ ++ __func__, __LINE__) ++#define DWC_FREE(_addr_) dwc_free_debug(NULL, _addr_, __func__, __LINE__) ++ ++# ifdef DWC_LINUX ++#define DWC_DMA_ALLOC(_size_,_dma_) dwc_dma_alloc_debug(NULL, _size_, \ ++ _dma_, __func__, __LINE__) ++#define DWC_DMA_ALLOC_ATOMIC(_size_,_dma_) dwc_dma_alloc_atomic_debug(NULL, _size_, \ ++ _dma_, __func__, __LINE__) ++#define DWC_DMA_FREE(_size_,_virt_,_dma_) dwc_dma_free_debug(NULL, _size_, \ ++ _virt_, _dma_, __func__, __LINE__) ++# endif ++ ++# if defined(DWC_FREEBSD) || defined(DWC_NETBSD) ++#define DWC_DMA_ALLOC(_ctx_,_size_,_dma_) dwc_dma_alloc_debug(_ctx_, _size_, \ ++ _dma_, __func__, __LINE__) ++#define DWC_DMA_FREE(_ctx_,_size_,_virt_,_dma_) dwc_dma_free_debug(_ctx_, _size_, \ ++ _virt_, _dma_, __func__, __LINE__) ++# endif ++ ++#endif /* DWC_DEBUG_MEMORY */ ++ ++#define dwc_alloc(_ctx_,_size_) DWC_ALLOC(_size_) ++#define dwc_alloc_atomic(_ctx_,_size_) DWC_ALLOC_ATOMIC(_size_) ++#define dwc_free(_ctx_,_addr_) DWC_FREE(_addr_) ++ ++#ifdef DWC_LINUX ++/* Linux doesn't need any extra parameters for DMA buffer allocation, so we ++ * just throw away the DMA context parameter. ++ */ ++#define dwc_dma_alloc(_ctx_,_size_,_dma_) DWC_DMA_ALLOC(_size_, _dma_) ++#define dwc_dma_alloc_atomic(_ctx_,_size_,_dma_) DWC_DMA_ALLOC_ATOMIC(_size_, _dma_) ++#define dwc_dma_free(_ctx_,_size_,_virt_,_dma_) DWC_DMA_FREE(_size_, _virt_, _dma_) ++#endif ++ ++#if defined(DWC_FREEBSD) || defined(DWC_NETBSD) ++/** BSD needs several extra parameters for DMA buffer allocation, so we pass ++ * them in using the DMA context parameter. ++ */ ++#define dwc_dma_alloc DWC_DMA_ALLOC ++#define dwc_dma_free DWC_DMA_FREE ++#endif ++ ++ ++/** @name Memory and String Processing */ ++ ++/** memset() clone */ ++extern void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size); ++#define dwc_memset DWC_MEMSET ++ ++/** memcpy() clone */ ++extern void *DWC_MEMCPY(void *dest, void const *src, uint32_t size); ++#define dwc_memcpy DWC_MEMCPY ++ ++/** memmove() clone */ ++extern void *DWC_MEMMOVE(void *dest, void *src, uint32_t size); ++#define dwc_memmove DWC_MEMMOVE ++ ++/** memcmp() clone */ ++extern int DWC_MEMCMP(void *m1, void *m2, uint32_t size); ++#define dwc_memcmp DWC_MEMCMP ++ ++/** strcmp() clone */ ++extern int DWC_STRCMP(void *s1, void *s2); ++#define dwc_strcmp DWC_STRCMP ++ ++/** strncmp() clone */ ++extern int DWC_STRNCMP(void *s1, void *s2, uint32_t size); ++#define dwc_strncmp DWC_STRNCMP ++ ++/** strlen() clone, for NULL terminated ASCII strings */ ++extern int DWC_STRLEN(char const *str); ++#define dwc_strlen DWC_STRLEN ++ ++/** strcpy() clone, for NULL terminated ASCII strings */ ++extern char *DWC_STRCPY(char *to, const char *from); ++#define dwc_strcpy DWC_STRCPY ++ ++/** strdup() clone. If you wish to use memory allocation debugging, this ++ * implementation of strdup should use the DWC_* memory routines instead of ++ * calling a predefined strdup. Otherwise the memory allocated by this routine ++ * will not be seen by the debugging routines. */ ++extern char *DWC_STRDUP(char const *str); ++#define dwc_strdup(_ctx_,_str_) DWC_STRDUP(_str_) ++ ++/** NOT an atoi() clone. Read the description carefully. Returns an integer ++ * converted from the string str in base 10 unless the string begins with a "0x" ++ * in which case it is base 16. String must be a NULL terminated sequence of ++ * ASCII characters and may optionally begin with whitespace, a + or -, and a ++ * "0x" prefix if base 16. The remaining characters must be valid digits for ++ * the number and end with a NULL character. If any invalid characters are ++ * encountered or it returns with a negative error code and the results of the ++ * conversion are undefined. On sucess it returns 0. Overflow conditions are ++ * undefined. An example implementation using atoi() can be referenced from the ++ * Linux implementation. */ ++extern int DWC_ATOI(const char *str, int32_t *value); ++#define dwc_atoi DWC_ATOI ++ ++/** Same as above but for unsigned. */ ++extern int DWC_ATOUI(const char *str, uint32_t *value); ++#define dwc_atoui DWC_ATOUI ++ ++#ifdef DWC_UTFLIB ++/** This routine returns a UTF16LE unicode encoded string from a UTF8 string. */ ++extern int DWC_UTF8_TO_UTF16LE(uint8_t const *utf8string, uint16_t *utf16string, unsigned len); ++#define dwc_utf8_to_utf16le DWC_UTF8_TO_UTF16LE ++#endif ++ ++ ++/** @name Wait queues ++ * ++ * Wait queues provide a means of synchronizing between threads or processes. A ++ * process can block on a waitq if some condition is not true, waiting for it to ++ * become true. When the waitq is triggered all waiting process will get ++ * unblocked and the condition will be check again. Waitqs should be triggered ++ * every time a condition can potentially change.*/ ++struct dwc_waitq; ++ ++/** Type for a waitq */ ++typedef struct dwc_waitq dwc_waitq_t; ++ ++/** The type of waitq condition callback function. This is called every time ++ * condition is evaluated. */ ++typedef int (*dwc_waitq_condition_t)(void *data); ++ ++/** Allocate a waitq */ ++extern dwc_waitq_t *DWC_WAITQ_ALLOC(void); ++#define dwc_waitq_alloc(_ctx_) DWC_WAITQ_ALLOC() ++ ++/** Free a waitq */ ++extern void DWC_WAITQ_FREE(dwc_waitq_t *wq); ++#define dwc_waitq_free DWC_WAITQ_FREE ++ ++/** Check the condition and if it is false, block on the waitq. When unblocked, check the ++ * condition again. The function returns when the condition becomes true. The return value ++ * is 0 on condition true, DWC_WAITQ_ABORTED on abort or killed, or DWC_WAITQ_UNKNOWN on error. */ ++extern int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data); ++#define dwc_waitq_wait DWC_WAITQ_WAIT ++ ++/** Check the condition and if it is false, block on the waitq. When unblocked, ++ * check the condition again. The function returns when the condition become ++ * true or the timeout has passed. The return value is 0 on condition true or ++ * DWC_TIMED_OUT on timeout, or DWC_WAITQ_ABORTED, or DWC_WAITQ_UNKNOWN on ++ * error. */ ++extern int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, ++ void *data, int32_t msecs); ++#define dwc_waitq_wait_timeout DWC_WAITQ_WAIT_TIMEOUT ++ ++/** Trigger a waitq, unblocking all processes. This should be called whenever a condition ++ * has potentially changed. */ ++extern void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq); ++#define dwc_waitq_trigger DWC_WAITQ_TRIGGER ++ ++/** Unblock all processes waiting on the waitq with an ABORTED result. */ ++extern void DWC_WAITQ_ABORT(dwc_waitq_t *wq); ++#define dwc_waitq_abort DWC_WAITQ_ABORT ++ ++ ++/** @name Threads ++ * ++ * A thread must be explicitly stopped. It must check DWC_THREAD_SHOULD_STOP ++ * whenever it is woken up, and then return. The DWC_THREAD_STOP function ++ * returns the value from the thread. ++ */ ++ ++struct dwc_thread; ++ ++/** Type for a thread */ ++typedef struct dwc_thread dwc_thread_t; ++ ++/** The thread function */ ++typedef int (*dwc_thread_function_t)(void *data); ++ ++/** Create a thread and start it running the thread_function. Returns a handle ++ * to the thread */ ++extern dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data); ++#define dwc_thread_run(_ctx_,_func_,_name_,_data_) DWC_THREAD_RUN(_func_, _name_, _data_) ++ ++/** Stops a thread. Return the value returned by the thread. Or will return ++ * DWC_ABORT if the thread never started. */ ++extern int DWC_THREAD_STOP(dwc_thread_t *thread); ++#define dwc_thread_stop DWC_THREAD_STOP ++ ++/** Signifies to the thread that it must stop. */ ++#ifdef DWC_LINUX ++/* Linux doesn't need any parameters for kthread_should_stop() */ ++extern dwc_bool_t DWC_THREAD_SHOULD_STOP(void); ++#define dwc_thread_should_stop(_thrd_) DWC_THREAD_SHOULD_STOP() ++ ++/* No thread_exit function in Linux */ ++#define dwc_thread_exit(_thrd_) ++#endif ++ ++#if defined(DWC_FREEBSD) || defined(DWC_NETBSD) ++/** BSD needs the thread pointer for kthread_suspend_check() */ ++extern dwc_bool_t DWC_THREAD_SHOULD_STOP(dwc_thread_t *thread); ++#define dwc_thread_should_stop DWC_THREAD_SHOULD_STOP ++ ++/** The thread must call this to exit. */ ++extern void DWC_THREAD_EXIT(dwc_thread_t *thread); ++#define dwc_thread_exit DWC_THREAD_EXIT ++#endif ++ ++ ++/** @name Work queues ++ * ++ * Workqs are used to queue a callback function to be called at some later time, ++ * in another thread. */ ++struct dwc_workq; ++ ++/** Type for a workq */ ++typedef struct dwc_workq dwc_workq_t; ++ ++/** The type of the callback function to be called. */ ++typedef void (*dwc_work_callback_t)(void *data); ++ ++/** Allocate a workq */ ++extern dwc_workq_t *DWC_WORKQ_ALLOC(char *name); ++#define dwc_workq_alloc(_ctx_,_name_) DWC_WORKQ_ALLOC(_name_) ++ ++/** Free a workq. All work must be completed before being freed. */ ++extern void DWC_WORKQ_FREE(dwc_workq_t *workq); ++#define dwc_workq_free DWC_WORKQ_FREE ++ ++/** Schedule a callback on the workq, passing in data. The function will be ++ * scheduled at some later time. */ ++extern void DWC_WORKQ_SCHEDULE(dwc_workq_t *workq, dwc_work_callback_t cb, ++ void *data, char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 4, 5))); ++#else ++ ; ++#endif ++#define dwc_workq_schedule DWC_WORKQ_SCHEDULE ++ ++/** Schedule a callback on the workq, that will be called until at least ++ * given number miliseconds have passed. */ ++extern void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *workq, dwc_work_callback_t cb, ++ void *data, uint32_t time, char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 5, 6))); ++#else ++ ; ++#endif ++#define dwc_workq_schedule_delayed DWC_WORKQ_SCHEDULE_DELAYED ++ ++/** The number of processes in the workq */ ++extern int DWC_WORKQ_PENDING(dwc_workq_t *workq); ++#define dwc_workq_pending DWC_WORKQ_PENDING ++ ++/** Blocks until all the work in the workq is complete or timed out. Returns < ++ * 0 on timeout. */ ++extern int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout); ++#define dwc_workq_wait_work_done DWC_WORKQ_WAIT_WORK_DONE ++ ++ ++/** @name Tasklets ++ * ++ */ ++struct dwc_tasklet; ++ ++/** Type for a tasklet */ ++typedef struct dwc_tasklet dwc_tasklet_t; ++ ++/** The type of the callback function to be called */ ++typedef void (*dwc_tasklet_callback_t)(void *data); ++ ++/** Allocates a tasklet */ ++extern dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data); ++#define dwc_task_alloc(_ctx_,_name_,_cb_,_data_) DWC_TASK_ALLOC(_name_, _cb_, _data_) ++ ++/** Frees a tasklet */ ++extern void DWC_TASK_FREE(dwc_tasklet_t *task); ++#define dwc_task_free DWC_TASK_FREE ++ ++/** Schedules a tasklet to run */ ++extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task); ++#define dwc_task_schedule DWC_TASK_SCHEDULE ++ ++ ++/** @name Timer ++ * ++ * Callbacks must be small and atomic. ++ */ ++struct dwc_timer; ++ ++/** Type for a timer */ ++typedef struct dwc_timer dwc_timer_t; ++ ++/** The type of the callback function to be called */ ++typedef void (*dwc_timer_callback_t)(void *data); ++ ++/** Allocates a timer */ ++extern dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data); ++#define dwc_timer_alloc(_ctx_,_name_,_cb_,_data_) DWC_TIMER_ALLOC(_name_,_cb_,_data_) ++ ++/** Frees a timer */ ++extern void DWC_TIMER_FREE(dwc_timer_t *timer); ++#define dwc_timer_free DWC_TIMER_FREE ++ ++/** Schedules the timer to run at time ms from now. And will repeat at every ++ * repeat_interval msec therafter ++ * ++ * Modifies a timer that is still awaiting execution to a new expiration time. ++ * The mod_time is added to the old time. */ ++extern void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time); ++#define dwc_timer_schedule DWC_TIMER_SCHEDULE ++ ++/** Disables the timer from execution. */ ++extern void DWC_TIMER_CANCEL(dwc_timer_t *timer); ++#define dwc_timer_cancel DWC_TIMER_CANCEL ++ ++ ++/** @name Spinlocks ++ * ++ * These locks are used when the work between the lock/unlock is atomic and ++ * short. Interrupts are also disabled during the lock/unlock and thus they are ++ * suitable to lock between interrupt/non-interrupt context. They also lock ++ * between processes if you have multiple CPUs or Preemption. If you don't have ++ * multiple CPUS or Preemption, then the you can simply implement the ++ * DWC_SPINLOCK and DWC_SPINUNLOCK to disable and enable interrupts. Because ++ * the work between the lock/unlock is atomic, the process context will never ++ * change, and so you never have to lock between processes. */ ++ ++struct dwc_spinlock; ++ ++/** Type for a spinlock */ ++typedef struct dwc_spinlock dwc_spinlock_t; ++ ++/** Type for the 'flags' argument to spinlock funtions */ ++typedef unsigned long dwc_irqflags_t; ++ ++/** Returns an initialized lock variable. This function should allocate and ++ * initialize the OS-specific data structure used for locking. This data ++ * structure is to be used for the DWC_LOCK and DWC_UNLOCK functions and should ++ * be freed by the DWC_FREE_LOCK when it is no longer used. */ ++extern dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void); ++#define dwc_spinlock_alloc(_ctx_) DWC_SPINLOCK_ALLOC() ++ ++/** Frees an initialized lock variable. */ ++extern void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock); ++#define dwc_spinlock_free(_ctx_,_lock_) DWC_SPINLOCK_FREE(_lock_) ++ ++/** Disables interrupts and blocks until it acquires the lock. ++ * ++ * @param lock Pointer to the spinlock. ++ * @param flags Unsigned long for irq flags storage. ++ */ ++extern void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags); ++#define dwc_spinlock_irqsave DWC_SPINLOCK_IRQSAVE ++ ++/** Re-enables the interrupt and releases the lock. ++ * ++ * @param lock Pointer to the spinlock. ++ * @param flags Unsigned long for irq flags storage. Must be the same as was ++ * passed into DWC_LOCK. ++ */ ++extern void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags); ++#define dwc_spinunlock_irqrestore DWC_SPINUNLOCK_IRQRESTORE ++ ++/** Blocks until it acquires the lock. ++ * ++ * @param lock Pointer to the spinlock. ++ */ ++extern void DWC_SPINLOCK(dwc_spinlock_t *lock); ++#define dwc_spinlock DWC_SPINLOCK ++ ++/** Releases the lock. ++ * ++ * @param lock Pointer to the spinlock. ++ */ ++extern void DWC_SPINUNLOCK(dwc_spinlock_t *lock); ++#define dwc_spinunlock DWC_SPINUNLOCK ++ ++ ++/** @name Mutexes ++ * ++ * Unlike spinlocks Mutexes lock only between processes and the work between the ++ * lock/unlock CAN block, therefore it CANNOT be called from interrupt context. ++ */ ++ ++struct dwc_mutex; ++ ++/** Type for a mutex */ ++typedef struct dwc_mutex dwc_mutex_t; ++ ++/* For Linux Mutex Debugging make it inline because the debugging routines use ++ * the symbol to determine recursive locking. This makes it falsely think ++ * recursive locking occurs. */ ++#if defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES) ++#define DWC_MUTEX_ALLOC_LINUX_DEBUG(__mutexp) ({ \ ++ __mutexp = (dwc_mutex_t *)DWC_ALLOC(sizeof(struct mutex)); \ ++ mutex_init((struct mutex *)__mutexp); \ ++}) ++#endif ++ ++/** Allocate a mutex */ ++extern dwc_mutex_t *DWC_MUTEX_ALLOC(void); ++#define dwc_mutex_alloc(_ctx_) DWC_MUTEX_ALLOC() ++ ++/* For memory leak debugging when using Linux Mutex Debugging */ ++#if defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES) ++#define DWC_MUTEX_FREE(__mutexp) do { \ ++ mutex_destroy((struct mutex *)__mutexp); \ ++ DWC_FREE(__mutexp); \ ++} while(0) ++#else ++/** Free a mutex */ ++extern void DWC_MUTEX_FREE(dwc_mutex_t *mutex); ++#define dwc_mutex_free(_ctx_,_mutex_) DWC_MUTEX_FREE(_mutex_) ++#endif ++ ++/** Lock a mutex */ ++extern void DWC_MUTEX_LOCK(dwc_mutex_t *mutex); ++#define dwc_mutex_lock DWC_MUTEX_LOCK ++ ++/** Non-blocking lock returns 1 on successful lock. */ ++extern int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex); ++#define dwc_mutex_trylock DWC_MUTEX_TRYLOCK ++ ++/** Unlock a mutex */ ++extern void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex); ++#define dwc_mutex_unlock DWC_MUTEX_UNLOCK ++ ++ ++/** @name Time */ ++ ++/** Microsecond delay. ++ * ++ * @param usecs Microseconds to delay. ++ */ ++extern void DWC_UDELAY(uint32_t usecs); ++#define dwc_udelay DWC_UDELAY ++ ++/** Millisecond delay. ++ * ++ * @param msecs Milliseconds to delay. ++ */ ++extern void DWC_MDELAY(uint32_t msecs); ++#define dwc_mdelay DWC_MDELAY ++ ++/** Non-busy waiting. ++ * Sleeps for specified number of milliseconds. ++ * ++ * @param msecs Milliseconds to sleep. ++ */ ++extern void DWC_MSLEEP(uint32_t msecs); ++#define dwc_msleep DWC_MSLEEP ++ ++/** ++ * Returns number of milliseconds since boot. ++ */ ++extern uint32_t DWC_TIME(void); ++#define dwc_time DWC_TIME ++ ++ ++ ++ ++/* @mainpage DWC Portability and Common Library ++ * ++ * This is the documentation for the DWC Portability and Common Library. ++ * ++ * @section intro Introduction ++ * ++ * The DWC Portability library consists of wrapper calls and data structures to ++ * all low-level functions which are typically provided by the OS. The WUDEV ++ * driver uses only these functions. In order to port the WUDEV driver, only ++ * the functions in this library need to be re-implemented, with the same ++ * behavior as documented here. ++ * ++ * The Common library consists of higher level functions, which rely only on ++ * calling the functions from the DWC Portability library. These common ++ * routines are shared across modules. Some of the common libraries need to be ++ * used directly by the driver programmer when porting WUDEV. Such as the ++ * parameter and notification libraries. ++ * ++ * @section low Portability Library OS Wrapper Functions ++ * ++ * Any function starting with DWC and in all CAPS is a low-level OS-wrapper that ++ * needs to be implemented when porting, for example DWC_MUTEX_ALLOC(). All of ++ * these functions are included in the dwc_os.h file. ++ * ++ * There are many functions here covering a wide array of OS services. Please ++ * see dwc_os.h for details, and implementation notes for each function. ++ * ++ * @section common Common Library Functions ++ * ++ * Any function starting with dwc and in all lowercase is a common library ++ * routine. These functions have a portable implementation and do not need to ++ * be reimplemented when porting. The common routines can be used by any ++ * driver, and some must be used by the end user to control the drivers. For ++ * example, you must use the Parameter common library in order to set the ++ * parameters in the WUDEV module. ++ * ++ * The common libraries consist of the following: ++ * ++ * - Connection Contexts - Used internally and can be used by end-user. See dwc_cc.h ++ * - Parameters - Used internally and can be used by end-user. See dwc_params.h ++ * - Notifications - Used internally and can be used by end-user. See dwc_notifier.h ++ * - Lists - Used internally and can be used by end-user. See dwc_list.h ++ * - Memory Debugging - Used internally and can be used by end-user. See dwc_os.h ++ * - Modpow - Used internally only. See dwc_modpow.h ++ * - DH - Used internally only. See dwc_dh.h ++ * - Crypto - Used internally only. See dwc_crypto.h ++ * ++ * ++ * @section prereq Prerequistes For dwc_os.h ++ * @subsection types Data Types ++ * ++ * The dwc_os.h file assumes that several low-level data types are pre defined for the ++ * compilation environment. These data types are: ++ * ++ * - uint8_t - unsigned 8-bit data type ++ * - int8_t - signed 8-bit data type ++ * - uint16_t - unsigned 16-bit data type ++ * - int16_t - signed 16-bit data type ++ * - uint32_t - unsigned 32-bit data type ++ * - int32_t - signed 32-bit data type ++ * - uint64_t - unsigned 64-bit data type ++ * - int64_t - signed 64-bit data type ++ * ++ * Ensure that these are defined before using dwc_os.h. The easiest way to do ++ * that is to modify the top of the file to include the appropriate header. ++ * This is already done for the Linux environment. If the DWC_LINUX macro is ++ * defined, the correct header will be added. A standard header <stdint.h> is ++ * also used for environments where standard C headers are available. ++ * ++ * @subsection stdarg Variable Arguments ++ * ++ * Variable arguments are provided by a standard C header <stdarg.h>. it is ++ * available in Both the Linux and ANSI C enviornment. An equivalent must be ++ * provided in your enviornment in order to use dwc_os.h with the debug and ++ * tracing message functionality. ++ * ++ * @subsection thread Threading ++ * ++ * WUDEV Core must be run on an operating system that provides for multiple ++ * threads/processes. Threading can be implemented in many ways, even in ++ * embedded systems without an operating system. At the bare minimum, the ++ * system should be able to start any number of processes at any time to handle ++ * special work. It need not be a pre-emptive system. Process context can ++ * change upon a call to a blocking function. The hardware interrupt context ++ * that calls the module's ISR() function must be differentiable from process ++ * context, even if your processes are impemented via a hardware interrupt. ++ * Further locking mechanism between process must exist (or be implemented), and ++ * process context must have a way to disable interrupts for a period of time to ++ * lock them out. If all of this exists, the functions in dwc_os.h related to ++ * threading should be able to be implemented with the defined behavior. ++ * ++ */ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_OS_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_adp.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_adp.c +new file mode 100644 +index 0000000..db309f3 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_adp.c +@@ -0,0 +1,908 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_adp.c $ ++ * $Revision: #16 $ ++ * $Date: 2013/04/22 $ ++ * $Change: 2211149 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#include "dwc_os.h" ++#include "dwc_otg_regs.h" ++#include "dwc_otg_cil.h" ++#include "dwc_otg_adp.h" ++ ++/** @file ++ * ++ * This file contains the most of the Attach Detect Protocol implementation for ++ * the driver to support OTG Rev2.0. ++ * ++ */ ++ ++void dwc_otg_adp_write_reg(dwc_otg_core_if_t * core_if, uint32_t value) ++{ ++ adpctl_data_t adpctl; ++ ++ adpctl.d32 = value; ++ adpctl.b.ar = 0x2; ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs->adpctl, adpctl.d32); ++ ++ while (adpctl.b.ar) { ++ adpctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->adpctl); ++ } ++ ++} ++ ++/** ++ * Function is called to read ADP registers ++ */ ++uint32_t dwc_otg_adp_read_reg(dwc_otg_core_if_t * core_if) ++{ ++ adpctl_data_t adpctl; ++ ++ adpctl.d32 = 0; ++ adpctl.b.ar = 0x1; ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs->adpctl, adpctl.d32); ++ ++ while (adpctl.b.ar) { ++ adpctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->adpctl); ++ } ++ ++ return adpctl.d32; ++} ++ ++/** ++ * Function is called to read ADPCTL register and filter Write-clear bits ++ */ ++uint32_t dwc_otg_adp_read_reg_filter(dwc_otg_core_if_t * core_if) ++{ ++ adpctl_data_t adpctl; ++ ++ adpctl.d32 = dwc_otg_adp_read_reg(core_if); ++ adpctl.b.adp_tmout_int = 0; ++ adpctl.b.adp_prb_int = 0; ++ adpctl.b.adp_tmout_int = 0; ++ ++ return adpctl.d32; ++} ++ ++/** ++ * Function is called to write ADP registers ++ */ ++void dwc_otg_adp_modify_reg(dwc_otg_core_if_t * core_if, uint32_t clr, ++ uint32_t set) ++{ ++ dwc_otg_adp_write_reg(core_if, ++ (dwc_otg_adp_read_reg(core_if) & (~clr)) | set); ++} ++ ++static void adp_probe_func(void * ptr) ++{ ++ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr; ++ dwc_otg_adp_probe_start(core_if); ++} ++ ++static void adp_sense_timeout(void *ptr) ++{ ++ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr; ++ core_if->adp.sense_timer_started = 0; ++ DWC_DEBUGPL(DBG_PCD, "ADP SENSE TIMEOUT\n"); ++ if (core_if->adp_enable) { ++ dwc_otg_adp_sense_stop(core_if); ++ DWC_WORKQ_SCHEDULE_DELAYED(core_if->wq_otg, adp_probe_func, ++ core_if, 100 , "start probe"); ++ } ++} ++ ++/** ++ * This function is called when the ADP vbus timer expires. Timeout is 1.1s. ++ */ ++static void adp_vbuson_timeout(void *ptr) ++{ ++ gpwrdn_data_t gpwrdn; ++ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr; ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ DWC_PRINTF("%s: 1.1 seconds expire after turning on VBUS\n",__FUNCTION__); ++ if (core_if) { ++ core_if->adp.vbuson_timer_started = 0; ++ if(dwc_otg_is_host_mode(core_if)) { ++ /* Turn off vbus */ ++ hprt0.b.prtpwr = 1; ++ DWC_MODIFY_REG32(core_if->host_if->hprt0, hprt0.d32, 0); ++ cil_hcd_disconnect(core_if); ++ } ++ gpwrdn.d32 = 0; ++ ++ /* Power off the core */ ++ if (core_if->power_down == 2) { ++ /* Enable Wakeup Logic */ ++// gpwrdn.b.wkupactiv = 1; ++ gpwrdn.b.pmuactv = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ gpwrdn.b.pwrdnclmp = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, ++ gpwrdn.d32); ++ ++ /* Suspend the Phy Clock */ ++ pcgcctl.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32); ++ ++ /* Switch on VDD */ ++// gpwrdn.b.wkupactiv = 1; ++ gpwrdn.b.pmuactv = 1; ++ gpwrdn.b.pwrdnrstn = 1; ++ gpwrdn.b.pwrdnclmp = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, ++ gpwrdn.d32); ++ } else { ++ /* Enable Power Down Logic */ ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ if(dwc_otg_is_host_mode(core_if)) ++ gpwrdn.b.dis_vbus = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ } ++ ++ /* Power off the core */ ++ if (core_if->power_down == 2) { ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, ++ gpwrdn.d32, 0); ++ } ++ ++ /* Unmask SRP detected interrupt from Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.srp_det_msk = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ ++ dwc_mdelay(220); ++ dwc_otg_adp_probe_start(core_if); ++ } ++ ++} ++ ++/** ++ * Start the ADP Initial Probe timer to detect if Port Connected interrupt is ++ * not asserted within 1.1 seconds. ++ * ++ * @param core_if the pointer to core_if strucure. ++ */ ++void dwc_otg_adp_vbuson_timer_start(dwc_otg_core_if_t * core_if) ++{ ++ core_if->adp.vbuson_timer_started = 1; ++ if (core_if->adp.vbuson_timer) ++ { ++ DWC_PRINTF("SCHEDULING VBUSON TIMER\n"); ++ /* 1.1 secs + 60ms necessary for cil_hcd_start*/ ++ DWC_TIMER_SCHEDULE(core_if->adp.vbuson_timer, 1160); ++ } else { ++ DWC_WARN("VBUSON_TIMER = %p\n",core_if->adp.vbuson_timer); ++ } ++} ++ ++#if 0 ++/** ++ * Masks all DWC OTG core interrupts ++ * ++ */ ++static void mask_all_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ int i; ++ gahbcfg_data_t ahbcfg = {.d32 = 0 }; ++ ++ /* Mask Host Interrupts */ ++ ++ /* Clear and disable HCINTs */ ++ for (i = 0; i < core_if->core_params->host_channels; i++) { ++ DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcintmsk, 0); ++ DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcint, 0xFFFFFFFF); ++ ++ } ++ ++ /* Clear and disable HAINT */ ++ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haintmsk, 0x0000); ++ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haint, 0xFFFFFFFF); ++ ++ /* Mask Device Interrupts */ ++ if (!core_if->multiproc_int_enable) { ++ /* Clear and disable IN Endpoint interrupts */ ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->diepmsk, 0); ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]-> ++ diepint, 0xFFFFFFFF); ++ } ++ ++ /* Clear and disable OUT Endpoint interrupts */ ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->doepmsk, 0); ++ for (i = 0; i <= core_if->dev_if->num_out_eps; i++) { ++ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]-> ++ doepint, 0xFFFFFFFF); ++ } ++ ++ /* Clear and disable DAINT */ ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daint, ++ 0xFFFFFFFF); ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daintmsk, 0); ++ } else { ++ for (i = 0; i < core_if->dev_if->num_in_eps; ++i) { ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs-> ++ diepeachintmsk[i], 0); ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]-> ++ diepint, 0xFFFFFFFF); ++ } ++ ++ for (i = 0; i < core_if->dev_if->num_out_eps; ++i) { ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs-> ++ doepeachintmsk[i], 0); ++ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]-> ++ doepint, 0xFFFFFFFF); ++ } ++ ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->deachintmsk, ++ 0); ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->deachint, ++ 0xFFFFFFFF); ++ ++ } ++ ++ /* Disable interrupts */ ++ ahbcfg.b.glblintrmsk = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0); ++ ++ /* Disable all interrupts. */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, 0); ++ ++ /* Clear any pending interrupts */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Clear any pending OTG Interrupts */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gotgint, 0xFFFFFFFF); ++} ++ ++/** ++ * Unmask Port Connection Detected interrupt ++ * ++ */ ++static void unmask_conn_det_intr(dwc_otg_core_if_t * core_if) ++{ ++ gintmsk_data_t gintmsk = {.d32 = 0,.b.portintr = 1 }; ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32); ++} ++#endif ++ ++/** ++ * Starts the ADP Probing ++ * ++ * @param core_if the pointer to core_if structure. ++ */ ++uint32_t dwc_otg_adp_probe_start(dwc_otg_core_if_t * core_if) ++{ ++ ++ adpctl_data_t adpctl = {.d32 = 0}; ++ gpwrdn_data_t gpwrdn; ++#if 0 ++ adpctl_data_t adpctl_int = {.d32 = 0, .b.adp_prb_int = 1, ++ .b.adp_sns_int = 1, b.adp_tmout_int}; ++#endif ++ if (core_if->stop_adpprb) { ++ core_if->stop_adpprb = 0; ++ return 0; ++ } ++ ++ dwc_otg_disable_global_interrupts(core_if); ++ DWC_DEBUGPL(DBG_ANY, "ADP Probe Start\n"); ++ core_if->adp.probe_enabled = 1; ++ ++ adpctl.b.adpres = 1; ++ dwc_otg_adp_write_reg(core_if, adpctl.d32); ++ ++ while (adpctl.b.adpres) { ++ adpctl.d32 = dwc_otg_adp_read_reg(core_if); ++ } ++ ++ adpctl.d32 = 0; ++ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ ++ /* In Host mode unmask SRP detected interrupt also change the ++ * probe preiod accordingly */ ++ if (!gpwrdn.b.idsts) { ++ gpwrdn.d32 = 0; ++ gpwrdn.b.srp_det_msk = 1; ++ adpctl.b.prb_per = 0; ++ } ++ else { ++ gpwrdn.d32 = 0; ++ gpwrdn.b.srp_det_msk = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, gpwrdn.d32, 0); ++ gpwrdn.d32 = 0; ++ gpwrdn.b.sts_chngint_msk = 1; ++ adpctl.b.prb_per = 1; ++ } ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ ++ adpctl.b.adp_tmout_int_msk = 1; ++ adpctl.b.adp_prb_int_msk = 1; ++ adpctl.b.prb_dschg = 1; ++ adpctl.b.prb_delta = 1; ++ dwc_otg_adp_write_reg(core_if, adpctl.d32); ++ ++ adpctl.b.adpen = 1; ++ adpctl.b.enaprb = 1; ++ dwc_otg_adp_write_reg(core_if, adpctl.d32); ++ DWC_DEBUGPL(DBG_ANY, "ADP Probe Finish\n"); ++ ++ return 0; ++} ++ ++/** ++ * Starts the ADP Sense timer to detect if ADP Sense interrupt is not asserted ++ * within 3 seconds. ++ * ++ * @param core_if the pointer to core_if strucure. ++ */ ++void dwc_otg_adp_sense_timer_start(dwc_otg_core_if_t * core_if) ++{ ++ core_if->adp.sense_timer_started = 1; ++ DWC_TIMER_SCHEDULE(core_if->adp.sense_timer, 3300 /* 3.3 secs */ ); ++} ++ ++/** ++ * Starts the ADP Sense ++ * ++ * @param core_if the pointer to core_if strucure. ++ */ ++uint32_t dwc_otg_adp_sense_start(dwc_otg_core_if_t * core_if) ++{ ++ adpctl_data_t adpctl; ++ ++ DWC_DEBUGPL(DBG_PCD, "ADP Sense Start\n"); ++ ++ /* Set ADP reset bit*/ ++ adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if); ++ adpctl.b.adpres = 1; ++ dwc_otg_adp_write_reg(core_if, adpctl.d32); ++ ++ while (adpctl.b.adpres) { ++ adpctl.d32 = dwc_otg_adp_read_reg(core_if); ++ } ++ ++ /* Unmask ADP sense interrupt and mask all other from the core */ ++ adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if); ++ adpctl.b.adp_sns_int_msk = 1; ++ dwc_otg_adp_write_reg(core_if, adpctl.d32); ++ dwc_otg_disable_global_interrupts(core_if); ++ ++ adpctl.b.adpres = 0; ++ adpctl.b.adpen = 1; ++ adpctl.b.enasns = 1; ++ dwc_otg_adp_write_reg(core_if, adpctl.d32); ++ ++ dwc_otg_adp_sense_timer_start(core_if); ++ ++ return 0; ++} ++ ++/** ++ * Stops the ADP Probing ++ * ++ * @param core_if the pointer to core_if strucure. ++ */ ++uint32_t dwc_otg_adp_probe_stop(dwc_otg_core_if_t * core_if) ++{ ++ ++ adpctl_data_t adpctl; ++ DWC_DEBUGPL(DBG_ANY, "Stop ADP probe\n"); ++ core_if->adp.probe_enabled = 0; ++ //core_if->adp.probe_counter = 0; ++ adpctl.d32 = dwc_otg_adp_read_reg(core_if); ++ ++ adpctl.b.adpen = 0; ++ adpctl.b.adp_prb_int = 1; ++ adpctl.b.adp_tmout_int = 1; ++ adpctl.b.adp_sns_int = 1; ++ dwc_otg_adp_write_reg(core_if, adpctl.d32); ++ ++ return 0; ++} ++ ++/** ++ * Stops the ADP Sensing ++ * ++ * @param core_if the pointer to core_if strucure. ++ */ ++uint32_t dwc_otg_adp_sense_stop(dwc_otg_core_if_t * core_if) ++{ ++ adpctl_data_t adpctl; ++ ++ core_if->adp.sense_enabled = 0; ++ ++ adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if); ++ adpctl.b.enasns = 0; ++ adpctl.b.adp_sns_int = 1; ++ dwc_otg_adp_write_reg(core_if, adpctl.d32); ++ ++ return 0; ++} ++ ++/** ++ * Called to turn on the VBUS after initial ADP probe in host mode. ++ * If port power was already enabled in cil_hcd_start function then ++ * only schedule a timer. ++ * ++ * @param core_if the pointer to core_if structure. ++ */ ++void dwc_otg_adp_turnon_vbus(dwc_otg_core_if_t * core_if) ++{ ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ DWC_PRINTF("Turn on VBUS for 1.1s, port power is %d\n", hprt0.b.prtpwr); ++ ++ if (hprt0.b.prtpwr == 0) { ++ hprt0.b.prtpwr = 1; ++ //DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ } ++ ++ dwc_otg_adp_vbuson_timer_start(core_if); ++} ++ ++/** ++ * Called right after driver is loaded ++ * to perform initial actions for ADP ++ * ++ * @param core_if the pointer to core_if structure. ++ * @param is_host - flag for current mode of operation either from GINTSTS or GPWRDN ++ */ ++void dwc_otg_adp_start(dwc_otg_core_if_t * core_if, uint8_t is_host) ++{ ++ gpwrdn_data_t gpwrdn; ++ ++ DWC_DEBUGPL(DBG_ANY, "ADP Initial Start\n"); ++ core_if->adp.adp_started = 1; ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF); ++ dwc_otg_disable_global_interrupts(core_if); ++ if (is_host) { ++ DWC_PRINTF("HOST MODE\n"); ++ //core_if->op_state = A_HOST; - vahrama, modified checking in hcd_start() ++ /* Enable Power Down Logic Interrupt*/ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ /* Initialize first ADP probe to obtain Ramp Time value */ ++ core_if->adp.initial_probe = 1; ++ dwc_otg_adp_probe_start(core_if); ++ } else { ++ gotgctl_data_t gotgctl; ++ gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl); ++ DWC_DEBUGPL(DBG_ANY, "DEVICE MODE\n"); ++ //dwc_otg_core_init(core_if); ++ if (gotgctl.b.bsesvld == 0) { ++ /* Enable Power Down Logic Interrupt*/ ++ gpwrdn.d32 = 0; ++ DWC_DEBUGPL(DBG_ANY, "VBUS is not valid - start ADP probe\n"); ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ /* Do not need to return to inital probe if we are coming back to ++ * the device mode after HNP */ ++ if (core_if->op_state != B_HOST) ++ core_if->adp.initial_probe = 1; ++ dwc_otg_adp_probe_start(core_if); ++ } else { ++ DWC_PRINTF("VBUS is valid - initialize core as a Device\n"); ++ core_if->op_state = B_PERIPHERAL; ++ //dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_pcd_start(core_if); ++ dwc_otg_dump_global_registers(core_if); ++ dwc_otg_dump_dev_registers(core_if); ++ } ++ } ++} ++ ++void dwc_otg_adp_init(dwc_otg_core_if_t * core_if) ++{ ++ core_if->adp.adp_started = 0; ++ core_if->adp.initial_probe = 0; ++ core_if->adp.probe_timer_values[0] = -1; ++ core_if->adp.probe_timer_values[1] = -1; ++ core_if->adp.probe_enabled = 0; ++ core_if->adp.sense_enabled = 0; ++ core_if->adp.sense_timer_started = 0; ++ core_if->adp.vbuson_timer_started = 0; ++ core_if->adp.probe_counter = 0; ++ core_if->adp.gpwrdn = 0; ++ core_if->adp.attached = DWC_OTG_ADP_UNKOWN; ++ /* Initialize timers */ ++ core_if->adp.sense_timer = ++ DWC_TIMER_ALLOC("ADP SENSE TIMER", adp_sense_timeout, core_if); ++ core_if->adp.vbuson_timer = ++ DWC_TIMER_ALLOC("ADP VBUS ON TIMER", adp_vbuson_timeout, core_if); ++ if (!core_if->adp.sense_timer || !core_if->adp.vbuson_timer) ++ { ++ DWC_ERROR("Could not allocate memory for ADP timers\n"); ++ } ++} ++ ++void dwc_otg_adp_remove(dwc_otg_core_if_t * core_if) ++{ ++ gpwrdn_data_t gpwrdn = { .d32 = 0 }; ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ if (core_if->adp.probe_enabled) ++ dwc_otg_adp_probe_stop(core_if); ++ if (core_if->adp.sense_enabled) ++ dwc_otg_adp_sense_stop(core_if); ++ if (core_if->adp.sense_timer_started) ++ DWC_TIMER_CANCEL(core_if->adp.sense_timer); ++ if (core_if->adp.vbuson_timer_started) ++ DWC_TIMER_CANCEL(core_if->adp.vbuson_timer); ++ DWC_TIMER_FREE(core_if->adp.sense_timer); ++ DWC_TIMER_FREE(core_if->adp.vbuson_timer); ++} ++ ++///////////////////////////////////////////////////////////////////// ++////////////// ADP Interrupt Handlers /////////////////////////////// ++///////////////////////////////////////////////////////////////////// ++/** ++ * This function sets Ramp Timer values ++ */ ++static uint32_t set_timer_value(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ if (core_if->adp.probe_timer_values[0] == -1) { ++ core_if->adp.probe_timer_values[0] = val; ++ core_if->adp.probe_timer_values[1] = -1; ++ return 1; ++ } else { ++ core_if->adp.probe_timer_values[1] = ++ core_if->adp.probe_timer_values[0]; ++ core_if->adp.probe_timer_values[0] = val; ++ return 0; ++ } ++} ++ ++/** ++ * This function compares Ramp Timer values ++ */ ++static uint32_t compare_timer_values(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t diff; ++ uint32_t thres; ++ gpwrdn_data_t gpwrdn; ++ ++ /* RTIM difference thresold differs for host and device modes */ ++ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ if (!gpwrdn.b.idsts) ++ thres = HOST_RTIM_THRESHOLD; ++ else ++ thres = DEVICE_RTIM_THRESHOLD; ++ ++ DWC_DEBUGPL(DBG_ANY, "timer value 0 %d timer value 1 %d\n", ++ core_if->adp.probe_timer_values[0], core_if->adp.probe_timer_values[1]); ++ if (core_if->adp.probe_timer_values[0] >= core_if->adp.probe_timer_values[1]) ++ diff = core_if->adp.probe_timer_values[0] - core_if->adp.probe_timer_values[1]; ++ else ++ diff = core_if->adp.probe_timer_values[1] - core_if->adp.probe_timer_values[0]; ++ if (diff < thres) ++ return 0; ++ else ++ return 1; ++} ++ ++/** ++ * This function handles ADP Probe Interrupts ++ */ ++static int32_t dwc_otg_adp_handle_prb_intr(dwc_otg_core_if_t * core_if, ++ uint32_t val) ++{ ++ adpctl_data_t adpctl = {.d32 = 0 }; ++ gpwrdn_data_t gpwrdn, temp; ++ adpctl.d32 = val; ++ ++ temp.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ ++ core_if->adp.gpwrdn = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ if (adpctl.b.rtim == 0 /*&& !temp.b.idsts*/){ ++ DWC_PRINTF("RTIM value is 0\n"); ++ goto exit; ++ } ++ core_if->adp.probe_counter++; ++ ++ if (set_timer_value(core_if, adpctl.b.rtim) && ++ core_if->adp.initial_probe) { ++ core_if->adp.initial_probe = 0; ++ dwc_otg_adp_probe_stop(core_if); ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuactv = 1; ++ gpwrdn.b.pmuintsel = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* check which value is for device mode and which for Host mode */ ++ if (!temp.b.idsts) { /* considered host mode value is 0 */ ++ /* Choose right op_state depending on previous one */ ++ if (core_if->op_state == B_PERIPHERAL) ++ core_if->op_state = B_HOST; ++ else ++ core_if->op_state = A_HOST; ++ dwc_otg_enable_global_interrupts(core_if); ++ /* ++ * Turn on VBUS after initial ADP probe. ++ */ ++ DWC_SPINUNLOCK(core_if->lock); ++ cil_hcd_start(core_if); ++ dwc_otg_adp_turnon_vbus(core_if); ++ DWC_SPINLOCK(core_if->lock); ++ } else { ++ /* ++ * Initiate SRP after initial ADP probe. ++ */ ++ dwc_otg_enable_global_interrupts(core_if); ++ dwc_otg_initiate_srp(core_if); ++ } ++ } else if (core_if->adp.probe_counter > 2){ ++ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ if (compare_timer_values(core_if)) { ++ DWC_PRINTF("Difference in timer values !!! \n"); ++// core_if->adp.attached = DWC_OTG_ADP_ATTACHED; ++ dwc_otg_adp_probe_stop(core_if); ++ ++ /* Power on the core */ ++ if (core_if->power_down == 2) { ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ } ++ ++ /* check which value is for device mode and which for Host mode */ ++ if (!temp.b.idsts) { /* considered host mode value is 0 */ ++ /* Disable Interrupt from Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, gpwrdn.d32, 0); ++ ++ /* ++ * Initialize the Core for Host mode. ++ * Choose right op_state depending on previous one ++ */ ++ if (core_if->op_state == B_PERIPHERAL) ++ core_if->op_state = B_HOST; ++ else ++ core_if->op_state = A_HOST; ++ ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_hcd_start(core_if); ++ dwc_otg_adp_turnon_vbus(core_if); ++ } else { ++ gotgctl_data_t gotgctl; ++ /* Mask SRP detected interrupt from Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.srp_det_msk = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, gpwrdn.d32, 0); ++ ++ /* Disable Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, gpwrdn.d32, 0); ++ ++ /* ++ * Initialize the Core for Device mode. ++ */ ++ core_if->op_state = B_PERIPHERAL; ++ //dwc_otg_core_init(core_if); ++ cil_pcd_start(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ ++ gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl); ++ if (!gotgctl.b.bsesvld) ++ dwc_otg_initiate_srp(core_if); ++ } ++ } ++ if (core_if->power_down == 2) { ++ if (gpwrdn.b.bsessvld) { ++ /* Mask SRP detected interrupt from Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.srp_det_msk = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /* Disable Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /* ++ * Initialize the Core for Device mode. ++ */ ++ core_if->op_state = B_PERIPHERAL; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_pcd_start(core_if); ++ } ++ } ++ } ++exit: ++ /* Clear interrupt */ ++ adpctl.d32 = dwc_otg_adp_read_reg(core_if); ++ adpctl.b.adp_prb_int = 1; ++ dwc_otg_adp_write_reg(core_if, adpctl.d32); ++ ++ return 0; ++} ++ ++/** ++ * This function hadles ADP Sense Interrupt ++ */ ++static int32_t dwc_otg_adp_handle_sns_intr(dwc_otg_core_if_t * core_if) ++{ ++ adpctl_data_t adpctl; ++ /* Stop ADP Sense timer */ ++ DWC_TIMER_CANCEL(core_if->adp.sense_timer); ++ ++ /* Restart ADP Sense timer */ ++ dwc_otg_adp_sense_timer_start(core_if); ++ ++ /* Clear interrupt */ ++ adpctl.d32 = dwc_otg_adp_read_reg(core_if); ++ adpctl.b.adp_sns_int = 1; ++ dwc_otg_adp_write_reg(core_if, adpctl.d32); ++ ++ return 0; ++} ++ ++/** ++ * This function handles ADP Probe Interrupts ++ */ ++static int32_t dwc_otg_adp_handle_prb_tmout_intr(dwc_otg_core_if_t * core_if, ++ uint32_t val) ++{ ++ adpctl_data_t adpctl = {.d32 = 0 }; ++ adpctl.d32 = val; ++ set_timer_value(core_if, adpctl.b.rtim); ++ ++ /* Clear interrupt */ ++ adpctl.d32 = dwc_otg_adp_read_reg(core_if); ++ adpctl.b.adp_tmout_int = 1; ++ dwc_otg_adp_write_reg(core_if, adpctl.d32); ++ ++ return 0; ++} ++ ++/** ++ * ADP Interrupt handler. ++ * ++ */ ++int32_t dwc_otg_adp_handle_intr(dwc_otg_core_if_t * core_if) ++{ ++ int retval = 0; ++ adpctl_data_t adpctl = {.d32 = 0}; ++ ++ adpctl.d32 = dwc_otg_adp_read_reg(core_if); ++ DWC_DEBUGPL(DBG_ANY, "ADPCTL = %08x RAMP TIME = %d\n", adpctl.d32, adpctl.b.rtim); ++ ++ if (adpctl.b.adp_sns_int & adpctl.b.adp_sns_int_msk) { ++ DWC_DEBUGPL(DBG_ANY, "ADP Sense interrupt\n"); ++ retval |= dwc_otg_adp_handle_sns_intr(core_if); ++ } ++ if (adpctl.b.adp_tmout_int & adpctl.b.adp_tmout_int_msk) { ++ DWC_DEBUGPL(DBG_ANY, "ADP timeout interrupt\n"); ++ retval |= dwc_otg_adp_handle_prb_tmout_intr(core_if, adpctl.d32); ++ } ++ if (adpctl.b.adp_prb_int & adpctl.b.adp_prb_int_msk) { ++ DWC_DEBUGPL(DBG_ANY, "ADP Probe interrupt\n"); ++ adpctl.b.adp_prb_int = 1; ++ retval |= dwc_otg_adp_handle_prb_intr(core_if, adpctl.d32); ++ } ++ ++// dwc_otg_adp_modify_reg(core_if, adpctl.d32, 0); ++ //dwc_otg_adp_write_reg(core_if, adpctl.d32); ++ DWC_DEBUGPL(DBG_ANY, "RETURN FROM ADP ISR\n"); ++ ++ return retval; ++} ++ ++/** ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_adp_handle_srp_intr(dwc_otg_core_if_t * core_if) ++{ ++ ++#ifndef DWC_HOST_ONLY ++ hprt0_data_t hprt0; ++ gpwrdn_data_t gpwrdn; ++ DWC_DEBUGPL(DBG_ANY, "++ Power Down Logic Session Request Interrupt++\n"); ++ ++ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ /* check which value is for device mode and which for Host mode */ ++ if (!gpwrdn.b.idsts) { /* considered host mode value is 0 */ ++ DWC_PRINTF("SRP: Host mode\n"); ++ ++ if (core_if->adp_enable) { ++ dwc_otg_adp_probe_stop(core_if); ++ ++ /* Power on the core */ ++ if (core_if->power_down == 2) { ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ } ++ ++ core_if->op_state = A_HOST; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_hcd_start(core_if); ++ } ++ ++ /* Turn on the port power bit. */ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtpwr = 1; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ ++ /* Start the Connection timer. So a message can be displayed ++ * if connect does not occur within 10 seconds. */ ++ cil_hcd_session_start(core_if); ++ } else { ++ DWC_DEBUGPL(DBG_PCD, "SRP: Device mode %s\n", __FUNCTION__); ++ if (core_if->adp_enable) { ++ dwc_otg_adp_probe_stop(core_if); ++ ++ /* Power on the core */ ++ if (core_if->power_down == 2) { ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ } ++ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuactv = 0; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, ++ gpwrdn.d32); ++ ++ core_if->op_state = B_PERIPHERAL; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_pcd_start(core_if); ++ } ++ } ++#endif ++ return 1; ++} +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_adp.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_adp.h +new file mode 100644 +index 0000000..c21b2f0 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_adp.h +@@ -0,0 +1,82 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_adp.h $ ++ * $Revision: #8 $ ++ * $Date: 2013/04/09 $ ++ * $Change: 2201932 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef __DWC_OTG_ADP_H__ ++#define __DWC_OTG_ADP_H__ ++ ++/** ++ * @file ++ * ++ * This file contains the Attach Detect Protocol interfaces and defines ++ * (functions) and structures for Linux. ++ * ++ */ ++ ++#define DWC_OTG_ADP_UNATTACHED 0 ++#define DWC_OTG_ADP_ATTACHED 1 ++#define DWC_OTG_ADP_UNKOWN 2 ++#define HOST_RTIM_THRESHOLD 5 ++#define DEVICE_RTIM_THRESHOLD 3 ++ ++typedef struct dwc_otg_adp { ++ uint32_t adp_started; ++ uint32_t initial_probe; ++ int32_t probe_timer_values[2]; ++ uint32_t probe_enabled; ++ uint32_t sense_enabled; ++ dwc_timer_t *sense_timer; ++ uint32_t sense_timer_started; ++ dwc_timer_t *vbuson_timer; ++ uint32_t vbuson_timer_started; ++ uint32_t attached; ++ uint32_t probe_counter; ++ uint32_t gpwrdn; ++} dwc_otg_adp_t; ++ ++/** ++ * Attach Detect Protocol functions ++ */ ++ ++extern void dwc_otg_adp_write_reg(dwc_otg_core_if_t * core_if, uint32_t value); ++extern uint32_t dwc_otg_adp_read_reg(dwc_otg_core_if_t * core_if); ++extern uint32_t dwc_otg_adp_probe_start(dwc_otg_core_if_t * core_if); ++extern uint32_t dwc_otg_adp_sense_start(dwc_otg_core_if_t * core_if); ++extern uint32_t dwc_otg_adp_probe_stop(dwc_otg_core_if_t * core_if); ++extern uint32_t dwc_otg_adp_sense_stop(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_adp_start(dwc_otg_core_if_t * core_if, uint8_t is_host); ++extern void dwc_otg_adp_init(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_adp_remove(dwc_otg_core_if_t * core_if); ++extern int32_t dwc_otg_adp_handle_intr(dwc_otg_core_if_t * core_if); ++extern int32_t dwc_otg_adp_handle_srp_intr(dwc_otg_core_if_t * core_if); ++ ++#endif //__DWC_OTG_ADP_H__ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_attr.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_attr.c +new file mode 100644 +index 0000000..73fc330 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_attr.c +@@ -0,0 +1,1311 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.c $ ++ * $Revision: #46 $ ++ * $Date: 2012/12/12 $ ++ * $Change: 2124654 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The diagnostic interface will provide access to the controller for ++ * bringing up the hardware and testing. The Linux driver attributes ++ * feature will be used to provide the Linux Diagnostic ++ * Interface. These attributes are accessed through sysfs. ++ */ ++ ++/** @page "Linux Module Attributes" ++ * ++ * The Linux module attributes feature is used to provide the Linux ++ * Diagnostic Interface. These attributes are accessed through sysfs. ++ * The diagnostic interface will provide access to the controller for ++ * bringing up the hardware and testing. ++ ++ The following table shows the attributes. ++ <table> ++ <tr> ++ <td><b> Name</b></td> ++ <td><b> Description</b></td> ++ <td><b> Access</b></td> ++ </tr> ++ ++ <tr> ++ <td> mode </td> ++ <td> Returns the current mode: 0 for device mode, 1 for host mode</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> hnpcapable </td> ++ <td> Gets or sets the "HNP-capable" bit in the Core USB Configuraton Register. ++ Read returns the current value.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> srpcapable </td> ++ <td> Gets or sets the "SRP-capable" bit in the Core USB Configuraton Register. ++ Read returns the current value.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> hsic_connect </td> ++ <td> Gets or sets the "HSIC-Connect" bit in the GLPMCFG Register. ++ Read returns the current value.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> inv_sel_hsic </td> ++ <td> Gets or sets the "Invert Select HSIC" bit in the GLPMFG Register. ++ Read returns the current value.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> hnp </td> ++ <td> Initiates the Host Negotiation Protocol. Read returns the status.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> srp </td> ++ <td> Initiates the Session Request Protocol. Read returns the status.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> buspower </td> ++ <td> Gets or sets the Power State of the bus (0 - Off or 1 - On)</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> bussuspend </td> ++ <td> Suspends the USB bus.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> busconnected </td> ++ <td> Gets the connection status of the bus</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> gotgctl </td> ++ <td> Gets or sets the Core Control Status Register.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> gusbcfg </td> ++ <td> Gets or sets the Core USB Configuration Register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> grxfsiz </td> ++ <td> Gets or sets the Receive FIFO Size Register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> gnptxfsiz </td> ++ <td> Gets or sets the non-periodic Transmit Size Register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> gpvndctl </td> ++ <td> Gets or sets the PHY Vendor Control Register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> ggpio </td> ++ <td> Gets the value in the lower 16-bits of the General Purpose IO Register ++ or sets the upper 16 bits.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> guid </td> ++ <td> Gets or sets the value of the User ID Register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> gsnpsid </td> ++ <td> Gets the value of the Synopsys ID Regester</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> devspeed </td> ++ <td> Gets or sets the device speed setting in the DCFG register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> enumspeed </td> ++ <td> Gets the device enumeration Speed.</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> hptxfsiz </td> ++ <td> Gets the value of the Host Periodic Transmit FIFO</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> hprt0 </td> ++ <td> Gets or sets the value in the Host Port Control and Status Register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> regoffset </td> ++ <td> Sets the register offset for the next Register Access</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> regvalue </td> ++ <td> Gets or sets the value of the register at the offset in the regoffset attribute.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> remote_wakeup </td> ++ <td> On read, shows the status of Remote Wakeup. On write, initiates a remote ++ wakeup of the host. When bit 0 is 1 and Remote Wakeup is enabled, the Remote ++ Wakeup signalling bit in the Device Control Register is set for 1 ++ milli-second.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> rem_wakeup_pwrdn </td> ++ <td> On read, shows the status core - hibernated or not. On write, initiates ++ a remote wakeup of the device from Hibernation. </td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> mode_ch_tim_en </td> ++ <td> This bit is used to enable or disable the host core to wait for 200 PHY ++ clock cycles at the end of Resume to change the opmode signal to the PHY to 00 ++ after Suspend or LPM. </td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> fr_interval </td> ++ <td> On read, shows the value of HFIR Frame Interval. On write, dynamically ++ reload HFIR register during runtime. The application can write a value to this ++ register only after the Port Enable bit of the Host Port Control and Status ++ register (HPRT.PrtEnaPort) has been set </td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> disconnect_us </td> ++ <td> On read, shows the status of disconnect_device_us. On write, sets disconnect_us ++ which causes soft disconnect for 100us. Applicable only for device mode of operation.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> regdump </td> ++ <td> Dumps the contents of core registers.</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> spramdump </td> ++ <td> Dumps the contents of core registers.</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> hcddump </td> ++ <td> Dumps the current HCD state.</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> hcd_frrem </td> ++ <td> Shows the average value of the Frame Remaining ++ field in the Host Frame Number/Frame Remaining register when an SOF interrupt ++ occurs. This can be used to determine the average interrupt latency. Also ++ shows the average Frame Remaining value for start_transfer and the "a" and ++ "b" sample points. The "a" and "b" sample points may be used during debugging ++ bto determine how long it takes to execute a section of the HCD code.</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> rd_reg_test </td> ++ <td> Displays the time required to read the GNPTXFSIZ register many times ++ (the output shows the number of times the register is read). ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> wr_reg_test </td> ++ <td> Displays the time required to write the GNPTXFSIZ register many times ++ (the output shows the number of times the register is written). ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> lpm_response </td> ++ <td> Gets or sets lpm_response mode. Applicable only in device mode. ++ <td> Write</td> ++ </tr> ++ ++ <tr> ++ <td> sleep_status </td> ++ <td> Shows sleep status of device. ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> hird_thres </td> ++ <td> Gets or sets the "HIRD_Thres[3:0]" bits in the Core LPM Configuration Register. ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> besl_reject </td> ++ <td> Gets or sets the "besl_reject" bit in the Device Control Register. ++ <td> Read/Write</td> ++ </tr> ++ ++ </table> ++ ++ Example usage: ++ To get the current mode: ++ cat /sys/devices/lm0/mode ++ ++ To power down the USB: ++ echo 0 > /sys/devices/lm0/buspower ++ */ ++ ++#include "dwc_otg_os_dep.h" ++#include "dwc_os.h" ++#include "dwc_otg_driver.h" ++#include "dwc_otg_attr.h" ++#include "dwc_otg_core_if.h" ++#include "dwc_otg_pcd_if.h" ++#include "dwc_otg_hcd_if.h" ++ ++/* ++ * MACROs for defining sysfs attribute ++ */ ++ ++ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); \ ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); \ ++ uint32_t val; \ ++ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \ ++ return sprintf (buf, "%s = 0x%x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); \ ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); \ ++ uint32_t set = simple_strtoul(buf, NULL, 16); \ ++ dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\ ++ return count; \ ++} ++ ++ ++/* ++ * MACROs for defining sysfs attribute for 32-bit registers ++ */ ++#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); \ ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); \ ++ uint32_t val; \ ++ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \ ++ return sprintf (buf, "%s = 0x%08x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); \ ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); \ ++ uint32_t val = simple_strtoul(buf, NULL, 16); \ ++ dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \ ++ return count; \ ++} ++ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_RW(_otg_attr_name_,_string_) \ ++DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \ ++DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store); ++ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_RO(_otg_attr_name_,_string_) \ ++DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL); ++ ++#define DWC_OTG_DEVICE_ATTR_REG32_RW(_otg_attr_name_,_addr_,_string_) \ ++DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \ ++DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store); ++ ++#define DWC_OTG_DEVICE_ATTR_REG32_RO(_otg_attr_name_,_addr_,_string_) \ ++DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL); ++ ++/** @name Functions for Show/Store of Attributes */ ++/**@{*/ ++ ++/** ++ * Show the register offset of the Register Access. ++ */ ++static ssize_t regoffset_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ return snprintf(buf, sizeof("0xFFFFFFFF\n") + 1, "0x%08x\n", ++ otg_dev->os_dep.reg_offset); ++} ++ ++/** ++ * Set the register offset for the next Register Access Read/Write ++ */ ++static ssize_t regoffset_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ uint32_t offset = simple_strtoul(buf, NULL, 16); ++ if (offset < SZ_256K) { ++ otg_dev->os_dep.reg_offset = offset; ++ } else { ++ dev_err(_dev, "invalid offset\n"); ++ } ++ ++ return count; ++} ++ ++DEVICE_ATTR(regoffset, S_IRUGO | S_IWUSR, regoffset_show, regoffset_store); ++ ++/** ++ * Show the value of the register at the offset in the reg_offset ++ * attribute. ++ */ ++static ssize_t regvalue_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ uint32_t val; ++ volatile uint32_t *addr; ++ ++ if (otg_dev->os_dep.reg_offset != 0xFFFFFFFF && 0 != otg_dev->os_dep.base) { ++ /* Calculate the address */ ++ addr = (uint32_t *) (otg_dev->os_dep.reg_offset + ++ (uint8_t *) otg_dev->os_dep.base); ++ val = DWC_READ_REG32(addr); ++ return snprintf(buf, ++ sizeof("Reg@0xFFFFFFFF = 0xFFFFFFFF\n") + 1, ++ "Reg@0x%06x = 0x%08x\n", otg_dev->os_dep.reg_offset, ++ val); ++ } else { ++ dev_err(_dev, "Invalid offset (0x%0x)\n", otg_dev->os_dep.reg_offset); ++ return sprintf(buf, "invalid offset\n"); ++ } ++} ++ ++/** ++ * Store the value in the register at the offset in the reg_offset ++ * attribute. ++ * ++ */ ++static ssize_t regvalue_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ volatile uint32_t *addr; ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ //dev_dbg(_dev, "Offset=0x%08x Val=0x%08x\n", otg_dev->reg_offset, val); ++ if (otg_dev->os_dep.reg_offset != 0xFFFFFFFF && 0 != otg_dev->os_dep.base) { ++ /* Calculate the address */ ++ addr = (uint32_t *) (otg_dev->os_dep.reg_offset + ++ (uint8_t *) otg_dev->os_dep.base); ++ DWC_WRITE_REG32(addr, val); ++ } else { ++ dev_err(_dev, "Invalid Register Offset (0x%08x)\n", ++ otg_dev->os_dep.reg_offset); ++ } ++ return count; ++} ++ ++DEVICE_ATTR(regvalue, S_IRUGO | S_IWUSR, regvalue_show, regvalue_store); ++ ++/* ++ * Attributes ++ */ ++DWC_OTG_DEVICE_ATTR_BITFIELD_RO(mode, "Mode"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hnpcapable, "HNPCapable"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(srpcapable, "SRPCapable"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hsic_connect, "HSIC Connect"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(inv_sel_hsic, "Invert Select HSIC"); ++ ++//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(buspower,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode"); ++//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(bussuspend,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RO(busconnected, "Bus Connected"); ++ ++DWC_OTG_DEVICE_ATTR_REG32_RW(gotgctl, 0, "GOTGCTL"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(gusbcfg, ++ &(otg_dev->core_if->core_global_regs->gusbcfg), ++ "GUSBCFG"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(grxfsiz, ++ &(otg_dev->core_if->core_global_regs->grxfsiz), ++ "GRXFSIZ"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(gnptxfsiz, ++ &(otg_dev->core_if->core_global_regs->gnptxfsiz), ++ "GNPTXFSIZ"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(gpvndctl, ++ &(otg_dev->core_if->core_global_regs->gpvndctl), ++ "GPVNDCTL"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(ggpio, ++ &(otg_dev->core_if->core_global_regs->ggpio), ++ "GGPIO"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(guid, &(otg_dev->core_if->core_global_regs->guid), ++ "GUID"); ++DWC_OTG_DEVICE_ATTR_REG32_RO(gsnpsid, ++ &(otg_dev->core_if->core_global_regs->gsnpsid), ++ "GSNPSID"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(devspeed, "Device Speed"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RO(enumspeed, "Device Enumeration Speed"); ++ ++DWC_OTG_DEVICE_ATTR_REG32_RO(hptxfsiz, ++ &(otg_dev->core_if->core_global_regs->hptxfsiz), ++ "HPTXFSIZ"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(hprt0, otg_dev->core_if->host_if->hprt0, "HPRT0"); ++ ++/** ++ * @todo Add code to initiate the HNP. ++ */ ++/** ++ * Show the HNP status bit ++ */ ++static ssize_t hnp_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ return sprintf(buf, "HstNegScs = 0x%x\n", ++ dwc_otg_get_hnpstatus(otg_dev->core_if)); ++} ++ ++/** ++ * Set the HNP Request bit ++ */ ++static ssize_t hnp_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ uint32_t in = simple_strtoul(buf, NULL, 16); ++ dwc_otg_set_hnpreq(otg_dev->core_if, in); ++ return count; ++} ++ ++DEVICE_ATTR(hnp, 0644, hnp_show, hnp_store); ++ ++/** ++ * @todo Add code to initiate the SRP. ++ */ ++/** ++ * Show the SRP status bit ++ */ ++static ssize_t srp_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_HOST_ONLY ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ return sprintf(buf, "SesReqScs = 0x%x\n", ++ dwc_otg_get_srpstatus(otg_dev->core_if)); ++#else ++ return sprintf(buf, "Host Only Mode!\n"); ++#endif ++} ++ ++/** ++ * Set the SRP Request bit ++ */ ++static ssize_t srp_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++#ifndef DWC_HOST_ONLY ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ dwc_otg_pcd_initiate_srp(otg_dev->pcd); ++#endif ++ return count; ++} ++ ++DEVICE_ATTR(srp, 0644, srp_show, srp_store); ++ ++/** ++ * @todo Need to do more for power on/off? ++ */ ++/** ++ * Show the Bus Power status ++ */ ++static ssize_t buspower_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ return sprintf(buf, "Bus Power = 0x%x\n", ++ dwc_otg_get_prtpower(otg_dev->core_if)); ++} ++ ++/** ++ * Set the Bus Power status ++ */ ++static ssize_t buspower_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ uint32_t on = simple_strtoul(buf, NULL, 16); ++ dwc_otg_set_prtpower(otg_dev->core_if, on); ++ return count; ++} ++ ++DEVICE_ATTR(buspower, 0644, buspower_show, buspower_store); ++ ++/** ++ * @todo Need to do more for suspend? ++ */ ++/** ++ * Show the Bus Suspend status ++ */ ++static ssize_t bussuspend_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ return sprintf(buf, "Bus Suspend = 0x%x\n", ++ dwc_otg_get_prtsuspend(otg_dev->core_if)); ++} ++ ++/** ++ * Set the Bus Suspend status ++ */ ++static ssize_t bussuspend_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ uint32_t in = simple_strtoul(buf, NULL, 16); ++ dwc_otg_set_prtsuspend(otg_dev->core_if, in); ++ return count; ++} ++ ++DEVICE_ATTR(bussuspend, 0644, bussuspend_show, bussuspend_store); ++ ++/** ++ * Show the Mode Change Ready Timer status ++ */ ++static ssize_t mode_ch_tim_en_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ return sprintf(buf, "Mode Change Ready Timer Enable = 0x%x\n", ++ dwc_otg_get_mode_ch_tim(otg_dev->core_if)); ++} ++ ++/** ++ * Set the Mode Change Ready Timer status ++ */ ++static ssize_t mode_ch_tim_en_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ uint32_t in = simple_strtoul(buf, NULL, 16); ++ dwc_otg_set_mode_ch_tim(otg_dev->core_if, in); ++ return count; ++} ++ ++DEVICE_ATTR(mode_ch_tim_en, 0644, mode_ch_tim_en_show, mode_ch_tim_en_store); ++ ++/** ++ * Show the value of HFIR Frame Interval bitfield ++ */ ++static ssize_t fr_interval_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ return sprintf(buf, "Frame Interval = 0x%x\n", ++ dwc_otg_get_fr_interval(otg_dev->core_if)); ++} ++ ++/** ++ * Set the HFIR Frame Interval value ++ */ ++static ssize_t fr_interval_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ uint32_t in = simple_strtoul(buf, NULL, 10); ++ dwc_otg_set_fr_interval(otg_dev->core_if, in); ++ return count; ++} ++ ++DEVICE_ATTR(fr_interval, 0644, fr_interval_show, fr_interval_store); ++ ++/** ++ * Show the status of Remote Wakeup. ++ */ ++static ssize_t remote_wakeup_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_HOST_ONLY ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev =platform_get_drvdata(lm_dev); ++ ++ ++ return sprintf(buf, ++ "Remote Wakeup Sig = %d Enabled = %d LPM Remote Wakeup = %d\n", ++ dwc_otg_get_remotewakesig(otg_dev->core_if), ++ dwc_otg_pcd_get_rmwkup_enable(otg_dev->pcd), ++ dwc_otg_get_lpm_remotewakeenabled(otg_dev->core_if)); ++#else ++ return sprintf(buf, "Host Only Mode!\n"); ++#endif /* DWC_HOST_ONLY */ ++} ++ ++/** ++ * Initiate a remote wakeup of the host. The Device control register ++ * Remote Wakeup Signal bit is written if the PCD Remote wakeup enable ++ * flag is set. ++ * ++ */ ++static ssize_t remote_wakeup_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++#ifndef DWC_HOST_ONLY ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ ++ if (val & 1) { ++ dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 1); ++ } else { ++ dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 0); ++ } ++#endif /* DWC_HOST_ONLY */ ++ return count; ++} ++ ++DEVICE_ATTR(remote_wakeup, S_IRUGO | S_IWUSR, remote_wakeup_show, ++ remote_wakeup_store); ++ ++/** ++ * Show the whether core is hibernated or not. ++ */ ++static ssize_t rem_wakeup_pwrdn_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_HOST_ONLY ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ if (dwc_otg_get_core_state(otg_dev->core_if)) { ++ DWC_PRINTF("Core is in hibernation\n"); ++ } else { ++ DWC_PRINTF("Core is not in hibernation\n"); ++ } ++#endif /* DWC_HOST_ONLY */ ++ return 0; ++} ++ ++extern int dwc_otg_device_hibernation_restore(dwc_otg_core_if_t * core_if, ++ int rem_wakeup, int reset); ++ ++/** ++ * Initiate a remote wakeup of the device to exit from hibernation. ++ */ ++static ssize_t rem_wakeup_pwrdn_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++#ifndef DWC_HOST_ONLY ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ dwc_otg_device_hibernation_restore(otg_dev->core_if, 1, 0); ++#endif ++ return count; ++} ++ ++DEVICE_ATTR(rem_wakeup_pwrdn, S_IRUGO | S_IWUSR, rem_wakeup_pwrdn_show, ++ rem_wakeup_pwrdn_store); ++ ++static ssize_t disconnect_us(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ++#ifndef DWC_HOST_ONLY ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ DWC_PRINTF("The Passed value is %04x\n", val); ++ ++ dwc_otg_pcd_disconnect_us(otg_dev->pcd, 50); ++ ++#endif /* DWC_HOST_ONLY */ ++ return count; ++} ++ ++DEVICE_ATTR(disconnect_us, S_IWUSR, 0, disconnect_us); ++ ++/** ++ * Dump global registers and either host or device registers (depending on the ++ * current mode of the core). ++ */ ++static ssize_t regdump_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ dwc_otg_dump_global_registers(otg_dev->core_if); ++ if (dwc_otg_is_host_mode(otg_dev->core_if)) { ++ dwc_otg_dump_host_registers(otg_dev->core_if); ++ } else { ++ dwc_otg_dump_dev_registers(otg_dev->core_if); ++ ++ } ++ return sprintf(buf, "Register Dump\n"); ++} ++ ++DEVICE_ATTR(regdump, S_IRUGO | S_IWUSR, regdump_show, 0); ++ ++/** ++ * Dump global registers and either host or device registers (depending on the ++ * current mode of the core). ++ */ ++static ssize_t spramdump_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ dwc_otg_dump_spram(otg_dev->core_if); ++ ++ return sprintf(buf, "SPRAM Dump\n"); ++} ++ ++DEVICE_ATTR(spramdump, S_IRUGO | S_IWUSR, spramdump_show, 0); ++ ++/** ++ * Dump the current hcd state. ++ */ ++static ssize_t hcddump_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_DEVICE_ONLY ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev =platform_get_drvdata(lm_dev); ++ ++ ++ dwc_otg_hcd_dump_state(otg_dev->hcd); ++#endif /* DWC_DEVICE_ONLY */ ++ return sprintf(buf, "HCD Dump\n"); ++} ++ ++DEVICE_ATTR(hcddump, S_IRUGO | S_IWUSR, hcddump_show, 0); ++ ++/** ++ * Dump the average frame remaining at SOF. This can be used to ++ * determine average interrupt latency. Frame remaining is also shown for ++ * start transfer and two additional sample points. ++ */ ++static ssize_t hcd_frrem_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_DEVICE_ONLY ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ dwc_otg_hcd_dump_frrem(otg_dev->hcd); ++#endif /* DWC_DEVICE_ONLY */ ++ return sprintf(buf, "HCD Dump Frame Remaining\n"); ++} ++ ++DEVICE_ATTR(hcd_frrem, S_IRUGO | S_IWUSR, hcd_frrem_show, 0); ++ ++/** ++ * Displays the time required to read the GNPTXFSIZ register many times (the ++ * output shows the number of times the register is read). ++ */ ++#define RW_REG_COUNT 10000000 ++#define MSEC_PER_JIFFIE 1000/HZ ++static ssize_t rd_reg_test_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ int i; ++ int time; ++ int start_jiffies; ++ ++ printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n", ++ HZ, MSEC_PER_JIFFIE, loops_per_jiffy); ++ start_jiffies = jiffies; ++ for (i = 0; i < RW_REG_COUNT; i++) { ++ dwc_otg_get_gnptxfsiz(otg_dev->core_if); ++ } ++ time = jiffies - start_jiffies; ++ return sprintf(buf, ++ "Time to read GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n", ++ RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); ++} ++ ++DEVICE_ATTR(rd_reg_test, S_IRUGO | S_IWUSR, rd_reg_test_show, 0); ++ ++/** ++ * Displays the time required to write the GNPTXFSIZ register many times (the ++ * output shows the number of times the register is written). ++ */ ++static ssize_t wr_reg_test_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ uint32_t reg_val; ++ int i; ++ int time; ++ int start_jiffies; ++ ++ printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n", ++ HZ, MSEC_PER_JIFFIE, loops_per_jiffy); ++ reg_val = dwc_otg_get_gnptxfsiz(otg_dev->core_if); ++ start_jiffies = jiffies; ++ for (i = 0; i < RW_REG_COUNT; i++) { ++ dwc_otg_set_gnptxfsiz(otg_dev->core_if, reg_val); ++ } ++ time = jiffies - start_jiffies; ++ return sprintf(buf, ++ "Time to write GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n", ++ RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); ++} ++ ++DEVICE_ATTR(wr_reg_test, S_IRUGO | S_IWUSR, wr_reg_test_show, 0); ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ ++/** ++* Show the lpm_response attribute. ++*/ ++static ssize_t lpmresp_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if)) ++ return sprintf(buf, "** LPM is DISABLED **\n"); ++ ++ if (!dwc_otg_is_device_mode(otg_dev->core_if)) { ++ return sprintf(buf, "** Current mode is not device mode\n"); ++ } ++ return sprintf(buf, "lpm_response = %d\n", ++ dwc_otg_get_lpmresponse(otg_dev->core_if)); ++} ++ ++/** ++* Store the lpm_response attribute. ++*/ ++static ssize_t lpmresp_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ ++ if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if)) { ++ return 0; ++ } ++ ++ if (!dwc_otg_is_device_mode(otg_dev->core_if)) { ++ return 0; ++ } ++ ++ dwc_otg_set_lpmresponse(otg_dev->core_if, val); ++ return count; ++} ++ ++DEVICE_ATTR(lpm_response, S_IRUGO | S_IWUSR, lpmresp_show, lpmresp_store); ++ ++/** ++* Show the besl_reject attribute. ++*/ ++static ssize_t beslreject_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if)) ++ return sprintf(buf, "** LPM is DISABLED **\n"); ++ if (!dwc_otg_get_param_besl_enable(otg_dev->core_if)) ++ return sprintf(buf, "** EnBesl is DISABLED **\n"); ++ ++ if (!dwc_otg_is_device_mode(otg_dev->core_if)) { ++ return sprintf(buf, "** Current mode is not device mode\n"); ++ } ++ ++ return sprintf(buf, "besl_reject = %d\n", ++ dwc_otg_get_beslreject(otg_dev->core_if)); ++} ++ ++/** ++* Store the besl_reject attribute. ++*/ ++static ssize_t beslreject_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ ++ if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if)) { ++ return 0; ++ } ++ ++ if (!dwc_otg_get_param_besl_enable(otg_dev->core_if)) { ++ return 0; ++ } ++ ++ if (!dwc_otg_is_device_mode(otg_dev->core_if)) { ++ return 0; ++ } ++ ++ dwc_otg_set_beslreject(otg_dev->core_if,val); ++ ++ return count; ++} ++ ++DEVICE_ATTR(besl_reject, S_IRUGO | S_IWUSR, beslreject_show, beslreject_store); ++ ++/** ++* Show the hird_thresh attribute. ++*/ ++static ssize_t hirdthresh_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if)) ++ return sprintf(buf, "** LPM is DISABLED **\n"); ++ ++ if (!dwc_otg_is_device_mode(otg_dev->core_if)) { ++ return sprintf(buf, "** Current mode is not device mode\n"); ++ } ++ ++ return sprintf(buf, "hirdthresh = 0x%x\n", ++ dwc_otg_get_hirdthresh(otg_dev->core_if)); ++} ++ ++/** ++* Store the hird_thresh attribute. ++*/ ++static ssize_t hirdthresh_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ ++ if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if)) { ++ return 0; ++ } ++ ++ if (!dwc_otg_is_device_mode(otg_dev->core_if)) { ++ return 0; ++ } ++ ++ dwc_otg_set_hirdthresh(otg_dev->core_if,val); ++ ++ return count; ++} ++ ++DEVICE_ATTR(hird_thres, S_IRUGO | S_IWUSR, hirdthresh_show, hirdthresh_store); ++ ++/** ++* Show the sleep_status attribute. ++*/ ++static ssize_t sleepstatus_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ return sprintf(buf, "Sleep Status = %d\n", ++ dwc_otg_get_lpm_portsleepstatus(otg_dev->core_if)); ++} ++ ++/** ++ * Store the sleep_status attribure. ++ */ ++static ssize_t sleepstatus_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct platform_device *lm_dev = container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(lm_dev); ++ ++ ++ dwc_otg_core_if_t *core_if = otg_dev->core_if; ++ ++ if (dwc_otg_get_lpm_portsleepstatus(otg_dev->core_if)) { ++ if (dwc_otg_is_host_mode(core_if)) { ++ ++ DWC_PRINTF("Host initiated resume\n"); ++ dwc_otg_set_prtresume(otg_dev->core_if, 1); ++ } ++ } ++ ++ return count; ++} ++ ++DEVICE_ATTR(sleep_status, S_IRUGO | S_IWUSR, sleepstatus_show, ++ sleepstatus_store); ++ ++#endif /* CONFIG_USB_DWC_OTG_LPM_ENABLE */ ++ ++/**@}*/ ++ ++/** ++ * Create the device files ++ */ ++void dwc_otg_attr_create(struct platform_device *dev) ++{ ++ int error; ++ ++ error = device_create_file(&dev->dev, &dev_attr_regoffset); ++ error = device_create_file(&dev->dev, &dev_attr_regvalue); ++ error = device_create_file(&dev->dev, &dev_attr_mode); ++ error = device_create_file(&dev->dev, &dev_attr_hnpcapable); ++ error = device_create_file(&dev->dev, &dev_attr_srpcapable); ++ error = device_create_file(&dev->dev, &dev_attr_hsic_connect); ++ error = device_create_file(&dev->dev, &dev_attr_inv_sel_hsic); ++ error = device_create_file(&dev->dev, &dev_attr_hnp); ++ error = device_create_file(&dev->dev, &dev_attr_srp); ++ error = device_create_file(&dev->dev, &dev_attr_buspower); ++ error = device_create_file(&dev->dev, &dev_attr_bussuspend); ++ error = device_create_file(&dev->dev, &dev_attr_mode_ch_tim_en); ++ error = device_create_file(&dev->dev, &dev_attr_fr_interval); ++ error = device_create_file(&dev->dev, &dev_attr_busconnected); ++ error = device_create_file(&dev->dev, &dev_attr_gotgctl); ++ error = device_create_file(&dev->dev, &dev_attr_gusbcfg); ++ error = device_create_file(&dev->dev, &dev_attr_grxfsiz); ++ error = device_create_file(&dev->dev, &dev_attr_gnptxfsiz); ++ error = device_create_file(&dev->dev, &dev_attr_gpvndctl); ++ error = device_create_file(&dev->dev, &dev_attr_ggpio); ++ error = device_create_file(&dev->dev, &dev_attr_guid); ++ error = device_create_file(&dev->dev, &dev_attr_gsnpsid); ++ error = device_create_file(&dev->dev, &dev_attr_devspeed); ++ error = device_create_file(&dev->dev, &dev_attr_enumspeed); ++ error = device_create_file(&dev->dev, &dev_attr_hptxfsiz); ++ error = device_create_file(&dev->dev, &dev_attr_hprt0); ++ error = device_create_file(&dev->dev, &dev_attr_remote_wakeup); ++ error = device_create_file(&dev->dev, &dev_attr_rem_wakeup_pwrdn); ++ error = device_create_file(&dev->dev, &dev_attr_disconnect_us); ++ error = device_create_file(&dev->dev, &dev_attr_regdump); ++ error = device_create_file(&dev->dev, &dev_attr_spramdump); ++ error = device_create_file(&dev->dev, &dev_attr_hcddump); ++ error = device_create_file(&dev->dev, &dev_attr_hcd_frrem); ++ error = device_create_file(&dev->dev, &dev_attr_rd_reg_test); ++ error = device_create_file(&dev->dev, &dev_attr_wr_reg_test); ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ error = device_create_file(&dev->dev, &dev_attr_lpm_response); ++ error = device_create_file(&dev->dev, &dev_attr_sleep_status); ++ error = device_create_file(&dev->dev, &dev_attr_besl_reject); ++ error = device_create_file(&dev->dev, &dev_attr_hird_thres); ++#endif ++} ++ ++/** ++ * Remove the device files ++ */ ++void dwc_otg_attr_remove(struct platform_device *dev ++ ++ ) ++{ ++ device_remove_file(&dev->dev, &dev_attr_regoffset); ++ device_remove_file(&dev->dev, &dev_attr_regvalue); ++ device_remove_file(&dev->dev, &dev_attr_mode); ++ device_remove_file(&dev->dev, &dev_attr_hnpcapable); ++ device_remove_file(&dev->dev, &dev_attr_srpcapable); ++ device_remove_file(&dev->dev, &dev_attr_hsic_connect); ++ device_remove_file(&dev->dev, &dev_attr_inv_sel_hsic); ++ device_remove_file(&dev->dev, &dev_attr_hnp); ++ device_remove_file(&dev->dev, &dev_attr_srp); ++ device_remove_file(&dev->dev, &dev_attr_buspower); ++ device_remove_file(&dev->dev, &dev_attr_bussuspend); ++ device_remove_file(&dev->dev, &dev_attr_mode_ch_tim_en); ++ device_remove_file(&dev->dev, &dev_attr_fr_interval); ++ device_remove_file(&dev->dev, &dev_attr_busconnected); ++ device_remove_file(&dev->dev, &dev_attr_gotgctl); ++ device_remove_file(&dev->dev, &dev_attr_gusbcfg); ++ device_remove_file(&dev->dev, &dev_attr_grxfsiz); ++ device_remove_file(&dev->dev, &dev_attr_gnptxfsiz); ++ device_remove_file(&dev->dev, &dev_attr_gpvndctl); ++ device_remove_file(&dev->dev, &dev_attr_ggpio); ++ device_remove_file(&dev->dev, &dev_attr_guid); ++ device_remove_file(&dev->dev, &dev_attr_gsnpsid); ++ device_remove_file(&dev->dev, &dev_attr_devspeed); ++ device_remove_file(&dev->dev, &dev_attr_enumspeed); ++ device_remove_file(&dev->dev, &dev_attr_hptxfsiz); ++ device_remove_file(&dev->dev, &dev_attr_hprt0); ++ device_remove_file(&dev->dev, &dev_attr_remote_wakeup); ++ device_remove_file(&dev->dev, &dev_attr_rem_wakeup_pwrdn); ++ device_remove_file(&dev->dev, &dev_attr_disconnect_us); ++ device_remove_file(&dev->dev, &dev_attr_regdump); ++ device_remove_file(&dev->dev, &dev_attr_spramdump); ++ device_remove_file(&dev->dev, &dev_attr_hcddump); ++ device_remove_file(&dev->dev, &dev_attr_hcd_frrem); ++ device_remove_file(&dev->dev, &dev_attr_rd_reg_test); ++ device_remove_file(&dev->dev, &dev_attr_wr_reg_test); ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ device_remove_file(&dev->dev, &dev_attr_lpm_response); ++ device_remove_file(&dev->dev, &dev_attr_sleep_status); ++ device_remove_file(&dev->dev, &dev_attr_besl_reject); ++ device_remove_file(&dev->dev, &dev_attr_hird_thres); ++#endif ++} +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_attr.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_attr.h +new file mode 100644 +index 0000000..4d43296 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_attr.h +@@ -0,0 +1,78 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.h $ ++ * $Revision: #13 $ ++ * $Date: 2010/06/21 $ ++ * $Change: 1532021 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_OTG_ATTR_H__) ++#define __DWC_OTG_ATTR_H__ ++ ++#include <linux/platform_device.h> ++ ++/** @file ++ * This file contains the interface to the Linux device attributes. ++ */ ++extern struct device_attribute dev_attr_regoffset; ++extern struct device_attribute dev_attr_regvalue; ++ ++extern struct device_attribute dev_attr_mode; ++extern struct device_attribute dev_attr_hnpcapable; ++extern struct device_attribute dev_attr_srpcapable; ++extern struct device_attribute dev_attr_hnp; ++extern struct device_attribute dev_attr_srp; ++extern struct device_attribute dev_attr_buspower; ++extern struct device_attribute dev_attr_bussuspend; ++extern struct device_attribute dev_attr_mode_ch_tim_en; ++extern struct device_attribute dev_attr_fr_interval; ++extern struct device_attribute dev_attr_busconnected; ++extern struct device_attribute dev_attr_gotgctl; ++extern struct device_attribute dev_attr_gusbcfg; ++extern struct device_attribute dev_attr_grxfsiz; ++extern struct device_attribute dev_attr_gnptxfsiz; ++extern struct device_attribute dev_attr_gpvndctl; ++extern struct device_attribute dev_attr_ggpio; ++extern struct device_attribute dev_attr_guid; ++extern struct device_attribute dev_attr_gsnpsid; ++extern struct device_attribute dev_attr_devspeed; ++extern struct device_attribute dev_attr_enumspeed; ++extern struct device_attribute dev_attr_hptxfsiz; ++extern struct device_attribute dev_attr_hprt0; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++extern struct device_attribute dev_attr_lpm_response; ++extern struct device_attribute devi_attr_sleep_status; ++#endif ++ ++void dwc_otg_attr_create(struct platform_device *dev ++ ); ++ ++void dwc_otg_attr_remove(struct platform_device *dev ++ ++ ); ++#endif +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_cfi.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_cfi.c +new file mode 100644 +index 0000000..bbb3d32 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_cfi.c +@@ -0,0 +1,1876 @@ ++/* ========================================================================== ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * This file contains the most of the CFI(Core Feature Interface) ++ * implementation for the OTG. ++ */ ++ ++#ifdef DWC_UTE_CFI ++ ++#include "dwc_otg_pcd.h" ++#include "dwc_otg_cfi.h" ++ ++/** This definition should actually migrate to the Portability Library */ ++#define DWC_CONSTANT_CPU_TO_LE16(x) (x) ++ ++extern dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t * pcd, u16 wIndex); ++ ++static int cfi_core_features_buf(uint8_t * buf, uint16_t buflen); ++static int cfi_get_feature_value(uint8_t * buf, uint16_t buflen, ++ struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *ctrl_req); ++static int cfi_set_feature_value(struct dwc_otg_pcd *pcd); ++static int cfi_ep_get_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req); ++static int cfi_ep_get_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req); ++static int cfi_ep_get_align_val(uint8_t * buf, struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req); ++static int cfi_preproc_reset(struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req); ++static void cfi_free_ep_bs_dyn_data(cfi_ep_t * cfiep); ++ ++static uint16_t get_dfifo_size(dwc_otg_core_if_t * core_if); ++static int32_t get_rxfifo_size(dwc_otg_core_if_t * core_if, uint16_t wValue); ++static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue); ++ ++static uint8_t resize_fifos(dwc_otg_core_if_t * core_if); ++ ++/** This is the header of the all features descriptor */ ++static cfi_all_features_header_t all_props_desc_header = { ++ .wVersion = DWC_CONSTANT_CPU_TO_LE16(0x100), ++ .wCoreID = DWC_CONSTANT_CPU_TO_LE16(CFI_CORE_ID_OTG), ++ .wNumFeatures = DWC_CONSTANT_CPU_TO_LE16(9), ++}; ++ ++/** This is an array of statically allocated feature descriptors */ ++static cfi_feature_desc_header_t prop_descs[] = { ++ ++ /* FT_ID_DMA_MODE */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_MODE), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(1), ++ }, ++ ++ /* FT_ID_DMA_BUFFER_SETUP */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFFER_SETUP), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), ++ }, ++ ++ /* FT_ID_DMA_BUFF_ALIGN */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFF_ALIGN), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), ++ }, ++ ++ /* FT_ID_DMA_CONCAT_SETUP */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CONCAT_SETUP), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ //.wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), ++ }, ++ ++ /* FT_ID_DMA_CIRCULAR */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CIRCULAR), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), ++ }, ++ ++ /* FT_ID_THRESHOLD_SETUP */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_THRESHOLD_SETUP), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), ++ }, ++ ++ /* FT_ID_DFIFO_DEPTH */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DFIFO_DEPTH), ++ .bmAttributes = CFI_FEATURE_ATTR_RO, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), ++ }, ++ ++ /* FT_ID_TX_FIFO_DEPTH */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_TX_FIFO_DEPTH), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), ++ }, ++ ++ /* FT_ID_RX_FIFO_DEPTH */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_RX_FIFO_DEPTH), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), ++ } ++}; ++ ++/** The table of feature names */ ++cfi_string_t prop_name_table[] = { ++ {FT_ID_DMA_MODE, "dma_mode"}, ++ {FT_ID_DMA_BUFFER_SETUP, "buffer_setup"}, ++ {FT_ID_DMA_BUFF_ALIGN, "buffer_align"}, ++ {FT_ID_DMA_CONCAT_SETUP, "concat_setup"}, ++ {FT_ID_DMA_CIRCULAR, "buffer_circular"}, ++ {FT_ID_THRESHOLD_SETUP, "threshold_setup"}, ++ {FT_ID_DFIFO_DEPTH, "dfifo_depth"}, ++ {FT_ID_TX_FIFO_DEPTH, "txfifo_depth"}, ++ {FT_ID_RX_FIFO_DEPTH, "rxfifo_depth"}, ++ {} ++}; ++ ++/************************************************************************/ ++ ++/** ++ * Returns the name of the feature by its ID ++ * or NULL if no featute ID matches. ++ * ++ */ ++const uint8_t *get_prop_name(uint16_t prop_id, int *len) ++{ ++ cfi_string_t *pstr; ++ *len = 0; ++ ++ for (pstr = prop_name_table; pstr && pstr->s; pstr++) { ++ if (pstr->id == prop_id) { ++ *len = DWC_STRLEN(pstr->s); ++ return pstr->s; ++ } ++ } ++ return NULL; ++} ++ ++/** ++ * This function handles all CFI specific control requests. ++ * ++ * Return a negative value to stall the DCE. ++ */ ++int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl) ++{ ++ int retval = 0; ++ dwc_otg_pcd_ep_t *ep = NULL; ++ cfiobject_t *cfi = pcd->cfi; ++ struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd); ++ uint16_t wLen = DWC_LE16_TO_CPU(&ctrl->wLength); ++ uint16_t wValue = DWC_LE16_TO_CPU(&ctrl->wValue); ++ uint16_t wIndex = DWC_LE16_TO_CPU(&ctrl->wIndex); ++ uint32_t regaddr = 0; ++ uint32_t regval = 0; ++ ++ /* Save this Control Request in the CFI object. ++ * The data field will be assigned in the data stage completion CB function. ++ */ ++ cfi->ctrl_req = *ctrl; ++ cfi->ctrl_req.data = NULL; ++ ++ cfi->need_gadget_att = 0; ++ cfi->need_status_in_complete = 0; ++ ++ switch (ctrl->bRequest) { ++ case VEN_CORE_GET_FEATURES: ++ retval = cfi_core_features_buf(cfi->buf_in.buf, CFI_IN_BUF_LEN); ++ if (retval >= 0) { ++ //dump_msg(cfi->buf_in.buf, retval); ++ ep = &pcd->ep0; ++ ++ retval = min((uint16_t) retval, wLen); ++ /* Transfer this buffer to the host through the EP0-IN EP */ ++ ep->dwc_ep.dma_addr = cfi->buf_in.addr; ++ ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf; ++ ep->dwc_ep.xfer_buff = cfi->buf_in.buf; ++ ep->dwc_ep.xfer_len = retval; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ pcd->ep0_pending = 1; ++ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); ++ } ++ retval = 0; ++ break; ++ ++ case VEN_CORE_GET_FEATURE: ++ CFI_INFO("VEN_CORE_GET_FEATURE\n"); ++ retval = cfi_get_feature_value(cfi->buf_in.buf, CFI_IN_BUF_LEN, ++ pcd, ctrl); ++ if (retval >= 0) { ++ ep = &pcd->ep0; ++ ++ retval = min((uint16_t) retval, wLen); ++ /* Transfer this buffer to the host through the EP0-IN EP */ ++ ep->dwc_ep.dma_addr = cfi->buf_in.addr; ++ ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf; ++ ep->dwc_ep.xfer_buff = cfi->buf_in.buf; ++ ep->dwc_ep.xfer_len = retval; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ pcd->ep0_pending = 1; ++ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); ++ } ++ CFI_INFO("VEN_CORE_GET_FEATURE=%d\n", retval); ++ dump_msg(cfi->buf_in.buf, retval); ++ break; ++ ++ case VEN_CORE_SET_FEATURE: ++ CFI_INFO("VEN_CORE_SET_FEATURE\n"); ++ /* Set up an XFER to get the data stage of the control request, ++ * which is the new value of the feature to be modified. ++ */ ++ ep = &pcd->ep0; ++ ep->dwc_ep.is_in = 0; ++ ep->dwc_ep.dma_addr = cfi->buf_out.addr; ++ ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf; ++ ep->dwc_ep.xfer_buff = cfi->buf_out.buf; ++ ep->dwc_ep.xfer_len = wLen; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ pcd->ep0_pending = 1; ++ /* Read the control write's data stage */ ++ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); ++ retval = 0; ++ break; ++ ++ case VEN_CORE_RESET_FEATURES: ++ CFI_INFO("VEN_CORE_RESET_FEATURES\n"); ++ cfi->need_gadget_att = 1; ++ cfi->need_status_in_complete = 1; ++ retval = cfi_preproc_reset(pcd, ctrl); ++ CFI_INFO("VEN_CORE_RESET_FEATURES = (%d)\n", retval); ++ break; ++ ++ case VEN_CORE_ACTIVATE_FEATURES: ++ CFI_INFO("VEN_CORE_ACTIVATE_FEATURES\n"); ++ break; ++ ++ case VEN_CORE_READ_REGISTER: ++ CFI_INFO("VEN_CORE_READ_REGISTER\n"); ++ /* wValue optionally contains the HI WORD of the register offset and ++ * wIndex contains the LOW WORD of the register offset ++ */ ++ if (wValue == 0) { ++ /* @TODO - MAS - fix the access to the base field */ ++ regaddr = 0; ++ //regaddr = (uint32_t) pcd->otg_dev->os_dep.base; ++ //GET_CORE_IF(pcd)->co ++ regaddr |= wIndex; ++ } else { ++ regaddr = (wValue << 16) | wIndex; ++ } ++ ++ /* Read a 32-bit value of the memory at the regaddr */ ++ regval = DWC_READ_REG32((uint32_t *) regaddr); ++ ++ ep = &pcd->ep0; ++ dwc_memcpy(cfi->buf_in.buf, ®val, sizeof(uint32_t)); ++ ep->dwc_ep.is_in = 1; ++ ep->dwc_ep.dma_addr = cfi->buf_in.addr; ++ ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf; ++ ep->dwc_ep.xfer_buff = cfi->buf_in.buf; ++ ep->dwc_ep.xfer_len = wLen; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ pcd->ep0_pending = 1; ++ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); ++ cfi->need_gadget_att = 0; ++ retval = 0; ++ break; ++ ++ case VEN_CORE_WRITE_REGISTER: ++ CFI_INFO("VEN_CORE_WRITE_REGISTER\n"); ++ /* Set up an XFER to get the data stage of the control request, ++ * which is the new value of the register to be modified. ++ */ ++ ep = &pcd->ep0; ++ ep->dwc_ep.is_in = 0; ++ ep->dwc_ep.dma_addr = cfi->buf_out.addr; ++ ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf; ++ ep->dwc_ep.xfer_buff = cfi->buf_out.buf; ++ ep->dwc_ep.xfer_len = wLen; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ pcd->ep0_pending = 1; ++ /* Read the control write's data stage */ ++ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); ++ retval = 0; ++ break; ++ ++ default: ++ retval = -DWC_E_NOT_SUPPORTED; ++ break; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function prepares the core features descriptors and copies its ++ * raw representation into the buffer <buf>. ++ * ++ * The buffer structure is as follows: ++ * all_features_header (8 bytes) ++ * features_#1 (8 bytes + feature name string length) ++ * features_#2 (8 bytes + feature name string length) ++ * ..... ++ * features_#n - where n=the total count of feature descriptors ++ */ ++static int cfi_core_features_buf(uint8_t * buf, uint16_t buflen) ++{ ++ cfi_feature_desc_header_t *prop_hdr = prop_descs; ++ cfi_feature_desc_header_t *prop; ++ cfi_all_features_header_t *all_props_hdr = &all_props_desc_header; ++ cfi_all_features_header_t *tmp; ++ uint8_t *tmpbuf = buf; ++ const uint8_t *pname = NULL; ++ int i, j, namelen = 0, totlen; ++ ++ /* Prepare and copy the core features into the buffer */ ++ CFI_INFO("%s:\n", __func__); ++ ++ tmp = (cfi_all_features_header_t *) tmpbuf; ++ *tmp = *all_props_hdr; ++ tmpbuf += CFI_ALL_FEATURES_HDR_LEN; ++ ++ j = sizeof(prop_descs) / sizeof(cfi_all_features_header_t); ++ for (i = 0; i < j; i++, prop_hdr++) { ++ pname = get_prop_name(prop_hdr->wFeatureID, &namelen); ++ prop = (cfi_feature_desc_header_t *) tmpbuf; ++ *prop = *prop_hdr; ++ ++ prop->bNameLen = namelen; ++ prop->wLength = ++ DWC_CONSTANT_CPU_TO_LE16(CFI_FEATURE_DESC_HDR_LEN + ++ namelen); ++ ++ tmpbuf += CFI_FEATURE_DESC_HDR_LEN; ++ dwc_memcpy(tmpbuf, pname, namelen); ++ tmpbuf += namelen; ++ } ++ ++ totlen = tmpbuf - buf; ++ ++ if (totlen > 0) { ++ tmp = (cfi_all_features_header_t *) buf; ++ tmp->wTotalLen = DWC_CONSTANT_CPU_TO_LE16(totlen); ++ } ++ ++ return totlen; ++} ++ ++/** ++ * This function releases all the dynamic memory in the CFI object. ++ */ ++static void cfi_release(cfiobject_t * cfiobj) ++{ ++ cfi_ep_t *cfiep; ++ dwc_list_link_t *tmp; ++ ++ CFI_INFO("%s\n", __func__); ++ ++ if (cfiobj->buf_in.buf) { ++ DWC_DMA_FREE(CFI_IN_BUF_LEN, cfiobj->buf_in.buf, ++ cfiobj->buf_in.addr); ++ cfiobj->buf_in.buf = NULL; ++ } ++ ++ if (cfiobj->buf_out.buf) { ++ DWC_DMA_FREE(CFI_OUT_BUF_LEN, cfiobj->buf_out.buf, ++ cfiobj->buf_out.addr); ++ cfiobj->buf_out.buf = NULL; ++ } ++ ++ /* Free the Buffer Setup values for each EP */ ++ //list_for_each_entry(cfiep, &cfiobj->active_eps, lh) { ++ DWC_LIST_FOREACH(tmp, &cfiobj->active_eps) { ++ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ cfi_free_ep_bs_dyn_data(cfiep); ++ } ++} ++ ++/** ++ * This function frees the dynamically allocated EP buffer setup data. ++ */ ++static void cfi_free_ep_bs_dyn_data(cfi_ep_t * cfiep) ++{ ++ if (cfiep->bm_sg) { ++ DWC_FREE(cfiep->bm_sg); ++ cfiep->bm_sg = NULL; ++ } ++ ++ if (cfiep->bm_align) { ++ DWC_FREE(cfiep->bm_align); ++ cfiep->bm_align = NULL; ++ } ++ ++ if (cfiep->bm_concat) { ++ if (NULL != cfiep->bm_concat->wTxBytes) { ++ DWC_FREE(cfiep->bm_concat->wTxBytes); ++ cfiep->bm_concat->wTxBytes = NULL; ++ } ++ DWC_FREE(cfiep->bm_concat); ++ cfiep->bm_concat = NULL; ++ } ++} ++ ++/** ++ * This function initializes the default values of the features ++ * for a specific endpoint and should be called only once when ++ * the EP is enabled first time. ++ */ ++static int cfi_ep_init_defaults(struct dwc_otg_pcd *pcd, cfi_ep_t * cfiep) ++{ ++ int retval = 0; ++ ++ cfiep->bm_sg = DWC_ALLOC(sizeof(ddma_sg_buffer_setup_t)); ++ if (NULL == cfiep->bm_sg) { ++ CFI_INFO("Failed to allocate memory for SG feature value\n"); ++ return -DWC_E_NO_MEMORY; ++ } ++ dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t)); ++ ++ /* For the Concatenation feature's default value we do not allocate ++ * memory for the wTxBytes field - it will be done in the set_feature_value ++ * request handler. ++ */ ++ cfiep->bm_concat = DWC_ALLOC(sizeof(ddma_concat_buffer_setup_t)); ++ if (NULL == cfiep->bm_concat) { ++ CFI_INFO ++ ("Failed to allocate memory for CONCATENATION feature value\n"); ++ DWC_FREE(cfiep->bm_sg); ++ return -DWC_E_NO_MEMORY; ++ } ++ dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t)); ++ ++ cfiep->bm_align = DWC_ALLOC(sizeof(ddma_align_buffer_setup_t)); ++ if (NULL == cfiep->bm_align) { ++ CFI_INFO ++ ("Failed to allocate memory for Alignment feature value\n"); ++ DWC_FREE(cfiep->bm_sg); ++ DWC_FREE(cfiep->bm_concat); ++ return -DWC_E_NO_MEMORY; ++ } ++ dwc_memset(cfiep->bm_align, 0, sizeof(ddma_align_buffer_setup_t)); ++ ++ return retval; ++} ++ ++/** ++ * The callback function that notifies the CFI on the activation of ++ * an endpoint in the PCD. The following steps are done in this function: ++ * ++ * Create a dynamically allocated cfi_ep_t object (a CFI wrapper to the PCD's ++ * active endpoint) ++ * Create MAX_DMA_DESCS_PER_EP count DMA Descriptors for the EP ++ * Set the Buffer Mode to standard ++ * Initialize the default values for all EP modes (SG, Circular, Concat, Align) ++ * Add the cfi_ep_t object to the list of active endpoints in the CFI object ++ */ ++static int cfi_ep_enable(struct cfiobject *cfi, struct dwc_otg_pcd *pcd, ++ struct dwc_otg_pcd_ep *ep) ++{ ++ cfi_ep_t *cfiep; ++ int retval = -DWC_E_NOT_SUPPORTED; ++ ++ CFI_INFO("%s: epname=%s; epnum=0x%02x\n", __func__, ++ "EP_" /*ep->ep.name */ , ep->desc->bEndpointAddress); ++ /* MAS - Check whether this endpoint already is in the list */ ++ cfiep = get_cfi_ep_by_pcd_ep(cfi, ep); ++ ++ if (NULL == cfiep) { ++ /* Allocate a cfi_ep_t object */ ++ cfiep = DWC_ALLOC(sizeof(cfi_ep_t)); ++ if (NULL == cfiep) { ++ CFI_INFO ++ ("Unable to allocate memory for <cfiep> in function %s\n", ++ __func__); ++ return -DWC_E_NO_MEMORY; ++ } ++ dwc_memset(cfiep, 0, sizeof(cfi_ep_t)); ++ ++ /* Save the dwc_otg_pcd_ep pointer in the cfiep object */ ++ cfiep->ep = ep; ++ ++ /* Allocate the DMA Descriptors chain of MAX_DMA_DESCS_PER_EP count */ ++ ep->dwc_ep.descs = ++ DWC_DMA_ALLOC(MAX_DMA_DESCS_PER_EP * ++ sizeof(dwc_otg_dma_desc_t), ++ &ep->dwc_ep.descs_dma_addr); ++ ++ if (NULL == ep->dwc_ep.descs) { ++ DWC_FREE(cfiep); ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ DWC_LIST_INIT(&cfiep->lh); ++ ++ /* Set the buffer mode to BM_STANDARD. It will be modified ++ * when building descriptors for a specific buffer mode */ ++ ep->dwc_ep.buff_mode = BM_STANDARD; ++ ++ /* Create and initialize the default values for this EP's Buffer modes */ ++ if ((retval = cfi_ep_init_defaults(pcd, cfiep)) < 0) ++ return retval; ++ ++ /* Add the cfi_ep_t object to the CFI object's list of active endpoints */ ++ DWC_LIST_INSERT_TAIL(&cfi->active_eps, &cfiep->lh); ++ retval = 0; ++ } else { /* The sought EP already is in the list */ ++ CFI_INFO("%s: The sought EP already is in the list\n", ++ __func__); ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function is called when the data stage of a 3-stage Control Write request ++ * is complete. ++ * ++ */ ++static int cfi_ctrl_write_complete(struct cfiobject *cfi, ++ struct dwc_otg_pcd *pcd) ++{ ++ uint32_t addr, reg_value; ++ uint16_t wIndex, wValue; ++ uint8_t bRequest; ++ uint8_t *buf = cfi->buf_out.buf; ++ //struct usb_ctrlrequest *ctrl_req = &cfi->ctrl_req_saved; ++ struct cfi_usb_ctrlrequest *ctrl_req = &cfi->ctrl_req; ++ int retval = -DWC_E_NOT_SUPPORTED; ++ ++ CFI_INFO("%s\n", __func__); ++ ++ bRequest = ctrl_req->bRequest; ++ wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex); ++ wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue); ++ ++ /* ++ * Save the pointer to the data stage in the ctrl_req's <data> field. ++ * The request should be already saved in the command stage by now. ++ */ ++ ctrl_req->data = cfi->buf_out.buf; ++ cfi->need_status_in_complete = 0; ++ cfi->need_gadget_att = 0; ++ ++ switch (bRequest) { ++ case VEN_CORE_WRITE_REGISTER: ++ /* The buffer contains raw data of the new value for the register */ ++ reg_value = *((uint32_t *) buf); ++ if (wValue == 0) { ++ addr = 0; ++ //addr = (uint32_t) pcd->otg_dev->os_dep.base; ++ addr += wIndex; ++ } else { ++ addr = (wValue << 16) | wIndex; ++ } ++ ++ //writel(reg_value, addr); ++ ++ retval = 0; ++ cfi->need_status_in_complete = 1; ++ break; ++ ++ case VEN_CORE_SET_FEATURE: ++ /* The buffer contains raw data of the new value of the feature */ ++ retval = cfi_set_feature_value(pcd); ++ if (retval < 0) ++ return retval; ++ ++ cfi->need_status_in_complete = 1; ++ break; ++ ++ default: ++ break; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function builds the DMA descriptors for the SG buffer mode. ++ */ ++static void cfi_build_sg_descs(struct cfiobject *cfi, cfi_ep_t * cfiep, ++ dwc_otg_pcd_request_t * req) ++{ ++ struct dwc_otg_pcd_ep *ep = cfiep->ep; ++ ddma_sg_buffer_setup_t *sgval = cfiep->bm_sg; ++ struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs; ++ struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs; ++ dma_addr_t buff_addr = req->dma; ++ int i; ++ uint32_t txsize, off; ++ ++ txsize = sgval->wSize; ++ off = sgval->bOffset; ++ ++// CFI_INFO("%s: %s TXSIZE=0x%08x; OFFSET=0x%08x\n", ++// __func__, cfiep->ep->ep.name, txsize, off); ++ ++ for (i = 0; i < sgval->bCount; i++) { ++ desc->status.b.bs = BS_HOST_BUSY; ++ desc->buf = buff_addr; ++ desc->status.b.l = 0; ++ desc->status.b.ioc = 0; ++ desc->status.b.sp = 0; ++ desc->status.b.bytes = txsize; ++ desc->status.b.bs = BS_HOST_READY; ++ ++ /* Set the next address of the buffer */ ++ buff_addr += txsize + off; ++ desc_last = desc; ++ desc++; ++ } ++ ++ /* Set the last, ioc and sp bits on the Last DMA Descriptor */ ++ desc_last->status.b.l = 1; ++ desc_last->status.b.ioc = 1; ++ desc_last->status.b.sp = ep->dwc_ep.sent_zlp; ++ /* Save the last DMA descriptor pointer */ ++ cfiep->dma_desc_last = desc_last; ++ cfiep->desc_count = sgval->bCount; ++} ++ ++/** ++ * This function builds the DMA descriptors for the Concatenation buffer mode. ++ */ ++static void cfi_build_concat_descs(struct cfiobject *cfi, cfi_ep_t * cfiep, ++ dwc_otg_pcd_request_t * req) ++{ ++ struct dwc_otg_pcd_ep *ep = cfiep->ep; ++ ddma_concat_buffer_setup_t *concatval = cfiep->bm_concat; ++ struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs; ++ struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs; ++ dma_addr_t buff_addr = req->dma; ++ int i; ++ uint16_t *txsize; ++ ++ txsize = concatval->wTxBytes; ++ ++ for (i = 0; i < concatval->hdr.bDescCount; i++) { ++ desc->buf = buff_addr; ++ desc->status.b.bs = BS_HOST_BUSY; ++ desc->status.b.l = 0; ++ desc->status.b.ioc = 0; ++ desc->status.b.sp = 0; ++ desc->status.b.bytes = *txsize; ++ desc->status.b.bs = BS_HOST_READY; ++ ++ txsize++; ++ /* Set the next address of the buffer */ ++ buff_addr += UGETW(ep->desc->wMaxPacketSize); ++ desc_last = desc; ++ desc++; ++ } ++ ++ /* Set the last, ioc and sp bits on the Last DMA Descriptor */ ++ desc_last->status.b.l = 1; ++ desc_last->status.b.ioc = 1; ++ desc_last->status.b.sp = ep->dwc_ep.sent_zlp; ++ cfiep->dma_desc_last = desc_last; ++ cfiep->desc_count = concatval->hdr.bDescCount; ++} ++ ++/** ++ * This function builds the DMA descriptors for the Circular buffer mode ++ */ ++static void cfi_build_circ_descs(struct cfiobject *cfi, cfi_ep_t * cfiep, ++ dwc_otg_pcd_request_t * req) ++{ ++ /* @todo: MAS - add implementation when this feature needs to be tested */ ++} ++ ++/** ++ * This function builds the DMA descriptors for the Alignment buffer mode ++ */ ++static void cfi_build_align_descs(struct cfiobject *cfi, cfi_ep_t * cfiep, ++ dwc_otg_pcd_request_t * req) ++{ ++ struct dwc_otg_pcd_ep *ep = cfiep->ep; ++ ddma_align_buffer_setup_t *alignval = cfiep->bm_align; ++ struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs; ++ dma_addr_t buff_addr = req->dma; ++ ++ desc->status.b.bs = BS_HOST_BUSY; ++ desc->status.b.l = 1; ++ desc->status.b.ioc = 1; ++ desc->status.b.sp = ep->dwc_ep.sent_zlp; ++ desc->status.b.bytes = req->length; ++ /* Adjust the buffer alignment */ ++ desc->buf = (buff_addr + alignval->bAlign); ++ desc->status.b.bs = BS_HOST_READY; ++ cfiep->dma_desc_last = desc; ++ cfiep->desc_count = 1; ++} ++ ++/** ++ * This function builds the DMA descriptors chain for different modes of the ++ * buffer setup of an endpoint. ++ */ ++static void cfi_build_descriptors(struct cfiobject *cfi, ++ struct dwc_otg_pcd *pcd, ++ struct dwc_otg_pcd_ep *ep, ++ dwc_otg_pcd_request_t * req) ++{ ++ cfi_ep_t *cfiep; ++ ++ /* Get the cfiep by the dwc_otg_pcd_ep */ ++ cfiep = get_cfi_ep_by_pcd_ep(cfi, ep); ++ if (NULL == cfiep) { ++ CFI_INFO("%s: Unable to find a matching active endpoint\n", ++ __func__); ++ return; ++ } ++ ++ cfiep->xfer_len = req->length; ++ ++ /* Iterate through all the DMA descriptors */ ++ switch (cfiep->ep->dwc_ep.buff_mode) { ++ case BM_SG: ++ cfi_build_sg_descs(cfi, cfiep, req); ++ break; ++ ++ case BM_CONCAT: ++ cfi_build_concat_descs(cfi, cfiep, req); ++ break; ++ ++ case BM_CIRCULAR: ++ cfi_build_circ_descs(cfi, cfiep, req); ++ break; ++ ++ case BM_ALIGN: ++ cfi_build_align_descs(cfi, cfiep, req); ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++/** ++ * Allocate DMA buffer for different Buffer modes. ++ */ ++static void *cfi_ep_alloc_buf(struct cfiobject *cfi, struct dwc_otg_pcd *pcd, ++ struct dwc_otg_pcd_ep *ep, dma_addr_t * dma, ++ unsigned size, gfp_t flags) ++{ ++ return DWC_DMA_ALLOC(size, dma); ++} ++ ++/** ++ * This function initializes the CFI object. ++ */ ++int init_cfi(cfiobject_t * cfiobj) ++{ ++ CFI_INFO("%s\n", __func__); ++ ++ /* Allocate a buffer for IN XFERs */ ++ cfiobj->buf_in.buf = ++ DWC_DMA_ALLOC(CFI_IN_BUF_LEN, &cfiobj->buf_in.addr); ++ if (NULL == cfiobj->buf_in.buf) { ++ CFI_INFO("Unable to allocate buffer for INs\n"); ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ /* Allocate a buffer for OUT XFERs */ ++ cfiobj->buf_out.buf = ++ DWC_DMA_ALLOC(CFI_OUT_BUF_LEN, &cfiobj->buf_out.addr); ++ if (NULL == cfiobj->buf_out.buf) { ++ CFI_INFO("Unable to allocate buffer for OUT\n"); ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ /* Initialize the callback function pointers */ ++ cfiobj->ops.release = cfi_release; ++ cfiobj->ops.ep_enable = cfi_ep_enable; ++ cfiobj->ops.ctrl_write_complete = cfi_ctrl_write_complete; ++ cfiobj->ops.build_descriptors = cfi_build_descriptors; ++ cfiobj->ops.ep_alloc_buf = cfi_ep_alloc_buf; ++ ++ /* Initialize the list of active endpoints in the CFI object */ ++ DWC_LIST_INIT(&cfiobj->active_eps); ++ ++ return 0; ++} ++ ++/** ++ * This function reads the required feature's current value into the buffer ++ * ++ * @retval: Returns negative as error, or the data length of the feature ++ */ ++static int cfi_get_feature_value(uint8_t * buf, uint16_t buflen, ++ struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *ctrl_req) ++{ ++ int retval = -DWC_E_NOT_SUPPORTED; ++ struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd); ++ uint16_t dfifo, rxfifo, txfifo; ++ ++ switch (ctrl_req->wIndex) { ++ /* Whether the DDMA is enabled or not */ ++ case FT_ID_DMA_MODE: ++ *buf = (coreif->dma_enable && coreif->dma_desc_enable) ? 1 : 0; ++ retval = 1; ++ break; ++ ++ case FT_ID_DMA_BUFFER_SETUP: ++ retval = cfi_ep_get_sg_val(buf, pcd, ctrl_req); ++ break; ++ ++ case FT_ID_DMA_BUFF_ALIGN: ++ retval = cfi_ep_get_align_val(buf, pcd, ctrl_req); ++ break; ++ ++ case FT_ID_DMA_CONCAT_SETUP: ++ retval = cfi_ep_get_concat_val(buf, pcd, ctrl_req); ++ break; ++ ++ case FT_ID_DMA_CIRCULAR: ++ CFI_INFO("GetFeature value (FT_ID_DMA_CIRCULAR)\n"); ++ break; ++ ++ case FT_ID_THRESHOLD_SETUP: ++ CFI_INFO("GetFeature value (FT_ID_THRESHOLD_SETUP)\n"); ++ break; ++ ++ case FT_ID_DFIFO_DEPTH: ++ dfifo = get_dfifo_size(coreif); ++ *((uint16_t *) buf) = dfifo; ++ retval = sizeof(uint16_t); ++ break; ++ ++ case FT_ID_TX_FIFO_DEPTH: ++ retval = get_txfifo_size(pcd, ctrl_req->wValue); ++ if (retval >= 0) { ++ txfifo = retval; ++ *((uint16_t *) buf) = txfifo; ++ retval = sizeof(uint16_t); ++ } ++ break; ++ ++ case FT_ID_RX_FIFO_DEPTH: ++ retval = get_rxfifo_size(coreif, ctrl_req->wValue); ++ if (retval >= 0) { ++ rxfifo = retval; ++ *((uint16_t *) buf) = rxfifo; ++ retval = sizeof(uint16_t); ++ } ++ break; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function resets the SG for the specified EP to its default value ++ */ ++static int cfi_reset_sg_val(cfi_ep_t * cfiep) ++{ ++ dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t)); ++ return 0; ++} ++ ++/** ++ * This function resets the Alignment for the specified EP to its default value ++ */ ++static int cfi_reset_align_val(cfi_ep_t * cfiep) ++{ ++ dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t)); ++ return 0; ++} ++ ++/** ++ * This function resets the Concatenation for the specified EP to its default value ++ * This function will also set the value of the wTxBytes field to NULL after ++ * freeing the memory previously allocated for this field. ++ */ ++static int cfi_reset_concat_val(cfi_ep_t * cfiep) ++{ ++ /* First we need to free the wTxBytes field */ ++ if (cfiep->bm_concat->wTxBytes) { ++ DWC_FREE(cfiep->bm_concat->wTxBytes); ++ cfiep->bm_concat->wTxBytes = NULL; ++ } ++ ++ dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t)); ++ return 0; ++} ++ ++/** ++ * This function resets all the buffer setups of the specified endpoint ++ */ ++static int cfi_ep_reset_all_setup_vals(cfi_ep_t * cfiep) ++{ ++ cfi_reset_sg_val(cfiep); ++ cfi_reset_align_val(cfiep); ++ cfi_reset_concat_val(cfiep); ++ return 0; ++} ++ ++static int cfi_handle_reset_fifo_val(struct dwc_otg_pcd *pcd, uint8_t ep_addr, ++ uint8_t rx_rst, uint8_t tx_rst) ++{ ++ int retval = -DWC_E_INVALID; ++ uint16_t tx_siz[15]; ++ uint16_t rx_siz = 0; ++ dwc_otg_pcd_ep_t *ep = NULL; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params; ++ ++ if (rx_rst) { ++ rx_siz = params->dev_rx_fifo_size; ++ params->dev_rx_fifo_size = GET_CORE_IF(pcd)->init_rxfsiz; ++ } ++ ++ if (tx_rst) { ++ if (ep_addr == 0) { ++ int i; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ tx_siz[i] = ++ core_if->core_params->dev_tx_fifo_size[i]; ++ core_if->core_params->dev_tx_fifo_size[i] = ++ core_if->init_txfsiz[i]; ++ } ++ } else { ++ ++ ep = get_ep_by_addr(pcd, ep_addr); ++ ++ if (NULL == ep) { ++ CFI_INFO ++ ("%s: Unable to get the endpoint addr=0x%02x\n", ++ __func__, ep_addr); ++ return -DWC_E_INVALID; ++ } ++ ++ tx_siz[0] = ++ params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - ++ 1]; ++ params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = ++ GET_CORE_IF(pcd)->init_txfsiz[ep-> ++ dwc_ep.tx_fifo_num - ++ 1]; ++ } ++ } ++ ++ if (resize_fifos(GET_CORE_IF(pcd))) { ++ retval = 0; ++ } else { ++ CFI_INFO ++ ("%s: Error resetting the feature Reset All(FIFO size)\n", ++ __func__); ++ if (rx_rst) { ++ params->dev_rx_fifo_size = rx_siz; ++ } ++ ++ if (tx_rst) { ++ if (ep_addr == 0) { ++ int i; ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; ++ i++) { ++ core_if-> ++ core_params->dev_tx_fifo_size[i] = ++ tx_siz[i]; ++ } ++ } else { ++ params->dev_tx_fifo_size[ep-> ++ dwc_ep.tx_fifo_num - ++ 1] = tx_siz[0]; ++ } ++ } ++ retval = -DWC_E_INVALID; ++ } ++ return retval; ++} ++ ++static int cfi_handle_reset_all(struct dwc_otg_pcd *pcd, uint8_t addr) ++{ ++ int retval = 0; ++ cfi_ep_t *cfiep; ++ cfiobject_t *cfi = pcd->cfi; ++ dwc_list_link_t *tmp; ++ ++ retval = cfi_handle_reset_fifo_val(pcd, addr, 1, 1); ++ if (retval < 0) { ++ return retval; ++ } ++ ++ /* If the EP address is known then reset the features for only that EP */ ++ if (addr) { ++ cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == cfiep) { ++ CFI_INFO("%s: Error getting the EP address 0x%02x\n", ++ __func__, addr); ++ return -DWC_E_INVALID; ++ } ++ retval = cfi_ep_reset_all_setup_vals(cfiep); ++ cfiep->ep->dwc_ep.buff_mode = BM_STANDARD; ++ } ++ /* Otherwise (wValue == 0), reset all features of all EP's */ ++ else { ++ /* Traverse all the active EP's and reset the feature(s) value(s) */ ++ //list_for_each_entry(cfiep, &cfi->active_eps, lh) { ++ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { ++ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ retval = cfi_ep_reset_all_setup_vals(cfiep); ++ cfiep->ep->dwc_ep.buff_mode = BM_STANDARD; ++ if (retval < 0) { ++ CFI_INFO ++ ("%s: Error resetting the feature Reset All\n", ++ __func__); ++ return retval; ++ } ++ } ++ } ++ return retval; ++} ++ ++static int cfi_handle_reset_dma_buff_setup(struct dwc_otg_pcd *pcd, ++ uint8_t addr) ++{ ++ int retval = 0; ++ cfi_ep_t *cfiep; ++ cfiobject_t *cfi = pcd->cfi; ++ dwc_list_link_t *tmp; ++ ++ /* If the EP address is known then reset the features for only that EP */ ++ if (addr) { ++ cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == cfiep) { ++ CFI_INFO("%s: Error getting the EP address 0x%02x\n", ++ __func__, addr); ++ return -DWC_E_INVALID; ++ } ++ retval = cfi_reset_sg_val(cfiep); ++ } ++ /* Otherwise (wValue == 0), reset all features of all EP's */ ++ else { ++ /* Traverse all the active EP's and reset the feature(s) value(s) */ ++ //list_for_each_entry(cfiep, &cfi->active_eps, lh) { ++ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { ++ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ retval = cfi_reset_sg_val(cfiep); ++ if (retval < 0) { ++ CFI_INFO ++ ("%s: Error resetting the feature Buffer Setup\n", ++ __func__); ++ return retval; ++ } ++ } ++ } ++ return retval; ++} ++ ++static int cfi_handle_reset_concat_val(struct dwc_otg_pcd *pcd, uint8_t addr) ++{ ++ int retval = 0; ++ cfi_ep_t *cfiep; ++ cfiobject_t *cfi = pcd->cfi; ++ dwc_list_link_t *tmp; ++ ++ /* If the EP address is known then reset the features for only that EP */ ++ if (addr) { ++ cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == cfiep) { ++ CFI_INFO("%s: Error getting the EP address 0x%02x\n", ++ __func__, addr); ++ return -DWC_E_INVALID; ++ } ++ retval = cfi_reset_concat_val(cfiep); ++ } ++ /* Otherwise (wValue == 0), reset all features of all EP's */ ++ else { ++ /* Traverse all the active EP's and reset the feature(s) value(s) */ ++ //list_for_each_entry(cfiep, &cfi->active_eps, lh) { ++ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { ++ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ retval = cfi_reset_concat_val(cfiep); ++ if (retval < 0) { ++ CFI_INFO ++ ("%s: Error resetting the feature Concatenation Value\n", ++ __func__); ++ return retval; ++ } ++ } ++ } ++ return retval; ++} ++ ++static int cfi_handle_reset_align_val(struct dwc_otg_pcd *pcd, uint8_t addr) ++{ ++ int retval = 0; ++ cfi_ep_t *cfiep; ++ cfiobject_t *cfi = pcd->cfi; ++ dwc_list_link_t *tmp; ++ ++ /* If the EP address is known then reset the features for only that EP */ ++ if (addr) { ++ cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == cfiep) { ++ CFI_INFO("%s: Error getting the EP address 0x%02x\n", ++ __func__, addr); ++ return -DWC_E_INVALID; ++ } ++ retval = cfi_reset_align_val(cfiep); ++ } ++ /* Otherwise (wValue == 0), reset all features of all EP's */ ++ else { ++ /* Traverse all the active EP's and reset the feature(s) value(s) */ ++ //list_for_each_entry(cfiep, &cfi->active_eps, lh) { ++ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { ++ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ retval = cfi_reset_align_val(cfiep); ++ if (retval < 0) { ++ CFI_INFO ++ ("%s: Error resetting the feature Aliignment Value\n", ++ __func__); ++ return retval; ++ } ++ } ++ } ++ return retval; ++ ++} ++ ++static int cfi_preproc_reset(struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req) ++{ ++ int retval = 0; ++ ++ switch (req->wIndex) { ++ case 0: ++ /* Reset all features */ ++ retval = cfi_handle_reset_all(pcd, req->wValue & 0xff); ++ break; ++ ++ case FT_ID_DMA_BUFFER_SETUP: ++ /* Reset the SG buffer setup */ ++ retval = ++ cfi_handle_reset_dma_buff_setup(pcd, req->wValue & 0xff); ++ break; ++ ++ case FT_ID_DMA_CONCAT_SETUP: ++ /* Reset the Concatenation buffer setup */ ++ retval = cfi_handle_reset_concat_val(pcd, req->wValue & 0xff); ++ break; ++ ++ case FT_ID_DMA_BUFF_ALIGN: ++ /* Reset the Alignment buffer setup */ ++ retval = cfi_handle_reset_align_val(pcd, req->wValue & 0xff); ++ break; ++ ++ case FT_ID_TX_FIFO_DEPTH: ++ retval = ++ cfi_handle_reset_fifo_val(pcd, req->wValue & 0xff, 0, 1); ++ pcd->cfi->need_gadget_att = 0; ++ break; ++ ++ case FT_ID_RX_FIFO_DEPTH: ++ retval = cfi_handle_reset_fifo_val(pcd, 0, 1, 0); ++ pcd->cfi->need_gadget_att = 0; ++ break; ++ default: ++ break; ++ } ++ return retval; ++} ++ ++/** ++ * This function sets a new value for the SG buffer setup. ++ */ ++static int cfi_ep_set_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd) ++{ ++ uint8_t inaddr, outaddr; ++ cfi_ep_t *epin, *epout; ++ ddma_sg_buffer_setup_t *psgval; ++ uint32_t desccount, size; ++ ++ CFI_INFO("%s\n", __func__); ++ ++ psgval = (ddma_sg_buffer_setup_t *) buf; ++ desccount = (uint32_t) psgval->bCount; ++ size = (uint32_t) psgval->wSize; ++ ++ /* Check the DMA descriptor count */ ++ if ((desccount > MAX_DMA_DESCS_PER_EP) || (desccount == 0)) { ++ CFI_INFO ++ ("%s: The count of DMA Descriptors should be between 1 and %d\n", ++ __func__, MAX_DMA_DESCS_PER_EP); ++ return -DWC_E_INVALID; ++ } ++ ++ /* Check the DMA descriptor count */ ++ ++ if (size == 0) { ++ ++ CFI_INFO("%s: The transfer size should be at least 1 byte\n", ++ __func__); ++ ++ return -DWC_E_INVALID; ++ ++ } ++ ++ inaddr = psgval->bInEndpointAddress; ++ outaddr = psgval->bOutEndpointAddress; ++ ++ epin = get_cfi_ep_by_addr(pcd->cfi, inaddr); ++ epout = get_cfi_ep_by_addr(pcd->cfi, outaddr); ++ ++ if (NULL == epin || NULL == epout) { ++ CFI_INFO ++ ("%s: Unable to get the endpoints inaddr=0x%02x outaddr=0x%02x\n", ++ __func__, inaddr, outaddr); ++ return -DWC_E_INVALID; ++ } ++ ++ epin->ep->dwc_ep.buff_mode = BM_SG; ++ dwc_memcpy(epin->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t)); ++ ++ epout->ep->dwc_ep.buff_mode = BM_SG; ++ dwc_memcpy(epout->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t)); ++ ++ return 0; ++} ++ ++/** ++ * This function sets a new value for the buffer Alignment setup. ++ */ ++static int cfi_ep_set_alignment_val(uint8_t * buf, struct dwc_otg_pcd *pcd) ++{ ++ cfi_ep_t *ep; ++ uint8_t addr; ++ ddma_align_buffer_setup_t *palignval; ++ ++ palignval = (ddma_align_buffer_setup_t *) buf; ++ addr = palignval->bEndpointAddress; ++ ++ ep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", ++ __func__, addr); ++ return -DWC_E_INVALID; ++ } ++ ++ ep->ep->dwc_ep.buff_mode = BM_ALIGN; ++ dwc_memcpy(ep->bm_align, palignval, sizeof(ddma_align_buffer_setup_t)); ++ ++ return 0; ++} ++ ++/** ++ * This function sets a new value for the Concatenation buffer setup. ++ */ ++static int cfi_ep_set_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd) ++{ ++ uint8_t addr; ++ cfi_ep_t *ep; ++ struct _ddma_concat_buffer_setup_hdr *pConcatValHdr; ++ uint16_t *pVals; ++ uint32_t desccount; ++ int i; ++ uint16_t mps; ++ ++ pConcatValHdr = (struct _ddma_concat_buffer_setup_hdr *)buf; ++ desccount = (uint32_t) pConcatValHdr->bDescCount; ++ pVals = (uint16_t *) (buf + BS_CONCAT_VAL_HDR_LEN); ++ ++ /* Check the DMA descriptor count */ ++ if (desccount > MAX_DMA_DESCS_PER_EP) { ++ CFI_INFO("%s: Maximum DMA Descriptor count should be %d\n", ++ __func__, MAX_DMA_DESCS_PER_EP); ++ return -DWC_E_INVALID; ++ } ++ ++ addr = pConcatValHdr->bEndpointAddress; ++ ep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", ++ __func__, addr); ++ return -DWC_E_INVALID; ++ } ++ ++ mps = UGETW(ep->ep->desc->wMaxPacketSize); ++ ++#if 0 ++ for (i = 0; i < desccount; i++) { ++ CFI_INFO("%s: wTxSize[%d]=0x%04x\n", __func__, i, pVals[i]); ++ } ++ CFI_INFO("%s: epname=%s; mps=%d\n", __func__, ep->ep->ep.name, mps); ++#endif ++ ++ /* Check the wTxSizes to be less than or equal to the mps */ ++ for (i = 0; i < desccount; i++) { ++ if (pVals[i] > mps) { ++ CFI_INFO ++ ("%s: ERROR - the wTxSize[%d] should be <= MPS (wTxSize=%d)\n", ++ __func__, i, pVals[i]); ++ return -DWC_E_INVALID; ++ } ++ } ++ ++ ep->ep->dwc_ep.buff_mode = BM_CONCAT; ++ dwc_memcpy(ep->bm_concat, pConcatValHdr, BS_CONCAT_VAL_HDR_LEN); ++ ++ /* Free the previously allocated storage for the wTxBytes */ ++ if (ep->bm_concat->wTxBytes) { ++ DWC_FREE(ep->bm_concat->wTxBytes); ++ } ++ ++ /* Allocate a new storage for the wTxBytes field */ ++ ep->bm_concat->wTxBytes = ++ DWC_ALLOC(sizeof(uint16_t) * pConcatValHdr->bDescCount); ++ if (NULL == ep->bm_concat->wTxBytes) { ++ CFI_INFO("%s: Unable to allocate memory\n", __func__); ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ /* Copy the new values into the wTxBytes filed */ ++ dwc_memcpy(ep->bm_concat->wTxBytes, buf + BS_CONCAT_VAL_HDR_LEN, ++ sizeof(uint16_t) * pConcatValHdr->bDescCount); ++ ++ return 0; ++} ++ ++/** ++ * This function calculates the total of all FIFO sizes ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ * @return The total of data FIFO sizes. ++ * ++ */ ++static uint16_t get_dfifo_size(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_params_t *params = core_if->core_params; ++ uint16_t dfifo_total = 0; ++ int i; ++ ++ /* The shared RxFIFO size */ ++ dfifo_total = ++ params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size; ++ ++ /* Add up each TxFIFO size to the total */ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ dfifo_total += params->dev_tx_fifo_size[i]; ++ } ++ ++ return dfifo_total; ++} ++ ++/** ++ * This function returns Rx FIFO size ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ * @return The total of data FIFO sizes. ++ * ++ */ ++static int32_t get_rxfifo_size(dwc_otg_core_if_t * core_if, uint16_t wValue) ++{ ++ switch (wValue >> 8) { ++ case 0: ++ return (core_if->pwron_rxfsiz < ++ 32768) ? core_if->pwron_rxfsiz : 32768; ++ break; ++ case 1: ++ return core_if->core_params->dev_rx_fifo_size; ++ break; ++ default: ++ return -DWC_E_INVALID; ++ break; ++ } ++} ++ ++/** ++ * This function returns Tx FIFO size for IN EP ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ * @return The total of data FIFO sizes. ++ * ++ */ ++static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ ++ ep = get_ep_by_addr(pcd, wValue & 0xff); ++ ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", ++ __func__, wValue & 0xff); ++ return -DWC_E_INVALID; ++ } ++ ++ if (!ep->dwc_ep.is_in) { ++ CFI_INFO ++ ("%s: No Tx FIFO assingned to the Out endpoint addr=0x%02x\n", ++ __func__, wValue & 0xff); ++ return -DWC_E_INVALID; ++ } ++ ++ switch (wValue >> 8) { ++ case 0: ++ return (GET_CORE_IF(pcd)->pwron_txfsiz ++ [ep->dwc_ep.tx_fifo_num - 1] < ++ 768) ? GET_CORE_IF(pcd)->pwron_txfsiz[ep-> ++ dwc_ep.tx_fifo_num ++ - 1] : 32768; ++ break; ++ case 1: ++ return GET_CORE_IF(pcd)->core_params-> ++ dev_tx_fifo_size[ep->dwc_ep.num - 1]; ++ break; ++ default: ++ return -DWC_E_INVALID; ++ break; ++ } ++} ++ ++/** ++ * This function checks if the submitted combination of ++ * device mode FIFO sizes is possible or not. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ * @return 1 if possible, 0 otherwise. ++ * ++ */ ++static uint8_t check_fifo_sizes(dwc_otg_core_if_t * core_if) ++{ ++ uint16_t dfifo_actual = 0; ++ dwc_otg_core_params_t *params = core_if->core_params; ++ uint16_t start_addr = 0; ++ int i; ++ ++ dfifo_actual = ++ params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ dfifo_actual += params->dev_tx_fifo_size[i]; ++ } ++ ++ if (dfifo_actual > core_if->total_fifo_size) { ++ return 0; ++ } ++ ++ if (params->dev_rx_fifo_size > 32768 || params->dev_rx_fifo_size < 16) ++ return 0; ++ ++ if (params->dev_nperio_tx_fifo_size > 32768 ++ || params->dev_nperio_tx_fifo_size < 16) ++ return 0; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ ++ if (params->dev_tx_fifo_size[i] > 768 ++ || params->dev_tx_fifo_size[i] < 4) ++ return 0; ++ } ++ ++ if (params->dev_rx_fifo_size > core_if->pwron_rxfsiz) ++ return 0; ++ start_addr = params->dev_rx_fifo_size; ++ ++ if (params->dev_nperio_tx_fifo_size > core_if->pwron_gnptxfsiz) ++ return 0; ++ start_addr += params->dev_nperio_tx_fifo_size; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ ++ if (params->dev_tx_fifo_size[i] > core_if->pwron_txfsiz[i]) ++ return 0; ++ start_addr += params->dev_tx_fifo_size[i]; ++ } ++ ++ return 1; ++} ++ ++/** ++ * This function resizes Device mode FIFOs ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ * @return 1 if successful, 0 otherwise ++ * ++ */ ++static uint8_t resize_fifos(dwc_otg_core_if_t * core_if) ++{ ++ int i = 0; ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ dwc_otg_core_params_t *params = core_if->core_params; ++ uint32_t rx_fifo_size; ++ fifosize_data_t nptxfifosize; ++ fifosize_data_t txfifosize[15]; ++ ++ uint32_t rx_fsz_bak; ++ uint32_t nptxfsz_bak; ++ uint32_t txfsz_bak[15]; ++ ++ uint16_t start_address; ++ uint8_t retval = 1; ++ ++ if (!check_fifo_sizes(core_if)) { ++ return 0; ++ } ++ ++ /* Configure data FIFO sizes */ ++ if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) { ++ rx_fsz_bak = DWC_READ_REG32(&global_regs->grxfsiz); ++ rx_fifo_size = params->dev_rx_fifo_size; ++ DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fifo_size); ++ ++ /* ++ * Tx FIFOs These FIFOs are numbered from 1 to 15. ++ * Indexes of the FIFO size module parameters in the ++ * dev_tx_fifo_size array and the FIFO size registers in ++ * the dtxfsiz array run from 0 to 14. ++ */ ++ ++ /* Non-periodic Tx FIFO */ ++ nptxfsz_bak = DWC_READ_REG32(&global_regs->gnptxfsiz); ++ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size; ++ start_address = params->dev_rx_fifo_size; ++ nptxfifosize.b.startaddr = start_address; ++ ++ DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfifosize.d32); ++ ++ start_address += nptxfifosize.b.depth; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ txfsz_bak[i] = DWC_READ_REG32(&global_regs->dtxfsiz[i]); ++ ++ txfifosize[i].b.depth = params->dev_tx_fifo_size[i]; ++ txfifosize[i].b.startaddr = start_address; ++ DWC_WRITE_REG32(&global_regs->dtxfsiz[i], ++ txfifosize[i].d32); ++ ++ start_address += txfifosize[i].b.depth; ++ } ++ ++ /** Check if register values are set correctly */ ++ if (rx_fifo_size != DWC_READ_REG32(&global_regs->grxfsiz)) { ++ retval = 0; ++ } ++ ++ if (nptxfifosize.d32 != DWC_READ_REG32(&global_regs->gnptxfsiz)) { ++ retval = 0; ++ } ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ if (txfifosize[i].d32 != ++ DWC_READ_REG32(&global_regs->dtxfsiz[i])) { ++ retval = 0; ++ } ++ } ++ ++ /** If register values are not set correctly, reset old values */ ++ if (retval == 0) { ++ DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fsz_bak); ++ ++ /* Non-periodic Tx FIFO */ ++ DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfsz_bak); ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ DWC_WRITE_REG32(&global_regs->dtxfsiz[i], ++ txfsz_bak[i]); ++ } ++ } ++ } else { ++ return 0; ++ } ++ ++ /* Flush the FIFOs */ ++ dwc_otg_flush_tx_fifo(core_if, 0x10); /* all Tx FIFOs */ ++ dwc_otg_flush_rx_fifo(core_if); ++ ++ return retval; ++} ++ ++/** ++ * This function sets a new value for the buffer Alignment setup. ++ */ ++static int cfi_ep_set_tx_fifo_val(uint8_t * buf, dwc_otg_pcd_t * pcd) ++{ ++ int retval; ++ uint32_t fsiz; ++ uint16_t size; ++ uint16_t ep_addr; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params; ++ tx_fifo_size_setup_t *ptxfifoval; ++ ++ ptxfifoval = (tx_fifo_size_setup_t *) buf; ++ ep_addr = ptxfifoval->bEndpointAddress; ++ size = ptxfifoval->wDepth; ++ ++ ep = get_ep_by_addr(pcd, ep_addr); ++ ++ CFI_INFO ++ ("%s: Set Tx FIFO size: endpoint addr=0x%02x, depth=%d, FIFO Num=%d\n", ++ __func__, ep_addr, size, ep->dwc_ep.tx_fifo_num); ++ ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", ++ __func__, ep_addr); ++ return -DWC_E_INVALID; ++ } ++ ++ fsiz = params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1]; ++ params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = size; ++ ++ if (resize_fifos(GET_CORE_IF(pcd))) { ++ retval = 0; ++ } else { ++ CFI_INFO ++ ("%s: Error setting the feature Tx FIFO Size for EP%d\n", ++ __func__, ep_addr); ++ params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = fsiz; ++ retval = -DWC_E_INVALID; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function sets a new value for the buffer Alignment setup. ++ */ ++static int cfi_set_rx_fifo_val(uint8_t * buf, dwc_otg_pcd_t * pcd) ++{ ++ int retval; ++ uint32_t fsiz; ++ uint16_t size; ++ dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params; ++ rx_fifo_size_setup_t *prxfifoval; ++ ++ prxfifoval = (rx_fifo_size_setup_t *) buf; ++ size = prxfifoval->wDepth; ++ ++ fsiz = params->dev_rx_fifo_size; ++ params->dev_rx_fifo_size = size; ++ ++ if (resize_fifos(GET_CORE_IF(pcd))) { ++ retval = 0; ++ } else { ++ CFI_INFO("%s: Error setting the feature Rx FIFO Size\n", ++ __func__); ++ params->dev_rx_fifo_size = fsiz; ++ retval = -DWC_E_INVALID; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function reads the SG of an EP's buffer setup into the buffer buf ++ */ ++static int cfi_ep_get_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req) ++{ ++ int retval = -DWC_E_INVALID; ++ uint8_t addr; ++ cfi_ep_t *ep; ++ ++ /* The Low Byte of the wValue contains a non-zero address of the endpoint */ ++ addr = req->wValue & 0xFF; ++ if (addr == 0) /* The address should be non-zero */ ++ return retval; ++ ++ ep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n", ++ __func__, addr); ++ return retval; ++ } ++ ++ dwc_memcpy(buf, ep->bm_sg, BS_SG_VAL_DESC_LEN); ++ retval = BS_SG_VAL_DESC_LEN; ++ return retval; ++} ++ ++/** ++ * This function reads the Concatenation value of an EP's buffer mode into ++ * the buffer buf ++ */ ++static int cfi_ep_get_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req) ++{ ++ int retval = -DWC_E_INVALID; ++ uint8_t addr; ++ cfi_ep_t *ep; ++ uint8_t desc_count; ++ ++ /* The Low Byte of the wValue contains a non-zero address of the endpoint */ ++ addr = req->wValue & 0xFF; ++ if (addr == 0) /* The address should be non-zero */ ++ return retval; ++ ++ ep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n", ++ __func__, addr); ++ return retval; ++ } ++ ++ /* Copy the header to the buffer */ ++ dwc_memcpy(buf, ep->bm_concat, BS_CONCAT_VAL_HDR_LEN); ++ /* Advance the buffer pointer by the header size */ ++ buf += BS_CONCAT_VAL_HDR_LEN; ++ ++ desc_count = ep->bm_concat->hdr.bDescCount; ++ /* Copy alll the wTxBytes to the buffer */ ++ dwc_memcpy(buf, ep->bm_concat->wTxBytes, sizeof(uid16_t) * desc_count); ++ ++ retval = BS_CONCAT_VAL_HDR_LEN + sizeof(uid16_t) * desc_count; ++ return retval; ++} ++ ++/** ++ * This function reads the buffer Alignment value of an EP's buffer mode into ++ * the buffer buf ++ * ++ * @return The total number of bytes copied to the buffer or negative error code. ++ */ ++static int cfi_ep_get_align_val(uint8_t * buf, struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req) ++{ ++ int retval = -DWC_E_INVALID; ++ uint8_t addr; ++ cfi_ep_t *ep; ++ ++ /* The Low Byte of the wValue contains a non-zero address of the endpoint */ ++ addr = req->wValue & 0xFF; ++ if (addr == 0) /* The address should be non-zero */ ++ return retval; ++ ++ ep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n", ++ __func__, addr); ++ return retval; ++ } ++ ++ dwc_memcpy(buf, ep->bm_align, BS_ALIGN_VAL_HDR_LEN); ++ retval = BS_ALIGN_VAL_HDR_LEN; ++ ++ return retval; ++} ++ ++/** ++ * This function sets a new value for the specified feature ++ * ++ * @param pcd A pointer to the PCD object ++ * ++ * @return 0 if successful, negative error code otherwise to stall the DCE. ++ */ ++static int cfi_set_feature_value(struct dwc_otg_pcd *pcd) ++{ ++ int retval = -DWC_E_NOT_SUPPORTED; ++ uint16_t wIndex, wValue; ++ uint8_t bRequest; ++ struct dwc_otg_core_if *coreif; ++ cfiobject_t *cfi = pcd->cfi; ++ struct cfi_usb_ctrlrequest *ctrl_req; ++ uint8_t *buf; ++ ctrl_req = &cfi->ctrl_req; ++ ++ buf = pcd->cfi->ctrl_req.data; ++ ++ coreif = GET_CORE_IF(pcd); ++ bRequest = ctrl_req->bRequest; ++ wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex); ++ wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue); ++ ++ /* See which feature is to be modified */ ++ switch (wIndex) { ++ case FT_ID_DMA_BUFFER_SETUP: ++ /* Modify the feature */ ++ if ((retval = cfi_ep_set_sg_val(buf, pcd)) < 0) ++ return retval; ++ ++ /* And send this request to the gadget */ ++ cfi->need_gadget_att = 1; ++ break; ++ ++ case FT_ID_DMA_BUFF_ALIGN: ++ if ((retval = cfi_ep_set_alignment_val(buf, pcd)) < 0) ++ return retval; ++ cfi->need_gadget_att = 1; ++ break; ++ ++ case FT_ID_DMA_CONCAT_SETUP: ++ /* Modify the feature */ ++ if ((retval = cfi_ep_set_concat_val(buf, pcd)) < 0) ++ return retval; ++ cfi->need_gadget_att = 1; ++ break; ++ ++ case FT_ID_DMA_CIRCULAR: ++ CFI_INFO("FT_ID_DMA_CIRCULAR\n"); ++ break; ++ ++ case FT_ID_THRESHOLD_SETUP: ++ CFI_INFO("FT_ID_THRESHOLD_SETUP\n"); ++ break; ++ ++ case FT_ID_DFIFO_DEPTH: ++ CFI_INFO("FT_ID_DFIFO_DEPTH\n"); ++ break; ++ ++ case FT_ID_TX_FIFO_DEPTH: ++ CFI_INFO("FT_ID_TX_FIFO_DEPTH\n"); ++ if ((retval = cfi_ep_set_tx_fifo_val(buf, pcd)) < 0) ++ return retval; ++ cfi->need_gadget_att = 0; ++ break; ++ ++ case FT_ID_RX_FIFO_DEPTH: ++ CFI_INFO("FT_ID_RX_FIFO_DEPTH\n"); ++ if ((retval = cfi_set_rx_fifo_val(buf, pcd)) < 0) ++ return retval; ++ cfi->need_gadget_att = 0; ++ break; ++ } ++ ++ return retval; ++} ++ ++#endif //DWC_UTE_CFI +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_cfi.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_cfi.h +new file mode 100644 +index 0000000..55fd337 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_cfi.h +@@ -0,0 +1,320 @@ ++/* ========================================================================== ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_OTG_CFI_H__) ++#define __DWC_OTG_CFI_H__ ++ ++#include "dwc_otg_pcd.h" ++#include "dwc_cfi_common.h" ++ ++/** ++ * @file ++ * This file contains the CFI related OTG PCD specific common constants, ++ * interfaces(functions and macros) and data structures.The CFI Protocol is an ++ * optional interface for internal testing purposes that a DUT may implement to ++ * support testing of configurable features. ++ * ++ */ ++ ++struct dwc_otg_pcd; ++struct dwc_otg_pcd_ep; ++ ++/** OTG CFI Features (properties) ID constants */ ++/** This is a request for all Core Features */ ++#define FT_ID_DMA_MODE 0x0001 ++#define FT_ID_DMA_BUFFER_SETUP 0x0002 ++#define FT_ID_DMA_BUFF_ALIGN 0x0003 ++#define FT_ID_DMA_CONCAT_SETUP 0x0004 ++#define FT_ID_DMA_CIRCULAR 0x0005 ++#define FT_ID_THRESHOLD_SETUP 0x0006 ++#define FT_ID_DFIFO_DEPTH 0x0007 ++#define FT_ID_TX_FIFO_DEPTH 0x0008 ++#define FT_ID_RX_FIFO_DEPTH 0x0009 ++ ++/**********************************************************/ ++#define CFI_INFO_DEF ++ ++#ifdef CFI_INFO_DEF ++#define CFI_INFO(fmt...) DWC_PRINTF("CFI: " fmt); ++#else ++#define CFI_INFO(fmt...) ++#endif ++ ++#define min(x,y) ({ \ ++ x < y ? x : y; }) ++ ++#define max(x,y) ({ \ ++ x > y ? x : y; }) ++ ++/** ++ * Descriptor DMA SG Buffer setup structure (SG buffer). This structure is ++ * also used for setting up a buffer for Circular DDMA. ++ */ ++struct _ddma_sg_buffer_setup { ++#define BS_SG_VAL_DESC_LEN 6 ++ /* The OUT EP address */ ++ uint8_t bOutEndpointAddress; ++ /* The IN EP address */ ++ uint8_t bInEndpointAddress; ++ /* Number of bytes to put between transfer segments (must be DWORD boundaries) */ ++ uint8_t bOffset; ++ /* The number of transfer segments (a DMA descriptors per each segment) */ ++ uint8_t bCount; ++ /* Size (in byte) of each transfer segment */ ++ uint16_t wSize; ++} __attribute__ ((packed)); ++typedef struct _ddma_sg_buffer_setup ddma_sg_buffer_setup_t; ++ ++/** Descriptor DMA Concatenation Buffer setup structure */ ++struct _ddma_concat_buffer_setup_hdr { ++#define BS_CONCAT_VAL_HDR_LEN 4 ++ /* The endpoint for which the buffer is to be set up */ ++ uint8_t bEndpointAddress; ++ /* The count of descriptors to be used */ ++ uint8_t bDescCount; ++ /* The total size of the transfer */ ++ uint16_t wSize; ++} __attribute__ ((packed)); ++typedef struct _ddma_concat_buffer_setup_hdr ddma_concat_buffer_setup_hdr_t; ++ ++/** Descriptor DMA Concatenation Buffer setup structure */ ++struct _ddma_concat_buffer_setup { ++ /* The SG header */ ++ ddma_concat_buffer_setup_hdr_t hdr; ++ ++ /* The XFER sizes pointer (allocated dynamically) */ ++ uint16_t *wTxBytes; ++} __attribute__ ((packed)); ++typedef struct _ddma_concat_buffer_setup ddma_concat_buffer_setup_t; ++ ++/** Descriptor DMA Alignment Buffer setup structure */ ++struct _ddma_align_buffer_setup { ++#define BS_ALIGN_VAL_HDR_LEN 2 ++ uint8_t bEndpointAddress; ++ uint8_t bAlign; ++} __attribute__ ((packed)); ++typedef struct _ddma_align_buffer_setup ddma_align_buffer_setup_t; ++ ++/** Transmit FIFO Size setup structure */ ++struct _tx_fifo_size_setup { ++ uint8_t bEndpointAddress; ++ uint16_t wDepth; ++} __attribute__ ((packed)); ++typedef struct _tx_fifo_size_setup tx_fifo_size_setup_t; ++ ++/** Transmit FIFO Size setup structure */ ++struct _rx_fifo_size_setup { ++ uint16_t wDepth; ++} __attribute__ ((packed)); ++typedef struct _rx_fifo_size_setup rx_fifo_size_setup_t; ++ ++/** ++ * struct cfi_usb_ctrlrequest - the CFI implementation of the struct usb_ctrlrequest ++ * This structure encapsulates the standard usb_ctrlrequest and adds a pointer ++ * to the data returned in the data stage of a 3-stage Control Write requests. ++ */ ++struct cfi_usb_ctrlrequest { ++ uint8_t bRequestType; ++ uint8_t bRequest; ++ uint16_t wValue; ++ uint16_t wIndex; ++ uint16_t wLength; ++ uint8_t *data; ++} UPACKED; ++ ++/*---------------------------------------------------------------------------*/ ++ ++/** ++ * The CFI wrapper of the enabled and activated dwc_otg_pcd_ep structures. ++ * This structure is used to store the buffer setup data for any ++ * enabled endpoint in the PCD. ++ */ ++struct cfi_ep { ++ /* Entry for the list container */ ++ dwc_list_link_t lh; ++ /* Pointer to the active PCD endpoint structure */ ++ struct dwc_otg_pcd_ep *ep; ++ /* The last descriptor in the chain of DMA descriptors of the endpoint */ ++ struct dwc_otg_dma_desc *dma_desc_last; ++ /* The SG feature value */ ++ ddma_sg_buffer_setup_t *bm_sg; ++ /* The Circular feature value */ ++ ddma_sg_buffer_setup_t *bm_circ; ++ /* The Concatenation feature value */ ++ ddma_concat_buffer_setup_t *bm_concat; ++ /* The Alignment feature value */ ++ ddma_align_buffer_setup_t *bm_align; ++ /* XFER length */ ++ uint32_t xfer_len; ++ /* ++ * Count of DMA descriptors currently used. ++ * The total should not exceed the MAX_DMA_DESCS_PER_EP value ++ * defined in the dwc_otg_cil.h ++ */ ++ uint32_t desc_count; ++}; ++typedef struct cfi_ep cfi_ep_t; ++ ++typedef struct cfi_dma_buff { ++#define CFI_IN_BUF_LEN 1024 ++#define CFI_OUT_BUF_LEN 1024 ++ dma_addr_t addr; ++ uint8_t *buf; ++} cfi_dma_buff_t; ++ ++struct cfiobject; ++ ++/** ++ * This is the interface for the CFI operations. ++ * ++ * @param ep_enable Called when any endpoint is enabled and activated. ++ * @param release Called when the CFI object is released and it needs to correctly ++ * deallocate the dynamic memory ++ * @param ctrl_write_complete Called when the data stage of the request is complete ++ */ ++typedef struct cfi_ops { ++ int (*ep_enable) (struct cfiobject * cfi, struct dwc_otg_pcd * pcd, ++ struct dwc_otg_pcd_ep * ep); ++ void *(*ep_alloc_buf) (struct cfiobject * cfi, struct dwc_otg_pcd * pcd, ++ struct dwc_otg_pcd_ep * ep, dma_addr_t * dma, ++ unsigned size, gfp_t flags); ++ void (*release) (struct cfiobject * cfi); ++ int (*ctrl_write_complete) (struct cfiobject * cfi, ++ struct dwc_otg_pcd * pcd); ++ void (*build_descriptors) (struct cfiobject * cfi, ++ struct dwc_otg_pcd * pcd, ++ struct dwc_otg_pcd_ep * ep, ++ dwc_otg_pcd_request_t * req); ++} cfi_ops_t; ++ ++struct cfiobject { ++ cfi_ops_t ops; ++ struct dwc_otg_pcd *pcd; ++ struct usb_gadget *gadget; ++ ++ /* Buffers used to send/receive CFI-related request data */ ++ cfi_dma_buff_t buf_in; ++ cfi_dma_buff_t buf_out; ++ ++ /* CFI specific Control request wrapper */ ++ struct cfi_usb_ctrlrequest ctrl_req; ++ ++ /* The list of active EP's in the PCD of type cfi_ep_t */ ++ dwc_list_link_t active_eps; ++ ++ /* This flag shall control the propagation of a specific request ++ * to the gadget's processing routines. ++ * 0 - no gadget handling ++ * 1 - the gadget needs to know about this request (w/o completing a status ++ * phase - just return a 0 to the _setup callback) ++ */ ++ uint8_t need_gadget_att; ++ ++ /* Flag indicating whether the status IN phase needs to be ++ * completed by the PCD ++ */ ++ uint8_t need_status_in_complete; ++}; ++typedef struct cfiobject cfiobject_t; ++ ++#define DUMP_MSG ++ ++#if defined(DUMP_MSG) ++static inline void dump_msg(const u8 * buf, unsigned int length) ++{ ++ unsigned int start, num, i; ++ char line[52], *p; ++ ++ if (length >= 512) ++ return; ++ ++ start = 0; ++ while (length > 0) { ++ num = min(length, 16u); ++ p = line; ++ for (i = 0; i < num; ++i) { ++ if (i == 8) ++ *p++ = ' '; ++ DWC_SPRINTF(p, " %02x", buf[i]); ++ p += 3; ++ } ++ *p = 0; ++ DWC_DEBUG("%6x: %s\n", start, line); ++ buf += num; ++ start += num; ++ length -= num; ++ } ++} ++#else ++static inline void dump_msg(const u8 * buf, unsigned int length) ++{ ++} ++#endif ++ ++/** ++ * This function returns a pointer to cfi_ep_t object with the addr address. ++ */ ++static inline struct cfi_ep *get_cfi_ep_by_addr(struct cfiobject *cfi, ++ uint8_t addr) ++{ ++ struct cfi_ep *pcfiep; ++ dwc_list_link_t *tmp; ++ ++ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { ++ pcfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ ++ if (pcfiep->ep->desc->bEndpointAddress == addr) { ++ return pcfiep; ++ } ++ } ++ ++ return NULL; ++} ++ ++/** ++ * This function returns a pointer to cfi_ep_t object that matches ++ * the dwc_otg_pcd_ep object. ++ */ ++static inline struct cfi_ep *get_cfi_ep_by_pcd_ep(struct cfiobject *cfi, ++ struct dwc_otg_pcd_ep *ep) ++{ ++ struct cfi_ep *pcfiep = NULL; ++ dwc_list_link_t *tmp; ++ ++ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { ++ pcfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ if (pcfiep->ep == ep) { ++ return pcfiep; ++ } ++ } ++ return NULL; ++} ++ ++int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl); ++ ++#endif /* (__DWC_OTG_CFI_H__) */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_cil.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_cil.c +new file mode 100644 +index 0000000..5beb0ff +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_cil.c +@@ -0,0 +1,7362 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.c $ ++ * $Revision: #203 $ ++ * $Date: 2013/05/16 $ ++ * $Change: 2231774 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The Core Interface Layer provides basic services for accessing and ++ * managing the DWC_otg hardware. These services are used by both the ++ * Host Controller Driver and the Peripheral Controller Driver. ++ * ++ * The CIL manages the memory map for the core so that the HCD and PCD ++ * don't have to do this separately. It also handles basic tasks like ++ * reading/writing the registers and data FIFOs in the controller. ++ * Some of the data access functions provide encapsulation of several ++ * operations required to perform a task, such as writing multiple ++ * registers to start a transfer. Finally, the CIL performs basic ++ * services that are not specific to either the host or device modes ++ * of operation. These services include management of the OTG Host ++ * Negotiation Protocol (HNP) and Session Request Protocol (SRP). A ++ * Diagnostic API is also provided to allow testing of the controller ++ * hardware. ++ * ++ * The Core Interface Layer has the following requirements: ++ * - Provides basic controller operations. ++ * - Minimal use of OS services. ++ * - The OS services used will be abstracted by using inline functions ++ * or macros. ++ * ++ */ ++ ++#include "dwc_os.h" ++#include "dwc_otg_regs.h" ++#include "dwc_otg_cil.h" ++ ++static int dwc_otg_setup_params(dwc_otg_core_if_t * core_if); ++ ++/** ++ * This function is called to initialize the DWC_otg CSR data ++ * structures. The register addresses in the device and host ++ * structures are initialized from the base address supplied by the ++ * caller. The calling function must make the OS calls to get the ++ * base address of the DWC_otg controller registers. The core_params ++ * argument holds the parameters that specify how the core should be ++ * configured. ++ * ++ * @param reg_base_addr Base address of DWC_otg core registers ++ * ++ */ ++dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t * reg_base_addr) ++{ ++ dwc_otg_core_if_t *core_if = 0; ++ dwc_otg_dev_if_t *dev_if = 0; ++ dwc_otg_host_if_t *host_if = 0; ++ uint8_t *reg_base = (uint8_t *) reg_base_addr; ++ int i = 0; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, reg_base_addr); ++ ++ core_if = DWC_ALLOC(sizeof(dwc_otg_core_if_t)); ++ ++ if (core_if == NULL) { ++ DWC_DEBUGPL(DBG_CIL, ++ "Allocation of dwc_otg_core_if_t failed\n"); ++ return 0; ++ } ++ core_if->core_global_regs = (dwc_otg_core_global_regs_t *) reg_base; ++ ++ /* ++ * Allocate the Device Mode structures. ++ */ ++ dev_if = DWC_ALLOC(sizeof(dwc_otg_dev_if_t)); ++ ++ if (dev_if == NULL) { ++ DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_dev_if_t failed\n"); ++ DWC_FREE(core_if); ++ return 0; ++ } ++ ++ dev_if->dev_global_regs = ++ (dwc_otg_device_global_regs_t *) (reg_base + ++ DWC_DEV_GLOBAL_REG_OFFSET); ++ ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ dev_if->in_ep_regs[i] = (dwc_otg_dev_in_ep_regs_t *) ++ (reg_base + DWC_DEV_IN_EP_REG_OFFSET + ++ (i * DWC_EP_REG_OFFSET)); ++ ++ dev_if->out_ep_regs[i] = (dwc_otg_dev_out_ep_regs_t *) ++ (reg_base + DWC_DEV_OUT_EP_REG_OFFSET + ++ (i * DWC_EP_REG_OFFSET)); ++ DWC_DEBUGPL(DBG_CILV, "in_ep_regs[%d]->diepctl=%p\n", ++ i, &dev_if->in_ep_regs[i]->diepctl); ++ DWC_DEBUGPL(DBG_CILV, "out_ep_regs[%d]->doepctl=%p\n", ++ i, &dev_if->out_ep_regs[i]->doepctl); ++ } ++ ++ dev_if->speed = 0; // unknown ++ ++ core_if->dev_if = dev_if; ++ ++ /* ++ * Allocate the Host Mode structures. ++ */ ++ host_if = DWC_ALLOC(sizeof(dwc_otg_host_if_t)); ++ ++ if (host_if == NULL) { ++ DWC_DEBUGPL(DBG_CIL, ++ "Allocation of dwc_otg_host_if_t failed\n"); ++ DWC_FREE(dev_if); ++ DWC_FREE(core_if); ++ return 0; ++ } ++ ++ host_if->host_global_regs = (dwc_otg_host_global_regs_t *) ++ (reg_base + DWC_OTG_HOST_GLOBAL_REG_OFFSET); ++ ++ host_if->hprt0 = ++ (uint32_t *) (reg_base + DWC_OTG_HOST_PORT_REGS_OFFSET); ++ ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ host_if->hc_regs[i] = (dwc_otg_hc_regs_t *) ++ (reg_base + DWC_OTG_HOST_CHAN_REGS_OFFSET + ++ (i * DWC_OTG_CHAN_REGS_OFFSET)); ++ DWC_DEBUGPL(DBG_CILV, "hc_reg[%d]->hcchar=%p\n", ++ i, &host_if->hc_regs[i]->hcchar); ++ } ++ ++ host_if->num_host_channels = MAX_EPS_CHANNELS; ++ core_if->host_if = host_if; ++ ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ core_if->data_fifo[i] = ++ (uint32_t *) (reg_base + DWC_OTG_DATA_FIFO_OFFSET + ++ (i * DWC_OTG_DATA_FIFO_SIZE)); ++ DWC_DEBUGPL(DBG_CILV, "data_fifo[%d]=0x%08lx\n", ++ i, (unsigned long)core_if->data_fifo[i]); ++ } ++ ++ core_if->pcgcctl = (uint32_t *) (reg_base + DWC_OTG_PCGCCTL_OFFSET); ++ ++ /* Initiate lx_state to L3 disconnected state */ ++ core_if->lx_state = DWC_OTG_L3; ++ /* ++ * Store the contents of the hardware configuration registers here for ++ * easy access later. ++ */ ++ core_if->hwcfg1.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->ghwcfg1); ++ core_if->hwcfg2.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->ghwcfg2); ++ core_if->hwcfg3.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->ghwcfg3); ++ core_if->hwcfg4.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->ghwcfg4); ++ ++ /* Force host mode to get HPTXFSIZ exact power on value */ ++ { ++ gusbcfg_data_t gusbcfg = {.d32 = 0 }; ++ gusbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++ gusbcfg.b.force_host_mode = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32); ++ dwc_mdelay(10); ++ core_if->hptxfsiz.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->hptxfsiz); ++ gusbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++ gusbcfg.b.force_host_mode = 0; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32); ++ dwc_mdelay(10); ++ ++ gusbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++ gusbcfg.b.force_dev_mode = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32); ++ dwc_mdelay(10); ++ } ++ ++ DWC_DEBUGPL(DBG_CILV, "hwcfg1=%08x\n", core_if->hwcfg1.d32); ++ DWC_DEBUGPL(DBG_CILV, "hwcfg2=%08x\n", core_if->hwcfg2.d32); ++ DWC_DEBUGPL(DBG_CILV, "hwcfg3=%08x\n", core_if->hwcfg3.d32); ++ DWC_DEBUGPL(DBG_CILV, "hwcfg4=%08x\n", core_if->hwcfg4.d32); ++ ++ core_if->hcfg.d32 = ++ DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg); ++ core_if->dcfg.d32 = ++ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg); ++ ++ DWC_DEBUGPL(DBG_CILV, "hcfg=%08x\n", core_if->hcfg.d32); ++ DWC_DEBUGPL(DBG_CILV, "dcfg=%08x\n", core_if->dcfg.d32); ++ ++ DWC_DEBUGPL(DBG_CILV, "op_mode=%0x\n", core_if->hwcfg2.b.op_mode); ++ DWC_DEBUGPL(DBG_CILV, "arch=%0x\n", core_if->hwcfg2.b.architecture); ++ DWC_DEBUGPL(DBG_CILV, "num_dev_ep=%d\n", core_if->hwcfg2.b.num_dev_ep); ++ DWC_DEBUGPL(DBG_CILV, "num_host_chan=%d\n", ++ core_if->hwcfg2.b.num_host_chan); ++ DWC_DEBUGPL(DBG_CILV, "nonperio_tx_q_depth=0x%0x\n", ++ core_if->hwcfg2.b.nonperio_tx_q_depth); ++ DWC_DEBUGPL(DBG_CILV, "host_perio_tx_q_depth=0x%0x\n", ++ core_if->hwcfg2.b.host_perio_tx_q_depth); ++ DWC_DEBUGPL(DBG_CILV, "dev_token_q_depth=0x%0x\n", ++ core_if->hwcfg2.b.dev_token_q_depth); ++ ++ DWC_DEBUGPL(DBG_CILV, "Total FIFO SZ=%d\n", ++ core_if->hwcfg3.b.dfifo_depth); ++ DWC_DEBUGPL(DBG_CILV, "xfer_size_cntr_width=%0x\n", ++ core_if->hwcfg3.b.xfer_size_cntr_width); ++ ++ /* ++ * Set the SRP sucess bit for FS-I2c ++ */ ++ core_if->srp_success = 0; ++ core_if->srp_timer_started = 0; ++ ++ /* ++ * Create new workqueue and init works ++ */ ++ core_if->wq_otg = DWC_WORKQ_ALLOC("dwc_otg"); ++ if (core_if->wq_otg == 0) { ++ DWC_WARN("DWC_WORKQ_ALLOC failed\n"); ++ DWC_FREE(host_if); ++ DWC_FREE(dev_if); ++ DWC_FREE(core_if); ++ return 0; ++ } ++ ++ core_if->snpsid = DWC_READ_REG32(&core_if->core_global_regs->gsnpsid); ++ ++ DWC_PRINTF("Core Release: %x.%x%x%x\n", ++ (core_if->snpsid >> 12 & 0xF), ++ (core_if->snpsid >> 8 & 0xF), ++ (core_if->snpsid >> 4 & 0xF), (core_if->snpsid & 0xF)); ++ ++ core_if->wkp_timer = DWC_TIMER_ALLOC("Wake Up Timer", ++ w_wakeup_detected, core_if); ++ if (core_if->wkp_timer == 0) { ++ DWC_WARN("DWC_TIMER_ALLOC failed\n"); ++ DWC_FREE(host_if); ++ DWC_FREE(dev_if); ++ DWC_WORKQ_FREE(core_if->wq_otg); ++ DWC_FREE(core_if); ++ return 0; ++ } ++ ++ if (dwc_otg_setup_params(core_if)) { ++ DWC_WARN("Error while setting core params\n"); ++ } ++ ++ core_if->hibernation_suspend = 0; ++ if (core_if->otg_ver) ++ core_if->test_mode = 0; ++ ++ /** ADP initialization */ ++ dwc_otg_adp_init(core_if); ++ ++ return core_if; ++} ++ ++/** ++ * This function frees the structures allocated by dwc_otg_cil_init(). ++ * ++ * @param core_if The core interface pointer returned from ++ * dwc_otg_cil_init(). ++ * ++ */ ++void dwc_otg_cil_remove(dwc_otg_core_if_t * core_if) ++{ ++ dctl_data_t dctl = {.d32 = 0 }; ++ /* Disable all interrupts */ ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, 1, 0); ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, 0); ++ ++ dctl.b.sftdiscon = 1; ++ if (core_if->snpsid >= OTG_CORE_REV_3_00a) { ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, ++ dctl.d32); ++ } ++ ++ if (core_if->wq_otg) { ++ DWC_WORKQ_WAIT_WORK_DONE(core_if->wq_otg, 500); ++ DWC_WORKQ_FREE(core_if->wq_otg); ++ } ++ if (core_if->dev_if) { ++ DWC_FREE(core_if->dev_if); ++ } ++ if (core_if->host_if) { ++ DWC_FREE(core_if->host_if); ++ } ++ ++ /** Remove ADP Stuff */ ++ dwc_otg_adp_remove(core_if); ++ if (core_if->core_params) { ++ DWC_FREE(core_if->core_params); ++ } ++ if (core_if->wkp_timer) { ++ DWC_TIMER_FREE(core_if->wkp_timer); ++ } ++ if (core_if->srp_timer) { ++ DWC_TIMER_FREE(core_if->srp_timer); ++ } ++ DWC_FREE(core_if); ++} ++ ++/** ++ * This function enables the controller's Global Interrupt in the AHB Config ++ * register. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_enable_global_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ gahbcfg_data_t ahbcfg = {.d32 = 0 }; ++ ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */ ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, 0, ahbcfg.d32); ++} ++ ++/** ++ * This function disables the controller's Global Interrupt in the AHB Config ++ * register. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_disable_global_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ gahbcfg_data_t ahbcfg = {.d32 = 0 }; ++ ahbcfg.b.glblintrmsk = 1; /* Disable interrupts */ ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0); ++} ++ ++/** ++ * This function initializes the commmon interrupts, used in both ++ * device and host modes. ++ * ++ * @param core_if Programming view of the DWC_otg controller ++ * ++ */ ++static void dwc_otg_enable_common_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ /* Clear any pending OTG Interrupts */ ++ DWC_WRITE_REG32(&global_regs->gotgint, 0xFFFFFFFF); ++ ++ /* Clear any pending interrupts */ ++ DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* ++ * Enable the interrupts in the GINTMSK. ++ */ ++ if (!core_if->core_params->otg_ver) ++ /* To avoid system hang during OTG 2.0 role switch */ ++ intr_mask.b.modemismatch = 1; ++ intr_mask.b.otgintr = 1; ++ ++ if (!core_if->dma_enable) { ++ intr_mask.b.rxstsqlvl = 1; ++ } ++ ++ intr_mask.b.conidstschng = 1; ++ intr_mask.b.wkupintr = 1; ++ intr_mask.b.disconnect = 0; ++ intr_mask.b.usbsuspend = 1; ++ intr_mask.b.sessreqintr = 1; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ if (core_if->core_params->lpm_enable) { ++ intr_mask.b.lpmtranrcvd = 1; ++ } ++#endif ++ DWC_WRITE_REG32(&global_regs->gintmsk, intr_mask.d32); ++} ++ ++/* ++ * The restore operation is modified to support Synopsys Emulated Powerdown and ++ * Hibernation. This function is for exiting from Device mode hibernation by ++ * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup. ++ * @param core_if Programming view of DWC_otg controller. ++ * @param rem_wakeup - indicates whether resume is initiated by Device or Host. ++ * @param reset - indicates whether resume is initiated by Reset. ++ */ ++int dwc_otg_device_hibernation_restore(dwc_otg_core_if_t * core_if, ++ int rem_wakeup, int reset) ++{ ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ dctl_data_t dctl = {.d32 = 0 }; ++ ++ int timeout = 2000; ++ ++ if (!core_if->hibernation_suspend) { ++ DWC_PRINTF("Already exited from Hibernation\n"); ++ return 1; ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "%s called\n", __FUNCTION__); ++ /* Switch-on voltage to the core */ ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Reset core */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Assert Restore signal */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.restore = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Disable power clamps */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnclmp = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ if (rem_wakeup) { ++ dwc_udelay(70); ++ } ++ ++ /* Deassert Reset core */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Disable PMU interrupt */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /* Mask interrupts from gpwrdn */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.connect_det_msk = 1; ++ gpwrdn.b.srp_det_msk = 1; ++ gpwrdn.b.disconn_det_msk = 1; ++ gpwrdn.b.rst_det_msk = 1; ++ gpwrdn.b.lnstchng_msk = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /* Indicates that we are going out from hibernation */ ++ core_if->hibernation_suspend = 0; ++ ++ /* ++ * Set Restore Essential Regs bit in PCGCCTL register, restore_mode = 1 ++ * indicates restore from remote_wakeup ++ */ ++ restore_essential_regs(core_if, rem_wakeup, 0); ++ ++ /* ++ * Wait a little for seeing new value of variable hibernation_suspend if ++ * Restore done interrupt received before polling ++ */ ++ dwc_udelay(10); ++ ++ if (core_if->hibernation_suspend == 0) { ++ /* ++ * Wait For Restore_done Interrupt. This mechanism of polling the ++ * interrupt is introduced to avoid any possible race conditions ++ */ ++ do { ++ gintsts_data_t gintsts; ++ gintsts.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ if (gintsts.b.restoredone) { ++ gintsts.d32 = 0; ++ gintsts.b.restoredone = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs-> ++ gintsts, gintsts.d32); ++ DWC_PRINTF("Restore Done Interrupt seen\n"); ++ break; ++ } ++ dwc_udelay(10); ++ } while (--timeout); ++ if (!timeout) { ++ DWC_PRINTF("Restore Done interrupt wasn't generated here\n"); ++ } ++ } ++ /* Clear all pending interupts */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* De-assert Restore */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.restore = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ if (!rem_wakeup) { ++ pcgcctl.d32 = 0; ++ pcgcctl.b.rstpdwnmodule = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0); ++ } ++ ++ /* Restore GUSBCFG and DCFG */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, ++ core_if->gr_backup->gusbcfg_local); ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, ++ core_if->dr_backup->dcfg); ++ ++ /* De-assert Wakeup Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ if (!rem_wakeup) { ++ /* Set Device programming done bit */ ++ dctl.b.pwronprgdone = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32); ++ } else { ++ /* Start Remote Wakeup Signaling */ ++ dctl.d32 = core_if->dr_backup->dctl; ++ dctl.b.rmtwkupsig = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32); ++ } ++ ++ dwc_mdelay(2); ++ /* Clear all pending interupts */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Restore global registers */ ++ dwc_otg_restore_global_regs(core_if); ++ /* Restore device global registers */ ++ dwc_otg_restore_dev_regs(core_if, rem_wakeup); ++ ++ if (rem_wakeup) { ++ dwc_mdelay(7); ++ dctl.d32 = 0; ++ dctl.b.rmtwkupsig = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0); ++ } ++ ++ core_if->hibernation_suspend = 0; ++ /* The core will be in ON STATE */ ++ core_if->lx_state = DWC_OTG_L0; ++ DWC_PRINTF("Hibernation recovery completes here\n"); ++ ++ return 1; ++} ++ ++/* ++ * The restore operation is modified to support Synopsys Emulated Powerdown and ++ * Hibernation. This function is for exiting from Host mode hibernation by ++ * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup. ++ * @param core_if Programming view of DWC_otg controller. ++ * @param rem_wakeup - indicates whether resume is initiated by Device or Host. ++ * @param reset - indicates whether resume is initiated by Reset. ++ */ ++int dwc_otg_host_hibernation_restore(dwc_otg_core_if_t * core_if, ++ int rem_wakeup, int reset) ++{ ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++ ++ int timeout = 2000; ++ ++ DWC_DEBUGPL(DBG_HCD, "%s called\n", __FUNCTION__); ++ /* Switch-on voltage to the core */ ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Reset core */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Assert Restore signal */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.restore = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Disable power clamps */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnclmp = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ if (!rem_wakeup) { ++ dwc_udelay(50); ++ } ++ ++ /* Deassert Reset core */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Disable PMU interrupt */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.connect_det_msk = 1; ++ gpwrdn.b.srp_det_msk = 1; ++ gpwrdn.b.disconn_det_msk = 1; ++ gpwrdn.b.rst_det_msk = 1; ++ gpwrdn.b.lnstchng_msk = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /* Indicates that we are going out from hibernation */ ++ core_if->hibernation_suspend = 0; ++ ++ /* Set Restore Essential Regs bit in PCGCCTL register */ ++ restore_essential_regs(core_if, rem_wakeup, 1); ++ ++ /* Wait a little for seeing new value of variable hibernation_suspend if ++ * Restore done interrupt received before polling */ ++ dwc_udelay(10); ++ ++ if (core_if->hibernation_suspend == 0) { ++ /* Wait For Restore_done Interrupt. This mechanism of polling the ++ * interrupt is introduced to avoid any possible race conditions ++ */ ++ do { ++ gintsts_data_t gintsts; ++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ if (gintsts.b.restoredone) { ++ gintsts.d32 = 0; ++ gintsts.b.restoredone = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ DWC_DEBUGPL(DBG_HCD,"Restore Done Interrupt seen\n"); ++ break; ++ } ++ dwc_udelay(10); ++ } while (--timeout); ++ if (!timeout) { ++ DWC_WARN("Restore Done interrupt wasn't generated\n"); ++ } ++ } ++ ++ /* Set the flag's value to 0 again after receiving restore done interrupt */ ++ core_if->hibernation_suspend = 0; ++ ++ /* This step is not described in functional spec but if not wait for this ++ * delay, mismatch interrupts occurred because just after restore core is ++ * in Device mode(gintsts.curmode == 0) */ ++ dwc_mdelay(100); ++ ++ /* Clear all pending interrupts */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* De-assert Restore */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.restore = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Restore GUSBCFG and HCFG */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, ++ core_if->gr_backup->gusbcfg_local); ++ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, ++ core_if->hr_backup->hcfg_local); ++ ++ /* De-assert Wakeup Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Start the Resume operation by programming HPRT0 */ ++ hprt0.d32 = core_if->hr_backup->hprt0_local; ++ hprt0.b.prtpwr = 1; ++ hprt0.b.prtena = 0; ++ hprt0.b.prtsusp = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ ++ DWC_PRINTF("Resume Starts Now\n"); ++ if (!reset) { // Indicates it is Resume Operation ++ hprt0.d32 = core_if->hr_backup->hprt0_local; ++ hprt0.b.prtres = 1; ++ hprt0.b.prtpwr = 1; ++ hprt0.b.prtena = 0; ++ hprt0.b.prtsusp = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ ++ if (!rem_wakeup) ++ hprt0.b.prtres = 0; ++ /* Wait for Resume time and then program HPRT again */ ++ dwc_mdelay(100); ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ ++ } else { // Indicates it is Reset Operation ++ hprt0.d32 = core_if->hr_backup->hprt0_local; ++ hprt0.b.prtrst = 1; ++ hprt0.b.prtpwr = 1; ++ hprt0.b.prtena = 0; ++ hprt0.b.prtsusp = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ /* Wait for Reset time and then program HPRT again */ ++ dwc_mdelay(60); ++ hprt0.b.prtrst = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ } ++ /* Clear all interrupt status */ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtconndet = 1; ++ hprt0.b.prtenchng = 1; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ ++ /* Clear all pending interupts */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Restore global registers */ ++ dwc_otg_restore_global_regs(core_if); ++ /* Restore host global registers */ ++ dwc_otg_restore_host_regs(core_if, reset); ++ ++ /* The core will be in ON STATE */ ++ core_if->lx_state = DWC_OTG_L0; ++ DWC_PRINTF("Hibernation recovery is complete here\n"); ++ return 0; ++} ++ ++/** Saves some register values into system memory. */ ++int dwc_otg_save_global_regs(dwc_otg_core_if_t * core_if) ++{ ++ struct dwc_otg_global_regs_backup *gr; ++ int i; ++ ++ gr = core_if->gr_backup; ++ if (!gr) { ++ gr = DWC_ALLOC(sizeof(*gr)); ++ if (!gr) { ++ return -DWC_E_NO_MEMORY; ++ } ++ core_if->gr_backup = gr; ++ } ++ ++ gr->gotgctl_local = DWC_READ_REG32(&core_if->core_global_regs->gotgctl); ++ gr->gintmsk_local = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); ++ gr->gahbcfg_local = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg); ++ gr->gusbcfg_local = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++ gr->grxfsiz_local = DWC_READ_REG32(&core_if->core_global_regs->grxfsiz); ++ gr->gnptxfsiz_local = DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz); ++ gr->hptxfsiz_local = DWC_READ_REG32(&core_if->core_global_regs->hptxfsiz); ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ gr->glpmcfg_local = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++#endif ++ gr->gi2cctl_local = DWC_READ_REG32(&core_if->core_global_regs->gi2cctl); ++ gr->pcgcctl_local = DWC_READ_REG32(core_if->pcgcctl); ++ gr->gdfifocfg_local = ++ DWC_READ_REG32(&core_if->core_global_regs->gdfifocfg); ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ gr->dtxfsiz_local[i] = ++ DWC_READ_REG32(&(core_if->core_global_regs->dtxfsiz[i])); ++ } ++ ++ DWC_DEBUGPL(DBG_ANY, "===========Backing Global registers==========\n"); ++ DWC_DEBUGPL(DBG_ANY, "Backed up gotgctl = %08x\n", gr->gotgctl_local); ++ DWC_DEBUGPL(DBG_ANY, "Backed up gintmsk = %08x\n", gr->gintmsk_local); ++ DWC_DEBUGPL(DBG_ANY, "Backed up gahbcfg = %08x\n", gr->gahbcfg_local); ++ DWC_DEBUGPL(DBG_ANY, "Backed up gusbcfg = %08x\n", gr->gusbcfg_local); ++ DWC_DEBUGPL(DBG_ANY, "Backed up grxfsiz = %08x\n", gr->grxfsiz_local); ++ DWC_DEBUGPL(DBG_ANY, "Backed up gnptxfsiz = %08x\n", ++ gr->gnptxfsiz_local); ++ DWC_DEBUGPL(DBG_ANY, "Backed up hptxfsiz = %08x\n", ++ gr->hptxfsiz_local); ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ DWC_DEBUGPL(DBG_ANY, "Backed up glpmcfg = %08x\n", gr->glpmcfg_local); ++#endif ++ DWC_DEBUGPL(DBG_ANY, "Backed up gi2cctl = %08x\n", gr->gi2cctl_local); ++ DWC_DEBUGPL(DBG_ANY, "Backed up pcgcctl = %08x\n", gr->pcgcctl_local); ++ DWC_DEBUGPL(DBG_ANY,"Backed up gdfifocfg = %08x\n",gr->gdfifocfg_local); ++ ++ return 0; ++} ++ ++/** Saves GINTMSK register before setting the msk bits. */ ++int dwc_otg_save_gintmsk_reg(dwc_otg_core_if_t * core_if) ++{ ++ struct dwc_otg_global_regs_backup *gr; ++ ++ gr = core_if->gr_backup; ++ if (!gr) { ++ gr = DWC_ALLOC(sizeof(*gr)); ++ if (!gr) { ++ return -DWC_E_NO_MEMORY; ++ } ++ core_if->gr_backup = gr; ++ } ++ ++ gr->gintmsk_local = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); ++ ++ DWC_DEBUGPL(DBG_ANY,"=============Backing GINTMSK registers============\n"); ++ DWC_DEBUGPL(DBG_ANY, "Backed up gintmsk = %08x\n", gr->gintmsk_local); ++ ++ return 0; ++} ++ ++int dwc_otg_save_dev_regs(dwc_otg_core_if_t * core_if) ++{ ++ struct dwc_otg_dev_regs_backup *dr; ++ int i; ++ ++ dr = core_if->dr_backup; ++ if (!dr) { ++ dr = DWC_ALLOC(sizeof(*dr)); ++ if (!dr) { ++ return -DWC_E_NO_MEMORY; ++ } ++ core_if->dr_backup = dr; ++ } ++ ++ dr->dcfg = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg); ++ dr->dctl = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl); ++ dr->daintmsk = ++ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daintmsk); ++ dr->diepmsk = ++ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->diepmsk); ++ dr->doepmsk = ++ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->doepmsk); ++ ++ for (i = 0; i < core_if->dev_if->num_in_eps; ++i) { ++ dr->diepctl[i] = ++ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl); ++ dr->dieptsiz[i] = ++ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->dieptsiz); ++ dr->diepdma[i] = ++ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepdma); ++ } ++ ++ DWC_DEBUGPL(DBG_ANY, ++ "=============Backing Host registers==============\n"); ++ DWC_DEBUGPL(DBG_ANY, "Backed up dcfg = %08x\n", dr->dcfg); ++ DWC_DEBUGPL(DBG_ANY, "Backed up dctl = %08x\n", dr->dctl); ++ DWC_DEBUGPL(DBG_ANY, "Backed up daintmsk = %08x\n", ++ dr->daintmsk); ++ DWC_DEBUGPL(DBG_ANY, "Backed up diepmsk = %08x\n", dr->diepmsk); ++ DWC_DEBUGPL(DBG_ANY, "Backed up doepmsk = %08x\n", dr->doepmsk); ++ for (i = 0; i < core_if->dev_if->num_in_eps; ++i) { ++ DWC_DEBUGPL(DBG_ANY, "Backed up diepctl[%d] = %08x\n", i, ++ dr->diepctl[i]); ++ DWC_DEBUGPL(DBG_ANY, "Backed up dieptsiz[%d] = %08x\n", ++ i, dr->dieptsiz[i]); ++ DWC_DEBUGPL(DBG_ANY, "Backed up diepdma[%d] = %08x\n", i, ++ dr->diepdma[i]); ++ } ++ ++ return 0; ++} ++ ++int dwc_otg_save_host_regs(dwc_otg_core_if_t * core_if) ++{ ++ struct dwc_otg_host_regs_backup *hr; ++ int i; ++ ++ hr = core_if->hr_backup; ++ if (!hr) { ++ hr = DWC_ALLOC(sizeof(*hr)); ++ if (!hr) { ++ return -DWC_E_NO_MEMORY; ++ } ++ core_if->hr_backup = hr; ++ } ++ ++ hr->hcfg_local = ++ DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg); ++ hr->haintmsk_local = ++ DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk); ++ for (i = 0; i < dwc_otg_get_param_host_channels(core_if); ++i) { ++ hr->hcintmsk_local[i] = ++ DWC_READ_REG32(&core_if->host_if->hc_regs[i]->hcintmsk); ++ } ++ hr->hprt0_local = DWC_READ_REG32(core_if->host_if->hprt0); ++ hr->hfir_local = ++ DWC_READ_REG32(&core_if->host_if->host_global_regs->hfir); ++ ++ DWC_DEBUGPL(DBG_ANY, ++ "=============Backing Host registers===============\n"); ++ DWC_DEBUGPL(DBG_ANY, "Backed up hcfg = %08x\n", ++ hr->hcfg_local); ++ DWC_DEBUGPL(DBG_ANY, "Backed up haintmsk = %08x\n", hr->haintmsk_local); ++ for (i = 0; i < dwc_otg_get_param_host_channels(core_if); ++i) { ++ DWC_DEBUGPL(DBG_ANY, "Backed up hcintmsk[%02d]=%08x\n", i, ++ hr->hcintmsk_local[i]); ++ } ++ DWC_DEBUGPL(DBG_ANY, "Backed up hprt0 = %08x\n", ++ hr->hprt0_local); ++ DWC_DEBUGPL(DBG_ANY, "Backed up hfir = %08x\n", ++ hr->hfir_local); ++ ++ return 0; ++} ++ ++int dwc_otg_restore_global_regs(dwc_otg_core_if_t * core_if) ++{ ++ struct dwc_otg_global_regs_backup *gr; ++ int i; ++ ++ gr = core_if->gr_backup; ++ if (!gr) { ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, gr->gotgctl_local); ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gr->gintmsk_local); ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gr->gusbcfg_local); ++ DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg, gr->gahbcfg_local); ++ DWC_WRITE_REG32(&core_if->core_global_regs->grxfsiz, gr->grxfsiz_local); ++ DWC_WRITE_REG32(&core_if->core_global_regs->gnptxfsiz, ++ gr->gnptxfsiz_local); ++ DWC_WRITE_REG32(&core_if->core_global_regs->hptxfsiz, ++ gr->hptxfsiz_local); ++ DWC_WRITE_REG32(&core_if->core_global_regs->gdfifocfg, ++ gr->gdfifocfg_local); ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ DWC_WRITE_REG32(&core_if->core_global_regs->dtxfsiz[i], ++ gr->dtxfsiz_local[i]); ++ } ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF); ++ DWC_WRITE_REG32(core_if->host_if->hprt0, 0x0000100A); ++ DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg, ++ (gr->gahbcfg_local)); ++ return 0; ++} ++ ++int dwc_otg_restore_dev_regs(dwc_otg_core_if_t * core_if, int rem_wakeup) ++{ ++ struct dwc_otg_dev_regs_backup *dr; ++ int i; ++ ++ dr = core_if->dr_backup; ++ ++ if (!dr) { ++ return -DWC_E_INVALID; ++ } ++ ++ if (!rem_wakeup) { ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, ++ dr->dctl); ++ } ++ ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daintmsk, dr->daintmsk); ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->diepmsk, dr->diepmsk); ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->doepmsk, dr->doepmsk); ++ ++ for (i = 0; i < core_if->dev_if->num_in_eps; ++i) { ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->dieptsiz, dr->dieptsiz[i]); ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->diepdma, dr->diepdma[i]); ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl, dr->diepctl[i]); ++ } ++ ++ return 0; ++} ++ ++int dwc_otg_restore_host_regs(dwc_otg_core_if_t * core_if, int reset) ++{ ++ struct dwc_otg_host_regs_backup *hr; ++ int i; ++ hr = core_if->hr_backup; ++ ++ if (!hr) { ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, hr->hcfg_local); ++ //if (!reset) ++ //{ ++ // DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hfir, hr->hfir_local); ++ //} ++ ++ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haintmsk, ++ hr->haintmsk_local); ++ for (i = 0; i < dwc_otg_get_param_host_channels(core_if); ++i) { ++ DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcintmsk, ++ hr->hcintmsk_local[i]); ++ } ++ ++ return 0; ++} ++ ++int restore_lpm_i2c_regs(dwc_otg_core_if_t * core_if) ++{ ++ struct dwc_otg_global_regs_backup *gr; ++ ++ gr = core_if->gr_backup; ++ ++ /* Restore values for LPM and I2C */ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, gr->glpmcfg_local); ++#endif ++ DWC_WRITE_REG32(&core_if->core_global_regs->gi2cctl, gr->gi2cctl_local); ++ ++ return 0; ++} ++ ++int restore_essential_regs(dwc_otg_core_if_t * core_if, int rmode, int is_host) ++{ ++ struct dwc_otg_global_regs_backup *gr; ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ gahbcfg_data_t gahbcfg = {.d32 = 0 }; ++ gusbcfg_data_t gusbcfg = {.d32 = 0 }; ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ ++ /* Restore LPM and I2C registers */ ++ restore_lpm_i2c_regs(core_if); ++ ++ /* Set PCGCCTL to 0 */ ++ DWC_WRITE_REG32(core_if->pcgcctl, 0x00000000); ++ ++ gr = core_if->gr_backup; ++ /* Load restore values for [31:14] bits */ ++ DWC_WRITE_REG32(core_if->pcgcctl, ++ ((gr->pcgcctl_local & 0xffffc000) | 0x00020000)); ++ ++ /* Umnask global Interrupt in GAHBCFG and restore it */ ++ gahbcfg.d32 = gr->gahbcfg_local; ++ gahbcfg.b.glblintrmsk = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg, gahbcfg.d32); ++ ++ /* Clear all pending interupts */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Unmask restore done interrupt */ ++ gintmsk.b.restoredone = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32); ++ ++ /* Restore GUSBCFG and HCFG/DCFG */ ++ gusbcfg.d32 = core_if->gr_backup->gusbcfg_local; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32); ++ ++ if (is_host) { ++ hcfg_data_t hcfg = {.d32 = 0 }; ++ hcfg.d32 = core_if->hr_backup->hcfg_local; ++ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, ++ hcfg.d32); ++ ++ /* Load restore values for [31:14] bits */ ++ pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000; ++ pcgcctl.d32 = gr->pcgcctl_local | 0x00020000; ++ ++ if (rmode) ++ pcgcctl.b.restoremode = 1; ++ DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32); ++ dwc_udelay(10); ++ ++ /* Load restore values for [31:14] bits and set EssRegRestored bit */ ++ pcgcctl.d32 = gr->pcgcctl_local | 0xffffc000; ++ pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000; ++ pcgcctl.b.ess_reg_restored = 1; ++ if (rmode) ++ pcgcctl.b.restoremode = 1; ++ DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32); ++ } else { ++ dcfg_data_t dcfg = {.d32 = 0 }; ++ dcfg.d32 = core_if->dr_backup->dcfg; ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32); ++ ++ /* Load restore values for [31:14] bits */ ++ pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000; ++ pcgcctl.d32 = gr->pcgcctl_local | 0x00020000; ++ if (!rmode) { ++ pcgcctl.d32 |= 0x208; ++ } ++ DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32); ++ dwc_udelay(10); ++ ++ /* Load restore values for [31:14] bits */ ++ pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000; ++ pcgcctl.d32 = gr->pcgcctl_local | 0x00020000; ++ pcgcctl.b.ess_reg_restored = 1; ++ if (!rmode) ++ pcgcctl.d32 |= 0x208; ++ DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Initializes the FSLSPClkSel field of the HCFG register depending on the PHY ++ * type. ++ */ ++static void init_fslspclksel(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t val; ++ hcfg_data_t hcfg; ++ ++ if (((core_if->hwcfg2.b.hs_phy_type == 2) && ++ (core_if->hwcfg2.b.fs_phy_type == 1) && ++ (core_if->core_params->ulpi_fs_ls)) || ++ (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) { ++ /* Full speed PHY */ ++ val = DWC_HCFG_48_MHZ; ++ } else { ++ /* High speed PHY running at full speed or high speed */ ++ val = DWC_HCFG_30_60_MHZ; ++ } ++ ++ DWC_DEBUGPL(DBG_CIL, "Initializing HCFG.FSLSPClkSel to 0x%1x\n", val); ++ hcfg.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg); ++ hcfg.b.fslspclksel = val; ++ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, hcfg.d32); ++} ++ ++/** ++ * Initializes the DevSpd field of the DCFG register depending on the PHY type ++ * and the enumeration speed of the device. ++ */ ++static void init_devspd(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t val; ++ dcfg_data_t dcfg; ++ ++ if (((core_if->hwcfg2.b.hs_phy_type == 2) && ++ (core_if->hwcfg2.b.fs_phy_type == 1) && ++ (core_if->core_params->ulpi_fs_ls)) || ++ (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) { ++ /* Full speed PHY */ ++ val = 0x3; ++ } else if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) { ++ /* High speed PHY running at full speed */ ++ val = 0x1; ++ } else { ++ /* High speed PHY running at high speed */ ++ val = 0x0; ++ } ++ ++ DWC_DEBUGPL(DBG_CIL, "Initializing DCFG.DevSpd to 0x%1x\n", val); ++ ++ dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg); ++ dcfg.b.devspd = val; ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32); ++} ++ ++/** ++ * This function calculates the number of IN EPS ++ * using GHWCFG1 and GHWCFG2 registers values ++ * ++ * @param core_if Programming view of the DWC_otg controller ++ */ ++static uint32_t calc_num_in_eps(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t num_in_eps = 0; ++ uint32_t num_eps = core_if->hwcfg2.b.num_dev_ep; ++ uint32_t hwcfg1 = core_if->hwcfg1.d32 >> 3; ++ uint32_t num_tx_fifos = core_if->hwcfg4.b.num_in_eps; ++ int i; ++ ++ for (i = 0; i < num_eps; ++i) { ++ if (!(hwcfg1 & 0x1)) ++ num_in_eps++; ++ ++ hwcfg1 >>= 2; ++ } ++ ++ if (core_if->hwcfg4.b.ded_fifo_en) { ++ num_in_eps = ++ (num_in_eps > num_tx_fifos) ? num_tx_fifos : num_in_eps; ++ } ++ ++ return num_in_eps; ++} ++ ++/** ++ * This function calculates the number of OUT EPS ++ * using GHWCFG1 and GHWCFG2 registers values ++ * ++ * @param core_if Programming view of the DWC_otg controller ++ */ ++static uint32_t calc_num_out_eps(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t num_out_eps = 0; ++ uint32_t num_eps = core_if->hwcfg2.b.num_dev_ep; ++ uint32_t hwcfg1 = core_if->hwcfg1.d32 >> 2; ++ int i; ++ ++ for (i = 0; i < num_eps; ++i) { ++ if (!(hwcfg1 & 0x1)) ++ num_out_eps++; ++ ++ hwcfg1 >>= 2; ++ } ++ return num_out_eps; ++} ++ ++/** ++ * This function initializes the DWC_otg controller registers and ++ * prepares the core for device mode or host mode operation. ++ * ++ * @param core_if Programming view of the DWC_otg controller ++ * ++ */ ++void dwc_otg_core_init(dwc_otg_core_if_t * core_if) ++{ ++ int i = 0; ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ gahbcfg_data_t ahbcfg = {.d32 = 0 }; ++ gusbcfg_data_t usbcfg = {.d32 = 0 }; ++ gi2cctl_data_t i2cctl = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_CILV, "dwc_otg_core_init(%p)\n", core_if); ++ ++ /* Common Initialization */ ++ usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg); ++ ++ /* Program the ULPI External VBUS bit if needed */ ++ usbcfg.b.ulpi_ext_vbus_drv = ++ (core_if->core_params->phy_ulpi_ext_vbus == ++ DWC_PHY_ULPI_EXTERNAL_VBUS) ? 1 : 0; ++ ++ /* Set external TS Dline pulsing */ ++ usbcfg.b.term_sel_dl_pulse = ++ (core_if->core_params->ts_dline == 1) ? 1 : 0; ++ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Reset the Controller */ ++ dwc_otg_core_reset(core_if); ++ ++ core_if->adp_enable = core_if->core_params->adp_supp_enable; ++ core_if->power_down = core_if->core_params->power_down; ++ ++ /* Initialize parameters from Hardware configuration registers. */ ++ dev_if->num_in_eps = calc_num_in_eps(core_if); ++ dev_if->num_out_eps = calc_num_out_eps(core_if); ++ ++ DWC_DEBUGPL(DBG_CIL, "num_dev_perio_in_ep=%d\n", ++ core_if->hwcfg4.b.num_dev_perio_in_ep); ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; i++) { ++ dev_if->perio_tx_fifo_size[i] = ++ DWC_READ_REG32(&global_regs->dtxfsiz[i]) >> 16; ++ DWC_DEBUGPL(DBG_CIL, "Periodic Tx FIFO SZ #%d=0x%0x\n", ++ i, dev_if->perio_tx_fifo_size[i]); ++ } ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ dev_if->tx_fifo_size[i] = ++ DWC_READ_REG32(&global_regs->dtxfsiz[i]) >> 16; ++ DWC_DEBUGPL(DBG_CIL, "Tx FIFO SZ #%d=0x%0x\n", ++ i, dev_if->tx_fifo_size[i]); ++ } ++ ++ core_if->total_fifo_size = core_if->hwcfg3.b.dfifo_depth; ++ core_if->rx_fifo_size = DWC_READ_REG32(&global_regs->grxfsiz); ++ core_if->nperio_tx_fifo_size = ++ DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16; ++ ++ DWC_DEBUGPL(DBG_CIL, "Total FIFO SZ=%d\n", core_if->total_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "Rx FIFO SZ=%d\n", core_if->rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO SZ=%d\n", ++ core_if->nperio_tx_fifo_size); ++ ++ /* This programming sequence needs to happen in FS mode before any other ++ * programming occurs */ ++ if ((core_if->core_params->speed == DWC_SPEED_PARAM_FULL) && ++ (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) { ++ /* If FS mode with FS PHY */ ++ ++ /* core_init() is now called on every switch so only call the ++ * following for the first time through. */ ++ if (!core_if->phy_init_done) { ++ core_if->phy_init_done = 1; ++ DWC_DEBUGPL(DBG_CIL, "FS_PHY detected\n"); ++ usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg); ++ usbcfg.b.physel = 1; ++ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Reset after a PHY select */ ++ dwc_otg_core_reset(core_if); ++ } ++ ++ /* Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also ++ * do this on HNP Dev/Host mode switches (done in dev_init and ++ * host_init). */ ++ if (dwc_otg_is_host_mode(core_if)) { ++ init_fslspclksel(core_if); ++ } else { ++ init_devspd(core_if); ++ } ++ ++ if (core_if->core_params->i2c_enable) { ++ DWC_DEBUGPL(DBG_CIL, "FS_PHY Enabling I2c\n"); ++ /* Program GUSBCFG.OtgUtmifsSel to I2C */ ++ usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg); ++ usbcfg.b.otgutmifssel = 1; ++ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Program GI2CCTL.I2CEn */ ++ i2cctl.d32 = DWC_READ_REG32(&global_regs->gi2cctl); ++ i2cctl.b.i2cdevaddr = 1; ++ i2cctl.b.i2cen = 0; ++ DWC_WRITE_REG32(&global_regs->gi2cctl, i2cctl.d32); ++ i2cctl.b.i2cen = 1; ++ DWC_WRITE_REG32(&global_regs->gi2cctl, i2cctl.d32); ++ } ++ ++ } /* endif speed == DWC_SPEED_PARAM_FULL */ ++ else { ++ /* High speed PHY. */ ++ if (!core_if->phy_init_done) { ++ core_if->phy_init_done = 1; ++ /* HS PHY parameters. These parameters are preserved ++ * during soft reset so only program the first time. Do ++ * a soft reset immediately after setting phyif. */ ++ ++ if (core_if->core_params->phy_type == 2) { ++ /* ULPI interface */ ++ usbcfg.b.ulpi_utmi_sel = 1; ++ usbcfg.b.phyif = 0; ++ usbcfg.b.ddrsel = ++ core_if->core_params->phy_ulpi_ddr; ++ } else if (core_if->core_params->phy_type == 1) { ++ /* UTMI+ interface */ ++ usbcfg.b.ulpi_utmi_sel = 0; ++ if (core_if->core_params->phy_utmi_width == 16) { ++ usbcfg.b.phyif = 1; ++ ++ } else { ++ usbcfg.b.phyif = 0; ++ } ++ } else { ++ DWC_ERROR("FS PHY TYPE\n"); ++ } ++ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); ++ /* Reset after setting the PHY parameters */ ++ dwc_otg_core_reset(core_if); ++ } ++ } ++ ++ if ((core_if->hwcfg2.b.hs_phy_type == 2) && ++ (core_if->hwcfg2.b.fs_phy_type == 1) && ++ (core_if->core_params->ulpi_fs_ls)) { ++ DWC_DEBUGPL(DBG_CIL, "Setting ULPI FSLS\n"); ++ usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg); ++ usbcfg.b.ulpi_fsls = 1; ++ usbcfg.b.ulpi_clk_sus_m = 1; ++ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); ++ } else { ++ usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg); ++ usbcfg.b.ulpi_fsls = 0; ++ usbcfg.b.ulpi_clk_sus_m = 0; ++ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); ++ } ++ ++ /* Program the GAHBCFG Register. */ ++ switch (core_if->hwcfg2.b.architecture) { ++ ++ case DWC_SLAVE_ONLY_ARCH: ++ DWC_DEBUGPL(DBG_CIL, "Slave Only Mode\n"); ++ ahbcfg.b.nptxfemplvl_txfemplvl = ++ DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY; ++ ahbcfg.b.ptxfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY; ++ core_if->dma_enable = 0; ++ core_if->dma_desc_enable = 0; ++ break; ++ ++ case DWC_EXT_DMA_ARCH: ++ DWC_DEBUGPL(DBG_CIL, "External DMA Mode\n"); ++ { ++ uint8_t brst_sz = core_if->core_params->dma_burst_size; ++ ahbcfg.b.hburstlen = 0; ++ while (brst_sz > 1) { ++ ahbcfg.b.hburstlen++; ++ brst_sz >>= 1; ++ } ++ } ++ core_if->dma_enable = (core_if->core_params->dma_enable != 0); ++ core_if->dma_desc_enable = ++ (core_if->core_params->dma_desc_enable != 0); ++ break; ++ ++ case DWC_INT_DMA_ARCH: ++ DWC_DEBUGPL(DBG_CIL, "Internal DMA Mode\n"); ++ /* Old value was DWC_GAHBCFG_INT_DMA_BURST_INCR - done for ++ Host mode ISOC in issue fix - vahrama */ ++ ahbcfg.b.hburstlen = DWC_GAHBCFG_INT_DMA_BURST_INCR4; ++ core_if->dma_enable = (core_if->core_params->dma_enable != 0); ++ core_if->dma_desc_enable = ++ (core_if->core_params->dma_desc_enable != 0); ++ break; ++ ++ } ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable) { ++ DWC_PRINTF("Using Descriptor DMA mode\n"); ++ } else { ++ DWC_PRINTF("Using Buffer DMA mode\n"); ++ } ++ } else { ++ DWC_PRINTF("Using Slave mode\n"); ++ core_if->dma_desc_enable = 0; ++ } ++ ++ if (core_if->core_params->ahb_single) { ++ ahbcfg.b.ahbsingle = 1; ++ } ++ ++ ahbcfg.b.dmaenable = core_if->dma_enable; ++ DWC_WRITE_REG32(&global_regs->gahbcfg, ahbcfg.d32); ++ ++ core_if->en_multiple_tx_fifo = core_if->hwcfg4.b.ded_fifo_en; ++ ++ core_if->pti_enh_enable = core_if->core_params->pti_enable != 0; ++ core_if->multiproc_int_enable = core_if->core_params->mpi_enable; ++ ++ /* ++ * Program the GUSBCFG register. ++ */ ++ usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg); ++ ++ switch (core_if->hwcfg2.b.op_mode) { ++ case DWC_MODE_HNP_SRP_CAPABLE: ++ usbcfg.b.hnpcap = (core_if->core_params->otg_cap == ++ DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE); ++ usbcfg.b.srpcap = (core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_SRP_ONLY_CAPABLE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = (core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_NO_HNP_SRP_CAPABLE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ break; ++ ++ case DWC_MODE_SRP_CAPABLE_DEVICE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = (core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_NO_SRP_CAPABLE_DEVICE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ break; ++ ++ case DWC_MODE_SRP_CAPABLE_HOST: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = (core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_NO_SRP_CAPABLE_HOST: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ break; ++ } ++ ++ DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32); ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ if (core_if->core_params->lpm_enable) { ++ glpmcfg_data_t lpmcfg = {.d32 = 0 }; ++ ++ /* To enable LPM support set lpm_cap_en bit */ ++ lpmcfg.b.lpm_cap_en = 1; ++ ++ /* Make AppL1Res ACK */ ++ lpmcfg.b.appl_resp = 1; ++ ++ /* Retry 3 times */ ++ lpmcfg.b.retry_count = 3; ++ ++ DWC_MODIFY_REG32(&core_if->core_global_regs->glpmcfg, ++ 0, lpmcfg.d32); ++ ++ } ++#endif ++ if (core_if->core_params->ic_usb_cap) { ++ gusbcfg_data_t gusbcfg = {.d32 = 0 }; ++ gusbcfg.b.ic_usb_cap = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gusbcfg, ++ 0, gusbcfg.d32); ++ } ++ { ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ gotgctl.b.otgver = core_if->core_params->otg_ver; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gotgctl, 0, ++ gotgctl.d32); ++ /* Set OTG version supported */ ++ core_if->otg_ver = core_if->core_params->otg_ver; ++ } ++ ++ /* Enable common interrupts */ ++ dwc_otg_enable_common_interrupts(core_if); ++ ++ /* Do device or host intialization based on mode during PCD ++ * and HCD initialization */ ++ if (dwc_otg_is_host_mode(core_if)) { ++ DWC_DEBUGPL(DBG_ANY, "Host Mode\n"); ++ core_if->op_state = A_HOST; ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "Device Mode\n"); ++ core_if->op_state = B_PERIPHERAL; ++#ifdef DWC_DEVICE_ONLY ++ dwc_otg_core_dev_init(core_if); ++#endif ++ } ++} ++ ++/** ++ * This function enables the Device mode interrupts. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ */ ++void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ ++ DWC_DEBUGPL(DBG_CIL, "%s()\n", __func__); ++ ++ /* Disable all interrupts. */ ++ DWC_WRITE_REG32(&global_regs->gintmsk, 0); ++ ++ /* Clear any pending interrupts */ ++ DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Enable the common interrupts */ ++ dwc_otg_enable_common_interrupts(core_if); ++ ++ /* Enable interrupts */ ++ intr_mask.b.usbreset = 1; ++ intr_mask.b.enumdone = 1; ++ /* Disable Disconnect interrupt in Device mode */ ++ intr_mask.b.disconnect = 0; ++ ++ if (!core_if->multiproc_int_enable) { ++ intr_mask.b.inepintr = 1; ++ intr_mask.b.outepintr = 1; ++ } ++ ++ intr_mask.b.erlysuspend = 1; ++ ++ if (core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.epmismatch = 1; ++ } ++ ++ //intr_mask.b.incomplisoout = 1; ++ if (!core_if->dma_desc_enable) ++ intr_mask.b.incomplisoin = 1; ++ ++/* Enable the ignore frame number for ISOC xfers - MAS */ ++/* Disable to support high bandwith ISOC transfers - manukz */ ++#if 0 ++#ifdef DWC_UTE_PER_IO ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable) { ++ dctl_data_t dctl1 = {.d32 = 0 }; ++ dctl1.b.ifrmnum = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs-> ++ dctl, 0, dctl1.d32); ++ DWC_DEBUG("----Enabled Ignore frame number (0x%08x)", ++ DWC_READ_REG32(&core_if->dev_if-> ++ dev_global_regs->dctl)); ++ } ++ } ++#endif ++#endif ++#ifdef DWC_EN_ISOC ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable == 0) { ++ if (core_if->pti_enh_enable) { ++ dctl_data_t dctl = {.d32 = 0 }; ++ dctl.b.ifrmnum = 1; ++ DWC_MODIFY_REG32(&core_if-> ++ dev_if->dev_global_regs->dctl, ++ 0, dctl.d32); ++ } else { ++ intr_mask.b.incomplisoin = 1; ++ intr_mask.b.incomplisoout = 1; ++ } ++ } ++ } else { ++ intr_mask.b.incomplisoin = 1; ++ intr_mask.b.incomplisoout = 1; ++ } ++#endif /* DWC_EN_ISOC */ ++ ++ /** @todo NGS: Should this be a module parameter? */ ++#ifdef USE_PERIODIC_EP ++ intr_mask.b.isooutdrop = 1; ++ intr_mask.b.eopframe = 1; ++ intr_mask.b.incomplisoin = 1; ++ intr_mask.b.incomplisoout = 1; ++#endif ++ ++ DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, "%s() gintmsk=%0x\n", __func__, ++ DWC_READ_REG32(&global_regs->gintmsk)); ++} ++ ++/** ++ * This function initializes the DWC_otg controller registers for ++ * device mode. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ */ ++void dwc_otg_core_dev_init(dwc_otg_core_if_t * core_if) ++{ ++ int i; ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dwc_otg_core_params_t *params = core_if->core_params; ++ dcfg_data_t dcfg = {.d32 = 0 }; ++ depctl_data_t diepctl = {.d32 = 0 }; ++ grstctl_t resetctl = {.d32 = 0 }; ++ uint32_t rx_fifo_size; ++ fifosize_data_t nptxfifosize; ++ fifosize_data_t txfifosize; ++ dthrctl_data_t dthrctl; ++ fifosize_data_t ptxfifosize; ++ uint16_t rxfsiz, nptxfsiz; ++ gdfifocfg_data_t gdfifocfg = {.d32 = 0 }; ++ hwcfg3_data_t hwcfg3 = {.d32 = 0 }; ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ ++ /* Restart the Phy Clock */ ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ /* Restart the Phy Clock */ ++ pcgcctl.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0); ++ dwc_udelay(10); ++ ++ /* Device configuration register */ ++ init_devspd(core_if); ++ dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg); ++ dcfg.b.descdma = (core_if->dma_desc_enable) ? 1 : 0; ++ dcfg.b.perfrint = DWC_DCFG_FRAME_INTERVAL_80; ++ /* Enable Device OUT NAK in case of DDMA mode */ ++ if (core_if->core_params->dev_out_nak) { ++ dcfg.b.endevoutnak = 1; ++ } ++ ++ if (core_if->core_params->cont_on_bna) { ++ dctl_data_t dctl = {.d32 = 0 }; ++ dctl.b.encontonbna = 1; ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, 0, dctl.d32); ++ } ++ /** should be done before every reset */ ++ if (core_if->otg_ver) { ++ core_if->otg_sts = 0; ++ gotgctl.b.devhnpen = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gotgctl, gotgctl.d32, 0); ++ } ++ ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32); ++ ++ /* Configure data FIFO sizes */ ++ if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) { ++ DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n", ++ core_if->total_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n", ++ params->dev_rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n", ++ params->dev_nperio_tx_fifo_size); ++ ++ /* Rx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n", ++ DWC_READ_REG32(&global_regs->grxfsiz)); ++ ++#ifdef DWC_UTE_CFI ++ core_if->pwron_rxfsiz = DWC_READ_REG32(&global_regs->grxfsiz); ++ core_if->init_rxfsiz = params->dev_rx_fifo_size; ++#endif ++ rx_fifo_size = params->dev_rx_fifo_size; ++ DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fifo_size); ++ ++ DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n", ++ DWC_READ_REG32(&global_regs->grxfsiz)); ++ ++ /** Set Periodic Tx FIFO Mask all bits 0 */ ++ core_if->p_tx_msk = 0; ++ ++ /** Set Tx FIFO Mask all bits 0 */ ++ core_if->tx_msk = 0; ++ ++ if (core_if->en_multiple_tx_fifo == 0) { ++ /* Non-periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n", ++ DWC_READ_REG32(&global_regs->gnptxfsiz)); ++ ++ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size; ++ nptxfifosize.b.startaddr = params->dev_rx_fifo_size; ++ ++ DWC_WRITE_REG32(&global_regs->gnptxfsiz, ++ nptxfifosize.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n", ++ DWC_READ_REG32(&global_regs->gnptxfsiz)); ++ ++ /**@todo NGS: Fix Periodic FIFO Sizing! */ ++ /* ++ * Periodic Tx FIFOs These FIFOs are numbered from 1 to 15. ++ * Indexes of the FIFO size module parameters in the ++ * dev_perio_tx_fifo_size array and the FIFO size registers in ++ * the dptxfsiz array run from 0 to 14. ++ */ ++ /** @todo Finish debug of this */ ++ ptxfifosize.b.startaddr = ++ nptxfifosize.b.startaddr + nptxfifosize.b.depth; ++ for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; i++) { ++ ptxfifosize.b.depth = ++ params->dev_perio_tx_fifo_size[i]; ++ DWC_DEBUGPL(DBG_CIL, ++ "initial dtxfsiz[%d]=%08x\n", i, ++ DWC_READ_REG32(&global_regs->dtxfsiz ++ [i])); ++ DWC_WRITE_REG32(&global_regs->dtxfsiz[i], ++ ptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL, "new dtxfsiz[%d]=%08x\n", ++ i, ++ DWC_READ_REG32(&global_regs->dtxfsiz ++ [i])); ++ ptxfifosize.b.startaddr += ptxfifosize.b.depth; ++ } ++ } else { ++ /* ++ * Tx FIFOs These FIFOs are numbered from 1 to 15. ++ * Indexes of the FIFO size module parameters in the ++ * dev_tx_fifo_size array and the FIFO size registers in ++ * the dtxfsiz array run from 0 to 14. ++ */ ++ ++ /* Non-periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n", ++ DWC_READ_REG32(&global_regs->gnptxfsiz)); ++ ++#ifdef DWC_UTE_CFI ++ core_if->pwron_gnptxfsiz = ++ (DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16); ++ core_if->init_gnptxfsiz = ++ params->dev_nperio_tx_fifo_size; ++#endif ++ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size; ++ nptxfifosize.b.startaddr = params->dev_rx_fifo_size; ++ ++ DWC_WRITE_REG32(&global_regs->gnptxfsiz, ++ nptxfifosize.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n", ++ DWC_READ_REG32(&global_regs->gnptxfsiz)); ++ ++ txfifosize.b.startaddr = ++ nptxfifosize.b.startaddr + nptxfifosize.b.depth; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ ++ txfifosize.b.depth = ++ params->dev_tx_fifo_size[i]; ++ ++ DWC_DEBUGPL(DBG_CIL, ++ "initial dtxfsiz[%d]=%08x\n", ++ i, ++ DWC_READ_REG32(&global_regs->dtxfsiz ++ [i])); ++ ++#ifdef DWC_UTE_CFI ++ core_if->pwron_txfsiz[i] = ++ (DWC_READ_REG32 ++ (&global_regs->dtxfsiz[i]) >> 16); ++ core_if->init_txfsiz[i] = ++ params->dev_tx_fifo_size[i]; ++#endif ++ DWC_WRITE_REG32(&global_regs->dtxfsiz[i], ++ txfifosize.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, ++ "new dtxfsiz[%d]=%08x\n", ++ i, ++ DWC_READ_REG32(&global_regs->dtxfsiz ++ [i])); ++ ++ txfifosize.b.startaddr += txfifosize.b.depth; ++ } ++ ++ /* Calculating DFIFOCFG for Device mode to include RxFIFO and NPTXFIFO ++ * Before 3.00a EpInfoBase was being configured in ep enable/disable ++ * routine as well. Starting from 3.00a it will be set to the end of ++ * allocated FIFO space here due to ep 0 OUT always keeping enabled ++ */ ++ gdfifocfg.d32 = DWC_READ_REG32(&global_regs->gdfifocfg); ++ hwcfg3.d32 = DWC_READ_REG32(&global_regs->ghwcfg3); ++ gdfifocfg.b.gdfifocfg = (DWC_READ_REG32(&global_regs->ghwcfg3) >> 16); ++ DWC_WRITE_REG32(&global_regs->gdfifocfg, gdfifocfg.d32); ++ if (core_if->snpsid <= OTG_CORE_REV_2_94a) { ++ rxfsiz = (DWC_READ_REG32(&global_regs->grxfsiz) & 0x0000ffff); ++ nptxfsiz = (DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16); ++ gdfifocfg.b.epinfobase = rxfsiz + nptxfsiz; ++ } else { ++ gdfifocfg.b.epinfobase = txfifosize.b.startaddr; ++ } ++ DWC_WRITE_REG32(&global_regs->gdfifocfg, gdfifocfg.d32); ++ } ++ } ++ ++ /* Flush the FIFOs */ ++ dwc_otg_flush_tx_fifo(core_if, 0x10); /* all Tx FIFOs */ ++ dwc_otg_flush_rx_fifo(core_if); ++ ++ /* Flush the Learning Queue. */ ++ resetctl.b.intknqflsh = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32); ++ ++ if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) { ++ core_if->start_predict = 0; ++ for (i = 0; i <= core_if->dev_if->num_in_eps; ++i) { ++ core_if->nextep_seq[i] = 0xff; // 0xff - EP not active ++ } ++ core_if->nextep_seq[0] = 0; ++ core_if->first_in_nextep_seq = 0; ++ diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl); ++ diepctl.b.nextep = 0; ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32); ++ ++ /* Update IN Endpoint Mismatch Count by active IN NP EP count + 1 */ ++ dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg); ++ dcfg.b.epmscnt = 2; ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32); ++ ++ DWC_DEBUGPL(DBG_CILV, ++ "%s first_in_nextep_seq= %2d; nextep_seq[]:\n", ++ __func__, core_if->first_in_nextep_seq); ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ DWC_DEBUGPL(DBG_CILV, "%2d ", core_if->nextep_seq[i]); ++ } ++ DWC_DEBUGPL(DBG_CILV, "\n"); ++ } ++ ++ /* Clear all pending Device Interrupts */ ++ /** @todo - if the condition needed to be checked ++ * or in any case all pending interrutps should be cleared? ++ */ ++ if (core_if->multiproc_int_enable) { ++ for (i = 0; i < core_if->dev_if->num_in_eps; ++i) { ++ DWC_WRITE_REG32(&dev_if->dev_global_regs-> ++ diepeachintmsk[i], 0); ++ } ++ ++ for (i = 0; i < core_if->dev_if->num_out_eps; ++i) { ++ DWC_WRITE_REG32(&dev_if->dev_global_regs-> ++ doepeachintmsk[i], 0); ++ } ++ ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->deachint, 0xFFFFFFFF); ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->deachintmsk, 0); ++ } else { ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->diepmsk, 0); ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->doepmsk, 0); ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->daint, 0xFFFFFFFF); ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->daintmsk, 0); ++ } ++ ++ for (i = 0; i <= dev_if->num_in_eps; i++) { ++ depctl_data_t depctl; ++ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl); ++ if (depctl.b.epena) { ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ } else { ++ depctl.d32 = 0; ++ depctl.b.snak = 1; ++ } ++ ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32); ++ ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->dieptsiz, 0); ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepdma, 0); ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepint, 0xFF); ++ } ++ ++ for (i = 1; i <= dev_if->num_out_eps; i++) { ++ depctl_data_t depctl; ++ depctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[i]->doepctl); ++ if (depctl.b.epena) { ++ int j = 0; ++ dctl_data_t dctl = {.d32 = 0 }; ++ gintmsk_data_t gintsts = {.d32 = 0 }; ++ doepint_data_t doepint = {.d32 = 0 }; ++ device_grxsts_data_t status; ++ dctl.b.sgoutnak = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32); ++ if (!core_if->dma_enable) { ++ do { ++ j++; ++ dwc_udelay(10); ++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ if (j == 100000) { ++ DWC_ERROR("SNAK is not set during 10s\n"); ++ break; ++ } ++ } while (!gintsts.b.rxstsqlvl); ++ status.d32 = DWC_READ_REG32(&global_regs->grxstsp); ++ if (status.b.pktsts == DWC_DSTS_GOUT_NAK) ++ DWC_DEBUGPL(DBG_PCDV, "Global OUT NAK\n"); ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ } ++ j = 0; ++ do { ++ j++; ++ dwc_udelay(10); ++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ if (j == 100000) { ++ DWC_ERROR("SNAK is not set during 10s\n"); ++ break; ++ } ++ } while (!gintsts.b.goutnakeff); ++ gintsts.d32 = 0; ++ gintsts.b.goutnakeff = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ j = 0; ++ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]->doepctl, depctl.d32); ++ do { ++ dwc_udelay(10); ++ doepint.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ out_ep_regs[i]->doepint); ++ if (j == 100000) { ++ DWC_ERROR("EPDIS was not set during 10s\n"); ++ break; ++ } ++ } while (!doepint.b.epdisabled); ++ ++ doepint.b.epdisabled = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]->doepint, doepint.d32); ++ ++ dctl.d32 = 0; ++ dctl.b.cgoutnak = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32); ++ } else { ++ depctl.d32 = 0; ++ depctl.b.snak = 1; ++ } ++ ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl, depctl.d32); ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doeptsiz, 0); ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepdma, 0); ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepint, 0xFF); ++ } ++ ++ if (core_if->en_multiple_tx_fifo && core_if->dma_enable) { ++ dev_if->non_iso_tx_thr_en = params->thr_ctl & 0x1; ++ dev_if->iso_tx_thr_en = (params->thr_ctl >> 1) & 0x1; ++ dev_if->rx_thr_en = (params->thr_ctl >> 2) & 0x1; ++ ++ dev_if->rx_thr_length = params->rx_thr_length; ++ dev_if->tx_thr_length = params->tx_thr_length; ++ ++ dev_if->setup_desc_index = 0; ++ ++ dthrctl.d32 = 0; ++ dthrctl.b.non_iso_thr_en = dev_if->non_iso_tx_thr_en; ++ dthrctl.b.iso_thr_en = dev_if->iso_tx_thr_en; ++ dthrctl.b.tx_thr_len = dev_if->tx_thr_length; ++ dthrctl.b.rx_thr_en = dev_if->rx_thr_en; ++ dthrctl.b.rx_thr_len = dev_if->rx_thr_length; ++ dthrctl.b.ahb_thr_ratio = params->ahb_thr_ratio; ++ ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->dtknqr3_dthrctl, ++ dthrctl.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, ++ "Non ISO Tx Thr - %d\nISO Tx Thr - %d\nRx Thr - %d\nTx Thr Len - %d\nRx Thr Len - %d\n", ++ dthrctl.b.non_iso_thr_en, dthrctl.b.iso_thr_en, ++ dthrctl.b.rx_thr_en, dthrctl.b.tx_thr_len, ++ dthrctl.b.rx_thr_len); ++ ++ } ++ ++ dwc_otg_enable_device_interrupts(core_if); ++ ++ { ++ diepmsk_data_t msk = {.d32 = 0 }; ++ msk.b.txfifoundrn = 1; ++ if (core_if->multiproc_int_enable) { ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs-> ++ diepeachintmsk[0], msk.d32, msk.d32); ++ } else { ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs->diepmsk, ++ msk.d32, msk.d32); ++ } ++ } ++ ++ if (core_if->multiproc_int_enable) { ++ /* Set NAK on Babble */ ++ dctl_data_t dctl = {.d32 = 0 }; ++ dctl.b.nakonbble = 1; ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, 0, dctl.d32); ++ } ++ ++ if (core_if->snpsid >= OTG_CORE_REV_2_94a) { ++ dctl_data_t dctl = {.d32 = 0 }; ++ dctl.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dctl); ++ dctl.b.sftdiscon = 0; ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->dctl, dctl.d32); ++ } ++} ++ ++/** ++ * This function enables the Host mode interrupts. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ */ ++void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_CIL, "%s()\n", __func__); ++ ++ /* Disable all interrupts. */ ++ DWC_WRITE_REG32(&global_regs->gintmsk, 0); ++ ++ /* Clear any pending interrupts. */ ++ DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Enable the common interrupts */ ++ dwc_otg_enable_common_interrupts(core_if); ++ ++ /* ++ * Enable host mode interrupts without disturbing common ++ * interrupts. ++ */ ++ ++ intr_mask.b.disconnect = 1; ++ intr_mask.b.portintr = 1; ++ intr_mask.b.hcintr = 1; ++ ++ DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++} ++ ++/** ++ * This function disables the Host Mode interrupts. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ */ ++void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s()\n", __func__); ++ ++ /* ++ * Disable host mode interrupts without disturbing common ++ * interrupts. ++ */ ++ intr_mask.b.sofintr = 1; ++ intr_mask.b.portintr = 1; ++ intr_mask.b.hcintr = 1; ++ intr_mask.b.ptxfempty = 1; ++ intr_mask.b.nptxfempty = 1; ++ ++ DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, 0); ++} ++ ++/** ++ * This function initializes the DWC_otg controller registers for ++ * host mode. ++ * ++ * This function flushes the Tx and Rx FIFOs and it flushes any entries in the ++ * request queues. Host channels are reset to ensure that they are ready for ++ * performing transfers. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ */ ++void dwc_otg_core_host_init(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ dwc_otg_host_if_t *host_if = core_if->host_if; ++ dwc_otg_core_params_t *params = core_if->core_params; ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++ fifosize_data_t nptxfifosize; ++ fifosize_data_t ptxfifosize; ++ uint16_t rxfsiz, nptxfsiz, hptxfsiz; ++ gdfifocfg_data_t gdfifocfg = {.d32 = 0 }; ++ int i; ++ hcchar_data_t hcchar; ++ hcfg_data_t hcfg; ++ hfir_data_t hfir; ++ dwc_otg_hc_regs_t *hc_regs; ++ int num_channels; ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, core_if); ++ ++ /* Restart the Phy Clock */ ++ pcgcctl.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0); ++ dwc_udelay(10); ++ ++ if ((core_if->otg_ver == 1) && (core_if->op_state == A_HOST)) { ++ DWC_PRINTF("Init: Port Power? op_state=%d\n", core_if->op_state); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ DWC_PRINTF("Init: Power Port (%d)\n", hprt0.b.prtpwr); ++ if (hprt0.b.prtpwr == 0) { ++ hprt0.b.prtpwr = 1; ++ DWC_WRITE_REG32(host_if->hprt0, hprt0.d32); ++ } ++ } ++ ++ /* Initialize Host Configuration Register */ ++ init_fslspclksel(core_if); ++ if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) { ++ hcfg.d32 = DWC_READ_REG32(&host_if->host_global_regs->hcfg); ++ hcfg.b.fslssupp = 1; ++ DWC_WRITE_REG32(&host_if->host_global_regs->hcfg, hcfg.d32); ++ ++ } ++ ++ /* This bit allows dynamic reloading of the HFIR register ++ * during runtime. This bit needs to be programmed during ++ * initial configuration and its value must not be changed ++ * during runtime.*/ ++ if (core_if->core_params->reload_ctl == 1) { ++ hfir.d32 = DWC_READ_REG32(&host_if->host_global_regs->hfir); ++ hfir.b.hfirrldctrl = 1; ++ DWC_WRITE_REG32(&host_if->host_global_regs->hfir, hfir.d32); ++ } ++ ++ if (core_if->core_params->dma_desc_enable) { ++ uint8_t op_mode = core_if->hwcfg2.b.op_mode; ++ if (! ++ (core_if->hwcfg4.b.desc_dma ++ && (core_if->snpsid >= OTG_CORE_REV_2_90a) ++ && ((op_mode == DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) ++ || (op_mode == DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) ++ || (op_mode == ++ DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG) ++ || (op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST) ++ || (op_mode == ++ DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST)))) { ++ ++ DWC_ERROR("Host can't operate in Descriptor DMA mode.\n" ++ "Either core version is below 2.90a or " ++ "GHWCFG2, GHWCFG4 registers' values do not allow Descriptor DMA in host mode.\n" ++ "To run the driver in Buffer DMA host mode set dma_desc_enable " ++ "module parameter to 0.\n"); ++ return; ++ } ++ hcfg.d32 = DWC_READ_REG32(&host_if->host_global_regs->hcfg); ++ hcfg.b.descdma = 1; ++ DWC_WRITE_REG32(&host_if->host_global_regs->hcfg, hcfg.d32); ++ } ++ ++ /* Configure data FIFO sizes */ ++ if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) { ++ DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n", ++ core_if->total_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n", ++ params->host_rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n", ++ params->host_nperio_tx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "P Tx FIFO Size=%d\n", ++ params->host_perio_tx_fifo_size); ++ ++ /* Rx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n", ++ DWC_READ_REG32(&global_regs->grxfsiz)); ++ DWC_WRITE_REG32(&global_regs->grxfsiz, ++ params->host_rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n", ++ DWC_READ_REG32(&global_regs->grxfsiz)); ++ ++ /* Non-periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n", ++ DWC_READ_REG32(&global_regs->gnptxfsiz)); ++ nptxfifosize.b.depth = params->host_nperio_tx_fifo_size; ++ nptxfifosize.b.startaddr = params->host_rx_fifo_size; ++ DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n", ++ DWC_READ_REG32(&global_regs->gnptxfsiz)); ++ ++ /* Periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial hptxfsiz=%08x\n", ++ DWC_READ_REG32(&global_regs->hptxfsiz)); ++ ptxfifosize.b.depth = params->host_perio_tx_fifo_size; ++ ptxfifosize.b.startaddr = ++ nptxfifosize.b.startaddr + nptxfifosize.b.depth; ++ DWC_WRITE_REG32(&global_regs->hptxfsiz, ptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL, "new hptxfsiz=%08x\n", ++ DWC_READ_REG32(&global_regs->hptxfsiz)); ++ ++ if (core_if->en_multiple_tx_fifo) { ++ /* Global DFIFOCFG calculation for Host mode - include RxFIFO, NPTXFIFO and HPTXFIFO */ ++ gdfifocfg.d32 = DWC_READ_REG32(&global_regs->gdfifocfg); ++ rxfsiz = (DWC_READ_REG32(&global_regs->grxfsiz) & 0x0000ffff); ++ nptxfsiz = (DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16); ++ hptxfsiz = (DWC_READ_REG32(&global_regs->hptxfsiz) >> 16); ++ gdfifocfg.b.epinfobase = rxfsiz + nptxfsiz + hptxfsiz; ++ DWC_WRITE_REG32(&global_regs->gdfifocfg, gdfifocfg.d32); ++ } ++ } ++ ++ /* TODO - check this */ ++ /* Clear Host Set HNP Enable in the OTG Control Register */ ++ gotgctl.b.hstsethnpen = 1; ++ DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0); ++ /* Make sure the FIFOs are flushed. */ ++ dwc_otg_flush_tx_fifo(core_if, 0x10 /* all TX FIFOs */ ); ++ dwc_otg_flush_rx_fifo(core_if); ++ ++ /* Clear Host Set HNP Enable in the OTG Control Register */ ++ gotgctl.b.hstsethnpen = 1; ++ DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0); ++ ++ if (!core_if->core_params->dma_desc_enable) { ++ /* Flush out any leftover queued requests. */ ++ num_channels = core_if->core_params->host_channels; ++ ++ for (i = 0; i < num_channels; i++) { ++ hc_regs = core_if->host_if->hc_regs[i]; ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hcchar.b.chen = 0; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++ } ++ ++ /* Halt all channels to put them into a known state. */ ++ for (i = 0; i < num_channels; i++) { ++ int count = 0; ++ hc_regs = core_if->host_if->hc_regs[i]; ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++ DWC_DEBUGPL(DBG_HCDV, "%s: Halt channel %d\n", __func__, i); ++ do { ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ if (++count > 1000) { ++ DWC_ERROR ++ ("%s: Unable to clear halt on channel %d\n", ++ __func__, i); ++ break; ++ } ++ dwc_udelay(1); ++ } while (hcchar.b.chen); ++ } ++ } ++ ++ /* Turn on the vbus power. */ ++ if ((core_if->otg_ver == 0) && (core_if->op_state == A_HOST)) { ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ DWC_PRINTF("Init: Power Port (%d)\n", hprt0.b.prtpwr); ++ if (hprt0.b.prtpwr == 0) { ++ hprt0.b.prtpwr = 1; ++ DWC_WRITE_REG32(host_if->hprt0, hprt0.d32); ++ } ++ } ++ ++ dwc_otg_enable_host_interrupts(core_if); ++} ++ ++/** ++ * Prepares a host channel for transferring packets to/from a specific ++ * endpoint. The HCCHARn register is set up with the characteristics specified ++ * in _hc. Host channel interrupts that may need to be serviced while this ++ * transfer is in progress are enabled. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * @param hc Information needed to initialize the host channel ++ */ ++void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ uint32_t intr_enable; ++ hcintmsk_data_t hc_intr_mask; ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ ++ uint8_t hc_num = hc->hc_num; ++ dwc_otg_host_if_t *host_if = core_if->host_if; ++ dwc_otg_hc_regs_t *hc_regs = host_if->hc_regs[hc_num]; ++ ++ /* Clear old interrupt conditions for this host channel. */ ++ hc_intr_mask.d32 = 0xFFFFFFFF; ++ hc_intr_mask.b.reserved14_31 = 0; ++ DWC_WRITE_REG32(&hc_regs->hcint, hc_intr_mask.d32); ++ ++ /* Enable channel interrupts required for this transfer. */ ++ hc_intr_mask.d32 = 0; ++ hc_intr_mask.b.chhltd = 1; ++ if (core_if->dma_enable) { ++ /* For Descriptor DMA mode core halts the channel on AHB error. Interrupt is not required */ ++ if (!core_if->dma_desc_enable) ++ hc_intr_mask.b.ahberr = 1; ++ else { ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) ++ hc_intr_mask.b.xfercompl = 1; ++ } ++ ++ if (hc->error_state && !hc->do_split && ++ hc->ep_type != DWC_OTG_EP_TYPE_ISOC) { ++ hc_intr_mask.b.ack = 1; ++ if (hc->ep_is_in) { ++ hc_intr_mask.b.datatglerr = 1; ++ if (hc->ep_type != DWC_OTG_EP_TYPE_INTR) { ++ hc_intr_mask.b.nak = 1; ++ } ++ } ++ } ++ } else { ++ switch (hc->ep_type) { ++ case DWC_OTG_EP_TYPE_CONTROL: ++ case DWC_OTG_EP_TYPE_BULK: ++ hc_intr_mask.b.xfercompl = 1; ++ hc_intr_mask.b.stall = 1; ++ hc_intr_mask.b.xacterr = 1; ++ hc_intr_mask.b.datatglerr = 1; ++ if (hc->ep_is_in) { ++ hc_intr_mask.b.bblerr = 1; ++ } else { ++ hc_intr_mask.b.nak = 1; ++ hc_intr_mask.b.nyet = 1; ++ if (hc->do_ping) { ++ hc_intr_mask.b.ack = 1; ++ } ++ } ++ ++ if (hc->do_split) { ++ hc_intr_mask.b.nak = 1; ++ if (hc->complete_split) { ++ hc_intr_mask.b.nyet = 1; ++ } else { ++ hc_intr_mask.b.ack = 1; ++ } ++ } ++ ++ if (hc->error_state) { ++ hc_intr_mask.b.ack = 1; ++ } ++ break; ++ case DWC_OTG_EP_TYPE_INTR: ++ hc_intr_mask.b.xfercompl = 1; ++ hc_intr_mask.b.nak = 1; ++ hc_intr_mask.b.stall = 1; ++ hc_intr_mask.b.xacterr = 1; ++ hc_intr_mask.b.datatglerr = 1; ++ hc_intr_mask.b.frmovrun = 1; ++ ++ if (hc->ep_is_in) { ++ hc_intr_mask.b.bblerr = 1; ++ } ++ if (hc->error_state) { ++ hc_intr_mask.b.ack = 1; ++ } ++ if (hc->do_split) { ++ if (hc->complete_split) { ++ hc_intr_mask.b.nyet = 1; ++ } else { ++ hc_intr_mask.b.ack = 1; ++ } ++ } ++ break; ++ case DWC_OTG_EP_TYPE_ISOC: ++ hc_intr_mask.b.xfercompl = 1; ++ hc_intr_mask.b.frmovrun = 1; ++ hc_intr_mask.b.ack = 1; ++ ++ if (hc->ep_is_in) { ++ hc_intr_mask.b.xacterr = 1; ++ hc_intr_mask.b.bblerr = 1; ++ } ++ break; ++ } ++ } ++ DWC_WRITE_REG32(&hc_regs->hcintmsk, hc_intr_mask.d32); ++ ++ /* Enable the top level host channel interrupt. */ ++ intr_enable = (1 << hc_num); ++ DWC_MODIFY_REG32(&host_if->host_global_regs->haintmsk, 0, intr_enable); ++ ++ /* Make sure host channel interrupts are enabled. */ ++ gintmsk.b.hcintr = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32); ++ ++ /* ++ * Program the HCCHARn register with the endpoint characteristics for ++ * the current transfer. ++ */ ++ hcchar.d32 = 0; ++ hcchar.b.devaddr = hc->dev_addr; ++ hcchar.b.epnum = hc->ep_num; ++ hcchar.b.epdir = hc->ep_is_in; ++ hcchar.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW); ++ hcchar.b.eptype = hc->ep_type; ++ hcchar.b.mps = hc->max_packet; ++ ++ DWC_WRITE_REG32(&host_if->hc_regs[hc_num]->hcchar, hcchar.d32); ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " Dev Addr: %d\n", hcchar.b.devaddr); ++ DWC_DEBUGPL(DBG_HCDV, " Ep Num: %d\n", hcchar.b.epnum); ++ DWC_DEBUGPL(DBG_HCDV, " Is In: %d\n", hcchar.b.epdir); ++ DWC_DEBUGPL(DBG_HCDV, " Is Low Speed: %d\n", hcchar.b.lspddev); ++ DWC_DEBUGPL(DBG_HCDV, " Ep Type: %d\n", hcchar.b.eptype); ++ DWC_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n", hcchar.b.mps); ++ DWC_DEBUGPL(DBG_HCDV, " Multi Cnt: %d\n", hcchar.b.multicnt); ++ ++ /* ++ * Program the HCSPLIT register for SPLITs ++ */ ++ hcsplt.d32 = 0; ++ if (hc->do_split) { ++ DWC_DEBUGPL(DBG_HCDV, "Programming HC %d with split --> %s\n", ++ hc->hc_num, ++ hc->complete_split ? "CSPLIT" : "SSPLIT"); ++ hcsplt.b.compsplt = hc->complete_split; ++ hcsplt.b.xactpos = hc->xact_pos; ++ hcsplt.b.hubaddr = hc->hub_addr; ++ hcsplt.b.prtaddr = hc->port_addr; ++ DWC_DEBUGPL(DBG_HCDV, " comp split %d\n", hc->complete_split); ++ DWC_DEBUGPL(DBG_HCDV, " xact pos %d\n", hc->xact_pos); ++ DWC_DEBUGPL(DBG_HCDV, " hub addr %d\n", hc->hub_addr); ++ DWC_DEBUGPL(DBG_HCDV, " port addr %d\n", hc->port_addr); ++ DWC_DEBUGPL(DBG_HCDV, " is_in %d\n", hc->ep_is_in); ++ DWC_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n", hcchar.b.mps); ++ DWC_DEBUGPL(DBG_HCDV, " xferlen: %d\n", hc->xfer_len); ++ } ++ DWC_WRITE_REG32(&host_if->hc_regs[hc_num]->hcsplt, hcsplt.d32); ++ ++} ++ ++/** ++ * Attempts to halt a host channel. This function should only be called in ++ * Slave mode or to abort a transfer in either Slave mode or DMA mode. Under ++ * normal circumstances in DMA mode, the controller halts the channel when the ++ * transfer is complete or a condition occurs that requires application ++ * intervention. ++ * ++ * In slave mode, checks for a free request queue entry, then sets the Channel ++ * Enable and Channel Disable bits of the Host Channel Characteristics ++ * register of the specified channel to intiate the halt. If there is no free ++ * request queue entry, sets only the Channel Disable bit of the HCCHARn ++ * register to flush requests for this channel. In the latter case, sets a ++ * flag to indicate that the host channel needs to be halted when a request ++ * queue slot is open. ++ * ++ * In DMA mode, always sets the Channel Enable and Channel Disable bits of the ++ * HCCHARn register. The controller ensures there is space in the request ++ * queue before submitting the halt request. ++ * ++ * Some time may elapse before the core flushes any posted requests for this ++ * host channel and halts. The Channel Halted interrupt handler completes the ++ * deactivation of the host channel. ++ * ++ * @param core_if Controller register interface. ++ * @param hc Host channel to halt. ++ * @param halt_status Reason for halting the channel. ++ */ ++void dwc_otg_hc_halt(dwc_otg_core_if_t * core_if, ++ dwc_hc_t * hc, dwc_otg_halt_status_e halt_status) ++{ ++ gnptxsts_data_t nptxsts; ++ hptxsts_data_t hptxsts; ++ hcchar_data_t hcchar; ++ dwc_otg_hc_regs_t *hc_regs; ++ dwc_otg_core_global_regs_t *global_regs; ++ dwc_otg_host_global_regs_t *host_global_regs; ++ ++ hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ global_regs = core_if->core_global_regs; ++ host_global_regs = core_if->host_if->host_global_regs; ++ ++ DWC_ASSERT(!(halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS), ++ "halt_status = %d\n", halt_status); ++ ++ if (halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE || ++ halt_status == DWC_OTG_HC_XFER_AHB_ERR) { ++ /* ++ * Disable all channel interrupts except Ch Halted. The QTD ++ * and QH state associated with this transfer has been cleared ++ * (in the case of URB_DEQUEUE), so the channel needs to be ++ * shut down carefully to prevent crashes. ++ */ ++ hcintmsk_data_t hcintmsk; ++ hcintmsk.d32 = 0; ++ hcintmsk.b.chhltd = 1; ++ DWC_WRITE_REG32(&hc_regs->hcintmsk, hcintmsk.d32); ++ ++ /* ++ * Make sure no other interrupts besides halt are currently ++ * pending. Handling another interrupt could cause a crash due ++ * to the QTD and QH state. ++ */ ++ DWC_WRITE_REG32(&hc_regs->hcint, ~hcintmsk.d32); ++ ++ /* ++ * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR ++ * even if the channel was already halted for some other ++ * reason. ++ */ ++ hc->halt_status = halt_status; ++ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ if (hcchar.b.chen == 0) { ++ /* ++ * The channel is either already halted or it hasn't ++ * started yet. In DMA mode, the transfer may halt if ++ * it finishes normally or a condition occurs that ++ * requires driver intervention. Don't want to halt ++ * the channel again. In either Slave or DMA mode, ++ * it's possible that the transfer has been assigned ++ * to a channel, but not started yet when an URB is ++ * dequeued. Don't want to halt a channel that hasn't ++ * started yet. ++ */ ++ return; ++ } ++ } ++ if (hc->halt_pending) { ++ /* ++ * A halt has already been issued for this channel. This might ++ * happen when a transfer is aborted by a higher level in ++ * the stack. ++ */ ++#ifdef DEBUG ++ DWC_PRINTF ++ ("*** %s: Channel %d, _hc->halt_pending already set ***\n", ++ __func__, hc->hc_num); ++ ++#endif ++ return; ++ } ++ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ ++ /* No need to set the bit in DDMA for disabling the channel */ ++ //TODO check it everywhere channel is disabled ++ if (!core_if->core_params->dma_desc_enable) ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 1; ++ ++ if (!core_if->dma_enable) { ++ /* Check for space in the request queue to issue the halt. */ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || ++ hc->ep_type == DWC_OTG_EP_TYPE_BULK) { ++ nptxsts.d32 = DWC_READ_REG32(&global_regs->gnptxsts); ++ if (nptxsts.b.nptxqspcavail == 0) { ++ hcchar.b.chen = 0; ++ } ++ } else { ++ hptxsts.d32 = ++ DWC_READ_REG32(&host_global_regs->hptxsts); ++ if ((hptxsts.b.ptxqspcavail == 0) ++ || (core_if->queuing_high_bandwidth)) { ++ hcchar.b.chen = 0; ++ } ++ } ++ } ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++ ++ hc->halt_status = halt_status; ++ ++ if (hcchar.b.chen) { ++ hc->halt_pending = 1; ++ hc->halt_on_queue = 0; ++ } else { ++ hc->halt_on_queue = 1; ++ } ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " hcchar: 0x%08x\n", hcchar.d32); ++ DWC_DEBUGPL(DBG_HCDV, " halt_pending: %d\n", hc->halt_pending); ++ DWC_DEBUGPL(DBG_HCDV, " halt_on_queue: %d\n", hc->halt_on_queue); ++ DWC_DEBUGPL(DBG_HCDV, " halt_status: %d\n", hc->halt_status); ++ ++ return; ++} ++ ++/** ++ * Clears the transfer state for a host channel. This function is normally ++ * called after a transfer is done and the host channel is being released. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param hc Identifies the host channel to clean up. ++ */ ++void dwc_otg_hc_cleanup(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ dwc_otg_hc_regs_t *hc_regs; ++ ++ hc->xfer_started = 0; ++ ++ /* ++ * Clear channel interrupt enables and any unhandled channel interrupt ++ * conditions. ++ */ ++ hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ DWC_WRITE_REG32(&hc_regs->hcintmsk, 0); ++ DWC_WRITE_REG32(&hc_regs->hcint, 0xFFFFFFFF); ++#ifdef DEBUG ++ DWC_TIMER_CANCEL(core_if->hc_xfer_timer[hc->hc_num]); ++#endif ++} ++ ++/** ++ * Sets the channel property that indicates in which frame a periodic transfer ++ * should occur. This is always set to the _next_ frame. This function has no ++ * effect on non-periodic transfers. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param hc Identifies the host channel to set up and its properties. ++ * @param hcchar Current value of the HCCHAR register for the specified host ++ * channel. ++ */ ++static inline void hc_set_even_odd_frame(dwc_otg_core_if_t * core_if, ++ dwc_hc_t * hc, hcchar_data_t * hcchar) ++{ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ hfnum_data_t hfnum; ++ hfnum.d32 = ++ DWC_READ_REG32(&core_if->host_if->host_global_regs->hfnum); ++ ++ /* 1 if _next_ frame is odd, 0 if it's even */ ++ hcchar->b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1; ++#ifdef DEBUG ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR && hc->do_split ++ && !hc->complete_split) { ++ switch (hfnum.b.frnum & 0x7) { ++ case 7: ++ core_if->hfnum_7_samples++; ++ core_if->hfnum_7_frrem_accum += hfnum.b.frrem; ++ break; ++ case 0: ++ core_if->hfnum_0_samples++; ++ core_if->hfnum_0_frrem_accum += hfnum.b.frrem; ++ break; ++ default: ++ core_if->hfnum_other_samples++; ++ core_if->hfnum_other_frrem_accum += ++ hfnum.b.frrem; ++ break; ++ } ++ } ++#endif ++ } ++} ++ ++#ifdef DEBUG ++void hc_xfer_timeout(void *ptr) ++{ ++ hc_xfer_info_t *xfer_info = NULL; ++ int hc_num = 0; ++ ++ if (ptr) ++ xfer_info = (hc_xfer_info_t *) ptr; ++ ++ if (!xfer_info) ++ return; ++ ++ if (!xfer_info->hc) { ++ DWC_ERROR("xfer_info->hc = %p\n", xfer_info->hc); ++ return; ++ } ++ ++ hc_num = xfer_info->hc->hc_num; ++ DWC_WARN("%s: timeout on channel %d\n", __func__, hc_num); ++ DWC_WARN(" start_hcchar_val 0x%08x\n", ++ xfer_info->core_if->start_hcchar_val[hc_num]); ++} ++#endif ++ ++void ep_xfer_timeout(void *ptr) ++{ ++ ep_xfer_info_t *xfer_info = NULL; ++ int ep_num = 0; ++ dctl_data_t dctl = {.d32 = 0 }; ++ gintsts_data_t gintsts = {.d32 = 0 }; ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ ++ if (ptr) ++ xfer_info = (ep_xfer_info_t *) ptr; ++ ++ if (!xfer_info->ep) { ++ DWC_ERROR("xfer_info->ep = %p\n", xfer_info->ep); ++ return; ++ } ++ ++ ep_num = xfer_info->ep->num; ++ DWC_WARN("%s: timeout on endpoit %d\n", __func__, ep_num); ++ /* Put the sate to 2 as it was time outed */ ++ xfer_info->state = 2; ++ ++ dctl.d32 = ++ DWC_READ_REG32(&xfer_info->core_if->dev_if->dev_global_regs->dctl); ++ gintsts.d32 = ++ DWC_READ_REG32(&xfer_info->core_if->core_global_regs->gintsts); ++ gintmsk.d32 = ++ DWC_READ_REG32(&xfer_info->core_if->core_global_regs->gintmsk); ++ ++ if (!gintmsk.b.goutnakeff) { ++ /* Unmask it */ ++ gintmsk.b.goutnakeff = 1; ++ DWC_WRITE_REG32(&xfer_info->core_if->core_global_regs->gintmsk, ++ gintmsk.d32); ++ ++ } ++ ++ if (!gintsts.b.goutnakeff) { ++ dctl.b.sgoutnak = 1; ++ } ++ DWC_WRITE_REG32(&xfer_info->core_if->dev_if->dev_global_regs->dctl, ++ dctl.d32); ++ ++} ++ ++void set_pid_isoc(dwc_hc_t * hc) ++{ ++ /* Set up the initial PID for the transfer. */ ++ if (hc->speed == DWC_OTG_EP_SPEED_HIGH) { ++ if (hc->ep_is_in) { ++ if (hc->multi_count == 1) { ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA0; ++ } else if (hc->multi_count == 2) { ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA1; ++ } else { ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA2; ++ } ++ } else { ++ if (hc->multi_count == 1) { ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA0; ++ } else { ++ hc->data_pid_start = DWC_OTG_HC_PID_MDATA; ++ } ++ } ++ } else { ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA0; ++ } ++} ++ ++/** ++ * This function does the setup for a data transfer for a host channel and ++ * starts the transfer. May be called in either Slave mode or DMA mode. In ++ * Slave mode, the caller must ensure that there is sufficient space in the ++ * request queue and Tx Data FIFO. ++ * ++ * For an OUT transfer in Slave mode, it loads a data packet into the ++ * appropriate FIFO. If necessary, additional data packets will be loaded in ++ * the Host ISR. ++ * ++ * For an IN transfer in Slave mode, a data packet is requested. The data ++ * packets are unloaded from the Rx FIFO in the Host ISR. If necessary, ++ * additional data packets are requested in the Host ISR. ++ * ++ * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ ++ * register along with a packet count of 1 and the channel is enabled. This ++ * causes a single PING transaction to occur. Other fields in HCTSIZ are ++ * simply set to 0 since no data transfer occurs in this case. ++ * ++ * For a PING transfer in DMA mode, the HCTSIZ register is initialized with ++ * all the information required to perform the subsequent data transfer. In ++ * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the ++ * controller performs the entire PING protocol, then starts the data ++ * transfer. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param hc Information needed to initialize the host channel. The xfer_len ++ * value may be reduced to accommodate the max widths of the XferSize and ++ * PktCnt fields in the HCTSIZn register. The multi_count value may be changed ++ * to reflect the final xfer_len value. ++ */ ++void dwc_otg_hc_start_transfer(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ uint16_t num_packets; ++ uint32_t max_hc_xfer_size = core_if->core_params->max_transfer_size; ++ uint16_t max_hc_pkt_count = core_if->core_params->max_packet_count; ++ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ ++ hctsiz.d32 = 0; ++ ++ if (hc->do_ping) { ++ if (!core_if->dma_enable) { ++ dwc_otg_hc_do_ping(core_if, hc); ++ hc->xfer_started = 1; ++ return; ++ } else { ++ hctsiz.b.dopng = 1; ++ } ++ } ++ ++ if (hc->do_split) { ++ num_packets = 1; ++ ++ if (hc->complete_split && !hc->ep_is_in) { ++ /* For CSPLIT OUT Transfer, set the size to 0 so the ++ * core doesn't expect any data written to the FIFO */ ++ hc->xfer_len = 0; ++ } else if (hc->ep_is_in || (hc->xfer_len > hc->max_packet)) { ++ hc->xfer_len = hc->max_packet; ++ } else if (!hc->ep_is_in && (hc->xfer_len > 188)) { ++ hc->xfer_len = 188; ++ } ++ ++ hctsiz.b.xfersize = hc->xfer_len; ++ } else { ++ /* ++ * Ensure that the transfer length and packet count will fit ++ * in the widths allocated for them in the HCTSIZn register. ++ */ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * Make sure the transfer size is no larger than one ++ * (micro)frame's worth of data. (A check was done ++ * when the periodic transfer was accepted to ensure ++ * that a (micro)frame's worth of data can be ++ * programmed into a channel.) ++ */ ++ uint32_t max_periodic_len = ++ hc->multi_count * hc->max_packet; ++ if (hc->xfer_len > max_periodic_len) { ++ hc->xfer_len = max_periodic_len; ++ } else { ++ } ++ } else if (hc->xfer_len > max_hc_xfer_size) { ++ /* Make sure that xfer_len is a multiple of max packet size. */ ++ hc->xfer_len = max_hc_xfer_size - hc->max_packet + 1; ++ } ++ ++ if (hc->xfer_len > 0) { ++ num_packets = ++ (hc->xfer_len + hc->max_packet - ++ 1) / hc->max_packet; ++ if (num_packets > max_hc_pkt_count) { ++ num_packets = max_hc_pkt_count; ++ hc->xfer_len = num_packets * hc->max_packet; ++ } ++ } else { ++ /* Need 1 packet for transfer length of 0. */ ++ num_packets = 1; ++ } ++ ++ if (hc->ep_is_in) { ++ /* Always program an integral # of max packets for IN transfers. */ ++ hc->xfer_len = num_packets * hc->max_packet; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * Make sure that the multi_count field matches the ++ * actual transfer length. ++ */ ++ hc->multi_count = num_packets; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) ++ set_pid_isoc(hc); ++ ++ hctsiz.b.xfersize = hc->xfer_len; ++ } ++ ++ hc->start_pkt_count = num_packets; ++ hctsiz.b.pktcnt = num_packets; ++ hctsiz.b.pid = hc->data_pid_start; ++ DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " Xfer Size: %d\n", hctsiz.b.xfersize); ++ DWC_DEBUGPL(DBG_HCDV, " Num Pkts: %d\n", hctsiz.b.pktcnt); ++ DWC_DEBUGPL(DBG_HCDV, " Start PID: %d\n", hctsiz.b.pid); ++ ++ if (core_if->dma_enable) { ++ dwc_dma_t dma_addr; ++ if (hc->align_buff) { ++ dma_addr = hc->align_buff; ++ } else { ++ dma_addr = ((unsigned long)hc->xfer_buff & 0xffffffff); ++ } ++ DWC_WRITE_REG32(&hc_regs->hcdma, dma_addr); ++ } ++ ++ /* Start the split */ ++ if (hc->do_split) { ++ hcsplt_data_t hcsplt; ++ hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt); ++ hcsplt.b.spltena = 1; ++ DWC_WRITE_REG32(&hc_regs->hcsplt, hcsplt.d32); ++ } ++ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hcchar.b.multicnt = hc->multi_count; ++ hc_set_even_odd_frame(core_if, hc, &hcchar); ++#ifdef DEBUG ++ core_if->start_hcchar_val[hc->hc_num] = hcchar.d32; ++ if (hcchar.b.chdis) { ++ DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", ++ __func__, hc->hc_num, hcchar.d32); ++ } ++#endif ++ ++ /* Set host channel enable after all other setup is complete. */ ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++ ++ hc->xfer_started = 1; ++ hc->requests++; ++ ++ if (!core_if->dma_enable && !hc->ep_is_in && hc->xfer_len > 0) { ++ /* Load OUT packet into the appropriate Tx FIFO. */ ++ dwc_otg_hc_write_packet(core_if, hc); ++ } ++#ifdef DEBUG ++ if (hc->ep_type != DWC_OTG_EP_TYPE_INTR) { ++ core_if->hc_xfer_info[hc->hc_num].core_if = core_if; ++ core_if->hc_xfer_info[hc->hc_num].hc = hc; ++ ++ /* Start a timer for this transfer. */ ++ DWC_TIMER_SCHEDULE(core_if->hc_xfer_timer[hc->hc_num], 10000); ++ } ++#endif ++} ++ ++/** ++ * This function does the setup for a data transfer for a host channel ++ * and starts the transfer in Descriptor DMA mode. ++ * ++ * Initializes HCTSIZ register. For a PING transfer the Do Ping bit is set. ++ * Sets PID and NTD values. For periodic transfers ++ * initializes SCHED_INFO field with micro-frame bitmap. ++ * ++ * Initializes HCDMA register with descriptor list address and CTD value ++ * then starts the transfer via enabling the channel. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param hc Information needed to initialize the host channel. ++ */ ++void dwc_otg_hc_start_transfer_ddma(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ hcdma_data_t hcdma; ++ ++ hctsiz.d32 = 0; ++ ++ if (hc->do_ping) ++ hctsiz.b_ddma.dopng = 1; ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) ++ set_pid_isoc(hc); ++ ++ /* Packet Count and Xfer Size are not used in Descriptor DMA mode */ ++ hctsiz.b_ddma.pid = hc->data_pid_start; ++ hctsiz.b_ddma.ntd = hc->ntd - 1; /* 0 - 1 descriptor, 1 - 2 descriptors, etc. */ ++ hctsiz.b_ddma.schinfo = hc->schinfo; /* Non-zero only for high-speed interrupt endpoints */ ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " Start PID: %d\n", hctsiz.b.pid); ++ DWC_DEBUGPL(DBG_HCDV, " NTD: %d\n", hctsiz.b_ddma.ntd); ++ ++ DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ hcdma.d32 = 0; ++ hcdma.b.dma_addr = ((uint32_t) hc->desc_list_addr) >> 11; ++ ++ /* Always start from first descriptor. */ ++ hcdma.b.ctd = 0; ++ DWC_WRITE_REG32(&hc_regs->hcdma, hcdma.d32); ++ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hcchar.b.multicnt = hc->multi_count; ++ ++#ifdef DEBUG ++ core_if->start_hcchar_val[hc->hc_num] = hcchar.d32; ++ if (hcchar.b.chdis) { ++ DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", ++ __func__, hc->hc_num, hcchar.d32); ++ } ++#endif ++ ++ /* Set host channel enable after all other setup is complete. */ ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++ ++ hc->xfer_started = 1; ++ hc->requests++; ++ ++#ifdef DEBUG ++ if ((hc->ep_type != DWC_OTG_EP_TYPE_INTR) ++ && (hc->ep_type != DWC_OTG_EP_TYPE_ISOC)) { ++ core_if->hc_xfer_info[hc->hc_num].core_if = core_if; ++ core_if->hc_xfer_info[hc->hc_num].hc = hc; ++ /* Start a timer for this transfer. */ ++ DWC_TIMER_SCHEDULE(core_if->hc_xfer_timer[hc->hc_num], 10000); ++ } ++#endif ++ ++} ++ ++/** ++ * This function continues a data transfer that was started by previous call ++ * to <code>dwc_otg_hc_start_transfer</code>. The caller must ensure there is ++ * sufficient space in the request queue and Tx Data FIFO. This function ++ * should only be called in Slave mode. In DMA mode, the controller acts ++ * autonomously to complete transfers programmed to a host channel. ++ * ++ * For an OUT transfer, a new data packet is loaded into the appropriate FIFO ++ * if there is any data remaining to be queued. For an IN transfer, another ++ * data packet is always requested. For the SETUP phase of a control transfer, ++ * this function does nothing. ++ * ++ * @return 1 if a new request is queued, 0 if no more requests are required ++ * for this transfer. ++ */ ++int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ ++ if (hc->do_split) { ++ /* SPLITs always queue just once per channel */ ++ return 0; ++ } else if (hc->data_pid_start == DWC_OTG_HC_PID_SETUP) { ++ /* SETUPs are queued only once since they can't be NAKed. */ ++ return 0; ++ } else if (hc->ep_is_in) { ++ /* ++ * Always queue another request for other IN transfers. If ++ * back-to-back INs are issued and NAKs are received for both, ++ * the driver may still be processing the first NAK when the ++ * second NAK is received. When the interrupt handler clears ++ * the NAK interrupt for the first NAK, the second NAK will ++ * not be seen. So we can't depend on the NAK interrupt ++ * handler to requeue a NAKed request. Instead, IN requests ++ * are issued each time this function is called. When the ++ * transfer completes, the extra requests for the channel will ++ * be flushed. ++ */ ++ hcchar_data_t hcchar; ++ dwc_otg_hc_regs_t *hc_regs = ++ core_if->host_if->hc_regs[hc->hc_num]; ++ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hc_set_even_odd_frame(core_if, hc, &hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ DWC_DEBUGPL(DBG_HCDV, " IN xfer: hcchar = 0x%08x\n", ++ hcchar.d32); ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++ hc->requests++; ++ return 1; ++ } else { ++ /* OUT transfers. */ ++ if (hc->xfer_count < hc->xfer_len) { ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ hcchar_data_t hcchar; ++ dwc_otg_hc_regs_t *hc_regs; ++ hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hc_set_even_odd_frame(core_if, hc, &hcchar); ++ } ++ ++ /* Load OUT packet into the appropriate Tx FIFO. */ ++ dwc_otg_hc_write_packet(core_if, hc); ++ hc->requests++; ++ return 1; ++ } else { ++ return 0; ++ } ++ } ++} ++ ++/** ++ * Starts a PING transfer. This function should only be called in Slave mode. ++ * The Do Ping bit is set in the HCTSIZ register, then the channel is enabled. ++ */ ++void dwc_otg_hc_do_ping(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ ++ hctsiz.d32 = 0; ++ hctsiz.b.dopng = 1; ++ hctsiz.b.pktcnt = 1; ++ DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++} ++ ++/* ++ * This function writes a packet into the Tx FIFO associated with the Host ++ * Channel. For a channel associated with a non-periodic EP, the non-periodic ++ * Tx FIFO is written. For a channel associated with a periodic EP, the ++ * periodic Tx FIFO is written. This function should only be called in Slave ++ * mode. ++ * ++ * Upon return the xfer_buff and xfer_count fields in _hc are incremented by ++ * then number of bytes written to the Tx FIFO. ++ */ ++void dwc_otg_hc_write_packet(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ uint32_t i; ++ uint32_t remaining_count; ++ uint32_t byte_count; ++ uint32_t dword_count; ++ ++ uint32_t *data_buff = (uint32_t *) (hc->xfer_buff); ++ uint32_t *data_fifo = core_if->data_fifo[hc->hc_num]; ++ ++ remaining_count = hc->xfer_len - hc->xfer_count; ++ if (remaining_count > hc->max_packet) { ++ byte_count = hc->max_packet; ++ } else { ++ byte_count = remaining_count; ++ } ++ ++ dword_count = (byte_count + 3) / 4; ++ ++ if ((((unsigned long)data_buff) & 0x3) == 0) { ++ /* xfer_buff is DWORD aligned. */ ++ for (i = 0; i < dword_count; i++, data_buff++) { ++ DWC_WRITE_REG32(data_fifo, *data_buff); ++ } ++ } else { ++ /* xfer_buff is not DWORD aligned. */ ++ for (i = 0; i < dword_count; i++, data_buff++) { ++ uint32_t data; ++ data = ++ (data_buff[0] | data_buff[1] << 8 | data_buff[2] << ++ 16 | data_buff[3] << 24); ++ DWC_WRITE_REG32(data_fifo, data); ++ } ++ } ++ ++ hc->xfer_count += byte_count; ++ hc->xfer_buff += byte_count; ++} ++ ++/** ++ * Gets the current USB frame number. This is the frame number from the last ++ * SOF packet. ++ */ ++uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t * core_if) ++{ ++ dsts_data_t dsts; ++ dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ /* read current frame/microframe number from DSTS register */ ++ return dsts.b.soffn; ++} ++ ++/** ++ * Calculates and gets the frame Interval value of HFIR register according PHY ++ * type and speed.The application can modify a value of HFIR register only after ++ * the Port Enable bit of the Host Port Control and Status register ++ * (HPRT.PrtEnaPort) has been set. ++*/ ++ ++uint32_t calc_frame_interval(dwc_otg_core_if_t * core_if) ++{ ++ gusbcfg_data_t usbcfg; ++ hwcfg2_data_t hwcfg2; ++ hprt0_data_t hprt0; ++ int clock = 60; // default value ++ usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++ hwcfg2.d32 = DWC_READ_REG32(&core_if->core_global_regs->ghwcfg2); ++ hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0); ++ if (!usbcfg.b.physel && usbcfg.b.ulpi_utmi_sel && !usbcfg.b.phyif) ++ clock = 60; ++ if (usbcfg.b.physel && hwcfg2.b.fs_phy_type == 3) ++ clock = 48; ++ if (!usbcfg.b.phylpwrclksel && !usbcfg.b.physel && ++ !usbcfg.b.ulpi_utmi_sel && usbcfg.b.phyif) ++ clock = 30; ++ if (!usbcfg.b.phylpwrclksel && !usbcfg.b.physel && ++ !usbcfg.b.ulpi_utmi_sel && !usbcfg.b.phyif) ++ clock = 60; ++ if (usbcfg.b.phylpwrclksel && !usbcfg.b.physel && ++ !usbcfg.b.ulpi_utmi_sel && usbcfg.b.phyif) ++ clock = 48; ++ if (usbcfg.b.physel && !usbcfg.b.phyif && hwcfg2.b.fs_phy_type == 2) ++ clock = 48; ++ if (usbcfg.b.physel && hwcfg2.b.fs_phy_type == 1) ++ clock = 48; ++ if (hprt0.b.prtspd == 0) ++ /* High speed case */ ++ return 125 * clock; ++ else ++ /* FS/LS case */ ++ return 1000 * clock; ++} ++ ++/** ++ * This function reads a setup packet from the Rx FIFO into the destination ++ * buffer. This function is called from the Rx Status Queue Level (RxStsQLvl) ++ * Interrupt routine when a SETUP packet has been received in Slave mode. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dest Destination buffer for packet data. ++ */ ++void dwc_otg_read_setup_packet(dwc_otg_core_if_t * core_if, uint32_t * dest) ++{ ++ device_grxsts_data_t status; ++ /* Get the 8 bytes of a setup transaction data */ ++ ++ /* Pop 2 DWORDS off the receive data FIFO into memory */ ++ dest[0] = DWC_READ_REG32(core_if->data_fifo[0]); ++ dest[1] = DWC_READ_REG32(core_if->data_fifo[0]); ++ if (core_if->snpsid >= OTG_CORE_REV_3_00a) { ++ status.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->grxstsp); ++ DWC_DEBUGPL(DBG_ANY, ++ "EP:%d BCnt:%d " "pktsts:%x Frame:%d(0x%0x)\n", ++ status.b.epnum, status.b.bcnt, status.b.pktsts, ++ status.b.fn, status.b.fn); ++ } ++} ++ ++/** ++ * This function enables EP0 OUT to receive SETUP packets and configures EP0 ++ * IN for transmitting packets. It is normally called when the ++ * "Enumeration Done" interrupt occurs. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP0 data. ++ */ ++void dwc_otg_ep0_activate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dsts_data_t dsts; ++ depctl_data_t diepctl, depctl_tmp; ++ depctl_data_t doepctl; ++ dctl_data_t dctl = {.d32 = 0 }; ++ uint8_t i; ++ ++ ep->stp_rollover = 0; ++ /* Read the Device Status and Endpoint 0 Control registers */ ++ dsts.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dsts); ++ diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl); ++ doepctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl); ++ ++ /* Set the MPS of the IN EP based on the enumeration speed */ ++ switch (dsts.b.enumspd) { ++ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: ++ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: ++ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ: ++ diepctl.b.mps = DWC_DEP0CTL_MPS_64; ++ break; ++ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ: ++ diepctl.b.mps = DWC_DEP0CTL_MPS_8; ++ break; ++ } ++ ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32); ++ ++ /* Enable OUT EP for receive */ ++ if (core_if->snpsid <= OTG_CORE_REV_2_94a) { ++ doepctl.b.epena = 1; ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32); ++ } ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, "doepctl0=%0x\n", ++ DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl)); ++ DWC_DEBUGPL(DBG_PCDV, "diepctl0=%0x\n", ++ DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl)); ++#endif ++ dctl.b.cgnpinnak = 1; ++ ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); ++ DWC_DEBUGPL(DBG_PCDV, "dctl=%0x\n", ++ DWC_READ_REG32(&dev_if->dev_global_regs->dctl)); ++ ++ for (i = 1; i <= core_if->dev_if->num_in_eps; i++) { ++ depctl_tmp.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl); ++ if (depctl_tmp.b.epena) { ++ depctl_tmp.d32 = 0; ++ depctl_tmp.b.epdis = 1; ++ depctl_tmp.b.snak = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]-> ++ diepctl, depctl_tmp.d32); ++ } ++ } ++} ++ ++/** ++ * This function activates an EP. The Device EP control register for ++ * the EP is configured as defined in the ep structure. Note: This ++ * function is not used for EP0. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to activate. ++ */ ++void dwc_otg_ep_activate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ depctl_data_t depctl; ++ volatile uint32_t *addr; ++ daint_data_t daintmsk = {.d32 = 0 }; ++ dcfg_data_t dcfg; ++ uint8_t i; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s() EP%d-%s\n", __func__, ep->num, ++ (ep->is_in ? "IN" : "OUT")); ++ ++#ifdef DWC_UTE_PER_IO ++ ep->xiso_frame_num = 0xFFFFFFFF; ++ ep->xiso_active_xfers = 0; ++ ep->xiso_queued_xfers = 0; ++#endif ++ /* Read DEPCTLn register */ ++ if (ep->is_in == 1) { ++ addr = &dev_if->in_ep_regs[ep->num]->diepctl; ++ daintmsk.ep.in = 1 << ep->num; ++ } else { ++ addr = &dev_if->out_ep_regs[ep->num]->doepctl; ++ daintmsk.ep.out = 1 << ep->num; ++ } ++ ++ /* If the EP is already active don't change the EP Control ++ * register. */ ++ depctl.d32 = DWC_READ_REG32(addr); ++ if (!depctl.b.usbactep) { ++ if (ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ if (((ep->num == 4) || (ep->num == 6)) && (ep->maxpacket < 256)) { ++ depctl_data_t depctl_tmp; ++ volatile uint32_t *addr_tmp; ++ ep->maxpacket = 256; ++ addr_tmp = &dev_if->in_ep_regs[3]->diepctl; ++ depctl_tmp.d32 = DWC_READ_REG32(addr_tmp); ++ if (depctl_tmp.b.eptype == 0x2) { ++ dctl_data_t dctl; ++ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl); ++ dctl.b.ifrmnum = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32); ++ } ++ } ++ } ++ depctl.b.mps = ep->maxpacket; ++ depctl.b.eptype = ep->type; ++ ++ if (ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ if (ep->num == 3) ++ ep->tx_fifo_num = 0x2; ++ if (ep->num == 4) ++ ep->tx_fifo_num = 0x3; ++ } ++ depctl.b.txfnum = ep->tx_fifo_num; ++ ++ /* ++ there are some rootcase have to be confirm with SNPS ++ whether ISOC-xfer need depctl.b.setd0pid = 1. ++ as current version ,we have to keep the follow opteration. ++ so, just remove if else, keep depctl.b.setd0pid = 1; ++ if (ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ depctl.b.setd0pid = 1; // ??? ++ } else { ++ depctl.b.setd0pid = 1; ++ } ++ depctl.b.usbactep = 1; ++ */ ++ depctl.b.usbactep = 1; ++ ++ /* Update nextep_seq array and EPMSCNT in DCFG */ ++ if (!(depctl.b.eptype & 1) && (ep->is_in == 1)) { // NP IN EP ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ if (core_if->nextep_seq[i] == core_if->first_in_nextep_seq) ++ break; ++ } ++ core_if->nextep_seq[i] = ep->num; ++ core_if->nextep_seq[ep->num] = core_if->first_in_nextep_seq; ++ depctl.b.nextep = core_if->nextep_seq[ep->num]; ++ dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg); ++ dcfg.b.epmscnt++; ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32); ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "%s first_in_nextep_seq= %2d; nextep_seq[]:\n", ++ __func__, core_if->first_in_nextep_seq); ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ DWC_DEBUGPL(DBG_PCDV, "%2d\n", ++ core_if->nextep_seq[i]); ++ } ++ ++ } ++ ++ ++ DWC_WRITE_REG32(addr, depctl.d32); ++ DWC_DEBUGPL(DBG_PCDV, "DEPCTL=%08x\n", DWC_READ_REG32(addr)); ++ } ++ ++ /* Enable the Interrupt for this EP */ ++ if (core_if->multiproc_int_enable) { ++ if (ep->is_in == 1) { ++ diepmsk_data_t diepmsk = {.d32 = 0 }; ++ diepmsk.b.xfercompl = 1; ++ diepmsk.b.timeout = 1; ++ diepmsk.b.epdisabled = 1; ++ diepmsk.b.ahberr = 1; ++ diepmsk.b.intknepmis = 1; ++ if (!core_if->en_multiple_tx_fifo && core_if->dma_enable) ++ diepmsk.b.intknepmis = 0; ++ diepmsk.b.txfifoundrn = 1; //????? ++ if (ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ diepmsk.b.nak = 1; ++ } ++ ++/* ++ if (core_if->dma_desc_enable) { ++ diepmsk.b.bna = 1; ++ } ++*/ ++/* ++ if (core_if->dma_enable) { ++ doepmsk.b.nak = 1; ++ } ++*/ ++ DWC_WRITE_REG32(&dev_if->dev_global_regs-> ++ diepeachintmsk[ep->num], diepmsk.d32); ++ ++ } else { ++ doepmsk_data_t doepmsk = {.d32 = 0 }; ++ doepmsk.b.xfercompl = 1; ++ doepmsk.b.ahberr = 1; ++ doepmsk.b.epdisabled = 1; ++ if (ep->type == DWC_OTG_EP_TYPE_ISOC) ++ doepmsk.b.outtknepdis = 1; ++ ++/* ++ ++ if (core_if->dma_desc_enable) { ++ doepmsk.b.bna = 1; ++ } ++*/ ++/* ++ doepmsk.b.babble = 1; ++ doepmsk.b.nyet = 1; ++ doepmsk.b.nak = 1; ++*/ ++ DWC_WRITE_REG32(&dev_if->dev_global_regs-> ++ doepeachintmsk[ep->num], doepmsk.d32); ++ } ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs->deachintmsk, ++ 0, daintmsk.d32); ++ } else { ++ if (ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ if (ep->is_in) { ++ diepmsk_data_t diepmsk = {.d32 = 0 }; ++ diepmsk.b.nak = 1; ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs->diepmsk, 0, diepmsk.d32); ++ } else { ++ doepmsk_data_t doepmsk = {.d32 = 0 }; ++ doepmsk.b.outtknepdis = 1; ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs->doepmsk, 0, doepmsk.d32); ++ } ++ } ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs->daintmsk, ++ 0, daintmsk.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "DAINTMSK=%0x\n", ++ DWC_READ_REG32(&dev_if->dev_global_regs->daintmsk)); ++ ++ ep->stall_clear_flag = 0; ++ ++ return; ++} ++ ++/** ++ * This function deactivates an EP. This is done by clearing the USB Active ++ * EP bit in the Device EP control register. Note: This function is not used ++ * for EP0. EP0 cannot be deactivated. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to deactivate. ++ */ ++void dwc_otg_ep_deactivate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl = {.d32 = 0 }; ++ volatile uint32_t *addr; ++ daint_data_t daintmsk = {.d32 = 0 }; ++ dcfg_data_t dcfg; ++ uint8_t i = 0; ++ ++#ifdef DWC_UTE_PER_IO ++ ep->xiso_frame_num = 0xFFFFFFFF; ++ ep->xiso_active_xfers = 0; ++ ep->xiso_queued_xfers = 0; ++#endif ++ ++ /* Read DEPCTLn register */ ++ if (ep->is_in == 1) { ++ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; ++ daintmsk.ep.in = 1 << ep->num; ++ } else { ++ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; ++ daintmsk.ep.out = 1 << ep->num; ++ } ++ ++ depctl.d32 = DWC_READ_REG32(addr); ++ ++ depctl.b.usbactep = 0; ++ ++ /* Update nextep_seq array and EPMSCNT in DCFG */ ++ if (!(depctl.b.eptype & 1) && ep->is_in == 1) { // NP EP IN ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ if (core_if->nextep_seq[i] == ep->num) ++ break; ++ } ++ core_if->nextep_seq[i] = core_if->nextep_seq[ep->num]; ++ if (core_if->first_in_nextep_seq == ep->num) ++ core_if->first_in_nextep_seq = i; ++ core_if->nextep_seq[ep->num] = 0xff; ++ depctl.b.nextep = 0; ++ dcfg.d32 = ++ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg); ++ dcfg.b.epmscnt--; ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, ++ dcfg.d32); ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "%s first_in_nextep_seq= %2d; nextep_seq[]:\n", ++ __func__, core_if->first_in_nextep_seq); ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ DWC_DEBUGPL(DBG_PCDV, "%2d\n", core_if->nextep_seq[i]); ++ } ++ } ++ ++ if (ep->is_in == 1) ++ depctl.b.txfnum = 0; ++ ++ if (core_if->dma_desc_enable) ++ depctl.b.epdis = 1; ++ ++ DWC_WRITE_REG32(addr, depctl.d32); ++ depctl.d32 = DWC_READ_REG32(addr); ++ if (core_if->dma_enable && depctl.b.epena) { ++ depctl_data_t depctl = {.d32 = 0 }; ++ if (ep->is_in) { ++ diepint_data_t diepint = {.d32 = 0 }; ++ ++ depctl.b.snak = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]-> ++ diepctl, depctl.d32); ++/* do { */ ++ dwc_udelay(10); ++ diepint.d32 = ++ DWC_READ_REG32(&core_if-> ++ dev_if->in_ep_regs[ep->num]-> ++ diepint); ++/* } while (!diepint.b.inepnakeff); */ ++ diepint.b.inepnakeff = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]-> ++ diepint, diepint.d32); ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]-> ++ diepctl, depctl.d32); ++/* do { */ ++ dwc_udelay(10); ++ diepint.d32 = ++ DWC_READ_REG32(&core_if-> ++ dev_if->in_ep_regs[ep->num]-> ++ diepint); ++/* } while (!diepint.b.epdisabled); */ ++ diepint.b.epdisabled = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]-> ++ diepint, diepint.d32); ++ } else if (ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ dctl_data_t dctl = {.d32 = 0}; ++ gintmsk_data_t gintsts = {.d32 = 0}; ++ doepint_data_t doepint = {.d32 = 0}; ++ dctl.b.sgoutnak = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs-> ++ dctl, 0, dctl.d32); ++ do { ++ dwc_udelay(10); ++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ } while (!gintsts.b.goutnakeff); ++ gintsts.d32 = 0; ++ gintsts.b.goutnakeff = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]->doepctl, depctl.d32); ++ do ++ { ++ dwc_udelay(10); ++ doepint.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ out_ep_regs[ep->num]->doepint); ++ } while (!doepint.b.epdisabled); ++ ++ doepint.b.epdisabled = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]->doepint, doepint.d32); ++ ++ dctl.d32 = 0; ++ dctl.b.cgoutnak = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32); ++ } ++ } ++ ++ /* Disable the Interrupt for this EP */ ++ if (core_if->multiproc_int_enable) { ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->deachintmsk, ++ daintmsk.d32, 0); ++ ++ if (ep->is_in == 1) { ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs-> ++ diepeachintmsk[ep->num], 0); ++ } else { ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs-> ++ doepeachintmsk[ep->num], 0); ++ } ++ } else { ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->daintmsk, ++ daintmsk.d32, 0); ++ } ++ ++} ++ ++/** ++ * This function initializes dma descriptor chain. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++static void init_dma_desc_chain(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ uint32_t offset; ++ uint32_t xfer_est; ++ int i; ++ unsigned maxxfer_local, total_len; ++ ++ if (!ep->is_in && ep->type == DWC_OTG_EP_TYPE_INTR && ++ (ep->maxpacket % 4)) { ++ maxxfer_local = ep->maxpacket; ++ total_len = ep->xfer_len; ++ } else { ++ maxxfer_local = ep->maxxfer; ++ total_len = ep->total_len; ++ } ++ ++ ep->desc_cnt = (total_len / maxxfer_local) + ++ ((total_len % maxxfer_local) ? 1 : 0); ++ ++ if (!ep->desc_cnt) ++ ep->desc_cnt = 1; ++ ++ if (ep->desc_cnt > MAX_DMA_DESC_CNT) ++ ep->desc_cnt = MAX_DMA_DESC_CNT; ++ ++ dma_desc = ep->desc_addr; ++ if (maxxfer_local == ep->maxpacket) { ++ if ((total_len % maxxfer_local) && ++ (total_len / maxxfer_local < MAX_DMA_DESC_CNT)) { ++ xfer_est = (ep->desc_cnt - 1) * maxxfer_local + ++ (total_len % maxxfer_local); ++ } else ++ xfer_est = ep->desc_cnt * maxxfer_local; ++ } else ++ xfer_est = total_len; ++ offset = 0; ++ for (i = 0; i < ep->desc_cnt; ++i) { ++ /** DMA Descriptor Setup */ ++ if (xfer_est > maxxfer_local) { ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 0; ++ dma_desc->status.b.ioc = 0; ++ dma_desc->status.b.sp = 0; ++ dma_desc->status.b.bytes = maxxfer_local; ++ dma_desc->buf = ep->dma_addr + offset; ++ dma_desc->status.b.sts = 0; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ xfer_est -= maxxfer_local; ++ offset += maxxfer_local; ++ } else { ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ if (ep->is_in) { ++ dma_desc->status.b.sp = ++ (xfer_est % ++ ep->maxpacket) ? 1 : ((ep-> ++ sent_zlp) ? 1 : 0); ++ dma_desc->status.b.bytes = xfer_est; ++ } else { ++ if (maxxfer_local == ep->maxpacket) ++ dma_desc->status.b.bytes = xfer_est; ++ else ++ dma_desc->status.b.bytes = ++ xfer_est + ((4 - (xfer_est & 0x3)) & 0x3); ++ } ++ ++ dma_desc->buf = ep->dma_addr + offset; ++ dma_desc->status.b.sts = 0; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ } ++ dma_desc++; ++ } ++} ++ ++/** ++ * This function is called when to write ISOC data into appropriate dedicated ++ * periodic FIFO. ++ */ ++static int32_t write_isoc_tx_fifo(dwc_otg_core_if_t * core_if, dwc_ep_t * dwc_ep) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dwc_otg_dev_in_ep_regs_t *ep_regs; ++ dtxfsts_data_t txstatus = {.d32 = 0 }; ++ uint32_t len = 0; ++ int epnum = dwc_ep->num; ++ int dwords; ++ ++ DWC_DEBUGPL(DBG_PCD, "Dedicated TxFifo Empty: %d \n", epnum); ++ ++ ep_regs = core_if->dev_if->in_ep_regs[epnum]; ++ ++ len = dwc_ep->xfer_len - dwc_ep->xfer_count; ++ ++ if (len > dwc_ep->maxpacket) { ++ len = dwc_ep->maxpacket; ++ } ++ ++ dwords = (len + 3) / 4; ++ ++ /* While there is space in the queue and space in the FIFO and ++ * More data to tranfer, Write packets to the Tx FIFO */ ++ txstatus.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, txstatus.d32); ++ ++ while (txstatus.b.txfspcavail >= dwords && ++ dwc_ep->xfer_count < dwc_ep->xfer_len && dwc_ep->xfer_len != 0) { ++ /* Write the FIFO */ ++ dwc_otg_ep_write_packet(core_if, dwc_ep, 0); ++ ++ len = dwc_ep->xfer_len - dwc_ep->xfer_count; ++ if (len > dwc_ep->maxpacket) { ++ len = dwc_ep->maxpacket; ++ } ++ ++ dwords = (len + 3) / 4; ++ txstatus.d32 = ++ DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", epnum, ++ txstatus.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, ++ DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts)); ++ ++ return 1; ++} ++ ++/** ++ * This function does the setup for a data transfer for an EP and ++ * starts the transfer. For an IN transfer, the packets will be ++ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers, ++ * the packets are unloaded from the Rx FIFO in the ISR. the ISR. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++ ++void dwc_otg_ep_start_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl; ++ deptsiz_data_t deptsiz; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__); ++ DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d " ++ "xfer_buff=%p start_xfer_buff=%p, total_len = %d\n", ++ ep->num, (ep->is_in ? "IN" : "OUT"), ep->xfer_len, ++ ep->xfer_count, ep->xfer_buff, ep->start_xfer_buff, ++ ep->total_len); ++ /* IN endpoint */ ++ if (ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[ep->num]; ++ ++ gnptxsts_data_t gtxstatus; ++ ++ gtxstatus.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->gnptxsts); ++ ++ if (core_if->en_multiple_tx_fifo == 0 ++ && gtxstatus.b.nptxqspcavail == 0 && !core_if->dma_enable) { ++#ifdef DEBUG ++ DWC_PRINTF("TX Queue Full (0x%0x)\n", gtxstatus.d32); ++#endif ++ return; ++ } ++ ++ depctl.d32 = DWC_READ_REG32(&(in_regs->diepctl)); ++ deptsiz.d32 = DWC_READ_REG32(&(in_regs->dieptsiz)); ++ ++ if (ep->maxpacket > ep->maxxfer / MAX_PKT_CNT) ++ ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ? ++ ep->maxxfer : (ep->total_len - ep->xfer_len); ++ else ++ ep->xfer_len += (MAX_PKT_CNT * ep->maxpacket < (ep->total_len - ep->xfer_len)) ? ++ MAX_PKT_CNT * ep->maxpacket : (ep->total_len - ep->xfer_len); ++ ++ ++ /* Zero Length Packet? */ ++ if ((ep->xfer_len - ep->xfer_count) == 0) { ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 1; ++ } else { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count; ++ deptsiz.b.pktcnt = ++ (ep->xfer_len - ep->xfer_count - 1 + ++ ep->maxpacket) / ep->maxpacket; ++ if (ep->type == DWC_OTG_EP_TYPE_ISOC) ++ deptsiz.b.mc = deptsiz.b.pktcnt; ++ } ++ ++ /* Write the DMA register */ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable == 0) { ++ if (ep->type != DWC_OTG_EP_TYPE_ISOC) ++ deptsiz.b.mc = 1; ++ DWC_WRITE_REG32(&in_regs->dieptsiz, ++ deptsiz.d32); ++ DWC_WRITE_REG32(&(in_regs->diepdma), ++ (uint32_t) ep->dma_addr); ++ } else { ++#ifdef DWC_UTE_CFI ++ /* The descriptor chain should be already initialized by now */ ++ if (ep->buff_mode != BM_STANDARD) { ++ DWC_WRITE_REG32(&in_regs->diepdma, ++ ep->descs_dma_addr); ++ } else { ++#endif ++ init_dma_desc_chain(core_if, ep); ++ /** DIEPDMAn Register write */ ++ DWC_WRITE_REG32(&in_regs->diepdma, ++ ep->dma_desc_addr); ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ } ++ } else { ++ DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32); ++ if (ep->type != DWC_OTG_EP_TYPE_ISOC) { ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, ++ * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode, ++ * the data will be written into the fifo by the ISR. ++ */ ++ if (core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ DWC_MODIFY_REG32 ++ (&core_if->core_global_regs->gintmsk, ++ intr_mask.d32, intr_mask.d32); ++ } else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if (ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk = 1 << ep->num; ++ DWC_MODIFY_REG32 ++ (&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk, ++ 0, fifoemptymsk); ++ ++ } ++ } ++ } ++ } ++ if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) ++ depctl.b.nextep = core_if->nextep_seq[ep->num]; ++ ++ if (ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ dsts_data_t dsts = {.d32 = 0 }; ++ if (ep->bInterval == 1) { ++ dsts.d32 = ++ DWC_READ_REG32(&core_if->dev_if-> ++ dev_global_regs->dsts); ++ ep->frame_num = dsts.b.soffn + ep->bInterval; ++ if (ep->frame_num > 0x3FFF) { ++ ep->frm_overrun = 1; ++ ep->frame_num &= 0x3FFF; ++ } else ++ ep->frm_overrun = 0; ++ if (ep->frame_num & 0x1) { ++ depctl.b.setd1pid = 1; ++ } else { ++ depctl.b.setd0pid = 1; ++ } ++ } ++ } ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32); ++ ++ if (!core_if->dma_enable && ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ write_isoc_tx_fifo(core_if, ep); ++ } ++ ++ } else { ++ /* OUT endpoint */ ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[ep->num]; ++ ++ depctl.d32 = DWC_READ_REG32(&(out_regs->doepctl)); ++ deptsiz.d32 = DWC_READ_REG32(&(out_regs->doeptsiz)); ++ ++ if (!core_if->dma_desc_enable) { ++ if (ep->maxpacket > ep->maxxfer / MAX_PKT_CNT) ++ ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ? ++ ep->maxxfer : (ep->total_len - ep->xfer_len); ++ else ++ ep->xfer_len += (MAX_PKT_CNT * ep->maxpacket < (ep->total_len ++ - ep->xfer_len)) ? MAX_PKT_CNT * ep->maxpacket : (ep->total_len - ep->xfer_len); ++ } ++ ++ /* Program the transfer size and packet count as follows: ++ * ++ * pktcnt = N ++ * xfersize = N * maxpacket ++ */ ++ if ((ep->xfer_len - ep->xfer_count) == 0) { ++ /* Zero Length Packet */ ++ deptsiz.b.xfersize = ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ } else { ++ deptsiz.b.pktcnt = ++ (ep->xfer_len - ep->xfer_count + ++ (ep->maxpacket - 1)) / ep->maxpacket; ++ if (!core_if->dma_desc_enable) { ++ ep->xfer_len = ++ deptsiz.b.pktcnt * ep->maxpacket + ep->xfer_count; ++ } ++ deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count; ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "ep%d xfersize=%d pktcnt=%d\n", ++ ep->num, deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ ++ if (core_if->dma_enable) { ++ if (!core_if->dma_desc_enable) { ++ DWC_WRITE_REG32(&out_regs->doeptsiz, ++ deptsiz.d32); ++ ++ DWC_WRITE_REG32(&(out_regs->doepdma), ++ (uint32_t) ep->dma_addr); ++ } else { ++#ifdef DWC_UTE_CFI ++ /* The descriptor chain should be already initialized by now */ ++ if (ep->buff_mode != BM_STANDARD) { ++ DWC_WRITE_REG32(&out_regs->doepdma, ++ ep->descs_dma_addr); ++ } else { ++#endif ++ /** This is used for interrupt out transfers*/ ++ if (!ep->xfer_len) ++ ep->xfer_len = ep->total_len; ++ init_dma_desc_chain(core_if, ep); ++ ++ if (core_if->core_params->dev_out_nak) { ++ if (ep->type == DWC_OTG_EP_TYPE_BULK) { ++ deptsiz.b.pktcnt = (ep->total_len + ++ (ep->maxpacket - 1)) / ep->maxpacket; ++ deptsiz.b.xfersize = ep->total_len; ++ /* Remember initial value of doeptsiz */ ++ core_if->start_doeptsiz_val[ep->num] = deptsiz.d32; ++ DWC_WRITE_REG32(&out_regs->doeptsiz, ++ deptsiz.d32); ++ } ++ } ++ /** DOEPDMAn Register write */ ++ DWC_WRITE_REG32(&out_regs->doepdma, ++ ep->dma_desc_addr); ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ } ++ } else { ++ DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32); ++ } ++ ++ if (ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ dsts_data_t dsts = {.d32 = 0 }; ++ if (ep->bInterval == 1) { ++ dsts.d32 = ++ DWC_READ_REG32(&core_if->dev_if-> ++ dev_global_regs->dsts); ++ ep->frame_num = dsts.b.soffn + ep->bInterval; ++ if (ep->frame_num > 0x3FFF) { ++ ep->frm_overrun = 1; ++ ep->frame_num &= 0x3FFF; ++ } else ++ ep->frm_overrun = 0; ++ ++ if (ep->frame_num & 0x1) { ++ depctl.b.setd1pid = 1; ++ } else { ++ depctl.b.setd0pid = 1; ++ } ++ } ++ } ++ ++ /* EP enable */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ ++ DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32); ++ ++ DWC_DEBUGPL(DBG_PCD, "DOEPCTL=%08x DOEPTSIZ=%08x\n", ++ DWC_READ_REG32(&out_regs->doepctl), ++ DWC_READ_REG32(&out_regs->doeptsiz)); ++ DWC_DEBUGPL(DBG_PCD, "DAINTMSK=%08x GINTMSK=%08x\n", ++ DWC_READ_REG32(&core_if->dev_if->dev_global_regs-> ++ daintmsk), ++ DWC_READ_REG32(&core_if->core_global_regs-> ++ gintmsk)); ++ ++ /* Timer is scheduling only for out bulk transfers for ++ * "Device DDMA OUT NAK Enhancement" feature to inform user ++ * about received data payload in case of timeout ++ */ ++ if (core_if->core_params->dev_out_nak) { ++ if (ep->type == DWC_OTG_EP_TYPE_BULK) { ++ core_if->ep_xfer_info[ep->num].core_if = core_if; ++ core_if->ep_xfer_info[ep->num].ep = ep; ++ core_if->ep_xfer_info[ep->num].state = 1; ++ ++ /* Start a timer for this transfer. */ ++ DWC_TIMER_SCHEDULE(core_if->ep_xfer_timer[ep->num], 10000); ++ } ++ } ++ } ++} ++ ++/** ++ * This function setup a zero length transfer in Buffer DMA and ++ * Slave modes for usb requests with zero field set ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ ++ depctl_data_t depctl; ++ deptsiz_data_t deptsiz; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__); ++ DWC_PRINTF("zero length transfer is called\n"); ++ ++ /* IN endpoint */ ++ if (ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[ep->num]; ++ ++ depctl.d32 = DWC_READ_REG32(&(in_regs->diepctl)); ++ deptsiz.d32 = DWC_READ_REG32(&(in_regs->dieptsiz)); ++ ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 1; ++ ++ /* Write the DMA register */ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable == 0) { ++ deptsiz.b.mc = 1; ++ DWC_WRITE_REG32(&in_regs->dieptsiz, ++ deptsiz.d32); ++ DWC_WRITE_REG32(&(in_regs->diepdma), ++ (uint32_t) ep->dma_addr); ++ } ++ } else { ++ DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32); ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, ++ * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode, ++ * the data will be written into the fifo by the ISR. ++ */ ++ if (core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ DWC_MODIFY_REG32(&core_if-> ++ core_global_regs->gintmsk, ++ intr_mask.d32, intr_mask.d32); ++ } else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if (ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk = 1 << ep->num; ++ DWC_MODIFY_REG32(&core_if-> ++ dev_if->dev_global_regs->dtknqr4_fifoemptymsk, ++ 0, fifoemptymsk); ++ } ++ } ++ } ++ ++ if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) ++ depctl.b.nextep = core_if->nextep_seq[ep->num]; ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32); ++ ++ } else { ++ /* OUT endpoint */ ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[ep->num]; ++ ++ depctl.d32 = DWC_READ_REG32(&(out_regs->doepctl)); ++ deptsiz.d32 = DWC_READ_REG32(&(out_regs->doeptsiz)); ++ ++ /* Zero Length Packet */ ++ deptsiz.b.xfersize = ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ ++ if (core_if->dma_enable) { ++ if (!core_if->dma_desc_enable) { ++ DWC_WRITE_REG32(&out_regs->doeptsiz, ++ deptsiz.d32); ++ ++ DWC_WRITE_REG32(&(out_regs->doepdma), ++ (uint32_t) ep->dma_addr); ++ } ++ } else { ++ DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32); ++ } ++ ++ /* EP enable */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ ++ DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32); ++ ++ } ++} ++ ++/** ++ * This function does the setup for a data transfer for EP0 and starts ++ * the transfer. For an IN transfer, the packets will be loaded into ++ * the appropriate Tx FIFO in the ISR. For OUT transfers, the packets are ++ * unloaded from the Rx FIFO in the ISR. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP0 data. ++ */ ++void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl; ++ deptsiz0_data_t deptsiz; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ ++ DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d " ++ "xfer_buff=%p start_xfer_buff=%p \n", ++ ep->num, (ep->is_in ? "IN" : "OUT"), ep->xfer_len, ++ ep->xfer_count, ep->xfer_buff, ep->start_xfer_buff); ++ ++ ep->total_len = ep->xfer_len; ++ ++ /* IN endpoint */ ++ if (ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[0]; ++ ++ gnptxsts_data_t gtxstatus; ++ ++ if (core_if->snpsid >= OTG_CORE_REV_3_00a) { ++ depctl.d32 = DWC_READ_REG32(&in_regs->diepctl); ++ if (depctl.b.epena) ++ return; ++ } ++ ++ gtxstatus.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->gnptxsts); ++ ++ /* If dedicated FIFO every time flush fifo before enable ep*/ ++ if (core_if->en_multiple_tx_fifo == 0 ++ && gtxstatus.b.nptxqspcavail == 0 ++ && !core_if->dma_enable) { ++#ifdef DEBUG ++ deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz); ++ DWC_DEBUGPL(DBG_PCD, "DIEPCTL0=%0x\n", ++ DWC_READ_REG32(&in_regs->diepctl)); ++ DWC_DEBUGPL(DBG_PCD, "DIEPTSIZ0=%0x (sz=%d, pcnt=%d)\n", ++ deptsiz.d32, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ DWC_PRINTF("TX Queue or FIFO Full (0x%0x)\n", ++ gtxstatus.d32); ++#endif ++ return; ++ } ++ ++ depctl.d32 = DWC_READ_REG32(&in_regs->diepctl); ++ deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz); ++ ++ /* Zero Length Packet? */ ++ if (ep->xfer_len == 0) { ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 1; ++ } else { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ if (ep->xfer_len > ep->maxpacket) { ++ ep->xfer_len = ep->maxpacket; ++ deptsiz.b.xfersize = ep->maxpacket; ++ } else { ++ deptsiz.b.xfersize = ep->xfer_len; ++ } ++ deptsiz.b.pktcnt = 1; ++ ++ } ++ DWC_DEBUGPL(DBG_PCDV, ++ "IN len=%d xfersize=%d pktcnt=%d [%08x]\n", ++ ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt, ++ deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable == 0) { ++ DWC_WRITE_REG32(&in_regs->dieptsiz, ++ deptsiz.d32); ++ ++ DWC_WRITE_REG32(&(in_regs->diepdma), ++ (uint32_t) ep->dma_addr); ++ } else { ++ dma_desc = core_if->dev_if->in_desc_addr; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.sp = ++ (ep->xfer_len == ep->maxpacket) ? 0 : 1; ++ dma_desc->status.b.bytes = ep->xfer_len; ++ dma_desc->buf = ep->dma_addr; ++ dma_desc->status.b.sts = 0; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DIEPDMA0 Register write */ ++ DWC_WRITE_REG32(&in_regs->diepdma, ++ core_if-> ++ dev_if->dma_in_desc_addr); ++ } ++ } else { ++ DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32); ++ } ++ ++ if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) ++ depctl.b.nextep = core_if->nextep_seq[ep->num]; ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32); ++ ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, the ++ * data will be written into the fifo by the ISR. ++ */ ++ if (!core_if->dma_enable) { ++ if (core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ DWC_MODIFY_REG32(&core_if-> ++ core_global_regs->gintmsk, ++ intr_mask.d32, intr_mask.d32); ++ } else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if (ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk |= 1 << ep->num; ++ DWC_MODIFY_REG32(&core_if-> ++ dev_if->dev_global_regs->dtknqr4_fifoemptymsk, ++ 0, fifoemptymsk); ++ } ++ } ++ } ++ } else { ++ /* OUT endpoint */ ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[0]; ++ ++ depctl.d32 = DWC_READ_REG32(&out_regs->doepctl); ++ deptsiz.d32 = DWC_READ_REG32(&out_regs->doeptsiz); ++ ++ /* Program the transfer size and packet count as follows: ++ * xfersize = N * (maxpacket + 4 - (maxpacket % 4)) ++ * pktcnt = N */ ++ /* Zero Length Packet */ ++ deptsiz.b.xfersize = ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ if (core_if->snpsid >= OTG_CORE_REV_3_00a) ++ deptsiz.b.supcnt = 3; ++ ++ DWC_DEBUGPL(DBG_PCDV, "len=%d xfersize=%d pktcnt=%d\n", ++ ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ ++ if (core_if->dma_enable) { ++ if (!core_if->dma_desc_enable) { ++ DWC_WRITE_REG32(&out_regs->doeptsiz, ++ deptsiz.d32); ++ ++ DWC_WRITE_REG32(&(out_regs->doepdma), ++ (uint32_t) ep->dma_addr); ++ } else { ++ dma_desc = core_if->dev_if->out_desc_addr; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ if (core_if->snpsid >= OTG_CORE_REV_3_00a) { ++ dma_desc->status.b.mtrf = 0; ++ dma_desc->status.b.sr = 0; ++ } ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.bytes = ep->maxpacket; ++ dma_desc->buf = ep->dma_addr; ++ dma_desc->status.b.sts = 0; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DOEPDMA0 Register write */ ++ DWC_WRITE_REG32(&out_regs->doepdma, ++ core_if->dev_if-> ++ dma_out_desc_addr); ++ } ++ } else { ++ DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32); ++ } ++ ++ /* EP enable */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ DWC_WRITE_REG32(&(out_regs->doepctl), depctl.d32); ++ } ++} ++ ++/** ++ * This function continues control IN transfers started by ++ * dwc_otg_ep0_start_transfer, when the transfer does not fit in a ++ * single packet. NOTE: The DIEPCTL0/DOEPCTL0 registers only have one ++ * bit for the packet count. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP0 data. ++ */ ++void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl; ++ deptsiz0_data_t deptsiz; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ ++ if (ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[0]; ++ gnptxsts_data_t tx_status = {.d32 = 0 }; ++ ++ tx_status.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->gnptxsts); ++ /** @todo Should there be check for room in the Tx ++ * Status Queue. If not remove the code above this comment. */ ++ ++ depctl.d32 = DWC_READ_REG32(&in_regs->diepctl); ++ deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz); ++ ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ ++ if (core_if->dma_desc_enable == 0) { ++ deptsiz.b.xfersize = ++ (ep->total_len - ep->xfer_count) > ++ ep->maxpacket ? ep->maxpacket : (ep->total_len - ++ ep->xfer_count); ++ deptsiz.b.pktcnt = 1; ++ if (core_if->dma_enable == 0) { ++ ep->xfer_len += deptsiz.b.xfersize; ++ } else { ++ ep->xfer_len = deptsiz.b.xfersize; ++ } ++ DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32); ++ } else { ++ ep->xfer_len = ++ (ep->total_len - ep->xfer_count) > ++ ep->maxpacket ? ep->maxpacket : (ep->total_len - ++ ep->xfer_count); ++ ++ dma_desc = core_if->dev_if->in_desc_addr; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.sp = ++ (ep->xfer_len == ep->maxpacket) ? 0 : 1; ++ dma_desc->status.b.bytes = ep->xfer_len; ++ dma_desc->buf = ep->dma_addr; ++ dma_desc->status.b.sts = 0; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DIEPDMA0 Register write */ ++ DWC_WRITE_REG32(&in_regs->diepdma, ++ core_if->dev_if->dma_in_desc_addr); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "IN len=%d xfersize=%d pktcnt=%d [%08x]\n", ++ ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt, ++ deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) { ++ if (core_if->dma_desc_enable == 0) ++ DWC_WRITE_REG32(&(in_regs->diepdma), ++ (uint32_t) ep->dma_addr); ++ } ++ if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) ++ depctl.b.nextep = core_if->nextep_seq[ep->num]; ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32); ++ ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, the ++ * data will be written into the fifo by the ISR. ++ */ ++ if (!core_if->dma_enable) { ++ if (core_if->en_multiple_tx_fifo == 0) { ++ /* First clear it from GINTSTS */ ++ intr_mask.b.nptxfempty = 1; ++ DWC_MODIFY_REG32(&core_if-> ++ core_global_regs->gintmsk, ++ intr_mask.d32, intr_mask.d32); ++ ++ } else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if (ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk |= 1 << ep->num; ++ DWC_MODIFY_REG32(&core_if-> ++ dev_if->dev_global_regs->dtknqr4_fifoemptymsk, ++ 0, fifoemptymsk); ++ } ++ } ++ } ++ } else { ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[0]; ++ ++ depctl.d32 = DWC_READ_REG32(&out_regs->doepctl); ++ deptsiz.d32 = DWC_READ_REG32(&out_regs->doeptsiz); ++ ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.xfersize = ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ ++ if (core_if->dma_desc_enable == 0) { ++ DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32); ++ } else { ++ dma_desc = core_if->dev_if->out_desc_addr; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.bytes = ep->maxpacket; ++ dma_desc->buf = ep->dma_addr; ++ dma_desc->status.b.sts = 0; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DOEPDMA0 Register write */ ++ DWC_WRITE_REG32(&out_regs->doepdma, ++ core_if->dev_if->dma_out_desc_addr); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "IN len=%d xfersize=%d pktcnt=%d [%08x]\n", ++ ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt, ++ deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) { ++ if (core_if->dma_desc_enable == 0) ++ DWC_WRITE_REG32(&(out_regs->doepdma), ++ (uint32_t) ep->dma_addr); ++ ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32); ++ ++ } ++} ++ ++#ifdef DEBUG ++void dump_msg(const u8 * buf, unsigned int length) ++{ ++ unsigned int start, num, i; ++ char line[52], *p; ++ ++ if (length >= 512) ++ return; ++ start = 0; ++ while (length > 0) { ++ num = length < 16u ? length : 16u; ++ p = line; ++ for (i = 0; i < num; ++i) { ++ if (i == 8) ++ *p++ = ' '; ++ DWC_SPRINTF(p, " %02x", buf[i]); ++ p += 3; ++ } ++ *p = 0; ++ DWC_PRINTF("%6x: %s\n", start, line); ++ buf += num; ++ start += num; ++ length -= num; ++ } ++} ++#else ++static inline void dump_msg(const u8 * buf, unsigned int length) ++{ ++} ++#endif ++ ++/** ++ * This function writes a packet into the Tx FIFO associated with the ++ * EP. For non-periodic EPs the non-periodic Tx FIFO is written. For ++ * periodic EPs the periodic Tx FIFO associated with the EP is written ++ * with all packets for the next micro-frame. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to write packet for. ++ * @param dma Indicates if DMA is being used. ++ */ ++void dwc_otg_ep_write_packet(dwc_otg_core_if_t * core_if, dwc_ep_t * ep, ++ int dma) ++{ ++ /** ++ * The buffer is padded to DWORD on a per packet basis in ++ * slave/dma mode if the MPS is not DWORD aligned. The last ++ * packet, if short, is also padded to a multiple of DWORD. ++ * ++ * ep->xfer_buff always starts DWORD aligned in memory and is a ++ * multiple of DWORD in length ++ * ++ * ep->xfer_len can be any number of bytes ++ * ++ * ep->xfer_count is a multiple of ep->maxpacket until the last ++ * packet ++ * ++ * FIFO access is DWORD */ ++ ++ uint32_t i; ++ uint32_t byte_count; ++ uint32_t dword_count; ++ uint32_t *fifo; ++ uint32_t *data_buff = (uint32_t *) ep->xfer_buff; ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p)\n", __func__, core_if, ++ ep); ++ if (ep->xfer_count >= ep->xfer_len) { ++ DWC_WARN("%s() No data for EP%d!!!\n", __func__, ep->num); ++ return; ++ } ++ ++ /* Find the byte length of the packet either short packet or MPS */ ++ if ((ep->xfer_len - ep->xfer_count) < ep->maxpacket) { ++ byte_count = ep->xfer_len - ep->xfer_count; ++ } else { ++ byte_count = ep->maxpacket; ++ } ++ ++ /* Find the DWORD length, padded by extra bytes as neccessary if MPS ++ * is not a multiple of DWORD */ ++ dword_count = (byte_count + 3) / 4; ++ ++#ifdef VERBOSE ++ dump_msg(ep->xfer_buff, byte_count); ++#endif ++ ++ /**@todo NGS Where are the Periodic Tx FIFO addresses ++ * intialized? What should this be? */ ++ ++ fifo = core_if->data_fifo[ep->num]; ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "fifo=%p buff=%p *p=%08x bc=%d\n", ++ fifo, data_buff, *data_buff, byte_count); ++ ++ if (!dma) { ++ for (i = 0; i < dword_count; i++, data_buff++) { ++ DWC_WRITE_REG32(fifo, *data_buff); ++ } ++ } ++ ++ ep->xfer_count += byte_count; ++ ep->xfer_buff += byte_count; ++ ep->dma_addr += byte_count; ++} ++ ++/** ++ * Set the EP STALL. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to set the stall on. ++ */ ++void dwc_otg_ep_set_stall(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl; ++ volatile uint32_t *depctl_addr; ++ ++ DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, ep->num, ++ (ep->is_in ? "IN" : "OUT")); ++ ++ if (ep->is_in == 1) { ++ depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl); ++ depctl.d32 = DWC_READ_REG32(depctl_addr); ++ ++ /* set the disable and stall bits */ ++ if (depctl.b.epena) { ++ depctl.b.epdis = 1; ++ } ++ depctl.b.stall = 1; ++ DWC_WRITE_REG32(depctl_addr, depctl.d32); ++ } else { ++ depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl); ++ depctl.d32 = DWC_READ_REG32(depctl_addr); ++ ++ /* set the stall bit */ ++ depctl.b.stall = 1; ++ DWC_WRITE_REG32(depctl_addr, depctl.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "DEPCTL=%0x\n", DWC_READ_REG32(depctl_addr)); ++ ++ return; ++} ++ ++/** ++ * Clear the EP STALL. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to clear stall from. ++ */ ++void dwc_otg_ep_clear_stall(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl; ++ volatile uint32_t *depctl_addr; ++ ++ DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, ep->num, ++ (ep->is_in ? "IN" : "OUT")); ++ ++ if (ep->is_in == 1) { ++ depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl); ++ } else { ++ depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl); ++ } ++ ++ depctl.d32 = DWC_READ_REG32(depctl_addr); ++ ++ /* clear the stall bits */ ++ depctl.b.stall = 0; ++ ++ /* ++ * USB Spec 9.4.5: For endpoints using data toggle, regardless ++ * of whether an endpoint has the Halt feature set, a ++ * ClearFeature(ENDPOINT_HALT) request always results in the ++ * data toggle being reinitialized to DATA0. ++ */ ++ if (ep->type == DWC_OTG_EP_TYPE_INTR || ++ ep->type == DWC_OTG_EP_TYPE_BULK) { ++ depctl.b.setd0pid = 1; /* DATA0 */ ++ } ++ ++ DWC_WRITE_REG32(depctl_addr, depctl.d32); ++ DWC_DEBUGPL(DBG_PCD, "DEPCTL=%0x\n", DWC_READ_REG32(depctl_addr)); ++ return; ++} ++ ++/** ++ * This function reads a packet from the Rx FIFO into the destination ++ * buffer. To read SETUP data use dwc_otg_read_setup_packet. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dest Destination buffer for the packet. ++ * @param bytes Number of bytes to copy to the destination. ++ */ ++void dwc_otg_read_packet(dwc_otg_core_if_t * core_if, ++ uint8_t * dest, uint16_t bytes) ++{ ++ int i; ++ int word_count = (bytes + 3) / 4; ++ ++ volatile uint32_t *fifo = core_if->data_fifo[0]; ++ uint32_t *data_buff = (uint32_t *) dest; ++ ++ /** ++ * @todo Account for the case where _dest is not dword aligned. This ++ * requires reading data from the FIFO into a uint32_t temp buffer, ++ * then moving it into the data buffer. ++ */ ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p,%d)\n", __func__, ++ core_if, dest, bytes); ++ ++ for (i = 0; i < word_count; i++, data_buff++) { ++ *data_buff = DWC_READ_REG32(fifo); ++ } ++ ++ return; ++} ++ ++/** ++ * This functions reads the device registers and prints them ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_dev_registers(dwc_otg_core_if_t * core_if) ++{ ++ int i; ++ volatile uint32_t *addr; ++ ++ DWC_PRINTF("Device Global Registers\n"); ++ addr = &core_if->dev_if->dev_global_regs->dcfg; ++ DWC_PRINTF("DCFG @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->dctl; ++ DWC_PRINTF("DCTL @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->dsts; ++ DWC_PRINTF("DSTS @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->diepmsk; ++ DWC_PRINTF("DIEPMSK @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->doepmsk; ++ DWC_PRINTF("DOEPMSK @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->daint; ++ DWC_PRINTF("DAINT @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->daintmsk; ++ DWC_PRINTF("DAINTMSK @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->dtknqr1; ++ DWC_PRINTF("DTKNQR1 @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ if (core_if->hwcfg2.b.dev_token_q_depth > 6) { ++ addr = &core_if->dev_if->dev_global_regs->dtknqr2; ++ DWC_PRINTF("DTKNQR2 @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ } ++ ++ addr = &core_if->dev_if->dev_global_regs->dvbusdis; ++ DWC_PRINTF("DVBUSID @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ ++ addr = &core_if->dev_if->dev_global_regs->dvbuspulse; ++ DWC_PRINTF("DVBUSPULSE @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ ++ addr = &core_if->dev_if->dev_global_regs->dtknqr3_dthrctl; ++ DWC_PRINTF("DTKNQR3_DTHRCTL @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ ++ if (core_if->hwcfg2.b.dev_token_q_depth > 22) { ++ addr = &core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk; ++ DWC_PRINTF("DTKNQR4 @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ } ++ ++ addr = &core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk; ++ DWC_PRINTF("FIFOEMPMSK @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ ++ if (core_if->hwcfg2.b.multi_proc_int) { ++ ++ addr = &core_if->dev_if->dev_global_regs->deachint; ++ DWC_PRINTF("DEACHINT @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->deachintmsk; ++ DWC_PRINTF("DEACHINTMSK @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ addr = ++ &core_if->dev_if-> ++ dev_global_regs->diepeachintmsk[i]; ++ DWC_PRINTF("DIEPEACHINTMSK[%d] @0x%08lX : 0x%08X\n", ++ i, (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ } ++ ++ for (i = 0; i <= core_if->dev_if->num_out_eps; i++) { ++ addr = ++ &core_if->dev_if-> ++ dev_global_regs->doepeachintmsk[i]; ++ DWC_PRINTF("DOEPEACHINTMSK[%d] @0x%08lX : 0x%08X\n", ++ i, (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ } ++ } ++ ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ DWC_PRINTF("Device IN EP %d Registers\n", i); ++ addr = &core_if->dev_if->in_ep_regs[i]->diepctl; ++ DWC_PRINTF("DIEPCTL @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->in_ep_regs[i]->diepint; ++ DWC_PRINTF("DIEPINT @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->in_ep_regs[i]->dieptsiz; ++ DWC_PRINTF("DIETSIZ @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->in_ep_regs[i]->diepdma; ++ DWC_PRINTF("DIEPDMA @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->in_ep_regs[i]->dtxfsts; ++ DWC_PRINTF("DTXFSTS @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->in_ep_regs[i]->diepdmab; ++ DWC_PRINTF("DIEPDMAB @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, 0 /*DWC_READ_REG32(addr) */ ); ++ } ++ ++ for (i = 0; i <= core_if->dev_if->num_out_eps; i++) { ++ DWC_PRINTF("Device OUT EP %d Registers\n", i); ++ addr = &core_if->dev_if->out_ep_regs[i]->doepctl; ++ DWC_PRINTF("DOEPCTL @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->out_ep_regs[i]->doepint; ++ DWC_PRINTF("DOEPINT @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->out_ep_regs[i]->doeptsiz; ++ DWC_PRINTF("DOETSIZ @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->dev_if->out_ep_regs[i]->doepdma; ++ DWC_PRINTF("DOEPDMA @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ if (core_if->dma_enable) { /* Don't access this register in SLAVE mode */ ++ addr = &core_if->dev_if->out_ep_regs[i]->doepdmab; ++ DWC_PRINTF("DOEPDMAB @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ } ++ ++ } ++} ++ ++/** ++ * This functions reads the SPRAM and prints its content ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_spram(dwc_otg_core_if_t * core_if) ++{ ++ volatile uint8_t *addr, *start_addr, *end_addr; ++ ++ DWC_PRINTF("SPRAM Data:\n"); ++ start_addr = (void *)core_if->core_global_regs; ++ DWC_PRINTF("Base Address: 0x%8lX\n", (unsigned long)start_addr); ++ start_addr += 0x00028000; ++ end_addr = (void *)core_if->core_global_regs; ++ end_addr += 0x000280e0; ++ ++ for (addr = start_addr; addr < end_addr; addr += 16) { ++ DWC_PRINTF ++ ("0x%8lX:\t%2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X\n", ++ (unsigned long)addr, addr[0], addr[1], addr[2], addr[3], ++ addr[4], addr[5], addr[6], addr[7], addr[8], addr[9], ++ addr[10], addr[11], addr[12], addr[13], addr[14], addr[15] ++ ); ++ } ++ ++ return; ++} ++ ++/** ++ * This function reads the host registers and prints them ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_host_registers(dwc_otg_core_if_t * core_if) ++{ ++ int i; ++ volatile uint32_t *addr; ++ ++ DWC_PRINTF("Host Global Registers\n"); ++ addr = &core_if->host_if->host_global_regs->hcfg; ++ DWC_PRINTF("HCFG @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->host_if->host_global_regs->hfir; ++ DWC_PRINTF("HFIR @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->host_if->host_global_regs->hfnum; ++ DWC_PRINTF("HFNUM @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->host_if->host_global_regs->hptxsts; ++ DWC_PRINTF("HPTXSTS @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->host_if->host_global_regs->haint; ++ DWC_PRINTF("HAINT @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->host_if->host_global_regs->haintmsk; ++ DWC_PRINTF("HAINTMSK @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ if (core_if->dma_desc_enable) { ++ addr = &core_if->host_if->host_global_regs->hflbaddr; ++ DWC_PRINTF("HFLBADDR @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ } ++ ++ addr = core_if->host_if->hprt0; ++ DWC_PRINTF("HPRT0 @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ ++ for (i = 0; i < core_if->core_params->host_channels; i++) { ++ DWC_PRINTF("Host Channel %d Specific Registers\n", i); ++ addr = &core_if->host_if->hc_regs[i]->hcchar; ++ DWC_PRINTF("HCCHAR @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->host_if->hc_regs[i]->hcsplt; ++ DWC_PRINTF("HCSPLT @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->host_if->hc_regs[i]->hcint; ++ DWC_PRINTF("HCINT @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->host_if->hc_regs[i]->hcintmsk; ++ DWC_PRINTF("HCINTMSK @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->host_if->hc_regs[i]->hctsiz; ++ DWC_PRINTF("HCTSIZ @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->host_if->hc_regs[i]->hcdma; ++ DWC_PRINTF("HCDMA @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ if (core_if->dma_desc_enable) { ++ addr = &core_if->host_if->hc_regs[i]->hcdmab; ++ DWC_PRINTF("HCDMAB @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ } ++ ++ } ++ return; ++} ++ ++/** ++ * This function reads the core global registers and prints them ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_global_registers(dwc_otg_core_if_t * core_if) ++{ ++ int i, ep_num; ++ volatile uint32_t *addr; ++ char *txfsiz; ++ ++ DWC_PRINTF("Core Global Registers\n"); ++ addr = &core_if->core_global_regs->gotgctl; ++ DWC_PRINTF("GOTGCTL @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->gotgint; ++ DWC_PRINTF("GOTGINT @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->gahbcfg; ++ DWC_PRINTF("GAHBCFG @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->gusbcfg; ++ DWC_PRINTF("GUSBCFG @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->grstctl; ++ DWC_PRINTF("GRSTCTL @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->gintsts; ++ DWC_PRINTF("GINTSTS @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->gintmsk; ++ DWC_PRINTF("GINTMSK @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->grxstsr; ++ DWC_PRINTF("GRXSTSR @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->grxfsiz; ++ DWC_PRINTF("GRXFSIZ @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->gnptxfsiz; ++ DWC_PRINTF("GNPTXFSIZ @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->gnptxsts; ++ DWC_PRINTF("GNPTXSTS @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->gi2cctl; ++ DWC_PRINTF("GI2CCTL @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->gpvndctl; ++ DWC_PRINTF("GPVNDCTL @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->ggpio; ++ DWC_PRINTF("GGPIO @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->guid; ++ DWC_PRINTF("GUID @0x%08lX : 0x%08X\n", ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->gsnpsid; ++ DWC_PRINTF("GSNPSID @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->ghwcfg1; ++ DWC_PRINTF("GHWCFG1 @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->ghwcfg2; ++ DWC_PRINTF("GHWCFG2 @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->ghwcfg3; ++ DWC_PRINTF("GHWCFG3 @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->ghwcfg4; ++ DWC_PRINTF("GHWCFG4 @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->glpmcfg; ++ DWC_PRINTF("GLPMCFG @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->gpwrdn; ++ DWC_PRINTF("GPWRDN @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->gdfifocfg; ++ DWC_PRINTF("GDFIFOCFG @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ addr = &core_if->core_global_regs->adpctl; ++ DWC_PRINTF("ADPCTL @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ dwc_otg_adp_read_reg(core_if)); ++ addr = &core_if->core_global_regs->hptxfsiz; ++ DWC_PRINTF("HPTXFSIZ @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++ ++ if (core_if->en_multiple_tx_fifo == 0) { ++ ep_num = core_if->hwcfg4.b.num_dev_perio_in_ep; ++ txfsiz = "DPTXFSIZ"; ++ } else { ++ ep_num = core_if->hwcfg4.b.num_in_eps; ++ txfsiz = "DIENPTXF"; ++ } ++ for (i = 0; i < ep_num; i++) { ++ addr = &core_if->core_global_regs->dtxfsiz[i]; ++ DWC_PRINTF("%s[%d] @0x%08lX : 0x%08X\n", txfsiz, i + 1, ++ (unsigned long)addr, DWC_READ_REG32(addr)); ++ } ++ addr = core_if->pcgcctl; ++ DWC_PRINTF("PCGCCTL @0x%08lX : 0x%08X\n", (unsigned long)addr, ++ DWC_READ_REG32(addr)); ++} ++ ++/** ++ * Flush a Tx FIFO. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param num Tx FIFO to flush. ++ */ ++void dwc_otg_flush_tx_fifo(dwc_otg_core_if_t * core_if, const int num) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ volatile grstctl_t greset = {.d32 = 0 }; ++ int count = 0; ++ ++ DWC_DEBUGPL((DBG_CIL | DBG_PCDV), "Flush Tx FIFO %d\n", num); ++ ++ greset.b.txfflsh = 1; ++ greset.b.txfnum = num; ++ DWC_WRITE_REG32(&global_regs->grstctl, greset.d32); ++ ++ do { ++ greset.d32 = DWC_READ_REG32(&global_regs->grstctl); ++ if (++count > 10000) { ++ DWC_WARN("%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n", ++ __func__, greset.d32, ++ DWC_READ_REG32(&global_regs->gnptxsts)); ++ break; ++ } ++ dwc_udelay(1); ++ } while (greset.b.txfflsh == 1); ++ ++ /* Wait for 3 PHY Clocks */ ++ dwc_udelay(1); ++} ++ ++/** ++ * Flush Rx FIFO. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ volatile grstctl_t greset = {.d32 = 0 }; ++ int count = 0; ++ ++ DWC_DEBUGPL((DBG_CIL | DBG_PCDV), "%s\n", __func__); ++ /* ++ * ++ */ ++ greset.b.rxfflsh = 1; ++ DWC_WRITE_REG32(&global_regs->grstctl, greset.d32); ++ ++ do { ++ greset.d32 = DWC_READ_REG32(&global_regs->grstctl); ++ if (++count > 10000) { ++ DWC_WARN("%s() HANG! GRSTCTL=%0x\n", __func__, ++ greset.d32); ++ break; ++ } ++ dwc_udelay(1); ++ } while (greset.b.rxfflsh == 1); ++ ++ /* Wait for 3 PHY Clocks */ ++ dwc_udelay(1); ++} ++ ++/** ++ * Do core a soft reset of the core. Be careful with this because it ++ * resets all the internal state machines of the core. ++ */ ++void dwc_otg_core_reset(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ volatile grstctl_t greset = {.d32 = 0 }; ++ int count = 0; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s\n", __func__); ++ /* Wait for AHB master IDLE state. */ ++ do { ++ dwc_udelay(10); ++ greset.d32 = DWC_READ_REG32(&global_regs->grstctl); ++ if (++count > 100000) { ++ DWC_WARN("%s() HANG! AHB Idle GRSTCTL=%0x\n", __func__, ++ greset.d32); ++ return; ++ } ++ } ++ while (greset.b.ahbidle == 0); ++ ++ /* Core Soft Reset */ ++ count = 0; ++ greset.b.csftrst = 1; ++ DWC_WRITE_REG32(&global_regs->grstctl, greset.d32); ++ do { ++ greset.d32 = DWC_READ_REG32(&global_regs->grstctl); ++ if (++count > 10000) { ++ DWC_WARN("%s() HANG! Soft Reset GRSTCTL=%0x\n", ++ __func__, greset.d32); ++ break; ++ } ++ dwc_udelay(1); ++ } ++ while (greset.b.csftrst == 1); ++ ++ /* Wait for 3 PHY Clocks */ ++ dwc_mdelay(100); ++} ++ ++uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t * _core_if) ++{ ++ return (dwc_otg_mode(_core_if) != DWC_HOST_MODE); ++} ++ ++uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t * _core_if) ++{ ++ return (dwc_otg_mode(_core_if) == DWC_HOST_MODE); ++} ++ ++/** ++ * Register HCD callbacks. The callbacks are used to start and stop ++ * the HCD for interrupt processing. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param cb the HCD callback structure. ++ * @param p pointer to be passed to callback function (usb_hcd*). ++ */ ++void dwc_otg_cil_register_hcd_callbacks(dwc_otg_core_if_t * core_if, ++ dwc_otg_cil_callbacks_t * cb, void *p) ++{ ++ core_if->hcd_cb = cb; ++ cb->p = p; ++} ++ ++/** ++ * Register PCD callbacks. The callbacks are used to start and stop ++ * the PCD for interrupt processing. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param cb the PCD callback structure. ++ * @param p pointer to be passed to callback function (pcd*). ++ */ ++void dwc_otg_cil_register_pcd_callbacks(dwc_otg_core_if_t * core_if, ++ dwc_otg_cil_callbacks_t * cb, void *p) ++{ ++ core_if->pcd_cb = cb; ++ cb->p = p; ++} ++ ++#ifdef DWC_EN_ISOC ++ ++/** ++ * This function writes isoc data per 1 (micro)frame into tx fifo ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void write_isoc_frame_data(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ dwc_otg_dev_in_ep_regs_t *ep_regs; ++ dtxfsts_data_t txstatus = {.d32 = 0 }; ++ uint32_t len = 0; ++ uint32_t dwords; ++ ++ ep->xfer_len = ep->data_per_frame; ++ ep->xfer_count = 0; ++ ++ ep_regs = core_if->dev_if->in_ep_regs[ep->num]; ++ ++ len = ep->xfer_len - ep->xfer_count; ++ ++ if (len > ep->maxpacket) { ++ len = ep->maxpacket; ++ } ++ ++ dwords = (len + 3) / 4; ++ ++ /* While there is space in the queue and space in the FIFO and ++ * More data to tranfer, Write packets to the Tx FIFO */ ++ txstatus.d32 = ++ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[ep->num]->dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", ep->num, txstatus.d32); ++ ++ while (txstatus.b.txfspcavail > dwords && ++ ep->xfer_count < ep->xfer_len && ep->xfer_len != 0) { ++ /* Write the FIFO */ ++ dwc_otg_ep_write_packet(core_if, ep, 0); ++ ++ len = ep->xfer_len - ep->xfer_count; ++ if (len > ep->maxpacket) { ++ len = ep->maxpacket; ++ } ++ ++ dwords = (len + 3) / 4; ++ txstatus.d32 = ++ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[ep->num]-> ++ dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", ep->num, ++ txstatus.d32); ++ } ++} ++ ++/** ++ * This function initializes a descriptor chain for Isochronous transfer ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * ep) ++{ ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ dsts_data_t dsts = {.d32 = 0 }; ++ volatile uint32_t *addr; ++ ++ if (ep->is_in) { ++ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; ++ } else { ++ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; ++ } ++ ++ ep->xfer_len = ep->data_per_frame; ++ ep->xfer_count = 0; ++ ep->xfer_buff = ep->cur_pkt_addr; ++ ep->dma_addr = ep->cur_pkt_dma_addr; ++ ++ if (ep->is_in) { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.xfersize = ep->xfer_len; ++ deptsiz.b.pktcnt = ++ (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket; ++ deptsiz.b.mc = deptsiz.b.pktcnt; ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->dieptsiz, ++ deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (core_if->dma_enable) { ++ DWC_WRITE_REG32(& ++ (core_if->dev_if->in_ep_regs[ep->num]-> ++ diepdma), (uint32_t) ep->dma_addr); ++ } ++ } else { ++ deptsiz.b.pktcnt = ++ (ep->xfer_len + (ep->maxpacket - 1)) / ep->maxpacket; ++ deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket; ++ ++ DWC_WRITE_REG32(&core_if->dev_if-> ++ out_ep_regs[ep->num]->doeptsiz, deptsiz.d32); ++ ++ if (core_if->dma_enable) { ++ DWC_WRITE_REG32(& ++ (core_if->dev_if-> ++ out_ep_regs[ep->num]->doepdma), ++ (uint32_t) ep->dma_addr); ++ } ++ } ++ ++ /** Enable endpoint, clear nak */ ++ ++ depctl.d32 = 0; ++ if (ep->bInterval == 1) { ++ dsts.d32 = ++ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); ++ ep->next_frame = dsts.b.soffn + ep->bInterval; ++ ++ if (ep->next_frame & 0x1) { ++ depctl.b.setd1pid = 1; ++ } else { ++ depctl.b.setd0pid = 1; ++ } ++ } else { ++ ep->next_frame += ep->bInterval; ++ ++ if (ep->next_frame & 0x1) { ++ depctl.b.setd1pid = 1; ++ } else { ++ depctl.b.setd0pid = 1; ++ } ++ } ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ ++ DWC_MODIFY_REG32(addr, 0, depctl.d32); ++ depctl.d32 = DWC_READ_REG32(addr); ++ ++ if (ep->is_in && core_if->dma_enable == 0) { ++ write_isoc_frame_data(core_if, ep); ++ } ++ ++} ++#endif /* DWC_EN_ISOC */ ++ ++static void dwc_otg_set_uninitialized(int32_t * p, int size) ++{ ++ int i; ++ for (i = 0; i < size; i++) { ++ p[i] = -1; ++ } ++} ++ ++static int dwc_otg_param_initialized(int32_t val) ++{ ++ return val != -1; ++} ++ ++static int dwc_otg_setup_params(dwc_otg_core_if_t * core_if) ++{ ++ int i; ++ gintsts_data_t gintsts; ++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ ++ core_if->core_params = DWC_ALLOC(sizeof(*core_if->core_params)); ++ if (!core_if->core_params) { ++ return -DWC_E_NO_MEMORY; ++ } ++ dwc_otg_set_uninitialized((int32_t *) core_if->core_params, ++ sizeof(*core_if->core_params) / ++ sizeof(int32_t)); ++ DWC_PRINTF("Setting default values for core params\n"); ++ dwc_otg_set_param_otg_cap(core_if, dwc_param_otg_cap_default); ++ dwc_otg_set_param_dma_enable(core_if, dwc_param_dma_enable_default); ++ dwc_otg_set_param_dma_desc_enable(core_if, ++ dwc_param_dma_desc_enable_default); ++ dwc_otg_set_param_opt(core_if, dwc_param_opt_default); ++ dwc_otg_set_param_dma_burst_size(core_if, ++ dwc_param_dma_burst_size_default); ++ dwc_otg_set_param_host_support_fs_ls_low_power(core_if, ++ dwc_param_host_support_fs_ls_low_power_default); ++ dwc_otg_set_param_enable_dynamic_fifo(core_if, ++ dwc_param_enable_dynamic_fifo_default); ++ dwc_otg_set_param_data_fifo_size(core_if, ++ dwc_param_data_fifo_size_default); ++ dwc_otg_set_param_dev_rx_fifo_size(core_if, ++ dwc_param_dev_rx_fifo_size_default); ++ dwc_otg_set_param_dev_nperio_tx_fifo_size(core_if, ++ dwc_param_dev_nperio_tx_fifo_size_default); ++ dwc_otg_set_param_host_rx_fifo_size(core_if, ++ dwc_param_host_rx_fifo_size_default); ++ dwc_otg_set_param_host_nperio_tx_fifo_size(core_if, ++ dwc_param_host_nperio_tx_fifo_size_default); ++ dwc_otg_set_param_host_perio_tx_fifo_size(core_if, ++ dwc_param_host_perio_tx_fifo_size_default); ++ dwc_otg_set_param_max_transfer_size(core_if, ++ dwc_param_max_transfer_size_default); ++ dwc_otg_set_param_max_packet_count(core_if, ++ dwc_param_max_packet_count_default); ++ dwc_otg_set_param_host_channels(core_if, ++ dwc_param_host_channels_default); ++ dwc_otg_set_param_dev_endpoints(core_if, ++ dwc_param_dev_endpoints_default); ++ dwc_otg_set_param_phy_type(core_if, dwc_param_phy_type_default); ++ dwc_otg_set_param_speed(core_if, dwc_param_speed_default); ++ dwc_otg_set_param_host_ls_low_power_phy_clk(core_if, ++ dwc_param_host_ls_low_power_phy_clk_default); ++ dwc_otg_set_param_phy_ulpi_ddr(core_if, dwc_param_phy_ulpi_ddr_default); ++ dwc_otg_set_param_phy_ulpi_ext_vbus(core_if, ++ dwc_param_phy_ulpi_ext_vbus_default); ++ dwc_otg_set_param_phy_utmi_width(core_if, ++ dwc_param_phy_utmi_width_default); ++ dwc_otg_set_param_ts_dline(core_if, dwc_param_ts_dline_default); ++ dwc_otg_set_param_i2c_enable(core_if, dwc_param_i2c_enable_default); ++ dwc_otg_set_param_ulpi_fs_ls(core_if, dwc_param_ulpi_fs_ls_default); ++ dwc_otg_set_param_en_multiple_tx_fifo(core_if, ++ dwc_param_en_multiple_tx_fifo_default); ++ ++ if (gintsts.b.curmode) { ++ /* Force device mode to get power-on values of device FIFOs */ ++ gusbcfg_data_t gusbcfg = {.d32 = 0 }; ++ gusbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++ gusbcfg.b.force_dev_mode = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32); ++ dwc_mdelay(100); ++ for (i = 0; i < 15; i++) { ++ dwc_otg_set_param_dev_perio_tx_fifo_size(core_if, ++ dwc_param_dev_perio_tx_fifo_size_default, i); ++ } ++ for (i = 0; i < 15; i++) { ++ dwc_otg_set_param_dev_tx_fifo_size(core_if, ++ dwc_param_dev_tx_fifo_size_default, i); ++ } ++ gusbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++ gusbcfg.b.force_dev_mode = 0; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32); ++ dwc_mdelay(100); ++ } else { ++ for (i = 0; i < 15; i++) { ++ dwc_otg_set_param_dev_perio_tx_fifo_size(core_if, ++ dwc_param_dev_perio_tx_fifo_size_default, i); ++ } ++ for (i = 0; i < 15; i++) { ++ dwc_otg_set_param_dev_tx_fifo_size(core_if, ++ dwc_param_dev_tx_fifo_size_default, i); ++ } ++ } ++ ++ dwc_otg_set_param_thr_ctl(core_if, dwc_param_thr_ctl_default); ++ dwc_otg_set_param_mpi_enable(core_if, dwc_param_mpi_enable_default); ++ dwc_otg_set_param_pti_enable(core_if, dwc_param_pti_enable_default); ++ dwc_otg_set_param_lpm_enable(core_if, dwc_param_lpm_enable_default); ++ ++ dwc_otg_set_param_besl_enable(core_if, dwc_param_besl_enable_default); ++ dwc_otg_set_param_baseline_besl(core_if, dwc_param_baseline_besl_default); ++ dwc_otg_set_param_deep_besl(core_if, dwc_param_deep_besl_default); ++ ++ dwc_otg_set_param_ic_usb_cap(core_if, dwc_param_ic_usb_cap_default); ++ dwc_otg_set_param_tx_thr_length(core_if, ++ dwc_param_tx_thr_length_default); ++ dwc_otg_set_param_rx_thr_length(core_if, ++ dwc_param_rx_thr_length_default); ++ dwc_otg_set_param_ahb_thr_ratio(core_if, ++ dwc_param_ahb_thr_ratio_default); ++ dwc_otg_set_param_power_down(core_if, dwc_param_power_down_default); ++ dwc_otg_set_param_reload_ctl(core_if, dwc_param_reload_ctl_default); ++ dwc_otg_set_param_dev_out_nak(core_if, dwc_param_dev_out_nak_default); ++ dwc_otg_set_param_cont_on_bna(core_if, dwc_param_cont_on_bna_default); ++ dwc_otg_set_param_ahb_single(core_if, dwc_param_ahb_single_default); ++ dwc_otg_set_param_otg_ver(core_if, dwc_param_otg_ver_default); ++ dwc_otg_set_param_adp_enable(core_if, dwc_param_adp_enable_default); ++ return 0; ++} ++ ++uint8_t dwc_otg_is_dma_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->dma_enable; ++} ++ ++/* Checks if the parameter is outside of its valid range of values */ ++#define DWC_OTG_PARAM_TEST(_param_, _low_, _high_) \ ++ (((_param_) < (_low_)) || \ ++ ((_param_) > (_high_))) ++ ++/* Parameter access functions */ ++int dwc_otg_set_param_otg_cap(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int valid; ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 2)) { ++ DWC_WARN("Wrong value for otg_cap parameter\n"); ++ DWC_WARN("otg_cap parameter must be 0,1 or 2\n"); ++ retval = -DWC_E_INVALID; ++ goto out; ++ } ++ ++ valid = 1; ++ switch (val) { ++ case DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE: ++ if (core_if->hwcfg2.b.op_mode != ++ DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) ++ valid = 0; ++ break; ++ case DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE: ++ if ((core_if->hwcfg2.b.op_mode != ++ DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) ++ && (core_if->hwcfg2.b.op_mode != ++ DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) ++ && (core_if->hwcfg2.b.op_mode != ++ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ++ && (core_if->hwcfg2.b.op_mode != ++ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) { ++ valid = 0; ++ } ++ break; ++ case DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE: ++ /* always valid */ ++ break; ++ } ++ if (!valid) { ++ if (dwc_otg_param_initialized(core_if->core_params->otg_cap)) { ++ DWC_ERROR ++ ("%d invalid for otg_cap paremter. Check HW configuration.\n", ++ val); ++ } ++ val = ++ (((core_if->hwcfg2.b.op_mode == ++ DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) ++ || (core_if->hwcfg2.b.op_mode == ++ DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) ++ || (core_if->hwcfg2.b.op_mode == ++ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ++ || (core_if->hwcfg2.b.op_mode == ++ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) ? ++ DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE : ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->otg_cap = val; ++out: ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_otg_cap(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->otg_cap; ++} ++ ++int dwc_otg_set_param_opt(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for opt parameter\n"); ++ return -DWC_E_INVALID; ++ } ++ core_if->core_params->opt = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_opt(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->opt; ++} ++ ++int dwc_otg_set_param_dma_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for dma enable\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == 1) && (core_if->hwcfg2.b.architecture == 0)) { ++ if (dwc_otg_param_initialized(core_if->core_params->dma_enable)) { ++ DWC_ERROR ++ ("%d invalid for dma_enable paremter. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->dma_enable = val; ++ if (val == 0) { ++ dwc_otg_set_param_dma_desc_enable(core_if, 0); ++ } ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dma_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dma_enable; ++} ++ ++int dwc_otg_set_param_dma_desc_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for dma_enable\n"); ++ DWC_WARN("dma_desc_enable must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == 1) ++ && ((dwc_otg_get_param_dma_enable(core_if) == 0) ++ || (core_if->hwcfg4.b.desc_dma == 0))) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->dma_desc_enable)) { ++ DWC_ERROR ++ ("%d invalid for dma_desc_enable paremter. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++ core_if->core_params->dma_desc_enable = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dma_desc_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dma_desc_enable; ++} ++ ++int dwc_otg_set_param_host_support_fs_ls_low_power(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for host_support_fs_low_power\n"); ++ DWC_WARN("host_support_fs_low_power must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ core_if->core_params->host_support_fs_ls_low_power = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_host_support_fs_ls_low_power(dwc_otg_core_if_t * ++ core_if) ++{ ++ return core_if->core_params->host_support_fs_ls_low_power; ++} ++ ++int dwc_otg_set_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for enable_dynamic_fifo\n"); ++ DWC_WARN("enable_dynamic_fifo must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == 1) && (core_if->hwcfg2.b.dynamic_fifo == 0)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->enable_dynamic_fifo)) { ++ DWC_ERROR ++ ("%d invalid for enable_dynamic_fifo paremter. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++ core_if->core_params->enable_dynamic_fifo = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->enable_dynamic_fifo; ++} ++ ++int dwc_otg_set_param_data_fifo_size(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 32, 32768)) { ++ DWC_WARN("Wrong value for data_fifo_size\n"); ++ DWC_WARN("data_fifo_size must be 32-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > core_if->hwcfg3.b.dfifo_depth) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->data_fifo_size)) { ++ DWC_ERROR ++ ("%d invalid for data_fifo_size parameter. Check HW configuration.\n", ++ val); ++ } ++ val = core_if->hwcfg3.b.dfifo_depth; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->data_fifo_size = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_data_fifo_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->data_fifo_size; ++} ++ ++int dwc_otg_set_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) { ++ DWC_WARN("Wrong value for dev_rx_fifo_size\n"); ++ DWC_WARN("dev_rx_fifo_size must be 16-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > DWC_READ_REG32(&core_if->core_global_regs->grxfsiz)) { ++ if (dwc_otg_param_initialized(core_if->core_params->dev_rx_fifo_size)) { ++ DWC_WARN("%d invalid for dev_rx_fifo_size parameter\n", val); ++ } ++ val = DWC_READ_REG32(&core_if->core_global_regs->grxfsiz); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->dev_rx_fifo_size = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dev_rx_fifo_size; ++} ++ ++int dwc_otg_set_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) { ++ DWC_WARN("Wrong value for dev_nperio_tx_fifo\n"); ++ DWC_WARN("dev_nperio_tx_fifo must be 16-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >> 16)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->dev_nperio_tx_fifo_size)) { ++ DWC_ERROR ++ ("%d invalid for dev_nperio_tx_fifo_size. Check HW configuration.\n", ++ val); ++ } ++ val = ++ (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >> ++ 16); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->dev_nperio_tx_fifo_size = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dev_nperio_tx_fifo_size; ++} ++ ++int dwc_otg_set_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) { ++ DWC_WARN("Wrong value for host_rx_fifo_size\n"); ++ DWC_WARN("host_rx_fifo_size must be 16-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > DWC_READ_REG32(&core_if->core_global_regs->grxfsiz)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->host_rx_fifo_size)) { ++ DWC_ERROR ++ ("%d invalid for host_rx_fifo_size. Check HW configuration.\n", ++ val); ++ } ++ val = DWC_READ_REG32(&core_if->core_global_regs->grxfsiz); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->host_rx_fifo_size = val; ++ return retval; ++ ++} ++ ++int32_t dwc_otg_get_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->host_rx_fifo_size; ++} ++ ++int dwc_otg_set_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) { ++ DWC_WARN("Wrong value for host_nperio_tx_fifo_size\n"); ++ DWC_WARN("host_nperio_tx_fifo_size must be 16-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >> 16)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->host_nperio_tx_fifo_size)) { ++ DWC_ERROR ++ ("%d invalid for host_nperio_tx_fifo_size. Check HW configuration.\n", ++ val); ++ } ++ val = ++ (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >> ++ 16); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->host_nperio_tx_fifo_size = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->host_nperio_tx_fifo_size; ++} ++ ++int dwc_otg_set_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) { ++ DWC_WARN("Wrong value for host_perio_tx_fifo_size\n"); ++ DWC_WARN("host_perio_tx_fifo_size must be 16-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > ((core_if->hptxfsiz.d32) >> 16)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->host_perio_tx_fifo_size)) { ++ DWC_ERROR ++ ("%d invalid for host_perio_tx_fifo_size. Check HW configuration.\n", ++ val); ++ } ++ val = (core_if->hptxfsiz.d32) >> 16; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->host_perio_tx_fifo_size = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->host_perio_tx_fifo_size; ++} ++ ++int dwc_otg_set_param_max_transfer_size(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 2047, 524288)) { ++ DWC_WARN("Wrong value for max_transfer_size\n"); ++ DWC_WARN("max_transfer_size must be 2047-524288\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val >= (1 << (core_if->hwcfg3.b.xfer_size_cntr_width + 11))) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->max_transfer_size)) { ++ DWC_ERROR ++ ("%d invalid for max_transfer_size. Check HW configuration.\n", ++ val); ++ } ++ val = ++ ((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 11)) - ++ 1); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->max_transfer_size = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_max_transfer_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->max_transfer_size; ++} ++ ++int dwc_otg_set_param_max_packet_count(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 15, 511)) { ++ DWC_WARN("Wrong value for max_packet_count\n"); ++ DWC_WARN("max_packet_count must be 15-511\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > (1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4))) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->max_packet_count)) { ++ DWC_ERROR ++ ("%d invalid for max_packet_count. Check HW configuration.\n", ++ val); ++ } ++ val = ++ ((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4)) - 1); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->max_packet_count = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_max_packet_count(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->max_packet_count; ++} ++ ++int dwc_otg_set_param_host_channels(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 1, 16)) { ++ DWC_WARN("Wrong value for host_channels\n"); ++ DWC_WARN("host_channels must be 1-16\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > (core_if->hwcfg2.b.num_host_chan + 1)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->host_channels)) { ++ DWC_ERROR ++ ("%d invalid for host_channels. Check HW configurations.\n", ++ val); ++ } ++ val = (core_if->hwcfg2.b.num_host_chan + 1); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->host_channels = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_host_channels(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->host_channels; ++} ++ ++int dwc_otg_set_param_dev_endpoints(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 1, 15)) { ++ DWC_WARN("Wrong value for dev_endpoints\n"); ++ DWC_WARN("dev_endpoints must be 1-15\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > (core_if->hwcfg2.b.num_dev_ep)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->dev_endpoints)) { ++ DWC_ERROR ++ ("%d invalid for dev_endpoints. Check HW configurations.\n", ++ val); ++ } ++ val = core_if->hwcfg2.b.num_dev_ep; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->dev_endpoints = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dev_endpoints(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dev_endpoints; ++} ++ ++int dwc_otg_set_param_phy_type(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ int valid = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 2)) { ++ DWC_WARN("Wrong value for phy_type\n"); ++ DWC_WARN("phy_type must be 0,1 or 2\n"); ++ return -DWC_E_INVALID; ++ } ++#ifndef NO_FS_PHY_HW_CHECKS ++ if ((val == DWC_PHY_TYPE_PARAM_UTMI) && ++ ((core_if->hwcfg2.b.hs_phy_type == 1) || ++ (core_if->hwcfg2.b.hs_phy_type == 3))) { ++ valid = 1; ++ } else if ((val == DWC_PHY_TYPE_PARAM_ULPI) && ++ ((core_if->hwcfg2.b.hs_phy_type == 2) || ++ (core_if->hwcfg2.b.hs_phy_type == 3))) { ++ valid = 1; ++ } else if ((val == DWC_PHY_TYPE_PARAM_FS) && ++ (core_if->hwcfg2.b.fs_phy_type == 1)) { ++ valid = 1; ++ } ++ if (!valid) { ++ if (dwc_otg_param_initialized(core_if->core_params->phy_type)) { ++ DWC_ERROR ++ ("%d invalid for phy_type. Check HW configurations.\n", ++ val); ++ } ++ if (core_if->hwcfg2.b.hs_phy_type) { ++ if ((core_if->hwcfg2.b.hs_phy_type == 3) || ++ (core_if->hwcfg2.b.hs_phy_type == 1)) { ++ val = DWC_PHY_TYPE_PARAM_UTMI; ++ } else { ++ val = DWC_PHY_TYPE_PARAM_ULPI; ++ } ++ } ++ retval = -DWC_E_INVALID; ++ } ++#endif ++ core_if->core_params->phy_type = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_phy_type(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->phy_type; ++} ++ ++int dwc_otg_set_param_speed(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for speed parameter\n"); ++ DWC_WARN("max_speed parameter must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ if ((val == 0) ++ && dwc_otg_get_param_phy_type(core_if) == DWC_PHY_TYPE_PARAM_FS) { ++ if (dwc_otg_param_initialized(core_if->core_params->speed)) { ++ DWC_ERROR ++ ("%d invalid for speed paremter. Check HW configuration.\n", ++ val); ++ } ++ val = ++ (dwc_otg_get_param_phy_type(core_if) == ++ DWC_PHY_TYPE_PARAM_FS ? 1 : 0); ++ retval = -DWC_E_INVALID; ++ } ++ core_if->core_params->speed = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_speed(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->speed; ++} ++ ++int dwc_otg_set_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN ++ ("Wrong value for host_ls_low_power_phy_clk parameter\n"); ++ DWC_WARN("host_ls_low_power_phy_clk must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ) ++ && (dwc_otg_get_param_phy_type(core_if) == DWC_PHY_TYPE_PARAM_FS)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->host_ls_low_power_phy_clk)) { ++ DWC_ERROR ++ ("%d invalid for host_ls_low_power_phy_clk. Check HW configuration.\n", ++ val); ++ } ++ val = ++ (dwc_otg_get_param_phy_type(core_if) == ++ DWC_PHY_TYPE_PARAM_FS) ? ++ DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ : ++ DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->host_ls_low_power_phy_clk = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->host_ls_low_power_phy_clk; ++} ++ ++int dwc_otg_set_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for phy_ulpi_ddr\n"); ++ DWC_WARN("phy_upli_ddr must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->phy_ulpi_ddr = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->phy_ulpi_ddr; ++} ++ ++int dwc_otg_set_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong valaue for phy_ulpi_ext_vbus\n"); ++ DWC_WARN("phy_ulpi_ext_vbus must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->phy_ulpi_ext_vbus = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->phy_ulpi_ext_vbus; ++} ++ ++int dwc_otg_set_param_phy_utmi_width(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 8, 8) && DWC_OTG_PARAM_TEST(val, 16, 16)) { ++ DWC_WARN("Wrong valaue for phy_utmi_width\n"); ++ DWC_WARN("phy_utmi_width must be 8 or 16\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->phy_utmi_width = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_phy_utmi_width(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->phy_utmi_width; ++} ++ ++int dwc_otg_set_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong valaue for ulpi_fs_ls\n"); ++ DWC_WARN("ulpi_fs_ls must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->ulpi_fs_ls = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->ulpi_fs_ls; ++} ++ ++int dwc_otg_set_param_ts_dline(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong valaue for ts_dline\n"); ++ DWC_WARN("ts_dline must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->ts_dline = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_ts_dline(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->ts_dline; ++} ++ ++int dwc_otg_set_param_i2c_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong valaue for i2c_enable\n"); ++ DWC_WARN("i2c_enable must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++#ifndef NO_FS_PHY_HW_CHECK ++ if (val == 1 && core_if->hwcfg3.b.i2c == 0) { ++ if (dwc_otg_param_initialized(core_if->core_params->i2c_enable)) { ++ DWC_ERROR ++ ("%d invalid for i2c_enable. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++#endif ++ ++ core_if->core_params->i2c_enable = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_i2c_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->i2c_enable; ++} ++ ++int dwc_otg_set_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val, int fifo_num) ++{ ++ int retval = 0; ++ gintsts_data_t gintsts; ++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ ++ if (DWC_OTG_PARAM_TEST(val, 4, 768)) { ++ DWC_WARN("Wrong value for dev_perio_tx_fifo_size\n"); ++ DWC_WARN("dev_perio_tx_fifo_size must be 4-768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > ++ (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]) >> 16)) { ++ DWC_WARN("Value is larger then power-on FIFO size\n"); ++ if (dwc_otg_param_initialized ++ (core_if->core_params->dev_perio_tx_fifo_size[fifo_num])) { ++ DWC_ERROR ++ ("`%d' invalid for parameter `dev_perio_fifo_size_%d'. Check HW configuration.\n", ++ val, fifo_num); ++ } ++ val = (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]) >> 16); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->dev_perio_tx_fifo_size[fifo_num] = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int fifo_num) ++{ ++ return core_if->core_params->dev_perio_tx_fifo_size[fifo_num]; ++} ++ ++int dwc_otg_set_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong valaue for en_multiple_tx_fifo,\n"); ++ DWC_WARN("en_multiple_tx_fifo must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val == 1 && core_if->hwcfg4.b.ded_fifo_en == 0) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->en_multiple_tx_fifo)) { ++ DWC_ERROR ++ ("%d invalid for parameter en_multiple_tx_fifo. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->en_multiple_tx_fifo = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->en_multiple_tx_fifo; ++} ++ ++int dwc_otg_set_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if, int32_t val, ++ int fifo_num) ++{ ++ int retval = 0; ++ fifosize_data_t txfifosize; ++ txfifosize.d32 = DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]); ++ ++ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) { ++ DWC_WARN("Wrong value for dev_tx_fifo_size\n"); ++ DWC_WARN("dev_tx_fifo_size must be 16-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > txfifosize.b.depth) { ++ DWC_WARN("Value is larger then power-on FIFO size\n"); ++ if (dwc_otg_param_initialized ++ (core_if->core_params->dev_tx_fifo_size[fifo_num])) { ++ DWC_ERROR ++ ("`%d' invalid for parameter `dev_tx_fifo_size_%d'. Check HW configuration.\n", ++ val, fifo_num); ++ } ++ val = txfifosize.b.depth; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->dev_tx_fifo_size[fifo_num] = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int fifo_num) ++{ ++ return core_if->core_params->dev_tx_fifo_size[fifo_num]; ++} ++ ++int dwc_otg_set_param_thr_ctl(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 7)) { ++ DWC_WARN("Wrong value for thr_ctl\n"); ++ DWC_WARN("thr_ctl must be 0-7\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val != 0) && ++ (!dwc_otg_get_param_dma_enable(core_if) || ++ !core_if->hwcfg4.b.ded_fifo_en)) { ++ if (dwc_otg_param_initialized(core_if->core_params->thr_ctl)) { ++ DWC_ERROR ++ ("%d invalid for parameter thr_ctl. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->thr_ctl = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_thr_ctl(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->thr_ctl; ++} ++ ++int dwc_otg_set_param_lpm_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for lpm_enable\n"); ++ DWC_WARN("lpm_enable must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val && !core_if->hwcfg3.b.otg_lpm_en) { ++ if (dwc_otg_param_initialized(core_if->core_params->lpm_enable)) { ++ DWC_ERROR ++ ("%d invalid for parameter lpm_enable. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->lpm_enable = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_lpm_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->lpm_enable; ++} ++ ++int dwc_otg_set_param_besl_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for besl_enable\n"); ++ DWC_WARN("besl_enable must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->besl_enable = val; ++ ++ if(val) ++ { ++ retval += dwc_otg_set_param_lpm_enable(core_if,val); ++ } ++ ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_besl_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->besl_enable; ++} ++ ++int dwc_otg_set_param_baseline_besl(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 15)) { ++ DWC_WARN("Wrong value for baseline_besl\n"); ++ DWC_WARN("baseline_besl must be 0-15\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->baseline_besl = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_baseline_besl(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->baseline_besl; ++} ++ ++int dwc_otg_set_param_deep_besl(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 15)) { ++ DWC_WARN("Wrong value for deep_besl\n"); ++ DWC_WARN("deep_besl must be 0-15\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->deep_besl = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_deep_besl(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->deep_besl; ++} ++ ++int dwc_otg_set_param_tx_thr_length(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 8, 128)) { ++ DWC_WARN("Wrong valaue for tx_thr_length\n"); ++ DWC_WARN("tx_thr_length must be 8 - 128\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->tx_thr_length = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_tx_thr_length(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->tx_thr_length; ++} ++ ++int dwc_otg_set_param_rx_thr_length(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 8, 128)) { ++ DWC_WARN("Wrong valaue for rx_thr_length\n"); ++ DWC_WARN("rx_thr_length must be 8 - 128\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->rx_thr_length = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_rx_thr_length(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->rx_thr_length; ++} ++ ++int dwc_otg_set_param_dma_burst_size(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 1, 1) && ++ DWC_OTG_PARAM_TEST(val, 4, 4) && ++ DWC_OTG_PARAM_TEST(val, 8, 8) && ++ DWC_OTG_PARAM_TEST(val, 16, 16) && ++ DWC_OTG_PARAM_TEST(val, 32, 32) && ++ DWC_OTG_PARAM_TEST(val, 64, 64) && ++ DWC_OTG_PARAM_TEST(val, 128, 128) && ++ DWC_OTG_PARAM_TEST(val, 256, 256)) { ++ DWC_WARN("`%d' invalid for parameter `dma_burst_size'\n", val); ++ return -DWC_E_INVALID; ++ } ++ core_if->core_params->dma_burst_size = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_dma_burst_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dma_burst_size; ++} ++ ++int dwc_otg_set_param_pti_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("`%d' invalid for parameter `pti_enable'\n", val); ++ return -DWC_E_INVALID; ++ } ++ if (val && (core_if->snpsid < OTG_CORE_REV_2_72a)) { ++ if (dwc_otg_param_initialized(core_if->core_params->pti_enable)) { ++ DWC_ERROR ++ ("%d invalid for parameter pti_enable. Check HW configuration.\n", ++ val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ core_if->core_params->pti_enable = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_pti_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->pti_enable; ++} ++ ++int dwc_otg_set_param_mpi_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("`%d' invalid for parameter `mpi_enable'\n", val); ++ return -DWC_E_INVALID; ++ } ++ if (val && (core_if->hwcfg2.b.multi_proc_int == 0)) { ++ if (dwc_otg_param_initialized(core_if->core_params->mpi_enable)) { ++ DWC_ERROR ++ ("%d invalid for parameter mpi_enable. Check HW configuration.\n", ++ val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ core_if->core_params->mpi_enable = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_mpi_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->mpi_enable; ++} ++ ++int dwc_otg_set_param_adp_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("`%d' invalid for parameter `adp_enable'\n", val); ++ return -DWC_E_INVALID; ++ } ++ if (val && (core_if->hwcfg3.b.adp_supp == 0)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->adp_supp_enable)) { ++ DWC_ERROR ++ ("%d invalid for parameter adp_enable. Check HW configuration.\n", ++ val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ core_if->core_params->adp_supp_enable = val; ++ /* Set OTG version 2.0 in case of enabling ADP */ ++ if (val) ++ dwc_otg_set_param_otg_ver(core_if, 1); ++ ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_adp_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->adp_supp_enable; ++} ++ ++int dwc_otg_set_param_ic_usb_cap(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("`%d' invalid for parameter `ic_usb_cap'\n", val); ++ DWC_WARN("ic_usb_cap must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val && (core_if->hwcfg2.b.otg_enable_ic_usb == 0)) { ++ if (dwc_otg_param_initialized(core_if->core_params->ic_usb_cap)) { ++ DWC_ERROR ++ ("%d invalid for parameter ic_usb_cap. Check HW configuration.\n", ++ val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ core_if->core_params->ic_usb_cap = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_ic_usb_cap(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->ic_usb_cap; ++} ++ ++int dwc_otg_set_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ int valid = 1; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 3)) { ++ DWC_WARN("`%d' invalid for parameter `ahb_thr_ratio'\n", val); ++ DWC_WARN("ahb_thr_ratio must be 0 - 3\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val ++ && (core_if->snpsid < OTG_CORE_REV_2_81a ++ || !dwc_otg_get_param_thr_ctl(core_if))) { ++ valid = 0; ++ } else if (val ++ && ((dwc_otg_get_param_tx_thr_length(core_if) / (1 << val)) < ++ 4)) { ++ valid = 0; ++ } ++ if (valid == 0) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->ahb_thr_ratio)) { ++ DWC_ERROR ++ ("%d invalid for parameter ahb_thr_ratio. Check HW configuration.\n", ++ val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ ++ core_if->core_params->ahb_thr_ratio = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->ahb_thr_ratio; ++} ++ ++int dwc_otg_set_param_power_down(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ int valid = 1; ++ hwcfg4_data_t hwcfg4 = {.d32 = 0 }; ++ hwcfg4.d32 = DWC_READ_REG32(&core_if->core_global_regs->ghwcfg4); ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 3)) { ++ DWC_WARN("`%d' invalid for parameter `power_down'\n", val); ++ DWC_WARN("power_down must be 0 - 2\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == 2) && (core_if->snpsid < OTG_CORE_REV_2_91a)) { ++ valid = 0; ++ } ++ if ((val == 3) ++ && ((core_if->snpsid < OTG_CORE_REV_3_00a) ++ || (hwcfg4.b.xhiber == 0))) { ++ valid = 0; ++ } ++ if (valid == 0) { ++ if (dwc_otg_param_initialized(core_if->core_params->power_down)) { ++ DWC_ERROR ++ ("%d invalid for parameter power_down. Check HW configuration.\n", ++ val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ core_if->core_params->power_down = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_power_down(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->power_down; ++} ++ ++int dwc_otg_set_param_reload_ctl(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ int valid = 1; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("`%d' invalid for parameter `reload_ctl'\n", val); ++ DWC_WARN("reload_ctl must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == 1) && (core_if->snpsid < OTG_CORE_REV_2_92a)) { ++ valid = 0; ++ } ++ if (valid == 0) { ++ if (dwc_otg_param_initialized(core_if->core_params->reload_ctl)) { ++ DWC_ERROR("%d invalid for parameter reload_ctl." ++ "Check HW configuration.\n", val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ core_if->core_params->reload_ctl = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_reload_ctl(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->reload_ctl; ++} ++ ++int dwc_otg_set_param_dev_out_nak(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ int valid = 1; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("`%d' invalid for parameter `dev_out_nak'\n", val); ++ DWC_WARN("dev_out_nak must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == 1) && ((core_if->snpsid < OTG_CORE_REV_2_93a) || ++ !(core_if->core_params->dma_desc_enable))) { ++ valid = 0; ++ } ++ if (valid == 0) { ++ if (dwc_otg_param_initialized(core_if->core_params->dev_out_nak)) { ++ DWC_ERROR("%d invalid for parameter dev_out_nak." ++ "Check HW configuration.\n", val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ core_if->core_params->dev_out_nak = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dev_out_nak(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dev_out_nak; ++} ++ ++int dwc_otg_set_param_cont_on_bna(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ int valid = 1; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("`%d' invalid for parameter `cont_on_bna'\n", val); ++ DWC_WARN("cont_on_bna must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == 1) && ((core_if->snpsid < OTG_CORE_REV_2_94a) || ++ !(core_if->core_params->dma_desc_enable))) { ++ valid = 0; ++ } ++ if (valid == 0) { ++ if (dwc_otg_param_initialized(core_if->core_params->cont_on_bna)) { ++ DWC_ERROR("%d invalid for parameter cont_on_bna." ++ "Check HW configuration.\n", val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ core_if->core_params->cont_on_bna = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_cont_on_bna(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->cont_on_bna; ++} ++ ++int dwc_otg_set_param_ahb_single(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ int valid = 1; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("`%d' invalid for parameter `ahb_single'\n", val); ++ DWC_WARN("ahb_single must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == 1) && (core_if->snpsid < OTG_CORE_REV_2_94a)) { ++ valid = 0; ++ } ++ if (valid == 0) { ++ if (dwc_otg_param_initialized(core_if->core_params->ahb_single)) { ++ DWC_ERROR("%d invalid for parameter ahb_single." ++ "Check HW configuration.\n", val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ core_if->core_params->ahb_single = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_ahb_single(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->ahb_single; ++} ++ ++int dwc_otg_set_param_otg_ver(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("`%d' invalid for parameter `otg_ver'\n", val); ++ DWC_WARN ++ ("otg_ver must be 0(for OTG 1.3 support) or 1(for OTG 2.0 support)\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->otg_ver = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_otg_ver(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->otg_ver; ++} ++ ++uint32_t dwc_otg_get_hnpstatus(dwc_otg_core_if_t * core_if) ++{ ++ gotgctl_data_t otgctl; ++ otgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl); ++ return otgctl.b.hstnegscs; ++} ++ ++uint32_t dwc_otg_get_srpstatus(dwc_otg_core_if_t * core_if) ++{ ++ gotgctl_data_t otgctl; ++ otgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl); ++ return otgctl.b.sesreqscs; ++} ++ ++void dwc_otg_set_hnpreq(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ if(core_if->otg_ver == 0) { ++ gotgctl_data_t otgctl; ++ otgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl); ++ otgctl.b.hnpreq = val; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, otgctl.d32); ++ } else { ++ core_if->otg_sts = val; ++ } ++} ++ ++uint32_t dwc_otg_get_gsnpsid(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->snpsid; ++} ++ ++uint32_t dwc_otg_get_mode(dwc_otg_core_if_t * core_if) ++{ ++ gintsts_data_t gintsts; ++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ return gintsts.b.curmode; ++} ++ ++uint32_t dwc_otg_get_hnpcapable(dwc_otg_core_if_t * core_if) ++{ ++ gusbcfg_data_t usbcfg; ++ usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++ return usbcfg.b.hnpcap; ++} ++ ++void dwc_otg_set_hnpcapable(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ gusbcfg_data_t usbcfg; ++ usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++ usbcfg.b.hnpcap = val; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, usbcfg.d32); ++} ++ ++uint32_t dwc_otg_get_srpcapable(dwc_otg_core_if_t * core_if) ++{ ++ gusbcfg_data_t usbcfg; ++ usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++ return usbcfg.b.srpcap; ++} ++ ++void dwc_otg_set_srpcapable(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ gusbcfg_data_t usbcfg; ++ usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++ usbcfg.b.srpcap = val; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, usbcfg.d32); ++} ++ ++uint32_t dwc_otg_get_devspeed(dwc_otg_core_if_t * core_if) ++{ ++ dcfg_data_t dcfg; ++ dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg); ++ return dcfg.b.devspd; ++} ++ ++void dwc_otg_set_devspeed(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ dcfg_data_t dcfg; ++ dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg); ++ dcfg.b.devspd = val; ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32); ++} ++ ++uint32_t dwc_otg_get_busconnected(dwc_otg_core_if_t * core_if) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0); ++ return hprt0.b.prtconnsts; ++} ++ ++uint32_t dwc_otg_get_enumspeed(dwc_otg_core_if_t * core_if) ++{ ++ dsts_data_t dsts; ++ dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); ++ return dsts.b.enumspd; ++} ++ ++uint32_t dwc_otg_get_prtpower(dwc_otg_core_if_t * core_if) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0); ++ return hprt0.b.prtpwr; ++ ++} ++ ++uint32_t dwc_otg_get_core_state(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->hibernation_suspend; ++} ++ ++void dwc_otg_set_prtpower(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtpwr = val; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++} ++ ++uint32_t dwc_otg_get_prtsuspend(dwc_otg_core_if_t * core_if) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0); ++ return hprt0.b.prtsusp; ++ ++} ++ ++void dwc_otg_set_prtsuspend(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtsusp = val; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++} ++ ++uint32_t dwc_otg_get_fr_interval(dwc_otg_core_if_t * core_if) ++{ ++ hfir_data_t hfir; ++ hfir.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hfir); ++ return hfir.b.frint; ++ ++} ++ ++void dwc_otg_set_fr_interval(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ hfir_data_t hfir; ++ uint32_t fram_int; ++ fram_int = calc_frame_interval(core_if); ++ hfir.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hfir); ++ if (!core_if->core_params->reload_ctl) { ++ DWC_WARN("\nCannot reload HFIR register.HFIR.HFIRRldCtrl bit is" ++ "not set to 1.\nShould load driver with reload_ctl=1" ++ " module parameter\n"); ++ return; ++ } ++ switch (fram_int) { ++ case 3750: ++ if ((val < 3350) || (val > 4150)) { ++ DWC_WARN("HFIR interval for HS core and 30 MHz" ++ "clock freq should be from 3350 to 4150\n"); ++ return; ++ } ++ break; ++ case 30000: ++ if ((val < 26820) || (val > 33180)) { ++ DWC_WARN("HFIR interval for FS/LS core and 30 MHz" ++ "clock freq should be from 26820 to 33180\n"); ++ return; ++ } ++ break; ++ case 6000: ++ if ((val < 5360) || (val > 6640)) { ++ DWC_WARN("HFIR interval for HS core and 48 MHz" ++ "clock freq should be from 5360 to 6640\n"); ++ return; ++ } ++ break; ++ case 48000: ++ if ((val < 42912) || (val > 53088)) { ++ DWC_WARN("HFIR interval for FS/LS core and 48 MHz" ++ "clock freq should be from 42912 to 53088\n"); ++ return; ++ } ++ break; ++ case 7500: ++ if ((val < 6700) || (val > 8300)) { ++ DWC_WARN("HFIR interval for HS core and 60 MHz" ++ "clock freq should be from 6700 to 8300\n"); ++ return; ++ } ++ break; ++ case 60000: ++ if ((val < 53640) || (val > 65536)) { ++ DWC_WARN("HFIR interval for FS/LS core and 60 MHz" ++ "clock freq should be from 53640 to 65536\n"); ++ return; ++ } ++ break; ++ default: ++ DWC_WARN("Unknown frame interval\n"); ++ return; ++ break; ++ ++ } ++ hfir.b.frint = val; ++ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hfir, hfir.d32); ++} ++ ++uint32_t dwc_otg_get_mode_ch_tim(dwc_otg_core_if_t * core_if) ++{ ++ hcfg_data_t hcfg; ++ hcfg.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg); ++ return hcfg.b.modechtimen; ++ ++} ++ ++void dwc_otg_set_mode_ch_tim(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ hcfg_data_t hcfg; ++ hcfg.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg); ++ hcfg.b.modechtimen = val; ++ DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, hcfg.d32); ++} ++ ++void dwc_otg_set_prtresume(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtres = val; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++} ++ ++uint32_t dwc_otg_get_remotewakesig(dwc_otg_core_if_t * core_if) ++{ ++ dctl_data_t dctl; ++ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl); ++ return dctl.b.rmtwkupsig; ++} ++ ++uint32_t dwc_otg_get_beslreject(dwc_otg_core_if_t * core_if) ++{ ++ dctl_data_t dctl; ++ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl); ++ return dctl.b.besl_reject; ++} ++ ++void dwc_otg_set_beslreject(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ dctl_data_t dctl; ++ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl); ++ dctl.b.besl_reject = val; ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32); ++} ++uint32_t dwc_otg_get_hirdthresh(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ return lpmcfg.b.hird_thres; ++} ++ ++void dwc_otg_set_hirdthresh(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ glpmcfg_data_t lpmcfg; ++ ++ if (val > 15) { ++ DWC_WARN("Wrong valaue for hird_thres\n"); ++ DWC_WARN("hird_thres must be 0-f\n"); ++ return ; ++ } ++ ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ lpmcfg.b.hird_thres &= (1<<4); ++ lpmcfg.b.hird_thres |= val; ++ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++} ++ ++uint32_t dwc_otg_get_lpm_portsleepstatus(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ ++ DWC_ASSERT(! ++ ((core_if->lx_state == DWC_OTG_L1) ^ lpmcfg.b.prt_sleep_sts), ++ "lx_state = %d, lmpcfg.prt_sleep_sts = %d\n", ++ core_if->lx_state, lpmcfg.b.prt_sleep_sts); ++ ++ return lpmcfg.b.prt_sleep_sts; ++} ++ ++uint32_t dwc_otg_get_lpm_remotewakeenabled(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ return lpmcfg.b.rem_wkup_en; ++} ++ ++uint32_t dwc_otg_get_lpmresponse(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ return lpmcfg.b.appl_resp; ++} ++ ++void dwc_otg_set_lpmresponse(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ lpmcfg.b.appl_resp = val; ++ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++} ++ ++uint32_t dwc_otg_get_hsic_connect(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ return lpmcfg.b.hsic_connect; ++} ++ ++void dwc_otg_set_hsic_connect(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ lpmcfg.b.hsic_connect = val; ++ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++} ++ ++uint32_t dwc_otg_get_inv_sel_hsic(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ return lpmcfg.b.inv_sel_hsic; ++ ++} ++ ++void dwc_otg_set_inv_sel_hsic(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ lpmcfg.b.inv_sel_hsic = val; ++ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++} ++ ++uint32_t dwc_otg_get_gotgctl(dwc_otg_core_if_t * core_if) ++{ ++ return DWC_READ_REG32(&core_if->core_global_regs->gotgctl); ++} ++ ++void dwc_otg_set_gotgctl(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, val); ++} ++ ++uint32_t dwc_otg_get_gusbcfg(dwc_otg_core_if_t * core_if) ++{ ++ return DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++} ++ ++void dwc_otg_set_gusbcfg(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, val); ++} ++ ++uint32_t dwc_otg_get_grxfsiz(dwc_otg_core_if_t * core_if) ++{ ++ return DWC_READ_REG32(&core_if->core_global_regs->grxfsiz); ++} ++ ++void dwc_otg_set_grxfsiz(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ DWC_WRITE_REG32(&core_if->core_global_regs->grxfsiz, val); ++} ++ ++uint32_t dwc_otg_get_gnptxfsiz(dwc_otg_core_if_t * core_if) ++{ ++ return DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz); ++} ++ ++void dwc_otg_set_gnptxfsiz(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gnptxfsiz, val); ++} ++ ++uint32_t dwc_otg_get_gpvndctl(dwc_otg_core_if_t * core_if) ++{ ++ return DWC_READ_REG32(&core_if->core_global_regs->gpvndctl); ++} ++ ++void dwc_otg_set_gpvndctl(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gpvndctl, val); ++} ++ ++uint32_t dwc_otg_get_ggpio(dwc_otg_core_if_t * core_if) ++{ ++ return DWC_READ_REG32(&core_if->core_global_regs->ggpio); ++} ++ ++void dwc_otg_set_ggpio(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ DWC_WRITE_REG32(&core_if->core_global_regs->ggpio, val); ++} ++ ++uint32_t dwc_otg_get_hprt0(dwc_otg_core_if_t * core_if) ++{ ++ return DWC_READ_REG32(core_if->host_if->hprt0); ++ ++} ++ ++void dwc_otg_set_hprt0(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ DWC_WRITE_REG32(core_if->host_if->hprt0, val); ++} ++ ++uint32_t dwc_otg_get_guid(dwc_otg_core_if_t * core_if) ++{ ++ return DWC_READ_REG32(&core_if->core_global_regs->guid); ++} ++ ++void dwc_otg_set_guid(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ DWC_WRITE_REG32(&core_if->core_global_regs->guid, val); ++} ++ ++uint32_t dwc_otg_get_hptxfsiz(dwc_otg_core_if_t * core_if) ++{ ++ return DWC_READ_REG32(&core_if->core_global_regs->hptxfsiz); ++} ++ ++uint16_t dwc_otg_get_otg_version(dwc_otg_core_if_t * core_if) ++{ ++ return ((core_if->otg_ver == 1) ? (uint16_t)0x0200 : (uint16_t)0x0103); ++} ++ ++/** ++ * Start the SRP timer to detect when the SRP does not complete within ++ * 6 seconds. ++ * ++ * @param core_if the pointer to core_if strucure. ++ */ ++void dwc_otg_pcd_start_srp_timer(dwc_otg_core_if_t * core_if) ++{ ++ core_if->srp_timer_started = 1; ++ DWC_TIMER_SCHEDULE(core_if->srp_timer, 6000 /* 6 secs */ ); ++} ++ ++void dwc_otg_initiate_srp(void * p) ++{ ++ dwc_otg_core_if_t * core_if = p; ++ uint32_t *addr = (uint32_t *) & (core_if->core_global_regs->gotgctl); ++ gotgctl_data_t mem; ++ gotgctl_data_t val; ++ ++ val.d32 = DWC_READ_REG32(addr); ++ if (val.b.sesreq) { ++ DWC_ERROR("Session Request Already active!\n"); ++ return; ++ } ++ ++ DWC_INFO("Session Request Initated\n"); //NOTICE ++ mem.d32 = DWC_READ_REG32(addr); ++ mem.b.sesreq = 1; ++ DWC_WRITE_REG32(addr, mem.d32); ++ ++ /* Start the SRP timer */ ++ dwc_otg_pcd_start_srp_timer(core_if); ++ return; ++} ++ ++int dwc_otg_check_haps_status(dwc_otg_core_if_t * core_if) ++{ ++ int retval = 0; ++ ++ if(DWC_READ_REG32(&core_if->core_global_regs->gsnpsid) == 0xffffffff) ++ { ++ return -1; ++ } else { ++ return retval; ++ } ++ ++} +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_cil.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_cil.h +new file mode 100644 +index 0000000..b52a280 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_cil.h +@@ -0,0 +1,1498 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.h $ ++ * $Revision: #128 $ ++ * $Date: 2013/05/16 $ ++ * $Change: 2231774 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_CIL_H__) ++#define __DWC_CIL_H__ ++ ++#include "dwc_list.h" ++#include "dwc_otg_dbg.h" ++#include "dwc_otg_regs.h" ++ ++#include "dwc_otg_core_if.h" ++#include "dwc_otg_adp.h" ++ ++/** ++ * @file ++ * This file contains the interface to the Core Interface Layer. ++ */ ++ ++#ifdef DWC_UTE_CFI ++ ++#define MAX_DMA_DESCS_PER_EP 256 ++ ++/** ++ * Enumeration for the data buffer mode ++ */ ++typedef enum _data_buffer_mode { ++ BM_STANDARD = 0, /* data buffer is in normal mode */ ++ BM_SG = 1, /* data buffer uses the scatter/gather mode */ ++ BM_CONCAT = 2, /* data buffer uses the concatenation mode */ ++ BM_CIRCULAR = 3, /* data buffer uses the circular DMA mode */ ++ BM_ALIGN = 4 /* data buffer is in buffer alignment mode */ ++} data_buffer_mode_e; ++#endif //DWC_UTE_CFI ++ ++/** Macros defined for DWC OTG HW Release version */ ++ ++#define OTG_CORE_REV_2_60a 0x4F54260A ++#define OTG_CORE_REV_2_71a 0x4F54271A ++#define OTG_CORE_REV_2_72a 0x4F54272A ++#define OTG_CORE_REV_2_80a 0x4F54280A ++#define OTG_CORE_REV_2_81a 0x4F54281A ++#define OTG_CORE_REV_2_90a 0x4F54290A ++#define OTG_CORE_REV_2_91a 0x4F54291A ++#define OTG_CORE_REV_2_92a 0x4F54292A ++#define OTG_CORE_REV_2_93a 0x4F54293A ++#define OTG_CORE_REV_2_94a 0x4F54294A ++#define OTG_CORE_REV_3_00a 0x4F54300A ++#define OTG_CORE_REV_3_10a 0x4F54310A ++ ++/** ++ * Information for each ISOC packet. ++ */ ++typedef struct iso_pkt_info { ++ uint32_t offset; ++ uint32_t length; ++ int32_t status; ++} iso_pkt_info_t; ++ ++/** ++ * The <code>dwc_ep</code> structure represents the state of a single ++ * endpoint when acting in device mode. It contains the data items ++ * needed for an endpoint to be activated and transfer packets. ++ */ ++typedef struct dwc_ep { ++ /** EP number used for register address lookup */ ++ uint8_t num; ++ /** EP direction 0 = OUT */ ++ unsigned is_in:1; ++ /** EP active. */ ++ unsigned active:1; ++ ++ /** ++ * Periodic Tx FIFO # for IN EPs For INTR EP set to 0 to use non-periodic ++ * Tx FIFO. If dedicated Tx FIFOs are enabled Tx FIFO # FOR IN EPs*/ ++ unsigned tx_fifo_num:4; ++ /** EP type: 0 - Control, 1 - ISOC, 2 - BULK, 3 - INTR */ ++ unsigned type:2; ++#define DWC_OTG_EP_TYPE_CONTROL 0 ++#define DWC_OTG_EP_TYPE_ISOC 1 ++#define DWC_OTG_EP_TYPE_BULK 2 ++#define DWC_OTG_EP_TYPE_INTR 3 ++ ++ /** DATA start PID for INTR and BULK EP */ ++ unsigned data_pid_start:1; ++ /** Frame (even/odd) for ISOC EP */ ++ unsigned even_odd_frame:1; ++ /** Max Packet bytes */ ++ unsigned maxpacket:11; ++ ++ /** Max Transfer size */ ++ uint32_t maxxfer; ++ ++ /** @name Transfer state */ ++ /** @{ */ ++ ++ /** ++ * Pointer to the beginning of the transfer buffer -- do not modify ++ * during transfer. ++ */ ++ dwc_dma_t dma_addr; ++ ++ dwc_dma_t dma_desc_addr; ++ dwc_otg_dev_dma_desc_t *desc_addr; ++ ++ /* Additional desc chain for ISO transfers */ ++ dwc_dma_t dma_desc_addr1; ++ dwc_otg_dev_dma_desc_t *desc_addr1; ++ /* Flag indicating which one of two ISO desc chains currently is in use */ ++ uint8_t use_add_buf; ++ ++ uint8_t *start_xfer_buff; ++ /** pointer to the transfer buffer */ ++ uint8_t *xfer_buff; ++ /** Number of bytes to transfer */ ++ unsigned xfer_len:19; ++ /** Number of bytes transferred. */ ++ unsigned xfer_count:19; ++ /** Sent ZLP */ ++ unsigned sent_zlp:1; ++ /** Total len for control transfer */ ++ unsigned total_len:19; ++ ++ /** stall clear flag */ ++ unsigned stall_clear_flag:1; ++ ++ /** SETUP pkt cnt rollover flag for EP0 out*/ ++ unsigned stp_rollover; ++ ++#ifdef DWC_UTE_CFI ++ /* The buffer mode */ ++ data_buffer_mode_e buff_mode; ++ ++ /* The chain of DMA descriptors. ++ * MAX_DMA_DESCS_PER_EP will be allocated for each active EP. ++ */ ++ dwc_otg_dma_desc_t *descs; ++ ++ /* The DMA address of the descriptors chain start */ ++ dma_addr_t descs_dma_addr; ++ /** This variable stores the length of the last enqueued request */ ++ uint32_t cfi_req_len; ++#endif //DWC_UTE_CFI ++ ++/** Max DMA Descriptor count for any EP */ ++#define MAX_DMA_DESC_CNT 256 ++ /** Allocated DMA Desc count */ ++ uint32_t desc_cnt; ++ ++ /** First ISO Desc in use in the first chain*/ ++ uint32_t iso_desc_first; ++ /** Last ISO Desc in use in the second chain */ ++ uint32_t iso_desc_second; ++ /** Flag indicated that iso transfers were started */ ++ uint8_t iso_transfer_started; ++ ++ /** bInterval */ ++ uint32_t bInterval; ++ /** Next frame num to setup next ISOC transfer */ ++ uint32_t frame_num; ++ /** Indicates SOF number overrun in DSTS */ ++ uint8_t frm_overrun; ++ ++#ifdef DWC_UTE_PER_IO ++ /** Next frame num for which will be setup DMA Desc */ ++ uint32_t xiso_frame_num; ++ /** bInterval */ ++ uint32_t xiso_bInterval; ++ /** Count of currently active transfers - shall be either 0 or 1 */ ++ int xiso_active_xfers; ++ int xiso_queued_xfers; ++#endif ++#ifdef DWC_EN_ISOC ++ /** ++ * Variables specific for ISOC EPs ++ * ++ */ ++ /** DMA addresses of ISOC buffers */ ++ dwc_dma_t dma_addr0; ++ dwc_dma_t dma_addr1; ++ ++ dwc_dma_t iso_dma_desc_addr; ++ dwc_otg_dev_dma_desc_t *iso_desc_addr; ++ ++ /** pointer to the transfer buffers */ ++ uint8_t *xfer_buff0; ++ uint8_t *xfer_buff1; ++ ++ /** number of ISOC Buffer is processing */ ++ uint32_t proc_buf_num; ++ /** Interval of ISOC Buffer processing */ ++ uint32_t buf_proc_intrvl; ++ /** Data size for regular frame */ ++ uint32_t data_per_frame; ++ ++ /* todo - pattern data support is to be implemented in the future */ ++ /** Data size for pattern frame */ ++ uint32_t data_pattern_frame; ++ /** Frame number of pattern data */ ++ uint32_t sync_frame; ++ ++ /** bInterval */ ++ uint32_t bInterval; ++ /** ISO Packet number per frame */ ++ uint32_t pkt_per_frm; ++ /** Next frame num for which will be setup DMA Desc */ ++ uint32_t next_frame; ++ /** Number of packets per buffer processing */ ++ uint32_t pkt_cnt; ++ /** Info for all isoc packets */ ++ iso_pkt_info_t *pkt_info; ++ /** current pkt number */ ++ uint32_t cur_pkt; ++ /** current pkt number */ ++ uint8_t *cur_pkt_addr; ++ /** current pkt number */ ++ uint32_t cur_pkt_dma_addr; ++#endif /* DWC_EN_ISOC */ ++ ++/** @} */ ++} dwc_ep_t; ++ ++/* ++ * Reasons for halting a host channel. ++ */ ++typedef enum dwc_otg_halt_status { ++ DWC_OTG_HC_XFER_NO_HALT_STATUS, ++ DWC_OTG_HC_XFER_COMPLETE, ++ DWC_OTG_HC_XFER_URB_COMPLETE, ++ DWC_OTG_HC_XFER_ACK, ++ DWC_OTG_HC_XFER_NAK, ++ DWC_OTG_HC_XFER_NYET, ++ DWC_OTG_HC_XFER_STALL, ++ DWC_OTG_HC_XFER_XACT_ERR, ++ DWC_OTG_HC_XFER_FRAME_OVERRUN, ++ DWC_OTG_HC_XFER_BABBLE_ERR, ++ DWC_OTG_HC_XFER_DATA_TOGGLE_ERR, ++ DWC_OTG_HC_XFER_AHB_ERR, ++ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE, ++ DWC_OTG_HC_XFER_URB_DEQUEUE ++} dwc_otg_halt_status_e; ++ ++/** ++ * Host channel descriptor. This structure represents the state of a single ++ * host channel when acting in host mode. It contains the data items needed to ++ * transfer packets to an endpoint via a host channel. ++ */ ++typedef struct dwc_hc { ++ /** Host channel number used for register address lookup */ ++ uint8_t hc_num; ++ ++ /** Device to access */ ++ unsigned dev_addr:7; ++ ++ /** EP to access */ ++ unsigned ep_num:4; ++ ++ /** EP direction. 0: OUT, 1: IN */ ++ unsigned ep_is_in:1; ++ ++ /** ++ * EP speed. ++ * One of the following values: ++ * - DWC_OTG_EP_SPEED_LOW ++ * - DWC_OTG_EP_SPEED_FULL ++ * - DWC_OTG_EP_SPEED_HIGH ++ */ ++ unsigned speed:2; ++#define DWC_OTG_EP_SPEED_LOW 0 ++#define DWC_OTG_EP_SPEED_FULL 1 ++#define DWC_OTG_EP_SPEED_HIGH 2 ++ ++ /** ++ * Endpoint type. ++ * One of the following values: ++ * - DWC_OTG_EP_TYPE_CONTROL: 0 ++ * - DWC_OTG_EP_TYPE_ISOC: 1 ++ * - DWC_OTG_EP_TYPE_BULK: 2 ++ * - DWC_OTG_EP_TYPE_INTR: 3 ++ */ ++ unsigned ep_type:2; ++ ++ /** Max packet size in bytes */ ++ unsigned max_packet:11; ++ ++ /** ++ * PID for initial transaction. ++ * 0: DATA0,<br> ++ * 1: DATA2,<br> ++ * 2: DATA1,<br> ++ * 3: MDATA (non-Control EP), ++ * SETUP (Control EP) ++ */ ++ unsigned data_pid_start:2; ++#define DWC_OTG_HC_PID_DATA0 0 ++#define DWC_OTG_HC_PID_DATA2 1 ++#define DWC_OTG_HC_PID_DATA1 2 ++#define DWC_OTG_HC_PID_MDATA 3 ++#define DWC_OTG_HC_PID_SETUP 3 ++ ++ /** Number of periodic transactions per (micro)frame */ ++ unsigned multi_count:2; ++ ++ /** @name Transfer State */ ++ /** @{ */ ++ ++ /** Pointer to the current transfer buffer position. */ ++ uint8_t *xfer_buff; ++ /** ++ * In Buffer DMA mode this buffer will be used ++ * if xfer_buff is not DWORD aligned. ++ */ ++ dwc_dma_t align_buff; ++ /** Total number of bytes to transfer. */ ++ uint32_t xfer_len; ++ /** Number of bytes transferred so far. */ ++ uint32_t xfer_count; ++ /** Packet count at start of transfer.*/ ++ uint16_t start_pkt_count; ++ ++ /** ++ * Flag to indicate whether the transfer has been started. Set to 1 if ++ * it has been started, 0 otherwise. ++ */ ++ uint8_t xfer_started; ++ ++ /** ++ * Set to 1 to indicate that a PING request should be issued on this ++ * channel. If 0, process normally. ++ */ ++ uint8_t do_ping; ++ ++ /** ++ * Set to 1 to indicate that the error count for this transaction is ++ * non-zero. Set to 0 if the error count is 0. ++ */ ++ uint8_t error_state; ++ ++ /** ++ * Set to 1 to indicate that this channel should be halted the next ++ * time a request is queued for the channel. This is necessary in ++ * slave mode if no request queue space is available when an attempt ++ * is made to halt the channel. ++ */ ++ uint8_t halt_on_queue; ++ ++ /** ++ * Set to 1 if the host channel has been halted, but the core is not ++ * finished flushing queued requests. Otherwise 0. ++ */ ++ uint8_t halt_pending; ++ ++ /** ++ * Reason for halting the host channel. ++ */ ++ dwc_otg_halt_status_e halt_status; ++ ++ /* ++ * Split settings for the host channel ++ */ ++ uint8_t do_split; /**< Enable split for the channel */ ++ uint8_t complete_split; /**< Enable complete split */ ++ uint8_t hub_addr; /**< Address of high speed hub */ ++ ++ uint8_t port_addr; /**< Port of the low/full speed device */ ++ /** Split transaction position ++ * One of the following values: ++ * - DWC_HCSPLIT_XACTPOS_MID ++ * - DWC_HCSPLIT_XACTPOS_BEGIN ++ * - DWC_HCSPLIT_XACTPOS_END ++ * - DWC_HCSPLIT_XACTPOS_ALL */ ++ uint8_t xact_pos; ++ ++ /** Set when the host channel does a short read. */ ++ uint8_t short_read; ++ ++ /** ++ * Number of requests issued for this channel since it was assigned to ++ * the current transfer (not counting PINGs). ++ */ ++ uint8_t requests; ++ ++ /** ++ * Queue Head for the transfer being processed by this channel. ++ */ ++ struct dwc_otg_qh *qh; ++ ++ /** @} */ ++ ++ /** Entry in list of host channels. */ ++ DWC_CIRCLEQ_ENTRY(dwc_hc) hc_list_entry; ++ ++ /** @name Descriptor DMA support */ ++ /** @{ */ ++ ++ /** Number of Transfer Descriptors */ ++ uint16_t ntd; ++ ++ /** Descriptor List DMA address */ ++ dwc_dma_t desc_list_addr; ++ ++ /** Scheduling micro-frame bitmap. */ ++ uint8_t schinfo; ++ ++ /** @} */ ++} dwc_hc_t; ++ ++/** ++ * The following parameters may be specified when starting the module. These ++ * parameters define how the DWC_otg controller should be configured. ++ */ ++typedef struct dwc_otg_core_params { ++ int32_t opt; ++ ++ /** ++ * Specifies the OTG capabilities. The driver will automatically ++ * detect the value for this parameter if none is specified. ++ * 0 - HNP and SRP capable (default) ++ * 1 - SRP Only capable ++ * 2 - No HNP/SRP capable ++ */ ++ int32_t otg_cap; ++ ++ /** ++ * Specifies whether to use slave or DMA mode for accessing the data ++ * FIFOs. The driver will automatically detect the value for this ++ * parameter if none is specified. ++ * 0 - Slave ++ * 1 - DMA (default, if available) ++ */ ++ int32_t dma_enable; ++ ++ /** ++ * When DMA mode is enabled specifies whether to use address DMA or DMA ++ * Descriptor mode for accessing the data FIFOs in device mode. The driver ++ * will automatically detect the value for this if none is specified. ++ * 0 - address DMA ++ * 1 - DMA Descriptor(default, if available) ++ */ ++ int32_t dma_desc_enable; ++ /** The DMA Burst size (applicable only for External DMA ++ * Mode). 1, 4, 8 16, 32, 64, 128, 256 (default 32) ++ */ ++ int32_t dma_burst_size; /* Translate this to GAHBCFG values */ ++ ++ /** ++ * Specifies the maximum speed of operation in host and device mode. ++ * The actual speed depends on the speed of the attached device and ++ * the value of phy_type. The actual speed depends on the speed of the ++ * attached device. ++ * 0 - High Speed (default) ++ * 1 - Full Speed ++ */ ++ int32_t speed; ++ /** Specifies whether low power mode is supported when attached ++ * to a Full Speed or Low Speed device in host mode. ++ * 0 - Don't support low power mode (default) ++ * 1 - Support low power mode ++ */ ++ int32_t host_support_fs_ls_low_power; ++ ++ /** Specifies the PHY clock rate in low power mode when connected to a ++ * Low Speed device in host mode. This parameter is applicable only if ++ * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS ++ * then defaults to 6 MHZ otherwise 48 MHZ. ++ * ++ * 0 - 48 MHz ++ * 1 - 6 MHz ++ */ ++ int32_t host_ls_low_power_phy_clk; ++ ++ /** ++ * 0 - Use cC FIFO size parameters ++ * 1 - Allow dynamic FIFO sizing (default) ++ */ ++ int32_t enable_dynamic_fifo; ++ ++ /** Total number of 4-byte words in the data FIFO memory. This ++ * memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic ++ * Tx FIFOs. ++ * 32 to 32768 (default 8192) ++ * Note: The total FIFO memory depth in the FPGA configuration is 8192. ++ */ ++ int32_t data_fifo_size; ++ ++ /** Number of 4-byte words in the Rx FIFO in device mode when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1064) ++ */ ++ int32_t dev_rx_fifo_size; ++ ++ /** Number of 4-byte words in the non-periodic Tx FIFO in device mode ++ * when dynamic FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t dev_nperio_tx_fifo_size; ++ ++ /** Number of 4-byte words in each of the periodic Tx FIFOs in device ++ * mode when dynamic FIFO sizing is enabled. ++ * 4 to 768 (default 256) ++ */ ++ uint32_t dev_perio_tx_fifo_size[MAX_PERIO_FIFOS]; ++ ++ /** Number of 4-byte words in the Rx FIFO in host mode when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t host_rx_fifo_size; ++ ++ /** Number of 4-byte words in the non-periodic Tx FIFO in host mode ++ * when Dynamic FIFO sizing is enabled in the core. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t host_nperio_tx_fifo_size; ++ ++ /** Number of 4-byte words in the host periodic Tx FIFO when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t host_perio_tx_fifo_size; ++ ++ /** The maximum transfer size supported in bytes. ++ * 2047 to 65,535 (default 65,535) ++ */ ++ int32_t max_transfer_size; ++ ++ /** The maximum number of packets in a transfer. ++ * 15 to 511 (default 511) ++ */ ++ int32_t max_packet_count; ++ ++ /** The number of host channel registers to use. ++ * 1 to 16 (default 12) ++ * Note: The FPGA configuration supports a maximum of 12 host channels. ++ */ ++ int32_t host_channels; ++ ++ /** The number of endpoints in addition to EP0 available for device ++ * mode operations. ++ * 1 to 15 (default 6 IN and OUT) ++ * Note: The FPGA configuration supports a maximum of 6 IN and OUT ++ * endpoints in addition to EP0. ++ */ ++ int32_t dev_endpoints; ++ ++ /** ++ * Specifies the type of PHY interface to use. By default, the driver ++ * will automatically detect the phy_type. ++ * ++ * 0 - Full Speed PHY ++ * 1 - UTMI+ (default) ++ * 2 - ULPI ++ */ ++ int32_t phy_type; ++ ++ /** ++ * Specifies the UTMI+ Data Width. This parameter is ++ * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI ++ * PHY_TYPE, this parameter indicates the data width between ++ * the MAC and the ULPI Wrapper.) Also, this parameter is ++ * applicable only if the OTG_HSPHY_WIDTH cC parameter was set ++ * to "8 and 16 bits", meaning that the core has been ++ * configured to work at either data path width. ++ * ++ * 8 or 16 bits (default 16) ++ */ ++ int32_t phy_utmi_width; ++ ++ /** ++ * Specifies whether the ULPI operates at double or single ++ * data rate. This parameter is only applicable if PHY_TYPE is ++ * ULPI. ++ * ++ * 0 - single data rate ULPI interface with 8 bit wide data ++ * bus (default) ++ * 1 - double data rate ULPI interface with 4 bit wide data ++ * bus ++ */ ++ int32_t phy_ulpi_ddr; ++ ++ /** ++ * Specifies whether to use the internal or external supply to ++ * drive the vbus with a ULPI phy. ++ */ ++ int32_t phy_ulpi_ext_vbus; ++ ++ /** ++ * Specifies whether to use the I2Cinterface for full speed PHY. This ++ * parameter is only applicable if PHY_TYPE is FS. ++ * 0 - No (default) ++ * 1 - Yes ++ */ ++ int32_t i2c_enable; ++ ++ int32_t ulpi_fs_ls; ++ ++ int32_t ts_dline; ++ ++ /** ++ * Specifies whether dedicated transmit FIFOs are ++ * enabled for non periodic IN endpoints in device mode ++ * 0 - No ++ * 1 - Yes ++ */ ++ int32_t en_multiple_tx_fifo; ++ ++ /** Number of 4-byte words in each of the Tx FIFOs in device ++ * mode when dynamic FIFO sizing is enabled. ++ * 4 to 768 (default 256) ++ */ ++ uint32_t dev_tx_fifo_size[MAX_TX_FIFOS]; ++ ++ /** Thresholding enable flag- ++ * bit 0 - enable non-ISO Tx thresholding ++ * bit 1 - enable ISO Tx thresholding ++ * bit 2 - enable Rx thresholding ++ */ ++ uint32_t thr_ctl; ++ ++ /** Thresholding length for Tx ++ * FIFOs in 32 bit DWORDs ++ */ ++ uint32_t tx_thr_length; ++ ++ /** Thresholding length for Rx ++ * FIFOs in 32 bit DWORDs ++ */ ++ uint32_t rx_thr_length; ++ ++ /** ++ * Specifies whether LPM (Link Power Management) support is enabled ++ */ ++ int32_t lpm_enable; ++ ++ /** ++ * Specifies whether LPM Errata (Link Power Management) support is enabled ++ */ ++ int32_t besl_enable; ++ ++ /** ++ * Specifies the baseline besl value ++ */ ++ int32_t baseline_besl; ++ ++ /** ++ * Specifies the deep besl value ++ */ ++ int32_t deep_besl; ++ /** Per Transfer Interrupt ++ * mode enable flag ++ * 1 - Enabled ++ * 0 - Disabled ++ */ ++ int32_t pti_enable; ++ ++ /** Multi Processor Interrupt ++ * mode enable flag ++ * 1 - Enabled ++ * 0 - Disabled ++ */ ++ int32_t mpi_enable; ++ ++ /** IS_USB Capability ++ * 1 - Enabled ++ * 0 - Disabled ++ */ ++ int32_t ic_usb_cap; ++ ++ /** AHB Threshold Ratio ++ * 2'b00 AHB Threshold = MAC Threshold ++ * 2'b01 AHB Threshold = 1/2 MAC Threshold ++ * 2'b10 AHB Threshold = 1/4 MAC Threshold ++ * 2'b11 AHB Threshold = 1/8 MAC Threshold ++ */ ++ int32_t ahb_thr_ratio; ++ ++ /** ADP Support ++ * 1 - Enabled ++ * 0 - Disabled ++ */ ++ int32_t adp_supp_enable; ++ ++ /** HFIR Reload Control ++ * 0 - The HFIR cannot be reloaded dynamically. ++ * 1 - Allow dynamic reloading of the HFIR register during runtime. ++ */ ++ int32_t reload_ctl; ++ ++ /** DCFG: Enable device Out NAK ++ * 0 - The core does not set NAK after Bulk Out transfer complete. ++ * 1 - The core sets NAK after Bulk OUT transfer complete. ++ */ ++ int32_t dev_out_nak; ++ ++ /** DCFG: Enable Continue on BNA ++ * After receiving BNA interrupt the core disables the endpoint,when the ++ * endpoint is re-enabled by the application the core starts processing ++ * 0 - from the DOEPDMA descriptor ++ * 1 - from the descriptor which received the BNA. ++ */ ++ int32_t cont_on_bna; ++ ++ /** GAHBCFG: AHB Single Support ++ * This bit when programmed supports SINGLE transfers for remainder ++ * data in a transfer for DMA mode of operation. ++ * 0 - in this case the remainder data will be sent using INCR burst size. ++ * 1 - in this case the remainder data will be sent using SINGLE burst size. ++ */ ++ int32_t ahb_single; ++ ++ /** Core Power down mode ++ * 0 - No Power Down is enabled ++ * 1 - Reserved ++ * 2 - Complete Power Down (Hibernation) ++ */ ++ int32_t power_down; ++ ++ /** OTG revision supported ++ * 0 - OTG 1.3 revision ++ * 1 - OTG 2.0 revision ++ */ ++ int32_t otg_ver; ++ ++} dwc_otg_core_params_t; ++ ++#ifdef DEBUG ++struct dwc_otg_core_if; ++typedef struct hc_xfer_info { ++ struct dwc_otg_core_if *core_if; ++ dwc_hc_t *hc; ++} hc_xfer_info_t; ++#endif ++ ++typedef struct ep_xfer_info { ++ struct dwc_otg_core_if *core_if; ++ dwc_ep_t *ep; ++ uint8_t state; ++} ep_xfer_info_t; ++/* ++ * Device States ++ */ ++typedef enum dwc_otg_lx_state { ++ /** On state */ ++ DWC_OTG_L0, ++ /** LPM sleep state*/ ++ DWC_OTG_L1, ++ /** USB suspend state*/ ++ DWC_OTG_L2, ++ /** Off state*/ ++ DWC_OTG_L3 ++} dwc_otg_lx_state_e; ++ ++struct dwc_otg_global_regs_backup { ++ uint32_t gotgctl_local; ++ uint32_t gintmsk_local; ++ uint32_t gahbcfg_local; ++ uint32_t gusbcfg_local; ++ uint32_t grxfsiz_local; ++ uint32_t gnptxfsiz_local; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ uint32_t glpmcfg_local; ++#endif ++ uint32_t gi2cctl_local; ++ uint32_t hptxfsiz_local; ++ uint32_t pcgcctl_local; ++ uint32_t gdfifocfg_local; ++ uint32_t dtxfsiz_local[MAX_EPS_CHANNELS]; ++ uint32_t gpwrdn_local; ++ uint32_t xhib_pcgcctl; ++ uint32_t xhib_gpwrdn; ++}; ++ ++struct dwc_otg_host_regs_backup { ++ uint32_t hcfg_local; ++ uint32_t haintmsk_local; ++ uint32_t hcintmsk_local[MAX_EPS_CHANNELS]; ++ uint32_t hprt0_local; ++ uint32_t hfir_local; ++}; ++ ++struct dwc_otg_dev_regs_backup { ++ uint32_t dcfg; ++ uint32_t dctl; ++ uint32_t daintmsk; ++ uint32_t diepmsk; ++ uint32_t doepmsk; ++ uint32_t diepctl[MAX_EPS_CHANNELS]; ++ uint32_t dieptsiz[MAX_EPS_CHANNELS]; ++ uint32_t diepdma[MAX_EPS_CHANNELS]; ++}; ++/** ++ * The <code>dwc_otg_core_if</code> structure contains information needed to manage ++ * the DWC_otg controller acting in either host or device mode. It ++ * represents the programming view of the controller as a whole. ++ */ ++struct dwc_otg_core_if { ++ /** Parameters that define how the core should be configured.*/ ++ dwc_otg_core_params_t *core_params; ++ ++ /** Core Global registers starting at offset 000h. */ ++ dwc_otg_core_global_regs_t *core_global_regs; ++ ++ /** Device-specific information */ ++ dwc_otg_dev_if_t *dev_if; ++ /** Host-specific information */ ++ dwc_otg_host_if_t *host_if; ++ ++ /** Value from SNPSID register */ ++ uint32_t snpsid; ++ ++ /* ++ * Set to 1 if the core PHY interface bits in USBCFG have been ++ * initialized. ++ */ ++ uint8_t phy_init_done; ++ ++ /* ++ * SRP Success flag, set by srp success interrupt in FS I2C mode ++ */ ++ uint8_t srp_success; ++ uint8_t srp_timer_started; ++ /** Timer for SRP. If it expires before SRP is successful ++ * clear the SRP. */ ++ dwc_timer_t *srp_timer; ++ ++#ifdef DWC_DEV_SRPCAP ++ /* This timer is needed to power on the hibernated host core if SRP is not ++ * initiated on connected SRP capable device for limited period of time ++ */ ++ uint8_t pwron_timer_started; ++ dwc_timer_t *pwron_timer; ++#endif ++ /* Common configuration information */ ++ /** Power and Clock Gating Control Register */ ++ volatile uint32_t *pcgcctl; ++#define DWC_OTG_PCGCCTL_OFFSET 0xE00 ++ ++ /** Push/pop addresses for endpoints or host channels.*/ ++ uint32_t *data_fifo[MAX_EPS_CHANNELS]; ++#define DWC_OTG_DATA_FIFO_OFFSET 0x1000 ++#define DWC_OTG_DATA_FIFO_SIZE 0x1000 ++ ++ /** Total RAM for FIFOs (Bytes) */ ++ uint16_t total_fifo_size; ++ /** Size of Rx FIFO (Bytes) */ ++ uint16_t rx_fifo_size; ++ /** Size of Non-periodic Tx FIFO (Bytes) */ ++ uint16_t nperio_tx_fifo_size; ++ ++ /** 1 if DMA is enabled, 0 otherwise. */ ++ uint8_t dma_enable; ++ ++ /** 1 if DMA descriptor is enabled, 0 otherwise. */ ++ uint8_t dma_desc_enable; ++ ++ /** 1 if PTI Enhancement mode is enabled, 0 otherwise. */ ++ uint8_t pti_enh_enable; ++ ++ /** 1 if MPI Enhancement mode is enabled, 0 otherwise. */ ++ uint8_t multiproc_int_enable; ++ ++ /** 1 if dedicated Tx FIFOs are enabled, 0 otherwise. */ ++ uint8_t en_multiple_tx_fifo; ++ ++ /** Set to 1 if multiple packets of a high-bandwidth transfer is in ++ * process of being queued */ ++ uint8_t queuing_high_bandwidth; ++ ++ /** Hardware Configuration -- stored here for convenience.*/ ++ hwcfg1_data_t hwcfg1; ++ hwcfg2_data_t hwcfg2; ++ hwcfg3_data_t hwcfg3; ++ hwcfg4_data_t hwcfg4; ++ fifosize_data_t hptxfsiz; ++ ++ /** Host and Device Configuration -- stored here for convenience.*/ ++ hcfg_data_t hcfg; ++ dcfg_data_t dcfg; ++ ++ /** The operational State, during transations ++ * (a_host>>a_peripherial and b_device=>b_host) this may not ++ * match the core but allows the software to determine ++ * transitions. ++ */ ++ uint8_t op_state; ++ ++ /** Test mode for PET testing */ ++ uint8_t test_mode; ++ ++ /** ++ * Set to 1 if the HCD needs to be restarted on a session request ++ * interrupt. This is required if no connector ID status change has ++ * occurred since the HCD was last disconnected. ++ */ ++ uint8_t restart_hcd_on_session_req; ++ ++ /** HCD callbacks */ ++ /** A-Device is a_host */ ++#define A_HOST (1) ++ /** A-Device is a_suspend */ ++#define A_SUSPEND (2) ++ /** A-Device is a_peripherial */ ++#define A_PERIPHERAL (3) ++ /** B-Device is operating as a Peripheral. */ ++#define B_PERIPHERAL (4) ++ /** B-Device is operating as a Host. */ ++#define B_HOST (5) ++ ++ /** HCD callbacks */ ++ struct dwc_otg_cil_callbacks *hcd_cb; ++ /** PCD callbacks */ ++ struct dwc_otg_cil_callbacks *pcd_cb; ++ ++ /** Device mode Periodic Tx FIFO Mask */ ++ uint32_t p_tx_msk; ++ /** Device mode Periodic Tx FIFO Mask */ ++ uint32_t tx_msk; ++ ++ /** Workqueue object used for handling several interrupts */ ++ dwc_workq_t *wq_otg; ++ ++ /** Timer object used for handling "Wakeup Detected" Interrupt */ ++ dwc_timer_t *wkp_timer; ++ /** This arrays used for debug purposes for DEV OUT NAK enhancement */ ++ uint32_t start_doeptsiz_val[MAX_EPS_CHANNELS]; ++ ep_xfer_info_t ep_xfer_info[MAX_EPS_CHANNELS]; ++ dwc_timer_t *ep_xfer_timer[MAX_EPS_CHANNELS]; ++#ifdef DEBUG ++ uint32_t start_hcchar_val[MAX_EPS_CHANNELS]; ++ ++ hc_xfer_info_t hc_xfer_info[MAX_EPS_CHANNELS]; ++ dwc_timer_t *hc_xfer_timer[MAX_EPS_CHANNELS]; ++ ++ uint32_t hfnum_7_samples; ++ uint64_t hfnum_7_frrem_accum; ++ uint32_t hfnum_0_samples; ++ uint64_t hfnum_0_frrem_accum; ++ uint32_t hfnum_other_samples; ++ uint64_t hfnum_other_frrem_accum; ++#endif ++ ++#ifdef DWC_UTE_CFI ++ uint16_t pwron_rxfsiz; ++ uint16_t pwron_gnptxfsiz; ++ uint16_t pwron_txfsiz[15]; ++ ++ uint16_t init_rxfsiz; ++ uint16_t init_gnptxfsiz; ++ uint16_t init_txfsiz[15]; ++#endif ++ ++ /** Lx state of device */ ++ dwc_otg_lx_state_e lx_state; ++ ++ /** Saved Core Global registers */ ++ struct dwc_otg_global_regs_backup *gr_backup; ++ /** Saved Host registers */ ++ struct dwc_otg_host_regs_backup *hr_backup; ++ /** Saved Device registers */ ++ struct dwc_otg_dev_regs_backup *dr_backup; ++ ++ /** Power Down Enable */ ++ uint32_t power_down; ++ ++ /** ADP support Enable */ ++ uint32_t adp_enable; ++ ++ /** ADP structure object */ ++ dwc_otg_adp_t adp; ++ ++ /** hibernation/suspend flag */ ++ int hibernation_suspend; ++ ++ /** Device mode extended hibernation flag */ ++ int xhib; ++ ++ /** OTG revision supported */ ++ uint32_t otg_ver; ++ ++ /** OTG status flag used for HNP polling */ ++ uint8_t otg_sts; ++ ++ /** Pointer to either hcd->lock or pcd->lock */ ++ dwc_spinlock_t *lock; ++ ++ /** Start predict NextEP based on Learning Queue if equal 1, ++ * also used as counter of disabled NP IN EP's */ ++ uint8_t start_predict; ++ ++ /** NextEp sequence, including EP0: nextep_seq[] = EP if non-periodic and ++ * active, 0xff otherwise */ ++ uint8_t nextep_seq[MAX_EPS_CHANNELS]; ++ ++ /** Index of fisrt EP in nextep_seq array which should be re-enabled **/ ++ uint8_t first_in_nextep_seq; ++ ++ /** Frame number while entering to ISR - needed for ISOCs **/ ++ uint32_t frame_num; ++ ++ /** Flag to not perform ADP probing if IDSTS event happened */ ++ uint8_t stop_adpprb; ++ ++}; ++ ++#ifdef DEBUG ++/* ++ * This function is called when transfer is timed out. ++ */ ++extern void hc_xfer_timeout(void *ptr); ++#endif ++ ++/* ++ * This function is called when transfer is timed out on endpoint. ++ */ ++extern void ep_xfer_timeout(void *ptr); ++ ++/* ++ * The following functions are functions for works ++ * using during handling some interrupts ++ */ ++extern void w_conn_id_status_change(void *p); ++ ++extern void w_wakeup_detected(void *p); ++ ++/** Saves global register values into system memory. */ ++extern int dwc_otg_save_global_regs(dwc_otg_core_if_t * core_if); ++/** Saves device register values into system memory. */ ++extern int dwc_otg_save_dev_regs(dwc_otg_core_if_t * core_if); ++/** Saves host register values into system memory. */ ++extern int dwc_otg_save_host_regs(dwc_otg_core_if_t * core_if); ++/** Restore global register values. */ ++extern int dwc_otg_restore_global_regs(dwc_otg_core_if_t * core_if); ++/** Restore host register values. */ ++extern int dwc_otg_restore_host_regs(dwc_otg_core_if_t * core_if, int reset); ++/** Restore device register values. */ ++extern int dwc_otg_restore_dev_regs(dwc_otg_core_if_t * core_if, ++ int rem_wakeup); ++extern int restore_lpm_i2c_regs(dwc_otg_core_if_t * core_if); ++extern int restore_essential_regs(dwc_otg_core_if_t * core_if, int rmode, ++ int is_host); ++ ++extern int dwc_otg_host_hibernation_restore(dwc_otg_core_if_t * core_if, ++ int restore_mode, int reset); ++extern int dwc_otg_device_hibernation_restore(dwc_otg_core_if_t * core_if, ++ int rem_wakeup, int reset); ++ ++/* ++ * The following functions support initialization of the CIL driver component ++ * and the DWC_otg controller. ++ */ ++extern void dwc_otg_core_host_init(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_core_dev_init(dwc_otg_core_if_t * _core_if); ++ ++/** @name Device CIL Functions ++ * The following functions support managing the DWC_otg controller in device ++ * mode. ++ */ ++/**@{*/ ++extern void dwc_otg_wakeup(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_read_setup_packet(dwc_otg_core_if_t * _core_if, ++ uint32_t * _dest); ++extern uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_ep0_activate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep); ++extern void dwc_otg_ep_activate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep); ++extern void dwc_otg_ep_deactivate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep); ++extern void dwc_otg_ep_start_transfer(dwc_otg_core_if_t * _core_if, ++ dwc_ep_t * _ep); ++extern void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t * _core_if, ++ dwc_ep_t * _ep); ++extern void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t * _core_if, ++ dwc_ep_t * _ep); ++extern void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t * _core_if, ++ dwc_ep_t * _ep); ++extern void dwc_otg_ep_write_packet(dwc_otg_core_if_t * _core_if, ++ dwc_ep_t * _ep, int _dma); ++extern void dwc_otg_ep_set_stall(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep); ++extern void dwc_otg_ep_clear_stall(dwc_otg_core_if_t * _core_if, ++ dwc_ep_t * _ep); ++extern void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t * _core_if); ++ ++#ifdef DWC_EN_ISOC ++extern void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * ep); ++extern void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * ep); ++#endif /* DWC_EN_ISOC */ ++/**@}*/ ++ ++/** @name Host CIL Functions ++ * The following functions support managing the DWC_otg controller in host ++ * mode. ++ */ ++/**@{*/ ++extern void dwc_otg_hc_init(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc); ++extern void dwc_otg_hc_halt(dwc_otg_core_if_t * _core_if, ++ dwc_hc_t * _hc, dwc_otg_halt_status_e _halt_status); ++extern void dwc_otg_hc_cleanup(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc); ++extern void dwc_otg_hc_start_transfer(dwc_otg_core_if_t * _core_if, ++ dwc_hc_t * _hc); ++extern int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t * _core_if, ++ dwc_hc_t * _hc); ++extern void dwc_otg_hc_do_ping(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc); ++extern void dwc_otg_hc_write_packet(dwc_otg_core_if_t * _core_if, ++ dwc_hc_t * _hc); ++extern void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t * _core_if); ++ ++extern void dwc_otg_hc_start_transfer_ddma(dwc_otg_core_if_t * core_if, ++ dwc_hc_t * hc); ++ ++extern uint32_t calc_frame_interval(dwc_otg_core_if_t * core_if); ++extern int dwc_otg_check_haps_status(dwc_otg_core_if_t * core_if); ++ ++/* Macro used to clear one channel interrupt */ ++#define clear_hc_int(_hc_regs_, _intr_) \ ++do { \ ++ hcint_data_t hcint_clear = {.d32 = 0}; \ ++ hcint_clear.b._intr_ = 1; \ ++ DWC_WRITE_REG32(&(_hc_regs_)->hcint, hcint_clear.d32); \ ++} while (0) ++ ++/* ++ * Macro used to disable one channel interrupt. Channel interrupts are ++ * disabled when the channel is halted or released by the interrupt handler. ++ * There is no need to handle further interrupts of that type until the ++ * channel is re-assigned. In fact, subsequent handling may cause crashes ++ * because the channel structures are cleaned up when the channel is released. ++ */ ++#define disable_hc_int(_hc_regs_, _intr_) \ ++do { \ ++ hcintmsk_data_t hcintmsk = {.d32 = 0}; \ ++ hcintmsk.b._intr_ = 1; \ ++ DWC_MODIFY_REG32(&(_hc_regs_)->hcintmsk, hcintmsk.d32, 0); \ ++} while (0) ++ ++/** ++ * This function Reads HPRT0 in preparation to modify. It keeps the ++ * WC bits 0 so that if they are read as 1, they won't clear when you ++ * write it back ++ */ ++static inline uint32_t dwc_otg_read_hprt0(dwc_otg_core_if_t * _core_if) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = DWC_READ_REG32(_core_if->host_if->hprt0); ++ hprt0.b.prtena = 0; ++ hprt0.b.prtconndet = 0; ++ hprt0.b.prtenchng = 0; ++ hprt0.b.prtovrcurrchng = 0; ++ return hprt0.d32; ++} ++ ++/**@}*/ ++ ++/** @name Common CIL Functions ++ * The following functions support managing the DWC_otg controller in either ++ * device or host mode. ++ */ ++/**@{*/ ++ ++extern void dwc_otg_read_packet(dwc_otg_core_if_t * core_if, ++ uint8_t * dest, uint16_t bytes); ++ ++extern void dwc_otg_flush_tx_fifo(dwc_otg_core_if_t * _core_if, const int _num); ++extern void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_core_reset(dwc_otg_core_if_t * _core_if); ++ ++/** ++ * This function returns the Core Interrupt register. ++ */ ++static inline uint32_t dwc_otg_read_core_intr(dwc_otg_core_if_t * core_if) ++{ ++ return (DWC_READ_REG32(&core_if->core_global_regs->gintsts) & ++ DWC_READ_REG32(&core_if->core_global_regs->gintmsk)); ++} ++ ++/** ++ * This function returns the OTG Interrupt register. ++ */ ++static inline uint32_t dwc_otg_read_otg_intr(dwc_otg_core_if_t * core_if) ++{ ++ return (DWC_READ_REG32(&core_if->core_global_regs->gotgint)); ++} ++ ++/** ++ * This function reads the Device All Endpoints Interrupt register and ++ * returns the IN endpoint interrupt bits. ++ */ ++static inline uint32_t dwc_otg_read_dev_all_in_ep_intr(dwc_otg_core_if_t * ++ core_if) ++{ ++ ++ uint32_t v; ++ ++ if (core_if->multiproc_int_enable) { ++ v = DWC_READ_REG32(&core_if->dev_if-> ++ dev_global_regs->deachint) & ++ DWC_READ_REG32(&core_if-> ++ dev_if->dev_global_regs->deachintmsk); ++ } else { ++ v = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daint) & ++ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daintmsk); ++ } ++ return (v & 0xffff); ++} ++ ++/** ++ * This function reads the Device All Endpoints Interrupt register and ++ * returns the OUT endpoint interrupt bits. ++ */ ++static inline uint32_t dwc_otg_read_dev_all_out_ep_intr(dwc_otg_core_if_t * ++ core_if) ++{ ++ uint32_t v; ++ ++ if (core_if->multiproc_int_enable) { ++ v = DWC_READ_REG32(&core_if->dev_if-> ++ dev_global_regs->deachint) & ++ DWC_READ_REG32(&core_if-> ++ dev_if->dev_global_regs->deachintmsk); ++ } else { ++ v = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daint) & ++ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daintmsk); ++ } ++ ++ return ((v & 0xffff0000) >> 16); ++} ++ ++/** ++ * This function returns the Device IN EP Interrupt register ++ */ ++static inline uint32_t dwc_otg_read_dev_in_ep_intr(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * ep) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ uint32_t v, msk, emp; ++ ++ if (core_if->multiproc_int_enable) { ++ msk = ++ DWC_READ_REG32(&dev_if-> ++ dev_global_regs->diepeachintmsk[ep->num]); ++ emp = ++ DWC_READ_REG32(&dev_if-> ++ dev_global_regs->dtknqr4_fifoemptymsk); ++ msk |= ((emp >> ep->num) & 0x1) << 7; ++ v = DWC_READ_REG32(&dev_if->in_ep_regs[ep->num]->diepint) & msk; ++ } else { ++ msk = DWC_READ_REG32(&dev_if->dev_global_regs->diepmsk); ++ emp = ++ DWC_READ_REG32(&dev_if-> ++ dev_global_regs->dtknqr4_fifoemptymsk); ++ msk |= ((emp >> ep->num) & 0x1) << 7; ++ v = DWC_READ_REG32(&dev_if->in_ep_regs[ep->num]->diepint) & msk; ++ } ++ ++ return v; ++} ++ ++/** ++ * This function returns the Device OUT EP Interrupt register ++ */ ++static inline uint32_t dwc_otg_read_dev_out_ep_intr(dwc_otg_core_if_t * ++ _core_if, dwc_ep_t * _ep) ++{ ++ dwc_otg_dev_if_t *dev_if = _core_if->dev_if; ++ uint32_t v; ++ doepmsk_data_t msk = {.d32 = 0 }; ++ ++ if (_core_if->multiproc_int_enable) { ++ msk.d32 = ++ DWC_READ_REG32(&dev_if-> ++ dev_global_regs->doepeachintmsk[_ep->num]); ++ if (_core_if->pti_enh_enable) { ++ msk.b.pktdrpsts = 1; ++ } ++ v = DWC_READ_REG32(&dev_if-> ++ out_ep_regs[_ep->num]->doepint) & msk.d32; ++ } else { ++ msk.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->doepmsk); ++ if (_core_if->pti_enh_enable) { ++ msk.b.pktdrpsts = 1; ++ } ++ v = DWC_READ_REG32(&dev_if-> ++ out_ep_regs[_ep->num]->doepint) & msk.d32; ++ } ++ return v; ++} ++ ++/** ++ * This function returns the Host All Channel Interrupt register ++ */ ++static inline uint32_t dwc_otg_read_host_all_channels_intr(dwc_otg_core_if_t * ++ _core_if) ++{ ++ return (DWC_READ_REG32(&_core_if->host_if->host_global_regs->haint)); ++} ++ ++static inline uint32_t dwc_otg_read_host_channel_intr(dwc_otg_core_if_t * ++ _core_if, dwc_hc_t * _hc) ++{ ++ return (DWC_READ_REG32 ++ (&_core_if->host_if->hc_regs[_hc->hc_num]->hcint)); ++} ++ ++/** ++ * This function returns the mode of the operation, host or device. ++ * ++ * @return 0 - Device Mode, 1 - Host Mode ++ */ ++static inline uint32_t dwc_otg_mode(dwc_otg_core_if_t * _core_if) ++{ ++ return (DWC_READ_REG32(&_core_if->core_global_regs->gintsts) & 0x1); ++} ++ ++/**@}*/ ++ ++/** ++ * DWC_otg CIL callback structure. This structure allows the HCD and ++ * PCD to register functions used for starting and stopping the PCD ++ * and HCD for role change on for a DRD. ++ */ ++typedef struct dwc_otg_cil_callbacks { ++ /** Start function for role change */ ++ int (*start) (void *_p); ++ /** Stop Function for role change */ ++ int (*stop) (void *_p); ++ /** Disconnect Function for role change */ ++ int (*disconnect) (void *_p); ++ /** Resume/Remote wakeup Function */ ++ int (*resume_wakeup) (void *_p); ++ /** Suspend function */ ++ int (*suspend) (void *_p); ++ /** Session Start (SRP) */ ++ int (*session_start) (void *_p); ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ /** Sleep (switch to L0 state) */ ++ int (*sleep) (void *_p); ++#endif ++ /** Pointer passed to start() and stop() */ ++ void *p; ++} dwc_otg_cil_callbacks_t; ++ ++extern void dwc_otg_cil_register_pcd_callbacks(dwc_otg_core_if_t * _core_if, ++ dwc_otg_cil_callbacks_t * _cb, ++ void *_p); ++extern void dwc_otg_cil_register_hcd_callbacks(dwc_otg_core_if_t * _core_if, ++ dwc_otg_cil_callbacks_t * _cb, ++ void *_p); ++ ++void dwc_otg_initiate_srp(void * core_if); ++ ++////////////////////////////////////////////////////////////////////// ++/** Start the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void cil_hcd_start(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->start) { ++ core_if->hcd_cb->start(core_if->hcd_cb->p); ++ } ++} ++ ++/** Stop the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void cil_hcd_stop(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->stop) { ++ core_if->hcd_cb->stop(core_if->hcd_cb->p); ++ } ++} ++ ++/** Disconnect the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void cil_hcd_disconnect(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->disconnect) { ++ core_if->hcd_cb->disconnect(core_if->hcd_cb->p); ++ } ++} ++ ++/** Inform the HCD the a New Session has begun. Helper function for ++ * using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void cil_hcd_session_start(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->session_start) { ++ core_if->hcd_cb->session_start(core_if->hcd_cb->p); ++ } ++} ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++/** ++ * Inform the HCD about LPM sleep. ++ * Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void cil_hcd_sleep(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->sleep) { ++ core_if->hcd_cb->sleep(core_if->hcd_cb->p); ++ } ++} ++#endif ++ ++/** Resume the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void cil_hcd_resume(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->resume_wakeup) { ++ core_if->hcd_cb->resume_wakeup(core_if->hcd_cb->p); ++ } ++} ++ ++/** Start the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void cil_pcd_start(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->pcd_cb && core_if->pcd_cb->start) { ++ core_if->pcd_cb->start(core_if->pcd_cb->p); ++ } ++} ++ ++/** Stop the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void cil_pcd_stop(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->pcd_cb && core_if->pcd_cb->stop) { ++ core_if->pcd_cb->stop(core_if->pcd_cb->p); ++ } ++} ++ ++/** Suspend the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void cil_pcd_suspend(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->pcd_cb && core_if->pcd_cb->suspend) { ++ core_if->pcd_cb->suspend(core_if->pcd_cb->p); ++ } ++} ++ ++/** Resume the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void cil_pcd_resume(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { ++ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); ++ } ++} ++ ++////////////////////////////////////////////////////////////////////// ++ ++#endif +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_cil_intr.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_cil_intr.c +new file mode 100644 +index 0000000..46c4692 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_cil_intr.c +@@ -0,0 +1,1738 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil_intr.c $ ++ * $Revision: #37 $ ++ * $Date: 2013/04/16 $ ++ * $Change: 2207267 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The Core Interface Layer provides basic services for accessing and ++ * managing the DWC_otg hardware. These services are used by both the ++ * Host Controller Driver and the Peripheral Controller Driver. ++ * ++ * This file contains the Common Interrupt handlers. ++ */ ++#include "dwc_os.h" ++#include "dwc_otg_regs.h" ++#include "dwc_otg_cil.h" ++#include "dwc_otg_driver.h" ++#include "dwc_otg_pcd.h" ++#include "dwc_otg_hcd.h" ++ ++#ifdef DEBUG ++inline const char *op_state_str(dwc_otg_core_if_t * core_if) ++{ ++ return (core_if->op_state == A_HOST ? "a_host" : ++ (core_if->op_state == A_SUSPEND ? "a_suspend" : ++ (core_if->op_state == A_PERIPHERAL ? "a_peripheral" : ++ (core_if->op_state == B_PERIPHERAL ? "b_peripheral" : ++ (core_if->op_state == B_HOST ? "b_host" : "unknown"))))); ++} ++#endif ++ ++/** This function will log a debug message ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_mode_mismatch_intr(dwc_otg_core_if_t * core_if) ++{ ++ gintsts_data_t gintsts; ++ DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n", ++ dwc_otg_mode(core_if) ? "Host" : "Device"); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.modemismatch = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++ ++/** ++ * This function handles the OTG Interrupts. It reads the OTG ++ * Interrupt Register (GOTGINT) to determine what interrupt has ++ * occurred. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ gotgint_data_t gotgint; ++ gotgctl_data_t gotgctl; ++ gintmsk_data_t gintmsk; ++ gpwrdn_data_t gpwrdn; ++ ++ gotgint.d32 = DWC_READ_REG32(&global_regs->gotgint); ++ gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl); ++ DWC_DEBUGPL(DBG_CIL, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32, ++ op_state_str(core_if)); ++ ++ if (gotgint.b.sesenddet) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "Session End Detected++ (%s)\n", ++ op_state_str(core_if)); ++ gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl); ++ ++ if (core_if->op_state == B_HOST) { ++ if (core_if->adp_enable && DWC_WORKQ_PENDING(core_if->wq_otg)) { ++ ++ /* During ST_B_ADP test after HNP HSOTG tries to go to B_HOST ++ * mode but PET is not expecting fully functional host at that ++ * point and switches off the VBUS expecting immediate ADP probe */ ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ dwc_mdelay(20); ++ dwc_otg_adp_probe_start(core_if); ++ goto exit_interrupt; ++ } ++ cil_pcd_start(core_if); ++ core_if->op_state = B_PERIPHERAL; ++ } else { ++ /* If not B_HOST and Device HNP still set. HNP ++ * Did not succeed!*/ ++ if (gotgctl.b.devhnpen) { ++ DWC_DEBUGPL(DBG_ANY, "Session End Detected\n"); ++ __DWC_ERROR("Device Not Connected/Responding!\n"); ++ } ++ ++ /* If Session End Detected the B-Cable has ++ * been disconnected. */ ++ /* Reset PCD and Gadget driver to a ++ * clean state. */ ++ core_if->lx_state = DWC_OTG_L0; ++ DWC_SPINUNLOCK(core_if->lock); ++ cil_pcd_stop(core_if); ++ DWC_SPINLOCK(core_if->lock); ++ ++ if (core_if->otg_ver) { ++ /** PET testing*/ ++ gotgctl.d32 = 0; ++ gotgctl.b.devhnpen = 1; ++ DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0); ++ if (core_if->test_mode == 6) { ++ DWC_WORKQ_SCHEDULE_DELAYED(core_if->wq_otg, dwc_otg_initiate_srp, ++ core_if, 3000, "initate SRP"); //manukz: old value was 50 ++ core_if->test_mode = 0; ++ } else if (core_if->adp_enable) { ++ if (core_if->power_down == 2) { ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if-> ++ core_global_regs-> ++ gpwrdn, gpwrdn.d32, 0); ++ } ++ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ dwc_otg_adp_sense_start(core_if); ++ } ++ } ++ } ++exit_interrupt: ++ if (core_if->otg_ver == 0) { ++ gotgctl.d32 = 0; ++ gotgctl.b.devhnpen = 1; ++ DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0); ++ } ++ } ++ if (gotgint.b.sesreqsucstschng) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "Session Reqeust Success Status Change++\n"); ++ gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl); ++ if (gotgctl.b.sesreqscs) { ++ ++ if ((core_if->core_params->phy_type == ++ DWC_PHY_TYPE_PARAM_FS) && (core_if->core_params->i2c_enable)) { ++ core_if->srp_success = 1; ++ } else { ++ DWC_SPINUNLOCK(core_if->lock); ++ cil_pcd_resume(core_if); ++ DWC_SPINLOCK(core_if->lock); ++ /* Clear Session Request */ ++ gotgctl.d32 = 0; ++ gotgctl.b.sesreq = 1; ++ DWC_MODIFY_REG32(&global_regs->gotgctl, ++ gotgctl.d32, 0); ++ } ++ } ++ } ++ if (gotgint.b.hstnegsucstschng) { ++ /* Print statements during the HNP interrupt handling ++ * can cause it to fail.*/ ++ gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl); ++ /* WA for 3.00a- HW is not setting cur_mode, even sometimes ++ * this does not help*/ ++ if (core_if->snpsid >= OTG_CORE_REV_3_00a) ++ dwc_udelay(100); ++ if (gotgctl.b.hstnegscs) { ++ if (dwc_otg_is_host_mode(core_if)) { ++ core_if->op_state = B_HOST; ++ /* ++ * Need to disable SOF interrupt immediately. ++ * When switching from device to host, the PCD ++ * interrupt handler won't handle the ++ * interrupt if host mode is already set. The ++ * HCD interrupt handler won't get called if ++ * the HCD state is HALT. This means that the ++ * interrupt does not get handled and Linux ++ * complains loudly. ++ */ ++ gintmsk.d32 = 0; ++ gintmsk.b.sofintr = 1; ++ /* To avoid multiple USB Suspend interrupts during ++ * OTG 2.0 role change */ ++ if (core_if->otg_ver) ++ gintmsk.b.usbsuspend = 1; ++ DWC_MODIFY_REG32(&global_regs->gintmsk, ++ gintmsk.d32, 0); ++ /* Call callback function with spin lock released */ ++ DWC_SPINUNLOCK(core_if->lock); ++ cil_pcd_stop(core_if); ++ /* ++ * Initialize the Core for Host mode. ++ */ ++ if (core_if->otg_ver) { ++ dwc_mdelay(100); ++ cil_hcd_start(core_if); ++ cil_hcd_session_start(core_if); ++ } else { ++ cil_hcd_start(core_if); ++ } ++ DWC_SPINLOCK(core_if->lock); ++ } ++ } else { ++ gotgctl.d32 = 0; ++ gotgctl.b.hnpreq = 1; ++ gotgctl.b.devhnpen = 1; ++ DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0); ++ DWC_DEBUGPL(DBG_ANY, "HNP Failed\n"); ++ __DWC_ERROR("Device Not Connected/Responding\n"); ++ } ++ } ++ if (gotgint.b.hstnegdet) { ++ /* The disconnect interrupt is set at the same time as ++ * Host Negotiation Detected. During the mode ++ * switch all interrupts are cleared so the disconnect ++ * interrupt handler will not get executed. ++ */ ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "Host Negotiation Detected++ (%s)\n", ++ (dwc_otg_is_host_mode(core_if) ? "Host" : ++ "Device")); ++ if (dwc_otg_is_device_mode(core_if)) { ++ DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n", ++ core_if->op_state); ++ DWC_SPINUNLOCK(core_if->lock); ++ cil_hcd_disconnect(core_if); ++ cil_pcd_start(core_if); ++ DWC_SPINLOCK(core_if->lock); ++ core_if->op_state = A_PERIPHERAL; ++ } else { ++ /* ++ * Need to disable SOF interrupt immediately. When ++ * switching from device to host, the PCD interrupt ++ * handler won't handle the interrupt if host mode is ++ * already set. The HCD interrupt handler won't get ++ * called if the HCD state is HALT. This means that ++ * the interrupt does not get handled and Linux ++ * complains loudly. ++ */ ++ gintmsk.d32 = 0; ++ gintmsk.b.sofintr = 1; ++ DWC_MODIFY_REG32(&global_regs->gintmsk, gintmsk.d32, 0); ++ DWC_SPINUNLOCK(core_if->lock); ++ cil_pcd_stop(core_if); ++ cil_hcd_start(core_if); ++ DWC_SPINLOCK(core_if->lock); ++ core_if->op_state = A_HOST; ++ } ++ } ++ if (gotgint.b.adevtoutchng) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "A-Device Timeout Change++\n"); ++ } ++ if (gotgint.b.debdone) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " "Debounce Done++\n"); ++ /* Need to power off VBUS after 10s if OTG2 non-hnp capable host*/ ++ if (core_if->otg_ver && core_if->op_state == A_PERIPHERAL) { ++ DWC_DEBUGPL(DBG_ANY, "a_peripheral->a_host\n"); ++ /* Clear the a_peripheral flag, back to a_host. */ ++ DWC_SPINUNLOCK(core_if->lock); ++ cil_pcd_stop(core_if); ++ cil_hcd_start(core_if); ++ DWC_SPINLOCK(core_if->lock); ++ core_if->op_state = A_HOST; ++ } ++ ++ if(core_if->otg_ver == 1) ++ cil_hcd_session_start(core_if); ++ } ++ ++ /* Clear GOTGINT */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gotgint, gotgint.d32); ++ ++ return 1; ++} ++ ++void w_conn_id_status_change(void *p) ++{ ++ dwc_otg_core_if_t *core_if = p; ++ uint32_t count = 0; ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ ++ gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl); ++ DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32); ++ DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts); ++ ++ /* B-Device connector (Device Mode) */ ++ if (gotgctl.b.conidsts) { ++ gotgctl_data_t gotgctl_local; ++ /* Wait for switch to device mode. */ ++ while (!dwc_otg_is_device_mode(core_if)) { ++ gotgctl_local.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl); ++ DWC_DEBUGPL(DBG_ANY, "Waiting for Peripheral Mode, Mode=%s count = %d gotgctl=%08x\n", ++ (dwc_otg_is_host_mode(core_if) ? "Host" : ++ "Peripheral"), count, gotgctl_local.d32); ++ dwc_mdelay(1); //vahrama previous value was 100 ++ if(!gotgctl_local.b.conidsts) ++ goto host; ++ if (++count > 10000) ++ break; ++ } ++ DWC_ASSERT(++count < 10000, ++ "Connection id status change timed out"); ++ core_if->op_state = B_PERIPHERAL; ++ if(core_if->otg_ver == 0) ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_pcd_start(core_if); ++ } else { ++host: ++ /* A-Device connector (Host Mode) */ ++ while (!dwc_otg_is_host_mode(core_if)) { ++ DWC_DEBUGPL(DBG_ANY,"Waiting for Host Mode, Mode=%s\n", ++ (dwc_otg_is_host_mode(core_if) ? "Host" : ++ "Peripheral")); ++ dwc_mdelay(1); //vahrama previously was 100 ++ if (++count > 10000) ++ break; ++ } ++ DWC_ASSERT(++count < 10000, ++ "Connection id status change timed out"); ++ core_if->op_state = A_HOST; ++ /* ++ * Initialize the Core for Host mode. ++ */ ++ if (core_if->otg_ver) ++ /* To power off the bus in 10s from the beginning ++ * of test while denounce has not come yet */ ++ cil_hcd_session_start(core_if); ++ else ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_hcd_start(core_if); ++ } ++} ++ ++/** ++ * This function handles the Connector ID Status Change Interrupt. It ++ * reads the OTG Interrupt Register (GOTCTL) to determine whether this ++ * is a Device to Host Mode transition or a Host Mode to Device ++ * Transition. ++ * ++ * This only occurs when the cable is connected/removed from the PHY ++ * connector. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t * core_if) ++{ ++ ++ /* ++ * Need to disable SOF interrupt immediately. If switching from device ++ * to host, the PCD interrupt handler won't handle the interrupt if ++ * host mode is already set. The HCD interrupt handler won't get ++ * called if the HCD state is HALT. This means that the interrupt does ++ * not get handled and Linux complains loudly. ++ */ ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ gintsts_data_t gintsts = {.d32 = 0 }; ++ ++ gintmsk.b.sofintr = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0); ++ ++ DWC_DEBUGPL(DBG_CIL, ++ " ++Connector ID Status Change Interrupt++ (%s)\n", ++ (dwc_otg_is_host_mode(core_if) ? "Host" : "Device")); ++ ++ DWC_SPINUNLOCK(core_if->lock); ++ ++ /* Needed to avoit conn_id_status change duplication */ ++ //if (core_if->otg_ver) ++ //dwc_mdelay(50); ++ /* ++ * Need to schedule a work, as there are possible DELAY function calls ++ * Release lock before scheduling workq as it holds spinlock during scheduling ++ */ ++ ++ DWC_WORKQ_SCHEDULE(core_if->wq_otg, w_conn_id_status_change, ++ core_if, "connection id status change"); ++ DWC_SPINLOCK(core_if->lock); ++ ++ /* Set flag and clear interrupt */ ++ gintsts.b.conidstschng = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that a device is initiating the Session ++ * Request Protocol to request the host to turn on bus power so a new ++ * session can begin. The handler responds by turning on bus power. If ++ * the DWC_otg controller is in low power mode, the handler brings the ++ * controller out of low power mode before turning on bus power. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t * core_if) ++{ ++ gintsts_data_t gintsts; ++ ++#ifndef DWC_HOST_ONLY ++ DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n"); ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ DWC_DEBUGPL(DBG_PCD, "SRP: Device mode\n"); ++ gotgctl.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->gotgctl); ++ if (gotgctl.b.sesreqscs) ++ DWC_PRINTF("SRP Success\n"); ++ else ++ DWC_PRINTF("SRP Fail\n"); ++ if (core_if->otg_ver) { ++ gotgctl.d32 = 0 ; ++ gotgctl.b.devhnpen = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gotgctl, gotgctl.d32, 0); ++ } ++ } else { ++ hprt0_data_t hprt0; ++ DWC_PRINTF("SRP: Host mode\n"); ++ ++ /* Turn on the port power bit. */ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtpwr = 1; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ ++ /* Start the Connection timer. So a message can be displayed ++ * if connect does not occur within 10 seconds. */ ++ cil_hcd_session_start(core_if); ++ } ++#endif ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.sessreqintr = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++void w_wakeup_detected(void *p) ++{ ++ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) p; ++ /* ++ * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms ++ * so that OPT tests pass with all PHYs). ++ */ ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++#if 0 ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ /* Restart the Phy Clock */ ++ pcgcctl.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0); ++ dwc_udelay(10); ++#endif //0 ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ DWC_DEBUGPL(DBG_ANY, "Resume: HPRT0=%0x\n", hprt0.d32); ++// dwc_mdelay(70); ++ hprt0.b.prtres = 0; /* Resume */ ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ DWC_DEBUGPL(DBG_ANY, "Clear Resume: HPRT0=%0x\n", ++ DWC_READ_REG32(core_if->host_if->hprt0)); ++ ++ cil_hcd_resume(core_if); ++ ++ /** Change to L0 state*/ ++ core_if->lx_state = DWC_OTG_L0; ++} ++ ++/** ++ * This interrupt indicates that the DWC_otg controller has detected a ++ * resume or remote wakeup sequence. If the DWC_otg controller is in ++ * low power mode, the handler must brings the controller out of low ++ * power mode. The controller automatically begins resume ++ * signaling. The handler schedules a time to stop resume signaling. ++ */ ++int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t * core_if) ++{ ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_ANY, ++ "++Resume and Remote Wakeup Detected Interrupt++\n"); ++ ++ DWC_PRINTF("%s lxstate = %d\n", __func__, core_if->lx_state); ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ dctl_data_t dctl = {.d32 = 0 }; ++ DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", ++ DWC_READ_REG32(&core_if->dev_if->dev_global_regs-> ++ dsts)); ++ if (core_if->lx_state == DWC_OTG_L2) { ++#ifdef PARTIAL_POWER_DOWN ++ if (core_if->hwcfg4.b.power_optimiz) { ++ pcgcctl_data_t power = {.d32 = 0 }; ++ ++ power.d32 = DWC_READ_REG32(core_if->pcgcctl); ++ DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n", ++ power.d32); ++ ++ power.b.stoppclk = 0; ++ DWC_WRITE_REG32(core_if->pcgcctl, power.d32); ++ ++ power.b.pwrclmp = 0; ++ DWC_WRITE_REG32(core_if->pcgcctl, power.d32); ++ ++ power.b.rstpdwnmodule = 0; ++ DWC_WRITE_REG32(core_if->pcgcctl, power.d32); ++ } ++#endif ++ /* Clear the Remote Wakeup Signaling */ ++ dctl.b.rmtwkupsig = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs-> ++ dctl, dctl.d32, 0); ++ ++ DWC_SPINUNLOCK(core_if->lock); ++ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { ++ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); ++ } ++ DWC_SPINLOCK(core_if->lock); ++ } else { ++ glpmcfg_data_t lpmcfg; ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ ++ lpmcfg.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ lpmcfg.b.hird_thres &= (~(1 << 4)); ++ lpmcfg.b.en_utmi_sleep = 0; ++ ++ /* Clear Enbl_L1Gating bit. */ ++ pcgcctl.b.enbl_sleep_gating = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32,0); ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, ++ lpmcfg.d32); ++ } ++ /** Change to L0 state*/ ++ core_if->lx_state = DWC_OTG_L0; ++ } else { ++ if (core_if->lx_state != DWC_OTG_L1) { ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ ++ /* Restart the Phy Clock */ ++ pcgcctl.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0); ++ DWC_TIMER_SCHEDULE(core_if->wkp_timer, 71); ++ } else { ++ /** Change to L0 state*/ ++ core_if->lx_state = DWC_OTG_L0; ++ } ++ } ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.wkupintr = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that the Wakeup Logic has detected a ++ * Device disconnect. ++ */ ++static int32_t dwc_otg_handle_pwrdn_disconnect_intr(dwc_otg_core_if_t * core_if) ++{ ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ gpwrdn_data_t gpwrdn_temp = {.d32 = 0 }; ++ gpwrdn_temp.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ ++ DWC_PRINTF("%s called\n", __FUNCTION__); ++ ++ if (!core_if->hibernation_suspend) { ++ DWC_PRINTF("Already exited from Hibernation\n"); ++ return 1; ++ } ++ ++ /* Switch on the voltage to the core */ ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Reset the core */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Disable power clamps */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnclmp = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /* Remove reset the core signal */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Disable PMU interrupt */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ core_if->hibernation_suspend = 0; ++ ++ /* Disable PMU */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ if (gpwrdn_temp.b.idsts) { ++ core_if->op_state = B_PERIPHERAL; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_pcd_start(core_if); ++ } else { ++ core_if->op_state = A_HOST; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_hcd_start(core_if); ++ } ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that the Wakeup Logic has detected a ++ * remote wakeup sequence. ++ */ ++static int32_t dwc_otg_handle_pwrdn_wakeup_detected_intr(dwc_otg_core_if_t * core_if) ++{ ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ DWC_DEBUGPL(DBG_ANY, ++ "++Powerdown Remote Wakeup Detected Interrupt++\n"); ++ ++ if (!core_if->hibernation_suspend) { ++ DWC_PRINTF("Already exited from Hibernation\n"); ++ return 1; ++ } ++ ++ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ if (gpwrdn.b.idsts) { // Device Mode ++ if ((core_if->power_down == 2) ++ && (core_if->hibernation_suspend == 1)) { ++ dwc_otg_device_hibernation_restore(core_if, 0, 0); ++ } ++ } else { ++ if ((core_if->power_down == 2) ++ && (core_if->hibernation_suspend == 1)) { ++ dwc_otg_host_hibernation_restore(core_if, 1, 0); ++ } ++ } ++ return 1; ++} ++ ++static int32_t dwc_otg_handle_pwrdn_idsts_change(dwc_otg_device_t * otg_dev) ++{ ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ gpwrdn_data_t gpwrdn_temp = {.d32 = 0 }; ++ dwc_otg_core_if_t *core_if = otg_dev->core_if; ++ ++ DWC_DEBUGPL(DBG_ANY, "%s called\n", __FUNCTION__); ++ gpwrdn_temp.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ if (core_if->power_down == 2) { ++ if (!core_if->hibernation_suspend) { ++ DWC_PRINTF("Already exited from Hibernation\n"); ++ return 1; ++ } ++ DWC_DEBUGPL(DBG_ANY, "Exit from hibernation on ID sts change\n"); ++ /* Switch on the voltage to the core */ ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Reset the core */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Disable power clamps */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnclmp = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /* Remove reset the core signal */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Disable PMU interrupt */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /*Indicates that we are exiting from hibernation */ ++ core_if->hibernation_suspend = 0; ++ ++ /* Disable PMU */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ gpwrdn.d32 = core_if->gr_backup->gpwrdn_local; ++ if (gpwrdn.b.dis_vbus == 1) { ++ gpwrdn.d32 = 0; ++ gpwrdn.b.dis_vbus = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ } ++ ++ if (gpwrdn_temp.b.idsts) { ++ core_if->op_state = B_PERIPHERAL; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_pcd_start(core_if); ++ } else { ++ core_if->op_state = A_HOST; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_hcd_start(core_if); ++ } ++ } ++ ++ if (core_if->adp_enable) { ++ uint8_t is_host = 0; ++ DWC_SPINUNLOCK(core_if->lock); ++ /* Change the core_if's lock to hcd/pcd lock depend on mode? */ ++#ifndef DWC_HOST_ONLY ++ if (gpwrdn_temp.b.idsts) ++ core_if->lock = otg_dev->pcd->lock; ++#endif ++#ifndef DWC_DEVICE_ONLY ++ if (!gpwrdn_temp.b.idsts) { ++ core_if->lock = otg_dev->hcd->lock; ++ is_host = 1; ++ } ++#endif ++ DWC_DEBUGPL(DBG_ANY, "RESTART ADP\n"); ++ if (core_if->adp.probe_enabled) ++ dwc_otg_adp_probe_stop(core_if); ++ if (core_if->adp.sense_enabled) ++ dwc_otg_adp_sense_stop(core_if); ++ if (core_if->adp.sense_timer_started) ++ DWC_TIMER_CANCEL(core_if->adp.sense_timer); ++ if (core_if->adp.vbuson_timer_started) ++ DWC_TIMER_CANCEL(core_if->adp.vbuson_timer); ++ /* Do not need to reset ADP if we are coming back ++ * to the device mode after HNP. This is needed ++ * not to perform SRP after reverse, just do ADP ++ * probe and compare the RTIM values with the one ++ * before HNP */ ++ if (core_if->op_state != B_HOST) { ++ core_if->adp.probe_timer_values[0] = -1; ++ core_if->adp.probe_timer_values[1] = -1; ++ core_if->adp.probe_counter = 0; ++ core_if->adp.gpwrdn = 0; ++ } ++ core_if->adp.sense_timer_started = 0; ++ core_if->adp.vbuson_timer_started = 0; ++ ++ /* Disable PMU and restart ADP */ ++ gpwrdn_temp.d32 = 0; ++ gpwrdn_temp.b.pmuactv = 1; ++ gpwrdn_temp.b.pmuintsel = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_mdelay(110); ++ dwc_otg_adp_start(core_if, is_host); ++ DWC_SPINLOCK(core_if->lock); ++ } ++ ++ return 1; ++} ++ ++static int32_t dwc_otg_handle_pwrdn_session_change(dwc_otg_core_if_t * core_if) ++{ ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ int32_t otg_cap_param = core_if->core_params->otg_cap; ++ DWC_DEBUGPL(DBG_ANY, "%s called\n", __FUNCTION__); ++ ++ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ if (core_if->power_down == 2) { ++ if (!core_if->hibernation_suspend) { ++ DWC_PRINTF("Already exited from Hibernation\n"); ++ return 1; ++ } ++ ++ if (gpwrdn.b.bsessvld == 0) { ++ /* Save gpwrdn register for further usage if stschng interrupt */ ++ core_if->gr_backup->gpwrdn_local = ++ DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ /*Exit from ISR and wait for stschng interrupt with bsessvld = 1 */ ++ return 1; ++ } ++ ++ /* Switch on the voltage to the core */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Reset the core */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Disable power clamps */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnclmp = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /* Remove reset the core signal */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Disable PMU interrupt */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /*Indicates that we are exiting from hibernation */ ++ core_if->hibernation_suspend = 0; ++ ++ /* Disable PMU */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ core_if->op_state = B_PERIPHERAL; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_pcd_start(core_if); ++ ++ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE || ++ otg_cap_param == DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE) { ++ /* ++ * Initiate SRP after initial ADP probe. ++ */ ++ dwc_otg_initiate_srp(core_if); ++ } ++ } else if (core_if->adp_enable && core_if->op_state != A_HOST){ ++ dwc_otg_adp_probe_stop(core_if); ++ if (DWC_WORKQ_PENDING(core_if->wq_otg)) ++ core_if->stop_adpprb = 1; ++ /* Disable Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, gpwrdn.d32, 0); ++ ++ /* ++ * Initialize the Core for Device mode. ++ */ ++ core_if->op_state = B_PERIPHERAL; ++ cil_pcd_start(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ } ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that the Wakeup Logic has detected a ++ * status change either on IDDIG or BSessVld. ++ */ ++static uint32_t dwc_otg_handle_pwrdn_stschng_intr(dwc_otg_device_t * otg_dev) ++{ ++ int retval = 0; ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ gpwrdn_data_t gpwrdn_temp = {.d32 = 0 }; ++ dwc_otg_core_if_t *core_if = otg_dev->core_if; ++ ++ DWC_DEBUGPL(DBG_CIL, "%s called\n", __FUNCTION__); ++ ++ if (core_if->power_down == 2) { ++ if (core_if->hibernation_suspend <= 0) { ++ DWC_PRINTF("Already exited from Hibernation\n"); ++ return 1; ++ } else ++ gpwrdn_temp.d32 = core_if->gr_backup->gpwrdn_local; ++ ++ } else { ++ gpwrdn_temp.d32 = core_if->adp.gpwrdn; ++ } ++ ++ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ ++ if (gpwrdn.b.idsts ^ gpwrdn_temp.b.idsts) { ++ retval = dwc_otg_handle_pwrdn_idsts_change(otg_dev); ++ } else if (gpwrdn.b.bsessvld ^ gpwrdn_temp.b.bsessvld) { ++ retval = dwc_otg_handle_pwrdn_session_change(core_if); ++ } ++ ++ return retval; ++} ++ ++/** ++ * This interrupt indicates that the Wakeup Logic has detected a ++ * SRP. ++ */ ++static int32_t dwc_otg_handle_pwrdn_srp_intr(dwc_otg_core_if_t * core_if) ++{ ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ ++ DWC_PRINTF("%s called\n", __FUNCTION__); ++ ++ if (core_if->power_down == 2) { ++ if (!core_if->hibernation_suspend) { ++ DWC_PRINTF("Already exited from Hibernation\n"); ++ return 1; ++ } ++#ifdef DWC_DEV_SRPCAP ++ if (core_if->pwron_timer_started) { ++ core_if->pwron_timer_started = 0; ++ DWC_TIMER_CANCEL(core_if->pwron_timer); ++ } ++#endif ++ ++ /* Switch on the voltage to the core */ ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Reset the core */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Disable power clamps */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnclmp = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /* Remove reset the core signal */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Disable PMU interrupt */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /* Indicates that we are exiting from hibernation */ ++ core_if->hibernation_suspend = 0; ++ ++ /* Disable PMU */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Programm Disable VBUS to 0 */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.dis_vbus = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /*Initialize the core as Host */ ++ core_if->op_state = A_HOST; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_hcd_start(core_if); ++ } ++ /* Do not need to du anything if this is "old" SRP and we are already ++ * in the normal mode of operation */ ++ if(core_if->adp_enable) { ++ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ if (!gpwrdn.b.pmuactv) { ++ return 1; ++ } ++ ++ dwc_otg_adp_probe_stop(core_if); ++ /* Disable Interrupt from Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, gpwrdn.d32, 0); ++ ++ /* ++ * Initialize the Core for Host mode. ++ */ ++ core_if->op_state = A_HOST; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_hcd_start(core_if); ++ /* Start the Connection timer. So a message can be displayed ++ * if connect does not occur within 10 seconds. */ ++ cil_hcd_session_start(core_if); ++ } ++ ++ return 1; ++} ++ ++/** This interrupt indicates that restore command after Hibernation ++ * was completed by the core. */ ++int32_t dwc_otg_handle_restore_done_intr(dwc_otg_core_if_t * core_if) ++{ ++ pcgcctl_data_t pcgcctl; ++ DWC_DEBUGPL(DBG_ANY, "++Restore Done Interrupt++\n"); ++ ++ //TODO De-assert restore signal. 8.a ++ pcgcctl.d32 = DWC_READ_REG32(core_if->pcgcctl); ++ if (pcgcctl.b.restoremode == 1) { ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ /* ++ * If restore mode is Remote Wakeup, ++ * unmask Remote Wakeup interrupt. ++ */ ++ gintmsk.b.wkupintr = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, ++ 0, gintmsk.d32); ++ } ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that a device has been disconnected from ++ * the root port. ++ */ ++int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t * core_if) ++{ ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n", ++ (dwc_otg_is_host_mode(core_if) ? "Host" : "Device"), ++ op_state_str(core_if)); ++ ++/** @todo Consolidate this if statement. */ ++#ifndef DWC_HOST_ONLY ++ if (core_if->op_state == B_HOST) { ++ /* If in device mode Disconnect and stop the HCD, then ++ * start the PCD. */ ++ DWC_SPINUNLOCK(core_if->lock); ++ cil_hcd_disconnect(core_if); ++ cil_pcd_start(core_if); ++ DWC_SPINLOCK(core_if->lock); ++ core_if->op_state = B_PERIPHERAL; ++ } else if (dwc_otg_is_device_mode(core_if)) { ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ gotgctl.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->gotgctl); ++ if (gotgctl.b.hstsethnpen == 1) { ++ /* Do nothing, if HNP in process the OTG ++ * interrupt "Host Negotiation Detected" ++ * interrupt will do the mode switch. ++ */ ++ } else if (gotgctl.b.devhnpen == 0) { ++ /* If in device mode Disconnect and stop the HCD, then ++ * start the PCD. */ ++ DWC_SPINUNLOCK(core_if->lock); ++ cil_hcd_disconnect(core_if); ++ cil_pcd_start(core_if); ++ DWC_SPINLOCK(core_if->lock); ++ core_if->op_state = B_PERIPHERAL; ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "!a_peripheral && !devhnpen\n"); ++ } ++ } else { ++ if (core_if->op_state == A_HOST) { ++ /* A-Cable still connected but device disconnected. */ ++ cil_hcd_disconnect(core_if); ++ if (core_if->adp_enable) { ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ cil_hcd_stop(core_if); ++ /* Enable Power Down Logic */ ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ dwc_otg_adp_probe_start(core_if); ++ ++ /* Power off the core */ ++ if (core_if->power_down == 2) { ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32 ++ (&core_if->core_global_regs->gpwrdn, ++ gpwrdn.d32, 0); ++ } ++ } ++ } ++ } ++#endif ++ /* Change to L3(OFF) state */ ++ core_if->lx_state = DWC_OTG_L3; ++ ++ gintsts.d32 = 0; ++ gintsts.b.disconnect = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that SUSPEND state has been detected on ++ * the USB. ++ * ++ * For HNP the USB Suspend interrupt signals the change from ++ * "a_peripheral" to "a_host". ++ * ++ * When power management is enabled the core will be put in low power ++ * mode. ++ */ ++extern int otg_usbhost_stat; ++int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t * core_if) ++{ ++ dsts_data_t dsts; ++ gintsts_data_t gintsts; ++ dcfg_data_t dcfg; ++ ++ DWC_DEBUGPL(DBG_ANY, "USB SUSPEND\n"); ++ otg_usbhost_stat = 0; ++ ++ if ((core_if->otg_ver == 1) && (core_if->op_state == A_PERIPHERAL)) { ++ core_if->lx_state = DWC_OTG_L2; ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.usbsuspend = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++ } ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ /* Check the Device status register to determine if the Suspend ++ * state is active. */ ++ dsts.d32 = ++ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); ++ DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32); ++ DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d " ++ "HWCFG4.power Optimize=%d\n", ++ dsts.b.suspsts, core_if->hwcfg4.b.power_optimiz); ++ ++#ifdef PARTIAL_POWER_DOWN ++/** @todo Add a module parameter for power management. */ ++ ++ if (dsts.b.suspsts && core_if->hwcfg4.b.power_optimiz) { ++ pcgcctl_data_t power = {.d32 = 0 }; ++ DWC_DEBUGPL(DBG_CIL, "suspend\n"); ++ ++ power.b.pwrclmp = 1; ++ DWC_WRITE_REG32(core_if->pcgcctl, power.d32); ++ ++ power.b.rstpdwnmodule = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, 0, power.d32); ++ ++ power.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, 0, power.d32); ++ ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "disconnect?\n"); ++ } ++#endif ++ /* PCD callback for suspend. Release the lock inside of callback function */ ++ cil_pcd_suspend(core_if); ++ if (core_if->power_down == 2) { ++ dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg); ++ DWC_DEBUGPL(DBG_ANY,"lx_state = %08x\n",core_if->lx_state); ++ DWC_DEBUGPL(DBG_ANY," device address = %08d\n",dcfg.b.devaddr); ++ ++ if (core_if->lx_state != DWC_OTG_L3 && dcfg.b.devaddr) { ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ gusbcfg_data_t gusbcfg = {.d32 = 0 }; ++ ++ /* Change to L2(suspend) state */ ++ core_if->lx_state = DWC_OTG_L2; ++ ++ /* Clear interrupt in gintsts */ ++ gintsts.d32 = 0; ++ gintsts.b.usbsuspend = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs-> ++ gintsts, gintsts.d32); ++ DWC_PRINTF("Start of hibernation completed\n"); ++ dwc_otg_save_global_regs(core_if); ++ dwc_otg_save_dev_regs(core_if); ++ ++ gusbcfg.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs-> ++ gusbcfg); ++ if (gusbcfg.b.ulpi_utmi_sel == 1) { ++ /* ULPI interface */ ++ /* Suspend the Phy Clock */ ++ pcgcctl.d32 = 0; ++ pcgcctl.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, 0, ++ pcgcctl.d32); ++ dwc_udelay(10); ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if-> ++ core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ } else { ++ /* UTMI+ Interface */ ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if-> ++ core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ pcgcctl.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, 0, ++ pcgcctl.d32); ++ dwc_udelay(10); ++ } ++ ++ /* Set flag to indicate that we are in hibernation */ ++ core_if->hibernation_suspend = 1; ++ /* Enable interrupts from wake up logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Unmask device mode interrupts in GPWRDN */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.rst_det_msk = 1; ++ gpwrdn.b.lnstchng_msk = 1; ++ gpwrdn.b.sts_chngint_msk = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Enable Power Down Clamp */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnclmp = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Switch off VDD */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ ++ /* Save gpwrdn register for further usage if stschng interrupt */ ++ core_if->gr_backup->gpwrdn_local = ++ DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ DWC_PRINTF("Hibernation completed\n"); ++ ++ return 1; ++ } ++ } else if (core_if->power_down == 3) { ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg); ++ DWC_DEBUGPL(DBG_ANY, "lx_state = %08x\n",core_if->lx_state); ++ DWC_DEBUGPL(DBG_ANY, " device address = %08d\n",dcfg.b.devaddr); ++ ++ if (core_if->lx_state != DWC_OTG_L3 && dcfg.b.devaddr) { ++ DWC_DEBUGPL(DBG_ANY, "Start entering to extended hibernation\n"); ++ core_if->xhib = 1; ++ ++ /* Clear interrupt in gintsts */ ++ gintsts.d32 = 0; ++ gintsts.b.usbsuspend = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs-> ++ gintsts, gintsts.d32); ++ ++ dwc_otg_save_global_regs(core_if); ++ dwc_otg_save_dev_regs(core_if); ++ ++ /* Wait for 10 PHY clocks */ ++ dwc_udelay(10); ++ ++ /* Program GPIO register while entering to xHib */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->ggpio, 0x1); ++ ++ pcgcctl.b.enbl_extnd_hiber = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32); ++ DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32); ++ ++ pcgcctl.d32 = 0; ++ pcgcctl.b.extnd_hiber_pwrclmp = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32); ++ ++ pcgcctl.d32 = 0; ++ pcgcctl.b.extnd_hiber_switch = 1; ++ core_if->gr_backup->xhib_gpwrdn = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ core_if->gr_backup->xhib_pcgcctl = DWC_READ_REG32(core_if->pcgcctl) | pcgcctl.d32; ++ DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32); ++ ++ DWC_DEBUGPL(DBG_ANY, "Finished entering to extended hibernation\n"); ++ ++ return 1; ++ } ++ } ++ if ((core_if->otg_ver == 1) && (core_if->core_params->otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE)) { ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl); ++ if (gotgctl.b.devhnpen && core_if->otg_ver == 1){ ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ dwc_mdelay(5); ++ /**@todo Is the gotgctl.devhnpen cleared ++ * by a USB Reset? */ ++ gotgctl.b.devhnpen = 1; ++ gotgctl.b.hnpreq = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, ++ gotgctl.d32); ++ } ++ } ++ } else { ++ if (core_if->op_state == A_PERIPHERAL) { ++ DWC_DEBUGPL(DBG_ANY, "a_peripheral->a_host\n"); ++ /* Clear the a_peripheral flag, back to a_host. */ ++ DWC_SPINUNLOCK(core_if->lock); ++ cil_pcd_stop(core_if); ++ cil_hcd_start(core_if); ++ DWC_SPINLOCK(core_if->lock); ++ core_if->op_state = A_HOST; ++ } ++ } ++ ++ /* Change to L2(suspend) state */ ++ core_if->lx_state = DWC_OTG_L2; ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.usbsuspend = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++static int32_t dwc_otg_handle_xhib_exit_intr(dwc_otg_core_if_t * core_if) ++{ ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ gahbcfg_data_t gahbcfg = {.d32 = 0 }; ++ ++ dwc_udelay(10); ++ ++ /* Program GPIO register while entering to xHib */ ++ DWC_WRITE_REG32(&core_if->core_global_regs->ggpio, 0x0); ++ ++ pcgcctl.d32 = core_if->gr_backup->xhib_pcgcctl; ++ pcgcctl.b.extnd_hiber_pwrclmp = 0; ++ DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32); ++ dwc_udelay(10); ++ ++ gpwrdn.d32 = core_if->gr_backup->xhib_gpwrdn; ++ gpwrdn.b.restore = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ restore_lpm_i2c_regs(core_if); ++ ++ pcgcctl.d32 = core_if->gr_backup->pcgcctl_local & (0x3FFFF << 14); ++ pcgcctl.b.max_xcvrselect = 1; ++ pcgcctl.b.ess_reg_restored = 0; ++ pcgcctl.b.extnd_hiber_switch = 0; ++ pcgcctl.b.extnd_hiber_pwrclmp = 0; ++ pcgcctl.b.enbl_extnd_hiber = 1; ++ DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32); ++ ++ gahbcfg.d32 = core_if->gr_backup->gahbcfg_local; ++ gahbcfg.b.glblintrmsk = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg, gahbcfg.d32); ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF); ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, 0x1 << 16); ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, ++ core_if->gr_backup->gusbcfg_local); ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, ++ core_if->dr_backup->dcfg); ++ ++ pcgcctl.d32 = 0; ++ pcgcctl.d32 = core_if->gr_backup->pcgcctl_local & (0x3FFFF << 14); ++ pcgcctl.b.max_xcvrselect = 1; ++ pcgcctl.d32 |= 0x608; ++ DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32); ++ dwc_udelay(10); ++ ++ pcgcctl.d32 = 0; ++ pcgcctl.d32 = core_if->gr_backup->pcgcctl_local & (0x3FFFF << 14); ++ pcgcctl.b.max_xcvrselect = 1; ++ pcgcctl.b.ess_reg_restored = 1; ++ pcgcctl.b.enbl_extnd_hiber = 1; ++ pcgcctl.b.rstpdwnmodule = 1; ++ pcgcctl.b.restoremode = 1; ++ DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32); ++ ++ DWC_DEBUGPL(DBG_ANY, "%s called\n", __FUNCTION__); ++ ++ return 1; ++} ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++/** ++ * This function hadles LPM transaction received interrupt. ++ */ ++static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ gintsts_data_t gintsts; ++ ++ if (!core_if->core_params->lpm_enable) { ++ DWC_PRINTF("Unexpected LPM interrupt\n"); ++ } ++ ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ DWC_PRINTF("LPM config register = 0x%08x\n", lpmcfg.d32); ++ ++ if (dwc_otg_is_host_mode(core_if)) { ++ cil_hcd_sleep(core_if); ++ } else { ++ ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ ++ lpmcfg.b.hird_thres |= (1 << 4); ++ lpmcfg.b.en_utmi_sleep = 1; ++ ++ pcgcctl.b.enbl_sleep_gating = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl,0,pcgcctl.d32); ++ ++ if(dwc_otg_get_param_besl_enable(core_if)) { ++ lpmcfg.b.en_besl = 1; ++ } ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, ++ lpmcfg.d32); ++ } ++ ++ /* Examine prt_sleep_sts after TL1TokenTetry period max (10 us) */ ++ dwc_udelay(10); ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ if (lpmcfg.b.prt_sleep_sts) { ++ /* Save the current state */ ++ core_if->lx_state = DWC_OTG_L1; ++ } ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.lpmtranrcvd = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++#endif /* CONFIG_USB_DWC_OTG_LPM */ ++ ++/** ++ * This function returns the Core Interrupt register. ++ */ ++static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if) ++{ ++ gahbcfg_data_t gahbcfg = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ gintmsk_data_t gintmsk; ++ gintmsk_data_t gintmsk_common = {.d32 = 0 }; ++ gintmsk_common.b.wkupintr = 1; ++ gintmsk_common.b.sessreqintr = 1; ++ gintmsk_common.b.conidstschng = 1; ++ gintmsk_common.b.otgintr = 1; ++ gintmsk_common.b.modemismatch = 1; ++ gintmsk_common.b.disconnect = 1; ++ gintmsk_common.b.usbsuspend = 1; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ gintmsk_common.b.lpmtranrcvd = 1; ++#endif ++ gintmsk_common.b.restoredone = 1; ++ /** @todo: The port interrupt occurs while in device ++ * mode. Added code to CIL to clear the interrupt for now! ++ */ ++ gintmsk_common.b.portintr = 1; ++ ++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); ++ gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg); ++ ++#ifdef DEBUG ++ /* if any common interrupts set */ ++ if (gintsts.d32 & gintmsk_common.d32) { ++ DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n", ++ gintsts.d32, gintmsk.d32); ++ } ++#endif ++ if (gahbcfg.b.glblintrmsk) ++ return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); ++ else ++ return 0; ++ ++} ++ ++/* MACRO for clearing interupt bits in GPWRDN register */ ++#define CLEAR_GPWRDN_INTR(__core_if,__intr) \ ++do { \ ++ gpwrdn_data_t gpwrdn = {.d32=0}; \ ++ gpwrdn.b.__intr = 1; \ ++ DWC_MODIFY_REG32(&__core_if->core_global_regs->gpwrdn, \ ++ 0, gpwrdn.d32); \ ++} while (0) ++ ++/** ++ * Common interrupt handler. ++ * ++ * The common interrupts are those that occur in both Host and Device mode. ++ * This handler handles the following interrupts: ++ * - Mode Mismatch Interrupt ++ * - Disconnect Interrupt ++ * - OTG Interrupt ++ * - Connector ID Status Change Interrupt ++ * - Session Request Interrupt. ++ * - Resume / Remote Wakeup Detected Interrupt. ++ * - LPM Transaction Received Interrupt ++ * - ADP Transaction Received Interrupt ++ * ++ */ ++int32_t dwc_otg_handle_common_intr(void *dev) ++{ ++ int retval = 0; ++ gintsts_data_t gintsts; ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ dwc_otg_device_t *otg_dev = dev; ++ dwc_otg_core_if_t *core_if = otg_dev->core_if; ++ gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ ++ if (dwc_otg_check_haps_status(core_if) == -1 ) { ++ DWC_WARN("HAPS is disconnected"); ++ return retval; ++ } ++ ++ if (dwc_otg_is_device_mode(core_if)) ++ core_if->frame_num = dwc_otg_get_frame_number(core_if); ++ ++ if (core_if->lock) ++ DWC_SPINLOCK(core_if->lock); ++ ++ if (core_if->power_down == 3 && core_if->xhib == 1) { ++ DWC_DEBUGPL(DBG_ANY, "Exiting from xHIB state\n"); ++ retval |= dwc_otg_handle_xhib_exit_intr(core_if); ++ core_if->xhib = 2; ++ if (core_if->lock) ++ DWC_SPINUNLOCK(core_if->lock); ++ ++ return retval; ++ } ++ ++ if (core_if->hibernation_suspend <= 0) { ++ gintsts.d32 = dwc_otg_read_common_intr(core_if); ++ ++ if (gintsts.b.modemismatch) { ++ retval |= dwc_otg_handle_mode_mismatch_intr(core_if); ++ } ++ if (gintsts.b.otgintr) { ++ retval |= dwc_otg_handle_otg_intr(core_if); ++ } ++ if (gintsts.b.conidstschng) { ++ retval |= ++ dwc_otg_handle_conn_id_status_change_intr(core_if); ++ } ++ if (gintsts.b.disconnect) { ++ retval |= dwc_otg_handle_disconnect_intr(core_if); ++ } ++ if (gintsts.b.sessreqintr) { ++ retval |= dwc_otg_handle_session_req_intr(core_if); ++ } ++ if (gintsts.b.wkupintr) { ++ retval |= dwc_otg_handle_wakeup_detected_intr(core_if); ++ } ++ if (gintsts.b.usbsuspend) { ++ retval |= dwc_otg_handle_usb_suspend_intr(core_if); ++ } ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ if (gintsts.b.lpmtranrcvd) { ++ retval |= dwc_otg_handle_lpm_intr(core_if); ++ } ++#endif ++ if (gintsts.b.restoredone) { ++ gintsts.d32 = 0; ++ if (core_if->power_down == 2) ++ core_if->hibernation_suspend = -1; ++ else if (core_if->power_down == 3 && core_if->xhib == 2) { ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ dctl_data_t dctl = {.d32 = 0 }; ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs-> ++ gintsts, 0xFFFFFFFF); ++ ++ DWC_DEBUGPL(DBG_ANY, ++ "RESTORE DONE generated\n"); ++ ++ gpwrdn.b.restore = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ pcgcctl.b.rstpdwnmodule = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0); ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, core_if->gr_backup->gusbcfg_local); ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, core_if->dr_backup->dcfg); ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, core_if->dr_backup->dctl); ++ dwc_udelay(50); ++ ++ dctl.b.pwronprgdone = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32); ++ dwc_udelay(10); ++ ++ dwc_otg_restore_global_regs(core_if); ++ dwc_otg_restore_dev_regs(core_if, 0); ++ ++ dctl.d32 = 0; ++ dctl.b.pwronprgdone = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0); ++ dwc_udelay(10); ++ ++ pcgcctl.d32 = 0; ++ pcgcctl.b.enbl_extnd_hiber = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0); ++ ++ /* The core will be in ON STATE */ ++ core_if->lx_state = DWC_OTG_L0; ++ core_if->xhib = 0; ++ ++ DWC_SPINUNLOCK(core_if->lock); ++ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { ++ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); ++ } ++ DWC_SPINLOCK(core_if->lock); ++ ++ } ++ ++ gintsts.b.restoredone = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32); ++ DWC_PRINTF(" --Restore done interrupt received-- \n"); ++ retval |= 1; ++ } ++ if (gintsts.b.portintr && dwc_otg_is_device_mode(core_if)) { ++ /* The port interrupt occurs while in device mode with HPRT0 ++ * Port Enable/Disable. ++ */ ++ gintsts.d32 = 0; ++ gintsts.b.portintr = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32); ++ retval |= 1; ++ ++ } ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32); ++ ++ if (gpwrdn.b.disconn_det && gpwrdn.b.disconn_det_msk) { ++ CLEAR_GPWRDN_INTR(core_if, disconn_det); ++ if (gpwrdn.b.linestate == 0) { ++ dwc_otg_handle_pwrdn_disconnect_intr(core_if); ++ } else { ++ DWC_PRINTF("Disconnect detected while linestate is not 0\n"); ++ } ++ ++ retval |= 1; ++ } ++ if (gpwrdn.b.lnstschng && gpwrdn.b.lnstchng_msk) { ++ CLEAR_GPWRDN_INTR(core_if, lnstschng); ++ /* remote wakeup from hibernation */ ++ if (gpwrdn.b.linestate == 2 || gpwrdn.b.linestate == 1) { ++ dwc_otg_handle_pwrdn_wakeup_detected_intr(core_if); ++ } else { ++ DWC_PRINTF("gpwrdn.linestate = %d\n", gpwrdn.b.linestate); ++ } ++ retval |= 1; ++ } ++ if (gpwrdn.b.rst_det && gpwrdn.b.rst_det_msk) { ++ CLEAR_GPWRDN_INTR(core_if, rst_det); ++ if (gpwrdn.b.linestate == 0) { ++ DWC_PRINTF("Reset detected\n"); ++ retval |= dwc_otg_device_hibernation_restore(core_if, 0, 1); ++ } ++ } ++ if (gpwrdn.b.srp_det && gpwrdn.b.srp_det_msk) { ++ CLEAR_GPWRDN_INTR(core_if, srp_det); ++ dwc_otg_handle_pwrdn_srp_intr(core_if); ++ retval |= 1; ++ } ++ } ++ /* Handle ADP interrupt here */ ++ if (gpwrdn.b.adp_int) { ++ CLEAR_GPWRDN_INTR(core_if, adp_int); ++ dwc_otg_adp_handle_intr(core_if); ++ retval |= 1; ++ } ++ if (gpwrdn.b.sts_chngint && gpwrdn.b.sts_chngint_msk) { ++ CLEAR_GPWRDN_INTR(core_if, sts_chngint); ++ dwc_otg_handle_pwrdn_stschng_intr(otg_dev); ++ ++ retval |= 1; ++ } ++ if (gpwrdn.b.srp_det && gpwrdn.b.srp_det_msk) { ++ CLEAR_GPWRDN_INTR(core_if, srp_det); ++ dwc_otg_handle_pwrdn_srp_intr(core_if); ++ retval |= 1; ++ } ++ if (core_if->lock) ++ DWC_SPINUNLOCK(core_if->lock); ++ ++ return retval; ++} +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_core_if.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_core_if.h +new file mode 100644 +index 0000000..c9ab2e5 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_core_if.h +@@ -0,0 +1,743 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_core_if.h $ ++ * $Revision: #15 $ ++ * $Date: 2012/12/10 $ ++ * $Change: 2123206 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#if !defined(__DWC_CORE_IF_H__) ++#define __DWC_CORE_IF_H__ ++ ++#include "dwc_os.h" ++ ++/** @file ++ * This file defines DWC_OTG Core API ++ */ ++ ++struct dwc_otg_core_if; ++typedef struct dwc_otg_core_if dwc_otg_core_if_t; ++ ++/** Maximum number of Periodic FIFOs */ ++#define MAX_PERIO_FIFOS 15 ++/** Maximum number of Periodic FIFOs */ ++#define MAX_TX_FIFOS 15 ++ ++/** Maximum number of Endpoints/HostChannels */ ++#define MAX_EPS_CHANNELS 16 ++ ++extern dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t * _reg_base_addr); ++extern void dwc_otg_core_init(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_cil_remove(dwc_otg_core_if_t * _core_if); ++ ++extern void dwc_otg_enable_global_interrupts(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_disable_global_interrupts(dwc_otg_core_if_t * _core_if); ++ ++extern uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t * _core_if); ++extern uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t * _core_if); ++ ++extern uint8_t dwc_otg_is_dma_enable(dwc_otg_core_if_t * core_if); ++ ++/** This function should be called on every hardware interrupt. */ ++extern int32_t dwc_otg_handle_common_intr(void *otg_dev); ++ ++/** @name OTG Core Parameters */ ++/** @{ */ ++ ++/** ++ * Specifies the OTG capabilities. The driver will automatically ++ * detect the value for this parameter if none is specified. ++ * 0 - HNP and SRP capable (default) ++ * 1 - SRP Only capable ++ * 2 - No HNP/SRP capable ++ */ ++extern int dwc_otg_set_param_otg_cap(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_param_otg_cap(dwc_otg_core_if_t * core_if); ++#define DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE 0 ++#define DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE 1 ++#define DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE 2 ++#define dwc_param_otg_cap_default DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE ++ ++extern int dwc_otg_set_param_opt(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_param_opt(dwc_otg_core_if_t * core_if); ++#define dwc_param_opt_default 1 ++ ++/** ++ * Specifies whether to use slave or DMA mode for accessing the data ++ * FIFOs. The driver will automatically detect the value for this ++ * parameter if none is specified. ++ * 0 - Slave ++ * 1 - DMA (default, if available) ++ */ ++extern int dwc_otg_set_param_dma_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_dma_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_dma_enable_default 1 ++ ++/** ++ * When DMA mode is enabled specifies whether to use ++ * address DMA or DMA Descritor mode for accessing the data ++ * FIFOs in device mode. The driver will automatically detect ++ * the value for this parameter if none is specified. ++ * 0 - address DMA ++ * 1 - DMA Descriptor(default, if available) ++ */ ++extern int dwc_otg_set_param_dma_desc_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_dma_desc_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_dma_desc_enable_default 1 ++ ++/** The DMA Burst size (applicable only for External DMA ++ * Mode). 1, 4, 8 16, 32, 64, 128, 256 (default 32) ++ */ ++extern int dwc_otg_set_param_dma_burst_size(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_dma_burst_size(dwc_otg_core_if_t * core_if); ++#define dwc_param_dma_burst_size_default 32 ++ ++/** ++ * Specifies the maximum speed of operation in host and device mode. ++ * The actual speed depends on the speed of the attached device and ++ * the value of phy_type. The actual speed depends on the speed of the ++ * attached device. ++ * 0 - High Speed (default) ++ * 1 - Full Speed ++ */ ++extern int dwc_otg_set_param_speed(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_param_speed(dwc_otg_core_if_t * core_if); ++#define dwc_param_speed_default 0 ++#define DWC_SPEED_PARAM_HIGH 0 ++#define DWC_SPEED_PARAM_FULL 1 ++ ++/** Specifies whether low power mode is supported when attached ++ * to a Full Speed or Low Speed device in host mode. ++ * 0 - Don't support low power mode (default) ++ * 1 - Support low power mode ++ */ ++extern int dwc_otg_set_param_host_support_fs_ls_low_power(dwc_otg_core_if_t * ++ core_if, int32_t val); ++extern int32_t dwc_otg_get_param_host_support_fs_ls_low_power(dwc_otg_core_if_t ++ * core_if); ++#define dwc_param_host_support_fs_ls_low_power_default 0 ++ ++/** Specifies the PHY clock rate in low power mode when connected to a ++ * Low Speed device in host mode. This parameter is applicable only if ++ * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS ++ * then defaults to 6 MHZ otherwise 48 MHZ. ++ * ++ * 0 - 48 MHz ++ * 1 - 6 MHz ++ */ ++extern int dwc_otg_set_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * ++ core_if, int32_t val); ++extern int32_t dwc_otg_get_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * ++ core_if); ++#define dwc_param_host_ls_low_power_phy_clk_default 0 ++#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0 ++#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1 ++ ++/** ++ * 0 - Use cC FIFO size parameters ++ * 1 - Allow dynamic FIFO sizing (default) ++ */ ++extern int dwc_otg_set_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_enable_dynamic_fifo(dwc_otg_core_if_t * ++ core_if); ++#define dwc_param_enable_dynamic_fifo_default 1 ++ ++/** Total number of 4-byte words in the data FIFO memory. This ++ * memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic ++ * Tx FIFOs. ++ * 32 to 32768 (default 8192) ++ * Note: The total FIFO memory depth in the FPGA configuration is 8192. ++ */ ++extern int dwc_otg_set_param_data_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_data_fifo_size(dwc_otg_core_if_t * core_if); ++#define dwc_param_data_fifo_size_default 8192 ++ ++/** Number of 4-byte words in the Rx FIFO in device mode when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1064) ++ */ ++extern int dwc_otg_set_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if); ++#define dwc_param_dev_rx_fifo_size_default 1064 ++ ++/** Number of 4-byte words in the non-periodic Tx FIFO in device mode ++ * when dynamic FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++extern int dwc_otg_set_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if, int32_t val); ++extern int32_t dwc_otg_get_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if); ++#define dwc_param_dev_nperio_tx_fifo_size_default 1024 ++ ++/** Number of 4-byte words in each of the periodic Tx FIFOs in device ++ * mode when dynamic FIFO sizing is enabled. ++ * 4 to 768 (default 256) ++ */ ++extern int dwc_otg_set_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val, int fifo_num); ++extern int32_t dwc_otg_get_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if, int fifo_num); ++#define dwc_param_dev_perio_tx_fifo_size_default 256 ++ ++/** Number of 4-byte words in the Rx FIFO in host mode when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++extern int dwc_otg_set_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if); ++#define dwc_param_host_rx_fifo_size_default 1024 ++ ++/** Number of 4-byte words in the non-periodic Tx FIFO in host mode ++ * when Dynamic FIFO sizing is enabled in the core. ++ * 16 to 32768 (default 1024) ++ */ ++extern int dwc_otg_set_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if, int32_t val); ++extern int32_t dwc_otg_get_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if); ++#define dwc_param_host_nperio_tx_fifo_size_default 1024 ++ ++/** Number of 4-byte words in the host periodic Tx FIFO when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++extern int dwc_otg_set_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if, int32_t val); ++extern int32_t dwc_otg_get_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if); ++#define dwc_param_host_perio_tx_fifo_size_default 1024 ++ ++/** The maximum transfer size supported in bytes. ++ * 2047 to 65,535 (default 65,535) ++ */ ++extern int dwc_otg_set_param_max_transfer_size(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_max_transfer_size(dwc_otg_core_if_t * core_if); ++#define dwc_param_max_transfer_size_default 65535 ++ ++/** The maximum number of packets in a transfer. ++ * 15 to 511 (default 511) ++ */ ++extern int dwc_otg_set_param_max_packet_count(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_max_packet_count(dwc_otg_core_if_t * core_if); ++#define dwc_param_max_packet_count_default 511 ++ ++/** The number of host channel registers to use. ++ * 1 to 16 (default 12) ++ * Note: The FPGA configuration supports a maximum of 12 host channels. ++ */ ++extern int dwc_otg_set_param_host_channels(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_host_channels(dwc_otg_core_if_t * core_if); ++#define dwc_param_host_channels_default 12 ++ ++/** The number of endpoints in addition to EP0 available for device ++ * mode operations. ++ * 1 to 15 (default 6 IN and OUT) ++ * Note: The FPGA configuration supports a maximum of 6 IN and OUT ++ * endpoints in addition to EP0. ++ */ ++extern int dwc_otg_set_param_dev_endpoints(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_dev_endpoints(dwc_otg_core_if_t * core_if); ++#define dwc_param_dev_endpoints_default 6 ++ ++/** ++ * Specifies the type of PHY interface to use. By default, the driver ++ * will automatically detect the phy_type. ++ * ++ * 0 - Full Speed PHY ++ * 1 - UTMI+ (default) ++ * 2 - ULPI ++ */ ++extern int dwc_otg_set_param_phy_type(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_param_phy_type(dwc_otg_core_if_t * core_if); ++#define DWC_PHY_TYPE_PARAM_FS 0 ++#define DWC_PHY_TYPE_PARAM_UTMI 1 ++#define DWC_PHY_TYPE_PARAM_ULPI 2 ++#define dwc_param_phy_type_default DWC_PHY_TYPE_PARAM_UTMI ++ ++/** ++ * Specifies the UTMI+ Data Width. This parameter is ++ * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI ++ * PHY_TYPE, this parameter indicates the data width between ++ * the MAC and the ULPI Wrapper.) Also, this parameter is ++ * applicable only if the OTG_HSPHY_WIDTH cC parameter was set ++ * to "8 and 16 bits", meaning that the core has been ++ * configured to work at either data path width. ++ * ++ * 8 or 16 bits (default 16) ++ */ ++extern int dwc_otg_set_param_phy_utmi_width(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_phy_utmi_width(dwc_otg_core_if_t * core_if); ++#define dwc_param_phy_utmi_width_default 16 ++ ++/** ++ * Specifies whether the ULPI operates at double or single ++ * data rate. This parameter is only applicable if PHY_TYPE is ++ * ULPI. ++ * ++ * 0 - single data rate ULPI interface with 8 bit wide data ++ * bus (default) ++ * 1 - double data rate ULPI interface with 4 bit wide data ++ * bus ++ */ ++extern int dwc_otg_set_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if); ++#define dwc_param_phy_ulpi_ddr_default 0 ++ ++/** ++ * Specifies whether to use the internal or external supply to ++ * drive the vbus with a ULPI phy. ++ */ ++extern int dwc_otg_set_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if); ++#define DWC_PHY_ULPI_INTERNAL_VBUS 0 ++#define DWC_PHY_ULPI_EXTERNAL_VBUS 1 ++#define dwc_param_phy_ulpi_ext_vbus_default DWC_PHY_ULPI_INTERNAL_VBUS ++ ++/** ++ * Specifies whether to use the I2Cinterface for full speed PHY. This ++ * parameter is only applicable if PHY_TYPE is FS. ++ * 0 - No (default) ++ * 1 - Yes ++ */ ++extern int dwc_otg_set_param_i2c_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_i2c_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_i2c_enable_default 0 ++ ++extern int dwc_otg_set_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if); ++#define dwc_param_ulpi_fs_ls_default 0 ++ ++extern int dwc_otg_set_param_ts_dline(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_param_ts_dline(dwc_otg_core_if_t * core_if); ++#define dwc_param_ts_dline_default 0 ++ ++/** ++ * Specifies whether dedicated transmit FIFOs are ++ * enabled for non periodic IN endpoints in device mode ++ * 0 - No ++ * 1 - Yes ++ */ ++extern int dwc_otg_set_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_en_multiple_tx_fifo(dwc_otg_core_if_t * ++ core_if); ++#define dwc_param_en_multiple_tx_fifo_default 1 ++ ++/** Number of 4-byte words in each of the Tx FIFOs in device ++ * mode when dynamic FIFO sizing is enabled. ++ * 4 to 768 (default 256) ++ */ ++extern int dwc_otg_set_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int fifo_num, int32_t val); ++extern int32_t dwc_otg_get_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int fifo_num); ++#define dwc_param_dev_tx_fifo_size_default 256 ++ ++/** Thresholding enable flag- ++ * bit 0 - enable non-ISO Tx thresholding ++ * bit 1 - enable ISO Tx thresholding ++ * bit 2 - enable Rx thresholding ++ */ ++extern int dwc_otg_set_param_thr_ctl(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_thr_ctl(dwc_otg_core_if_t * core_if, int fifo_num); ++#define dwc_param_thr_ctl_default 0 ++ ++/** Thresholding length for Tx ++ * FIFOs in 32 bit DWORDs ++ */ ++extern int dwc_otg_set_param_tx_thr_length(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_tx_thr_length(dwc_otg_core_if_t * core_if); ++#define dwc_param_tx_thr_length_default 64 ++ ++/** Thresholding length for Rx ++ * FIFOs in 32 bit DWORDs ++ */ ++extern int dwc_otg_set_param_rx_thr_length(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_rx_thr_length(dwc_otg_core_if_t * core_if); ++#define dwc_param_rx_thr_length_default 64 ++ ++/** ++ * Specifies whether LPM (Link Power Management) support is enabled ++ */ ++extern int dwc_otg_set_param_lpm_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_lpm_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_lpm_enable_default 1 ++ ++/** ++ * Specifies whether LPM Errata (Link Power Management) support is enabled ++ */ ++extern int dwc_otg_set_param_besl_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_besl_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_besl_enable_default 0 ++ ++/** ++ * Specifies baseline_besl default value ++ */ ++extern int dwc_otg_set_param_baseline_besl(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_baseline_besl(dwc_otg_core_if_t * core_if); ++#define dwc_param_baseline_besl_default 0 ++ ++/** ++ * Specifies deep_besl default value ++ */ ++extern int dwc_otg_set_param_deep_besl(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_deep_besl(dwc_otg_core_if_t * core_if); ++#define dwc_param_deep_besl_default 15 ++ ++/** ++ * Specifies whether PTI enhancement is enabled ++ */ ++extern int dwc_otg_set_param_pti_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_pti_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_pti_enable_default 0 ++ ++/** ++ * Specifies whether MPI enhancement is enabled ++ */ ++extern int dwc_otg_set_param_mpi_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_mpi_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_mpi_enable_default 0 ++ ++/** ++ * Specifies whether ADP capability is enabled ++ */ ++extern int dwc_otg_set_param_adp_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_adp_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_adp_enable_default 0 ++ ++/** ++ * Specifies whether IC_USB capability is enabled ++ */ ++ ++extern int dwc_otg_set_param_ic_usb_cap(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_ic_usb_cap(dwc_otg_core_if_t * core_if); ++#define dwc_param_ic_usb_cap_default 0 ++ ++extern int dwc_otg_set_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if); ++#define dwc_param_ahb_thr_ratio_default 0 ++ ++extern int dwc_otg_set_param_power_down(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_power_down(dwc_otg_core_if_t * core_if); ++#define dwc_param_power_down_default 0 ++ ++extern int dwc_otg_set_param_reload_ctl(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_reload_ctl(dwc_otg_core_if_t * core_if); ++#define dwc_param_reload_ctl_default 0 ++ ++extern int dwc_otg_set_param_dev_out_nak(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_dev_out_nak(dwc_otg_core_if_t * core_if); ++#define dwc_param_dev_out_nak_default 0 ++ ++extern int dwc_otg_set_param_cont_on_bna(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_cont_on_bna(dwc_otg_core_if_t * core_if); ++#define dwc_param_cont_on_bna_default 0 ++ ++extern int dwc_otg_set_param_ahb_single(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_ahb_single(dwc_otg_core_if_t * core_if); ++#define dwc_param_ahb_single_default 0 ++ ++extern int dwc_otg_set_param_otg_ver(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_param_otg_ver(dwc_otg_core_if_t * core_if); ++#define dwc_param_otg_ver_default 0 ++ ++/** @} */ ++ ++/** @name Access to registers and bit-fields */ ++ ++/** ++ * Dump core registers and SPRAM ++ */ ++extern void dwc_otg_dump_dev_registers(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_dump_spram(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_dump_host_registers(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_dump_global_registers(dwc_otg_core_if_t * _core_if); ++ ++/** ++ * Get host negotiation status. ++ */ ++extern uint32_t dwc_otg_get_hnpstatus(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get srp status ++ */ ++extern uint32_t dwc_otg_get_srpstatus(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Set hnpreq bit in the GOTGCTL register. ++ */ ++extern void dwc_otg_set_hnpreq(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get Content of SNPSID register. ++ */ ++extern uint32_t dwc_otg_get_gsnpsid(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get current mode. ++ * Returns 0 if in device mode, and 1 if in host mode. ++ */ ++extern uint32_t dwc_otg_get_mode(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get value of hnpcapable field in the GUSBCFG register ++ */ ++extern uint32_t dwc_otg_get_hnpcapable(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of hnpcapable field in the GUSBCFG register ++ */ ++extern void dwc_otg_set_hnpcapable(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of srpcapable field in the GUSBCFG register ++ */ ++extern uint32_t dwc_otg_get_srpcapable(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of srpcapable field in the GUSBCFG register ++ */ ++extern void dwc_otg_set_srpcapable(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of devspeed field in the DCFG register ++ */ ++extern uint32_t dwc_otg_get_devspeed(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of devspeed field in the DCFG register ++ */ ++extern void dwc_otg_set_devspeed(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get the value of busconnected field from the HPRT0 register ++ */ ++extern uint32_t dwc_otg_get_busconnected(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Gets the device enumeration Speed. ++ */ ++extern uint32_t dwc_otg_get_enumspeed(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get value of prtpwr field from the HPRT0 register ++ */ ++extern uint32_t dwc_otg_get_prtpower(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get value of flag indicating core state - hibernated or not ++ */ ++extern uint32_t dwc_otg_get_core_state(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Set value of prtpwr field from the HPRT0 register ++ */ ++extern void dwc_otg_set_prtpower(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of prtsusp field from the HPRT0 regsiter ++ */ ++extern uint32_t dwc_otg_get_prtsuspend(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of prtpwr field from the HPRT0 register ++ */ ++extern void dwc_otg_set_prtsuspend(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of ModeChTimEn field from the HCFG regsiter ++ */ ++extern uint32_t dwc_otg_get_mode_ch_tim(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of ModeChTimEn field from the HCFG regsiter ++ */ ++extern void dwc_otg_set_mode_ch_tim(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of Fram Interval field from the HFIR regsiter ++ */ ++extern uint32_t dwc_otg_get_fr_interval(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of Frame Interval field from the HFIR regsiter ++ */ ++extern void dwc_otg_set_fr_interval(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Set value of prtres field from the HPRT0 register ++ *FIXME Remove? ++ */ ++extern void dwc_otg_set_prtresume(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of rmtwkupsig bit in DCTL register ++ */ ++extern uint32_t dwc_otg_get_remotewakesig(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get value of besl_reject bit in DCTL register ++ */ ++ ++extern uint32_t dwc_otg_get_beslreject(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Set value of besl_reject bit in DCTL register ++ */ ++ ++extern void dwc_otg_set_beslreject(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of prt_sleep_sts field from the GLPMCFG register ++ */ ++extern uint32_t dwc_otg_get_lpm_portsleepstatus(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get value of rem_wkup_en field from the GLPMCFG register ++ */ ++extern uint32_t dwc_otg_get_lpm_remotewakeenabled(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get value of appl_resp field from the GLPMCFG register ++ */ ++extern uint32_t dwc_otg_get_lpmresponse(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of appl_resp field from the GLPMCFG register ++ */ ++extern void dwc_otg_set_lpmresponse(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of hsic_connect field from the GLPMCFG register ++ */ ++extern uint32_t dwc_otg_get_hsic_connect(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of hsic_connect field from the GLPMCFG register ++ */ ++extern void dwc_otg_set_hsic_connect(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of inv_sel_hsic field from the GLPMCFG register. ++ */ ++extern uint32_t dwc_otg_get_inv_sel_hsic(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of inv_sel_hsic field from the GLPMFG register. ++ */ ++extern void dwc_otg_set_inv_sel_hsic(dwc_otg_core_if_t * core_if, uint32_t val); ++/** ++ * Set value of hird_thresh field from the GLPMFG register. ++ */ ++extern void dwc_otg_set_hirdthresh(dwc_otg_core_if_t * core_if, uint32_t val); ++/** ++ * Get value of hird_thresh field from the GLPMFG register. ++ */ ++extern uint32_t dwc_otg_get_hirdthresh(dwc_otg_core_if_t * core_if); ++ ++ ++/* ++ * Some functions for accessing registers ++ */ ++ ++/** ++ * GOTGCTL register ++ */ ++extern uint32_t dwc_otg_get_gotgctl(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_gotgctl(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * GUSBCFG register ++ */ ++extern uint32_t dwc_otg_get_gusbcfg(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_gusbcfg(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * GRXFSIZ register ++ */ ++extern uint32_t dwc_otg_get_grxfsiz(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_grxfsiz(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * GNPTXFSIZ register ++ */ ++extern uint32_t dwc_otg_get_gnptxfsiz(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_gnptxfsiz(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++extern uint32_t dwc_otg_get_gpvndctl(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_gpvndctl(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * GGPIO register ++ */ ++extern uint32_t dwc_otg_get_ggpio(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_ggpio(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * GUID register ++ */ ++extern uint32_t dwc_otg_get_guid(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_guid(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * HPRT0 register ++ */ ++extern uint32_t dwc_otg_get_hprt0(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_hprt0(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * GHPTXFSIZE ++ */ ++extern uint32_t dwc_otg_get_hptxfsiz(dwc_otg_core_if_t * core_if); ++ ++/** @} */ ++ ++#endif /* __DWC_CORE_IF_H__ */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_dbg.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_dbg.h +new file mode 100644 +index 0000000..32c7d10 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_dbg.h +@@ -0,0 +1,113 @@ ++/* ========================================================================== ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef __DWC_OTG_DBG_H__ ++#define __DWC_OTG_DBG_H__ ++ ++/** @file ++ * This file defines debug levels. ++ * Debugging support vanishes in non-debug builds. ++ */ ++ ++/** ++ * The Debug Level bit-mask variable. ++ */ ++extern uint32_t g_dbg_lvl; ++/** ++ * Set the Debug Level variable. ++ */ ++static inline uint32_t SET_DEBUG_LEVEL(const uint32_t new) ++{ ++ uint32_t old = g_dbg_lvl; ++ g_dbg_lvl = new; ++ return old; ++} ++ ++/** When debug level has the DBG_CIL bit set, display CIL Debug messages. */ ++#define DBG_CIL (0x2) ++/** When debug level has the DBG_CILV bit set, display CIL Verbose debug ++ * messages */ ++#define DBG_CILV (0x20) ++/** When debug level has the DBG_PCD bit set, display PCD (Device) debug ++ * messages */ ++#define DBG_PCD (0x4) ++/** When debug level has the DBG_PCDV set, display PCD (Device) Verbose debug ++ * messages */ ++#define DBG_PCDV (0x40) ++/** When debug level has the DBG_HCD bit set, display Host debug messages */ ++#define DBG_HCD (0x8) ++/** When debug level has the DBG_HCDV bit set, display Verbose Host debug ++ * messages */ ++#define DBG_HCDV (0x80) ++/** When debug level has the DBG_HCD_URB bit set, display enqueued URBs in host ++ * mode. */ ++#define DBG_HCD_URB (0x800) ++ ++/** When debug level has any bit set, display debug messages */ ++#define DBG_ANY (0xFF) ++ ++/** All debug messages off */ ++#define DBG_OFF 0 ++ ++/** Prefix string for DWC_DEBUG print macros. */ ++#define USB_DWC "DWC_otg: " ++ ++/** ++ * Print a debug message when the Global debug level variable contains ++ * the bit defined in <code>lvl</code>. ++ * ++ * @param[in] lvl - Debug level, use one of the DBG_ constants above. ++ * @param[in] x - like printf ++ * ++ * Example:<p> ++ * <code> ++ * DWC_DEBUGPL( DBG_ANY, "%s(%p)\n", __func__, _reg_base_addr); ++ * </code> ++ * <br> ++ * results in:<br> ++ * <code> ++ * usb-DWC_otg: dwc_otg_cil_init(ca867000) ++ * </code> ++ */ ++#ifdef DEBUG ++ ++# define DWC_DEBUGPL(lvl, x...) do{ if ((lvl)&g_dbg_lvl)__DWC_DEBUG(USB_DWC x ); }while(0) ++# define DWC_DEBUGP(x...) DWC_DEBUGPL(DBG_ANY, x ) ++ ++# define CHK_DEBUG_LEVEL(level) ((level) & g_dbg_lvl) ++ ++#else ++ ++# define DWC_DEBUGPL(lvl, x...) do{}while(0) ++# define DWC_DEBUGP(x...) ++ ++# define CHK_DEBUG_LEVEL(level) (0) ++ ++#endif /*DEBUG*/ ++#endif +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_driver.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_driver.c +new file mode 100644 +index 0000000..7f291c4 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_driver.c +@@ -0,0 +1,812 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.c $ ++ * $Revision: #96 $ ++ * $Date: 2013/05/20 $ ++ * $Change: 2234037 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * The dwc_otg_driver module provides the initialization and cleanup entry ++ * points for the DWC_otg driver. This module will be dynamically installed ++ * after Linux is booted using the insmod command. When the module is ++ * installed, the dwc_otg_driver_init function is called. When the module is ++ * removed (using rmmod), the dwc_otg_driver_cleanup function is called. ++ * ++ * This module also defines a data structure for the dwc_otg_driver, which is ++ * used in conjunction with the standard ARM lm_device structure. These ++ * structures allow the OTG driver to comply with the standard Linux driver ++ * model in which devices and drivers are registered with a bus driver. This ++ * has the benefit that Linux can expose attributes of the driver and device ++ * in its special sysfs file system. Users can then read or write files in ++ * this file system to perform diagnostics on the driver components or the ++ * device. ++ */ ++#include <linux/clk.h> ++#include <linux/dma-mapping.h> ++#include <linux/err.h> ++#include <linux/kernel.h> ++#include <linux/hrtimer.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++ ++#include "dwc_otg_os_dep.h" ++#include "dwc_os.h" ++#include "dwc_otg_dbg.h" ++#include "dwc_otg_driver.h" ++#include "dwc_otg_attr.h" ++#include "dwc_otg_core_if.h" ++#include "dwc_otg_pcd_if.h" ++#include "dwc_otg_hcd_if.h" ++ ++ ++#define DWC_DRIVER_VERSION "3.00a 10-AUG-2012" ++ ++static const char driver_name[] = "hiudc"; ++ ++extern int pcd_init( struct platform_device *_dev , int irqnum); ++extern int hcd_init( struct platform_device *_dev ); ++extern int pcd_remove( struct platform_device *_dev ); ++extern void hcd_remove( struct platform_device *_dev ); ++extern void dwc_otg_adp_start(dwc_otg_core_if_t * core_if, uint8_t is_host); ++ ++/******************************************************************************/ ++ ++/* Encapsulate the module parameter settings */ ++ ++struct dwc_otg_driver_module_params { ++ int32_t opt; ++ int32_t otg_cap; ++ int32_t dma_enable; ++ int32_t dma_desc_enable; ++ int32_t dma_burst_size; ++ int32_t speed; ++ int32_t host_support_fs_ls_low_power; ++ int32_t host_ls_low_power_phy_clk; ++ int32_t enable_dynamic_fifo; ++ int32_t data_fifo_size; ++ int32_t dev_rx_fifo_size; ++ int32_t dev_nperio_tx_fifo_size; ++ uint32_t dev_perio_tx_fifo_size[MAX_PERIO_FIFOS]; ++ int32_t host_rx_fifo_size; ++ int32_t host_nperio_tx_fifo_size; ++ int32_t host_perio_tx_fifo_size; ++ int32_t max_transfer_size; ++ int32_t max_packet_count; ++ int32_t host_channels; ++ int32_t dev_endpoints; ++ int32_t phy_type; ++ int32_t phy_utmi_width; ++ int32_t phy_ulpi_ddr; ++ int32_t phy_ulpi_ext_vbus; ++ int32_t i2c_enable; ++ int32_t ulpi_fs_ls; ++ int32_t ts_dline; ++ int32_t en_multiple_tx_fifo; ++ uint32_t dev_tx_fifo_size[MAX_TX_FIFOS]; ++ uint32_t thr_ctl; ++ uint32_t tx_thr_length; ++ uint32_t rx_thr_length; ++ int32_t pti_enable; ++ int32_t mpi_enable; ++ int32_t lpm_enable; ++ int32_t besl_enable; ++ int32_t baseline_besl; ++ int32_t deep_besl; ++ int32_t ic_usb_cap; ++ int32_t ahb_thr_ratio; ++ int32_t power_down; ++ int32_t reload_ctl; ++ int32_t dev_out_nak; ++ int32_t cont_on_bna; ++ int32_t ahb_single; ++ int32_t otg_ver; ++ int32_t adp_enable; ++}; ++/******************************************************************************/ ++ ++static struct dwc_otg_driver_module_params dwc_otg_module_params = { ++ .opt = -1, ++ .otg_cap = 2, /*non-hnp/srp-capable*/ ++ .dma_enable = 1, /* enable */ ++ .dma_desc_enable = 1, ++ .dma_burst_size = -1, ++ .speed = -1,/*high-speed*/ ++ .host_support_fs_ls_low_power = -1, /* lowpower mode isn't supported */ ++ .host_ls_low_power_phy_clk = -1, ++ .enable_dynamic_fifo = -1, /* use coreconsultant fifo size */ ++ .data_fifo_size = -1, ++ .dev_rx_fifo_size = -1, ++ .dev_nperio_tx_fifo_size = -1, ++ .dev_perio_tx_fifo_size = { ++ /* dev_perio_tx_fifo_size_1 */ ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1 ++ /* 15 */ ++ }, ++ .host_rx_fifo_size = -1, ++ .host_nperio_tx_fifo_size = -1, ++ .host_perio_tx_fifo_size = -1, ++ .max_transfer_size = -1, ++ .max_packet_count = -1, ++ .host_channels = -1, ++ .dev_endpoints = -1, ++ .phy_type = -1,/*utmi+*/ ++ .phy_utmi_width = 8, ++ .phy_ulpi_ddr = -1, ++ .phy_ulpi_ext_vbus = -1, ++ .i2c_enable = -1, ++ .ulpi_fs_ls = -1, ++ .ts_dline = -1, ++ .en_multiple_tx_fifo = -1, ++ .dev_tx_fifo_size = { ++ /* dev_tx_fifo_size */ ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1 ++ /* 15 */ ++ }, ++ .thr_ctl = -1, ++ .tx_thr_length = -1, ++ .rx_thr_length = -1, ++ .pti_enable = -1, ++ .mpi_enable = -1, ++ .lpm_enable = -1, ++ .besl_enable = -1, ++ .baseline_besl = -1, ++ .deep_besl = -1, ++ .ic_usb_cap = -1, ++ .ahb_thr_ratio = -1, ++ .power_down = -1, ++ .reload_ctl = -1, ++ .dev_out_nak = -1, ++ .cont_on_bna = -1, ++ .ahb_single = -1, ++ .otg_ver = -1, ++ .adp_enable = -1, ++}; ++/******************************************************************************/ ++ ++/** ++ * This function is called during module intialization ++ * to pass module parameters to the DWC_OTG CORE. ++ */ ++static int set_parameters(dwc_otg_core_if_t * core_if) ++{ ++ int retval = 0; ++ int i; ++ ++ if (dwc_otg_module_params.otg_cap != -1) { ++ retval += ++ dwc_otg_set_param_otg_cap(core_if, ++ dwc_otg_module_params.otg_cap); ++ } ++ if (dwc_otg_module_params.dma_enable != -1) { ++ retval += ++ dwc_otg_set_param_dma_enable(core_if, ++ dwc_otg_module_params. ++ dma_enable); ++ } ++ if (dwc_otg_module_params.dma_desc_enable != -1) { ++ retval += ++ dwc_otg_set_param_dma_desc_enable(core_if, ++ dwc_otg_module_params. ++ dma_desc_enable); ++ } ++ if (dwc_otg_module_params.opt != -1) { ++ retval += ++ dwc_otg_set_param_opt(core_if, dwc_otg_module_params.opt); ++ } ++ if (dwc_otg_module_params.dma_burst_size != -1) { ++ retval += ++ dwc_otg_set_param_dma_burst_size(core_if, ++ dwc_otg_module_params. ++ dma_burst_size); ++ } ++ if (dwc_otg_module_params.host_support_fs_ls_low_power != -1) { ++ retval += ++ dwc_otg_set_param_host_support_fs_ls_low_power(core_if, ++ dwc_otg_module_params. ++ host_support_fs_ls_low_power); ++ } ++ if (dwc_otg_module_params.enable_dynamic_fifo != -1) { ++ retval += ++ dwc_otg_set_param_enable_dynamic_fifo(core_if, ++ dwc_otg_module_params. ++ enable_dynamic_fifo); ++ } ++ if (dwc_otg_module_params.data_fifo_size != -1) { ++ retval += ++ dwc_otg_set_param_data_fifo_size(core_if, ++ dwc_otg_module_params. ++ data_fifo_size); ++ } ++ if (dwc_otg_module_params.dev_rx_fifo_size != -1) { ++ retval += ++ dwc_otg_set_param_dev_rx_fifo_size(core_if, ++ dwc_otg_module_params. ++ dev_rx_fifo_size); ++ } ++ if (dwc_otg_module_params.dev_nperio_tx_fifo_size != -1) { ++ retval += ++ dwc_otg_set_param_dev_nperio_tx_fifo_size(core_if, ++ dwc_otg_module_params. ++ dev_nperio_tx_fifo_size); ++ } ++ if (dwc_otg_module_params.host_rx_fifo_size != -1) { ++ retval += ++ dwc_otg_set_param_host_rx_fifo_size(core_if, ++ dwc_otg_module_params.host_rx_fifo_size); ++ } ++ if (dwc_otg_module_params.host_nperio_tx_fifo_size != -1) { ++ retval += ++ dwc_otg_set_param_host_nperio_tx_fifo_size(core_if, ++ dwc_otg_module_params. ++ host_nperio_tx_fifo_size); ++ } ++ if (dwc_otg_module_params.host_perio_tx_fifo_size != -1) { ++ retval += ++ dwc_otg_set_param_host_perio_tx_fifo_size(core_if, ++ dwc_otg_module_params. ++ host_perio_tx_fifo_size); ++ } ++ if (dwc_otg_module_params.max_transfer_size != -1) { ++ retval += ++ dwc_otg_set_param_max_transfer_size(core_if, ++ dwc_otg_module_params. ++ max_transfer_size); ++ } ++ if (dwc_otg_module_params.max_packet_count != -1) { ++ retval += ++ dwc_otg_set_param_max_packet_count(core_if, ++ dwc_otg_module_params. ++ max_packet_count); ++ } ++ if (dwc_otg_module_params.host_channels != -1) { ++ retval += ++ dwc_otg_set_param_host_channels(core_if, ++ dwc_otg_module_params. ++ host_channels); ++ } ++ if (dwc_otg_module_params.dev_endpoints != -1) { ++ retval += ++ dwc_otg_set_param_dev_endpoints(core_if, ++ dwc_otg_module_params. ++ dev_endpoints); ++ } ++ if (dwc_otg_module_params.phy_type != -1) { ++ retval += ++ dwc_otg_set_param_phy_type(core_if, ++ dwc_otg_module_params.phy_type); ++ } ++ if (dwc_otg_module_params.speed != -1) { ++ retval += ++ dwc_otg_set_param_speed(core_if, ++ dwc_otg_module_params.speed); ++ } ++ if (dwc_otg_module_params.host_ls_low_power_phy_clk != -1) { ++ retval += ++ dwc_otg_set_param_host_ls_low_power_phy_clk(core_if, ++ dwc_otg_module_params. ++ host_ls_low_power_phy_clk); ++ } ++ if (dwc_otg_module_params.phy_ulpi_ddr != -1) { ++ retval += ++ dwc_otg_set_param_phy_ulpi_ddr(core_if, ++ dwc_otg_module_params. ++ phy_ulpi_ddr); ++ } ++ if (dwc_otg_module_params.phy_ulpi_ext_vbus != -1) { ++ retval += ++ dwc_otg_set_param_phy_ulpi_ext_vbus(core_if, ++ dwc_otg_module_params. ++ phy_ulpi_ext_vbus); ++ } ++ if (dwc_otg_module_params.phy_utmi_width != -1) { ++ retval += ++ dwc_otg_set_param_phy_utmi_width(core_if, ++ dwc_otg_module_params. ++ phy_utmi_width); ++ } ++ if (dwc_otg_module_params.ulpi_fs_ls != -1) { ++ retval += ++ dwc_otg_set_param_ulpi_fs_ls(core_if, ++ dwc_otg_module_params.ulpi_fs_ls); ++ } ++ if (dwc_otg_module_params.ts_dline != -1) { ++ retval += ++ dwc_otg_set_param_ts_dline(core_if, ++ dwc_otg_module_params.ts_dline); ++ } ++ if (dwc_otg_module_params.i2c_enable != -1) { ++ retval += ++ dwc_otg_set_param_i2c_enable(core_if, ++ dwc_otg_module_params. ++ i2c_enable); ++ } ++ if (dwc_otg_module_params.en_multiple_tx_fifo != -1) { ++ retval += ++ dwc_otg_set_param_en_multiple_tx_fifo(core_if, ++ dwc_otg_module_params. ++ en_multiple_tx_fifo); ++ } ++ for (i = 0; i < 15; i++) { ++ if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] != -1) { ++ retval += ++ dwc_otg_set_param_dev_perio_tx_fifo_size(core_if, ++ dwc_otg_module_params. ++ dev_perio_tx_fifo_size ++ [i], i); ++ } ++ } ++ ++ for (i = 0; i < 15; i++) { ++ if (dwc_otg_module_params.dev_tx_fifo_size[i] != -1) { ++ retval += dwc_otg_set_param_dev_tx_fifo_size(core_if, ++ dwc_otg_module_params. ++ dev_tx_fifo_size ++ [i], i); ++ } ++ } ++ if (dwc_otg_module_params.thr_ctl != -1) { ++ retval += ++ dwc_otg_set_param_thr_ctl(core_if, ++ dwc_otg_module_params.thr_ctl); ++ } ++ if (dwc_otg_module_params.mpi_enable != -1) { ++ retval += ++ dwc_otg_set_param_mpi_enable(core_if, ++ dwc_otg_module_params. ++ mpi_enable); ++ } ++ if (dwc_otg_module_params.pti_enable != -1) { ++ retval += ++ dwc_otg_set_param_pti_enable(core_if, ++ dwc_otg_module_params. ++ pti_enable); ++ } ++ if (dwc_otg_module_params.lpm_enable != -1) { ++ retval += ++ dwc_otg_set_param_lpm_enable(core_if, ++ dwc_otg_module_params. ++ lpm_enable); ++ } ++ if (dwc_otg_module_params.besl_enable != -1) { ++ retval += ++ dwc_otg_set_param_besl_enable(core_if, ++ dwc_otg_module_params. ++ besl_enable); ++ } ++ if (dwc_otg_module_params.baseline_besl != -1) { ++ retval += ++ dwc_otg_set_param_baseline_besl(core_if, ++ dwc_otg_module_params. ++ baseline_besl); ++ } ++ if (dwc_otg_module_params.deep_besl != -1) { ++ retval += ++ dwc_otg_set_param_deep_besl(core_if, ++ dwc_otg_module_params. ++ deep_besl); ++ } ++ if (dwc_otg_module_params.ic_usb_cap != -1) { ++ retval += ++ dwc_otg_set_param_ic_usb_cap(core_if, ++ dwc_otg_module_params. ++ ic_usb_cap); ++ } ++ if (dwc_otg_module_params.tx_thr_length != -1) { ++ retval += ++ dwc_otg_set_param_tx_thr_length(core_if, ++ dwc_otg_module_params.tx_thr_length); ++ } ++ if (dwc_otg_module_params.rx_thr_length != -1) { ++ retval += ++ dwc_otg_set_param_rx_thr_length(core_if, ++ dwc_otg_module_params. ++ rx_thr_length); ++ } ++ if (dwc_otg_module_params.ahb_thr_ratio != -1) { ++ retval += ++ dwc_otg_set_param_ahb_thr_ratio(core_if, ++ dwc_otg_module_params.ahb_thr_ratio); ++ } ++ if (dwc_otg_module_params.power_down != -1) { ++ retval += ++ dwc_otg_set_param_power_down(core_if, ++ dwc_otg_module_params.power_down); ++ } ++ if (dwc_otg_module_params.reload_ctl != -1) { ++ retval += ++ dwc_otg_set_param_reload_ctl(core_if, ++ dwc_otg_module_params.reload_ctl); ++ } ++ ++ if (dwc_otg_module_params.dev_out_nak != -1) { ++ retval += ++ dwc_otg_set_param_dev_out_nak(core_if, ++ dwc_otg_module_params.dev_out_nak); ++ } ++ ++ if (dwc_otg_module_params.cont_on_bna != -1) { ++ retval += ++ dwc_otg_set_param_cont_on_bna(core_if, ++ dwc_otg_module_params.cont_on_bna); ++ } ++ ++ if (dwc_otg_module_params.ahb_single != -1) { ++ retval += ++ dwc_otg_set_param_ahb_single(core_if, ++ dwc_otg_module_params.ahb_single); ++ } ++ ++ if (dwc_otg_module_params.otg_ver != -1) { ++ retval += ++ dwc_otg_set_param_otg_ver(core_if, ++ dwc_otg_module_params.otg_ver); ++ } ++ if (dwc_otg_module_params.adp_enable != -1) { ++ retval += ++ dwc_otg_set_param_adp_enable(core_if, ++ dwc_otg_module_params. ++ adp_enable); ++ } ++ return retval; ++} ++/******************************************************************************/ ++ ++/** ++ * This function is the top level interrupt handler for the Common ++ * (Device and host modes) interrupts. ++ */ ++static irqreturn_t dwc_otg_common_irq(int irq, void *dev) ++{ ++ int32_t retval = IRQ_NONE; ++ ++ retval = dwc_otg_handle_common_intr(dev); ++ ++ return IRQ_RETVAL(retval); ++} ++/******************************************************************************/ ++ ++static int hisi_udc_remove( struct platform_device *_dev) ++{ ++ ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); ++ ++ DWC_DEBUGPL(DBG_ANY, "%s(%p)\n", __func__, _dev); ++ ++ if (!otg_dev) { ++ /* Memory allocation for the dwc_otg_device failed. */ ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__); ++ return -1; ++ } ++ ++ if (otg_dev->pcd) { ++ pcd_remove(_dev); ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->pcd NULL!\n", __func__); ++ return -1; ++ } ++ ++ /* ++ * Free the IRQ ++ */ ++ if (otg_dev->common_irq_installed) { ++ free_irq(_dev->resource[1].start, otg_dev); ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "%s: There is no installed irq!\n", __func__); ++ return -1; ++ } ++ ++ if (otg_dev->core_if) { ++ dwc_otg_cil_remove(otg_dev->core_if); ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->core_if NULL!\n", __func__); ++ return -1; ++ } ++ ++ /* ++ * Return the memory. ++ */ ++ if (otg_dev->os_dep.base) { ++ iounmap(otg_dev->os_dep.base); ++ } ++ ++ clk_disable_unprepare(otg_dev->clk); ++ DWC_FREE(otg_dev); ++ /* ++ * Clear the drvdata pointer. ++ */ ++ platform_set_drvdata(_dev, NULL); ++ ++ return 0; ++ ++} ++/******************************************************************************/ ++ ++static int hisi_udc_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ dwc_otg_device_t *dwc_otg_device; ++ int irq; ++ int ret; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "no irq provided"); ++ return irq; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "no memory resource provided"); ++ return -ENXIO; ++ } ++ ++ dwc_otg_device = DWC_ALLOC(sizeof(dwc_otg_device_t)); ++ ++ if (!dwc_otg_device) { ++ dev_err(&pdev->dev, "kmalloc of dwc_otg_device failed\n"); ++ return -ENOMEM; ++ } ++ ++ memset(dwc_otg_device, 0, sizeof(*dwc_otg_device)); ++ dwc_otg_device->os_dep.reg_offset = 0xFFFFFFFF; ++ ++ /* ++ * Map the DWC_otg Core memory into virtual address space. ++ */ ++ dwc_otg_device->os_dep.res_start = res->start; ++ dwc_otg_device->os_dep.base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(dwc_otg_device->os_dep.base)) { ++ DWC_FREE(dwc_otg_device); ++ return -ENOMEM; ++ } ++ ++ dev_dbg(&pdev->dev, "base=0x%08x\n",(unsigned)dwc_otg_device->os_dep.base); ++ ++ ++ dwc_otg_device->clk = devm_clk_get(&pdev->dev, "clk"); ++ if (IS_ERR_OR_NULL(dwc_otg_device->clk)) { ++ dev_err(&pdev->dev, "get clock fail.\n"); ++ ret = PTR_ERR(dwc_otg_device->clk); ++ DWC_FREE(dwc_otg_device); ++ return ret; ++ } ++ clk_prepare_enable(dwc_otg_device->clk); ++ ++ /* ++ * Initialize driver data to point to the global DWC_otg ++ * Device structure. ++ */ ++ platform_set_drvdata(pdev, dwc_otg_device); ++ ++ ++ dwc_otg_device->core_if = dwc_otg_cil_init(dwc_otg_device->os_dep.base); ++ if (!dwc_otg_device->core_if) { ++ dev_err(&pdev->dev, "CIL initialization failed!\n"); ++ ret = -ENOMEM; ++ goto fail; ++ ++ } ++ ++ /* ++ * Attempt to ensure this device is really a DWC_otg Controller. ++ * Read and verify the SNPSID register contents. The value should be ++ * 0x45F42XXX or 0x45F42XXX, which corresponds to either "OT2" or "OTG3", ++ * as in "OTG version 2.XX" or "OTG version 3.XX". ++ */ ++ ++ if (((dwc_otg_get_gsnpsid(dwc_otg_device->core_if) & 0xFFFFF000) != 0x4F542000) && ++ ((dwc_otg_get_gsnpsid(dwc_otg_device->core_if) & 0xFFFFF000) != 0x4F543000)) { ++ dev_err(&pdev->dev, "Bad value for SNPSID: 0x%08x\n", ++ dwc_otg_get_gsnpsid(dwc_otg_device->core_if)); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* ++ * Validate parameter values. ++ */ ++ if (set_parameters(dwc_otg_device->core_if)) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ ++ /* ++ * Disable the global interrupt until all the interrupt ++ * handlers are installed. ++ */ ++ dwc_otg_disable_global_interrupts(dwc_otg_device->core_if); ++ ++ ++ /* ++ * Install the interrupt handler for the common interrupts before ++ * enabling common interrupts in core_init below. ++ */ ++ DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n", ++ pdev->resource[1].start); ++ ret = request_irq(irq, dwc_otg_common_irq, ++ IRQF_SHARED | IRQF_DISABLED | IRQ_LEVEL, "dwc_otg", ++ dwc_otg_device); ++ if (ret) { ++ dev_err(&pdev->dev,"request of irq%d failed\n",irq); ++ ret = -EBUSY; ++ goto fail; ++ } else { ++ dwc_otg_device->common_irq_installed = 1; ++ } ++ ++ ++ /* ++ * Initialize the DWC_otg core. ++ */ ++ dwc_otg_core_init(dwc_otg_device->core_if); ++ ++ /* ++ * Initialize the PCD ++ */ ++ ret = pcd_init(pdev, irq); ++ if (ret != 0) { ++ dev_err(&pdev->dev,"pcd_init failed\n"); ++ dwc_otg_device->pcd = NULL; ++ goto fail; ++ } ++ ++ ++ /* ++ * Enable the global interrupt after all the interrupt ++ * handlers are installed if there is no ADP support else ++ * perform initial actions required for Internal ADP logic. ++ */ ++ if (!dwc_otg_get_param_adp_enable(dwc_otg_device->core_if)) ++ dwc_otg_enable_global_interrupts(dwc_otg_device->core_if); ++ else ++ dwc_otg_adp_start(dwc_otg_device->core_if, ++ dwc_otg_is_host_mode(dwc_otg_device->core_if)); ++ ++ return 0; ++ ++fail: ++ hisi_udc_remove(pdev); ++ return ret; ++} ++/******************************************************************************/ ++ ++#ifdef CONFIG_PM ++static int hisi_udc_suspend(struct device *dev) ++{ ++ dwc_otg_device_t *dwc_otg_device = dev_get_drvdata(dev); ++ int rc = 0; ++ ++ dwc_otg_disable_global_interrupts(dwc_otg_device->core_if); ++ clk_disable_unprepare(dwc_otg_device->clk); ++ ++ return rc; ++} ++/******************************************************************************/ ++ ++static int hisi_udc_resume(struct device *dev) ++{ ++ dwc_otg_device_t *dwc_otg_device = dev_get_drvdata(dev); ++ ++ clk_prepare_enable(dwc_otg_device->clk); ++ dwc_otg_core_init(dwc_otg_device->core_if); ++ dwc_otg_enable_global_interrupts(dwc_otg_device->core_if); ++ return 0; ++} ++#else ++#define hisi_udc_suspend NULL; ++#define hisi_udc_resume NULL; ++#endif ++/******************************************************************************/ ++ ++static const struct dev_pm_ops hisi_udc_pmops = { ++ .suspend = hisi_udc_suspend, ++ .resume = hisi_udc_resume, ++#if defined(CONFIG_PM_HIBERNATE) || defined(CONFIG_HISI_SNAPSHOT_BOOT) ++ .freeze = hisi_udc_suspend, ++ .thaw = hisi_udc_resume, ++ .poweroff = hisi_udc_suspend, ++ .restore = hisi_udc_resume, ++#endif ++}; ++ ++static const struct of_device_id hisi_udc_ids[] = { ++ { .compatible = "hiudc", }, ++ { /* null */ } ++}; ++MODULE_DEVICE_TABLE(of, hisi_udc_ids); ++ ++static struct platform_driver hisi_udc_pltfrm_driver = { ++ .probe = hisi_udc_probe, ++ .remove = hisi_udc_remove, ++ .driver = { ++ .name = (char *)driver_name, ++ .owner = THIS_MODULE, ++ .of_match_table = hisi_udc_ids, ++ .pm = &hisi_udc_pmops, ++ }, ++}; ++/******************************************************************************/ ++ ++static int __init hisi_udc_module_init(void) ++{ ++ int ret; ++ ++ printk(KERN_INFO "%s: version %s\n", driver_name, ++ DWC_DRIVER_VERSION); ++ ++ ret = platform_driver_register(&hisi_udc_pltfrm_driver); ++ ++ return ret; ++} ++module_init(hisi_udc_module_init); ++/******************************************************************************/ ++ ++static void __exit hisi_udc_module_exit (void) ++{ ++ ++ platform_driver_unregister(&hisi_udc_pltfrm_driver); ++ ++ printk(KERN_INFO "%s module removed\n", driver_name); ++} ++module_exit(hisi_udc_module_exit); ++ ++MODULE_AUTHOR("Hisilicon"); ++MODULE_DESCRIPTION("Hisilcon USB Device Controller driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_driver.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_driver.h +new file mode 100644 +index 0000000..63df919 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_driver.h +@@ -0,0 +1,89 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.h $ ++ * $Revision: #19 $ ++ * $Date: 2010/11/15 $ ++ * $Change: 1627671 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef __DWC_OTG_DRIVER_H__ ++#define __DWC_OTG_DRIVER_H__ ++ ++/** @file ++ * This file contains the interface to the Linux driver. ++ */ ++#include "dwc_otg_os_dep.h" ++#include "dwc_otg_core_if.h" ++#include <linux/platform_device.h> ++ ++/* Type declarations */ ++struct dwc_otg_pcd; ++struct dwc_otg_hcd; ++ ++/** ++ * This structure is a wrapper that encapsulates the driver components used to ++ * manage a single DWC_otg controller. ++ */ ++typedef struct dwc_otg_device { ++ /** Structure containing OS-dependent stuff. KEEP THIS STRUCT AT THE ++ * VERY BEGINNING OF THE DEVICE STRUCT. OSes such as FreeBSD and NetBSD ++ * require this. */ ++ struct os_dependent os_dep; ++ ++ /** Pointer to the core interface structure. */ ++ dwc_otg_core_if_t *core_if; ++ ++ /** Pointer to the PCD structure. */ ++ struct dwc_otg_pcd *pcd; ++ ++ /** Pointer to the HCD structure. */ ++ struct dwc_otg_hcd *hcd; ++ ++ struct clk *clk; ++ ++ /** Flag to indicate whether the common IRQ handler is installed. */ ++ uint8_t common_irq_installed; ++ ++} dwc_otg_device_t; ++ ++/*We must clear S3C24XX_EINTPEND external interrupt register ++ * because after clearing in this register trigerred IRQ from ++ * H/W core in kernel interrupt can be occured again before OTG ++ * handlers clear all IRQ sources of Core registers because of ++ * timing latencies and Low Level IRQ Type. ++ */ ++#ifdef CONFIG_MACH_IPMATE ++#define S3C2410X_CLEAR_EINTPEND() \ ++do { \ ++ __raw_writel(1UL << 11,S3C24XX_EINTPEND); \ ++} while (0) ++#else ++#define S3C2410X_CLEAR_EINTPEND() do { } while (0) ++#endif ++ ++#endif +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd.c +new file mode 100644 +index 0000000..baebe82 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd.c +@@ -0,0 +1,3431 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.c $ ++ * $Revision: #110 $ ++ * $Date: 2013/05/19 $ ++ * $Change: 2234022 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++/** @file ++ * This file implements HCD Core. All code in this file is portable and doesn't ++ * use any OS specific functions. ++ * Interface provided by HCD Core is defined in <code><hcd_if.h></code> ++ * header file. ++ */ ++ ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void) ++{ ++ return DWC_ALLOC(sizeof(dwc_otg_hcd_t)); ++} ++ ++/** ++ * Connection timeout function. An OTG host is required to display a ++ * message if the device does not connect within 10 seconds. ++ */ ++void dwc_otg_hcd_connect_timeout(void *ptr) ++{ ++ dwc_otg_hcd_t *hcd; ++ gpwrdn_data_t gpwrdn; ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, ptr); ++ DWC_PRINTF("Connect Timeout\n"); ++ __DWC_ERROR("Device Not Connected/Responding\n"); ++ /** Remove buspower after 10s */ ++ hcd = ptr; ++ if (hcd->core_if->otg_ver) ++ dwc_otg_set_prtpower(hcd->core_if, 0); ++ if (hcd->core_if->adp_enable && !hcd->core_if->adp.probe_enabled) { ++ cil_hcd_disconnect(hcd->core_if); ++ gpwrdn.d32 = 0; ++ /* Enable Power Down Logic */ ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ gpwrdn.b.dis_vbus = 1; ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ ++ /* Unmask SRP detected interrupt from Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.srp_det_msk = 1; ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ ++ dwc_mdelay(220); ++ dwc_otg_adp_probe_start(hcd->core_if); ++ } ++} ++ ++#ifdef DEBUG ++static void dump_channel_info(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ if (qh->channel != NULL) { ++ dwc_hc_t *hc = qh->channel; ++ dwc_list_link_t *item; ++ dwc_otg_qh_t *qh_item; ++ int num_channels = hcd->core_if->core_params->host_channels; ++ int i; ++ ++ dwc_otg_hc_regs_t *hc_regs; ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ hctsiz_data_t hctsiz; ++ uint32_t hcdma; ++ ++ hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt); ++ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz); ++ hcdma = DWC_READ_REG32(&hc_regs->hcdma); ++ ++ DWC_PRINTF(" Assigned to channel %p:\n", hc); ++ DWC_PRINTF(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, ++ hcsplt.d32); ++ DWC_PRINTF(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, ++ hcdma); ++ DWC_PRINTF(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n", ++ hc->dev_addr, hc->ep_num, hc->ep_is_in); ++ DWC_PRINTF(" ep_type: %d\n", hc->ep_type); ++ DWC_PRINTF(" max_packet: %d\n", hc->max_packet); ++ DWC_PRINTF(" data_pid_start: %d\n", hc->data_pid_start); ++ DWC_PRINTF(" xfer_started: %d\n", hc->xfer_started); ++ DWC_PRINTF(" halt_status: %d\n", hc->halt_status); ++ DWC_PRINTF(" xfer_buff: %p\n", hc->xfer_buff); ++ DWC_PRINTF(" xfer_len: %d\n", hc->xfer_len); ++ DWC_PRINTF(" qh: %p\n", hc->qh); ++ DWC_PRINTF(" NP inactive sched:\n"); ++ DWC_LIST_FOREACH(item, &hcd->non_periodic_sched_inactive) { ++ qh_item = ++ DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry); ++ DWC_PRINTF(" %p\n", qh_item); ++ } ++ DWC_PRINTF(" NP active sched:\n"); ++ DWC_LIST_FOREACH(item, &hcd->non_periodic_sched_active) { ++ qh_item = ++ DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry); ++ DWC_PRINTF(" %p\n", qh_item); ++ } ++ DWC_PRINTF(" Channels: \n"); ++ for (i = 0; i < num_channels; i++) { ++ dwc_hc_t *hc = hcd->hc_ptr_array[i]; ++ DWC_PRINTF(" %2d: %p\n", i, hc); ++ } ++ } ++} ++#endif /* DEBUG */ ++ ++/** ++ * Work queue function for starting the HCD when A-Cable is connected. ++ * The hcd_start() must be called in a process context. ++ */ ++static void hcd_start_func(void *_vp) ++{ ++ dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) _vp; ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s() %p\n", __func__, hcd); ++ if (hcd) { ++ hcd->fops->start(hcd); ++ } ++} ++ ++static void del_xfer_timers(dwc_otg_hcd_t * hcd) ++{ ++#ifdef DEBUG ++ int i; ++ int num_channels = hcd->core_if->core_params->host_channels; ++ for (i = 0; i < num_channels; i++) { ++ DWC_TIMER_CANCEL(hcd->core_if->hc_xfer_timer[i]); ++ } ++#endif ++} ++ ++static void del_timers(dwc_otg_hcd_t * hcd) ++{ ++ del_xfer_timers(hcd); ++ DWC_TIMER_CANCEL(hcd->conn_timer); ++} ++ ++/** ++ * Processes all the URBs in a single list of QHs. Completes them with ++ * -ETIMEDOUT and frees the QTD. ++ */ ++static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) ++{ ++ dwc_list_link_t *qh_item; ++ dwc_otg_qh_t *qh; ++ dwc_otg_qtd_t *qtd, *qtd_tmp; ++ ++ DWC_LIST_FOREACH(qh_item, qh_list) { ++ qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry); ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, ++ &qh->qtd_list, qtd_list_entry) { ++ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ if (qtd->urb != NULL) { ++ if(!qtd->urb->priv) { ++ DWC_ERROR("urb->priv is NULL !!!!\n"); ++ return; ++ } ++ if(!hcd->fops) ++ DWC_ERROR("hcd->fops is NULL !!!!!\n"); ++ if(!hcd->fops->complete) ++ DWC_ERROR("fops->complete is NULL !!!!\n"); ++ hcd->fops->complete(hcd, qtd->urb->priv, ++ qtd->urb, -DWC_E_TIMEOUT); ++ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); ++ } ++ ++ } ++ } ++} ++ ++/** ++ * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic ++ * and periodic schedules. The QTD associated with each URB is removed from ++ * the schedule and freed. This function may be called when a disconnect is ++ * detected or when the HCD is being stopped. ++ */ ++static void kill_all_urbs(dwc_otg_hcd_t * hcd) ++{ ++ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_inactive); ++ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_active); ++ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_inactive); ++ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_ready); ++ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_assigned); ++ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_queued); ++} ++ ++/** ++ * Start the connection timer. An OTG host is required to display a ++ * message if the device does not connect within 10 seconds. The ++ * timer is deleted if a port connect interrupt occurs before the ++ * timer expires. ++ */ ++static void dwc_otg_hcd_start_connect_timer(dwc_otg_hcd_t * hcd) ++{ ++ DWC_TIMER_SCHEDULE(hcd->conn_timer, 10000 /* 10 secs */ ); ++} ++ ++/** ++ * HCD Callback function for disconnect of the HCD. ++ * ++ * @param p void pointer to the <code>struct usb_hcd</code> ++ */ ++static int32_t dwc_otg_hcd_session_start_cb(void *p) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd; ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p); ++ dwc_otg_hcd = p; ++ dwc_otg_hcd_start_connect_timer(dwc_otg_hcd); ++ return 1; ++} ++ ++/** ++ * HCD Callback function for starting the HCD when A-Cable is ++ * connected. ++ * ++ * @param p void pointer to the <code>struct usb_hcd</code> ++ */ ++static int32_t dwc_otg_hcd_start_cb(void *p) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = p; ++ dwc_otg_core_if_t *core_if; ++ hprt0_data_t hprt0; ++ uint32_t timeout = 50; ++ ++ core_if = dwc_otg_hcd->core_if; ++ /**@todo vahrama: Check the timeout value for OTG 2.0 */ ++ if (core_if->otg_ver) ++ timeout = 25; ++ if (core_if->op_state == B_HOST) { ++ /* ++ * Reset the port. During a HNP mode switch the reset ++ * needs to occur within 1ms and have a duration of at ++ * least 50ms. ++ */ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtrst = 1; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ if (core_if->otg_ver) { ++ dwc_mdelay(60); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtrst = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ } ++ } ++ DWC_WORKQ_SCHEDULE_DELAYED(core_if->wq_otg, ++ hcd_start_func, dwc_otg_hcd, timeout, ++ "start hcd"); ++ ++ return 1; ++} ++ ++/** ++ * HCD Callback function for disconnect of the HCD. ++ * ++ * @param p void pointer to the <code>struct usb_hcd</code> ++ */ ++static int32_t dwc_otg_hcd_disconnect_cb(void *p) ++{ ++ gintsts_data_t intr; ++ dwc_otg_hcd_t *dwc_otg_hcd = p; ++ ++ /* ++ * Set status flags for the hub driver. ++ */ ++ dwc_otg_hcd->flags.b.port_connect_status_change = 1; ++ dwc_otg_hcd->flags.b.port_connect_status = 0; ++ ++ /* ++ * Shutdown any transfers in process by clearing the Tx FIFO Empty ++ * interrupt mask and status bits and disabling subsequent host ++ * channel interrupts. ++ */ ++ intr.d32 = 0; ++ intr.b.nptxfempty = 1; ++ intr.b.ptxfempty = 1; ++ intr.b.hcintr = 1; ++ DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, ++ intr.d32, 0); ++ DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintsts, ++ intr.d32, 0); ++ ++ /* ++ * Turn off the vbus power only if the core has transitioned to device ++ * mode. If still in host mode, need to keep power on to detect a ++ * reconnection. ++ */ ++ if (dwc_otg_is_device_mode(dwc_otg_hcd->core_if)) { ++ if (dwc_otg_hcd->core_if->op_state != A_SUSPEND) { ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++ DWC_PRINTF("Disconnect: PortPower off\n"); ++ hprt0.b.prtpwr = 0; ++ DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0, ++ hprt0.d32); ++ } ++ /** Delete timers if become device */ ++ del_timers(dwc_otg_hcd); ++ dwc_otg_disable_host_interrupts(dwc_otg_hcd->core_if); ++ } ++ ++ /* Respond with an error status to all URBs in the schedule. */ ++ kill_all_urbs(dwc_otg_hcd); ++ ++ if (dwc_otg_is_host_mode(dwc_otg_hcd->core_if)) { ++ /* Clean up any host channels that were in use. */ ++ int num_channels; ++ int i; ++ dwc_hc_t *channel; ++ dwc_otg_hc_regs_t *hc_regs; ++ hcchar_data_t hcchar; ++ ++ if (dwc_otg_hcd->core_if->otg_ver == 1) ++ del_xfer_timers(dwc_otg_hcd); ++ else ++ del_timers(dwc_otg_hcd); ++ ++ num_channels = dwc_otg_hcd->core_if->core_params->host_channels; ++ ++ if (!dwc_otg_hcd->core_if->dma_enable) { ++ /* Flush out any channel requests in slave mode. */ ++ for (i = 0; i < num_channels; i++) { ++ channel = dwc_otg_hcd->hc_ptr_array[i]; ++ if (DWC_CIRCLEQ_EMPTY_ENTRY ++ (channel, hc_list_entry)) { ++ hc_regs = ++ dwc_otg_hcd->core_if-> ++ host_if->hc_regs[i]; ++ hcchar.d32 = ++ DWC_READ_REG32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ hcchar.b.chen = 0; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ DWC_WRITE_REG32 ++ (&hc_regs->hcchar, ++ hcchar.d32); ++ } ++ } ++ } ++ } ++ ++ for (i = 0; i < num_channels; i++) { ++ channel = dwc_otg_hcd->hc_ptr_array[i]; ++ if (DWC_CIRCLEQ_EMPTY_ENTRY(channel, hc_list_entry)) { ++ hc_regs = ++ dwc_otg_hcd->core_if->host_if->hc_regs[i]; ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ /* Halt the channel. */ ++ hcchar.b.chdis = 1; ++ DWC_WRITE_REG32(&hc_regs->hcchar, ++ hcchar.d32); ++ } ++ ++ dwc_otg_hc_cleanup(dwc_otg_hcd->core_if, ++ channel); ++ DWC_CIRCLEQ_INSERT_TAIL ++ (&dwc_otg_hcd->free_hc_list, channel, ++ hc_list_entry); ++ /* ++ * Added for Descriptor DMA to prevent channel double cleanup ++ * in release_channel_ddma(). Which called from ep_disable ++ * when device disconnect. ++ */ ++ channel->qh = NULL; ++ } ++ } ++ } ++ ++ if (dwc_otg_hcd->fops->disconnect) { ++ dwc_otg_hcd->fops->disconnect(dwc_otg_hcd); ++ } ++ ++ return 1; ++} ++ ++/** ++ * HCD Callback function for stopping the HCD. ++ * ++ * @param p void pointer to the <code>struct usb_hcd</code> ++ */ ++static int32_t dwc_otg_hcd_stop_cb(void *p) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = p; ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p); ++ dwc_otg_hcd_stop(dwc_otg_hcd); ++ return 1; ++} ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++/** ++ * HCD Callback function for sleep of HCD. ++ * ++ * @param p void pointer to the <code>struct usb_hcd</code> ++ */ ++static int dwc_otg_hcd_sleep_cb(void *p) ++{ ++ dwc_otg_hcd_t *hcd = p; ++ ++ dwc_otg_hcd_free_hc_from_lpm(hcd); ++ ++ return 0; ++} ++#endif ++ ++/** ++ * HCD Callback function for Remote Wakeup. ++ * ++ * @param p void pointer to the <code>struct usb_hcd</code> ++ */ ++static int dwc_otg_hcd_rem_wakeup_cb(void *p) ++{ ++ dwc_otg_hcd_t *hcd = p; ++ ++ if (hcd->core_if->lx_state == DWC_OTG_L2) { ++ hcd->flags.b.port_suspend_change = 1; ++ } ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ else { ++ hcd->flags.b.port_l1_change = 1; ++ } ++#endif ++ return 0; ++} ++ ++/** ++ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are ++ * stopped. ++ */ ++void dwc_otg_hcd_stop(dwc_otg_hcd_t * hcd) ++{ ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD STOP\n"); ++ ++ /* ++ * The root hub should be disconnected before this function is called. ++ * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue) ++ * and the QH lists (via ..._hcd_endpoint_disable). ++ */ ++ ++ /* Turn off all host-specific interrupts. */ ++ dwc_otg_disable_host_interrupts(hcd->core_if); ++ ++ /* Turn off the vbus power */ ++ DWC_PRINTF("PortPower off\n"); ++ hprt0.b.prtpwr = 0; ++ DWC_WRITE_REG32(hcd->core_if->host_if->hprt0, hprt0.d32); ++ dwc_mdelay(1); ++} ++ ++int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, ++ dwc_otg_hcd_urb_t * dwc_otg_urb, void **ep_handle, ++ int atomic_alloc) ++{ ++ dwc_irqflags_t flags; ++ int retval = 0; ++ dwc_otg_qtd_t *qtd; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ if (!hcd->flags.b.port_connect_status) { ++ /* No longer connected. */ ++ DWC_ERROR("Not connected\n"); ++ return -DWC_E_NO_DEVICE; ++ } ++ ++ qtd = dwc_otg_hcd_qtd_create(dwc_otg_urb, atomic_alloc); ++ if (qtd == NULL) { ++ DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n"); ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ retval = ++ dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc); ++ if (retval < 0) { ++ DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. " ++ "Error status %d\n", retval); ++ dwc_otg_hcd_qtd_free(qtd); ++ } else { ++ qtd->qh = *ep_handle; ++ } ++ intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); ++ if (!intr_mask.b.sofintr && retval == 0) { ++ dwc_otg_transaction_type_e tr_type; ++ if ((qtd->qh->ep_type == UE_BULK) ++ && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) { ++ /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ ++ return 0; ++ } ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ tr_type = dwc_otg_hcd_select_transactions(hcd); ++ if (tr_type != DWC_OTG_TRANSACTION_NONE) { ++ dwc_otg_hcd_queue_transactions(hcd, tr_type); ++ } ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ } ++ ++ return retval; ++} ++ ++int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, ++ dwc_otg_hcd_urb_t * dwc_otg_urb) ++{ ++ dwc_otg_qh_t *qh; ++ dwc_otg_qtd_t *urb_qtd; ++ ++ urb_qtd = dwc_otg_urb->qtd; ++ qh = urb_qtd->qh; ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ if (urb_qtd->in_process) { ++ dump_channel_info(hcd, qh); ++ } ++ } ++#endif ++ if (urb_qtd->in_process && qh->channel) { ++ /* The QTD is in process (it has been assigned to a channel). */ ++ if (hcd->flags.b.port_connect_status) { ++ /* ++ * If still connected (i.e. in host mode), halt the ++ * channel so it can be used for other transfers. If ++ * no longer connected, the host registers can't be ++ * written to halt the channel since the core is in ++ * device mode. ++ */ ++ dwc_otg_hc_halt(hcd->core_if, qh->channel, ++ DWC_OTG_HC_XFER_URB_DEQUEUE); ++ } ++ } ++ ++ /* ++ * Free the QTD and clean up the associated QH. Leave the QH in the ++ * schedule if it has any remaining QTDs. ++ */ ++ ++ if (!hcd->core_if->dma_desc_enable) { ++ uint8_t b = urb_qtd->in_process; ++ dwc_otg_hcd_qtd_remove_and_free(hcd, urb_qtd, qh); ++ if (b) { ++ dwc_otg_hcd_qh_deactivate(hcd, qh, 0); ++ qh->channel = NULL; ++ } else if (DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ } ++ } else { ++ dwc_otg_hcd_qtd_remove_and_free(hcd, urb_qtd, qh); ++ } ++ return 0; ++} ++ ++int dwc_otg_hcd_endpoint_disable(dwc_otg_hcd_t * hcd, void *ep_handle, ++ int retry) ++{ ++ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle; ++ int retval = 0; ++ dwc_irqflags_t flags; ++ ++ if (retry < 0) { ++ retval = -DWC_E_INVALID; ++ goto done; ++ } ++ ++ if (!qh) { ++ retval = -DWC_E_INVALID; ++ goto done; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ ++ while (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list) && retry) { ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ retry--; ++ dwc_msleep(5); ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ } ++ ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ /* ++ * Split dwc_otg_hcd_qh_remove_and_free() into qh_remove ++ * and qh_free to prevent stack dump on DWC_DMA_FREE() with ++ * irq_disabled (spinlock_irqsave) in dwc_otg_hcd_desc_list_free() ++ * and dwc_otg_hcd_frame_list_alloc(). ++ */ ++ dwc_otg_hcd_qh_free(hcd, qh); ++ ++done: ++ return retval; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) ++int dwc_otg_hcd_endpoint_reset(dwc_otg_hcd_t * hcd, void *ep_handle) ++{ ++ int retval = 0; ++ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle; ++ if (!qh) ++ return -DWC_E_INVALID; ++ ++ qh->data_toggle = DWC_OTG_HC_PID_DATA0; ++ return retval; ++} ++#endif ++ ++/** ++ * HCD Callback structure for handling mode switching. ++ */ ++static dwc_otg_cil_callbacks_t hcd_cil_callbacks = { ++ .start = dwc_otg_hcd_start_cb, ++ .stop = dwc_otg_hcd_stop_cb, ++ .disconnect = dwc_otg_hcd_disconnect_cb, ++ .session_start = dwc_otg_hcd_session_start_cb, ++ .resume_wakeup = dwc_otg_hcd_rem_wakeup_cb, ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ .sleep = dwc_otg_hcd_sleep_cb, ++#endif ++ .p = 0, ++}; ++ ++/** ++ * Reset tasklet function ++ */ ++static void reset_tasklet_func(void *data) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t *) data; ++ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; ++ hprt0_data_t hprt0; ++ ++ DWC_DEBUGPL(DBG_HCDV, "USB RESET tasklet called\n"); ++ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtrst = 1; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ dwc_mdelay(60); ++ ++ hprt0.b.prtrst = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ dwc_otg_hcd->flags.b.port_reset_change = 1; ++} ++ ++static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) ++{ ++ dwc_list_link_t *item; ++ dwc_otg_qh_t *qh; ++ dwc_irqflags_t flags; ++ ++ if (!qh_list->next) { ++ /* The list hasn't been initialized yet. */ ++ return; ++ } ++ /* ++ * Hold spinlock here. Not needed in that case if bellow ++ * function is being called from ISR ++ */ ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ /* Ensure there are no QTDs or URBs left. */ ++ kill_urbs_in_qh_list(hcd, qh_list); ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ ++ DWC_LIST_FOREACH(item, qh_list) { ++ qh = DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry); ++ dwc_otg_hcd_qh_remove_and_free(hcd, qh); ++ } ++} ++ ++/** ++ * Exit from Hibernation if Host did not detect SRP from connected SRP capable ++ * Device during SRP time by host power up. ++ */ ++void dwc_otg_hcd_power_up(void *ptr) ++{ ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr; ++ ++ DWC_PRINTF("%s called\n", __FUNCTION__); ++ ++ if (!core_if->hibernation_suspend) { ++ DWC_PRINTF("Already exited from Hibernation\n"); ++ return; ++ } ++ ++ /* Switch on the voltage to the core */ ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Reset the core */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Disable power clamps */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnclmp = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ /* Remove reset the core signal */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnrstn = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Disable PMU interrupt */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ core_if->hibernation_suspend = 0; ++ ++ /* Disable PMU */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ dwc_udelay(10); ++ ++ /* Enable VBUS */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.dis_vbus = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0); ++ ++ core_if->op_state = A_HOST; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_hcd_start(core_if); ++} ++ ++/** ++ * Frees secondary storage associated with the dwc_otg_hcd structure contained ++ * in the struct usb_hcd field. ++ */ ++static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ int i; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD FREE\n"); ++ ++ del_timers(dwc_otg_hcd); ++ ++ /* Free memory for QH/QTD lists */ ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_inactive); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_active); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_inactive); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_ready); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_assigned); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_queued); ++ ++ /* Free memory for the host channels. */ ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ dwc_hc_t *hc = dwc_otg_hcd->hc_ptr_array[i]; ++ ++#ifdef DEBUG ++ if (dwc_otg_hcd->core_if->hc_xfer_timer[i]) { ++ DWC_TIMER_FREE(dwc_otg_hcd->core_if->hc_xfer_timer[i]); ++ } ++#endif ++ if (hc != NULL) { ++ DWC_DEBUGPL(DBG_HCDV, "HCD Free channel #%i, hc=%p\n", ++ i, hc); ++ DWC_FREE(hc); ++ } ++ } ++ ++ if (dwc_otg_hcd->core_if->dma_enable) { ++ if (dwc_otg_hcd->status_buf_dma) { ++ DWC_DMA_FREE(DWC_OTG_HCD_STATUS_BUF_SIZE, ++ dwc_otg_hcd->status_buf, ++ dwc_otg_hcd->status_buf_dma); ++ } ++ } else if (dwc_otg_hcd->status_buf != NULL) { ++ DWC_FREE(dwc_otg_hcd->status_buf); ++ } ++ DWC_SPINLOCK_FREE(dwc_otg_hcd->lock); ++ /* Set core_if's lock pointer to NULL */ ++ dwc_otg_hcd->core_if->lock = NULL; ++ ++ DWC_TIMER_FREE(dwc_otg_hcd->conn_timer); ++ DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet); ++ ++#ifdef DWC_DEV_SRPCAP ++ if (dwc_otg_hcd->core_if->power_down == 2 && ++ dwc_otg_hcd->core_if->pwron_timer) { ++ DWC_TIMER_FREE(dwc_otg_hcd->core_if->pwron_timer); ++ } ++#endif ++ DWC_FREE(dwc_otg_hcd); ++} ++ ++int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) ++{ ++ int retval = 0; ++ int num_channels; ++ int i; ++ dwc_hc_t *channel; ++ ++ hcd->lock = DWC_SPINLOCK_ALLOC(); ++ if (!hcd->lock) { ++ DWC_ERROR("Could not allocate lock for pcd"); ++ DWC_FREE(hcd); ++ retval = -DWC_E_NO_MEMORY; ++ goto out; ++ } ++ hcd->core_if = core_if; ++ ++ /* Register the HCD CIL Callbacks */ ++ dwc_otg_cil_register_hcd_callbacks(hcd->core_if, ++ &hcd_cil_callbacks, hcd); ++ ++ /* Initialize the non-periodic schedule. */ ++ DWC_LIST_INIT(&hcd->non_periodic_sched_inactive); ++ DWC_LIST_INIT(&hcd->non_periodic_sched_active); ++ ++ /* Initialize the periodic schedule. */ ++ DWC_LIST_INIT(&hcd->periodic_sched_inactive); ++ DWC_LIST_INIT(&hcd->periodic_sched_ready); ++ DWC_LIST_INIT(&hcd->periodic_sched_assigned); ++ DWC_LIST_INIT(&hcd->periodic_sched_queued); ++ ++ /* ++ * Create a host channel descriptor for each host channel implemented ++ * in the controller. Initialize the channel descriptor array. ++ */ ++ DWC_CIRCLEQ_INIT(&hcd->free_hc_list); ++ num_channels = hcd->core_if->core_params->host_channels; ++ DWC_MEMSET(hcd->hc_ptr_array, 0, sizeof(hcd->hc_ptr_array)); ++ for (i = 0; i < num_channels; i++) { ++ channel = DWC_ALLOC(sizeof(dwc_hc_t)); ++ if (channel == NULL) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: host channel allocation failed\n", ++ __func__); ++ dwc_otg_hcd_free(hcd); ++ goto out; ++ } ++ channel->hc_num = i; ++ hcd->hc_ptr_array[i] = channel; ++#ifdef DEBUG ++ hcd->core_if->hc_xfer_timer[i] = ++ DWC_TIMER_ALLOC("hc timer", hc_xfer_timeout, ++ &hcd->core_if->hc_xfer_info[i]); ++#endif ++ DWC_DEBUGPL(DBG_HCDV, "HCD Added channel #%d, hc=%p\n", i, ++ channel); ++ } ++ ++ /* Initialize the Connection timeout timer. */ ++ hcd->conn_timer = DWC_TIMER_ALLOC("Connection timer", ++ dwc_otg_hcd_connect_timeout, hcd); ++ ++ /* Initialize reset tasklet. */ ++ hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd); ++#ifdef DWC_DEV_SRPCAP ++ if (hcd->core_if->power_down == 2) { ++ /* Initialize Power on timer for Host power up in case hibernation */ ++ hcd->core_if->pwron_timer = DWC_TIMER_ALLOC("PWRON TIMER", ++ dwc_otg_hcd_power_up, core_if); ++ } ++#endif ++ ++ /* ++ * Allocate space for storing data on status transactions. Normally no ++ * data is sent, but this space acts as a bit bucket. This must be ++ * done after usb_add_hcd since that function allocates the DMA buffer ++ * pool. ++ */ ++ if (hcd->core_if->dma_enable) { ++ hcd->status_buf = ++ DWC_DMA_ALLOC(DWC_OTG_HCD_STATUS_BUF_SIZE, ++ &hcd->status_buf_dma); ++ } else { ++ hcd->status_buf = DWC_ALLOC(DWC_OTG_HCD_STATUS_BUF_SIZE); ++ } ++ if (!hcd->status_buf) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: status_buf allocation failed\n", __func__); ++ dwc_otg_hcd_free(hcd); ++ goto out; ++ } ++ ++ hcd->otg_port = 1; ++ hcd->frame_list = NULL; ++ hcd->frame_list_dma = 0; ++ hcd->periodic_qh_count = 0; ++out: ++ return retval; ++} ++ ++void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd) ++{ ++ /* Turn off all host-specific interrupts. */ ++ dwc_otg_disable_host_interrupts(hcd->core_if); ++ ++ dwc_otg_hcd_free(hcd); ++} ++ ++/** ++ * Initializes dynamic portions of the DWC_otg HCD state. ++ */ ++static void dwc_otg_hcd_reinit(dwc_otg_hcd_t * hcd) ++{ ++ int num_channels; ++ int i; ++ dwc_hc_t *channel; ++ dwc_hc_t *channel_tmp; ++ ++ hcd->flags.d32 = 0; ++ ++ hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active; ++ hcd->non_periodic_channels = 0; ++ hcd->periodic_channels = 0; ++ ++ /* ++ * Put all channels in the free channel list and clean up channel ++ * states. ++ */ ++ DWC_CIRCLEQ_FOREACH_SAFE(channel, channel_tmp, ++ &hcd->free_hc_list, hc_list_entry) { ++ DWC_CIRCLEQ_REMOVE(&hcd->free_hc_list, channel, hc_list_entry); ++ } ++ ++ num_channels = hcd->core_if->core_params->host_channels; ++ for (i = 0; i < num_channels; i++) { ++ channel = hcd->hc_ptr_array[i]; ++ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, channel, ++ hc_list_entry); ++ dwc_otg_hc_cleanup(hcd->core_if, channel); ++ } ++ ++ /* Initialize the DWC core for host mode operation. */ ++ dwc_otg_core_host_init(hcd->core_if); ++ ++ /* Set core_if's lock pointer to the hcd->lock */ ++ hcd->core_if->lock = hcd->lock; ++} ++ ++/** ++ * Assigns transactions from a QTD to a free host channel and initializes the ++ * host channel to perform the transactions. The host channel is removed from ++ * the free list. ++ * ++ * @param hcd The HCD state structure. ++ * @param qh Transactions from the first QTD for this QH are selected and ++ * assigned to a free host channel. ++ */ ++static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ dwc_hc_t *hc = NULL; ++ dwc_otg_qtd_t *qtd; ++ dwc_otg_hcd_urb_t *urb; ++ void* ptr = NULL; ++ hcchar_data_t hcchar; ++ int num_channels; ++ int i; ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p,%p)\n", __func__, hcd, qh); ++ ++ num_channels = hcd->core_if->core_params->host_channels; ++ ++ /* WA to not select channel with chdis bit set, this was ++ * observed after role switch as part of OTG 2.0 HNP ++ */ ++ for (i = 0; i < num_channels; i++) { ++ hc = DWC_CIRCLEQ_FIRST(&hcd->free_hc_list); ++ hcchar.d32 = DWC_READ_REG32(&hcd->core_if->host_if->hc_regs[hc->hc_num]->hcchar); ++ DWC_DEBUGPL(DBG_HCDV, "HC num = %d HCCHAR %08x\n", hc->hc_num, hcchar.d32); ++ if(!hcchar.b.chdis && !hcchar.b.chen) ++ break; ++ DWC_CIRCLEQ_REMOVE_INIT(&hcd->free_hc_list, hc, hc_list_entry); ++ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry); ++ hc = NULL; ++ } ++ if (!hc) { ++ DWC_ERROR("No free channel with en and dis bits 0\n"); ++ return; ++ } ++ ++ ++ ++ /* Remove the host channel from the free list. */ ++ DWC_CIRCLEQ_REMOVE_INIT(&hcd->free_hc_list, hc, hc_list_entry); ++ ++ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ ++ urb = qtd->urb; ++ qh->channel = hc; ++ ++ qtd->in_process = 1; ++ ++ /* ++ * Use usb_pipedevice to determine device address. This address is ++ * 0 before the SET_ADDRESS command and the correct address afterward. ++ */ ++ hc->dev_addr = dwc_otg_hcd_get_dev_addr(&urb->pipe_info); ++ hc->ep_num = dwc_otg_hcd_get_ep_num(&urb->pipe_info); ++ hc->speed = qh->dev_speed; ++ hc->max_packet = dwc_max_packet(qh->maxp); ++ ++ hc->xfer_started = 0; ++ hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS; ++ hc->error_state = (qtd->error_count > 0); ++ hc->halt_on_queue = 0; ++ hc->halt_pending = 0; ++ hc->requests = 0; ++ ++ /* ++ * The following values may be modified in the transfer type section ++ * below. The xfer_len value may be reduced when the transfer is ++ * started to accommodate the max widths of the XferSize and PktCnt ++ * fields in the HCTSIZn register. ++ */ ++ ++ hc->ep_is_in = (dwc_otg_hcd_is_pipe_in(&urb->pipe_info) != 0); ++ if (hc->ep_is_in) { ++ hc->do_ping = 0; ++ } else { ++ hc->do_ping = qh->ping_state; ++ } ++ ++ hc->data_pid_start = qh->data_toggle; ++ hc->multi_count = 1; ++ ++ if (hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *) urb->dma + urb->actual_length; ++ ++ /* For non-dword aligned case */ ++ if (((unsigned long)hc->xfer_buff & 0x3) ++ && !hcd->core_if->dma_desc_enable) { ++ ptr = (uint8_t *) urb->buf + urb->actual_length; ++ } ++ } else { ++ hc->xfer_buff = (uint8_t *) urb->buf + urb->actual_length; ++ } ++ hc->xfer_len = urb->length - urb->actual_length; ++ hc->xfer_count = 0; ++ ++ /* ++ * Set the split attributes ++ */ ++ hc->do_split = 0; ++ if (qh->do_split) { ++ uint32_t hub_addr, port_addr; ++ hc->do_split = 1; ++ hc->xact_pos = qtd->isoc_split_pos; ++ hc->complete_split = qtd->complete_split; ++ hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &port_addr); ++ hc->hub_addr = (uint8_t) hub_addr; ++ hc->port_addr = (uint8_t) port_addr; ++ } ++ ++ switch (dwc_otg_hcd_get_pipe_type(&urb->pipe_info)) { ++ case UE_CONTROL: ++ hc->ep_type = DWC_OTG_EP_TYPE_CONTROL; ++ switch (qtd->control_phase) { ++ case DWC_OTG_CONTROL_SETUP: ++ DWC_DEBUGPL(DBG_HCDV, " Control setup transaction\n"); ++ hc->do_ping = 0; ++ hc->ep_is_in = 0; ++ hc->data_pid_start = DWC_OTG_HC_PID_SETUP; ++ if (hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *) urb->setup_dma; ++ } else { ++ hc->xfer_buff = (uint8_t *) urb->setup_packet; ++ } ++ hc->xfer_len = 8; ++ ptr = NULL; ++ break; ++ case DWC_OTG_CONTROL_DATA: ++ DWC_DEBUGPL(DBG_HCDV, " Control data transaction\n"); ++ hc->data_pid_start = qtd->data_toggle; ++ break; ++ case DWC_OTG_CONTROL_STATUS: ++ /* ++ * Direction is opposite of data direction or IN if no ++ * data. ++ */ ++ DWC_DEBUGPL(DBG_HCDV, " Control status transaction\n"); ++ if (urb->length == 0) { ++ hc->ep_is_in = 1; ++ } else { ++ hc->ep_is_in = ++ dwc_otg_hcd_is_pipe_out(&urb->pipe_info); ++ } ++ if (hc->ep_is_in) { ++ hc->do_ping = 0; ++ } ++ ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA1; ++ ++ hc->xfer_len = 0; ++ if (hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *) hcd->status_buf_dma; ++ } else { ++ hc->xfer_buff = (uint8_t *) hcd->status_buf; ++ } ++ ptr = NULL; ++ break; ++ } ++ break; ++ case UE_BULK: ++ hc->ep_type = DWC_OTG_EP_TYPE_BULK; ++ break; ++ case UE_INTERRUPT: ++ hc->ep_type = DWC_OTG_EP_TYPE_INTR; ++ break; ++ case UE_ISOCHRONOUS: ++ { ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc; ++ ++ hc->ep_type = DWC_OTG_EP_TYPE_ISOC; ++ ++ if (hcd->core_if->dma_desc_enable) ++ break; ++ ++ frame_desc = &urb->iso_descs[qtd->isoc_frame_index]; ++ ++ frame_desc->status = 0; ++ ++ if (hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *) urb->dma; ++ } else { ++ hc->xfer_buff = (uint8_t *) urb->buf; ++ } ++ hc->xfer_buff += ++ frame_desc->offset + qtd->isoc_split_offset; ++ hc->xfer_len = ++ frame_desc->length - qtd->isoc_split_offset; ++ ++ /* For non-dword aligned buffers */ ++ if (((unsigned long)hc->xfer_buff & 0x3) ++ && hcd->core_if->dma_enable) { ++ ptr = ++ (uint8_t *) urb->buf + frame_desc->offset + ++ qtd->isoc_split_offset; ++ } else ++ ptr = NULL; ++ ++ if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) { ++ if (hc->xfer_len <= 188) { ++ hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL; ++ } else { ++ hc->xact_pos = ++ DWC_HCSPLIT_XACTPOS_BEGIN; ++ } ++ } ++ } ++ break; ++ } ++ /* non DWORD-aligned buffer case */ ++ if (ptr) { ++ uint32_t buf_size; ++ if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) { ++ buf_size = hcd->core_if->core_params->max_transfer_size; ++ } else { ++ buf_size = 4096; ++ } ++ if (!qh->dw_align_buf) { ++ qh->dw_align_buf = DWC_DMA_ALLOC_ATOMIC(buf_size, ++ &qh->dw_align_buf_dma); ++ if (!qh->dw_align_buf) { ++ DWC_ERROR ++ ("%s: Failed to allocate memory to handle " ++ "non-dword aligned buffer case\n", ++ __func__); ++ return; ++ } ++ } ++ if (!hc->ep_is_in) { ++ dwc_memcpy(qh->dw_align_buf, ptr, hc->xfer_len); ++ } ++ hc->align_buff = qh->dw_align_buf_dma; ++ } else { ++ hc->align_buff = 0; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * This value may be modified when the transfer is started to ++ * reflect the actual transfer length. ++ */ ++ hc->multi_count = dwc_hb_mult(qh->maxp); ++ } ++ ++ if (hcd->core_if->dma_desc_enable) ++ hc->desc_list_addr = qh->desc_list_dma; ++ ++ dwc_otg_hc_init(hcd->core_if, hc); ++ hc->qh = qh; ++} ++ ++/** ++ * This function selects transactions from the HCD transfer schedule and ++ * assigns them to available host channels. It is called from HCD interrupt ++ * handler functions. ++ * ++ * @param hcd The HCD state structure. ++ * ++ * @return The types of new transactions that were assigned to host channels. ++ */ ++dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) ++{ ++ dwc_list_link_t *qh_ptr; ++ dwc_otg_qh_t *qh; ++ int num_channels; ++ dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE; ++ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCD, " Select Transactions\n"); ++#endif ++ ++ /* Process entries in the periodic ready list. */ ++ qh_ptr = DWC_LIST_FIRST(&hcd->periodic_sched_ready); ++ ++ while (qh_ptr != &hcd->periodic_sched_ready && ++ !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { ++ ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ assign_and_init_hc(hcd, qh); ++ ++ /* ++ * Move the QH from the periodic ready schedule to the ++ * periodic assigned schedule. ++ */ ++ qh_ptr = DWC_LIST_NEXT(qh_ptr); ++ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned, ++ &qh->qh_list_entry); ++ ++ ret_val = DWC_OTG_TRANSACTION_PERIODIC; ++ } ++ ++ /* ++ * Process entries in the inactive portion of the non-periodic ++ * schedule. Some free host channels may not be used if they are ++ * reserved for periodic transfers. ++ */ ++ qh_ptr = hcd->non_periodic_sched_inactive.next; ++ num_channels = hcd->core_if->core_params->host_channels; ++ while (qh_ptr != &hcd->non_periodic_sched_inactive && ++ (hcd->non_periodic_channels < ++ num_channels - hcd->periodic_channels) && ++ !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { ++ ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ ++ assign_and_init_hc(hcd, qh); ++ ++ /* ++ * Move the QH from the non-periodic inactive schedule to the ++ * non-periodic active schedule. ++ */ ++ qh_ptr = DWC_LIST_NEXT(qh_ptr); ++ DWC_LIST_MOVE_HEAD(&hcd->non_periodic_sched_active, ++ &qh->qh_list_entry); ++ ++ if (ret_val == DWC_OTG_TRANSACTION_NONE) { ++ ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC; ++ } else { ++ ret_val = DWC_OTG_TRANSACTION_ALL; ++ } ++ ++ hcd->non_periodic_channels++; ++ } ++ ++ return ret_val; ++} ++ ++/** ++ * Attempts to queue a single transaction request for a host channel ++ * associated with either a periodic or non-periodic transfer. This function ++ * assumes that there is space available in the appropriate request queue. For ++ * an OUT transfer or SETUP transaction in Slave mode, it checks whether space ++ * is available in the appropriate Tx FIFO. ++ * ++ * @param hcd The HCD state structure. ++ * @param hc Host channel descriptor associated with either a periodic or ++ * non-periodic transfer. ++ * @param fifo_dwords_avail Number of DWORDs available in the periodic Tx ++ * FIFO for periodic transfers or the non-periodic Tx FIFO for non-periodic ++ * transfers. ++ * ++ * @return 1 if a request is queued and more requests may be needed to ++ * complete the transfer, 0 if no more requests are required for this ++ * transfer, -1 if there is insufficient space in the Tx FIFO. ++ */ ++static int queue_transaction(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, uint16_t fifo_dwords_avail) ++{ ++ int retval; ++ ++ if (hcd->core_if->dma_enable) { ++ if (hcd->core_if->dma_desc_enable) { ++ if (!hc->xfer_started ++ || (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)) { ++ dwc_otg_hcd_start_xfer_ddma(hcd, hc->qh); ++ hc->qh->ping_state = 0; ++ } ++ } else if (!hc->xfer_started) { ++ dwc_otg_hc_start_transfer(hcd->core_if, hc); ++ hc->qh->ping_state = 0; ++ } ++ retval = 0; ++ } else if (hc->halt_pending) { ++ /* Don't queue a request if the channel has been halted. */ ++ retval = 0; ++ } else if (hc->halt_on_queue) { ++ dwc_otg_hc_halt(hcd->core_if, hc, hc->halt_status); ++ retval = 0; ++ } else if (hc->do_ping) { ++ if (!hc->xfer_started) { ++ dwc_otg_hc_start_transfer(hcd->core_if, hc); ++ } ++ retval = 0; ++ } else if (!hc->ep_is_in || hc->data_pid_start == DWC_OTG_HC_PID_SETUP) { ++ if ((fifo_dwords_avail * 4) >= hc->max_packet) { ++ if (!hc->xfer_started) { ++ dwc_otg_hc_start_transfer(hcd->core_if, hc); ++ retval = 1; ++ } else { ++ retval = ++ dwc_otg_hc_continue_transfer(hcd->core_if, ++ hc); ++ } ++ } else { ++ retval = -1; ++ } ++ } else { ++ if (!hc->xfer_started) { ++ dwc_otg_hc_start_transfer(hcd->core_if, hc); ++ retval = 1; ++ } else { ++ retval = dwc_otg_hc_continue_transfer(hcd->core_if, hc); ++ } ++ } ++ ++ return retval; ++} ++ ++/** ++ * Processes periodic channels for the next frame and queues transactions for ++ * these channels to the DWC_otg controller. After queueing transactions, the ++ * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions ++ * to queue as Periodic Tx FIFO or request queue space becomes available. ++ * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled. ++ */ ++static void process_periodic_channels(dwc_otg_hcd_t * hcd) ++{ ++ hptxsts_data_t tx_status; ++ dwc_list_link_t *qh_ptr; ++ dwc_otg_qh_t *qh; ++ int status; ++ int no_queue_space = 0; ++ int no_fifo_space = 0; ++ ++ dwc_otg_host_global_regs_t *host_regs; ++ host_regs = hcd->core_if->host_if->host_global_regs; ++ ++ DWC_DEBUGPL(DBG_HCDV, "Queue periodic transactions\n"); ++#ifdef DEBUG ++ tx_status.d32 = DWC_READ_REG32(&host_regs->hptxsts); ++ DWC_DEBUGPL(DBG_HCDV, ++ " P Tx Req Queue Space Avail (before queue): %d\n", ++ tx_status.b.ptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, " P Tx FIFO Space Avail (before queue): %d\n", ++ tx_status.b.ptxfspcavail); ++#endif ++ ++ qh_ptr = hcd->periodic_sched_assigned.next; ++ while (qh_ptr != &hcd->periodic_sched_assigned) { ++ tx_status.d32 = DWC_READ_REG32(&host_regs->hptxsts); ++ if (tx_status.b.ptxqspcavail == 0) { ++ no_queue_space = 1; ++ break; ++ } ++ ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ ++ /* ++ * Set a flag if we're queuing high-bandwidth in slave mode. ++ * The flag prevents any halts to get into the request queue in ++ * the middle of multiple high-bandwidth packets getting queued. ++ */ ++ if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) { ++ hcd->core_if->queuing_high_bandwidth = 1; ++ } ++ status = ++ queue_transaction(hcd, qh->channel, ++ tx_status.b.ptxfspcavail); ++ if (status < 0) { ++ no_fifo_space = 1; ++ break; ++ } ++ ++ /* ++ * In Slave mode, stay on the current transfer until there is ++ * nothing more to do or the high-bandwidth request count is ++ * reached. In DMA mode, only need to queue one request. The ++ * controller automatically handles multiple packets for ++ * high-bandwidth transfers. ++ */ ++ if (hcd->core_if->dma_enable || status == 0 || ++ qh->channel->requests == qh->channel->multi_count) { ++ qh_ptr = qh_ptr->next; ++ /* ++ * Move the QH from the periodic assigned schedule to ++ * the periodic queued schedule. ++ */ ++ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_queued, ++ &qh->qh_list_entry); ++ ++ /* done queuing high bandwidth */ ++ hcd->core_if->queuing_high_bandwidth = 0; ++ } ++ } ++ ++ if (!hcd->core_if->dma_enable) { ++ dwc_otg_core_global_regs_t *global_regs; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ global_regs = hcd->core_if->core_global_regs; ++ intr_mask.b.ptxfempty = 1; ++#ifdef DEBUG ++ tx_status.d32 = DWC_READ_REG32(&host_regs->hptxsts); ++ DWC_DEBUGPL(DBG_HCDV, ++ " P Tx Req Queue Space Avail (after queue): %d\n", ++ tx_status.b.ptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, ++ " P Tx FIFO Space Avail (after queue): %d\n", ++ tx_status.b.ptxfspcavail); ++#endif ++ if (!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned) || ++ no_queue_space || no_fifo_space) { ++ /* ++ * May need to queue more transactions as the request ++ * queue or Tx FIFO empties. Enable the periodic Tx ++ * FIFO empty interrupt. (Always use the half-empty ++ * level to ensure that new requests are loaded as ++ * soon as possible.) ++ */ ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, ++ intr_mask.d32); ++ } else { ++ /* ++ * Disable the Tx FIFO empty interrupt since there are ++ * no more transactions that need to be queued right ++ * now. This function is called from interrupt ++ * handlers to queue more transactions as transfer ++ * states change. ++ */ ++ DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, ++ 0); ++ } ++ } ++} ++ ++/** ++ * Processes active non-periodic channels and queues transactions for these ++ * channels to the DWC_otg controller. After queueing transactions, the NP Tx ++ * FIFO Empty interrupt is enabled if there are more transactions to queue as ++ * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx ++ * FIFO Empty interrupt is disabled. ++ */ ++static void process_non_periodic_channels(dwc_otg_hcd_t * hcd) ++{ ++ gnptxsts_data_t tx_status; ++ dwc_list_link_t *orig_qh_ptr; ++ dwc_otg_qh_t *qh; ++ int status; ++ int no_queue_space = 0; ++ int no_fifo_space = 0; ++ int more_to_do = 0; ++ ++ dwc_otg_core_global_regs_t *global_regs = ++ hcd->core_if->core_global_regs; ++ ++ DWC_DEBUGPL(DBG_HCDV, "Queue non-periodic transactions\n"); ++#ifdef DEBUG ++ tx_status.d32 = DWC_READ_REG32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_HCDV, ++ " NP Tx Req Queue Space Avail (before queue): %d\n", ++ tx_status.b.nptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, " NP Tx FIFO Space Avail (before queue): %d\n", ++ tx_status.b.nptxfspcavail); ++#endif ++ /* ++ * Keep track of the starting point. Skip over the start-of-list ++ * entry. ++ */ ++ if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) { ++ hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next; ++ } ++ orig_qh_ptr = hcd->non_periodic_qh_ptr; ++ ++ /* ++ * Process once through the active list or until no more space is ++ * available in the request queue or the Tx FIFO. ++ */ ++ do { ++ tx_status.d32 = DWC_READ_REG32(&global_regs->gnptxsts); ++ if (!hcd->core_if->dma_enable && tx_status.b.nptxqspcavail == 0) { ++ no_queue_space = 1; ++ break; ++ } ++ ++ qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t, ++ qh_list_entry); ++ status = ++ queue_transaction(hcd, qh->channel, ++ tx_status.b.nptxfspcavail); ++ ++ if (status > 0) { ++ more_to_do = 1; ++ } else if (status < 0) { ++ no_fifo_space = 1; ++ break; ++ } ++ ++ /* Advance to next QH, skipping start-of-list entry. */ ++ hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next; ++ if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) { ++ hcd->non_periodic_qh_ptr = ++ hcd->non_periodic_qh_ptr->next; ++ } ++ ++ } while (hcd->non_periodic_qh_ptr != orig_qh_ptr); ++ ++ if (!hcd->core_if->dma_enable) { ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ intr_mask.b.nptxfempty = 1; ++ ++#ifdef DEBUG ++ tx_status.d32 = DWC_READ_REG32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_HCDV, ++ " NP Tx Req Queue Space Avail (after queue): %d\n", ++ tx_status.b.nptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, ++ " NP Tx FIFO Space Avail (after queue): %d\n", ++ tx_status.b.nptxfspcavail); ++#endif ++ if (more_to_do || no_queue_space || no_fifo_space) { ++ /* ++ * May need to queue more transactions as the request ++ * queue or Tx FIFO empties. Enable the non-periodic ++ * Tx FIFO empty interrupt. (Always use the half-empty ++ * level to ensure that new requests are loaded as ++ * soon as possible.) ++ */ ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, ++ intr_mask.d32); ++ } else { ++ /* ++ * Disable the Tx FIFO empty interrupt since there are ++ * no more transactions that need to be queued right ++ * now. This function is called from interrupt ++ * handlers to queue more transactions as transfer ++ * states change. ++ */ ++ DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, ++ 0); ++ } ++ } ++} ++ ++/** ++ * This function processes the currently active host channels and queues ++ * transactions for these channels to the DWC_otg controller. It is called ++ * from HCD interrupt handler functions. ++ * ++ * @param hcd The HCD state structure. ++ * @param tr_type The type(s) of transactions to queue (non-periodic, ++ * periodic, or both). ++ */ ++void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd, ++ dwc_otg_transaction_type_e tr_type) ++{ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCD, "Queue Transactions\n"); ++#endif ++ /* Process host channels associated with periodic transfers. */ ++ if ((tr_type == DWC_OTG_TRANSACTION_PERIODIC || ++ tr_type == DWC_OTG_TRANSACTION_ALL) && ++ !DWC_LIST_EMPTY(&hcd->periodic_sched_assigned)) { ++ ++ process_periodic_channels(hcd); ++ } ++ ++ /* Process host channels associated with non-periodic transfers. */ ++ if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC || ++ tr_type == DWC_OTG_TRANSACTION_ALL) { ++ if (!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active)) { ++ process_non_periodic_channels(hcd); ++ } else { ++ /* ++ * Ensure NP Tx FIFO empty interrupt is disabled when ++ * there are no non-periodic transfers to process. ++ */ ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ gintmsk.b.nptxfempty = 1; ++ DWC_MODIFY_REG32(&hcd->core_if-> ++ core_global_regs->gintmsk, gintmsk.d32, ++ 0); ++ } ++ } ++} ++ ++#ifdef DWC_HS_ELECT_TST ++/* ++ * Quick and dirty hack to implement the HS Electrical Test ++ * SINGLE_STEP_GET_DEVICE_DESCRIPTOR feature. ++ * ++ * This code was copied from our userspace app "hset". It sends a ++ * Get Device Descriptor control sequence in two parts, first the ++ * Setup packet by itself, followed some time later by the In and ++ * Ack packets. Rather than trying to figure out how to add this ++ * functionality to the normal driver code, we just hijack the ++ * hardware, using these two function to drive the hardware ++ * directly. ++ */ ++ ++static dwc_otg_core_global_regs_t *global_regs; ++static dwc_otg_host_global_regs_t *hc_global_regs; ++static dwc_otg_hc_regs_t *hc_regs; ++static uint32_t *data_fifo; ++ ++static void do_setup(void) ++{ ++ gintsts_data_t gintsts; ++ hctsiz_data_t hctsiz; ++ hcchar_data_t hcchar; ++ haint_data_t haint; ++ hcint_data_t hcint; ++ ++ /* Enable HAINTs */ ++ DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0001); ++ ++ /* Enable HCINTs */ ++ DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x04a3); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* Read HAINT */ ++ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* ++ * Send Setup packet (Get Device Descriptor) ++ */ ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ hcchar.b.chdis = 1; ++// hcchar.b.chen = 1; ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ dwc_mdelay(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* Read HAINT */ ++ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 8; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = DWC_OTG_HC_PID_SETUP; ++ DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.epdir = 0; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++ ++ /* Fill FIFO with Setup data for Get Device Descriptor */ ++ data_fifo = (uint32_t *) ((char *)global_regs + 0x1000); ++ DWC_WRITE_REG32(data_fifo++, 0x01000680); ++ DWC_WRITE_REG32(data_fifo++, 0x00080000); ++ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ /* Disable HCINTs */ ++ DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x0000); ++ ++ /* Disable HAINTs */ ++ DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0000); ++ ++ /* Read HAINT */ ++ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++} ++ ++static void do_in_ack(void) ++{ ++ gintsts_data_t gintsts; ++ hctsiz_data_t hctsiz; ++ hcchar_data_t hcchar; ++ haint_data_t haint; ++ hcint_data_t hcint; ++ host_grxsts_data_t grxsts; ++ ++ /* Enable HAINTs */ ++ DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0001); ++ ++ /* Enable HCINTs */ ++ DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x04a3); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* Read HAINT */ ++ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* ++ * Receive Control In packet ++ */ ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ hcchar.b.chdis = 1; ++ hcchar.b.chen = 1; ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ dwc_mdelay(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* Read HAINT */ ++ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 8; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = DWC_OTG_HC_PID_DATA1; ++ DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.epdir = 1; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* Wait for receive status queue interrupt */ ++ do { ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ } while (gintsts.b.rxstsqlvl == 0); ++ ++ /* Read RXSTS */ ++ grxsts.d32 = DWC_READ_REG32(&global_regs->grxstsp); ++ ++ /* Clear RXSTSQLVL in GINTSTS */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ switch (grxsts.b.pktsts) { ++ case DWC_GRXSTS_PKTSTS_IN: ++ /* Read the data into the host buffer */ ++ if (grxsts.b.bcnt > 0) { ++ int i; ++ int word_count = (grxsts.b.bcnt + 3) / 4; ++ ++ data_fifo = (uint32_t *) ((char *)global_regs + 0x1000); ++ ++ for (i = 0; i < word_count; i++) { ++ (void)DWC_READ_REG32(data_fifo++); ++ } ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* Wait for receive status queue interrupt */ ++ do { ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ } while (gintsts.b.rxstsqlvl == 0); ++ ++ /* Read RXSTS */ ++ grxsts.d32 = DWC_READ_REG32(&global_regs->grxstsp); ++ ++ /* Clear RXSTSQLVL in GINTSTS */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ switch (grxsts.b.pktsts) { ++ case DWC_GRXSTS_PKTSTS_IN_XFER_COMP: ++ break; ++ ++ default: ++ break; ++ } ++ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ /* Read HAINT */ ++ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++// usleep(100000); ++// mdelay(100); ++ dwc_mdelay(1); ++ ++ /* ++ * Send handshake packet ++ */ ++ ++ /* Read HAINT */ ++ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ hcchar.b.chdis = 1; ++ hcchar.b.chen = 1; ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ dwc_mdelay(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* Read HAINT */ ++ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 0; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = DWC_OTG_HC_PID_DATA1; ++ DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.epdir = 0; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32); ++ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ /* Disable HCINTs */ ++ DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x0000); ++ ++ /* Disable HAINTs */ ++ DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0000); ++ ++ /* Read HAINT */ ++ haint.d32 = DWC_READ_REG32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts); ++} ++#endif ++ ++/** Handles hub class-specific requests. */ ++int dwc_otg_hcd_hub_control(dwc_otg_hcd_t * dwc_otg_hcd, ++ uint16_t typeReq, ++ uint16_t wValue, ++ uint16_t wIndex, uint8_t * buf, uint16_t wLength) ++{ ++ int retval = 0; ++ ++ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; ++ usb_hub_descriptor_t *hub_desc; ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++ ++ uint32_t port_status; ++ ++ switch (typeReq) { ++ case UCR_CLEAR_HUB_FEATURE: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearHubFeature 0x%x\n", wValue); ++ switch (wValue) { ++ case UHF_C_HUB_LOCAL_POWER: ++ case UHF_C_HUB_OVER_CURRENT: ++ /* Nothing required here */ ++ break; ++ default: ++ retval = -DWC_E_INVALID; ++ DWC_ERROR("DWC OTG HCD - " ++ "ClearHubFeature request %xh unknown\n", ++ wValue); ++ } ++ break; ++ case UCR_CLEAR_PORT_FEATURE: ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ if (wValue != UHF_PORT_L1) ++#endif ++ if (!wIndex || wIndex > 1) ++ goto error; ++ ++ switch (wValue) { ++ case UHF_PORT_ENABLE: ++ DWC_DEBUGPL(DBG_ANY, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_ENABLE\n"); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtena = 1; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case UHF_PORT_SUSPEND: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); ++ ++ if (core_if->power_down == 2) { ++ dwc_otg_host_hibernation_restore(core_if, 0, 0); ++ } else { ++ DWC_WRITE_REG32(core_if->pcgcctl, 0); ++ dwc_mdelay(5); ++ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtres = 1; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ hprt0.b.prtsusp = 0; ++ /* Clear Resume bit */ ++ dwc_mdelay(100); ++ hprt0.b.prtres = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ } ++ break; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ case UHF_PORT_L1: ++ { ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ glpmcfg_data_t lpmcfg = {.d32 = 0 }; ++ ++ lpmcfg.d32 = ++ DWC_READ_REG32(&core_if-> ++ core_global_regs->glpmcfg); ++ lpmcfg.b.en_utmi_sleep = 0; ++ lpmcfg.b.hird_thres &= (~(1 << 4)); ++ lpmcfg.b.prt_sleep_sts = 1; ++ DWC_WRITE_REG32(&core_if-> ++ core_global_regs->glpmcfg, ++ lpmcfg.d32); ++ ++ /* Clear Enbl_L1Gating bit. */ ++ pcgcctl.b.enbl_sleep_gating = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, ++ 0); ++ ++ dwc_mdelay(5); ++ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtres = 1; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, ++ hprt0.d32); ++ /* This bit will be cleared in wakeup interrupt handle */ ++ break; ++ } ++#endif ++ case UHF_PORT_POWER: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_POWER\n"); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtpwr = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case UHF_PORT_INDICATOR: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_INDICATOR\n"); ++ /* Port inidicator not supported */ ++ break; ++ case UHF_C_PORT_CONNECTION: ++ /* Clears drivers internal connect status change ++ * flag */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n"); ++ dwc_otg_hcd->flags.b.port_connect_status_change = 0; ++ break; ++ case UHF_C_PORT_RESET: ++ /* Clears the driver's internal Port Reset Change ++ * flag */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_RESET\n"); ++ dwc_otg_hcd->flags.b.port_reset_change = 0; ++ break; ++ case UHF_C_PORT_ENABLE: ++ /* Clears the driver's internal Port ++ * Enable/Disable Change flag */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_ENABLE\n"); ++ dwc_otg_hcd->flags.b.port_enable_change = 0; ++ break; ++ case UHF_C_PORT_SUSPEND: ++ /* Clears the driver's internal Port Suspend ++ * Change flag, which is set when resume signaling on ++ * the host port is complete */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n"); ++ dwc_otg_hcd->flags.b.port_suspend_change = 0; ++ break; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ case UHF_C_PORT_L1: ++ dwc_otg_hcd->flags.b.port_l1_change = 0; ++ break; ++#endif ++ case UHF_C_PORT_OVER_CURRENT: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n"); ++ dwc_otg_hcd->flags.b.port_over_current_change = 0; ++ break; ++ default: ++ retval = -DWC_E_INVALID; ++ DWC_ERROR("DWC OTG HCD - " ++ "ClearPortFeature request %xh " ++ "unknown or unsupported\n", wValue); ++ } ++ break; ++ case UCR_GET_HUB_DESCRIPTOR: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "GetHubDescriptor\n"); ++ hub_desc = (usb_hub_descriptor_t *) buf; ++ hub_desc->bDescLength = 9; ++ hub_desc->bDescriptorType = 0x29; ++ hub_desc->bNbrPorts = 1; ++ USETW(hub_desc->wHubCharacteristics, 0x08); ++ hub_desc->bPwrOn2PwrGood = 1; ++ hub_desc->bHubContrCurrent = 0; ++ hub_desc->DeviceRemovable[0] = 0; ++ hub_desc->DeviceRemovable[1] = 0xff; ++ break; ++ case UCR_GET_HUB_STATUS: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "GetHubStatus\n"); ++ DWC_MEMSET(buf, 0, 4); ++ break; ++ case UCR_GET_PORT_STATUS: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "GetPortStatus wIndex = 0x%04x FLAGS=0x%08x\n", ++ wIndex, dwc_otg_hcd->flags.d32); ++ if (!wIndex || wIndex > 1) ++ goto error; ++ ++ port_status = 0; ++ ++ if (dwc_otg_hcd->flags.b.port_connect_status_change) ++ port_status |= (1 << UHF_C_PORT_CONNECTION); ++ ++ if (dwc_otg_hcd->flags.b.port_enable_change) ++ port_status |= (1 << UHF_C_PORT_ENABLE); ++ ++ if (dwc_otg_hcd->flags.b.port_suspend_change) ++ port_status |= (1 << UHF_C_PORT_SUSPEND); ++ ++ if (dwc_otg_hcd->flags.b.port_l1_change) ++ port_status |= (1 << UHF_C_PORT_L1); ++ ++ if (dwc_otg_hcd->flags.b.port_reset_change) { ++ port_status |= (1 << UHF_C_PORT_RESET); ++ } ++ ++ if (dwc_otg_hcd->flags.b.port_over_current_change) { ++ DWC_WARN("Overcurrent change detected\n"); ++ port_status |= (1 << UHF_C_PORT_OVER_CURRENT); ++ } ++ ++ if (!dwc_otg_hcd->flags.b.port_connect_status) { ++ /* ++ * The port is disconnected, which means the core is ++ * either in device mode or it soon will be. Just ++ * return 0's for the remainder of the port status ++ * since the port register can't be read if the core ++ * is in device mode. ++ */ ++ *((__le32 *) buf) = dwc_cpu_to_le32(&port_status); ++ break; ++ } ++ ++ hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0); ++ DWC_DEBUGPL(DBG_HCDV, " HPRT0: 0x%08x\n", hprt0.d32); ++ ++ if (hprt0.b.prtconnsts) ++ port_status |= (1 << UHF_PORT_CONNECTION); ++ ++ if (hprt0.b.prtena) ++ port_status |= (1 << UHF_PORT_ENABLE); ++ ++ if (hprt0.b.prtsusp) ++ port_status |= (1 << UHF_PORT_SUSPEND); ++ ++ if (hprt0.b.prtovrcurract) ++ port_status |= (1 << UHF_PORT_OVER_CURRENT); ++ ++ if (hprt0.b.prtrst) ++ port_status |= (1 << UHF_PORT_RESET); ++ ++ if (hprt0.b.prtpwr) ++ port_status |= (1 << UHF_PORT_POWER); ++ ++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) ++ port_status |= (1 << UHF_PORT_HIGH_SPEED); ++ else if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED) ++ port_status |= (1 << UHF_PORT_LOW_SPEED); ++ ++ if (hprt0.b.prttstctl) ++ port_status |= (1 << UHF_PORT_TEST); ++ if (dwc_otg_get_lpm_portsleepstatus(dwc_otg_hcd->core_if)) { ++ port_status |= (1 << UHF_PORT_L1); ++ } ++ /* ++ For Synopsys HW emulation of Power down wkup_control asserts the ++ hreset_n and prst_n on suspned. This causes the HPRT0 to be zero. ++ We intentionally tell the software that port is in L2Suspend state. ++ Only for STE. ++ */ ++ if ((core_if->power_down == 2) ++ && (core_if->hibernation_suspend == 1)) { ++ port_status |= (1 << UHF_PORT_SUSPEND); ++ } ++ /* USB_PORT_FEAT_INDICATOR unsupported always 0 */ ++ ++ *((__le32 *) buf) = dwc_cpu_to_le32(&port_status); ++ ++ break; ++ case UCR_SET_HUB_FEATURE: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetHubFeature\n"); ++ /* No HUB features supported */ ++ break; ++ case UCR_SET_PORT_FEATURE: ++ if (wValue != UHF_PORT_TEST && (!wIndex || wIndex > 1)) ++ goto error; ++ ++ if (!dwc_otg_hcd->flags.b.port_connect_status) { ++ /* ++ * The port is disconnected, which means the core is ++ * either in device mode or it soon will be. Just ++ * return without doing anything since the port ++ * register can't be written if the core is in device ++ * mode. ++ */ ++ break; ++ } ++ ++ switch (wValue) { ++ case UHF_PORT_SUSPEND: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); ++ if (dwc_otg_hcd_otg_port(dwc_otg_hcd) != wIndex) { ++ goto error; ++ } ++ if (core_if->power_down == 2) { ++ int timeout = 300; ++ dwc_irqflags_t flags; ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ gusbcfg_data_t gusbcfg = {.d32 = 0 }; ++#ifdef DWC_DEV_SRPCAP ++ int32_t otg_cap_param = core_if->core_params->otg_cap; ++#endif ++ DWC_PRINTF("Preparing for complete power-off\n"); ++ ++ /* Save registers before hibernation */ ++ dwc_otg_save_global_regs(core_if); ++ dwc_otg_save_host_regs(core_if); ++ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtsusp = 1; ++ hprt0.b.prtena = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ /* Spin hprt0.b.prtsusp to became 1 */ ++ do { ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ if (hprt0.b.prtsusp) { ++ break; ++ } ++ dwc_mdelay(1); ++ } while (--timeout); ++ if (!timeout) { ++ DWC_WARN("Suspend wasn't genereted\n"); ++ } ++ dwc_udelay(10); ++ ++ /* ++ * We need to disable interrupts to prevent servicing of any IRQ ++ * during going to hibernation ++ */ ++ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags); ++ core_if->lx_state = DWC_OTG_L2; ++#ifdef DWC_DEV_SRPCAP ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtpwr = 0; ++ hprt0.b.prtena = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, ++ hprt0.d32); ++#endif ++ gusbcfg.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs-> ++ gusbcfg); ++ if (gusbcfg.b.ulpi_utmi_sel == 1) { ++ /* ULPI interface */ ++ /* Suspend the Phy Clock */ ++ pcgcctl.d32 = 0; ++ pcgcctl.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, 0, ++ pcgcctl.d32); ++ dwc_udelay(10); ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if-> ++ core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ } else { ++ /* UTMI+ Interface */ ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if-> ++ core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ pcgcctl.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32); ++ dwc_udelay(10); ++ } ++#ifdef DWC_DEV_SRPCAP ++ gpwrdn.d32 = 0; ++ gpwrdn.b.dis_vbus = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++#endif ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ gpwrdn.d32 = 0; ++#ifdef DWC_DEV_SRPCAP ++ gpwrdn.b.srp_det_msk = 1; ++#endif ++ gpwrdn.b.disconn_det_msk = 1; ++ gpwrdn.b.lnstchng_msk = 1; ++ gpwrdn.b.sts_chngint_msk = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Enable Power Down Clamp and all interrupts in GPWRDN */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnclmp = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ dwc_udelay(10); ++ ++ /* Switch off VDD */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ ++#ifdef DWC_DEV_SRPCAP ++ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) ++ { ++ core_if->pwron_timer_started = 1; ++ DWC_TIMER_SCHEDULE(core_if->pwron_timer, 6000 /* 6 secs */ ); ++ } ++#endif ++ /* Save gpwrdn register for further usage if stschng interrupt */ ++ core_if->gr_backup->gpwrdn_local = ++ DWC_READ_REG32(&core_if->core_global_regs->gpwrdn); ++ ++ /* Set flag to indicate that we are in hibernation */ ++ core_if->hibernation_suspend = 1; ++ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock,flags); ++ ++ DWC_PRINTF("Host hibernation completed\n"); ++ // Exit from case statement ++ break; ++ ++ } ++ if (dwc_otg_hcd_otg_port(dwc_otg_hcd) == wIndex && ++ dwc_otg_hcd->fops->get_b_hnp_enable(dwc_otg_hcd)) { ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ gotgctl.b.hstsethnpen = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gotgctl, 0, gotgctl.d32); ++ core_if->op_state = A_SUSPEND; ++ } ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtsusp = 1; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ { ++ dwc_irqflags_t flags; ++ /* Update lx_state */ ++ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags); ++ core_if->lx_state = DWC_OTG_L2; ++ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags); ++ } ++ /* Suspend the Phy Clock */ ++ if (core_if->otg_ver == 0) { ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ pcgcctl.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, 0, ++ pcgcctl.d32); ++ dwc_udelay(10); ++ } ++ ++ /* For HNP the bus must be suspended for at least 200ms. */ ++ if (dwc_otg_hcd->fops->get_b_hnp_enable(dwc_otg_hcd)) { ++ if (core_if->otg_ver) { ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ pcgcctl.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0); ++ } ++ dwc_mdelay(200); ++ } ++ ++ /** @todo - check how sw can wait for 1 sec to check asesvld??? */ ++#if 0 //vahrama !!!!!!!!!!!!!!!!!! ++ if (core_if->adp_enable) { ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ gpwrdn_data_t gpwrdn; ++ ++ while (gotgctl.b.asesvld == 1) { ++ gotgctl.d32 = ++ DWC_READ_REG32(&core_if-> ++ core_global_regs-> ++ gotgctl); ++ dwc_mdelay(100); ++ } ++ ++ /* Enable Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ ++ /* Unmask SRP detected interrupt from Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.srp_det_msk = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs-> ++ gpwrdn, 0, gpwrdn.d32); ++ ++ dwc_otg_adp_probe_start(core_if); ++ } ++#endif ++ break; ++ case UHF_PORT_POWER: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_POWER\n"); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtpwr = 1; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case UHF_PORT_RESET: ++ if ((core_if->power_down == 2) ++ && (core_if->hibernation_suspend == 1)) { ++ /* If we are going to exit from Hibernated ++ * state via USB RESET. ++ */ ++ dwc_otg_host_hibernation_restore(core_if, 0, 1); ++ } else { ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ ++ DWC_DEBUGPL(DBG_HCD, ++ "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_RESET\n"); ++ { ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ pcgcctl.b.enbl_sleep_gating = 1; ++ pcgcctl.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0); ++ DWC_WRITE_REG32(core_if->pcgcctl, 0); ++ } ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ { ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ if (lpmcfg.b.prt_sleep_sts) { ++ lpmcfg.b.en_utmi_sleep = 0; ++ lpmcfg.b.hird_thres &= (~(1 << 4)); ++ DWC_WRITE_REG32 ++ (&core_if->core_global_regs->glpmcfg, ++ lpmcfg.d32); ++ dwc_mdelay(1); ++ } ++ } ++#endif ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ /* Clear suspend bit if resetting from suspended state. */ ++ hprt0.b.prtsusp = 0; ++ /* When B-Host the Port reset bit is set in ++ * the Start HCD Callback function, so that ++ * the reset is started within 1ms of the HNP ++ * success interrupt. */ ++ if (!dwc_otg_hcd_is_b_host(dwc_otg_hcd)) { ++ hprt0.b.prtpwr = 1; ++ hprt0.b.prtrst = 1; ++ DWC_PRINTF("Indeed it is in host mode hprt0 = %08x\n",hprt0.d32); ++ DWC_WRITE_REG32(core_if->host_if->hprt0, ++ hprt0.d32); ++ } ++ /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ ++ dwc_mdelay(60); ++ hprt0.b.prtrst = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ core_if->lx_state = DWC_OTG_L0; /* Now back to the on state */ ++ } ++ break; ++#ifdef DWC_HS_ELECT_TST ++ case UHF_PORT_TEST: ++ { ++ uint32_t t; ++ gintmsk_data_t gintmsk; ++ ++ t = (wIndex >> 8); /* MSB wIndex USB */ ++ DWC_DEBUGPL(DBG_HCD, ++ "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_TEST %d\n", ++ t); ++ DWC_WARN("USB_PORT_FEAT_TEST %d\n", t); ++ if (t < 6) { ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prttstctl = t; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, ++ hprt0.d32); ++ } else { ++ /* Setup global vars with reg addresses (quick and ++ * dirty hack, should be cleaned up) ++ */ ++ global_regs = core_if->core_global_regs; ++ hc_global_regs = ++ core_if->host_if->host_global_regs; ++ hc_regs = ++ (dwc_otg_hc_regs_t *) ((char *) ++ global_regs + ++ 0x500); ++ data_fifo = ++ (uint32_t *) ((char *)global_regs + ++ 0x1000); ++ ++ if (t == 6) { /* HS_HOST_PORT_SUSPEND_RESUME */ ++ /* Save current interrupt mask */ ++ gintmsk.d32 = ++ DWC_READ_REG32 ++ (&global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ DWC_WRITE_REG32(&global_regs->gintmsk, 0); ++ ++ /* 15 second delay per the test spec */ ++ dwc_mdelay(15000); ++ ++ /* Drive suspend on the root port */ ++ hprt0.d32 = ++ dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtsusp = 1; ++ hprt0.b.prtres = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ ++ /* 15 second delay per the test spec */ ++ dwc_mdelay(15000); ++ ++ /* Drive resume on the root port */ ++ hprt0.d32 = ++ dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtsusp = 0; ++ hprt0.b.prtres = 1; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ dwc_mdelay(100); ++ ++ /* Clear the resume bit */ ++ hprt0.b.prtres = 0; ++ DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32); ++ ++ /* Restore interrupts */ ++ DWC_WRITE_REG32(&global_regs->gintmsk, gintmsk.d32); ++ } else if (t == 7) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */ ++ /* Save current interrupt mask */ ++ gintmsk.d32 = ++ DWC_READ_REG32 ++ (&global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ DWC_WRITE_REG32(&global_regs->gintmsk, 0); ++ ++ /* 15 second delay per the test spec */ ++ dwc_mdelay(15000); ++ ++ /* Send the Setup packet */ ++ do_setup(); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ dwc_mdelay(15000); ++ ++ /* Restore interrupts */ ++ DWC_WRITE_REG32(&global_regs->gintmsk, gintmsk.d32); ++ } else if (t == 8) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */ ++ /* Save current interrupt mask */ ++ gintmsk.d32 = ++ DWC_READ_REG32 ++ (&global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ DWC_WRITE_REG32(&global_regs->gintmsk, 0); ++ ++ /* Send the Setup packet */ ++ do_setup(); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ dwc_mdelay(15000); ++ ++ /* Send the In and Ack packets */ ++ do_in_ack(); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ dwc_mdelay(15000); ++ ++ /* Restore interrupts */ ++ DWC_WRITE_REG32(&global_regs->gintmsk, gintmsk.d32); ++ } ++ } ++ break; ++ } ++#endif /* DWC_HS_ELECT_TST */ ++ ++ case UHF_PORT_INDICATOR: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_INDICATOR\n"); ++ /* Not supported */ ++ break; ++ default: ++ retval = -DWC_E_INVALID; ++ DWC_ERROR("DWC OTG HCD - " ++ "SetPortFeature request %xh " ++ "unknown or unsupported\n", wValue); ++ break; ++ } ++ break; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ case UCR_SET_AND_TEST_PORT_FEATURE: ++ if (wValue != UHF_PORT_L1) { ++ goto error; ++ } ++ { ++ int portnum, hird, devaddr, remwake; ++ glpmcfg_data_t lpmcfg; ++ uint32_t time_usecs; ++ gintsts_data_t gintsts; ++ gintmsk_data_t gintmsk; ++ ++ if (!dwc_otg_get_param_lpm_enable(core_if)) { ++ goto error; ++ } ++ if (wValue != UHF_PORT_L1 || wLength != 1) { ++ goto error; ++ } ++ /* Check if the port currently is in SLEEP state */ ++ lpmcfg.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ if (lpmcfg.b.prt_sleep_sts) { ++ DWC_INFO("Port is already in sleep mode\n"); ++ buf[0] = 0; /* Return success */ ++ break; ++ } ++ ++ portnum = wIndex & 0xf; ++ hird = (wIndex >> 4) & 0xf; ++ devaddr = (wIndex >> 8) & 0x7f; ++ remwake = (wIndex >> 15); ++ ++ if (portnum != 1) { ++ retval = -DWC_E_INVALID; ++ DWC_WARN ++ ("Wrong port number(%d) in SetandTestPortFeature request\n", ++ portnum); ++ break; ++ } ++ ++ DWC_PRINTF ++ ("SetandTestPortFeature request: portnum = %d, hird = %d, devaddr = %d, rewake = %d\n", ++ portnum, hird, devaddr, remwake); ++ /* Disable LPM interrupt */ ++ gintmsk.d32 = 0; ++ gintmsk.b.lpmtranrcvd = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, ++ gintmsk.d32, 0); ++ ++ if (dwc_otg_hcd_send_lpm ++ (dwc_otg_hcd, devaddr, hird, remwake)) { ++ retval = -DWC_E_INVALID; ++ break; ++ } ++ ++ time_usecs = 10 * (lpmcfg.b.retry_count + 1); ++ /* We will consider timeout if time_usecs microseconds pass, ++ * and we don't receive LPM transaction status. ++ * After receiving non-error responce(ACK/NYET/STALL) from device, ++ * core will set lpmtranrcvd bit. ++ */ ++ do { ++ gintsts.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ if (gintsts.b.lpmtranrcvd) { ++ break; ++ } ++ dwc_udelay(1); ++ } while (--time_usecs); ++ /* lpm_int bit will be cleared in LPM interrupt handler */ ++ ++ /* Now fill status ++ * 0x00 - Success ++ * 0x10 - NYET ++ * 0x11 - Timeout ++ */ ++ if (!gintsts.b.lpmtranrcvd) { ++ buf[0] = 0x3; /* Completion code is Timeout */ ++ dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd); ++ } else { ++ lpmcfg.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ if (lpmcfg.b.lpm_resp == 0x3) { ++ /* ACK responce from the device */ ++ buf[0] = 0x00; /* Success */ ++ } else if (lpmcfg.b.lpm_resp == 0x2) { ++ /* NYET responce from the device */ ++ buf[0] = 0x2; ++ } else { ++ /* Otherwise responce with Timeout */ ++ buf[0] = 0x3; ++ } ++ } ++ DWC_PRINTF("Device responce to LPM trans is %x\n", ++ lpmcfg.b.lpm_resp); ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, ++ gintmsk.d32); ++ ++ break; ++ } ++#endif /* CONFIG_USB_DWC_OTG_LPM */ ++ default: ++error: ++ retval = -DWC_E_INVALID; ++ DWC_WARN("DWC OTG HCD - " ++ "Unknown hub control request type or invalid typeReq: %xh wIndex: %xh wValue: %xh\n", ++ typeReq, wIndex, wValue); ++ break; ++ } ++ ++ return retval; ++} ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++/** Returns index of host channel to perform LPM transaction. */ ++int dwc_otg_hcd_get_hc_for_lpm_tran(dwc_otg_hcd_t * hcd, uint8_t devaddr) ++{ ++ dwc_otg_core_if_t *core_if = hcd->core_if; ++ dwc_hc_t *hc; ++ hcchar_data_t hcchar; ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ ++ if (DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { ++ DWC_PRINTF("No free channel to select for LPM transaction\n"); ++ return -1; ++ } ++ ++ hc = DWC_CIRCLEQ_FIRST(&hcd->free_hc_list); ++ ++ /* Mask host channel interrupts. */ ++ gintmsk.b.hcintr = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0); ++ ++ /* Fill fields that core needs for LPM transaction */ ++ hcchar.b.devaddr = devaddr; ++ hcchar.b.epnum = 0; ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.mps = 64; ++ hcchar.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW); ++ hcchar.b.epdir = 0; /* OUT */ ++ DWC_WRITE_REG32(&core_if->host_if->hc_regs[hc->hc_num]->hcchar, ++ hcchar.d32); ++ ++ /* Remove the host channel from the free list. */ ++ DWC_CIRCLEQ_REMOVE_INIT(&hcd->free_hc_list, hc, hc_list_entry); ++ ++ DWC_PRINTF("hcnum = %d devaddr = %d\n", hc->hc_num, devaddr); ++ ++ return hc->hc_num; ++} ++ ++/** Release hc after performing LPM transaction */ ++void dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd_t * hcd) ++{ ++ dwc_hc_t *hc; ++ glpmcfg_data_t lpmcfg; ++ uint8_t hc_num; ++ ++ lpmcfg.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->glpmcfg); ++ hc_num = lpmcfg.b.lpm_chan_index; ++ ++ hc = hcd->hc_ptr_array[hc_num]; ++ ++ DWC_PRINTF("Freeing channel %d after LPM\n", hc_num); ++ /* Return host channel to free list */ ++ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry); ++} ++ ++int dwc_otg_hcd_send_lpm(dwc_otg_hcd_t * hcd, uint8_t devaddr, uint8_t hird, ++ uint8_t bRemoteWake) ++{ ++ glpmcfg_data_t lpmcfg; ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ int channel; ++ ++ channel = dwc_otg_hcd_get_hc_for_lpm_tran(hcd, devaddr); ++ if (channel < 0) { ++ return channel; ++ } ++ ++ pcgcctl.b.enbl_sleep_gating = 1; ++ DWC_MODIFY_REG32(hcd->core_if->pcgcctl, 0, pcgcctl.d32); ++ ++ /* Read LPM config register */ ++ lpmcfg.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->glpmcfg); ++ ++ /* Program LPM transaction fields */ ++ lpmcfg.b.rem_wkup_en = bRemoteWake; ++ lpmcfg.b.hird = hird; ++ ++ if(dwc_otg_get_param_besl_enable(hcd->core_if)) { ++ lpmcfg.b.hird_thres = 0x16; ++ lpmcfg.b.en_besl = 1; ++ } else { ++ lpmcfg.b.hird_thres = 0x1c; ++ } ++ ++ lpmcfg.b.lpm_chan_index = channel; ++ lpmcfg.b.en_utmi_sleep = 1; ++ /* Program LPM config register */ ++ DWC_WRITE_REG32(&hcd->core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++ ++ /* Send LPM transaction */ ++ lpmcfg.b.send_lpm = 1; ++ DWC_WRITE_REG32(&hcd->core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++ ++ return 0; ++} ++ ++#endif /* CONFIG_USB_DWC_OTG_LPM */ ++ ++int dwc_otg_hcd_is_status_changed(dwc_otg_hcd_t * hcd, int port) ++{ ++ int retval; ++ ++ if (port != 1) { ++ return -DWC_E_INVALID; ++ } ++ ++ retval = (hcd->flags.b.port_connect_status_change || ++ hcd->flags.b.port_reset_change || ++ hcd->flags.b.port_enable_change || ++ hcd->flags.b.port_suspend_change || ++ hcd->flags.b.port_over_current_change); ++#ifdef DEBUG ++ if (retval) { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB STATUS DATA:" ++ " Root port status changed\n"); ++ DWC_DEBUGPL(DBG_HCDV, " port_connect_status_change: %d\n", ++ hcd->flags.b.port_connect_status_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_reset_change: %d\n", ++ hcd->flags.b.port_reset_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_enable_change: %d\n", ++ hcd->flags.b.port_enable_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_suspend_change: %d\n", ++ hcd->flags.b.port_suspend_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_over_current_change: %d\n", ++ hcd->flags.b.port_over_current_change); ++ } ++#endif ++ return retval; ++} ++ ++int dwc_otg_hcd_get_frame_number(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ hfnum_data_t hfnum; ++ hfnum.d32 = ++ DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs-> ++ hfnum); ++ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD GET FRAME NUMBER %d\n", ++ hfnum.b.frnum); ++#endif ++ return hfnum.b.frnum; ++} ++ ++int dwc_otg_hcd_start(dwc_otg_hcd_t * hcd, ++ struct dwc_otg_hcd_function_ops *fops) ++{ ++ int retval = 0; ++ hprt0_data_t hprt0; ++ ++ hcd->fops = fops; ++ if (!dwc_otg_is_device_mode(hcd->core_if) && ++ (!hcd->core_if->adp_enable || hcd->core_if->adp.adp_started)) { ++ dwc_otg_hcd_reinit(hcd); ++ } else { ++ if (hcd->core_if->adp_enable) { ++ /* Clear any interrupt pending in the HPRT, sometimes ++ * Port Connect Detected is not being cleared*/ ++ hprt0.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0); ++ DWC_WRITE_REG32(hcd->core_if->host_if->hprt0, hprt0.d32); ++ } ++ retval = -DWC_E_NO_DEVICE; ++ } ++ ++ return retval; ++} ++ ++void *dwc_otg_hcd_get_priv_data(dwc_otg_hcd_t * hcd) ++{ ++ return hcd->priv; ++} ++ ++void dwc_otg_hcd_set_priv_data(dwc_otg_hcd_t * hcd, void *priv_data) ++{ ++ hcd->priv = priv_data; ++} ++ ++uint32_t dwc_otg_hcd_otg_port(dwc_otg_hcd_t * hcd) ++{ ++ return hcd->otg_port; ++} ++ ++uint32_t dwc_otg_hcd_is_b_host(dwc_otg_hcd_t * hcd) ++{ ++ uint32_t is_b_host; ++ if (hcd->core_if->op_state == B_HOST) { ++ is_b_host = 1; ++ } else { ++ is_b_host = 0; ++ } ++ ++ return is_b_host; ++} ++ ++dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd, ++ int iso_desc_count, int atomic_alloc) ++{ ++ dwc_otg_hcd_urb_t *dwc_otg_urb; ++ uint32_t size; ++ ++ size = ++ sizeof(*dwc_otg_urb) + ++ iso_desc_count * sizeof(struct dwc_otg_hcd_iso_packet_desc); ++ if (atomic_alloc) ++ dwc_otg_urb = DWC_ALLOC_ATOMIC(size); ++ else ++ dwc_otg_urb = DWC_ALLOC(size); ++ ++ dwc_otg_urb->packet_count = iso_desc_count; ++ ++ return dwc_otg_urb; ++} ++ ++void dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_hcd_urb_t * dwc_otg_urb, ++ uint8_t dev_addr, uint8_t ep_num, ++ uint8_t ep_type, uint8_t ep_dir, uint16_t mps) ++{ ++ dwc_otg_hcd_fill_pipe(&dwc_otg_urb->pipe_info, dev_addr, ep_num, ++ ep_type, ep_dir, mps); ++#if 0 ++ DWC_PRINTF ++ ("addr = %d, ep_num = %d, ep_dir = 0x%x, ep_type = 0x%x, mps = %d\n", ++ dev_addr, ep_num, ep_dir, ep_type, mps); ++#endif ++} ++ ++void dwc_otg_hcd_urb_set_params(dwc_otg_hcd_urb_t * dwc_otg_urb, ++ void *urb_handle, void *buf, dwc_dma_t dma, ++ uint32_t buflen, void *setup_packet, ++ dwc_dma_t setup_dma, uint32_t flags, ++ uint16_t interval) ++{ ++ dwc_otg_urb->priv = urb_handle; ++ dwc_otg_urb->buf = buf; ++ dwc_otg_urb->dma = dma; ++ dwc_otg_urb->length = buflen; ++ dwc_otg_urb->setup_packet = setup_packet; ++ dwc_otg_urb->setup_dma = setup_dma; ++ dwc_otg_urb->flags = flags; ++ dwc_otg_urb->interval = interval; ++ dwc_otg_urb->status = -DWC_E_IN_PROGRESS; ++} ++ ++uint32_t dwc_otg_hcd_urb_get_status(dwc_otg_hcd_urb_t * dwc_otg_urb) ++{ ++ return dwc_otg_urb->status; ++} ++ ++uint32_t dwc_otg_hcd_urb_get_actual_length(dwc_otg_hcd_urb_t * dwc_otg_urb) ++{ ++ return dwc_otg_urb->actual_length; ++} ++ ++uint32_t dwc_otg_hcd_urb_get_error_count(dwc_otg_hcd_urb_t * dwc_otg_urb) ++{ ++ return dwc_otg_urb->error_count; ++} ++ ++void dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_hcd_urb_t * dwc_otg_urb, ++ int desc_num, uint32_t offset, ++ uint32_t length) ++{ ++ dwc_otg_urb->iso_descs[desc_num].offset = offset; ++ dwc_otg_urb->iso_descs[desc_num].length = length; ++} ++ ++uint32_t dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_hcd_urb_t * dwc_otg_urb, ++ int desc_num) ++{ ++ return dwc_otg_urb->iso_descs[desc_num].status; ++} ++ ++uint32_t dwc_otg_hcd_urb_get_iso_desc_actual_length(dwc_otg_hcd_urb_t * ++ dwc_otg_urb, int desc_num) ++{ ++ return dwc_otg_urb->iso_descs[desc_num].actual_length; ++} ++ ++int dwc_otg_hcd_is_bandwidth_allocated(dwc_otg_hcd_t * hcd, void *ep_handle) ++{ ++ int allocated = 0; ++ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle; ++ ++ if (qh) { ++ if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) { ++ allocated = 1; ++ } ++ } ++ return allocated; ++} ++ ++int dwc_otg_hcd_is_bandwidth_freed(dwc_otg_hcd_t * hcd, void *ep_handle) ++{ ++ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle; ++ int freed = 0; ++ DWC_ASSERT(qh, "qh is not allocated\n"); ++ ++ if (DWC_LIST_EMPTY(&qh->qh_list_entry)) { ++ freed = 1; ++ } ++ ++ return freed; ++} ++ ++uint8_t dwc_otg_hcd_get_ep_bandwidth(dwc_otg_hcd_t * hcd, void *ep_handle) ++{ ++ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle; ++ DWC_ASSERT(qh, "qh is not allocated\n"); ++ return qh->usecs; ++} ++ ++void dwc_otg_hcd_dump_state(dwc_otg_hcd_t * hcd) ++{ ++#ifdef DEBUG ++ int num_channels; ++ int i; ++ gnptxsts_data_t np_tx_status; ++ hptxsts_data_t p_tx_status; ++ ++ num_channels = hcd->core_if->core_params->host_channels; ++ DWC_PRINTF("\n"); ++ DWC_PRINTF ++ ("************************************************************\n"); ++ DWC_PRINTF("HCD State:\n"); ++ DWC_PRINTF(" Num channels: %d\n", num_channels); ++ for (i = 0; i < num_channels; i++) { ++ dwc_hc_t *hc = hcd->hc_ptr_array[i]; ++ DWC_PRINTF(" Channel %d:\n", i); ++ DWC_PRINTF(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n", ++ hc->dev_addr, hc->ep_num, hc->ep_is_in); ++ DWC_PRINTF(" speed: %d\n", hc->speed); ++ DWC_PRINTF(" ep_type: %d\n", hc->ep_type); ++ DWC_PRINTF(" max_packet: %d\n", hc->max_packet); ++ DWC_PRINTF(" data_pid_start: %d\n", hc->data_pid_start); ++ DWC_PRINTF(" multi_count: %d\n", hc->multi_count); ++ DWC_PRINTF(" xfer_started: %d\n", hc->xfer_started); ++ DWC_PRINTF(" xfer_buff: %p\n", hc->xfer_buff); ++ DWC_PRINTF(" xfer_len: %d\n", hc->xfer_len); ++ DWC_PRINTF(" xfer_count: %d\n", hc->xfer_count); ++ DWC_PRINTF(" halt_on_queue: %d\n", hc->halt_on_queue); ++ DWC_PRINTF(" halt_pending: %d\n", hc->halt_pending); ++ DWC_PRINTF(" halt_status: %d\n", hc->halt_status); ++ DWC_PRINTF(" do_split: %d\n", hc->do_split); ++ DWC_PRINTF(" complete_split: %d\n", hc->complete_split); ++ DWC_PRINTF(" hub_addr: %d\n", hc->hub_addr); ++ DWC_PRINTF(" port_addr: %d\n", hc->port_addr); ++ DWC_PRINTF(" xact_pos: %d\n", hc->xact_pos); ++ DWC_PRINTF(" requests: %d\n", hc->requests); ++ DWC_PRINTF(" qh: %p\n", hc->qh); ++ if (hc->xfer_started) { ++ hfnum_data_t hfnum; ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ hfnum.d32 = ++ DWC_READ_REG32(&hcd->core_if-> ++ host_if->host_global_regs->hfnum); ++ hcchar.d32 = ++ DWC_READ_REG32(&hcd->core_if->host_if-> ++ hc_regs[i]->hcchar); ++ hctsiz.d32 = ++ DWC_READ_REG32(&hcd->core_if->host_if-> ++ hc_regs[i]->hctsiz); ++ hcint.d32 = ++ DWC_READ_REG32(&hcd->core_if->host_if-> ++ hc_regs[i]->hcint); ++ hcintmsk.d32 = ++ DWC_READ_REG32(&hcd->core_if->host_if-> ++ hc_regs[i]->hcintmsk); ++ DWC_PRINTF(" hfnum: 0x%08x\n", hfnum.d32); ++ DWC_PRINTF(" hcchar: 0x%08x\n", hcchar.d32); ++ DWC_PRINTF(" hctsiz: 0x%08x\n", hctsiz.d32); ++ DWC_PRINTF(" hcint: 0x%08x\n", hcint.d32); ++ DWC_PRINTF(" hcintmsk: 0x%08x\n", hcintmsk.d32); ++ } ++ if (hc->xfer_started && hc->qh) { ++ dwc_otg_qtd_t *qtd; ++ dwc_otg_hcd_urb_t *urb; ++ ++ DWC_CIRCLEQ_FOREACH(qtd, &hc->qh->qtd_list, qtd_list_entry) { ++ if (!qtd->in_process) ++ break; ++ ++ urb = qtd->urb; ++ DWC_PRINTF(" URB Info:\n"); ++ DWC_PRINTF(" qtd: %p, urb: %p\n", qtd, urb); ++ if (urb) { ++ DWC_PRINTF(" Dev: %d, EP: %d %s\n", ++ dwc_otg_hcd_get_dev_addr(&urb-> ++ pipe_info), ++ dwc_otg_hcd_get_ep_num(&urb-> ++ pipe_info), ++ dwc_otg_hcd_is_pipe_in(&urb-> ++ pipe_info) ? ++ "IN" : "OUT"); ++ DWC_PRINTF(" Max packet size: %d\n", ++ dwc_otg_hcd_get_mps(&urb-> ++ pipe_info)); ++ DWC_PRINTF(" transfer_buffer: %p\n", ++ urb->buf); ++ DWC_PRINTF(" transfer_dma: %p\n", ++ (void *)urb->dma); ++ DWC_PRINTF(" transfer_buffer_length: %d\n", ++ urb->length); ++ DWC_PRINTF(" actual_length: %d\n", ++ urb->actual_length); ++ } ++ } ++ } ++ } ++ DWC_PRINTF(" non_periodic_channels: %d\n", hcd->non_periodic_channels); ++ DWC_PRINTF(" periodic_channels: %d\n", hcd->periodic_channels); ++ DWC_PRINTF(" periodic_usecs: %d\n", hcd->periodic_usecs); ++ np_tx_status.d32 = ++ DWC_READ_REG32(&hcd->core_if->core_global_regs->gnptxsts); ++ DWC_PRINTF(" NP Tx Req Queue Space Avail: %d\n", ++ np_tx_status.b.nptxqspcavail); ++ DWC_PRINTF(" NP Tx FIFO Space Avail: %d\n", ++ np_tx_status.b.nptxfspcavail); ++ p_tx_status.d32 = ++ DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hptxsts); ++ DWC_PRINTF(" P Tx Req Queue Space Avail: %d\n", ++ p_tx_status.b.ptxqspcavail); ++ DWC_PRINTF(" P Tx FIFO Space Avail: %d\n", p_tx_status.b.ptxfspcavail); ++ dwc_otg_hcd_dump_frrem(hcd); ++ dwc_otg_dump_global_registers(hcd->core_if); ++ dwc_otg_dump_host_registers(hcd->core_if); ++ DWC_PRINTF ++ ("************************************************************\n"); ++ DWC_PRINTF("\n"); ++#endif ++} ++ ++#ifdef DEBUG ++void dwc_print_setup_data(uint8_t * setup) ++{ ++ int i; ++ if (CHK_DEBUG_LEVEL(DBG_HCD)) { ++ DWC_PRINTF("Setup Data = MSB "); ++ for (i = 7; i >= 0; i--) ++ DWC_PRINTF("%02x ", setup[i]); ++ DWC_PRINTF("\n"); ++ DWC_PRINTF(" bmRequestType Tranfer = %s\n", ++ (setup[0] & 0x80) ? "Device-to-Host" : ++ "Host-to-Device"); ++ DWC_PRINTF(" bmRequestType Type = "); ++ switch ((setup[0] & 0x60) >> 5) { ++ case 0: ++ DWC_PRINTF("Standard\n"); ++ break; ++ case 1: ++ DWC_PRINTF("Class\n"); ++ break; ++ case 2: ++ DWC_PRINTF("Vendor\n"); ++ break; ++ case 3: ++ DWC_PRINTF("Reserved\n"); ++ break; ++ } ++ DWC_PRINTF(" bmRequestType Recipient = "); ++ switch (setup[0] & 0x1f) { ++ case 0: ++ DWC_PRINTF("Device\n"); ++ break; ++ case 1: ++ DWC_PRINTF("Interface\n"); ++ break; ++ case 2: ++ DWC_PRINTF("Endpoint\n"); ++ break; ++ case 3: ++ DWC_PRINTF("Other\n"); ++ break; ++ default: ++ DWC_PRINTF("Reserved\n"); ++ break; ++ } ++ DWC_PRINTF(" bRequest = 0x%0x\n", setup[1]); ++ DWC_PRINTF(" wValue = 0x%0x\n", *((uint16_t *) & setup[2])); ++ DWC_PRINTF(" wIndex = 0x%0x\n", *((uint16_t *) & setup[4])); ++ DWC_PRINTF(" wLength = 0x%0x\n\n", *((uint16_t *) & setup[6])); ++ } ++} ++#endif ++ ++void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t * hcd) ++{ ++#if 0 ++ DWC_PRINTF("Frame remaining at SOF:\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->frrem_samples, hcd->frrem_accum, ++ (hcd->frrem_samples > 0) ? ++ hcd->frrem_accum / hcd->frrem_samples : 0); ++ ++ DWC_PRINTF("\n"); ++ DWC_PRINTF("Frame remaining at start_transfer (uframe 7):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->core_if->hfnum_7_samples, ++ hcd->core_if->hfnum_7_frrem_accum, ++ (hcd->core_if->hfnum_7_samples > ++ 0) ? hcd->core_if->hfnum_7_frrem_accum / ++ hcd->core_if->hfnum_7_samples : 0); ++ DWC_PRINTF("Frame remaining at start_transfer (uframe 0):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->core_if->hfnum_0_samples, ++ hcd->core_if->hfnum_0_frrem_accum, ++ (hcd->core_if->hfnum_0_samples > ++ 0) ? hcd->core_if->hfnum_0_frrem_accum / ++ hcd->core_if->hfnum_0_samples : 0); ++ DWC_PRINTF("Frame remaining at start_transfer (uframe 1-6):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->core_if->hfnum_other_samples, ++ hcd->core_if->hfnum_other_frrem_accum, ++ (hcd->core_if->hfnum_other_samples > ++ 0) ? hcd->core_if->hfnum_other_frrem_accum / ++ hcd->core_if->hfnum_other_samples : 0); ++ ++ DWC_PRINTF("\n"); ++ DWC_PRINTF("Frame remaining at sample point A (uframe 7):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_7_samples_a, hcd->hfnum_7_frrem_accum_a, ++ (hcd->hfnum_7_samples_a > 0) ? ++ hcd->hfnum_7_frrem_accum_a / hcd->hfnum_7_samples_a : 0); ++ DWC_PRINTF("Frame remaining at sample point A (uframe 0):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_0_samples_a, hcd->hfnum_0_frrem_accum_a, ++ (hcd->hfnum_0_samples_a > 0) ? ++ hcd->hfnum_0_frrem_accum_a / hcd->hfnum_0_samples_a : 0); ++ DWC_PRINTF("Frame remaining at sample point A (uframe 1-6):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_other_samples_a, hcd->hfnum_other_frrem_accum_a, ++ (hcd->hfnum_other_samples_a > 0) ? ++ hcd->hfnum_other_frrem_accum_a / ++ hcd->hfnum_other_samples_a : 0); ++ ++ DWC_PRINTF("\n"); ++ DWC_PRINTF("Frame remaining at sample point B (uframe 7):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_7_samples_b, hcd->hfnum_7_frrem_accum_b, ++ (hcd->hfnum_7_samples_b > 0) ? ++ hcd->hfnum_7_frrem_accum_b / hcd->hfnum_7_samples_b : 0); ++ DWC_PRINTF("Frame remaining at sample point B (uframe 0):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_0_samples_b, hcd->hfnum_0_frrem_accum_b, ++ (hcd->hfnum_0_samples_b > 0) ? ++ hcd->hfnum_0_frrem_accum_b / hcd->hfnum_0_samples_b : 0); ++ DWC_PRINTF("Frame remaining at sample point B (uframe 1-6):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_other_samples_b, hcd->hfnum_other_frrem_accum_b, ++ (hcd->hfnum_other_samples_b > 0) ? ++ hcd->hfnum_other_frrem_accum_b / ++ hcd->hfnum_other_samples_b : 0); ++#endif ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd.h +new file mode 100644 +index 0000000..23eea36 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd.h +@@ -0,0 +1,803 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.h $ ++ * $Revision: #58 $ ++ * $Date: 2011/09/15 $ ++ * $Change: 1846647 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++#ifndef __DWC_HCD_H__ ++#define __DWC_HCD_H__ ++ ++#include "dwc_otg_os_dep.h" ++#include "usb.h" ++#include "dwc_otg_hcd_if.h" ++#include "dwc_otg_core_if.h" ++#include "dwc_list.h" ++#include "dwc_otg_cil.h" ++ ++/** ++ * @file ++ * ++ * This file contains the structures, constants, and interfaces for ++ * the Host Contoller Driver (HCD). ++ * ++ * The Host Controller Driver (HCD) is responsible for translating requests ++ * from the USB Driver into the appropriate actions on the DWC_otg controller. ++ * It isolates the USBD from the specifics of the controller by providing an ++ * API to the USBD. ++ */ ++ ++struct dwc_otg_hcd_pipe_info { ++ uint8_t dev_addr; ++ uint8_t ep_num; ++ uint8_t pipe_type; ++ uint8_t pipe_dir; ++ uint16_t mps; ++}; ++ ++struct dwc_otg_hcd_iso_packet_desc { ++ uint32_t offset; ++ uint32_t length; ++ uint32_t actual_length; ++ uint32_t status; ++}; ++ ++struct dwc_otg_qtd; ++ ++struct dwc_otg_hcd_urb { ++ void *priv; ++ struct dwc_otg_qtd *qtd; ++ void *buf; ++ dwc_dma_t dma; ++ void *setup_packet; ++ dwc_dma_t setup_dma; ++ uint32_t length; ++ uint32_t actual_length; ++ uint32_t status; ++ uint32_t error_count; ++ uint32_t packet_count; ++ uint32_t flags; ++ uint16_t interval; ++ struct dwc_otg_hcd_pipe_info pipe_info; ++ struct dwc_otg_hcd_iso_packet_desc iso_descs[0]; ++}; ++ ++static inline uint8_t dwc_otg_hcd_get_ep_num(struct dwc_otg_hcd_pipe_info *pipe) ++{ ++ return pipe->ep_num; ++} ++ ++static inline uint8_t dwc_otg_hcd_get_pipe_type(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return pipe->pipe_type; ++} ++ ++static inline uint16_t dwc_otg_hcd_get_mps(struct dwc_otg_hcd_pipe_info *pipe) ++{ ++ return pipe->mps; ++} ++ ++static inline uint8_t dwc_otg_hcd_get_dev_addr(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return pipe->dev_addr; ++} ++ ++static inline uint8_t dwc_otg_hcd_is_pipe_isoc(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return (pipe->pipe_type == UE_ISOCHRONOUS); ++} ++ ++static inline uint8_t dwc_otg_hcd_is_pipe_int(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return (pipe->pipe_type == UE_INTERRUPT); ++} ++ ++static inline uint8_t dwc_otg_hcd_is_pipe_bulk(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return (pipe->pipe_type == UE_BULK); ++} ++ ++static inline uint8_t dwc_otg_hcd_is_pipe_control(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return (pipe->pipe_type == UE_CONTROL); ++} ++ ++static inline uint8_t dwc_otg_hcd_is_pipe_in(struct dwc_otg_hcd_pipe_info *pipe) ++{ ++ return (pipe->pipe_dir == UE_DIR_IN); ++} ++ ++static inline uint8_t dwc_otg_hcd_is_pipe_out(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return (!dwc_otg_hcd_is_pipe_in(pipe)); ++} ++ ++static inline void dwc_otg_hcd_fill_pipe(struct dwc_otg_hcd_pipe_info *pipe, ++ uint8_t devaddr, uint8_t ep_num, ++ uint8_t pipe_type, uint8_t pipe_dir, ++ uint16_t mps) ++{ ++ pipe->dev_addr = devaddr; ++ pipe->ep_num = ep_num; ++ pipe->pipe_type = pipe_type; ++ pipe->pipe_dir = pipe_dir; ++ pipe->mps = mps; ++} ++ ++/** ++ * Phases for control transfers. ++ */ ++typedef enum dwc_otg_control_phase { ++ DWC_OTG_CONTROL_SETUP, ++ DWC_OTG_CONTROL_DATA, ++ DWC_OTG_CONTROL_STATUS ++} dwc_otg_control_phase_e; ++ ++/** Transaction types. */ ++typedef enum dwc_otg_transaction_type { ++ DWC_OTG_TRANSACTION_NONE, ++ DWC_OTG_TRANSACTION_PERIODIC, ++ DWC_OTG_TRANSACTION_NON_PERIODIC, ++ DWC_OTG_TRANSACTION_ALL ++} dwc_otg_transaction_type_e; ++ ++struct dwc_otg_qh; ++ ++/** ++ * A Queue Transfer Descriptor (QTD) holds the state of a bulk, control, ++ * interrupt, or isochronous transfer. A single QTD is created for each URB ++ * (of one of these types) submitted to the HCD. The transfer associated with ++ * a QTD may require one or multiple transactions. ++ * ++ * A QTD is linked to a Queue Head, which is entered in either the ++ * non-periodic or periodic schedule for execution. When a QTD is chosen for ++ * execution, some or all of its transactions may be executed. After ++ * execution, the state of the QTD is updated. The QTD may be retired if all ++ * its transactions are complete or if an error occurred. Otherwise, it ++ * remains in the schedule so more transactions can be executed later. ++ */ ++typedef struct dwc_otg_qtd { ++ /** ++ * Determines the PID of the next data packet for the data phase of ++ * control transfers. Ignored for other transfer types.<br> ++ * One of the following values: ++ * - DWC_OTG_HC_PID_DATA0 ++ * - DWC_OTG_HC_PID_DATA1 ++ */ ++ uint8_t data_toggle; ++ ++ /** Current phase for control transfers (Setup, Data, or Status). */ ++ dwc_otg_control_phase_e control_phase; ++ ++ /** Keep track of the current split type ++ * for FS/LS endpoints on a HS Hub */ ++ uint8_t complete_split; ++ ++ /** How many bytes transferred during SSPLIT OUT */ ++ uint32_t ssplit_out_xfer_count; ++ ++ /** ++ * Holds the number of bus errors that have occurred for a transaction ++ * within this transfer. ++ */ ++ uint8_t error_count; ++ ++ /** ++ * Index of the next frame descriptor for an isochronous transfer. A ++ * frame descriptor describes the buffer position and length of the ++ * data to be transferred in the next scheduled (micro)frame of an ++ * isochronous transfer. It also holds status for that transaction. ++ * The frame index starts at 0. ++ */ ++ uint16_t isoc_frame_index; ++ ++ /** Position of the ISOC split on full/low speed */ ++ uint8_t isoc_split_pos; ++ ++ /** Position of the ISOC split in the buffer for the current frame */ ++ uint16_t isoc_split_offset; ++ ++ /** URB for this transfer */ ++ struct dwc_otg_hcd_urb *urb; ++ ++ struct dwc_otg_qh *qh; ++ ++ /** This list of QTDs */ ++ DWC_CIRCLEQ_ENTRY(dwc_otg_qtd) qtd_list_entry; ++ ++ /** Indicates if this QTD is currently processed by HW. */ ++ uint8_t in_process; ++ ++ /** Number of DMA descriptors for this QTD */ ++ uint8_t n_desc; ++ ++ /** ++ * Last activated frame(packet) index. ++ * Used in Descriptor DMA mode only. ++ */ ++ uint16_t isoc_frame_index_last; ++ ++} dwc_otg_qtd_t; ++ ++DWC_CIRCLEQ_HEAD(dwc_otg_qtd_list, dwc_otg_qtd); ++ ++/** ++ * A Queue Head (QH) holds the static characteristics of an endpoint and ++ * maintains a list of transfers (QTDs) for that endpoint. A QH structure may ++ * be entered in either the non-periodic or periodic schedule. ++ */ ++typedef struct dwc_otg_qh { ++ /** ++ * Endpoint type. ++ * One of the following values: ++ * - UE_CONTROL ++ * - UE_BULK ++ * - UE_INTERRUPT ++ * - UE_ISOCHRONOUS ++ */ ++ uint8_t ep_type; ++ uint8_t ep_is_in; ++ ++ /** wMaxPacketSize Field of Endpoint Descriptor. */ ++ uint16_t maxp; ++ ++ /** ++ * Device speed. ++ * One of the following values: ++ * - DWC_OTG_EP_SPEED_LOW ++ * - DWC_OTG_EP_SPEED_FULL ++ * - DWC_OTG_EP_SPEED_HIGH ++ */ ++ uint8_t dev_speed; ++ ++ /** ++ * Determines the PID of the next data packet for non-control ++ * transfers. Ignored for control transfers.<br> ++ * One of the following values: ++ * - DWC_OTG_HC_PID_DATA0 ++ * - DWC_OTG_HC_PID_DATA1 ++ */ ++ uint8_t data_toggle; ++ ++ /** Ping state if 1. */ ++ uint8_t ping_state; ++ ++ /** ++ * List of QTDs for this QH. ++ */ ++ struct dwc_otg_qtd_list qtd_list; ++ ++ /** Host channel currently processing transfers for this QH. */ ++ struct dwc_hc *channel; ++ ++ /** Full/low speed endpoint on high-speed hub requires split. */ ++ uint8_t do_split; ++ ++ /** @name Periodic schedule information */ ++ /** @{ */ ++ ++ /** Bandwidth in microseconds per (micro)frame. */ ++ uint16_t usecs; ++ ++ /** Interval between transfers in (micro)frames. */ ++ uint16_t interval; ++ ++ /** ++ * (micro)frame to initialize a periodic transfer. The transfer ++ * executes in the following (micro)frame. ++ */ ++ uint16_t sched_frame; ++ ++ /** (micro)frame at which last start split was initialized. */ ++ uint16_t start_split_frame; ++ ++ /** @} */ ++ ++ /** ++ * Used instead of original buffer if ++ * it(physical address) is not dword-aligned. ++ */ ++ uint8_t *dw_align_buf; ++ dwc_dma_t dw_align_buf_dma; ++ ++ /** Entry for QH in either the periodic or non-periodic schedule. */ ++ dwc_list_link_t qh_list_entry; ++ ++ /** @name Descriptor DMA support */ ++ /** @{ */ ++ ++ /** Descriptor List. */ ++ dwc_otg_host_dma_desc_t *desc_list; ++ ++ /** Descriptor List physical address. */ ++ dwc_dma_t desc_list_dma; ++ ++ /** ++ * Xfer Bytes array. ++ * Each element corresponds to a descriptor and indicates ++ * original XferSize size value for the descriptor. ++ */ ++ uint32_t *n_bytes; ++ ++ /** Actual number of transfer descriptors in a list. */ ++ uint16_t ntd; ++ ++ /** First activated isochronous transfer descriptor index. */ ++ uint8_t td_first; ++ /** Last activated isochronous transfer descriptor index. */ ++ uint8_t td_last; ++ ++ /** @} */ ++ ++} dwc_otg_qh_t; ++ ++DWC_CIRCLEQ_HEAD(hc_list, dwc_hc); ++ ++/** ++ * This structure holds the state of the HCD, including the non-periodic and ++ * periodic schedules. ++ */ ++struct dwc_otg_hcd { ++ /** The DWC otg device pointer */ ++ struct dwc_otg_device *otg_dev; ++ /** DWC OTG Core Interface Layer */ ++ dwc_otg_core_if_t *core_if; ++ ++ /** Function HCD driver callbacks */ ++ struct dwc_otg_hcd_function_ops *fops; ++ ++ /** Internal DWC HCD Flags */ ++ volatile union dwc_otg_hcd_internal_flags { ++ uint32_t d32; ++ struct { ++ unsigned port_connect_status_change:1; ++ unsigned port_connect_status:1; ++ unsigned port_reset_change:1; ++ unsigned port_enable_change:1; ++ unsigned port_suspend_change:1; ++ unsigned port_over_current_change:1; ++ unsigned port_l1_change:1; ++ unsigned reserved:26; ++ } b; ++ } flags; ++ ++ /** ++ * Inactive items in the non-periodic schedule. This is a list of ++ * Queue Heads. Transfers associated with these Queue Heads are not ++ * currently assigned to a host channel. ++ */ ++ dwc_list_link_t non_periodic_sched_inactive; ++ ++ /** ++ * Active items in the non-periodic schedule. This is a list of ++ * Queue Heads. Transfers associated with these Queue Heads are ++ * currently assigned to a host channel. ++ */ ++ dwc_list_link_t non_periodic_sched_active; ++ ++ /** ++ * Pointer to the next Queue Head to process in the active ++ * non-periodic schedule. ++ */ ++ dwc_list_link_t *non_periodic_qh_ptr; ++ ++ /** ++ * Inactive items in the periodic schedule. This is a list of QHs for ++ * periodic transfers that are _not_ scheduled for the next frame. ++ * Each QH in the list has an interval counter that determines when it ++ * needs to be scheduled for execution. This scheduling mechanism ++ * allows only a simple calculation for periodic bandwidth used (i.e. ++ * must assume that all periodic transfers may need to execute in the ++ * same frame). However, it greatly simplifies scheduling and should ++ * be sufficient for the vast majority of OTG hosts, which need to ++ * connect to a small number of peripherals at one time. ++ * ++ * Items move from this list to periodic_sched_ready when the QH ++ * interval counter is 0 at SOF. ++ */ ++ dwc_list_link_t periodic_sched_inactive; ++ ++ /** ++ * List of periodic QHs that are ready for execution in the next ++ * frame, but have not yet been assigned to host channels. ++ * ++ * Items move from this list to periodic_sched_assigned as host ++ * channels become available during the current frame. ++ */ ++ dwc_list_link_t periodic_sched_ready; ++ ++ /** ++ * List of periodic QHs to be executed in the next frame that are ++ * assigned to host channels. ++ * ++ * Items move from this list to periodic_sched_queued as the ++ * transactions for the QH are queued to the DWC_otg controller. ++ */ ++ dwc_list_link_t periodic_sched_assigned; ++ ++ /** ++ * List of periodic QHs that have been queued for execution. ++ * ++ * Items move from this list to either periodic_sched_inactive or ++ * periodic_sched_ready when the channel associated with the transfer ++ * is released. If the interval for the QH is 1, the item moves to ++ * periodic_sched_ready because it must be rescheduled for the next ++ * frame. Otherwise, the item moves to periodic_sched_inactive. ++ */ ++ dwc_list_link_t periodic_sched_queued; ++ ++ /** ++ * Total bandwidth claimed so far for periodic transfers. This value ++ * is in microseconds per (micro)frame. The assumption is that all ++ * periodic transfers may occur in the same (micro)frame. ++ */ ++ uint16_t periodic_usecs; ++ ++ /** ++ * Frame number read from the core at SOF. The value ranges from 0 to ++ * DWC_HFNUM_MAX_FRNUM. ++ */ ++ uint16_t frame_number; ++ ++ /** ++ * Count of periodic QHs, if using several eps. For SOF enable/disable. ++ */ ++ uint16_t periodic_qh_count; ++ ++ /** ++ * Free host channels in the controller. This is a list of ++ * dwc_hc_t items. ++ */ ++ struct hc_list free_hc_list; ++ /** ++ * Number of host channels assigned to periodic transfers. Currently ++ * assuming that there is a dedicated host channel for each periodic ++ * transaction and at least one host channel available for ++ * non-periodic transactions. ++ */ ++ int periodic_channels; ++ ++ /** ++ * Number of host channels assigned to non-periodic transfers. ++ */ ++ int non_periodic_channels; ++ ++ /** ++ * Array of pointers to the host channel descriptors. Allows accessing ++ * a host channel descriptor given the host channel number. This is ++ * useful in interrupt handlers. ++ */ ++ struct dwc_hc *hc_ptr_array[MAX_EPS_CHANNELS]; ++ ++ /** ++ * Buffer to use for any data received during the status phase of a ++ * control transfer. Normally no data is transferred during the status ++ * phase. This buffer is used as a bit bucket. ++ */ ++ uint8_t *status_buf; ++ ++ /** ++ * DMA address for status_buf. ++ */ ++ dma_addr_t status_buf_dma; ++#define DWC_OTG_HCD_STATUS_BUF_SIZE 64 ++ ++ /** ++ * Connection timer. An OTG host must display a message if the device ++ * does not connect. Started when the VBus power is turned on via ++ * sysfs attribute "buspower". ++ */ ++ dwc_timer_t *conn_timer; ++ ++ /* Tasket to do a reset */ ++ dwc_tasklet_t *reset_tasklet; ++ ++ /* */ ++ dwc_spinlock_t *lock; ++ ++ /** ++ * Private data that could be used by OS wrapper. ++ */ ++ void *priv; ++ ++ uint8_t otg_port; ++ ++ /** Frame List */ ++ uint32_t *frame_list; ++ ++ /** Frame List DMA address */ ++ dma_addr_t frame_list_dma; ++ ++#ifdef DEBUG ++ uint32_t frrem_samples; ++ uint64_t frrem_accum; ++ ++ uint32_t hfnum_7_samples_a; ++ uint64_t hfnum_7_frrem_accum_a; ++ uint32_t hfnum_0_samples_a; ++ uint64_t hfnum_0_frrem_accum_a; ++ uint32_t hfnum_other_samples_a; ++ uint64_t hfnum_other_frrem_accum_a; ++ ++ uint32_t hfnum_7_samples_b; ++ uint64_t hfnum_7_frrem_accum_b; ++ uint32_t hfnum_0_samples_b; ++ uint64_t hfnum_0_frrem_accum_b; ++ uint32_t hfnum_other_samples_b; ++ uint64_t hfnum_other_frrem_accum_b; ++#endif ++}; ++ ++/** @name Transaction Execution Functions */ ++/** @{ */ ++extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t ++ * hcd); ++extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd, ++ dwc_otg_transaction_type_e tr_type); ++ ++/** @} */ ++ ++/** @name Interrupt Handler Functions */ ++/** @{ */ ++extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t * ++ dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t * ++ dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t * ++ dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_incomplete_periodic_intr(dwc_otg_hcd_t * ++ dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_conn_id_status_change_intr(dwc_otg_hcd_t * ++ dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_disconnect_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, ++ uint32_t num); ++extern int32_t dwc_otg_hcd_handle_session_req_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_wakeup_detected_intr(dwc_otg_hcd_t * ++ dwc_otg_hcd); ++/** @} */ ++ ++/** @name Schedule Queue Functions */ ++/** @{ */ ++ ++/* Implemented in dwc_otg_hcd_queue.c */ ++extern dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd, ++ dwc_otg_hcd_urb_t * urb, int atomic_alloc); ++extern void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh); ++extern int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh); ++extern void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh); ++extern void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, ++ int sched_csplit); ++ ++/** Remove and free a QH */ ++static inline void dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd_t * hcd, ++ dwc_otg_qh_t * qh) ++{ ++ dwc_irqflags_t flags; ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ dwc_otg_hcd_qh_free(hcd, qh); ++} ++ ++/** Allocates memory for a QH structure. ++ * @return Returns the memory allocate or NULL on error. */ ++static inline dwc_otg_qh_t *dwc_otg_hcd_qh_alloc(int atomic_alloc) ++{ ++ if (atomic_alloc) ++ return (dwc_otg_qh_t *) DWC_ALLOC_ATOMIC(sizeof(dwc_otg_qh_t)); ++ else ++ return (dwc_otg_qh_t *) DWC_ALLOC(sizeof(dwc_otg_qh_t)); ++} ++ ++extern dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb, ++ int atomic_alloc); ++extern void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb); ++extern int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, dwc_otg_hcd_t * dwc_otg_hcd, ++ dwc_otg_qh_t ** qh, int atomic_alloc); ++ ++/** Allocates memory for a QTD structure. ++ * @return Returns the memory allocate or NULL on error. */ ++static inline dwc_otg_qtd_t *dwc_otg_hcd_qtd_alloc(int atomic_alloc) ++{ ++ if (atomic_alloc) ++ return (dwc_otg_qtd_t *) DWC_ALLOC_ATOMIC(sizeof(dwc_otg_qtd_t)); ++ else ++ return (dwc_otg_qtd_t *) DWC_ALLOC(sizeof(dwc_otg_qtd_t)); ++} ++ ++/** Frees the memory for a QTD structure. QTD should already be removed from ++ * list. ++ * @param qtd QTD to free.*/ ++static inline void dwc_otg_hcd_qtd_free(dwc_otg_qtd_t * qtd) ++{ ++ DWC_FREE(qtd); ++} ++ ++/** Removes a QTD from list. ++ * @param hcd HCD instance. ++ * @param qtd QTD to remove from list. ++ * @param qh QTD belongs to. ++ */ ++static inline void dwc_otg_hcd_qtd_remove(dwc_otg_hcd_t * hcd, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_qh_t * qh) ++{ ++ DWC_CIRCLEQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry); ++} ++ ++/** Remove and free a QTD ++ * Need to disable IRQ and hold hcd lock while calling this function out of ++ * interrupt servicing chain */ ++static inline void dwc_otg_hcd_qtd_remove_and_free(dwc_otg_hcd_t * hcd, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_qh_t * qh) ++{ ++ dwc_otg_hcd_qtd_remove(hcd, qtd, qh); ++ dwc_otg_hcd_qtd_free(qtd); ++} ++ ++/** @} */ ++ ++/** @name Descriptor DMA Supporting Functions */ ++/** @{ */ ++ ++extern void dwc_otg_hcd_start_xfer_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh); ++extern void dwc_otg_hcd_complete_xfer_ddma(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_halt_status_e halt_status); ++ ++extern int dwc_otg_hcd_qh_init_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh); ++extern void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh); ++ ++/** @} */ ++ ++/** @name Internal Functions */ ++/** @{ */ ++dwc_otg_qh_t *dwc_urb_to_qh(dwc_otg_hcd_urb_t * urb); ++/** @} */ ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++extern int dwc_otg_hcd_get_hc_for_lpm_tran(dwc_otg_hcd_t * hcd, ++ uint8_t devaddr); ++extern void dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd_t * hcd); ++#endif ++ ++/** Gets the QH that contains the list_head */ ++#define dwc_list_to_qh(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qh_t, qh_list_entry) ++ ++/** Gets the QTD that contains the list_head */ ++#define dwc_list_to_qtd(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qtd_t, qtd_list_entry) ++ ++/** Check if QH is non-periodic */ ++#define dwc_qh_is_non_per(_qh_ptr_) ((_qh_ptr_->ep_type == UE_BULK) || \ ++ (_qh_ptr_->ep_type == UE_CONTROL)) ++ ++/** High bandwidth multiplier as encoded in highspeed endpoint descriptors */ ++#define dwc_hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) ++ ++/** Packet size for any kind of endpoint descriptor */ ++#define dwc_max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) ++ ++/** ++ * Returns true if _frame1 is less than or equal to _frame2. The comparison is ++ * done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the ++ * frame number when the max frame number is reached. ++ */ ++static inline int dwc_frame_num_le(uint16_t frame1, uint16_t frame2) ++{ ++ return ((frame2 - frame1) & DWC_HFNUM_MAX_FRNUM) <= ++ (DWC_HFNUM_MAX_FRNUM >> 1); ++} ++ ++/** ++ * Returns true if _frame1 is greater than _frame2. The comparison is done ++ * modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the frame ++ * number when the max frame number is reached. ++ */ ++static inline int dwc_frame_num_gt(uint16_t frame1, uint16_t frame2) ++{ ++ return (frame1 != frame2) && ++ (((frame1 - frame2) & DWC_HFNUM_MAX_FRNUM) < ++ (DWC_HFNUM_MAX_FRNUM >> 1)); ++} ++ ++/** ++ * Increments _frame by the amount specified by _inc. The addition is done ++ * modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value. ++ */ ++static inline uint16_t dwc_frame_num_inc(uint16_t frame, uint16_t inc) ++{ ++ return (frame + inc) & DWC_HFNUM_MAX_FRNUM; ++} ++ ++static inline uint16_t dwc_full_frame_num(uint16_t frame) ++{ ++ return (frame & DWC_HFNUM_MAX_FRNUM) >> 3; ++} ++ ++static inline uint16_t dwc_micro_frame_num(uint16_t frame) ++{ ++ return frame & 0x7; ++} ++ ++void dwc_otg_hcd_save_data_toggle(dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd); ++ ++#ifdef DEBUG ++/** ++ * Macro to sample the remaining PHY clocks left in the current frame. This ++ * may be used during debugging to determine the average time it takes to ++ * execute sections of code. There are two possible sample points, "a" and ++ * "b", so the _letter argument must be one of these values. ++ * ++ * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For ++ * example, "cat /sys/devices/lm0/hcd_frrem". ++ */ ++#define dwc_sample_frrem(_hcd, _qh, _letter) \ ++{ \ ++ hfnum_data_t hfnum; \ ++ dwc_otg_qtd_t *qtd; \ ++ qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); \ ++ if (usb_pipeint(qtd->urb->pipe) && _qh->start_split_frame != 0 && !qtd->complete_split) { \ ++ hfnum.d32 = DWC_READ_REG32(&_hcd->core_if->host_if->host_global_regs->hfnum); \ ++ switch (hfnum.b.frnum & 0x7) { \ ++ case 7: \ ++ _hcd->hfnum_7_samples_##_letter++; \ ++ _hcd->hfnum_7_frrem_accum_##_letter += hfnum.b.frrem; \ ++ break; \ ++ case 0: \ ++ _hcd->hfnum_0_samples_##_letter++; \ ++ _hcd->hfnum_0_frrem_accum_##_letter += hfnum.b.frrem; \ ++ break; \ ++ default: \ ++ _hcd->hfnum_other_samples_##_letter++; \ ++ _hcd->hfnum_other_frrem_accum_##_letter += hfnum.b.frrem; \ ++ break; \ ++ } \ ++ } \ ++} ++#else ++#define dwc_sample_frrem(_hcd, _qh, _letter) ++#endif ++#endif ++#endif /* DWC_DEVICE_ONLY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_ddma.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_ddma.c +new file mode 100644 +index 0000000..fd20354 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_ddma.c +@@ -0,0 +1,1122 @@ ++/*========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_ddma.c $ ++ * $Revision: #11 $ ++ * $Date: 2013/01/24 $ ++ * $Change: 2150761 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++/** @file ++ * This file contains Descriptor DMA support implementation for host mode. ++ */ ++ ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++static inline uint8_t frame_list_idx(uint16_t frame) ++{ ++ return (frame & (MAX_FRLIST_EN_NUM - 1)); ++} ++ ++static inline uint16_t desclist_idx_inc(uint16_t idx, uint16_t inc, uint8_t speed) ++{ ++ return (idx + inc) & ++ (((speed == ++ DWC_OTG_EP_SPEED_HIGH) ? MAX_DMA_DESC_NUM_HS_ISOC : ++ MAX_DMA_DESC_NUM_GENERIC) - 1); ++} ++ ++static inline uint16_t desclist_idx_dec(uint16_t idx, uint16_t inc, uint8_t speed) ++{ ++ return (idx - inc) & ++ (((speed == ++ DWC_OTG_EP_SPEED_HIGH) ? MAX_DMA_DESC_NUM_HS_ISOC : ++ MAX_DMA_DESC_NUM_GENERIC) - 1); ++} ++ ++static inline uint16_t max_desc_num(dwc_otg_qh_t * qh) ++{ ++ return (((qh->ep_type == UE_ISOCHRONOUS) ++ && (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH)) ++ ? MAX_DMA_DESC_NUM_HS_ISOC : MAX_DMA_DESC_NUM_GENERIC); ++} ++static inline uint16_t frame_incr_val(dwc_otg_qh_t * qh) ++{ ++ return ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) ++ ? ((qh->interval + 8 - 1) / 8) ++ : qh->interval); ++} ++ ++static int desc_list_alloc(dwc_otg_qh_t * qh) ++{ ++ int retval = 0; ++ ++ qh->desc_list = (dwc_otg_host_dma_desc_t *) ++ DWC_DMA_ALLOC(sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh), ++ &qh->desc_list_dma); ++ ++ if (!qh->desc_list) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: DMA descriptor list allocation failed\n", __func__); ++ ++ } ++ ++ dwc_memset(qh->desc_list, 0x00, ++ sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh)); ++ ++ qh->n_bytes = ++ (uint32_t *) DWC_ALLOC(sizeof(uint32_t) * max_desc_num(qh)); ++ ++ if (!qh->n_bytes) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR ++ ("%s: Failed to allocate array for descriptors' size actual values\n", ++ __func__); ++ ++ } ++ return retval; ++ ++} ++ ++static void desc_list_free(dwc_otg_qh_t * qh) ++{ ++ if (qh->desc_list) { ++ DWC_DMA_FREE(max_desc_num(qh), qh->desc_list, ++ qh->desc_list_dma); ++ qh->desc_list = NULL; ++ } ++ ++ if (qh->n_bytes) { ++ DWC_FREE(qh->n_bytes); ++ qh->n_bytes = NULL; ++ } ++} ++ ++static int frame_list_alloc(dwc_otg_hcd_t * hcd) ++{ ++ int retval = 0; ++ if (hcd->frame_list) ++ return 0; ++ ++ hcd->frame_list = DWC_DMA_ALLOC(4 * MAX_FRLIST_EN_NUM, ++ &hcd->frame_list_dma); ++ if (!hcd->frame_list) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: Frame List allocation failed\n", __func__); ++ } ++ ++ dwc_memset(hcd->frame_list, 0x00, 4 * MAX_FRLIST_EN_NUM); ++ ++ return retval; ++} ++ ++static void frame_list_free(dwc_otg_hcd_t * hcd) ++{ ++ if (!hcd->frame_list) ++ return; ++ ++ DWC_DMA_FREE(4 * MAX_FRLIST_EN_NUM, hcd->frame_list, hcd->frame_list_dma); ++ hcd->frame_list = NULL; ++} ++ ++static void per_sched_enable(dwc_otg_hcd_t * hcd, uint16_t fr_list_en) ++{ ++ ++ hcfg_data_t hcfg; ++ ++ hcfg.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hcfg); ++ ++ if (hcfg.b.perschedena) { ++ /* already enabled */ ++ return; ++ } ++ ++ DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hflbaddr, ++ hcd->frame_list_dma); ++ ++ switch (fr_list_en) { ++ case 64: ++ hcfg.b.frlisten = 3; ++ break; ++ case 32: ++ hcfg.b.frlisten = 2; ++ break; ++ case 16: ++ hcfg.b.frlisten = 1; ++ break; ++ case 8: ++ hcfg.b.frlisten = 0; ++ break; ++ default: ++ break; ++ } ++ ++ hcfg.b.perschedena = 1; ++ ++ DWC_DEBUGPL(DBG_HCD, "Enabling Periodic schedule\n"); ++ DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hcfg, hcfg.d32); ++ ++} ++ ++static void per_sched_disable(dwc_otg_hcd_t * hcd) ++{ ++ hcfg_data_t hcfg; ++ ++ hcfg.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hcfg); ++ ++ if (!hcfg.b.perschedena) { ++ /* already disabled */ ++ return; ++ } ++ hcfg.b.perschedena = 0; ++ ++ DWC_DEBUGPL(DBG_HCD, "Disabling Periodic schedule\n"); ++ DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hcfg, hcfg.d32); ++} ++ ++/* ++ * Activates/Deactivates FrameList entries for the channel ++ * based on endpoint servicing period. ++ */ ++void update_frame_list(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, uint8_t enable) ++{ ++ uint16_t i, j, inc; ++ dwc_hc_t *hc = NULL; ++ ++ if (!qh->channel) { ++ DWC_ERROR("qh->channel = %p", qh->channel); ++ return; ++ } ++ ++ if (!hcd) { ++ DWC_ERROR("------hcd = %p", hcd); ++ return; ++ } ++ ++ if (!hcd->frame_list) { ++ DWC_ERROR("-------hcd->frame_list = %p", hcd->frame_list); ++ return; ++ } ++ ++ hc = qh->channel; ++ inc = frame_incr_val(qh); ++ if (qh->ep_type == UE_ISOCHRONOUS) ++ i = frame_list_idx(qh->sched_frame); ++ else ++ i = 0; ++ ++ j = i; ++ do { ++ if (enable) ++ hcd->frame_list[j] |= (1 << hc->hc_num); ++ else ++ hcd->frame_list[j] &= ~(1 << hc->hc_num); ++ j = (j + inc) & (MAX_FRLIST_EN_NUM - 1); ++ } ++ while (j != i); ++ if (!enable) ++ return; ++ hc->schinfo = 0; ++ if (qh->channel->speed == DWC_OTG_EP_SPEED_HIGH) { ++ j = 1; ++ /* TODO - check this */ ++ inc = (8 + qh->interval - 1) / qh->interval; ++ for (i = 0; i < inc; i++) { ++ hc->schinfo |= j; ++ j = j << qh->interval; ++ } ++ } else { ++ hc->schinfo = 0xff; ++ } ++} ++ ++#if 1 ++void dump_frame_list(dwc_otg_hcd_t * hcd) ++{ ++ int i = 0; ++ DWC_PRINTF("--FRAME LIST (hex) --\n"); ++ for (i = 0; i < MAX_FRLIST_EN_NUM; i++) { ++ DWC_PRINTF("%x\t", hcd->frame_list[i]); ++ if (!(i % 8) && i) ++ DWC_PRINTF("\n"); ++ } ++ DWC_PRINTF("\n----\n"); ++ ++} ++#endif ++ ++static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ dwc_hc_t *hc = qh->channel; ++ if (dwc_qh_is_non_per(qh)) ++ hcd->non_periodic_channels--; ++ else ++ update_frame_list(hcd, qh, 0); ++ ++ /* ++ * The condition is added to prevent double cleanup try in case of device ++ * disconnect. See channel cleanup in dwc_otg_hcd_disconnect_cb(). ++ */ ++ if (hc->qh) { ++ dwc_otg_hc_cleanup(hcd->core_if, hc); ++ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry); ++ hc->qh = NULL; ++ } ++ ++ qh->channel = NULL; ++ qh->ntd = 0; ++ ++ if (qh->desc_list) { ++ dwc_memset(qh->desc_list, 0x00, ++ sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh)); ++ } ++} ++ ++/** ++ * Initializes a QH structure's Descriptor DMA related members. ++ * Allocates memory for descriptor list. ++ * On first periodic QH, allocates memory for FrameList ++ * and enables periodic scheduling. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh The QH to init. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++int dwc_otg_hcd_qh_init_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ int retval = 0; ++ ++ if (qh->do_split) { ++ DWC_ERROR("SPLIT Transfers are not supported in Descriptor DMA.\n"); ++ return -1; ++ } ++ ++ retval = desc_list_alloc(qh); ++ ++ if ((retval == 0) ++ && (qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT)) { ++ if (!hcd->frame_list) { ++ retval = frame_list_alloc(hcd); ++ /* Enable periodic schedule on first periodic QH */ ++ if (retval == 0) ++ per_sched_enable(hcd, MAX_FRLIST_EN_NUM); ++ } ++ } ++ ++ qh->ntd = 0; ++ ++ return retval; ++} ++ ++/** ++ * Frees descriptor list memory associated with the QH. ++ * If QH is periodic and the last, frees FrameList memory ++ * and disables periodic scheduling. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh The QH to init. ++ */ ++void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ desc_list_free(qh); ++ ++ /* ++ * Channel still assigned due to some reasons. ++ * Seen on Isoc URB dequeue. Channel halted but no subsequent ++ * ChHalted interrupt to release the channel. Afterwards ++ * when it comes here from endpoint disable routine ++ * channel remains assigned. ++ */ ++ if (qh->channel) ++ release_channel_ddma(hcd, qh); ++ ++ if ((qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT) ++ && !hcd->periodic_channels && hcd->frame_list) { ++ ++ per_sched_disable(hcd); ++ frame_list_free(hcd); ++ } ++} ++ ++static uint8_t frame_to_desc_idx(dwc_otg_qh_t * qh, uint16_t frame_idx) ++{ ++ if (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) { ++ /* ++ * Descriptor set(8 descriptors) index ++ * which is 8-aligned. ++ */ ++ return (frame_idx & ((MAX_DMA_DESC_NUM_HS_ISOC / 8) - 1)) * 8; ++ } else { ++ return (frame_idx & (MAX_DMA_DESC_NUM_GENERIC - 1)); ++ } ++} ++ ++/* ++ * Determine starting frame for Isochronous transfer. ++ * Few frames skipped to prevent race condition with HC. ++ */ ++static uint8_t calc_starting_frame(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, ++ uint8_t * skip_frames) ++{ ++ uint16_t frame = 0; ++ hcd->frame_number = dwc_otg_hcd_get_frame_number(hcd); ++ ++ /* sched_frame is always frame number(not uFrame) both in FS and HS !! */ ++ ++ /* ++ * skip_frames is used to limit activated descriptors number ++ * to avoid the situation when HC services the last activated ++ * descriptor firstly. ++ * Example for FS: ++ * Current frame is 1, scheduled frame is 3. Since HC always fetches the descriptor ++ * corresponding to curr_frame+1, the descriptor corresponding to frame 2 ++ * will be fetched. If the number of descriptors is max=64 (or greather) the ++ * list will be fully programmed with Active descriptors and it is possible ++ * case(rare) that the latest descriptor(considering rollback) corresponding ++ * to frame 2 will be serviced first. HS case is more probable because, in fact, ++ * up to 11 uframes(16 in the code) may be skipped. ++ */ ++ if (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) { ++ /* ++ * Consider uframe counter also, to start xfer asap. ++ * If half of the frame elapsed skip 2 frames otherwise ++ * just 1 frame. ++ * Starting descriptor index must be 8-aligned, so ++ * if the current frame is near to complete the next one ++ * is skipped as well. ++ */ ++ ++ if (dwc_micro_frame_num(hcd->frame_number) >= 5) { ++ *skip_frames = 2 * 8; ++ frame = dwc_frame_num_inc(hcd->frame_number, *skip_frames); ++ } else { ++ *skip_frames = 1 * 8; ++ frame = dwc_frame_num_inc(hcd->frame_number, *skip_frames); ++ } ++ ++ frame = dwc_full_frame_num(frame); ++ } else { ++ /* ++ * Two frames are skipped for FS - the current and the next. ++ * But for descriptor programming, 1 frame(descriptor) is enough, ++ * see example above. ++ */ ++ *skip_frames = 1; ++ frame = dwc_frame_num_inc(hcd->frame_number, 2); ++ } ++ ++ return frame; ++} ++ ++/* ++ * Calculate initial descriptor index for isochronous transfer ++ * based on scheduled frame. ++ */ ++static uint8_t recalc_initial_desc_idx(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ uint16_t frame = 0, fr_idx, fr_idx_tmp; ++ uint8_t skip_frames = 0; ++ /* ++ * With current ISOC processing algorithm the channel is being ++ * released when no more QTDs in the list(qh->ntd == 0). ++ * Thus this function is called only when qh->ntd == 0 and qh->channel == 0. ++ * ++ * So qh->channel != NULL branch is not used and just not removed from the ++ * source file. It is required for another possible approach which is, ++ * do not disable and release the channel when ISOC session completed, ++ * just move QH to inactive schedule until new QTD arrives. ++ * On new QTD, the QH moved back to 'ready' schedule, ++ * starting frame and therefore starting desc_index are recalculated. ++ * In this case channel is released only on ep_disable. ++ */ ++ ++ /* Calculate starting descriptor index. For INTERRUPT endpoint it is always 0. */ ++ if (qh->channel) { ++ frame = calc_starting_frame(hcd, qh, &skip_frames); ++ /* ++ * Calculate initial descriptor index based on FrameList current bitmap ++ * and servicing period. ++ */ ++ fr_idx_tmp = frame_list_idx(frame); ++ fr_idx = ++ (MAX_FRLIST_EN_NUM + frame_list_idx(qh->sched_frame) - ++ fr_idx_tmp) ++ % frame_incr_val(qh); ++ fr_idx = (fr_idx + fr_idx_tmp) % MAX_FRLIST_EN_NUM; ++ } else { ++ qh->sched_frame = calc_starting_frame(hcd, qh, &skip_frames); ++ fr_idx = frame_list_idx(qh->sched_frame); ++ } ++ ++ qh->td_first = qh->td_last = frame_to_desc_idx(qh, fr_idx); ++ ++ return skip_frames; ++} ++ ++#define ISOC_URB_GIVEBACK_ASAP ++ ++#define MAX_ISOC_XFER_SIZE_FS 1023 ++#define MAX_ISOC_XFER_SIZE_HS 3072 ++#define DESCNUM_THRESHOLD 4 ++ ++static void init_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, ++ uint8_t skip_frames) ++{ ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc; ++ dwc_otg_qtd_t *qtd; ++ dwc_otg_host_dma_desc_t *dma_desc; ++ uint16_t idx, inc, n_desc, ntd_max, max_xfer_size; ++ ++ idx = qh->td_last; ++ inc = qh->interval; ++ n_desc = 0; ++ ++ ntd_max = (max_desc_num(qh) + qh->interval - 1) / qh->interval; ++ if (skip_frames && !qh->channel) ++ ntd_max = ntd_max - skip_frames / qh->interval; ++ ++ max_xfer_size = ++ (qh->dev_speed == ++ DWC_OTG_EP_SPEED_HIGH) ? MAX_ISOC_XFER_SIZE_HS : ++ MAX_ISOC_XFER_SIZE_FS; ++ ++ DWC_CIRCLEQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) { ++ while ((qh->ntd < ntd_max) ++ && (qtd->isoc_frame_index_last < ++ qtd->urb->packet_count)) { ++ ++ dma_desc = &qh->desc_list[idx]; ++ dwc_memset(dma_desc, 0x00, sizeof(dwc_otg_host_dma_desc_t)); ++ ++ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last]; ++ ++ if (frame_desc->length > max_xfer_size) ++ qh->n_bytes[idx] = max_xfer_size; ++ else ++ qh->n_bytes[idx] = frame_desc->length; ++ dma_desc->status.b_isoc.n_bytes = qh->n_bytes[idx]; ++ dma_desc->status.b_isoc.a = 1; ++ dma_desc->status.b_isoc.sts = 0; ++ ++ dma_desc->buf = qtd->urb->dma + frame_desc->offset; ++ ++ qh->ntd++; ++ ++ qtd->isoc_frame_index_last++; ++ ++#ifdef ISOC_URB_GIVEBACK_ASAP ++ /* ++ * Set IOC for each descriptor corresponding to the ++ * last frame of the URB. ++ */ ++ if (qtd->isoc_frame_index_last == ++ qtd->urb->packet_count) ++ dma_desc->status.b_isoc.ioc = 1; ++ ++#endif ++ idx = desclist_idx_inc(idx, inc, qh->dev_speed); ++ n_desc++; ++ ++ } ++ qtd->in_process = 1; ++ } ++ ++ qh->td_last = idx; ++ ++#ifdef ISOC_URB_GIVEBACK_ASAP ++ /* Set IOC for the last descriptor if descriptor list is full */ ++ if (qh->ntd == ntd_max) { ++ idx = desclist_idx_dec(qh->td_last, inc, qh->dev_speed); ++ qh->desc_list[idx].status.b_isoc.ioc = 1; ++ } ++#else ++ /* ++ * Set IOC bit only for one descriptor. ++ * Always try to be ahead of HW processing, ++ * i.e. on IOC generation driver activates next descriptors but ++ * core continues to process descriptors followed the one with IOC set. ++ */ ++ ++ if (n_desc > DESCNUM_THRESHOLD) { ++ /* ++ * Move IOC "up". Required even if there is only one QTD ++ * in the list, cause QTDs migth continue to be queued, ++ * but during the activation it was only one queued. ++ * Actually more than one QTD might be in the list if this function called ++ * from XferCompletion - QTDs was queued during HW processing of the previous ++ * descriptor chunk. ++ */ ++ idx = dwc_desclist_idx_dec(idx, inc * ((qh->ntd + 1) / 2), qh->dev_speed); ++ } else { ++ /* ++ * Set the IOC for the latest descriptor ++ * if either number of descriptor is not greather than threshold ++ * or no more new descriptors activated. ++ */ ++ idx = dwc_desclist_idx_dec(qh->td_last, inc, qh->dev_speed); ++ } ++ ++ qh->desc_list[idx].status.b_isoc.ioc = 1; ++#endif ++} ++ ++static void init_non_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ ++ dwc_hc_t *hc; ++ dwc_otg_host_dma_desc_t *dma_desc; ++ dwc_otg_qtd_t *qtd; ++ int num_packets, len, n_desc = 0; ++ ++ hc = qh->channel; ++ ++ /* ++ * Start with hc->xfer_buff initialized in ++ * assign_and_init_hc(), then if SG transfer consists of multiple URBs, ++ * this pointer re-assigned to the buffer of the currently processed QTD. ++ * For non-SG request there is always one QTD active. ++ */ ++ ++ DWC_CIRCLEQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) { ++ ++ if (n_desc) { ++ /* SG request - more than 1 QTDs */ ++ hc->xfer_buff = (uint8_t *)qtd->urb->dma + qtd->urb->actual_length; ++ hc->xfer_len = qtd->urb->length - qtd->urb->actual_length; ++ } ++ ++ qtd->n_desc = 0; ++ ++ do { ++ dma_desc = &qh->desc_list[n_desc]; ++ len = hc->xfer_len; ++ ++ if (len > MAX_DMA_DESC_SIZE) ++ len = MAX_DMA_DESC_SIZE - hc->max_packet + 1; ++ ++ if (hc->ep_is_in) { ++ if (len > 0) { ++ num_packets = (len + hc->max_packet - 1) / hc->max_packet; ++ } else { ++ /* Need 1 packet for transfer length of 0. */ ++ num_packets = 1; ++ } ++ /* Always program an integral # of max packets for IN transfers. */ ++ len = num_packets * hc->max_packet; ++ } ++ ++ dma_desc->status.b.n_bytes = len; ++ ++ qh->n_bytes[n_desc] = len; ++ ++ if ((qh->ep_type == UE_CONTROL) ++ && (qtd->control_phase == DWC_OTG_CONTROL_SETUP)) ++ dma_desc->status.b.sup = 1; /* Setup Packet */ ++ ++ dma_desc->status.b.a = 1; /* Active descriptor */ ++ dma_desc->status.b.sts = 0; ++ ++ dma_desc->buf = ++ ((unsigned long)hc->xfer_buff & 0xffffffff); ++ ++ /* ++ * Last descriptor(or single) of IN transfer ++ * with actual size less than MaxPacket. ++ */ ++ if (len > hc->xfer_len) { ++ hc->xfer_len = 0; ++ } else { ++ hc->xfer_buff += len; ++ hc->xfer_len -= len; ++ } ++ ++ qtd->n_desc++; ++ n_desc++; ++ } ++ while ((hc->xfer_len > 0) && (n_desc != MAX_DMA_DESC_NUM_GENERIC)); ++ ++ ++ qtd->in_process = 1; ++ ++ if (qh->ep_type == UE_CONTROL) ++ break; ++ ++ if (n_desc == MAX_DMA_DESC_NUM_GENERIC) ++ break; ++ } ++ ++ if (n_desc) { ++ /* Request Transfer Complete interrupt for the last descriptor */ ++ qh->desc_list[n_desc - 1].status.b.ioc = 1; ++ /* End of List indicator */ ++ qh->desc_list[n_desc - 1].status.b.eol = 1; ++ ++ hc->ntd = n_desc; ++ } ++} ++ ++/** ++ * For Control and Bulk endpoints initializes descriptor list ++ * and starts the transfer. ++ * ++ * For Interrupt and Isochronous endpoints initializes descriptor list ++ * then updates FrameList, marking appropriate entries as active. ++ * In case of Isochronous, the starting descriptor index is calculated based ++ * on the scheduled frame, but only on the first transfer descriptor within a session. ++ * Then starts the transfer via enabling the channel. ++ * For Isochronous endpoint the channel is not halted on XferComplete ++ * interrupt so remains assigned to the endpoint(QH) until session is done. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh The QH to init. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++void dwc_otg_hcd_start_xfer_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ /* Channel is already assigned */ ++ dwc_hc_t *hc = qh->channel; ++ uint8_t skip_frames = 0; ++ ++ switch (hc->ep_type) { ++ case DWC_OTG_EP_TYPE_CONTROL: ++ case DWC_OTG_EP_TYPE_BULK: ++ init_non_isoc_dma_desc(hcd, qh); ++ ++ dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc); ++ break; ++ case DWC_OTG_EP_TYPE_INTR: ++ init_non_isoc_dma_desc(hcd, qh); ++ ++ update_frame_list(hcd, qh, 1); ++ ++ dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc); ++ break; ++ case DWC_OTG_EP_TYPE_ISOC: ++ ++ if (!qh->ntd) ++ skip_frames = recalc_initial_desc_idx(hcd, qh); ++ ++ init_isoc_dma_desc(hcd, qh, skip_frames); ++ ++ if (!hc->xfer_started) { ++ ++ update_frame_list(hcd, qh, 1); ++ ++ /* ++ * Always set to max, instead of actual size. ++ * Otherwise ntd will be changed with ++ * channel being enabled. Not recommended. ++ * ++ */ ++ hc->ntd = max_desc_num(qh); ++ /* Enable channel only once for ISOC */ ++ dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc); ++ } ++ ++ break; ++ default: ++ ++ break; ++ } ++} ++ ++static void complete_isoc_xfer_ddma(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_halt_status_e halt_status) ++{ ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc; ++ dwc_otg_qtd_t *qtd, *qtd_tmp; ++ dwc_otg_qh_t *qh; ++ dwc_otg_host_dma_desc_t *dma_desc; ++ uint16_t idx, remain; ++ uint8_t urb_compl; ++ ++ qh = hc->qh; ++ idx = qh->td_first; ++ ++ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) { ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) ++ qtd->in_process = 0; ++ return; ++ } else if ((halt_status == DWC_OTG_HC_XFER_AHB_ERR) || ++ (halt_status == DWC_OTG_HC_XFER_BABBLE_ERR)) { ++ /* ++ * Channel is halted in these error cases. ++ * Considered as serious issues. ++ * Complete all URBs marking all frames as failed, ++ * irrespective whether some of the descriptors(frames) succeeded or no. ++ * Pass error code to completion routine as well, to ++ * update urb->status, some of class drivers might use it to stop ++ * queing transfer requests. ++ */ ++ int err = (halt_status == DWC_OTG_HC_XFER_AHB_ERR) ++ ? (-DWC_E_IO) ++ : (-DWC_E_OVERFLOW); ++ ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) { ++ for (idx = 0; idx < qtd->urb->packet_count; idx++) { ++ frame_desc = &qtd->urb->iso_descs[idx]; ++ frame_desc->status = err; ++ } ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, err); ++ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); ++ } ++ return; ++ } ++ ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) { ++ ++ if (!qtd->in_process) ++ break; ++ ++ urb_compl = 0; ++ ++ do { ++ ++ dma_desc = &qh->desc_list[idx]; ++ ++ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ remain = hc->ep_is_in ? dma_desc->status.b_isoc.n_bytes : 0; ++ ++ if (dma_desc->status.b_isoc.sts == DMA_DESC_STS_PKTERR) { ++ /* ++ * XactError or, unable to complete all the transactions ++ * in the scheduled micro-frame/frame, ++ * both indicated by DMA_DESC_STS_PKTERR. ++ */ ++ qtd->urb->error_count++; ++ frame_desc->actual_length = qh->n_bytes[idx] - remain; ++ frame_desc->status = -DWC_E_PROTOCOL; ++ } else { ++ /* Success */ ++ ++ frame_desc->actual_length = qh->n_bytes[idx] - remain; ++ frame_desc->status = 0; ++ } ++ ++ if (++qtd->isoc_frame_index == qtd->urb->packet_count) { ++ /* ++ * urb->status is not used for isoc transfers here. ++ * The individual frame_desc status are used instead. ++ */ ++ ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); ++ ++ /* ++ * This check is necessary because urb_dequeue can be called ++ * from urb complete callback(sound driver example). ++ * All pending URBs are dequeued there, so no need for ++ * further processing. ++ */ ++ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) { ++ return; ++ } ++ ++ urb_compl = 1; ++ ++ } ++ ++ qh->ntd--; ++ ++ /* Stop if IOC requested descriptor reached */ ++ if (dma_desc->status.b_isoc.ioc) { ++ idx = desclist_idx_inc(idx, qh->interval, hc->speed); ++ goto stop_scan; ++ } ++ ++ idx = desclist_idx_inc(idx, qh->interval, hc->speed); ++ ++ if (urb_compl) ++ break; ++ } ++ while (idx != qh->td_first); ++ } ++stop_scan: ++ qh->td_first = idx; ++} ++ ++uint8_t update_non_isoc_urb_state_ddma(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_host_dma_desc_t * dma_desc, ++ dwc_otg_halt_status_e halt_status, ++ uint32_t n_bytes, uint8_t * xfer_done) ++{ ++ ++ uint16_t remain = hc->ep_is_in ? dma_desc->status.b.n_bytes : 0; ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ ++ if (halt_status == DWC_OTG_HC_XFER_AHB_ERR) { ++ urb->status = -DWC_E_IO; ++ return 1; ++ } ++ if (dma_desc->status.b.sts == DMA_DESC_STS_PKTERR) { ++ switch (halt_status) { ++ case DWC_OTG_HC_XFER_STALL: ++ urb->status = -DWC_E_PIPE; ++ break; ++ case DWC_OTG_HC_XFER_BABBLE_ERR: ++ urb->status = -DWC_E_OVERFLOW; ++ break; ++ case DWC_OTG_HC_XFER_XACT_ERR: ++ urb->status = -DWC_E_PROTOCOL; ++ break; ++ default: ++ DWC_ERROR("%s: Unhandled descriptor error status (%d)\n", __func__, ++ halt_status); ++ break; ++ } ++ return 1; ++ } ++ ++ if (dma_desc->status.b.a == 1) { ++ DWC_DEBUGPL(DBG_HCDV, ++ "Active descriptor encountered on channel %d\n", ++ hc->hc_num); ++ return 0; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL) { ++ if (qtd->control_phase == DWC_OTG_CONTROL_DATA) { ++ urb->actual_length += n_bytes - remain; ++ if (remain || urb->actual_length == urb->length) { ++ /* ++ * For Control Data stage do not set urb->status=0 to prevent ++ * URB callback. Set it when Status phase done. See below. ++ */ ++ *xfer_done = 1; ++ } ++ ++ } else if (qtd->control_phase == DWC_OTG_CONTROL_STATUS) { ++ urb->status = 0; ++ *xfer_done = 1; ++ } ++ /* No handling for SETUP stage */ ++ } else { ++ /* BULK and INTR */ ++ urb->actual_length += n_bytes - remain; ++ if (remain || urb->actual_length == urb->length) { ++ urb->status = 0; ++ *xfer_done = 1; ++ } ++ } ++ ++ return 0; ++} ++ ++static void complete_non_isoc_xfer_ddma(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_halt_status_e halt_status) ++{ ++ dwc_otg_hcd_urb_t *urb = NULL; ++ dwc_otg_qtd_t *qtd, *qtd_tmp; ++ dwc_otg_qh_t *qh; ++ dwc_otg_host_dma_desc_t *dma_desc; ++ uint32_t n_bytes, n_desc, i; ++ uint8_t failed = 0, xfer_done; ++ ++ n_desc = 0; ++ ++ qh = hc->qh; ++ ++ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) { ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) { ++ qtd->in_process = 0; ++ } ++ return; ++ } ++ ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) { ++ ++ urb = qtd->urb; ++ ++ n_bytes = 0; ++ xfer_done = 0; ++ ++ for (i = 0; i < qtd->n_desc; i++) { ++ dma_desc = &qh->desc_list[n_desc]; ++ ++ n_bytes = qh->n_bytes[n_desc]; ++ ++ failed = ++ update_non_isoc_urb_state_ddma(hcd, hc, qtd, ++ dma_desc, ++ halt_status, n_bytes, ++ &xfer_done); ++ ++ if (failed ++ || (xfer_done ++ && (urb->status != -DWC_E_IN_PROGRESS))) { ++ ++ hcd->fops->complete(hcd, urb->priv, urb, ++ urb->status); ++ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); ++ ++ if (failed) ++ goto stop_scan; ++ } else if (qh->ep_type == UE_CONTROL) { ++ if (qtd->control_phase == DWC_OTG_CONTROL_SETUP) { ++ if (urb->length > 0) { ++ qtd->control_phase = DWC_OTG_CONTROL_DATA; ++ } else { ++ qtd->control_phase = DWC_OTG_CONTROL_STATUS; ++ } ++ DWC_DEBUGPL(DBG_HCDV, " Control setup transaction done\n"); ++ } else if (qtd->control_phase == DWC_OTG_CONTROL_DATA) { ++ if (xfer_done) { ++ qtd->control_phase = DWC_OTG_CONTROL_STATUS; ++ DWC_DEBUGPL(DBG_HCDV, " Control data transfer done\n"); ++ } else if (i + 1 == qtd->n_desc) { ++ /* ++ * Last descriptor for Control data stage which is ++ * not completed yet. ++ */ ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ } ++ } ++ } ++ ++ n_desc++; ++ } ++ ++ } ++ ++stop_scan: ++ ++ if (qh->ep_type != UE_CONTROL) { ++ /* ++ * Resetting the data toggle for bulk ++ * and interrupt endpoints in case of stall. See handle_hc_stall_intr() ++ */ ++ if (halt_status == DWC_OTG_HC_XFER_STALL) ++ qh->data_toggle = DWC_OTG_HC_PID_DATA0; ++ else ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ } ++ ++ if (halt_status == DWC_OTG_HC_XFER_COMPLETE) { ++ hcint_data_t hcint; ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ if (hcint.b.nyet) { ++ /* ++ * Got a NYET on the last transaction of the transfer. It ++ * means that the endpoint should be in the PING state at the ++ * beginning of the next transfer. ++ */ ++ qh->ping_state = 1; ++ clear_hc_int(hc_regs, nyet); ++ } ++ ++ } ++ ++} ++ ++/** ++ * This function is called from interrupt handlers. ++ * Scans the descriptor list, updates URB's status and ++ * calls completion routine for the URB if it's done. ++ * Releases the channel to be used by other transfers. ++ * In case of Isochronous endpoint the channel is not halted until ++ * the end of the session, i.e. QTD list is empty. ++ * If periodic channel released the FrameList is updated accordingly. ++ * ++ * Calls transaction selection routines to activate pending transfers. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param hc Host channel, the transfer is completed on. ++ * @param hc_regs Host channel registers. ++ * @param halt_status Reason the channel is being halted, ++ * or just XferComplete for isochronous transfer ++ */ ++void dwc_otg_hcd_complete_xfer_ddma(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_halt_status_e halt_status) ++{ ++ uint8_t continue_isoc_xfer = 0; ++ dwc_otg_transaction_type_e tr_type; ++ dwc_otg_qh_t *qh = hc->qh; ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ ++ complete_isoc_xfer_ddma(hcd, hc, hc_regs, halt_status); ++ ++ /* Release the channel if halted or session completed */ ++ if (halt_status != DWC_OTG_HC_XFER_COMPLETE || ++ DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { ++ ++ /* Halt the channel if session completed */ ++ if (halt_status == DWC_OTG_HC_XFER_COMPLETE) { ++ dwc_otg_hc_halt(hcd->core_if, hc, halt_status); ++ } ++ ++ release_channel_ddma(hcd, qh); ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ } else { ++ /* Keep in assigned schedule to continue transfer */ ++ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned, ++ &qh->qh_list_entry); ++ continue_isoc_xfer = 1; ++ ++ } ++ /** @todo Consider the case when period exceeds FrameList size. ++ * Frame Rollover interrupt should be used. ++ */ ++ } else { ++ /* Scan descriptor list to complete the URB(s), then release the channel */ ++ complete_non_isoc_xfer_ddma(hcd, hc, hc_regs, halt_status); ++ ++ release_channel_ddma(hcd, qh); ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ ++ if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { ++ /* Add back to inactive non-periodic schedule on normal completion */ ++ dwc_otg_hcd_qh_add(hcd, qh); ++ } ++ ++ } ++ tr_type = dwc_otg_hcd_select_transactions(hcd); ++ if (tr_type != DWC_OTG_TRANSACTION_NONE || continue_isoc_xfer) { ++ if (continue_isoc_xfer) { ++ if (tr_type == DWC_OTG_TRANSACTION_NONE) { ++ tr_type = DWC_OTG_TRANSACTION_PERIODIC; ++ } else if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC) { ++ tr_type = DWC_OTG_TRANSACTION_ALL; ++ } ++ } ++ dwc_otg_hcd_queue_transactions(hcd, tr_type); ++ } ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_if.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_if.h +new file mode 100644 +index 0000000..4823167 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_if.h +@@ -0,0 +1,412 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_if.h $ ++ * $Revision: #12 $ ++ * $Date: 2011/10/26 $ ++ * $Change: 1873028 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++#ifndef __DWC_HCD_IF_H__ ++#define __DWC_HCD_IF_H__ ++ ++#include "dwc_otg_core_if.h" ++ ++/** @file ++ * This file defines DWC_OTG HCD Core API. ++ */ ++ ++struct dwc_otg_hcd; ++typedef struct dwc_otg_hcd dwc_otg_hcd_t; ++ ++struct dwc_otg_hcd_urb; ++typedef struct dwc_otg_hcd_urb dwc_otg_hcd_urb_t; ++ ++/** @name HCD Function Driver Callbacks */ ++/** @{ */ ++ ++/** This function is called whenever core switches to host mode. */ ++typedef int (*dwc_otg_hcd_start_cb_t) (dwc_otg_hcd_t * hcd); ++ ++/** This function is called when device has been disconnected */ ++typedef int (*dwc_otg_hcd_disconnect_cb_t) (dwc_otg_hcd_t * hcd); ++ ++/** Wrapper provides this function to HCD to core, so it can get hub information to which device is connected */ ++typedef int (*dwc_otg_hcd_hub_info_from_urb_cb_t) (dwc_otg_hcd_t * hcd, ++ void *urb_handle, ++ uint32_t * hub_addr, ++ uint32_t * port_addr); ++/** Via this function HCD core gets device speed */ ++typedef int (*dwc_otg_hcd_speed_from_urb_cb_t) (dwc_otg_hcd_t * hcd, ++ void *urb_handle); ++ ++/** This function is called when urb is completed */ ++typedef int (*dwc_otg_hcd_complete_urb_cb_t) (dwc_otg_hcd_t * hcd, ++ void *urb_handle, ++ dwc_otg_hcd_urb_t * dwc_otg_urb, ++ int32_t status); ++ ++/** Via this function HCD core gets b_hnp_enable parameter */ ++typedef int (*dwc_otg_hcd_get_b_hnp_enable) (dwc_otg_hcd_t * hcd); ++ ++struct dwc_otg_hcd_function_ops { ++ dwc_otg_hcd_start_cb_t start; ++ dwc_otg_hcd_disconnect_cb_t disconnect; ++ dwc_otg_hcd_hub_info_from_urb_cb_t hub_info; ++ dwc_otg_hcd_speed_from_urb_cb_t speed; ++ dwc_otg_hcd_complete_urb_cb_t complete; ++ dwc_otg_hcd_get_b_hnp_enable get_b_hnp_enable; ++}; ++/** @} */ ++ ++/** @name HCD Core API */ ++/** @{ */ ++/** This function allocates dwc_otg_hcd structure and returns pointer on it. */ ++extern dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void); ++ ++/** This function should be called to initiate HCD Core. ++ * ++ * @param hcd The HCD ++ * @param core_if The DWC_OTG Core ++ * ++ * Returns -DWC_E_NO_MEMORY if no enough memory. ++ * Returns 0 on success ++ */ ++extern int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if); ++ ++/** Frees HCD ++ * ++ * @param hcd The HCD ++ */ ++extern void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd); ++ ++/** This function should be called on every hardware interrupt. ++ * ++ * @param dwc_otg_hcd The HCD ++ * ++ * Returns non zero if interrupt is handled ++ * Return 0 if interrupt is not handled ++ */ ++extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++ ++/** ++ * Returns private data set by ++ * dwc_otg_hcd_set_priv_data function. ++ * ++ * @param hcd The HCD ++ */ ++extern void *dwc_otg_hcd_get_priv_data(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Set private data. ++ * ++ * @param hcd The HCD ++ * @param priv_data pointer to be stored in private data ++ */ ++extern void dwc_otg_hcd_set_priv_data(dwc_otg_hcd_t * hcd, void *priv_data); ++ ++/** ++ * This function initializes the HCD Core. ++ * ++ * @param hcd The HCD ++ * @param fops The Function Driver Operations data structure containing pointers to all callbacks. ++ * ++ * Returns -DWC_E_NO_DEVICE if Core is currently is in device mode. ++ * Returns 0 on success ++ */ ++extern int dwc_otg_hcd_start(dwc_otg_hcd_t * hcd, ++ struct dwc_otg_hcd_function_ops *fops); ++ ++/** ++ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are ++ * stopped. ++ * ++ * @param hcd The HCD ++ */ ++extern void dwc_otg_hcd_stop(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Handles hub class-specific requests. ++ * ++ * @param dwc_otg_hcd The HCD ++ * @param typeReq Request Type ++ * @param wValue wValue from control request ++ * @param wIndex wIndex from control request ++ * @param buf data buffer ++ * @param wLength data buffer length ++ * ++ * Returns -DWC_E_INVALID if invalid argument is passed ++ * Returns 0 on success ++ */ ++extern int dwc_otg_hcd_hub_control(dwc_otg_hcd_t * dwc_otg_hcd, ++ uint16_t typeReq, uint16_t wValue, ++ uint16_t wIndex, uint8_t * buf, ++ uint16_t wLength); ++ ++/** ++ * Returns otg port number. ++ * ++ * @param hcd The HCD ++ */ ++extern uint32_t dwc_otg_hcd_otg_port(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Returns OTG version - either 1.3 or 2.0. ++ * ++ * @param core_if The core_if structure pointer ++ */ ++extern uint16_t dwc_otg_get_otg_version(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Returns 1 if currently core is acting as B host, and 0 otherwise. ++ * ++ * @param hcd The HCD ++ */ ++extern uint32_t dwc_otg_hcd_is_b_host(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Returns current frame number. ++ * ++ * @param hcd The HCD ++ */ ++extern int dwc_otg_hcd_get_frame_number(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Dumps hcd state. ++ * ++ * @param hcd The HCD ++ */ ++extern void dwc_otg_hcd_dump_state(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Dump the average frame remaining at SOF. This can be used to ++ * determine average interrupt latency. Frame remaining is also shown for ++ * start transfer and two additional sample points. ++ * Currently this function is not implemented. ++ * ++ * @param hcd The HCD ++ */ ++extern void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Sends LPM transaction to the local device. ++ * ++ * @param hcd The HCD ++ * @param devaddr Device Address ++ * @param hird Host initiated resume duration ++ * @param bRemoteWake Value of bRemoteWake field in LPM transaction ++ * ++ * Returns negative value if sending LPM transaction was not succeeded. ++ * Returns 0 on success. ++ */ ++extern int dwc_otg_hcd_send_lpm(dwc_otg_hcd_t * hcd, uint8_t devaddr, ++ uint8_t hird, uint8_t bRemoteWake); ++ ++/* URB interface */ ++ ++/** ++ * Allocates memory for dwc_otg_hcd_urb structure. ++ * Allocated memory should be freed by call of DWC_FREE. ++ * ++ * @param hcd The HCD ++ * @param iso_desc_count Count of ISOC descriptors ++ * @param atomic_alloc Specefies whether to perform atomic allocation. ++ */ ++extern dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd, ++ int iso_desc_count, ++ int atomic_alloc); ++ ++/** ++ * Set pipe information in URB. ++ * ++ * @param hcd_urb DWC_OTG URB ++ * @param devaddr Device Address ++ * @param ep_num Endpoint Number ++ * @param ep_type Endpoint Type ++ * @param ep_dir Endpoint Direction ++ * @param mps Max Packet Size ++ */ ++extern void dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_hcd_urb_t * hcd_urb, ++ uint8_t devaddr, uint8_t ep_num, ++ uint8_t ep_type, uint8_t ep_dir, ++ uint16_t mps); ++ ++/* Transfer flags */ ++#define URB_GIVEBACK_ASAP 0x1 ++#define URB_SEND_ZERO_PACKET 0x2 ++ ++/** ++ * Sets dwc_otg_hcd_urb parameters. ++ * ++ * @param urb DWC_OTG URB allocated by dwc_otg_hcd_urb_alloc function. ++ * @param urb_handle Unique handle for request, this will be passed back ++ * to function driver in completion callback. ++ * @param buf The buffer for the data ++ * @param dma The DMA buffer for the data ++ * @param buflen Transfer length ++ * @param sp Buffer for setup data ++ * @param sp_dma DMA address of setup data buffer ++ * @param flags Transfer flags ++ * @param interval Polling interval for interrupt or isochronous transfers. ++ */ ++extern void dwc_otg_hcd_urb_set_params(dwc_otg_hcd_urb_t * urb, ++ void *urb_handle, void *buf, ++ dwc_dma_t dma, uint32_t buflen, void *sp, ++ dwc_dma_t sp_dma, uint32_t flags, ++ uint16_t interval); ++ ++/** Gets status from dwc_otg_hcd_urb ++ * ++ * @param dwc_otg_urb DWC_OTG URB ++ */ ++extern uint32_t dwc_otg_hcd_urb_get_status(dwc_otg_hcd_urb_t * dwc_otg_urb); ++ ++/** Gets actual length from dwc_otg_hcd_urb ++ * ++ * @param dwc_otg_urb DWC_OTG URB ++ */ ++extern uint32_t dwc_otg_hcd_urb_get_actual_length(dwc_otg_hcd_urb_t * ++ dwc_otg_urb); ++ ++/** Gets error count from dwc_otg_hcd_urb. Only for ISOC URBs ++ * ++ * @param dwc_otg_urb DWC_OTG URB ++ */ ++extern uint32_t dwc_otg_hcd_urb_get_error_count(dwc_otg_hcd_urb_t * ++ dwc_otg_urb); ++ ++/** Set ISOC descriptor offset and length ++ * ++ * @param dwc_otg_urb DWC_OTG URB ++ * @param desc_num ISOC descriptor number ++ * @param offset Offset from beginig of buffer. ++ * @param length Transaction length ++ */ ++extern void dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_hcd_urb_t * dwc_otg_urb, ++ int desc_num, uint32_t offset, ++ uint32_t length); ++ ++/** Get status of ISOC descriptor, specified by desc_num ++ * ++ * @param dwc_otg_urb DWC_OTG URB ++ * @param desc_num ISOC descriptor number ++ */ ++extern uint32_t dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_hcd_urb_t * ++ dwc_otg_urb, int desc_num); ++ ++/** Get actual length of ISOC descriptor, specified by desc_num ++ * ++ * @param dwc_otg_urb DWC_OTG URB ++ * @param desc_num ISOC descriptor number ++ */ ++extern uint32_t dwc_otg_hcd_urb_get_iso_desc_actual_length(dwc_otg_hcd_urb_t * ++ dwc_otg_urb, ++ int desc_num); ++ ++/** Queue URB. After transfer is completes, the complete callback will be called with the URB status ++ * ++ * @param dwc_otg_hcd The HCD ++ * @param dwc_otg_urb DWC_OTG URB ++ * @param ep_handle Out parameter for returning endpoint handle ++ * @param atomic_alloc Flag to do atomic allocation if needed ++ * ++ * Returns -DWC_E_NO_DEVICE if no device is connected. ++ * Returns -DWC_E_NO_MEMORY if there is no enough memory. ++ * Returns 0 on success. ++ */ ++extern int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * dwc_otg_hcd, ++ dwc_otg_hcd_urb_t * dwc_otg_urb, ++ void **ep_handle, int atomic_alloc); ++ ++/** De-queue the specified URB ++ * ++ * @param dwc_otg_hcd The HCD ++ * @param dwc_otg_urb DWC_OTG URB ++ */ ++extern int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * dwc_otg_hcd, ++ dwc_otg_hcd_urb_t * dwc_otg_urb); ++ ++/** Frees resources in the DWC_otg controller related to a given endpoint. ++ * Any URBs for the endpoint must already be dequeued. ++ * ++ * @param hcd The HCD ++ * @param ep_handle Endpoint handle, returned by dwc_otg_hcd_urb_enqueue function ++ * @param retry Number of retries if there are queued transfers. ++ * ++ * Returns -DWC_E_INVALID if invalid arguments are passed. ++ * Returns 0 on success ++ */ ++extern int dwc_otg_hcd_endpoint_disable(dwc_otg_hcd_t * hcd, void *ep_handle, ++ int retry); ++ ++/* Resets the data toggle in qh structure. This function can be called from ++ * usb_clear_halt routine. ++ * ++ * @param hcd The HCD ++ * @param ep_handle Endpoint handle, returned by dwc_otg_hcd_urb_enqueue function ++ * ++ * Returns -DWC_E_INVALID if invalid arguments are passed. ++ * Returns 0 on success ++ */ ++extern int dwc_otg_hcd_endpoint_reset(dwc_otg_hcd_t * hcd, void *ep_handle); ++ ++/** Returns 1 if status of specified port is changed and 0 otherwise. ++ * ++ * @param hcd The HCD ++ * @param port Port number ++ */ ++extern int dwc_otg_hcd_is_status_changed(dwc_otg_hcd_t * hcd, int port); ++ ++/** Call this function to check if bandwidth was allocated for specified endpoint. ++ * Only for ISOC and INTERRUPT endpoints. ++ * ++ * @param hcd The HCD ++ * @param ep_handle Endpoint handle ++ */ ++extern int dwc_otg_hcd_is_bandwidth_allocated(dwc_otg_hcd_t * hcd, ++ void *ep_handle); ++ ++/** Call this function to check if bandwidth was freed for specified endpoint. ++ * ++ * @param hcd The HCD ++ * @param ep_handle Endpoint handle ++ */ ++extern int dwc_otg_hcd_is_bandwidth_freed(dwc_otg_hcd_t * hcd, void *ep_handle); ++ ++/** Returns bandwidth allocated for specified endpoint in microseconds. ++ * Only for ISOC and INTERRUPT endpoints. ++ * ++ * @param hcd The HCD ++ * @param ep_handle Endpoint handle ++ */ ++extern uint8_t dwc_otg_hcd_get_ep_bandwidth(dwc_otg_hcd_t * hcd, ++ void *ep_handle); ++ ++/** @} */ ++ ++#endif /* __DWC_HCD_IF_H__ */ ++#endif /* DWC_DEVICE_ONLY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_intr.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_intr.c +new file mode 100644 +index 0000000..23b1d9d +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_intr.c +@@ -0,0 +1,2105 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_intr.c $ ++ * $Revision: #94 $ ++ * $Date: 2013/01/31 $ ++ * $Change: 2155605 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++/** @file ++ * This file contains the implementation of the HCD Interrupt handlers. ++ */ ++ ++/** This function handles interrupts for the HCD. */ ++int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ int retval = 0; ++ ++ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; ++ gintsts_data_t gintsts; ++#ifdef DEBUG ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++#endif ++ ++ if (dwc_otg_check_haps_status(core_if) == -1 ) { ++ DWC_WARN("HAPS is disconnected"); ++ return retval; ++ } ++ ++ /* Exit from ISR if core is hibernated */ ++ if (core_if->hibernation_suspend == 1) { ++ return retval; ++ } ++ DWC_SPINLOCK(dwc_otg_hcd->lock); ++ /* Check if HOST Mode */ ++ if (dwc_otg_is_host_mode(core_if)) { ++ gintsts.d32 = dwc_otg_read_core_intr(core_if); ++ if (!gintsts.d32) { ++ DWC_SPINUNLOCK(dwc_otg_hcd->lock); ++ return 0; ++ } ++#ifdef DEBUG ++ /* Don't print debug message in the interrupt handler on SOF */ ++#ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++#endif ++ DWC_DEBUGPL(DBG_HCD, "\n"); ++#endif ++ ++#ifdef DEBUG ++#ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++#endif ++ DWC_DEBUGPL(DBG_HCD, ++ "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x\n", ++ gintsts.d32); ++#endif ++ ++ if (gintsts.b.sofintr) { ++ retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); ++ } ++ if (gintsts.b.rxstsqlvl) { ++ retval |= ++ dwc_otg_hcd_handle_rx_status_q_level_intr ++ (dwc_otg_hcd); ++ } ++ if (gintsts.b.nptxfempty) { ++ retval |= ++ dwc_otg_hcd_handle_np_tx_fifo_empty_intr ++ (dwc_otg_hcd); ++ } ++ if (gintsts.b.i2cintr) { ++ /** @todo Implement i2cintr handler. */ ++ } ++ if (gintsts.b.portintr) { ++ retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd); ++ } ++ if (gintsts.b.hcintr) { ++ retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd); ++ } ++ if (gintsts.b.ptxfempty) { ++ retval |= ++ dwc_otg_hcd_handle_perio_tx_fifo_empty_intr ++ (dwc_otg_hcd); ++ } ++#ifdef DEBUG ++#ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++#endif ++ { ++ DWC_DEBUGPL(DBG_HCD, ++ "DWC OTG HCD Finished Servicing Interrupts\n"); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintsts=0x%08x\n", ++ DWC_READ_REG32(&global_regs->gintsts)); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintmsk=0x%08x\n", ++ DWC_READ_REG32(&global_regs->gintmsk)); ++ } ++#endif ++ ++#ifdef DEBUG ++#ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++#endif ++ DWC_DEBUGPL(DBG_HCD, "\n"); ++#endif ++ ++ } ++ DWC_SPINUNLOCK(dwc_otg_hcd->lock); ++ return retval; ++} ++ ++#ifdef DWC_TRACK_MISSED_SOFS ++#warning Compiling code to track missed SOFs ++#define FRAME_NUM_ARRAY_SIZE 1000 ++/** ++ * This function is for debug only. ++ */ ++static inline void track_missed_sofs(uint16_t curr_frame_number) ++{ ++ static uint16_t frame_num_array[FRAME_NUM_ARRAY_SIZE]; ++ static uint16_t last_frame_num_array[FRAME_NUM_ARRAY_SIZE]; ++ static int frame_num_idx = 0; ++ static uint16_t last_frame_num = DWC_HFNUM_MAX_FRNUM; ++ static int dumped_frame_num_array = 0; ++ ++ if (frame_num_idx < FRAME_NUM_ARRAY_SIZE) { ++ if (((last_frame_num + 1) & DWC_HFNUM_MAX_FRNUM) != ++ curr_frame_number) { ++ frame_num_array[frame_num_idx] = curr_frame_number; ++ last_frame_num_array[frame_num_idx++] = last_frame_num; ++ } ++ } else if (!dumped_frame_num_array) { ++ int i; ++ DWC_PRINTF("Frame Last Frame\n"); ++ DWC_PRINTF("----- ----------\n"); ++ for (i = 0; i < FRAME_NUM_ARRAY_SIZE; i++) { ++ DWC_PRINTF("0x%04x 0x%04x\n", ++ frame_num_array[i], last_frame_num_array[i]); ++ } ++ dumped_frame_num_array = 1; ++ } ++ last_frame_num = curr_frame_number; ++} ++#endif ++ ++/** ++ * Handles the start-of-frame interrupt in host mode. Non-periodic ++ * transactions may be queued to the DWC_otg controller for the current ++ * (micro)frame. Periodic transactions may be queued to the controller for the ++ * next (micro)frame. ++ */ ++int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) ++{ ++ hfnum_data_t hfnum; ++ dwc_list_link_t *qh_entry; ++ dwc_otg_qh_t *qh; ++ dwc_otg_transaction_type_e tr_type; ++ gintsts_data_t gintsts = {.d32 = 0 }; ++ ++ hfnum.d32 = ++ DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); ++ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCD, "--Start of Frame Interrupt--\n"); ++#endif ++ hcd->frame_number = hfnum.b.frnum; ++ ++#ifdef DEBUG ++ hcd->frrem_accum += hfnum.b.frrem; ++ hcd->frrem_samples++; ++#endif ++ ++#ifdef DWC_TRACK_MISSED_SOFS ++ track_missed_sofs(hcd->frame_number); ++#endif ++ /* Determine whether any periodic QHs should be executed. */ ++ qh_entry = DWC_LIST_FIRST(&hcd->periodic_sched_inactive); ++ while (qh_entry != &hcd->periodic_sched_inactive) { ++ qh = DWC_LIST_ENTRY(qh_entry, dwc_otg_qh_t, qh_list_entry); ++ qh_entry = qh_entry->next; ++ if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number)) { ++ /* ++ * Move QH to the ready list to be executed next ++ * (micro)frame. ++ */ ++ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, ++ &qh->qh_list_entry); ++ } ++ } ++ tr_type = dwc_otg_hcd_select_transactions(hcd); ++ if (tr_type != DWC_OTG_TRANSACTION_NONE) { ++ dwc_otg_hcd_queue_transactions(hcd, tr_type); ++ } ++ ++ /* Clear interrupt */ ++ gintsts.b.sofintr = 1; ++ DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** Handles the Rx Status Queue Level Interrupt, which indicates that there is at ++ * least one packet in the Rx FIFO. The packets are moved from the FIFO to ++ * memory if the DWC_otg controller is operating in Slave mode. */ ++int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ host_grxsts_data_t grxsts; ++ dwc_hc_t *hc = NULL; ++ ++ DWC_DEBUGPL(DBG_HCD, "--RxStsQ Level Interrupt--\n"); ++ ++ grxsts.d32 = ++ DWC_READ_REG32(&dwc_otg_hcd->core_if->core_global_regs->grxstsp); ++ ++ hc = dwc_otg_hcd->hc_ptr_array[grxsts.b.chnum]; ++ if (!hc) { ++ DWC_ERROR("Unable to get corresponding channel\n"); ++ return 0; ++ } ++ ++ /* Packet Status */ ++ DWC_DEBUGPL(DBG_HCDV, " Ch num = %d\n", grxsts.b.chnum); ++ DWC_DEBUGPL(DBG_HCDV, " Count = %d\n", grxsts.b.bcnt); ++ DWC_DEBUGPL(DBG_HCDV, " DPID = %d, hc.dpid = %d\n", grxsts.b.dpid, ++ hc->data_pid_start); ++ DWC_DEBUGPL(DBG_HCDV, " PStatus = %d\n", grxsts.b.pktsts); ++ ++ switch (grxsts.b.pktsts) { ++ case DWC_GRXSTS_PKTSTS_IN: ++ /* Read the data into the host buffer. */ ++ if (grxsts.b.bcnt > 0) { ++ dwc_otg_read_packet(dwc_otg_hcd->core_if, ++ hc->xfer_buff, grxsts.b.bcnt); ++ ++ /* Update the HC fields for the next packet received. */ ++ hc->xfer_count += grxsts.b.bcnt; ++ hc->xfer_buff += grxsts.b.bcnt; ++ } ++ ++ case DWC_GRXSTS_PKTSTS_IN_XFER_COMP: ++ case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR: ++ case DWC_GRXSTS_PKTSTS_CH_HALTED: ++ /* Handled in interrupt, just ignore data */ ++ break; ++ default: ++ DWC_ERROR("RX_STS_Q Interrupt: Unknown status %d\n", ++ grxsts.b.pktsts); ++ break; ++ } ++ ++ return 1; ++} ++ ++/** This interrupt occurs when the non-periodic Tx FIFO is half-empty. More ++ * data packets may be written to the FIFO for OUT transfers. More requests ++ * may be written to the non-periodic request queue for IN transfers. This ++ * interrupt is enabled only in Slave mode. */ ++int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Non-Periodic TxFIFO Empty Interrupt--\n"); ++ dwc_otg_hcd_queue_transactions(dwc_otg_hcd, ++ DWC_OTG_TRANSACTION_NON_PERIODIC); ++ return 1; ++} ++ ++/** This interrupt occurs when the periodic Tx FIFO is half-empty. More data ++ * packets may be written to the FIFO for OUT transfers. More requests may be ++ * written to the periodic request queue for IN transfers. This interrupt is ++ * enabled only in Slave mode. */ ++int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Periodic TxFIFO Empty Interrupt--\n"); ++ dwc_otg_hcd_queue_transactions(dwc_otg_hcd, ++ DWC_OTG_TRANSACTION_PERIODIC); ++ return 1; ++} ++ ++/** There are multiple conditions that can cause a port interrupt. This function ++ * determines which interrupt conditions have occurred and handles them ++ * appropriately. */ ++int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ int retval = 0; ++ hprt0_data_t hprt0; ++ hprt0_data_t hprt0_modify; ++ ++ hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0); ++ hprt0_modify.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0); ++ ++ /* Clear appropriate bits in HPRT0 to clear the interrupt bit in ++ * GINTSTS */ ++ ++ hprt0_modify.b.prtena = 0; ++ hprt0_modify.b.prtconndet = 0; ++ hprt0_modify.b.prtenchng = 0; ++ hprt0_modify.b.prtovrcurrchng = 0; ++ ++ /* Port Connect Detected ++ * Set flag and clear if detected */ ++ if (dwc_otg_hcd->core_if->hibernation_suspend == 1) { ++ // Dont modify port status if we are in hibernation state ++ hprt0_modify.b.prtconndet = 1; ++ hprt0_modify.b.prtenchng = 1; ++ DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0_modify.d32); ++ hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0); ++ return retval; ++ } ++ ++ if (hprt0.b.prtconndet) { ++ /** @todo - check if steps performed in 'else' block should be perfromed regardles adp */ ++ if (dwc_otg_hcd->core_if->adp_enable && ++ dwc_otg_hcd->core_if->adp.vbuson_timer_started == 1) { ++ DWC_PRINTF("PORT CONNECT DETECTED ----------------\n"); ++ DWC_TIMER_CANCEL(dwc_otg_hcd->core_if->adp.vbuson_timer); ++ dwc_otg_hcd->core_if->adp.vbuson_timer_started = 0; ++ /* TODO - check if this is required, as ++ * host initialization was already performed ++ * after initial ADP probing ++ */ ++ /*dwc_otg_hcd->core_if->adp.vbuson_timer_started = 0; ++ dwc_otg_core_init(dwc_otg_hcd->core_if); ++ dwc_otg_enable_global_interrupts(dwc_otg_hcd->core_if); ++ cil_hcd_start(dwc_otg_hcd->core_if);*/ ++ } else { ++ hprt0_data_t hprt0_local; ++ DWC_DEBUGPL(DBG_HCD, "--Port Interrupt HPRT0=0x%08x " ++ "Port Connect Detected--\n", hprt0.d32); ++ dwc_otg_hcd->flags.b.port_connect_status_change = 1; ++ dwc_otg_hcd->flags.b.port_connect_status = 1; ++ hprt0_modify.b.prtconndet = 1; ++ /* PET testing */ ++ if (dwc_otg_hcd->core_if->otg_ver && (dwc_otg_hcd->core_if->test_mode == 7)) { ++ hprt0_local.d32 = dwc_otg_read_hprt0(dwc_otg_hcd->core_if); ++ hprt0_local.b.prtrst = 1; ++ DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0_local.d32); ++ dwc_mdelay(60); ++ hprt0.d32 = dwc_otg_read_hprt0(dwc_otg_hcd->core_if); ++ hprt0.b.prtrst = 0; ++ DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0.d32); ++ } ++ ++ /* B-Device has connected, Delete the connection timer. */ ++ DWC_TIMER_CANCEL(dwc_otg_hcd->conn_timer); ++ } ++ /* The Hub driver asserts a reset when it sees port connect ++ * status change flag */ ++ retval |= 1; ++ } ++ ++ /* Port Enable Changed ++ * Clear if detected - Set internal flag if disabled */ ++ if (hprt0.b.prtenchng) { ++ DWC_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x " ++ "Port Enable Changed--\n", hprt0.d32); ++ hprt0_modify.b.prtenchng = 1; ++ if (hprt0.b.prtena == 1) { ++ hfir_data_t hfir; ++ int do_reset = 0; ++ dwc_otg_core_params_t *params = ++ dwc_otg_hcd->core_if->core_params; ++ dwc_otg_core_global_regs_t *global_regs = ++ dwc_otg_hcd->core_if->core_global_regs; ++ dwc_otg_host_if_t *host_if = ++ dwc_otg_hcd->core_if->host_if; ++ ++ /* Every time when port enables calculate ++ * HFIR.FrInterval ++ */ ++ hfir.d32 = DWC_READ_REG32(&host_if->host_global_regs->hfir); ++ hfir.b.frint = calc_frame_interval(dwc_otg_hcd->core_if); ++ DWC_WRITE_REG32(&host_if->host_global_regs->hfir, hfir.d32); ++ ++ /* Check if we need to adjust the PHY clock speed for ++ * low power and adjust it */ ++ if (params->host_support_fs_ls_low_power) { ++ gusbcfg_data_t usbcfg; ++ ++ usbcfg.d32 = ++ DWC_READ_REG32(&global_regs->gusbcfg); ++ ++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED ++ || hprt0.b.prtspd == ++ DWC_HPRT0_PRTSPD_FULL_SPEED) { ++ /* ++ * Low power ++ */ ++ hcfg_data_t hcfg; ++ if (usbcfg.b.phylpwrclksel == 0) { ++ /* Set PHY low power clock select for FS/LS devices */ ++ usbcfg.b.phylpwrclksel = 1; ++ DWC_WRITE_REG32 ++ (&global_regs->gusbcfg, ++ usbcfg.d32); ++ do_reset = 1; ++ } ++ ++ hcfg.d32 = ++ DWC_READ_REG32 ++ (&host_if->host_global_regs->hcfg); ++ ++ if (hprt0.b.prtspd == ++ DWC_HPRT0_PRTSPD_LOW_SPEED ++ && params->host_ls_low_power_phy_clk ++ == ++ DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ) ++ { ++ /* 6 MHZ */ ++ DWC_DEBUGPL(DBG_CIL, ++ "FS_PHY programming HCFG to 6 MHz (Low Power)\n"); ++ if (hcfg.b.fslspclksel != ++ DWC_HCFG_6_MHZ) { ++ hcfg.b.fslspclksel = ++ DWC_HCFG_6_MHZ; ++ DWC_WRITE_REG32 ++ (&host_if->host_global_regs->hcfg, ++ hcfg.d32); ++ do_reset = 1; ++ } ++ } else { ++ /* 48 MHZ */ ++ DWC_DEBUGPL(DBG_CIL, ++ "FS_PHY programming HCFG to 48 MHz ()\n"); ++ if (hcfg.b.fslspclksel != ++ DWC_HCFG_48_MHZ) { ++ hcfg.b.fslspclksel = ++ DWC_HCFG_48_MHZ; ++ DWC_WRITE_REG32 ++ (&host_if->host_global_regs->hcfg, ++ hcfg.d32); ++ do_reset = 1; ++ } ++ } ++ } else { ++ /* ++ * Not low power ++ */ ++ if (usbcfg.b.phylpwrclksel == 1) { ++ usbcfg.b.phylpwrclksel = 0; ++ DWC_WRITE_REG32 ++ (&global_regs->gusbcfg, ++ usbcfg.d32); ++ do_reset = 1; ++ } ++ } ++ ++ if (do_reset) { ++ DWC_TASK_SCHEDULE(dwc_otg_hcd->reset_tasklet); ++ } ++ } ++ ++ if (!do_reset) { ++ /* Port has been enabled set the reset change flag */ ++ dwc_otg_hcd->flags.b.port_reset_change = 1; ++ } ++ } else { ++ dwc_otg_hcd->flags.b.port_enable_change = 1; ++ } ++ retval |= 1; ++ } ++ ++ /** Overcurrent Change Interrupt */ ++ if (hprt0.b.prtovrcurrchng) { ++ DWC_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x " ++ "Port Overcurrent Changed--\n", hprt0.d32); ++ dwc_otg_hcd->flags.b.port_over_current_change = 1; ++ hprt0_modify.b.prtovrcurrchng = 1; ++ retval |= 1; ++ } ++ ++ /* Clear Port Interrupts */ ++ DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0_modify.d32); ++ ++ return retval; ++} ++ ++/** This interrupt indicates that one or more host channels has a pending ++ * interrupt. There are multiple conditions that can cause each host channel ++ * interrupt. This function determines which conditions have occurred for each ++ * host channel interrupt and handles them appropriately. */ ++int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ int i; ++ int retval = 0; ++ haint_data_t haint; ++ ++ /* Clear appropriate bits in HCINTn to clear the interrupt bit in ++ * GINTSTS */ ++ ++ haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); ++ ++ for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) { ++ if (haint.b2.chint & (1 << i)) { ++ retval |= dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd, i); ++ } ++ } ++ ++ return retval; ++} ++ ++/** ++ * Gets the actual length of a transfer after the transfer halts. _halt_status ++ * holds the reason for the halt. ++ * ++ * For IN transfers where halt_status is DWC_OTG_HC_XFER_COMPLETE, ++ * *short_read is set to 1 upon return if less than the requested ++ * number of bytes were transferred. Otherwise, *short_read is set to 0 upon ++ * return. short_read may also be NULL on entry, in which case it remains ++ * unchanged. ++ */ ++static uint32_t get_actual_xfer_length(dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_halt_status_e halt_status, ++ int *short_read) ++{ ++ hctsiz_data_t hctsiz; ++ uint32_t length; ++ ++ if (short_read != NULL) { ++ *short_read = 0; ++ } ++ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz); ++ ++ if (halt_status == DWC_OTG_HC_XFER_COMPLETE) { ++ if (hc->ep_is_in) { ++ length = hc->xfer_len - hctsiz.b.xfersize; ++ if (short_read != NULL) { ++ *short_read = (hctsiz.b.xfersize != 0); ++ } ++ } else if (hc->qh->do_split) { ++ length = qtd->ssplit_out_xfer_count; ++ } else { ++ length = hc->xfer_len; ++ } ++ } else { ++ /* ++ * Must use the hctsiz.pktcnt field to determine how much data ++ * has been transferred. This field reflects the number of ++ * packets that have been transferred via the USB. This is ++ * always an integral number of packets if the transfer was ++ * halted before its normal completion. (Can't use the ++ * hctsiz.xfersize field because that reflects the number of ++ * bytes transferred via the AHB, not the USB). ++ */ ++ length = ++ (hc->start_pkt_count - hctsiz.b.pktcnt) * hc->max_packet; ++ } ++ ++ return length; ++} ++ ++/** ++ * Updates the state of the URB after a Transfer Complete interrupt on the ++ * host channel. Updates the actual_length field of the URB based on the ++ * number of bytes transferred via the host channel. Sets the URB status ++ * if the data transfer is finished. ++ * ++ * @return 1 if the data transfer specified by the URB is completely finished, ++ * 0 otherwise. ++ */ ++static int update_urb_state_xfer_comp(dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_hcd_urb_t * urb, ++ dwc_otg_qtd_t * qtd) ++{ ++ int xfer_done = 0; ++ int short_read = 0; ++ ++ int xfer_length; ++ ++ xfer_length = get_actual_xfer_length(hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_COMPLETE, ++ &short_read); ++ ++ ++ /* non DWORD-aligned buffer case handling. */ ++ if (hc->align_buff && xfer_length && hc->ep_is_in) { ++ dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf, ++ xfer_length); ++ } ++ ++ urb->actual_length += xfer_length; ++ ++ if (xfer_length && (hc->ep_type == DWC_OTG_EP_TYPE_BULK) && ++ (urb->flags & URB_SEND_ZERO_PACKET) ++ && (urb->actual_length == urb->length) ++ && !(urb->length % hc->max_packet)) { ++ xfer_done = 0; ++ } else if (short_read || urb->actual_length == urb->length) { ++ xfer_done = 1; ++ urb->status = 0; ++ } ++ ++#ifdef DEBUG ++ { ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz); ++ DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n", ++ __func__, (hc->ep_is_in ? "IN" : "OUT"), ++ hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " hc->xfer_len %d\n", hc->xfer_len); ++ DWC_DEBUGPL(DBG_HCDV, " hctsiz.xfersize %d\n", ++ hctsiz.b.xfersize); ++ DWC_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n", ++ urb->length); ++ DWC_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n", ++ urb->actual_length); ++ DWC_DEBUGPL(DBG_HCDV, " short_read %d, xfer_done %d\n", ++ short_read, xfer_done); ++ } ++#endif ++ ++ return xfer_done; ++} ++ ++/* ++ * Save the starting data toggle for the next transfer. The data toggle is ++ * saved in the QH for non-control transfers and it's saved in the QTD for ++ * control transfers. ++ */ ++void dwc_otg_hcd_save_data_toggle(dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, dwc_otg_qtd_t * qtd) ++{ ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz); ++ ++ if (hc->ep_type != DWC_OTG_EP_TYPE_CONTROL) { ++ dwc_otg_qh_t *qh = hc->qh; ++ if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) { ++ qh->data_toggle = DWC_OTG_HC_PID_DATA0; ++ } else { ++ qh->data_toggle = DWC_OTG_HC_PID_DATA1; ++ } ++ } else { ++ if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) { ++ qtd->data_toggle = DWC_OTG_HC_PID_DATA0; ++ } else { ++ qtd->data_toggle = DWC_OTG_HC_PID_DATA1; ++ } ++ } ++} ++ ++/** ++ * Updates the state of an Isochronous URB when the transfer is stopped for ++ * any reason. The fields of the current entry in the frame descriptor array ++ * are set based on the transfer state and the input _halt_status. Completes ++ * the Isochronous URB if all the URB frames have been completed. ++ * ++ * @return DWC_OTG_HC_XFER_COMPLETE if there are more frames remaining to be ++ * transferred in the URB. Otherwise return DWC_OTG_HC_XFER_URB_COMPLETE. ++ */ ++static dwc_otg_halt_status_e ++update_isoc_urb_state(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd, dwc_otg_halt_status_e halt_status) ++{ ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ dwc_otg_halt_status_e ret_val = halt_status; ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc; ++ ++ frame_desc = &urb->iso_descs[qtd->isoc_frame_index]; ++ switch (halt_status) { ++ case DWC_OTG_HC_XFER_COMPLETE: ++ frame_desc->status = 0; ++ frame_desc->actual_length = ++ get_actual_xfer_length(hc, hc_regs, qtd, halt_status, NULL); ++ ++ /* non DWORD-aligned buffer case handling. */ ++ if (hc->align_buff && frame_desc->actual_length && hc->ep_is_in) { ++ dwc_memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset, ++ hc->qh->dw_align_buf, frame_desc->actual_length); ++ } ++ ++ break; ++ case DWC_OTG_HC_XFER_FRAME_OVERRUN: ++ urb->error_count++; ++ if (hc->ep_is_in) { ++ frame_desc->status = -DWC_E_NO_STREAM_RES; ++ } else { ++ frame_desc->status = -DWC_E_COMMUNICATION; ++ } ++ frame_desc->actual_length = 0; ++ break; ++ case DWC_OTG_HC_XFER_BABBLE_ERR: ++ urb->error_count++; ++ frame_desc->status = -DWC_E_OVERFLOW; ++ /* Don't need to update actual_length in this case. */ ++ break; ++ case DWC_OTG_HC_XFER_XACT_ERR: ++ urb->error_count++; ++ frame_desc->status = -DWC_E_PROTOCOL; ++ frame_desc->actual_length = ++ get_actual_xfer_length(hc, hc_regs, qtd, halt_status, NULL); ++ ++ /* non DWORD-aligned buffer case handling. */ ++ if (hc->align_buff && frame_desc->actual_length && hc->ep_is_in) { ++ dwc_memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset, ++ hc->qh->dw_align_buf, frame_desc->actual_length); ++ } ++ /* Skip whole frame */ ++ if (hc->qh->do_split && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) && ++ hc->ep_is_in && hcd->core_if->dma_enable) { ++ qtd->complete_split = 0; ++ qtd->isoc_split_offset = 0; ++ } ++ ++ break; ++ default: ++ DWC_ASSERT(1, "Unhandled _halt_status (%d)\n", halt_status); ++ break; ++ } ++ if (++qtd->isoc_frame_index == urb->packet_count) { ++ /* ++ * urb->status is not used for isoc transfers. ++ * The individual frame_desc statuses are used instead. ++ */ ++ hcd->fops->complete(hcd, urb->priv, urb, 0); ++ ret_val = DWC_OTG_HC_XFER_URB_COMPLETE; ++ } else { ++ ret_val = DWC_OTG_HC_XFER_COMPLETE; ++ } ++ return ret_val; ++} ++ ++/** ++ * Frees the first QTD in the QH's list if free_qtd is 1. For non-periodic ++ * QHs, removes the QH from the active non-periodic schedule. If any QTDs are ++ * still linked to the QH, the QH is added to the end of the inactive ++ * non-periodic schedule. For periodic QHs, removes the QH from the periodic ++ * schedule if no more QTDs are linked to the QH. ++ */ ++static void deactivate_qh(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, int free_qtd) ++{ ++ int continue_split = 0; ++ dwc_otg_qtd_t *qtd; ++ ++ DWC_DEBUGPL(DBG_HCDV, " %s(%p,%p,%d)\n", __func__, hcd, qh, free_qtd); ++ ++ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ ++ if (qtd->complete_split) { ++ continue_split = 1; ++ } else if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID || ++ qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END) { ++ continue_split = 1; ++ } ++ ++ if (free_qtd) { ++ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); ++ continue_split = 0; ++ } ++ ++ qh->channel = NULL; ++ dwc_otg_hcd_qh_deactivate(hcd, qh, continue_split); ++} ++ ++/** ++ * Releases a host channel for use by other transfers. Attempts to select and ++ * queue more transactions since at least one host channel is available. ++ * ++ * @param hcd The HCD state structure. ++ * @param hc The host channel to release. ++ * @param qtd The QTD associated with the host channel. This QTD may be freed ++ * if the transfer is complete or an error has occurred. ++ * @param halt_status Reason the channel is being released. This status ++ * determines the actions taken by this function. ++ */ ++static void release_channel(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ dwc_otg_transaction_type_e tr_type; ++ int free_qtd; ++ ++ DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d\n", ++ __func__, hc->hc_num, halt_status); ++ ++ switch (halt_status) { ++ case DWC_OTG_HC_XFER_URB_COMPLETE: ++ free_qtd = 1; ++ break; ++ case DWC_OTG_HC_XFER_AHB_ERR: ++ case DWC_OTG_HC_XFER_STALL: ++ case DWC_OTG_HC_XFER_BABBLE_ERR: ++ free_qtd = 1; ++ break; ++ case DWC_OTG_HC_XFER_XACT_ERR: ++ if (qtd->error_count >= 3) { ++ DWC_DEBUGPL(DBG_HCDV, ++ " Complete URB with transaction error\n"); ++ free_qtd = 1; ++ qtd->urb->status = -DWC_E_PROTOCOL; ++ hcd->fops->complete(hcd, qtd->urb->priv, ++ qtd->urb, -DWC_E_PROTOCOL); ++ } else { ++ free_qtd = 0; ++ } ++ break; ++ case DWC_OTG_HC_XFER_URB_DEQUEUE: ++ /* ++ * The QTD has already been removed and the QH has been ++ * deactivated. Don't want to do anything except release the ++ * host channel and try to queue more transfers. ++ */ ++ goto cleanup; ++ case DWC_OTG_HC_XFER_NO_HALT_STATUS: ++ free_qtd = 0; ++ break; ++ case DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE: ++ DWC_DEBUGPL(DBG_HCDV, ++ " Complete URB with I/O error\n"); ++ free_qtd = 1; ++ qtd->urb->status = -DWC_E_IO; ++ hcd->fops->complete(hcd, qtd->urb->priv, ++ qtd->urb, -DWC_E_IO); ++ break; ++ default: ++ free_qtd = 0; ++ break; ++ } ++ ++ deactivate_qh(hcd, hc->qh, free_qtd); ++ ++cleanup: ++ /* ++ * Release the host channel for use by other transfers. The cleanup ++ * function clears the channel interrupt enables and conditions, so ++ * there's no need to clear the Channel Halted interrupt separately. ++ */ ++ dwc_otg_hc_cleanup(hcd->core_if, hc); ++ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry); ++ ++ switch (hc->ep_type) { ++ case DWC_OTG_EP_TYPE_CONTROL: ++ case DWC_OTG_EP_TYPE_BULK: ++ hcd->non_periodic_channels--; ++ break; ++ ++ default: ++ /* ++ * Don't release reservations for periodic channels here. ++ * That's done when a periodic transfer is descheduled (i.e. ++ * when the QH is removed from the periodic schedule). ++ */ ++ break; ++ } ++ ++ /* Try to queue more transfers now that there's a free channel. */ ++ tr_type = dwc_otg_hcd_select_transactions(hcd); ++ if (tr_type != DWC_OTG_TRANSACTION_NONE) { ++ dwc_otg_hcd_queue_transactions(hcd, tr_type); ++ } ++} ++ ++/** ++ * Halts a host channel. If the channel cannot be halted immediately because ++ * the request queue is full, this function ensures that the FIFO empty ++ * interrupt for the appropriate queue is enabled so that the halt request can ++ * be queued when there is space in the request queue. ++ * ++ * This function may also be called in DMA mode. In that case, the channel is ++ * simply released since the core always halts the channel automatically in ++ * DMA mode. ++ */ ++static void halt_channel(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_qtd_t * qtd, dwc_otg_halt_status_e halt_status) ++{ ++ if (hcd->core_if->dma_enable) { ++ release_channel(hcd, hc, qtd, halt_status); ++ return; ++ } ++ ++ /* Slave mode processing... */ ++ dwc_otg_hc_halt(hcd->core_if, hc, halt_status); ++ ++ if (hc->halt_on_queue) { ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ dwc_otg_core_global_regs_t *global_regs; ++ global_regs = hcd->core_if->core_global_regs; ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || ++ hc->ep_type == DWC_OTG_EP_TYPE_BULK) { ++ /* ++ * Make sure the Non-periodic Tx FIFO empty interrupt ++ * is enabled so that the non-periodic schedule will ++ * be processed. ++ */ ++ gintmsk.b.nptxfempty = 1; ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } else { ++ /* ++ * Move the QH from the periodic queued schedule to ++ * the periodic assigned schedule. This allows the ++ * halt to be queued when the periodic schedule is ++ * processed. ++ */ ++ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned, ++ &hc->qh->qh_list_entry); ++ ++ /* ++ * Make sure the Periodic Tx FIFO Empty interrupt is ++ * enabled so that the periodic schedule will be ++ * processed. ++ */ ++ gintmsk.b.ptxfempty = 1; ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } ++ } ++} ++ ++/** ++ * Performs common cleanup for non-periodic transfers after a Transfer ++ * Complete interrupt. This function should be called after any endpoint type ++ * specific handling is finished to release the host channel. ++ */ ++static void complete_non_periodic_xfer(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ hcint_data_t hcint; ++ ++ qtd->error_count = 0; ++ ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ if (hcint.b.nyet) { ++ /* ++ * Got a NYET on the last transaction of the transfer. This ++ * means that the endpoint should be in the PING state at the ++ * beginning of the next transfer. ++ */ ++ hc->qh->ping_state = 1; ++ clear_hc_int(hc_regs, nyet); ++ } ++ ++ /* ++ * Always halt and release the host channel to make it available for ++ * more transfers. There may still be more phases for a control ++ * transfer or more data packets for a bulk transfer at this point, ++ * but the host channel is still halted. A channel will be reassigned ++ * to the transfer when the non-periodic schedule is processed after ++ * the channel is released. This allows transactions to be queued ++ * properly via dwc_otg_hcd_queue_transactions, which also enables the ++ * Tx FIFO Empty interrupt if necessary. ++ */ ++ if (hc->ep_is_in) { ++ /* ++ * IN transfers in Slave mode require an explicit disable to ++ * halt the channel. (In DMA mode, this call simply releases ++ * the channel.) ++ */ ++ halt_channel(hcd, hc, qtd, halt_status); ++ } else { ++ /* ++ * The channel is automatically disabled by the core for OUT ++ * transfers in Slave mode. ++ */ ++ release_channel(hcd, hc, qtd, halt_status); ++ } ++} ++ ++/** ++ * Performs common cleanup for periodic transfers after a Transfer Complete ++ * interrupt. This function should be called after any endpoint type specific ++ * handling is finished to release the host channel. ++ */ ++static void complete_periodic_xfer(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ hctsiz_data_t hctsiz; ++ qtd->error_count = 0; ++ ++ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz); ++ if (!hc->ep_is_in || hctsiz.b.pktcnt == 0) { ++ /* Core halts channel in these cases. */ ++ release_channel(hcd, hc, qtd, halt_status); ++ } else { ++ /* Flush any outstanding requests from the Tx queue. */ ++ halt_channel(hcd, hc, qtd, halt_status); ++ } ++} ++ ++static int32_t handle_xfercomp_isoc_split_in(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ uint32_t len; ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc; ++ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ ++ len = get_actual_xfer_length(hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_COMPLETE, NULL); ++ ++ if (!len) { ++ qtd->complete_split = 0; ++ qtd->isoc_split_offset = 0; ++ return 0; ++ } ++ frame_desc->actual_length += len; ++ ++ if (hc->align_buff && len) ++ dwc_memcpy(qtd->urb->buf + frame_desc->offset + ++ qtd->isoc_split_offset, hc->qh->dw_align_buf, len); ++ qtd->isoc_split_offset += len; ++ ++ if (frame_desc->length == frame_desc->actual_length) { ++ frame_desc->status = 0; ++ qtd->isoc_frame_index++; ++ qtd->complete_split = 0; ++ qtd->isoc_split_offset = 0; ++ } ++ ++ if (qtd->isoc_frame_index == qtd->urb->packet_count) { ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ } ++ ++ return 1; /* Indicates that channel released */ ++} ++ ++/** ++ * Handles a host channel Transfer Complete interrupt. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_xfercomp_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ int urb_xfer_done; ++ dwc_otg_halt_status_e halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ int pipe_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info); ++ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Transfer Complete--\n", hc->hc_num); ++ ++ if (hcd->core_if->dma_desc_enable) { ++ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, halt_status); ++ if (pipe_type == UE_ISOCHRONOUS) { ++ /* Do not disable the interrupt, just clear it */ ++ clear_hc_int(hc_regs, xfercomp); ++ return 1; ++ } ++ goto handle_xfercomp_done; ++ } ++ ++ /* ++ * Handle xfer complete on CSPLIT. ++ */ ++ ++ if (hc->qh->do_split) { ++ if ((hc->ep_type == DWC_OTG_EP_TYPE_ISOC) && hc->ep_is_in ++ && hcd->core_if->dma_enable) { ++ if (qtd->complete_split ++ && handle_xfercomp_isoc_split_in(hcd, hc, hc_regs, ++ qtd)) ++ goto handle_xfercomp_done; ++ } else { ++ qtd->complete_split = 0; ++ } ++ } ++ ++ /* Update the QTD and URB states. */ ++ switch (pipe_type) { ++ case UE_CONTROL: ++ switch (qtd->control_phase) { ++ case DWC_OTG_CONTROL_SETUP: ++ if (urb->length > 0) { ++ qtd->control_phase = DWC_OTG_CONTROL_DATA; ++ } else { ++ qtd->control_phase = DWC_OTG_CONTROL_STATUS; ++ } ++ DWC_DEBUGPL(DBG_HCDV, ++ " Control setup transaction done\n"); ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ break; ++ case DWC_OTG_CONTROL_DATA:{ ++ urb_xfer_done = ++ update_urb_state_xfer_comp(hc, hc_regs, urb, ++ qtd); ++ if (urb_xfer_done) { ++ qtd->control_phase = ++ DWC_OTG_CONTROL_STATUS; ++ DWC_DEBUGPL(DBG_HCDV, ++ " Control data transfer done\n"); ++ } else { ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ } ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ break; ++ } ++ case DWC_OTG_CONTROL_STATUS: ++ DWC_DEBUGPL(DBG_HCDV, " Control transfer complete\n"); ++ if (urb->status == -DWC_E_IN_PROGRESS) { ++ urb->status = 0; ++ } ++ hcd->fops->complete(hcd, urb->priv, urb, urb->status); ++ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE; ++ if (!hcd->core_if->dma_enable && hcd->core_if->otg_ver == 1) ++ qtd->urb = NULL; ++ break; ++ } ++ ++ complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status); ++ break; ++ case UE_BULK: ++ DWC_DEBUGPL(DBG_HCDV, " Bulk transfer complete\n"); ++ urb_xfer_done = ++ update_urb_state_xfer_comp(hc, hc_regs, urb, qtd); ++ if (urb_xfer_done) { ++ hcd->fops->complete(hcd, urb->priv, urb, urb->status); ++ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE; ++ } else { ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ } ++ ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status); ++ break; ++ case UE_INTERRUPT: ++ DWC_DEBUGPL(DBG_HCDV, " Interrupt transfer complete\n"); ++ urb_xfer_done = ++ update_urb_state_xfer_comp(hc, hc_regs, urb, qtd); ++ ++ /* ++ * Interrupt URB is done on the first transfer complete ++ * interrupt. ++ */ ++ if (urb_xfer_done) { ++ hcd->fops->complete(hcd, urb->priv, urb, urb->status); ++ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE; ++ } else { ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ } ++ ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ complete_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status); ++ break; ++ case UE_ISOCHRONOUS: ++ DWC_DEBUGPL(DBG_HCDV, " Isochronous transfer complete\n"); ++ if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_ALL) { ++ halt_status = ++ update_isoc_urb_state(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_COMPLETE); ++ } ++ complete_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status); ++ break; ++ } ++ ++handle_xfercomp_done: ++ disable_hc_int(hc_regs, xfercompl); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel STALL interrupt. This handler may be called in ++ * either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_stall_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ int pipe_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info); ++ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "STALL Received--\n", hc->hc_num); ++ ++ if (hcd->core_if->dma_desc_enable) { ++ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, DWC_OTG_HC_XFER_STALL); ++ goto handle_stall_done; ++ } ++ ++ if (pipe_type == UE_CONTROL) { ++ hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_PIPE); ++ } ++ ++ if (pipe_type == UE_BULK || pipe_type == UE_INTERRUPT) { ++ hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_PIPE); ++ /* ++ * USB protocol requires resetting the data toggle for bulk ++ * and interrupt endpoints when a CLEAR_FEATURE(ENDPOINT_HALT) ++ * setup command is issued to the endpoint. Anticipate the ++ * CLEAR_FEATURE command since a STALL has occurred and reset ++ * the data toggle now. ++ */ ++ hc->qh->data_toggle = 0; ++ } ++ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_STALL); ++ ++handle_stall_done: ++ disable_hc_int(hc_regs, stall); ++ ++ return 1; ++} ++ ++/* ++ * Updates the state of the URB when a transfer has been stopped due to an ++ * abnormal condition before the transfer completes. Modifies the ++ * actual_length field of the URB to reflect the number of bytes that have ++ * actually been transferred via the host channel. ++ */ ++static void update_urb_state_xfer_intr(dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_hcd_urb_t * urb, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ uint32_t bytes_transferred = get_actual_xfer_length(hc, hc_regs, qtd, ++ halt_status, NULL); ++ /* non DWORD-aligned buffer case handling. */ ++ if (hc->align_buff && bytes_transferred && hc->ep_is_in) { ++ dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf, ++ bytes_transferred); ++ } ++ ++ urb->actual_length += bytes_transferred; ++ ++#ifdef DEBUG ++ { ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz); ++ DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n", ++ __func__, (hc->ep_is_in ? "IN" : "OUT"), ++ hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " hc->start_pkt_count %d\n", ++ hc->start_pkt_count); ++ DWC_DEBUGPL(DBG_HCDV, " hctsiz.pktcnt %d\n", hctsiz.b.pktcnt); ++ DWC_DEBUGPL(DBG_HCDV, " hc->max_packet %d\n", hc->max_packet); ++ DWC_DEBUGPL(DBG_HCDV, " bytes_transferred %d\n", ++ bytes_transferred); ++ DWC_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n", ++ urb->actual_length); ++ DWC_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n", ++ urb->length); ++ } ++#endif ++} ++ ++/** ++ * Handles a host channel NAK interrupt. This handler may be called in either ++ * DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "NAK Received--\n", hc->hc_num); ++ ++ /* ++ * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and ++ * interrupt. Re-start the SSPLIT transfer. ++ */ ++ if (hc->do_split) { ++ if (hc->complete_split) { ++ qtd->error_count = 0; ++ } ++ qtd->complete_split = 0; ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK); ++ goto handle_nak_done; ++ } ++ ++ switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { ++ case UE_CONTROL: ++ case UE_BULK: ++ if (hcd->core_if->dma_enable && hc->ep_is_in) { ++ /* ++ * NAK interrupts are enabled on bulk/control IN ++ * transfers in DMA mode for the sole purpose of ++ * resetting the error count after a transaction error ++ * occurs. The core will continue transferring data. ++ */ ++ qtd->error_count = 0; ++ goto handle_nak_done; ++ } ++ ++ /* ++ * NAK interrupts normally occur during OUT transfers in DMA ++ * or Slave mode. For IN transfers, more requests will be ++ * queued as request queue space is available. ++ */ ++ qtd->error_count = 0; ++ ++ if (!hc->qh->ping_state) { ++ update_urb_state_xfer_intr(hc, hc_regs, ++ qtd->urb, qtd, ++ DWC_OTG_HC_XFER_NAK); ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ ++ if (hc->speed == DWC_OTG_EP_SPEED_HIGH) ++ hc->qh->ping_state = 1; ++ } ++ ++ /* ++ * Halt the channel so the transfer can be re-started from ++ * the appropriate point or the PING protocol will ++ * start/continue. ++ */ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK); ++ break; ++ case UE_INTERRUPT: ++ qtd->error_count = 0; ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK); ++ break; ++ case UE_ISOCHRONOUS: ++ /* Should never get called for isochronous transfers. */ ++ DWC_ASSERT(1, "NACK interrupt for ISOC transfer\n"); ++ break; ++ } ++ ++handle_nak_done: ++ disable_hc_int(hc_regs, nak); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel ACK interrupt. This interrupt is enabled when ++ * performing the PING protocol in Slave mode, when errors occur during ++ * either Slave mode or DMA mode, and during Start Split transactions. ++ */ ++static int32_t handle_hc_ack_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "ACK Received--\n", hc->hc_num); ++ ++ if (hc->do_split) { ++ /* ++ * Handle ACK on SSPLIT. ++ * ACK should not occur in CSPLIT. ++ */ ++ if (!hc->ep_is_in && hc->data_pid_start != DWC_OTG_HC_PID_SETUP) { ++ qtd->ssplit_out_xfer_count = hc->xfer_len; ++ } ++ if (!(hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in)) { ++ /* Don't need complete for isochronous out transfers. */ ++ qtd->complete_split = 1; ++ } ++ ++ /* ISOC OUT */ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in) { ++ switch (hc->xact_pos) { ++ case DWC_HCSPLIT_XACTPOS_ALL: ++ break; ++ case DWC_HCSPLIT_XACTPOS_END: ++ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL; ++ qtd->isoc_split_offset = 0; ++ break; ++ case DWC_HCSPLIT_XACTPOS_BEGIN: ++ case DWC_HCSPLIT_XACTPOS_MID: ++ /* ++ * For BEGIN or MID, calculate the length for ++ * the next microframe to determine the correct ++ * SSPLIT token, either MID or END. ++ */ ++ { ++ struct dwc_otg_hcd_iso_packet_desc ++ *frame_desc; ++ ++ frame_desc = ++ &qtd->urb-> ++ iso_descs[qtd->isoc_frame_index]; ++ qtd->isoc_split_offset += 188; ++ ++ if ((frame_desc->length - ++ qtd->isoc_split_offset) <= 188) { ++ qtd->isoc_split_pos = ++ DWC_HCSPLIT_XACTPOS_END; ++ } else { ++ qtd->isoc_split_pos = ++ DWC_HCSPLIT_XACTPOS_MID; ++ } ++ ++ } ++ break; ++ } ++ } else { ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK); ++ } ++ } else { ++ qtd->error_count = 0; ++ ++ if (hc->qh->ping_state) { ++ hc->qh->ping_state = 0; ++ /* ++ * Halt the channel so the transfer can be re-started ++ * from the appropriate point. This only happens in ++ * Slave mode. In DMA mode, the ping_state is cleared ++ * when the transfer is started because the core ++ * automatically executes the PING, then the transfer. ++ */ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK); ++ } ++ } ++ ++ /* ++ * If the ACK occurred when _not_ in the PING state, let the channel ++ * continue transferring data after clearing the error count. ++ */ ++ ++ disable_hc_int(hc_regs, ack); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel NYET interrupt. This interrupt should only occur on ++ * Bulk and Control OUT endpoints and for complete split transactions. If a ++ * NYET occurs at the same time as a Transfer Complete interrupt, it is ++ * handled in the xfercomp interrupt handler, not here. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "NYET Received--\n", hc->hc_num); ++ ++ /* ++ * NYET on CSPLIT ++ * re-do the CSPLIT immediately on non-periodic ++ */ ++ if (hc->do_split && hc->complete_split) { ++ if (hc->ep_is_in && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) ++ && hcd->core_if->dma_enable) { ++ qtd->complete_split = 0; ++ qtd->isoc_split_offset = 0; ++ if (++qtd->isoc_frame_index == qtd->urb->packet_count) { ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } ++ else ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ goto handle_nyet_done; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ int frnum = dwc_otg_hcd_get_frame_number(hcd); ++ ++ if (dwc_full_frame_num(frnum) != ++ dwc_full_frame_num(hc->qh->sched_frame)) { ++ /* ++ * No longer in the same full speed frame. ++ * Treat this as a transaction error. ++ */ ++#if 0 ++ /** @todo Fix system performance so this can ++ * be treated as an error. Right now complete ++ * splits cannot be scheduled precisely enough ++ * due to other system activity, so this error ++ * occurs regularly in Slave mode. ++ */ ++ qtd->error_count++; ++#endif ++ qtd->complete_split = 0; ++ halt_channel(hcd, hc, qtd, ++ DWC_OTG_HC_XFER_XACT_ERR); ++ /** @todo add support for isoc release */ ++ goto handle_nyet_done; ++ } ++ } ++ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET); ++ goto handle_nyet_done; ++ } ++ ++ hc->qh->ping_state = 1; ++ qtd->error_count = 0; ++ ++ update_urb_state_xfer_intr(hc, hc_regs, qtd->urb, qtd, ++ DWC_OTG_HC_XFER_NYET); ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ ++ /* ++ * Halt the channel and re-start the transfer so the PING ++ * protocol will start. ++ */ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET); ++ ++handle_nyet_done: ++ disable_hc_int(hc_regs, nyet); ++ return 1; ++} ++ ++/** ++ * Handles a host channel babble interrupt. This handler may be called in ++ * either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_babble_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Babble Error--\n", hc->hc_num); ++ ++ if (hcd->core_if->dma_desc_enable) { ++ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, ++ DWC_OTG_HC_XFER_BABBLE_ERR); ++ goto handle_babble_done; ++ } ++ ++ if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) { ++ hcd->fops->complete(hcd, qtd->urb->priv, ++ qtd->urb, -DWC_E_OVERFLOW); ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_BABBLE_ERR); ++ } else { ++ dwc_otg_halt_status_e halt_status; ++ halt_status = update_isoc_urb_state(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_BABBLE_ERR); ++ halt_channel(hcd, hc, qtd, halt_status); ++ } ++ ++handle_babble_done: ++ disable_hc_int(hc_regs, bblerr); ++ return 1; ++} ++ ++/** ++ * Handles a host channel AHB error interrupt. This handler is only called in ++ * DMA mode. ++ */ ++static int32_t handle_hc_ahberr_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ hctsiz_data_t hctsiz; ++ uint32_t hcdma; ++ char *pipetype, *speed; ++ ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "AHB Error--\n", hc->hc_num); ++ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt); ++ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz); ++ hcdma = DWC_READ_REG32(&hc_regs->hcdma); ++ ++ DWC_ERROR("AHB ERROR, Channel %d\n", hc->hc_num); ++ DWC_ERROR(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32); ++ DWC_ERROR(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma); ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Enqueue\n"); ++ DWC_ERROR(" Device address: %d\n", ++ dwc_otg_hcd_get_dev_addr(&urb->pipe_info)); ++ DWC_ERROR(" Endpoint: %d, %s\n", ++ dwc_otg_hcd_get_ep_num(&urb->pipe_info), ++ (dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT")); ++ ++ switch (dwc_otg_hcd_get_pipe_type(&urb->pipe_info)) { ++ case UE_CONTROL: ++ pipetype = "CONTROL"; ++ break; ++ case UE_BULK: ++ pipetype = "BULK"; ++ break; ++ case UE_INTERRUPT: ++ pipetype = "INTERRUPT"; ++ break; ++ case UE_ISOCHRONOUS: ++ pipetype = "ISOCHRONOUS"; ++ break; ++ default: ++ pipetype = "UNKNOWN"; ++ break; ++ } ++ ++ DWC_ERROR(" Endpoint type: %s\n", pipetype); ++ ++ switch (hc->speed) { ++ case DWC_OTG_EP_SPEED_HIGH: ++ speed = "HIGH"; ++ break; ++ case DWC_OTG_EP_SPEED_FULL: ++ speed = "FULL"; ++ break; ++ case DWC_OTG_EP_SPEED_LOW: ++ speed = "LOW"; ++ break; ++ default: ++ speed = "UNKNOWN"; ++ break; ++ }; ++ ++ DWC_ERROR(" Speed: %s\n", speed); ++ ++ DWC_ERROR(" Max packet size: %d\n", ++ dwc_otg_hcd_get_mps(&urb->pipe_info)); ++ DWC_ERROR(" Data buffer length: %d\n", urb->length); ++ DWC_ERROR(" Transfer buffer: %p, Transfer DMA: %p\n", ++ urb->buf, (void *)urb->dma); ++ DWC_ERROR(" Setup buffer: %p, Setup DMA: %p\n", ++ urb->setup_packet, (void *)urb->setup_dma); ++ DWC_ERROR(" Interval: %d\n", urb->interval); ++ ++ /* Core haltes the channel for Descriptor DMA mode */ ++ if (hcd->core_if->dma_desc_enable) { ++ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, ++ DWC_OTG_HC_XFER_AHB_ERR); ++ goto handle_ahberr_done; ++ } ++ ++ hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_IO); ++ ++ /* ++ * Force a channel halt. Don't call halt_channel because that won't ++ * write to the HCCHARn register in DMA mode to force the halt. ++ */ ++ dwc_otg_hc_halt(hcd->core_if, hc, DWC_OTG_HC_XFER_AHB_ERR); ++handle_ahberr_done: ++ disable_hc_int(hc_regs, ahberr); ++ return 1; ++} ++ ++/** ++ * Handles a host channel transaction error interrupt. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_xacterr_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Transaction Error--\n", hc->hc_num); ++ ++ if (hcd->core_if->dma_desc_enable) { ++ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, ++ DWC_OTG_HC_XFER_XACT_ERR); ++ goto handle_xacterr_done; ++ } ++ ++ switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { ++ case UE_CONTROL: ++ case UE_BULK: ++ qtd->error_count++; ++ if (!hc->qh->ping_state) { ++ ++ update_urb_state_xfer_intr(hc, hc_regs, ++ qtd->urb, qtd, ++ DWC_OTG_HC_XFER_XACT_ERR); ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ if (!hc->ep_is_in && hc->speed == DWC_OTG_EP_SPEED_HIGH) { ++ hc->qh->ping_state = 1; ++ } ++ } ++ ++ /* ++ * Halt the channel so the transfer can be re-started from ++ * the appropriate point or the PING protocol will start. ++ */ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ break; ++ case UE_INTERRUPT: ++ qtd->error_count++; ++ if (hc->do_split && hc->complete_split) { ++ qtd->complete_split = 0; ++ } ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ break; ++ case UE_ISOCHRONOUS: ++ { ++ dwc_otg_halt_status_e halt_status; ++ halt_status = ++ update_isoc_urb_state(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_XACT_ERR); ++ ++ halt_channel(hcd, hc, qtd, halt_status); ++ } ++ break; ++ } ++handle_xacterr_done: ++ disable_hc_int(hc_regs, xacterr); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel frame overrun interrupt. This handler may be called ++ * in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_frmovrun_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Frame Overrun--\n", hc->hc_num); ++ ++ switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { ++ case UE_CONTROL: ++ case UE_BULK: ++ break; ++ case UE_INTERRUPT: ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_FRAME_OVERRUN); ++ break; ++ case UE_ISOCHRONOUS: ++ { ++ dwc_otg_halt_status_e halt_status; ++ halt_status = ++ update_isoc_urb_state(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_FRAME_OVERRUN); ++ ++ halt_channel(hcd, hc, qtd, halt_status); ++ } ++ break; ++ } ++ ++ disable_hc_int(hc_regs, frmovrun); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel data toggle error interrupt. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Data Toggle Error--\n", hc->hc_num); ++ ++ if (hc->ep_is_in) { ++ qtd->error_count = 0; ++ } else { ++ DWC_ERROR("Data Toggle Error on OUT transfer," ++ "channel %d\n", hc->hc_num); ++ } ++ ++ disable_hc_int(hc_regs, datatglerr); ++ ++ return 1; ++} ++ ++#ifdef DEBUG ++/** ++ * This function is for debug only. It checks that a valid halt status is set ++ * and that HCCHARn.chdis is clear. If there's a problem, corrective action is ++ * taken and a warning is issued. ++ * @return 1 if halt status is ok, 0 otherwise. ++ */ ++static inline int halt_status_ok(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ hcsplt_data_t hcsplt; ++ ++ if (hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS) { ++ /* ++ * This code is here only as a check. This condition should ++ * never happen. Ignore the halt if it does occur. ++ */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz); ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); ++ hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt); ++ DWC_WARN ++ ("%s: hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS, " ++ "channel %d, hcchar 0x%08x, hctsiz 0x%08x, " ++ "hcint 0x%08x, hcintmsk 0x%08x, " ++ "hcsplt 0x%08x, qtd->complete_split %d\n", __func__, ++ hc->hc_num, hcchar.d32, hctsiz.d32, hcint.d32, ++ hcintmsk.d32, hcsplt.d32, qtd->complete_split); ++ ++ DWC_WARN("%s: no halt status, channel %d, ignoring interrupt\n", ++ __func__, hc->hc_num); ++ DWC_WARN("\n"); ++ clear_hc_int(hc_regs, chhltd); ++ return 0; ++ } ++ ++ /* ++ * This code is here only as a check. hcchar.chdis should ++ * never be set when the halt interrupt occurs. Halt the ++ * channel again if it does occur. ++ */ ++ hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar); ++ if (hcchar.b.chdis) { ++ DWC_WARN("%s: hcchar.chdis set unexpectedly, " ++ "hcchar 0x%08x, trying to halt again\n", ++ __func__, hcchar.d32); ++ clear_hc_int(hc_regs, chhltd); ++ hc->halt_pending = 0; ++ halt_channel(hcd, hc, qtd, hc->halt_status); ++ return 0; ++ } ++ ++ return 1; ++} ++#endif ++ ++/** ++ * Handles a host Channel Halted interrupt in DMA mode. This handler ++ * determines the reason the channel halted and proceeds accordingly. ++ */ ++static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ int out_nak_enh = 0; ++ ++ /* For core with OUT NAK enhancement, the flow for high- ++ * speed CONTROL/BULK OUT is handled a little differently. ++ */ ++ if (hcd->core_if->snpsid >= OTG_CORE_REV_2_71a) { ++ if (hc->speed == DWC_OTG_EP_SPEED_HIGH && !hc->ep_is_in && ++ (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || ++ hc->ep_type == DWC_OTG_EP_TYPE_BULK)) { ++ out_nak_enh = 1; ++ } ++ } ++ ++ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE || ++ (hc->halt_status == DWC_OTG_HC_XFER_AHB_ERR ++ && !hcd->core_if->dma_desc_enable)) { ++ /* ++ * Just release the channel. A dequeue can happen on a ++ * transfer timeout. In the case of an AHB Error, the channel ++ * was forced to halt because there's no way to gracefully ++ * recover. ++ */ ++ if (hcd->core_if->dma_desc_enable) ++ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, ++ hc->halt_status); ++ else ++ release_channel(hcd, hc, qtd, hc->halt_status); ++ return; ++ } ++ ++ /* Read the HCINTn register to determine the cause for the halt. */ ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); ++ ++ if (hcint.b.xfercomp) { ++ /** @todo This is here because of a possible hardware bug. Spec ++ * says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT ++ * interrupt w/ACK bit set should occur, but I only see the ++ * XFERCOMP bit, even with it masked out. This is a workaround ++ * for that behavior. Should fix this when hardware is fixed. ++ */ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in) { ++ handle_hc_ack_intr(hcd, hc, hc_regs, qtd); ++ } ++ handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.stall) { ++ handle_hc_stall_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.xacterr && !hcd->core_if->dma_desc_enable) { ++ if (out_nak_enh) { ++ if (hcint.b.nyet || hcint.b.nak || hcint.b.ack) { ++ DWC_DEBUG("XactErr with NYET/NAK/ACK\n"); ++ qtd->error_count = 0; ++ } else { ++ DWC_DEBUG("XactErr without NYET/NAK/ACK\n"); ++ } ++ } ++ ++ /* ++ * Must handle xacterr before nak or ack. Could get a xacterr ++ * at the same time as either of these on a BULK/CONTROL OUT ++ * that started with a PING. The xacterr takes precedence. ++ */ ++ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.xcs_xact && hcd->core_if->dma_desc_enable) { ++ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.ahberr && hcd->core_if->dma_desc_enable) { ++ handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.bblerr) { ++ handle_hc_babble_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.frmovrun) { ++ handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd); ++ } else if (!out_nak_enh) { ++ if (hcint.b.nyet) { ++ /* ++ * Must handle nyet before nak or ack. Could get a nyet at the ++ * same time as either of those on a BULK/CONTROL OUT that ++ * started with a PING. The nyet takes precedence. ++ */ ++ handle_hc_nyet_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.nak && !hcintmsk.b.nak) { ++ /* ++ * If nak is not masked, it's because a non-split IN transfer ++ * is in an error state. In that case, the nak is handled by ++ * the nak interrupt handler, not here. Handle nak here for ++ * BULK/CONTROL OUT transfers, which halt on a NAK to allow ++ * rewinding the buffer pointer. ++ */ ++ handle_hc_nak_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.ack && !hcintmsk.b.ack) { ++ /* ++ * If ack is not masked, it's because a non-split IN transfer ++ * is in an error state. In that case, the ack is handled by ++ * the ack interrupt handler, not here. Handle ack here for ++ * split transfers. Start splits halt on ACK. ++ */ ++ handle_hc_ack_intr(hcd, hc, hc_regs, qtd); ++ } else { ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * A periodic transfer halted with no other channel ++ * interrupts set. Assume it was halted by the core ++ * because it could not be completed in its scheduled ++ * (micro)frame. ++ */ ++#ifdef DEBUG ++ DWC_PRINTF ++ ("%s: Halt channel %d (assume incomplete periodic transfer)\n", ++ __func__, hc->hc_num); ++#endif ++ halt_channel(hcd, hc, qtd, ++ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE); ++ } else { ++ DWC_ERROR ++ ("%s: Channel %d, DMA Mode -- ChHltd set, but reason " ++ "for halting is unknown, hcint 0x%08x, intsts 0x%08x\n", ++ __func__, hc->hc_num, hcint.d32, ++ DWC_READ_REG32(&hcd-> ++ core_if->core_global_regs-> ++ gintsts)); ++ disable_hc_int(hc_regs, chhltd); ++ } ++ ++ } ++ } else { ++ DWC_PRINTF("NYET/NAK/ACK/other in non-error case, 0x%08x\n", ++ hcint.d32); ++ disable_hc_int(hc_regs, chhltd); ++ } ++} ++ ++/** ++ * Handles a host channel Channel Halted interrupt. ++ * ++ * In slave mode, this handler is called only when the driver specifically ++ * requests a halt. This occurs during handling other host channel interrupts ++ * (e.g. nak, xacterr, stall, nyet, etc.). ++ * ++ * In DMA mode, this is the interrupt that occurs when the core has finished ++ * processing a transfer on a channel. Other host channel interrupts (except ++ * ahberr) are disabled in DMA mode. ++ */ ++static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Channel Halted--\n", hc->hc_num); ++ ++ if (hcd->core_if->dma_enable) { ++ handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd); ++ } else { ++#ifdef DEBUG ++ if (!halt_status_ok(hcd, hc, hc_regs, qtd)) { ++ return 1; ++ } ++#endif ++ release_channel(hcd, hc, qtd, hc->halt_status); ++ } ++ ++ return 1; ++} ++ ++/** Handles interrupt for a specific Host Channel */ ++int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) ++{ ++ int retval = 0; ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ dwc_hc_t *hc; ++ dwc_otg_hc_regs_t *hc_regs; ++ dwc_otg_qtd_t *qtd; ++ ++ DWC_DEBUGPL(DBG_HCDV, "--Host Channel Interrupt--, Channel %d\n", num); ++ ++ hc = dwc_otg_hcd->hc_ptr_array[num]; ++ hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num]; ++ qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); ++ ++ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); ++ hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); ++ DWC_DEBUGPL(DBG_HCDV, ++ " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", ++ hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32)); ++ hcint.d32 = hcint.d32 & hcintmsk.d32; ++ ++ if (!dwc_otg_hcd->core_if->dma_enable) { ++ if (hcint.b.chhltd && hcint.d32 != 0x2) { ++ hcint.b.chhltd = 0; ++ } ++ } ++ ++ if (hcint.b.xfercomp) { ++ retval |= ++ handle_hc_xfercomp_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ /* ++ * If NYET occurred at same time as Xfer Complete, the NYET is ++ * handled by the Xfer Complete interrupt handler. Don't want ++ * to call the NYET interrupt handler in this case. ++ */ ++ hcint.b.nyet = 0; ++ } ++ if (hcint.b.chhltd) { ++ retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.ahberr) { ++ retval |= handle_hc_ahberr_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.stall) { ++ retval |= handle_hc_stall_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.nak) { ++ retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.ack) { ++ retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.nyet) { ++ retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.xacterr) { ++ retval |= handle_hc_xacterr_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.bblerr) { ++ retval |= handle_hc_babble_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.frmovrun) { ++ retval |= ++ handle_hc_frmovrun_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.datatglerr) { ++ retval |= ++ handle_hc_datatglerr_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ ++ return retval; ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_linux.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_linux.c +new file mode 100644 +index 0000000..7323ef3 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_linux.c +@@ -0,0 +1,840 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_linux.c $ ++ * $Revision: #23 $ ++ * $Date: 2013/04/22 $ ++ * $Change: 2211149 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++/** ++ * @file ++ * ++ * This file contains the implementation of the HCD. In Linux, the HCD ++ * implements the hc_driver API. ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/errno.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/string.h> ++#include <linux/dma-mapping.h> ++#include <linux/version.h> ++#include <asm/io.h> ++#include <linux/usb.h> ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) ++#include <../drivers/usb/core/hcd.h> ++#else ++#include <linux/usb/hcd.h> ++#endif ++ ++#include "dwc_otg_hcd_if.h" ++#include "dwc_otg_dbg.h" ++#include "dwc_otg_driver.h" ++#include "dwc_otg_hcd.h" ++/** ++ * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is ++ * qualified with its direction (possible 32 endpoints per device). ++ */ ++#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \ ++ ((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4) ++ ++static const char dwc_otg_hcd_name[] = "dwc_otg_hcd"; ++ ++/** @name Linux HC Driver API Functions */ ++/** @{ */ ++static int urb_enqueue(struct usb_hcd *hcd, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ struct usb_host_endpoint *ep, ++#endif ++ struct urb *urb, gfp_t mem_flags); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb); ++#else ++static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); ++#endif ++ ++static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) ++static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep); ++#endif ++static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd); ++extern int hcd_start(struct usb_hcd *hcd); ++extern void hcd_stop(struct usb_hcd *hcd); ++static int get_frame_number(struct usb_hcd *hcd); ++extern int hub_status_data(struct usb_hcd *hcd, char *buf); ++extern int hub_control(struct usb_hcd *hcd, ++ u16 typeReq, ++ u16 wValue, u16 wIndex, char *buf, u16 wLength); ++ ++struct wrapper_priv_data { ++ dwc_otg_hcd_t *dwc_otg_hcd; ++}; ++ ++/** @} */ ++ ++static struct hc_driver dwc_otg_hc_driver = { ++ ++ .description = dwc_otg_hcd_name, ++ .product_desc = "DWC OTG Controller", ++ .hcd_priv_size = sizeof(struct wrapper_priv_data), ++ ++ .irq = dwc_otg_hcd_irq, ++ ++ .flags = HCD_MEMORY | HCD_USB2, ++ ++ //.reset = ++ .start = hcd_start, ++ //.suspend = ++ //.resume = ++ .stop = hcd_stop, ++ ++ .urb_enqueue = urb_enqueue, ++ .urb_dequeue = urb_dequeue, ++ .endpoint_disable = endpoint_disable, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) ++ .endpoint_reset = endpoint_reset, ++#endif ++ .get_frame_number = get_frame_number, ++ ++ .hub_status_data = hub_status_data, ++ .hub_control = hub_control, ++ //.bus_suspend = ++ //.bus_resume = ++}; ++ ++/** Gets the dwc_otg_hcd from a struct usb_hcd */ ++static inline dwc_otg_hcd_t *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd) ++{ ++ struct wrapper_priv_data *p; ++ p = (struct wrapper_priv_data *)(hcd->hcd_priv); ++ return p->dwc_otg_hcd; ++} ++ ++/** Gets the struct usb_hcd that contains a dwc_otg_hcd_t. */ ++static inline struct usb_hcd *dwc_otg_hcd_to_hcd(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ return dwc_otg_hcd_get_priv_data(dwc_otg_hcd); ++} ++ ++/** Gets the usb_host_endpoint associated with an URB. */ ++inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb *urb) ++{ ++ struct usb_device *dev = urb->dev; ++ int ep_num = usb_pipeendpoint(urb->pipe); ++ ++ if (usb_pipein(urb->pipe)) ++ return dev->ep_in[ep_num]; ++ else ++ return dev->ep_out[ep_num]; ++} ++ ++static int _disconnect(dwc_otg_hcd_t * hcd) ++{ ++ struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd); ++ ++ usb_hcd->self.is_b_host = 0; ++ return 0; ++} ++ ++static int _start(dwc_otg_hcd_t * hcd) ++{ ++ struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd); ++ ++ usb_hcd->self.is_b_host = dwc_otg_hcd_is_b_host(hcd); ++ hcd_start(usb_hcd); ++ ++ return 0; ++} ++ ++static int _hub_info(dwc_otg_hcd_t * hcd, void *urb_handle, uint32_t * hub_addr, ++ uint32_t * port_addr) ++{ ++ struct urb *urb = (struct urb *)urb_handle; ++ if (urb->dev->tt) { ++ *hub_addr = urb->dev->tt->hub->devnum; ++ } else { ++ *hub_addr = 0; ++ } ++ *port_addr = urb->dev->ttport; ++ return 0; ++} ++ ++static int _speed(dwc_otg_hcd_t * hcd, void *urb_handle) ++{ ++ struct urb *urb = (struct urb *)urb_handle; ++ return urb->dev->speed; ++} ++ ++static int _get_b_hnp_enable(dwc_otg_hcd_t * hcd) ++{ ++ struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd); ++ return usb_hcd->self.b_hnp_enable; ++} ++ ++static void allocate_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw, ++ struct urb *urb) ++{ ++ hcd_to_bus(hcd)->bandwidth_allocated += bw / urb->interval; ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ hcd_to_bus(hcd)->bandwidth_isoc_reqs++; ++ } else { ++ hcd_to_bus(hcd)->bandwidth_int_reqs++; ++ } ++} ++ ++static void free_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw, ++ struct urb *urb) ++{ ++ hcd_to_bus(hcd)->bandwidth_allocated -= bw / urb->interval; ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ hcd_to_bus(hcd)->bandwidth_isoc_reqs--; ++ } else { ++ hcd_to_bus(hcd)->bandwidth_int_reqs--; ++ } ++} ++ ++/** ++ * Sets the final status of an URB and returns it to the device driver. Any ++ * required cleanup of the URB is performed. ++ */ ++static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, ++ dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status) ++{ ++ struct urb *urb = (struct urb *)urb_handle; ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n", ++ __func__, urb, usb_pipedevice(urb->pipe), ++ usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "IN" : "OUT", status); ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ int i; ++ for (i = 0; i < urb->number_of_packets; i++) { ++ DWC_PRINTF(" ISO Desc %d status: %d\n", ++ i, urb->iso_frame_desc[i].status); ++ } ++ } ++ } ++#endif ++ ++ urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb); ++ /* Convert status value. */ ++ switch (status) { ++ case -DWC_E_PROTOCOL: ++ status = -EPROTO; ++ break; ++ case -DWC_E_IN_PROGRESS: ++ status = -EINPROGRESS; ++ break; ++ case -DWC_E_PIPE: ++ status = -EPIPE; ++ break; ++ case -DWC_E_IO: ++ status = -EIO; ++ break; ++ case -DWC_E_TIMEOUT: ++ status = -ETIMEDOUT; ++ break; ++ case -DWC_E_OVERFLOW: ++ status = -EOVERFLOW; ++ break; ++ default: ++ if (status) { ++ DWC_PRINTF("Uknown urb status %d\n", status); ++ ++ } ++ } ++ ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ int i; ++ ++ urb->error_count = dwc_otg_hcd_urb_get_error_count(dwc_otg_urb); ++ for (i = 0; i < urb->number_of_packets; ++i) { ++ urb->iso_frame_desc[i].actual_length = ++ dwc_otg_hcd_urb_get_iso_desc_actual_length ++ (dwc_otg_urb, i); ++ urb->iso_frame_desc[i].status = ++ dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_urb, i); ++ } ++ } ++ ++ urb->status = status; ++ urb->hcpriv = NULL; ++ if (!status) { ++ if ((urb->transfer_flags & URB_SHORT_NOT_OK) && ++ (urb->actual_length < urb->transfer_buffer_length)) { ++ urb->status = -EREMOTEIO; ++ } ++ } ++ ++ if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) || ++ (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) { ++ struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb); ++ if (ep) { ++ free_bus_bandwidth(dwc_otg_hcd_to_hcd(hcd), ++ dwc_otg_hcd_get_ep_bandwidth(hcd, ++ ep->hcpriv), ++ urb); ++ } ++ } ++ ++ DWC_FREE(dwc_otg_urb); ++ ++ DWC_SPINUNLOCK(hcd->lock); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb); ++#else ++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status); ++#endif ++ DWC_SPINLOCK(hcd->lock); ++ ++ return 0; ++} ++ ++static struct dwc_otg_hcd_function_ops hcd_fops = { ++ .start = _start, ++ .disconnect = _disconnect, ++ .hub_info = _hub_info, ++ .speed = _speed, ++ .complete = _complete, ++ .get_b_hnp_enable = _get_b_hnp_enable, ++}; ++ ++/** ++ * Initializes the HCD. This function allocates memory for and initializes the ++ * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the ++ * USB bus with the core and calls the hc_driver->start() function. It returns ++ * a negative error on failure. ++ */ ++int hcd_init( ++#ifdef LM_INTERFACE ++ struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *_dev ++#endif ++ ) ++{ ++ struct usb_hcd *hcd = NULL; ++ dwc_otg_hcd_t *dwc_otg_hcd = NULL; ++#ifdef LM_INTERFACE ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev); ++#endif ++ ++ int retval = 0; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT\n"); ++ ++ /* Set device flags indicating whether the HCD supports DMA. */ ++ if (dwc_otg_is_dma_enable(otg_dev->core_if)) { ++#ifdef LM_INTERFACE ++ _dev->dev.dma_mask = (void *)~0; ++ _dev->dev.coherent_dma_mask = ~0; ++#elif defined(PCI_INTERFACE) ++ pci_set_dma_mask(_dev, DMA_BIT_MASK(32)); ++ pci_set_consistent_dma_mask(_dev, DMA_BIT_MASK(32)); ++#endif ++ ++ } else { ++#ifdef LM_INTERFACE ++ _dev->dev.dma_mask = (void *)0; ++ _dev->dev.coherent_dma_mask = 0; ++#elif defined(PCI_INTERFACE) ++ pci_set_dma_mask(_dev, 0); ++ pci_set_consistent_dma_mask(_dev, 0); ++#endif ++ } ++ ++ /* ++ * Allocate memory for the base HCD plus the DWC OTG HCD. ++ * Initialize the base HCD. ++ */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) ++ hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, _dev->dev.bus_id); ++#else ++ hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, dev_name(&_dev->dev)); ++ hcd->has_tt = 1; ++// hcd->uses_new_polling = 1; ++// hcd->poll_rh = 0; ++#endif ++ if (!hcd) { ++ retval = -ENOMEM; ++ goto error1; ++ } ++ ++ hcd->regs = otg_dev->os_dep.base; ++ ++ /* Initialize the DWC OTG HCD. */ ++ dwc_otg_hcd = dwc_otg_hcd_alloc_hcd(); ++ if (!dwc_otg_hcd) { ++ goto error2; ++ } ++ ((struct wrapper_priv_data *)(hcd->hcd_priv))->dwc_otg_hcd = ++ dwc_otg_hcd; ++ otg_dev->hcd = dwc_otg_hcd; ++ ++ if (dwc_otg_hcd_init(dwc_otg_hcd, otg_dev->core_if)) { ++ goto error2; ++ } ++ ++ otg_dev->hcd->otg_dev = otg_dev; ++ hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) //don't support for LM(with 2.6.20.1 kernel) ++ hcd->self.otg_version = dwc_otg_get_otg_version(otg_dev->core_if); ++ /* Don't support SG list at this point */ ++ hcd->self.sg_tablesize = 0; ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) ++ /* Do not to do HNP polling if not capable */ ++ if (otg_dev->core_if->otg_ver) ++ hcd->self.is_hnp_cap = dwc_otg_get_hnpcapable(otg_dev->core_if); ++#endif ++ /* ++ * Finish generic HCD initialization and start the HCD. This function ++ * allocates the DMA buffer pool, registers the USB bus, requests the ++ * IRQ line, and calls hcd_start method. ++ */ ++ retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED | IRQF_DISABLED); ++ if (retval < 0) { ++ goto error2; ++ } ++ ++ dwc_otg_hcd_set_priv_data(dwc_otg_hcd, hcd); ++ return 0; ++ ++error2: ++ usb_put_hcd(hcd); ++error1: ++ return retval; ++} ++ ++/** ++ * Removes the HCD. ++ * Frees memory and resources associated with the HCD and deregisters the bus. ++ */ ++void hcd_remove( ++#ifdef LM_INTERFACE ++ struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *_dev ++#endif ++ ) ++{ ++#ifdef LM_INTERFACE ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev); ++#endif ++ ++ dwc_otg_hcd_t *dwc_otg_hcd; ++ struct usb_hcd *hcd; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD REMOVE\n"); ++ ++ if (!otg_dev) { ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__); ++ return; ++ } ++ ++ dwc_otg_hcd = otg_dev->hcd; ++ ++ if (!dwc_otg_hcd) { ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__); ++ return; ++ } ++ ++ hcd = dwc_otg_hcd_to_hcd(dwc_otg_hcd); ++ ++ if (!hcd) { ++ DWC_DEBUGPL(DBG_ANY, ++ "%s: dwc_otg_hcd_to_hcd(dwc_otg_hcd) NULL!\n", ++ __func__); ++ return; ++ } ++ usb_remove_hcd(hcd); ++ dwc_otg_hcd_set_priv_data(dwc_otg_hcd, NULL); ++ dwc_otg_hcd_remove(dwc_otg_hcd); ++ usb_put_hcd(hcd); ++} ++ ++/* ========================================================================= ++ * Linux HC Driver Functions ++ * ========================================================================= */ ++ ++/** Initializes the DWC_otg controller and its root hub and prepares it for host ++ * mode operation. Activates the root port. Returns 0 on success and a negative ++ * error code on failure. */ ++int hcd_start(struct usb_hcd *hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ struct usb_bus *bus; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD START\n"); ++ bus = hcd_to_bus(hcd); ++ ++ hcd->state = HC_STATE_RUNNING; ++ if (dwc_otg_hcd_start(dwc_otg_hcd, &hcd_fops)) { ++ if (dwc_otg_hcd->core_if->otg_ver && dwc_otg_is_device_mode(dwc_otg_hcd->core_if)) ++ dwc_otg_hcd->core_if->op_state = B_PERIPHERAL; ++ return 0; ++ } ++ ++ /* Initialize and connect root hub if one is not already attached */ ++ if (bus->root_hub) { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Has Root Hub\n"); ++ /* Inform the HUB driver to resume. */ ++ usb_hcd_resume_root_hub(hcd); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are ++ * stopped. ++ */ ++void hcd_stop(struct usb_hcd *hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ ++ dwc_otg_hcd_stop(dwc_otg_hcd); ++} ++ ++/** Returns the current frame number. */ ++static int get_frame_number(struct usb_hcd *hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ ++ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd); ++} ++ ++#ifdef DEBUG ++static void dump_urb_info(struct urb *urb, char *fn_name) ++{ ++ DWC_PRINTF("%s, urb %p\n", fn_name, urb); ++ DWC_PRINTF(" Device address: %d\n", usb_pipedevice(urb->pipe)); ++ DWC_PRINTF(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe), ++ (usb_pipein(urb->pipe) ? "IN" : "OUT")); ++ DWC_PRINTF(" Endpoint type: %s\n", ( { ++ char *pipetype; ++ switch (usb_pipetype(urb->pipe)) { ++case PIPE_CONTROL: ++pipetype = "CONTROL"; break; case PIPE_BULK: ++pipetype = "BULK"; break; case PIPE_INTERRUPT: ++pipetype = "INTERRUPT"; break; case PIPE_ISOCHRONOUS: ++pipetype = "ISOCHRONOUS"; break; default: ++ pipetype = "UNKNOWN"; break;}; ++ pipetype;} ++ )) ; ++ DWC_PRINTF(" Speed: %s\n", ( { ++ char *speed; switch (urb->dev->speed) { ++case USB_SPEED_HIGH: ++speed = "HIGH"; break; case USB_SPEED_FULL: ++speed = "FULL"; break; case USB_SPEED_LOW: ++speed = "LOW"; break; default: ++ speed = "UNKNOWN"; break;}; ++ speed;} ++ )) ; ++ DWC_PRINTF(" Max packet size: %d\n", ++ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); ++ DWC_PRINTF(" Data buffer length: %d\n", urb->transfer_buffer_length); ++ DWC_PRINTF(" Transfer buffer: %p, Transfer DMA: %p\n", ++ urb->transfer_buffer, (void *)urb->transfer_dma); ++ DWC_PRINTF(" Setup buffer: %p, Setup DMA: %p\n", ++ urb->setup_packet, (void *)urb->setup_dma); ++ DWC_PRINTF(" Interval: %d\n", urb->interval); ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ int i; ++ for (i = 0; i < urb->number_of_packets; i++) { ++ DWC_PRINTF(" ISO Desc %d:\n", i); ++ DWC_PRINTF(" offset: %d, length %d\n", ++ urb->iso_frame_desc[i].offset, ++ urb->iso_frame_desc[i].length); ++ } ++ } ++} ++ ++#endif ++ ++/** Starts processing a USB transfer request specified by a USB Request Block ++ * (URB). mem_flags indicates the type of memory allocation to use while ++ * processing this URB. */ ++static int urb_enqueue(struct usb_hcd *hcd, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ struct usb_host_endpoint *ep, ++#endif ++ struct urb *urb, gfp_t mem_flags) ++{ ++ int retval = 0; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) ++ struct usb_host_endpoint *ep = urb->ep; ++#endif ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ dwc_otg_hcd_urb_t *dwc_otg_urb; ++ int i; ++ int alloc_bandwidth = 0; ++ uint8_t ep_type = 0; ++ uint32_t flags = 0; ++ void *buf; ++ ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ dump_urb_info(urb, "urb_enqueue"); ++ } ++#endif ++ ++ if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) ++ || (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) { ++ if (!dwc_otg_hcd_is_bandwidth_allocated ++ (dwc_otg_hcd, &ep->hcpriv)) { ++ alloc_bandwidth = 1; ++ } ++ } ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: ++ ep_type = USB_ENDPOINT_XFER_CONTROL; ++ break; ++ case PIPE_ISOCHRONOUS: ++ ep_type = USB_ENDPOINT_XFER_ISOC; ++ break; ++ case PIPE_BULK: ++ ep_type = USB_ENDPOINT_XFER_BULK; ++ break; ++ case PIPE_INTERRUPT: ++ ep_type = USB_ENDPOINT_XFER_INT; ++ break; ++ default: ++ DWC_WARN("Wrong ep type\n"); ++ } ++ ++ dwc_otg_urb = dwc_otg_hcd_urb_alloc(dwc_otg_hcd, ++ urb->number_of_packets, ++ mem_flags == GFP_ATOMIC ? 1 : 0); ++ ++ dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe), ++ usb_pipeendpoint(urb->pipe), ep_type, ++ usb_pipein(urb->pipe), ++ usb_maxpacket(urb->dev, urb->pipe, ++ !(usb_pipein(urb->pipe)))); ++ ++ buf = urb->transfer_buffer; ++ if (hcd->self.uses_dma) { ++ /* ++ * Calculate virtual address from physical address, ++ * because some class driver may not fill transfer_buffer. ++ * In Buffer DMA mode virual address is used, ++ * when handling non DWORD aligned buffers. ++ */ ++ buf = phys_to_virt(urb->transfer_dma); ++ } ++ ++ if (!(urb->transfer_flags & URB_NO_INTERRUPT)) ++ flags |= URB_GIVEBACK_ASAP; ++ if (urb->transfer_flags & URB_ZERO_PACKET) ++ flags |= URB_SEND_ZERO_PACKET; ++ ++ dwc_otg_hcd_urb_set_params(dwc_otg_urb, urb, buf, ++ urb->transfer_dma, ++ urb->transfer_buffer_length, ++ urb->setup_packet, ++ urb->setup_dma, flags, urb->interval); ++ ++ for (i = 0; i < urb->number_of_packets; ++i) { ++ dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_urb, i, ++ urb-> ++ iso_frame_desc[i].offset, ++ urb-> ++ iso_frame_desc[i].length); ++ } ++ ++ urb->hcpriv = dwc_otg_urb; ++ retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb, &ep->hcpriv, ++ mem_flags == GFP_ATOMIC ? 1 : 0); ++ if (!retval) { ++ if (alloc_bandwidth) { ++ allocate_bus_bandwidth(hcd, ++ dwc_otg_hcd_get_ep_bandwidth ++ (dwc_otg_hcd, ep->hcpriv), urb); ++ } ++ } else { ++ if (retval == -DWC_E_NO_DEVICE) { ++ retval = -ENODEV; ++ } ++ } ++ ++ return retval; ++} ++ ++/** Aborts/cancels a USB transfer request. Always returns 0 to indicate ++ * success. */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb) ++#else ++static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ++#endif ++{ ++ dwc_irqflags_t flags; ++ dwc_otg_hcd_t *dwc_otg_hcd; ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue\n"); ++ ++ dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ dump_urb_info(urb, "urb_dequeue"); ++ } ++#endif ++ ++ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags); ++ ++ dwc_otg_hcd_urb_dequeue(dwc_otg_hcd, urb->hcpriv); ++ ++ DWC_FREE(urb->hcpriv); ++ urb->hcpriv = NULL; ++ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags); ++ ++ /* Higher layer software sets URB status. */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ usb_hcd_giveback_urb(hcd, urb); ++#else ++ usb_hcd_giveback_urb(hcd, urb, status); ++#endif ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ DWC_PRINTF("Called usb_hcd_giveback_urb()\n"); ++ DWC_PRINTF(" urb->status = %d\n", urb->status); ++ } ++ ++ return 0; ++} ++ ++/* Frees resources in the DWC_otg controller related to a given endpoint. Also ++ * clears state in the HCD related to the endpoint. Any URBs for the endpoint ++ * must already be dequeued. */ ++static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ ++ DWC_DEBUGPL(DBG_HCD, ++ "DWC OTG HCD EP DISABLE: _bEndpointAddress=0x%02x, " ++ "endpoint=%d\n", ep->desc.bEndpointAddress, ++ dwc_ep_addr_to_endpoint(ep->desc.bEndpointAddress)); ++ dwc_otg_hcd_endpoint_disable(dwc_otg_hcd, ep->hcpriv, 250); ++ ep->hcpriv = NULL; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30) ++/* Resets endpoint specific parameter values, in current version used to reset ++ * the data toggle(as a WA). This function can be called from usb_clear_halt routine */ ++static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) ++{ ++ dwc_irqflags_t flags; ++ struct usb_device *udev = NULL; ++ int epnum = usb_endpoint_num(&ep->desc); ++ int is_out = usb_endpoint_dir_out(&ep->desc); ++ int is_control = usb_endpoint_xfer_control(&ep->desc); ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++#ifdef LM_INTERFACE ++ struct lm_device *_dev = dwc_otg_hcd->otg_dev->os_dep.lmdev; ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *_dev = dwc_otg_hcd->otg_dev->os_dep.pcidev; ++#endif ++ ++ if (_dev) ++ udev = to_usb_device(&_dev->dev); ++ else ++ return; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD EP RESET: Endpoint Num=0x%02d\n", epnum); ++ ++ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags); ++ usb_settoggle(udev, epnum, is_out, 0); ++ if (is_control) ++ usb_settoggle(udev, epnum, !is_out, 0); ++ ++ if (ep->hcpriv) { ++ dwc_otg_hcd_endpoint_reset(dwc_otg_hcd, ep->hcpriv); ++ } ++ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags); ++} ++#endif ++ ++/** Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if ++ * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid ++ * interrupt. ++ * ++ * This function is called by the USB core when an interrupt occurs */ ++static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ int32_t retval = dwc_otg_hcd_handle_intr(dwc_otg_hcd); ++ if (retval != 0) { ++ S3C2410X_CLEAR_EINTPEND(); ++ } ++ return IRQ_RETVAL(retval); ++} ++ ++/** Creates Status Change bitmap for the root hub and root port. The bitmap is ++ * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1 ++ * is the status change indicator for the single root port. Returns 1 if either ++ * change indicator is 1, otherwise returns 0. */ ++int hub_status_data(struct usb_hcd *hcd, char *buf) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ ++ buf[0] = 0; ++ buf[0] |= (dwc_otg_hcd_is_status_changed(dwc_otg_hcd, 1)) << 1; ++ ++ return (buf[0] != 0); ++} ++ ++/** Handles hub class-specific requests. */ ++int hub_control(struct usb_hcd *hcd, ++ u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) ++{ ++ int retval; ++ ++ retval = dwc_otg_hcd_hub_control(hcd_to_dwc_otg_hcd(hcd), ++ typeReq, wValue, wIndex, buf, wLength); ++ ++ switch (retval) { ++ case -DWC_E_INVALID: ++ retval = -EINVAL; ++ break; ++ } ++ ++ return retval; ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_queue.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_queue.c +new file mode 100644 +index 0000000..5682867 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_hcd_queue.c +@@ -0,0 +1,727 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_queue.c $ ++ * $Revision: #45 $ ++ * $Date: 2013/01/24 $ ++ * $Change: 2150293 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++/** ++ * @file ++ * ++ * This file contains the functions to manage Queue Heads and Queue ++ * Transfer Descriptors. ++ */ ++ ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++/** ++ * Free each QTD in the QH's QTD-list then free the QH. QH should already be ++ * removed from a list. QTD list should already be empty if called from URB ++ * Dequeue. ++ * ++ * @param hcd HCD instance. ++ * @param qh The QH to free. ++ */ ++void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ dwc_otg_qtd_t *qtd, *qtd_tmp; ++ dwc_irqflags_t flags; ++ ++ /* Free each QTD in the QTD list */ ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) { ++ DWC_CIRCLEQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry); ++ dwc_otg_hcd_qtd_free(qtd); ++ } ++ ++ if (hcd->core_if->dma_desc_enable) { ++ dwc_otg_hcd_qh_free_ddma(hcd, qh); ++ } else if (qh->dw_align_buf) { ++ uint32_t buf_size; ++ if (qh->ep_type == UE_ISOCHRONOUS) { ++ buf_size = 4096; ++ } else { ++ buf_size = hcd->core_if->core_params->max_transfer_size; ++ } ++ DWC_DMA_FREE(buf_size, qh->dw_align_buf, qh->dw_align_buf_dma); ++ } ++ ++ DWC_FREE(qh); ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ return; ++} ++ ++#define BitStuffTime(bytecount) ((8 * 7* bytecount) / 6) ++#define HS_HOST_DELAY 5 /* nanoseconds */ ++#define FS_LS_HOST_DELAY 1000 /* nanoseconds */ ++#define HUB_LS_SETUP 333 /* nanoseconds */ ++#define NS_TO_US(ns) ((ns + 500) / 1000) ++ /* convert & round nanoseconds to microseconds */ ++ ++static uint32_t calc_bus_time(int speed, int is_in, int is_isoc, int bytecount) ++{ ++ unsigned long retval; ++ ++ switch (speed) { ++ case USB_SPEED_HIGH: ++ if (is_isoc) { ++ retval = ++ ((38 * 8 * 2083) + ++ (2083 * (3 + BitStuffTime(bytecount)))) / 1000 + ++ HS_HOST_DELAY; ++ } else { ++ retval = ++ ((55 * 8 * 2083) + ++ (2083 * (3 + BitStuffTime(bytecount)))) / 1000 + ++ HS_HOST_DELAY; ++ } ++ break; ++ case USB_SPEED_FULL: ++ if (is_isoc) { ++ retval = ++ (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000; ++ if (is_in) { ++ retval = 7268 + FS_LS_HOST_DELAY + retval; ++ } else { ++ retval = 6265 + FS_LS_HOST_DELAY + retval; ++ } ++ } else { ++ retval = ++ (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000; ++ retval = 9107 + FS_LS_HOST_DELAY + retval; ++ } ++ break; ++ case USB_SPEED_LOW: ++ if (is_in) { ++ retval = ++ (67667 * (31 + 10 * BitStuffTime(bytecount))) / ++ 1000; ++ retval = ++ 64060 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY + ++ retval; ++ } else { ++ retval = ++ (66700 * (31 + 10 * BitStuffTime(bytecount))) / ++ 1000; ++ retval = ++ 64107 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY + ++ retval; ++ } ++ break; ++ default: ++ DWC_WARN("Unknown device speed\n"); ++ retval = -1; ++ } ++ ++ return NS_TO_US(retval); ++} ++ ++/** ++ * Initializes a QH structure. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh The QH to init. ++ * @param urb Holds the information about the device/endpoint that we need ++ * to initialize the QH. ++ */ ++#define SCHEDULE_SLOP 10 ++void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb) ++{ ++ char *speed, *type; ++ int dev_speed; ++ uint32_t hub_addr, hub_port; ++ ++ dwc_memset(qh, 0, sizeof(dwc_otg_qh_t)); ++ ++ /* Initialize QH */ ++ qh->ep_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info); ++ qh->ep_is_in = dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0; ++ ++ qh->data_toggle = DWC_OTG_HC_PID_DATA0; ++ qh->maxp = dwc_otg_hcd_get_mps(&urb->pipe_info); ++ DWC_CIRCLEQ_INIT(&qh->qtd_list); ++ DWC_LIST_INIT(&qh->qh_list_entry); ++ qh->channel = NULL; ++ ++ /* FS/LS Enpoint on HS Hub ++ * NOT virtual root hub */ ++ dev_speed = hcd->fops->speed(hcd, urb->priv); ++ ++ hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &hub_port); ++ qh->do_split = 0; ++ ++ if (((dev_speed == USB_SPEED_LOW) || ++ (dev_speed == USB_SPEED_FULL)) && ++ (hub_addr != 0 && hub_addr != 1)) { ++ DWC_DEBUGPL(DBG_HCD, ++ "QH init: EP %d: TT found at hub addr %d, for port %d\n", ++ dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr, ++ hub_port); ++ qh->do_split = 1; ++ } ++ ++ if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) { ++ /* Compute scheduling parameters once and save them. */ ++ hprt0_data_t hprt; ++ ++ /** @todo Account for split transfers in the bus time. */ ++ int bytecount = ++ dwc_hb_mult(qh->maxp) * dwc_max_packet(qh->maxp); ++ ++ qh->usecs = ++ calc_bus_time((qh->do_split ? USB_SPEED_HIGH : dev_speed), ++ qh->ep_is_in, (qh->ep_type == UE_ISOCHRONOUS), ++ bytecount); ++ /* Start in a slightly future (micro)frame. */ ++ qh->sched_frame = dwc_frame_num_inc(hcd->frame_number, ++ SCHEDULE_SLOP); ++ qh->interval = urb->interval; ++ ++#if 0 ++ /* Increase interrupt polling rate for debugging. */ ++ if (qh->ep_type == UE_INTERRUPT) { ++ qh->interval = 8; ++ } ++#endif ++ hprt.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0); ++ if ((hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) && ++ ((dev_speed == USB_SPEED_LOW) || ++ (dev_speed == USB_SPEED_FULL))) { ++ qh->interval *= 8; ++ qh->sched_frame |= 0x7; ++ qh->start_split_frame = qh->sched_frame; ++ } ++ ++ } ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n"); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - qh = %p\n", qh); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Device Address = %d\n", ++ dwc_otg_hcd_get_dev_addr(&urb->pipe_info)); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Endpoint %d, %s\n", ++ dwc_otg_hcd_get_ep_num(&urb->pipe_info), ++ dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT"); ++ switch (dev_speed) { ++ case USB_SPEED_LOW: ++ qh->dev_speed = DWC_OTG_EP_SPEED_LOW; ++ speed = "low"; ++ break; ++ case USB_SPEED_FULL: ++ qh->dev_speed = DWC_OTG_EP_SPEED_FULL; ++ speed = "full"; ++ break; ++ case USB_SPEED_HIGH: ++ qh->dev_speed = DWC_OTG_EP_SPEED_HIGH; ++ speed = "high"; ++ break; ++ default: ++ speed = "?"; ++ break; ++ } ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Speed = %s\n", speed); ++ ++ switch (qh->ep_type) { ++ case UE_ISOCHRONOUS: ++ type = "isochronous"; ++ break; ++ case UE_INTERRUPT: ++ type = "interrupt"; ++ break; ++ case UE_CONTROL: ++ type = "control"; ++ break; ++ case UE_BULK: ++ type = "bulk"; ++ break; ++ default: ++ type = "?"; ++ break; ++ } ++ ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Type = %s\n", type); ++ ++#ifdef DEBUG ++ if (qh->ep_type == UE_INTERRUPT) { ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - usecs = %d\n", ++ qh->usecs); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - interval = %d\n", ++ qh->interval); ++ } ++#endif ++ ++} ++ ++/** ++ * This function allocates and initializes a QH. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param urb Holds the information about the device/endpoint that we need ++ * to initialize the QH. ++ * @param atomic_alloc Flag to do atomic allocation if needed ++ * ++ * @return Returns pointer to the newly allocated QH, or NULL on error. */ ++dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd, ++ dwc_otg_hcd_urb_t * urb, int atomic_alloc) ++{ ++ dwc_otg_qh_t *qh; ++ ++ /* Allocate memory */ ++ /** @todo add memflags argument */ ++ qh = dwc_otg_hcd_qh_alloc(atomic_alloc); ++ if (qh == NULL) { ++ DWC_ERROR("qh allocation failed"); ++ return NULL; ++ } ++ ++ qh_init(hcd, qh, urb); ++ ++ if (hcd->core_if->dma_desc_enable ++ && (dwc_otg_hcd_qh_init_ddma(hcd, qh) < 0)) { ++ dwc_otg_hcd_qh_free(hcd, qh); ++ return NULL; ++ } ++ ++ return qh; ++} ++ ++/** ++ * Checks that a channel is available for a periodic transfer. ++ * ++ * @return 0 if successful, negative error code otherise. ++ */ ++static int periodic_channel_available(dwc_otg_hcd_t * hcd) ++{ ++ /* ++ * Currently assuming that there is a dedicated host channnel for each ++ * periodic transaction plus at least one host channel for ++ * non-periodic transactions. ++ */ ++ int status; ++ int num_channels; ++ ++ num_channels = hcd->core_if->core_params->host_channels; ++ if ((hcd->periodic_channels + hcd->non_periodic_channels < num_channels) ++ && (hcd->periodic_channels < num_channels - 1)) { ++ status = 0; ++ } else { ++ DWC_INFO("%s: Total channels: %d, Periodic: %d, Non-periodic: %d\n", ++ __func__, num_channels, hcd->periodic_channels, hcd->non_periodic_channels); //NOTICE ++ status = -DWC_E_NO_SPACE; ++ } ++ ++ return status; ++} ++ ++/** ++ * Checks that there is sufficient bandwidth for the specified QH in the ++ * periodic schedule. For simplicity, this calculation assumes that all the ++ * transfers in the periodic schedule may occur in the same (micro)frame. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh QH containing periodic bandwidth required. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++static int check_periodic_bandwidth(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ int status; ++ int16_t max_claimed_usecs; ++ ++ status = 0; ++ ++ if ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) || qh->do_split) { ++ /* ++ * High speed mode. ++ * Max periodic usecs is 80% x 125 usec = 100 usec. ++ */ ++ ++ max_claimed_usecs = 100 - qh->usecs; ++ } else { ++ /* ++ * Full speed mode. ++ * Max periodic usecs is 90% x 1000 usec = 900 usec. ++ */ ++ max_claimed_usecs = 900 - qh->usecs; ++ } ++ ++ if (hcd->periodic_usecs > max_claimed_usecs) { ++ DWC_INFO("%s: already claimed usecs %d, required usecs %d\n", __func__, hcd->periodic_usecs, qh->usecs); //NOTICE ++ status = -DWC_E_NO_SPACE; ++ } ++ ++ return status; ++} ++ ++/** ++ * Checks that the max transfer size allowed in a host channel is large enough ++ * to handle the maximum data transfer in a single (micro)frame for a periodic ++ * transfer. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh QH for a periodic endpoint. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ int status; ++ uint32_t max_xfer_size; ++ uint32_t max_channel_xfer_size; ++ ++ status = 0; ++ ++ max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp); ++ max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size; ++ ++ if (max_xfer_size > max_channel_xfer_size) { ++ DWC_INFO("%s: Periodic xfer length %d > " "max xfer length for channel %d\n", ++ __func__, max_xfer_size, max_channel_xfer_size); //NOTICE ++ status = -DWC_E_NO_SPACE; ++ } ++ ++ return status; ++} ++ ++/** ++ * Schedules an interrupt or isochronous transfer in the periodic schedule. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh QH for the periodic transfer. The QH should already contain the ++ * scheduling information. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ int status = 0; ++ ++ status = periodic_channel_available(hcd); ++ if (status) { ++ DWC_INFO("%s: No host channel available for periodic " "transfer.\n", __func__); //NOTICE ++ return status; ++ } ++ ++ status = check_periodic_bandwidth(hcd, qh); ++ if (status) { ++ DWC_INFO("%s: Insufficient periodic bandwidth for " "periodic transfer.\n", __func__); //NOTICE ++ return status; ++ } ++ ++ status = check_max_xfer_size(hcd, qh); ++ if (status) { ++ DWC_INFO("%s: Channel max transfer size too small " "for periodic transfer.\n", __func__); //NOTICE ++ return status; ++ } ++ ++ if (hcd->core_if->dma_desc_enable) { ++ /* Don't rely on SOF and start in ready schedule */ ++ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry); ++ } ++ else { ++ /* Always start in the inactive schedule. */ ++ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry); ++ } ++ ++ /* Reserve the periodic channel. */ ++ hcd->periodic_channels++; ++ ++ /* Update claimed usecs per (micro)frame. */ ++ hcd->periodic_usecs += qh->usecs; ++ ++ return status; ++} ++ ++/** ++ * This function adds a QH to either the non periodic or periodic schedule if ++ * it is not already in the schedule. If the QH is already in the schedule, no ++ * action is taken. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ int status = 0; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) { ++ /* QH already in a schedule. */ ++ return status; ++ } ++ ++ /* Add the new QH to the appropriate schedule */ ++ if (dwc_qh_is_non_per(qh)) { ++ /* Always start in the inactive schedule. */ ++ DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive, ++ &qh->qh_list_entry); ++ } else { ++ status = schedule_periodic(hcd, qh); ++ if ( !hcd->periodic_qh_count ) { ++ intr_mask.b.sofintr = 1; ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, ++ intr_mask.d32, intr_mask.d32); ++ } ++ hcd->periodic_qh_count++; ++ } ++ ++ return status; ++} ++ ++/** ++ * Removes an interrupt or isochronous transfer from the periodic schedule. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh QH for the periodic transfer. ++ */ ++static void deschedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ DWC_LIST_REMOVE_INIT(&qh->qh_list_entry); ++ ++ /* Release the periodic channel reservation. */ ++ hcd->periodic_channels--; ++ ++ /* Update claimed usecs per (micro)frame. */ ++ hcd->periodic_usecs -= qh->usecs; ++} ++ ++/** ++ * Removes a QH from either the non-periodic or periodic schedule. Memory is ++ * not freed. ++ * ++ * @param hcd The HCD state structure. ++ * @param qh QH to remove from schedule. */ ++void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ if (DWC_LIST_EMPTY(&qh->qh_list_entry)) { ++ /* QH is not in a schedule. */ ++ return; ++ } ++ ++ if (dwc_qh_is_non_per(qh)) { ++ if (hcd->non_periodic_qh_ptr == &qh->qh_list_entry) { ++ hcd->non_periodic_qh_ptr = ++ hcd->non_periodic_qh_ptr->next; ++ } ++ DWC_LIST_REMOVE_INIT(&qh->qh_list_entry); ++ } else { ++ deschedule_periodic(hcd, qh); ++ hcd->periodic_qh_count--; ++ if( !hcd->periodic_qh_count ) { ++ intr_mask.b.sofintr = 1; ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ } ++ } ++} ++ ++/** ++ * Deactivates a QH. For non-periodic QHs, removes the QH from the active ++ * non-periodic schedule. The QH is added to the inactive non-periodic ++ * schedule if any QTDs are still attached to the QH. ++ * ++ * For periodic QHs, the QH is removed from the periodic queued schedule. If ++ * there are any QTDs still attached to the QH, the QH is added to either the ++ * periodic inactive schedule or the periodic ready schedule and its next ++ * scheduled frame is calculated. The QH is placed in the ready schedule if ++ * the scheduled frame has been reached already. Otherwise it's placed in the ++ * inactive schedule. If there are no QTDs attached to the QH, the QH is ++ * completely removed from the periodic schedule. ++ */ ++void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, ++ int sched_next_periodic_split) ++{ ++ if (dwc_qh_is_non_per(qh)) { ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { ++ /* Add back to inactive non-periodic schedule. */ ++ dwc_otg_hcd_qh_add(hcd, qh); ++ } ++ } else { ++ uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd); ++ ++ if (qh->do_split) { ++ /* Schedule the next continuing periodic split transfer */ ++ if (sched_next_periodic_split) { ++ ++ qh->sched_frame = frame_number; ++ if (dwc_frame_num_le(frame_number, ++ dwc_frame_num_inc ++ (qh->start_split_frame, ++ 1))) { ++ /* ++ * Allow one frame to elapse after start ++ * split microframe before scheduling ++ * complete split, but DONT if we are ++ * doing the next start split in the ++ * same frame for an ISOC out. ++ */ ++ if ((qh->ep_type != UE_ISOCHRONOUS) || ++ (qh->ep_is_in != 0)) { ++ qh->sched_frame = ++ dwc_frame_num_inc(qh->sched_frame, 1); ++ } ++ } ++ } else { ++ qh->sched_frame = ++ dwc_frame_num_inc(qh->start_split_frame, ++ qh->interval); ++ if (dwc_frame_num_le ++ (qh->sched_frame, frame_number)) { ++ qh->sched_frame = frame_number; ++ } ++ qh->sched_frame |= 0x7; ++ qh->start_split_frame = qh->sched_frame; ++ } ++ } else { ++ qh->sched_frame = ++ dwc_frame_num_inc(qh->sched_frame, qh->interval); ++ if (dwc_frame_num_le(qh->sched_frame, frame_number)) { ++ qh->sched_frame = frame_number; ++ } ++ } ++ ++ if (DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ } else { ++ /* ++ * Remove from periodic_sched_queued and move to ++ * appropriate queue. ++ */ ++ if (qh->sched_frame == frame_number) { ++ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, ++ &qh->qh_list_entry); ++ } else { ++ DWC_LIST_MOVE_HEAD ++ (&hcd->periodic_sched_inactive, ++ &qh->qh_list_entry); ++ } ++ } ++ } ++} ++ ++/** ++ * This function allocates and initializes a QTD. ++ * ++ * @param urb The URB to create a QTD from. Each URB-QTD pair will end up ++ * pointing to each other so each pair should have a unique correlation. ++ * @param atomic_alloc Flag to do atomic alloc if needed ++ * ++ * @return Returns pointer to the newly allocated QTD, or NULL on error. */ ++dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb, int atomic_alloc) ++{ ++ dwc_otg_qtd_t *qtd; ++ ++ qtd = dwc_otg_hcd_qtd_alloc(atomic_alloc); ++ if (qtd == NULL) { ++ return NULL; ++ } ++ ++ dwc_otg_hcd_qtd_init(qtd, urb); ++ return qtd; ++} ++ ++/** ++ * Initializes a QTD structure. ++ * ++ * @param qtd The QTD to initialize. ++ * @param urb The URB to use for initialization. */ ++void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb) ++{ ++ dwc_memset(qtd, 0, sizeof(dwc_otg_qtd_t)); ++ qtd->urb = urb; ++ if (dwc_otg_hcd_get_pipe_type(&urb->pipe_info) == UE_CONTROL) { ++ /* ++ * The only time the QTD data toggle is used is on the data ++ * phase of control transfers. This phase always starts with ++ * DATA1. ++ */ ++ qtd->data_toggle = DWC_OTG_HC_PID_DATA1; ++ qtd->control_phase = DWC_OTG_CONTROL_SETUP; ++ } ++ ++ /* start split */ ++ qtd->complete_split = 0; ++ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL; ++ qtd->isoc_split_offset = 0; ++ qtd->in_process = 0; ++ ++ /* Store the qtd ptr in the urb to reference what QTD. */ ++ urb->qtd = qtd; ++ return; ++} ++ ++/** ++ * This function adds a QTD to the QTD-list of a QH. It will find the correct ++ * QH to place the QTD into. If it does not find a QH, then it will create a ++ * new QH. If the QH to which the QTD is added is not currently scheduled, it ++ * is placed into the proper schedule based on its EP type. ++ * ++ * @param[in] qtd The QTD to add ++ * @param[in] hcd The DWC HCD structure ++ * @param[out] qh out parameter to return queue head ++ * @param atomic_alloc Flag to do atomic alloc if needed ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, ++ dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh, int atomic_alloc) ++{ ++ int retval = 0; ++ dwc_irqflags_t flags; ++ ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ ++ /* ++ * Get the QH which holds the QTD-list to insert to. Create QH if it ++ * doesn't exist. ++ */ ++ if (*qh == NULL) { ++ *qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc); ++ if (*qh == NULL) { ++ retval = -1; ++ goto done; ++ } ++ } ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ retval = dwc_otg_hcd_qh_add(hcd, *qh); ++ if (retval == 0) { ++ DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd, ++ qtd_list_entry); ++ } ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ ++done: ++ ++ return retval; ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_os_dep.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_os_dep.h +new file mode 100644 +index 0000000..7e491fe +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_os_dep.h +@@ -0,0 +1,88 @@ ++#ifndef _DWC_OS_DEP_H_ ++#define _DWC_OS_DEP_H_ ++ ++/** ++ * @file ++ * ++ * This file contains OS dependent structures. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/errno.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/ctype.h> ++#include <linux/string.h> ++#include <linux/dma-mapping.h> ++#include <linux/jiffies.h> ++#include <linux/delay.h> ++#include <linux/timer.h> ++#include <linux/workqueue.h> ++#include <linux/stat.h> ++#include <linux/pci.h> ++ ++#include <linux/version.h> ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++# include <linux/irq.h> ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++# include <linux/usb/ch9.h> ++#else ++# include <linux/usb_ch9.h> ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) ++# include <linux/usb/gadget.h> ++#else ++# include <linux/usb_gadget.h> ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++# include <asm/irq.h> ++#endif ++ ++ ++#include <asm/unaligned.h> ++#include <asm/sizes.h> ++#include <asm/param.h> ++#include <asm/io.h> ++ ++ ++/** The OS page size */ ++#define DWC_OS_PAGE_SIZE PAGE_SIZE ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) ++typedef int gfp_t; ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) ++# define IRQF_SHARED SA_SHIRQ ++#endif ++ ++typedef struct os_dependent { ++ /** Base address returned from ioremap() */ ++ void *base; ++ ++ /** Register offset for Diagnostic API */ ++ uint32_t reg_offset; ++ ++ uint32_t res_start; ++ ++ struct platform_device *lmdev; ++ ++} os_dependent_t; ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_OS_DEP_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd.c +new file mode 100644 +index 0000000..1382647 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd.c +@@ -0,0 +1,2932 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.c $ ++ * $Revision: #105 $ ++ * $Date: 2013/05/16 $ ++ * $Change: 2231774 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++ ++/** @file ++ * This file implements PCD Core. All code in this file is portable and doesn't ++ * use any OS specific functions. ++ * PCD Core provides Interface, defined in <code><dwc_otg_pcd_if.h></code> ++ * header file, which can be used to implement OS specific PCD interface. ++ * ++ * An important function of the PCD is managing interrupts generated ++ * by the DWC_otg controller. The implementation of the DWC_otg device ++ * mode interrupt service routines is in dwc_otg_pcd_intr.c. ++ * ++ * @todo Add Device Mode test modes (Test J mode, Test K mode, etc). ++ * @todo Does it work when the request size is greater than DEPTSIZ ++ * transfer size ++ * ++ */ ++ ++#include "dwc_otg_pcd.h" ++ ++#ifdef DWC_UTE_CFI ++#include "dwc_otg_cfi.h" ++ ++extern int init_cfi(cfiobject_t * cfiobj); ++#endif ++static int bulk_num = 0; ++/** ++ * Choose endpoint from ep arrays using usb_ep structure. ++ */ ++static dwc_otg_pcd_ep_t *get_ep_from_handle(dwc_otg_pcd_t * pcd, void *handle) ++{ ++ int i; ++ if (pcd->ep0.priv == handle) { ++ return &pcd->ep0; ++ } ++ for (i = 0; i < MAX_EPS_CHANNELS - 1; i++) { ++ if (pcd->in_ep[i].priv == handle) ++ return &pcd->in_ep[i]; ++ if (pcd->out_ep[i].priv == handle) ++ return &pcd->out_ep[i]; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * This function completes a request. It call's the request call back. ++ */ ++void dwc_otg_request_done(dwc_otg_pcd_ep_t * ep, dwc_otg_pcd_request_t * req, ++ int32_t status) ++{ ++ unsigned stopped = ep->stopped; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(ep %p req %p)\n", __func__, ep, req); ++ DWC_CIRCLEQ_REMOVE_INIT(&ep->queue, req, queue_entry); ++ ++ /* don't modify queue heads during completion callback */ ++ ep->stopped = 1; ++ /* spin_unlock/spin_lock now done in fops->complete() */ ++ ep->pcd->fops->complete(ep->pcd, ep->priv, req->priv, status, ++ req->actual); ++ ++ if (ep->pcd->request_pending > 0) { ++ --ep->pcd->request_pending; ++ } ++ ++ ep->stopped = stopped; ++ DWC_FREE(req); ++} ++ ++/** ++ * This function terminates all the requsts in the EP request queue. ++ */ ++void dwc_otg_request_nuke(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_pcd_request_t *req; ++ ++ ep->stopped = 1; ++ ++ /* called with irqs blocked?? */ ++ while (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ dwc_otg_request_done(ep, req, -DWC_E_SHUTDOWN); ++ } ++} ++ ++void dwc_otg_pcd_start(dwc_otg_pcd_t * pcd, ++ const struct dwc_otg_pcd_function_ops *fops) ++{ ++ pcd->fops = fops; ++} ++ ++/** ++ * PCD Callback function for initializing the PCD when switching to ++ * device mode. ++ * ++ * @param p void pointer to the <code>dwc_otg_pcd_t</code> ++ */ ++static int32_t dwc_otg_pcd_start_cb(void *p) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ ++ /* ++ * Initialized the Core for Device mode. ++ */ ++ if (dwc_otg_is_device_mode(core_if)) { ++ dwc_otg_core_dev_init(core_if); ++ /* Set core_if's lock pointer to the pcd->lock */ ++ core_if->lock = pcd->lock; ++ } ++ return 1; ++} ++ ++/** CFI-specific buffer allocation function for EP */ ++#ifdef DWC_UTE_CFI ++uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, dwc_dma_t * addr, ++ size_t buflen, int flags) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ ep = get_ep_from_handle(pcd, pep); ++ if (!ep) { ++ DWC_WARN("bad ep\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ return pcd->cfi->ops.ep_alloc_buf(pcd->cfi, pcd, ep, addr, buflen, ++ flags); ++} ++#else ++uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, dwc_dma_t * addr, ++ size_t buflen, int flags); ++#endif ++ ++/** ++ * PCD Callback function for notifying the PCD when resuming from ++ * suspend. ++ * ++ * @param p void pointer to the <code>dwc_otg_pcd_t</code> ++ */ ++static int32_t dwc_otg_pcd_resume_cb(void *p) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p; ++ ++ if (pcd->fops->resume) { ++ pcd->fops->resume(pcd); ++ } ++ ++ /* Stop the SRP timeout timer. */ ++ if ((GET_CORE_IF(pcd)->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS) ++ || (!GET_CORE_IF(pcd)->core_params->i2c_enable)) { ++ if (GET_CORE_IF(pcd)->srp_timer_started) { ++ GET_CORE_IF(pcd)->srp_timer_started = 0; ++ DWC_TIMER_CANCEL(GET_CORE_IF(pcd)->srp_timer); ++ } ++ } ++ return 1; ++} ++ ++/** ++ * PCD Callback function for notifying the PCD device is suspended. ++ * ++ * @param p void pointer to the <code>dwc_otg_pcd_t</code> ++ */ ++static int32_t dwc_otg_pcd_suspend_cb(void *p) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p; ++ ++ if (pcd->fops->suspend) { ++ DWC_SPINUNLOCK(pcd->lock); ++ pcd->fops->suspend(pcd); ++ DWC_SPINLOCK(pcd->lock); ++ } ++ ++ return 1; ++} ++ ++/** ++ * PCD Callback function for stopping the PCD when switching to Host ++ * mode. ++ * ++ * @param p void pointer to the <code>dwc_otg_pcd_t</code> ++ */ ++static int32_t dwc_otg_pcd_stop_cb(void *p) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p; ++ extern void dwc_otg_pcd_stop(dwc_otg_pcd_t * _pcd); ++ ++ dwc_otg_pcd_stop(pcd); ++ return 1; ++} ++ ++/** ++ * PCD Callback structure for handling mode switching. ++ */ ++static dwc_otg_cil_callbacks_t pcd_callbacks = { ++ .start = dwc_otg_pcd_start_cb, ++ .stop = dwc_otg_pcd_stop_cb, ++ .suspend = dwc_otg_pcd_suspend_cb, ++ .resume_wakeup = dwc_otg_pcd_resume_cb, ++ .p = 0, /* Set at registration */ ++}; ++ ++/** ++ * This function allocates a DMA Descriptor chain for the Endpoint ++ * buffer to be used for a transfer to/from the specified endpoint. ++ */ ++dwc_otg_dev_dma_desc_t *dwc_otg_ep_alloc_desc_chain(dwc_dma_t * dma_desc_addr, ++ uint32_t count) ++{ ++ return DWC_DMA_ALLOC_ATOMIC(count * sizeof(dwc_otg_dev_dma_desc_t), ++ dma_desc_addr); ++} ++ ++/** ++ * This function frees a DMA Descriptor chain that was allocated by ep_alloc_desc. ++ */ ++void dwc_otg_ep_free_desc_chain(dwc_otg_dev_dma_desc_t * desc_addr, ++ uint32_t dma_desc_addr, uint32_t count) ++{ ++ DWC_DMA_FREE(count * sizeof(dwc_otg_dev_dma_desc_t), desc_addr, ++ dma_desc_addr); ++} ++ ++#ifdef DWC_EN_ISOC ++ ++/** ++ * This function initializes a descriptor chain for Isochronous transfer ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dwc_ep The EP to start the transfer on. ++ * ++ */ ++void dwc_otg_iso_ep_start_ddma_transfer(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * dwc_ep) ++{ ++ ++ dsts_data_t dsts = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ volatile uint32_t *addr; ++ int i, j; ++ uint32_t len; ++ ++ if (dwc_ep->is_in) ++ dwc_ep->desc_cnt = dwc_ep->buf_proc_intrvl / dwc_ep->bInterval; ++ else ++ dwc_ep->desc_cnt = ++ dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / ++ dwc_ep->bInterval; ++ ++ /** Allocate descriptors for double buffering */ ++ dwc_ep->iso_desc_addr = ++ dwc_otg_ep_alloc_desc_chain(&dwc_ep->iso_dma_desc_addr, ++ dwc_ep->desc_cnt * 2); ++ if (dwc_ep->desc_addr) { ++ DWC_WARN("%s, can't allocate DMA descriptor chain\n", __func__); ++ return; ++ } ++ ++ dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ /** ISO OUT EP */ ++ if (dwc_ep->is_in == 0) { ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ dwc_otg_dev_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr; ++ dma_addr_t dma_ad; ++ uint32_t data_per_desc; ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[dwc_ep->num]; ++ int offset; ++ ++ addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl; ++ dma_ad = (dma_addr_t) DWC_READ_REG32(&(out_regs->doepdma)); ++ ++ /** Buffer 0 descriptors setup */ ++ dma_ad = dwc_ep->dma_addr0; ++ ++ sts.b_iso_out.bs = BS_HOST_READY; ++ sts.b_iso_out.rxsts = 0; ++ sts.b_iso_out.l = 0; ++ sts.b_iso_out.sp = 0; ++ sts.b_iso_out.ioc = 0; ++ sts.b_iso_out.pid = 0; ++ sts.b_iso_out.framenum = 0; ++ ++ offset = 0; ++ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; ++ i += dwc_ep->pkt_per_frm) { ++ ++ for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { ++ uint32_t len = (j + 1) * dwc_ep->maxpacket; ++ if (len > dwc_ep->data_per_frame) ++ data_per_desc = ++ dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket; ++ else ++ data_per_desc = dwc_ep->maxpacket; ++ len = data_per_desc % 4; ++ if (len) ++ data_per_desc += 4 - len; ++ ++ sts.b_iso_out.rxbytes = data_per_desc; ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ offset += data_per_desc; ++ dma_desc++; ++ dma_ad += data_per_desc; ++ } ++ } ++ ++ for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { ++ uint32_t len = (j + 1) * dwc_ep->maxpacket; ++ if (len > dwc_ep->data_per_frame) ++ data_per_desc = ++ dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket; ++ else ++ data_per_desc = dwc_ep->maxpacket; ++ len = data_per_desc % 4; ++ if (len) ++ data_per_desc += 4 - len; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ offset += data_per_desc; ++ dma_desc++; ++ dma_ad += data_per_desc; ++ } ++ ++ sts.b_iso_out.ioc = 1; ++ len = (j + 1) * dwc_ep->maxpacket; ++ if (len > dwc_ep->data_per_frame) ++ data_per_desc = ++ dwc_ep->data_per_frame - j * dwc_ep->maxpacket; ++ else ++ data_per_desc = dwc_ep->maxpacket; ++ len = data_per_desc % 4; ++ if (len) ++ data_per_desc += 4 - len; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ dma_desc++; ++ ++ /** Buffer 1 descriptors setup */ ++ sts.b_iso_out.ioc = 0; ++ dma_ad = dwc_ep->dma_addr1; ++ ++ offset = 0; ++ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; ++ i += dwc_ep->pkt_per_frm) { ++ for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { ++ uint32_t len = (j + 1) * dwc_ep->maxpacket; ++ if (len > dwc_ep->data_per_frame) ++ data_per_desc = ++ dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket; ++ else ++ data_per_desc = dwc_ep->maxpacket; ++ len = data_per_desc % 4; ++ if (len) ++ data_per_desc += 4 - len; ++ ++ data_per_desc = ++ sts.b_iso_out.rxbytes = data_per_desc; ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ offset += data_per_desc; ++ dma_desc++; ++ dma_ad += data_per_desc; ++ } ++ } ++ for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ offset += data_per_desc; ++ dma_desc++; ++ dma_ad += data_per_desc; ++ } ++ ++ sts.b_iso_out.ioc = 1; ++ sts.b_iso_out.l = 1; ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ dwc_ep->next_frame = 0; ++ ++ /** Write dma_ad into DOEPDMA register */ ++ DWC_WRITE_REG32(&(out_regs->doepdma), ++ (uint32_t) dwc_ep->iso_dma_desc_addr); ++ ++ } ++ /** ISO IN EP */ ++ else { ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ dwc_otg_dev_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr; ++ dma_addr_t dma_ad; ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[dwc_ep->num]; ++ unsigned int frmnumber; ++ fifosize_data_t txfifosize, rxfifosize; ++ ++ txfifosize.d32 = ++ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[dwc_ep->num]-> ++ dtxfsts); ++ rxfifosize.d32 = ++ DWC_READ_REG32(&core_if->core_global_regs->grxfsiz); ++ ++ addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl; ++ ++ dma_ad = dwc_ep->dma_addr0; ++ ++ dsts.d32 = ++ DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ sts.b_iso_in.bs = BS_HOST_READY; ++ sts.b_iso_in.txsts = 0; ++ sts.b_iso_in.sp = ++ (dwc_ep->data_per_frame % dwc_ep->maxpacket) ? 1 : 0; ++ sts.b_iso_in.ioc = 0; ++ sts.b_iso_in.pid = dwc_ep->pkt_per_frm; ++ ++ frmnumber = dwc_ep->next_frame; ++ ++ sts.b_iso_in.framenum = frmnumber; ++ sts.b_iso_in.txbytes = dwc_ep->data_per_frame; ++ sts.b_iso_in.l = 0; ++ ++ /** Buffer 0 descriptors setup */ ++ for (i = 0; i < dwc_ep->desc_cnt - 1; i++) { ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ dma_desc++; ++ ++ dma_ad += dwc_ep->data_per_frame; ++ sts.b_iso_in.framenum += dwc_ep->bInterval; ++ } ++ ++ sts.b_iso_in.ioc = 1; ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++dma_desc; ++ ++ /** Buffer 1 descriptors setup */ ++ sts.b_iso_in.ioc = 0; ++ dma_ad = dwc_ep->dma_addr1; ++ ++ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; ++ i += dwc_ep->pkt_per_frm) { ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ dma_desc++; ++ ++ dma_ad += dwc_ep->data_per_frame; ++ sts.b_iso_in.framenum += dwc_ep->bInterval; ++ ++ sts.b_iso_in.ioc = 0; ++ } ++ sts.b_iso_in.ioc = 1; ++ sts.b_iso_in.l = 1; ++ ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ dwc_ep->next_frame = sts.b_iso_in.framenum + dwc_ep->bInterval; ++ ++ /** Write dma_ad into diepdma register */ ++ DWC_WRITE_REG32(&(in_regs->diepdma), ++ (uint32_t) dwc_ep->iso_dma_desc_addr); ++ } ++ /** Enable endpoint, clear nak */ ++ depctl.d32 = 0; ++ depctl.b.epena = 1; ++ depctl.b.usbactep = 1; ++ depctl.b.cnak = 1; ++ ++ DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32); ++ depctl.d32 = DWC_READ_REG32(addr); ++} ++ ++/** ++ * This function initializes a descriptor chain for Isochronous transfer ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * ep) ++{ ++ depctl_data_t depctl = {.d32 = 0 }; ++ volatile uint32_t *addr; ++ ++ if (ep->is_in) { ++ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; ++ } else { ++ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; ++ } ++ ++ if (core_if->dma_enable == 0 || core_if->dma_desc_enable != 0) { ++ return; ++ } else { ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ ++ ep->xfer_len = ++ ep->data_per_frame * ep->buf_proc_intrvl / ep->bInterval; ++ ep->pkt_cnt = ++ (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket; ++ ep->xfer_count = 0; ++ ep->xfer_buff = ++ (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0; ++ ep->dma_addr = ++ (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0; ++ ++ if (ep->is_in) { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.mc = ep->pkt_per_frm; ++ deptsiz.b.xfersize = ep->xfer_len; ++ deptsiz.b.pktcnt = ++ (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket; ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]-> ++ dieptsiz, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ DWC_WRITE_REG32(& ++ (core_if->dev_if->in_ep_regs[ep->num]-> ++ diepdma), (uint32_t) ep->dma_addr); ++ ++ } else { ++ deptsiz.b.pktcnt = ++ (ep->xfer_len + (ep->maxpacket - 1)) / ++ ep->maxpacket; ++ deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket; ++ ++ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]-> ++ doeptsiz, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ DWC_WRITE_REG32(& ++ (core_if->dev_if->out_ep_regs[ep->num]-> ++ doepdma), (uint32_t) ep->dma_addr); ++ ++ } ++ /** Enable endpoint, clear nak */ ++ depctl.d32 = 0; ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ ++ DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32); ++ } ++} ++ ++/** ++ * This function does the setup for a data transfer for an EP and ++ * starts the transfer. For an IN transfer, the packets will be ++ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers, ++ * the packets are unloaded from the Rx FIFO in the ISR. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++ ++static void dwc_otg_iso_ep_start_transfer(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * ep) ++{ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable) { ++ if (ep->is_in) { ++ ep->desc_cnt = ep->pkt_cnt / ep->pkt_per_frm; ++ } else { ++ ep->desc_cnt = ep->pkt_cnt; ++ } ++ dwc_otg_iso_ep_start_ddma_transfer(core_if, ep); ++ } else { ++ if (core_if->pti_enh_enable) { ++ dwc_otg_iso_ep_start_buf_transfer(core_if, ep); ++ } else { ++ ep->cur_pkt_addr = ++ (ep->proc_buf_num) ? ep->xfer_buff1 : ep-> ++ xfer_buff0; ++ ep->cur_pkt_dma_addr = ++ (ep->proc_buf_num) ? ep->dma_addr1 : ep-> ++ dma_addr0; ++ dwc_otg_iso_ep_start_frm_transfer(core_if, ep); ++ } ++ } ++ } else { ++ ep->cur_pkt_addr = ++ (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0; ++ ep->cur_pkt_dma_addr = ++ (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0; ++ dwc_otg_iso_ep_start_frm_transfer(core_if, ep); ++ } ++} ++ ++/** ++ * This function stops transfer for an EP and ++ * resets the ep's variables. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++ ++void dwc_otg_iso_ep_stop_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl = {.d32 = 0 }; ++ volatile uint32_t *addr; ++ ++ if (ep->is_in == 1) { ++ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; ++ } else { ++ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; ++ } ++ ++ /* disable the ep */ ++ depctl.d32 = DWC_READ_REG32(addr); ++ ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ ++ DWC_WRITE_REG32(addr, depctl.d32); ++ ++ if (core_if->dma_desc_enable && ++ ep->iso_desc_addr && ep->iso_dma_desc_addr) { ++ dwc_otg_ep_free_desc_chain(ep->iso_desc_addr, ++ ep->iso_dma_desc_addr, ++ ep->desc_cnt * 2); ++ } ++ ++ /* reset varibales */ ++ ep->dma_addr0 = 0; ++ ep->dma_addr1 = 0; ++ ep->xfer_buff0 = 0; ++ ep->xfer_buff1 = 0; ++ ep->data_per_frame = 0; ++ ep->data_pattern_frame = 0; ++ ep->sync_frame = 0; ++ ep->buf_proc_intrvl = 0; ++ ep->bInterval = 0; ++ ep->proc_buf_num = 0; ++ ep->pkt_per_frm = 0; ++ ep->pkt_per_frm = 0; ++ ep->desc_cnt = 0; ++ ep->iso_desc_addr = 0; ++ ep->iso_dma_desc_addr = 0; ++} ++ ++int dwc_otg_pcd_iso_ep_start(dwc_otg_pcd_t * pcd, void *ep_handle, ++ uint8_t * buf0, uint8_t * buf1, dwc_dma_t dma0, ++ dwc_dma_t dma1, int sync_frame, int dp_frame, ++ int data_per_frame, int start_frame, ++ int buf_proc_intrvl, void *req_handle, ++ int atomic_alloc) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ dwc_irqflags_t flags = 0; ++ dwc_ep_t *dwc_ep; ++ int32_t frm_data; ++ dsts_data_t dsts; ++ dwc_otg_core_if_t *core_if; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ ++ if (!ep || !ep->desc || ep->dwc_ep.num == 0) { ++ DWC_WARN("bad ep\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ core_if = GET_CORE_IF(pcd); ++ dwc_ep = &ep->dwc_ep; ++ ++ if (ep->iso_req_handle) { ++ DWC_WARN("ISO request in progress\n"); ++ } ++ ++ dwc_ep->dma_addr0 = dma0; ++ dwc_ep->dma_addr1 = dma1; ++ ++ dwc_ep->xfer_buff0 = buf0; ++ dwc_ep->xfer_buff1 = buf1; ++ ++ dwc_ep->data_per_frame = data_per_frame; ++ ++ /** @todo - pattern data support is to be implemented in the future */ ++ dwc_ep->data_pattern_frame = dp_frame; ++ dwc_ep->sync_frame = sync_frame; ++ ++ dwc_ep->buf_proc_intrvl = buf_proc_intrvl; ++ ++ dwc_ep->bInterval = 1 << (ep->desc->bInterval - 1); ++ ++ dwc_ep->proc_buf_num = 0; ++ ++ dwc_ep->pkt_per_frm = 0; ++ frm_data = ep->dwc_ep.data_per_frame; ++ while (frm_data > 0) { ++ dwc_ep->pkt_per_frm++; ++ frm_data -= ep->dwc_ep.maxpacket; ++ } ++ ++ dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ if (start_frame == -1) { ++ dwc_ep->next_frame = dsts.b.soffn + 1; ++ if (dwc_ep->bInterval != 1) { ++ dwc_ep->next_frame = ++ dwc_ep->next_frame + (dwc_ep->bInterval - 1 - ++ dwc_ep->next_frame % ++ dwc_ep->bInterval); ++ } ++ } else { ++ dwc_ep->next_frame = start_frame; ++ } ++ ++ if (!core_if->pti_enh_enable) { ++ dwc_ep->pkt_cnt = ++ dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / ++ dwc_ep->bInterval; ++ } else { ++ dwc_ep->pkt_cnt = ++ (dwc_ep->data_per_frame * ++ (dwc_ep->buf_proc_intrvl / dwc_ep->bInterval) ++ - 1 + dwc_ep->maxpacket) / dwc_ep->maxpacket; ++ } ++ ++ if (core_if->dma_desc_enable) { ++ dwc_ep->desc_cnt = ++ dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / ++ dwc_ep->bInterval; ++ } ++ ++ if (atomic_alloc) { ++ dwc_ep->pkt_info = ++ DWC_ALLOC_ATOMIC(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt); ++ } else { ++ dwc_ep->pkt_info = ++ DWC_ALLOC(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt); ++ } ++ if (!dwc_ep->pkt_info) { ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ return -DWC_E_NO_MEMORY; ++ } ++ if (core_if->pti_enh_enable) { ++ dwc_memset(dwc_ep->pkt_info, 0, ++ sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt); ++ } ++ ++ dwc_ep->cur_pkt = 0; ++ ep->iso_req_handle = req_handle; ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ dwc_otg_iso_ep_start_transfer(core_if, dwc_ep); ++ return 0; ++} ++ ++int dwc_otg_pcd_iso_ep_stop(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle) ++{ ++ dwc_irqflags_t flags = 0; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ if (!ep || !ep->desc || ep->dwc_ep.num == 0) { ++ DWC_WARN("bad ep\n"); ++ return -DWC_E_INVALID; ++ } ++ dwc_ep = &ep->dwc_ep; ++ ++ dwc_otg_iso_ep_stop_transfer(GET_CORE_IF(pcd), dwc_ep); ++ ++ DWC_FREE(dwc_ep->pkt_info); ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ if (ep->iso_req_handle != req_handle) { ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++ ep->iso_req_handle = 0; ++ return 0; ++} ++ ++/** ++ * This function is used for perodical data exchnage between PCD and gadget drivers. ++ * for Isochronous EPs ++ * ++ * - Every time a sync period completes this function is called to ++ * perform data exchange between PCD and gadget ++ */ ++void dwc_otg_iso_buffer_done(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep, ++ void *req_handle) ++{ ++ int i; ++ dwc_ep_t *dwc_ep; ++ ++ dwc_ep = &ep->dwc_ep; ++ ++ DWC_SPINUNLOCK(ep->pcd->lock); ++ pcd->fops->isoc_complete(pcd, ep->priv, ep->iso_req_handle, ++ dwc_ep->proc_buf_num ^ 0x1); ++ DWC_SPINLOCK(ep->pcd->lock); ++ ++ for (i = 0; i < dwc_ep->pkt_cnt; ++i) { ++ dwc_ep->pkt_info[i].status = 0; ++ dwc_ep->pkt_info[i].offset = 0; ++ dwc_ep->pkt_info[i].length = 0; ++ } ++} ++ ++int dwc_otg_pcd_get_iso_packet_count(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *iso_req_handle) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ if (!ep->desc || ep->dwc_ep.num == 0) { ++ DWC_WARN("bad ep\n"); ++ return -DWC_E_INVALID; ++ } ++ dwc_ep = &ep->dwc_ep; ++ ++ return dwc_ep->pkt_cnt; ++} ++ ++void dwc_otg_pcd_get_iso_packet_params(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *iso_req_handle, int packet, ++ int *status, int *actual, int *offset) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ if (!ep) ++ DWC_WARN("bad ep\n"); ++ ++ dwc_ep = &ep->dwc_ep; ++ ++ *status = dwc_ep->pkt_info[packet].status; ++ *actual = dwc_ep->pkt_info[packet].length; ++ *offset = dwc_ep->pkt_info[packet].offset; ++} ++ ++#endif /* DWC_EN_ISOC */ ++ ++static void dwc_otg_pcd_init_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * pcd_ep, ++ uint32_t is_in, uint32_t ep_num) ++{ ++ /* Init EP structure */ ++ pcd_ep->desc = 0; ++ pcd_ep->pcd = pcd; ++ pcd_ep->stopped = 1; ++ pcd_ep->queue_sof = 0; ++ ++ /* Init DWC ep structure */ ++ pcd_ep->dwc_ep.is_in = is_in; ++ pcd_ep->dwc_ep.num = ep_num; ++ pcd_ep->dwc_ep.active = 0; ++ pcd_ep->dwc_ep.tx_fifo_num = 0; ++ /* Control until ep is actvated */ ++ pcd_ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL; ++ pcd_ep->dwc_ep.maxpacket = MAX_PACKET_SIZE; ++ pcd_ep->dwc_ep.dma_addr = 0; ++ pcd_ep->dwc_ep.start_xfer_buff = 0; ++ pcd_ep->dwc_ep.xfer_buff = 0; ++ pcd_ep->dwc_ep.xfer_len = 0; ++ pcd_ep->dwc_ep.xfer_count = 0; ++ pcd_ep->dwc_ep.sent_zlp = 0; ++ pcd_ep->dwc_ep.total_len = 0; ++ pcd_ep->dwc_ep.desc_addr = 0; ++ pcd_ep->dwc_ep.dma_desc_addr = 0; ++ DWC_CIRCLEQ_INIT(&pcd_ep->queue); ++} ++ ++/** ++ * Initialize ep's ++ */ ++static void dwc_otg_pcd_reinit(dwc_otg_pcd_t * pcd) ++{ ++ int i; ++ uint32_t hwcfg1; ++ dwc_otg_pcd_ep_t *ep; ++ int in_ep_cntr, out_ep_cntr; ++ uint32_t num_in_eps = (GET_CORE_IF(pcd))->dev_if->num_in_eps; ++ uint32_t num_out_eps = (GET_CORE_IF(pcd))->dev_if->num_out_eps; ++ ++ /** ++ * Initialize the EP0 structure. ++ */ ++ ep = &pcd->ep0; ++ dwc_otg_pcd_init_ep(pcd, ep, 0, 0); ++ ++ in_ep_cntr = 0; ++ hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 3; ++ for (i = 1; in_ep_cntr < num_in_eps; i++) { ++ if ((hwcfg1 & 0x1) == 0) { ++ dwc_otg_pcd_ep_t *ep = &pcd->in_ep[in_ep_cntr]; ++ in_ep_cntr++; ++ /** ++ * @todo NGS: Add direction to EP, based on contents ++ * of HWCFG1. Need a copy of HWCFG1 in pcd structure? ++ * sprintf(";r ++ */ ++ dwc_otg_pcd_init_ep(pcd, ep, 1 /* IN */ , i); ++ ++ DWC_CIRCLEQ_INIT(&ep->queue); ++ } ++ hwcfg1 >>= 2; ++ } ++ ++ out_ep_cntr = 0; ++ hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 2; ++ for (i = 1; out_ep_cntr < num_out_eps; i++) { ++ if ((hwcfg1 & 0x1) == 0) { ++ dwc_otg_pcd_ep_t *ep = &pcd->out_ep[out_ep_cntr]; ++ out_ep_cntr++; ++ /** ++ * @todo NGS: Add direction to EP, based on contents ++ * of HWCFG1. Need a copy of HWCFG1 in pcd structure? ++ * sprintf(";r ++ */ ++ dwc_otg_pcd_init_ep(pcd, ep, 0 /* OUT */ , i); ++ DWC_CIRCLEQ_INIT(&ep->queue); ++ } ++ hwcfg1 >>= 2; ++ } ++ ++ pcd->ep0state = EP0_DISCONNECT; ++ pcd->ep0.dwc_ep.maxpacket = MAX_EP0_SIZE; ++ pcd->ep0.dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL; ++} ++ ++/** ++ * This function is called when the SRP timer expires. The SRP should ++ * complete within 6 seconds. ++ */ ++static void srp_timeout(void *ptr) ++{ ++ gotgctl_data_t gotgctl; ++ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr; ++ volatile uint32_t *addr = &core_if->core_global_regs->gotgctl; ++ ++ gotgctl.d32 = DWC_READ_REG32(addr); ++ ++ core_if->srp_timer_started = 0; ++ ++ if (core_if->adp_enable) { ++ if (gotgctl.b.bsesvld == 0) { ++ gpwrdn_data_t gpwrdn = {.d32 = 0 }; ++ DWC_PRINTF("SRP Timeout BSESSVLD = 0\n"); ++ /* Power off the core */ ++ if (core_if->power_down == 2) { ++ gpwrdn.b.pwrdnswtch = 1; ++ DWC_MODIFY_REG32(&core_if-> ++ core_global_regs->gpwrdn, ++ gpwrdn.d32, 0); ++ } ++ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuintsel = 1; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, ++ gpwrdn.d32); ++ dwc_otg_adp_probe_start(core_if); ++ } else { ++ DWC_PRINTF("SRP Timeout BSESSVLD = 1\n"); ++ core_if->op_state = B_PERIPHERAL; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_pcd_start(core_if); ++ } ++ } ++ ++ if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) && ++ (core_if->core_params->i2c_enable)) { ++ DWC_PRINTF("SRP Timeout\n"); ++ ++ if ((core_if->srp_success) && (gotgctl.b.bsesvld)) { ++ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { ++ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); ++ } ++ ++ /* Clear Session Request */ ++ gotgctl.d32 = 0; ++ gotgctl.b.sesreq = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gotgctl, ++ gotgctl.d32, 0); ++ ++ core_if->srp_success = 0; ++ } else { ++ __DWC_ERROR("Device not connected/responding\n"); ++ gotgctl.b.sesreq = 0; ++ DWC_WRITE_REG32(addr, gotgctl.d32); ++ } ++ } else if (gotgctl.b.sesreq) { ++ DWC_PRINTF("SRP Timeout\n"); ++ ++ __DWC_ERROR("Device not connected/responding\n"); ++ gotgctl.b.sesreq = 0; ++ DWC_WRITE_REG32(addr, gotgctl.d32); ++ } else { ++ DWC_PRINTF(" SRP GOTGCTL=%0x\n", gotgctl.d32); ++ } ++} ++ ++/** ++ * Tasklet ++ * ++ */ ++extern void start_next_request(dwc_otg_pcd_ep_t * ep); ++ ++static void start_xfer_tasklet_func(void *data) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) data; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ ++ int i; ++ depctl_data_t diepctl; ++ ++ DWC_DEBUGPL(DBG_PCDV, "Start xfer tasklet\n"); ++ ++ diepctl.d32 = DWC_READ_REG32(&core_if->dev_if->in_ep_regs[0]->diepctl); ++ ++ if (pcd->ep0.queue_sof) { ++ pcd->ep0.queue_sof = 0; ++ start_next_request(&pcd->ep0); ++ // break; ++ } ++ ++ for (i = 0; i < core_if->dev_if->num_in_eps; i++) { ++ depctl_data_t diepctl; ++ diepctl.d32 = ++ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl); ++ ++ if (pcd->in_ep[i].queue_sof) { ++ pcd->in_ep[i].queue_sof = 0; ++ start_next_request(&pcd->in_ep[i]); ++ // break; ++ } ++ } ++ ++ return; ++} ++ ++/** ++ * This function initialized the PCD portion of the driver. ++ * ++ */ ++dwc_otg_pcd_t *dwc_otg_pcd_init(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_pcd_t *pcd = NULL; ++ dwc_otg_dev_if_t *dev_if; ++ int i; ++ ++ /* ++ * Allocate PCD structure ++ */ ++ pcd = DWC_ALLOC(sizeof(dwc_otg_pcd_t)); ++ ++ if (pcd == NULL) { ++ return NULL; ++ } ++ ++ pcd->lock = DWC_SPINLOCK_ALLOC(); ++ if (!pcd->lock) { ++ DWC_ERROR("Could not allocate lock for pcd"); ++ DWC_FREE(pcd); ++ return NULL; ++ } ++ /* Set core_if's lock pointer to hcd->lock */ ++ core_if->lock = pcd->lock; ++ pcd->core_if = core_if; ++ ++ dev_if = core_if->dev_if; ++ dev_if->isoc_ep = NULL; ++ ++ if (core_if->hwcfg4.b.ded_fifo_en) { ++ DWC_PRINTF("Dedicated Tx FIFOs mode\n"); ++ } else { ++ DWC_PRINTF("Shared Tx FIFO mode\n"); ++ } ++ ++ /* ++ * Initialized the Core for Device mode here if there is nod ADP support. ++ * Otherwise it will be done later in dwc_otg_adp_start routine. ++ */ ++ if (dwc_otg_is_device_mode(core_if) /*&& !core_if->adp_enable */ ) { ++ dwc_otg_core_dev_init(core_if); ++ } ++ ++ /* ++ * Register the PCD Callbacks. ++ */ ++ dwc_otg_cil_register_pcd_callbacks(core_if, &pcd_callbacks, pcd); ++ ++ /* ++ * Initialize the DMA buffer for SETUP packets ++ */ ++ if (GET_CORE_IF(pcd)->dma_enable) { ++ pcd->setup_pkt = ++ DWC_DMA_ALLOC(sizeof(*pcd->setup_pkt) * 5, ++ &pcd->setup_pkt_dma_handle); ++ if (pcd->setup_pkt == NULL) { ++ DWC_FREE(pcd); ++ return NULL; ++ } ++ ++ pcd->status_buf = ++ DWC_DMA_ALLOC(sizeof(uint16_t), ++ &pcd->status_buf_dma_handle); ++ if (pcd->status_buf == NULL) { ++ DWC_DMA_FREE(sizeof(*pcd->setup_pkt) * 5, ++ pcd->setup_pkt, pcd->setup_pkt_dma_handle); ++ DWC_FREE(pcd); ++ return NULL; ++ } ++ ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++ dev_if->setup_desc_addr[0] = ++ dwc_otg_ep_alloc_desc_chain ++ (&dev_if->dma_setup_desc_addr[0], 1); ++ dev_if->setup_desc_addr[1] = ++ dwc_otg_ep_alloc_desc_chain ++ (&dev_if->dma_setup_desc_addr[1], 1); ++ dev_if->in_desc_addr = ++ dwc_otg_ep_alloc_desc_chain ++ (&dev_if->dma_in_desc_addr, 1); ++ dev_if->out_desc_addr = ++ dwc_otg_ep_alloc_desc_chain ++ (&dev_if->dma_out_desc_addr, 1); ++ pcd->data_terminated = 0; ++ ++ if (dev_if->setup_desc_addr[0] == 0 ++ || dev_if->setup_desc_addr[1] == 0 ++ || dev_if->in_desc_addr == 0 ++ || dev_if->out_desc_addr == 0) { ++ ++ if (dev_if->out_desc_addr) ++ dwc_otg_ep_free_desc_chain ++ (dev_if->out_desc_addr, ++ dev_if->dma_out_desc_addr, 1); ++ if (dev_if->in_desc_addr) ++ dwc_otg_ep_free_desc_chain ++ (dev_if->in_desc_addr, ++ dev_if->dma_in_desc_addr, 1); ++ if (dev_if->setup_desc_addr[1]) ++ dwc_otg_ep_free_desc_chain ++ (dev_if->setup_desc_addr[1], ++ dev_if->dma_setup_desc_addr[1], 1); ++ if (dev_if->setup_desc_addr[0]) ++ dwc_otg_ep_free_desc_chain ++ (dev_if->setup_desc_addr[0], ++ dev_if->dma_setup_desc_addr[0], 1); ++ ++ DWC_DMA_FREE(sizeof(*pcd->setup_pkt) * 5, ++ pcd->setup_pkt, ++ pcd->setup_pkt_dma_handle); ++ DWC_DMA_FREE(sizeof(*pcd->status_buf), ++ pcd->status_buf, ++ pcd->status_buf_dma_handle); ++ ++ DWC_FREE(pcd); ++ ++ return NULL; ++ } ++ } ++ } else { ++ pcd->setup_pkt = DWC_ALLOC(sizeof(*pcd->setup_pkt) * 5); ++ if (pcd->setup_pkt == NULL) { ++ DWC_FREE(pcd); ++ return NULL; ++ } ++ ++ pcd->status_buf = DWC_ALLOC(sizeof(uint16_t)); ++ if (pcd->status_buf == NULL) { ++ DWC_FREE(pcd->setup_pkt); ++ DWC_FREE(pcd); ++ return NULL; ++ } ++ } ++ ++ dwc_otg_pcd_reinit(pcd); ++ ++ /* Allocate the cfi object for the PCD */ ++#ifdef DWC_UTE_CFI ++ pcd->cfi = DWC_ALLOC(sizeof(cfiobject_t)); ++ if (NULL == pcd->cfi) ++ goto fail; ++ if (init_cfi(pcd->cfi)) { ++ CFI_INFO("%s: Failed to init the CFI object\n", __func__); ++ goto fail; ++ } ++#endif ++ ++ /* Initialize tasklets */ ++ pcd->start_xfer_tasklet = DWC_TASK_ALLOC("xfer_tasklet", ++ start_xfer_tasklet_func, pcd); ++ pcd->test_mode_tasklet = DWC_TASK_ALLOC("test_mode_tasklet", ++ do_test_mode, pcd); ++ ++ /* Initialize SRP timer */ ++ core_if->srp_timer = DWC_TIMER_ALLOC("SRP TIMER", srp_timeout, core_if); ++ ++ if (core_if->core_params->dev_out_nak) { ++ /** ++ * Initialize xfer timeout timer. Implemented for ++ * 2.93a feature "Device DDMA OUT NAK Enhancement" ++ */ ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ pcd->core_if->ep_xfer_timer[i] = ++ DWC_TIMER_ALLOC("ep timer", ep_xfer_timeout, ++ &pcd->core_if->ep_xfer_info[i]); ++ } ++ } ++ ++ return pcd; ++#ifdef DWC_UTE_CFI ++fail: ++#endif ++ if (pcd->setup_pkt) ++ DWC_FREE(pcd->setup_pkt); ++ if (pcd->status_buf) ++ DWC_FREE(pcd->status_buf); ++#ifdef DWC_UTE_CFI ++ if (pcd->cfi) ++ DWC_FREE(pcd->cfi); ++#endif ++ if (pcd) ++ DWC_FREE(pcd); ++ return NULL; ++ ++} ++ ++/** ++ * Remove PCD specific data ++ */ ++void dwc_otg_pcd_remove(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if; ++ int i; ++ if (pcd->core_if->core_params->dev_out_nak) { ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[i]); ++ pcd->core_if->ep_xfer_info[i].state = 0; ++ } ++ } ++ ++ if (GET_CORE_IF(pcd)->dma_enable) { ++ DWC_DMA_FREE(sizeof(*pcd->setup_pkt) * 5, pcd->setup_pkt, ++ pcd->setup_pkt_dma_handle); ++ DWC_DMA_FREE(sizeof(uint16_t), pcd->status_buf, ++ pcd->status_buf_dma_handle); ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++ dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[0], ++ dev_if->dma_setup_desc_addr ++ [0], 1); ++ dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[1], ++ dev_if->dma_setup_desc_addr ++ [1], 1); ++ dwc_otg_ep_free_desc_chain(dev_if->in_desc_addr, ++ dev_if->dma_in_desc_addr, 1); ++ dwc_otg_ep_free_desc_chain(dev_if->out_desc_addr, ++ dev_if->dma_out_desc_addr, ++ 1); ++ } ++ } else { ++ DWC_FREE(pcd->setup_pkt); ++ DWC_FREE(pcd->status_buf); ++ } ++ DWC_SPINLOCK_FREE(pcd->lock); ++ /* Set core_if's lock pointer to NULL */ ++ pcd->core_if->lock = NULL; ++ ++ DWC_TASK_FREE(pcd->start_xfer_tasklet); ++ DWC_TASK_FREE(pcd->test_mode_tasklet); ++ if (pcd->core_if->core_params->dev_out_nak) { ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ if (pcd->core_if->ep_xfer_timer[i]) { ++ DWC_TIMER_FREE(pcd->core_if->ep_xfer_timer[i]); ++ } ++ } ++ } ++ ++/* Release the CFI object's dynamic memory */ ++#ifdef DWC_UTE_CFI ++ if (pcd->cfi->ops.release) { ++ pcd->cfi->ops.release(pcd->cfi); ++ } ++#endif ++ ++ DWC_FREE(pcd); ++} ++ ++/** ++ * Returns whether registered pcd is dual speed or not ++ */ ++uint32_t dwc_otg_pcd_is_dualspeed(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ ++ if ((core_if->core_params->speed == DWC_SPEED_PARAM_FULL) || ++ ((core_if->hwcfg2.b.hs_phy_type == 2) && ++ (core_if->hwcfg2.b.fs_phy_type == 1) && ++ (core_if->core_params->ulpi_fs_ls))) { ++ return 0; ++ } ++ ++ return 1; ++} ++ ++/** ++ * Returns whether registered pcd is OTG capable or not ++ */ ++uint32_t dwc_otg_pcd_is_otg(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ gusbcfg_data_t usbcfg = {.d32 = 0 }; ++ uint32_t retval = 0; ++ ++ usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0) ++ if (!usbcfg.b.srpcap || !usbcfg.b.hnpcap) ++ return 0; ++ else ++ return 1; ++# else ++ if (!usbcfg.b.srpcap) ++ return 0; ++ else ++ retval |= 1; ++ ++ if (usbcfg.b.hnpcap) ++ retval |= 2; ++ ++ if (core_if->adp_enable) ++ retval |= 4; ++#endif ++ ++ return retval; ++} ++ ++/** ++ * This function assigns periodic Tx FIFO to an periodic EP ++ * in shared Tx FIFO mode ++ */ ++static uint32_t assign_tx_fifo(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t TxMsk = 1; ++ int i; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; ++i) { ++ if ((TxMsk & core_if->tx_msk) == 0) { ++ core_if->tx_msk |= TxMsk; ++ return i + 1; ++ } ++ TxMsk <<= 1; ++ } ++ return 0; ++} ++ ++/** ++ * This function assigns periodic Tx FIFO to an periodic EP ++ * in shared Tx FIFO mode ++ */ ++static uint32_t assign_perio_tx_fifo(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t PerTxMsk = 1; ++ int i; ++ for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; ++i) { ++ if ((PerTxMsk & core_if->p_tx_msk) == 0) { ++ core_if->p_tx_msk |= PerTxMsk; ++ return i + 1; ++ } ++ PerTxMsk <<= 1; ++ } ++ return 0; ++} ++ ++/** ++ * This function releases periodic Tx FIFO ++ * in shared Tx FIFO mode ++ */ ++static void release_perio_tx_fifo(dwc_otg_core_if_t * core_if, ++ uint32_t fifo_num) ++{ ++ core_if->p_tx_msk = ++ (core_if->p_tx_msk & (1 << (fifo_num - 1))) ^ core_if->p_tx_msk; ++} ++ ++/** ++ * This function releases periodic Tx FIFO ++ * in shared Tx FIFO mode ++ */ ++static void release_tx_fifo(dwc_otg_core_if_t * core_if, uint32_t fifo_num) ++{ ++ core_if->tx_msk = ++ (core_if->tx_msk & (1 << (fifo_num - 1))) ^ core_if->tx_msk; ++} ++ ++/** ++ * This function is being called from gadget ++ * to enable PCD endpoint. ++ */ ++int dwc_otg_pcd_ep_enable(dwc_otg_pcd_t * pcd, ++ const uint8_t * ep_desc, void *usb_ep) ++{ ++ int num, dir; ++ dwc_otg_pcd_ep_t *ep = NULL; ++ const usb_endpoint_descriptor_t *desc; ++ dwc_irqflags_t flags; ++ fifosize_data_t dptxfsiz = {.d32 = 0 }; ++ gdfifocfg_data_t gdfifocfg = {.d32 = 0 }; ++ gdfifocfg_data_t gdfifocfgbase = {.d32 = 0 }; ++ int retval = 0; ++ int i, epcount; ++ ++ desc = (const usb_endpoint_descriptor_t *)ep_desc; ++ ++ if (!desc) { ++ pcd->ep0.priv = usb_ep; ++ retval = -DWC_E_INVALID; ++ goto out; ++ } ++ ++ num = UE_GET_ADDR(desc->bEndpointAddress); ++ dir = UE_GET_DIR(desc->bEndpointAddress); ++ ++ if (dir == UE_DIR_IN) { ++ epcount = pcd->core_if->dev_if->num_in_eps; ++ for (i = 0; i < epcount; i++) { ++ if (num == pcd->in_ep[i].dwc_ep.num) { ++ ep = &pcd->in_ep[i]; ++ break; ++ } ++ } ++ } else { ++ epcount = pcd->core_if->dev_if->num_out_eps; ++ for (i = 0; i < epcount; i++) { ++ if (num == pcd->out_ep[i].dwc_ep.num) { ++ ep = &pcd->out_ep[i]; ++ break; ++ } ++ } ++ } ++ ++ if (!ep) { ++ DWC_WARN("bad address\n"); ++ retval = -DWC_E_INVALID; ++ goto out; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ ++ ep->desc = desc; ++ ep->priv = usb_ep; ++ ++ /* ++ * Activate the EP ++ */ ++ ep->stopped = 0; ++ ++ ep->dwc_ep.is_in = (dir == UE_DIR_IN); ++ ep->dwc_ep.maxpacket = UGETW(desc->wMaxPacketSize); ++ ++ ep->dwc_ep.type = desc->bmAttributes & UE_XFERTYPE; ++ if (UE_BULK == ep->dwc_ep.type) ++ bulk_num = ep->dwc_ep.num; ++ ++ if (ep->dwc_ep.is_in) { ++ if (!GET_CORE_IF(pcd)->en_multiple_tx_fifo) { ++ ep->dwc_ep.tx_fifo_num = 0; ++ ++ if (ep->dwc_ep.type == UE_ISOCHRONOUS) { ++ /* ++ * if ISOC EP then assign a Periodic Tx FIFO. ++ */ ++ ep->dwc_ep.tx_fifo_num = ++ assign_perio_tx_fifo(GET_CORE_IF(pcd)); ++ } ++ } else { ++ /* ++ * if Dedicated FIFOs mode is on then assign a Tx FIFO. ++ */ ++ ep->dwc_ep.tx_fifo_num = ++ assign_tx_fifo(GET_CORE_IF(pcd)); ++ } ++ ++ /* Calculating EP info controller base address */ ++ if (ep->dwc_ep.tx_fifo_num ++ && GET_CORE_IF(pcd)->en_multiple_tx_fifo) { ++ gdfifocfg.d32 = ++ DWC_READ_REG32(&GET_CORE_IF(pcd)-> ++ core_global_regs->gdfifocfg); ++ gdfifocfgbase.d32 = gdfifocfg.d32 >> 16; ++ dptxfsiz.d32 = ++ (DWC_READ_REG32 ++ (&GET_CORE_IF(pcd)->core_global_regs-> ++ dtxfsiz[ep->dwc_ep.tx_fifo_num - 1]) >> 16); ++ gdfifocfg.b.epinfobase = ++ gdfifocfgbase.d32 + dptxfsiz.d32; ++ if (GET_CORE_IF(pcd)->snpsid <= OTG_CORE_REV_2_94a) { ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)-> ++ core_global_regs->gdfifocfg, ++ gdfifocfg.d32); ++ } ++ } ++ } ++ /* Set initial data PID. */ ++ if (ep->dwc_ep.type == UE_BULK) { ++ ep->dwc_ep.data_pid_start = 0; ++ } ++ ++ /* Alloc DMA Descriptors */ ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++#ifndef DWC_UTE_PER_IO ++ if (ep->dwc_ep.type != UE_ISOCHRONOUS) { ++#endif ++ ep->dwc_ep.desc_addr = ++ dwc_otg_ep_alloc_desc_chain(&ep-> ++ dwc_ep.dma_desc_addr, ++ MAX_DMA_DESC_CNT); ++ if (!ep->dwc_ep.desc_addr) { ++ DWC_WARN("%s, can't allocate DMA descriptor\n", ++ __func__); ++ retval = -DWC_E_SHUTDOWN; ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ goto out; ++ } ++#ifndef DWC_UTE_PER_IO ++ } else { ++ ep->dwc_ep.desc_addr = ++ dwc_otg_ep_alloc_desc_chain(&ep-> ++ dwc_ep.dma_desc_addr, ++ MAX_DMA_DESC_CNT/2); ++ ep->dwc_ep.desc_addr1 = ++ dwc_otg_ep_alloc_desc_chain(&ep-> ++ dwc_ep.dma_desc_addr1, ++ MAX_DMA_DESC_CNT/2); ++ if (!ep->dwc_ep.desc_addr || !ep->dwc_ep.desc_addr1) { ++ DWC_WARN("%s, can't allocate DMA descriptor\n", ++ __func__); ++ retval = -DWC_E_SHUTDOWN; ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ goto out; ++ } ++ /* Set initial data PID. */ ++ if (ep->dwc_ep.type == UE_ISOCHRONOUS) { ++ ep->dwc_ep.iso_desc_first = 0; ++ ep->dwc_ep.iso_desc_second = 0; ++ ep->dwc_ep.iso_transfer_started = 0; ++ } ++ } ++#endif ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "Activate %s: type=%d, mps=%d desc=%p\n", ++ (ep->dwc_ep.is_in ? "IN" : "OUT"), ++ ep->dwc_ep.type, ep->dwc_ep.maxpacket, ep->desc); ++#ifdef DWC_UTE_PER_IO ++ ep->dwc_ep.xiso_bInterval = 1 << (ep->desc->bInterval - 1); ++#endif ++ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) { ++ ep->dwc_ep.bInterval = 1 << (ep->desc->bInterval - 1); ++ ep->dwc_ep.frame_num = 0xFFFFFFFF; ++ } ++ ++ dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep); ++ ++#ifdef DWC_UTE_CFI ++ if (pcd->cfi->ops.ep_enable) { ++ pcd->cfi->ops.ep_enable(pcd->cfi, pcd, ep); ++ } ++#endif ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++out: ++ return retval; ++} ++ ++/** ++ * This function is being called from gadget ++ * to disable PCD endpoint. ++ */ ++int dwc_otg_pcd_ep_disable(dwc_otg_pcd_t * pcd, void *ep_handle) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ dwc_irqflags_t flags; ++ dwc_otg_dev_dma_desc_t *desc_addr; ++ dwc_dma_t dma_desc_addr; ++ gdfifocfg_data_t gdfifocfgbase = {.d32 = 0 }; ++ gdfifocfg_data_t gdfifocfg = {.d32 = 0 }; ++ fifosize_data_t dptxfsiz = {.d32 = 0 }; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ ++ if (!ep || !ep->desc) { ++ DWC_DEBUGPL(DBG_PCD, "bad ep address\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ ++ dwc_otg_request_nuke(ep); ++ ++ dwc_otg_ep_deactivate(GET_CORE_IF(pcd), &ep->dwc_ep); ++ if (pcd->core_if->core_params->dev_out_nak) { ++ DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[ep->dwc_ep.num]); ++ pcd->core_if->ep_xfer_info[ep->dwc_ep.num].state = 0; ++ } ++ ep->desc = NULL; ++ ep->stopped = 1; ++ ++ gdfifocfg.d32 = ++ DWC_READ_REG32(&GET_CORE_IF(pcd)->core_global_regs->gdfifocfg); ++ gdfifocfgbase.d32 = gdfifocfg.d32 >> 16; ++ ++ if (ep->dwc_ep.is_in) { ++ if (GET_CORE_IF(pcd)->en_multiple_tx_fifo) { ++ /* Flush the Tx FIFO */ ++ dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), ++ ep->dwc_ep.tx_fifo_num); ++ } ++ release_perio_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num); ++ release_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num); ++ if (GET_CORE_IF(pcd)->en_multiple_tx_fifo) { ++ /* Decreasing EPinfo Base Addr */ ++ dptxfsiz.d32 = ++ (DWC_READ_REG32 ++ (&GET_CORE_IF(pcd)-> ++ core_global_regs->dtxfsiz[ep->dwc_ep.tx_fifo_num-1]) >> 16); ++ gdfifocfg.b.epinfobase = gdfifocfgbase.d32 - dptxfsiz.d32; ++ if (GET_CORE_IF(pcd)->snpsid <= OTG_CORE_REV_2_94a) { ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gdfifocfg, ++ gdfifocfg.d32); ++ } ++ } ++ } ++ ++ /* Free DMA Descriptors */ ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++ if (ep->dwc_ep.type != UE_ISOCHRONOUS) { ++ desc_addr = ep->dwc_ep.desc_addr; ++ dma_desc_addr = ep->dwc_ep.dma_desc_addr; ++ ++ /* Cannot call dma_free_coherent() with IRQs disabled */ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ dwc_otg_ep_free_desc_chain(desc_addr, dma_desc_addr, ++ MAX_DMA_DESC_CNT); ++ ++ } else { ++ desc_addr = ep->dwc_ep.desc_addr; ++ dma_desc_addr = ep->dwc_ep.dma_desc_addr; ++ ++ /* Cannot call dma_free_coherent() with IRQs disabled */ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ dwc_otg_ep_free_desc_chain(desc_addr, dma_desc_addr, ++ MAX_DMA_DESC_CNT/2); ++ desc_addr = ep->dwc_ep.desc_addr1; ++ dma_desc_addr = ep->dwc_ep.dma_desc_addr1; ++ dwc_otg_ep_free_desc_chain(desc_addr, dma_desc_addr, ++ MAX_DMA_DESC_CNT/2); ++ } ++ goto out_unlocked; ++ } ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++out_unlocked: ++ DWC_DEBUGPL(DBG_PCD, "%d %s disabled\n", ep->dwc_ep.num, ++ ep->dwc_ep.is_in ? "IN" : "OUT"); ++ return 0; ++ ++} ++ ++/** ++ * This function initializes dma descriptor chain for ISOC transfers. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++void dwc_otg_pcd_start_iso_ddma(dwc_otg_core_if_t * core_if, dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ dwc_otg_pcd_request_t *req = NULL; ++ dwc_ep_t *dwcep = NULL; ++ uint32_t frame_num = 0; ++ int i = 0; ++ int j; ++ int sync_request = 4; ++ uint16_t nat; ++ depctl_data_t depctl; ++ ++ dwcep = &ep->dwc_ep; ++ dma_desc = dwcep->desc_addr; ++ ++ nat = UGETW(ep->desc->wMaxPacketSize); ++ nat = (nat >> 11) & 0x03; ++ DWC_DEBUGPL(DBG_PCD, "nat=%u binterval =%02x\n",nat, dwcep->bInterval); ++ DWC_DEBUGPL(DBG_PCD, "frame_num = %d\n", dwcep->frame_num); ++ ++ /* Complete first three IN EP requests for the synchronization */ ++ if (dwcep->is_in) { ++ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ for (j = 0; j < sync_request; j++) { ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ if (!req) { ++ DWC_PRINTF("ISOC 0x%p, req = NULL!\n", ep); ++ return; ++ } else { ++ /* Complete first request */ ++ req->actual = 0; ++ dwc_otg_request_done(ep, req, 0); ++ } ++ } ++ } else { ++ DWC_PRINTF("ISOC ep 0x%p, ep->queue empty!\n", ep); ++ return; ++ } ++ ++ frame_num = dwcep->frame_num + (sync_request -1)*dwcep->bInterval; ++ ++ DWC_CIRCLEQ_FOREACH(req, &ep->queue, queue_entry) { ++ i = i+1; ++ frame_num = (frame_num + dwcep->bInterval) & 0x3FFF; ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b_iso_in.bs = BS_HOST_BUSY; ++ dma_desc->buf = req->dma; ++ dma_desc->status.b_iso_in.txbytes = req->length; ++ dma_desc->status.b_iso_in.framenum = frame_num; ++ dma_desc->status.b_iso_in.txsts = 0; ++ dma_desc->status.b_iso_in.sp = (req->length % dwcep->maxpacket) ? 1 : 0; ++ dma_desc->status.b_iso_in.ioc = 1; ++ dma_desc->status.b_iso_in.pid = nat + 1; ++ dma_desc->status.b_iso_in.l = 0; ++ ++ if (req == DWC_CIRCLEQ_LAST(&ep->queue)) { ++ dma_desc->status.b_iso_in.l = 1; ++ } ++ dma_desc->status.b_iso_in.bs = BS_HOST_READY; ++ DWC_DEBUGPL(DBG_PCD, "ISO_DESC #%d %p status = %08x\n", i, dma_desc, dma_desc->status.d32); ++ if (i == MAX_DMA_DESC_CNT/2 - 1) { ++ dma_desc->status.b_iso_in.l = 1; ++ break; ++ } ++ dma_desc++; ++ } ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[dwcep->num]->diepdma, dwcep->dma_desc_addr); ++ DWC_DEBUGPL(DBG_PCD, "%d ISOC IN descs were programmed\n", i-1); ++ depctl.d32 = 0; ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->in_ep_regs[dwcep->num]->diepctl, 0, depctl.d32); ++ } else { ++ DWC_CIRCLEQ_FOREACH(req, &ep->queue, queue_entry) { ++ i = i+1; ++ frame_num = (frame_num + dwcep->bInterval) & 0x3FFF; ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b_iso_out.bs = BS_HOST_BUSY; ++ dma_desc->buf = req->dma; ++ dma_desc->status.b_iso_out.rxbytes = req->length; ++ dma_desc->status.b_iso_out.rxsts = 0; ++ dma_desc->status.b_iso_out.sp = (req->length % dwcep->maxpacket) ? 1 : 0; ++ dma_desc->status.b_iso_out.ioc = 1; ++ dma_desc->status.b_iso_out.pid = nat + 1; ++ dma_desc->status.b_iso_out.l = 0; ++ ++ if (req == DWC_CIRCLEQ_LAST(&ep->queue)) { ++ dma_desc->status.b_iso_out.l = 1; ++ } ++ dma_desc->status.b_iso_in.bs = BS_HOST_READY; ++ DWC_DEBUGPL(DBG_PCD, "ISO_DESC #%d %p status = %08x\n", i, dma_desc, dma_desc->status.d32); ++ if (i == MAX_DMA_DESC_CNT/2 - 1) { ++ dma_desc->status.b_iso_out.l = 1; ++ break; ++ } ++ dma_desc++; ++ } ++ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[dwcep->num]->doepdma, dwcep->dma_desc_addr); ++ DWC_DEBUGPL(DBG_PCD, "%d ISOC OUT descs were programmed\n", i-1); ++ depctl.d32 = 0; ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->out_ep_regs[dwcep->num]->doepctl, 0, depctl.d32); ++ } ++ dwcep->iso_desc_first = i; //vahrama - pay attention previous one was i-1 ++ dwcep->iso_transfer_started = 1; ++ dwcep->frame_num = frame_num; ++ dwcep->use_add_buf = 1; ++} ++/** ++ * Program next ISO request to the DMA chain ++ * ++ */ ++static void program_next_iso_request_ddma (dwc_otg_pcd_ep_t * ep, dwc_otg_pcd_request_t * req) ++{ ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ dwc_dma_t dma_desc_addr; ++ uint32_t frame_num = 0; ++ uint32_t nat; ++ uint32_t index; ++ ++ DWC_DEBUGPL(DBG_PCD, "%s", __FUNCTION__); ++ ++ if (ep->dwc_ep.use_add_buf) { ++ index = ep->dwc_ep.iso_desc_second + 1; ++ } else { ++ index = ep->dwc_ep.iso_desc_first + 1; ++ } ++ ++ if (index > MAX_DMA_DESC_CNT/2) { ++ DWC_PRINTF("There are no free descs in the chain!\n"); ++ return; ++ } ++ ++ if (ep->dwc_ep.use_add_buf) { ++ dma_desc = &ep->dwc_ep.desc_addr1[ep->dwc_ep.iso_desc_second]; ++ dma_desc_addr = ep->dwc_ep.dma_desc_addr1; ++ ep->dwc_ep.iso_desc_second += 1; ++ } else { ++ dma_desc = &ep->dwc_ep.desc_addr[ep->dwc_ep.iso_desc_first]; ++ dma_desc_addr = ep->dwc_ep.dma_desc_addr; ++ ep->dwc_ep.iso_desc_first += 1; ++ } ++ nat = (req->length + ep->dwc_ep.maxpacket - 1) ++ / ep->dwc_ep.maxpacket; ++ ++ if (nat) ++ nat--; ++ frame_num = (ep->dwc_ep.frame_num + ep->dwc_ep.bInterval) & 0x3FFF; ++ if (ep->dwc_ep.is_in) { ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b_iso_in.bs = BS_HOST_BUSY; ++ dma_desc->buf = req->dma; ++ dma_desc->status.b_iso_in.txbytes = req->length; ++ dma_desc->status.b_iso_in.framenum = frame_num; ++ dma_desc->status.b_iso_in.txsts = 0; ++ dma_desc->status.b_iso_in.sp = (req->length % ep->dwc_ep.maxpacket) ? 1 : 0; ++ dma_desc->status.b_iso_in.ioc = 1; ++ dma_desc->status.b_iso_in.pid = nat + 1; ++ dma_desc->status.b_iso_in.l = 1; ++ ++ dma_desc->status.b_iso_in.bs = BS_HOST_READY; ++ ++ /* Clear L bit on the previous desc of the chain */ ++ if (index > 1) { ++ dma_desc--; ++ dma_desc->status.b_iso_in.l = 0; ++ } ++ } else { ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b_iso_out.bs = BS_HOST_BUSY; ++ dma_desc->buf = req->dma; ++ dma_desc->status.b_iso_out.rxbytes = req->length; ++ dma_desc->status.b_iso_out.rxsts = 0; ++ dma_desc->status.b_iso_out.sp = (req->length % ep->dwc_ep.maxpacket) ? 1 : 0; ++ dma_desc->status.b_iso_out.ioc = 1; ++ dma_desc->status.b_iso_out.pid = nat + 1; ++ dma_desc->status.b_iso_out.l = 1; ++ ++ dma_desc->status.b_iso_out.bs = BS_HOST_READY; ++ ++ /* Clear L bit on the previous desc of the chain */ ++ if (index > 1) { ++ dma_desc--; ++ dma_desc->status.b_iso_out.l = 0; ++ } ++ } ++ ep->dwc_ep.frame_num = frame_num; ++ ++} ++ ++/******************************************************************************/ ++#ifdef DWC_UTE_PER_IO ++ ++/** ++ * Free the request and its extended parts ++ * ++ */ ++void dwc_pcd_xiso_ereq_free(dwc_otg_pcd_ep_t * ep, dwc_otg_pcd_request_t * req) ++{ ++ DWC_FREE(req->ext_req.per_io_frame_descs); ++ DWC_FREE(req); ++} ++ ++/** ++ * Start the next request in the endpoint's queue. ++ * ++ */ ++int dwc_otg_pcd_xiso_start_next_request(dwc_otg_pcd_t * pcd, ++ dwc_otg_pcd_ep_t * ep) ++{ ++ int i; ++ dwc_otg_pcd_request_t *req = NULL; ++ dwc_ep_t *dwcep = NULL; ++ struct dwc_iso_xreq_port *ereq = NULL; ++ struct dwc_iso_pkt_desc_port *ddesc_iso; ++ uint16_t nat; ++ depctl_data_t diepctl; ++ ++ dwcep = &ep->dwc_ep; ++ ++ if (dwcep->xiso_active_xfers > 0) { ++#if 0 //Disable this to decrease s/w overhead that is crucial for Isoc transfers ++ DWC_WARN("There are currently active transfers for EP%d \ ++ (active=%d; queued=%d)", dwcep->num, dwcep->xiso_active_xfers, ++ dwcep->xiso_queued_xfers); ++#endif ++ return 0; ++ } ++ ++ nat = UGETW(ep->desc->wMaxPacketSize); ++ nat = (nat >> 11) & 0x03; ++ ++ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ ereq = &req->ext_req; ++ ep->stopped = 0; ++ ++ /* Get the frame number */ ++ dwcep->xiso_frame_num = ++ dwc_otg_get_frame_number(GET_CORE_IF(pcd)); ++ DWC_DEBUG("FRM_NUM=%d", dwcep->xiso_frame_num); ++ ++ ddesc_iso = ereq->per_io_frame_descs; ++ ++ if (dwcep->is_in) { ++ /* Setup DMA Descriptor chain for IN Isoc request */ ++ for (i = 0; i < ereq->pio_pkt_count; i++) { ++ //if ((i % (nat + 1)) == 0) ++ if (i > 0) ++ dwcep->xiso_frame_num = ++ (dwcep->xiso_bInterval + ++ dwcep->xiso_frame_num) & 0x3FFF; ++ dwcep->desc_addr[i].buf = ++ req->dma + ddesc_iso[i].offset; ++ dwcep->desc_addr[i].status.b_iso_in.txbytes = ++ ddesc_iso[i].length; ++ dwcep->desc_addr[i].status.b_iso_in.framenum = ++ dwcep->xiso_frame_num; ++ dwcep->desc_addr[i].status.b_iso_in.bs = ++ BS_HOST_READY; ++ dwcep->desc_addr[i].status.b_iso_in.txsts = 0; ++ dwcep->desc_addr[i].status.b_iso_in.sp = ++ (ddesc_iso[i].length % ++ dwcep->maxpacket) ? 1 : 0; ++ dwcep->desc_addr[i].status.b_iso_in.ioc = 0; ++ dwcep->desc_addr[i].status.b_iso_in.pid = nat + 1; ++ dwcep->desc_addr[i].status.b_iso_in.l = 0; ++ ++ /* Process the last descriptor */ ++ if (i == ereq->pio_pkt_count - 1) { ++ dwcep->desc_addr[i].status.b_iso_in.ioc = 1; ++ dwcep->desc_addr[i].status.b_iso_in.l = 1; ++ } ++ } ++ ++ /* Setup and start the transfer for this endpoint */ ++ dwcep->xiso_active_xfers++; ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->dev_if-> ++ in_ep_regs[dwcep->num]->diepdma, ++ dwcep->dma_desc_addr); ++ diepctl.d32 = 0; ++ diepctl.b.epena = 1; ++ diepctl.b.cnak = 1; ++ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->dev_if-> ++ in_ep_regs[dwcep->num]->diepctl, 0, ++ diepctl.d32); ++ } else { ++ /* Setup DMA Descriptor chain for OUT Isoc request */ ++ for (i = 0; i < ereq->pio_pkt_count; i++) { ++ //if ((i % (nat + 1)) == 0) ++ dwcep->xiso_frame_num = (dwcep->xiso_bInterval + ++ dwcep->xiso_frame_num) & 0x3FFF; ++ dwcep->desc_addr[i].buf = ++ req->dma + ddesc_iso[i].offset; ++ dwcep->desc_addr[i].status.b_iso_out.rxbytes = ++ ddesc_iso[i].length; ++ dwcep->desc_addr[i].status.b_iso_out.framenum = ++ dwcep->xiso_frame_num; ++ dwcep->desc_addr[i].status.b_iso_out.bs = ++ BS_HOST_READY; ++ dwcep->desc_addr[i].status.b_iso_out.rxsts = 0; ++ dwcep->desc_addr[i].status.b_iso_out.sp = ++ (ddesc_iso[i].length % ++ dwcep->maxpacket) ? 1 : 0; ++ dwcep->desc_addr[i].status.b_iso_out.ioc = 0; ++ dwcep->desc_addr[i].status.b_iso_out.pid = nat + 1; ++ dwcep->desc_addr[i].status.b_iso_out.l = 0; ++ ++ /* Process the last descriptor */ ++ if (i == ereq->pio_pkt_count - 1) { ++ dwcep->desc_addr[i].status.b_iso_out.ioc = 1; ++ dwcep->desc_addr[i].status.b_iso_out.l = 1; ++ } ++ } ++ ++ /* Setup and start the transfer for this endpoint */ ++ dwcep->xiso_active_xfers++; ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)-> ++ dev_if->out_ep_regs[dwcep->num]-> ++ doepdma, dwcep->dma_desc_addr); ++ diepctl.d32 = 0; ++ diepctl.b.epena = 1; ++ diepctl.b.cnak = 1; ++ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)-> ++ dev_if->out_ep_regs[dwcep->num]-> ++ doepctl, 0, diepctl.d32); ++ } ++ ++ } else { ++ ep->stopped = 1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * - Remove the request from the queue ++ */ ++void complete_xiso_ep(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_pcd_request_t *req = NULL; ++ struct dwc_iso_xreq_port *ereq = NULL; ++ struct dwc_iso_pkt_desc_port *ddesc_iso = NULL; ++ dwc_ep_t *dwcep = NULL; ++ int i; ++ ++ //DWC_DEBUG(); ++ dwcep = &ep->dwc_ep; ++ ++ /* Get the first pending request from the queue */ ++ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ if (!req) { ++ DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep); ++ return; ++ } ++ dwcep->xiso_active_xfers--; ++ dwcep->xiso_queued_xfers--; ++ /* Remove this request from the queue */ ++ DWC_CIRCLEQ_REMOVE_INIT(&ep->queue, req, queue_entry); ++ } else { ++ DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep); ++ return; ++ } ++ ++ ep->stopped = 1; ++ ereq = &req->ext_req; ++ ddesc_iso = ereq->per_io_frame_descs; ++ ++ if (dwcep->xiso_active_xfers < 0) { ++ DWC_WARN("EP#%d (xiso_active_xfers=%d)", dwcep->num, ++ dwcep->xiso_active_xfers); ++ } ++ ++ /* Fill the Isoc descs of portable extended req from dma descriptors */ ++ for (i = 0; i < ereq->pio_pkt_count; i++) { ++ if (dwcep->is_in) { /* IN endpoints */ ++ ddesc_iso[i].actual_length = ddesc_iso[i].length - ++ dwcep->desc_addr[i].status.b_iso_in.txbytes; ++ ddesc_iso[i].status = ++ dwcep->desc_addr[i].status.b_iso_in.txsts; ++ } else { /* OUT endpoints */ ++ ddesc_iso[i].actual_length = ddesc_iso[i].length - ++ dwcep->desc_addr[i].status.b_iso_out.rxbytes; ++ ddesc_iso[i].status = ++ dwcep->desc_addr[i].status.b_iso_out.rxsts; ++ } ++ } ++ ++ DWC_SPINUNLOCK(ep->pcd->lock); ++ ++ /* Call the completion function in the non-portable logic */ ++ ep->pcd->fops->xisoc_complete(ep->pcd, ep->priv, req->priv, 0, ++ &req->ext_req); ++ ++ DWC_SPINLOCK(ep->pcd->lock); ++ ++ /* Free the request - specific freeing needed for extended request object */ ++ dwc_pcd_xiso_ereq_free(ep, req); ++ ++ /* Start the next request */ ++ dwc_otg_pcd_xiso_start_next_request(ep->pcd, ep); ++ ++ return; ++} ++ ++/** ++ * Create and initialize the Isoc pkt descriptors of the extended request. ++ * ++ */ ++static int dwc_otg_pcd_xiso_create_pkt_descs(dwc_otg_pcd_request_t * req, ++ void *ereq_nonport, ++ int atomic_alloc) ++{ ++ struct dwc_iso_xreq_port *ereq = NULL; ++ struct dwc_iso_xreq_port *req_mapped = NULL; ++ struct dwc_iso_pkt_desc_port *ipds = NULL; /* To be created in this function */ ++ uint32_t pkt_count; ++ int i; ++ ++ ereq = &req->ext_req; ++ req_mapped = (struct dwc_iso_xreq_port *)ereq_nonport; ++ pkt_count = req_mapped->pio_pkt_count; ++ ++ /* Create the isoc descs */ ++ if (atomic_alloc) { ++ ipds = DWC_ALLOC_ATOMIC(sizeof(*ipds) * pkt_count); ++ } else { ++ ipds = DWC_ALLOC(sizeof(*ipds) * pkt_count); ++ } ++ ++ if (!ipds) { ++ DWC_ERROR("Failed to allocate isoc descriptors"); ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ /* Initialize the extended request fields */ ++ ereq->per_io_frame_descs = ipds; ++ ereq->error_count = 0; ++ ereq->pio_alloc_pkt_count = pkt_count; ++ ereq->pio_pkt_count = pkt_count; ++ ereq->tr_sub_flags = req_mapped->tr_sub_flags; ++ ++ /* Init the Isoc descriptors */ ++ for (i = 0; i < pkt_count; i++) { ++ ipds[i].length = req_mapped->per_io_frame_descs[i].length; ++ ipds[i].offset = req_mapped->per_io_frame_descs[i].offset; ++ ipds[i].status = req_mapped->per_io_frame_descs[i].status; /* 0 */ ++ ipds[i].actual_length = ++ req_mapped->per_io_frame_descs[i].actual_length; ++ } ++ ++ return 0; ++} ++ ++static void prn_ext_request(struct dwc_iso_xreq_port *ereq) ++{ ++ struct dwc_iso_pkt_desc_port *xfd = NULL; ++ int i; ++ ++ DWC_DEBUG("per_io_frame_descs=%p", ereq->per_io_frame_descs); ++ DWC_DEBUG("tr_sub_flags=%d", ereq->tr_sub_flags); ++ DWC_DEBUG("error_count=%d", ereq->error_count); ++ DWC_DEBUG("pio_alloc_pkt_count=%d", ereq->pio_alloc_pkt_count); ++ DWC_DEBUG("pio_pkt_count=%d", ereq->pio_pkt_count); ++ DWC_DEBUG("res=%d", ereq->res); ++ ++ for (i = 0; i < ereq->pio_pkt_count; i++) { ++ xfd = &ereq->per_io_frame_descs[0]; ++ DWC_DEBUG("FD #%d", i); ++ ++ DWC_DEBUG("xfd->actual_length=%d", xfd->actual_length); ++ DWC_DEBUG("xfd->length=%d", xfd->length); ++ DWC_DEBUG("xfd->offset=%d", xfd->offset); ++ DWC_DEBUG("xfd->status=%d", xfd->status); ++ } ++} ++ ++/** ++ * ++ */ ++int dwc_otg_pcd_xiso_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle, ++ uint8_t * buf, dwc_dma_t dma_buf, uint32_t buflen, ++ int zero, void *req_handle, int atomic_alloc, ++ void *ereq_nonport) ++{ ++ dwc_otg_pcd_request_t *req = NULL; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_irqflags_t flags; ++ int res; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ if (!ep) { ++ DWC_WARN("bad ep\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ /* We support this extension only for DDMA mode */ ++ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) ++ if (!GET_CORE_IF(pcd)->dma_desc_enable) ++ return -DWC_E_INVALID; ++ ++ /* Create a dwc_otg_pcd_request_t object */ ++ if (atomic_alloc) { ++ req = DWC_ALLOC_ATOMIC(sizeof(*req)); ++ } else { ++ req = DWC_ALLOC(sizeof(*req)); ++ } ++ ++ if (!req) { ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ /* Create the Isoc descs for this request which shall be the exact match ++ * of the structure sent to us from the non-portable logic */ ++ res = ++ dwc_otg_pcd_xiso_create_pkt_descs(req, ereq_nonport, atomic_alloc); ++ if (res) { ++ DWC_WARN("Failed to init the Isoc descriptors"); ++ DWC_FREE(req); ++ return res; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ ++ DWC_CIRCLEQ_INIT_ENTRY(req, queue_entry); ++ req->buf = buf; ++ req->dma = dma_buf; ++ req->length = buflen; ++ req->sent_zlp = zero; ++ req->priv = req_handle; ++ ++ //DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ ep->dwc_ep.dma_addr = dma_buf; ++ ep->dwc_ep.start_xfer_buff = buf; ++ ep->dwc_ep.xfer_buff = buf; ++ ep->dwc_ep.xfer_len = 0; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = buflen; ++ ++ /* Add this request to the tail */ ++ DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry); ++ ep->dwc_ep.xiso_queued_xfers++; ++ ++//DWC_DEBUG("CP_0"); ++//DWC_DEBUG("req->ext_req.tr_sub_flags=%d", req->ext_req.tr_sub_flags); ++//prn_ext_request((struct dwc_iso_xreq_port *) ereq_nonport); ++//prn_ext_request(&req->ext_req); ++ ++ //DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++ /* If the req->status == ASAP then check if there is any active transfer ++ * for this endpoint. If no active transfers, then get the first entry ++ * from the queue and start that transfer ++ */ ++ if (req->ext_req.tr_sub_flags == DWC_EREQ_TF_ASAP) { ++ res = dwc_otg_pcd_xiso_start_next_request(pcd, ep); ++ if (res) { ++ DWC_WARN("Failed to start the next Isoc transfer"); ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ DWC_FREE(req); ++ return res; ++ } ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ return 0; ++} ++ ++#endif ++/* END ifdef DWC_UTE_PER_IO ***************************************************/ ++int dwc_otg_pcd_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle, ++ uint8_t * buf, dwc_dma_t dma_buf, uint32_t buflen, ++ int zero, void *req_handle, int atomic_alloc) ++{ ++ dwc_irqflags_t flags; ++ dwc_otg_pcd_request_t *req; ++ dwc_otg_pcd_ep_t *ep; ++ uint32_t max_transfer; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ if (!ep || (!ep->desc && ep->dwc_ep.num != 0)) { ++ DWC_WARN("bad ep\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (atomic_alloc) { ++ req = DWC_ALLOC_ATOMIC(sizeof(*req)); ++ } else { ++ req = DWC_ALLOC(sizeof(*req)); ++ } ++ ++ if (!req) { ++ return -DWC_E_NO_MEMORY; ++ } ++ DWC_CIRCLEQ_INIT_ENTRY(req, queue_entry); ++ if (!GET_CORE_IF(pcd)->core_params->opt) { ++ if (ep->dwc_ep.num != 0) { ++ DWC_ERROR("queue req %p, len %d buf %p\n", ++ req_handle, buflen, buf); ++ } ++ } ++ ++ req->buf = buf; ++ req->dma = dma_buf; ++ req->length = buflen; ++ req->sent_zlp = zero; ++ req->priv = req_handle; ++ req->dw_align_buf = NULL; ++ if ((dma_buf & 0x3) && GET_CORE_IF(pcd)->dma_enable ++ && !GET_CORE_IF(pcd)->dma_desc_enable) ++ req->dw_align_buf = DWC_DMA_ALLOC(buflen, ++ &req->dw_align_buf_dma); ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ ++ /* ++ * After adding request to the queue for IN ISOC wait for In Token Received ++ * when TX FIFO is empty interrupt and for OUT ISOC wait for OUT Token ++ * Received when EP is disabled interrupt to obtain starting microframe ++ * (odd/even) start transfer ++ */ ++ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) { ++ if (req != 0) { ++ depctl_data_t depctl = {.d32 = ++ DWC_READ_REG32(&pcd->core_if->dev_if-> ++ in_ep_regs[ep->dwc_ep.num]-> ++ diepctl) }; ++ ++pcd->request_pending; ++ ++ DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry); ++ if (ep->dwc_ep.is_in) { ++ depctl.b.cnak = 1; ++ DWC_WRITE_REG32(&pcd->core_if->dev_if-> ++ in_ep_regs[ep->dwc_ep.num]-> ++ diepctl, depctl.d32); ++ } ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++ if (ep->dwc_ep.iso_transfer_started) { ++ /* ++ * Add next request to the descriptor chain ++ * currently not in use by HW ++ */ ++ program_next_iso_request_ddma(ep, req); ++ } else if (!ep->dwc_ep.is_in) ++ /* For OUT start first request immediately after queue */ ++ dwc_otg_pcd_start_iso_ddma(GET_CORE_IF(pcd), ep); ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ } ++ return 0; ++ } ++ ++ /* ++ * For EP0 IN without premature status, zlp is required? ++ */ ++ if (ep->dwc_ep.num == 0 && ep->dwc_ep.is_in) { ++ DWC_DEBUGPL(DBG_PCDV, "%d-OUT ZLP\n", ep->dwc_ep.num); ++ //_req->zero = 1; ++ } ++ ++ /* Start the transfer */ ++ if (DWC_CIRCLEQ_EMPTY(&ep->queue) && !ep->stopped) { ++ /* EP0 Transfer? */ ++ if (ep->dwc_ep.num == 0) { ++ switch (pcd->ep0state) { ++ case EP0_IN_DATA_PHASE: ++ DWC_DEBUGPL(DBG_PCD, ++ "%s ep0: EP0_IN_DATA_PHASE\n", ++ __func__); ++ break; ++ ++ case EP0_OUT_DATA_PHASE: ++ DWC_DEBUGPL(DBG_PCD, ++ "%s ep0: EP0_OUT_DATA_PHASE\n", ++ __func__); ++ if (pcd->request_config) { ++ /* Complete STATUS PHASE */ ++ ep->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_STATUS_PHASE; ++ } ++ break; ++ ++ case EP0_IN_STATUS_PHASE: ++ DWC_DEBUGPL(DBG_PCD, ++ "%s ep0: EP0_IN_STATUS_PHASE\n", ++ __func__); ++ break; ++ ++ default: ++ DWC_DEBUGPL(DBG_ANY, "ep0: odd state %d\n", ++ pcd->ep0state); ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ return -DWC_E_SHUTDOWN; ++ } ++ ++ ep->dwc_ep.dma_addr = dma_buf; ++ ep->dwc_ep.start_xfer_buff = buf; ++ ep->dwc_ep.xfer_buff = buf; ++ ep->dwc_ep.xfer_len = buflen; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ if (zero) { ++ if ((ep->dwc_ep.xfer_len % ++ ep->dwc_ep.maxpacket == 0) ++ && (ep->dwc_ep.xfer_len != 0)) { ++ ep->dwc_ep.sent_zlp = 1; ++ } ++ ++ } ++ ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), ++ &ep->dwc_ep); ++ } // non-ep0 endpoints ++ else { ++#ifdef DWC_UTE_CFI ++ if (ep->dwc_ep.buff_mode != BM_STANDARD) { ++ /* store the request length */ ++ ep->dwc_ep.cfi_req_len = buflen; ++ pcd->cfi->ops.build_descriptors(pcd->cfi, pcd, ++ ep, req); ++ } else { ++#endif ++ max_transfer = ++ GET_CORE_IF(ep->pcd)->core_params-> ++ max_transfer_size; ++ ++ /* Setup and start the Transfer */ ++ if (req->dw_align_buf) { ++ if (ep->dwc_ep.is_in) ++ dwc_memcpy(req->dw_align_buf, ++ buf, buflen); ++ ep->dwc_ep.dma_addr = ++ req->dw_align_buf_dma; ++ ep->dwc_ep.start_xfer_buff = ++ req->dw_align_buf; ++ ep->dwc_ep.xfer_buff = ++ req->dw_align_buf; ++ } else { ++ ep->dwc_ep.dma_addr = dma_buf; ++ ep->dwc_ep.start_xfer_buff = buf; ++ ep->dwc_ep.xfer_buff = buf; ++ } ++ ep->dwc_ep.xfer_len = 0; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = buflen; ++ ++ ep->dwc_ep.maxxfer = max_transfer; ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++ uint32_t out_max_xfer = ++ DDMA_MAX_TRANSFER_SIZE - ++ (DDMA_MAX_TRANSFER_SIZE % 4); ++ if (ep->dwc_ep.is_in) { ++ if (ep->dwc_ep.maxxfer > ++ DDMA_MAX_TRANSFER_SIZE) { ++ ep->dwc_ep.maxxfer = ++ DDMA_MAX_TRANSFER_SIZE; ++ } ++ } else { ++ if (ep->dwc_ep.maxxfer > ++ out_max_xfer) { ++ ep->dwc_ep.maxxfer = ++ out_max_xfer; ++ } ++ } ++ } ++ if (ep->dwc_ep.maxxfer < ep->dwc_ep.total_len) { ++ ep->dwc_ep.maxxfer -= ++ (ep->dwc_ep.maxxfer % ++ ep->dwc_ep.maxpacket); ++ } ++ ++ if (zero) { ++ if ((ep->dwc_ep.total_len % ++ ep->dwc_ep.maxpacket == 0) ++ && (ep->dwc_ep.total_len != 0)) { ++ ep->dwc_ep.sent_zlp = 1; ++ } ++ } ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ dwc_otg_ep_start_transfer(GET_CORE_IF(pcd), ++ &ep->dwc_ep); ++ } ++ } ++ ++ if (req != 0) { ++ ++pcd->request_pending; ++ DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry); ++ if (ep->dwc_ep.is_in && ep->stopped ++ && !(GET_CORE_IF(pcd)->dma_enable)) { ++ /** @todo NGS Create a function for this. */ ++ diepmsk_data_t diepmsk = {.d32 = 0 }; ++ diepmsk.b.intktxfemp = 1; ++ if (GET_CORE_IF(pcd)->multiproc_int_enable) { ++ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)-> ++ dev_if->dev_global_regs->diepeachintmsk ++ [ep->dwc_ep.num], 0, ++ diepmsk.d32); ++ } else { ++ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)-> ++ dev_if->dev_global_regs-> ++ diepmsk, 0, diepmsk.d32); ++ } ++ ++ } ++ } ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++ return 0; ++} ++ ++int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle) ++{ ++ dwc_irqflags_t flags; ++ dwc_otg_pcd_request_t *req; ++ dwc_otg_pcd_ep_t *ep; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ if (!ep || (!ep->desc && ep->dwc_ep.num != 0)) { ++ DWC_WARN("bad argument\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ ++ /* make sure it's actually queued on this endpoint */ ++ DWC_CIRCLEQ_FOREACH(req, &ep->queue, queue_entry) { ++ if (req->priv == (void *)req_handle) { ++ break; ++ } ++ } ++ ++ if (req->priv != (void *)req_handle) { ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ return -DWC_E_INVALID; ++ } ++ ++ if (!DWC_CIRCLEQ_EMPTY_ENTRY(req, queue_entry)) { ++ if(( ep != &pcd->ep0)&&(!ep->dwc_ep.is_in)) { ++ ep->dwc_ep.xfer_buff =NULL; ++ } ++ dwc_otg_request_done(ep, req, -DWC_E_RESTART); ++ } else { ++ req = NULL; ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++ return req ? 0 : -DWC_E_SHUTDOWN; ++ ++} ++ ++int dwc_otg_pcd_ep_halt(dwc_otg_pcd_t * pcd, void *ep_handle, int value) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ dwc_irqflags_t flags; ++ int retval = 0; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ ++ if (!ep || (!ep->desc && ep != &pcd->ep0) || ++ (ep->desc && (ep->desc->bmAttributes == UE_ISOCHRONOUS))) { ++ DWC_WARN("%s, bad ep\n", __func__); ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ DWC_WARN("%d %s XFer In process\n", ep->dwc_ep.num, ++ ep->dwc_ep.is_in ? "IN" : "OUT"); ++ retval = -DWC_E_AGAIN; ++ } else if (value == 0) { ++ ep->dwc_ep.stall_clear_flag = 0; ++ dwc_otg_ep_clear_stall(GET_CORE_IF(pcd), &ep->dwc_ep); ++ } else if (value == 1) { ++ stall: ++ if (ep->dwc_ep.is_in == 1 && GET_CORE_IF(pcd)->dma_desc_enable) { ++ dtxfsts_data_t txstatus; ++ fifosize_data_t txfifosize; ++ ++ txfifosize.d32 = ++ DWC_READ_REG32(&GET_CORE_IF(pcd)-> ++ core_global_regs->dtxfsiz[ep->dwc_ep. ++ tx_fifo_num]); ++ txstatus.d32 = ++ DWC_READ_REG32(&GET_CORE_IF(pcd)-> ++ dev_if->in_ep_regs[ep->dwc_ep.num]-> ++ dtxfsts); ++ ++ if (txstatus.b.txfspcavail < txfifosize.b.depth) { ++ retval = -DWC_E_AGAIN; ++ } else { ++ if (ep->dwc_ep.num == 0) { ++ pcd->ep0state = EP0_STALL; ++ } ++ ++ ep->stopped = 1; ++ dwc_otg_ep_set_stall(GET_CORE_IF(pcd), ++ &ep->dwc_ep); ++ } ++ } else { ++ if (ep->dwc_ep.num == 0) { ++ pcd->ep0state = EP0_STALL; ++ } ++ ++ ep->stopped = 1; ++ dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep->dwc_ep); ++ } ++ } else if (value == 2) { ++ ep->dwc_ep.stall_clear_flag = 0; ++ } else if (value == 3) { ++ ep->dwc_ep.stall_clear_flag = 1; ++ goto stall; ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++ return retval; ++} ++ ++/** ++ * This function initiates remote wakeup of the host from suspend state. ++ */ ++void dwc_otg_pcd_rem_wkup_from_suspend(dwc_otg_pcd_t * pcd, int set) ++{ ++ dctl_data_t dctl = { 0 }; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dsts_data_t dsts; ++ ++ dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); ++ if (!dsts.b.suspsts) { ++ DWC_WARN("Remote wakeup while is not in suspend state\n"); ++ } ++ /* Check if DEVICE_REMOTE_WAKEUP feature enabled */ ++ if (pcd->remote_wakeup_enable) { ++ if (set) { ++ ++ if (core_if->adp_enable) { ++ gpwrdn_data_t gpwrdn; ++ ++ dwc_otg_adp_probe_stop(core_if); ++ ++ /* Mask SRP detected interrupt from Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.srp_det_msk = 1; ++ DWC_MODIFY_REG32(&core_if-> ++ core_global_regs->gpwrdn, ++ gpwrdn.d32, 0); ++ ++ /* Disable Power Down Logic */ ++ gpwrdn.d32 = 0; ++ gpwrdn.b.pmuactv = 1; ++ DWC_MODIFY_REG32(&core_if-> ++ core_global_regs->gpwrdn, ++ gpwrdn.d32, 0); ++ ++ /* ++ * Initialize the Core for Device mode. ++ */ ++ core_if->op_state = B_PERIPHERAL; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ cil_pcd_start(core_if); ++ ++ dwc_otg_initiate_srp(core_if); ++ } ++ ++ dctl.b.rmtwkupsig = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs-> ++ dctl, 0, dctl.d32); ++ DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n"); ++ ++ dwc_mdelay(2); ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs-> ++ dctl, dctl.d32, 0); ++ DWC_DEBUGPL(DBG_PCD, "Clear Remote Wakeup\n"); ++ } ++ } else { ++ DWC_DEBUGPL(DBG_PCD, "Remote Wakeup is disabled\n"); ++ } ++} ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++/** ++ * This function initiates remote wakeup of the host from L1 sleep state. ++ */ ++void dwc_otg_pcd_rem_wkup_from_sleep(dwc_otg_pcd_t * pcd, int set) ++{ ++ glpmcfg_data_t lpmcfg; ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ ++ /* Check if we are in L1 state */ ++ if (!lpmcfg.b.prt_sleep_sts) { ++ DWC_DEBUGPL(DBG_PCD, "Device is not in sleep state\n"); ++ return; ++ } ++ ++ /* Check if host allows remote wakeup */ ++ if (!lpmcfg.b.rem_wkup_en) { ++ DWC_DEBUGPL(DBG_PCD, "Host does not allow remote wakeup\n"); ++ return; ++ } ++ ++ /* Check if Resume OK */ ++ if (!lpmcfg.b.sleep_state_resumeok) { ++ DWC_DEBUGPL(DBG_PCD, "Sleep state resume is not OK\n"); ++ return; ++ } ++ ++ lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg); ++ lpmcfg.b.en_utmi_sleep = 0; ++ lpmcfg.b.hird_thres &= (~(1 << 4)); ++ ++ /* Clear Enbl_L1Gating bit. */ ++ pcgcctl.b.enbl_sleep_gating = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32,0); ++ ++ DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++ ++ if (set) { ++ dctl_data_t dctl = {.d32 = 0 }; ++ dctl.b.rmtwkupsig = 1; ++ /* Set RmtWkUpSig bit to start remote wakup signaling. ++ * Hardware will automatically clear this bit. ++ */ ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, ++ 0, dctl.d32); ++ DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n"); ++ } ++ ++} ++#endif ++ ++/** ++ * Performs remote wakeup. ++ */ ++void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t * pcd, int set) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_irqflags_t flags; ++ if (dwc_otg_is_device_mode(core_if)) { ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ if (core_if->lx_state == DWC_OTG_L1) { ++ dwc_otg_pcd_rem_wkup_from_sleep(pcd, set); ++ } else { ++#endif ++ dwc_otg_pcd_rem_wkup_from_suspend(pcd, set); ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ } ++#endif ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ } ++ return; ++} ++ ++void dwc_otg_pcd_disconnect_us(dwc_otg_pcd_t * pcd, int no_of_usecs) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dctl_data_t dctl = { 0 }; ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ dctl.b.sftdiscon = 1; ++ DWC_PRINTF("Soft disconnect for %d useconds\n",no_of_usecs); ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32); ++ dwc_udelay(no_of_usecs); ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32,0); ++ ++ } else{ ++ DWC_PRINTF("NOT SUPPORTED IN HOST MODE\n"); ++ } ++ return; ++ ++} ++ ++int dwc_otg_pcd_wakeup(dwc_otg_pcd_t * pcd) ++{ ++ dsts_data_t dsts; ++ gotgctl_data_t gotgctl; ++ ++ /* ++ * This function starts the Protocol if no session is in progress. If ++ * a session is already in progress, but the device is suspended, ++ * remote wakeup signaling is started. ++ */ ++ ++ /* Check if valid session */ ++ gotgctl.d32 = ++ DWC_READ_REG32(&(GET_CORE_IF(pcd)->core_global_regs->gotgctl)); ++ if (gotgctl.b.bsesvld) { ++ /* Check if suspend state */ ++ dsts.d32 = ++ DWC_READ_REG32(& ++ (GET_CORE_IF(pcd)->dev_if-> ++ dev_global_regs->dsts)); ++ if (dsts.b.suspsts) { ++ dwc_otg_pcd_remote_wakeup(pcd, 1); ++ } ++ } else { ++ dwc_otg_pcd_initiate_srp(pcd); ++ } ++ ++ return 0; ++ ++} ++ ++void dwc_otg_pcd_pullup(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ depctl_data_t depctl; ++ ++ depctl.d32 = 0; ++ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[bulk_num]->diepctl); ++ ++ depctl.b.setd1pid = 0; ++ depctl.b.setd0pid = 1; ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[bulk_num]->diepctl, depctl.d32); ++ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[3]->diepctl); ++} ++ ++/** ++ * Start the SRP timer to detect when the SRP does not complete within ++ * 6 seconds. ++ * ++ * @param pcd the pcd structure. ++ */ ++void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t * pcd) ++{ ++ dwc_irqflags_t flags; ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ dwc_otg_initiate_srp(GET_CORE_IF(pcd)); ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++} ++ ++int dwc_otg_pcd_get_frame_number(dwc_otg_pcd_t * pcd) ++{ ++ return dwc_otg_get_frame_number(GET_CORE_IF(pcd)); ++} ++ ++int dwc_otg_pcd_is_lpm_enabled(dwc_otg_pcd_t * pcd) ++{ ++ return GET_CORE_IF(pcd)->core_params->lpm_enable; ++} ++ ++int dwc_otg_pcd_is_besl_enabled(dwc_otg_pcd_t * pcd) ++{ ++ return GET_CORE_IF(pcd)->core_params->besl_enable; ++} ++ ++int dwc_otg_pcd_get_param_baseline_besl(dwc_otg_pcd_t * pcd) ++{ ++ return GET_CORE_IF(pcd)->core_params->baseline_besl; ++} ++ ++int dwc_otg_pcd_get_param_deep_besl(dwc_otg_pcd_t * pcd) ++{ ++ return GET_CORE_IF(pcd)->core_params->deep_besl; ++} ++ ++uint32_t get_b_hnp_enable(dwc_otg_pcd_t * pcd) ++{ ++ return pcd->b_hnp_enable; ++} ++ ++uint32_t get_a_hnp_support(dwc_otg_pcd_t * pcd) ++{ ++ return pcd->a_hnp_support; ++} ++ ++uint32_t get_a_alt_hnp_support(dwc_otg_pcd_t * pcd) ++{ ++ return pcd->a_alt_hnp_support; ++} ++ ++int dwc_otg_pcd_get_rmwkup_enable(dwc_otg_pcd_t * pcd) ++{ ++ return pcd->remote_wakeup_enable; ++} ++ ++#endif /* DWC_HOST_ONLY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd.h +new file mode 100644 +index 0000000..54ced23 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd.h +@@ -0,0 +1,268 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.h $ ++ * $Revision: #49 $ ++ * $Date: 2013/05/16 $ ++ * $Change: 2231774 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++#if !defined(__DWC_PCD_H__) ++#define __DWC_PCD_H__ ++ ++#include "dwc_otg_os_dep.h" ++#include "usb.h" ++#include "dwc_otg_cil.h" ++#include "dwc_otg_pcd_if.h" ++struct cfiobject; ++ ++/** ++ * @file ++ * ++ * This file contains the structures, constants, and interfaces for ++ * the Perpherial Contoller Driver (PCD). ++ * ++ * The Peripheral Controller Driver (PCD) for Linux will implement the ++ * Gadget API, so that the existing Gadget drivers can be used. For ++ * the Mass Storage Function driver the File-backed USB Storage Gadget ++ * (FBS) driver will be used. The FBS driver supports the ++ * Control-Bulk (CB), Control-Bulk-Interrupt (CBI), and Bulk-Only ++ * transports. ++ * ++ */ ++ ++/** Invalid DMA Address */ ++#define DWC_DMA_ADDR_INVALID (~(dwc_dma_t)0) ++ ++/** Max Transfer size for any EP */ ++#define DDMA_MAX_TRANSFER_SIZE 65535 ++ ++/** ++ * Get the pointer to the core_if from the pcd pointer. ++ */ ++#define GET_CORE_IF( _pcd ) (_pcd->core_if) ++ ++/** ++ * States of EP0. ++ */ ++typedef enum ep0_state { ++ EP0_DISCONNECT, /* no host */ ++ EP0_IDLE, ++ EP0_IN_DATA_PHASE, ++ EP0_OUT_DATA_PHASE, ++ EP0_IN_STATUS_PHASE, ++ EP0_OUT_STATUS_PHASE, ++ EP0_STALL, ++} ep0state_e; ++ ++/** Fordward declaration.*/ ++struct dwc_otg_pcd; ++ ++/** DWC_otg iso request structure. ++ * ++ */ ++typedef struct usb_iso_request dwc_otg_pcd_iso_request_t; ++ ++#ifdef DWC_UTE_PER_IO ++ ++/** ++ * This shall be the exact analogy of the same type structure defined in the ++ * usb_gadget.h. Each descriptor contains ++ */ ++struct dwc_iso_pkt_desc_port { ++ uint32_t offset; ++ uint32_t length; /* expected length */ ++ uint32_t actual_length; ++ uint32_t status; ++}; ++ ++struct dwc_iso_xreq_port { ++ /** transfer/submission flag */ ++ uint32_t tr_sub_flags; ++ /** Start the request ASAP */ ++#define DWC_EREQ_TF_ASAP 0x00000002 ++ /** Just enqueue the request w/o initiating a transfer */ ++#define DWC_EREQ_TF_ENQUEUE 0x00000004 ++ ++ /** ++ * count of ISO packets attached to this request - shall ++ * not exceed the pio_alloc_pkt_count ++ */ ++ uint32_t pio_pkt_count; ++ /** count of ISO packets allocated for this request */ ++ uint32_t pio_alloc_pkt_count; ++ /** number of ISO packet errors */ ++ uint32_t error_count; ++ /** reserved for future extension */ ++ uint32_t res; ++ /** Will be allocated and freed in the UTE gadget and based on the CFC value */ ++ struct dwc_iso_pkt_desc_port *per_io_frame_descs; ++}; ++#endif ++/** DWC_otg request structure. ++ * This structure is a list of requests. ++ */ ++typedef struct dwc_otg_pcd_request { ++ void *priv; ++ void *buf; ++ dwc_dma_t dma; ++ uint32_t length; ++ uint32_t actual; ++ unsigned sent_zlp:1; ++ /** ++ * Used instead of original buffer if ++ * it(physical address) is not dword-aligned. ++ **/ ++ uint8_t *dw_align_buf; ++ dwc_dma_t dw_align_buf_dma; ++ ++ DWC_CIRCLEQ_ENTRY(dwc_otg_pcd_request) queue_entry; ++#ifdef DWC_UTE_PER_IO ++ struct dwc_iso_xreq_port ext_req; ++ //void *priv_ereq_nport; /* */ ++#endif ++} dwc_otg_pcd_request_t; ++ ++DWC_CIRCLEQ_HEAD(req_list, dwc_otg_pcd_request); ++ ++/** PCD EP structure. ++ * This structure describes an EP, there is an array of EPs in the PCD ++ * structure. ++ */ ++typedef struct dwc_otg_pcd_ep { ++ /** USB EP Descriptor */ ++ const usb_endpoint_descriptor_t *desc; ++ ++ /** queue of dwc_otg_pcd_requests. */ ++ struct req_list queue; ++ unsigned stopped:1; ++ unsigned disabling:1; ++ unsigned dma:1; ++ unsigned queue_sof:1; ++ ++#ifdef DWC_EN_ISOC ++ /** ISOC req handle passed */ ++ void *iso_req_handle; ++#endif //_EN_ISOC_ ++ ++ /** DWC_otg ep data. */ ++ dwc_ep_t dwc_ep; ++ ++ /** Pointer to PCD */ ++ struct dwc_otg_pcd *pcd; ++ ++ void *priv; ++} dwc_otg_pcd_ep_t; ++ ++/** DWC_otg PCD Structure. ++ * This structure encapsulates the data for the dwc_otg PCD. ++ */ ++struct dwc_otg_pcd { ++ const struct dwc_otg_pcd_function_ops *fops; ++ /** The DWC otg device pointer */ ++ struct dwc_otg_device *otg_dev; ++ /** Core Interface */ ++ dwc_otg_core_if_t *core_if; ++ /** State of EP0 */ ++ ep0state_e ep0state; ++ /** EP0 Request is pending */ ++ unsigned ep0_pending:1; ++ /** Indicates when SET CONFIGURATION Request is in process */ ++ unsigned request_config:1; ++ /** The state of the Remote Wakeup Enable. */ ++ unsigned remote_wakeup_enable:1; ++ /** The state of the B-Device HNP Enable. */ ++ unsigned b_hnp_enable:1; ++ /** The state of A-Device HNP Support. */ ++ unsigned a_hnp_support:1; ++ /** The state of the A-Device Alt HNP support. */ ++ unsigned a_alt_hnp_support:1; ++ /** Count of pending Requests */ ++ unsigned request_pending; ++ ++ /** SETUP packet for EP0 ++ * This structure is allocated as a DMA buffer on PCD initialization ++ * with enough space for up to 3 setup packets. ++ */ ++ union { ++ usb_device_request_t req; ++ uint32_t d32[2]; ++ } *setup_pkt; ++ ++ dwc_dma_t setup_pkt_dma_handle; ++ ++ /* Additional buffer and flag for CTRL_WR premature case */ ++ uint8_t *backup_buf; ++ unsigned data_terminated; ++ ++ /** 2-byte dma buffer used to return status from GET_STATUS */ ++ uint16_t *status_buf; ++ dwc_dma_t status_buf_dma_handle; ++ ++ /** EP0 */ ++ dwc_otg_pcd_ep_t ep0; ++ ++ /** Array of IN EPs. */ ++ dwc_otg_pcd_ep_t in_ep[MAX_EPS_CHANNELS - 1]; ++ /** Array of OUT EPs. */ ++ dwc_otg_pcd_ep_t out_ep[MAX_EPS_CHANNELS - 1]; ++ /** number of valid EPs in the above array. */ ++// unsigned num_eps : 4; ++ dwc_spinlock_t *lock; ++ ++ /** Tasklet to defer starting of TEST mode transmissions until ++ * Status Phase has been completed. ++ */ ++ dwc_tasklet_t *test_mode_tasklet; ++ ++ /** Tasklet to delay starting of xfer in DMA mode */ ++ dwc_tasklet_t *start_xfer_tasklet; ++ ++ /** The test mode to enter when the tasklet is executed. */ ++ unsigned test_mode; ++ /** The cfi_api structure that implements most of the CFI API ++ * and OTG specific core configuration functionality ++ */ ++#ifdef DWC_UTE_CFI ++ struct cfiobject *cfi; ++#endif ++ ++}; ++ ++//FIXME this functions should be static, and this prototypes should be removed ++extern void dwc_otg_request_nuke(dwc_otg_pcd_ep_t * ep); ++extern void dwc_otg_request_done(dwc_otg_pcd_ep_t * ep, ++ dwc_otg_pcd_request_t * req, int32_t status); ++ ++void dwc_otg_iso_buffer_done(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep, ++ void *req_handle); ++extern void dwc_otg_pcd_start_iso_ddma(dwc_otg_core_if_t * core_if, ++ dwc_otg_pcd_ep_t * ep); ++ ++extern void do_test_mode(void *data); ++#endif ++#endif /* DWC_HOST_ONLY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd_if.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd_if.h +new file mode 100644 +index 0000000..6cd75fa +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd_if.h +@@ -0,0 +1,368 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_if.h $ ++ * $Revision: #13 $ ++ * $Date: 2012/12/12 $ ++ * $Change: 2125019 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++ ++#if !defined(__DWC_PCD_IF_H__) ++#define __DWC_PCD_IF_H__ ++ ++//#include "dwc_os.h" ++#include "dwc_otg_core_if.h" ++ ++/** @file ++ * This file defines DWC_OTG PCD Core API. ++ */ ++ ++struct dwc_otg_pcd; ++typedef struct dwc_otg_pcd dwc_otg_pcd_t; ++ ++/** Maxpacket size for EP0 */ ++#define MAX_EP0_SIZE 64 ++/** Maxpacket size for any EP */ ++#define MAX_PACKET_SIZE 1024 ++ ++/** @name Function Driver Callbacks */ ++/** @{ */ ++ ++/** This function will be called whenever a previously queued request has ++ * completed. The status value will be set to -DWC_E_SHUTDOWN to indicated a ++ * failed or aborted transfer, or -DWC_E_RESTART to indicate the device was reset, ++ * or -DWC_E_TIMEOUT to indicate it timed out, or -DWC_E_INVALID to indicate invalid ++ * parameters. */ ++typedef int (*dwc_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle, int32_t status, ++ uint32_t actual); ++/** ++ * This function will be called whenever a previousle queued ISOC request has ++ * completed. Count of ISOC packets could be read using dwc_otg_pcd_get_iso_packet_count ++ * function. ++ * The status of each ISOC packet could be read using dwc_otg_pcd_get_iso_packet_* ++ * functions. ++ */ ++typedef int (*dwc_isoc_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle, int proc_buf_num); ++/** This function should handle any SETUP request that cannot be handled by the ++ * PCD Core. This includes most GET_DESCRIPTORs, SET_CONFIGS, Any ++ * class-specific requests, etc. The function must non-blocking. ++ * ++ * Returns 0 on success. ++ * Returns -DWC_E_NOT_SUPPORTED if the request is not supported. ++ * Returns -DWC_E_INVALID if the setup request had invalid parameters or bytes. ++ * Returns -DWC_E_SHUTDOWN on any other error. */ ++typedef int (*dwc_setup_cb_t) (dwc_otg_pcd_t * pcd, uint8_t * bytes); ++/** This is called whenever the device has been disconnected. The function ++ * driver should take appropriate action to clean up all pending requests in the ++ * PCD Core, remove all endpoints (except ep0), and initialize back to reset ++ * state. */ ++typedef int (*dwc_disconnect_cb_t) (dwc_otg_pcd_t * pcd); ++/** This function is called when device has been connected. */ ++typedef int (*dwc_connect_cb_t) (dwc_otg_pcd_t * pcd, int speed); ++/** This function is called when device has been suspended */ ++typedef int (*dwc_suspend_cb_t) (dwc_otg_pcd_t * pcd); ++/** This function is called when device has received LPM tokens, i.e. ++ * device has been sent to sleep state. */ ++typedef int (*dwc_sleep_cb_t) (dwc_otg_pcd_t * pcd); ++/** This function is called when device has been resumed ++ * from suspend(L2) or L1 sleep state. */ ++typedef int (*dwc_resume_cb_t) (dwc_otg_pcd_t * pcd); ++/** This function is called whenever hnp params has been changed. ++ * User can call get_b_hnp_enable, get_a_hnp_support, get_a_alt_hnp_support functions ++ * to get hnp parameters. */ ++typedef int (*dwc_hnp_params_changed_cb_t) (dwc_otg_pcd_t * pcd); ++/** This function is called whenever USB RESET is detected. */ ++typedef int (*dwc_reset_cb_t) (dwc_otg_pcd_t * pcd); ++ ++typedef int (*cfi_setup_cb_t) (dwc_otg_pcd_t * pcd, void *ctrl_req_bytes); ++ ++/** ++ * ++ * @param ep_handle Void pointer to the usb_ep structure ++ * @param ereq_port Pointer to the extended request structure created in the ++ * portable part. ++ */ ++typedef int (*xiso_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle, int32_t status, ++ void *ereq_port); ++/** Function Driver Ops Data Structure */ ++struct dwc_otg_pcd_function_ops { ++ dwc_connect_cb_t connect; ++ dwc_disconnect_cb_t disconnect; ++ dwc_setup_cb_t setup; ++ dwc_completion_cb_t complete; ++ dwc_isoc_completion_cb_t isoc_complete; ++ dwc_suspend_cb_t suspend; ++ dwc_sleep_cb_t sleep; ++ dwc_resume_cb_t resume; ++ dwc_reset_cb_t reset; ++ dwc_hnp_params_changed_cb_t hnp_changed; ++ cfi_setup_cb_t cfi_setup; ++#ifdef DWC_UTE_PER_IO ++ xiso_completion_cb_t xisoc_complete; ++#endif ++}; ++/** @} */ ++ ++/** @name Function Driver Functions */ ++/** @{ */ ++ ++/** Call this function to get pointer on dwc_otg_pcd_t, ++ * this pointer will be used for all PCD API functions. ++ * ++ * @param core_if The DWC_OTG Core ++ */ ++extern dwc_otg_pcd_t *dwc_otg_pcd_init(dwc_otg_core_if_t * core_if); ++ ++/** Frees PCD allocated by dwc_otg_pcd_init ++ * ++ * @param pcd The PCD ++ */ ++extern void dwc_otg_pcd_remove(dwc_otg_pcd_t * pcd); ++ ++/** Call this to bind the function driver to the PCD Core. ++ * ++ * @param pcd Pointer on dwc_otg_pcd_t returned by dwc_otg_pcd_init function. ++ * @param fops The Function Driver Ops data structure containing pointers to all callbacks. ++ */ ++extern void dwc_otg_pcd_start(dwc_otg_pcd_t * pcd, ++ const struct dwc_otg_pcd_function_ops *fops); ++ ++/** Enables an endpoint for use. This function enables an endpoint in ++ * the PCD. The endpoint is described by the ep_desc which has the ++ * same format as a USB ep descriptor. The ep_handle parameter is used to refer ++ * to the endpoint from other API functions and in callbacks. Normally this ++ * should be called after a SET_CONFIGURATION/SET_INTERFACE to configure the ++ * core for that interface. ++ * ++ * Returns -DWC_E_INVALID if invalid parameters were passed. ++ * Returns -DWC_E_SHUTDOWN if any other error ocurred. ++ * Returns 0 on success. ++ * ++ * @param pcd The PCD ++ * @param ep_desc Endpoint descriptor ++ * @param ep_handle Handle on endpoint, that will be used to identify endpoint. ++ */ ++extern int dwc_otg_pcd_ep_enable(dwc_otg_pcd_t * pcd, ++ const uint8_t * ep_desc, void *ep_handle); ++ ++/** Disable the endpoint referenced by ep_handle. ++ * ++ * Returns -DWC_E_INVALID if invalid parameters were passed. ++ * Returns -DWC_E_SHUTDOWN if any other error occurred. ++ * Returns 0 on success. */ ++extern int dwc_otg_pcd_ep_disable(dwc_otg_pcd_t * pcd, void *ep_handle); ++ ++/** Queue a data transfer request on the endpoint referenced by ep_handle. ++ * After the transfer is completes, the complete callback will be called with ++ * the request status. ++ * ++ * @param pcd The PCD ++ * @param ep_handle The handle of the endpoint ++ * @param buf The buffer for the data ++ * @param dma_buf The DMA buffer for the data ++ * @param buflen The length of the data transfer ++ * @param zero Specifies whether to send zero length last packet. ++ * @param req_handle Set this handle to any value to use to reference this ++ * request in the ep_dequeue function or from the complete callback ++ * @param atomic_alloc If driver need to perform atomic allocations ++ * for internal data structures. ++ * ++ * Returns -DWC_E_INVALID if invalid parameters were passed. ++ * Returns -DWC_E_SHUTDOWN if any other error ocurred. ++ * Returns 0 on success. */ ++extern int dwc_otg_pcd_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle, ++ uint8_t * buf, dwc_dma_t dma_buf, ++ uint32_t buflen, int zero, void *req_handle, ++ int atomic_alloc); ++#ifdef DWC_UTE_PER_IO ++/** ++ * ++ * @param ereq_nonport Pointer to the extended request part of the ++ * usb_request structure defined in usb_gadget.h file. ++ */ ++extern int dwc_otg_pcd_xiso_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle, ++ uint8_t * buf, dwc_dma_t dma_buf, ++ uint32_t buflen, int zero, ++ void *req_handle, int atomic_alloc, ++ void *ereq_nonport); ++ ++#endif ++ ++/** De-queue the specified data transfer that has not yet completed. ++ * ++ * Returns -DWC_E_INVALID if invalid parameters were passed. ++ * Returns -DWC_E_SHUTDOWN if any other error ocurred. ++ * Returns 0 on success. */ ++extern int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle); ++ ++/** Halt (STALL) an endpoint or clear it. ++ * ++ * Returns -DWC_E_INVALID if invalid parameters were passed. ++ * Returns -DWC_E_SHUTDOWN if any other error ocurred. ++ * Returns -DWC_E_AGAIN if the STALL cannot be sent and must be tried again later ++ * Returns 0 on success. */ ++extern int dwc_otg_pcd_ep_halt(dwc_otg_pcd_t * pcd, void *ep_handle, int value); ++ ++/** This function should be called on every hardware interrupt */ ++extern int32_t dwc_otg_pcd_handle_intr(dwc_otg_pcd_t * pcd); ++ ++/** This function returns current frame number */ ++extern int dwc_otg_pcd_get_frame_number(dwc_otg_pcd_t * pcd); ++ ++/** ++ * Start isochronous transfers on the endpoint referenced by ep_handle. ++ * For isochronous transfers duble buffering is used. ++ * After processing each of buffers comlete callback will be called with ++ * status for each transaction. ++ * ++ * @param pcd The PCD ++ * @param ep_handle The handle of the endpoint ++ * @param buf0 The virtual address of first data buffer ++ * @param buf1 The virtual address of second data buffer ++ * @param dma0 The DMA address of first data buffer ++ * @param dma1 The DMA address of second data buffer ++ * @param sync_frame Data pattern frame number ++ * @param dp_frame Data size for pattern frame ++ * @param data_per_frame Data size for regular frame ++ * @param start_frame Frame number to start transfers, if -1 then start transfers ASAP. ++ * @param buf_proc_intrvl Interval of ISOC Buffer processing ++ * @param req_handle Handle of ISOC request ++ * @param atomic_alloc Specefies whether to perform atomic allocation for ++ * internal data structures. ++ * ++ * Returns -DWC_E_NO_MEMORY if there is no enough memory. ++ * Returns -DWC_E_INVALID if incorrect arguments are passed to the function. ++ * Returns -DW_E_SHUTDOWN for any other error. ++ * Returns 0 on success ++ */ ++extern int dwc_otg_pcd_iso_ep_start(dwc_otg_pcd_t * pcd, void *ep_handle, ++ uint8_t * buf0, uint8_t * buf1, ++ dwc_dma_t dma0, dwc_dma_t dma1, ++ int sync_frame, int dp_frame, ++ int data_per_frame, int start_frame, ++ int buf_proc_intrvl, void *req_handle, ++ int atomic_alloc); ++ ++/** Stop ISOC transfers on endpoint referenced by ep_handle. ++ * ++ * @param pcd The PCD ++ * @param ep_handle The handle of the endpoint ++ * @param req_handle Handle of ISOC request ++ * ++ * Returns -DWC_E_INVALID if incorrect arguments are passed to the function ++ * Returns 0 on success ++ */ ++int dwc_otg_pcd_iso_ep_stop(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle); ++ ++/** Get ISOC packet status. ++ * ++ * @param pcd The PCD ++ * @param ep_handle The handle of the endpoint ++ * @param iso_req_handle Isochronoush request handle ++ * @param packet Number of packet ++ * @param status Out parameter for returning status ++ * @param actual Out parameter for returning actual length ++ * @param offset Out parameter for returning offset ++ * ++ */ ++extern void dwc_otg_pcd_get_iso_packet_params(dwc_otg_pcd_t * pcd, ++ void *ep_handle, ++ void *iso_req_handle, int packet, ++ int *status, int *actual, ++ int *offset); ++ ++/** Get ISOC packet count. ++ * ++ * @param pcd The PCD ++ * @param ep_handle The handle of the endpoint ++ * @param iso_req_handle ++ */ ++extern int dwc_otg_pcd_get_iso_packet_count(dwc_otg_pcd_t * pcd, ++ void *ep_handle, ++ void *iso_req_handle); ++ ++/** This function starts the SRP Protocol if no session is in progress. If ++ * a session is already in progress, but the device is suspended, ++ * remote wakeup signaling is started. ++ */ ++extern int dwc_otg_pcd_wakeup(dwc_otg_pcd_t * pcd); ++ ++extern void dwc_otg_pcd_pullup(dwc_otg_pcd_t *pcd); ++ ++/** This function returns 1 if LPM support is enabled, and 0 otherwise. */ ++extern int dwc_otg_pcd_is_lpm_enabled(dwc_otg_pcd_t * pcd); ++ ++/** This function returns 1 if LPM Errata support is enabled, and 0 otherwise. */ ++extern int dwc_otg_pcd_is_besl_enabled(dwc_otg_pcd_t * pcd); ++ ++/** This function returns baseline_besl module parametr. */ ++extern int dwc_otg_pcd_get_param_baseline_besl(dwc_otg_pcd_t * pcd); ++ ++/** This function returns deep_besl module parametr. */ ++extern int dwc_otg_pcd_get_param_deep_besl(dwc_otg_pcd_t * pcd); ++ ++/** This function returns 1 if remote wakeup is allowed and 0, otherwise. */ ++extern int dwc_otg_pcd_get_rmwkup_enable(dwc_otg_pcd_t * pcd); ++ ++/** Initiate SRP */ ++extern void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t * pcd); ++ ++/** Starts remote wakeup signaling. */ ++extern void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t * pcd, int set); ++ ++/** Starts micorsecond soft disconnect. */ ++extern void dwc_otg_pcd_disconnect_us(dwc_otg_pcd_t * pcd, int no_of_usecs); ++/** This function returns whether device is dualspeed.*/ ++extern uint32_t dwc_otg_pcd_is_dualspeed(dwc_otg_pcd_t * pcd); ++ ++/** This function returns whether device is otg. */ ++extern uint32_t dwc_otg_pcd_is_otg(dwc_otg_pcd_t * pcd); ++ ++/** These functions allow to get hnp parameters */ ++extern uint32_t get_b_hnp_enable(dwc_otg_pcd_t * pcd); ++extern uint32_t get_a_hnp_support(dwc_otg_pcd_t * pcd); ++extern uint32_t get_a_alt_hnp_support(dwc_otg_pcd_t * pcd); ++ ++/** CFI specific Interface functions */ ++/** Allocate a cfi buffer */ ++extern uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, ++ dwc_dma_t * addr, size_t buflen, ++ int flags); ++ ++/******************************************************************************/ ++ ++/** @} */ ++ ++#endif /* __DWC_PCD_IF_H__ */ ++ ++#endif /* DWC_HOST_ONLY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd_intr.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd_intr.c +new file mode 100644 +index 0000000..9a151e0 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd_intr.c +@@ -0,0 +1,5463 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_intr.c $ ++ * $Revision: #125 $ ++ * $Date: 2013/05/20 $ ++ * $Change: 2234037 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++ ++#include "dwc_otg_pcd.h" ++ ++#ifdef DWC_UTE_CFI ++#include "dwc_otg_cfi.h" ++#endif ++ ++//#include <linux/hisilicon/hiotg.h> ++ ++#ifdef DWC_UTE_PER_IO ++extern void complete_xiso_ep(dwc_otg_pcd_ep_t * ep); ++#endif ++//#define PRINT_CFI_DMA_DESCS ++ ++#define DEBUG_EP0 ++ ++/* avoid null point */ ++uint8_t readpacket_buf[1024]; ++ ++/** ++ * This function updates OTG. ++ */ ++extern int otg_usbhost_stat; ++extern void hisi_switch_func(int otg); ++static void dwc_otg_pcd_update_otg(dwc_otg_pcd_t * pcd, const unsigned reset) ++{ ++ ++ if (reset) { ++ pcd->b_hnp_enable = 0; ++ pcd->a_hnp_support = 0; ++ pcd->a_alt_hnp_support = 0; ++ } ++ ++ if (pcd->fops->hnp_changed) { ++ pcd->fops->hnp_changed(pcd); ++ } ++} ++ ++/** @file ++ * This file contains the implementation of the PCD Interrupt handlers. ++ * ++ * The PCD handles the device interrupts. Many conditions can cause a ++ * device interrupt. When an interrupt occurs, the device interrupt ++ * service routine determines the cause of the interrupt and ++ * dispatches handling to the appropriate function. These interrupt ++ * handling functions are described below. ++ * All interrupt registers are processed from LSB to MSB. ++ */ ++ ++/** ++ * This function prints the ep0 state for debug purposes. ++ */ ++static inline void print_ep0_state(dwc_otg_pcd_t * pcd) ++{ ++#ifdef DEBUG ++ char str[40]; ++ ++ switch (pcd->ep0state) { ++ case EP0_DISCONNECT: ++ dwc_strcpy(str, "EP0_DISCONNECT"); ++ break; ++ case EP0_IDLE: ++ dwc_strcpy(str, "EP0_IDLE"); ++ break; ++ case EP0_IN_DATA_PHASE: ++ dwc_strcpy(str, "EP0_IN_DATA_PHASE"); ++ break; ++ case EP0_OUT_DATA_PHASE: ++ dwc_strcpy(str, "EP0_OUT_DATA_PHASE"); ++ break; ++ case EP0_IN_STATUS_PHASE: ++ dwc_strcpy(str, "EP0_IN_STATUS_PHASE"); ++ break; ++ case EP0_OUT_STATUS_PHASE: ++ dwc_strcpy(str, "EP0_OUT_STATUS_PHASE"); ++ break; ++ case EP0_STALL: ++ dwc_strcpy(str, "EP0_STALL"); ++ break; ++ default: ++ dwc_strcpy(str, "EP0_INVALID"); ++ } ++ ++ DWC_DEBUGPL(DBG_ANY, "%s(%d)\n", str, pcd->ep0state); ++#endif ++} ++ ++/** ++ * This function calculate the size of the payload in the memory ++ * for out endpoints and prints size for debug purposes(used in ++ * 2.93a DevOutNak feature). ++ */ ++static inline void print_memory_payload(dwc_otg_pcd_t * pcd, dwc_ep_t * ep) ++{ ++#ifdef DEBUG ++ deptsiz_data_t deptsiz_init = {.d32 = 0 }; ++ deptsiz_data_t deptsiz_updt = {.d32 = 0 }; ++ int pack_num; ++ unsigned payload; ++ ++ deptsiz_init.d32 = pcd->core_if->start_doeptsiz_val[ep->num]; ++ deptsiz_updt.d32 = ++ DWC_READ_REG32(&pcd->core_if->dev_if-> ++ out_ep_regs[ep->num]->doeptsiz); ++ /* Payload will be */ ++ payload = deptsiz_init.b.xfersize - deptsiz_updt.b.xfersize; ++ /* Packet count is decremented every time a packet ++ * is written to the RxFIFO not in to the external memory ++ * So, if payload == 0, then it means no packet was sent to ext memory*/ ++ pack_num = (!payload) ? 0 : (deptsiz_init.b.pktcnt - deptsiz_updt.b.pktcnt); ++ DWC_DEBUGPL(DBG_PCDV, ++ "Payload for EP%d-%s\n", ++ ep->num, (ep->is_in ? "IN" : "OUT")); ++ DWC_DEBUGPL(DBG_PCDV, ++ "Number of transfered bytes = 0x%08x\n", payload); ++ DWC_DEBUGPL(DBG_PCDV, ++ "Number of transfered packets = %d\n", pack_num); ++#endif ++} ++ ++ ++#ifdef DWC_UTE_CFI ++static inline void print_desc(struct dwc_otg_dma_desc *ddesc, ++ const uint8_t * epname, int descnum) ++{ ++ CFI_INFO ++ ("%s DMA_DESC(%d) buf=0x%08x bytes=0x%04x; sp=0x%x; l=0x%x; sts=0x%02x; bs=0x%02x\n", ++ epname, descnum, ddesc->buf, ddesc->status.b.bytes, ++ ddesc->status.b.sp, ddesc->status.b.l, ddesc->status.b.sts, ++ ddesc->status.b.bs); ++} ++#endif ++ ++/** ++ * This function returns pointer to in ep struct with number ep_num ++ */ ++static inline dwc_otg_pcd_ep_t *get_in_ep(dwc_otg_pcd_t * pcd, uint32_t ep_num) ++{ ++ int i; ++ int num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps; ++ if (ep_num == 0) { ++ return &pcd->ep0; ++ } else { ++ for (i = 0; i < num_in_eps; ++i) { ++ if (pcd->in_ep[i].dwc_ep.num == ep_num) ++ return &pcd->in_ep[i]; ++ } ++ return 0; ++ } ++} ++ ++/** ++ * This function returns pointer to out ep struct with number ep_num ++ */ ++static inline dwc_otg_pcd_ep_t *get_out_ep(dwc_otg_pcd_t * pcd, uint32_t ep_num) ++{ ++ int i; ++ int num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps; ++ if (ep_num == 0) { ++ return &pcd->ep0; ++ } else { ++ for (i = 0; i < num_out_eps; ++i) { ++ if (pcd->out_ep[i].dwc_ep.num == ep_num) ++ return &pcd->out_ep[i]; ++ } ++ return 0; ++ } ++} ++ ++/** ++ * This functions gets a pointer to an EP from the wIndex address ++ * value of the control request. ++ */ ++dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t * pcd, u16 wIndex) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ uint32_t ep_num = UE_GET_ADDR(wIndex); ++ ++ if (ep_num == 0) { ++ ep = &pcd->ep0; ++ } else if (UE_GET_DIR(wIndex) == UE_DIR_IN) { /* in ep */ ++ ep = &pcd->in_ep[ep_num - 1]; ++ } else { ++ ep = &pcd->out_ep[ep_num - 1]; ++ } ++ ++ return ep; ++} ++ ++/** ++ * This function checks the EP request queue, if the queue is not ++ * empty the next request is started. ++ */ ++void start_next_request(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_pcd_request_t *req = 0; ++ uint32_t max_transfer = ++ GET_CORE_IF(ep->pcd)->core_params->max_transfer_size; ++ ++#ifdef DWC_UTE_CFI ++ struct dwc_otg_pcd *pcd; ++ pcd = ep->pcd; ++#endif ++ ++ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ ++#ifdef DWC_UTE_CFI ++ if (ep->dwc_ep.buff_mode != BM_STANDARD) { ++ ep->dwc_ep.cfi_req_len = req->length; ++ pcd->cfi->ops.build_descriptors(pcd->cfi, pcd, ep, req); ++ } else { ++#endif ++ /* Setup and start the Transfer */ ++ if (req->dw_align_buf) { ++ ep->dwc_ep.dma_addr = req->dw_align_buf_dma; ++ ep->dwc_ep.start_xfer_buff = req->dw_align_buf; ++ ep->dwc_ep.xfer_buff = req->dw_align_buf; ++ } else { ++ ep->dwc_ep.dma_addr = req->dma; ++ ep->dwc_ep.start_xfer_buff = req->buf; ++ ep->dwc_ep.xfer_buff = req->buf; ++ } ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = req->length; ++ ep->dwc_ep.xfer_len = 0; ++ ep->dwc_ep.xfer_count = 0; ++ ++ ep->dwc_ep.maxxfer = max_transfer; ++ if (GET_CORE_IF(ep->pcd)->dma_desc_enable) { ++ uint32_t out_max_xfer = DDMA_MAX_TRANSFER_SIZE ++ - (DDMA_MAX_TRANSFER_SIZE % 4); ++ if (ep->dwc_ep.is_in) { ++ if (ep->dwc_ep.maxxfer > ++ DDMA_MAX_TRANSFER_SIZE) { ++ ep->dwc_ep.maxxfer = ++ DDMA_MAX_TRANSFER_SIZE; ++ } ++ } else { ++ if (ep->dwc_ep.maxxfer > out_max_xfer) { ++ ep->dwc_ep.maxxfer = ++ out_max_xfer; ++ } ++ } ++ } ++ if (ep->dwc_ep.maxxfer < ep->dwc_ep.total_len) { ++ ep->dwc_ep.maxxfer -= ++ (ep->dwc_ep.maxxfer % ep->dwc_ep.maxpacket); ++ } ++ if (req->sent_zlp) { ++ if ((ep->dwc_ep.total_len % ++ ep->dwc_ep.maxpacket == 0) ++ && (ep->dwc_ep.total_len != 0)) { ++ ep->dwc_ep.sent_zlp = 1; ++ } ++ ++ } ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ dwc_otg_ep_start_transfer(GET_CORE_IF(ep->pcd), &ep->dwc_ep); ++ } else if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) { ++ diepmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ intr_mask.b.nak = 1; ++ ++ if (GET_CORE_IF(ep->pcd)->multiproc_int_enable) { ++ DWC_MODIFY_REG32(&GET_CORE_IF(ep->pcd)->dev_if->dev_global_regs-> ++ diepeachintmsk[ep->dwc_ep.num], intr_mask.d32, 0); ++ } else { ++ DWC_MODIFY_REG32(&GET_CORE_IF(ep->pcd)->dev_if->dev_global_regs->diepmsk, ++ intr_mask.d32, 0); ++ } ++ DWC_PRINTF("There are no more ISOC requests \n"); ++ ep->dwc_ep.frame_num = 0xFFFFFFFF; ++ } ++} ++ ++/** ++ * This function handles the SOF Interrupts. At this time the SOF ++ * Interrupt is disabled. ++ */ ++int32_t dwc_otg_pcd_handle_sof_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_PCD, "SOF\n"); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.sofintr = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This function handles the Rx Status Queue Level Interrupt, which ++ * indicates that there is a least one packet in the Rx FIFO. The ++ * packets are moved from the FIFO to memory, where they will be ++ * processed when the Endpoint Interrupt Register indicates Transfer ++ * Complete or SETUP Phase Done. ++ * ++ * Repeat the following until the Rx Status Queue is empty: ++ * -# Read the Receive Status Pop Register (GRXSTSP) to get Packet ++ * info ++ * -# If Receive FIFO is empty then skip to step Clear the interrupt ++ * and exit ++ * -# If SETUP Packet call dwc_otg_read_setup_packet to copy the ++ * SETUP data to the buffer ++ * -# If OUT Data Packet call dwc_otg_read_packet to copy the data ++ * to the destination buffer ++ */ ++int32_t dwc_otg_pcd_handle_rx_status_q_level_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ gintmsk_data_t gintmask = {.d32 = 0 }; ++ device_grxsts_data_t status; ++ dwc_otg_pcd_ep_t *ep; ++ gintsts_data_t gintsts; ++#ifdef DEBUG ++ static char *dpid_str[] = { "D0", "D2", "D1", "MDATA" }; ++#endif ++ ++ //DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _pcd); ++ /* Disable the Rx Status Queue Level interrupt */ ++ gintmask.b.rxstsqlvl = 1; ++ DWC_MODIFY_REG32(&global_regs->gintmsk, gintmask.d32, 0); ++ ++ /* Get the Status from the top of the FIFO */ ++ status.d32 = DWC_READ_REG32(&global_regs->grxstsp); ++ ++ DWC_DEBUGPL(DBG_PCD, "EP:%d BCnt:%d DPID:%s " ++ "pktsts:%x Frame:%d(0x%0x)\n", ++ status.b.epnum, status.b.bcnt, ++ dpid_str[status.b.dpid], ++ status.b.pktsts, status.b.fn, status.b.fn); ++ /* Get pointer to EP structure */ ++ ep = get_out_ep(pcd, status.b.epnum); ++ if (!ep) ++ return -EINVAL; ++ ++ switch (status.b.pktsts) { ++ case DWC_DSTS_GOUT_NAK: ++ DWC_DEBUGPL(DBG_PCDV, "Global OUT NAK\n"); ++ break; ++ case DWC_STS_DATA_UPDT: ++ DWC_DEBUGPL(DBG_PCDV, "OUT Data Packet\n"); ++ if (status.b.bcnt && ep->dwc_ep.xfer_buff) { ++ /** @todo NGS Check for buffer overflow? */ ++ dwc_otg_read_packet(core_if, ++ ep->dwc_ep.xfer_buff, ++ status.b.bcnt); ++ ep->dwc_ep.xfer_count += status.b.bcnt; ++ ep->dwc_ep.xfer_buff += status.b.bcnt; ++ } ++ if (status.b.bcnt &&(status.b.bcnt<1024) ++ && !ep->dwc_ep.xfer_buff) { ++ dwc_otg_read_packet(core_if, ++ readpacket_buf, ++ status.b.bcnt); ++ ep->dwc_ep.xfer_count += status.b.bcnt; ++ } ++ break; ++ case DWC_STS_XFER_COMP: ++ DWC_DEBUGPL(DBG_PCDV, "OUT Complete\n"); ++ break; ++ case DWC_DSTS_SETUP_COMP: ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "Setup Complete\n"); ++#endif ++ break; ++ case DWC_DSTS_SETUP_UPDT: ++ dwc_otg_read_setup_packet(core_if, pcd->setup_pkt->d32); ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, ++ "SETUP PKT: %02x.%02x v%04x i%04x l%04x\n", ++ pcd->setup_pkt->req.bmRequestType, ++ pcd->setup_pkt->req.bRequest, ++ UGETW(pcd->setup_pkt->req.wValue), ++ UGETW(pcd->setup_pkt->req.wIndex), ++ UGETW(pcd->setup_pkt->req.wLength)); ++#endif ++ ep->dwc_ep.xfer_count += status.b.bcnt; ++ break; ++ default: ++ DWC_DEBUGPL(DBG_PCDV, "Invalid Packet Status (0x%0x)\n", ++ status.b.pktsts); ++ break; ++ } ++ ++ /* Enable the Rx Status Queue Level interrupt */ ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmask.d32); ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ //DWC_DEBUGPL(DBG_PCDV, "EXIT: %s\n", __func__); ++ return 1; ++} ++ ++/** ++ * This function examines the Device IN Token Learning Queue to ++ * determine the EP number of the last IN token received. This ++ * implementation is for the Mass Storage device where there are only ++ * 2 IN EPs (Control-IN and BULK-IN). ++ * ++ * The EP numbers for the first six IN Tokens are in DTKNQR1 and there ++ * are 8 EP Numbers in each of the other possible DTKNQ Registers. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * ++ */ ++static inline int get_ep_of_last_in_token(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_device_global_regs_t *dev_global_regs = ++ core_if->dev_if->dev_global_regs; ++ const uint32_t TOKEN_Q_DEPTH = core_if->hwcfg2.b.dev_token_q_depth; ++ /* Number of Token Queue Registers */ ++ const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8; ++ dtknq1_data_t dtknqr1; ++ uint32_t in_tkn_epnums[4]; ++ int ndx = 0; ++ int i = 0; ++ volatile uint32_t *addr = &dev_global_regs->dtknqr1; ++ int epnum = 0; ++ ++ //DWC_DEBUGPL(DBG_PCD,"dev_token_q_depth=%d\n",TOKEN_Q_DEPTH); ++ ++ /* Read the DTKNQ Registers */ ++ for (i = 0; i < DTKNQ_REG_CNT; i++) { ++ in_tkn_epnums[i] = DWC_READ_REG32(addr); ++ DWC_DEBUGPL(DBG_PCDV, "DTKNQR%d=0x%08x\n", i + 1, ++ in_tkn_epnums[i]); ++ if (addr == &dev_global_regs->dvbusdis) { ++ addr = &dev_global_regs->dtknqr3_dthrctl; ++ } else { ++ ++addr; ++ } ++ ++ } ++ ++ /* Copy the DTKNQR1 data to the bit field. */ ++ dtknqr1.d32 = in_tkn_epnums[0]; ++ /* Get the EP numbers */ ++ in_tkn_epnums[0] = dtknqr1.b.epnums0_5; ++ ndx = dtknqr1.b.intknwptr - 1; ++ ++ //DWC_DEBUGPL(DBG_PCDV,"ndx=%d\n",ndx); ++ if (ndx == -1) { ++ /** @todo Find a simpler way to calculate the max ++ * queue position.*/ ++ int cnt = TOKEN_Q_DEPTH; ++ if (TOKEN_Q_DEPTH <= 6) { ++ cnt = TOKEN_Q_DEPTH - 1; ++ } else if (TOKEN_Q_DEPTH <= 14) { ++ cnt = TOKEN_Q_DEPTH - 7; ++ } else if (TOKEN_Q_DEPTH <= 22) { ++ cnt = TOKEN_Q_DEPTH - 15; ++ } else { ++ cnt = TOKEN_Q_DEPTH - 23; ++ } ++ epnum = (in_tkn_epnums[DTKNQ_REG_CNT - 1] >> (cnt * 4)) & 0xF; ++ } else { ++ if (ndx <= 5) { ++ epnum = (in_tkn_epnums[0] >> (ndx * 4)) & 0xF; ++ } else if (ndx <= 13) { ++ ndx -= 6; ++ epnum = (in_tkn_epnums[1] >> (ndx * 4)) & 0xF; ++ } else if (ndx <= 21) { ++ ndx -= 14; ++ epnum = (in_tkn_epnums[2] >> (ndx * 4)) & 0xF; ++ } else if (ndx <= 29) { ++ ndx -= 22; ++ epnum = (in_tkn_epnums[3] >> (ndx * 4)) & 0xF; ++ } ++ } ++ //DWC_DEBUGPL(DBG_PCD,"epnum=%d\n",epnum); ++ return epnum; ++} ++ ++/** ++ * This interrupt occurs when the non-periodic Tx FIFO is half-empty. ++ * The active request is checked for the next packet to be loaded into ++ * the non-periodic Tx FIFO. ++ */ ++int32_t dwc_otg_pcd_handle_np_tx_fifo_empty_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ dwc_otg_dev_in_ep_regs_t *ep_regs; ++ gnptxsts_data_t txstatus = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ ++ int epnum = 0; ++ dwc_otg_pcd_ep_t *ep = 0; ++ uint32_t len = 0; ++ int dwords; ++ ++ /* Get the epnum from the IN Token Learning Queue. */ ++ epnum = get_ep_of_last_in_token(core_if); ++ ep = get_in_ep(pcd, epnum); ++ if (!ep) ++ return -EINVAL; ++ ++ DWC_DEBUGPL(DBG_PCD, "NP TxFifo Empty: %d \n", epnum); ++ ++ ep_regs = core_if->dev_if->in_ep_regs[epnum]; ++ ++ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ if (len > ep->dwc_ep.maxpacket) { ++ len = ep->dwc_ep.maxpacket; ++ } ++ dwords = (len + 3) / 4; ++ ++ /* While there is space in the queue and space in the FIFO and ++ * More data to tranfer, Write packets to the Tx FIFO */ ++ txstatus.d32 = DWC_READ_REG32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_PCDV, "b4 GNPTXSTS=0x%08x\n", txstatus.d32); ++ ++ while (txstatus.b.nptxqspcavail > 0 && ++ txstatus.b.nptxfspcavail > dwords && ++ ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len) { ++ /* Write the FIFO */ ++ dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0); ++ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ ++ if (len > ep->dwc_ep.maxpacket) { ++ len = ep->dwc_ep.maxpacket; ++ } ++ ++ dwords = (len + 3) / 4; ++ txstatus.d32 = DWC_READ_REG32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n", txstatus.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n", ++ DWC_READ_REG32(&global_regs->gnptxsts)); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.nptxfempty = 1; ++ DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This function is called when dedicated Tx FIFO Empty interrupt occurs. ++ * The active request is checked for the next packet to be loaded into ++ * apropriate Tx FIFO. ++ */ ++static int32_t write_empty_tx_fifo(dwc_otg_pcd_t * pcd, uint32_t epnum) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dwc_otg_dev_in_ep_regs_t *ep_regs; ++ dtxfsts_data_t txstatus = {.d32 = 0 }; ++ dwc_otg_pcd_ep_t *ep = 0; ++ uint32_t len = 0; ++ int dwords; ++ ++ ep = get_in_ep(pcd, epnum); ++ if (!ep) ++ return -EINVAL; ++ ++ DWC_DEBUGPL(DBG_PCD, "Dedicated TxFifo Empty: %d \n", epnum); ++ ++ ep_regs = core_if->dev_if->in_ep_regs[epnum]; ++ ++ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ ++ if (len > ep->dwc_ep.maxpacket) { ++ len = ep->dwc_ep.maxpacket; ++ } ++ ++ dwords = (len + 3) / 4; ++ ++ /* While there is space in the queue and space in the FIFO and ++ * More data to tranfer, Write packets to the Tx FIFO */ ++ txstatus.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, txstatus.d32); ++ ++ while (txstatus.b.txfspcavail >= dwords && ++ ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len && ++ ep->dwc_ep.xfer_len != 0) { ++ /* Write the FIFO */ ++ dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0); ++ ++ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ if (len > ep->dwc_ep.maxpacket) { ++ len = ep->dwc_ep.maxpacket; ++ } ++ ++ dwords = (len + 3) / 4; ++ txstatus.d32 = ++ DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", epnum, ++ txstatus.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, ++ DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts)); ++ ++ return 1; ++} ++ ++/** ++ * This function is called when the Device is disconnected. It stops ++ * any active requests and informs the Gadget driver of the ++ * disconnect. ++ */ ++void dwc_otg_pcd_stop(dwc_otg_pcd_t * pcd) ++{ ++ int i, num_in_eps, num_out_eps; ++ dwc_otg_pcd_ep_t *ep; ++ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_SPINLOCK(pcd->lock); ++ ++ num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps; ++ num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s() \n", __func__); ++ /* don't disconnect drivers more than once */ ++ if (pcd->ep0state == EP0_DISCONNECT) { ++ DWC_DEBUGPL(DBG_ANY, "%s() Already Disconnected\n", __func__); ++ DWC_SPINUNLOCK(pcd->lock); ++ return; ++ } ++ pcd->ep0state = EP0_DISCONNECT; ++ ++ /* Reset the OTG state. */ ++ dwc_otg_pcd_update_otg(pcd, 1); ++ ++ /* Disable the NP Tx Fifo Empty Interrupt. */ ++ intr_mask.b.nptxfempty = 1; ++ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Flush the FIFOs */ ++ /**@todo NGS Flush Periodic FIFOs */ ++ dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), 0x10); ++ dwc_otg_flush_rx_fifo(GET_CORE_IF(pcd)); ++ ++ /* prevent new request submissions, kill any outstanding requests */ ++ ep = &pcd->ep0; ++ dwc_otg_request_nuke(ep); ++ /* prevent new request submissions, kill any outstanding requests */ ++ for (i = 0; i < num_in_eps; i++) { ++ dwc_otg_pcd_ep_t *ep = &pcd->in_ep[i]; ++ dwc_otg_request_nuke(ep); ++ } ++ /* prevent new request submissions, kill any outstanding requests */ ++ for (i = 0; i < num_out_eps; i++) { ++ dwc_otg_pcd_ep_t *ep = &pcd->out_ep[i]; ++ dwc_otg_request_nuke(ep); ++ } ++ ++ /* report disconnect; the driver is already quiesced */ ++ if (pcd->fops->disconnect) { ++ DWC_SPINUNLOCK(pcd->lock); ++ pcd->fops->disconnect(pcd); ++ DWC_SPINLOCK(pcd->lock); ++ } ++ DWC_SPINUNLOCK(pcd->lock); ++} ++ ++/** ++ * This interrupt indicates that ... ++ */ ++int32_t dwc_otg_pcd_handle_i2c_intr(dwc_otg_pcd_t * pcd) ++{ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "i2cintr"); ++ intr_mask.b.i2cintr = 1; ++ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.i2cintr = 1; ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that ... ++ */ ++int32_t dwc_otg_pcd_handle_early_suspend_intr(dwc_otg_pcd_t * pcd) ++{ ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_PCDV,"Early Suspend Detected\n"); ++ ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.erlysuspend = 1; ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This function configures EPO to receive SETUP packets. ++ * ++ * @todo NGS: Update the comments from the HW FS. ++ * ++ * -# Program the following fields in the endpoint specific registers ++ * for Control OUT EP 0, in order to receive a setup packet ++ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back ++ * setup packets) ++ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back ++ * to back setup packets) ++ * - In DMA mode, DOEPDMA0 Register with a memory address to ++ * store any setup packets received ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param pcd Programming view of the PCD. ++ */ ++static inline void ep0_out_start(dwc_otg_core_if_t * core_if, ++ dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ deptsiz0_data_t doeptsize0 = {.d32 = 0 }; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ depctl_data_t doepctl = {.d32 = 0 }; ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, "%s() doepctl0=%0x\n", __func__, ++ DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl)); ++#endif ++ if (core_if->snpsid >= OTG_CORE_REV_3_00a) { ++ doepctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl); ++ if (doepctl.b.epena) { ++ return; ++ } ++ } ++ ++ doeptsize0.b.supcnt = 3; ++ doeptsize0.b.pktcnt = 1; ++ doeptsize0.b.xfersize = 8 * 3; ++ ++ if (core_if->dma_enable) { ++ if (!core_if->dma_desc_enable) { ++ /** put here as for Hermes mode deptisz register should not be written */ ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doeptsiz, ++ doeptsize0.d32); ++ ++ /** @todo dma needs to handle multiple setup packets (up to 3) */ ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepdma, ++ pcd->setup_pkt_dma_handle); ++ } else { ++ dev_if->setup_desc_index = ++ (dev_if->setup_desc_index + 1) & 1; ++ dma_desc = ++ dev_if->setup_desc_addr[dev_if->setup_desc_index]; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ if (core_if->snpsid >= OTG_CORE_REV_3_00a) { ++ dma_desc->status.b.sr = 0; ++ dma_desc->status.b.mtrf = 0; ++ } ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.bytes = pcd->ep0.dwc_ep.maxpacket; ++ dma_desc->buf = pcd->setup_pkt_dma_handle; ++ dma_desc->status.b.sts = 0; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DOEPDMA0 Register write */ ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepdma, ++ dev_if->dma_setup_desc_addr ++ [dev_if->setup_desc_index]); ++ } ++ ++ } else { ++ /** put here as for Hermes mode deptisz register should not be written */ ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doeptsiz, ++ doeptsize0.d32); ++ } ++ ++ /** DOEPCTL0 Register write cnak will be set after setup interrupt */ ++ doepctl.d32 = 0; ++ doepctl.b.epena = 1; ++ if (core_if->snpsid <= OTG_CORE_REV_2_94a) { ++ doepctl.b.cnak = 1; ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32); ++ } else { ++ DWC_MODIFY_REG32(&dev_if->out_ep_regs[0]->doepctl, 0, doepctl.d32); ++ } ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, "doepctl0=%0x\n", ++ DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl)); ++ DWC_DEBUGPL(DBG_PCDV, "diepctl0=%0x\n", ++ DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl)); ++#endif ++} ++ ++/** ++ * This interrupt occurs when a USB Reset is detected. When the USB ++ * Reset Interrupt occurs the device state is set to DEFAULT and the ++ * EP0 state is set to IDLE. ++ * -# Set the NAK bit for all OUT endpoints (DOEPCTLn.SNAK = 1) ++ * -# Unmask the following interrupt bits ++ * - DAINTMSK.INEP0 = 1 (Control 0 IN endpoint) ++ * - DAINTMSK.OUTEP0 = 1 (Control 0 OUT endpoint) ++ * - DOEPMSK.SETUP = 1 ++ * - DOEPMSK.XferCompl = 1 ++ * - DIEPMSK.XferCompl = 1 ++ * - DIEPMSK.TimeOut = 1 ++ * -# Program the following fields in the endpoint specific registers ++ * for Control OUT EP 0, in order to receive a setup packet ++ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back ++ * setup packets) ++ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back ++ * to back setup packets) ++ * - In DMA mode, DOEPDMA0 Register with a memory address to ++ * store any setup packets received ++ * At this point, all the required initialization, except for enabling ++ * the control 0 OUT endpoint is done, for receiving SETUP packets. ++ */ ++int32_t dwc_otg_pcd_handle_usb_reset_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ depctl_data_t doepctl = {.d32 = 0 }; ++ depctl_data_t diepctl = {.d32 = 0 }; ++ daint_data_t daintmsk = {.d32 = 0 }; ++ doepmsk_data_t doepmsk = {.d32 = 0 }; ++ diepmsk_data_t diepmsk = {.d32 = 0 }; ++ dcfg_data_t dcfg = {.d32 = 0 }; ++ grstctl_t resetctl = {.d32 = 0 }; ++ dctl_data_t dctl = {.d32 = 0 }; ++ int i = 0; ++ gintsts_data_t gintsts; ++ pcgcctl_data_t power = {.d32 = 0 }; ++ ++ power.d32 = DWC_READ_REG32(core_if->pcgcctl); ++ if (power.b.stoppclk) { ++ power.d32 = 0; ++ power.b.stoppclk = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0); ++ ++ power.b.pwrclmp = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0); ++ ++ power.b.rstpdwnmodule = 1; ++ DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0); ++ } ++ ++ core_if->lx_state = DWC_OTG_L0; ++ core_if->otg_sts = 0; ++ ++ ++#ifdef DWC_EN_ISOC ++ for (i = 1; i < 16; ++i) { ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ep = get_in_ep(pcd, i); ++ if (ep != 0) { ++ dwc_ep = &ep->dwc_ep; ++ dwc_ep->next_frame = 0xffffffff; ++ } ++ } ++#endif /* DWC_EN_ISOC */ ++ ++ /* reset the HNP settings */ ++ dwc_otg_pcd_update_otg(pcd, 1); ++ ++ /* Clear the Remote Wakeup Signalling */ ++ dctl.b.rmtwkupsig = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0); ++ ++ /* Set NAK for all OUT EPs */ ++ doepctl.b.snak = 1; ++ for (i = 0; i <= dev_if->num_out_eps; i++) { ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl, doepctl.d32); ++ } ++ ++ /* Flush the NP Tx FIFO */ ++ dwc_otg_flush_tx_fifo(core_if, 0x10); ++ /* Flush the Learning Queue */ ++ resetctl.b.intknqflsh = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32); ++ ++ if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) { ++ core_if->start_predict = 0; ++ for (i = 0; i <= core_if->dev_if->num_in_eps; ++i) { ++ core_if->nextep_seq[i] = 0xff; // 0xff - EP not active ++ } ++ core_if->nextep_seq[0] = 0; ++ core_if->first_in_nextep_seq = 0; ++ diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl); ++ diepctl.b.nextep = 0; ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32); ++ ++ /* Update IN Endpoint Mismatch Count by active IN NP EP count + 1 */ ++ dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg); ++ dcfg.b.epmscnt = 2; ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32); ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "%s first_in_nextep_seq= %2d; nextep_seq[]:\n", ++ __func__, core_if->first_in_nextep_seq); ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ DWC_DEBUGPL(DBG_PCDV, "%2d\n", core_if->nextep_seq[i]); ++ } ++ } ++ ++ if (core_if->multiproc_int_enable) { ++ daintmsk.b.inep0 = 1; ++ daintmsk.b.outep0 = 1; ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->deachintmsk, ++ daintmsk.d32); ++ ++ doepmsk.b.setup = 1; ++ doepmsk.b.xfercompl = 1; ++ doepmsk.b.ahberr = 1; ++ doepmsk.b.epdisabled = 1; ++ ++ if ((core_if->dma_desc_enable) || ++ (core_if->dma_enable ++ && core_if->snpsid >= OTG_CORE_REV_3_00a)) { ++ doepmsk.b.stsphsercvd = 1; ++ } ++ if (core_if->dma_desc_enable) ++ doepmsk.b.bna = 1; ++/* ++ doepmsk.b.babble = 1; ++ doepmsk.b.nyet = 1; ++ ++ if (core_if->dma_enable) { ++ doepmsk.b.nak = 1; ++ } ++*/ ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->doepeachintmsk[0], ++ doepmsk.d32); ++ ++ diepmsk.b.xfercompl = 1; ++ diepmsk.b.timeout = 1; ++ diepmsk.b.epdisabled = 1; ++ diepmsk.b.ahberr = 1; ++ diepmsk.b.intknepmis = 1; ++ if (!core_if->en_multiple_tx_fifo && core_if->dma_enable) ++ diepmsk.b.intknepmis = 0; ++ ++/* if (core_if->dma_desc_enable) { ++ diepmsk.b.bna = 1; ++ } ++*/ ++/* ++ if (core_if->dma_enable) { ++ diepmsk.b.nak = 1; ++ } ++*/ ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->diepeachintmsk[0], ++ diepmsk.d32); ++ } else { ++ daintmsk.b.inep0 = 1; ++ daintmsk.b.outep0 = 1; ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->daintmsk, ++ daintmsk.d32); ++ ++ doepmsk.b.setup = 1; ++ doepmsk.b.xfercompl = 1; ++ doepmsk.b.ahberr = 1; ++ doepmsk.b.epdisabled = 1; ++ ++ if ((core_if->dma_desc_enable) || ++ (core_if->dma_enable ++ && core_if->snpsid >= OTG_CORE_REV_3_00a)) { ++ doepmsk.b.stsphsercvd = 1; ++ } ++ if (core_if->dma_desc_enable) ++ doepmsk.b.bna = 1; ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->doepmsk, doepmsk.d32); ++ ++ diepmsk.b.xfercompl = 1; ++ diepmsk.b.timeout = 1; ++ diepmsk.b.epdisabled = 1; ++ diepmsk.b.ahberr = 1; ++ if (!core_if->en_multiple_tx_fifo && core_if->dma_enable) ++ diepmsk.b.intknepmis = 0; ++/* ++ if (core_if->dma_desc_enable) { ++ diepmsk.b.bna = 1; ++ } ++*/ ++ ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->diepmsk, diepmsk.d32); ++ } ++ ++ /* Reset Device Address */ ++ dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg); ++ dcfg.b.devaddr = 0; ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32); ++ ++ /* setup EP0 to receive SETUP packets */ ++ if (core_if->snpsid <= OTG_CORE_REV_2_94a) ++ ep0_out_start(core_if, pcd); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.usbreset = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * Get the device speed from the device status register and convert it ++ * to USB speed constant. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static int get_device_speed(dwc_otg_core_if_t * core_if) ++{ ++ dsts_data_t dsts; ++ int speed = 0; ++ dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ switch (dsts.b.enumspd) { ++ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: ++ speed = USB_SPEED_HIGH; ++ break; ++ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: ++ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ: ++ speed = USB_SPEED_FULL; ++ break; ++ ++ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ: ++ speed = USB_SPEED_LOW; ++ break; ++ } ++ ++ return speed; ++} ++ ++/** ++ * Read the device status register and set the device speed in the ++ * data structure. ++ * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate. ++ */ ++int32_t dwc_otg_pcd_handle_enum_done_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ gintsts_data_t gintsts; ++ gusbcfg_data_t gusbcfg; ++ dwc_otg_core_global_regs_t *global_regs = ++ GET_CORE_IF(pcd)->core_global_regs; ++ uint8_t utmi16b, utmi8b; ++ int speed; ++ dcfg_data_t dcfg; ++ ++ DWC_DEBUGPL(DBG_PCD, "SPEED ENUM\n"); ++ ++ /* WA for the case when SW gets SPEED ENUM without first USB RESET case ++ * due to USB RESET issued by the host earlier. Anyways USB Reset routine ++ * needs to be called to at least program EP 0 OUT - vahrama ++ */ ++ dcfg.d32 = DWC_READ_REG32(&pcd->core_if->dev_if->dev_global_regs->dcfg); ++ if (pcd->core_if->otg_ver && dcfg.b.devaddr) ++ dwc_otg_pcd_handle_usb_reset_intr(pcd); ++ ++ ++ if (GET_CORE_IF(pcd)->snpsid >= OTG_CORE_REV_2_60a) { ++ utmi16b = 6; //vahrama old value was 6; ++ utmi8b = 9; ++ } else { ++ utmi16b = 4; ++ utmi8b = 8; ++ } ++ dwc_otg_ep0_activate(GET_CORE_IF(pcd), &ep0->dwc_ep); ++ if (GET_CORE_IF(pcd)->snpsid >= OTG_CORE_REV_3_00a) { ++ ep0_out_start(GET_CORE_IF(pcd), pcd); ++ } ++ ++#ifdef DEBUG_EP0 ++ print_ep0_state(pcd); ++#endif ++ ++ if (pcd->ep0state == EP0_DISCONNECT) { ++ pcd->ep0state = EP0_IDLE; ++ } else if (pcd->ep0state == EP0_STALL) { ++ pcd->ep0state = EP0_IDLE; ++ } ++ ++ pcd->ep0state = EP0_IDLE; ++ ++ ep0->stopped = 0; ++ ++ speed = get_device_speed(GET_CORE_IF(pcd)); ++ pcd->fops->connect(pcd, speed); ++ ++ /* Set USB turnaround time based on device speed and PHY interface. */ ++ gusbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg); ++ if (speed == USB_SPEED_HIGH) { ++ if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == ++ DWC_HWCFG2_HS_PHY_TYPE_ULPI) { ++ /* ULPI interface */ ++ gusbcfg.b.usbtrdtim = 9; ++ } ++ if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == ++ DWC_HWCFG2_HS_PHY_TYPE_UTMI) { ++ /* UTMI+ interface */ ++ if (GET_CORE_IF(pcd)->hwcfg4.b.utmi_phy_data_width == 0) { ++ gusbcfg.b.usbtrdtim = utmi8b; ++ } else if (GET_CORE_IF(pcd)->hwcfg4. ++ b.utmi_phy_data_width == 1) { ++ gusbcfg.b.usbtrdtim = utmi16b; ++ } else if (GET_CORE_IF(pcd)-> ++ core_params->phy_utmi_width == 8) { ++ gusbcfg.b.usbtrdtim = utmi8b; ++ } else { ++ gusbcfg.b.usbtrdtim = utmi16b; ++ } ++ } ++ if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == ++ DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI) { ++ /* UTMI+ OR ULPI interface */ ++ if (gusbcfg.b.ulpi_utmi_sel == 1) { ++ /* ULPI interface */ ++ gusbcfg.b.usbtrdtim = 9; ++ } else { ++ /* UTMI+ interface */ ++ if (GET_CORE_IF(pcd)-> ++ core_params->phy_utmi_width == 16) { ++ gusbcfg.b.usbtrdtim = utmi16b; ++ } else { ++ gusbcfg.b.usbtrdtim = utmi8b; ++ } ++ } ++ } ++ } else { ++ /* Full or low speed */ ++ gusbcfg.b.usbtrdtim = 9; ++ } ++ DWC_WRITE_REG32(&global_regs->gusbcfg, gusbcfg.d32); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.enumdone = 1; ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that the ISO OUT Packet was dropped due to ++ * Rx FIFO full or Rx Status Queue Full. If this interrupt occurs ++ * read all the data from the Rx FIFO. ++ */ ++int32_t dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(dwc_otg_pcd_t * pcd) ++{ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ ++ DWC_WARN("INTERRUPT Handler not implemented for %s\n", ++ "ISOC Out Dropped"); ++ ++ intr_mask.b.isooutdrop = 1; ++ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.isooutdrop = 1; ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates the end of the portion of the micro-frame ++ * for periodic transactions. If there is a periodic transaction for ++ * the next frame, load the packets into the EP periodic Tx FIFO. ++ */ ++int32_t dwc_otg_pcd_handle_end_periodic_frame_intr(dwc_otg_pcd_t * pcd) ++{ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "EOP"); ++ ++ intr_mask.b.eopframe = 1; ++ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.eopframe = 1; ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that EP of the packet on the top of the ++ * non-periodic Tx FIFO does not match EP of the IN Token received. ++ * ++ * The "Device IN Token Queue" Registers are read to determine the ++ * order the IN Tokens have been received. The non-periodic Tx FIFO ++ * is flushed, so it can be reloaded in the order seen in the IN Token ++ * Queue. ++ */ ++int32_t dwc_otg_pcd_handle_ep_mismatch_intr(dwc_otg_pcd_t * pcd) ++{ ++ gintsts_data_t gintsts; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dctl_data_t dctl; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ if (!core_if->en_multiple_tx_fifo && core_if->dma_enable) { ++ core_if->start_predict = 1; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, core_if); ++ ++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ if (!gintsts.b.ginnakeff) { ++ /* Disable EP Mismatch interrupt */ ++ intr_mask.d32 = 0; ++ intr_mask.b.epmismatch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, intr_mask.d32, 0); ++ /* Enable the Global IN NAK Effective Interrupt */ ++ intr_mask.d32 = 0; ++ intr_mask.b.ginnakeff = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, intr_mask.d32); ++ /* Set the global non-periodic IN NAK handshake */ ++ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl); ++ dctl.b.sgnpinnak = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32); ++ } else { ++ DWC_PRINTF("gintsts.b.ginnakeff = 1! dctl.b.sgnpinnak not set\n"); ++ } ++ /* Disabling of all EP's will be done in dwc_otg_pcd_handle_in_nak_effective() ++ * handler after Global IN NAK Effective interrupt will be asserted */ ++ } ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.epmismatch = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt is valid only in DMA mode. This interrupt indicates that the ++ * core has stopped fetching data for IN endpoints due to the unavailability of ++ * TxFIFO space or Request Queue space. This interrupt is used by the ++ * application for an endpoint mismatch algorithm. ++ * ++ * @param pcd The PCD ++ */ ++int32_t dwc_otg_pcd_handle_ep_fetsusp_intr(dwc_otg_pcd_t * pcd) ++{ ++ gintsts_data_t gintsts; ++ gintmsk_data_t gintmsk_data; ++ dctl_data_t dctl; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, core_if); ++ ++ /* Clear the global non-periodic IN NAK handshake */ ++ dctl.d32 = 0; ++ dctl.b.cgnpinnak = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); ++ ++ /* Mask GINTSTS.FETSUSP interrupt */ ++ gintmsk_data.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); ++ gintmsk_data.b.fetsusp = 0; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_data.d32); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.fetsusp = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This funcion stalls EP0. ++ */ ++static inline void ep0_do_stall(dwc_otg_pcd_t * pcd, const int err_val) ++{ ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++// usb_device_request_t *ctrl = &pcd->setup_pkt->req; ++// DWC_WARN("req %02x.%02x protocol STALL; err %d\n", ++// ctrl->bmRequestType, ctrl->bRequest, err_val); ++ ++ ep0->dwc_ep.is_in = 1; ++ dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep0->dwc_ep); ++ ep0->dwc_ep.is_in = 0; ++ dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep0->dwc_ep); ++ pcd->ep0.stopped = 1; ++ pcd->ep0state = EP0_IDLE; ++ ep0_out_start(GET_CORE_IF(pcd), pcd); ++} ++ ++/** ++ * This functions delegates the setup command to the gadget driver. ++ */ ++static inline void do_gadget_setup(dwc_otg_pcd_t * pcd, ++ usb_device_request_t * ctrl) ++{ ++ int ret = 0; ++ DWC_SPINUNLOCK(pcd->lock); ++ ret = pcd->fops->setup(pcd, (uint8_t *) ctrl); ++ DWC_SPINLOCK(pcd->lock); ++ if (ret < 0) { ++ ep0_do_stall(pcd, ret); ++ } ++ ++ /** @todo This is a g_file_storage gadget driver specific ++ * workaround: a DELAYED_STATUS result from the fsg_setup ++ * routine will result in the gadget queueing a EP0 IN status ++ * phase for a two-stage control transfer. Exactly the same as ++ * a SET_CONFIGURATION/SET_INTERFACE except that this is a class ++ * specific request. Need a generic way to know when the gadget ++ * driver will queue the status phase. Can we assume when we ++ * call the gadget driver setup() function that it will always ++ * queue and require the following flag? Need to look into ++ * this. ++ */ ++ ++ if (ret == 256 + 999) { ++ pcd->request_config = 1; ++ } ++} ++ ++#ifdef DWC_UTE_CFI ++/** ++ * This functions delegates the CFI setup commands to the gadget driver. ++ * This function will return a negative value to indicate a failure. ++ */ ++static inline int cfi_gadget_setup(dwc_otg_pcd_t * pcd, ++ struct cfi_usb_ctrlrequest *ctrl_req) ++{ ++ int ret = 0; ++ ++ if (pcd->fops && pcd->fops->cfi_setup) { ++ DWC_SPINUNLOCK(pcd->lock); ++ ret = pcd->fops->cfi_setup(pcd, ctrl_req); ++ DWC_SPINLOCK(pcd->lock); ++ if (ret < 0) { ++ ep0_do_stall(pcd, ret); ++ return ret; ++ } ++ } ++ ++ return ret; ++} ++#endif ++ ++/** ++ * This function starts the Zero-Length Packet for the IN status phase ++ * of a 2 stage control transfer. ++ */ ++static inline void do_setup_in_status_phase(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ if (pcd->ep0state == EP0_STALL) { ++ return; ++ } ++ ++ pcd->ep0state = EP0_IN_STATUS_PHASE; ++ ++ /* Prepare for more SETUP Packets */ ++ DWC_DEBUGPL(DBG_PCD, "EP0 IN ZLP\n"); ++ if ((GET_CORE_IF(pcd)->snpsid >= OTG_CORE_REV_3_00a) ++ && (pcd->core_if->dma_desc_enable) ++ && (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len)) { ++ DWC_DEBUGPL(DBG_PCDV, ++ "Data terminated wait next packet in out_desc_addr\n"); ++ pcd->backup_buf = phys_to_virt(ep0->dwc_ep.dma_addr); ++ pcd->data_terminated = 1; ++ } ++ ep0->dwc_ep.xfer_len = 0; ++ ep0->dwc_ep.xfer_count = 0; ++ ep0->dwc_ep.is_in = 1; ++ ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle; ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); ++ ++ /* Prepare for more SETUP Packets */ ++ //ep0_out_start(GET_CORE_IF(pcd), pcd); ++} ++ ++/** ++ * This function starts the Zero-Length Packet for the OUT status phase ++ * of a 2 stage control transfer. ++ */ ++static inline void do_setup_out_status_phase(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ doepint_data_t doepint; ++ doepint.d32 = DWC_READ_REG32(&pcd->core_if->dev_if->out_ep_regs[0]->doepint); ++ if (pcd->ep0state == EP0_STALL) { ++ DWC_DEBUGPL(DBG_PCD, "EP0 STALLED\n"); ++ return; ++ } ++ pcd->ep0state = EP0_OUT_STATUS_PHASE; ++ ++ DWC_DEBUGPL(DBG_PCD, "EP0 OUT ZLP\n"); ++ ep0->dwc_ep.xfer_len = 0; ++ ep0->dwc_ep.xfer_count = 0; ++ ep0->dwc_ep.is_in = 0; ++ ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle; ++ /* If there is xfercomplete on EP0 OUT do not start OUT Status stage. ++ * xfercomplete means that ZLP was already received as EP0 OUT is enabled ++ * during IN Data stage ++ */ ++ if ((doepint.b.xfercompl == 1) && (pcd->core_if->snpsid >= OTG_CORE_REV_3_00a) ++ && (pcd->core_if->dma_enable == 1) && (pcd->core_if->dma_desc_enable == 0)) { ++ DWC_DEBUGPL(DBG_PCD, "Status stage already completed\n"); ++ return; ++ } ++ ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); ++ ++ /* Prepare for more SETUP Packets */ ++ if (GET_CORE_IF(pcd)->dma_enable == 0) { ++ ep0_out_start(GET_CORE_IF(pcd), pcd); ++ } ++} ++ ++/** ++ * Clear the EP halt (STALL) and if pending requests start the ++ * transfer. ++ */ ++static inline void pcd_clear_halt(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep) ++{ ++ if (ep->dwc_ep.stall_clear_flag) { ++ /* Start Control Status Phase */ ++ do_setup_in_status_phase(pcd); ++ return; ++ } ++ ++ dwc_otg_ep_clear_stall(GET_CORE_IF(pcd), &ep->dwc_ep); ++ ++ /* Reactive the EP */ ++ dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep); ++ if (ep->stopped) { ++ ep->stopped = 0; ++ /* If there is a request in the EP queue start it */ ++ ++ /** @todo FIXME: this causes an EP mismatch in DMA mode. ++ * epmismatch not yet implemented. */ ++ ++ /* ++ * Above fixme is solved by implmenting a tasklet to call the ++ * start_next_request(), outside of interrupt context at some ++ * time after the current time, after a clear-halt setup packet. ++ * Still need to implement ep mismatch in the future if a gadget ++ * ever uses more than one endpoint at once ++ */ ++ ep->queue_sof = 1; ++ DWC_TASK_SCHEDULE(pcd->start_xfer_tasklet); ++ } ++ /* Start Control Status Phase */ ++ do_setup_in_status_phase(pcd); ++} ++ ++/** ++ * This function is called when the SET_FEATURE TEST_MODE Setup packet ++ * is sent from the host. The Device Control register is written with ++ * the Test Mode bits set to the specified Test Mode. This is done as ++ * a tasklet so that the "Status" phase of the control transfer ++ * completes before transmitting the TEST packets. ++ * ++ * @todo This has not been tested since the tasklet struct was put ++ * into the PCD struct! ++ * ++ */ ++void do_test_mode(void *data) ++{ ++ dctl_data_t dctl; ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) data; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ int test_mode = pcd->test_mode; ++ ++// DWC_WARN("%s() has not been tested since being rewritten!\n", __func__); ++ ++ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl); ++ switch (test_mode) { ++ case 1: // TEST_J ++ dctl.b.tstctl = 1; ++ break; ++ ++ case 2: // TEST_K ++ dctl.b.tstctl = 2; ++ break; ++ ++ case 3: // TEST_SE0_NAK ++ dctl.b.tstctl = 3; ++ break; ++ ++ case 4: // TEST_PACKET ++ dctl.b.tstctl = 4; ++ break; ++ ++ case 5: // TEST_FORCE_ENABLE ++ dctl.b.tstctl = 5; ++ break; ++ case 7: ++ dwc_otg_set_hnpreq(core_if, 1); ++ } ++ DWC_PRINTF("test mode = %d\n",test_mode); ++ core_if->test_mode = test_mode; ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32); ++} ++ ++/** ++ * This function process the GET_STATUS Setup Commands. ++ */ ++static inline void do_get_status(dwc_otg_pcd_t * pcd) ++{ ++ usb_device_request_t ctrl = pcd->setup_pkt->req; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ uint16_t *status = pcd->status_buf; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, ++ "GET_STATUS %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, ++ UGETW(ctrl.wValue), UGETW(ctrl.wIndex), ++ UGETW(ctrl.wLength)); ++#endif ++ ++ switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) { ++ case UT_DEVICE: ++ if (UGETW(ctrl.wIndex) == 0xF000) { /* OTG Status selector */ ++ DWC_PRINTF("wIndex - %d\n", UGETW(ctrl.wIndex)); ++ DWC_PRINTF("OTG VERSION - %d\n", core_if->otg_ver); ++ DWC_PRINTF("OTG CAP - %d, %d\n", ++ core_if->core_params->otg_cap, ++ DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE); ++ if (core_if->otg_ver == 1 ++ && core_if->core_params->otg_cap == ++ DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { ++ uint8_t *otgsts = (uint8_t *) pcd->status_buf; ++ *otgsts = (core_if->otg_sts & 0x1); ++ pcd->ep0_pending = 1; ++ ep0->dwc_ep.start_xfer_buff = ++ (uint8_t *) otgsts; ++ ep0->dwc_ep.xfer_buff = (uint8_t *) otgsts; ++ ep0->dwc_ep.dma_addr = ++ pcd->status_buf_dma_handle; ++ ep0->dwc_ep.xfer_len = 1; ++ ep0->dwc_ep.xfer_count = 0; ++ ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len; ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), ++ &ep0->dwc_ep); ++ return; ++ } else { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ break; ++ } else { ++ *status = 0x1; /* Self powered */ ++ *status |= pcd->remote_wakeup_enable << 1; ++ break; ++ } ++ case UT_INTERFACE: ++ *status = 0; ++ break; ++ ++ case UT_ENDPOINT: ++ ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex)); ++ if (ep == 0 || UGETW(ctrl.wLength) > 2) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ /** @todo check for EP stall */ ++ *status = ep->stopped; ++ break; ++ } ++ pcd->ep0_pending = 1; ++ ep0->dwc_ep.start_xfer_buff = (uint8_t *) status; ++ ep0->dwc_ep.xfer_buff = (uint8_t *) status; ++ ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle; ++ ep0->dwc_ep.xfer_len = 2; ++ ep0->dwc_ep.xfer_count = 0; ++ ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len; ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); ++} ++ ++/** ++ * This function process the SET_FEATURE Setup Commands. ++ */ ++static inline void do_set_feature(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ usb_device_request_t ctrl = pcd->setup_pkt->req; ++ dwc_otg_pcd_ep_t *ep = 0; ++ int32_t otg_cap_param = core_if->core_params->otg_cap; ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_PCD, "SET_FEATURE:%02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, ++ UGETW(ctrl.wValue), UGETW(ctrl.wIndex), ++ UGETW(ctrl.wLength)); ++ DWC_DEBUGPL(DBG_PCD, "otg_cap=%d\n", otg_cap_param); ++ ++ switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) { ++ case UT_DEVICE: ++ switch (UGETW(ctrl.wValue)) { ++ case UF_DEVICE_REMOTE_WAKEUP: ++ pcd->remote_wakeup_enable = 1; ++ break; ++ ++ case UF_TEST_MODE: ++ /* Setup the Test Mode tasklet to do the Test ++ * Packet generation after the SETUP Status ++ * phase has completed. */ ++ ++ /** @todo This has not been tested since the ++ * tasklet struct was put into the PCD ++ * struct! */ ++ pcd->test_mode = UGETW(ctrl.wIndex) >> 8; ++ DWC_TASK_SCHEDULE(pcd->test_mode_tasklet); ++ break; ++ ++ case UF_DEVICE_B_HNP_ENABLE: ++ DWC_DEBUGPL(DBG_PCDV, ++ "SET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); ++ ++ /* dev may initiate HNP */ ++ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { ++ gotgctl.b.devhnpen = 1; ++ if (core_if->otg_ver) { ++ DWC_MODIFY_REG32(&global_regs->gotgctl, 0, gotgctl.d32); ++ /* Ensure that USB Suspend interrupt is unmasked */ ++ gintmsk.b.usbsuspend = 1; ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } ++ else { ++ pcd->b_hnp_enable = 1; ++ dwc_otg_pcd_update_otg(pcd, 0); ++ DWC_DEBUGPL(DBG_PCD, "Request B HNP\n"); ++ /**@todo Is the gotgctl.devhnpen cleared ++ * by a USB Reset? */ ++ gotgctl.b.hnpreq = 1; ++ DWC_WRITE_REG32(&global_regs->gotgctl, gotgctl.d32); ++ } ++ } else { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ break; ++ ++ case UF_DEVICE_A_HNP_SUPPORT: ++ /* RH port supports HNP */ ++ DWC_DEBUGPL(DBG_PCDV, ++ "SET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n"); ++ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { ++ pcd->a_hnp_support = 1; ++ dwc_otg_pcd_update_otg(pcd, 0); ++ } else { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ break; ++ ++ case UF_DEVICE_A_ALT_HNP_SUPPORT: ++ /* other RH port does */ ++ DWC_DEBUGPL(DBG_PCDV, ++ "SET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); ++ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { ++ pcd->a_alt_hnp_support = 1; ++ dwc_otg_pcd_update_otg(pcd, 0); ++ } else { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ break; ++ ++ default: ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ ++ } ++ do_setup_in_status_phase(pcd); ++ break; ++ ++ case UT_INTERFACE: ++ do_gadget_setup(pcd, &ctrl); ++ break; ++ ++ case UT_ENDPOINT: ++ if (UGETW(ctrl.wValue) == UF_ENDPOINT_HALT) { ++ ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex)); ++ if (ep == 0) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ep->stopped = 1; ++ dwc_otg_ep_set_stall(core_if, &ep->dwc_ep); ++ } ++ do_setup_in_status_phase(pcd); ++ break; ++ } ++} ++ ++/** ++ * This function process the CLEAR_FEATURE Setup Commands. ++ */ ++static inline void do_clear_feature(dwc_otg_pcd_t * pcd) ++{ ++ usb_device_request_t ctrl = pcd->setup_pkt->req; ++ dwc_otg_pcd_ep_t *ep = 0; ++ ++ DWC_DEBUGPL(DBG_PCD, ++ "CLEAR_FEATURE:%02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, ++ UGETW(ctrl.wValue), UGETW(ctrl.wIndex), ++ UGETW(ctrl.wLength)); ++ ++ switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) { ++ case UT_DEVICE: ++ switch (UGETW(ctrl.wValue)) { ++ case UF_DEVICE_REMOTE_WAKEUP: ++ pcd->remote_wakeup_enable = 0; ++ break; ++ ++ case UF_TEST_MODE: ++ /** @todo Add CLEAR_FEATURE for TEST modes. */ ++ break; ++ ++ default: ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ do_setup_in_status_phase(pcd); ++ break; ++ ++ case UT_ENDPOINT: ++ ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex)); ++ if (ep == 0) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ pcd_clear_halt(pcd, ep); ++ ++ break; ++ } ++} ++ ++/** ++ * This function process the SET_ADDRESS Setup Commands. ++ */ ++static inline void do_set_address(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if; ++ usb_device_request_t ctrl = pcd->setup_pkt->req; ++ ++ if (ctrl.bmRequestType == UT_DEVICE) { ++ dcfg_data_t dcfg = {.d32 = 0 }; ++ ++#ifdef DEBUG_EP0 ++// DWC_DEBUGPL(DBG_PCDV, "SET_ADDRESS:%d\n", ctrl.wValue); ++#endif ++ dcfg.b.devaddr = UGETW(ctrl.wValue); ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dcfg, 0, dcfg.d32); ++ do_setup_in_status_phase(pcd); ++ } ++} ++ ++/** ++ * This function processes SETUP commands. In Linux, the USB Command ++ * processing is done in two places - the first being the PCD and the ++ * second in the Gadget Driver (for example, the File-Backed Storage ++ * Gadget Driver). ++ * ++ * <table> ++ * <tr><td>Command </td><td>Driver </td><td>Description</td></tr> ++ * ++ * <tr><td>GET_STATUS </td><td>PCD </td><td>Command is processed as ++ * defined in chapter 9 of the USB 2.0 Specification chapter 9 ++ * </td></tr> ++ * ++ * <tr><td>CLEAR_FEATURE </td><td>PCD </td><td>The Device and Endpoint ++ * requests are the ENDPOINT_HALT feature is procesed, all others the ++ * interface requests are ignored.</td></tr> ++ * ++ * <tr><td>SET_FEATURE </td><td>PCD </td><td>The Device and Endpoint ++ * requests are processed by the PCD. Interface requests are passed ++ * to the Gadget Driver.</td></tr> ++ * ++ * <tr><td>SET_ADDRESS </td><td>PCD </td><td>Program the DCFG reg, ++ * with device address received </td></tr> ++ * ++ * <tr><td>GET_DESCRIPTOR </td><td>Gadget Driver </td><td>Return the ++ * requested descriptor</td></tr> ++ * ++ * <tr><td>SET_DESCRIPTOR </td><td>Gadget Driver </td><td>Optional - ++ * not implemented by any of the existing Gadget Drivers.</td></tr> ++ * ++ * <tr><td>SET_CONFIGURATION </td><td>Gadget Driver </td><td>Disable ++ * all EPs and enable EPs for new configuration.</td></tr> ++ * ++ * <tr><td>GET_CONFIGURATION </td><td>Gadget Driver </td><td>Return ++ * the current configuration</td></tr> ++ * ++ * <tr><td>SET_INTERFACE </td><td>Gadget Driver </td><td>Disable all ++ * EPs and enable EPs for new configuration.</td></tr> ++ * ++ * <tr><td>GET_INTERFACE </td><td>Gadget Driver </td><td>Return the ++ * current interface.</td></tr> ++ * ++ * <tr><td>SYNC_FRAME </td><td>PCD </td><td>Display debug ++ * message.</td></tr> ++ * </table> ++ * ++ * When the SETUP Phase Done interrupt occurs, the PCD SETUP commands are ++ * processed by pcd_setup. Calling the Function Driver's setup function from ++ * pcd_setup processes the gadget SETUP commands. ++ */ ++static inline void pcd_setup(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ usb_device_request_t ctrl = pcd->setup_pkt->req; ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ ++ deptsiz0_data_t doeptsize0 = {.d32 = 0 }; ++ ++#ifdef DWC_UTE_CFI ++ int retval = 0; ++ struct cfi_usb_ctrlrequest cfi_req; ++#endif ++ ++ doeptsize0.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[0]->doeptsiz); ++ ++ /** In BDMA more then 1 setup packet is not supported till 3.00a */ ++ if (core_if->dma_enable && core_if->dma_desc_enable == 0 ++ && (doeptsize0.b.supcnt < 2) ++ && (core_if->snpsid < OTG_CORE_REV_2_94a)) { ++ DWC_ERROR ++ ("\n\n----------- CANNOT handle > 1 setup packet in DMA mode\n\n"); ++ } ++ if ((core_if->snpsid >= OTG_CORE_REV_3_00a) ++ && (core_if->dma_enable == 1) && (core_if->dma_desc_enable == 0)) { ++ if (doeptsize0.b.supcnt == 3 && ep0->dwc_ep.stp_rollover == 0) { ++ DWC_ERROR(" !!! Setup packet count was not updated by the core\n"); ++ return; ++ } ++ ctrl = ++ (pcd->setup_pkt + ++ (3 - doeptsize0.b.supcnt - 1 + ++ ep0->dwc_ep.stp_rollover))->req; ++ } ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "SETUP %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, ++ UGETW(ctrl.wValue), UGETW(ctrl.wIndex), ++ UGETW(ctrl.wLength)); ++#endif ++ ++ /* Clean up the request queue */ ++ dwc_otg_request_nuke(ep0); ++ ep0->stopped = 0; ++ ++ if (ctrl.bmRequestType & UE_DIR_IN) { ++ ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_DATA_PHASE; ++ } else { ++ ep0->dwc_ep.is_in = 0; ++ pcd->ep0state = EP0_OUT_DATA_PHASE; ++ } ++ ++ if (UGETW(ctrl.wLength) == 0) { ++ ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_STATUS_PHASE; ++ } ++ ++ if (UT_GET_TYPE(ctrl.bmRequestType) != UT_STANDARD) { ++ ++#ifdef DWC_UTE_CFI ++ DWC_MEMCPY(&cfi_req, &ctrl, sizeof(usb_device_request_t)); ++ ++ //printk(KERN_ALERT "CFI: req_type=0x%02x; req=0x%02x\n", ++ ctrl.bRequestType, ctrl.bRequest); ++ if (UT_GET_TYPE(cfi_req.bRequestType) == UT_VENDOR) { ++ if (cfi_req.bRequest > 0xB0 && cfi_req.bRequest < 0xBF) { ++ retval = cfi_setup(pcd, &cfi_req); ++ if (retval < 0) { ++ ep0_do_stall(pcd, retval); ++ pcd->ep0_pending = 0; ++ return; ++ } ++ ++ /* if need gadget setup then call it and check the retval */ ++ if (pcd->cfi->need_gadget_att) { ++ retval = ++ cfi_gadget_setup(pcd, ++ &pcd-> ++ cfi->ctrl_req); ++ if (retval < 0) { ++ pcd->ep0_pending = 0; ++ return; ++ } ++ } ++ ++ if (pcd->cfi->need_status_in_complete) { ++ do_setup_in_status_phase(pcd); ++ } ++ return; ++ } ++ } ++#endif ++ ++ /* handle non-standard (class/vendor) requests in the gadget driver */ ++ do_gadget_setup(pcd, &ctrl); ++ return; ++ } ++ ++ /** @todo NGS: Handle bad setup packet? */ ++ ++/////////////////////////////////////////// ++//// --- Standard Request handling --- //// ++ ++ switch (ctrl.bRequest) { ++ case UR_GET_STATUS: ++ do_get_status(pcd); ++ break; ++ ++ case UR_CLEAR_FEATURE: ++ do_clear_feature(pcd); ++ break; ++ ++ case UR_SET_FEATURE: ++ do_set_feature(pcd); ++ break; ++ ++ case UR_SET_ADDRESS: ++ do_set_address(pcd); ++ break; ++ ++ case UR_SET_INTERFACE: ++ case UR_SET_CONFIG: ++// _pcd->request_config = 1; /* Configuration changed */ ++ do_gadget_setup(pcd, &ctrl); ++ break; ++ ++ case UR_SYNCH_FRAME: ++ do_gadget_setup(pcd, &ctrl); ++ break; ++ ++ default: ++ /* Call the Gadget Driver's setup functions */ ++ do_gadget_setup(pcd, &ctrl); ++ break; ++ } ++} ++ ++/** ++ * This function completes the ep0 control transfer. ++ */ ++static int32_t ep0_complete_request(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dwc_otg_dev_in_ep_regs_t *in_ep_regs = ++ dev_if->in_ep_regs[ep->dwc_ep.num]; ++#ifdef DEBUG_EP0 ++ dwc_otg_dev_out_ep_regs_t *out_ep_regs = ++ dev_if->out_ep_regs[ep->dwc_ep.num]; ++#endif ++ deptsiz0_data_t deptsiz; ++ dev_dma_desc_sts_t desc_sts = {.d32 = 0 }; ++ dwc_otg_pcd_request_t *req; ++ int is_last = 0; ++ dwc_otg_pcd_t *pcd = ep->pcd; ++ ++#ifdef DWC_UTE_CFI ++ struct cfi_usb_ctrlrequest *ctrlreq; ++ int retval = -DWC_E_NOT_SUPPORTED; ++#endif ++ ++ if (pcd->ep0_pending && DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ if (ep->dwc_ep.is_in) { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "Do setup OUT status phase\n"); ++#endif ++ do_setup_out_status_phase(pcd); ++ } else { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "Do setup IN status phase\n"); ++#endif ++ ++#ifdef DWC_UTE_CFI ++ ctrlreq = &pcd->cfi->ctrl_req; ++ ++ if (UT_GET_TYPE(ctrlreq->bRequestType) == UT_VENDOR) { ++ if (ctrlreq->bRequest > 0xB0 ++ && ctrlreq->bRequest < 0xBF) { ++ ++ /* Return if the PCD failed to handle the request */ ++ if ((retval = ++ pcd->cfi->ops. ++ ctrl_write_complete(pcd->cfi, ++ pcd)) < 0) { ++ CFI_INFO ++ ("ERROR setting a new value in the PCD(%d)\n", ++ retval); ++ ep0_do_stall(pcd, retval); ++ pcd->ep0_pending = 0; ++ return 0; ++ } ++ ++ /* If the gadget needs to be notified on the request */ ++ if (pcd->cfi->need_gadget_att == 1) { ++ //retval = do_gadget_setup(pcd, &pcd->cfi->ctrl_req); ++ retval = ++ cfi_gadget_setup(pcd, ++ &pcd->cfi-> ++ ctrl_req); ++ ++ /* Return from the function if the gadget failed to process ++ * the request properly - this should never happen !!! ++ */ ++ if (retval < 0) { ++ CFI_INFO ++ ("ERROR setting a new value in the gadget(%d)\n", ++ retval); ++ pcd->ep0_pending = 0; ++ return 0; ++ } ++ } ++ ++ CFI_INFO("%s: RETVAL=%d\n", __func__, ++ retval); ++ /* If we hit here then the PCD and the gadget has properly ++ * handled the request - so send the ZLP IN to the host. ++ */ ++ /* @todo: MAS - decide whether we need to start the setup ++ * stage based on the need_setup value of the cfi object ++ */ ++ do_setup_in_status_phase(pcd); ++ pcd->ep0_pending = 0; ++ return 1; ++ } ++ } ++#endif ++ ++ do_setup_in_status_phase(pcd); ++ } ++ pcd->ep0_pending = 0; ++ return 1; ++ } ++ ++ if (DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ return 0; ++ } ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ ++ if (pcd->ep0state == EP0_OUT_STATUS_PHASE ++ || pcd->ep0state == EP0_IN_STATUS_PHASE) { ++ is_last = 1; ++ } else if (ep->dwc_ep.is_in) { ++ deptsiz.d32 = DWC_READ_REG32(&in_ep_regs->dieptsiz); ++ if (core_if->dma_desc_enable != 0) ++ desc_sts = dev_if->in_desc_addr->status; ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "%d len=%d xfersize=%d pktcnt=%d\n", ++ ep->dwc_ep.num, ep->dwc_ep.xfer_len, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++#endif ++ ++ if (((core_if->dma_desc_enable == 0) ++ && (deptsiz.b.xfersize == 0)) ++ || ((core_if->dma_desc_enable != 0) ++ && (desc_sts.b.bytes == 0))) { ++ req->actual = ep->dwc_ep.xfer_count; ++ /* Is a Zero Len Packet needed? */ ++ if (req->sent_zlp) { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "Setup Rx ZLP\n"); ++#endif ++ req->sent_zlp = 0; ++ } ++ do_setup_out_status_phase(pcd); ++ } ++ } else { ++ /* ep0-OUT */ ++#ifdef DEBUG_EP0 ++ deptsiz.d32 = DWC_READ_REG32(&out_ep_regs->doeptsiz); ++ DWC_DEBUGPL(DBG_PCDV, "%d len=%d xsize=%d pktcnt=%d\n", ++ ep->dwc_ep.num, ep->dwc_ep.xfer_len, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++#endif ++ req->actual = ep->dwc_ep.xfer_count; ++ ++ /* Is a Zero Len Packet needed? */ ++ if (req->sent_zlp) { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "Setup Tx ZLP\n"); ++#endif ++ req->sent_zlp = 0; ++ } ++ /* For older cores do setup in status phase in Slave/BDMA modes, ++ * starting from 3.00 do that only in slave, and for DMA modes ++ * just re-enable ep 0 OUT here*/ ++ if (core_if->dma_enable == 0 ++ || (core_if->dma_desc_enable == 0 ++ && core_if->snpsid <= OTG_CORE_REV_2_94a)) { ++ do_setup_in_status_phase(pcd); ++ } else if (core_if->snpsid >= OTG_CORE_REV_3_00a) { ++ DWC_DEBUGPL(DBG_PCDV, ++ "Enable out ep before in status phase\n"); ++ ep0_out_start(core_if, pcd); ++ } ++ } ++ ++ /* Complete the request */ ++ if (is_last) { ++ dwc_otg_request_done(ep, req, 0); ++ ep->dwc_ep.start_xfer_buff = 0; ++ ep->dwc_ep.xfer_buff = 0; ++ ep->dwc_ep.xfer_len = 0; ++ return 1; ++ } ++ return 0; ++} ++ ++#ifdef DWC_UTE_CFI ++/** ++ * This function calculates traverses all the CFI DMA descriptors and ++ * and accumulates the bytes that are left to be transfered. ++ * ++ * @return The total bytes left to transfered, or a negative value as failure ++ */ ++static inline int cfi_calc_desc_residue(dwc_otg_pcd_ep_t * ep) ++{ ++ int32_t ret = 0; ++ int i; ++ struct dwc_otg_dma_desc *ddesc = NULL; ++ struct cfi_ep *cfiep; ++ ++ /* See if the pcd_ep has its respective cfi_ep mapped */ ++ cfiep = get_cfi_ep_by_pcd_ep(ep->pcd->cfi, ep); ++ if (!cfiep) { ++ CFI_INFO("%s: Failed to find ep\n", __func__); ++ return -1; ++ } ++ ++ ddesc = ep->dwc_ep.descs; ++ ++ for (i = 0; (i < cfiep->desc_count) && (i < MAX_DMA_DESCS_PER_EP); i++) { ++ ++#if defined(PRINT_CFI_DMA_DESCS) ++ print_desc(ddesc, ep->ep.name, i); ++#endif ++ ret += ddesc->status.b.bytes; ++ ddesc++; ++ } ++ ++ if (ret) ++ CFI_INFO("!!!!!!!!!! WARNING (%s) - residue=%d\n", __func__, ++ ret); ++ ++ return ret; ++} ++#endif ++ ++/** ++ * This function completes the request for the EP. If there are ++ * additional requests for the EP in the queue they will be started. ++ */ ++static void complete_ep(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dwc_otg_dev_in_ep_regs_t *in_ep_regs = ++ dev_if->in_ep_regs[ep->dwc_ep.num]; ++ deptsiz_data_t deptsiz; ++ dev_dma_desc_sts_t desc_sts; ++ dwc_otg_pcd_request_t *req = 0; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ uint32_t byte_count = 0; ++ int is_last = 0; ++ int i; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s() %d-%s\n", __func__, ep->dwc_ep.num, ++ (ep->dwc_ep.is_in ? "IN" : "OUT")); ++ ++ /* Get any pending requests */ ++ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ if (!req) { ++ DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep); ++ return; ++ } ++ } else { ++ DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep); ++ return; ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "Requests %d\n", ep->pcd->request_pending); ++ ++ if (ep->dwc_ep.is_in) { ++ deptsiz.d32 = DWC_READ_REG32(&in_ep_regs->dieptsiz); ++ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable == 0) { ++ if (deptsiz.b.xfersize == 0 ++ && deptsiz.b.pktcnt == 0) { ++ byte_count = ++ ep->dwc_ep.xfer_len - ++ ep->dwc_ep.xfer_count; ++ ++ ep->dwc_ep.xfer_buff += byte_count; ++ ep->dwc_ep.dma_addr += byte_count; ++ ep->dwc_ep.xfer_count += byte_count; ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "%d-%s len=%d xfersize=%d pktcnt=%d\n", ++ ep->dwc_ep.num, ++ (ep->dwc_ep. ++ is_in ? "IN" : "OUT"), ++ ep->dwc_ep.xfer_len, ++ deptsiz.b.xfersize, ++ deptsiz.b.pktcnt); ++ ++ if (ep->dwc_ep.xfer_len < ++ ep->dwc_ep.total_len) { ++ dwc_otg_ep_start_transfer ++ (core_if, &ep->dwc_ep); ++ } else if (ep->dwc_ep.sent_zlp) { ++ /* ++ * This fragment of code should initiate 0 ++ * length transfer in case if it is queued ++ * a transfer with size divisible to EPs max ++ * packet size and with usb_request zero field ++ * is set, which means that after data is transfered, ++ * it is also should be transfered ++ * a 0 length packet at the end. For Slave and ++ * Buffer DMA modes in this case SW has ++ * to initiate 2 transfers one with transfer size, ++ * and the second with 0 size. For Descriptor ++ * DMA mode SW is able to initiate a transfer, ++ * which will handle all the packets including ++ * the last 0 length. ++ */ ++ ep->dwc_ep.sent_zlp = 0; ++ dwc_otg_ep_start_zl_transfer ++ (core_if, &ep->dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } else { ++ if (ep->dwc_ep.type == ++ DWC_OTG_EP_TYPE_ISOC) { ++ req->actual = 0; ++ dwc_otg_request_done(ep, req, 0); ++ ++ ep->dwc_ep.start_xfer_buff = 0; ++ ep->dwc_ep.xfer_buff = 0; ++ ep->dwc_ep.xfer_len = 0; ++ ++ /* If there is a request in the queue start it. */ ++ start_next_request(ep); ++ } else ++ DWC_WARN ++ ("Incomplete transfer (%d - %s [siz=%d pkt=%d])\n", ++ ep->dwc_ep.num, ++ (ep->dwc_ep.is_in ? "IN" : "OUT"), ++ deptsiz.b.xfersize, ++ deptsiz.b.pktcnt); ++ } ++ } else { ++ dma_desc = ep->dwc_ep.desc_addr; ++ byte_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ++#ifdef DWC_UTE_CFI ++ CFI_INFO("%s: BUFFER_MODE=%d\n", __func__, ++ ep->dwc_ep.buff_mode); ++ if (ep->dwc_ep.buff_mode != BM_STANDARD) { ++ int residue; ++ ++ residue = cfi_calc_desc_residue(ep); ++ if (residue < 0) ++ return; ++ ++ byte_count = residue; ++ } else { ++#endif ++ for (i = 0; i < ep->dwc_ep.desc_cnt; ++ ++i) { ++ desc_sts = dma_desc->status; ++ byte_count += desc_sts.b.bytes; ++ dma_desc++; ++ } ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ if (byte_count == 0) { ++ ep->dwc_ep.xfer_count = ++ ep->dwc_ep.total_len; ++ is_last = 1; ++ } else { ++ DWC_WARN("Incomplete transfer\n"); ++ } ++ } ++ } else { ++ if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0) { ++ DWC_DEBUGPL(DBG_PCDV, ++ "%d-%s len=%d xfersize=%d pktcnt=%d\n", ++ ep->dwc_ep.num, ++ ep->dwc_ep.is_in ? "IN" : "OUT", ++ ep->dwc_ep.xfer_len, ++ deptsiz.b.xfersize, ++ deptsiz.b.pktcnt); ++ ++ /* Check if the whole transfer was completed, ++ * if no, setup transfer for next portion of data ++ */ ++ if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { ++ dwc_otg_ep_start_transfer(core_if, ++ &ep->dwc_ep); ++ } else if (ep->dwc_ep.sent_zlp) { ++ /* ++ * This fragment of code should initiate 0 ++ * length trasfer in case if it is queued ++ * a trasfer with size divisible to EPs max ++ * packet size and with usb_request zero field ++ * is set, which means that after data is transfered, ++ * it is also should be transfered ++ * a 0 length packet at the end. For Slave and ++ * Buffer DMA modes in this case SW has ++ * to initiate 2 transfers one with transfer size, ++ * and the second with 0 size. For Desriptor ++ * DMA mode SW is able to initiate a transfer, ++ * which will handle all the packets including ++ * the last 0 legth. ++ */ ++ ep->dwc_ep.sent_zlp = 0; ++ dwc_otg_ep_start_zl_transfer(core_if, ++ &ep->dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } else { ++ DWC_WARN ++ ("Incomplete transfer (%d-%s [siz=%d pkt=%d])\n", ++ ep->dwc_ep.num, ++ (ep->dwc_ep.is_in ? "IN" : "OUT"), ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ } ++ } ++ } else { ++ dwc_otg_dev_out_ep_regs_t *out_ep_regs = ++ dev_if->out_ep_regs[ep->dwc_ep.num]; ++ desc_sts.d32 = 0; ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable) { ++ dma_desc = ep->dwc_ep.desc_addr; ++ byte_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ++#ifdef DWC_UTE_CFI ++ CFI_INFO("%s: BUFFER_MODE=%d\n", __func__, ++ ep->dwc_ep.buff_mode); ++ if (ep->dwc_ep.buff_mode != BM_STANDARD) { ++ int residue; ++ residue = cfi_calc_desc_residue(ep); ++ if (residue < 0) ++ return; ++ byte_count = residue; ++ } else { ++#endif ++ ++ for (i = 0; i < ep->dwc_ep.desc_cnt; ++ ++i) { ++ desc_sts = dma_desc->status; ++ byte_count += desc_sts.b.bytes; ++ dma_desc++; ++ } ++ ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ /* Checking for interrupt Out transfers with not ++ * dword aligned mps sizes ++ */ ++ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_INTR && ++ (ep->dwc_ep.maxpacket % 4)) { ++ ep->dwc_ep.xfer_count = ++ ep->dwc_ep.total_len - byte_count; ++ if ((ep->dwc_ep.xfer_len % ++ ep->dwc_ep.maxpacket) ++ && (ep->dwc_ep.xfer_len / ++ ep->dwc_ep.maxpacket < ++ MAX_DMA_DESC_CNT)) ++ ep->dwc_ep.xfer_len -= ++ (ep->dwc_ep.desc_cnt - ++ 1) * ep->dwc_ep.maxpacket + ++ ep->dwc_ep.xfer_len % ++ ep->dwc_ep.maxpacket; ++ else ++ ep->dwc_ep.xfer_len -= ++ ep->dwc_ep.desc_cnt * ++ ep->dwc_ep.maxpacket; ++ if (ep->dwc_ep.xfer_len > 0) { ++ dwc_otg_ep_start_transfer ++ (core_if, &ep->dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } else { ++ ep->dwc_ep.xfer_count = ++ ep->dwc_ep.total_len - byte_count + ++ ((4 - ++ (ep->dwc_ep. ++ total_len & 0x3)) & 0x3); ++ is_last = 1; ++ } ++ } else { ++ deptsiz.d32 = 0; ++ deptsiz.d32 = ++ DWC_READ_REG32(&out_ep_regs->doeptsiz); ++ ++ byte_count = (ep->dwc_ep.xfer_len - ++ ep->dwc_ep.xfer_count - ++ deptsiz.b.xfersize); ++ ep->dwc_ep.xfer_buff += byte_count; ++ ep->dwc_ep.dma_addr += byte_count; ++ ep->dwc_ep.xfer_count += byte_count; ++ ++ /* Check if the whole transfer was completed, ++ * if no, setup transfer for next portion of data ++ */ ++ if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { ++ dwc_otg_ep_start_transfer(core_if, ++ &ep->dwc_ep); ++ } else if (ep->dwc_ep.sent_zlp) { ++ /* ++ * This fragment of code should initiate 0 ++ * length trasfer in case if it is queued ++ * a trasfer with size divisible to EPs max ++ * packet size and with usb_request zero field ++ * is set, which means that after data is transfered, ++ * it is also should be transfered ++ * a 0 length packet at the end. For Slave and ++ * Buffer DMA modes in this case SW has ++ * to initiate 2 transfers one with transfer size, ++ * and the second with 0 size. For Desriptor ++ * DMA mode SW is able to initiate a transfer, ++ * which will handle all the packets including ++ * the last 0 legth. ++ */ ++ ep->dwc_ep.sent_zlp = 0; ++ dwc_otg_ep_start_zl_transfer(core_if, ++ &ep->dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } ++ } else { ++ /* Check if the whole transfer was completed, ++ * if no, setup transfer for next portion of data ++ */ ++ if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { ++ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep); ++ } else if (ep->dwc_ep.sent_zlp) { ++ /* ++ * This fragment of code should initiate 0 ++ * length transfer in case if it is queued ++ * a transfer with size divisible to EPs max ++ * packet size and with usb_request zero field ++ * is set, which means that after data is transfered, ++ * it is also should be transfered ++ * a 0 length packet at the end. For Slave and ++ * Buffer DMA modes in this case SW has ++ * to initiate 2 transfers one with transfer size, ++ * and the second with 0 size. For Descriptor ++ * DMA mode SW is able to initiate a transfer, ++ * which will handle all the packets including ++ * the last 0 length. ++ */ ++ ep->dwc_ep.sent_zlp = 0; ++ dwc_otg_ep_start_zl_transfer(core_if, ++ &ep->dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "addr %p, %d-%s len=%d cnt=%d xsize=%d pktcnt=%d\n", ++ &out_ep_regs->doeptsiz, ep->dwc_ep.num, ++ ep->dwc_ep.is_in ? "IN" : "OUT", ++ ep->dwc_ep.xfer_len, ep->dwc_ep.xfer_count, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ } ++ ++ /* Complete the request */ ++ if (is_last) { ++#ifdef DWC_UTE_CFI ++ if (ep->dwc_ep.buff_mode != BM_STANDARD) { ++ req->actual = ep->dwc_ep.cfi_req_len - byte_count; ++ } else { ++#endif ++ req->actual = ep->dwc_ep.xfer_count; ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ if (req->dw_align_buf) { ++ if (!ep->dwc_ep.is_in) { ++ dwc_memcpy(req->buf, req->dw_align_buf, req->length); ++ } ++ DWC_DMA_FREE(req->length, req->dw_align_buf, ++ req->dw_align_buf_dma); ++ } ++ ++ dwc_otg_request_done(ep, req, 0); ++ ++ ep->dwc_ep.start_xfer_buff = 0; ++ ep->dwc_ep.xfer_buff = 0; ++ ep->dwc_ep.xfer_len = 0; ++ ++ /* If there is a request in the queue start it. */ ++ start_next_request(ep); ++ } ++} ++/** ++ * This function completes the request for the ISO EP in DDMA. If it is last ++ * descriptor and ep was disabled, then program already prepared(during ep_queue) ++ * descriptor chain if there are more requests to process ++ */ ++static void complete_ddma_iso_ep(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd); ++ dev_dma_desc_sts_t desc_sts; ++ dwc_otg_pcd_request_t *req = 0; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ dwc_dma_t dma_desc_addr; ++ dwc_ep_t *dwc_ep; ++ uint32_t depdma; ++ uint32_t index; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s() %d-%s\n", __func__, ep->dwc_ep.num, ++ (ep->dwc_ep.is_in ? "IN" : "OUT")); ++ dwc_ep = &ep->dwc_ep; ++ if (dwc_ep->use_add_buf) { ++ dma_desc_addr = dwc_ep->dma_desc_addr; ++ dma_desc = dwc_ep->desc_addr; ++ } else { ++ dma_desc_addr = dwc_ep->dma_desc_addr1; ++ dma_desc = dwc_ep->desc_addr1; ++ } ++ /* Get any pending requests */ ++ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ if (!req) { ++ DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep); ++ return; ++ } ++ } else { ++ DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep); ++ return; ++ } ++ ++ if (dwc_ep->is_in) { ++ depdma = DWC_READ_REG32(&core_if->dev_if->in_ep_regs[dwc_ep->num]->diepdma); ++ index = (depdma - dma_desc_addr)/sizeof(dwc_otg_dev_dma_desc_t) - 1; ++ desc_sts = dma_desc[index].status; ++ req->actual = req->length - desc_sts.b_iso_in.txbytes; ++ } else { ++ depdma = DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepdma); ++ index = (depdma - dma_desc_addr)/sizeof(dwc_otg_dev_dma_desc_t) - 1; ++ desc_sts = dma_desc[index].status; ++ if (req->length%4) ++ req->actual = req->length - desc_sts.b_iso_out.rxbytes + (4 - req->length%4); ++ else ++ req->actual = req->length - desc_sts.b_iso_out.rxbytes; ++ } ++ ++ /* Complete the request */ ++ dwc_otg_request_done(ep, req, 0); ++} ++ ++#ifdef DWC_EN_ISOC ++ ++/** ++ * This function BNA interrupt for Isochronous EPs ++ * ++ */ ++static void dwc_otg_pcd_handle_iso_bna(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_ep_t *dwc_ep = &ep->dwc_ep; ++ volatile uint32_t *addr; ++ depctl_data_t depctl = {.d32 = 0 }; ++ dwc_otg_pcd_t *pcd = ep->pcd; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ int i; ++ ++ dma_desc = ++ dwc_ep->iso_desc_addr + dwc_ep->desc_cnt * (dwc_ep->proc_buf_num); ++ ++ if (dwc_ep->is_in) { ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ for (i = 0; i < dwc_ep->desc_cnt; ++i, ++dma_desc) { ++ sts.d32 = dma_desc->status.d32; ++ sts.b_iso_in.bs = BS_HOST_READY; ++ dma_desc->status.d32 = sts.d32; ++ } ++ } else { ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ for (i = 0; i < dwc_ep->desc_cnt; ++i, ++dma_desc) { ++ sts.d32 = dma_desc->status.d32; ++ sts.b_iso_out.bs = BS_HOST_READY; ++ dma_desc->status.d32 = sts.d32; ++ } ++ } ++ ++ if (dwc_ep->is_in == 0) { ++ addr = ++ &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep-> ++ num]->doepctl; ++ } else { ++ addr = ++ &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl; ++ } ++ depctl.b.epena = 1; ++ DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32); ++} ++ ++/** ++ * This function sets latest iso packet information(non-PTI mode) ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void set_current_pkt_info(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ dma_addr_t dma_addr; ++ uint32_t offset; ++ ++ if (ep->proc_buf_num) ++ dma_addr = ep->dma_addr1; ++ else ++ dma_addr = ep->dma_addr0; ++ ++ if (ep->is_in) { ++ deptsiz.d32 = ++ DWC_READ_REG32(&core_if->dev_if-> ++ in_ep_regs[ep->num]->dieptsiz); ++ offset = ep->data_per_frame; ++ } else { ++ deptsiz.d32 = ++ DWC_READ_REG32(&core_if->dev_if-> ++ out_ep_regs[ep->num]->doeptsiz); ++ offset = ++ ep->data_per_frame + ++ (0x4 & (0x4 - (ep->data_per_frame & 0x3))); ++ } ++ ++ if (!deptsiz.b.xfersize) { ++ ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame; ++ ep->pkt_info[ep->cur_pkt].offset = ++ ep->cur_pkt_dma_addr - dma_addr; ++ ep->pkt_info[ep->cur_pkt].status = 0; ++ } else { ++ ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame; ++ ep->pkt_info[ep->cur_pkt].offset = ++ ep->cur_pkt_dma_addr - dma_addr; ++ ep->pkt_info[ep->cur_pkt].status = -DWC_E_NO_DATA; ++ } ++ ep->cur_pkt_addr += offset; ++ ep->cur_pkt_dma_addr += offset; ++ ep->cur_pkt++; ++} ++ ++/** ++ * This function sets latest iso packet information(DDMA mode) ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dwc_ep The EP to start the transfer on. ++ * ++ */ ++static void set_ddma_iso_pkts_info(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * dwc_ep) ++{ ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ iso_pkt_info_t *iso_packet; ++ uint32_t data_per_desc; ++ uint32_t offset; ++ int i, j; ++ ++ iso_packet = dwc_ep->pkt_info; ++ ++ /** Reinit closed DMA Descriptors*/ ++ /** ISO OUT EP */ ++ if (dwc_ep->is_in == 0) { ++ dma_desc = ++ dwc_ep->iso_desc_addr + ++ dwc_ep->desc_cnt * dwc_ep->proc_buf_num; ++ offset = 0; ++ ++ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; ++ i += dwc_ep->pkt_per_frm) { ++ for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep-> ++ data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - ++ data_per_desc % ++ 4) : 0; ++ ++ sts.d32 = dma_desc->status.d32; ++ ++ /* Write status in iso_packet_decsriptor */ ++ iso_packet->status = ++ sts.b_iso_out.rxsts + ++ (sts.b_iso_out.bs ^ BS_DMA_DONE); ++ if (iso_packet->status) { ++ iso_packet->status = -DWC_E_NO_DATA; ++ } ++ ++ /* Received data length */ ++ if (!sts.b_iso_out.rxbytes) { ++ iso_packet->length = ++ data_per_desc - ++ sts.b_iso_out.rxbytes; ++ } else { ++ iso_packet->length = ++ data_per_desc - ++ sts.b_iso_out.rxbytes + (4 - ++ dwc_ep->data_per_frame ++ % 4); ++ } ++ ++ iso_packet->offset = offset; ++ ++ offset += data_per_desc; ++ dma_desc++; ++ iso_packet++; ++ } ++ } ++ ++ for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; ++ ++ sts.d32 = dma_desc->status.d32; ++ ++ /* Write status in iso_packet_decsriptor */ ++ iso_packet->status = ++ sts.b_iso_out.rxsts + ++ (sts.b_iso_out.bs ^ BS_DMA_DONE); ++ if (iso_packet->status) { ++ iso_packet->status = -DWC_E_NO_DATA; ++ } ++ ++ /* Received data length */ ++ iso_packet->length = ++ dwc_ep->data_per_frame - sts.b_iso_out.rxbytes; ++ ++ iso_packet->offset = offset; ++ ++ offset += data_per_desc; ++ iso_packet++; ++ dma_desc++; ++ } ++ ++ sts.d32 = dma_desc->status.d32; ++ ++ /* Write status in iso_packet_decsriptor */ ++ iso_packet->status = ++ sts.b_iso_out.rxsts + (sts.b_iso_out.bs ^ BS_DMA_DONE); ++ if (iso_packet->status) { ++ iso_packet->status = -DWC_E_NO_DATA; ++ } ++ /* Received data length */ ++ if (!sts.b_iso_out.rxbytes) { ++ iso_packet->length = ++ dwc_ep->data_per_frame - sts.b_iso_out.rxbytes; ++ } else { ++ iso_packet->length = ++ dwc_ep->data_per_frame - sts.b_iso_out.rxbytes + ++ (4 - dwc_ep->data_per_frame % 4); ++ } ++ ++ iso_packet->offset = offset; ++ } else { ++/** ISO IN EP */ ++ ++ dma_desc = ++ dwc_ep->iso_desc_addr + ++ dwc_ep->desc_cnt * dwc_ep->proc_buf_num; ++ ++ for (i = 0; i < dwc_ep->desc_cnt - 1; i++) { ++ sts.d32 = dma_desc->status.d32; ++ ++ /* Write status in iso packet descriptor */ ++ iso_packet->status = ++ sts.b_iso_in.txsts + ++ (sts.b_iso_in.bs ^ BS_DMA_DONE); ++ if (iso_packet->status != 0) { ++ iso_packet->status = -DWC_E_NO_DATA; ++ ++ } ++ /* Bytes has been transfered */ ++ iso_packet->length = ++ dwc_ep->data_per_frame - sts.b_iso_in.txbytes; ++ ++ dma_desc++; ++ iso_packet++; ++ } ++ ++ sts.d32 = dma_desc->status.d32; ++ while (sts.b_iso_in.bs == BS_DMA_BUSY) { ++ sts.d32 = dma_desc->status.d32; ++ } ++ ++ /* Write status in iso packet descriptor ??? do be done with ERROR codes */ ++ iso_packet->status = ++ sts.b_iso_in.txsts + (sts.b_iso_in.bs ^ BS_DMA_DONE); ++ if (iso_packet->status != 0) { ++ iso_packet->status = -DWC_E_NO_DATA; ++ } ++ ++ /* Bytes has been transfered */ ++ iso_packet->length = ++ dwc_ep->data_per_frame - sts.b_iso_in.txbytes; ++ } ++} ++ ++/** ++ * This function reinitialize DMA Descriptors for Isochronous transfer ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dwc_ep The EP to start the transfer on. ++ * ++ */ ++static void reinit_ddma_iso_xfer(dwc_otg_core_if_t * core_if, dwc_ep_t * dwc_ep) ++{ ++ int i, j; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ dma_addr_t dma_ad; ++ volatile uint32_t *addr; ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ uint32_t data_per_desc; ++ ++ if (dwc_ep->is_in == 0) { ++ addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl; ++ } else { ++ addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl; ++ } ++ ++ if (dwc_ep->proc_buf_num == 0) { ++ /** Buffer 0 descriptors setup */ ++ dma_ad = dwc_ep->dma_addr0; ++ } else { ++ /** Buffer 1 descriptors setup */ ++ dma_ad = dwc_ep->dma_addr1; ++ } ++ ++ /** Reinit closed DMA Descriptors*/ ++ /** ISO OUT EP */ ++ if (dwc_ep->is_in == 0) { ++ dma_desc = ++ dwc_ep->iso_desc_addr + ++ dwc_ep->desc_cnt * dwc_ep->proc_buf_num; ++ ++ sts.b_iso_out.bs = BS_HOST_READY; ++ sts.b_iso_out.rxsts = 0; ++ sts.b_iso_out.l = 0; ++ sts.b_iso_out.sp = 0; ++ sts.b_iso_out.ioc = 0; ++ sts.b_iso_out.pid = 0; ++ sts.b_iso_out.framenum = 0; ++ ++ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; ++ i += dwc_ep->pkt_per_frm) { ++ for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep-> ++ data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - ++ data_per_desc % ++ 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ dma_ad += data_per_desc; ++ dma_desc++; ++ } ++ } ++ ++ for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { ++ ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ dma_desc++; ++ dma_ad += data_per_desc; ++ } ++ ++ sts.b_iso_out.ioc = 1; ++ sts.b_iso_out.l = dwc_ep->proc_buf_num; ++ ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ } else { ++/** ISO IN EP */ ++ ++ dma_desc = ++ dwc_ep->iso_desc_addr + ++ dwc_ep->desc_cnt * dwc_ep->proc_buf_num; ++ ++ sts.b_iso_in.bs = BS_HOST_READY; ++ sts.b_iso_in.txsts = 0; ++ sts.b_iso_in.sp = 0; ++ sts.b_iso_in.ioc = 0; ++ sts.b_iso_in.pid = dwc_ep->pkt_per_frm; ++ sts.b_iso_in.framenum = dwc_ep->next_frame; ++ sts.b_iso_in.txbytes = dwc_ep->data_per_frame; ++ sts.b_iso_in.l = 0; ++ ++ for (i = 0; i < dwc_ep->desc_cnt - 1; i++) { ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ sts.b_iso_in.framenum += dwc_ep->bInterval; ++ dma_ad += dwc_ep->data_per_frame; ++ dma_desc++; ++ } ++ ++ sts.b_iso_in.ioc = 1; ++ sts.b_iso_in.l = dwc_ep->proc_buf_num; ++ ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ dwc_ep->next_frame = ++ sts.b_iso_in.framenum + dwc_ep->bInterval * 1; ++ } ++ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1; ++} ++ ++/** ++ * This function is to handle Iso EP transfer complete interrupt ++ * in case Iso out packet was dropped ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dwc_ep The EP for wihich transfer complete was asserted ++ * ++ */ ++static uint32_t handle_iso_out_pkt_dropped(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * dwc_ep) ++{ ++ uint32_t dma_addr; ++ uint32_t drp_pkt; ++ uint32_t drp_pkt_cnt; ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ int i; ++ ++ deptsiz.d32 = ++ DWC_READ_REG32(&core_if->dev_if-> ++ out_ep_regs[dwc_ep->num]->doeptsiz); ++ ++ drp_pkt = dwc_ep->pkt_cnt - deptsiz.b.pktcnt; ++ drp_pkt_cnt = dwc_ep->pkt_per_frm - (drp_pkt % dwc_ep->pkt_per_frm); ++ ++ /* Setting dropped packets status */ ++ for (i = 0; i < drp_pkt_cnt; ++i) { ++ dwc_ep->pkt_info[drp_pkt].status = -DWC_E_NO_DATA; ++ drp_pkt++; ++ deptsiz.b.pktcnt--; ++ } ++ ++ if (deptsiz.b.pktcnt > 0) { ++ deptsiz.b.xfersize = ++ dwc_ep->xfer_len - (dwc_ep->pkt_cnt - ++ deptsiz.b.pktcnt) * dwc_ep->maxpacket; ++ } else { ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 0; ++ } ++ ++ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doeptsiz, ++ deptsiz.d32); ++ ++ if (deptsiz.b.pktcnt > 0) { ++ if (dwc_ep->proc_buf_num) { ++ dma_addr = ++ dwc_ep->dma_addr1 + dwc_ep->xfer_len - ++ deptsiz.b.xfersize; ++ } else { ++ dma_addr = ++ dwc_ep->dma_addr0 + dwc_ep->xfer_len - ++ deptsiz.b.xfersize;; ++ } ++ ++ DWC_WRITE_REG32(&core_if->dev_if-> ++ out_ep_regs[dwc_ep->num]->doepdma, dma_addr); ++ ++ /** Re-enable endpoint, clear nak */ ++ depctl.d32 = 0; ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ ++ DWC_MODIFY_REG32(&core_if->dev_if-> ++ out_ep_regs[dwc_ep->num]->doepctl, depctl.d32, ++ depctl.d32); ++ return 0; ++ } else { ++ return 1; ++ } ++} ++ ++/** ++ * This function sets iso packets information(PTI mode) ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++static uint32_t set_iso_pkts_info(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ int i, j; ++ dma_addr_t dma_ad; ++ iso_pkt_info_t *packet_info = ep->pkt_info; ++ uint32_t offset; ++ uint32_t frame_data; ++ deptsiz_data_t deptsiz; ++ ++ if (ep->proc_buf_num == 0) { ++ /** Buffer 0 descriptors setup */ ++ dma_ad = ep->dma_addr0; ++ } else { ++ /** Buffer 1 descriptors setup */ ++ dma_ad = ep->dma_addr1; ++ } ++ ++ if (ep->is_in) { ++ deptsiz.d32 = ++ DWC_READ_REG32(&core_if->dev_if->in_ep_regs[ep->num]-> ++ dieptsiz); ++ } else { ++ deptsiz.d32 = ++ DWC_READ_REG32(&core_if->dev_if->out_ep_regs[ep->num]-> ++ doeptsiz); ++ } ++ ++ if (!deptsiz.b.xfersize) { ++ offset = 0; ++ for (i = 0; i < ep->pkt_cnt; i += ep->pkt_per_frm) { ++ frame_data = ep->data_per_frame; ++ for (j = 0; j < ep->pkt_per_frm; ++j) { ++ ++ /* Packet status - is not set as initially ++ * it is set to 0 and if packet was sent ++ successfully, status field will remain 0*/ ++ ++ /* Bytes has been transfered */ ++ packet_info->length = ++ (ep->maxpacket < ++ frame_data) ? ep->maxpacket : frame_data; ++ ++ /* Received packet offset */ ++ packet_info->offset = offset; ++ offset += packet_info->length; ++ frame_data -= packet_info->length; ++ ++ packet_info++; ++ } ++ } ++ return 1; ++ } else { ++ /* This is a workaround for in case of Transfer Complete with ++ * PktDrpSts interrupts merging - in this case Transfer complete ++ * interrupt for Isoc Out Endpoint is asserted without PktDrpSts ++ * set and with DOEPTSIZ register non zero. Investigations showed, ++ * that this happens when Out packet is dropped, but because of ++ * interrupts merging during first interrupt handling PktDrpSts ++ * bit is cleared and for next merged interrupts it is not reset. ++ * In this case SW hadles the interrupt as if PktDrpSts bit is set. ++ */ ++ if (ep->is_in) { ++ return 1; ++ } else { ++ return handle_iso_out_pkt_dropped(core_if, ep); ++ } ++ } ++} ++ ++/** ++ * This function is to handle Iso EP transfer complete interrupt ++ * ++ * @param pcd The PCD ++ * @param ep The EP for which transfer complete was asserted ++ * ++ */ ++static void complete_iso_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd); ++ dwc_ep_t *dwc_ep = &ep->dwc_ep; ++ uint8_t is_last = 0; ++ ++ if (ep->dwc_ep.next_frame == 0xffffffff) { ++ DWC_WARN("Next frame is not set!\n"); ++ return; ++ } ++ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable) { ++ set_ddma_iso_pkts_info(core_if, dwc_ep); ++ reinit_ddma_iso_xfer(core_if, dwc_ep); ++ is_last = 1; ++ } else { ++ if (core_if->pti_enh_enable) { ++ if (set_iso_pkts_info(core_if, dwc_ep)) { ++ dwc_ep->proc_buf_num = ++ (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ dwc_otg_iso_ep_start_buf_transfer ++ (core_if, dwc_ep); ++ is_last = 1; ++ } ++ } else { ++ set_current_pkt_info(core_if, dwc_ep); ++ if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { ++ is_last = 1; ++ dwc_ep->cur_pkt = 0; ++ dwc_ep->proc_buf_num = ++ (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ if (dwc_ep->proc_buf_num) { ++ dwc_ep->cur_pkt_addr = ++ dwc_ep->xfer_buff1; ++ dwc_ep->cur_pkt_dma_addr = ++ dwc_ep->dma_addr1; ++ } else { ++ dwc_ep->cur_pkt_addr = ++ dwc_ep->xfer_buff0; ++ dwc_ep->cur_pkt_dma_addr = ++ dwc_ep->dma_addr0; ++ } ++ ++ } ++ dwc_otg_iso_ep_start_frm_transfer(core_if, ++ dwc_ep); ++ } ++ } ++ } else { ++ set_current_pkt_info(core_if, dwc_ep); ++ if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { ++ is_last = 1; ++ dwc_ep->cur_pkt = 0; ++ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ if (dwc_ep->proc_buf_num) { ++ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff1; ++ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr1; ++ } else { ++ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff0; ++ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr0; ++ } ++ ++ } ++ dwc_otg_iso_ep_start_frm_transfer(core_if, dwc_ep); ++ } ++ if (is_last) ++ dwc_otg_iso_buffer_done(pcd, ep, ep->iso_req_handle); ++} ++#endif /* DWC_EN_ISOC */ ++ ++/** ++ * This function handle BNA interrupt for Non Isochronous EPs ++ * ++ */ ++static void dwc_otg_pcd_handle_noniso_bna(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_ep_t *dwc_ep = &ep->dwc_ep; ++ volatile uint32_t *addr; ++ depctl_data_t depctl = {.d32 = 0 }; ++ dwc_otg_pcd_t *pcd = ep->pcd; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ dwc_otg_core_if_t *core_if = ep->pcd->core_if; ++ int i, start; ++ ++ if (!dwc_ep->desc_cnt) ++ DWC_WARN("Ep%d %s Descriptor count = %d \n", dwc_ep->num, ++ (dwc_ep->is_in ? "IN" : "OUT"), dwc_ep->desc_cnt); ++ ++ if (core_if->core_params->cont_on_bna && !dwc_ep->is_in ++ && dwc_ep->type != DWC_OTG_EP_TYPE_CONTROL) { ++ uint32_t doepdma; ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[dwc_ep->num]; ++ doepdma = DWC_READ_REG32(&(out_regs->doepdma)); ++ start = (doepdma - dwc_ep->dma_desc_addr)/sizeof(dwc_otg_dev_dma_desc_t); ++ dma_desc = &(dwc_ep->desc_addr[start]); ++ } else { ++ start = 0; ++ dma_desc = dwc_ep->desc_addr; ++ } ++ ++ ++ for (i = start; i < dwc_ep->desc_cnt; ++i, ++dma_desc) { ++ sts.d32 = dma_desc->status.d32; ++ sts.b.bs = BS_HOST_READY; ++ dma_desc->status.d32 = sts.d32; ++ } ++ ++ if (dwc_ep->is_in == 0) { ++ addr = ++ &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep->num]-> ++ doepctl; ++ } else { ++ addr = ++ &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl; ++ } ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ DWC_MODIFY_REG32(addr, 0, depctl.d32); ++} ++ ++/** ++ * This function handles EP0 Control transfers. ++ * ++ * The state of the control transfers are tracked in ++ * <code>ep0state</code>. ++ */ ++static void handle_ep0(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ dev_dma_desc_sts_t desc_sts; ++ deptsiz0_data_t deptsiz; ++ uint32_t byte_count; ++ ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__); ++ print_ep0_state(pcd); ++#endif ++ ++ switch (pcd->ep0state) { ++ case EP0_DISCONNECT: ++ break; ++ ++ case EP0_IDLE: ++ pcd->request_config = 0; ++ ++ pcd_setup(pcd); ++ break; ++ ++ case EP0_IN_DATA_PHASE: ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "DATA_IN EP%d-%s: type=%d, mps=%d\n", ++ ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"), ++ ep0->dwc_ep.type, ep0->dwc_ep.maxpacket); ++#endif ++ ++ if (core_if->dma_enable != 0) { ++ /* ++ * For EP0 we can only program 1 packet at a time so we ++ * need to do the make calculations after each complete. ++ * Call write_packet to make the calculations, as in ++ * slave mode, and use those values to determine if we ++ * can complete. ++ */ ++ if (core_if->dma_desc_enable == 0) { ++ deptsiz.d32 = ++ DWC_READ_REG32(&core_if-> ++ dev_if->in_ep_regs[0]-> ++ dieptsiz); ++ byte_count = ++ ep0->dwc_ep.xfer_len - deptsiz.b.xfersize; ++ } else { ++ desc_sts = ++ core_if->dev_if->in_desc_addr->status; ++ byte_count = ++ ep0->dwc_ep.xfer_len - desc_sts.b.bytes; ++ } ++ ep0->dwc_ep.xfer_count += byte_count; ++ ep0->dwc_ep.xfer_buff += byte_count; ++ ep0->dwc_ep.dma_addr += byte_count; ++ } ++ if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) { ++ dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd), ++ &ep0->dwc_ep); ++ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n"); ++ } else if (ep0->dwc_ep.sent_zlp) { ++ dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd), ++ &ep0->dwc_ep); ++ ep0->dwc_ep.sent_zlp = 0; ++ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER sent zlp\n"); ++ } else { ++ ep0_complete_request(ep0); ++ DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n"); ++ } ++ break; ++ case EP0_OUT_DATA_PHASE: ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "DATA_OUT EP%d-%s: type=%d, mps=%d\n", ++ ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"), ++ ep0->dwc_ep.type, ep0->dwc_ep.maxpacket); ++#endif ++ if (core_if->dma_enable != 0) { ++ if (core_if->dma_desc_enable == 0) { ++ deptsiz.d32 = ++ DWC_READ_REG32(&core_if-> ++ dev_if->out_ep_regs[0]-> ++ doeptsiz); ++ byte_count = ++ ep0->dwc_ep.maxpacket - deptsiz.b.xfersize; ++ } else { ++ desc_sts = ++ core_if->dev_if->out_desc_addr->status; ++ byte_count = ++ ep0->dwc_ep.maxpacket - desc_sts.b.bytes; ++ } ++ ep0->dwc_ep.xfer_count += byte_count; ++ ep0->dwc_ep.xfer_buff += byte_count; ++ ep0->dwc_ep.dma_addr += byte_count; ++ } ++ if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) { ++ dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd), ++ &ep0->dwc_ep); ++ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n"); ++ } else if (ep0->dwc_ep.sent_zlp) { ++ dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd), ++ &ep0->dwc_ep); ++ ep0->dwc_ep.sent_zlp = 0; ++ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER sent zlp\n"); ++ } else { ++ ep0_complete_request(ep0); ++ DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n"); ++ } ++ break; ++ ++ case EP0_IN_STATUS_PHASE: ++ case EP0_OUT_STATUS_PHASE: ++ DWC_DEBUGPL(DBG_PCD, "CASE: EP0_STATUS\n"); ++ ep0_complete_request(ep0); ++ pcd->ep0state = EP0_IDLE; ++ ep0->stopped = 1; ++ ep0->dwc_ep.is_in = 0; /* OUT for next SETUP */ ++ ++ /* Prepare for more SETUP Packets */ ++ if (core_if->dma_enable) { ++ ep0_out_start(core_if, pcd); ++ } ++ break; ++ ++ case EP0_STALL: ++ DWC_ERROR("EP0 STALLed, should not get here pcd_setup()\n"); ++ break; ++ } ++#ifdef DEBUG_EP0 ++ print_ep0_state(pcd); ++#endif ++} ++ ++/** ++ * Restart transfer ++ */ ++static void restart_transfer(dwc_otg_pcd_t * pcd, const uint32_t epnum) ++{ ++ dwc_otg_core_if_t *core_if; ++ dwc_otg_dev_if_t *dev_if; ++ deptsiz_data_t dieptsiz = {.d32 = 0 }; ++ dwc_otg_pcd_ep_t *ep; ++ ++ ep = get_in_ep(pcd, epnum); ++ if (!ep) ++ return; ++ ++#ifdef DWC_EN_ISOC ++ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) { ++ return; ++ } ++#endif /* DWC_EN_ISOC */ ++ ++ core_if = GET_CORE_IF(pcd); ++ dev_if = core_if->dev_if; ++ ++ dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dieptsiz); ++ ++ DWC_DEBUGPL(DBG_PCD, "xfer_buff=%p xfer_count=%0x xfer_len=%0x" ++ " stopped=%d\n", ep->dwc_ep.xfer_buff, ++ ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len, ep->stopped); ++ /* ++ * If xfersize is 0 and pktcnt in not 0, resend the last packet. ++ */ ++ if (dieptsiz.b.pktcnt && dieptsiz.b.xfersize == 0 && ++ ep->dwc_ep.start_xfer_buff != 0) { ++ if (ep->dwc_ep.total_len <= ep->dwc_ep.maxpacket) { ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.xfer_buff = ep->dwc_ep.start_xfer_buff; ++ ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count; ++ } else { ++ ep->dwc_ep.xfer_count -= ep->dwc_ep.maxpacket; ++ /* convert packet size to dwords. */ ++ ep->dwc_ep.xfer_buff -= ep->dwc_ep.maxpacket; ++ ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count; ++ } ++ ep->stopped = 0; ++ DWC_DEBUGPL(DBG_PCD, "xfer_buff=%p xfer_count=%0x " ++ "xfer_len=%0x stopped=%d\n", ++ ep->dwc_ep.xfer_buff, ++ ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len, ++ ep->stopped); ++ if (epnum == 0) { ++ dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep); ++ } else { ++ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep); ++ } ++ } ++} ++ ++/* ++ * This function create new nextep sequnce based on Learn Queue. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ */ ++void predict_nextep_seq( dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_device_global_regs_t *dev_global_regs = ++ core_if->dev_if->dev_global_regs; ++ const uint32_t TOKEN_Q_DEPTH = core_if->hwcfg2.b.dev_token_q_depth; ++ /* Number of Token Queue Registers */ ++ const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8; ++ dtknq1_data_t dtknqr1; ++ uint32_t in_tkn_epnums[4]; ++ uint8_t seqnum[MAX_EPS_CHANNELS]; ++ uint8_t intkn_seq[TOKEN_Q_DEPTH]; ++ grstctl_t resetctl = {.d32 = 0 }; ++ uint8_t temp; ++ int ndx = 0; ++ int start = 0; ++ int end = 0; ++ int sort_done = 0; ++ int i = 0; ++ volatile uint32_t *addr = &dev_global_regs->dtknqr1; ++ ++ DWC_DEBUGPL(DBG_PCD, "dev_token_q_depth=%d\n", TOKEN_Q_DEPTH); ++ ++ /* Read the DTKNQ Registers */ ++ for (i = 0; i < DTKNQ_REG_CNT; i++) { ++ in_tkn_epnums[i] = DWC_READ_REG32(addr); ++ DWC_DEBUGPL(DBG_PCDV, "DTKNQR%d=0x%08x\n", i + 1, ++ in_tkn_epnums[i]); ++ if (addr == &dev_global_regs->dvbusdis) { ++ addr = &dev_global_regs->dtknqr3_dthrctl; ++ } else { ++ ++addr; ++ } ++ ++ } ++ ++ /* Copy the DTKNQR1 data to the bit field. */ ++ dtknqr1.d32 = in_tkn_epnums[0]; ++ if (dtknqr1.b.wrap_bit) { ++ ndx = dtknqr1.b.intknwptr; ++ end = ndx - 1; ++ if (end < 0) ++ end = TOKEN_Q_DEPTH - 1; ++ } else { ++ ndx = 0; ++ end = dtknqr1.b.intknwptr - 1; ++ if (end < 0) ++ end = 0; ++ } ++ start = ndx; ++ ++ /* Fill seqnum[] by initial values: EP number + 31 */ ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ seqnum[i] = i + 31; ++ } ++ ++ /* Fill intkn_seq[] from in_tkn_epnums[0] */ ++ for (i = 0; i < 6; i++) ++ intkn_seq[i] = (in_tkn_epnums[0] >> ((7 - i) * 4)) & 0xf; ++ ++ if (TOKEN_Q_DEPTH > 6) { ++ /* Fill intkn_seq[] from in_tkn_epnums[1] */ ++ for (i = 6; i < 14; i++) ++ intkn_seq[i] = ++ (in_tkn_epnums[1] >> ((7 - (i - 6)) * 4)) & 0xf; ++ } ++ ++ if (TOKEN_Q_DEPTH > 14) { ++ /* Fill intkn_seq[] from in_tkn_epnums[1] */ ++ for (i = 14; i < 22; i++) ++ intkn_seq[i] = ++ (in_tkn_epnums[2] >> ((7 - (i - 14)) * 4)) & 0xf; ++ } ++ ++ if (TOKEN_Q_DEPTH > 22) { ++ /* Fill intkn_seq[] from in_tkn_epnums[1] */ ++ for (i = 22; i < 30; i++) ++ intkn_seq[i] = ++ (in_tkn_epnums[3] >> ((7 - (i - 22)) * 4)) & 0xf; ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s start=%d end=%d intkn_seq[]:\n", __func__, ++ start, end); ++ for (i = 0; i < TOKEN_Q_DEPTH; i++) ++ DWC_DEBUGPL(DBG_PCDV, "%d\n", intkn_seq[i]); ++ ++ /* Update seqnum based on intkn_seq[] */ ++ i = 0; ++ do { ++ seqnum[intkn_seq[ndx]] = i; ++ ndx++; ++ i++; ++ if (ndx == TOKEN_Q_DEPTH) ++ ndx = 0; ++ } while (i < TOKEN_Q_DEPTH); ++ ++ /* Mark non active EP's in seqnum[] by 0xff */ ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ if (core_if->nextep_seq[i] == 0xff) ++ seqnum[i] = 0xff; ++ } ++ ++ /* Sort seqnum[] */ ++ sort_done = 0; ++ while (!sort_done) { ++ sort_done = 1; ++ for (i = 0; i < core_if->dev_if->num_in_eps; i++) { ++ if (seqnum[i] > seqnum[i + 1]) { ++ temp = seqnum[i]; ++ seqnum[i] = seqnum[i + 1]; ++ seqnum[i + 1] = temp; ++ sort_done = 0; ++ } ++ } ++ } ++ ++ ndx = start + seqnum[0]; ++ if (ndx >= TOKEN_Q_DEPTH) ++ ndx = ndx % TOKEN_Q_DEPTH; ++ core_if->first_in_nextep_seq = intkn_seq[ndx]; ++ ++ /* Update seqnum[] by EP numbers */ ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ ndx = start + i; ++ if (seqnum[i] < 31) { ++ ndx = start + seqnum[i]; ++ if (ndx >= TOKEN_Q_DEPTH) ++ ndx = ndx % TOKEN_Q_DEPTH; ++ seqnum[i] = intkn_seq[ndx]; ++ } else { ++ if (seqnum[i] < 0xff) { ++ seqnum[i] = seqnum[i] - 31; ++ } else { ++ break; ++ } ++ } ++ } ++ ++ /* Update nextep_seq[] based on seqnum[] */ ++ for (i = 0; i < core_if->dev_if->num_in_eps; i++) { ++ if (seqnum[i] != 0xff) { ++ if (seqnum[i + 1] != 0xff) { ++ core_if->nextep_seq[seqnum[i]] = seqnum[i + 1]; ++ } else { ++ core_if->nextep_seq[seqnum[i]] = core_if->first_in_nextep_seq; ++ break; ++ } ++ } else { ++ break; ++ } ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s first_in_nextep_seq= %2d; nextep_seq[]:\n", ++ __func__, core_if->first_in_nextep_seq); ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ DWC_DEBUGPL(DBG_PCDV, "%2d\n", core_if->nextep_seq[i]); ++ } ++ ++ /* Flush the Learning Queue */ ++ resetctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->grstctl); ++ resetctl.b.intknqflsh = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32); ++ ++ ++} ++ ++/** ++ * handle the IN EP disable interrupt. ++ */ ++static inline void handle_in_ep_disable_intr(dwc_otg_pcd_t * pcd, ++ const uint32_t epnum) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ deptsiz_data_t dieptsiz = {.d32 = 0 }; ++ dctl_data_t dctl = {.d32 = 0 }; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ gintmsk_data_t gintmsk_data; ++ depctl_data_t depctl; ++ uint32_t diepdma; ++ uint32_t remain_to_transfer = 0; ++ uint8_t i; ++ uint32_t xfer_size; ++ ++ ep = get_in_ep(pcd, epnum); ++ if (!ep) ++ return; ++ ++ dwc_ep = &ep->dwc_ep; ++ if (!dwc_ep) ++ return; ++ ++ if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num); ++ complete_ep(ep); ++ return; ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "diepctl%d=%0x\n", epnum, ++ DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl)); ++ dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dieptsiz); ++ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl); ++ ++ DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n", ++ dieptsiz.b.pktcnt, dieptsiz.b.xfersize); ++ ++ if ((core_if->start_predict == 0) || (depctl.b.eptype & 1)) { ++ if (ep->stopped) { ++ if (core_if->en_multiple_tx_fifo) ++ /* Flush the Tx FIFO */ ++ dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num); ++ /* Clear the Global IN NP NAK */ ++ dctl.d32 = 0; ++ dctl.b.cgnpinnak = 1; ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); ++ /* Restart the transaction */ ++ if (dieptsiz.b.pktcnt != 0 || dieptsiz.b.xfersize != 0) { ++ restart_transfer(pcd, epnum); ++ } ++ } else { ++ /* Restart the transaction */ ++ if (dieptsiz.b.pktcnt != 0 || dieptsiz.b.xfersize != 0) { ++ restart_transfer(pcd, epnum); ++ } ++ DWC_DEBUGPL(DBG_ANY, "STOPPED!!!\n"); ++ } ++ return; ++ } ++ ++ if (core_if->start_predict > 2) { // NP IN EP ++ core_if->start_predict--; ++ return; ++ } ++ ++ core_if->start_predict--; ++ ++ if (core_if->start_predict == 1) { // All NP IN Ep's disabled now ++ ++ predict_nextep_seq(core_if); ++ ++ /* Update all active IN EP's NextEP field based of nextep_seq[] */ ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ depctl.d32 = ++ DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl); ++ if (core_if->nextep_seq[i] != 0xff) { // Active NP IN EP ++ depctl.b.nextep = core_if->nextep_seq[i]; ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32); ++ } ++ } ++ /* Flush Shared NP TxFIFO */ ++ dwc_otg_flush_tx_fifo(core_if, 0); ++ /* Rewind buffers */ ++ if (!core_if->dma_desc_enable) { ++ i = core_if->first_in_nextep_seq; ++ do { ++ ep = get_in_ep(pcd, i); ++ if (!ep) ++ return; ++ ++ dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->dieptsiz); ++ xfer_size = ep->dwc_ep.total_len - ep->dwc_ep.xfer_count; ++ if (xfer_size > ep->dwc_ep.maxxfer) ++ xfer_size = ep->dwc_ep.maxxfer; ++ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl); ++ if (dieptsiz.b.pktcnt != 0) { ++ if (xfer_size == 0) { ++ remain_to_transfer = 0; ++ } else { ++ if ((xfer_size % ep->dwc_ep.maxpacket) == 0) { ++ remain_to_transfer = ++ dieptsiz.b.pktcnt * ep->dwc_ep.maxpacket; ++ } else { ++ remain_to_transfer = ((dieptsiz.b.pktcnt -1) * ep->dwc_ep.maxpacket) ++ + (xfer_size % ep->dwc_ep.maxpacket); ++ } ++ } ++ diepdma = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepdma); ++ dieptsiz.b.xfersize = remain_to_transfer; ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->dieptsiz, dieptsiz.d32); ++ diepdma = ep->dwc_ep.dma_addr + (xfer_size - remain_to_transfer); ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepdma, diepdma); ++ } ++ i = core_if->nextep_seq[i]; ++ } while (i != core_if->first_in_nextep_seq); ++ } else { // dma_desc_enable ++ DWC_PRINTF("%s Learning Queue not supported in DDMA\n", __func__); ++ } ++ ++ /* Restart transfers in predicted sequences */ ++ i = core_if->first_in_nextep_seq; ++ do { ++ dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->dieptsiz); ++ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl); ++ if (dieptsiz.b.pktcnt != 0) { ++ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl); ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32); ++ } ++ i = core_if->nextep_seq[i]; ++ } while (i != core_if->first_in_nextep_seq); ++ ++ /* Clear the global non-periodic IN NAK handshake */ ++ dctl.d32 = 0; ++ dctl.b.cgnpinnak = 1; ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); ++ ++ /* Unmask EP Mismatch interrupt */ ++ gintmsk_data.d32 = 0; ++ gintmsk_data.b.epmismatch = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk_data.d32); ++ ++ core_if->start_predict = 0; ++ ++ } ++} ++ ++/** ++ * Handler for the IN EP timeout handshake interrupt. ++ */ ++static inline void handle_in_ep_timeout_intr(dwc_otg_pcd_t * pcd, ++ const uint32_t epnum) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ ++#ifdef DEBUG ++ deptsiz_data_t dieptsiz = {.d32 = 0 }; ++ uint32_t num = 0; ++#endif ++ dctl_data_t dctl = {.d32 = 0 }; ++ dwc_otg_pcd_ep_t *ep; ++ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ ep = get_in_ep(pcd, epnum); ++ if (!ep) ++ return; ++ ++ /* Disable the NP Tx Fifo Empty Interrrupt */ ++ if (!core_if->dma_enable) { ++ intr_mask.b.nptxfempty = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ } ++ /** @todo NGS Check EP type. ++ * Implement for Periodic EPs */ ++ /* ++ * Non-periodic EP ++ */ ++ /* Enable the Global IN NAK Effective Interrupt */ ++ intr_mask.b.ginnakeff = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, intr_mask.d32); ++ ++ /* Set Global IN NAK */ ++ dctl.b.sgnpinnak = 1; ++ DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); ++ ++ ep->stopped = 1; ++ ++#ifdef DEBUG ++ dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[num]->dieptsiz); ++ DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n", ++ dieptsiz.b.pktcnt, dieptsiz.b.xfersize); ++#endif ++ ++#ifdef DISABLE_PERIODIC_EP ++ /* ++ * Set the NAK bit for this EP to ++ * start the disable process. ++ */ ++ diepctl.d32 = 0; ++ diepctl.b.snak = 1; ++ DWC_MODIFY_REG32(&dev_if->in_ep_regs[num]->diepctl, diepctl.d32, ++ diepctl.d32); ++ ep->disabling = 1; ++ ep->stopped = 1; ++#endif ++} ++ ++/** ++ * Handler for the IN EP NAK interrupt. ++ */ ++static inline int32_t handle_in_ep_nak_intr(dwc_otg_pcd_t * pcd, ++ const uint32_t epnum) ++{ ++ /** @todo implement ISR */ ++ dwc_otg_core_if_t *core_if; ++ diepmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "IN EP NAK"); ++ core_if = GET_CORE_IF(pcd); ++ intr_mask.b.nak = 1; ++ ++ if (core_if->multiproc_int_enable) { ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs-> ++ diepeachintmsk[epnum], intr_mask.d32, 0); ++ } else { ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->diepmsk, ++ intr_mask.d32, 0); ++ } ++ ++ return 1; ++} ++ ++/** ++ * Handler for the OUT EP Babble interrupt. ++ */ ++static inline int32_t handle_out_ep_babble_intr(dwc_otg_pcd_t * pcd, ++ const uint32_t epnum) ++{ ++ /** @todo implement ISR */ ++ dwc_otg_core_if_t *core_if; ++ doepmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", ++ "OUT EP Babble"); ++ core_if = GET_CORE_IF(pcd); ++ intr_mask.b.babble = 1; ++ ++ if (core_if->multiproc_int_enable) { ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs-> ++ doepeachintmsk[epnum], intr_mask.d32, 0); ++ } else { ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk, ++ intr_mask.d32, 0); ++ } ++ ++ return 1; ++} ++ ++/** ++ * Handler for the OUT EP NAK interrupt. ++ */ ++static inline int32_t handle_out_ep_nak_intr(dwc_otg_pcd_t * pcd, ++ const uint32_t epnum) ++{ ++ /** @todo implement ISR */ ++ dwc_otg_core_if_t *core_if; ++ doepmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_ANY, "INTERRUPT Handler not implemented for %s\n", "OUT EP NAK"); ++ core_if = GET_CORE_IF(pcd); ++ intr_mask.b.nak = 1; ++ ++ if (core_if->multiproc_int_enable) { ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs-> ++ doepeachintmsk[epnum], intr_mask.d32, 0); ++ } else { ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk, ++ intr_mask.d32, 0); ++ } ++ ++ return 1; ++} ++ ++/** ++ * Handler for the OUT EP NYET interrupt. ++ */ ++static inline int32_t handle_out_ep_nyet_intr(dwc_otg_pcd_t * pcd, ++ const uint32_t epnum) ++{ ++ /** @todo implement ISR */ ++ dwc_otg_core_if_t *core_if; ++ doepmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "OUT EP NYET"); ++ core_if = GET_CORE_IF(pcd); ++ intr_mask.b.nyet = 1; ++ ++ if (core_if->multiproc_int_enable) { ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs-> ++ doepeachintmsk[epnum], intr_mask.d32, 0); ++ } else { ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk, ++ intr_mask.d32, 0); ++ } ++ ++ return 1; ++} ++static void handle_xfercompl_iso_ddma (dwc_otg_dev_if_t *dev_if, dwc_otg_pcd_ep_t *ep) ++{ ++ depctl_data_t depctl; ++ dwc_ep_t *dwc_ep; ++ uint32_t doepdma; ++ dwc_dma_t dma_desc_addr; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ int index = 0, i = 0; ++ uint8_t epnum; ++ ++ dwc_ep = &ep->dwc_ep; ++ epnum = dwc_ep->num; ++ ++ complete_ddma_iso_ep(ep); ++ ++ if (dwc_ep->is_in) { ++ do { ++ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl); ++ if (!depctl.b.epena) ++ break; ++ udelay(1); ++ } while (i++ < 125); ++ ++ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl); ++ if (!depctl.b.epena) { ++ if (dwc_ep->use_add_buf) { ++ DWC_DEBUGPL(DBG_PCD, "go to second buffer \n"); ++ dwc_ep->use_add_buf = 0; ++ dwc_ep->iso_desc_first = 0; ++ if (dwc_ep->iso_desc_second) { ++ depctl_data_t diepctl; ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[epnum]->diepdma, ++ dwc_ep->dma_desc_addr1); ++ diepctl.d32 = 0; ++ diepctl.b.epena = 1; ++ diepctl.b.cnak = 1; ++ DWC_MODIFY_REG32(&dev_if->in_ep_regs[epnum]->diepctl, ++ 0, diepctl.d32); ++ } else { ++ DWC_DEBUGPL(DBG_PCD, "DDMA: No more ISOC requests 1\n"); ++ } ++ } else { ++ DWC_DEBUGPL(DBG_PCD, "go to first buffer \n"); ++ dwc_ep->use_add_buf = 1; ++ dwc_ep->iso_desc_second = 0; ++ if (dwc_ep->iso_desc_first) { ++ depctl_data_t diepctl; ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[epnum]->diepdma, ++ dwc_ep->dma_desc_addr); ++ diepctl.d32 = 0; ++ diepctl.b.epena = 1; ++ diepctl.b.cnak = 1; ++ DWC_MODIFY_REG32(&dev_if->in_ep_regs[epnum]->diepctl, ++ 0, diepctl.d32); ++ } else { ++ DWC_DEBUGPL(DBG_PCD, "DDMA: No more ISOC requests 2\n"); ++ } ++ } ++ } ++ } else { ++ depctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[epnum]->doepctl); ++ doepdma = DWC_READ_REG32(&dev_if->out_ep_regs[epnum]->doepdma); ++ ++ if (dwc_ep->use_add_buf) { ++ index = dwc_ep->iso_desc_first; ++ dma_desc_addr = dwc_ep->dma_desc_addr; ++ } else { ++ index = dwc_ep->iso_desc_second; ++ dma_desc_addr = dwc_ep->dma_desc_addr1; ++ } ++ ++ if (index == (doepdma - dma_desc_addr)/sizeof(dwc_otg_dev_dma_desc_t)) { ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ DWC_MODIFY_REG32(&dev_if->out_ep_regs[epnum]->doepctl, 0, depctl.d32); ++ } ++ dma_desc = dwc_ep->desc_addr + dwc_ep->iso_desc_first; ++ if (!depctl.b.epena) { ++ if (dwc_ep->use_add_buf) { ++ DWC_DEBUGPL(DBG_PCD, "go to second buffer \n"); ++ dwc_ep->use_add_buf = 0; ++ dwc_ep->iso_desc_first = 0; ++ if (dwc_ep->iso_desc_second) { ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[epnum]->doepdma, dwc_ep->dma_desc_addr1); ++ depctl.d32 = 0; ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ DWC_MODIFY_REG32(&dev_if->out_ep_regs[epnum]->doepctl, 0, depctl.d32); ++ } else { ++ DWC_DEBUGPL(DBG_PCD, "DDMA: There are no more ISOC requests 1!!! \n"); ++ } ++ } else { ++ dwc_ep->use_add_buf = 1; ++ dwc_ep->iso_desc_second = 0; ++ if (dwc_ep->iso_desc_first) { ++ DWC_DEBUGPL(DBG_PCD, "go to first buffer"); ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[epnum]->doepdma, dwc_ep->dma_desc_addr); ++ depctl.d32 = 0; ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ DWC_MODIFY_REG32(&dev_if->out_ep_regs[epnum]->doepctl, 0, depctl.d32); ++ } else { ++ DWC_DEBUGPL(DBG_PCD, "DDMA: There are no more ISOC requests 2!!! \n"); ++ } ++ } ++ } ++ } ++} ++/** ++ * This interrupt indicates that an IN EP has a pending Interrupt. ++ * The sequence for handling the IN EP interrupt is shown below: ++ * -# Read the Device All Endpoint Interrupt register ++ * -# Repeat the following for each IN EP interrupt bit set (from ++ * LSB to MSB). ++ * -# Read the Device Endpoint Interrupt (DIEPINTn) register ++ * -# If "Transfer Complete" call the request complete function ++ * -# If "Endpoint Disabled" complete the EP disable procedure. ++ * -# If "AHB Error Interrupt" log error ++ * -# If "Time-out Handshake" log error ++ * -# If "IN Token Received when TxFIFO Empty" write packet to Tx ++ * FIFO. ++ * -# If "IN Token EP Mismatch" (disable, this is handled by EP ++ * Mismatch Interrupt) ++ */ ++static int32_t dwc_otg_pcd_handle_in_ep_intr(dwc_otg_pcd_t * pcd) ++{ ++#define CLEAR_IN_EP_INTR(__core_if,__epnum,__intr) \ ++do { \ ++ diepint_data_t diepint = {.d32=0}; \ ++ diepint.b.__intr = 1; \ ++ DWC_WRITE_REG32(&__core_if->dev_if->in_ep_regs[__epnum]->diepint, \ ++ diepint.d32); \ ++} while (0) ++ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ diepint_data_t diepint = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ uint32_t ep_intr; ++ uint32_t epnum = 0; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, pcd); ++ ++ /* Read in the device interrupt bits */ ++ ep_intr = dwc_otg_read_dev_all_in_ep_intr(core_if); ++ ++ /* Service the Device IN interrupts for each endpoint */ ++ while (ep_intr) { ++ if (ep_intr & 0x1) { ++ uint32_t empty_msk; ++ /* Get EP pointer */ ++ ep = get_in_ep(pcd, epnum); ++ if (!ep) ++ return -EINVAL; ++ ++ dwc_ep = &ep->dwc_ep; ++ if (!dwc_ep) ++ return -EINVAL; ++ ++ depctl.d32 = ++ DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl); ++ empty_msk = ++ DWC_READ_REG32(&dev_if-> ++ dev_global_regs->dtknqr4_fifoemptymsk); ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "IN EP INTERRUPT - %d\nepmty_msk - %8x diepctl - %8x\n", ++ epnum, empty_msk, depctl.d32); ++ ++ DWC_DEBUGPL(DBG_PCD, ++ "EP%d-%s: type=%d, mps=%d\n", ++ dwc_ep->num, (dwc_ep->is_in ? "IN" : "OUT"), ++ dwc_ep->type, dwc_ep->maxpacket); ++ ++ diepint.d32 = ++ dwc_otg_read_dev_in_ep_intr(core_if, dwc_ep); ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "EP %d Interrupt Register - 0x%x\n", epnum, ++ diepint.d32); ++ /* Transfer complete */ ++ if (diepint.b.xfercompl) { ++ /* Disable the NP Tx FIFO Empty ++ * Interrupt */ ++ if (core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ DWC_MODIFY_REG32 ++ (&core_if->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ } else { ++ /* Disable the Tx FIFO Empty Interrupt for this EP */ ++ uint32_t fifoemptymsk = ++ 0x1 << dwc_ep->num; ++ DWC_MODIFY_REG32(&core_if-> ++ dev_if->dev_global_regs->dtknqr4_fifoemptymsk, ++ fifoemptymsk, 0); ++ } ++ /* Clear the bit in DIEPINTn for this interrupt */ ++ CLEAR_IN_EP_INTR(core_if, epnum, xfercompl); ++ ++ /* Complete the transfer */ ++ if (epnum == 0) { ++ handle_ep0(pcd); ++ } ++#ifdef DWC_EN_ISOC ++ else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ if (!ep->stopped) ++ complete_iso_ep(pcd, ep); ++ } ++#endif /* DWC_EN_ISOC */ ++#ifdef DWC_UTE_PER_IO ++ else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ if (!ep->stopped) ++ complete_xiso_ep(ep); ++ } ++#endif /* DWC_UTE_PER_IO */ ++ else { ++ if (core_if->dma_desc_enable && dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ handle_xfercompl_iso_ddma(dev_if, ep); ++ } else { ++ if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC && ++ dwc_ep->bInterval > 1) { ++ dwc_ep->frame_num += dwc_ep->bInterval; ++ if (dwc_ep->frame_num > 0x3FFF) ++ { ++ dwc_ep->frm_overrun = 1; ++ dwc_ep->frame_num &= 0x3FFF; ++ } else ++ dwc_ep->frm_overrun = 0; ++ } ++ complete_ep(ep); ++ if(diepint.b.nak) ++ CLEAR_IN_EP_INTR(core_if, epnum, nak); ++ } ++ } ++ } ++ /* Endpoint disable */ ++ if (diepint.b.epdisabled) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d IN disabled\n", ++ epnum); ++ handle_in_ep_disable_intr(pcd, epnum); ++ ++ /* Clear the bit in DIEPINTn for this interrupt */ ++ CLEAR_IN_EP_INTR(core_if, epnum, epdisabled); ++ } ++ /* AHB Error */ ++ if (diepint.b.ahberr) { ++ DWC_ERROR("EP%d IN AHB Error\n", epnum); ++ /* Clear the bit in DIEPINTn for this interrupt */ ++ CLEAR_IN_EP_INTR(core_if, epnum, ahberr); ++ } ++ /* TimeOUT Handshake (non-ISOC IN EPs) */ ++ if (diepint.b.timeout) { ++ DWC_ERROR("EP%d IN Time-out\n", epnum); ++ handle_in_ep_timeout_intr(pcd, epnum); ++ ++ CLEAR_IN_EP_INTR(core_if, epnum, timeout); ++ } ++ /** IN Token received with TxF Empty */ ++ if (diepint.b.intktxfemp) { ++ DWC_DEBUGPL(DBG_ANY, ++ "EP%d IN TKN TxFifo Empty\n", ++ epnum); ++ if (!ep->stopped && epnum != 0) { ++ ++ diepmsk_data_t diepmsk = {.d32 = 0 }; ++ diepmsk.b.intktxfemp = 1; ++ ++ if (core_if->multiproc_int_enable) { ++ DWC_MODIFY_REG32 ++ (&dev_if->dev_global_regs->diepeachintmsk ++ [epnum], diepmsk.d32, 0); ++ } else { ++ DWC_MODIFY_REG32 ++ (&dev_if->dev_global_regs->diepmsk, ++ diepmsk.d32, 0); ++ } ++ } else if (core_if->dma_desc_enable ++ && epnum == 0 ++ && pcd->ep0state == ++ EP0_OUT_STATUS_PHASE) { ++ // EP0 IN set STALL ++ depctl.d32 = ++ DWC_READ_REG32(&dev_if->in_ep_regs ++ [epnum]->diepctl); ++ ++ /* set the disable and stall bits */ ++ if (depctl.b.epena) { ++ depctl.b.epdis = 1; ++ } ++ depctl.b.stall = 1; ++ DWC_WRITE_REG32(&dev_if->in_ep_regs ++ [epnum]->diepctl, ++ depctl.d32); ++ } ++ CLEAR_IN_EP_INTR(core_if, epnum, intktxfemp); ++ } ++ /** IN Token Received with EP mismatch */ ++ if (diepint.b.intknepmis) { ++ DWC_DEBUGPL(DBG_ANY, ++ "EP%d IN TKN EP Mismatch\n", epnum); ++ CLEAR_IN_EP_INTR(core_if, epnum, intknepmis); ++ } ++ /** IN Endpoint NAK Effective */ ++ if (diepint.b.inepnakeff) { ++ DWC_DEBUGPL(DBG_ANY, ++ "EP%d IN EP NAK Effective\n", ++ epnum); ++ /* Periodic EP */ ++ if (ep->disabling) { ++ depctl.d32 = 0; ++ depctl.b.snak = 1; ++ depctl.b.epdis = 1; ++ DWC_MODIFY_REG32(&dev_if->in_ep_regs ++ [epnum]->diepctl, ++ depctl.d32, ++ depctl.d32); ++ } ++ CLEAR_IN_EP_INTR(core_if, epnum, inepnakeff); ++ ++ } ++ ++ /** IN EP Tx FIFO Empty Intr */ ++ if (diepint.b.emptyintr) { ++ DWC_DEBUGPL(DBG_ANY, ++ "EP%d Tx FIFO Empty Intr \n", ++ epnum); ++ write_empty_tx_fifo(pcd, epnum); ++ ++ CLEAR_IN_EP_INTR(core_if, epnum, emptyintr); ++ ++ } ++ ++ /** IN EP BNA Intr */ ++ if (diepint.b.bna) { ++ CLEAR_IN_EP_INTR(core_if, epnum, bna); ++ if (core_if->dma_desc_enable) { ++#ifdef DWC_EN_ISOC ++ if (dwc_ep->type == ++ DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * This checking is performed to prevent first "false" BNA ++ * handling occuring right after reconnect ++ */ ++ if (dwc_ep->next_frame != ++ 0xffffffff) ++ dwc_otg_pcd_handle_iso_bna(ep); ++ } else ++#endif /* DWC_EN_ISOC */ ++ { ++ dwc_otg_pcd_handle_noniso_bna(ep); ++ } ++ } ++ } ++ /* NAK Interrupt */ ++ if (diepint.b.nak) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d IN NAK Interrupt\n", ++ epnum); ++ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) { ++ if (core_if->dma_desc_enable) { ++ if (ep->dwc_ep.frame_num == 0xFFFFFFFF) { ++ ep->dwc_ep.frame_num = core_if->frame_num; ++ dwc_otg_pcd_start_iso_ddma(core_if, ep); ++ } else { ++ ep->dwc_ep.frame_num = core_if->frame_num + ep->dwc_ep.bInterval; ++ CLEAR_IN_EP_INTR(core_if, epnum, nak); ++ } ++ } else { ++ depctl_data_t depctl; ++ if (ep->dwc_ep.frame_num == 0xFFFFFFFF) { ++ ep->dwc_ep.frame_num = core_if->frame_num; ++ if (ep->dwc_ep.bInterval > 1) { ++ depctl.d32 = 0; ++ depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl); ++ if (ep->dwc_ep.frame_num & 0x1) { ++ depctl.b.setd1pid = 1; ++ depctl.b.setd0pid = 0; ++ } else { ++ depctl.b.setd0pid = 1; ++ depctl.b.setd1pid = 0; ++ } ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[epnum]->diepctl, depctl.d32); ++ } ++ start_next_request(ep); ++ } ++ ep->dwc_ep.frame_num += ep->dwc_ep.bInterval; ++ if (dwc_ep->frame_num > 0x3FFF) { ++ dwc_ep->frm_overrun = 1; ++ dwc_ep->frame_num &= 0x3FFF; ++ } else { ++ dwc_ep->frm_overrun = 0; ++ } ++ } ++ } ++ ++ CLEAR_IN_EP_INTR(core_if, epnum, nak); ++ } ++ } ++ epnum++; ++ ep_intr >>= 1; ++ } ++ ++ return 1; ++#undef CLEAR_IN_EP_INTR ++} ++ ++/** ++ * This interrupt indicates that an OUT EP has a pending Interrupt. ++ * The sequence for handling the OUT EP interrupt is shown below: ++ * -# Read the Device All Endpoint Interrupt register ++ * -# Repeat the following for each OUT EP interrupt bit set (from ++ * LSB to MSB). ++ * -# Read the Device Endpoint Interrupt (DOEPINTn) register ++ * -# If "Transfer Complete" call the request complete function ++ * -# If "Endpoint Disabled" complete the EP disable procedure. ++ * -# If "AHB Error Interrupt" log error ++ * -# If "Setup Phase Done" process Setup Packet (See Standard USB ++ * Command Processing) ++ */ ++static int32_t dwc_otg_pcd_handle_out_ep_intr(dwc_otg_pcd_t * pcd) ++{ ++#define CLEAR_OUT_EP_INTR(__core_if,__epnum,__intr) \ ++do { \ ++ doepint_data_t doepint = {.d32=0}; \ ++ doepint.b.__intr = 1; \ ++ DWC_WRITE_REG32(&__core_if->dev_if->out_ep_regs[__epnum]->doepint, \ ++ doepint.d32); \ ++} while (0) ++ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ uint32_t ep_intr; ++ doepint_data_t doepint = {.d32 = 0 }; ++ uint32_t epnum = 0; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ dctl_data_t dctl = {.d32 = 0 }; ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__); ++ ++ /* Read in the device interrupt bits */ ++ ep_intr = dwc_otg_read_dev_all_out_ep_intr(core_if); ++ ++ while (ep_intr) { ++ if (ep_intr & 0x1) { ++ /* Get EP pointer */ ++ ep = get_out_ep(pcd, epnum); ++ if (!ep) ++ return -EINVAL; ++ ++ dwc_ep = &ep->dwc_ep; ++ if (!dwc_ep) ++ return -EINVAL; ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, ++ "EP%d-%s: type=%d, mps=%d\n", ++ dwc_ep->num, (dwc_ep->is_in ? "IN" : "OUT"), ++ dwc_ep->type, dwc_ep->maxpacket); ++#endif ++ doepint.d32 = ++ dwc_otg_read_dev_out_ep_intr(core_if, dwc_ep); ++ ++ /* Transfer complete */ ++ if (doepint.b.xfercompl) { ++ ++ if (epnum == 0) { ++ /* Clear the bit in DOEPINTn for this interrupt */ ++ CLEAR_OUT_EP_INTR(core_if, epnum, xfercompl); ++ if (core_if->snpsid >= OTG_CORE_REV_3_00a) { ++ DWC_DEBUGPL(DBG_PCDV, "in xfer xomplete DOEPINT=%x doepint=%x\n", ++ DWC_READ_REG32(&core_if->dev_if->out_ep_regs[0]->doepint), ++ doepint.d32); ++ DWC_DEBUGPL(DBG_PCDV, "DOEPCTL=%x \n", ++ DWC_READ_REG32(&core_if->dev_if->out_ep_regs[0]->doepctl)); ++ ++ if (core_if->snpsid >= OTG_CORE_REV_3_00a ++ && core_if->dma_enable == 0) { ++ doepint_data_t doepint; ++ doepint.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ out_ep_regs[0]->doepint); ++ if (pcd->ep0state == EP0_IDLE && doepint.b.sr) { ++ CLEAR_OUT_EP_INTR(core_if, epnum, sr); ++ if (doepint.b.stsphsercvd) ++ CLEAR_OUT_EP_INTR(core_if, epnum, stsphsercvd); ++ goto exit_xfercompl; ++ } ++ } ++ /* In case of DDMA look at SR bit to go to the Data Stage */ ++ if (core_if->dma_desc_enable) { ++ dev_dma_desc_sts_t status = {.d32 = 0}; ++ if (pcd->ep0state == EP0_IDLE) { ++ status.d32 = core_if->dev_if->setup_desc_addr[core_if-> ++ dev_if->setup_desc_index]->status.d32; ++ if(pcd->data_terminated) { ++ pcd->data_terminated = 0; ++ status.d32 = core_if->dev_if->out_desc_addr->status.d32; ++ dwc_memcpy(&pcd->setup_pkt->req, pcd->backup_buf, 8); ++ } ++ if (status.b.sr) { ++ if (doepint.b.setup) { ++ DWC_DEBUGPL(DBG_PCDV, "DMA DESC EP0_IDLE SR=1 setup=1\n"); ++ /* Already started data stage, clear setup */ ++ CLEAR_OUT_EP_INTR(core_if, epnum, setup); ++ doepint.b.setup = 0; ++ handle_ep0(pcd); ++ /* Prepare for more setup packets */ ++ if (pcd->ep0state == EP0_IN_STATUS_PHASE || ++ pcd->ep0state == EP0_IN_DATA_PHASE) { ++ ep0_out_start(core_if, pcd); ++ } ++ ++ goto exit_xfercompl; ++ } else { ++ /* Prepare for more setup packets */ ++ DWC_DEBUGPL(DBG_PCDV, ++ "EP0_IDLE SR=1 setup=0 new setup comes\n"); ++ ep0_out_start(core_if, pcd); ++ } ++ } ++ } else { ++ dwc_otg_pcd_request_t *req; ++ dev_dma_desc_sts_t status = {.d32 = 0}; ++ diepint_data_t diepint0; ++ diepint0.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ in_ep_regs[0]->diepint); ++ ++ if (pcd->ep0state == EP0_STALL || pcd->ep0state == EP0_DISCONNECT) { ++ DWC_ERROR("EP0 is stalled/disconnected\n"); ++ } ++ ++ /* Clear IN xfercompl if set */ ++ if (diepint0.b.xfercompl && (pcd->ep0state == EP0_IN_STATUS_PHASE ++ || pcd->ep0state == EP0_IN_DATA_PHASE)) { ++ DWC_WRITE_REG32(&core_if->dev_if-> ++ in_ep_regs[0]->diepint, diepint0.d32); ++ } ++ ++ status.d32 = core_if->dev_if->setup_desc_addr[core_if-> ++ dev_if->setup_desc_index]->status.d32; ++ ++ if ((pcd->ep0state == EP0_OUT_STATUS_PHASE) || ++ (ep->dwc_ep.xfer_count != ep->dwc_ep.total_len ++ && pcd->ep0state == EP0_OUT_DATA_PHASE)) ++ status.d32 = core_if->dev_if->out_desc_addr->status.d32; ++ if (status.b.sr) { ++ if (DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ DWC_DEBUGPL(DBG_PCDV, "Request queue empty!!\n"); ++ } else { ++ DWC_DEBUGPL(DBG_PCDV, "complete req!!\n"); ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ if (ep->dwc_ep.xfer_count != ep->dwc_ep.total_len && ++ pcd->ep0state == EP0_OUT_DATA_PHASE) { ++ /* Read arrived setup packet from req->buf */ ++ dwc_memcpy(&pcd->setup_pkt->req, ++ req->buf + ep->dwc_ep.xfer_count, 8); ++ } ++ req->actual = ep->dwc_ep.xfer_count; ++ dwc_otg_request_done(ep, req, -ECONNRESET); ++ ep->dwc_ep.start_xfer_buff = 0; ++ ep->dwc_ep.xfer_buff = 0; ++ ep->dwc_ep.xfer_len = 0; ++ } ++ pcd->ep0state = EP0_IDLE; ++ if (doepint.b.setup) { ++ DWC_DEBUGPL(DBG_PCDV, "EP0_IDLE SR=1 setup=1\n"); ++ /* Data stage started, clear setup */ ++ CLEAR_OUT_EP_INTR(core_if, epnum, setup); ++ doepint.b.setup = 0; ++ handle_ep0(pcd); ++ /* Prepare for setup packets if ep0in was enabled*/ ++ if (pcd->ep0state == EP0_IN_STATUS_PHASE) { ++ ep0_out_start(core_if, pcd); ++ } ++ ++ goto exit_xfercompl; ++ } else { ++ /* Prepare for more setup packets */ ++ DWC_DEBUGPL(DBG_PCDV, ++ "EP0_IDLE SR=1 setup=0 new setup comes 2\n"); ++ ep0_out_start(core_if, pcd); ++ } ++ } ++ } ++ } ++ if (core_if->snpsid >= OTG_CORE_REV_3_00a && core_if->dma_enable ++ && core_if->dma_desc_enable == 0) { ++ doepint_data_t doepint_temp = {.d32 = 0}; ++ deptsiz0_data_t doeptsize0 = {.d32 = 0 }; ++ doepint_temp.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ out_ep_regs[ep->dwc_ep.num]->doepint); ++ doeptsize0.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ out_ep_regs[ep->dwc_ep.num]->doeptsiz); ++ if (((ep->dwc_ep.xfer_count == ep->dwc_ep.total_len || doeptsize0.b.xfersize == 64) && ++ pcd->ep0state == EP0_OUT_DATA_PHASE && doepint.b.stsphsercvd) || ++ (doeptsize0.b.xfersize == 24 && pcd->ep0state == EP0_IN_STATUS_PHASE)) { ++ CLEAR_OUT_EP_INTR(core_if, epnum, xfercompl); ++ DWC_DEBUGPL(DBG_PCDV, "WA for xfercompl along with stsphs \n"); ++ doepint.b.xfercompl = 0; ++ ep0_out_start(core_if, pcd); ++ goto exit_xfercompl; ++ } ++ ++ if (pcd->ep0state == EP0_IDLE) { ++ if (doepint_temp.b.sr) { ++ CLEAR_OUT_EP_INTR(core_if, epnum, sr); ++ } ++ /* Delay is needed for core to update setup ++ * packet count from 3 to 2 after receiving ++ * setup packet*/ ++ dwc_udelay(100); ++ doepint.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ out_ep_regs[0]->doepint); ++ if (doeptsize0.b.supcnt == 3) { ++ DWC_DEBUGPL(DBG_ANY, "Rolling over!!!!!!!\n"); ++ ep->dwc_ep.stp_rollover = 1; ++ } ++ if (doepint.b.setup) { ++retry: ++ /* Already started data stage, clear setup */ ++ CLEAR_OUT_EP_INTR(core_if, epnum, setup); ++ doepint.b.setup = 0; ++ handle_ep0(pcd); ++ ep->dwc_ep.stp_rollover = 0; ++ /* Prepare for more setup packets */ ++ if (pcd->ep0state == EP0_IN_STATUS_PHASE || ++ pcd->ep0state == EP0_IN_DATA_PHASE) { ++ depctl_data_t depctl = {.d32 = 0}; ++ depctl.b.cnak = 1; ++ ep0_out_start(core_if, pcd); ++ /* Core not updating setup packet count ++ * in case of PET testing - @TODO vahrama ++ * to check with HW team further */ ++ if (!core_if->otg_ver) { ++ DWC_MODIFY_REG32(&core_if->dev_if-> ++ out_ep_regs[0]->doepctl, 0, depctl.d32); ++ } ++ } ++ goto exit_xfercompl; ++ } else { ++ /* Prepare for more setup packets */ ++ DWC_DEBUGPL(DBG_ANY, ++ "EP0_IDLE SR=1 setup=0 new setup comes\n"); ++ doepint.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ out_ep_regs[0]->doepint); ++ if(doepint.b.setup) ++ goto retry; ++ ep0_out_start(core_if, pcd); ++ } ++ } else { ++ dwc_otg_pcd_request_t *req; ++ diepint_data_t diepint0 = {.d32 = 0}; ++ doepint_data_t doepint_temp = {.d32 = 0}; ++ depctl_data_t diepctl0; ++ diepint0.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ in_ep_regs[0]->diepint); ++ diepctl0.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ in_ep_regs[0]->diepctl); ++ ++ if (pcd->ep0state == EP0_IN_DATA_PHASE ++ || pcd->ep0state == EP0_IN_STATUS_PHASE) { ++ if (diepint0.b.xfercompl) { ++ DWC_WRITE_REG32(&core_if->dev_if-> ++ in_ep_regs[0]->diepint, diepint0.d32); ++ } ++ if (diepctl0.b.epena) { ++ diepint_data_t diepint = {.d32 = 0}; ++ diepctl0.b.snak = 1; ++ DWC_WRITE_REG32(&core_if->dev_if-> ++ in_ep_regs[0]->diepctl, diepctl0.d32); ++ do { ++ dwc_udelay(10); ++ diepint.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ in_ep_regs[0]->diepint); ++ } while (!diepint.b.inepnakeff); ++ diepint.b.inepnakeff = 1; ++ DWC_WRITE_REG32(&core_if->dev_if-> ++ in_ep_regs[0]->diepint, diepint.d32); ++ diepctl0.d32 = 0; ++ diepctl0.b.epdis = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[0]->diepctl, ++ diepctl0.d32); ++ do { ++ dwc_udelay(10); ++ diepint.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ in_ep_regs[0]->diepint); ++ } while (!diepint.b.epdisabled); ++ diepint.b.epdisabled = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[0]->diepint, ++ diepint.d32); ++ } ++ } ++ doepint_temp.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ out_ep_regs[ep->dwc_ep.num]->doepint); ++ if (doepint_temp.b.sr) { ++ CLEAR_OUT_EP_INTR(core_if, epnum, sr); ++ if (DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ DWC_DEBUGPL(DBG_PCDV, "Request queue empty!!\n"); ++ } else { ++ DWC_DEBUGPL(DBG_PCDV, "complete req!!\n"); ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ if (ep->dwc_ep.xfer_count != ep->dwc_ep.total_len && ++ pcd->ep0state == EP0_OUT_DATA_PHASE) { ++ /* Read arrived setup packet from req->buf */ ++ dwc_memcpy(&pcd->setup_pkt->req, ++ req->buf + ep->dwc_ep.xfer_count, 8); ++ } ++ req->actual = ep->dwc_ep.xfer_count; ++ dwc_otg_request_done(ep, req, -ECONNRESET); ++ ep->dwc_ep.start_xfer_buff = 0; ++ ep->dwc_ep.xfer_buff = 0; ++ ep->dwc_ep.xfer_len = 0; ++ } ++ pcd->ep0state = EP0_IDLE; ++ if (doepint.b.setup) { ++ DWC_DEBUGPL(DBG_PCDV, "EP0_IDLE SR=1 setup=1\n"); ++ /* Data stage started, clear setup */ ++ CLEAR_OUT_EP_INTR(core_if, epnum, setup); ++ doepint.b.setup = 0; ++ handle_ep0(pcd); ++ /* Prepare for setup packets if ep0in was enabled*/ ++ if (pcd->ep0state == EP0_IN_STATUS_PHASE) { ++ depctl_data_t depctl = {.d32 = 0}; ++ depctl.b.cnak = 1; ++ ep0_out_start(core_if, pcd); ++ /* Core not updating setup packet count ++ * in case of PET testing - @TODO vahrama ++ * to check with HW team further */ ++ if (!core_if->otg_ver) { ++ DWC_MODIFY_REG32(&core_if->dev_if-> ++ out_ep_regs[0]->doepctl, 0, depctl.d32); ++ } ++ } ++ goto exit_xfercompl; ++ } else { ++ /* Prepare for more setup packets */ ++ DWC_DEBUGPL(DBG_PCDV, ++ "EP0_IDLE SR=1 setup=0 new setup comes 2\n"); ++ ep0_out_start(core_if, pcd); ++ } ++ } ++ } ++ } ++ if (core_if->dma_enable == 0 || pcd->ep0state != EP0_IDLE) ++ handle_ep0(pcd); ++exit_xfercompl: ++ DWC_DEBUGPL(DBG_PCDV, "after DOEPINT=%x doepint=%x\n", ++ dwc_otg_read_dev_out_ep_intr(core_if, dwc_ep), doepint.d32); ++ } else { ++ if (core_if->dma_desc_enable == 0 ++ || pcd->ep0state != EP0_IDLE) ++ handle_ep0(pcd); ++ } ++#ifdef DWC_EN_ISOC ++ } else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ if (doepint.b.pktdrpsts == 0) { ++ /* Clear the bit in DOEPINTn for this interrupt */ ++ CLEAR_OUT_EP_INTR(core_if, ++ epnum, ++ xfercompl); ++ complete_iso_ep(pcd, ep); ++ } else { ++ ++ doepint_data_t doepint = {.d32 = 0 }; ++ doepint.b.xfercompl = 1; ++ doepint.b.pktdrpsts = 1; ++ DWC_WRITE_REG32 ++ (&core_if->dev_if->out_ep_regs ++ [epnum]->doepint, ++ doepint.d32); ++ if (handle_iso_out_pkt_dropped ++ (core_if, dwc_ep)) { ++ complete_iso_ep(pcd, ++ ep); ++ } ++ } ++#endif /* DWC_EN_ISOC */ ++#ifdef DWC_UTE_PER_IO ++ } else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ CLEAR_OUT_EP_INTR(core_if, epnum, xfercompl); ++ if (!ep->stopped) ++ complete_xiso_ep(ep); ++#endif /* DWC_UTE_PER_IO */ ++ } else { ++ /* Clear the bit in DOEPINTn for this interrupt */ ++ CLEAR_OUT_EP_INTR(core_if, epnum, ++ xfercompl); ++ ++ if (core_if->core_params->dev_out_nak) { ++ DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[epnum]); ++ pcd->core_if->ep_xfer_info[epnum].state = 0; ++#ifdef DEBUG ++ print_memory_payload(pcd, dwc_ep); ++#endif ++ } ++ if (core_if->dma_desc_enable && dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ handle_xfercompl_iso_ddma(core_if->dev_if, ep); ++ } else { ++ complete_ep(ep); ++ } ++ } ++ ++ } ++ if (doepint.b.stsphsercvd) { ++ deptsiz0_data_t deptsiz; ++ CLEAR_OUT_EP_INTR(core_if, epnum, stsphsercvd); ++ deptsiz.d32 = ++ DWC_READ_REG32(&core_if->dev_if-> ++ out_ep_regs[0]->doeptsiz); ++ if ((core_if->dma_desc_enable) || (core_if->dma_enable && ++ core_if->snpsid >= OTG_CORE_REV_3_00a)) { ++ do_setup_in_status_phase(pcd); ++ } ++ } ++ ++ /* Endpoint disable */ ++ if (doepint.b.epdisabled) { ++ ++ /* Clear the bit in DOEPINTn for this interrupt */ ++ CLEAR_OUT_EP_INTR(core_if, epnum, epdisabled); ++ if (core_if->core_params->dev_out_nak) { ++#ifdef DEBUG ++ print_memory_payload(pcd, dwc_ep); ++#endif ++ /* In case of timeout condition */ ++ if (core_if->ep_xfer_info[epnum].state == 2) { ++ dctl.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ dev_global_regs->dctl); ++ dctl.b.cgoutnak = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, ++ dctl.d32); ++ /* Unmask goutnakeff interrupt which was masked ++ * during handle nak out interrupt */ ++ gintmsk.b.goutnakeff = 1; ++ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, ++ 0, gintmsk.d32); ++ ++ complete_ep(ep); ++ } ++ } ++ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) ++ { ++ dctl_data_t dctl; ++ gintmsk_data_t intr_mask = {.d32 = 0}; ++ dwc_otg_pcd_request_t *req = 0; ++ ++ dctl.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ dev_global_regs->dctl); ++ dctl.b.cgoutnak = 1; ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, ++ dctl.d32); ++ ++ intr_mask.d32 = 0; ++ intr_mask.b.incomplisoout = 1; ++ ++ /* Get any pending requests */ ++ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ if (!req) { ++ DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep); ++ } else { ++ dwc_otg_request_done(ep, req, 0); ++ start_next_request(ep); ++ } ++ } else { ++ DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep); ++ } ++ } ++ } ++ /* AHB Error */ ++ if (doepint.b.ahberr) { ++ DWC_ERROR("EP%d OUT AHB Error\n", epnum); ++ DWC_ERROR("EP%d DEPDMA=0x%08x \n", ++ epnum, core_if->dev_if->out_ep_regs[epnum]->doepdma); ++ CLEAR_OUT_EP_INTR(core_if, epnum, ahberr); ++ } ++ /* Setup Phase Done (contorl EPs) */ ++ if (doepint.b.setup) { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "EP%d SETUP Done\n", epnum); ++#endif ++ CLEAR_OUT_EP_INTR(core_if, epnum, setup); ++ ++ handle_ep0(pcd); ++ } ++ ++ /** OUT EP BNA Intr */ ++ if (doepint.b.bna) { ++ CLEAR_OUT_EP_INTR(core_if, epnum, bna); ++ if (core_if->dma_desc_enable) { ++#ifdef DWC_EN_ISOC ++ if (dwc_ep->type == ++ DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * This checking is performed to prevent first "false" BNA ++ * handling occuring right after reconnect ++ */ ++ if (dwc_ep->next_frame != ++ 0xffffffff) ++ dwc_otg_pcd_handle_iso_bna(ep); ++ } else ++#endif /* DWC_EN_ISOC */ ++ if (ep->dwc_ep.type != DWC_OTG_EP_TYPE_ISOC) { ++ dwc_otg_pcd_handle_noniso_bna(ep); ++ } ++ } ++ } ++ /* Babble Interrupt */ ++ if (doepint.b.babble) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d OUT Babble\n", ++ epnum); ++ handle_out_ep_babble_intr(pcd, epnum); ++ ++ CLEAR_OUT_EP_INTR(core_if, epnum, babble); ++ } ++ if (doepint.b.outtknepdis) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d OUT Token received when EP is \ ++ disabled\n",epnum); ++ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) { ++ if (core_if->dma_desc_enable) { ++ if (!ep->dwc_ep.iso_transfer_started) { ++ ep->dwc_ep.frame_num = core_if->frame_num; ++ dwc_otg_pcd_start_iso_ddma(core_if, ep); ++ } ++ } else { ++ doepmsk_data_t doepmsk = {.d32 = 0}; ++ ep->dwc_ep.frame_num = core_if->frame_num; ++ if (ep->dwc_ep.bInterval > 1) { ++ depctl_data_t depctl; ++ depctl.d32 = DWC_READ_REG32(&core_if->dev_if-> ++ out_ep_regs[epnum]->doepctl); ++ if (ep->dwc_ep.frame_num & 0x1) { ++ depctl.b.setd1pid = 1; ++ depctl.b.setd0pid = 0; ++ } else { ++ depctl.b.setd0pid = 1; ++ depctl.b.setd1pid = 0; ++ } ++ DWC_WRITE_REG32(&core_if->dev_if-> ++ out_ep_regs[epnum]->doepctl, depctl.d32); ++ } ++ ++ start_next_request(ep); ++ doepmsk.b.outtknepdis = 1; ++ DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk, ++ doepmsk.d32, 0); ++ } ++ } ++ CLEAR_OUT_EP_INTR(core_if, epnum, outtknepdis); ++ } ++ ++ /* NAK Interrutp */ ++ if (doepint.b.nak) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d OUT NAK\n", epnum); ++ handle_out_ep_nak_intr(pcd, epnum); ++ ++ CLEAR_OUT_EP_INTR(core_if, epnum, nak); ++ } ++ /* NYET Interrutp */ ++ if (doepint.b.nyet) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d OUT NYET\n", epnum); ++ handle_out_ep_nyet_intr(pcd, epnum); ++ ++ CLEAR_OUT_EP_INTR(core_if, epnum, nyet); ++ } ++ } ++ ++ epnum++; ++ ep_intr >>= 1; ++ } ++ ++ return 1; ++ ++#undef CLEAR_OUT_EP_INTR ++} ++static int drop_transfer(uint32_t trgt_fr, uint32_t curr_fr, uint8_t frm_overrun) ++{ ++ int retval = 0; ++ if(!frm_overrun && curr_fr >= trgt_fr) ++ retval = 1; ++ else if (frm_overrun ++ && (curr_fr >= trgt_fr && ((curr_fr - trgt_fr) < 0x3FFF / 2))) ++ retval = 1; ++ return retval; ++} ++ ++/** ++ * Incomplete ISO IN Transfer Interrupt. ++ * This interrupt indicates one of the following conditions occurred ++ * while transmitting an ISOC transaction. ++ * - Corrupted IN Token for ISOC EP. ++ * - Packet not complete in FIFO. ++ * The follow actions will be taken: ++ * -# Determine the EP ++ * -# Set incomplete flag in dwc_ep structure ++ * -# Disable EP; when "Endpoint Disabled" interrupt is received ++ * Flush FIFO ++ */ ++int32_t dwc_otg_pcd_handle_incomplete_isoc_in_intr(dwc_otg_pcd_t * pcd) ++{ ++ gintsts_data_t gintsts; ++ ++#ifdef DWC_EN_ISOC ++ dwc_otg_dev_if_t *dev_if; ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ dsts_data_t dsts = {.d32 = 0 }; ++ dwc_ep_t *dwc_ep; ++ int i; ++ ++ dev_if = GET_CORE_IF(pcd)->dev_if; ++ ++ for (i = 1; i <= dev_if->num_in_eps; ++i) { ++ dwc_ep = &pcd->in_ep[i].dwc_ep; ++ if (dwc_ep->active && dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ deptsiz.d32 = ++ DWC_READ_REG32(&dev_if->in_ep_regs[i]->dieptsiz); ++ depctl.d32 = ++ DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl); ++ ++ if (depctl.b.epdis && deptsiz.d32) { ++ set_current_pkt_info(GET_CORE_IF(pcd), dwc_ep); ++ if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { ++ dwc_ep->cur_pkt = 0; ++ dwc_ep->proc_buf_num = ++ (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ ++ if (dwc_ep->proc_buf_num) { ++ dwc_ep->cur_pkt_addr = ++ dwc_ep->xfer_buff1; ++ dwc_ep->cur_pkt_dma_addr = ++ dwc_ep->dma_addr1; ++ } else { ++ dwc_ep->cur_pkt_addr = ++ dwc_ep->xfer_buff0; ++ dwc_ep->cur_pkt_dma_addr = ++ dwc_ep->dma_addr0; ++ } ++ ++ } ++ ++ dsts.d32 = ++ DWC_READ_REG32(&GET_CORE_IF(pcd)->dev_if-> ++ dev_global_regs->dsts); ++ dwc_ep->next_frame = dsts.b.soffn; ++ ++ dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF ++ (pcd), ++ dwc_ep); ++ } ++ } ++ } ++ ++#else ++ depctl_data_t depctl = {.d32 = 0 }; ++ dwc_ep_t *dwc_ep; ++ dwc_otg_dev_if_t *dev_if; ++ int i; ++ dev_if = GET_CORE_IF(pcd)->dev_if; ++ ++ DWC_DEBUGPL(DBG_PCD,"Incomplete ISO IN \n"); ++ ++ for (i = 1; i <= dev_if->num_in_eps; ++i) { ++ dwc_ep = &pcd->in_ep[i-1].dwc_ep; ++ depctl.d32 = ++ DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl); ++ if (depctl.b.epena && dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ if (drop_transfer(dwc_ep->frame_num, GET_CORE_IF(pcd)->frame_num, ++ dwc_ep->frm_overrun)) ++ { ++ depctl.d32 = ++ DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl); ++ depctl.b.snak = 1; ++ depctl.b.epdis = 1; ++ DWC_MODIFY_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32, depctl.d32); ++ } ++ } ++ } ++ ++ /*intr_mask.b.incomplisoin = 1; ++ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); */ ++#endif //DWC_EN_ISOC ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.incomplisoin = 1; ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * Incomplete ISO OUT Transfer Interrupt. ++ * ++ * This interrupt indicates that the core has dropped an ISO OUT ++ * packet. The following conditions can be the cause: ++ * - FIFO Full, the entire packet would not fit in the FIFO. ++ * - CRC Error ++ * - Corrupted Token ++ * The follow actions will be taken: ++ * -# Determine the EP ++ * -# Set incomplete flag in dwc_ep structure ++ * -# Read any data from the FIFO ++ * -# Disable EP. When "Endpoint Disabled" interrupt is received ++ * re-enable EP. ++ */ ++int32_t dwc_otg_pcd_handle_incomplete_isoc_out_intr(dwc_otg_pcd_t * pcd) ++{ ++ ++ gintsts_data_t gintsts; ++ ++#ifdef DWC_EN_ISOC ++ dwc_otg_dev_if_t *dev_if; ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ dsts_data_t dsts = {.d32 = 0 }; ++ dwc_ep_t *dwc_ep; ++ int i; ++ ++ dev_if = GET_CORE_IF(pcd)->dev_if; ++ ++ for (i = 1; i <= dev_if->num_out_eps; ++i) { ++ dwc_ep = &pcd->in_ep[i].dwc_ep; ++ if (pcd->out_ep[i].dwc_ep.active && ++ pcd->out_ep[i].dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) { ++ deptsiz.d32 = ++ DWC_READ_REG32(&dev_if->out_ep_regs[i]->doeptsiz); ++ depctl.d32 = ++ DWC_READ_REG32(&dev_if->out_ep_regs[i]->doepctl); ++ ++ if (depctl.b.epdis && deptsiz.d32) { ++ set_current_pkt_info(GET_CORE_IF(pcd), ++ &pcd->out_ep[i].dwc_ep); ++ if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { ++ dwc_ep->cur_pkt = 0; ++ dwc_ep->proc_buf_num = ++ (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ ++ if (dwc_ep->proc_buf_num) { ++ dwc_ep->cur_pkt_addr = ++ dwc_ep->xfer_buff1; ++ dwc_ep->cur_pkt_dma_addr = ++ dwc_ep->dma_addr1; ++ } else { ++ dwc_ep->cur_pkt_addr = ++ dwc_ep->xfer_buff0; ++ dwc_ep->cur_pkt_dma_addr = ++ dwc_ep->dma_addr0; ++ } ++ ++ } ++ ++ dsts.d32 = ++ DWC_READ_REG32(&GET_CORE_IF(pcd)->dev_if-> ++ dev_global_regs->dsts); ++ dwc_ep->next_frame = dsts.b.soffn; ++ ++ dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF ++ (pcd), ++ dwc_ep); ++ } ++ } ++ } ++#else ++ /** @todo implement ISR */ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ dwc_otg_core_if_t *core_if; ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ dctl_data_t dctl = {.d32 = 0 }; ++ dwc_ep_t *dwc_ep = NULL; ++ int i; ++ core_if = GET_CORE_IF(pcd); ++ ++ for (i = 0; i < core_if->dev_if->num_out_eps; ++i) { ++ dwc_ep = &pcd->out_ep[i].dwc_ep; ++ depctl.d32 = ++ DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl); ++ if (depctl.b.epena && depctl.b.dpid == (core_if->frame_num & 0x1)) { ++ core_if->dev_if->isoc_ep = dwc_ep; ++ deptsiz.d32 = ++ DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doeptsiz); ++ break; ++ } ++ } ++ dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl); ++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ intr_mask.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); ++ ++ if (!intr_mask.b.goutnakeff) { ++ /* Unmask it */ ++ intr_mask.b.goutnakeff = 1; ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, intr_mask.d32); ++ } ++ if (!gintsts.b.goutnakeff) { ++ dctl.b.sgoutnak = 1; ++ } ++ DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32); ++ ++ depctl.d32 = DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl); ++ if (depctl.b.epena) { ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ } ++ DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl, depctl.d32); ++ ++ intr_mask.d32 = 0; ++ intr_mask.b.incomplisoout = 1; ++ ++#endif /* DWC_EN_ISOC */ ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.incomplisoout = 1; ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This function handles the Global IN NAK Effective interrupt. ++ * ++ */ ++int32_t dwc_otg_pcd_handle_in_nak_effective(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if; ++ depctl_data_t diepctl = {.d32 = 0 }; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ int i; ++ ++ DWC_DEBUGPL(DBG_PCD, "Global IN NAK Effective\n"); ++ ++ /* Disable all active IN EPs */ ++ for (i = 0; i <= dev_if->num_in_eps; i++) { ++ diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl); ++ if (!(diepctl.b.eptype & 1) && diepctl.b.epena) { ++ if (core_if->start_predict > 0) ++ core_if->start_predict++; ++ diepctl.b.epdis = 1; ++ diepctl.b.snak = 1; ++ DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, diepctl.d32); ++ } ++ } ++ ++ ++ /* Disable the Global IN NAK Effective Interrupt */ ++ intr_mask.b.ginnakeff = 1; ++ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.ginnakeff = 1; ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * OUT NAK Effective. ++ * ++ */ ++int32_t dwc_otg_pcd_handle_out_nak_effective(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ depctl_data_t doepctl; ++ int i; ++ ++ /* Disable the Global OUT NAK Effective Interrupt */ ++ intr_mask.b.goutnakeff = 1; ++ DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* If DEV OUT NAK enabled */ ++ if (pcd->core_if->core_params->dev_out_nak) { ++ /* Run over all out endpoints to determine the ep number on ++ * which the timeout has happened ++ */ ++ for (i = 0; i <= dev_if->num_out_eps; i++) { ++ if (pcd->core_if->ep_xfer_info[i].state == 2) ++ break; ++ } ++ if (i > dev_if->num_out_eps) { ++ dctl_data_t dctl; ++ dctl.d32 = ++ DWC_READ_REG32(&dev_if->dev_global_regs->dctl); ++ dctl.b.cgoutnak = 1; ++ DWC_WRITE_REG32(&dev_if->dev_global_regs->dctl, ++ dctl.d32); ++ goto out; ++ } ++ ++ /* Disable the endpoint */ ++ doepctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[i]->doepctl); ++ if (doepctl.b.epena) { ++ doepctl.b.epdis = 1; ++ doepctl.b.snak = 1; ++ } ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl, doepctl.d32); ++ return 1; ++ } ++ /* We come here from Incomplete ISO OUT handler */ ++ if (dev_if->isoc_ep) { ++ dwc_ep_t *dwc_ep = (dwc_ep_t *) dev_if->isoc_ep; ++ uint32_t epnum = dwc_ep->num; ++ doepint_data_t doepint; ++ doepint.d32 = ++ DWC_READ_REG32(&dev_if->out_ep_regs[dwc_ep->num]->doepint); ++ dev_if->isoc_ep = NULL; ++ doepctl.d32 = ++ DWC_READ_REG32(&dev_if->out_ep_regs[epnum]->doepctl); ++ DWC_PRINTF("Before disable DOEPCTL = %08x\n", doepctl.d32); ++ if (doepctl.b.epena) { ++ doepctl.b.epdis = 1; ++ doepctl.b.snak = 1; ++ } ++ DWC_WRITE_REG32(&dev_if->out_ep_regs[epnum]->doepctl, ++ doepctl.d32); ++ return 1; ++ } else ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", ++ "Global OUT NAK Effective\n"); ++ ++out: ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.goutnakeff = 1; ++ DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * PCD interrupt handler. ++ * ++ * The PCD handles the device interrupts. Many conditions can cause a ++ * device interrupt. When an interrupt occurs, the device interrupt ++ * service routine determines the cause of the interrupt and ++ * dispatches handling to the appropriate function. These interrupt ++ * handling functions are described below. ++ * ++ * All interrupt registers are processed from LSB to MSB. ++ * ++ */ ++int32_t dwc_otg_pcd_handle_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++#ifdef VERBOSE ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++#endif ++ gintsts_data_t gintr_status; ++ int32_t retval = 0; ++ ++ if (dwc_otg_check_haps_status(core_if) == -1 ) { ++ DWC_WARN("HAPS is disconnected"); ++ return retval; ++ } ++ ++ /* Exit from ISR if core is hibernated */ ++ if (core_if->hibernation_suspend == 1) { ++ return retval; ++ } ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_ANY, "%s() gintsts=%08x gintmsk=%08x\n", ++ __func__, ++ DWC_READ_REG32(&global_regs->gintsts), ++ DWC_READ_REG32(&global_regs->gintmsk)); ++#endif ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ DWC_SPINLOCK(pcd->lock); ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%08x gintmsk=%08x\n", ++ __func__, ++ DWC_READ_REG32(&global_regs->gintsts), ++ DWC_READ_REG32(&global_regs->gintmsk)); ++#endif ++ ++ gintr_status.d32 = dwc_otg_read_core_intr(core_if); ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s: gintsts&gintmsk=%08x\n", ++ __func__, gintr_status.d32); ++ ++ if (gintr_status.b.sofintr) { ++ retval |= dwc_otg_pcd_handle_sof_intr(pcd); ++ } ++ if (gintr_status.b.rxstsqlvl) { ++ retval |= ++ dwc_otg_pcd_handle_rx_status_q_level_intr(pcd); ++ } ++ if (gintr_status.b.nptxfempty) { ++ retval |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr(pcd); ++ } ++ if (gintr_status.b.goutnakeff) { ++ retval |= dwc_otg_pcd_handle_out_nak_effective(pcd); ++ } ++ if (gintr_status.b.i2cintr) { ++ retval |= dwc_otg_pcd_handle_i2c_intr(pcd); ++ } ++ if (gintr_status.b.erlysuspend) { ++ retval |= dwc_otg_pcd_handle_early_suspend_intr(pcd); ++ otg_usbhost_stat = 0; ++ hisi_switch_func(0); ++ } ++ if (gintr_status.b.usbreset) { ++ otg_usbhost_stat = 1; ++ retval |= dwc_otg_pcd_handle_usb_reset_intr(pcd); ++ hisi_switch_func(0); ++ } ++ if (gintr_status.b.enumdone) { ++ retval |= dwc_otg_pcd_handle_enum_done_intr(pcd); ++ } ++ if (gintr_status.b.isooutdrop) { ++ retval |= ++ dwc_otg_pcd_handle_isoc_out_packet_dropped_intr ++ (pcd); ++ } ++ if (gintr_status.b.eopframe) { ++ retval |= ++ dwc_otg_pcd_handle_end_periodic_frame_intr(pcd); ++ } ++ if (gintr_status.b.inepint) { ++ if (!core_if->multiproc_int_enable) { ++ retval |= dwc_otg_pcd_handle_in_ep_intr(pcd); ++ } ++ } ++ if (gintr_status.b.outepintr) { ++ otg_usbhost_stat = 1; ++ if (!core_if->multiproc_int_enable) { ++ retval |= dwc_otg_pcd_handle_out_ep_intr(pcd); ++ } ++ } ++ if (gintr_status.b.epmismatch) { ++ retval |= dwc_otg_pcd_handle_ep_mismatch_intr(pcd); ++ } ++ if (gintr_status.b.fetsusp) { ++ retval |= dwc_otg_pcd_handle_ep_fetsusp_intr(pcd); ++ } ++ if (gintr_status.b.ginnakeff) { ++ retval |= dwc_otg_pcd_handle_in_nak_effective(pcd); ++ } ++ if (gintr_status.b.incomplisoin) { ++ retval |= ++ dwc_otg_pcd_handle_incomplete_isoc_in_intr(pcd); ++ } ++ if (gintr_status.b.incomplisoout) { ++ retval |= ++ dwc_otg_pcd_handle_incomplete_isoc_out_intr(pcd); ++ } ++ ++ /* In MPI mode Device Endpoints interrupts are asserted ++ * without setting outepintr and inepint bits set, so these ++ * Interrupt handlers are called without checking these bit-fields ++ */ ++ if (core_if->multiproc_int_enable) { ++ retval |= dwc_otg_pcd_handle_in_ep_intr(pcd); ++ retval |= dwc_otg_pcd_handle_out_ep_intr(pcd); ++ } ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%0x\n", __func__, ++ DWC_READ_REG32(&global_regs->gintsts)); ++#endif ++ DWC_SPINUNLOCK(pcd->lock); ++ } ++ return retval; ++} ++ ++#endif /* DWC_HOST_ONLY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd_linux.c b/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd_linux.c +new file mode 100644 +index 0000000..109ac01 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_pcd_linux.c +@@ -0,0 +1,1460 @@ ++ /* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_linux.c $ ++ * $Revision: #28 $ ++ * $Date: 2013/05/07 $ ++ * $Change: 2224063 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++ ++/** @file ++ * This file implements the Peripheral Controller Driver. ++ * ++ * The Peripheral Controller Driver (PCD) is responsible for ++ * translating requests from the Function Driver into the appropriate ++ * actions on the DWC_otg controller. It isolates the Function Driver ++ * from the specifics of the controller by providing an API to the ++ * Function Driver. ++ * ++ * The Peripheral Controller Driver for Linux will implement the ++ * Gadget API, so that the existing Gadget drivers can be used. ++ * (Gadget Driver is the Linux terminology for a Function Driver.) ++ * ++ * The Linux Gadget API is defined in the header file ++ * <code><linux/usb_gadget.h></code>. The USB EP operations API is ++ * defined in the structure <code>usb_ep_ops</code> and the USB ++ * Controller API is defined in the structure ++ * <code>usb_gadget_ops</code>. ++ * ++ */ ++ ++#include "dwc_otg_os_dep.h" ++#include "dwc_otg_pcd_if.h" ++#include "dwc_otg_pcd.h" ++#include "dwc_otg_driver.h" ++#include "dwc_otg_dbg.h" ++ ++static struct gadget_wrapper { ++ dwc_otg_pcd_t *pcd; ++ ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ ++ struct usb_ep ep0; ++ struct usb_ep in_ep[16]; ++ struct usb_ep out_ep[16]; ++ ++} *gadget_wrapper; ++ ++/* Display the contents of the buffer */ ++extern void dump_msg(const u8 * buf, unsigned int length); ++ ++int udc_attach_driver(const char *name, struct usb_gadget_driver *driver) ++{ ++ //do nothing ,only make ++ return 0; ++} ++EXPORT_SYMBOL_GPL(udc_attach_driver); ++ ++void usb_gadget_set_state(struct usb_gadget *gadget, ++ enum usb_device_state state) ++{ ++ gadget->state = state; ++} ++EXPORT_SYMBOL_GPL(usb_gadget_set_state); ++ ++static int usb_gadget_map_req(struct usb_gadget *gadget, ++ struct usb_request *req, struct dwc_otg_pcd_ep *ep) ++{ ++ if (req->length == 0) ++ return 0; ++ ++ if (req->num_sgs) { ++ dev_err(&gadget->dev, "controller not support scatter/gather dma\n"); ++ return -EFAULT; ++ ++ } else { ++ ++ if (ep == &gadget_wrapper->pcd->ep0) { ++ req->dma = dma_map_single(&gadget->dev, req->buf, req->length, ++ DMA_BIDIRECTIONAL); ++ } else { ++ req->dma = dma_map_single(&gadget->dev, req->buf, req->length, ++ ep->dwc_ep.is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); ++ } ++ ++ if (dma_mapping_error(&gadget->dev, req->dma)) { ++ dev_err(&gadget->dev, "failed to map buffer\n"); ++ return -EFAULT; ++ } ++ } ++ ++ return 0; ++} ++ ++static void usb_gadget_unmap_req(struct usb_gadget *gadget, ++ struct usb_request *req, struct dwc_otg_pcd_ep *ep) ++{ ++ if (req->length == 0) ++ return; ++ ++ if (req->num_mapped_sgs) { ++ dev_err(&gadget->dev, "controller not support scatter/gather dma\n"); ++ } else { ++ ++ if (ep == &gadget_wrapper->pcd->ep0) { ++ dma_unmap_single(&gadget->dev, req->dma, req->length, ++ DMA_BIDIRECTIONAL); ++ } else { ++ dma_unmap_single(&gadget->dev, req->dma, req->length, ++ ep->dwc_ep.is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); ++ } ++ } ++} ++ ++/** ++ * Get the dwc_otg_pcd_ep_t* from usb_ep* pointer - NULL in case ++ * if the endpoint is not found ++ */ ++static struct dwc_otg_pcd_ep *ep_from_handle(dwc_otg_pcd_t * pcd, void *handle) ++{ ++ int i; ++ if (pcd->ep0.priv == handle) { ++ return &pcd->ep0; ++ } ++ ++ for (i = 0; i < MAX_EPS_CHANNELS - 1; i++) { ++ if (pcd->in_ep[i].priv == handle) ++ return &pcd->in_ep[i]; ++ if (pcd->out_ep[i].priv == handle) ++ return &pcd->out_ep[i]; ++ } ++ ++ return NULL; ++} ++ ++/* USB Endpoint Operations */ ++/* ++ * The following sections briefly describe the behavior of the Gadget ++ * API endpoint operations implemented in the DWC_otg driver ++ * software. Detailed descriptions of the generic behavior of each of ++ * these functions can be found in the Linux header file ++ * include/linux/usb_gadget.h. ++ * ++ * The Gadget API provides wrapper functions for each of the function ++ * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper ++ * function, which then calls the underlying PCD function. The ++ * following sections are named according to the wrapper ++ * functions. Within each section, the corresponding DWC_otg PCD ++ * function name is specified. ++ * ++ */ ++ ++/** ++ * This function is called by the Gadget Driver for each EP to be ++ * configured for the current configuration (SET_CONFIGURATION). ++ * ++ * This function initializes the dwc_otg_ep_t data structure, and then ++ * calls dwc_otg_ep_activate. ++ */ ++static int ep_enable(struct usb_ep *usb_ep, ++ const struct usb_endpoint_descriptor *ep_desc) ++{ ++ int retval; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, ep_desc); ++ ++ if (!usb_ep || !ep_desc || ep_desc->bDescriptorType != USB_DT_ENDPOINT) { ++ DWC_WARN("%s, bad ep or descriptor\n", __func__); ++ return -EINVAL; ++ } ++ if (usb_ep == &gadget_wrapper->ep0) { ++ DWC_WARN("%s, bad ep(0)\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* Check FIFO size? */ ++ if (!ep_desc->wMaxPacketSize) { ++ DWC_WARN("%s, bad %s maxpacket\n", __func__, usb_ep->name); ++ return -ERANGE; ++ } ++ ++ if (!gadget_wrapper->driver || ++ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_WARN("%s, bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ ++ /* Delete after check - MAS */ ++#if 0 ++ nat = (uint32_t) ep_desc->wMaxPacketSize; ++ printk(KERN_ALERT "%s: nat (before) =%d\n", __func__, nat); ++ nat = (nat >> 11) & 0x03; ++ printk(KERN_ALERT "%s: nat (after) =%d\n", __func__, nat); ++#endif ++ retval = dwc_otg_pcd_ep_enable(gadget_wrapper->pcd, ++ (const uint8_t *)ep_desc, ++ (void *)usb_ep); ++ if (retval) { ++ DWC_WARN("dwc_otg_pcd_ep_enable failed\n"); ++ return -EINVAL; ++ } ++ ++ usb_ep->maxpacket = le16_to_cpu(ep_desc->wMaxPacketSize); ++ ++ if (usb_ep->maxpacket == 5120) ++ usb_ep->maxpacket = 3072; ++ else if (usb_ep->maxpacket == 3072) ++ usb_ep->maxpacket = 2048; ++ ++ return 0; ++} ++ ++/** ++ * This function is called when an EP is disabled due to disconnect or ++ * change in configuration. Any pending requests will terminate with a ++ * status of -ESHUTDOWN. ++ * ++ * This function modifies the dwc_otg_ep_t data structure for this EP, ++ * and then calls dwc_otg_ep_deactivate. ++ */ ++static int ep_disable(struct usb_ep *usb_ep) ++{ ++ int retval; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, usb_ep); ++ if (!usb_ep) { ++ DWC_DEBUGPL(DBG_PCD, "%s, %s not enabled\n", __func__, ++ usb_ep ? usb_ep->name : NULL); ++ return -EINVAL; ++ } ++ ++ retval = dwc_otg_pcd_ep_disable(gadget_wrapper->pcd, usb_ep); ++ if (retval) { ++ retval = -EINVAL; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function allocates a request object to use with the specified ++ * endpoint. ++ * ++ * @param ep The endpoint to be used with with the request ++ * @param gfp_flags the GFP_* flags to use. ++ */ ++static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *ep, ++ gfp_t gfp_flags) ++{ ++ struct usb_request *usb_req; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d)\n", __func__, ep, gfp_flags); ++ if (0 == ep) { ++ DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n"); ++ return 0; ++ } ++ usb_req = kmalloc(sizeof(*usb_req), gfp_flags); ++ if (0 == usb_req) { ++ DWC_WARN("%s() %s\n", __func__, "request allocation failed!\n"); ++ return 0; ++ } ++ memset(usb_req, 0, sizeof(*usb_req)); ++ usb_req->dma = DWC_DMA_ADDR_INVALID; ++ ++ return usb_req; ++} ++ ++/** ++ * This function frees a request object. ++ * ++ * @param ep The endpoint associated with the request ++ * @param req The request being freed ++ */ ++static void dwc_otg_pcd_free_request(struct usb_ep *ep, struct usb_request *req) ++{ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, ep, req); ++ ++ if (0 == ep || 0 == req) { ++ DWC_WARN("%s() %s\n", __func__, ++ "Invalid ep or req argument!\n"); ++ return; ++ } ++ ++ kfree(req); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++/** ++ * This function allocates an I/O buffer to be used for a transfer ++ * to/from the specified endpoint. ++ * ++ * @param usb_ep The endpoint to be used with with the request ++ * @param bytes The desired number of bytes for the buffer ++ * @param dma Pointer to the buffer's DMA address; must be valid ++ * @param gfp_flags the GFP_* flags to use. ++ * @return address of a new buffer or null is buffer could not be allocated. ++ */ ++static void *dwc_otg_pcd_alloc_buffer(struct usb_ep *usb_ep, unsigned bytes, ++ dma_addr_t * dma, gfp_t gfp_flags) ++{ ++ void *buf; ++ dwc_otg_pcd_t *pcd = 0; ++ ++ pcd = gadget_wrapper->pcd; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d,%p,%0x)\n", __func__, usb_ep, bytes, ++ dma, gfp_flags); ++ ++ /* Check dword alignment */ ++ if ((bytes & 0x3UL) != 0) { ++ DWC_WARN("%s() Buffer size is not a multiple of" ++ "DWORD size (%d)", __func__, bytes); ++ } ++ ++ buf = dma_alloc_coherent(NULL, bytes, dma, gfp_flags); ++ ++ /* Check dword alignment */ ++ if (((int)buf & 0x3UL) != 0) { ++ DWC_WARN("%s() Buffer is not DWORD aligned (%p)", ++ __func__, buf); ++ } ++ ++ return buf; ++} ++ ++/** ++ * This function frees an I/O buffer that was allocated by alloc_buffer. ++ * ++ * @param usb_ep the endpoint associated with the buffer ++ * @param buf address of the buffer ++ * @param dma The buffer's DMA address ++ * @param bytes The number of bytes of the buffer ++ */ ++static void dwc_otg_pcd_free_buffer(struct usb_ep *usb_ep, void *buf, ++ dma_addr_t dma, unsigned bytes) ++{ ++ dwc_otg_pcd_t *pcd = 0; ++ ++ pcd = gadget_wrapper->pcd; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%0x,%d)\n", __func__, buf, dma, bytes); ++ ++ dma_free_coherent(NULL, bytes, buf, dma); ++} ++#endif ++ ++/** ++ * This function is used to submit an I/O Request to an EP. ++ * ++ * - When the request completes the request's completion callback ++ * is called to return the request to the driver. ++ * - An EP, except control EPs, may have multiple requests ++ * pending. ++ * - Once submitted the request cannot be examined or modified. ++ * - Each request is turned into one or more packets. ++ * - A BULK EP can queue any amount of data; the transfer is ++ * packetized. ++ * - Zero length Packets are specified with the request 'zero' ++ * flag. ++ */ ++static int ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req, ++ gfp_t gfp_flags) ++{ ++ dwc_otg_pcd_t *pcd; ++ struct dwc_otg_pcd_ep *ep; ++ int retval, is_isoc_ep; ++ dma_addr_t dma_addr = 0; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p,%d)\n", ++ __func__, usb_ep, usb_req, gfp_flags); ++ ++ if (!usb_req || !usb_req->complete || !usb_req->buf) { ++ DWC_WARN("bad params\n"); ++ return -EINVAL; ++ } ++ ++ if (!usb_ep) { ++ DWC_WARN("bad ep\n"); ++ return -EINVAL; ++ } ++ ++ pcd = gadget_wrapper->pcd; ++ if (!gadget_wrapper->driver || ++ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", ++ gadget_wrapper->gadget.speed); ++ DWC_WARN("bogus device state\n"); ++ return -ESHUTDOWN; ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "%s queue req %p, len %d buf %p\n", ++ usb_ep->name, usb_req, usb_req->length, usb_req->buf); ++ ++ usb_req->status = -EINPROGRESS; ++ usb_req->actual = 0; ++ ++ ep = ep_from_handle(pcd, usb_ep); ++ if (ep == NULL) ++ return -EINVAL; ++ else ++ is_isoc_ep = (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) ? 1 : 0; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ dma_addr = usb_req->dma; ++#else ++ if (GET_CORE_IF(pcd)->dma_enable) { ++ retval = usb_gadget_map_req(&gadget_wrapper->gadget, ++ usb_req, ep); ++ if (retval) { ++ return -EINVAL; ++ } ++ dma_addr =usb_req->dma; ++ } ++#endif ++ ++#ifdef DWC_UTE_PER_IO ++ if (is_isoc_ep == 1) { ++ retval = ++ dwc_otg_pcd_xiso_ep_queue(pcd, usb_ep, usb_req->buf, ++ dma_addr, usb_req->length, ++ usb_req->zero, usb_req, ++ gfp_flags == GFP_ATOMIC ? 1 : 0, ++ &usb_req->ext_req); ++ if (retval) ++ return -EINVAL; ++ ++ return 0; ++ } ++#endif ++ retval = dwc_otg_pcd_ep_queue(pcd, usb_ep, usb_req->buf, dma_addr, ++ usb_req->length, usb_req->zero, usb_req, ++ gfp_flags == GFP_ATOMIC ? 1 : 0); ++ if (retval) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * This function cancels an I/O request from an EP. ++ */ ++static int ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) ++{ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, usb_req); ++ ++ if (!usb_ep || !usb_req) { ++ DWC_WARN("bad argument\n"); ++ return -EINVAL; ++ } ++ if (!gadget_wrapper->driver || ++ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_WARN("bogus device state\n"); ++ return -ESHUTDOWN; ++ } ++ if (dwc_otg_pcd_ep_dequeue(gadget_wrapper->pcd, usb_ep, usb_req)) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * usb_ep_set_halt stalls an endpoint. ++ * ++ * usb_ep_clear_halt clears an endpoint halt and resets its data ++ * toggle. ++ * ++ * Both of these functions are implemented with the same underlying ++ * function. The behavior depends on the value argument. ++ * ++ * @param[in] usb_ep the Endpoint to halt or clear halt. ++ * @param[in] value ++ * - 0 means clear_halt. ++ * - 1 means set_halt, ++ * - 2 means clear stall lock flag. ++ * - 3 means set stall lock flag. ++ */ ++static int ep_halt(struct usb_ep *usb_ep, int value) ++{ ++ int retval = 0; ++ ++ DWC_DEBUGPL(DBG_PCD, "HALT %s %d\n", usb_ep->name, value); ++ ++ if (!usb_ep) { ++ DWC_WARN("bad ep\n"); ++ return -EINVAL; ++ } ++ ++ retval = dwc_otg_pcd_ep_halt(gadget_wrapper->pcd, usb_ep, value); ++ if (retval == -DWC_E_AGAIN) { ++ return -EAGAIN; ++ } else if (retval) { ++ retval = -EINVAL; ++ } ++ ++ return retval; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) ++static int ep_wedge(struct usb_ep *usb_ep) ++{ ++ DWC_DEBUGPL(DBG_PCD, "WEDGE %s\n", usb_ep->name); ++ ++ return ep_halt(usb_ep, 3); ++} ++#endif ++ ++#ifdef DWC_EN_ISOC ++/** ++ * This function is used to submit an ISOC Transfer Request to an EP. ++ * ++ * - Every time a sync period completes the request's completion callback ++ * is called to provide data to the gadget driver. ++ * - Once submitted the request cannot be modified. ++ * - Each request is turned into periodic data packets untill ISO ++ * Transfer is stopped.. ++ */ ++static int iso_ep_start(struct usb_ep *usb_ep, struct usb_iso_request *req, ++ gfp_t gfp_flags) ++{ ++ int retval = 0; ++ ++ if (!req || !req->process_buffer || !req->buf0 || !req->buf1) { ++ DWC_WARN("bad params\n"); ++ return -EINVAL; ++ } ++ ++ if (!usb_ep) { ++ DWC_PRINTF("bad params\n"); ++ return -EINVAL; ++ } ++ ++ req->status = -EINPROGRESS; ++ ++ retval = ++ dwc_otg_pcd_iso_ep_start(gadget_wrapper->pcd, usb_ep, req->buf0, ++ req->buf1, req->dma0, req->dma1, ++ req->sync_frame, req->data_pattern_frame, ++ req->data_per_frame, ++ req-> ++ flags & USB_REQ_ISO_ASAP ? -1 : ++ req->start_frame, req->buf_proc_intrvl, ++ req, gfp_flags == GFP_ATOMIC ? 1 : 0); ++ ++ if (retval) { ++ return -EINVAL; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function stops ISO EP Periodic Data Transfer. ++ */ ++static int iso_ep_stop(struct usb_ep *usb_ep, struct usb_iso_request *req) ++{ ++ int retval = 0; ++ if (!usb_ep) { ++ DWC_WARN("bad ep\n"); ++ } ++ ++ if (!gadget_wrapper->driver || ++ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", ++ gadget_wrapper->gadget.speed); ++ DWC_WARN("bogus device state\n"); ++ } ++ ++ dwc_otg_pcd_iso_ep_stop(gadget_wrapper->pcd, usb_ep, req); ++ if (retval) { ++ retval = -EINVAL; ++ } ++ ++ return retval; ++} ++ ++static struct usb_iso_request *alloc_iso_request(struct usb_ep *ep, ++ int packets, gfp_t gfp_flags) ++{ ++ struct usb_iso_request *pReq = NULL; ++ uint32_t req_size; ++ ++ req_size = sizeof(struct usb_iso_request); ++ req_size += ++ (2 * packets * (sizeof(struct usb_gadget_iso_packet_descriptor))); ++ ++ pReq = kmalloc(req_size, gfp_flags); ++ if (!pReq) { ++ DWC_WARN("Can't allocate Iso Request\n"); ++ return 0; ++ } ++ pReq->iso_packet_desc0 = (void *)(pReq + 1); ++ ++ pReq->iso_packet_desc1 = pReq->iso_packet_desc0 + packets; ++ ++ return pReq; ++} ++ ++static void free_iso_request(struct usb_ep *ep, struct usb_iso_request *req) ++{ ++ kfree(req); ++} ++ ++static struct usb_isoc_ep_ops dwc_otg_pcd_ep_ops = { ++ .ep_ops = { ++ .enable = ep_enable, ++ .disable = ep_disable, ++ ++ .alloc_request = dwc_otg_pcd_alloc_request, ++ .free_request = dwc_otg_pcd_free_request, ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ .alloc_buffer = dwc_otg_pcd_alloc_buffer, ++ .free_buffer = dwc_otg_pcd_free_buffer, ++#endif ++ ++ .queue = ep_queue, ++ .dequeue = ep_dequeue, ++ ++ .set_halt = ep_halt, ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) ++ .set_wedge = ep_wedge, ++ #endif ++ .fifo_status = 0, ++ .fifo_flush = 0, ++ }, ++ ++ .iso_ep_start = iso_ep_start, ++ .iso_ep_stop = iso_ep_stop, ++ .alloc_iso_request = alloc_iso_request, ++ .free_iso_request = free_iso_request, ++}; ++ ++#else ++ ++static struct usb_ep_ops dwc_otg_pcd_ep_ops = { ++ .enable = ep_enable, ++ .disable = ep_disable, ++ ++ .alloc_request = dwc_otg_pcd_alloc_request, ++ .free_request = dwc_otg_pcd_free_request, ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) ++ .alloc_buffer = dwc_otg_pcd_alloc_buffer, ++ .free_buffer = dwc_otg_pcd_free_buffer, ++#endif ++ ++ .queue = ep_queue, ++ .dequeue = ep_dequeue, ++ ++ .set_halt = ep_halt, ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) ++ .set_wedge = ep_wedge, ++ #endif ++ ++ .fifo_status = 0, ++ .fifo_flush = 0, ++ ++}; ++ ++#endif /* _EN_ISOC_ */ ++/* Gadget Operations */ ++/** ++ * The following gadget operations will be implemented in the DWC_otg ++ * PCD. Functions in the API that are not described below are not ++ * implemented. ++ * ++ * The Gadget API provides wrapper functions for each of the function ++ * pointers defined in usb_gadget_ops. The Gadget Driver calls the ++ * wrapper function, which then calls the underlying PCD function. The ++ * following sections are named according to the wrapper functions ++ * (except for ioctl, which doesn't have a wrapper function). Within ++ * each section, the corresponding DWC_otg PCD function name is ++ * specified. ++ * ++ */ ++ ++/** ++ *Gets the USB Frame number of the last SOF. ++ */ ++static int get_frame_number(struct usb_gadget *gadget) ++{ ++ struct gadget_wrapper *d; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget); ++ ++ if (gadget == 0) { ++ return -ENODEV; ++ } ++ ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ return dwc_otg_pcd_get_frame_number(d->pcd); ++} ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++static int test_lpm_enabled(struct usb_gadget *gadget) ++{ ++ struct gadget_wrapper *d; ++ ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ ++ return dwc_otg_pcd_is_lpm_enabled(d->pcd); ++} ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) ++static int test_besl_enabled(struct usb_gadget *gadget) ++{ ++ struct gadget_wrapper *d; ++ ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ ++ return dwc_otg_pcd_is_besl_enabled(d->pcd); ++} ++static int get_param_baseline_besl(struct usb_gadget *gadget) ++{ ++ struct gadget_wrapper *d; ++ ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ ++ return dwc_otg_pcd_get_param_baseline_besl(d->pcd); ++} ++static int get_param_deep_besl(struct usb_gadget *gadget) ++{ ++ struct gadget_wrapper *d; ++ ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ ++ return dwc_otg_pcd_get_param_deep_besl(d->pcd); ++} ++#endif ++#endif ++ ++/** ++ * Initiates Session Request Protocol (SRP) to wakeup the host if no ++ * session is in progress. If a session is already in progress, but ++ * the device is suspended, remote wakeup signaling is started. ++ * ++ */ ++static int wakeup(struct usb_gadget *gadget) ++{ ++ struct gadget_wrapper *d; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget); ++ ++ if (gadget == 0) { ++ return -ENODEV; ++ } else { ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ } ++ dwc_otg_pcd_wakeup(d->pcd); ++ return 0; ++} ++ ++static int pullup(struct usb_gadget *gadget, int is_on) ++{ ++ struct gadget_wrapper *d; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget); ++ ++ if (gadget == 0) { ++ return -ENODEV; ++ } else { ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ } ++ ++ if (!is_on) ++ dwc_otg_pcd_pullup(d->pcd); ++ ++ return 0; ++} ++ ++static const struct usb_gadget_ops dwc_otg_pcd_ops = { ++ .get_frame = get_frame_number, ++ .wakeup = wakeup, ++ .pullup = pullup, ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ .lpm_support = test_lpm_enabled, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) ++ .besl_support = test_besl_enabled, ++ .get_baseline_besl = get_param_baseline_besl, ++ .get_deep_besl = get_param_deep_besl, ++#endif ++#endif ++ // current versions must always be self-powered ++}; ++ ++static int _setup(dwc_otg_pcd_t * pcd, uint8_t * bytes) ++{ ++ int retval = -DWC_E_NOT_SUPPORTED; ++ if (gadget_wrapper->driver && gadget_wrapper->driver->setup) { ++ retval = gadget_wrapper->driver->setup(&gadget_wrapper->gadget, ++ (struct usb_ctrlrequest ++ *)bytes); ++ } ++ ++ if (retval == -ENOTSUPP) { ++ retval = -DWC_E_NOT_SUPPORTED; ++ } else if (retval < 0) { ++ retval = -DWC_E_INVALID; ++ } ++ ++ return retval; ++} ++ ++#ifdef DWC_EN_ISOC ++static int _isoc_complete(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle, int proc_buf_num) ++{ ++ int i, packet_count; ++ struct usb_gadget_iso_packet_descriptor *iso_packet = 0; ++ struct usb_iso_request *iso_req = req_handle; ++ ++ if (proc_buf_num) { ++ iso_packet = iso_req->iso_packet_desc1; ++ } else { ++ iso_packet = iso_req->iso_packet_desc0; ++ } ++ packet_count = ++ dwc_otg_pcd_get_iso_packet_count(pcd, ep_handle, req_handle); ++ for (i = 0; i < packet_count; ++i) { ++ int status; ++ int actual; ++ int offset; ++ dwc_otg_pcd_get_iso_packet_params(pcd, ep_handle, req_handle, ++ i, &status, &actual, &offset); ++ switch (status) { ++ case -DWC_E_NO_DATA: ++ status = -ENODATA; ++ break; ++ default: ++ if (status) { ++ DWC_PRINTF("unknown status in isoc packet\n"); ++ } ++ ++ } ++ iso_packet[i].status = status; ++ iso_packet[i].offset = offset; ++ iso_packet[i].actual_length = actual; ++ } ++ ++ iso_req->status = 0; ++ iso_req->process_buffer(ep_handle, iso_req); ++ ++ return 0; ++} ++#endif /* DWC_EN_ISOC */ ++ ++#ifdef DWC_UTE_PER_IO ++/** ++ * Copy the contents of the extended request to the Linux usb_request's ++ * extended part and call the gadget's completion. ++ * ++ * @param pcd Pointer to the pcd structure ++ * @param ep_handle Void pointer to the usb_ep structure ++ * @param req_handle Void pointer to the usb_request structure ++ * @param status Request status returned from the portable logic ++ * @param ereq_port Void pointer to the extended request structure ++ * created in the the portable part that contains the ++ * results of the processed iso packets. ++ */ ++static int _xisoc_complete(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle, int32_t status, void *ereq_port) ++{ ++ struct dwc_ute_iso_req_ext *ereqorg = NULL; ++ struct dwc_iso_xreq_port *ereqport = NULL; ++ struct dwc_ute_iso_packet_descriptor *desc_org = NULL; ++ int i; ++ struct usb_request *req; ++ //struct dwc_ute_iso_packet_descriptor * ++ //int status = 0; ++ ++ req = (struct usb_request *)req_handle; ++ ereqorg = &req->ext_req; ++ ereqport = (struct dwc_iso_xreq_port *)ereq_port; ++ desc_org = ereqorg->per_io_frame_descs; ++ ++ if (req && req->complete) { ++ /* Copy the request data from the portable logic to our request */ ++ for (i = 0; i < ereqport->pio_pkt_count; i++) { ++ desc_org[i].actual_length = ++ ereqport->per_io_frame_descs[i].actual_length; ++ desc_org[i].status = ++ ereqport->per_io_frame_descs[i].status; ++ } ++ ++ switch (status) { ++ case -DWC_E_SHUTDOWN: ++ req->status = -ESHUTDOWN; ++ break; ++ case -DWC_E_RESTART: ++ req->status = -ECONNRESET; ++ break; ++ case -DWC_E_INVALID: ++ req->status = -EINVAL; ++ break; ++ case -DWC_E_TIMEOUT: ++ req->status = -ETIMEDOUT; ++ break; ++ default: ++ req->status = status; ++ } ++ ++ /* And call the gadget's completion */ ++ req->complete(ep_handle, req); ++ } ++ ++ return 0; ++} ++#endif /* DWC_UTE_PER_IO */ ++static int _complete(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle, int32_t status, uint32_t actual) ++{ ++ struct usb_request *req = (struct usb_request *)req_handle; ++ struct dwc_otg_pcd_ep *ep = NULL; ++ ++ ep = ep_from_handle(pcd, ep_handle); ++ if (!ep) ++ return -EINVAL; ++ ++ if (GET_CORE_IF(pcd)->dma_enable) { ++ if (req->dma) ++ usb_gadget_unmap_req(&gadget_wrapper->gadget, ++ req, ep); ++ req->dma = (dma_addr_t)0; ++ } ++ ++ if (req && req->complete) { ++ switch (status) { ++ case -DWC_E_SHUTDOWN: ++ req->status = -ESHUTDOWN; ++ break; ++ case -DWC_E_RESTART: ++ req->status = -ECONNRESET; ++ break; ++ case -DWC_E_INVALID: ++ req->status = -EINVAL; ++ break; ++ case -DWC_E_TIMEOUT: ++ req->status = -ETIMEDOUT; ++ break; ++ default: ++ req->status = status; ++ ++ } ++ ++ req->actual = actual; ++ DWC_SPINUNLOCK(pcd->lock); ++ req->complete(ep_handle, req); ++ DWC_SPINLOCK(pcd->lock); ++ } ++#ifdef PCI_INTERFACE ++ dev = gadget_wrapper->pcd->otg_dev->os_dep.pcidev; ++ ep = ep_from_handle(pcd, ep_handle); ++ if (GET_CORE_IF(pcd)->dma_enable) { ++ if (req->length != 0) ++ pci_unmap_single(dev, req->dma, req->length, ++ ep->dwc_ep.is_in ? PCI_DMA_TODEVICE : ++ PCI_DMA_FROMDEVICE); ++ } ++#endif ++ ++ return 0; ++} ++ ++static int _connect(dwc_otg_pcd_t * pcd, int speed) ++{ ++ gadget_wrapper->gadget.speed = speed; ++ return 0; ++} ++ ++static int _disconnect(dwc_otg_pcd_t * pcd) ++{ ++ if (gadget_wrapper->driver && gadget_wrapper->driver->disconnect) { ++ gadget_wrapper->driver->disconnect(&gadget_wrapper->gadget); ++ } ++ return 0; ++} ++ ++static int _resume(dwc_otg_pcd_t * pcd) ++{ ++ if (gadget_wrapper->driver && gadget_wrapper->driver->resume) { ++ gadget_wrapper->driver->resume(&gadget_wrapper->gadget); ++ } ++ ++ return 0; ++} ++ ++static int _suspend(dwc_otg_pcd_t * pcd) ++{ ++ if (gadget_wrapper->driver && gadget_wrapper->driver->suspend) { ++ gadget_wrapper->driver->suspend(&gadget_wrapper->gadget); ++ } ++ return 0; ++} ++ ++/** ++ * This function updates the otg values in the gadget structure. ++ */ ++static int _hnp_changed(dwc_otg_pcd_t * pcd) ++{ ++ ++ if (!gadget_wrapper->gadget.is_otg) ++ return 0; ++ ++ gadget_wrapper->gadget.b_hnp_enable = get_b_hnp_enable(pcd); ++ gadget_wrapper->gadget.a_hnp_support = get_a_hnp_support(pcd); ++ gadget_wrapper->gadget.a_alt_hnp_support = get_a_alt_hnp_support(pcd); ++ return 0; ++} ++ ++static int _reset(dwc_otg_pcd_t * pcd) ++{ ++ return 0; ++} ++ ++#ifdef DWC_UTE_CFI ++static int _cfi_setup(dwc_otg_pcd_t * pcd, void *cfi_req) ++{ ++ int retval = -DWC_E_INVALID; ++ if (gadget_wrapper->driver->cfi_feature_setup) { ++ retval = ++ gadget_wrapper->driver-> ++ cfi_feature_setup(&gadget_wrapper->gadget, ++ (struct cfi_usb_ctrlrequest *)cfi_req); ++ } ++ ++ return retval; ++} ++#endif ++ ++static const struct dwc_otg_pcd_function_ops fops = { ++ .complete = _complete, ++#ifdef DWC_EN_ISOC ++ .isoc_complete = _isoc_complete, ++#endif ++ .setup = _setup, ++ .disconnect = _disconnect, ++ .connect = _connect, ++ .resume = _resume, ++ .suspend = _suspend, ++ .hnp_changed = _hnp_changed, ++ .reset = _reset, ++#ifdef DWC_UTE_CFI ++ .cfi_setup = _cfi_setup, ++#endif ++#ifdef DWC_UTE_PER_IO ++ .xisoc_complete = _xisoc_complete, ++#endif ++}; ++ ++/** ++ * This function is the top level PCD interrupt handler. ++ */ ++static irqreturn_t dwc_otg_pcd_irq(int irq, void *dev) ++{ ++ dwc_otg_pcd_t *pcd = dev; ++ int32_t retval = IRQ_NONE; ++ ++ retval = dwc_otg_pcd_handle_intr(pcd); ++ if (retval != 0) { ++ S3C2410X_CLEAR_EINTPEND(); ++ } ++ return IRQ_RETVAL(retval); ++} ++ ++/** ++ * This function initialized the usb_ep structures to there default ++ * state. ++ * ++ * @param d Pointer on gadget_wrapper. ++ */ ++void gadget_add_eps(struct gadget_wrapper *d) ++{ ++ static const char *names[] = { ++ ++ "ep0", ++ "ep1in", ++ "ep2in", ++ "ep3in", ++ "ep4in", ++ "ep5in", ++ "ep6in", ++ "ep7in", ++ "ep8in", ++ "ep9in", ++ "ep10in", ++ "ep11in", ++ "ep12in", ++ "ep13in", ++ "ep14in", ++ "ep15in", ++ "ep1out", ++ "ep2out", ++ "ep3out", ++ "ep4out", ++ "ep5out", ++ "ep6out", ++ "ep7out", ++ "ep8out", ++ "ep9out", ++ "ep10out", ++ "ep11out", ++ "ep12out", ++ "ep13out", ++ "ep14out", ++ "ep15out" ++ }; ++ ++ int i; ++ struct usb_ep *ep; ++ int8_t dev_endpoints; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s\n", __func__); ++ ++ INIT_LIST_HEAD(&d->gadget.ep_list); ++ d->gadget.ep0 = &d->ep0; ++ d->gadget.speed = USB_SPEED_UNKNOWN; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) ++ d->gadget.max_speed = USB_SPEED_HIGH; ++#endif ++ ++ INIT_LIST_HEAD(&d->gadget.ep0->ep_list); ++ ++ /** ++ * Initialize the EP0 structure. ++ */ ++ ep = &d->ep0; ++ ++ /* Init the usb_ep structure. */ ++ ep->name = names[0]; ++ ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops; ++ ++ /** ++ * @todo NGS: What should the max packet size be set to ++ * here? Before EP type is set? ++ */ ++ ep->maxpacket = MAX_PACKET_SIZE; ++ dwc_otg_pcd_ep_enable(d->pcd, NULL, ep); ++ ++ list_add_tail(&ep->ep_list, &d->gadget.ep_list); ++ ++ /** ++ * Initialize the EP structures. ++ */ ++ dev_endpoints = d->pcd->core_if->dev_if->num_in_eps; ++ ++ for (i = 0; i < dev_endpoints; i++) { ++ ep = &d->in_ep[i]; ++ ++ /* Init the usb_ep structure. */ ++ ep->name = names[d->pcd->in_ep[i].dwc_ep.num]; ++ ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops; ++ ++ /** ++ * @todo NGS: What should the max packet size be set to ++ * here? Before EP type is set? ++ */ ++ ep->maxpacket = MAX_PACKET_SIZE; ++ ep->maxpacket_limit = MAX_PACKET_SIZE; ++ list_add_tail(&ep->ep_list, &d->gadget.ep_list); ++ } ++ ++ dev_endpoints = d->pcd->core_if->dev_if->num_out_eps; ++ ++ for (i = 0; i < dev_endpoints; i++) { ++ ep = &d->out_ep[i]; ++ ++ /* Init the usb_ep structure. */ ++ ep->name = names[15 + d->pcd->out_ep[i].dwc_ep.num]; ++ ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops; ++ ++ /** ++ * @todo NGS: What should the max packet size be set to ++ * here? Before EP type is set? ++ */ ++ ep->maxpacket = MAX_PACKET_SIZE; ++ ep->maxpacket_limit = MAX_PACKET_SIZE; ++ ++ list_add_tail(&ep->ep_list, &d->gadget.ep_list); ++ } ++ ++ /* remove ep0 from the list. There is a ep0 pointer. */ ++ list_del_init(&d->ep0.ep_list); ++ ++ d->ep0.maxpacket = MAX_EP0_SIZE; ++} ++ ++/** ++ * This function releases the Gadget device. ++ * required by device_unregister(). ++ * ++ * @todo Should this do something? Should it free the PCD? ++ */ ++static void dwc_otg_pcd_gadget_release(struct device *dev) ++{ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, dev); ++} ++ ++static struct gadget_wrapper *alloc_wrapper( ++ struct platform_device *_dev ++ ) ++{ ++ static char pcd_name[] = "dwc_otg_pcd"; ++ ++ dwc_otg_device_t *otg_dev =platform_get_drvdata(_dev); ++ ++ ++ struct gadget_wrapper *d; ++ int retval; ++ ++ d = DWC_ALLOC(sizeof(*d)); ++ if (d == NULL) { ++ return NULL; ++ } ++ ++ memset(d, 0, sizeof(*d)); ++ ++ d->gadget.name = pcd_name; ++ d->pcd = otg_dev->pcd; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) ++ strcpy(d->gadget.dev.bus_id, "gadget"); ++#else ++ dev_set_name(&d->gadget.dev, "%s", "gadget"); ++#endif ++ ++ d->gadget.dev.parent = &_dev->dev; ++ d->gadget.dev.release = dwc_otg_pcd_gadget_release; ++ d->gadget.ops = &dwc_otg_pcd_ops; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) ++ d->gadget.is_dualspeed = dwc_otg_pcd_is_dualspeed(otg_dev->pcd); ++#endif ++ d->gadget.is_otg = dwc_otg_pcd_is_otg(otg_dev->pcd); ++ ++ d->driver = 0; ++ /* Register the gadget device */ ++ retval = device_register(&d->gadget.dev); ++ if (retval != 0) { ++ DWC_ERROR("device_register failed\n"); ++ DWC_FREE(d); ++ return NULL; ++ } ++ ++ return d; ++} ++ ++static void free_wrapper(struct gadget_wrapper *d) ++{ ++ if (d->driver) { ++ /* should have been done already by driver model core */ ++ DWC_WARN("driver '%s' is still registered\n", ++ d->driver->driver.name); ++ usb_gadget_unregister_driver(d->driver); ++ } ++ ++ device_unregister(&d->gadget.dev); ++ DWC_FREE(d); ++} ++ ++/** ++ * This function initialized the PCD portion of the driver. ++ * ++ */ ++int pcd_init(struct platform_device *_dev, int irqnum ) ++{ ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); ++ ++ int retval = 0; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev); ++ ++ otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if); ++ ++ if (!otg_dev->pcd) { ++ DWC_ERROR("dwc_otg_pcd_init failed\n"); ++ return -ENOMEM; ++ } ++ ++ otg_dev->pcd->otg_dev = otg_dev; ++ gadget_wrapper = alloc_wrapper(_dev); ++ ++ /* ++ * Initialize EP structures ++ */ ++ gadget_add_eps(gadget_wrapper); ++ /* ++ * Setup interupt handler ++ */ ++ DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", irqnum); ++ retval = request_irq(irqnum, dwc_otg_pcd_irq, ++ IRQF_SHARED | IRQF_DISABLED, ++ gadget_wrapper->gadget.name, otg_dev->pcd); ++ if (retval != 0) { ++ DWC_ERROR("request of irq%d failed\n", irqnum); ++ free_wrapper(gadget_wrapper); ++ return -EBUSY; ++ } ++ ++ ++ dwc_otg_pcd_start(gadget_wrapper->pcd, &fops); ++ ++ return retval; ++} ++ ++/** ++ * Cleanup the PCD. ++ */ ++void pcd_remove( struct platform_device *_dev ) ++{ ++ ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); ++ ++ dwc_otg_pcd_t *pcd = otg_dev->pcd; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _dev); ++ ++ /* ++ * Free the IRQ ++ */ ++ free_irq(_dev->resource[1].start, pcd); ++ free_wrapper(gadget_wrapper); ++ dwc_otg_pcd_remove(otg_dev->pcd); ++ otg_dev->pcd = 0; ++} ++ ++/** ++ * This function registers a gadget driver with the PCD. ++ * ++ * When a driver is successfully registered, it will receive control ++ * requests including set_configuration(), which enables non-control ++ * requests. then usb traffic follows until a disconnect is reported. ++ * then a host may connect again, or the driver might get unbound. ++ * ++ * @param driver The driver being registered ++ * @param bind The bind function of gadget driver ++ */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++int usb_gadget_register_driver(struct usb_gadget_driver *driver) ++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) ++ int usb_gadget_probe_driver(struct usb_gadget_driver *driver) ++#else ++int usb_gadget_probe_driver(struct usb_gadget_driver *driver, ++ int (*bind)(struct usb_gadget *)) ++#endif ++{ ++ int retval; ++ ++ DWC_DEBUGPL(DBG_PCD, "registering gadget driver '%s'\n", ++ driver->driver.name); ++ ++ if (!driver || ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) ++ driver->speed == USB_SPEED_UNKNOWN || ++#else ++ driver->max_speed == USB_SPEED_UNKNOWN || ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) || LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) ++ !driver->bind || ++#else ++ !bind || ++#endif ++ !driver->unbind || !driver->disconnect || !driver->setup) { ++ DWC_DEBUGPL(DBG_PCDV, "EINVAL\n"); ++ return -EINVAL; ++ } ++ if (gadget_wrapper == 0) { ++ DWC_DEBUGPL(DBG_PCDV, "ENODEV\n"); ++ return -ENODEV; ++ } ++ if (gadget_wrapper->driver != 0) { ++ DWC_DEBUGPL(DBG_PCDV, "EBUSY (%p)\n", gadget_wrapper->driver); ++ return -EBUSY; ++ } ++ ++ /* hook up the driver */ ++ gadget_wrapper->driver = driver; ++ gadget_wrapper->gadget.dev.driver = &driver->driver; ++ ++ DWC_DEBUGPL(DBG_PCD, "bind to driver %s\n", driver->driver.name); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++ retval = driver->bind(&gadget_wrapper->gadget); ++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) ++ retval = driver->bind(&gadget_wrapper->gadget,gadget_wrapper->driver); ++#else ++ retval = bind(&gadget_wrapper->gadget); ++#endif ++ if (retval) { ++ DWC_ERROR("bind to driver %s --> error %d\n", ++ driver->driver.name, retval); ++ gadget_wrapper->driver = 0; ++ gadget_wrapper->gadget.dev.driver = 0; ++ return retval; ++ } ++ DWC_DEBUGPL(DBG_ANY, "registered gadget driver '%s'\n", ++ driver->driver.name); ++ return 0; ++} ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++EXPORT_SYMBOL(usb_gadget_register_driver); ++#else ++EXPORT_SYMBOL(usb_gadget_probe_driver); ++#endif ++ ++/** ++ * This function unregisters a gadget driver ++ * ++ * @param driver The driver being unregistered ++ */ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++ //DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, _driver); ++ ++ if (gadget_wrapper == 0) { ++ DWC_DEBUGPL(DBG_ANY, "%s Return(%d): s_pcd==0\n", __func__, ++ -ENODEV); ++ return -ENODEV; ++ } ++ if (driver == 0 || driver != gadget_wrapper->driver) { ++ DWC_DEBUGPL(DBG_ANY, "%s Return(%d): driver?\n", __func__, ++ -EINVAL); ++ return -EINVAL; ++ } ++ ++ driver->disconnect(&gadget_wrapper->gadget); ++ driver->unbind(&gadget_wrapper->gadget); ++ gadget_wrapper->driver = 0; ++ ++ DWC_DEBUGPL(DBG_ANY, "unregistered driver '%s'\n", driver->driver.name); ++ return 0; ++} ++ ++EXPORT_SYMBOL(usb_gadget_unregister_driver); ++ ++#endif /* DWC_HOST_ONLY */ +diff --git a/drivers/usb/gadget/udc/hiudc/dwc_otg_regs.h b/drivers/usb/gadget/udc/hiudc/dwc_otg_regs.h +new file mode 100644 +index 0000000..782976b +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/dwc_otg_regs.h +@@ -0,0 +1,2557 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_regs.h $ ++ * $Revision: #99 $ ++ * $Date: 2012/12/10 $ ++ * $Change: 2123206 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef __DWC_OTG_REGS_H__ ++#define __DWC_OTG_REGS_H__ ++ ++#include "dwc_otg_core_if.h" ++ ++/** ++ * @file ++ * ++ * This file contains the data structures for accessing the DWC_otg core registers. ++ * ++ * The application interfaces with the HS OTG core by reading from and ++ * writing to the Control and Status Register (CSR) space through the ++ * AHB Slave interface. These registers are 32 bits wide, and the ++ * addresses are 32-bit-block aligned. ++ * CSRs are classified as follows: ++ * - Core Global Registers ++ * - Device Mode Registers ++ * - Device Global Registers ++ * - Device Endpoint Specific Registers ++ * - Host Mode Registers ++ * - Host Global Registers ++ * - Host Port CSRs ++ * - Host Channel Specific Registers ++ * ++ * Only the Core Global registers can be accessed in both Device and ++ * Host modes. When the HS OTG core is operating in one mode, either ++ * Device or Host, the application must not access registers from the ++ * other mode. When the core switches from one mode to another, the ++ * registers in the new mode of operation must be reprogrammed as they ++ * would be after a power-on reset. ++ */ ++ ++/****************************************************************************/ ++/** DWC_otg Core registers . ++ * The dwc_otg_core_global_regs structure defines the size ++ * and relative field offsets for the Core Global registers. ++ */ ++typedef struct dwc_otg_core_global_regs { ++ /** OTG Control and Status Register. <i>Offset: 000h</i> */ ++ volatile uint32_t gotgctl; ++ /** OTG Interrupt Register. <i>Offset: 004h</i> */ ++ volatile uint32_t gotgint; ++ /**Core AHB Configuration Register. <i>Offset: 008h</i> */ ++ volatile uint32_t gahbcfg; ++ ++#define DWC_GLBINTRMASK 0x0001 ++#define DWC_DMAENABLE 0x0020 ++#define DWC_NPTXEMPTYLVL_EMPTY 0x0080 ++#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000 ++#define DWC_PTXEMPTYLVL_EMPTY 0x0100 ++#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000 ++ ++ /**Core USB Configuration Register. <i>Offset: 00Ch</i> */ ++ volatile uint32_t gusbcfg; ++ /**Core Reset Register. <i>Offset: 010h</i> */ ++ volatile uint32_t grstctl; ++ /**Core Interrupt Register. <i>Offset: 014h</i> */ ++ volatile uint32_t gintsts; ++ /**Core Interrupt Mask Register. <i>Offset: 018h</i> */ ++ volatile uint32_t gintmsk; ++ /**Receive Status Queue Read Register (Read Only). <i>Offset: 01Ch</i> */ ++ volatile uint32_t grxstsr; ++ /**Receive Status Queue Read & POP Register (Read Only). <i>Offset: 020h</i>*/ ++ volatile uint32_t grxstsp; ++ /**Receive FIFO Size Register. <i>Offset: 024h</i> */ ++ volatile uint32_t grxfsiz; ++ /**Non Periodic Transmit FIFO Size Register. <i>Offset: 028h</i> */ ++ volatile uint32_t gnptxfsiz; ++ /**Non Periodic Transmit FIFO/Queue Status Register (Read ++ * Only). <i>Offset: 02Ch</i> */ ++ volatile uint32_t gnptxsts; ++ /**I2C Access Register. <i>Offset: 030h</i> */ ++ volatile uint32_t gi2cctl; ++ /**PHY Vendor Control Register. <i>Offset: 034h</i> */ ++ volatile uint32_t gpvndctl; ++ /**General Purpose Input/Output Register. <i>Offset: 038h</i> */ ++ volatile uint32_t ggpio; ++ /**User ID Register. <i>Offset: 03Ch</i> */ ++ volatile uint32_t guid; ++ /**Synopsys ID Register (Read Only). <i>Offset: 040h</i> */ ++ volatile uint32_t gsnpsid; ++ /**User HW Config1 Register (Read Only). <i>Offset: 044h</i> */ ++ volatile uint32_t ghwcfg1; ++ /**User HW Config2 Register (Read Only). <i>Offset: 048h</i> */ ++ volatile uint32_t ghwcfg2; ++#define DWC_SLAVE_ONLY_ARCH 0 ++#define DWC_EXT_DMA_ARCH 1 ++#define DWC_INT_DMA_ARCH 2 ++ ++#define DWC_MODE_HNP_SRP_CAPABLE 0 ++#define DWC_MODE_SRP_ONLY_CAPABLE 1 ++#define DWC_MODE_NO_HNP_SRP_CAPABLE 2 ++#define DWC_MODE_SRP_CAPABLE_DEVICE 3 ++#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4 ++#define DWC_MODE_SRP_CAPABLE_HOST 5 ++#define DWC_MODE_NO_SRP_CAPABLE_HOST 6 ++ ++ /**User HW Config3 Register (Read Only). <i>Offset: 04Ch</i> */ ++ volatile uint32_t ghwcfg3; ++ /**User HW Config4 Register (Read Only). <i>Offset: 050h</i>*/ ++ volatile uint32_t ghwcfg4; ++ /** Core LPM Configuration register <i>Offset: 054h</i>*/ ++ volatile uint32_t glpmcfg; ++ /** Global PowerDn Register <i>Offset: 058h</i> */ ++ volatile uint32_t gpwrdn; ++ /** Global DFIFO SW Config Register <i>Offset: 05Ch</i> */ ++ volatile uint32_t gdfifocfg; ++ /** ADP Control Register <i>Offset: 060h</i> */ ++ volatile uint32_t adpctl; ++ /** Reserved <i>Offset: 064h-0FFh</i> */ ++ volatile uint32_t reserved39[39]; ++ /** Host Periodic Transmit FIFO Size Register. <i>Offset: 100h</i> */ ++ volatile uint32_t hptxfsiz; ++ /** Device Periodic Transmit FIFO#n Register if dedicated fifos are disabled, ++ otherwise Device Transmit FIFO#n Register. ++ * <i>Offset: 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15 (1<=n<=15).</i> */ ++ volatile uint32_t dtxfsiz[MAX_EPS_CHANNELS]; ++} dwc_otg_core_global_regs_t; ++ ++/** ++ * This union represents the bit fields of the Core OTG Control ++ * and Status Register (GOTGCTL). Set the bits using the bit ++ * fields then write the <i>d32</i> value to the register. ++ */ ++typedef union gotgctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned sesreqscs:1; ++ unsigned sesreq:1; ++ unsigned vbvalidoven:1; ++ unsigned vbvalidovval:1; ++ unsigned avalidoven:1; ++ unsigned avalidovval:1; ++ unsigned bvalidoven:1; ++ unsigned bvalidovval:1; ++ unsigned hstnegscs:1; ++ unsigned hnpreq:1; ++ unsigned hstsethnpen:1; ++ unsigned devhnpen:1; ++ unsigned reserved12_15:4; ++ unsigned conidsts:1; ++ unsigned dbnctime:1; ++ unsigned asesvld:1; ++ unsigned bsesvld:1; ++ unsigned otgver:1; ++ unsigned reserved1:1; ++ unsigned multvalidbc:5; ++ unsigned chirpen:1; ++ unsigned reserved28_31:4; ++ } b; ++} gotgctl_data_t; ++ ++/** ++ * This union represents the bit fields of the Core OTG Interrupt Register ++ * (GOTGINT). Set/clear the bits using the bit fields then write the <i>d32</i> ++ * value to the register. ++ */ ++typedef union gotgint_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Current Mode */ ++ unsigned reserved0_1:2; ++ ++ /** Session End Detected */ ++ unsigned sesenddet:1; ++ ++ unsigned reserved3_7:5; ++ ++ /** Session Request Success Status Change */ ++ unsigned sesreqsucstschng:1; ++ /** Host Negotiation Success Status Change */ ++ unsigned hstnegsucstschng:1; ++ ++ unsigned reserved10_16:7; ++ ++ /** Host Negotiation Detected */ ++ unsigned hstnegdet:1; ++ /** A-Device Timeout Change */ ++ unsigned adevtoutchng:1; ++ /** Debounce Done */ ++ unsigned debdone:1; ++ /** Multi-Valued input changed */ ++ unsigned mvic:1; ++ ++ unsigned reserved31_21:11; ++ ++ } b; ++} gotgint_data_t; ++ ++/** ++ * This union represents the bit fields of the Core AHB Configuration ++ * Register (GAHBCFG). Set/clear the bits using the bit fields then ++ * write the <i>d32</i> value to the register. ++ */ ++typedef union gahbcfg_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned glblintrmsk:1; ++#define DWC_GAHBCFG_GLBINT_ENABLE 1 ++ ++ unsigned hburstlen:4; ++#define DWC_GAHBCFG_INT_DMA_BURST_SINGLE 0 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR 1 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR4 3 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR8 5 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR16 7 ++ ++ unsigned dmaenable:1; ++#define DWC_GAHBCFG_DMAENABLE 1 ++ unsigned reserved:1; ++ unsigned nptxfemplvl_txfemplvl:1; ++ unsigned ptxfemplvl:1; ++#define DWC_GAHBCFG_TXFEMPTYLVL_EMPTY 1 ++#define DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0 ++ unsigned reserved9_20:12; ++ unsigned remmemsupp:1; ++ unsigned notialldmawrit:1; ++ unsigned ahbsingle:1; ++ unsigned reserved24_31:8; ++ } b; ++} gahbcfg_data_t; ++ ++/** ++ * This union represents the bit fields of the Core USB Configuration ++ * Register (GUSBCFG). Set the bits using the bit fields then write ++ * the <i>d32</i> value to the register. ++ */ ++typedef union gusbcfg_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned toutcal:3; ++ unsigned phyif:1; ++ unsigned ulpi_utmi_sel:1; ++ unsigned fsintf:1; ++ unsigned physel:1; ++ unsigned ddrsel:1; ++ unsigned srpcap:1; ++ unsigned hnpcap:1; ++ unsigned usbtrdtim:4; ++ unsigned reserved1:1; ++ unsigned phylpwrclksel:1; ++ unsigned otgutmifssel:1; ++ unsigned ulpi_fsls:1; ++ unsigned ulpi_auto_res:1; ++ unsigned ulpi_clk_sus_m:1; ++ unsigned ulpi_ext_vbus_drv:1; ++ unsigned ulpi_int_vbus_indicator:1; ++ unsigned term_sel_dl_pulse:1; ++ unsigned indicator_complement:1; ++ unsigned indicator_pass_through:1; ++ unsigned ulpi_int_prot_dis:1; ++ unsigned ic_usb_cap:1; ++ unsigned ic_traffic_pull_remove:1; ++ unsigned tx_end_delay:1; ++ unsigned force_host_mode:1; ++ unsigned force_dev_mode:1; ++ unsigned reserved31:1; ++ } b; ++} gusbcfg_data_t; ++ ++/** ++ * This union represents the bit fields of the Core Reset Register ++ * (GRSTCTL). Set/clear the bits using the bit fields then write the ++ * <i>d32</i> value to the register. ++ */ ++typedef union grstctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Core Soft Reset (CSftRst) (Device and Host) ++ * ++ * The application can flush the control logic in the ++ * entire core using this bit. This bit resets the ++ * pipelines in the AHB Clock domain as well as the ++ * PHY Clock domain. ++ * ++ * The state machines are reset to an IDLE state, the ++ * control bits in the CSRs are cleared, all the ++ * transmit FIFOs and the receive FIFO are flushed. ++ * ++ * The status mask bits that control the generation of ++ * the interrupt, are cleared, to clear the ++ * interrupt. The interrupt status bits are not ++ * cleared, so the application can get the status of ++ * any events that occurred in the core after it has ++ * set this bit. ++ * ++ * Any transactions on the AHB are terminated as soon ++ * as possible following the protocol. Any ++ * transactions on the USB are terminated immediately. ++ * ++ * The configuration settings in the CSRs are ++ * unchanged, so the software doesn't have to ++ * reprogram these registers (Device ++ * Configuration/Host Configuration/Core System ++ * Configuration/Core PHY Configuration). ++ * ++ * The application can write to this bit, any time it ++ * wants to reset the core. This is a self clearing ++ * bit and the core clears this bit after all the ++ * necessary logic is reset in the core, which may ++ * take several clocks, depending on the current state ++ * of the core. ++ */ ++ unsigned csftrst:1; ++ /** Hclk Soft Reset ++ * ++ * The application uses this bit to reset the control logic in ++ * the AHB clock domain. Only AHB clock domain pipelines are ++ * reset. ++ */ ++ unsigned hsftrst:1; ++ /** Host Frame Counter Reset (Host Only)<br> ++ * ++ * The application can reset the (micro)frame number ++ * counter inside the core, using this bit. When the ++ * (micro)frame counter is reset, the subsequent SOF ++ * sent out by the core, will have a (micro)frame ++ * number of 0. ++ */ ++ unsigned hstfrm:1; ++ /** In Token Sequence Learning Queue Flush ++ * (INTknQFlsh) (Device Only) ++ */ ++ unsigned intknqflsh:1; ++ /** RxFIFO Flush (RxFFlsh) (Device and Host) ++ * ++ * The application can flush the entire Receive FIFO ++ * using this bit. The application must first ++ * ensure that the core is not in the middle of a ++ * transaction. The application should write into ++ * this bit, only after making sure that neither the ++ * DMA engine is reading from the RxFIFO nor the MAC ++ * is writing the data in to the FIFO. The ++ * application should wait until the bit is cleared ++ * before performing any other operations. This bit ++ * will takes 8 clocks (slowest of PHY or AHB clock) ++ * to clear. ++ */ ++ unsigned rxfflsh:1; ++ /** TxFIFO Flush (TxFFlsh) (Device and Host). ++ * ++ * This bit is used to selectively flush a single or ++ * all transmit FIFOs. The application must first ++ * ensure that the core is not in the middle of a ++ * transaction. The application should write into ++ * this bit, only after making sure that neither the ++ * DMA engine is writing into the TxFIFO nor the MAC ++ * is reading the data out of the FIFO. The ++ * application should wait until the core clears this ++ * bit, before performing any operations. This bit ++ * will takes 8 clocks (slowest of PHY or AHB clock) ++ * to clear. ++ */ ++ unsigned txfflsh:1; ++ ++ /** TxFIFO Number (TxFNum) (Device and Host). ++ * ++ * This is the FIFO number which needs to be flushed, ++ * using the TxFIFO Flush bit. This field should not ++ * be changed until the TxFIFO Flush bit is cleared by ++ * the core. ++ * - 0x0 : Non Periodic TxFIFO Flush ++ * - 0x1 : Periodic TxFIFO #1 Flush in device mode ++ * or Periodic TxFIFO in host mode ++ * - 0x2 : Periodic TxFIFO #2 Flush in device mode. ++ * - ... ++ * - 0xF : Periodic TxFIFO #15 Flush in device mode ++ * - 0x10: Flush all the Transmit NonPeriodic and ++ * Transmit Periodic FIFOs in the core ++ */ ++ unsigned txfnum:5; ++ /** Reserved */ ++ unsigned reserved11_29:19; ++ /** DMA Request Signal. Indicated DMA request is in ++ * probress. Used for debug purpose. */ ++ unsigned dmareq:1; ++ /** AHB Master Idle. Indicates the AHB Master State ++ * Machine is in IDLE condition. */ ++ unsigned ahbidle:1; ++ } b; ++} grstctl_t; ++ ++/** ++ * This union represents the bit fields of the Core Interrupt Mask ++ * Register (GINTMSK). Set/clear the bits using the bit fields then ++ * write the <i>d32</i> value to the register. ++ */ ++typedef union gintmsk_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved0:1; ++ unsigned modemismatch:1; ++ unsigned otgintr:1; ++ unsigned sofintr:1; ++ unsigned rxstsqlvl:1; ++ unsigned nptxfempty:1; ++ unsigned ginnakeff:1; ++ unsigned goutnakeff:1; ++ unsigned ulpickint:1; ++ unsigned i2cintr:1; ++ unsigned erlysuspend:1; ++ unsigned usbsuspend:1; ++ unsigned usbreset:1; ++ unsigned enumdone:1; ++ unsigned isooutdrop:1; ++ unsigned eopframe:1; ++ unsigned restoredone:1; ++ unsigned epmismatch:1; ++ unsigned inepintr:1; ++ unsigned outepintr:1; ++ unsigned incomplisoin:1; ++ unsigned incomplisoout:1; ++ unsigned fetsusp:1; ++ unsigned resetdet:1; ++ unsigned portintr:1; ++ unsigned hcintr:1; ++ unsigned ptxfempty:1; ++ unsigned lpmtranrcvd:1; ++ unsigned conidstschng:1; ++ unsigned disconnect:1; ++ unsigned sessreqintr:1; ++ unsigned wkupintr:1; ++ } b; ++} gintmsk_data_t; ++/** ++ * This union represents the bit fields of the Core Interrupt Register ++ * (GINTSTS). Set/clear the bits using the bit fields then write the ++ * <i>d32</i> value to the register. ++ */ ++typedef union gintsts_data { ++ /** raw register data */ ++ uint32_t d32; ++#define DWC_SOF_INTR_MASK 0x0008 ++ /** register bits */ ++ struct { ++#define DWC_HOST_MODE 1 ++ unsigned curmode:1; ++ unsigned modemismatch:1; ++ unsigned otgintr:1; ++ unsigned sofintr:1; ++ unsigned rxstsqlvl:1; ++ unsigned nptxfempty:1; ++ unsigned ginnakeff:1; ++ unsigned goutnakeff:1; ++ unsigned ulpickint:1; ++ unsigned i2cintr:1; ++ unsigned erlysuspend:1; ++ unsigned usbsuspend:1; ++ unsigned usbreset:1; ++ unsigned enumdone:1; ++ unsigned isooutdrop:1; ++ unsigned eopframe:1; ++ unsigned restoredone:1; ++ unsigned epmismatch:1; ++ unsigned inepint:1; ++ unsigned outepintr:1; ++ unsigned incomplisoin:1; ++ unsigned incomplisoout:1; ++ unsigned fetsusp:1; ++ unsigned resetdet:1; ++ unsigned portintr:1; ++ unsigned hcintr:1; ++ unsigned ptxfempty:1; ++ unsigned lpmtranrcvd:1; ++ unsigned conidstschng:1; ++ unsigned disconnect:1; ++ unsigned sessreqintr:1; ++ unsigned wkupintr:1; ++ } b; ++} gintsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Device Receive Status Read and ++ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the <i>d32</i> ++ * element then read out the bits using the <i>b</i>it elements. ++ */ ++typedef union device_grxsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned epnum:4; ++ unsigned bcnt:11; ++ unsigned dpid:2; ++ ++#define DWC_STS_DATA_UPDT 0x2 // OUT Data Packet ++#define DWC_STS_XFER_COMP 0x3 // OUT Data Transfer Complete ++ ++#define DWC_DSTS_GOUT_NAK 0x1 // Global OUT NAK ++#define DWC_DSTS_SETUP_COMP 0x4 // Setup Phase Complete ++#define DWC_DSTS_SETUP_UPDT 0x6 // SETUP Packet ++ unsigned pktsts:4; ++ unsigned fn:4; ++ unsigned reserved25_31:7; ++ } b; ++} device_grxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Receive Status Read and ++ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the <i>d32</i> ++ * element then read out the bits using the <i>b</i>it elements. ++ */ ++typedef union host_grxsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned chnum:4; ++ unsigned bcnt:11; ++ unsigned dpid:2; ++ ++ unsigned pktsts:4; ++#define DWC_GRXSTS_PKTSTS_IN 0x2 ++#define DWC_GRXSTS_PKTSTS_IN_XFER_COMP 0x3 ++#define DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR 0x5 ++#define DWC_GRXSTS_PKTSTS_CH_HALTED 0x7 ++ ++ unsigned reserved21_31:11; ++ } b; ++} host_grxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the FIFO Size Registers (HPTXFSIZ, ++ * GNPTXFSIZ, DPTXFSIZn, DIEPTXFn). Read the register into the <i>d32</i> element ++ * then read out the bits using the <i>b</i>it elements. ++ */ ++typedef union fifosize_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned startaddr:16; ++ unsigned depth:16; ++ } b; ++} fifosize_data_t; ++ ++/** ++ * This union represents the bit fields in the Non-Periodic Transmit ++ * FIFO/Queue Status Register (GNPTXSTS). Read the register into the ++ * <i>d32</i> element then read out the bits using the <i>b</i>it ++ * elements. ++ */ ++typedef union gnptxsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned nptxfspcavail:16; ++ unsigned nptxqspcavail:8; ++ /** Top of the Non-Periodic Transmit Request Queue ++ * - bit 24 - Terminate (Last entry for the selected ++ * channel/EP) ++ * - bits 26:25 - Token Type ++ * - 2'b00 - IN/OUT ++ * - 2'b01 - Zero Length OUT ++ * - 2'b10 - PING/Complete Split ++ * - 2'b11 - Channel Halt ++ * - bits 30:27 - Channel/EP Number ++ */ ++ unsigned nptxqtop_terminate:1; ++ unsigned nptxqtop_token:2; ++ unsigned nptxqtop_chnep:4; ++ unsigned reserved:1; ++ } b; ++} gnptxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Transmit ++ * FIFO Status Register (DTXFSTS). Read the register into the ++ * <i>d32</i> element then read out the bits using the <i>b</i>it ++ * elements. ++ */ ++typedef union dtxfsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned txfspcavail:16; ++ unsigned reserved:16; ++ } b; ++} dtxfsts_data_t; ++ ++/** ++ * This union represents the bit fields in the I2C Control Register ++ * (I2CCTL). Read the register into the <i>d32</i> element then read out the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union gi2cctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned rwdata:8; ++ unsigned regaddr:8; ++ unsigned addr:7; ++ unsigned i2cen:1; ++ unsigned ack:1; ++ unsigned i2csuspctl:1; ++ unsigned i2cdevaddr:2; ++ unsigned i2cdatse0:1; ++ unsigned reserved:1; ++ unsigned rw:1; ++ unsigned bsydne:1; ++ } b; ++} gi2cctl_data_t; ++ ++/** ++ * This union represents the bit fields in the PHY Vendor Control Register ++ * (GPVNDCTL). Read the register into the <i>d32</i> element then read out the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union gpvndctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned regdata:8; ++ unsigned vctrl:8; ++ unsigned regaddr16_21:6; ++ unsigned regwr:1; ++ unsigned reserved23_24:2; ++ unsigned newregreq:1; ++ unsigned vstsbsy:1; ++ unsigned vstsdone:1; ++ unsigned reserved28_30:3; ++ unsigned disulpidrvr:1; ++ } b; ++} gpvndctl_data_t; ++ ++/** ++ * This union represents the bit fields in the General Purpose ++ * Input/Output Register (GGPIO). ++ * Read the register into the <i>d32</i> element then read out the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union ggpio_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned gpi:16; ++ unsigned gpo:16; ++ } b; ++} ggpio_data_t; ++ ++/** ++ * This union represents the bit fields in the User ID Register ++ * (GUID). Read the register into the <i>d32</i> element then read out the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union guid_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned rwdata:32; ++ } b; ++} guid_data_t; ++ ++/** ++ * This union represents the bit fields in the Synopsys ID Register ++ * (GSNPSID). Read the register into the <i>d32</i> element then read out the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union gsnpsid_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned rwdata:32; ++ } b; ++} gsnpsid_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config1 ++ * Register. Read the register into the <i>d32</i> element then read ++ * out the bits using the <i>b</i>it elements. ++ */ ++typedef union hwcfg1_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned ep_dir0:2; ++ unsigned ep_dir1:2; ++ unsigned ep_dir2:2; ++ unsigned ep_dir3:2; ++ unsigned ep_dir4:2; ++ unsigned ep_dir5:2; ++ unsigned ep_dir6:2; ++ unsigned ep_dir7:2; ++ unsigned ep_dir8:2; ++ unsigned ep_dir9:2; ++ unsigned ep_dir10:2; ++ unsigned ep_dir11:2; ++ unsigned ep_dir12:2; ++ unsigned ep_dir13:2; ++ unsigned ep_dir14:2; ++ unsigned ep_dir15:2; ++ } b; ++} hwcfg1_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config2 ++ * Register. Read the register into the <i>d32</i> element then read ++ * out the bits using the <i>b</i>it elements. ++ */ ++typedef union hwcfg2_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /* GHWCFG2 */ ++ unsigned op_mode:3; ++#define DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG 0 ++#define DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG 1 ++#define DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG 2 ++#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3 ++#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4 ++#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST 5 ++#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6 ++ ++ unsigned architecture:2; ++ unsigned point2point:1; ++ unsigned hs_phy_type:2; ++#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 ++#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1 ++#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2 ++#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 ++ ++ unsigned fs_phy_type:2; ++ unsigned num_dev_ep:4; ++ unsigned num_host_chan:4; ++ unsigned perio_ep_supported:1; ++ unsigned dynamic_fifo:1; ++ unsigned multi_proc_int:1; ++ unsigned reserved21:1; ++ unsigned nonperio_tx_q_depth:2; ++ unsigned host_perio_tx_q_depth:2; ++ unsigned dev_token_q_depth:5; ++ unsigned otg_enable_ic_usb:1; ++ } b; ++} hwcfg2_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config3 ++ * Register. Read the register into the <i>d32</i> element then read ++ * out the bits using the <i>b</i>it elements. ++ */ ++typedef union hwcfg3_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /* GHWCFG3 */ ++ unsigned xfer_size_cntr_width:4; ++ unsigned packet_size_cntr_width:3; ++ unsigned otg_func:1; ++ unsigned i2c:1; ++ unsigned vendor_ctrl_if:1; ++ unsigned optional_features:1; ++ unsigned synch_reset_type:1; ++ unsigned adp_supp:1; ++ unsigned otg_enable_hsic:1; ++ unsigned bc_support:1; ++ unsigned otg_lpm_en:1; ++ unsigned dfifo_depth:16; ++ } b; ++} hwcfg3_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config4 ++ * Register. Read the register into the <i>d32</i> element then read ++ * out the bits using the <i>b</i>it elements. ++ */ ++typedef union hwcfg4_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned num_dev_perio_in_ep:4; ++ unsigned power_optimiz:1; ++ unsigned min_ahb_freq:1; ++ unsigned hiber:1; ++ unsigned xhiber:1; ++ unsigned reserved:6; ++ unsigned utmi_phy_data_width:2; ++ unsigned num_dev_mode_ctrl_ep:4; ++ unsigned iddig_filt_en:1; ++ unsigned vbus_valid_filt_en:1; ++ unsigned a_valid_filt_en:1; ++ unsigned b_valid_filt_en:1; ++ unsigned session_end_filt_en:1; ++ unsigned ded_fifo_en:1; ++ unsigned num_in_eps:4; ++ unsigned desc_dma:1; ++ unsigned desc_dma_dyn:1; ++ } b; ++} hwcfg4_data_t; ++ ++/** ++ * This union represents the bit fields of the Core LPM Configuration ++ * Register (GLPMCFG). Set the bits using bit fields then write ++ * the <i>d32</i> value to the register. ++ */ ++typedef union glpmctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** LPM-Capable (LPMCap) (Device and Host) ++ * The application uses this bit to control ++ * the DWC_otg core LPM capabilities. ++ */ ++ unsigned lpm_cap_en:1; ++ /** LPM response programmed by application (AppL1Res) (Device) ++ * Handshake response to LPM token pre-programmed ++ * by device application software. ++ */ ++ unsigned appl_resp:1; ++ /** Host Initiated Resume Duration (HIRD) (Device and Host) ++ * In Host mode this field indicates the value of HIRD ++ * to be sent in an LPM transaction. ++ * In Device mode this field is updated with the ++ * Received LPM Token HIRD bmAttribute ++ * when an ACK/NYET/STALL response is sent ++ * to an LPM transaction. ++ */ ++ unsigned hird:4; ++ /** RemoteWakeEnable (bRemoteWake) (Device and Host) ++ * In Host mode this bit indicates the value of remote ++ * wake up to be sent in wIndex field of LPM transaction. ++ * In Device mode this field is updated with the ++ * Received LPM Token bRemoteWake bmAttribute ++ * when an ACK/NYET/STALL response is sent ++ * to an LPM transaction. ++ */ ++ unsigned rem_wkup_en:1; ++ /** Enable utmi_sleep_n (EnblSlpM) (Device and Host) ++ * The application uses this bit to control ++ * the utmi_sleep_n assertion to the PHY when in L1 state. ++ */ ++ unsigned en_utmi_sleep:1; ++ /** HIRD Threshold (HIRD_Thres) (Device and Host) ++ */ ++ unsigned hird_thres:5; ++ /** LPM Response (CoreL1Res) (Device and Host) ++ * In Host mode this bit contains handsake response to ++ * LPM transaction. ++ * In Device mode the response of the core to ++ * LPM transaction received is reflected in these two bits. ++ - 0x0 : ERROR (No handshake response) ++ - 0x1 : STALL ++ - 0x2 : NYET ++ - 0x3 : ACK ++ */ ++ unsigned lpm_resp:2; ++ /** Port Sleep Status (SlpSts) (Device and Host) ++ * This bit is set as long as a Sleep condition ++ * is present on the USB bus. ++ */ ++ unsigned prt_sleep_sts:1; ++ /** Sleep State Resume OK (L1ResumeOK) (Device and Host) ++ * Indicates that the application or host ++ * can start resume from Sleep state. ++ */ ++ unsigned sleep_state_resumeok:1; ++ /** LPM channel Index (LPM_Chnl_Indx) (Host) ++ * The channel number on which the LPM transaction ++ * has to be applied while sending ++ * an LPM transaction to the local device. ++ */ ++ unsigned lpm_chan_index:4; ++ /** LPM Retry Count (LPM_Retry_Cnt) (Host) ++ * Number host retries that would be performed ++ * if the device response was not valid response. ++ */ ++ unsigned retry_count:3; ++ /** Send LPM Transaction (SndLPM) (Host) ++ * When set by application software, ++ * an LPM transaction containing two tokens ++ * is sent. ++ */ ++ unsigned send_lpm:1; ++ /** LPM Retry status (LPM_RetryCnt_Sts) (Host) ++ * Number of LPM Host Retries still remaining ++ * to be transmitted for the current LPM sequence ++ */ ++ unsigned retry_count_sts:3; ++ /** Enable Best Effort Service Latency (BESL) (Device and Host) ++ * This bit enables the BESL features as defined in the LPM errata ++ */ ++ unsigned en_besl:1; ++ ++ unsigned reserved29:1; ++ /** In host mode once this bit is set, the host ++ * configures to drive the HSIC Idle state on the bus. ++ * It then waits for the device to initiate the Connect sequence. ++ * In device mode once this bit is set, the device waits for ++ * the HSIC Idle line state on the bus. Upon receving the Idle ++ * line state, it initiates the HSIC Connect sequence. ++ */ ++ unsigned hsic_connect:1; ++ /** This bit overrides and functionally inverts ++ * the if_select_hsic input port signal. ++ */ ++ unsigned inv_sel_hsic:1; ++ } b; ++} glpmcfg_data_t; ++ ++/** ++ * This union represents the bit fields of the Core ADP Timer, Control and ++ * Status Register (ADPTIMCTLSTS). Set the bits using bit fields then write ++ * the <i>d32</i> value to the register. ++ */ ++typedef union adpctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Probe Discharge (PRB_DSCHG) ++ * These bits set the times for TADP_DSCHG. ++ * These bits are defined as follows: ++ * 2'b00 - 4 msec ++ * 2'b01 - 8 msec ++ * 2'b10 - 16 msec ++ * 2'b11 - 32 msec ++ */ ++ unsigned prb_dschg:2; ++ /** Probe Delta (PRB_DELTA) ++ * These bits set the resolution for RTIM value. ++ * The bits are defined in units of 32 kHz clock cycles as follows: ++ * 2'b00 - 1 cycles ++ * 2'b01 - 2 cycles ++ * 2'b10 - 3 cycles ++ * 2'b11 - 4 cycles ++ * For example if this value is chosen to 2'b01, it means that RTIM ++ * increments for every 3(three) 32Khz clock cycles. ++ */ ++ unsigned prb_delta:2; ++ /** Probe Period (PRB_PER) ++ * These bits sets the TADP_PRD as shown in Figure 4 as follows: ++ * 2'b00 - 0.625 to 0.925 sec (typical 0.775 sec) ++ * 2'b01 - 1.25 to 1.85 sec (typical 1.55 sec) ++ * 2'b10 - 1.9 to 2.6 sec (typical 2.275 sec) ++ * 2'b11 - Reserved ++ */ ++ unsigned prb_per:2; ++ /** These bits capture the latest time it took for VBUS to ramp from ++ * VADP_SINK to VADP_PRB. ++ * 0x000 - 1 cycles ++ * 0x001 - 2 cycles ++ * 0x002 - 3 cycles ++ * etc ++ * 0x7FF - 2048 cycles ++ * A time of 1024 cycles at 32 kHz corresponds to a time of 32 msec. ++ */ ++ unsigned rtim:11; ++ /** Enable Probe (EnaPrb) ++ * When programmed to 1'b1, the core performs a probe operation. ++ * This bit is valid only if OTG_Ver = 1'b1. ++ */ ++ unsigned enaprb:1; ++ /** Enable Sense (EnaSns) ++ * When programmed to 1'b1, the core performs a Sense operation. ++ * This bit is valid only if OTG_Ver = 1'b1. ++ */ ++ unsigned enasns:1; ++ /** ADP Reset (ADPRes) ++ * When set, ADP controller is reset. ++ * This bit is valid only if OTG_Ver = 1'b1. ++ */ ++ unsigned adpres:1; ++ /** ADP Enable (ADPEn) ++ * When set, the core performs either ADP probing or sensing ++ * based on EnaPrb or EnaSns. ++ * This bit is valid only if OTG_Ver = 1'b1. ++ */ ++ unsigned adpen:1; ++ /** ADP Probe Interrupt (ADP_PRB_INT) ++ * When this bit is set, it means that the VBUS ++ * voltage is greater than VADP_PRB or VADP_PRB is reached. ++ * This bit is valid only if OTG_Ver = 1'b1. ++ */ ++ unsigned adp_prb_int:1; ++ /** ++ * ADP Sense Interrupt (ADP_SNS_INT) ++ * When this bit is set, it means that the VBUS voltage is greater than ++ * VADP_SNS value or VADP_SNS is reached. ++ * This bit is valid only if OTG_Ver = 1'b1. ++ */ ++ unsigned adp_sns_int:1; ++ /** ADP Tomeout Interrupt (ADP_TMOUT_INT) ++ * This bit is relevant only for an ADP probe. ++ * When this bit is set, it means that the ramp time has ++ * completed ie ADPCTL.RTIM has reached its terminal value ++ * of 0x7FF. This is a debug feature that allows software ++ * to read the ramp time after each cycle. ++ * This bit is valid only if OTG_Ver = 1'b1. ++ */ ++ unsigned adp_tmout_int:1; ++ /** ADP Probe Interrupt Mask (ADP_PRB_INT_MSK) ++ * When this bit is set, it unmasks the interrupt due to ADP_PRB_INT. ++ * This bit is valid only if OTG_Ver = 1'b1. ++ */ ++ unsigned adp_prb_int_msk:1; ++ /** ADP Sense Interrupt Mask (ADP_SNS_INT_MSK) ++ * When this bit is set, it unmasks the interrupt due to ADP_SNS_INT. ++ * This bit is valid only if OTG_Ver = 1'b1. ++ */ ++ unsigned adp_sns_int_msk:1; ++ /** ADP Timoeout Interrupt Mask (ADP_TMOUT_MSK) ++ * When this bit is set, it unmasks the interrupt due to ADP_TMOUT_INT. ++ * This bit is valid only if OTG_Ver = 1'b1. ++ */ ++ unsigned adp_tmout_int_msk:1; ++ /** Access Request ++ * 2'b00 - Read/Write Valid (updated by the core) ++ * 2'b01 - Read ++ * 2'b00 - Write ++ * 2'b00 - Reserved ++ */ ++ unsigned ar:2; ++ /** Reserved */ ++ unsigned reserved29_31:3; ++ } b; ++} adpctl_data_t; ++ ++//////////////////////////////////////////// ++// Device Registers ++/** ++ * Device Global Registers. <i>Offsets 800h-BFFh</i> ++ * ++ * The following structures define the size and relative field offsets ++ * for the Device Mode Registers. ++ * ++ * <i>These registers are visible only in Device mode and must not be ++ * accessed in Host mode, as the results are unknown.</i> ++ */ ++typedef struct dwc_otg_dev_global_regs { ++ /** Device Configuration Register. <i>Offset 800h</i> */ ++ volatile uint32_t dcfg; ++ /** Device Control Register. <i>Offset: 804h</i> */ ++ volatile uint32_t dctl; ++ /** Device Status Register (Read Only). <i>Offset: 808h</i> */ ++ volatile uint32_t dsts; ++ /** Reserved. <i>Offset: 80Ch</i> */ ++ uint32_t unused; ++ /** Device IN Endpoint Common Interrupt Mask ++ * Register. <i>Offset: 810h</i> */ ++ volatile uint32_t diepmsk; ++ /** Device OUT Endpoint Common Interrupt Mask ++ * Register. <i>Offset: 814h</i> */ ++ volatile uint32_t doepmsk; ++ /** Device All Endpoints Interrupt Register. <i>Offset: 818h</i> */ ++ volatile uint32_t daint; ++ /** Device All Endpoints Interrupt Mask Register. <i>Offset: ++ * 81Ch</i> */ ++ volatile uint32_t daintmsk; ++ /** Device IN Token Queue Read Register-1 (Read Only). ++ * <i>Offset: 820h</i> */ ++ volatile uint32_t dtknqr1; ++ /** Device IN Token Queue Read Register-2 (Read Only). ++ * <i>Offset: 824h</i> */ ++ volatile uint32_t dtknqr2; ++ /** Device VBUS discharge Register. <i>Offset: 828h</i> */ ++ volatile uint32_t dvbusdis; ++ /** Device VBUS Pulse Register. <i>Offset: 82Ch</i> */ ++ volatile uint32_t dvbuspulse; ++ /** Device IN Token Queue Read Register-3 (Read Only). / ++ * Device Thresholding control register (Read/Write) ++ * <i>Offset: 830h</i> */ ++ volatile uint32_t dtknqr3_dthrctl; ++ /** Device IN Token Queue Read Register-4 (Read Only). / ++ * Device IN EPs empty Inr. Mask Register (Read/Write) ++ * <i>Offset: 834h</i> */ ++ volatile uint32_t dtknqr4_fifoemptymsk; ++ /** Device Each Endpoint Interrupt Register (Read Only). / ++ * <i>Offset: 838h</i> */ ++ volatile uint32_t deachint; ++ /** Device Each Endpoint Interrupt mask Register (Read/Write). / ++ * <i>Offset: 83Ch</i> */ ++ volatile uint32_t deachintmsk; ++ /** Device Each In Endpoint Interrupt mask Register (Read/Write). / ++ * <i>Offset: 840h</i> */ ++ volatile uint32_t diepeachintmsk[MAX_EPS_CHANNELS]; ++ /** Device Each Out Endpoint Interrupt mask Register (Read/Write). / ++ * <i>Offset: 880h</i> */ ++ volatile uint32_t doepeachintmsk[MAX_EPS_CHANNELS]; ++} dwc_otg_device_global_regs_t; ++ ++/** ++ * This union represents the bit fields in the Device Configuration ++ * Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. Write the ++ * <i>d32</i> member to the dcfg register. ++ */ ++typedef union dcfg_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Device Speed */ ++ unsigned devspd:2; ++ /** Non Zero Length Status OUT Handshake */ ++ unsigned nzstsouthshk:1; ++#define DWC_DCFG_SEND_STALL 1 ++ ++ unsigned ena32khzs:1; ++ /** Device Addresses */ ++ unsigned devaddr:7; ++ /** Periodic Frame Interval */ ++ unsigned perfrint:2; ++#define DWC_DCFG_FRAME_INTERVAL_80 0 ++#define DWC_DCFG_FRAME_INTERVAL_85 1 ++#define DWC_DCFG_FRAME_INTERVAL_90 2 ++#define DWC_DCFG_FRAME_INTERVAL_95 3 ++ ++ /** Enable Device OUT NAK for bulk in DDMA mode */ ++ unsigned endevoutnak:1; ++ ++ unsigned reserved14_17:4; ++ /** In Endpoint Mis-match count */ ++ unsigned epmscnt:5; ++ /** Enable Descriptor DMA in Device mode */ ++ unsigned descdma:1; ++ unsigned perschintvl:2; ++ unsigned resvalid:6; ++ } b; ++} dcfg_data_t; ++ ++/** ++ * This union represents the bit fields in the Device Control ++ * Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union dctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Remote Wakeup */ ++ unsigned rmtwkupsig:1; ++ /** Soft Disconnect */ ++ unsigned sftdiscon:1; ++ /** Global Non-Periodic IN NAK Status */ ++ unsigned gnpinnaksts:1; ++ /** Global OUT NAK Status */ ++ unsigned goutnaksts:1; ++ /** Test Control */ ++ unsigned tstctl:3; ++ /** Set Global Non-Periodic IN NAK */ ++ unsigned sgnpinnak:1; ++ /** Clear Global Non-Periodic IN NAK */ ++ unsigned cgnpinnak:1; ++ /** Set Global OUT NAK */ ++ unsigned sgoutnak:1; ++ /** Clear Global OUT NAK */ ++ unsigned cgoutnak:1; ++ /** Power-On Programming Done */ ++ unsigned pwronprgdone:1; ++ /** Reserved */ ++ unsigned reserved:1; ++ /** Global Multi Count */ ++ unsigned gmc:2; ++ /** Ignore Frame Number for ISOC EPs */ ++ unsigned ifrmnum:1; ++ /** NAK on Babble */ ++ unsigned nakonbble:1; ++ /** Enable Continue on BNA */ ++ unsigned encontonbna:1; ++ /** Enable deep sleep besl reject feature*/ ++ unsigned besl_reject:1; ++ ++ unsigned reserved17_31:13; ++ } b; ++} dctl_data_t; ++ ++/** ++ * This union represents the bit fields in the Device Status ++ * Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union dsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Suspend Status */ ++ unsigned suspsts:1; ++ /** Enumerated Speed */ ++ unsigned enumspd:2; ++#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0 ++#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1 ++#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2 ++#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3 ++ /** Erratic Error */ ++ unsigned errticerr:1; ++ unsigned reserved4_7:4; ++ /** Frame or Microframe Number of the received SOF */ ++ unsigned soffn:14; ++ unsigned reserved22_31:10; ++ } b; ++} dsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Device IN EP Interrupt ++ * Register and the Device IN EP Common Mask Register. ++ * ++ * - Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union diepint_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Transfer complete mask */ ++ unsigned xfercompl:1; ++ /** Endpoint disable mask */ ++ unsigned epdisabled:1; ++ /** AHB Error mask */ ++ unsigned ahberr:1; ++ /** TimeOUT Handshake mask (non-ISOC EPs) */ ++ unsigned timeout:1; ++ /** IN Token received with TxF Empty mask */ ++ unsigned intktxfemp:1; ++ /** IN Token Received with EP mismatch mask */ ++ unsigned intknepmis:1; ++ /** IN Endpoint NAK Effective mask */ ++ unsigned inepnakeff:1; ++ /** Reserved */ ++ unsigned emptyintr:1; ++ ++ unsigned txfifoundrn:1; ++ ++ /** BNA Interrupt mask */ ++ unsigned bna:1; ++ ++ unsigned reserved10_12:3; ++ /** BNA Interrupt mask */ ++ unsigned nak:1; ++ ++ unsigned reserved14_31:18; ++ } b; ++} diepint_data_t; ++ ++/** ++ * This union represents the bit fields in the Device IN EP ++ * Common/Dedicated Interrupt Mask Register. ++ */ ++typedef union diepint_data diepmsk_data_t; ++ ++/** ++ * This union represents the bit fields in the Device OUT EP Interrupt ++ * Registerand Device OUT EP Common Interrupt Mask Register. ++ * ++ * - Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union doepint_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Transfer complete */ ++ unsigned xfercompl:1; ++ /** Endpoint disable */ ++ unsigned epdisabled:1; ++ /** AHB Error */ ++ unsigned ahberr:1; ++ /** Setup Phase Done (contorl EPs) */ ++ unsigned setup:1; ++ /** OUT Token Received when Endpoint Disabled */ ++ unsigned outtknepdis:1; ++ ++ unsigned stsphsercvd:1; ++ /** Back-to-Back SETUP Packets Received */ ++ unsigned back2backsetup:1; ++ ++ unsigned reserved7:1; ++ /** OUT packet Error */ ++ unsigned outpkterr:1; ++ /** BNA Interrupt */ ++ unsigned bna:1; ++ ++ unsigned reserved10:1; ++ /** Packet Drop Status */ ++ unsigned pktdrpsts:1; ++ /** Babble Interrupt */ ++ unsigned babble:1; ++ /** NAK Interrupt */ ++ unsigned nak:1; ++ /** NYET Interrupt */ ++ unsigned nyet:1; ++ /** Bit indicating setup packet received */ ++ unsigned sr:1; ++ ++ unsigned reserved16_31:16; ++ } b; ++} doepint_data_t; ++ ++/** ++ * This union represents the bit fields in the Device OUT EP ++ * Common/Dedicated Interrupt Mask Register. ++ */ ++typedef union doepint_data doepmsk_data_t; ++ ++/** ++ * This union represents the bit fields in the Device All EP Interrupt ++ * and Mask Registers. ++ * - Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union daint_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** IN Endpoint bits */ ++ unsigned in:16; ++ /** OUT Endpoint bits */ ++ unsigned out:16; ++ } ep; ++ struct { ++ /** IN Endpoint bits */ ++ unsigned inep0:1; ++ unsigned inep1:1; ++ unsigned inep2:1; ++ unsigned inep3:1; ++ unsigned inep4:1; ++ unsigned inep5:1; ++ unsigned inep6:1; ++ unsigned inep7:1; ++ unsigned inep8:1; ++ unsigned inep9:1; ++ unsigned inep10:1; ++ unsigned inep11:1; ++ unsigned inep12:1; ++ unsigned inep13:1; ++ unsigned inep14:1; ++ unsigned inep15:1; ++ /** OUT Endpoint bits */ ++ unsigned outep0:1; ++ unsigned outep1:1; ++ unsigned outep2:1; ++ unsigned outep3:1; ++ unsigned outep4:1; ++ unsigned outep5:1; ++ unsigned outep6:1; ++ unsigned outep7:1; ++ unsigned outep8:1; ++ unsigned outep9:1; ++ unsigned outep10:1; ++ unsigned outep11:1; ++ unsigned outep12:1; ++ unsigned outep13:1; ++ unsigned outep14:1; ++ unsigned outep15:1; ++ } b; ++} daint_data_t; ++ ++/** ++ * This union represents the bit fields in the Device IN Token Queue ++ * Read Registers. ++ * - Read the register into the <i>d32</i> member. ++ * - READ-ONLY Register ++ */ ++typedef union dtknq1_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** In Token Queue Write Pointer */ ++ unsigned intknwptr:5; ++ /** Reserved */ ++ unsigned reserved05_06:2; ++ /** write pointer has wrapped. */ ++ unsigned wrap_bit:1; ++ /** EP Numbers of IN Tokens 0 ... 4 */ ++ unsigned epnums0_5:24; ++ } b; ++} dtknq1_data_t; ++ ++/** ++ * This union represents Threshold control Register ++ * - Read and write the register into the <i>d32</i> member. ++ * - READ-WRITABLE Register ++ */ ++typedef union dthrctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** non ISO Tx Thr. Enable */ ++ unsigned non_iso_thr_en:1; ++ /** ISO Tx Thr. Enable */ ++ unsigned iso_thr_en:1; ++ /** Tx Thr. Length */ ++ unsigned tx_thr_len:9; ++ /** AHB Threshold ratio */ ++ unsigned ahb_thr_ratio:2; ++ /** Reserved */ ++ unsigned reserved13_15:3; ++ /** Rx Thr. Enable */ ++ unsigned rx_thr_en:1; ++ /** Rx Thr. Length */ ++ unsigned rx_thr_len:9; ++ unsigned reserved26:1; ++ /** Arbiter Parking Enable*/ ++ unsigned arbprken:1; ++ /** Reserved */ ++ unsigned reserved28_31:4; ++ } b; ++} dthrctl_data_t; ++ ++/** ++ * Device Logical IN Endpoint-Specific Registers. <i>Offsets ++ * 900h-AFCh</i> ++ * ++ * There will be one set of endpoint registers per logical endpoint ++ * implemented. ++ * ++ * <i>These registers are visible only in Device mode and must not be ++ * accessed in Host mode, as the results are unknown.</i> ++ */ ++typedef struct dwc_otg_dev_in_ep_regs { ++ /** Device IN Endpoint Control Register. <i>Offset:900h + ++ * (ep_num * 20h) + 00h</i> */ ++ volatile uint32_t diepctl; ++ /** Reserved. <i>Offset:900h + (ep_num * 20h) + 04h</i> */ ++ uint32_t reserved04; ++ /** Device IN Endpoint Interrupt Register. <i>Offset:900h + ++ * (ep_num * 20h) + 08h</i> */ ++ volatile uint32_t diepint; ++ /** Reserved. <i>Offset:900h + (ep_num * 20h) + 0Ch</i> */ ++ uint32_t reserved0C; ++ /** Device IN Endpoint Transfer Size ++ * Register. <i>Offset:900h + (ep_num * 20h) + 10h</i> */ ++ volatile uint32_t dieptsiz; ++ /** Device IN Endpoint DMA Address Register. <i>Offset:900h + ++ * (ep_num * 20h) + 14h</i> */ ++ volatile uint32_t diepdma; ++ /** Device IN Endpoint Transmit FIFO Status Register. <i>Offset:900h + ++ * (ep_num * 20h) + 18h</i> */ ++ volatile uint32_t dtxfsts; ++ /** Device IN Endpoint DMA Buffer Register. <i>Offset:900h + ++ * (ep_num * 20h) + 1Ch</i> */ ++ volatile uint32_t diepdmab; ++} dwc_otg_dev_in_ep_regs_t; ++ ++/** ++ * Device Logical OUT Endpoint-Specific Registers. <i>Offsets: ++ * B00h-CFCh</i> ++ * ++ * There will be one set of endpoint registers per logical endpoint ++ * implemented. ++ * ++ * <i>These registers are visible only in Device mode and must not be ++ * accessed in Host mode, as the results are unknown.</i> ++ */ ++typedef struct dwc_otg_dev_out_ep_regs { ++ /** Device OUT Endpoint Control Register. <i>Offset:B00h + ++ * (ep_num * 20h) + 00h</i> */ ++ volatile uint32_t doepctl; ++ /** Reserved. <i>Offset:B00h + (ep_num * 20h) + 04h</i> */ ++ uint32_t reserved04; ++ /** Device OUT Endpoint Interrupt Register. <i>Offset:B00h + ++ * (ep_num * 20h) + 08h</i> */ ++ volatile uint32_t doepint; ++ /** Reserved. <i>Offset:B00h + (ep_num * 20h) + 0Ch</i> */ ++ uint32_t reserved0C; ++ /** Device OUT Endpoint Transfer Size Register. <i>Offset: ++ * B00h + (ep_num * 20h) + 10h</i> */ ++ volatile uint32_t doeptsiz; ++ /** Device OUT Endpoint DMA Address Register. <i>Offset:B00h ++ * + (ep_num * 20h) + 14h</i> */ ++ volatile uint32_t doepdma; ++ /** Reserved. <i>Offset:B00h + * (ep_num * 20h) + 18h</i> */ ++ uint32_t unused; ++ /** Device OUT Endpoint DMA Buffer Register. <i>Offset:B00h ++ * + (ep_num * 20h) + 1Ch</i> */ ++ uint32_t doepdmab; ++} dwc_otg_dev_out_ep_regs_t; ++ ++/** ++ * This union represents the bit fields in the Device EP Control ++ * Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union depctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Maximum Packet Size ++ * IN/OUT EPn ++ * IN/OUT EP0 - 2 bits ++ * 2'b00: 64 Bytes ++ * 2'b01: 32 ++ * 2'b10: 16 ++ * 2'b11: 8 */ ++ unsigned mps:11; ++#define DWC_DEP0CTL_MPS_64 0 ++#define DWC_DEP0CTL_MPS_32 1 ++#define DWC_DEP0CTL_MPS_16 2 ++#define DWC_DEP0CTL_MPS_8 3 ++ ++ /** Next Endpoint ++ * IN EPn/IN EP0 ++ * OUT EPn/OUT EP0 - reserved */ ++ unsigned nextep:4; ++ ++ /** USB Active Endpoint */ ++ unsigned usbactep:1; ++ ++ /** Endpoint DPID (INTR/Bulk IN and OUT endpoints) ++ * This field contains the PID of the packet going to ++ * be received or transmitted on this endpoint. The ++ * application should program the PID of the first ++ * packet going to be received or transmitted on this ++ * endpoint , after the endpoint is ++ * activated. Application use the SetD1PID and ++ * SetD0PID fields of this register to program either ++ * D0 or D1 PID. ++ * ++ * The encoding for this field is ++ * - 0: D0 ++ * - 1: D1 ++ */ ++ unsigned dpid:1; ++ ++ /** NAK Status */ ++ unsigned naksts:1; ++ ++ /** Endpoint Type ++ * 2'b00: Control ++ * 2'b01: Isochronous ++ * 2'b10: Bulk ++ * 2'b11: Interrupt */ ++ unsigned eptype:2; ++ ++ /** Snoop Mode ++ * OUT EPn/OUT EP0 ++ * IN EPn/IN EP0 - reserved */ ++ unsigned snp:1; ++ ++ /** Stall Handshake */ ++ unsigned stall:1; ++ ++ /** Tx Fifo Number ++ * IN EPn/IN EP0 ++ * OUT EPn/OUT EP0 - reserved */ ++ unsigned txfnum:4; ++ ++ /** Clear NAK */ ++ unsigned cnak:1; ++ /** Set NAK */ ++ unsigned snak:1; ++ /** Set DATA0 PID (INTR/Bulk IN and OUT endpoints) ++ * Writing to this field sets the Endpoint DPID (DPID) ++ * field in this register to DATA0. Set Even ++ * (micro)frame (SetEvenFr) (ISO IN and OUT Endpoints) ++ * Writing to this field sets the Even/Odd ++ * (micro)frame (EO_FrNum) field to even (micro) ++ * frame. ++ */ ++ unsigned setd0pid:1; ++ /** Set DATA1 PID (INTR/Bulk IN and OUT endpoints) ++ * Writing to this field sets the Endpoint DPID (DPID) ++ * field in this register to DATA1 Set Odd ++ * (micro)frame (SetOddFr) (ISO IN and OUT Endpoints) ++ * Writing to this field sets the Even/Odd ++ * (micro)frame (EO_FrNum) field to odd (micro) frame. ++ */ ++ unsigned setd1pid:1; ++ ++ /** Endpoint Disable */ ++ unsigned epdis:1; ++ /** Endpoint Enable */ ++ unsigned epena:1; ++ } b; ++} depctl_data_t; ++ ++/** ++ * This union represents the bit fields in the Device EP Transfer ++ * Size Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union deptsiz_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Transfer size */ ++ unsigned xfersize:19; ++/** Max packet count for EP (pow(2,10)-1) */ ++#define MAX_PKT_CNT 1023 ++ /** Packet Count */ ++ unsigned pktcnt:10; ++ /** Multi Count - Periodic IN endpoints */ ++ unsigned mc:2; ++ unsigned reserved:1; ++ } b; ++} deptsiz_data_t; ++ ++/** ++ * This union represents the bit fields in the Device EP 0 Transfer ++ * Size Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union deptsiz0_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Transfer size */ ++ unsigned xfersize:7; ++ /** Reserved */ ++ unsigned reserved7_18:12; ++ /** Packet Count */ ++ unsigned pktcnt:2; ++ /** Reserved */ ++ unsigned reserved21_28:8; ++ /**Setup Packet Count (DOEPTSIZ0 Only) */ ++ unsigned supcnt:2; ++ unsigned reserved31; ++ } b; ++} deptsiz0_data_t; ++ ++///////////////////////////////////////////////// ++// DMA Descriptor Specific Structures ++// ++ ++/** Buffer status definitions */ ++ ++#define BS_HOST_READY 0x0 ++#define BS_DMA_BUSY 0x1 ++#define BS_DMA_DONE 0x2 ++#define BS_HOST_BUSY 0x3 ++ ++/** Receive/Transmit status definitions */ ++ ++#define RTS_SUCCESS 0x0 ++#define RTS_BUFFLUSH 0x1 ++#define RTS_RESERVED 0x2 ++#define RTS_BUFERR 0x3 ++ ++/** ++ * This union represents the bit fields in the DMA Descriptor ++ * status quadlet. Read the quadlet into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it, <i>b_iso_out</i> and ++ * <i>b_iso_in</i> elements. ++ */ ++typedef union dev_dma_desc_sts { ++ /** raw register data */ ++ uint32_t d32; ++ /** quadlet bits */ ++ struct { ++ /** Received number of bytes */ ++ unsigned bytes:16; ++ /** NAK bit - only for OUT EPs */ ++ unsigned nak:1; ++ unsigned reserved17_22:6; ++ /** Multiple Transfer - only for OUT EPs */ ++ unsigned mtrf:1; ++ /** Setup Packet received - only for OUT EPs */ ++ unsigned sr:1; ++ /** Interrupt On Complete */ ++ unsigned ioc:1; ++ /** Short Packet */ ++ unsigned sp:1; ++ /** Last */ ++ unsigned l:1; ++ /** Receive Status */ ++ unsigned sts:2; ++ /** Buffer Status */ ++ unsigned bs:2; ++ } b; ++ ++//#ifdef DWC_EN_ISOC ++ /** iso out quadlet bits */ ++ struct { ++ /** Received number of bytes */ ++ unsigned rxbytes:11; ++ ++ unsigned reserved11:1; ++ /** Frame Number */ ++ unsigned framenum:11; ++ /** Received ISO Data PID */ ++ unsigned pid:2; ++ /** Interrupt On Complete */ ++ unsigned ioc:1; ++ /** Short Packet */ ++ unsigned sp:1; ++ /** Last */ ++ unsigned l:1; ++ /** Receive Status */ ++ unsigned rxsts:2; ++ /** Buffer Status */ ++ unsigned bs:2; ++ } b_iso_out; ++ ++ /** iso in quadlet bits */ ++ struct { ++ /** Transmited number of bytes */ ++ unsigned txbytes:12; ++ /** Frame Number */ ++ unsigned framenum:11; ++ /** Transmited ISO Data PID */ ++ unsigned pid:2; ++ /** Interrupt On Complete */ ++ unsigned ioc:1; ++ /** Short Packet */ ++ unsigned sp:1; ++ /** Last */ ++ unsigned l:1; ++ /** Transmit Status */ ++ unsigned txsts:2; ++ /** Buffer Status */ ++ unsigned bs:2; ++ } b_iso_in; ++//#endif /* DWC_EN_ISOC */ ++} dev_dma_desc_sts_t; ++ ++/** ++ * DMA Descriptor structure ++ * ++ * DMA Descriptor structure contains two quadlets: ++ * Status quadlet and Data buffer pointer. ++ */ ++typedef struct dwc_otg_dev_dma_desc { ++ /** DMA Descriptor status quadlet */ ++ dev_dma_desc_sts_t status; ++ /** DMA Descriptor data buffer pointer */ ++ uint32_t buf; ++} dwc_otg_dev_dma_desc_t; ++ ++/** ++ * The dwc_otg_dev_if structure contains information needed to manage ++ * the DWC_otg controller acting in device mode. It represents the ++ * programming view of the device-specific aspects of the controller. ++ */ ++typedef struct dwc_otg_dev_if { ++ /** Pointer to device Global registers. ++ * Device Global Registers starting at offset 800h ++ */ ++ dwc_otg_device_global_regs_t *dev_global_regs; ++#define DWC_DEV_GLOBAL_REG_OFFSET 0x800 ++ ++ /** ++ * Device Logical IN Endpoint-Specific Registers 900h-AFCh ++ */ ++ dwc_otg_dev_in_ep_regs_t *in_ep_regs[MAX_EPS_CHANNELS]; ++#define DWC_DEV_IN_EP_REG_OFFSET 0x900 ++#define DWC_EP_REG_OFFSET 0x20 ++ ++ /** Device Logical OUT Endpoint-Specific Registers B00h-CFCh */ ++ dwc_otg_dev_out_ep_regs_t *out_ep_regs[MAX_EPS_CHANNELS]; ++#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00 ++ ++ /* Device configuration information */ ++ uint8_t speed; /**< Device Speed 0: Unknown, 1: LS, 2:FS, 3: HS */ ++ uint8_t num_in_eps; /**< Number # of Tx EP range: 0-15 exept ep0 */ ++ uint8_t num_out_eps; /**< Number # of Rx EP range: 0-15 exept ep 0*/ ++ ++ /** Size of periodic FIFOs (Bytes) */ ++ uint16_t perio_tx_fifo_size[MAX_PERIO_FIFOS]; ++ ++ /** Size of Tx FIFOs (Bytes) */ ++ uint16_t tx_fifo_size[MAX_TX_FIFOS]; ++ ++ /** Thresholding enable flags and length varaiables **/ ++ uint16_t rx_thr_en; ++ uint16_t iso_tx_thr_en; ++ uint16_t non_iso_tx_thr_en; ++ ++ uint16_t rx_thr_length; ++ uint16_t tx_thr_length; ++ ++ /** ++ * Pointers to the DMA Descriptors for EP0 Control ++ * transfers (virtual and physical) ++ */ ++ ++ /** 2 descriptors for SETUP packets */ ++ dwc_dma_t dma_setup_desc_addr[2]; ++ dwc_otg_dev_dma_desc_t *setup_desc_addr[2]; ++ ++ /** Pointer to Descriptor with latest SETUP packet */ ++ dwc_otg_dev_dma_desc_t *psetup; ++ ++ /** Index of current SETUP handler descriptor */ ++ uint32_t setup_desc_index; ++ ++ /** Descriptor for Data In or Status In phases */ ++ dwc_dma_t dma_in_desc_addr; ++ dwc_otg_dev_dma_desc_t *in_desc_addr; ++ ++ /** Descriptor for Data Out or Status Out phases */ ++ dwc_dma_t dma_out_desc_addr; ++ dwc_otg_dev_dma_desc_t *out_desc_addr; ++ ++ /** Setup Packet Detected - if set clear NAK when queueing */ ++ uint32_t spd; ++ /** Isoc ep pointer on which incomplete happens */ ++ void *isoc_ep; ++ ++} dwc_otg_dev_if_t; ++ ++///////////////////////////////////////////////// ++// Host Mode Register Structures ++// ++/** ++ * The Host Global Registers structure defines the size and relative ++ * field offsets for the Host Mode Global Registers. Host Global ++ * Registers offsets 400h-7FFh. ++*/ ++typedef struct dwc_otg_host_global_regs { ++ /** Host Configuration Register. <i>Offset: 400h</i> */ ++ volatile uint32_t hcfg; ++ /** Host Frame Interval Register. <i>Offset: 404h</i> */ ++ volatile uint32_t hfir; ++ /** Host Frame Number / Frame Remaining Register. <i>Offset: 408h</i> */ ++ volatile uint32_t hfnum; ++ /** Reserved. <i>Offset: 40Ch</i> */ ++ uint32_t reserved40C; ++ /** Host Periodic Transmit FIFO/ Queue Status Register. <i>Offset: 410h</i> */ ++ volatile uint32_t hptxsts; ++ /** Host All Channels Interrupt Register. <i>Offset: 414h</i> */ ++ volatile uint32_t haint; ++ /** Host All Channels Interrupt Mask Register. <i>Offset: 418h</i> */ ++ volatile uint32_t haintmsk; ++ /** Host Frame List Base Address Register . <i>Offset: 41Ch</i> */ ++ volatile uint32_t hflbaddr; ++} dwc_otg_host_global_regs_t; ++ ++/** ++ * This union represents the bit fields in the Host Configuration Register. ++ * Read the register into the <i>d32</i> member then set/clear the bits using ++ * the <i>b</i>it elements. Write the <i>d32</i> member to the hcfg register. ++ */ ++typedef union hcfg_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** FS/LS Phy Clock Select */ ++ unsigned fslspclksel:2; ++#define DWC_HCFG_30_60_MHZ 0 ++#define DWC_HCFG_48_MHZ 1 ++#define DWC_HCFG_6_MHZ 2 ++ ++ /** FS/LS Only Support */ ++ unsigned fslssupp:1; ++ unsigned reserved3_6:4; ++ /** Enable 32-KHz Suspend Mode */ ++ unsigned ena32khzs:1; ++ /** Resume Validation Periiod */ ++ unsigned resvalid:8; ++ unsigned reserved16_22:7; ++ /** Enable Scatter/gather DMA in Host mode */ ++ unsigned descdma:1; ++ /** Frame List Entries */ ++ unsigned frlisten:2; ++ /** Enable Periodic Scheduling */ ++ unsigned perschedena:1; ++ unsigned reserved27_30:4; ++ unsigned modechtimen:1; ++ } b; ++} hcfg_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Frame Remaing/Number ++ * Register. ++ */ ++typedef union hfir_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ unsigned frint:16; ++ unsigned hfirrldctrl:1; ++ unsigned reserved:15; ++ } b; ++} hfir_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Frame Remaing/Number ++ * Register. ++ */ ++typedef union hfnum_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ unsigned frnum:16; ++#define DWC_HFNUM_MAX_FRNUM 0x3FFF ++ unsigned frrem:16; ++ } b; ++} hfnum_data_t; ++ ++typedef union hptxsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ unsigned ptxfspcavail:16; ++ unsigned ptxqspcavail:8; ++ /** Top of the Periodic Transmit Request Queue ++ * - bit 24 - Terminate (last entry for the selected channel) ++ * - bits 26:25 - Token Type ++ * - 2'b00 - Zero length ++ * - 2'b01 - Ping ++ * - 2'b10 - Disable ++ * - bits 30:27 - Channel Number ++ * - bit 31 - Odd/even microframe ++ */ ++ unsigned ptxqtop_terminate:1; ++ unsigned ptxqtop_token:2; ++ unsigned ptxqtop_chnum:4; ++ unsigned ptxqtop_odd:1; ++ } b; ++} hptxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Port Control and Status ++ * Register. Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the ++ * hprt0 register. ++ */ ++typedef union hprt0_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned prtconnsts:1; ++ unsigned prtconndet:1; ++ unsigned prtena:1; ++ unsigned prtenchng:1; ++ unsigned prtovrcurract:1; ++ unsigned prtovrcurrchng:1; ++ unsigned prtres:1; ++ unsigned prtsusp:1; ++ unsigned prtrst:1; ++ unsigned reserved9:1; ++ unsigned prtlnsts:2; ++ unsigned prtpwr:1; ++ unsigned prttstctl:4; ++ unsigned prtspd:2; ++#define DWC_HPRT0_PRTSPD_HIGH_SPEED 0 ++#define DWC_HPRT0_PRTSPD_FULL_SPEED 1 ++#define DWC_HPRT0_PRTSPD_LOW_SPEED 2 ++ unsigned reserved19_31:13; ++ } b; ++} hprt0_data_t; ++ ++/** ++ * This union represents the bit fields in the Host All Interrupt ++ * Register. ++ */ ++typedef union haint_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned ch0:1; ++ unsigned ch1:1; ++ unsigned ch2:1; ++ unsigned ch3:1; ++ unsigned ch4:1; ++ unsigned ch5:1; ++ unsigned ch6:1; ++ unsigned ch7:1; ++ unsigned ch8:1; ++ unsigned ch9:1; ++ unsigned ch10:1; ++ unsigned ch11:1; ++ unsigned ch12:1; ++ unsigned ch13:1; ++ unsigned ch14:1; ++ unsigned ch15:1; ++ unsigned reserved:16; ++ } b; ++ ++ struct { ++ unsigned chint:16; ++ unsigned reserved:16; ++ } b2; ++} haint_data_t; ++ ++/** ++ * This union represents the bit fields in the Host All Interrupt ++ * Register. ++ */ ++typedef union haintmsk_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned ch0:1; ++ unsigned ch1:1; ++ unsigned ch2:1; ++ unsigned ch3:1; ++ unsigned ch4:1; ++ unsigned ch5:1; ++ unsigned ch6:1; ++ unsigned ch7:1; ++ unsigned ch8:1; ++ unsigned ch9:1; ++ unsigned ch10:1; ++ unsigned ch11:1; ++ unsigned ch12:1; ++ unsigned ch13:1; ++ unsigned ch14:1; ++ unsigned ch15:1; ++ unsigned reserved:16; ++ } b; ++ ++ struct { ++ unsigned chint:16; ++ unsigned reserved:16; ++ } b2; ++} haintmsk_data_t; ++ ++/** ++ * Host Channel Specific Registers. <i>500h-5FCh</i> ++ */ ++typedef struct dwc_otg_hc_regs { ++ /** Host Channel 0 Characteristic Register. <i>Offset: 500h + (chan_num * 20h) + 00h</i> */ ++ volatile uint32_t hcchar; ++ /** Host Channel 0 Split Control Register. <i>Offset: 500h + (chan_num * 20h) + 04h</i> */ ++ volatile uint32_t hcsplt; ++ /** Host Channel 0 Interrupt Register. <i>Offset: 500h + (chan_num * 20h) + 08h</i> */ ++ volatile uint32_t hcint; ++ /** Host Channel 0 Interrupt Mask Register. <i>Offset: 500h + (chan_num * 20h) + 0Ch</i> */ ++ volatile uint32_t hcintmsk; ++ /** Host Channel 0 Transfer Size Register. <i>Offset: 500h + (chan_num * 20h) + 10h</i> */ ++ volatile uint32_t hctsiz; ++ /** Host Channel 0 DMA Address Register. <i>Offset: 500h + (chan_num * 20h) + 14h</i> */ ++ volatile uint32_t hcdma; ++ volatile uint32_t reserved; ++ /** Host Channel 0 DMA Buffer Address Register. <i>Offset: 500h + (chan_num * 20h) + 1Ch</i> */ ++ volatile uint32_t hcdmab; ++} dwc_otg_hc_regs_t; ++ ++/** ++ * This union represents the bit fields in the Host Channel Characteristics ++ * Register. Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the ++ * hcchar register. ++ */ ++typedef union hcchar_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** Maximum packet size in bytes */ ++ unsigned mps:11; ++ ++ /** Endpoint number */ ++ unsigned epnum:4; ++ ++ /** 0: OUT, 1: IN */ ++ unsigned epdir:1; ++ ++ unsigned reserved:1; ++ ++ /** 0: Full/high speed device, 1: Low speed device */ ++ unsigned lspddev:1; ++ ++ /** 0: Control, 1: Isoc, 2: Bulk, 3: Intr */ ++ unsigned eptype:2; ++ ++ /** Packets per frame for periodic transfers. 0 is reserved. */ ++ unsigned multicnt:2; ++ ++ /** Device address */ ++ unsigned devaddr:7; ++ ++ /** ++ * Frame to transmit periodic transaction. ++ * 0: even, 1: odd ++ */ ++ unsigned oddfrm:1; ++ ++ /** Channel disable */ ++ unsigned chdis:1; ++ ++ /** Channel enable */ ++ unsigned chen:1; ++ } b; ++} hcchar_data_t; ++ ++typedef union hcsplt_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** Port Address */ ++ unsigned prtaddr:7; ++ ++ /** Hub Address */ ++ unsigned hubaddr:7; ++ ++ /** Transaction Position */ ++ unsigned xactpos:2; ++#define DWC_HCSPLIT_XACTPOS_MID 0 ++#define DWC_HCSPLIT_XACTPOS_END 1 ++#define DWC_HCSPLIT_XACTPOS_BEGIN 2 ++#define DWC_HCSPLIT_XACTPOS_ALL 3 ++ ++ /** Do Complete Split */ ++ unsigned compsplt:1; ++ ++ /** Reserved */ ++ unsigned reserved:14; ++ ++ /** Split Enble */ ++ unsigned spltena:1; ++ } b; ++} hcsplt_data_t; ++ ++/** ++ * This union represents the bit fields in the Host All Interrupt ++ * Register. ++ */ ++typedef union hcint_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Transfer Complete */ ++ unsigned xfercomp:1; ++ /** Channel Halted */ ++ unsigned chhltd:1; ++ /** AHB Error */ ++ unsigned ahberr:1; ++ /** STALL Response Received */ ++ unsigned stall:1; ++ /** NAK Response Received */ ++ unsigned nak:1; ++ /** ACK Response Received */ ++ unsigned ack:1; ++ /** NYET Response Received */ ++ unsigned nyet:1; ++ /** Transaction Err */ ++ unsigned xacterr:1; ++ /** Babble Error */ ++ unsigned bblerr:1; ++ /** Frame Overrun */ ++ unsigned frmovrun:1; ++ /** Data Toggle Error */ ++ unsigned datatglerr:1; ++ /** Buffer Not Available (only for DDMA mode) */ ++ unsigned bna:1; ++ /** Exessive transaction error (only for DDMA mode) */ ++ unsigned xcs_xact:1; ++ /** Frame List Rollover interrupt */ ++ unsigned frm_list_roll:1; ++ /** Reserved */ ++ unsigned reserved14_31:18; ++ } b; ++} hcint_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Channel Interrupt Mask ++ * Register. Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the ++ * hcintmsk register. ++ */ ++typedef union hcintmsk_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ unsigned xfercompl:1; ++ unsigned chhltd:1; ++ unsigned ahberr:1; ++ unsigned stall:1; ++ unsigned nak:1; ++ unsigned ack:1; ++ unsigned nyet:1; ++ unsigned xacterr:1; ++ unsigned bblerr:1; ++ unsigned frmovrun:1; ++ unsigned datatglerr:1; ++ unsigned bna:1; ++ unsigned xcs_xact:1; ++ unsigned frm_list_roll:1; ++ unsigned reserved14_31:18; ++ } b; ++} hcintmsk_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Channel Transfer Size ++ * Register. Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the ++ * hcchar register. ++ */ ++ ++typedef union hctsiz_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** Total transfer size in bytes */ ++ unsigned xfersize:19; ++ ++ /** Data packets to transfer */ ++ unsigned pktcnt:10; ++ ++ /** ++ * Packet ID for next data packet ++ * 0: DATA0 ++ * 1: DATA2 ++ * 2: DATA1 ++ * 3: MDATA (non-Control), SETUP (Control) ++ */ ++ unsigned pid:2; ++#define DWC_HCTSIZ_DATA0 0 ++#define DWC_HCTSIZ_DATA1 2 ++#define DWC_HCTSIZ_DATA2 1 ++#define DWC_HCTSIZ_MDATA 3 ++#define DWC_HCTSIZ_SETUP 3 ++ ++ /** Do PING protocol when 1 */ ++ unsigned dopng:1; ++ } b; ++ ++ /** register bits */ ++ struct { ++ /** Scheduling information */ ++ unsigned schinfo:8; ++ ++ /** Number of transfer descriptors. ++ * Max value: ++ * 64 in general, ++ * 256 only for HS isochronous endpoint. ++ */ ++ unsigned ntd:8; ++ ++ /** Data packets to transfer */ ++ unsigned reserved16_28:13; ++ ++ /** ++ * Packet ID for next data packet ++ * 0: DATA0 ++ * 1: DATA2 ++ * 2: DATA1 ++ * 3: MDATA (non-Control) ++ */ ++ unsigned pid:2; ++ ++ /** Do PING protocol when 1 */ ++ unsigned dopng:1; ++ } b_ddma; ++} hctsiz_data_t; ++ ++/** ++ * This union represents the bit fields in the Host DMA Address ++ * Register used in Descriptor DMA mode. ++ */ ++typedef union hcdma_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved0_2:3; ++ /** Current Transfer Descriptor. Not used for ISOC */ ++ unsigned ctd:8; ++ /** Start Address of Descriptor List */ ++ unsigned dma_addr:21; ++ } b; ++} hcdma_data_t; ++ ++/** ++ * This union represents the bit fields in the DMA Descriptor ++ * status quadlet for host mode. Read the quadlet into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union host_dma_desc_sts { ++ /** raw register data */ ++ uint32_t d32; ++ /** quadlet bits */ ++ ++ /* for non-isochronous */ ++ struct { ++ /** Number of bytes */ ++ unsigned n_bytes:17; ++ /** QTD offset to jump when Short Packet received - only for IN EPs */ ++ unsigned qtd_offset:6; ++ /** ++ * Set to request the core to jump to alternate QTD if ++ * Short Packet received - only for IN EPs ++ */ ++ unsigned a_qtd:1; ++ /** ++ * Setup Packet bit. When set indicates that buffer contains ++ * setup packet. ++ */ ++ unsigned sup:1; ++ /** Interrupt On Complete */ ++ unsigned ioc:1; ++ /** End of List */ ++ unsigned eol:1; ++ unsigned reserved27:1; ++ /** Rx/Tx Status */ ++ unsigned sts:2; ++#define DMA_DESC_STS_PKTERR 1 ++ unsigned reserved30:1; ++ /** Active Bit */ ++ unsigned a:1; ++ } b; ++ /* for isochronous */ ++ struct { ++ /** Number of bytes */ ++ unsigned n_bytes:12; ++ unsigned reserved12_24:13; ++ /** Interrupt On Complete */ ++ unsigned ioc:1; ++ unsigned reserved26_27:2; ++ /** Rx/Tx Status */ ++ unsigned sts:2; ++ unsigned reserved30:1; ++ /** Active Bit */ ++ unsigned a:1; ++ } b_isoc; ++} host_dma_desc_sts_t; ++ ++#define MAX_DMA_DESC_SIZE 131071 ++#define MAX_DMA_DESC_NUM_GENERIC 64 ++#define MAX_DMA_DESC_NUM_HS_ISOC 256 ++#define MAX_FRLIST_EN_NUM 64 ++/** ++ * Host-mode DMA Descriptor structure ++ * ++ * DMA Descriptor structure contains two quadlets: ++ * Status quadlet and Data buffer pointer. ++ */ ++typedef struct dwc_otg_host_dma_desc { ++ /** DMA Descriptor status quadlet */ ++ host_dma_desc_sts_t status; ++ /** DMA Descriptor data buffer pointer */ ++ uint32_t buf; ++} dwc_otg_host_dma_desc_t; ++ ++/** OTG Host Interface Structure. ++ * ++ * The OTG Host Interface Structure structure contains information ++ * needed to manage the DWC_otg controller acting in host mode. It ++ * represents the programming view of the host-specific aspects of the ++ * controller. ++ */ ++typedef struct dwc_otg_host_if { ++ /** Host Global Registers starting at offset 400h.*/ ++ dwc_otg_host_global_regs_t *host_global_regs; ++#define DWC_OTG_HOST_GLOBAL_REG_OFFSET 0x400 ++ ++ /** Host Port 0 Control and Status Register */ ++ volatile uint32_t *hprt0; ++#define DWC_OTG_HOST_PORT_REGS_OFFSET 0x440 ++ ++ /** Host Channel Specific Registers at offsets 500h-5FCh. */ ++ dwc_otg_hc_regs_t *hc_regs[MAX_EPS_CHANNELS]; ++#define DWC_OTG_HOST_CHAN_REGS_OFFSET 0x500 ++#define DWC_OTG_CHAN_REGS_OFFSET 0x20 ++ ++ /* Host configuration information */ ++ /** Number of Host Channels (range: 1-16) */ ++ uint8_t num_host_channels; ++ /** Periodic EPs supported (0: no, 1: yes) */ ++ uint8_t perio_eps_supported; ++ /** Periodic Tx FIFO Size (Only 1 host periodic Tx FIFO) */ ++ uint16_t perio_tx_fifo_size; ++ ++} dwc_otg_host_if_t; ++ ++/** ++ * This union represents the bit fields in the Power and Clock Gating Control ++ * Register. Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union pcgcctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** Stop Pclk */ ++ unsigned stoppclk:1; ++ /** Gate Hclk */ ++ unsigned gatehclk:1; ++ /** Power Clamp */ ++ unsigned pwrclmp:1; ++ /** Reset Power Down Modules */ ++ unsigned rstpdwnmodule:1; ++ /** Reserved */ ++ unsigned reserved:1; ++ /** Enable Sleep Clock Gating (Enbl_L1Gating) */ ++ unsigned enbl_sleep_gating:1; ++ /** PHY In Sleep (PhySleep) */ ++ unsigned phy_in_sleep:1; ++ /** Deep Sleep*/ ++ unsigned deep_sleep:1; ++ unsigned resetaftsusp:1; ++ unsigned restoremode:1; ++ unsigned enbl_extnd_hiber:1; ++ unsigned extnd_hiber_pwrclmp:1; ++ unsigned extnd_hiber_switch:1; ++ unsigned ess_reg_restored:1; ++ unsigned prt_clk_sel:2; ++ unsigned port_power:1; ++ unsigned max_xcvrselect:2; ++ unsigned max_termsel:1; ++ unsigned mac_dev_addr:7; ++ unsigned p2hd_dev_enum_spd:2; ++ unsigned p2hd_prt_spd:2; ++ unsigned if_dev_mode:1; ++ } b; ++} pcgcctl_data_t; ++ ++/** ++ * This union represents the bit fields in the Global Data FIFO Software ++ * Configuration Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union gdfifocfg_data { ++ /* raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** OTG Data FIFO depth */ ++ unsigned gdfifocfg:16; ++ /** Start address of EP info controller */ ++ unsigned epinfobase:16; ++ } b; ++} gdfifocfg_data_t; ++ ++/** ++ * This union represents the bit fields in the Global Power Down Register ++ * Register. Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union gpwrdn_data { ++ /* raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** PMU Interrupt Select */ ++ unsigned pmuintsel:1; ++ /** PMU Active */ ++ unsigned pmuactv:1; ++ /** Restore */ ++ unsigned restore:1; ++ /** Power Down Clamp */ ++ unsigned pwrdnclmp:1; ++ /** Power Down Reset */ ++ unsigned pwrdnrstn:1; ++ /** Power Down Switch */ ++ unsigned pwrdnswtch:1; ++ /** Disable VBUS */ ++ unsigned dis_vbus:1; ++ /** Line State Change */ ++ unsigned lnstschng:1; ++ /** Line state change mask */ ++ unsigned lnstchng_msk:1; ++ /** Reset Detected */ ++ unsigned rst_det:1; ++ /** Reset Detect mask */ ++ unsigned rst_det_msk:1; ++ /** Disconnect Detected */ ++ unsigned disconn_det:1; ++ /** Disconnect Detect mask */ ++ unsigned disconn_det_msk:1; ++ /** Connect Detected*/ ++ unsigned connect_det:1; ++ /** Connect Detected Mask*/ ++ unsigned connect_det_msk:1; ++ /** SRP Detected */ ++ unsigned srp_det:1; ++ /** SRP Detect mask */ ++ unsigned srp_det_msk:1; ++ /** Status Change Interrupt */ ++ unsigned sts_chngint:1; ++ /** Status Change Interrupt Mask */ ++ unsigned sts_chngint_msk:1; ++ /** Line State */ ++ unsigned linestate:2; ++ /** Indicates current mode(status of IDDIG signal) */ ++ unsigned idsts:1; ++ /** B Session Valid signal status*/ ++ unsigned bsessvld:1; ++ /** ADP Event Detected */ ++ unsigned adp_int:1; ++ /** Multi Valued ID pin */ ++ unsigned mult_val_id_bc:5; ++ /** Reserved 24_31 */ ++ unsigned reserved29_31:3; ++ } b; ++} gpwrdn_data_t; ++ ++#endif +diff --git a/drivers/usb/gadget/udc/hiudc/usb.h b/drivers/usb/gadget/udc/hiudc/usb.h +new file mode 100644 +index 0000000..27bda82 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc/usb.h +@@ -0,0 +1,946 @@ ++/* ++ * Copyright (c) 1998 The NetBSD Foundation, Inc. ++ * All rights reserved. ++ * ++ * This code is derived from software contributed to The NetBSD Foundation ++ * by Lennart Augustsson (lennart@augustsson.net) at ++ * Carlstedt Research & Technology. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * This product includes software developed by the NetBSD ++ * Foundation, Inc. and its contributors. ++ * 4. Neither the name of The NetBSD Foundation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS ++ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS ++ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++/* Modified by Synopsys, Inc, 12/12/2007 */ ++ ++ ++#ifndef _USB_H_ ++#define _USB_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* ++ * The USB records contain some unaligned little-endian word ++ * components. The U[SG]ETW macros take care of both the alignment ++ * and endian problem and should always be used to access non-byte ++ * values. ++ */ ++typedef u_int8_t uByte; ++typedef u_int8_t uWord[2]; ++typedef u_int8_t uDWord[4]; ++ ++#define USETW2(w,h,l) ((w)[0] = (u_int8_t)(l), (w)[1] = (u_int8_t)(h)) ++#define UCONSTW(x) { (x) & 0xff, ((x) >> 8) & 0xff } ++#define UCONSTDW(x) { (x) & 0xff, ((x) >> 8) & 0xff, \ ++ ((x) >> 16) & 0xff, ((x) >> 24) & 0xff } ++ ++#if 1 ++#define UGETW(w) ((w)[0] | ((w)[1] << 8)) ++#define USETW(w,v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8)) ++#define UGETDW(w) ((w)[0] | ((w)[1] << 8) | ((w)[2] << 16) | ((w)[3] << 24)) ++#define USETDW(w,v) ((w)[0] = (u_int8_t)(v), \ ++ (w)[1] = (u_int8_t)((v) >> 8), \ ++ (w)[2] = (u_int8_t)((v) >> 16), \ ++ (w)[3] = (u_int8_t)((v) >> 24)) ++#else ++/* ++ * On little-endian machines that can handle unanliged accesses ++ * (e.g. i386) these macros can be replaced by the following. ++ */ ++#define UGETW(w) (*(u_int16_t *)(w)) ++#define USETW(w,v) (*(u_int16_t *)(w) = (v)) ++#define UGETDW(w) (*(u_int32_t *)(w)) ++#define USETDW(w,v) (*(u_int32_t *)(w) = (v)) ++#endif ++ ++/* ++ * Macros for accessing UAS IU fields, which are big-endian ++ */ ++#define IUSETW2(w,h,l) ((w)[0] = (u_int8_t)(h), (w)[1] = (u_int8_t)(l)) ++#define IUCONSTW(x) { ((x) >> 8) & 0xff, (x) & 0xff } ++#define IUCONSTDW(x) { ((x) >> 24) & 0xff, ((x) >> 16) & 0xff, \ ++ ((x) >> 8) & 0xff, (x) & 0xff } ++#define IUGETW(w) (((w)[0] << 8) | (w)[1]) ++#define IUSETW(w,v) ((w)[0] = (u_int8_t)((v) >> 8), (w)[1] = (u_int8_t)(v)) ++#define IUGETDW(w) (((w)[0] << 24) | ((w)[1] << 16) | ((w)[2] << 8) | (w)[3]) ++#define IUSETDW(w,v) ((w)[0] = (u_int8_t)((v) >> 24), \ ++ (w)[1] = (u_int8_t)((v) >> 16), \ ++ (w)[2] = (u_int8_t)((v) >> 8), \ ++ (w)[3] = (u_int8_t)(v)) ++ ++#define UPACKED __attribute__((__packed__)) ++ ++typedef struct { ++ uByte bmRequestType; ++ uByte bRequest; ++ uWord wValue; ++ uWord wIndex; ++ uWord wLength; ++} UPACKED usb_device_request_t; ++ ++#define UT_GET_DIR(a) ((a) & 0x80) ++#define UT_WRITE 0x00 ++#define UT_READ 0x80 ++ ++#define UT_GET_TYPE(a) ((a) & 0x60) ++#define UT_STANDARD 0x00 ++#define UT_CLASS 0x20 ++#define UT_VENDOR 0x40 ++ ++#define UT_GET_RECIPIENT(a) ((a) & 0x1f) ++#define UT_DEVICE 0x00 ++#define UT_INTERFACE 0x01 ++#define UT_ENDPOINT 0x02 ++#define UT_OTHER 0x03 ++ ++#define UT_READ_DEVICE (UT_READ | UT_STANDARD | UT_DEVICE) ++#define UT_READ_INTERFACE (UT_READ | UT_STANDARD | UT_INTERFACE) ++#define UT_READ_ENDPOINT (UT_READ | UT_STANDARD | UT_ENDPOINT) ++#define UT_WRITE_DEVICE (UT_WRITE | UT_STANDARD | UT_DEVICE) ++#define UT_WRITE_INTERFACE (UT_WRITE | UT_STANDARD | UT_INTERFACE) ++#define UT_WRITE_ENDPOINT (UT_WRITE | UT_STANDARD | UT_ENDPOINT) ++#define UT_READ_CLASS_DEVICE (UT_READ | UT_CLASS | UT_DEVICE) ++#define UT_READ_CLASS_INTERFACE (UT_READ | UT_CLASS | UT_INTERFACE) ++#define UT_READ_CLASS_OTHER (UT_READ | UT_CLASS | UT_OTHER) ++#define UT_READ_CLASS_ENDPOINT (UT_READ | UT_CLASS | UT_ENDPOINT) ++#define UT_WRITE_CLASS_DEVICE (UT_WRITE | UT_CLASS | UT_DEVICE) ++#define UT_WRITE_CLASS_INTERFACE (UT_WRITE | UT_CLASS | UT_INTERFACE) ++#define UT_WRITE_CLASS_OTHER (UT_WRITE | UT_CLASS | UT_OTHER) ++#define UT_WRITE_CLASS_ENDPOINT (UT_WRITE | UT_CLASS | UT_ENDPOINT) ++#define UT_READ_VENDOR_DEVICE (UT_READ | UT_VENDOR | UT_DEVICE) ++#define UT_READ_VENDOR_INTERFACE (UT_READ | UT_VENDOR | UT_INTERFACE) ++#define UT_READ_VENDOR_OTHER (UT_READ | UT_VENDOR | UT_OTHER) ++#define UT_READ_VENDOR_ENDPOINT (UT_READ | UT_VENDOR | UT_ENDPOINT) ++#define UT_WRITE_VENDOR_DEVICE (UT_WRITE | UT_VENDOR | UT_DEVICE) ++#define UT_WRITE_VENDOR_INTERFACE (UT_WRITE | UT_VENDOR | UT_INTERFACE) ++#define UT_WRITE_VENDOR_OTHER (UT_WRITE | UT_VENDOR | UT_OTHER) ++#define UT_WRITE_VENDOR_ENDPOINT (UT_WRITE | UT_VENDOR | UT_ENDPOINT) ++ ++/* Requests */ ++#define UR_GET_STATUS 0x00 ++#define USTAT_STANDARD_STATUS 0x00 ++#define WUSTAT_WUSB_FEATURE 0x01 ++#define WUSTAT_CHANNEL_INFO 0x02 ++#define WUSTAT_RECEIVED_DATA 0x03 ++#define WUSTAT_MAS_AVAILABILITY 0x04 ++#define WUSTAT_CURRENT_TRANSMIT_POWER 0x05 ++#define UR_CLEAR_FEATURE 0x01 ++#define UR_SET_FEATURE 0x03 ++#define UR_SET_AND_TEST_FEATURE 0x0c ++#define UR_SET_ADDRESS 0x05 ++#define UR_GET_DESCRIPTOR 0x06 ++#define UDESC_DEVICE 0x01 ++#define UDESC_CONFIG 0x02 ++#define UDESC_STRING 0x03 ++#define UDESC_INTERFACE 0x04 ++#define UDESC_ENDPOINT 0x05 ++#define UDESC_SS_USB_COMPANION 0x30 ++#define UDESC_DEVICE_QUALIFIER 0x06 ++#define UDESC_OTHER_SPEED_CONFIGURATION 0x07 ++#define UDESC_INTERFACE_POWER 0x08 ++#define UDESC_OTG 0x09 ++#define WUDESC_SECURITY 0x0c ++#define WUDESC_KEY 0x0d ++#define WUD_GET_KEY_INDEX(_wValue_) ((_wValue_) & 0xf) ++#define WUD_GET_KEY_TYPE(_wValue_) (((_wValue_) & 0x30) >> 4) ++#define WUD_KEY_TYPE_ASSOC 0x01 ++#define WUD_KEY_TYPE_GTK 0x02 ++#define WUD_GET_KEY_ORIGIN(_wValue_) (((_wValue_) & 0x40) >> 6) ++#define WUD_KEY_ORIGIN_HOST 0x00 ++#define WUD_KEY_ORIGIN_DEVICE 0x01 ++#define WUDESC_ENCRYPTION_TYPE 0x0e ++#define WUDESC_BOS 0x0f ++#define WUDESC_DEVICE_CAPABILITY 0x10 ++#define WUDESC_WIRELESS_ENDPOINT_COMPANION 0x11 ++#define UDESC_BOS 0x0f ++#define UDESC_DEVICE_CAPABILITY 0x10 ++#define UDESC_CS_DEVICE 0x21 /* class specific */ ++#define UDESC_CS_CONFIG 0x22 ++#define UDESC_CS_STRING 0x23 ++#define UDESC_CS_INTERFACE 0x24 ++#define UDESC_CS_ENDPOINT 0x25 ++#define UDESC_HUB 0x29 ++#define UR_SET_DESCRIPTOR 0x07 ++#define UR_GET_CONFIG 0x08 ++#define UR_SET_CONFIG 0x09 ++#define UR_GET_INTERFACE 0x0a ++#define UR_SET_INTERFACE 0x0b ++#define UR_SYNCH_FRAME 0x0c ++#define WUR_SET_ENCRYPTION 0x0d ++#define WUR_GET_ENCRYPTION 0x0e ++#define WUR_SET_HANDSHAKE 0x0f ++#define WUR_GET_HANDSHAKE 0x10 ++#define WUR_SET_CONNECTION 0x11 ++#define WUR_SET_SECURITY_DATA 0x12 ++#define WUR_GET_SECURITY_DATA 0x13 ++#define WUR_SET_WUSB_DATA 0x14 ++#define WUDATA_DRPIE_INFO 0x01 ++#define WUDATA_TRANSMIT_DATA 0x02 ++#define WUDATA_TRANSMIT_PARAMS 0x03 ++#define WUDATA_RECEIVE_PARAMS 0x04 ++#define WUDATA_TRANSMIT_POWER 0x05 ++#define WUR_LOOPBACK_DATA_WRITE 0x15 ++#define WUR_LOOPBACK_DATA_READ 0x16 ++#define WUR_SET_INTERFACE_DS 0x17 ++ ++/* Feature numbers */ ++#define UF_ENDPOINT_HALT 0 ++#define UF_DEVICE_REMOTE_WAKEUP 1 ++#define UF_TEST_MODE 2 ++#define UF_DEVICE_B_HNP_ENABLE 3 ++#define UF_DEVICE_A_HNP_SUPPORT 4 ++#define UF_DEVICE_A_ALT_HNP_SUPPORT 5 ++#define WUF_WUSB 3 ++#define WUF_TX_DRPIE 0x0 ++#define WUF_DEV_XMIT_PACKET 0x1 ++#define WUF_COUNT_PACKETS 0x2 ++#define WUF_CAPTURE_PACKETS 0x3 ++#define UF_FUNCTION_SUSPEND 0 ++#define UF_U1_ENABLE 48 ++#define UF_U2_ENABLE 49 ++#define UF_LTM_ENABLE 50 ++ ++/* Class requests from the USB 2.0 hub spec, table 11-15 */ ++#define UCR_CLEAR_HUB_FEATURE (0x2000 | UR_CLEAR_FEATURE) ++#define UCR_CLEAR_PORT_FEATURE (0x2300 | UR_CLEAR_FEATURE) ++#define UCR_GET_HUB_DESCRIPTOR (0xa000 | UR_GET_DESCRIPTOR) ++#define UCR_GET_HUB_STATUS (0xa000 | UR_GET_STATUS) ++#define UCR_GET_PORT_STATUS (0xa300 | UR_GET_STATUS) ++#define UCR_SET_HUB_FEATURE (0x2000 | UR_SET_FEATURE) ++#define UCR_SET_PORT_FEATURE (0x2300 | UR_SET_FEATURE) ++#define UCR_SET_AND_TEST_PORT_FEATURE (0xa300 | UR_SET_AND_TEST_FEATURE) ++ ++#ifdef _MSC_VER ++#include <pshpack1.h> ++#endif ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDescriptorSubtype; ++} UPACKED usb_descriptor_t; ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++} UPACKED usb_descriptor_header_t; ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord bcdUSB; ++#define UD_USB_2_0 0x0200 ++#define UD_IS_USB2(d) (UGETW((d)->bcdUSB) >= UD_USB_2_0) ++ uByte bDeviceClass; ++ uByte bDeviceSubClass; ++ uByte bDeviceProtocol; ++ uByte bMaxPacketSize; ++ /* The fields below are not part of the initial descriptor. */ ++ uWord idVendor; ++ uWord idProduct; ++ uWord bcdDevice; ++ uByte iManufacturer; ++ uByte iProduct; ++ uByte iSerialNumber; ++ uByte bNumConfigurations; ++} UPACKED usb_device_descriptor_t; ++#define USB_DEVICE_DESCRIPTOR_SIZE 18 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord wTotalLength; ++ uByte bNumInterface; ++ uByte bConfigurationValue; ++ uByte iConfiguration; ++#define UC_ATT_ONE (1 << 7) /* must be set */ ++#define UC_ATT_SELFPOWER (1 << 6) /* self powered */ ++#define UC_ATT_WAKEUP (1 << 5) /* can wakeup */ ++#define UC_ATT_BATTERY (1 << 4) /* battery powered */ ++ uByte bmAttributes; ++#define UC_BUS_POWERED 0x80 ++#define UC_SELF_POWERED 0x40 ++#define UC_REMOTE_WAKEUP 0x20 ++ uByte bMaxPower; /* max current in 2 mA units */ ++#define UC_POWER_FACTOR 2 ++} UPACKED usb_config_descriptor_t; ++#define USB_CONFIG_DESCRIPTOR_SIZE 9 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bInterfaceNumber; ++ uByte bAlternateSetting; ++ uByte bNumEndpoints; ++ uByte bInterfaceClass; ++ uByte bInterfaceSubClass; ++ uByte bInterfaceProtocol; ++ uByte iInterface; ++} UPACKED usb_interface_descriptor_t; ++#define USB_INTERFACE_DESCRIPTOR_SIZE 9 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bEndpointAddress; ++#define UE_GET_DIR(a) ((a) & 0x80) ++#define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7)) ++#define UE_DIR_IN 0x80 ++#define UE_DIR_OUT 0x00 ++#define UE_ADDR 0x0f ++#define UE_GET_ADDR(a) ((a) & UE_ADDR) ++ uByte bmAttributes; ++#define UE_XFERTYPE 0x03 ++#define UE_CONTROL 0x00 ++#define UE_ISOCHRONOUS 0x01 ++#define UE_BULK 0x02 ++#define UE_INTERRUPT 0x03 ++#define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE) ++#define UE_ISO_TYPE 0x0c ++#define UE_ISO_ASYNC 0x04 ++#define UE_ISO_ADAPT 0x08 ++#define UE_ISO_SYNC 0x0c ++#define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE) ++ uWord wMaxPacketSize; ++ uByte bInterval; ++} UPACKED usb_endpoint_descriptor_t; ++#define USB_ENDPOINT_DESCRIPTOR_SIZE 7 ++ ++typedef struct ss_endpoint_companion_descriptor { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bMaxBurst; ++#define USSE_GET_MAX_STREAMS(a) ((a) & 0x1f) ++#define USSE_SET_MAX_STREAMS(a, b) ((a) | ((b) & 0x1f)) ++#define USSE_GET_MAX_PACKET_NUM(a) ((a) & 0x03) ++#define USSE_SET_MAX_PACKET_NUM(a, b) ((a) | ((b) & 0x03)) ++ uByte bmAttributes; ++ uWord wBytesPerInterval; ++} UPACKED ss_endpoint_companion_descriptor_t; ++#define USB_SS_ENDPOINT_COMPANION_DESCRIPTOR_SIZE 6 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord bString[127]; ++} UPACKED usb_string_descriptor_t; ++#define USB_MAX_STRING_LEN 128 ++#define USB_LANGUAGE_TABLE 0 /* # of the string language id table */ ++ ++/* Hub specific request */ ++#define UR_GET_BUS_STATE 0x02 ++#define UR_CLEAR_TT_BUFFER 0x08 ++#define UR_RESET_TT 0x09 ++#define UR_GET_TT_STATE 0x0a ++#define UR_STOP_TT 0x0b ++ ++/* Hub features */ ++#define UHF_C_HUB_LOCAL_POWER 0 ++#define UHF_C_HUB_OVER_CURRENT 1 ++#define UHF_PORT_CONNECTION 0 ++#define UHF_PORT_ENABLE 1 ++#define UHF_PORT_SUSPEND 2 ++#define UHF_PORT_OVER_CURRENT 3 ++#define UHF_PORT_RESET 4 ++#define UHF_PORT_L1 5 ++#define UHF_PORT_POWER 8 ++#define UHF_PORT_LOW_SPEED 9 ++#define UHF_PORT_HIGH_SPEED 10 ++#define UHF_C_PORT_CONNECTION 16 ++#define UHF_C_PORT_ENABLE 17 ++#define UHF_C_PORT_SUSPEND 18 ++#define UHF_C_PORT_OVER_CURRENT 19 ++#define UHF_C_PORT_RESET 20 ++#define UHF_C_PORT_L1 23 ++#define UHF_PORT_TEST 21 ++#define UHF_PORT_INDICATOR 22 ++ ++typedef struct { ++ uByte bDescLength; ++ uByte bDescriptorType; ++ uByte bNbrPorts; ++ uWord wHubCharacteristics; ++#define UHD_PWR 0x0003 ++#define UHD_PWR_GANGED 0x0000 ++#define UHD_PWR_INDIVIDUAL 0x0001 ++#define UHD_PWR_NO_SWITCH 0x0002 ++#define UHD_COMPOUND 0x0004 ++#define UHD_OC 0x0018 ++#define UHD_OC_GLOBAL 0x0000 ++#define UHD_OC_INDIVIDUAL 0x0008 ++#define UHD_OC_NONE 0x0010 ++#define UHD_TT_THINK 0x0060 ++#define UHD_TT_THINK_8 0x0000 ++#define UHD_TT_THINK_16 0x0020 ++#define UHD_TT_THINK_24 0x0040 ++#define UHD_TT_THINK_32 0x0060 ++#define UHD_PORT_IND 0x0080 ++ uByte bPwrOn2PwrGood; /* delay in 2 ms units */ ++#define UHD_PWRON_FACTOR 2 ++ uByte bHubContrCurrent; ++ uByte DeviceRemovable[32]; /* max 255 ports */ ++#define UHD_NOT_REMOV(desc, i) \ ++ (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1) ++ /* deprecated */ uByte PortPowerCtrlMask[1]; ++} UPACKED usb_hub_descriptor_t; ++#define USB_HUB_DESCRIPTOR_SIZE 9 /* includes deprecated PortPowerCtrlMask */ ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord bcdUSB; ++ uByte bDeviceClass; ++ uByte bDeviceSubClass; ++ uByte bDeviceProtocol; ++ uByte bMaxPacketSize0; ++ uByte bNumConfigurations; ++ uByte bReserved; ++} UPACKED usb_device_qualifier_t; ++#define USB_DEVICE_QUALIFIER_SIZE 10 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bmAttributes; ++#define UOTG_SRP 0x01 ++#define UOTG_HNP 0x02 ++} UPACKED usb_otg_descriptor_t; ++ ++/* OTG feature selectors */ ++#define UOTG_B_HNP_ENABLE 3 ++#define UOTG_A_HNP_SUPPORT 4 ++#define UOTG_A_ALT_HNP_SUPPORT 5 ++ ++typedef struct { ++ uWord wStatus; ++/* Device status flags */ ++#define UDS_SELF_POWERED 0x0001 ++#define UDS_REMOTE_WAKEUP 0x0002 ++/* Endpoint status flags */ ++#define UES_HALT 0x0001 ++} UPACKED usb_status_t; ++ ++typedef struct { ++ uWord wHubStatus; ++#define UHS_LOCAL_POWER 0x0001 ++#define UHS_OVER_CURRENT 0x0002 ++ uWord wHubChange; ++} UPACKED usb_hub_status_t; ++ ++typedef struct { ++ uWord wPortStatus; ++#define UPS_CURRENT_CONNECT_STATUS 0x0001 ++#define UPS_PORT_ENABLED 0x0002 ++#define UPS_SUSPEND 0x0004 ++#define UPS_OVERCURRENT_INDICATOR 0x0008 ++#define UPS_RESET 0x0010 ++#define UPS_PORT_POWER 0x0100 ++#define UPS_LOW_SPEED 0x0200 ++#define UPS_HIGH_SPEED 0x0400 ++#define UPS_PORT_TEST 0x0800 ++#define UPS_PORT_INDICATOR 0x1000 ++ uWord wPortChange; ++#define UPS_C_CONNECT_STATUS 0x0001 ++#define UPS_C_PORT_ENABLED 0x0002 ++#define UPS_C_SUSPEND 0x0004 ++#define UPS_C_OVERCURRENT_INDICATOR 0x0008 ++#define UPS_C_PORT_RESET 0x0010 ++} UPACKED usb_port_status_t; ++ ++#ifdef _MSC_VER ++#include <poppack.h> ++#endif ++ ++/* Device class codes */ ++#define UDCLASS_IN_INTERFACE 0x00 ++#define UDCLASS_COMM 0x02 ++#define UDCLASS_HUB 0x09 ++#define UDSUBCLASS_HUB 0x00 ++#define UDPROTO_FSHUB 0x00 ++#define UDPROTO_HSHUBSTT 0x01 ++#define UDPROTO_HSHUBMTT 0x02 ++#define UDCLASS_DIAGNOSTIC 0xdc ++#define UDCLASS_WIRELESS 0xe0 ++#define UDSUBCLASS_RF 0x01 ++#define UDPROTO_BLUETOOTH 0x01 ++#define UDCLASS_VENDOR 0xff ++ ++/* Interface class codes */ ++#define UICLASS_UNSPEC 0x00 ++ ++#define UICLASS_AUDIO 0x01 ++#define UISUBCLASS_AUDIOCONTROL 1 ++#define UISUBCLASS_AUDIOSTREAM 2 ++#define UISUBCLASS_MIDISTREAM 3 ++ ++#define UICLASS_CDC 0x02 /* communication */ ++#define UISUBCLASS_DIRECT_LINE_CONTROL_MODEL 1 ++#define UISUBCLASS_ABSTRACT_CONTROL_MODEL 2 ++#define UISUBCLASS_TELEPHONE_CONTROL_MODEL 3 ++#define UISUBCLASS_MULTICHANNEL_CONTROL_MODEL 4 ++#define UISUBCLASS_CAPI_CONTROLMODEL 5 ++#define UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL 6 ++#define UISUBCLASS_ATM_NETWORKING_CONTROL_MODEL 7 ++#define UIPROTO_CDC_AT 1 ++ ++#define UICLASS_HID 0x03 ++#define UISUBCLASS_BOOT 1 ++#define UIPROTO_BOOT_KEYBOARD 1 ++ ++#define UICLASS_PHYSICAL 0x05 ++ ++#define UICLASS_IMAGE 0x06 ++ ++#define UICLASS_PRINTER 0x07 ++#define UISUBCLASS_PRINTER 1 ++#define UIPROTO_PRINTER_UNI 1 ++#define UIPROTO_PRINTER_BI 2 ++#define UIPROTO_PRINTER_1284 3 ++ ++#define UICLASS_MASS 0x08 ++#define UISUBCLASS_RBC 1 ++#define UISUBCLASS_SFF8020I 2 ++#define UISUBCLASS_QIC157 3 ++#define UISUBCLASS_UFI 4 ++#define UISUBCLASS_SFF8070I 5 ++#define UISUBCLASS_SCSI 6 ++#define UIPROTO_MASS_CBI_I 0 ++#define UIPROTO_MASS_CBI 1 ++#define UIPROTO_MASS_BBB_OLD 2 /* Not in the spec anymore */ ++#define UIPROTO_MASS_BBB 80 /* 'P' for the Iomega Zip drive */ ++ ++#define UICLASS_HUB 0x09 ++#define UISUBCLASS_HUB 0 ++#define UIPROTO_FSHUB 0 ++#define UIPROTO_HSHUBSTT 0 /* Yes, same as previous */ ++#define UIPROTO_HSHUBMTT 1 ++ ++#define UICLASS_CDC_DATA 0x0a ++#define UISUBCLASS_DATA 0 ++#define UIPROTO_DATA_ISDNBRI 0x30 /* Physical iface */ ++#define UIPROTO_DATA_HDLC 0x31 /* HDLC */ ++#define UIPROTO_DATA_TRANSPARENT 0x32 /* Transparent */ ++#define UIPROTO_DATA_Q921M 0x50 /* Management for Q921 */ ++#define UIPROTO_DATA_Q921 0x51 /* Data for Q921 */ ++#define UIPROTO_DATA_Q921TM 0x52 /* TEI multiplexer for Q921 */ ++#define UIPROTO_DATA_V42BIS 0x90 /* Data compression */ ++#define UIPROTO_DATA_Q931 0x91 /* Euro-ISDN */ ++#define UIPROTO_DATA_V120 0x92 /* V.24 rate adaption */ ++#define UIPROTO_DATA_CAPI 0x93 /* CAPI 2.0 commands */ ++#define UIPROTO_DATA_HOST_BASED 0xfd /* Host based driver */ ++#define UIPROTO_DATA_PUF 0xfe /* see Prot. Unit Func. Desc.*/ ++#define UIPROTO_DATA_VENDOR 0xff /* Vendor specific */ ++ ++#define UICLASS_SMARTCARD 0x0b ++ ++/*#define UICLASS_FIRM_UPD 0x0c*/ ++ ++#define UICLASS_SECURITY 0x0d ++ ++#define UICLASS_DIAGNOSTIC 0xdc ++ ++#define UICLASS_WIRELESS 0xe0 ++#define UISUBCLASS_RF 0x01 ++#define UIPROTO_BLUETOOTH 0x01 ++ ++#define UICLASS_APPL_SPEC 0xfe ++#define UISUBCLASS_FIRMWARE_DOWNLOAD 1 ++#define UISUBCLASS_IRDA 2 ++#define UIPROTO_IRDA 0 ++ ++#define UICLASS_VENDOR 0xff ++ ++#define USB_HUB_MAX_DEPTH 5 ++ ++/* ++ * Minimum time a device needs to be powered down to go through ++ * a power cycle. XXX Are these time in the spec? ++ */ ++#define USB_POWER_DOWN_TIME 200 /* ms */ ++#define USB_PORT_POWER_DOWN_TIME 100 /* ms */ ++ ++#if 0 ++/* These are the values from the spec. */ ++#define USB_PORT_RESET_DELAY 10 /* ms */ ++#define USB_PORT_ROOT_RESET_DELAY 50 /* ms */ ++#define USB_PORT_RESET_RECOVERY 10 /* ms */ ++#define USB_PORT_POWERUP_DELAY 100 /* ms */ ++#define USB_SET_ADDRESS_SETTLE 2 /* ms */ ++#define USB_RESUME_DELAY (20*5) /* ms */ ++#define USB_RESUME_WAIT 10 /* ms */ ++#define USB_RESUME_RECOVERY 10 /* ms */ ++#define USB_EXTRA_POWER_UP_TIME 0 /* ms */ ++#else ++/* Allow for marginal (i.e. non-conforming) devices. */ ++#define USB_PORT_RESET_DELAY 50 /* ms */ ++#define USB_PORT_ROOT_RESET_DELAY 250 /* ms */ ++#define USB_PORT_RESET_RECOVERY 250 /* ms */ ++#define USB_PORT_POWERUP_DELAY 300 /* ms */ ++#define USB_SET_ADDRESS_SETTLE 10 /* ms */ ++#define USB_RESUME_DELAY (50*5) /* ms */ ++#define USB_RESUME_WAIT 50 /* ms */ ++#define USB_RESUME_RECOVERY 50 /* ms */ ++#define USB_EXTRA_POWER_UP_TIME 20 /* ms */ ++#endif ++ ++#define USB_MIN_POWER 100 /* mA */ ++#define USB_MAX_POWER 500 /* mA */ ++ ++#define USB_BUS_RESET_DELAY 100 /* ms XXX?*/ ++ ++#define USB_UNCONFIG_NO 0 ++#define USB_UNCONFIG_INDEX (-1) ++ ++/*** ioctl() related stuff ***/ ++ ++struct usb_ctl_request { ++ int ucr_addr; ++ usb_device_request_t ucr_request; ++ void *ucr_data; ++ int ucr_flags; ++#define USBD_SHORT_XFER_OK 0x04 /* allow short reads */ ++ int ucr_actlen; /* actual length transferred */ ++}; ++ ++struct usb_alt_interface { ++ int uai_config_index; ++ int uai_interface_index; ++ int uai_alt_no; ++}; ++ ++#define USB_CURRENT_CONFIG_INDEX (-1) ++#define USB_CURRENT_ALT_INDEX (-1) ++ ++struct usb_config_desc { ++ int ucd_config_index; ++ usb_config_descriptor_t ucd_desc; ++}; ++ ++struct usb_interface_desc { ++ int uid_config_index; ++ int uid_interface_index; ++ int uid_alt_index; ++ usb_interface_descriptor_t uid_desc; ++}; ++ ++struct usb_endpoint_desc { ++ int ued_config_index; ++ int ued_interface_index; ++ int ued_alt_index; ++ int ued_endpoint_index; ++ usb_endpoint_descriptor_t ued_desc; ++}; ++ ++struct usb_full_desc { ++ int ufd_config_index; ++ u_int ufd_size; ++ u_char *ufd_data; ++}; ++ ++struct usb_string_desc { ++ int usd_string_index; ++ int usd_language_id; ++ usb_string_descriptor_t usd_desc; ++}; ++ ++struct usb_ctl_report_desc { ++ int ucrd_size; ++ u_char ucrd_data[1024]; /* filled data size will vary */ ++}; ++ ++typedef struct { u_int32_t cookie; } usb_event_cookie_t; ++ ++#define USB_MAX_DEVNAMES 4 ++#define USB_MAX_DEVNAMELEN 16 ++struct usb_device_info { ++ u_int8_t udi_bus; ++ u_int8_t udi_addr; /* device address */ ++ usb_event_cookie_t udi_cookie; ++ char udi_product[USB_MAX_STRING_LEN]; ++ char udi_vendor[USB_MAX_STRING_LEN]; ++ char udi_release[8]; ++ u_int16_t udi_productNo; ++ u_int16_t udi_vendorNo; ++ u_int16_t udi_releaseNo; ++ u_int8_t udi_class; ++ u_int8_t udi_subclass; ++ u_int8_t udi_protocol; ++ u_int8_t udi_config; ++ u_int8_t udi_speed; ++#define USB_SPEED_UNKNOWN 0 ++#define USB_SPEED_LOW 1 ++#define USB_SPEED_FULL 2 ++#define USB_SPEED_HIGH 3 ++#define USB_SPEED_VARIABLE 4 ++#define USB_SPEED_SUPER 5 ++ int udi_power; /* power consumption in mA, 0 if selfpowered */ ++ int udi_nports; ++ char udi_devnames[USB_MAX_DEVNAMES][USB_MAX_DEVNAMELEN]; ++ u_int8_t udi_ports[16];/* hub only: addresses of devices on ports */ ++#define USB_PORT_ENABLED 0xff ++#define USB_PORT_SUSPENDED 0xfe ++#define USB_PORT_POWERED 0xfd ++#define USB_PORT_DISABLED 0xfc ++}; ++ ++struct usb_ctl_report { ++ int ucr_report; ++ u_char ucr_data[1024]; /* filled data size will vary */ ++}; ++ ++struct usb_device_stats { ++ u_long uds_requests[4]; /* indexed by transfer type UE_* */ ++}; ++ ++#define WUSB_MIN_IE 0x80 ++#define WUSB_WCTA_IE 0x80 ++#define WUSB_WCONNECTACK_IE 0x81 ++#define WUSB_WHOSTINFO_IE 0x82 ++#define WUHI_GET_CA(_bmAttributes_) ((_bmAttributes_) & 0x3) ++#define WUHI_CA_RECONN 0x00 ++#define WUHI_CA_LIMITED 0x01 ++#define WUHI_CA_ALL 0x03 ++#define WUHI_GET_MLSI(_bmAttributes_) (((_bmAttributes_) & 0x38) >> 3) ++#define WUSB_WCHCHANGEANNOUNCE_IE 0x83 ++#define WUSB_WDEV_DISCONNECT_IE 0x84 ++#define WUSB_WHOST_DISCONNECT_IE 0x85 ++#define WUSB_WRELEASE_CHANNEL_IE 0x86 ++#define WUSB_WWORK_IE 0x87 ++#define WUSB_WCHANNEL_STOP_IE 0x88 ++#define WUSB_WDEV_KEEPALIVE_IE 0x89 ++#define WUSB_WISOCH_DISCARD_IE 0x8A ++#define WUSB_WRESETDEVICE_IE 0x8B ++#define WUSB_WXMIT_PACKET_ADJUST_IE 0x8C ++#define WUSB_MAX_IE 0x8C ++ ++/* Device Notification Types */ ++ ++#define WUSB_DN_MIN 0x01 ++#define WUSB_DN_CONNECT 0x01 ++# define WUSB_DA_OLDCONN 0x00 ++# define WUSB_DA_NEWCONN 0x01 ++# define WUSB_DA_SELF_BEACON 0x02 ++# define WUSB_DA_DIR_BEACON 0x04 ++# define WUSB_DA_NO_BEACON 0x06 ++#define WUSB_DN_DISCONNECT 0x02 ++#define WUSB_DN_EPRDY 0x03 ++#define WUSB_DN_MASAVAILCHANGED 0x04 ++#define WUSB_DN_REMOTEWAKEUP 0x05 ++#define WUSB_DN_SLEEP 0x06 ++#define WUSB_DN_ALIVE 0x07 ++#define WUSB_DN_MAX 0x07 ++ ++#ifdef _MSC_VER ++#include <pshpack1.h> ++#endif ++ ++/* WUSB Handshake Data. Used during the SET/GET HANDSHAKE requests */ ++typedef struct wusb_hndshk_data { ++ uByte bMessageNumber; ++ uByte bStatus; ++ uByte tTKID[3]; ++ uByte bReserved; ++ uByte CDID[16]; ++ uByte Nonce[16]; ++ uByte MIC[8]; ++} UPACKED wusb_hndshk_data_t; ++#define WUSB_HANDSHAKE_LEN_FOR_MIC 38 ++ ++/* WUSB Connection Context */ ++typedef struct wusb_conn_context { ++ uByte CHID [16]; ++ uByte CDID [16]; ++ uByte CK [16]; ++} UPACKED wusb_conn_context_t; ++ ++/* WUSB Security Descriptor */ ++typedef struct wusb_security_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord wTotalLength; ++ uByte bNumEncryptionTypes; ++} UPACKED wusb_security_desc_t; ++ ++/* WUSB Encryption Type Descriptor */ ++typedef struct wusb_encrypt_type_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ ++ uByte bEncryptionType; ++#define WUETD_UNSECURE 0 ++#define WUETD_WIRED 1 ++#define WUETD_CCM_1 2 ++#define WUETD_RSA_1 3 ++ ++ uByte bEncryptionValue; ++ uByte bAuthKeyIndex; ++} UPACKED wusb_encrypt_type_desc_t; ++ ++/* WUSB Key Descriptor */ ++typedef struct wusb_key_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte tTKID[3]; ++ uByte bReserved; ++ uByte KeyData[1]; /* variable length */ ++} UPACKED wusb_key_desc_t; ++ ++/* WUSB BOS Descriptor (Binary device Object Store) */ ++typedef struct wusb_bos_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord wTotalLength; ++ uByte bNumDeviceCaps; ++} UPACKED wusb_bos_desc_t; ++ ++#define USB_DEVICE_CAPABILITY_20_EXTENSION 0x02 ++typedef struct usb_dev_cap_20_ext_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDevCapabilityType; ++#define USB_20_EXT_LPM 0x02 ++ uDWord bmAttributes; ++} UPACKED usb_dev_cap_20_ext_desc_t; ++ ++#define USB_DEVICE_CAPABILITY_SS_USB 0x03 ++typedef struct usb_dev_cap_ss_usb { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDevCapabilityType; ++#define USB_DC_SS_USB_LTM_CAPABLE 0x02 ++ uByte bmAttributes; ++#define USB_DC_SS_USB_SPEED_SUPPORT_LOW 0x01 ++#define USB_DC_SS_USB_SPEED_SUPPORT_FULL 0x02 ++#define USB_DC_SS_USB_SPEED_SUPPORT_HIGH 0x04 ++#define USB_DC_SS_USB_SPEED_SUPPORT_SS 0x08 ++ uWord wSpeedsSupported; ++ uByte bFunctionalitySupport; ++ uByte bU1DevExitLat; ++ uWord wU2DevExitLat; ++} UPACKED usb_dev_cap_ss_usb_t; ++ ++#define USB_DEVICE_CAPABILITY_CONTAINER_ID 0x04 ++typedef struct usb_dev_cap_container_id { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDevCapabilityType; ++ uByte bReserved; ++ uByte containerID[16]; ++} UPACKED usb_dev_cap_container_id_t; ++ ++/* Device Capability Type Codes */ ++#define WUSB_DEVICE_CAPABILITY_WIRELESS_USB 0x01 ++ ++/* Device Capability Descriptor */ ++typedef struct wusb_dev_cap_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDevCapabilityType; ++ uByte caps[1]; /* Variable length */ ++} UPACKED wusb_dev_cap_desc_t; ++ ++/* Device Capability Descriptor */ ++typedef struct wusb_dev_cap_uwb_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDevCapabilityType; ++ uByte bmAttributes; ++ uWord wPHYRates; /* Bitmap */ ++ uByte bmTFITXPowerInfo; ++ uByte bmFFITXPowerInfo; ++ uWord bmBandGroup; ++ uByte bReserved; ++} UPACKED wusb_dev_cap_uwb_desc_t; ++ ++/* Wireless USB Endpoint Companion Descriptor */ ++typedef struct wusb_endpoint_companion_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bMaxBurst; ++ uByte bMaxSequence; ++ uWord wMaxStreamDelay; ++ uWord wOverTheAirPacketSize; ++ uByte bOverTheAirInterval; ++ uByte bmCompAttributes; ++} UPACKED wusb_endpoint_companion_desc_t; ++ ++/* Wireless USB Numeric Association M1 Data Structure */ ++typedef struct wusb_m1_data { ++ uByte version; ++ uWord langId; ++ uByte deviceFriendlyNameLength; ++ uByte sha_256_m3[32]; ++ uByte deviceFriendlyName[256]; ++} UPACKED wusb_m1_data_t; ++ ++typedef struct wusb_m2_data { ++ uByte version; ++ uWord langId; ++ uByte hostFriendlyNameLength; ++ uByte pkh[384]; ++ uByte hostFriendlyName[256]; ++} UPACKED wusb_m2_data_t; ++ ++typedef struct wusb_m3_data { ++ uByte pkd[384]; ++ uByte nd; ++} UPACKED wusb_m3_data_t; ++ ++typedef struct wusb_m4_data { ++ uDWord _attributeTypeIdAndLength_1; ++ uWord associationTypeId; ++ ++ uDWord _attributeTypeIdAndLength_2; ++ uWord associationSubTypeId; ++ ++ uDWord _attributeTypeIdAndLength_3; ++ uDWord length; ++ ++ uDWord _attributeTypeIdAndLength_4; ++ uDWord associationStatus; ++ ++ uDWord _attributeTypeIdAndLength_5; ++ uByte chid[16]; ++ ++ uDWord _attributeTypeIdAndLength_6; ++ uByte cdid[16]; ++ ++ uDWord _attributeTypeIdAndLength_7; ++ uByte bandGroups[2]; ++} UPACKED wusb_m4_data_t; ++ ++#ifdef _MSC_VER ++#include <poppack.h> ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _USB_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/Kconfig b/drivers/usb/gadget/udc/hiudc3/Kconfig +new file mode 100644 +index 0000000..dc4c087 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/Kconfig +@@ -0,0 +1,44 @@ ++# ++# hisilicon usb2 device controller version 3.00a ++# ++ ++menuconfig HI_SS_DEVICE ++ tristate "hisilicon usb3 device controller version 2.50a driver" ++ default n ++ select USB_GADGET_DUALSPEED ++ select USB_GADGET_SELECTED ++ help ++ hisilicon usb3 device controller version 3.00a. ++ ++if HI_SS_DEVICE ++ ++config HIUSB_XHCI_REG_BASE_ADDRESS ++ hex "hiusb3dev system control register base address" ++ default "0xf98a0000" if ARCH_S40 ++ default "0x60180000" if ARCH_GODBOX ++ default "0x10180000" if ARCH_HI3519 ++ default "0x10180000" if ARCH_HI3519V101 ++ default "0x10180000" if ARCH_HI3559 ++ default "0x10180000" if ARCH_HI3556 ++ default "0x10180000" if ARCH_HI3516AV200 ++ ++config HIUSB_XHCI_REG_BASE_ADDRESS_LEN ++ hex "hiusb3dev system control register size length" ++ default "0x10000" if ARCH_S40 ++ default "0x10000" if ARCH_GODBOX ++ default "0x10000" if ARCH_HI3519 ++ default "0x10000" if ARCH_HI3519V101 ++ default "0x10000" if ARCH_HI3559 ++ default "0x10000" if ARCH_HI3556 ++ default "0x10000" if ARCH_HI3516AV200 ++ ++config HIUSB_XHCI_IRQ_NUMBER ++ int "hiusb3dev system control register interrupt number" ++ default "100" if ARCH_S40 ++ default "103" if ARCH_GODBOX ++ default "54" if ARCH_HI3519 ++ default "54" if ARCH_HI3519V101 ++ default "54" if ARCH_HI3559 ++ default "54" if ARCH_HI3556 ++ default "54" if ARCH_HI3516AV200 ++endif # HI_SS_DEVICE +diff --git a/drivers/usb/gadget/udc/hiudc3/Makefile b/drivers/usb/gadget/udc/hiudc3/Makefile +new file mode 100644 +index 0000000..094fdbd +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/Makefile +@@ -0,0 +1,99 @@ ++# ++# Makefile for DWC_usb3 SuperSpeed USB controller driver ++# ++ ++ifneq ($(KERNELRELEASE),) ++ ++# This line is required for 2.6.37 era kernels, or else files in ++# subdirectories won't compile ++EXTRA_CFLAGS += -I$(KBUILD_EXTMOD) ++ ++ifneq ($(NOOS),) ++EXTRA_CFLAGS += -DLINUXTEST ++else ++ifneq ($(SELA),) ++EXTRA_CFLAGS += -DLINUXTEST ++else ++ifneq ($(PLATFORM),) ++EXTRA_CFLAGS += -DDWC_PLATFORM_DEV ++endif ++endif ++endif ++ ++# Uncomment these for debug messages ++EXTRA_CFLAGS += -DDEBUG ++EXTRA_CFLAGS += -DVERBOSE ++EXTRA_CFLAGS += -DDEBUG_EP0 ++ ++# Uncomment this, and comment out the previous 3, for minimal debug for ISOC ++#EXTRA_CFLAGS += -DISOC_DEBUG ++ ++# Uncomment to force gasket regs to be accessed in BAR 2. Not needed if the ++# PCI device ID is 0xabce. ++#EXTRA_CFLAGS += -DDWC_BAR2_GASKET_REG ++ ++# Uncomment for OTG ++#EXTRA_CFLAGS += -DCONFIG_USB_OTG_DWC ++ ++# Uncomment for SSIC ++#EXTRA_CFLAGS += -DSSIC ++ ++# Uncomment this to support the UTE gadget, and also uncomment the line with ++# "dwc_usb3-objs += linux/ute_if.o" below ++#EXTRA_CFLAGS += -DDWC_UTE -I$(KBUILD_SRC)/../UTE-3.0/common -I$(KBUILD_EXTMOD)/../UTE-3.0/common ++ ++#EXTRA_CFLAGS += -DDWC_ISOC_INTR_MODERATION ++#EXTRA_CFLAGS += -DDWC_TEST_ISOC_CHAIN ++#EXTRA_CFLAGS += -DLECROY ++ ++#EXTRA_CFLAGS += -DDWC_STAR_9000446947_WORKAROUND ++#EXTRA_CFLAGS += -DDWC_STAR_9000449814_WORKAROUND ++#EXTRA_CFLAGS += -DDWC_STAR_9000459034_WORKAROUND ++#EXTRA_CFLAGS += -DDWC_STAR_9000463548_WORKAROUND ++#EXTRA_CFLAGS += -DDWC_STAR_9000468158_WORKAROUND ++#EXTRA_CFLAGS += -DDWC_STAR_9000483510_WORKAROUND ++ ++#obj-m := dwc_usb3.o ++obj-$(CONFIG_USB_HISI_UDC3) := dwc_usb3.o ++ ++dwc_usb3-objs := cil.o cil_intr.o ++dwc_usb3-objs += pcd.o pcd_intr.o pcd_hiber.o ep0.o ++#dwc_usb3-objs += hiusb3.o ++ ++ifneq ($(NOOS),) ++dwc_usb3-objs += no_os/no_os_init.o no_os/no_os_hiber.o no_os/no_os_ep0.o ++dwc_usb3-objs += no_os/no_os_gadget.o no_os/no_os_src_sink_lpbk.o ++else ++ifneq ($(SELA),) ++dwc_usb3-objs += sela/sela_bm_defs.o sela/sela_driver.o sela/sela_ep0.o ++dwc_usb3-objs += sela/sela_funcs.o sela/sela_gadget.o ++else ++dwc_usb3-objs += linux_gadget.o linux_hiber.o linux_sysfs.o ++#ifneq ($(PLATFORM),) ++dwc_usb3-objs += linux_plat.o ++#else ++#dwc_usb3-objs += linux/linux_pci.o ++#endif ++endif ++endif ++ ++# Uncomment this to support the UTE gadget, and also uncomment the line with ++# "-DDWC_UTE" above ++#dwc_usb3-objs += linux/ute_if.o ++ ++else ++endif ++ ++#ifneq ($(NOOS),) ++# ln -s no_os/no_os_defs.h os_defs.h ++# ln -s no_os/no_os_dev.h os_dev.h ++#else ++#ifneq ($(SELA),) ++# ln -s sela/sela_defs.h os_defs.h ++# ln -s sela/sela_dev.h os_dev.h ++#else ++# ln -s linux/linux_defs.h os_defs.h ++# ln -s linux/linux_dev.h os_dev.h ++# ln -s linux/ute_if.h ute_if.h ++#endif ++#endif +diff --git a/drivers/usb/gadget/udc/hiudc3/Makefile.no_os b/drivers/usb/gadget/udc/hiudc3/Makefile.no_os +new file mode 100644 +index 0000000..b0074d4 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/Makefile.no_os +@@ -0,0 +1,84 @@ ++# ++# Makefile for DWC_usb3 SuperSpeed USB controller driver ++# ++ ++CFLAGS := -O2 -Wall -Wno-pointer-sign -g -I. ++LDFLAGS := -g ++ ++CFLAGS += -DTESTCOMPILE ++ ++# Uncomment these for debug messages ++CFLAGS += -DDEBUG ++CFLAGS += -DVERBOSE ++CFLAGS += -DDEBUG_EP0 ++ ++# Uncomment this, and comment out the previous 3, for minimal debug for ISOC ++#CFLAGS += -DISOC_DEBUG ++ ++# Uncomment this to enable Bulk Streams, to support the UASP gadget ++CFLAGS += -DDWC_UASP_GADGET_STREAMS ++ ++# Uncomment this to support the UTE gadget, and also uncomment the line with ++# "dwc_usb3-objs += ute_if.o" below ++#CFLAGS += -DDWC_UTE -I$(KBUILD_SRC)/../UTE-3.0/common ++ ++#CFLAGS += -DDWC_ISOC_INTR_MODERATION ++#CFLAGS += -DDWC_TEST_ISOC_CHAIN ++#CFLAGS += -DLECROY ++ ++#CFLAGS += -DDWC_STAR_9000446947_WORKAROUND ++#CFLAGS += -DDWC_STAR_9000449814_WORKAROUND ++#CFLAGS += -DDWC_STAR_9000459034_WORKAROUND ++#CFLAGS += -DDWC_STAR_9000463548_WORKAROUND ++#CFLAGS += -DDWC_STAR_9000468158_WORKAROUND ++#CFLAGS += -DDWC_STAR_9000483510_WORKAROUND ++ ++obj-m := dwc_usb3 ++ ++dwc_usb3-objs := cil.o cil_intr.o ++dwc_usb3-objs += pcd.o pcd_intr.o pcd_hiber.o ep0.o ++dwc_usb3-objs += no_os/no_os_init.o no_os/no_os_hiber.o no_os/no_os_ep0.o ++dwc_usb3-objs += no_os/no_os_gadget.o no_os/no_os_src_sink_lpbk.o ++ ++# Uncomment this to support the UTE gadget, and also uncomment the line with ++# "-DDWC_UTE" above ++#dwc_usb3-objs += ute_if.o ++ ++PWD := $(shell pwd) ++ ++# Command paths ++CTAGS := /usr/bin/ctags ++DOXYGEN := /depot/doxygen-1.5.8/bin/doxygen ++ ++default: all ++ ++docs: ++ cd doc ; \ ++ $(DOXYGEN) ; \ ++ cp doxygen.sty latex/ ; \ ++ cp synopsys.eps latex/ ; \ ++ cp synopsys.pdf latex/ ; \ ++ make -C latex/ ; \ ++ cd .. ++ ++tags: $(wildcard *.[ch]) ++ $(CTAGS) -e $(wildcard *.[ch]) $(wildcard linux/*.[ch]) \ ++ $(wildcard $(KDIR)/include/linux/usb*.h) ++ ++driver: links $(obj-m) ++ ++$(obj-m): $(dwc_usb3-objs) ++ $(CC) $(LDFLAGS) -o $(obj-m) $(dwc_usb3-objs) ++ ++links: ++ rm -f os_defs.h os_dev.h ++ ln -s no_os/no_os_defs.h os_defs.h ++ ln -s no_os/no_os_dev.h os_dev.h ++ ++clean: ++ rm -rf $(obj-m) *.o no_os/*.o ++ ++cleanall: clean ++ rm -rf doc/html doc/latex doc/rtf ++ ++all: driver +diff --git a/drivers/usb/gadget/udc/hiudc3/cil.c b/drivers/usb/gadget/udc/hiudc3/cil.c +new file mode 100644 +index 0000000..66601ac +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/cil.c +@@ -0,0 +1,2066 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/cil.c $ ++ * $Revision: #110 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The Core Interface Layer provides basic services for accessing and ++ * managing the DWC_usb3 hardware. These services are used by both the ++ * Peripheral Controller Driver and the On The Go Driver. ++ * ++ * The CIL manages the memory map for the core so that the PCD and OTG drivers ++ * don't have to do this separately. The CIL also performs basic services ++ * that are not specific to either the Device or OTG modes of operation. ++ * These services include all functionality that requires specific ++ * knowledge of the CSR layout or the DMA descriptor (TRB) layout. Also ++ * included are services for invoking each of the commands provided by ++ * the DGCMD and DEPCMD registers (see the "Control and Status Registers" ++ * chapter of the USB3 controller databook for details). ++ * ++ * The Core Interface Layer has the following requirements: ++ * - Provides basic controller operations. ++ * - Minimal use of OS services. ++ * - The OS services used will be abstracted by using inline functions ++ * or macros. ++ * ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++/** ++ * Fill in the four dwords of a DMA descriptor (aka a TRB). ++ */ ++void dwc_usb3_fill_desc(dwc_usb3_dma_desc_t *desc, dwc_dma_t dma_addr, ++ u32 dma_len, u32 stream, u32 type, ++ u32 ctrlbits, int own) ++{ ++ desc->bptl = (u32)(dma_addr & 0xffffffffU); ++#ifdef DWC_64_BIT_ARCH ++ desc->bpth = (u32)(dma_addr >> 32U & 0xffffffffU); ++#else ++ desc->bpth = 0; ++#endif ++ desc->status = dma_len << DWC_DSCSTS_XFRCNT_SHIFT; ++ ++ /* Note: If type is 0, leave original control bits intact (for isoc) */ ++ if (type) ++ desc->control = type << DWC_DSCCTL_TRBCTL_SHIFT; ++ ++ desc->control |= stream << DWC_DSCCTL_STRMID_SOFN_SHIFT | ctrlbits; ++ ++ /* Must do this last! */ ++ if (own) { ++ wmb(); ++ desc->control |= DWC_DSCCTL_HWO_BIT; ++ } ++} ++ ++/** ++ * Make a DMA descriptor the start of a chain by setting its CHN bit and ++ * clearing its IOC bit. ++ * ++ * @param desc The DMA descriptor (TRB) to operate on. ++ */ ++void dwc_usb3_start_desc_chain(dwc_usb3_dma_desc_t *desc) ++{ ++ desc->control |= DWC_DSCCTL_CHN_BIT; ++ desc->control &= ~DWC_DSCCTL_IOC_BIT; ++} ++ ++/** ++ * Make a DMA descriptor the end of a chain by clearing its CHN bit and ++ * setting its IOC bit. ++ * ++ * @param desc The DMA descriptor (TRB) to operate on. ++ */ ++void dwc_usb3_end_desc_chain(dwc_usb3_dma_desc_t *desc) ++{ ++ desc->control &= ~DWC_DSCCTL_CHN_BIT; ++ desc->control |= DWC_DSCCTL_IOC_BIT; ++} ++ ++/** ++ * Enable a DMA descriptor by setting its HWO bit. ++ * ++ * @param desc The DMA descriptor (TRB) to operate on. ++ */ ++void dwc_usb3_enable_desc(dwc_usb3_dma_desc_t *desc) ++{ ++ wmb(); ++ desc->control |= DWC_DSCCTL_HWO_BIT; ++} ++ ++/** ++ * Disable a DMA descriptor by clearing its HWO bit. ++ * ++ * NOTE: This must only be called if it is known that the hardware has finished ++ * with the DMA descriptor, but for some reason the hardware has not cleared ++ * the HWO bit. ++ * ++ * @param desc The DMA descriptor (TRB) to operate on. ++ */ ++void dwc_usb3_disable_desc(dwc_usb3_dma_desc_t *desc) ++{ ++ desc->control &= ~DWC_DSCCTL_HWO_BIT; ++ wmb(); ++} ++ ++/** ++ * Spin on register bit until handshake completes or times out. ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ * @param ptr Address of register to read. ++ * @param mask Bit to look at in result of read. ++ * @param done Value of the bit when handshake succeeds. ++ * @return 1 when the mask bit has the specified value (handshake done), ++ * 0 when timeout has passed (handshake failed). ++ */ ++static int _handshake(dwc_usb3_device_t *dev, volatile u32 __iomem *ptr, ++ u32 mask, u32 done) ++{ ++ u32 usec = 100000; ++ u32 result; ++ ++ dwc_debug1(dev, "%s()\n", __func__); ++ ++ do { ++ result = dwc_rd32(dev, ptr); ++ if ((result & mask) == done) { ++ dwc_debug1(dev, "after DEPCMD=%08x\n", result); ++ return 1; ++ } ++ ++ dwc_udelay(dev, 1); ++ usec -= 1; ++ } while (usec > 0); ++ ++ return 0; ++} ++ ++/* ++ * Routines for sending the various Device commands to the hardware. ++ * See description of DGCMD register in "Control and Status Registers" ++ * section of the USB3 controller databook. ++ */ ++ ++/** ++ * Send TRANSMIT FUNCTION WAKE DEVICE NOTIFICATION command to Device ++ */ ++int dwc_usb3_xmit_fn_remote_wake(dwc_usb3_pcd_t *pcd, u32 intf) ++{ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ ++ /* Use the generic Transmit Device Notification command if the core ++ * supports it ++ */ ++ if (pcd->usb3_dev->snpsid >= 0x5533210a) { ++ /* Set param */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmdpar, ++ intf << DWC_DGCMDPAR_DEV_NOTIF_PARAM_SHIFT | ++ DWC_DGCMD_FUNCTION_WAKE_DEV_NOTIF); ++ dwc_debug1(pcd->usb3_dev, "DGCMDPAR=%08x\n", ++ intf << DWC_DGCMDPAR_DEV_NOTIF_PARAM_SHIFT | ++ DWC_DGCMD_FUNCTION_WAKE_DEV_NOTIF); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmd, ++ DWC_DGCMD_XMIT_DEV_NOTIF | DWC_DGCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DGCMD=%08x\n", ++ DWC_DGCMD_XMIT_DEV_NOTIF | DWC_DGCMD_ACT_BIT); ++ } else { ++ /* Set param */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmdpar, intf); ++ dwc_debug1(pcd->usb3_dev, "DGCMDPAR=%08x\n", intf); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmd, ++ DWC_DGCMD_XMIT_FUNC_WAKE_DEV_NOTIF | DWC_DGCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DGCMD=%08x\n", ++ DWC_DGCMD_XMIT_FUNC_WAKE_DEV_NOTIF | DWC_DGCMD_ACT_BIT); ++ } ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &pcd->dev_global_regs->dgcmd, ++ DWC_DGCMD_ACT_BIT, 0); ++ ++ return 0; ++} ++ ++/** ++ * Send LATENCY TOLERANCE MESSAGE DEVICE NOTIFICATION command to Device ++ */ ++int dwc_usb3_xmit_ltm(dwc_usb3_pcd_t *pcd, u32 value) ++{ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ ++ /* Use the generic Transmit Device Notification command if the core ++ * supports it. Otherwise this function is a no-op. ++ */ ++ if (pcd->usb3_dev->snpsid >= 0x5533210a) { ++ /* Set param */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmdpar, ++ value << DWC_DGCMDPAR_DEV_NOTIF_PARAM_SHIFT | ++ DWC_DGCMD_LATENCY_TOL_DEV_NOTIF); ++ dwc_debug1(pcd->usb3_dev, "DGCMDPAR=%08x\n", ++ value << DWC_DGCMDPAR_DEV_NOTIF_PARAM_SHIFT | ++ DWC_DGCMD_LATENCY_TOL_DEV_NOTIF); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmd, ++ DWC_DGCMD_XMIT_DEV_NOTIF | DWC_DGCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DGCMD=%08x\n", ++ DWC_DGCMD_XMIT_DEV_NOTIF | DWC_DGCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &pcd->dev_global_regs->dgcmd, ++ DWC_DGCMD_ACT_BIT, 0); ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_USB_OTG_DWC ++/** ++ * Send ROLE REQUEST DEVICE NOTIFICATION command to Device ++ */ ++int dwc_usb3_xmit_host_role_request(dwc_usb3_pcd_t *pcd, u32 param) ++{ ++#ifdef DEBUG ++ char *type = "UNKNOWN"; ++ ++ if (param == DWC_DGCMDPAR_HOST_ROLE_REQ_INITIATE) ++ type = "INITIATE"; ++ else if (param == DWC_DGCMDPAR_HOST_ROLE_REQ_CONFIRM) ++ type = "CONFIRM"; ++ ++ dwc_debug2(pcd->usb3_dev, "%s() - %s\n", __func__, type); ++#endif ++ ++ /* Use the generic Transmit Device Notification command if the core ++ * supports it ++ */ ++ if (pcd->usb3_dev->snpsid >= 0x5533210a) { ++ /* Set param */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmdpar, ++ param << DWC_DGCMDPAR_DEV_NOTIF_PARAM_SHIFT | ++ DWC_DGCMD_HOST_ROLE_REQ_DEV_NOTIF); ++ dwc_debug1(pcd->usb3_dev, "DGCMDPAR=%08x\n", ++ param << DWC_DGCMDPAR_DEV_NOTIF_PARAM_SHIFT | ++ DWC_DGCMD_HOST_ROLE_REQ_DEV_NOTIF); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmd, ++ DWC_DGCMD_XMIT_DEV_NOTIF | DWC_DGCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DGCMD=%08x\n", ++ DWC_DGCMD_XMIT_DEV_NOTIF | DWC_DGCMD_ACT_BIT); ++ } else { ++ /* Set param */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmdpar, param); ++ dwc_debug1(pcd->usb3_dev, "DGCMDPAR=%08x\n", param); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmd, ++ DWC_DGCMD_XMIT_HOST_ROLE_REQUEST | DWC_DGCMD_ACT_BIT); ++ ++ dwc_debug1(pcd->usb3_dev, "DGCMD=%08x\n", ++ DWC_DGCMD_XMIT_HOST_ROLE_REQUEST | DWC_DGCMD_ACT_BIT); ++ } ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &pcd->dev_global_regs->dgcmd, ++ DWC_DGCMD_ACT_BIT, 0); ++ ++ return 0; ++} ++#endif ++ ++/** ++ * Send SET SCRATCHPAD BUFFER ARRAY command to Device ++ */ ++int dwc_usb3_set_scratchpad_buf_array(dwc_usb3_pcd_t *pcd, dwc_dma_t dma_addr) ++{ ++ dwc_debug2(pcd->usb3_dev, "%s(%lx)\n", __func__, ++ (unsigned long)dma_addr); ++ ++ /* Set param */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmdpar, ++ dma_addr & 0xffffffffU); ++ dwc_debug1(pcd->usb3_dev, "DGCMDPAR=%08lx\n", ++ (unsigned long)(dma_addr & 0xffffffffU)); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmd, ++ DWC_DGCMD_SET_SCRATCHPAD_ARRAY_ADR_LO | DWC_DGCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DGCMD=%08x\n", ++ DWC_DGCMD_SET_SCRATCHPAD_ARRAY_ADR_LO | DWC_DGCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &pcd->dev_global_regs->dgcmd, ++ DWC_DGCMD_ACT_BIT, 0); ++ ++ return 0; ++} ++ ++/** ++ * Send SELECTED FIFO FLUSH command to Device ++ */ ++int dwc_usb3_flush_fifo(dwc_usb3_pcd_t *pcd, u32 fifo_sel) ++{ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ ++ /* Set param */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmdpar, fifo_sel); ++ dwc_debug1(pcd->usb3_dev, "DGCMDPAR=%08x\n", fifo_sel); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dgcmd, ++ DWC_DGCMD_SELECTED_FIFO_FLUSH | DWC_DGCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DGCMD=%08x\n", ++ DWC_DGCMD_SELECTED_FIFO_FLUSH | DWC_DGCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &pcd->dev_global_regs->dgcmd, ++ DWC_DGCMD_ACT_BIT, 0); ++ ++ return 0; ++} ++ ++/* ++ * Routines for sending the various Endpoint commands to the hardware. ++ * See description of DEPCMDn register in "Control and Status Registers" ++ * section of the USB3 controller databook. ++ */ ++ ++/** ++ * Send DEPCFG command to EP ++ */ ++int dwc_usb3_dep_cfg(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ u32 depcfg0, u32 depcfg1, u32 depcfg2) ++{ ++ dwc_debug1(pcd->usb3_dev, "dep_cfg, ep_reg=%lx\n", ++ (unsigned long)ep_reg); ++ ++ /* Set param 2 */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmdpar2, depcfg2); ++ dwc_debug1(pcd->usb3_dev, "DEPCFG2=%08x\n", depcfg2); ++ ++ /* Set param 1 */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmdpar1, depcfg1); ++ dwc_debug1(pcd->usb3_dev, "DEPCFG1=%08x\n", depcfg1); ++ ++ /* Set param 0 */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmdpar0, depcfg0); ++ dwc_debug1(pcd->usb3_dev, "DEPCFG0=%08x\n", depcfg0); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmd, ++ DWC_EPCMD_SET_EP_CFG | DWC_EPCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DEPCMD=%08x\n", ++ DWC_EPCMD_SET_EP_CFG | DWC_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, DWC_EPCMD_ACT_BIT, 0); ++ ++ return 0; ++} ++ ++/** ++ * Send DEPXFERCFG command to EP ++ */ ++int dwc_usb3_dep_xfercfg(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ u32 depstrmcfg) ++{ ++ dwc_debug1(pcd->usb3_dev, "dep_xfercfg, ep_reg=%lx\n", ++ (unsigned long)ep_reg); ++ ++ /* Set param 0 */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmdpar0, depstrmcfg); ++ dwc_debug1(pcd->usb3_dev, "DEPSTRMCFG=%08x\n", depstrmcfg); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmd, ++ DWC_EPCMD_SET_XFER_CFG | DWC_EPCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DEPCMD=%08x\n", ++ DWC_EPCMD_SET_XFER_CFG | DWC_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, DWC_EPCMD_ACT_BIT, 0); ++ ++ return 0; ++} ++ ++/** ++ * Send DEPGETEPSTATE command to EP ++ */ ++u32 dwc_usb3_dep_getepstate(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg) ++{ ++ u32 retval; ++ ++ dwc_debug1(pcd->usb3_dev, "dep_getepstate, ep_reg=%lx\n", ++ (unsigned long)ep_reg); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmd, ++ DWC_EPCMD_GET_EP_STATE | DWC_EPCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DEPCMD=%08x\n", ++ DWC_EPCMD_GET_EP_STATE | DWC_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, DWC_EPCMD_ACT_BIT, 0); ++ ++ /* Get state returned in DEPCMDPAR2 */ ++ retval = dwc_rd32(pcd->usb3_dev, &ep_reg->depcmdpar2); ++ ++ return retval; ++} ++ ++/** ++ * Send DEPSSTALL command to EP ++ */ ++int dwc_usb3_dep_sstall(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg) ++{ ++ dwc_debug1(pcd->usb3_dev, "dep_sstall, ep_reg=%lx\n", ++ (unsigned long)ep_reg); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmd, ++ DWC_EPCMD_SET_STALL | DWC_EPCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DEPCMD=%08x\n", ++ DWC_EPCMD_SET_STALL | DWC_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, DWC_EPCMD_ACT_BIT, 0); ++ ++ return 0; ++} ++ ++/** ++ * Send DEPCSTALL command to EP ++ */ ++int dwc_usb3_dep_cstall(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, int clr_pend) ++{ ++ u32 depcmd; ++ ++ dwc_debug1(pcd->usb3_dev, "dep_cstall, ep_reg=%lx\n", ++ (unsigned long)ep_reg); ++ ++ /* Fill clear stall command */ ++ depcmd = DWC_EPCMD_CLR_STALL | DWC_EPCMD_ACT_BIT; ++ if (clr_pend) ++ depcmd |= DWC_EPCMD_HP_FRM_BIT; ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmd, depcmd); ++ dwc_debug1(pcd->usb3_dev, "DEPCMD=%08x\n", depcmd); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, DWC_EPCMD_ACT_BIT, 0); ++ ++ return 0; ++} ++ ++/** ++ * Send DEPSTRTXFER command to EP ++ */ ++int dwc_usb3_dep_startxfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ dwc_dma_t dma_addr, u32 stream_or_uf) ++{ ++ u32 retval; ++ ++ dwc_debug1(pcd->usb3_dev, "dep_startxfer, ep_reg=%lx\n", ++ (unsigned long)ep_reg); ++ ++ /* Set param 1 */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmdpar1, dma_addr & 0xffffffffU); ++ dwc_debug1(pcd->usb3_dev, "TDADDRLO=%08lx\n", ++ (unsigned long)(dma_addr & 0xffffffffU)); ++ ++ /* Set param 0 */ ++#ifdef DWC_64_BIT_ARCH ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmdpar0, ++ dma_addr >> 32U & 0xffffffffU); ++ dwc_debug1(pcd->usb3_dev, "TDADDRHI=%08lx\n", ++ (unsigned long)(dma_addr >> 32U & 0xffffffffU)); ++#else ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmdpar0, 0); ++ dwc_debug1(pcd->usb3_dev, "TDADDRHI=%08x\n", 0); ++#endif ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmd, ++ stream_or_uf << DWC_EPCMD_STR_NUM_OR_UF_SHIFT | ++ DWC_EPCMD_START_XFER | DWC_EPCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DEPCMD=%08x\n", ++ stream_or_uf << DWC_EPCMD_STR_NUM_OR_UF_SHIFT | ++ DWC_EPCMD_START_XFER | DWC_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, DWC_EPCMD_ACT_BIT, 0); ++ ++ retval = dwc_rd32(pcd->usb3_dev, &ep_reg->depcmd); ++ ++ return retval >> DWC_EPCMD_XFER_RSRC_IDX_SHIFT & ++ DWC_EPCMD_XFER_RSRC_IDX_BITS >> DWC_EPCMD_XFER_RSRC_IDX_SHIFT; ++} ++ ++/** ++ * Send DEPUPDTXFER command to EP ++ */ ++int dwc_usb3_dep_updatexfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ u32 tri) ++{ ++ dwc_debug1(pcd->usb3_dev, "dep_updxfer, ep_reg=%lx\n", ++ (unsigned long)ep_reg); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmd, ++ tri << DWC_EPCMD_XFER_RSRC_IDX_SHIFT | ++ DWC_EPCMD_UPDATE_XFER | DWC_EPCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DEPCMD=%08x\n", ++ tri << DWC_EPCMD_XFER_RSRC_IDX_SHIFT | ++ DWC_EPCMD_UPDATE_XFER | DWC_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, DWC_EPCMD_ACT_BIT, 0); ++ ++ return 0; ++} ++ ++/** ++ * Send DEPENDXFER command to EP ++ */ ++int dwc_usb3_dep_endxfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ u32 tri, int flags, void *condition) ++{ ++ u32 depcmd; ++ ++ dwc_debug1(pcd->usb3_dev, "dep_endxfer, ep_reg=%lx\n", ++ (unsigned long)ep_reg); ++ ++ /* Fill end transfer command */ ++ depcmd = tri << DWC_EPCMD_XFER_RSRC_IDX_SHIFT | DWC_EPCMD_END_XFER; ++ depcmd |= DWC_EPCMD_ACT_BIT; ++ if (flags & DWC_ENDXFER_FORCE) ++ depcmd |= DWC_EPCMD_HP_FRM_BIT; ++ ++ /* Start the command. */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmd, depcmd); ++ dwc_debug1(pcd->usb3_dev, "DEPCMD=%08x\n", depcmd); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, DWC_EPCMD_ACT_BIT, 0); ++ if (!(flags & DWC_ENDXFER_NODELAY)) ++ dwc_udelay(dev, 100); ++ ++ return 0; ++} ++ ++#ifdef DWC_STAR_9000463548_WORKAROUND ++int dwc_usb3_dep_endxfer_nowait(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ u32 tri, int flags) ++{ ++ u32 depcmd; ++ ++ dwc_debug1(pcd->usb3_dev, "dep_endxfer_nowait, ep_reg=%lx\n", ++ (unsigned long)ep_reg); ++ ++ /* Fill end transfer command */ ++ depcmd = tri << DWC_EPCMD_XFER_RSRC_IDX_SHIFT | DWC_EPCMD_END_XFER; ++ depcmd |= DWC_EPCMD_ACT_BIT; ++ if (flags & DWC_ENDXFER_FORCE) ++ depcmd |= DWC_EPCMD_HP_FRM_BIT; ++ ++ /* Start the command. */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmd, depcmd); ++ dwc_debug1(pcd->usb3_dev, "DEPCMD=%08x\n", depcmd); ++ ++ return 0; ++} ++ ++int dwc_usb3_dep_wait_endxfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ void *condition) ++{ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, DWC_EPCMD_ACT_BIT, 0); ++ dwc_udelay(dev, 100); ++ ++ return 0; ++} ++#endif ++ ++/** ++ * Send DEPSTRTNEWCFG command to EP ++ */ ++int dwc_usb3_dep_startnewcfg(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ u32 rsrcidx) ++{ ++ dwc_debug1(pcd->usb3_dev, "dep_startnewcfg, ep_reg=%lx\n", ++ (unsigned long)ep_reg); ++ ++ /* Start the command */ ++ dwc_wr32(pcd->usb3_dev, &ep_reg->depcmd, ++ rsrcidx << DWC_EPCMD_XFER_RSRC_IDX_SHIFT | ++ DWC_EPCMD_START_NEW_CFG | DWC_EPCMD_ACT_BIT); ++ dwc_debug1(pcd->usb3_dev, "DEPCMD=%08x\n", ++ rsrcidx << DWC_EPCMD_XFER_RSRC_IDX_SHIFT | ++ DWC_EPCMD_START_NEW_CFG | DWC_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, DWC_EPCMD_ACT_BIT, 0); ++ ++ return 0; ++} ++ ++/**********************/ ++ ++/** ++ * Enable an EP in the DALEPENA register. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ep The EP to enable. ++ * @return 0 if succesful, -DWC_E_BUSY if already enabled. ++ */ ++int dwc_usb3_enable_ep(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep) ++{ ++ u32 ep_index_num, dalepena; ++ ++ ep_index_num = ep->dwc_ep.num * 2; ++ ++ if (ep->dwc_ep.is_in) ++ ep_index_num += 1; ++ ++ dalepena = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dalepena); ++ ++ /* If we have already enabled this EP, leave it alone ++ * (shouldn't happen) ++ */ ++ if (dalepena & 1 << ep_index_num) ++ return -DWC_E_BUSY; ++ ++ dalepena |= 1 << ep_index_num; ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dalepena, dalepena); ++ dwc_debug1(pcd->usb3_dev, "enable_ep: DALEPENA=%08x\n", dalepena); ++ return 0; ++} ++ ++/** ++ * Disable an EP in the DALEPENA register. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ep The EP to disable. ++ * @return 0 if succesful, -DWC_E_INVALID if already disabled. ++ */ ++int dwc_usb3_disable_ep(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep) ++{ ++ u32 ep_index_num, dalepena; ++ ++ ep_index_num = ep->dwc_ep.num * 2; ++ ++ if (ep->dwc_ep.is_in) ++ ep_index_num += 1; ++ ++ dalepena = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dalepena); ++ ++ /* If we have already disabled this EP, leave it alone ++ * (shouldn't happen) ++ */ ++ if (!(dalepena & 1 << ep_index_num)) ++ return -DWC_E_INVALID; ++ ++ dalepena &= ~(1 << ep_index_num); ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dalepena, dalepena); ++ dwc_debug1(pcd->usb3_dev, "disable_ep: DALEPENA=%08x\n", dalepena); ++ return 0; ++} ++ ++/** ++ * Get the device speed from the device status register and convert it ++ * to USB speed constant. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @return The device speed. ++ */ ++int dwc_usb3_get_device_speed(dwc_usb3_pcd_t *pcd) ++{ ++ u32 dsts; ++ int speed = USB_SPEED_UNKNOWN; ++ ++ dsts = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dsts); ++ ++ switch (dsts >> DWC_DSTS_CONNSPD_SHIFT & ++ DWC_DSTS_CONNSPD_BITS >> DWC_DSTS_CONNSPD_SHIFT) { ++ case DWC_SPEED_HS_PHY_30MHZ_OR_60MHZ: ++ dwc_debug0(pcd->usb3_dev, "HIGH SPEED\n"); ++ speed = USB_SPEED_HIGH; ++ break; ++ ++ case DWC_SPEED_FS_PHY_30MHZ_OR_60MHZ: ++ case DWC_SPEED_FS_PHY_48MHZ: ++ dwc_debug0(pcd->usb3_dev, "FULL SPEED\n"); ++ speed = USB_SPEED_FULL; ++ break; ++ ++ case DWC_SPEED_LS_PHY_6MHZ: ++ dwc_debug0(pcd->usb3_dev, "LOW SPEED\n"); ++ speed = USB_SPEED_LOW; ++ break; ++ ++ case DWC_SPEED_SS_PHY_125MHZ_OR_250MHZ: ++ dwc_debug0(pcd->usb3_dev, "SUPER SPEED\n"); ++ speed = USB_SPEED_SUPER; ++ break; ++ } ++ ++ return speed; ++} ++ ++/** ++ * Get the current microframe number. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @return The current microframe number. ++ */ ++int dwc_usb3_get_frame(dwc_usb3_pcd_t *pcd) ++{ ++ u32 dsts; ++ ++ /* read current frame/microframe number from DSTS register */ ++ dsts = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dsts); ++ ++ return dsts >> DWC_DSTS_SOF_FN_SHIFT & ++ DWC_DSTS_SOF_FN_BITS >> DWC_DSTS_SOF_FN_SHIFT; ++} ++ ++/** ++ * Get the current link state. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @return The current link state. ++ */ ++int dwc_usb3_pcd_get_link_state(dwc_usb3_pcd_t *pcd) ++{ ++ u32 status, state; ++ ++ status = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dsts); ++ state = status >> DWC_DSTS_USBLNK_STATE_SHIFT & ++ DWC_DSTS_USBLNK_STATE_BITS >> DWC_DSTS_USBLNK_STATE_SHIFT; ++ dwc_debug2(pcd->usb3_dev, "status=0x%08x state=%d\n", status, state); ++ ++ return state; ++} ++ ++/** ++ * Set state of USB link. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param state Link state to set. ++ */ ++void dwc_usb3_pcd_set_link_state(dwc_usb3_pcd_t *pcd, int state) ++{ ++ u32 dctl; ++ ++ dctl = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dctl); ++ dctl &= ~DWC_DCTL_ULST_CHNG_REQ_BITS; ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dctl, dctl); ++ ++ dctl = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dctl); ++ dctl &= ~DWC_DCTL_ULST_CHNG_REQ_BITS; ++ dctl |= state << DWC_DCTL_ULST_CHNG_REQ_SHIFT; ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dctl, dctl); ++} ++ ++/** ++ * Send a Remote Wakeup to the host. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param function Function that caused the remote wakeup. ++ */ ++void dwc_usb3_pcd_remote_wake(dwc_usb3_pcd_t *pcd, int function) ++{ ++ /* For USB 3.0, send function remote wake notification */ ++ if (pcd->speed == USB_SPEED_SUPER) ++ dwc_usb3_xmit_fn_remote_wake(pcd, function); ++} ++ ++/** ++ * Set the Device Address. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param addr The address to set. ++ */ ++void dwc_usb3_set_address(dwc_usb3_pcd_t *pcd, int addr) ++{ ++ u32 dcfg; ++ ++ dcfg = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dcfg); ++ dcfg &= ~DWC_DCFG_DEVADDR_BITS; ++ dcfg |= addr << DWC_DCFG_DEVADDR_SHIFT; ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dcfg, dcfg); ++} ++ ++/** ++ * Enable USB2 Phy suspend. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++static void dwc_usb3_ena_usb2_phy_suspend(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_usb3_device_t *dev = pcd->usb3_dev; ++ int hiber = dev->core_params->hibernate && ++ (dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT; ++ int writeit = 0; ++ u32 usb2phycfg; ++ ++ if (dev->core_params->phyctl || hiber || dev->core_params->lpmctl) { ++ usb2phycfg = dwc_rd32(dev, ++ &dev->core_global_regs->gusb2phycfg[0]); ++ ++ if ((dev->core_params->phyctl || hiber) && ++ !(usb2phycfg & DWC_USB2PHYCFG_SUS_PHY_BIT)) { ++ usb2phycfg |= DWC_USB2PHYCFG_SUS_PHY_BIT; ++ writeit = 1; ++ } ++ ++ if (dev->core_params->lpmctl && ++ !(usb2phycfg & DWC_USB2PHYCFG_ENBL_SLP_M_BIT)) { ++ usb2phycfg |= DWC_USB2PHYCFG_ENBL_SLP_M_BIT; ++ writeit = 1; ++ } ++ ++ if (writeit) ++ dwc_wr32(dev, &dev->core_global_regs->gusb2phycfg[0], ++ usb2phycfg); ++ } ++} ++ ++/** ++ * Disable USB2 Phy suspend. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++static void dwc_usb3_dis_usb2_phy_suspend(dwc_usb3_pcd_t *pcd) ++{ ++ u32 usb2phycfg; ++ ++ usb2phycfg = dwc_rd32(pcd->usb3_dev, ++ &pcd->usb3_dev->core_global_regs->gusb2phycfg[0]); ++ ++ if (usb2phycfg & (DWC_USB2PHYCFG_SUS_PHY_BIT | ++ DWC_USB2PHYCFG_ENBL_SLP_M_BIT)) { ++ usb2phycfg &= ~DWC_USB2PHYCFG_SUS_PHY_BIT; ++ usb2phycfg &= ~DWC_USB2PHYCFG_ENBL_SLP_M_BIT; ++ dwc_wr32(pcd->usb3_dev, ++ &pcd->usb3_dev->core_global_regs->gusb2phycfg[0], ++ usb2phycfg); ++ } ++} ++ ++/** ++ * Enable USB3 Phy suspend. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++static void dwc_usb3_ena_usb3_phy_suspend(dwc_usb3_pcd_t *pcd) ++{ ++#ifndef SELA_PLATFORM ++ u32 pipectl; ++ ++ pipectl = dwc_rd32(pcd->usb3_dev, ++ &pcd->usb3_dev->core_global_regs->gusb3pipectl[0]); ++ if (!(pipectl & DWC_PIPECTL_SUS_PHY_BIT)) { ++ pipectl |= DWC_PIPECTL_SUS_PHY_BIT; ++ dwc_wr32(pcd->usb3_dev, ++ &pcd->usb3_dev->core_global_regs->gusb3pipectl[0], ++ pipectl); ++ } ++#endif ++} ++ ++/** ++ * Disable USB3 Phy suspend. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++static void dwc_usb3_dis_usb3_phy_suspend(dwc_usb3_pcd_t *pcd) ++{ ++#ifndef SELA_PLATFORM ++ u32 pipectl; ++ ++ pipectl = dwc_rd32(pcd->usb3_dev, ++ &pcd->usb3_dev->core_global_regs->gusb3pipectl[0]); ++ if (pipectl & DWC_PIPECTL_SUS_PHY_BIT) { ++ pipectl &= ~DWC_PIPECTL_SUS_PHY_BIT; ++ dwc_wr32(pcd->usb3_dev, ++ &pcd->usb3_dev->core_global_regs->gusb3pipectl[0], ++ pipectl); ++ } ++#endif ++} ++ ++/** ++ * Enable USB2 Phy suspend if the device is connected at HS or FS. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++void dwc_usb3_ena_usb2_suspend(dwc_usb3_pcd_t *pcd) ++{ ++ if (pcd->speed == USB_SPEED_SUPER) ++ return; ++ ++ dwc_usb3_ena_usb2_phy_suspend(pcd); ++} ++ ++/** ++ * Disable USB2 Phy suspend if the device is connected at HS or FS. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++void dwc_usb3_dis_usb2_suspend(dwc_usb3_pcd_t *pcd) ++{ ++ if (pcd->speed == USB_SPEED_SUPER) ++ return; ++ ++ dwc_usb3_dis_usb2_phy_suspend(pcd); ++} ++ ++/** ++ * Enable the Device to accept U1 control commands from the Host. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++void dwc_usb3_accept_u1(dwc_usb3_pcd_t *pcd) ++{ ++ u32 dctl; ++ ++ dctl = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dctl); ++ dctl |= DWC_DCTL_ACCEPT_U1_EN_BIT; ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dctl, dctl); ++} ++ ++/** ++ * Enable the Device to accept U2 control commands from the Host. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++void dwc_usb3_accept_u2(dwc_usb3_pcd_t *pcd) ++{ ++#if 0 ++ u32 dctl; ++ ++ dctl = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dctl); ++ dctl |= DWC_DCTL_ACCEPT_U2_EN_BIT; ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dctl, dctl); ++#endif ++} ++ ++/** ++ * Enable U1 sleep. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++void dwc_usb3_enable_u1(dwc_usb3_pcd_t *pcd) ++{ ++ u32 dctl; ++ ++ dctl = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dctl); ++ dctl |= DWC_DCTL_INIT_U1_EN_BIT; ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dctl, dctl); ++} ++ ++/** ++ * Enable U2 sleep. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++void dwc_usb3_enable_u2(dwc_usb3_pcd_t *pcd) ++{ ++} ++ ++/** ++ * Disable U1 sleep. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++void dwc_usb3_disable_u1(dwc_usb3_pcd_t *pcd) ++{ ++} ++ ++/** ++ * Disable U2 sleep. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++void dwc_usb3_disable_u2(dwc_usb3_pcd_t *pcd) ++{ ++ u32 dctl; ++ ++ dctl = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dctl); ++ dctl &= ~DWC_DCTL_INIT_U2_EN_BIT; ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dctl, dctl); ++} ++ ++/** ++ * Test whether U1 sleep is enabled. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @return 1 if enabled, 0 if not. ++ */ ++int dwc_usb3_u1_enabled(dwc_usb3_pcd_t *pcd) ++{ ++ u32 dctl; ++ ++ dctl = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dctl); ++ return !!(dctl & DWC_DCTL_INIT_U1_EN_BIT); ++} ++ ++/** ++ * Test whether U2 sleep is enabled. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @return 1 if enabled, 0 if not. ++ */ ++int dwc_usb3_u2_enabled(dwc_usb3_pcd_t *pcd) ++{ ++ u32 dctl; ++ ++ dctl = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dctl); ++ return !!(dctl & DWC_DCTL_INIT_U2_EN_BIT); ++} ++ ++/** ++ * Clear 'eps_enabled' flag and 'ena_once' flags for all EPs, so EPs will get ++ * completely reconfigured by SetConfig and SetInterface. ++ */ ++void dwc_usb3_clr_eps_enabled(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_usb3_pcd_ep_t *ep; ++ int i; ++ ++ pcd->eps_enabled = 0; ++ ++ for (i = 0; i < pcd->num_in_eps; i++) { ++ ep = pcd->in_ep[i]; ++ if (ep) ++ ep->dwc_ep.ena_once = 0; ++ } ++ ++ for (i = 0; i < pcd->num_out_eps; i++) { ++ ep = pcd->out_ep[i]; ++ if (ep) ++ ep->dwc_ep.ena_once = 0; ++ } ++} ++ ++/** ++ * This routine is called when the SET_FEATURE TEST_MODE Setup packet ++ * is sent from the host. The Device Control register is written with ++ * the Test Mode bits set to the specified Test Mode. This is done as ++ * a tasklet so that the "Status" phase of the control transfer ++ * completes before transmitting the TEST packets. ++ */ ++void dwc_usb3_pcd_do_test_mode(unsigned long data) ++{ ++ dwc_usb3_pcd_t *pcd = (dwc_usb3_pcd_t *)data; ++ int test_mode = pcd->test_mode; ++ u32 dctl; ++ ++#ifdef CONFIG_USB_OTG_DWC ++ struct usb_phy *phy; ++ struct usb_otg *otg; ++#endif ++ ++ dctl = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dctl); ++ dctl &= ~DWC_DCTL_TSTCTL_BITS; ++ ++ switch (test_mode) { ++ case 1: // TEST_J ++ dctl |= 1 << DWC_DCTL_TSTCTL_SHIFT; ++ break; ++ ++ case 2: // TEST_K ++ dctl |= 2 << DWC_DCTL_TSTCTL_SHIFT; ++ break; ++ ++ case 3: // TEST_SE0_NAK ++ dctl |= 3 << DWC_DCTL_TSTCTL_SHIFT; ++ break; ++ ++ case 4: // TEST_PACKET ++ dctl |= 4 << DWC_DCTL_TSTCTL_SHIFT; ++ break; ++ ++ case 5: // TEST_FORCE_ENABLE ++ dctl |= 5 << DWC_DCTL_TSTCTL_SHIFT; ++ break; ++ ++#ifdef CONFIG_USB_OTG_DWC ++ case 6: // otg_srp_reqd ++ dwc_error0(pcd->usb3_dev, "otg_srp_reqd\n"); ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ if (IS_ERR(phy)) ++ break; ++ if (!phy) { ++ usb_put_phy(phy); ++ break; ++ } ++ otg = phy->otg; ++ if (!otg) { ++ usb_put_phy(phy); ++ break; ++ } ++ otg_start_srp(otg); ++ usb_put_phy(phy); ++ return; ++ ++ case 7: // otg_hnp_reqd ++ dwc_error0(pcd->usb3_dev, "otg_hnp_reqd\n"); ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ if (IS_ERR(phy)) ++ break; ++ if (!phy) { ++ usb_put_phy(phy); ++ break; ++ } ++ otg = phy->otg; ++ if (!otg) { ++ usb_put_phy(phy); ++ break; ++ } ++ otg_start_hnp(otg); ++ usb_put_phy(phy); ++ return; ++#endif ++ } ++ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dctl, dctl); ++} ++ ++/** ++ * This routine calculates the number of IN EPs (excluding EP0) ++ * using GHWPARAMS3 register values ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ */ ++static int calc_num_in_eps(dwc_usb3_device_t *dev) ++{ ++ u32 num_in_eps = dev->hwparams3 >> DWC_HWP3_NUM_IN_EPS_SHIFT & ++ DWC_HWP3_NUM_IN_EPS_BITS >> DWC_HWP3_NUM_IN_EPS_SHIFT; ++ ++ return num_in_eps - 1; ++} ++ ++/** ++ * This routine calculates the number of OUT EPs (excluding EP0) ++ * using GHWPARAMS3 register values ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ */ ++static int calc_num_out_eps(dwc_usb3_device_t *dev) ++{ ++ u32 num_eps = dev->hwparams3 >> DWC_HWP3_NUM_EPS_SHIFT & ++ DWC_HWP3_NUM_EPS_BITS >> DWC_HWP3_NUM_EPS_SHIFT; ++ u32 num_in_eps = dev->hwparams3 >> DWC_HWP3_NUM_IN_EPS_SHIFT & ++ DWC_HWP3_NUM_IN_EPS_BITS >> DWC_HWP3_NUM_IN_EPS_SHIFT; ++ ++ return num_eps - num_in_eps - 1; ++} ++ ++/** ++ * This routine is called to initialize the DWC_usb3 CSR data structures. The ++ * register addresses in the device structures are initialized from the ++ * <strong><em>base</em></strong> address supplied by the caller. The calling ++ * routine must make the OS calls to get the base address of the DWC_usb3 ++ * controller registers. The <strong><em>core_params</em></strong> argument ++ * holds the parameters that specify how the core should be configured. ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ * @param base Base address of DWC_usb3 core registers. ++ * @param core_params Pointer to the core configuration parameters. ++ */ ++int dwc_usb3_pcd_common_init(dwc_usb3_device_t *dev, volatile u8 __iomem *base, ++ dwc_usb3_core_params_t *core_params) ++{ ++ dwc_usb3_pcd_t *pcd; ++ u32 temp; ++ ++ dwc_debug3(dev, "%s(%lx,%lx)\n", __func__, (unsigned long)base, ++ (unsigned long)core_params); ++ ++ dev->core_params = core_params; ++ dev->core_global_regs = (dwc_usb3_core_global_regs_t __iomem *) ++ (base + DWC_CORE_GLOBAL_REG_OFFSET); ++ ++#ifdef COSIM ++ /* scramble-off, scaledown */ ++ dwc_wr32(dev, &dev->core_global_regs->gctl, 0x38); ++#endif ++ ++ pcd = &dev->pcd; ++ ++ pcd->dev_global_regs = (dwc_usb3_dev_global_regs_t __iomem *) ++ (base + DWC_DEV_GLOBAL_REG_OFFSET); ++ pcd->in_ep_regs = (dwc_usb3_dev_ep_regs_t __iomem *) ++ (base + DWC_DEV_IN_EP_REG_OFFSET); ++ pcd->out_ep_regs = (dwc_usb3_dev_ep_regs_t __iomem *) ++ (base + DWC_DEV_OUT_EP_REG_OFFSET); ++ ++#ifdef SSIC ++ dev->ssic_regs = (dwc_usb3_ssic_regs_t __iomem *) ++ (base + DWC_SSIC_REG_OFFSET); ++#endif ++ ++ /* ++ * Store the contents of the hardware configuration registers here for ++ * easy access later. ++ */ ++ dev->hwparams0 = dwc_rd32(dev, &dev->core_global_regs->ghwparams0); ++ dev->hwparams1 = dwc_rd32(dev, &dev->core_global_regs->ghwparams1); ++ dev->hwparams2 = dwc_rd32(dev, &dev->core_global_regs->ghwparams2); ++ dev->hwparams3 = dwc_rd32(dev, &dev->core_global_regs->ghwparams3); ++ dev->hwparams4 = dwc_rd32(dev, &dev->core_global_regs->ghwparams4); ++ dev->hwparams5 = dwc_rd32(dev, &dev->core_global_regs->ghwparams5); ++ dev->hwparams6 = dwc_rd32(dev, &dev->core_global_regs->ghwparams6); ++ dev->hwparams7 = dwc_rd32(dev, &dev->core_global_regs->ghwparams7); ++ dev->hwparams8 = dwc_rd32(dev, &dev->core_global_regs->ghwparams8); ++ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dcfg); ++ dwc_debug1(dev, "dcfg=%08x\n", temp); ++ ++#ifndef SELA_PLATFORM ++ dwc_debug1(dev, "mode=%0x\n", ++ dev->hwparams0 >> DWC_HWP0_MODE_SHIFT & ++ DWC_HWP0_MODE_BITS >> DWC_HWP0_MODE_SHIFT); ++ dwc_debug1(dev, "num_ep=%d\n", ++ dev->hwparams3 >> DWC_HWP3_NUM_EPS_SHIFT & ++ DWC_HWP3_NUM_EPS_BITS >> DWC_HWP3_NUM_EPS_SHIFT); ++ dwc_debug1(dev, "num_in_ep=%d\n", ++ dev->hwparams3 >> DWC_HWP3_NUM_IN_EPS_SHIFT & ++ DWC_HWP3_NUM_IN_EPS_BITS >> DWC_HWP3_NUM_IN_EPS_SHIFT); ++ dwc_debug1(dev, "dfq_fifo_depth=%d\n", ++ dev->hwparams5 >> DWC_HWP5_DFQ_FIFO_DEPTH_SHIFT & ++ DWC_HWP5_DFQ_FIFO_DEPTH_BITS >> DWC_HWP5_DFQ_FIFO_DEPTH_SHIFT); ++ dwc_debug1(dev, "dwq_fifo_depth=%d\n", ++ dev->hwparams5 >> DWC_HWP5_DWQ_FIFO_DEPTH_SHIFT & ++ DWC_HWP5_DWQ_FIFO_DEPTH_BITS >> DWC_HWP5_DWQ_FIFO_DEPTH_SHIFT); ++ dwc_debug1(dev, "txq_fifo_depth=%d\n", ++ dev->hwparams5 >> DWC_HWP5_TXQ_FIFO_DEPTH_SHIFT & ++ DWC_HWP5_TXQ_FIFO_DEPTH_BITS >> DWC_HWP5_TXQ_FIFO_DEPTH_SHIFT); ++ dwc_debug1(dev, "rxq_fifo_depth=%d\n", ++ dev->hwparams5 >> DWC_HWP5_RXQ_FIFO_DEPTH_SHIFT & ++ DWC_HWP5_RXQ_FIFO_DEPTH_BITS >> DWC_HWP5_RXQ_FIFO_DEPTH_SHIFT); ++#endif ++ ++ /* Initialize parameters from Hardware configuration registers. */ ++ dev->pcd.num_in_eps = calc_num_in_eps(dev); ++ dev->pcd.num_in_eps = 4; ++ printk("\n###%s,%d,dev->pcd.num_in_eps=0x%x\n",__func__,__LINE__,dev->pcd.num_in_eps); ++ if (dev->pcd.num_in_eps > 15) { ++ dwc_debug1(dev, "Number of IN endpoints (%d) too large\n", ++ dev->pcd.num_in_eps); ++ return -DWC_E_INVALID; ++ } ++ ++ dev->pcd.num_out_eps = calc_num_out_eps(dev); ++ dev->pcd.num_out_eps = 2; ++ printk("\n###%s,%d,dev->pcd.num_out_eps=0x%x\n",__func__,__LINE__,dev->pcd.num_out_eps); ++ if (dev->pcd.num_out_eps > 15) { ++ dwc_debug1(dev, "Number of OUT endpoints (%d) too large\n", ++ dev->pcd.num_out_eps); ++ return -DWC_E_INVALID; ++ } ++ ++#ifdef SELA_PLATFORM ++ /* Limit the number of EPs to speed up simulation */ ++ if (dev->pcd.num_in_eps > dev->pcd.max_in_eps) ++ dev->pcd.num_in_eps = dev->pcd.max_in_eps; ++ if (dev->pcd.num_out_eps > dev->pcd.max_out_eps) ++ dev->pcd.num_out_eps = dev->pcd.max_out_eps; ++#endif ++ ++ return 0; ++} ++ ++/** ++ * This routine frees any allocations made by dwc_usb3_pcd_common_init(). ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ */ ++void dwc_usb3_pcd_common_remove(dwc_usb3_device_t *dev) ++{ ++} ++ ++/** ++ * This routine ensures the device is really a DWC_usb3 controller, by reading ++ * and verifying the SNPSID register contents. The value should be 0x5533XXXX, ++ * which corresponds to "U3", as in "USB3 version X.XXX". ++ * ++ * The SNPSID value is also saved in <em>dev->snpsid</em> for later use in ++ * determining if any version-specific operations need to be performed. ++ * ++ * This routine should be called before any other initialization routines, to ++ * ensure that the <em>dev->snpsid</em> value is set in case any of the other ++ * routines need it. ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ * @param addr_ofs Offset to the Device registers in the CSR space. It is ++ * needed because this routine is called early, before the ++ * normal register access routines are functional. ++ * @return 0 if the SNPSID value is valid, -DWC_E_INVALID if not. ++ */ ++int dwc_usb3_pcd_check_snpsid(dwc_usb3_device_t *dev, u32 addr_ofs) ++{ ++ dev->snpsid = dwc_rd32(dev, (volatile u32 __iomem *) ++ (dev->base + addr_ofs + 0x120)); ++ if ((dev->snpsid & 0xffff0000) != 0x55330000) { ++ dwc_error1(dev, "bad value for SNPSID: 0x%08x!\n", dev->snpsid); ++ return -DWC_E_INVALID; ++ } ++ ++ return 0; ++} ++ ++/** ++ * This routine dumps the core's internal debug registers ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ */ ++void dwc_usb3_dump_dbgregs(dwc_usb3_device_t *dev) ++{ ++ u32 rd_data, rd_data2; ++ unsigned int i, num_eps; ++ ++ /* Dump the 11 LSP debug registers */ ++ for (i = 0; i < 11; i++) { ++ dwc_wr32(dev, &dev->core_global_regs->gdbglspmux, i << 4); ++ rd_data = dwc_rd32(dev, &dev->core_global_regs->gdbglsp); ++ dwc_info2(dev, "lsp%d: %08x\n", i, rd_data); ++ } ++ ++ /* Dump the EP debug registers */ ++ num_eps = dev->hwparams3 >> DWC_HWP3_NUM_EPS_SHIFT & ++ DWC_HWP3_NUM_EPS_BITS >> DWC_HWP3_NUM_EPS_SHIFT; ++ for (i = 0; i < num_eps; i++) { ++ dwc_wr32(dev, &dev->core_global_regs->gdbglspmux, i); ++ rd_data = dwc_rd32(dev, &dev->core_global_regs->gdbgepinfo0); ++ rd_data2 = dwc_rd32(dev, &dev->core_global_regs->gdbgepinfo1); ++ dwc_info3(dev, " ep%d: %08x %08x\n", i, rd_data, rd_data2); ++ } ++ ++ /* Dump the BMU debug register */ ++ rd_data = dwc_rd32(dev, &dev->core_global_regs->gdbgbmu); ++ dwc_info1(dev, "bmu: %08x\n", rd_data); ++} ++ ++/** ++ * This routine reads the core global registers and prints them ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ */ ++void dwc_usb3_dump_global_registers(dwc_usb3_device_t *dev) ++{ ++ volatile u32 __iomem *addr; ++ ++ dwc_print0(dev, "Core Global Registers\n"); ++ addr = &dev->core_global_regs->gsbuscfg0; ++ dwc_print2(dev, "GSBUSCFG0 @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++ addr = &dev->core_global_regs->gsbuscfg1; ++ dwc_print2(dev, "GSBUSCFG1 @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++ addr = &dev->core_global_regs->gusb2phycfg[0]; ++ dwc_print2(dev, "USB2PHYCFG0 @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++ addr = &dev->core_global_regs->gevten; ++ dwc_print2(dev, "GEVTEN @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++ addr = &dev->core_global_regs->grxfifosiz[0]; ++ dwc_print2(dev, "GRXFIFOSIZ0 @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++ addr = &dev->core_global_regs->gtxfifosiz[0]; ++ dwc_print2(dev, "GTXFIFOSIZ0 @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++ addr = &dev->core_global_regs->gtxfifosiz[1]; ++ dwc_print2(dev, "GTXFIFOSIZ1 @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++ addr = &dev->core_global_regs->gtxfifosiz[2]; ++ dwc_print2(dev, "GTXFIFOSIZ2 @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++ addr = &dev->core_global_regs->gtxfifosiz[3]; ++ dwc_print2(dev, "GTXFIFOSIZ3 @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++ addr = &dev->core_global_regs->gusb2i2cctl[0]; ++ dwc_print2(dev, "GUSB2I2CCTL0 @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++ addr = &dev->core_global_regs->ggpio; ++ dwc_print2(dev, "GGPIO @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++ addr = &dev->core_global_regs->guid; ++ dwc_print2(dev, "GUID @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++ addr = &dev->core_global_regs->gsnpsid; ++ dwc_print2(dev, "GSNPSID @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(dev, addr)); ++} ++ ++/** ++ * This routine reads the device registers and prints them ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++void dwc_usb3_dump_dev_registers(dwc_usb3_pcd_t *pcd) ++{ ++ volatile u32 __iomem *addr; ++ ++ dwc_print0(pcd->usb3_dev, "Device Global Registers\n"); ++ addr = &pcd->dev_global_regs->dcfg; ++ dwc_print2(pcd->usb3_dev, "DCFG @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(pcd->usb3_dev, addr)); ++ addr = &pcd->dev_global_regs->dctl; ++ dwc_print2(pcd->usb3_dev, "DCTL @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(pcd->usb3_dev, addr)); ++ addr = &pcd->dev_global_regs->dsts; ++ dwc_print2(pcd->usb3_dev, "DSTS @0x%08lx : 0x%08x\n", ++ (unsigned long)addr, dwc_rd32(pcd->usb3_dev, addr)); ++} ++ ++/** ++ * Set the size of the Tx FIFOs ++ * ++ * NOTE: The following code for setting the FIFO sizes only ++ * works for cores configured with the 3 RAM option. Setting ++ * FIFO sizes for the 2 RAM option is not implemented. ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ * @param sz New sizes for the FIFOs. ++ */ ++void dwc_usb3_set_tx_fifo_size(dwc_usb3_device_t *dev, int *sz) ++{ ++ dwc_usb3_core_global_regs_t __iomem *global_regs = ++ dev->core_global_regs; ++ int i, ram_width, ram_depth, size, prev_start, txsz[DWC_MAX_TX_FIFOS]; ++ ++ ram_width = (dev->hwparams0 >> DWC_HWP0_MDWIDTH_SHIFT & ++ DWC_HWP0_MDWIDTH_BITS >> DWC_HWP0_MDWIDTH_SHIFT) ++ / 8; ++ ram_depth = (dev->hwparams7 >> DWC_HWP7_RAM1_DEPTH_SHIFT & ++ DWC_HWP7_RAM1_DEPTH_BITS >> DWC_HWP7_RAM1_DEPTH_SHIFT) ++ * ram_width; ++ size = dwc_rd32(dev, &global_regs->gtxfifosiz[0]); ++ prev_start = size >> DWC_FIFOSZ_STARTADDR_SHIFT & ++ DWC_FIFOSZ_STARTADDR_BITS >> DWC_FIFOSZ_STARTADDR_SHIFT; ++ ++ for (i = 0; i < dev->pcd.num_in_eps + 1; i++) { ++ size = sz[i]; ++ ++ if (i == 0 && size && size < 512 + 2 * ram_width) { ++ dwc_print1(dev, "Requested Tx FIFO %d size too small\n", ++ i); ++ dwc_print0(dev, "Not setting Tx FIFO sizes\n"); ++ goto txerr; ++ } ++ ++ if (!size) { ++ /* Default to 512 for EP0, 1K for others */ ++ size = i ? 1024 : 512; ++ size += 2 * ram_width; ++ } ++ ++ size = (size + ram_width - 1) & ~(ram_width - 1); ++ dwc_debug3(dev, ++ "Tx FIFO %d size = %d bytes out of %d available\n", ++ i, size, ram_depth); ++ if (size > ram_depth) { ++ dwc_print1(dev, "Requested Tx FIFO %d size too large\n", ++ i); ++ dwc_print0(dev, "Not setting Tx FIFO sizes\n"); ++ goto txerr; ++ } ++ ++ txsz[i] = size; ++ ram_depth -= size; ++ } ++ ++ for (i = 0; i < dev->pcd.num_in_eps + 1; i++) { ++ size = txsz[i]; ++ dwc_debug2(dev, "Setting GTXFIFOSIZ%d = 0x%08x\n", i, ++ (size / ram_width) << DWC_FIFOSZ_DEPTH_SHIFT | ++ prev_start << DWC_FIFOSZ_STARTADDR_SHIFT); ++ dwc_wr32(dev, &global_regs->gtxfifosiz[i], ++ (size / ram_width) << DWC_FIFOSZ_DEPTH_SHIFT | ++ prev_start << DWC_FIFOSZ_STARTADDR_SHIFT); ++ dwc_debug2(dev, "GTXFIFOSIZ%d = 0x%08x\n", i, ++ dwc_rd32(dev, &global_regs->gtxfifosiz[i])); ++ prev_start += size / ram_width; ++ } ++txerr: ++ return; ++} ++ ++/** ++ * Set the size of the Rx FIFO ++ * ++ * NOTE: The following code for setting the FIFO sizes only ++ * works for cores configured with the 3 RAM option. Setting ++ * FIFO sizes for the 2 RAM option is not implemented. ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ * @param size New size for the FIFO. ++ */ ++void dwc_usb3_set_rx_fifo_size(dwc_usb3_device_t *dev, int size) ++{ ++ dwc_usb3_core_global_regs_t __iomem *global_regs = ++ dev->core_global_regs; ++ u32 sz, rxthrcfg; ++ int ram_width, ram_depth, prev_start, cnt, bst; ++ ++ ram_width = (dev->hwparams0 >> DWC_HWP0_MDWIDTH_SHIFT & ++ DWC_HWP0_MDWIDTH_BITS >> DWC_HWP0_MDWIDTH_SHIFT) ++ / 8; ++ ram_depth = (dev->hwparams7 >> DWC_HWP7_RAM2_DEPTH_SHIFT & ++ DWC_HWP7_RAM2_DEPTH_BITS >> DWC_HWP7_RAM2_DEPTH_SHIFT) ++ * ram_width; ++ sz = dwc_rd32(dev, &global_regs->grxfifosiz[0]); ++ prev_start = sz >> DWC_FIFOSZ_STARTADDR_SHIFT & ++ DWC_FIFOSZ_STARTADDR_BITS >> DWC_FIFOSZ_STARTADDR_SHIFT; ++ ++ if (size < 512 + 24 + 16 + (ram_width == 16 ? 24 : 0)) { ++ dwc_print0(dev, "Requested Rx FIFO size too small\n"); ++ dwc_print0(dev, "Not setting Rx FIFO size\n"); ++ goto rxerr; ++ } ++ ++ size = (size + ram_width - 1) & ~(ram_width - 1); ++ if (size > ram_depth) { ++ dwc_print0(dev, "Requested Rx FIFO size too large\n"); ++ dwc_print0(dev, "Not setting Rx FIFO size\n"); ++ goto rxerr; ++ } ++ ++ dwc_debug1(dev, "Setting GRXFIFOSIZ0 = 0x%08x\n", ++ (size / ram_width) << DWC_FIFOSZ_DEPTH_SHIFT | ++ prev_start << DWC_FIFOSZ_STARTADDR_SHIFT); ++ dwc_wr32(dev, &global_regs->grxfifosiz[0], ++ (size / ram_width) << DWC_FIFOSZ_DEPTH_SHIFT | ++ prev_start << DWC_FIFOSZ_STARTADDR_SHIFT); ++ dwc_debug1(dev, "GRXFIFOSIZ0 = 0x%08x\n", ++ dwc_rd32(dev, &global_regs->grxfifosiz[0])); ++ dwc_debug2(dev, "Rx FIFO size = %d bytes out of %d available\n", ++ size, ram_depth); ++ ++ /* ++ * If thresholding is enabled in GRXTHRCFG, update USBRxPktCnt according ++ * to the new FIFO size ++ */ ++ rxthrcfg = dwc_rd32(dev, &global_regs->grxthrcfg); ++ dwc_debug1(dev, "GRXTHRCFG = 0x%08x\n", rxthrcfg); ++ if (rxthrcfg & DWC_RXTHRCTL_USB_RX_PKT_CNT_EN_BIT) { ++ cnt = (size - ram_width * 4) / 1024; ++ if (cnt > 0) { ++ if (cnt > DWC_RXTHRCTL_USB_RX_PKT_CNT_BITS >> ++ DWC_RXTHRCTL_USB_RX_PKT_CNT_SHIFT) ++ cnt = DWC_RXTHRCTL_USB_RX_PKT_CNT_BITS >> ++ DWC_RXTHRCTL_USB_RX_PKT_CNT_SHIFT; ++ bst = rxthrcfg >> ++ DWC_RXTHRCTL_USB_MAX_RX_BURST_SIZE_SHIFT & ++ DWC_RXTHRCTL_USB_MAX_RX_BURST_SIZE_BITS >> ++ DWC_RXTHRCTL_USB_MAX_RX_BURST_SIZE_SHIFT; ++ if (cnt > bst) ++ cnt = bst; ++ if (cnt < 1) ++ goto disable; ++ rxthrcfg &= ~DWC_RXTHRCTL_USB_RX_PKT_CNT_BITS; ++ rxthrcfg |= cnt << DWC_RXTHRCTL_USB_RX_PKT_CNT_SHIFT; ++ } else { ++disable: ++ rxthrcfg &= ~DWC_RXTHRCTL_USB_RX_PKT_CNT_EN_BIT; ++ } ++ dwc_debug1(dev, "Setting GRXTHRCFG = 0x%08x\n", rxthrcfg); ++ dwc_wr32(dev, &global_regs->grxthrcfg, rxthrcfg); ++ } ++rxerr: ++ return; ++} ++ ++/** ++ * This routine initializes the DWC_usb3 controller registers. ++ * ++ * If the <strong><em>soft_reset</em></strong> parameter is ++ * <strong>true</strong>, then this routine must be called in a context that ++ * allows <em>dwc_msleep()</em> to be used, because that function is called ++ * while waiting for the core to come out of reset. ++ * ++ * This routine is called by dwc_usb3_pcd_init() when the driver is loaded, ++ * so it normally does not need to be called separately, except in special ++ * circumstances, such as when exiting from hibernation. ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ * @param soft_reset True if doing a soft reset of the core. ++ * @param restore True if restoring register state after hibernation. ++ */ ++void dwc_usb3_pcd_device_init(dwc_usb3_device_t *dev, int soft_reset, ++ int restore) ++{ ++ dwc_usb3_core_global_regs_t __iomem *global_regs = ++ dev->core_global_regs; ++ dwc_usb3_pcd_t *pcd = &dev->pcd; ++ u32 temp; ++ int i, ram_width, ram_depth, size; ++ ++ dwc_debug1(dev, "%s()\n", __func__); ++ ++ if (dev->program_gsbuscfg) { ++ dwc_debug2(dev, "Programming SBUSCFG0,1 to %08x %08x\n", ++ dev->gsbuscfg0, dev->gsbuscfg1); ++ dwc_wr32(dev, &global_regs->gsbuscfg0, dev->gsbuscfg0); ++ dwc_wr32(dev, &global_regs->gsbuscfg1, dev->gsbuscfg1); ++ } ++ ++ if (dev->soft_reset_hook) { ++ dev->soft_reset_hook(dev, soft_reset, restore); ++ } else { ++ /* ++ * TODO Workaround: PCD can't handle soft reset during HNP. ++ * RTL issue will be fixed. Skip the reset when called with ++ * soft_reset=0. When not configured for OTG do the reset ++ * unconditionally. ++ */ ++ if (soft_reset) { ++ /* Soft-reset the core */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ temp &= ~DWC_DCTL_RUN_STOP_BIT; ++ temp |= DWC_DCTL_CSFT_RST_BIT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, temp); ++ ++ /* Wait for core to come out of reset */ ++ do { ++ dwc_msleep(dev, 1); ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ } while (temp & DWC_DCTL_CSFT_RST_BIT); ++ ++ /* Wait for at least 3 PHY clocks */ ++ dwc_msleep(dev, 1); ++ } ++ } ++ ++ /* Soft reset clears the GSBUSCFG registers, so write them again */ ++ if (dev->program_gsbuscfg) { ++ dwc_debug2(dev, "Programming SBUSCFG0,1 to %08x %08x\n", ++ dev->gsbuscfg0, dev->gsbuscfg1); ++ dwc_wr32(dev, &global_regs->gsbuscfg0, dev->gsbuscfg0); ++ dwc_wr32(dev, &global_regs->gsbuscfg1, dev->gsbuscfg1); ++ } ++ ++ pcd->link_state = 0; ++ pcd->wkup_rdy = 0; ++ ++ if (dev->phy_config_hook) { ++ dev->phy_config_hook(dev, soft_reset, restore); ++ } ++ ++ if (dev->fifo_sizing_hook) { ++ dev->fifo_sizing_hook(dev, soft_reset, restore); ++ } else { ++ /* ++ * Set Tx FIFO sizes ++ */ ++ ram_width = (dev->hwparams0 >> DWC_HWP0_MDWIDTH_SHIFT & ++ DWC_HWP0_MDWIDTH_BITS >> DWC_HWP0_MDWIDTH_SHIFT) ++ / 8; ++ ram_depth = (dev->hwparams7 >> DWC_HWP7_RAM1_DEPTH_SHIFT & ++ DWC_HWP7_RAM1_DEPTH_BITS >> DWC_HWP7_RAM1_DEPTH_SHIFT) ++ * ram_width; ++ dwc_debug2(dev, "RAM width:%d RAM1 depth:%d\n", ram_width, ram_depth); ++ ++ for (i = 0; i < pcd->num_in_eps + 1; i++) { ++ size = dwc_rd32(dev, &global_regs->gtxfifosiz[i]); ++ dwc_debug2(dev, "Initial GTXFIFOSIZ%d = 0x%08x\n", i, size); ++ } ++ ++ /* Only set if non-default Tx FIFO sizes were requested */ ++ if (dev->core_params->txfsz_cnt) ++ dwc_usb3_set_tx_fifo_size(dev, dev->core_params->txfsz); ++ ++ /* ++ * Set Rx FIFO size ++ */ ++ ram_depth = (dev->hwparams7 >> DWC_HWP7_RAM2_DEPTH_SHIFT & ++ DWC_HWP7_RAM2_DEPTH_BITS >> DWC_HWP7_RAM2_DEPTH_SHIFT) ++ * ram_width; ++ dwc_debug1(dev, "RAM2 depth:%d\n", ram_depth); ++ size = dwc_rd32(dev, &global_regs->grxfifosiz[0]); ++ dwc_debug1(dev, "Initial GRXFIFOSIZ0 = 0x%08x\n", size); ++ size = dev->core_params->rxfsz; ++ ++ /* Only set if non-default Rx FIFO size was requested */ ++ if (size) ++ dwc_usb3_set_rx_fifo_size(dev, size); ++ } ++ ++#if 1 ++ if (dev->gctl_init_hook) { ++ dev->gctl_init_hook(dev, soft_reset, restore); ++ } else { ++ temp = dwc_rd32(dev, &global_regs->gctl); ++ temp &= ~(DWC_GCTL_PRT_CAP_DIR_BITS | DWC_GCTL_SCALE_DOWN_BITS); ++ ++#ifdef DWC_STAR_9000468158_WORKAROUND ++ temp |= DWC_GCTL_U2RSTECN_BIT; ++#endif ++#ifdef CONFIG_USB_OTG_DWC ++ temp |= DWC_GCTL_PRT_CAP_OTG << DWC_GCTL_PRT_CAP_DIR_SHIFT; ++#else ++ temp |= DWC_GCTL_PRT_CAP_DEVICE << DWC_GCTL_PRT_CAP_DIR_SHIFT; ++#endif ++#ifdef COSIM ++ /* Scale down, disable scrambling */ ++ temp |= 3 << DWC_GCTL_SCALE_DOWN_SHIFT | DWC_GCTL_DIS_SCRAMBLE_BIT; ++#else ++# if 1 ++ temp &= ~DWC_GCTL_PWR_DN_SCALE_BITS; ++ ++ switch (dev->core_params->phy) { ++ case 3: // 16-bit UTMI+ SNPS Phy ++ case 2: // 8-bit UTMI+ / ULPI TI or SNPS Phy ++ /* Set power down scale */ ++ temp |= 0x270 << DWC_GCTL_PWR_DN_SCALE_SHIFT; ++ break; ++ case 1: // old 8-bit UTMI+ SNPS Phy ++ /* Set LFPS filter */ ++ dwc_wr32(dev, &global_regs->gusb3pipectl[0], ++ DWC_PIPECTL_LFPS_FILTER_BIT | ++ 1 << DWC_PIPECTL_TX_DEMPH_SHIFT); ++ ++ /* Set power down scale */ ++ temp |= 0x270 << DWC_GCTL_PWR_DN_SCALE_SHIFT; ++ break; ++ default: // RocketIO Phy ++ /* Set power down scale, disable scrambling */ ++ temp |= 0x1e84 << DWC_GCTL_PWR_DN_SCALE_SHIFT | ++ DWC_GCTL_DEBUG_ATTACH_BIT | ++ DWC_GCTL_DIS_SCRAMBLE_BIT; ++ } ++# endif ++#endif ++ dwc_wr32(dev, &global_regs->gctl, temp); ++ } ++#endif ++ /* Initialize the Event Buffer registers */ ++ dwc_usb3_init_eventbuf(dev, 0, dev->event_buf[0], DWC_EVENT_BUF_SIZE, ++ dev->event_buf_dma[0]); ++ dev->event_ptr[0] = dev->event_buf[0]; ++ ++#ifdef CONFIG_USB_OTG_DWC ++ /* TODO Workaround: this is a workaround for OTG 3.0 where the utmi ++ * clock doesn't come unless the device speed is momentarily programmed ++ * to HS. This causes the peripheral state on OSTS to not get set ++ * properly. ++ */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dcfg); ++ temp &= ~(DWC_DCFG_DEVSPD_BITS << DWC_DCFG_DEVSPD_SHIFT); ++ temp |= DWC_SPEED_HS_PHY_30MHZ_OR_60MHZ << DWC_DCFG_DEVSPD_SHIFT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dcfg, temp); ++ dwc_udelay(dev, 200); ++#endif ++ ++ /* If forcing to a USB2 mode was requested */ ++ if (dev->core_params->usb2mode == 1) { ++ /* Set speed to Full */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dcfg); ++ temp &= ~(DWC_DCFG_DEVSPD_BITS << DWC_DCFG_DEVSPD_SHIFT); ++ temp |= DWC_SPEED_FS_PHY_30MHZ_OR_60MHZ ++ << DWC_DCFG_DEVSPD_SHIFT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dcfg, temp); ++ ++ } else if ((dev->core_params->usb2mode == 2) ++#ifdef CONFIG_USB_OTG_DWC ++ /* Check the OTG_SS bit when in OTG mode to see if superspeed ++ * is supported ++ */ ++ || !(dev->hwparams6 & DWC_HWP6_EN_OTG_BIT) ++#endif ++ ) { ++ /* Set speed to High */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dcfg); ++ temp &= ~(DWC_DCFG_DEVSPD_BITS << DWC_DCFG_DEVSPD_SHIFT); ++ temp |= DWC_SPEED_HS_PHY_30MHZ_OR_60MHZ ++ << DWC_DCFG_DEVSPD_SHIFT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dcfg, temp); ++ ++ } else { ++ /* Set speed to Super */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dcfg); ++ temp &= ~(DWC_DCFG_DEVSPD_BITS << DWC_DCFG_DEVSPD_SHIFT); ++ ++ if ((dev->hwparams3 & DWC_HWP3_SSPHY_IFC_BITS) == 0) ++ temp |= DWC_SPEED_HS_PHY_30MHZ_OR_60MHZ ++ << DWC_DCFG_DEVSPD_SHIFT; ++ else ++ temp |= DWC_SPEED_SS_PHY_125MHZ_OR_250MHZ ++ << DWC_DCFG_DEVSPD_SHIFT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dcfg, temp); ++ } ++ ++ /* If LPM enable was requested */ ++ if (dev->core_params->lpmctl) { ++ /* Set LPMCap bit */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dcfg); ++ temp |= DWC_DCFG_LPM_CAP_BIT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dcfg, temp); ++ ++ if (dev->core_params->lpmctl > 1) { ++ /* Set AppL1Res bit */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ temp |= DWC_DCTL_APP_L1_RES_BIT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, temp); ++ } ++ } ++ ++ /* If non-default NUMP was requested */ ++ if (dev->core_params->nump > 0 && dev->core_params->nump <= 16) { ++ /* Set NUMP */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dcfg); ++ temp &= ~DWC_DCFG_NUM_RCV_BUF_BITS; ++ temp |= dev->core_params->nump << DWC_DCFG_NUM_RCV_BUF_SHIFT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dcfg, temp); ++ } ++ ++ if (dev->set_address_hook) { ++ dev->set_address_hook(dev, soft_reset, restore); ++ } else { ++ if (!restore) ++ /* Set device address to 0 */ ++ dwc_usb3_set_address(pcd, 0); ++ } ++ ++ /* Enable hibernation if supported */ ++ if (dev->core_params->hibernate && ++ (dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT) { ++ /* Enable global hibernation bit */ ++ temp = dwc_rd32(dev, &global_regs->gctl); ++ temp |= DWC_GCTL_GBL_HIBER_EN_BIT; ++ if (dev->core_params->clkgatingen) ++ temp &= ~DWC_GCTL_DSBL_CLCK_GTNG_BIT; ++ else ++ temp |= DWC_GCTL_DSBL_CLCK_GTNG_BIT; ++ dwc_wr32(dev, &global_regs->gctl, temp); ++ ++ if (dev->core_params->lpmctl) { ++ /* Set L1 hibernation values */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ temp &= ~DWC_DCTL_HIRD_THR_BITS; ++ if (dev->hird_thresh) ++ temp |= dev->hird_thresh << DWC_DCTL_HIRD_THR_SHIFT & ++ DWC_DCTL_HIRD_THR_BITS; ++ else ++ temp |= 0x1c << DWC_DCTL_HIRD_THR_SHIFT; ++ ++ /* Enable L1 hibernation */ ++ temp |= DWC_DCTL_L1_HIBER_EN_BIT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, temp); ++ } ++ ++ if (!restore) { ++ /* Issue Set Scratchpad Buffer Array command */ ++ dwc_usb3_set_scratchpad_buf_array(pcd, ++ pcd->hiber_scratchpad_array_dma); ++ } ++ ++ /* Set GUSB2PHYCFG[6] (suspend 2.0 phy) */ ++ temp = dwc_rd32(dev, &global_regs->gusb2phycfg[0]); ++ temp |= DWC_USB2PHYCFG_SUS_PHY_BIT; ++ if (dev->core_params->lpmctl) ++ temp |= DWC_USB2PHYCFG_ENBL_SLP_M_BIT; ++ dwc_wr32(dev, &global_regs->gusb2phycfg[0], temp); ++ ++#ifndef SELA_PLATFORM ++ /* Set GUSB3PIPECTL[17] (suspend SS phy) */ ++ temp = dwc_rd32(dev, &global_regs->gusb3pipectl[0]); ++ temp |= DWC_PIPECTL_SUS_PHY_BIT; ++ dwc_wr32(dev, &global_regs->gusb3pipectl[0], temp); ++#endif ++ } else { ++ if (dev->core_params->phyctl) { ++ /* Enable Phy suspend */ ++ dwc_usb3_ena_usb3_phy_suspend(pcd); ++ dwc_usb3_ena_usb2_phy_suspend(pcd); ++ } else { ++ /* Disable Phy suspend */ ++ dwc_usb3_dis_usb3_phy_suspend(pcd); ++ dwc_usb3_dis_usb2_phy_suspend(pcd); ++ } ++ } ++ ++#ifndef CONFIG_USB_OTG_DWC ++ /* Enable Global and Device interrupts */ ++ dwc_usb3_enable_device_interrupts(dev); ++#endif ++ /* Activate EP0 */ ++ dwc_usb3_ep0_activate(pcd, restore); ++ ++ if (dev->ep0_start_hook) { ++ dev->ep0_start_hook(dev, soft_reset, restore); ++ } else { ++ /* Start EP0 to receive SETUP packets */ ++ dwc_usb3_pcd_ep0_out_start(pcd); ++ } ++ ++ /* Enable EP0-OUT/IN in DALEPENA register */ ++ dwc_wr32(dev, &pcd->dev_global_regs->dalepena, 3); ++ ++ pcd->eps_enabled = 0; ++ ++#ifndef CONFIG_USB_OTG_DWC ++ /* Set Run/Stop bit, and Keep-Connect bit if hibernation enabled */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ temp |= DWC_DCTL_RUN_STOP_BIT; ++ if (dev->core_params->hibernate && ++ (dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT) ++ temp |= DWC_DCTL_KEEP_CONNECT_BIT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, temp); ++#endif ++ ++#ifdef SSIC ++ dwc_debug(dev, "SSIC Initialization\n"); ++ temp = dwc_rd32(dev, &dev->core_global_regs->gusb3pipectl[0]); ++ temp |= DWC_PIPECTL_SSIC_EN_BIT; ++ temp |= DWC_PIPECTL_PHY_SOFT_RST_BIT; ++ dwc_wr32(dev, &dev->core_global_regs->gusb3pipectl[0], temp); ++ dwc_wr32(dev, &dev->ssic_regs->sctl[0], 0); ++ dwc_wr32(dev, &dev->ssic_regs->sevt[0], 0xffffffff); ++ dwc_wr32(dev, &dev->ssic_regs->sevten[0], 0); ++ dwc_wr32(dev, &dev->ssic_regs->sevten[0], ++ DWC_SEVTEN_ROM_INIT_CMPLT_EN_BIT | DWC_SEVTEN_MPHY_ST_CHNGD_EN_BIT); ++ ++ temp = dwc_rd32(dev, &dev->core_global_regs->gusb3rmmictl[0]); ++ temp &= ~(DWC_RMMICTL_AUTO_EXIT_RRAP_BIT | DWC_RMMICTL_AUTO_ROM_RRAP_BIT | ++ DWC_RMMICTL_AUTO_EXIT_H8_BIT | DWC_RMMICTL_AUTO_ROM_H8_BIT); ++ dwc_wr32(dev, &dev->core_global_regs->gusb3rmmictl[0], temp); ++ ++ temp = dwc_rd32(dev, &dev->core_global_regs->gusb3pipectl[0]); ++ temp &= ~DWC_PIPECTL_PHY_SOFT_RST_BIT; ++ dwc_wr32(dev, &dev->core_global_regs->gusb3pipectl[0], temp); ++ ++ if (!handshake(dev, &dev->core_global_regs->gusb3rmmictl[0], ++ DWC_RMMICTL_MPHY_STATE_BITS, ++ (DWC_MPHY_STATE_HIBERN8 << DWC_RMMICTL_MPHY_STATE_SHIFT))) { ++ dwc_error(dev, "%s: wait for mphy state hibern8 timed out\n", __func__); ++ } ++ ++ temp = dwc_rd32(dev, &dev->ssic_regs->sevten[0]); ++ temp |= (DWC_SEVTEN_RCMD_RES_SENT_EN_BIT | ++ DWC_SEVTEN_RCMD_RES_RCVD_EN_BIT | ++ DWC_SEVTEN_LACC_CMPLT_EN_BIT); ++ dwc_wr32(dev, &dev->ssic_regs->sevten[0], temp); ++ ++ temp = dwc_rd32(dev, &dev->ssic_regs->sctl[0]); ++ temp |= DWC_SCTL_CFG_DONE_BIT; ++ dwc_wr32(dev, &dev->ssic_regs->sctl[0], temp); ++ ++ if (!handshake(dev, &dev->ssic_regs->sctl[0], ++ DWC_SCTL_CFG_DONE_BIT, 0)) { ++ dwc_error(dev, "%s: cfg done timed out\n", __func__); ++ } ++#endif ++} ++ ++/** ++ * This routine deinitializes the DWC_usb3 controller registers. ++ * ++ * This routine is called by dwc_usb3_pcd_remove() when the driver is unloaded, ++ * so it normally does not need to be called separately, ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ */ ++void dwc_usb3_pcd_device_remove(dwc_usb3_device_t *dev) ++{ ++ dwc_usb3_core_global_regs_t __iomem *global_regs = ++ dev->core_global_regs; ++ dwc_usb3_pcd_t *pcd = &dev->pcd; ++ u32 temp; ++ ++ if (dev->hibernate >= DWC_HIBER_SLEEPING) ++ return; ++ ++ /* Clear the Run/Stop and Keep-Connect bits */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ temp &= ~(DWC_DCTL_RUN_STOP_BIT | DWC_DCTL_KEEP_CONNECT_BIT); ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, temp); ++ ++ /* Disable device interrupts */ ++ dwc_wr32(dev, &pcd->dev_global_regs->devten, 0); ++ ++ /* Disable hibernation if supported */ ++ if (dev->core_params->hibernate && ++ (dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT) { ++ /* Clear GUSB3PIPECTL[17] (suspend SS phy) */ ++ temp = dwc_rd32(dev, &global_regs->gusb3pipectl[0]); ++ temp &= ~DWC_PIPECTL_SUS_PHY_BIT; ++ dwc_wr32(dev, &global_regs->gusb3pipectl[0], temp); ++ ++ /* Clear GUSB2PHYCFG[6] (suspend 2.0 phy) */ ++ temp = dwc_rd32(dev, &global_regs->gusb2phycfg[0]); ++ temp &= ~DWC_USB2PHYCFG_SUS_PHY_BIT; ++ temp &= ~DWC_USB2PHYCFG_ENBL_SLP_M_BIT; ++ dwc_wr32(dev, &global_regs->gusb2phycfg[0], temp); ++ ++ /* Disable L1 hibernation */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ temp &= ~DWC_DCTL_L1_HIBER_EN_BIT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, temp); ++ ++ /* Disable global hibernation bit */ ++ temp = dwc_rd32(dev, &global_regs->gctl); ++ temp &= ~DWC_GCTL_GBL_HIBER_EN_BIT; ++ temp |= DWC_GCTL_DSBL_CLCK_GTNG_BIT; ++ dwc_wr32(dev, &global_regs->gctl, temp); ++ } ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/cil.h b/drivers/usb/gadget/udc/hiudc3/cil.h +new file mode 100644 +index 0000000..1799e2a +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/cil.h +@@ -0,0 +1,211 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/cil.h $ ++ * $Revision: #45 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef _DWC_CIL_H_ ++#define _DWC_CIL_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @file ++ * This file contains the interface to the Core Interface Layer. ++ */ ++ ++/** @addtogroup init_api_grp Initialization API Routines ++ * ++ * These routines handle initialization of the CIL and PCD driver components ++ * and the DWC_usb3 controller. ++ */ ++/** @{ */ ++extern int dwc_usb3_pcd_check_snpsid(dwc_usb3_device_t *dev, u32 addr_ofs); ++extern int dwc_usb3_pcd_common_init(dwc_usb3_device_t *dev, ++ volatile u8 __iomem *base, ++ dwc_usb3_core_params_t *core_params); ++extern void dwc_usb3_pcd_common_remove(dwc_usb3_device_t *dev); ++extern void dwc_usb3_pcd_device_init(dwc_usb3_device_t *dev, int soft_reset, ++ int restore); ++extern void dwc_usb3_pcd_device_remove(dwc_usb3_device_t *dev); ++/** @} */ ++ ++/** @addtogroup misc_api_grp */ ++/** @{ */ ++extern int dwc_usb3_pcd_get_link_state(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_pcd_set_link_state(dwc_usb3_pcd_t *pcd, int state); ++extern void dwc_usb3_pcd_remote_wake(dwc_usb3_pcd_t *pcd, int function); ++extern void dwc_usb3_pcd_do_test_mode(unsigned long data); ++/** @} */ ++ ++/* Peripheral CIL Routines ++ * ++ * The following routines support managing the DWC_usb3 controller in ++ * peripheral mode. ++ */ ++extern void dwc_usb3_fill_desc(dwc_usb3_dma_desc_t *desc, dwc_dma_t dma_addr, ++ u32 dma_len, u32 stream, u32 type, ++ u32 ctrlbits, int own); ++extern void dwc_usb3_start_desc_chain(dwc_usb3_dma_desc_t *desc); ++extern void dwc_usb3_end_desc_chain(dwc_usb3_dma_desc_t *desc); ++extern void dwc_usb3_enable_desc(dwc_usb3_dma_desc_t *desc); ++extern void dwc_usb3_disable_desc(dwc_usb3_dma_desc_t *desc); ++extern int dwc_usb3_xmit_fn_remote_wake(dwc_usb3_pcd_t *pcd, u32 intf); ++extern int dwc_usb3_xmit_ltm(dwc_usb3_pcd_t *pcd, u32 value); ++extern int dwc_usb3_xmit_host_role_request(dwc_usb3_pcd_t *pcd, u32 param); ++extern int dwc_usb3_set_scratchpad_buf_array(dwc_usb3_pcd_t *pcd, ++ dwc_dma_t dma_addr); ++extern int dwc_usb3_flush_fifo(dwc_usb3_pcd_t *pcd, u32 fifo_sel); ++extern int dwc_usb3_dep_cfg(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ u32 depcfg0, u32 depcfg1, ++ u32 depcfg2); ++extern int dwc_usb3_dep_xfercfg(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ u32 depstrmcfg); ++extern u32 dwc_usb3_dep_getepstate(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg); ++extern int dwc_usb3_dep_sstall(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg); ++extern int dwc_usb3_dep_cstall(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ int clr_pend); ++extern int dwc_usb3_dep_startxfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ dwc_dma_t dma_addr, u32 stream_or_uf); ++extern int dwc_usb3_dep_updatexfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ u32 tri); ++extern int dwc_usb3_dep_endxfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ u32 tri, int flags, void *condition); ++#define DWC_ENDXFER_FORCE 1 ++#define DWC_ENDXFER_NODELAY 2 ++ ++#ifdef DWC_STAR_9000463548_WORKAROUND ++extern int dwc_usb3_dep_endxfer_nowait(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ u32 tri, int flags); ++extern int dwc_usb3_dep_wait_endxfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ void *condition); ++#endif ++extern int dwc_usb3_dep_startnewcfg(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, ++ u32 rsrcidx); ++extern int dwc_usb3_enable_ep(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep); ++extern int dwc_usb3_disable_ep(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep); ++extern int dwc_usb3_get_device_speed(dwc_usb3_pcd_t *pcd); ++extern int dwc_usb3_get_frame(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_set_address(dwc_usb3_pcd_t *pcd, int addr); ++extern void dwc_usb3_ena_usb2_suspend(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_dis_usb2_suspend(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_accept_u1(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_accept_u2(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_enable_u1(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_enable_u2(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_disable_u1(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_disable_u2(dwc_usb3_pcd_t *pcd); ++extern int dwc_usb3_u1_enabled(dwc_usb3_pcd_t *pcd); ++extern int dwc_usb3_u2_enabled(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_clr_eps_enabled(dwc_usb3_pcd_t *pcd); ++ ++extern void dwc_usb3_dump_dev_registers(dwc_usb3_pcd_t *pcd); ++ ++#define dwc_usb3_is_hwo(desc) ((desc)->control & DWC_DSCCTL_HWO_BIT) ++#define dwc_usb3_is_ioc(desc) ((desc)->control & DWC_DSCCTL_IOC_BIT) ++ ++#define dwc_usb3_get_xfercnt(desc) \ ++ ((desc)->status >> DWC_DSCSTS_XFRCNT_SHIFT & \ ++ DWC_DSCSTS_XFRCNT_BITS >> DWC_DSCSTS_XFRCNT_SHIFT) ++ ++#define dwc_usb3_get_xfersts(desc) \ ++ ((desc)->status >> DWC_DSCSTS_TRBRSP_SHIFT & \ ++ DWC_DSCSTS_TRBRSP_BITS >> DWC_DSCSTS_TRBRSP_SHIFT) ++ ++#define dwc_usb3_get_xfersofn(desc) \ ++ ((desc)->control >> DWC_DSCCTL_STRMID_SOFN_SHIFT & \ ++ DWC_DSCCTL_STRMID_SOFN_BITS >> DWC_DSCCTL_STRMID_SOFN_SHIFT) ++ ++#define dwc_usb3_get_eventsofn(event) \ ++ ((event) >> DWC_DEPEVT_ISOC_UFRAME_NUM_SHIFT & \ ++ DWC_DEPEVT_ISOC_UFRAME_NUM_BITS >> DWC_DEPEVT_ISOC_UFRAME_NUM_SHIFT) ++ ++/* Common CIL Routines ++ */ ++extern void dwc_usb3_dump_dbgregs(dwc_usb3_device_t *dev); ++extern void dwc_usb3_dump_global_registers(dwc_usb3_device_t *dev); ++extern void dwc_usb3_set_tx_fifo_size(dwc_usb3_device_t *dev, int *sz); ++extern void dwc_usb3_set_rx_fifo_size(dwc_usb3_device_t *dev, int size); ++extern void dwc_usb3_init_eventbuf(dwc_usb3_device_t *dev, int bufno, ++ u32 *addr, unsigned int size, dwc_dma_t dma_addr); ++extern void dwc_usb3_dis_flush_eventbuf_intr(dwc_usb3_device_t *dev, int bufno); ++extern void dwc_usb3_enable_common_interrupts(dwc_usb3_device_t *dev); ++extern void dwc_usb3_enable_device_interrupts(dwc_usb3_device_t *dev); ++extern int dwc_usb3_handle_event(dwc_usb3_device_t *dev); ++extern int dwc_usb3_irq(dwc_usb3_device_t *dev, int irq); ++ ++/** ++ * This routine returns the current operating mode, host or device. ++ * ++ * @return 0 - Device Mode, 1 - Host Mode ++ */ ++static __inline u32 dwc_usb3_mode(dwc_usb3_device_t *dev) ++{ ++ return dwc_rd32(dev, &dev->core_global_regs->gsts) & 0x1; ++} ++ ++/** ++ * This routine returns true if the current operating mode is Device. ++ * ++ * @return 1 - Device mode, 0 - Not Device mode ++ */ ++static __inline int dwc_usb3_is_device_mode(dwc_usb3_device_t *dev) ++{ ++ return dwc_usb3_mode(dev) != DWC_GSTS_HOST_MODE; ++} ++ ++/** ++ * This routine returns true if the current operating mode is Host. ++ * ++ * @return 1 - Host mode, 0 - Not Host mode ++ */ ++static __inline int dwc_usb3_is_host_mode(dwc_usb3_device_t *dev) ++{ ++ return dwc_usb3_mode(dev) == DWC_GSTS_HOST_MODE; ++} ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_CIL_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/cil_intr.c b/drivers/usb/gadget/udc/hiudc3/cil_intr.c +new file mode 100644 +index 0000000..300faca +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/cil_intr.c +@@ -0,0 +1,596 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/cil_intr.c $ ++ * $Revision: #39 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The Core Interface Layer provides basic services for accessing and ++ * managing the DWC_usb3 hardware. These services are used by both the ++ * Peripheral Controller Driver and the On The Go Driver. ++ * ++ * This file contains the common interrupt handling functions. ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++/** ++ * This routine enables the Event Buffer interrupt. ++ */ ++static void ena_eventbuf_intr(dwc_usb3_device_t *dev, int bufno) ++{ ++ u32 eventsiz; ++ ++ eventsiz = ++ dwc_rd32(dev, &dev->core_global_regs->geventbuf[bufno].geventsiz); ++ eventsiz &= ~DWC_EVENTSIZ_INT_MSK_BIT; ++ dwc_wr32(dev, &dev->core_global_regs->geventbuf[bufno].geventsiz, ++ eventsiz); ++} ++ ++/** ++ * This routine disables the Event Buffer interrupt. ++ */ ++static void dis_eventbuf_intr(dwc_usb3_device_t *dev, int bufno) ++{ ++ u32 eventsiz; ++ ++ eventsiz = ++ dwc_rd32(dev, &dev->core_global_regs->geventbuf[bufno].geventsiz); ++ eventsiz |= DWC_EVENTSIZ_INT_MSK_BIT; ++ dwc_wr32(dev, &dev->core_global_regs->geventbuf[bufno].geventsiz, ++ eventsiz); ++} ++ ++/** ++ * This routine disables the Event Buffer interrupt and flushes any pending ++ * events from the buffer. ++ */ ++void dwc_usb3_dis_flush_eventbuf_intr(dwc_usb3_device_t *dev, int bufno) ++{ ++ u32 cnt; ++ ++ if (dev->hibernate >= DWC_HIBER_SLEEPING) ++ return; ++ ++ dis_eventbuf_intr(dev, bufno); ++ cnt = dwc_rd32(dev, &dev->core_global_regs->geventbuf[bufno].geventcnt); ++ dwc_wr32(dev, &dev->core_global_regs->geventbuf[bufno].geventcnt, cnt); ++} ++ ++/** ++ * This routine reads the current Event Buffer count. ++ */ ++static int get_eventbuf_count(dwc_usb3_device_t *dev, int bufno) ++{ ++ u32 cnt; ++ ++ cnt = dwc_rd32(dev, &dev->core_global_regs->geventbuf[bufno].geventcnt); ++ return cnt & DWC_EVENTCNT_CNT_BITS; ++} ++ ++/** ++ * This routine writes the Event Buffer count. ++ */ ++static void update_eventbuf_count(dwc_usb3_device_t *dev, int bufno, int cnt) ++{ ++ dwc_wr32(dev, &dev->core_global_regs->geventbuf[bufno].geventcnt, cnt); ++} ++ ++/** ++ * This routine fetches the next event from the Event Buffer. ++ */ ++static u32 get_eventbuf_event(dwc_usb3_device_t *dev, int bufno, int size) ++{ ++ u32 event; ++ ++ event = *dev->event_ptr[bufno]++; ++ if (dev->event_ptr[bufno] >= dev->event_buf[bufno] + size) ++ dev->event_ptr[bufno] = dev->event_buf[bufno]; ++ return event; ++} ++ ++/** ++ * This routine initializes an Event Buffer. ++ */ ++void dwc_usb3_init_eventbuf(dwc_usb3_device_t *dev, int bufno, ++ u32 *addr, unsigned int size, dwc_dma_t dma_addr) ++{ ++ dwc_debug4(dev, "Event buf %d addr 0x%08lx phys 0x%08lx size %d\n", ++ bufno, (unsigned long)addr, (unsigned long)dma_addr, size); ++ dwc_wr32(dev, &dev->core_global_regs->geventbuf[bufno].geventadr_lo, ++ dma_addr & 0xffffffffU); ++#ifdef DWC_64_BIT_ARCH ++ dwc_wr32(dev, &dev->core_global_regs->geventbuf[bufno].geventadr_hi, ++ dma_addr >> 32U & 0xffffffffU); ++#else ++ dwc_wr32(dev, &dev->core_global_regs->geventbuf[bufno].geventadr_hi, 0); ++#endif ++ dwc_wr32(dev, &dev->core_global_regs->geventbuf[bufno].geventsiz, ++ size << 2); ++ dwc_wr32(dev, &dev->core_global_regs->geventbuf[bufno].geventcnt, 0); ++} ++ ++/** ++ * This routine initializes the commmon interrupts. ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ */ ++void dwc_usb3_enable_common_interrupts(dwc_usb3_device_t *dev) ++{ ++ /* Clear any pending interrupts */ ++ dwc_usb3_dis_flush_eventbuf_intr(dev, 0); ++ ++ ena_eventbuf_intr(dev, 0); ++} ++ ++/** ++ * This routine enables the Device mode interrupts. ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ */ ++void dwc_usb3_enable_device_interrupts(dwc_usb3_device_t *dev) ++{ ++ u32 devten; ++ ++ dwc_debug1(dev, "%s()\n", __func__); ++ ++ /* Enable global interrupts */ ++ dwc_usb3_enable_common_interrupts(dev); ++ ++ devten = DWC_DEVTEN_DISCONN_BIT | DWC_DEVTEN_CONNDONE_BIT | ++ DWC_DEVTEN_USBRESET_BIT | DWC_DEVTEN_HIBER_REQ_BIT | ++ DWC_DEVTEN_WKUP_BIT; ++ ++ if (dev->snpsid >= 0x5533230a) ++ devten |= DWC_DEVTEN_U3_L2L1_SUSP_BIT; ++ else ++ devten |= DWC_DEVTEN_ULST_CHNG_BIT; ++ ++ /* Enable device interrupts */ ++ dwc_wr32(dev, &dev->pcd.dev_global_regs->devten, devten); ++ ++ dwc_debug2(dev, "%s() devten=%0x\n", __func__, ++ dwc_rd32(dev, &dev->pcd.dev_global_regs->devten)); ++} ++ ++/** ++ * This routine handles all interrupt events. It is called by the ++ * dwc_usb3_irq() interrupt handler routine, and by the enter_hibernation() ++ * routine after clearing the Run/Stop bit and waiting for the Halted bit to ++ * be set. ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ * return 1 if an interrupt event was seen, 0 if not. ++ */ ++int dwc_usb3_handle_event(dwc_usb3_device_t *dev) ++{ ++ dwc_usb3_pcd_t *pcd = &dev->pcd; ++ u32 event; ++ int count, intr, physep, i; ++ int ret = 0; ++ static int msg_cnt; ++ ++ count = get_eventbuf_count(dev, 0); ++ if (count) ++ dwc_debug1(dev, "Interrupt count %d\n", count); ++ ++ if ((count & DWC_EVENTCNT_CNT_BITS) == ++ (0xffffffff & DWC_EVENTCNT_CNT_BITS) || ++ count >= DWC_EVENT_BUF_SIZE * 4) { ++ if (msg_cnt > 100) { ++ msg_cnt = 100; ++ dwc_warn0(dev, "Too many bad events!!\n"); ++ } else { ++ msg_cnt++; ++ dwc_warn1(dev, ++ "Bad event count 0x%01x in dwc_usb3_irq() !!\n", ++ count); ++ } ++ ++ dis_eventbuf_intr(dev, 0); ++ update_eventbuf_count(dev, 0, count); ++ count = 0; ++ } ++ ++ if (!count) ++ goto out; ++ ret = 1; ++ ++#if defined(CONFIG_IPMATE) || defined(COSIM) || defined(VIRTIO_MODEL) ++ dis_eventbuf_intr(dev, 0); ++#endif ++ ++ for (i = 0; i < count; i += 4) { ++ dwc_debug1(dev, "Event addr 0x%08lx\n", ++ (unsigned long)dev->event_ptr[0]); ++ event = get_eventbuf_event(dev, 0, DWC_EVENT_BUF_SIZE); ++ update_eventbuf_count(dev, 0, 4); ++ if (event == 0) { ++ dwc_print0(dev, "## Null event! ##\n"); ++ ++ /* Ignore null events */ ++ continue; ++ } ++ ++ //dwc_debug1(dev, "Interrupt event 0x%08x\n", event); ++ if (event & DWC_EVENT_NON_EP_BIT) { ++ //dwc_debug0(dev, "Non-EP interrupt event\n"); ++ intr = event & DWC_EVENT_INTTYPE_BITS; ++ ++ if (intr == ++ DWC_EVENT_DEV_INT << DWC_EVENT_INTTYPE_SHIFT) { ++ dwc_debug1(dev, ++ "## Device interrupt 0x%08x ##\n", ++ event); ++ ret = dwc_usb3_handle_dev_intr(pcd, event); ++ if (ret) { ++ ret = 2; ++ goto out; ++ } ++ ret = 1; ++ } else { ++ dwc_debug1(dev, "## Core interrupt 0x%08x ##\n", ++ event); ++ ++ /* @todo Handle non-Device interrupts ++ * (OTG, CarKit, I2C) ++ */ ++ } ++ } else { ++ physep = event >> DWC_DEPEVT_EPNUM_SHIFT & ++ DWC_DEPEVT_EPNUM_BITS >> DWC_DEPEVT_EPNUM_SHIFT; ++ dwc_debug2(dev, ++ "## Physical EP%d interrupt 0x%08x ##\n", ++ physep, event); ++ dwc_debug2(dev, "[EP%d] %s\n", physep >> 1 & 0xf, ++ physep & 1 ? "IN" : "OUT"); ++ dwc_usb3_handle_ep_intr(pcd, physep, event); ++ } ++ } ++ ++#if defined(CONFIG_IPMATE) || defined(COSIM) || defined(VIRTIO_MODEL) ++ ena_eventbuf_intr(dev, 0); ++#endif ++out: ++ return ret; ++} ++ ++#ifdef SSIC ++ ++static int ssic_read_attr(dwc_usb3_device_t *dev, u32 aid, u32 *aval) ++{ ++ int i; ++ u32 sevt = 0; ++ u32 sctl = 0; ++ u32 data = 0; ++ ++ dwc_debug(dev, "%s: aid=0x%0x\n", __func__, aid); ++ ++ sctl |= DWC_SCTL_GO_ACC_BIT; ++ sctl |= (aid << DWC_SCTL_AID_SHIFT); ++ ++ dwc_debug(dev, "%s: writing sctl=0x%08x\n", __func__, sctl); ++ dwc_wr32(dev, &dev->ssic_regs->sctl[0], sctl); ++ ++ i = 100000; ++ do { ++ sevt = dwc_rd32(dev, &dev->ssic_regs->sevt[0]); ++ if (sevt & DWC_SEVT_LACC_CMPLT_BIT) ++ break; ++ dwc_udelay(dev, 1); ++ } while (--i > 0); ++ ++ if (i == 0) { ++ dwc_error(dev, "%s: lacc timeout\n", __func__); ++ return -1; ++ } ++ ++ data = (sevt & DWC_SEVT_RDATA_RCVD_BITS) >> DWC_SEVT_RDATA_RCVD_SHIFT; ++ if (aval) ++ *aval = (u8)data; ++ ++ dwc_debug(dev, "%s: rdata=%02x\n", __func__, data); ++ return 0; ++} ++ ++static int ssic_write_attr(dwc_usb3_device_t *dev, u32 aid, u32 aval) ++{ ++ int i; ++ u32 sctl = 0; ++ u32 sevt = 0; ++ ++ dwc_debug(dev, "%s: aid=0x%0x aval=%0x\n", __func__, aid, aval); ++ sctl |= (DWC_SCTL_GO_ACC_BIT | DWC_SCTL_RD_WR_N_BIT); ++ sctl |= (aid << DWC_SCTL_AID_SHIFT); ++ sctl |= (aval << DWC_SCTL_ADATA_SHIFT); ++ dwc_debug(dev, "%s: writing sctl=0x%08x\n", __func__, sctl); ++ dwc_wr32(dev, &dev->ssic_regs->sctl[0], sctl); ++ ++ i = 100000; ++ do { ++ sevt = dwc_rd32(dev, &dev->ssic_regs->sevt[0]); ++ if (sevt & DWC_SEVT_LACC_CMPLT_BIT) ++ break; ++ dwc_udelay(dev, 1); ++ } while (--i > 0); ++ ++ if (i == 0 ) { ++ dwc_error(dev, "%s: lacc timeout\n", __func__); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ssic_rrap_response(dwc_usb3_device_t *dev, int write, u32 aid, u32 aeid, u32 aval) ++{ ++ int i; ++ u32 sctl = 0; ++ u32 sevt = 0; ++ ++ dwc_debug(dev, "%s: aid=0x%0x, aeid=%0x, aval=%0x, write=%d\n", ++ __func__, aid, aeid, aval, write); ++ ++ sctl |= (DWC_SCTL_GO_ACC_BIT | DWC_SCTL_RACC_BIT); ++ if (write) ++ sctl |= (DWC_SCTL_RD_WR_N_BIT); ++ ++ sctl |= ((aid << DWC_SCTL_AID_SHIFT) & DWC_SCTL_AID_BITS); ++ sctl |= ((aeid << DWC_SCTL_EAID_SHIFT) & DWC_SCTL_EAID_BITS); ++ sctl |= ((aval << DWC_SCTL_ADATA_SHIFT) & DWC_SCTL_ADATA_BITS); ++ ++ dwc_debug(dev, "%s: writing sctl=0x%08x\n", __func__, sctl); ++ dwc_wr32(dev, &dev->ssic_regs->sctl[0], sctl); ++ ++ i = 100000; ++ do { ++ sevt = dwc_rd32(dev, &dev->ssic_regs->sevt[0]); ++ if (sevt & DWC_SEVT_RCMD_RES_SENT_BIT) ++ break; ++ dwc_udelay(dev, 1); ++ } while(--i > 0); ++ ++ if (i == 0) { ++ dwc_error(dev, "%s: racc response send timeout\n", __func__); ++ return -1; ++ } ++ ++ dwc_debug(dev, "%s: racc response sent result=0x%0x\n", __func__, ++ (sevt & DWC_SEVT_RACC_RESULT_BITS) >> DWC_SEVT_RACC_RESULT_SHIFT); ++ ++ return 0; ++} ++ ++static int ssic_cfg_done(dwc_usb3_device_t *dev) ++{ ++ u32 temp; ++ int usec = 100000; ++ dwc_debug1(dev, "%s\n", __func__); ++ ++ temp = dwc_rd32(dev, &dev->ssic_regs->sctl[0]); ++ temp |= DWC_SCTL_CFG_DONE_BIT; ++ dwc_wr32(dev, &dev->ssic_regs->sctl[0], temp); ++ ++ do { ++ u32 result = dwc_rd32(dev, &dev->core_global_regs->gusb3rmmictl[0]); ++ if (((result & DWC_RMMICTL_MPHY_STATE_BITS) >> ++ DWC_RMMICTL_MPHY_STATE_SHIFT) != DWC_MPHY_STATE_HIBERN8) { ++ break; ++ } ++ ++ dwc_udelay(dev, 1); ++ usec -= 1; ++ } while (usec > 0); ++ ++ if (!usec) { ++ dwc_error(dev, "%s: cfg done timed out\n", __func__); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int dwc_usb3_handle_ssic_event(dwc_usb3_device_t *dev) ++{ ++ int retval = -1; ++ u32 sevt = dwc_rd32(dev, &dev->ssic_regs->sevt[0]); ++ u32 temp = sevt; ++ dwc_debug(dev, "%s: SSIC Event 0x%08x\n", __func__, sevt); ++ ++ sevt &= (DWC_SEVT_ROM_INIT_CMPLT_BIT | ++ DWC_SEVT_LACC_CMPLT_BIT | ++ DWC_SEVT_RCMD_RES_RCVD_BIT | ++ DWC_SEVT_RCMD_RES_SENT_BIT | ++ DWC_SEVT_MPHY_ST_CHNG_BIT | ++ DWC_SEVT_OK_STRT_RRAP_BIT | ++ DWC_SEVT_RRAP_ERROR_BIT); ++ ++ dwc_wr32(dev, &dev->ssic_regs->sevt[0], sevt); ++ ++ sevt = temp; ++ ++ if (sevt & DWC_SEVT_MPHY_ST_CHNG_BIT) { ++ u32 rmmictl = dwc_rd32(dev, &dev->core_global_regs->gusb3rmmictl[0]); ++ if (((rmmictl & DWC_RMMICTL_MPHY_STATE_BITS) >> DWC_RMMICTL_MPHY_STATE_SHIFT) == ++ DWC_MPHY_STATE_HIBERN8) { ++ ssic_cfg_done(dev); ++ } ++ } ++ ++ if (sevt & DWC_SEVT_RCMD_RES_RCVD_BIT) { ++ u8 rdata = (sevt & DWC_SEVT_RDATA_RCVD_BITS) >> DWC_SEVT_RDATA_RCVD_SHIFT; ++ u8 rladdr = (sevt & DWC_SEVT_RLADDR_RCVD_BITS) >> DWC_SEVT_RLADDR_RCVD_SHIFT; ++ u8 ruaddr = (sevt & DWC_SEVT_RUADDR_RCVD_BITS) >> DWC_SEVT_RUADDR_RCVD_SHIFT; ++ int read = !!(sevt & DWC_SEVT_READ_RCVD_BIT); ++ ++ dwc_debug4(dev, "rdata=0x%02x, rladdr=0x%02x, ruaddr=0x%02x, read=%d\n", ++ rdata, rladdr, ruaddr, read); ++ ++ if (ruaddr <= 0x3) { ++ if (read) ++ dwc_error(dev, "Read not handled\n"); ++ else ++ dwc_error(dev, "Write not handled\n"); ++ } else if (ruaddr == 0x4) { ++ /* DSP_DISCONNECT */ ++ if (rladdr == 0x0) { ++ dwc_error(dev, "DSP_DISCONNECT not handled\n"); ++ /* CONFIGURE_FOR_HS */ ++ } else if (rladdr == 0x1) { ++ dwc_debug(dev, "CONFIGURE_FOR_HS\n"); ++ ssic_rrap_response(dev, !read, rladdr, ruaddr, 0x0); ++ ssic_write_attr(dev, 0x21, 0x2); ++ ssic_write_attr(dev, 0xa1, 0x2); ++ retval = 0; ++ /* BURST_CLOSURE */ ++ } else if (rladdr == 0x2) { ++ dwc_debug(dev, "BURST_CLOSURE\n"); ++ ssic_rrap_response(dev, !read, rladdr, ruaddr, 0x0); ++ ssic_cfg_done(dev); ++ /* DISABLE_SCRAMBLING */ ++ } else if (rladdr == 0x3) { ++ dwc_error(dev, "DISABLE_SCRAMBLING not handled\n"); ++ /* DISABLE_STALL_IN_U0 */ ++ } else if (rladdr == 0x4) { ++ dwc_error(dev, "DISABLE_STALL_IN_U0 not handled\n"); ++ /* TEST_MODE */ ++ } else if (rladdr == 0xff) { ++ dwc_error(dev, "DISABLE_TEST_MODE not handled\n"); ++ } else { ++ dwc_error(dev, "UNKNOWN\n"); ++ } ++ } else if (ruaddr == 0x5) { ++ dwc_error(dev, "UNKNOWN\n"); ++ } else if (ruaddr == 0x6) { ++ dwc_error(dev, "UNKNOWN\n"); ++ } else { ++ dwc_error(dev, "UNKNOWN\n"); ++ } ++ } ++ ++ return retval; ++} ++ ++#endif ++ ++ ++/** ++ * This is the common interrupt handler routine. ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ * @param irq IRQ number passed in by Linux kernel. ++ * @return 1 if an interrupt event was seen, 0 if not. ++ */ ++int dwc_usb3_irq(dwc_usb3_device_t *dev, int irq) ++{ ++ int state, temp, ret; ++ ++#ifdef SSIC ++ u32 gsts, sevt; ++ ++ gsts = dwc_rd32(dev, &dev->core_global_regs->gsts); ++ sevt = dwc_rd32(dev, &dev->ssic_regs->sevt[0]); ++ dwc_debug(dev, "%s: gsts=0x%08x, sevt=0x%08x\n", __func__, gsts, sevt); ++#endif ++ ++ if (!dev->cmn_irq_installed) ++ return 0; ++ ++#ifdef SSIC ++ gsts = dwc_rd32(dev, &dev->core_global_regs->gsts); ++ if (gsts & DWC_GSTS_SSIC_IP_BIT) { ++ dwc_usb3_handle_ssic_event(dev); ++ ret = 1; ++ } ++#endif ++ ++ state = dev->hibernate; ++ ++ if (state != DWC_HIBER_SLEEPING && dev->snpsid >= 0x5533230a && ++ dev->hiber_wait_u0) { ++ /* Handle waiting for U0 after requesting link state RECOVERY, ++ * because we don't have the link state change event enabled. ++ * We also do this in dwc_wait_pme_thread() in case an event ++ * doesn't come. ++ */ ++ temp = dwc_usb3_pcd_get_link_state(&dev->pcd); ++ dev->pcd.link_state = temp; ++ dwc_debug1(dev, "intr WAIT_U0 state=%d\n", temp); ++ ++ if (temp == DWC_LINK_STATE_U0) { ++ dwc_debug0(dev, "intr WAIT_U0 setting speed\n"); ++ dev->pcd.speed = dwc_usb3_get_device_speed(&dev->pcd); ++ if (dev->pcd.remote_wakeup_enable) ++ dwc_usb3_pcd_remote_wake(&dev->pcd, 0); ++ dev->hiber_wait_u0 = 0; ++ } ++ } ++ ++ if (state >= DWC_HIBER_SLEEPING) { ++ if (state == DWC_HIBER_WAIT_U0 || ++ state == DWC_HIBER_SS_DIS_QUIRK) { ++ dev->hibernate = DWC_HIBER_AWAKE; ++ } else { ++ if (dev->pme_ready) { ++#ifndef SELA_PLATFORM ++ ret = dwc_usb3_handle_pme_intr(dev); ++ return ret; ++#else ++ return 1; ++#endif ++ } else { ++ if (state != DWC_HIBER_WAIT_LINK_UP) ++ dwc_info0(dev, "Intr in hibernate but" ++ " pme_ready not set\n"); ++ return 1; ++ } ++ } ++ } ++ ++ ret = dwc_usb3_handle_event(dev); ++ if (ret == 2) ++ ret = 1; ++ ++#ifdef SSIC ++ gsts = dwc_rd32(dev, &dev->core_global_regs->gsts); ++ sevt = dwc_rd32(dev, &dev->ssic_regs->sevt[0]); ++ dwc_debug(dev, "%s DONE: gsts=0x%08x, sevt=0x%08x\n", __func__, gsts, sevt); ++#endif ++ ++ return ret; ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/dev.h b/drivers/usb/gadget/udc/hiudc3/dev.h +new file mode 100644 +index 0000000..5f6a303 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/dev.h +@@ -0,0 +1,210 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/dev.h $ ++ * $Revision: #12 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef _DWC_DEV_H_ ++#define _DWC_DEV_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** @file ++ * This file contains the interface to the Linux driver. ++ */ ++ ++/** ++ * This structure is a wrapper that encapsulates the driver components used to ++ * manage a single DWC_usb3 controller. ++ */ ++typedef struct dwc_usb3_device { ++ /** ++ * OS-specific stuff. KEEP THIS AT THE VERY BEGINNING OF THE DEVICE ++ * STRUCT. OSes such as FreeBSD and NetBSD require this. ++ */ ++ ++ /** Base address returned from ioremap() */ ++ volatile u8 __iomem *base; ++ volatile u8 __iomem *gasket_base; ++ ++ /** Offset to 'gasket' registers (Synopsys FPGA only) */ ++ int gasket_ofs; ++ ++#if defined(__linux__) || defined(LINUXTEST) ++ /** Device context */ ++ struct device *dev; ++ ++ /** Start address of IOMEM region */ ++ resource_size_t rsrc_start; ++ resource_size_t gasket_start; ++ ++ /** Length of IOMEM region */ ++ resource_size_t rsrc_len; ++ resource_size_t gasket_len; ++ ++ /** IRQ resource */ ++ int irq; ++ ++ struct task_struct *pme_thread; ++#endif ++ /* ==== End of OS-specific stuff ==== */ ++ ++ /** Count of threads inside Gadget API */ ++ int hiber_cnt; ++ ++ /** Hibernation state */ ++ int hibernate; ++ ++#define DWC_HIBER_AWAKE 0 ++#define DWC_HIBER_ENTER_NOSAVE 1 ++#define DWC_HIBER_ENTER_SAVE 2 ++#define DWC_HIBER_SLEEPING 3 ++#define DWC_HIBER_WAIT_LINK_UP 4 ++#define DWC_HIBER_WAIT_U0 5 ++#define DWC_HIBER_SS_DIS_QUIRK 6 ++ ++ int pme_ready; ++ ++ /** PCD structure */ ++ struct dwc_usb3_pcd pcd; ++ ++ /** Value from SNPSID register */ ++ u32 snpsid; ++ ++ /** Parameters that define how the core should be configured */ ++ dwc_usb3_core_params_t *core_params; ++ ++ /** Core Global registers starting at offset 100h */ ++ dwc_usb3_core_global_regs_t __iomem *core_global_regs; ++ ++#ifdef SSIC ++ dwc_usb3_ssic_regs_t __iomem *ssic_regs; ++#endif ++ ++ /** @{ */ ++#define DWC_EVENT_BUF_SIZE 256 // size in dwords ++#define DWC_NUM_EVENT_BUFS 1 ++ /** Event Buffers for receiving interrupts. Up to 32 buffers are ++ * supported by the hardware, but we only use 1. ++ */ ++ u32 *event_ptr[DWC_NUM_EVENT_BUFS]; ++ u32 *event_buf[DWC_NUM_EVENT_BUFS]; ++ dwc_dma_t event_buf_dma[DWC_NUM_EVENT_BUFS]; ++ /** @} */ ++ ++ /** Total RAM for FIFOs (Bytes) */ ++ u16 total_fifo_size; ++ ++ /** Size of Rx FIFO (Bytes) */ ++ u16 rx_fifo_size; ++ ++ /** @{ */ ++ /** Hardware Configuration - stored here for convenience */ ++ u32 hwparams0; ++ u32 hwparams1; ++ u32 hwparams2; ++ u32 hwparams3; ++ u32 hwparams4; ++ u32 hwparams5; ++ u32 hwparams6; ++ u32 hwparams7; ++ u32 hwparams8; ++ /** @} */ ++ ++ /** @{ */ ++ /** Register state, saved across core hibernation */ ++ u32 dcfg_save; ++ u32 dctl_save; ++ u32 gtxfifosiz0_save; ++ u32 gtxfifosiz1_save; ++ u32 gtxfifosiz2_save; ++ u32 gtxfifosiz3_save; ++ u32 grxfifosiz0_save; ++ u32 guctl_save; ++ u32 guctl1_save; ++ /** @} */ ++ ++ /** @{ */ ++ /** Hooks for customizing device initialization. See ++ * dwc_usb3_pcd_device_init() in cil.c to see how these work. ++ */ ++ void (*soft_reset_hook)(struct dwc_usb3_device *dev, int softrst, int rstor); ++ void (*phy_config_hook)(struct dwc_usb3_device *dev, int softrst, int rstor); ++ void (*fifo_sizing_hook)(struct dwc_usb3_device *dev, int softrst, int rstor); ++ void (*gctl_init_hook)(struct dwc_usb3_device *dev, int softrst, int rstor); ++ void (*set_address_hook)(struct dwc_usb3_device *dev, int softrst, int rstor); ++ void (*ep0_start_hook)(struct dwc_usb3_device *dev, int softrst, int rstor); ++ /** @} */ ++ ++ /** Value to write into the DCTL HIRD_Thresh field on register ++ * initialization. If 0 then a default value of 0x1c will be used. ++ */ ++ u32 hird_thresh; ++ ++ /** Values to write into GSBUSCFG0 and GSBUSCFG1 on initialization or ++ * when exiting from hibernation. 'program_gsbuscfg' below must also be ++ * set to 1 to enable the writing of these values. ++ */ ++ u32 gsbuscfg0; ++ u32 gsbuscfg1; ++ ++ /** True if common functionality has been initialized */ ++ unsigned int cmn_initialized : 1; ++ ++ /** True if Gadget has been initialized */ ++ unsigned int gadget_initialized : 1; ++ ++ /** True if PCD has been initialized */ ++ unsigned int pcd_initialized : 1; ++ ++ /** True if common IRQ handler has been installed */ ++ unsigned int cmn_irq_installed : 1; ++ ++ /** True if sysfs functions have been installed */ ++ unsigned int sysfs_initialized : 1; ++ ++ /** True if waiting for connect before resuming from hibernation */ ++ unsigned int hiber_wait_connect : 1; ++ ++ /** True if waiting for U0 state before sending remote wake */ ++ unsigned int hiber_wait_u0 : 1; ++ ++ /** True if GBUSCFG0/GBUSCFG1 should be written with the above ++ * values when exiting hibernation */ ++ unsigned int program_gsbuscfg : 1; ++} dwc_usb3_device_t; ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_DEV_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/dox.h b/drivers/usb/gadget/udc/hiudc3/dox.h +new file mode 100644 +index 0000000..d58532b +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/dox.h +@@ -0,0 +1,615 @@ ++#ifndef _DWC_DOX_H_ ++#define _DWC_DOX_H_ ++ ++/** ++ * @file ++ * ++ * image html synopsys.png "Synopsys Logo" ++ * image latex synopsys.eps "Synopsys Logo" ++ * ++ * This file contains the Doxygen comments that create the Functional ++ * Specification document for the driver. ++ * ++ * @page intro_chapter Introduction ++ * ++ * The DWC_usb3 Subsystem Version 2.90b is a host and peripheral controller ++ * that is compliant with the USB 3.0 specification. The peripheral controller ++ * has a Synopsys-proprietary programming interface, which bears some ++ * resemblance to the Intel xHCI host interface. This is because the controller ++ * supports xHCI when acting as a host, and some of the logic is shared between ++ * the host and peripheral modes. ++ * ++ * The 2.90b version supports SuperSpeed USB 3.0, and HS and FS USB 2.0. ++ * ++ * Source level driver software will be shipped with the controller. Customers ++ * can use this software as a reference for developing drivers for other ++ * platforms or operating environments. ++ * ++ * @section scope_sec Scope ++ * ++ * This document defines the functionality of the driver software that will ++ * be shipped with the DWC_usb3 Version 2.90b controller. It specifies what ++ * the software will do, not how it will accomplish these tasks. ++ * ++ * The driver software for DWC_usb3 Version 2.90b will support only Linux as ++ * its operating environment. Therefore, this document currently defines ++ * behavior under Linux. However, it is desired that many aspects of the driver ++ * software can be easily ported to other operating systems or environments, ++ * and towards that end, it has been written in a way to ease the porting ++ * effort (see section 1.2.3 @ref env_and_hw_deps_subsec). ++ * ++ * For subsequent releases of the DWC_usb3 controller, this document will be ++ * modified to include updates required in the driver software. These updates ++ * may be made to support new features in the controller or to support new ++ * operating environments. ++ * ++ * @section overview_sec Overview ++ * ++ * @subsection sw_arch_subsec Software Architecture ++ * ++ * The DWC_usb3 controller can act as either a host or a peripheral on the ++ * USB. Figure 1.1 shows the software architecture for the DWC_usb3 controller. ++ * ++ * There are two stacks in the software architecture - the Host Stack and the ++ * Peripheral Stack. Brief descriptions of each of these stacks are given ++ * below to set the context for the driver software. Since the host part of ++ * the DWC_usb3 controller conforms to the xHCI standard, the xHCI HCD ++ * driver in the host OS will provide the host stack functionality. Therefore, ++ * only the peripheral stack functionality will be implemented in the DWC_usb3 ++ * controller driver software. See the OTG 3.0 user manual for a description ++ * of the DRD/OTG functionality. ++ * ++ * The Host Stack is used to request transfers to or from USB devices when ++ * the DWC_usb3 component is acting in the role of a host. The top-level ++ * component in this stack is a Host Application that acts as a producer or ++ * consumer of data. The Class Drivers translate application requests into a ++ * protocol specific to a certain type (or class) of devices. They use I/O ++ * Request Packets (IRPs) to transfer data to or from USB devices. The USB ++ * Driver (USBD) provides services to allow multiple Class Drivers to ++ * configure, control, and exchange data with their associated devices. The ++ * Hub Driver is also involved in the configuration process. The USBD handles ++ * all communication with the Host Controller Driver (HCD), which must ++ * understand the hardware architecture of the host controller. The HCD ++ * interacts with the host controller hardware to execute the USB transfers ++ * requested by the USBD. ++ * ++ * The Peripheral Stack is used to respond to requests received from a USB ++ * host when the DWC_usb3 component is acting in the role of a peripheral. ++ * The Peripheral Function is the sink or source of data requested by the host. ++ * The Function Driver handles some USB requests directly. It also provides ++ * endpoint read/write data interfaces and notification services to the ++ * Peripheral Function. The Peripheral Controller Driver (PCD) understands ++ * the hardware architecture of the peripheral controller. The PCD interacts ++ * with the peripheral controller hardware to transfer data via the USB and ++ * notifies the Function Driver of USB requests. ++ * ++ * @image html sw_stack.png "Figure 1.1: Generic USB Software Architecture" ++ * @image latex sw_stack.eps "Generic USB Software Architecture" width=6.50in ++ * ++ * @subsection driver_sw_comp_subsec Driver Software Components ++ * ++ * There are two main driver software components - the Peripheral Controller ++ * Driver and the Core Interface Layer (CIL). Basic functionality of the PCD ++ * is described in section 1.2.1 @ref sw_arch_subsec above. A little more ++ * elaboration of the PCD and a description of the CIL are given below. ++ * ++ * The PCD and CIL can be viewed as a hardware abstraction layer. In other ++ * words, the Function Driver does not know or care about the underlying ++ * hardware of the peripheral controller. It merely transfers data and ++ * transmits commands via a software interface with the PCD. Changes made to ++ * the peripheral controller do not require changes to this interface ++ * (although the internal operation of the PCD and CIL would have to be ++ * modified). ++ * ++ * The CIL provides common services for accessing and managing the DWC_usb3 ++ * hardware. These services include initialization, mapping of registers, ++ * interrupt control, and low-level access to the CSRs and DMA descriptors ++ * (TRBs). ++ * ++ * @subsection env_and_hw_deps_subsec Environment and Hardware Dependencies ++ * ++ * As noted above, changes to the underlying hardware will require changes to ++ * the internal operation of the driver components. This is true for both of the ++ * driver components (PCD and CIL). Both of these components are aware of the ++ * internal architecture of the controller, so each of these components may need ++ * to change to adapt to any hardware changes. However, for a given operating ++ * system (such as Linux), the layers above the PCD would not require any ++ * changes. They would continue to use the same API to communicate with the PCD. ++ * That is why the PCD is considered a hardware abstraction layer. ++ * ++ * Most of the driver components are designed to be reasonably operating system ++ * independent, by making the low-level routines OS-agnostic and placing them ++ * into separate source files. The Linux-specific code is likewise contained in ++ * a few separate source files. That should simplify porting the driver software ++ * between operating environments. ++ * ++ * Starting with the 2.20a release, the software provides support for an ++ * additional platform, called No-OS. No-OS means the code contains no ++ * OS-specific dependencies at all. See the Porting Guide document for more ++ * information about the No-OS platform, and for general information about ++ * porting the driver to platforms other than Linux. ++ * ++ * Note, however, that the driver has only been completely tested under Linux ++ * 3.6.3. ++ * ++ * @section deliverables_sec Deliverables ++ * ++ * This section describes the driver components, documentation, and demo ++ * software included with the SuperSpeed USB3 Controller Linux Driver ++ * Software. ++ * ++ * @subsection driver_sw_subsec Driver Software ++ * ++ * All driver components will be developed for the Linux operating ++ * system on a PC + HAPS platform. The following components will be ++ * packaged and released: ++ * ++ * - Peripheral Controller Driver source code ++ * - Core Interface Layer source code ++ * - USB Attached SCSI Protocol (UASP) Gadget Driver source code ++ * ++ * These components will support the following features: ++ * ++ * - Control and Bulk transfers in Peripheral Mode using the legacy Mass ++ * Storage Class protocol ++ * - Control and Bulk transfers in Peripheral Mode using the new USB Attached ++ * SCSI protocol ++ * ++ * New for the 2.50b release: ++ * ++ * - "Bringup" driver source code. This is a stripped-down version of the PCD, ++ * with just enough logic for testing the registers, event interrupt, and DMA ++ * loopback features of the controller. It is intended to verify the basic ++ * functionality of the controller without needing any USB-specific support ++ * code in the OS. ++ * ++ * The bringup driver source, along with a README file describing how to build ++ * and run the driver, can be found in the bringup/ directory. ++ * ++ * @subsection sw_doc_subsec Software Documentation ++ * ++ * Source documentation in HTML or PDF format will be delivered for the driver ++ * components released with the DWC_usb3 controller. This will include ++ * documentation of the API for each of the driver components. ++ * ++ * Instructions for acquiring and installing the Linux kernel sources and ++ * applying any required patches will be included in the release. ++ * ++ * @subsection demo_sw_subsec Demo Software ++ * ++ * No need for special demo software is anticipated for the 2.90b release. ++ * The demo will consist of using the controller as a mass-storage device, ++ * and copying/streaming large files to and from the device from a host PC. ++ * ++ * @subsection bin_subsec Binaries ++ * ++ * No binaries will be distributed with the 2.90b release. ++ * ++ */ ++ ++/** ++ * @page env_chapter Environment Specific Features ++ * ++ * This section defines functionality of the driver software that depends on ++ * a particular operating system or operating environment. Since Linux is the ++ * only environment currently supported, the Linux implementation is described ++ * here. In the future, this section may include descriptions of wrappers to ++ * glue generic driver components into various operating system environments. ++ * ++ * @section linux_arch_sec Linux Architecture ++ * ++ * The Linux architecture is very similar to the generic DWC_usb3 software ++ * architecture shown in Figure 1.1. Figure 2.1 shows the Linux version of this ++ * architecture. ++ * ++ * The USB Core component in Linux includes the USB Driver functionality plus a ++ * framework to support Host Controller Drivers. The HCD framework is intended ++ * to allow different HCDs to share code. This makes it easier to write new ++ * HCDs and reduces the number of bugs. Only functionality that needs to be ++ * aware of the underlying hardware is implemented in the HCD. This is ++ * accomplished by defining the hc_driver interface. This interface consists of ++ * the functions and data that must be supplied by an HCD in order to plug into ++ * the Linux HCD framework. The OS-supplied xHCI HCD will implement the ++ * hc_driver interface. ++ * ++ * In Linux, peripheral devices are called gadgets. So the Peripheral Function ++ * is called a Gadget Application and the Function Driver is called a Gadget ++ * Driver. Linux defines a Gadget API which is the interface between the Gadget ++ * Driver and the Peripheral Controller Driver. Similar to the hc_driver ++ * interface, the Gadget API is intended to isolate hardware-specific behavior ++ * to the Peripheral Controller Driver. The Linux version of the DWC_usb3 PCD ++ * will implement the Gadget API. See section 4.2.1 @ref gadget_api_subsec for ++ * more information. ++ * ++ * The API of the Core Interface Layer is independent of the operating system. ++ * Services provided by this component are the same regardless of the operating ++ * system. ++ * ++ * @image html linux_stack.png "Figure 2.1: Linux USB Software Architecture with UASP Gadget" ++ * @image latex linux_stack.eps "Linux USB Software Architecture with UASP Gadget" width=6.50in ++ * ++ * @section linux_driver_sec Linux Driver Module ++ * ++ * All of the driver components are contained in a single driver module. The ++ * module wrapper code, including the module initialization and cleanup ++ * functions, is Linux dependent. This section describes the Linux module ++ * functionality. ++ * ++ * The dwc_usb3 module provides the initialization and cleanup entry points for ++ * the DWC_usb3 driver. This module will be dynamically installed after Linux is ++ * booted using the insmod command. When the module is installed, the ++ * dwc_usb3_driver_init function is called. When the module is removed (using ++ * rmmod), the dwc_usb3_driver_exit function is called. ++ * ++ * This module also defines a data structure for the dwc_usb3_device. This ++ * structure allows the DWC_usb3 driver to comply with the standard Linux driver ++ * model in which devices and drivers are registered with a bus driver. This has ++ * the benefit that Linux can expose attributes of the driver and device in its ++ * special sysfs file system. Users can then read or write files in this file ++ * system to perform diagnostics on the driver components or the device. ++ * ++ * @subsection data_struct_subsec Data Structures ++ * ++ * These are the main data structures used by the DWC_usb3 driver module. ++ * ++ * - struct dwc_usb3_device ++ * ++ * @subsection init_and_clean_subsec Initialization and Cleanup Functions ++ * ++ * These are the initialization and cleanup functions of the DWC_usb3 driver ++ * module. ++ * ++ * - dwc_usb3_driver_init() ++ * - dwc_usb3_driver_probe() ++ * - dwc_usb3_driver_remove() ++ * - dwc_usb3_driver_exit() ++ * ++ */ ++ ++/** ++ * @page cil_chapter Core Interface Layer ++ * ++ * The Core Interface Layer provides basic services for accessing and managing ++ * the DWC_usb3 hardware. The CIL also manages the memory map for the core so ++ * that the PCD doesn't have to do this separately. ++ * ++ * @section csr_sec Control and Status Register Structures ++ * ++ * The following structures define the size and relative field offsets for ++ * each register in the DWC_usb3 controller. These structures are not created ++ * in memory through normal memory allocation methods. After mapping memory ++ * for the controller into the OS memory space, these structures are overlaid ++ * on the mapped memory by setting the appropriate base address for each ++ * structure. Each register can then be accessed via its address in one of ++ * these structures. ++ * ++ * The precise method for accessing registers is OS-specific. It may be as ++ * simple as directly reading or writing a register field in one of these ++ * structures. Or it may require passing the mapped register address to a ++ * read/write method defined by the OS. ++ * ++ * @subsection core_glbl_regs_subsec Core Global Registers Structure ++ * ++ * The following structure defines the size and relative field offsets for ++ * the Core Global registers. ++ * ++ * - struct dwc_usb3_core_global_regs ++ * ++ * @subsection dev_glbl_regs_subsec Peripheral Mode Global Registers Structure ++ * ++ * The following structure defines the size and relative field offsets for ++ * the Peripheral Mode Global Registers. ++ * ++ * - struct dwc_usb3_dev_global_regs ++ * ++ * @subsection dev_ep_regs_subsec Peripheral Mode Per-Endpoint Registers Structures ++ * ++ * The following structure defines the size and relative field offsets for ++ * the Peripheral Mode Per-Endpoint Registers. There is one set of these ++ * registers for each endpoint that is instantiated in the core. ++ * ++ * - struct dwc_usb3_dev_ep_regs ++ * ++ * @section cil_func_sec Functions ++ * ++ * These are the main functions provided by the CIL. ++ * ++ * - dwc_usb3_pcd_common_init() ++ * - dwc_usb3_pcd_common_remove() ++ * - dwc_usb3_pcd_device_init() ++ * - dwc_usb3_enable_common_interrupts() ++ * - dwc_usb3_enable_device_interrupts() ++ * - dwc_usb3_mode() ++ * - dwc_usb3_is_device_mode() ++ * - dwc_usb3_is_host_mode() ++ * - dwc_usb3_dump_global_registers() ++ * - dwc_usb3_fill_desc() ++ * - dwc_usb3_start_desc_chain() ++ * - dwc_usb3_end_desc_chain() ++ * - dwc_usb3_enable_desc() ++ * - dwc_usb3_xmit_fn_remote_wake() ++ * - dwc_usb3_dep_cfg() ++ * - dwc_usb3_dep_xfercfg() ++ * - dwc_usb3_dep_sstall() ++ * - dwc_usb3_dep_cstall() ++ * - dwc_usb3_dep_startxfer() ++ * - dwc_usb3_dep_updatexfer() ++ * - dwc_usb3_dep_endxfer() ++ * - dwc_usb3_dep_startnewcfg() ++ * - dwc_usb3_enable_ep() ++ * - dwc_usb3_disable_ep() ++ * - dwc_usb3_get_device_speed() ++ * - dwc_usb3_get_frame() ++ * - dwc_usb3_pcd_get_link_state() ++ * - dwc_usb3_pcd_set_link_state() ++ * - dwc_usb3_set_address() ++ * - dwc_usb3_ena_usb2_suspend() ++ * - dwc_usb3_dis_usb2_suspend() ++ * - dwc_usb3_accept_u1() ++ * - dwc_usb3_accept_u2() ++ * - dwc_usb3_enable_u1() ++ * - dwc_usb3_enable_u2() ++ * - dwc_usb3_disable_u1() ++ * - dwc_usb3_disable_u2() ++ * - dwc_usb3_u1_enabled() ++ * - dwc_usb3_u2_enabled() ++ * - dwc_usb3_pcd_remote_wake() ++ */ ++ ++/** ++ * @page pcd_chapter Peripheral Controller Driver ++ * ++ * The Peripheral Controller Driver (PCD) is responsible for translating ++ * requests from the Function Driver into the appropriate actions on the ++ * DWC_usb3 controller. It isolates the Function Driver from the specifics of ++ * the controller by providing an API to the Function Driver. This API may ++ * vary between operating systems, but it remains constant within a given OS. ++ * Section 4.2 @ref fun_drvr_ifc_sec describes this API for supported ++ * operating systems. ++ * ++ * A USB device responds to commands issued from the host. Section 4.3 ++ * @ref std_usb_cmd_proc_sec describes handling of the standard USB ++ * commands within the DWC_usb3 software environment. ++ * ++ * An important function of the PCD is managing interrupts generated by the ++ * DWC_usb3 controller. The behaviors for each DWC_usb3 peripheral mode ++ * interrupt are described in section 4.4 @ref dev_intr_svc_rtn_sec. ++ * ++ * @section pcd_data_st_sec Data Structures ++ * ++ * These are the main data structures used by the PCD. ++ * ++ * @subsection pcd_subsec PCD Data Structure ++ * ++ * The following structure encapsulates the data for the dwc_usb3 PCD. ++ * ++ * - struct dwc_usb3_pcd ++ * ++ * @subsection pcd_ep_subsec Endpoint Data Structure ++ * ++ * The following structure describes an endpoint. There is an array of these ++ * in the PCD structure, one for each endpoint. ++ * ++ * - struct dwc_usb3_pcd_ep ++ * ++ * @subsection ep_subsec Endpoint State Structure ++ * ++ * The following structure represents the state of a single endpoint when ++ * acting in peripheral mode. It contains the data items needed for an endpoint ++ * to be activated and transfer packets. One of these structures is contained ++ * in each dwc_usb3_pcd_ep structure. ++ * ++ * - struct dwc_ep ++ * ++ * @subsection pcd_req_subsec Transfer Request Data Structure ++ * ++ * The following structure describes a transfer request. It is used to form ++ * a list of requests. ++ * ++ * - struct dwc_usb3_pcd_req ++ * ++ * @subsection req_subsec Transfer Request State Structure ++ * ++ * The following structure represents the state of a single transfer request ++ * when acting in peripheral mode. It contains the data items needed for a ++ * request to be started and completed. ++ * ++ * - struct dwc_req ++ * ++ * @subsection dev_dma_subsec DMA Descriptor Structure ++ * ++ * The following structure represents the DMA descriptors used by the dwc_usb3 ++ * controller in peripheral mode. These descriptors are also referred to as ++ * Transfer Request Blocks or TRBs (as in the xHCI spec). ++ * ++ * - struct dwc_usb3_dma_desc ++ * ++ * @section fun_drvr_ifc_sec Function Driver Interface ++ * ++ * This section describes the API that the PCD presents to the Function Driver ++ * for each supported operating system. Currently, Linux is the only supported ++ * OS. ++ * ++ * @subsection gadget_api_subsec Linux Gadget API ++ * ++ * The Peripheral Controller Driver for Linux will implement the Gadget API, ++ * so that the existing Gadget drivers can be used. (Gadget Driver is the ++ * Linux terminology for a Function Driver.) ++ * ++ * The Linux Gadget API is defined in the header file <linux/usb_gadget.h>. ++ * The following data structures define the functions implemented in the PCD ++ * to provide the interface. The USB EP operations API is defined in the ++ * structure usb_ep_ops and the USB Controller API is defined in the structure ++ * struct usb_gadget_ops. ++ * ++ * @subsection usb_ep_ops_subsec USB Endpoint Operations ++ * ++ * The following sections briefly describe the behavior of the Gadget API ++ * endpoint operations implemented in the DWC_usb3 driver software. Detailed ++ * descriptions of the generic behavior of each of these functions can be ++ * found in the Linux header file include/linux/usb_gadget.h. ++ * ++ * The Gadget API provides wrapper functions for each of the function pointers ++ * defined in usb_ep_ops. The Gadget Driver calls the wrapper function, which ++ * then calls the underlying PCD function. The following sections are named ++ * according to the wrapper functions. Within each section, the corresponding ++ * DWC_usb3 PCD function name is specified. ++ * ++ * Functions in the API that are not described below are not implemented. ++ * ++ * <strong><em>usb_ep_enable</em></strong> ++ * ++ * - ep_enable() ++ * ++ * <strong><em>usb_ep_disable</em></strong> ++ * ++ * - ep_disable() ++ * ++ * <strong><em>usb_ep_alloc_request</em></strong> ++ * ++ * - alloc_request() ++ * ++ * <strong><em>usb_ep_free_request</em></strong> ++ * ++ * - free_request() ++ * ++ * <strong><em>usb_ep_queue</em></strong> ++ * ++ * - ep_queue() ++ * ++ * <strong><em>usb_ep_dequeue</em></strong> ++ * ++ * - ep_dequeue() ++ * ++ * <strong><em>usb_ep_set_halt, usb_ep_clear_halt</em></strong> ++ * ++ * - ep_set_halt() ++ * ++ * @subsection gadget_ops_subsec Gadget Operations ++ * ++ * The following gadget operations will be implemented in the DWC_usb3 PCD. ++ * Functions in the API that are not described below are not implemented. ++ * ++ * The Gadget API provides wrapper functions for each of the function pointers ++ * defined in usb_gadget_ops. The Gadget Driver calls the wrapper function, ++ * which then calls the underlying PCD function. The following sections are ++ * named according to the wrapper functions. Within each section, the ++ * corresponding DWC_usb3 PCD function name is specified. ++ * ++ * <strong><em>usb_gadget_get_frame</em></strong> ++ * ++ * - dwc_get_frame() ++ * ++ * <strong><em>usb_gadget_wakeup</em></strong> ++ * ++ * - dwc_usb3_wakeup() ++ * ++ * @subsection streams_ext_subsec USB 3.0 Bulk Streams Extension ++ * ++ * USB 3.0 introduces the concept of <em>Bulk Streams</em>. Bulk Streams provide ++ * the ability to move multiple streams of data over a single Bulk endpoint. ++ * This is used by a new version of the Mass Storage Class protocol, called USB ++ * Attached SCSI Protocol (UASP). It allows multiple transfer requests to be ++ * queued up by the Host, which can then be completed in whatever order the data ++ * becomes available from the Peripheral. ++ * ++ * This requires some modifications to the Gadget API and the underlying USB ++ * request functions, to allow the stream capabilities of an endpoint to be set, ++ * and to indicate the stream ID associated with a transfer request. It also ++ * requires a new File Storage gadget which has the needed Bulk Streams support. ++ * ++ * @section std_usb_cmd_proc_sec Standard USB Command Processing ++ * ++ * In Linux, the USB Command processing is done in two places - the first ++ * being the PCD and the second being the Gadget Driver (for example, the ++ * Mass Storage Gadget Driver). See pcd_setup() for a detailed explanation. ++ * ++ * @section dev_intr_svc_rtn_sec Peripheral Interrupt Service Routine ++ * ++ * The PCD handles the peripheral interrupts. Many conditions can cause a ++ * peripheral interrupt. When an interrupt occurs, the peripheral interrupt ++ * service routine determines the cause of the interrupt and dispatches ++ * handling to the appropriate function. These interrupt handling functions ++ * are described below. ++ * ++ * <strong><em>Global Peripheral Interrupts</em></strong> ++ * ++ * - dwc_usb3_handle_dev_intr() ++ * - handle_disconnect_intr() ++ * - handle_usb_reset_intr() ++ * - dwc_usb3_handle_connect_done_intr() ++ * - handle_link_status_change_intr() ++ * - handle_wakeup_detected_intr() ++ * - handle_sof_intr() ++ * - handle_hiber_req_intr() ++ * ++ * <strong><em>Endpoint-Specific Peripheral Interrupts</em></strong> ++ * ++ * - dwc_usb3_handle_ep_intr() ++ * ++ * @section pcd_func_sec Other Functions ++ * ++ * These are the remaining functions provided by the PCD. ++ * ++ * - dwc_usb3_pcd_init() ++ * - dwc_usb3_pcd_remove() ++ * - dwc_usb3_pcd_ep_enable() ++ * - dwc_usb3_pcd_ep_disable() ++ * - dwc_usb3_pcd_ep_submit_req() ++ * - dwc_usb3_pcd_ep_cancel_req() ++ * - dwc_usb3_pcd_ep_set_halt() ++ * - dwc_usb3_pcd_get_frame_number() ++ * - dwc_usb3_ep0_activate() ++ * - dwc_usb3_pcd_ep0_out_start() ++ * - dwc_usb3_stop_all_xfers() ++ * - dwc_usb3_get_out_ep() ++ * - dwc_usb3_get_in_ep() ++ * - dwc_usb3_handle_ep0() ++ * - dwc_usb3_ep_complete_request() ++ * - usb_gadget_register_driver() / usb_gadget_probe_driver() ++ * - usb_gadget_unregister_driver() ++ */ ++ ++/** ++ * @page hiber_chapter Hibernation ++ * ++ * DWC_usb3 Subsystem Version 2.00a and above implements a new functionality, ++ * hibernation. Hibernation allows power to be removed from most parts of the ++ * controller during periods of inactivity. This is done through a combination ++ * of hardware and software. ++ * ++ * The controller signals a device event when the host has requested a low-power ++ * state suitable for the device to enter hibernation. When this event is ++ * received, the PCD will stop the controller, command it to save critical state ++ * information to main memory, save the content of the CSRs, and then shut off ++ * Vcc power to the core. ++ * ++ * When a condition is detected that requires the core to resume operation, the ++ * driver will receive an interrupt from the monitoring hardware, causing it to ++ * switch Vcc back on, restore the content of the CSRs, and command the ++ * controller to restore its critical state from main memory. ++ * ++ * The bulk of the hibernation code is contained in the files pcd_hiber.c and ++ * linux_hiber.c. Note that this is not a complete implementation; the HAPS ++ * platform on which it has been tested does not provide the normal PCIe PME ++ * mechanism, instead there is special logic in the custom PCIe "gasket" to ++ * handle power control and provide an interrupt to awaken the core from ++ * hibernation. Therefore, the code in linux_hiber.c which handles power ++ * control and wakeup will need to be rewritten for a "real" implementation. ++ * Also, since the CPU is still running while the controller is in the ++ * hibernated state, code has been added to linux_gadget.c to prevent any calls ++ * that would try to touch the controller hardware from entering the driver ++ * while hibernation is active. A "real" implementation should not require this. ++ * ++ * @section hiber_func_sec Hibernation Functions ++ * ++ * These are the functions provided by pcd_hiber.c and linux_hiber.c ++ * ++ * - dwc_enter_hibernation() ++ * - dwc_exit_hibernation_after_connect() ++ * - dwc_exit_hibernation() ++ * - dwc_wait_pme_thread() ++ * - dwc_usb3_handle_pme_intr() ++ * - dwc_usb3_power_ctl() ++ */ ++#endif /* _DWC_DOX_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/dox_port.h b/drivers/usb/gadget/udc/hiudc3/dox_port.h +new file mode 100644 +index 0000000..4cd573e +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/dox_port.h +@@ -0,0 +1,619 @@ ++#ifndef _DWC_DOX_PORT_H_ ++#define _DWC_DOX_PORT_H_ ++ ++/** ++ * @file ++ * ++ * image html synopsys.png "Synopsys Logo" ++ * image latex synopsys.eps "Synopsys Logo" ++ * ++ * This file contains the Doxygen comments that create the Porting Guide ++ * document for the driver. ++ * ++ * @page intro_chap Introduction ++ * ++ * This document gives some tips on how to port the DWC_usb3 example drivers to ++ * an operating system other than Linux. This document focuses on the Peripheral ++ * Controller Driver (PCD) in the \a driver/ source directory. The OTG3 and UAS ++ * Gadget drivers in the \a otg3/ and \a gadget/ directories are not covered. ++ * ++ * The PCD source code includes an example platform port in the \a driver/no_os/ ++ * directory. \a no_os stands for "No OS", which means the code contains no ++ * OS-specific dependencies at all. ++ * ++ * The \a no_os/ code also contains an implementation of a simple loopback-class ++ * Function Driver. This driver provides one Bulk OUT and one Bulk IN endpoint, ++ * and can either loop all packets received on the OUT endpoint back to the IN ++ * endpoint, or can sink any packets received on the OUT endpoint while ++ * simultaneously sourcing 0-filled packets on the IN endpoint. One of these two ++ * behaviors can be selected at compile time by changing a single line of source ++ * code. ++ * ++ * @page pcd_chap The PCD ++ * ++ * @section pcd_rules_sec Rules When Calling Into the PCD ++ * ++ * - The @ref dwc_usb3_pcd_init API routine in pcd.c must be called from process ++ * context or equivalent, because it uses the @ref dwc_msleep routine to delay ++ * while waiting for the DWC_usb3 core to come out of reset. The same is true ++ * for @ref dwc_usb3_pcd_device_init if its <strong><em>soft_reset</em></strong> ++ * parameter is <strong>true</strong>. ++ * ++ * - To protect against the IRQ handler corrupting core registers or data ++ * structures, interrupts should be disabled before calling any of the ++ * non-initialization routines in the PCD API. In addition, on multiprocessor ++ * platforms a spinlock should also be taken, to protect against two CPUs ++ * running at the same time and causing corruption. The <em>lock</em> field in ++ * struct dwc_usb3_pcd is provided for this purpose. ++ * ++ * - If the above spinlock is used, then it must be released in the @ref ++ * dwc_usb3_gadget_complete API routine before calling any Function Driver ++ * routines which may try to take the spinlock again to call back into the PCD. ++ * The spinlock must be retaken before @ref dwc_usb3_gadget_complete returns. ++ * ++ * @section comp_pcd_sec Porting the PCD ++ * ++ * First, try porting just the PCD code, which is cil.c, cil_intr.c, pcd.c, ++ * pcd_intr.c, pcd_hiber.c, and ep0.c. This code should be easy to port because ++ * it uses very few OS services. The following sections will walk you through ++ * how to do this. ++ * ++ * @subsection os_defs_and_dev_sec Create the os_defs.h and os_dev.h Files ++ * ++ * Create \a os_defs.h and \a os_dev.h files for your platform. You can ++ * either create a subdirectory (e.g. \a driver/myplat/) to contain these ++ * files, and replace the existing \a os_defs.h and \a os_dev.h symlinks with ++ * links to these files, or you can remove the symlinks and create these files ++ * directly in the \a driver/ directory. ++ * ++ * We recommend that you start with the no_os_defs.h and no_os_dev.h files and ++ * modify them as needed for your platform. ++ * ++ * @subsubsection os_defs_sec The os_defs.h File ++ * ++ * The \a os_defs.h file should contain all the \a \#include directives ++ * needed to compile the PCD code on your platform, as well as definitions ++ * needed to provide any missing types etc. that the PCD code requires. ++ * ++ * Specific things that need to be defined here include: ++ * <ul> ++ * <li> Definitions of the types \a u8, \a u16, \a u32, \a u64, \a u_int8_t, ++ * \a u_int16_t, \a u_int32_t, \a u_char, \a u_short, \a u_int, and \a ++ * u_long. ++ * ++ * <li> Definition of the type \a dwc_dma_t to hold a DMA address for your ++ * platform. This must be an integral type, not a pointer. ++ * ++ * <li> Definition of the \a UPACKED attribute for the compiler. If you are ++ * using GCC, or ARMCC with the \a --gnu flag, this should be: ++ * <code><p> \#define UPACKED __attribute__ ((__packed__)) ++ * </p></code> ++ * ++ * <p> If you are using a Microsoft compiler, you should define this to ++ * nothing: </p> ++ * <code><p> \#define UPACKED </p></code> ++ * ++ * <p> The \a \#include <pshpack1.h> and \a \#include <poppack.h> ++ * directives, as shown in the usb.h file, handle structure packing for ++ * Microsoft compilers. </p> ++ * ++ * <p> If you are using some other compiler, check the compiler manual to see ++ * how to accomplish this. </p> ++ * ++ * <li> Definition of the \a __iomem data attribute. For any platform except ++ * Linux, this should be defined to nothing: ++ * <code><p> \#define __iomem </p></code> ++ * ++ * <li> Definition of the @ref wmb macro (write memory barrier). For ++ * uniprocessor platforms, this can be defined to a null function. For ++ * multiprocessor platforms, the definition of this macro is highly ++ * platform-dependent. ++ * ++ * <p> This macro is only used by the DWC_usb3 hibernation code, so ++ * if you are not using the hibernation feature you can safely ++ * define it to a null function: </p> ++ * <code><p> \#define wmb() do {} while (0) </p></code> ++ * ++ * <li> Definitions of the usb_request and usb_ep data types. The content of ++ * these structures is completely user-defined, but they must be defined here ++ * because the PCD embeds them into its own request and endpoint structures. ++ * This allows them to be passed back to the platform code when the PCD ++ * completes a USB transfer. ++ * ++ * <li> Definitions of all the \a DWC_E_* error codes that the PCD uses. If ++ * your platform uses POSIX error codes, they can be easily mapped to the ++ * \a DWC_E_* error codes. ++ * ++ * <p> The only error codes with any operational significance are \a ++ * DWC_E_NOT_SUPPORTED, \a DWC_E_IN_PROGRESS, and \a DWC_E_SHUTDOWN, so these ++ * three error codes must be distinct, while the rest can be mapped to a ++ * single value if so desired. </p> ++ * ++ * <p> See the no_os_defs.h file for a complete list of the DWC_E_* error ++ * codes. </p> ++ * ++ * <li> Definition of the dwc_usb3_core_params struct. This contains an entry ++ * for each option that the PCD driver supports. It is defined here so that you ++ * can add additional options for your platform code if you wish. See the ++ * no_os_defs.h file for a minimal implementation of this structure. ++ * </ul> ++ * ++ * @subsubsection os_dev_sec The os_dev.h File ++ * ++ * Because the \a os_defs.h file is included first by the PCD code, it does not ++ * have access to any of the definitions in the PCD header files. Therefore, any ++ * of your definitions which need such access should be placed in the \a ++ * os_dev.h file instead, which is included last by the PCD code. Specific ++ * things that need to be defined here include: ++ * <ul> ++ * <li> Definitions of the @ref dwc_rd32 and @ref dwc_wr32 inline functions. ++ * Given an offset from the beginning of the DWC_usb3 core address space to a ++ * particular register, these functions read from or write to that register. ++ * ++ * <li> Definitions of the @ref dwc_usb3_get_pcd_req and @ref dwc_usb3_get_pcd_ep ++ * macros. Given a pointer to a usb_request or usb_ep, these macros return a ++ * pointer to the enclosing dwc_usb3_pcd_req or dwc_usb3_pcd_ep struct. For ++ * any compiler that provides the \a offsetof macro, these should be: ++ * <code> ++ * <p> \#define dwc_usb3_get_pcd_req(usbreq) \\ \n ++ * ++ * ((dwc_usb3_pcd_req_t *)((char *)(usbreq) - \\ \n ++ * ++ * offsetof(struct dwc_usb3_pcd_req, usb_req))) </p> ++ * <p> </p> ++ * <p> \#define dwc_usb3_get_pcd_ep(usbep) \\ \n ++ * ++ * ((dwc_usb3_pcd_ep_t *)((char *)(usbep) - \\ \n ++ * ++ * offsetof(struct dwc_usb3_pcd_ep, usb_ep))) </p> ++ * </code> ++ * ++ * <li> Definitions of the @ref dwc_udelay and @ref dwc_mdelay non-sleeping delay ++ * routines. Given a delay value in microseconds or milliseconds respectively, ++ * these routines must delay for at least as long as the delay value. ++ * Non-sleeping means these routines may be called from any context, including ++ * interrupt context, so they must not reschedule or require an interrupt to ++ * indicate the end of the delay. Typically these routines would be implemented ++ * using a calibrated busy loop or a dedicated hardware timer. ++ * ++ * <li> Definition of the @ref dwc_msleep sleeping delay routine. Given a delay ++ * value in milliseconds, this routine must delay for at least as long as the ++ * delay value. Sleeping means this routine will only be called from process ++ * context, so it can reschedule or require an interrupt to indicate the end of ++ * the delay. Typically this routine would be implemented using a system timer. ++ * ++ * <li> Definitions of the @ref dwc_print\<n\>, @ref dwc_error\<n\>, @ref ++ * dwc_warn\<n\>, @ref dwc_info\<n\>, @ref dwc_isocdbg\<n\>, and @ref dwc_debug\<n\> ++ * logging routines. ++ * Given a \a printf style format string and the number of arguments indicated ++ * by the last digit of the routine's name, they print a message to the system ++ * message log, or to the console. ++ * ++ * <p> The different routine names indicate the importance of the message, ++ * where @ref dwc_print\<n\> is the highest importance and @ref dwc_debug\<n\> ++ * is the lowest. </p> ++ * ++ * <p> Typically these routines would be implemented using a \a printf type ++ * function if one is available, but the explicit argument count means they ++ * can be implemented without needing a \a varargs facility, if necessary. </p> ++ * </ul> ++ * ++ * @subsection modify_pcd_h_sec Modify the pcd.h File ++ * ++ * The pcd.h file contains a few platform-dependent definitions, and may need ++ * some minor modifications to work on your platform: ++ * <ul> ++ * <li> dwc_usb3_pcd_req contains an \a entry field, which is used to link a ++ * request into the queue of requests for an endpoint. If your platform will ++ * use request queuing, then this field may need to be modified. ++ * ++ * <p> Since the \a entry field is not used by the PCD, it can be commented out ++ * for the purpose of test-compiling the PCD. </p> ++ * ++ * <li> dwc_ep contains a \a queue field, which is the head of the request ++ * queue for an endpoint. Again, this field may need to be modified for your ++ * platform, or it can be commented out for the purpose of test-compiling the ++ * PCD. ++ * ++ * <li> dwc_usb3_pcd contains a \a lock field, which is used for protecting ++ * the driver data from simultaneous access by multiple processors. If your ++ * platform is uniprocessor, then this field is not needed and you can comment ++ * it out, otherwise it may need to be modified for your platform. ++ * ++ * <p> Since the \a lock field is not used by the PCD, it can be commented out ++ * for the purpose of test-compiling the PCD. </p> ++ * </ul> ++ * ++ * @subsection makefile_or_project_sec Create the Makefile / Project File ++ * ++ * Create a makefile / project file for your platform, and add \a cil.o, \a ++ * cil_intr.o, \a pcd.o, \a pcd_intr.o, \a pcd_hiber.o, and \a ep0.o as the ++ * target object files. Because the result will not link successfully yet due ++ * to missing Function Driver routines, you should omit the link step, if ++ * possible. ++ * ++ * @subsection test_compile_sec Test Compile the PCD ++ * ++ * <p> Now try compiling your project. </p> ++ * ++ * <p> If there are errors during the compile phase, you must modify your \a ++ * os_defs.h and \a os_dev.h files, and/or pcd.h, to attempt to eliminate them. ++ * </p> ++ * ++ * <p> You may need to modify some of the other PCD source files to achieve a ++ * successful compile. For example, your compiler may require a different method ++ * of packing data structures, which may require you to modify usb.h. </p> ++ * ++ * <p> If there are warnings during the compile phase, you should investigate ++ * each one carefully to determine whether it signifies a real problem or not. ++ * </p> ++ * ++ * <p> If your makefile / project file contains a link step, you should expect ++ * it to fail, since at this point all of the Function Driver routines are ++ * missing. </p> ++ * ++ * <p> If there are no errors or warnings during the compile phase, then the ++ * initial porting phase is complete. </p> ++ * ++ * @page func_drvr_chap The Function Driver ++ * ++ * Porting the Function Driver is not as straightforward as the PCD, because ++ * there are more external factors to take into account. ++ * ++ * Because of this, we have provided the No-OS platform port. This port needs ++ * minimal support from the operating environment, and is designed to run as ++ * close to the hardware as possible. It is also self-contained, implementing ++ * its own Function Driver, so no OS support for USB functions is required. ++ * ++ * The Function implements a simple loopback test mode, where any data received ++ * from the host on the OUT endpoint is sent back to the host on the IN ++ * endpoint. Or, by changing the second line in ++ * no_os_src_sink_lpbk.c:dwc_usb3_function_init() to this: ++ * ++ * <code><p> loopbk.src_sink = 1; </p></code> ++ * ++ * the Function will implement a source/sink test mode, which sinks any packets ++ * received on the OUT endpoint while simultaneously sourcing 0-filled packets ++ * on the IN endpoint. ++ * ++ * The major limitation of the No-OS port is that all DMA data structures are ++ * statically allocated in the C code. This will only work in very simple ++ * operating environments where the system memory is mapped with virtual ++ * address == physical address. If you have an environment where this is not the ++ * case, then at a minimum you will need to replace these static allocations ++ * with calls to OS routines to perform proper DMA allocations. ++ * ++ * To help while debugging the No-OS port, we have included Linux code that is ++ * conditionally compiled if the LINUXTEST macro is defined. This allows the ++ * No-OS code to compile and run on a Linux platform, as a completely ++ * self-contained driver that does not use the Linux Gadget framework. The ++ * No-OS port can be built for Linux in this way by invoking "make" as follows: ++ * ++ * <code><p> make NOOS=1 </p></code> ++ * ++ * The no_os/ platform code consists of the following files: ++ * ++ * - no_os_init.c: Initialization code and "bus glue". ++ * - no_os_gadget.c: Implements a custom Function Driver API. ++ * - no_os_ep0.c: Contains the USB descriptors for the Function. ++ * - no_os_src_sink_lpbk.c: Contains an implementation of a simple ++ * source-sink/loopback Function. ++ * - no_os_hiber.c: Implements the platform-specific code needed to support the ++ * DWC_usb3 hibernation feature. Note: This code is incomplete, so you should ++ * consider DWC_usb3 hibernation as unsupported by the No-OS Function Driver ++ * at this time. ++ * ++ * @section func_walk_sec Code Walkthrough of the No-OS Function Driver ++ * ++ * @subsection func_init_sec Initialization ++ * ++ * At initialization time, @ref dwc_usb3_driver_init in no_os_init.c is called, ++ * which does the following: ++ * - Allocates memory for the \a usb3_dev driver data struct. ++ * - Maps the DWC_usb3 registers into the processor's memory space. ++ * - Calls the @ref dwc_usb3_pcd_check_snpsid API routine to make sure this is a ++ * DWC_usb3 device, and to save the value from the SNPSID register in the ++ * \a usb3_dev struct. ++ * - Allocates DMAable memory for the event buffer, and for the EP0 DMA ++ * descriptors and data buffers. ++ * - Calls the @ref dwc_usb3_pcd_common_init API routine. ++ * - Calls the @ref dwc_usb3_gadget_init API routine in no_os_gadget.c, which ++ * does the following: ++ * - Sets the dwc_ep.num field for each physical endpoint to the USB endpoint ++ * number associated with that endpoint. ++ * - Initializes the dwc_ep.queue field for each physical endpoint. This is ++ * the head of the queue where each transfer request for that endpoint will ++ * be queued. ++ * - Calls @ref dwc_usb3_function_init in no_os_src_sink_lpbk.c, which allocates ++ * data buffers for each Bulk endpoint. ++ * - Calls the @ref dwc_usb3_pcd_init API routine. ++ * - Hooks the @ref dwc_usb3_common_irq top-level interrupt handler routine to ++ * the DWC_usb3 IRQ vector. ++ * ++ * At this point the DWC_usb3 core and the driver are initialized, and waiting ++ * for the Device to be connected to a Host. ++ * ++ * @subsection func_conn_enum_sec Connection and Enumeration ++ * ++ * When a connection to a Host is established, a <strong>USBRst</strong> event ++ * is generated, followed by a <strong>ConnectDone</strong> event. ++ * ++ * The <strong>USBRst</strong> event is handled internally by the PCD. ++ * ++ * The <strong>ConnectDone</strong> event results in the following: ++ * - The PCD calls the @ref dwc_usb3_gadget_connect API routine in no_os_gadget.c. ++ * - @ref dwc_usb3_gadget_connect in turn calls @ref dwc_usb3_function_connect ++ * in no_os_src_sink_lpbk.c. ++ * - @ref dwc_usb3_function_connect sets the function's Bulk max packet size ++ * according to the connection speed. ++ * ++ * Once the connection sequence completes, the Host starts sending Control ++ * requests to the Device to enumerate it. The general flow for Control requests ++ * is as follows: ++ * - The PCD code in ep0.c handles most Control requests internally. ++ * - For those requests it cannot handle, the PCD calls the ++ * @ref dwc_usb3_gadget_setup API routine in no_os_gadget.c. ++ * - @ref dwc_usb3_gadget_setup in turn calls @ref dwc_usb3_no_os_setup in ++ * no_os_ep0.c. ++ * - @ref dwc_usb3_no_os_setup handles <strong>GET_DESCRIPTOR</strong> Control ++ * requests (except for the BOS descriptor, which is handled by ep0.c), using ++ * the information encoded in the USB descriptors in no_os_ep0.c. See @ref ++ * usb_desc_sec for details. ++ * - For all remaining Control requests, the no_os_ep0.c code calls @ref ++ * dwc_usb3_function_setup in no_os_src_sink_lpbk.c. ++ * ++ * @subsection func_config_sec Configuration ++ * ++ * Once the Host has enumerated the Device, it will configure it as follows: ++ * - The host sends a <strong>SET_CONFIGURATION</strong> Control request with a ++ * <strong>bConfigurationValue</strong> of <strong>1</strong> (since that is ++ * the value given in the Config descriptors in no_os_ep0.c), followed by a ++ * <strong>SET_INTERFACE</strong> Control request with a ++ * <strong>bInterfaceNumber</strong> of <strong>0</strong> and a ++ * <strong>bAlternateSetting</strong> of <strong>0</strong> (since those are ++ * the values given in the Interface descriptors in no_os_ep0.c). ++ * - <strong>SET_CONFIGURATION</strong> and <strong>SET_INTERFACE</strong> are ++ * two of the Control requests that are handled by @ref dwc_usb3_function_setup ++ * in no_os_src_sink_lpbk.c, so that routine will be called. ++ * - @ref dwc_usb3_function_setup in turn calls @ref set_interface (in the same file) ++ * with an <strong><em>alt</em></strong> parameter of <strong>0</strong>. ++ * - @ref set_interface calls @ref enable_eps (in the same file). ++ * - @ref enable_eps calls @ref dwc_usb3_ep_enable in no_os_gadget.c for each of ++ * the Bulk endpoints, with pointers to the USB descriptors from no_os_ep0.c ++ * corresponding to the speed of the connection. ++ * - @ref dwc_usb3_ep_enable does the following: ++ * - Gets the endpoint info from the Endpoint descriptor for that EP. ++ * - Decides how many TRBs to allocate for the EP. ++ * - Calls the @ref dwc_usb3_pcd_trb_alloc API routine to allocate the TRBs. ++ * Note: @ref dwc_usb3_pcd_trb_alloc will call back to the @ref ++ * dwc_usb3_gadget_alloc_dma API routine in no_os_gadget.c to do the ++ * memory allocation. ++ * - Calls the @ref dwc_usb3_pcd_ep_enable API routine to enable the endpoint ++ * in the DWC_usb3 core. ++ * - @ref set_interface then calls @ref dwc_usb3_alloc_request in no_os_gadget.c ++ * repeatedly, to allocate a number of transfer requests for each of the Bulk ++ * endpoints. ++ * - @ref dwc_usb3_alloc_request does the following: ++ * - Allocates a PCD request from the gadget's pool of requests. ++ * - Returns a pointer to the usb_request member of the PCD request. ++ * - @ref set_interface then fills in a number of fields in each usb_request, ++ * among them the \a complete function pointer, then calls @ref dwc_usb3_ep_queue ++ * in no_os_gadget.c to queue it for transfer. ++ * - @ref dwc_usb3_ep_queue does the following: ++ * - Sets the usb_request \a status field to -DWC_E_IN_PROGRESS. ++ * - Fills in a number of required fields in the PCD request. ++ * - Decides whether to start the request immediately, or to queue it to be ++ * started later. If it decides to start the request immediately, it: ++ * - Calls the @ref dwc_usb3_pcd_fill_trbs API routine to fill in the TRB ++ * for the request. ++ * - Calls the @ref dwc_usb3_pcd_ep_submit_req API routine to start the ++ * transfer in the DWC_usb3 core. ++ * - Adds the request to the endpoint's request queue. ++ * ++ * At this point the Device is in the Configured state, ready to receive Bulk ++ * transfers from the Host. ++ * ++ * @subsection func_oper_sec Operation ++ * ++ * When the Host requests a Bulk data transfer to or from the Device, the PCD ++ * will receive a <strong>XferComplete</strong> event from the DWC_usb3 core ++ * when the transfer completes. This will result in the following: ++ * - The PCD calls the @ref dwc_usb3_gadget_get_request API routine in ++ * no_os_gadget.c. ++ * - @ref dwc_usb3_gadget_get_request returns the request that is at the head of ++ * the endpoint's request queue. This will be the request whose transfer has ++ * just completed. ++ * - The PCD then calls the @ref dwc_usb3_gadget_complete API routine in ++ * no_os_gadget.c. ++ * - @ref dwc_usb3_gadget_complete removes the request from the endpoint's queue, ++ * fills in the \a status and \a actual fields in the usb_request, and calls ++ * the \a complete function pointer in the usb_request. ++ * - The \a complete function pointer points to @ref loopbk_complete in ++ * no_os_src_sink_lpbk.c, which does the following: ++ * - Checks the \a status field in the usb_request. If it is not ++ * <strong>0</strong> then an error or a disconnect occurred, which are ++ * handled appropriately. ++ * - If the \a status field is <strong>0</strong>, then the endpoint is ++ * checked. If it is the OUT EP, @ref dwc_usb3_ep_queue in no_os_gadget.c is ++ * called to requeue the usb_request on the IN EP for sending the data back ++ * to the host. If it is the IN EP, @ref dwc_usb3_ep_queue is called to requeue ++ * the usb_request on the OUT EP for receiving more data from the host. ++ * - The PCD then calls the @ref dwc_usb3_gadget_start_next_request API routine ++ * in no_os_gadget.c. ++ * - If there are any more requests queued for the endpoint, ++ * @ref dwc_usb3_gadget_start_next_request takes the first one and does the ++ * following: ++ * - Calls the @ref dwc_usb3_pcd_fill_trbs API routine to fill in the TRB for ++ * the request. ++ * - Calls the @ref dwc_usb3_pcd_ep_submit_req API routine to start the ++ * transfer in the DWC_usb3 core. ++ * ++ * @section usb_desc_sec USB Descriptors ++ * ++ * no_os_ep0.c contains a minimal set of USB descriptors - a set of String ++ * descriptors, a Device descriptor, a Device Qualifier descriptor, and three ++ * Configuration descriptors, one for each of the speeds that the DWC_usb3 core ++ * supports. Each of the Configuration descriptors contains two Endpoint ++ * descriptors, one for a Bulk IN endpoint and one for a Bulk OUT endpoint. The ++ * Super Speed Configuration descriptor also contains two Super Speed Endpoint ++ * Companion descriptors, one for each of the Bulk endpoints. All of these ++ * descriptors may need to be modified for your platform's particular Function ++ * implementation. ++ * ++ * ep0.c contains the required BOS descriptor. Fields that must be modified here ++ * are \a bU1DevExitLat and \a wU2DevExitLat in the Super Speed Device ++ * Capability descriptor, and \a containerID in the optional Container ID ++ * Capability descriptor (if used). ++ * ++ * The compiler must be instructed not to add padding between the fields in any ++ * of these descriptors. This is the purpose of the \a UPACKED attribute macro ++ * for the GCC compiler (or the ARMCC compiler with the \a --gnu flag). The ++ * \a \#include <pshpack1.h> and \a \#include <poppack.h> directives, ++ * as shown in usb.h and no_os_ep0.c, accomplish the same thing for the ++ * Microsoft compilers. ++ * ++ * The exact format for all of these descriptors is described in detail in ++ * Chapter 9 of the USB 3.0 specification. ++ * ++ * @section comp_func_sec Porting the No-OS Function Driver ++ * ++ * Copy all the .c files from the \a driver/no_os/ directory into the platform ++ * directory that you created earlier to hold your \a os_defs.h and \a os_dev.h ++ * files (e.g. \a driver/myplat/). Rename all the .c files accordingly (e.g. ++ * \a no_os_init.c -> \a myplat_init.c, \a no_os_gadget.c -> \a myplat_gadget.c ++ * ...). ++ * ++ * Modify the source code as necessary to work on your platform. ++ * ++ * Modify the makefile / project file for your platform and add e.g. \a ++ * myplat_init.o, \a myplat_gadget.o ... to the target object files. You should ++ * also add a link step if one does not exist already. ++ * ++ * Now try compiling your project. As with @ref test_compile_sec, you must ++ * modify the source files as necessary until the compile phase completes ++ * successfully. ++ * ++ * Once the compile phase is successful, try to resolve any errors that occur ++ * in the link phase. Most likely these will be due to missing support routines, ++ * such as \a memcpy or \a printf. You must either add libraries to the link step ++ * to provide these routines, write your own routines and include them in the ++ * build, or modify the driver source files to use alternate versions of these ++ * routines. ++ * ++ * @page pcd_api_chap PCD to Function Driver API ++ * ++ * @section pcd_funcs_sec API Routines in the PCD ++ * ++ * These routines are provided by the PCD (and the CIL), and make up the PCD ++ * side of the API. ++ * ++ * @subsection init_funcs_sec Initialization Routines ++ * ++ * These routines handle initialization of the CIL and PCD driver components ++ * and the DWC_usb3 core. ++ * ++ * See @ref init_api_grp for a detailed description of these routines. ++ * ++ * - dwc_usb3_pcd_check_snpsid() ++ * - dwc_usb3_pcd_common_init() ++ * - dwc_usb3_pcd_common_remove() ++ * - dwc_usb3_pcd_init() ++ * - dwc_usb3_pcd_remove() ++ * - dwc_usb3_pcd_device_init() ++ * - dwc_usb3_pcd_device_remove() ++ * ++ * @subsection trb_funcs_sec TRB Routines ++ * ++ * These routines handle the allocation, deallocation, and setup of TRBs. ++ * ++ * See @ref trb_api_grp for a detailed description of these routines. ++ * ++ * - dwc_usb3_pcd_trb_alloc() ++ * - dwc_usb3_pcd_trb_free() ++ * - dwc_usb3_pcd_fill_trbs() ++ * ++ * @subsection ep_funcs_sec Endpoint Routines ++ * ++ * These routines handle all the functionality required for configuring, ++ * enabling, controlling, and submitting transfers to an endpoint. ++ * ++ * Note: For Control endpoint 0, only the submit_req, cancel_req, request_done, ++ * and set_halt routines are used; the remaining functionality is handled either ++ * by the @ref ep0_funcs_sec below or internally by the PCD. ++ * ++ * See @ref ep_api_grp for a detailed description of these routines. ++ * ++ * - dwc_usb3_pcd_ep_enable() ++ * - dwc_usb3_pcd_ep_disable() ++ * - dwc_usb3_pcd_ep_submit_req() ++ * - dwc_usb3_pcd_ep_cancel_req() ++ * - dwc_usb3_pcd_request_done() ++ * - dwc_usb3_pcd_ep_start_transfer() ++ * - dwc_usb3_pcd_ep_set_stall() ++ * - dwc_usb3_pcd_ep_clear_stall() ++ * - dwc_usb3_pcd_ep_set_halt() ++ * ++ * @subsection ep0_funcs_sec Control Endpoint 0 Routines ++ * ++ * These routines are only used for Control endpoint 0. ++ * ++ * See @ref ep0_api_grp for a detailed description of these routines. ++ * ++ * - dwc_usb3_pcd_ep0_out_start() ++ * - dwc_usb3_pcd_ep0_start_transfer() ++ * - dwc_usb3_pcd_ep0_continue_transfer() ++ * - dwc_usb3_pcd_ep0_data_stage() ++ * ++ * @subsection misc_funcs_sec Miscellaneous Routines ++ * ++ * These are miscellaneous routines that don't fit into any of the other ++ * categories. ++ * ++ * See @ref misc_api_grp for a detailed description of these routines. ++ * ++ * - dwc_usb3_pcd_get_ep_by_addr() ++ * - dwc_usb3_pcd_get_frame_number() ++ * - dwc_usb3_pcd_isoc_ep_hiber_restart() ++ * - dwc_usb3_pcd_stop() ++ * - dwc_usb3_pcd_get_link_state() ++ * - dwc_usb3_pcd_set_link_state() ++ * - dwc_usb3_pcd_remote_wake() ++ * - dwc_usb3_pcd_do_test_mode() ++ * ++ * @section gadget_funcs_sec API Routines in the Function Driver ++ * ++ * These routines need to be implemented by the platform code, and make up the ++ * Function Driver side of the API. ++ * ++ * @subsection gadget_notif_sec Function Driver Notification Routines ++ * ++ * These routines receive notifications from the PCD when certain events occur ++ * which the Function Driver may need to be aware of. ++ * ++ * See @ref gadget_notif_grp for a detailed description of these routines. ++ * ++ * - dwc_usb3_gadget_connect() ++ * - dwc_usb3_gadget_disconnect() ++ * - dwc_usb3_gadget_suspend() ++ * - dwc_usb3_gadget_resume() ++ * - dwc_usb3_gadget_setup() ++ * - dwc_usb3_gadget_complete() ++ * ++ * @subsection gadget_callbk_sec Function Driver Callback Routines ++ * ++ * The PCD calls these routines when it needs something from the Function ++ * Driver. ++ * ++ * See @ref gadget_callbk_grp for a detailed description of these routines. ++ * ++ * - dwc_usb3_gadget_alloc_dma() ++ * - dwc_usb3_gadget_free_dma() ++ * - dwc_usb3_gadget_get_request() ++ * - dwc_usb3_gadget_start_next_request() ++ * - dwc_usb3_gadget_isoc_ep_start() ++ * - dwc_usb3_gadget_request_nuke() ++ * - dwc_usb3_gadget_set_ep_not_started() ++ * ++ */ ++ ++#endif /* _DWC_DOX_PORT_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/driver.sln b/drivers/usb/gadget/udc/hiudc3/driver.sln +new file mode 100644 +index 0000000..849a645 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/driver.sln +@@ -0,0 +1,20 @@ ++ ++Microsoft Visual Studio Solution File, Format Version 12.00 ++# Visual Studio 11 ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "driver", "driver.vcxproj", "{876C75FB-DB73-46F3-9ED0-92D9FA45FCA4}" ++EndProject ++Global ++ GlobalSection(SolutionConfigurationPlatforms) = preSolution ++ Debug|Win32 = Debug|Win32 ++ Release|Win32 = Release|Win32 ++ EndGlobalSection ++ GlobalSection(ProjectConfigurationPlatforms) = postSolution ++ {876C75FB-DB73-46F3-9ED0-92D9FA45FCA4}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {876C75FB-DB73-46F3-9ED0-92D9FA45FCA4}.Debug|Win32.Build.0 = Debug|Win32 ++ {876C75FB-DB73-46F3-9ED0-92D9FA45FCA4}.Release|Win32.ActiveCfg = Release|Win32 ++ {876C75FB-DB73-46F3-9ED0-92D9FA45FCA4}.Release|Win32.Build.0 = Release|Win32 ++ EndGlobalSection ++ GlobalSection(SolutionProperties) = preSolution ++ HideSolutionNode = FALSE ++ EndGlobalSection ++EndGlobal +diff --git a/drivers/usb/gadget/udc/hiudc3/driver.vcxproj b/drivers/usb/gadget/udc/hiudc3/driver.vcxproj +new file mode 100644 +index 0000000..b6d357d +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/driver.vcxproj +@@ -0,0 +1,91 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ++ <ItemGroup Label="ProjectConfigurations"> ++ <ProjectConfiguration Include="Debug|Win32"> ++ <Configuration>Debug</Configuration> ++ <Platform>Win32</Platform> ++ </ProjectConfiguration> ++ <ProjectConfiguration Include="Release|Win32"> ++ <Configuration>Release</Configuration> ++ <Platform>Win32</Platform> ++ </ProjectConfiguration> ++ </ItemGroup> ++ <PropertyGroup Label="Globals"> ++ <VCTargetsPath Condition="'$(VCTargetsPath11)' != '' and '$(VSVersion)' == '' and '$(VisualStudioVersion)' == ''">$(VCTargetsPath11)</VCTargetsPath> ++ </PropertyGroup> ++ <PropertyGroup Label="Globals"> ++ <ProjectGuid>{876C75FB-DB73-46F3-9ED0-92D9FA45FCA4}</ProjectGuid> ++ <RootNamespace>driver</RootNamespace> ++ </PropertyGroup> ++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> ++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> ++ <ConfigurationType>Application</ConfigurationType> ++ <UseDebugLibraries>true</UseDebugLibraries> ++ <PlatformToolset>v110</PlatformToolset> ++ <CharacterSet>MultiByte</CharacterSet> ++ </PropertyGroup> ++ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> ++ <ConfigurationType>Application</ConfigurationType> ++ <UseDebugLibraries>false</UseDebugLibraries> ++ <PlatformToolset>v110</PlatformToolset> ++ <WholeProgramOptimization>true</WholeProgramOptimization> ++ <CharacterSet>MultiByte</CharacterSet> ++ </PropertyGroup> ++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> ++ <ImportGroup Label="ExtensionSettings"> ++ </ImportGroup> ++ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> ++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> ++ </ImportGroup> ++ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> ++ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> ++ </ImportGroup> ++ <PropertyGroup Label="UserMacros" /> ++ <PropertyGroup /> ++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> ++ <ClCompile> ++ <WarningLevel>Level3</WarningLevel> ++ <Optimization>Disabled</Optimization> ++ <AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> ++ </ClCompile> ++ <Link> ++ <GenerateDebugInformation>true</GenerateDebugInformation> ++ </Link> ++ </ItemDefinitionGroup> ++ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> ++ <ClCompile> ++ <WarningLevel>Level3</WarningLevel> ++ <Optimization>MaxSpeed</Optimization> ++ <FunctionLevelLinking>true</FunctionLevelLinking> ++ <IntrinsicFunctions>true</IntrinsicFunctions> ++ <AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> ++ </ClCompile> ++ <Link> ++ <GenerateDebugInformation>true</GenerateDebugInformation> ++ <EnableCOMDATFolding>true</EnableCOMDATFolding> ++ <OptimizeReferences>true</OptimizeReferences> ++ </Link> ++ </ItemDefinitionGroup> ++ <ItemGroup> ++ <ClInclude Include="cil.h" /> ++ <ClInclude Include="dev.h" /> ++ <ClInclude Include="hw.h" /> ++ <ClInclude Include="os_defs.h" /> ++ <ClInclude Include="os_dev.h" /> ++ <ClInclude Include="pcd.h" /> ++ <ClInclude Include="usb.h" /> ++ </ItemGroup> ++ <ItemGroup> ++ <ClCompile Include="cil.c" /> ++ <ClCompile Include="cil_intr.c" /> ++ <ClCompile Include="ep0.c" /> ++ <ClCompile Include="pcd.c" /> ++ <ClCompile Include="pcd_hiber.c" /> ++ <ClCompile Include="pcd_intr.c" /> ++ <ClCompile Include="win\win_gadget.c" /> ++ <ClCompile Include="win\win_hiber.c" /> ++ </ItemGroup> ++ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> ++ <ImportGroup Label="ExtensionTargets"> ++ </ImportGroup> ++</Project> +\ No newline at end of file +diff --git a/drivers/usb/gadget/udc/hiudc3/driver.vcxproj.filters b/drivers/usb/gadget/udc/hiudc3/driver.vcxproj.filters +new file mode 100644 +index 0000000..b25fc0c +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/driver.vcxproj.filters +@@ -0,0 +1,66 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ++ <ItemGroup> ++ <Filter Include="Source Files"> ++ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> ++ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> ++ </Filter> ++ <Filter Include="Header Files"> ++ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> ++ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> ++ </Filter> ++ <Filter Include="Resource Files"> ++ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> ++ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> ++ </Filter> ++ </ItemGroup> ++ <ItemGroup> ++ <ClInclude Include="cil.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> ++ <ClInclude Include="dev.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> ++ <ClInclude Include="hw.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> ++ <ClInclude Include="os_defs.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> ++ <ClInclude Include="os_dev.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> ++ <ClInclude Include="pcd.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> ++ <ClInclude Include="usb.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> ++ </ItemGroup> ++ <ItemGroup> ++ <ClCompile Include="cil.c"> ++ <Filter>Source Files</Filter> ++ </ClCompile> ++ <ClCompile Include="cil_intr.c"> ++ <Filter>Source Files</Filter> ++ </ClCompile> ++ <ClCompile Include="ep0.c"> ++ <Filter>Source Files</Filter> ++ </ClCompile> ++ <ClCompile Include="pcd.c"> ++ <Filter>Source Files</Filter> ++ </ClCompile> ++ <ClCompile Include="pcd_hiber.c"> ++ <Filter>Source Files</Filter> ++ </ClCompile> ++ <ClCompile Include="pcd_intr.c"> ++ <Filter>Source Files</Filter> ++ </ClCompile> ++ <ClCompile Include="win\win_gadget.c"> ++ <Filter>Source Files</Filter> ++ </ClCompile> ++ <ClCompile Include="win\win_hiber.c"> ++ <Filter>Source Files</Filter> ++ </ClCompile> ++ </ItemGroup> ++</Project> +\ No newline at end of file +diff --git a/drivers/usb/gadget/udc/hiudc3/dwc_list.h b/drivers/usb/gadget/udc/hiudc3/dwc_list.h +new file mode 100644 +index 0000000..9224bd6 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/dwc_list.h +@@ -0,0 +1,468 @@ ++/* $OpenBSD: queue.h,v 1.26 2004/05/04 16:59:32 grange Exp $ */ ++/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ ++ ++/* ++ * Copyright (c) 1991, 1993 ++ * The Regents of the University of California. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the University nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * @(#)queue.h 8.5 (Berkeley) 8/20/94 ++ */ ++ ++#ifndef _DWC_LIST_H_ ++#define _DWC_LIST_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** @file ++ * ++ * This file defines linked list operations. It is derived from BSD with ++ * only the MACRO names being prefixed with DWC_. This is because a few of ++ * these names conflict with those on Linux. For documentation on use, see the ++ * inline comments in the source code. The original license for this source ++ * code applies and is preserved in the dwc_list.h source file. ++ */ ++ ++/* ++ * This file defines five types of data structures: singly-linked lists, ++ * lists, simple queues, tail queues, and circular queues. ++ * ++ * ++ * A singly-linked list is headed by a single forward pointer. The elements ++ * are singly linked for minimum space and pointer manipulation overhead at ++ * the expense of O(n) removal for arbitrary elements. New elements can be ++ * added to the list after an existing element or at the head of the list. ++ * Elements being removed from the head of the list should use the explicit ++ * macro for this purpose for optimum efficiency. A singly-linked list may ++ * only be traversed in the forward direction. Singly-linked lists are ideal ++ * for applications with large datasets and few or no removals or for ++ * implementing a LIFO queue. ++ * ++ * A list is headed by a single forward pointer (or an array of forward ++ * pointers for a hash table header). The elements are doubly linked ++ * so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before ++ * or after an existing element or at the head of the list. A list ++ * may only be traversed in the forward direction. ++ * ++ * A simple queue is headed by a pair of pointers, one the head of the ++ * list and the other to the tail of the list. The elements are singly ++ * linked to save space, so elements can only be removed from the ++ * head of the list. New elements can be added to the list before or after ++ * an existing element, at the head of the list, or at the end of the ++ * list. A simple queue may only be traversed in the forward direction. ++ * ++ * A tail queue is headed by a pair of pointers, one to the head of the ++ * list and the other to the tail of the list. The elements are doubly ++ * linked so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before or ++ * after an existing element, at the head of the list, or at the end of ++ * the list. A tail queue may be traversed in either direction. ++ * ++ * A circle queue is headed by a pair of pointers, one to the head of the ++ * list and the other to the tail of the list. The elements are doubly ++ * linked so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before or after ++ * an existing element, at the head of the list, or at the end of the list. ++ * A circle queue may be traversed in either direction, but has a more ++ * complex end of list detection. ++ * ++ * For details on the use of these macros, see the queue(3) manual page. ++ */ ++ ++/* ++ * Singly-linked List definitions. ++ */ ++#define DWC_SLIST_HEAD(name, type) \ ++struct name { \ ++ struct type *slh_first; /* first element */ \ ++} ++ ++#define DWC_SLIST_HEAD_INITIALIZER(head) \ ++ { NULL } ++ ++#define DWC_SLIST_ENTRY(type) \ ++struct { \ ++ struct type *sle_next; /* next element */ \ ++} ++ ++/* ++ * Singly-linked List access methods. ++ */ ++#define DWC_SLIST_FIRST(head) ((head)->slh_first) ++#define DWC_SLIST_END(head) NULL ++#define DWC_SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) ++#define DWC_SLIST_NEXT(elm, field) ((elm)->field.sle_next) ++ ++#define DWC_SLIST_FOREACH(var, head, field) \ ++ for((var) = SLIST_FIRST(head); \ ++ (var) != SLIST_END(head); \ ++ (var) = SLIST_NEXT(var, field)) ++ ++#define DWC_SLIST_FOREACH_PREVPTR(var, varp, head, field) \ ++ for((varp) = &SLIST_FIRST((head)); \ ++ ((var) = *(varp)) != SLIST_END(head); \ ++ (varp) = &SLIST_NEXT((var), field)) ++ ++/* ++ * Singly-linked List functions. ++ */ ++#define DWC_SLIST_INIT(head) { \ ++ SLIST_FIRST(head) = SLIST_END(head); \ ++} ++ ++#define DWC_SLIST_INSERT_AFTER(slistelm, elm, field) do { \ ++ (elm)->field.sle_next = (slistelm)->field.sle_next; \ ++ (slistelm)->field.sle_next = (elm); \ ++} while (0) ++ ++#define DWC_SLIST_INSERT_HEAD(head, elm, field) do { \ ++ (elm)->field.sle_next = (head)->slh_first; \ ++ (head)->slh_first = (elm); \ ++} while (0) ++ ++#define DWC_SLIST_REMOVE_NEXT(head, elm, field) do { \ ++ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ ++} while (0) ++ ++#define DWC_SLIST_REMOVE_HEAD(head, field) do { \ ++ (head)->slh_first = (head)->slh_first->field.sle_next; \ ++} while (0) ++ ++#define DWC_SLIST_REMOVE(head, elm, type, field) do { \ ++ if ((head)->slh_first == (elm)) { \ ++ SLIST_REMOVE_HEAD((head), field); \ ++ } \ ++ else { \ ++ struct type *curelm = (head)->slh_first; \ ++ while( curelm->field.sle_next != (elm) ) \ ++ curelm = curelm->field.sle_next; \ ++ curelm->field.sle_next = \ ++ curelm->field.sle_next->field.sle_next; \ ++ } \ ++} while (0) ++ ++/* ++ * Simple queue definitions. ++ */ ++#define DWC_SIMPLEQ_HEAD(name, type) \ ++struct name { \ ++ struct type *sqh_first; /* first element */ \ ++ struct type **sqh_last; /* addr of last next element */ \ ++} ++ ++#define DWC_SIMPLEQ_HEAD_INITIALIZER(head) \ ++ { NULL, &(head).sqh_first } ++ ++#define DWC_SIMPLEQ_ENTRY(type) \ ++struct { \ ++ struct type *sqe_next; /* next element */ \ ++} ++ ++/* ++ * Simple queue access methods. ++ */ ++#define DWC_SIMPLEQ_FIRST(head) ((head)->sqh_first) ++#define DWC_SIMPLEQ_END(head) NULL ++#define DWC_SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) ++#define DWC_SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) ++ ++#define DWC_SIMPLEQ_FOREACH(var, head, field) \ ++ for((var) = SIMPLEQ_FIRST(head); \ ++ (var) != SIMPLEQ_END(head); \ ++ (var) = SIMPLEQ_NEXT(var, field)) ++ ++/* ++ * Simple queue functions. ++ */ ++#define DWC_SIMPLEQ_INIT(head) do { \ ++ (head)->sqh_first = NULL; \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++} while (0) ++ ++#define DWC_SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ (head)->sqh_first = (elm); \ ++} while (0) ++ ++#define DWC_SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.sqe_next = NULL; \ ++ *(head)->sqh_last = (elm); \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++} while (0) ++ ++#define DWC_SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ (listelm)->field.sqe_next = (elm); \ ++} while (0) ++ ++#define DWC_SIMPLEQ_REMOVE_HEAD(head, field) do { \ ++ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++} while (0) ++ ++/* ++ * Tail queue definitions. ++ */ ++#define DWC_TAILQ_HEAD(name, type) \ ++struct name { \ ++ struct type *tqh_first; /* first element */ \ ++ struct type **tqh_last; /* addr of last next element */ \ ++} ++ ++#define DWC_TAILQ_HEAD_INITIALIZER(head) \ ++ { NULL, &(head).tqh_first } ++ ++#define DWC_TAILQ_ENTRY(type) \ ++struct { \ ++ struct type *tqe_next; /* next element */ \ ++ struct type **tqe_prev; /* address of previous next element */ \ ++} ++ ++/* ++ * tail queue access methods ++ */ ++#define DWC_TAILQ_FIRST(head) ((head)->tqh_first) ++#define DWC_TAILQ_END(head) NULL ++#define DWC_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) ++#define DWC_TAILQ_LAST(head, headname) \ ++ (*(((struct headname *)((head)->tqh_last))->tqh_last)) ++/* XXX */ ++#define DWC_TAILQ_PREV(elm, headname, field) \ ++ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) ++#define DWC_TAILQ_EMPTY(head) \ ++ (TAILQ_FIRST(head) == TAILQ_END(head)) ++ ++#define DWC_TAILQ_FOREACH(var, head, field) \ ++ for((var) = TAILQ_FIRST(head); \ ++ (var) != TAILQ_END(head); \ ++ (var) = TAILQ_NEXT(var, field)) ++ ++#define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \ ++ for((var) = TAILQ_LAST(head, headname); \ ++ (var) != TAILQ_END(head); \ ++ (var) = TAILQ_PREV(var, headname, field)) ++ ++/* ++ * Tail queue functions. ++ */ ++#define DWC_TAILQ_INIT(head) do { \ ++ (head)->tqh_first = NULL; \ ++ (head)->tqh_last = &(head)->tqh_first; \ ++} while (0) ++ ++#define DWC_TAILQ_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ ++ (head)->tqh_first->field.tqe_prev = \ ++ &(elm)->field.tqe_next; \ ++ else \ ++ (head)->tqh_last = &(elm)->field.tqe_next; \ ++ (head)->tqh_first = (elm); \ ++ (elm)->field.tqe_prev = &(head)->tqh_first; \ ++} while (0) ++ ++#define DWC_TAILQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.tqe_next = NULL; \ ++ (elm)->field.tqe_prev = (head)->tqh_last; \ ++ *(head)->tqh_last = (elm); \ ++ (head)->tqh_last = &(elm)->field.tqe_next; \ ++} while (0) ++ ++#define DWC_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ ++ (elm)->field.tqe_next->field.tqe_prev = \ ++ &(elm)->field.tqe_next; \ ++ else \ ++ (head)->tqh_last = &(elm)->field.tqe_next; \ ++ (listelm)->field.tqe_next = (elm); \ ++ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ ++} while (0) ++ ++#define DWC_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ ++ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ ++ (elm)->field.tqe_next = (listelm); \ ++ *(listelm)->field.tqe_prev = (elm); \ ++ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ ++} while (0) ++ ++#define DWC_TAILQ_REMOVE(head, elm, field) do { \ ++ if (((elm)->field.tqe_next) != NULL) \ ++ (elm)->field.tqe_next->field.tqe_prev = \ ++ (elm)->field.tqe_prev; \ ++ else \ ++ (head)->tqh_last = (elm)->field.tqe_prev; \ ++ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ ++} while (0) ++ ++#define DWC_TAILQ_REPLACE(head, elm, elm2, field) do { \ ++ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ ++ (elm2)->field.tqe_next->field.tqe_prev = \ ++ &(elm2)->field.tqe_next; \ ++ else \ ++ (head)->tqh_last = &(elm2)->field.tqe_next; \ ++ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ ++ *(elm2)->field.tqe_prev = (elm2); \ ++} while (0) ++ ++/* ++ * Circular queue definitions. ++ */ ++#define DWC_CIRCLEQ_HEAD(name, type) \ ++struct name { \ ++ struct type *cqh_first; /* first element */ \ ++ struct type *cqh_last; /* last element */ \ ++} ++ ++#define DWC_CIRCLEQ_HEAD_INITIALIZER(head) \ ++ { DWC_CIRCLEQ_END(&head), DWC_CIRCLEQ_END(&head) } ++ ++#define DWC_CIRCLEQ_ENTRY(type) \ ++struct { \ ++ struct type *cqe_next; /* next element */ \ ++ struct type *cqe_prev; /* previous element */ \ ++} ++ ++/* ++ * Circular queue access methods ++ */ ++#define DWC_CIRCLEQ_FIRST(head) ((head)->cqh_first) ++#define DWC_CIRCLEQ_LAST(head) ((head)->cqh_last) ++#define DWC_CIRCLEQ_END(head) ((void *)(head)) ++#define DWC_CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) ++#define DWC_CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) ++#define DWC_CIRCLEQ_EMPTY(head) \ ++ (DWC_CIRCLEQ_FIRST(head) == DWC_CIRCLEQ_END(head)) ++ ++#define DWC_CIRCLEQ_EMPTY_ENTRY(elm, field) (((elm)->field.cqe_next == NULL) && ((elm)->field.cqe_prev == NULL)) ++ ++#define DWC_CIRCLEQ_FOREACH(var, head, field) \ ++ for((var) = DWC_CIRCLEQ_FIRST(head); \ ++ (var) != DWC_CIRCLEQ_END(head); \ ++ (var) = DWC_CIRCLEQ_NEXT(var, field)) ++ ++#define DWC_CIRCLEQ_FOREACH_SAFE(var, var2, head, field) \ ++ for((var) = DWC_CIRCLEQ_FIRST(head), var2 = DWC_CIRCLEQ_NEXT(var, field); \ ++ (var) != DWC_CIRCLEQ_END(head); \ ++ (var) = var2, var2 = DWC_CIRCLEQ_NEXT(var, field)) ++ ++#define DWC_CIRCLEQ_FOREACH_REVERSE(var, head, field) \ ++ for((var) = DWC_CIRCLEQ_LAST(head); \ ++ (var) != DWC_CIRCLEQ_END(head); \ ++ (var) = DWC_CIRCLEQ_PREV(var, field)) ++ ++/* ++ * Circular queue functions. ++ */ ++#define DWC_CIRCLEQ_INIT(head) do { \ ++ (head)->cqh_first = DWC_CIRCLEQ_END(head); \ ++ (head)->cqh_last = DWC_CIRCLEQ_END(head); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INIT_ENTRY(elm, field) do { \ ++ (elm)->field.cqe_next = NULL; \ ++ (elm)->field.cqe_prev = NULL; \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ ++ (elm)->field.cqe_prev = (listelm); \ ++ if ((listelm)->field.cqe_next == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_last = (elm); \ ++ else \ ++ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ ++ (listelm)->field.cqe_next = (elm); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ ++ (elm)->field.cqe_next = (listelm); \ ++ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ ++ if ((listelm)->field.cqe_prev == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_first = (elm); \ ++ else \ ++ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ ++ (listelm)->field.cqe_prev = (elm); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ ++ (elm)->field.cqe_next = (head)->cqh_first; \ ++ (elm)->field.cqe_prev = DWC_CIRCLEQ_END(head); \ ++ if ((head)->cqh_last == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_last = (elm); \ ++ else \ ++ (head)->cqh_first->field.cqe_prev = (elm); \ ++ (head)->cqh_first = (elm); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.cqe_next = DWC_CIRCLEQ_END(head); \ ++ (elm)->field.cqe_prev = (head)->cqh_last; \ ++ if ((head)->cqh_first == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_first = (elm); \ ++ else \ ++ (head)->cqh_last->field.cqe_next = (elm); \ ++ (head)->cqh_last = (elm); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_REMOVE(head, elm, field) do { \ ++ if ((elm)->field.cqe_next == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_last = (elm)->field.cqe_prev; \ ++ else \ ++ (elm)->field.cqe_next->field.cqe_prev = \ ++ (elm)->field.cqe_prev; \ ++ if ((elm)->field.cqe_prev == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_first = (elm)->field.cqe_next; \ ++ else \ ++ (elm)->field.cqe_prev->field.cqe_next = \ ++ (elm)->field.cqe_next; \ ++} while (0) ++ ++#define DWC_CIRCLEQ_REMOVE_INIT(head, elm, field) do { \ ++ DWC_CIRCLEQ_REMOVE(head, elm, field); \ ++ DWC_CIRCLEQ_INIT_ENTRY(elm, field); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ ++ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ ++ DWC_CIRCLEQ_END(head)) \ ++ (head).cqh_last = (elm2); \ ++ else \ ++ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ ++ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ ++ DWC_CIRCLEQ_END(head)) \ ++ (head).cqh_first = (elm2); \ ++ else \ ++ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ ++} while (0) ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_LIST_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/ep0.c b/drivers/usb/gadget/udc/hiudc3/ep0.c +new file mode 100644 +index 0000000..da09604 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/ep0.c +@@ -0,0 +1,1252 @@ ++/** @file ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++#ifdef DWC_UTE ++#include "ute_if.h" ++#endif ++ ++/*=======================================================================*/ ++/* ++ * EP0 routines ++ */ ++ ++#if !defined(__linux__) || !defined(DWC_BOS_IN_GADGET) ++ ++/** The BOS Descriptor */ ++ ++static usb_dev_cap_20_ext_desc_t cap1_desc = { ++ sizeof(usb_dev_cap_20_ext_desc_t), /* bLength */ ++ UDESC_DEVICE_CAPABILITY, /* bDescriptorType */ ++ USB_DEVICE_CAPABILITY_20_EXTENSION, /* bDevCapabilityType */ ++ UCONSTDW(USB_20_EXT_LPM), /* bmAttributes */ ++}; ++ ++static usb_dev_cap_ss_usb_t cap2_desc = { ++ sizeof(usb_dev_cap_ss_usb_t), /* bLength */ ++ UDESC_DEVICE_CAPABILITY, /* bDescriptorType */ ++ USB_DEVICE_CAPABILITY_SS_USB, /* bDevCapabilityType */ ++ 0x0, /* bmAttributes */ ++ UCONSTW(USB_DC_SS_USB_SPEED_SUPPORT_SS /* wSpeedsSupported */ ++ | USB_DC_SS_USB_SPEED_SUPPORT_HIGH ++ | USB_DC_SS_USB_SPEED_SUPPORT_FULL), ++ USB_DC_SS_USB_SPEED_SUPPORT_FULL, /* bFunctionalitySupport */ ++ /* @todo set these to correct values */ ++ 10, /* bU1DevExitLat (10 us) */ ++ UCONSTW(256), /* wU2DevExitLat (256 us) */ ++}; ++ ++#ifdef DWC_OPTIONAL_UUID ++ /* The Denali host (and Win8) chokes on the optional container ID */ ++ ++static usb_dev_cap_container_id_t cap3_desc = { ++ sizeof(usb_dev_cap_container_id_t), /* bLength */ ++ UDESC_DEVICE_CAPABILITY, /* bDescriptorType */ ++ USB_DEVICE_CAPABILITY_CONTAINER_ID, /* bDevCapabilityType */ ++ 0, /* bReserved */ ++ /* @todo Create UUID */ ++ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, /* containerID */ ++}; ++#endif ++ ++static wusb_bos_desc_t bos_desc = { ++ sizeof(wusb_bos_desc_t), /* bLength */ ++ WUDESC_BOS, /* bDescriptorType */ ++ ++#ifdef DWC_OPTIONAL_UUID ++ UCONSTW(sizeof(wusb_bos_desc_t) /* wTotalLength */ ++ + sizeof(cap1_desc) + sizeof(cap2_desc) + sizeof(cap3_desc)), ++ 3, /* bNumDeviceCaps */ ++#else ++ UCONSTW(sizeof(wusb_bos_desc_t) /* wTotalLength */ ++ + sizeof(cap1_desc) + sizeof(cap2_desc)), ++ 2, /* bNumDeviceCaps */ ++#endif ++}; ++ ++#endif /* !__linux__ || ... */ ++ ++/** ++ * This routine starts the data stage of a 3-stage control command. ++ * pcd->ep0state must be set to EP0_OUT_DATA_PHASE or EP0_IN_DATA_PHASE, and ++ * pcd->ep0->dwc_ep.is_in must be set to 0 or 1 before calling this routine. ++ * For IN, the data to be sent must be placed in pcd->ep0_status_buf before ++ * the call. ++ */ ++void dwc_usb3_pcd_ep0_data_stage(dwc_usb3_pcd_t *pcd, int length) ++{ ++ pcd->ep0_req->dwc_req.buf[0] = (char *)pcd->ep0_status_buf; ++ pcd->ep0_req->dwc_req.bufdma[0] = pcd->ep0_status_buf_dma; ++ pcd->ep0_req->dwc_req.length = length; ++ pcd->ep0_req->dwc_req.actual = 0; ++ pcd->ep0_status_pending = 1; ++ pcd->ep0->dwc_ep.send_zlp = 0; ++ dwc_usb3_pcd_ep0_start_transfer(pcd, pcd->ep0_req); ++} ++ ++/** ++ * This routine processes the SET_ADDRESS Setup Commands. ++ */ ++static void do_set_address(dwc_usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ ++ dwc_debug0(pcd->usb3_dev, "SET ADDRESS\n"); ++ ++ if (ctrl.bmRequestType == UT_DEVICE) { ++#ifdef DEBUG_EP0 ++ dwc_debug1(pcd->usb3_dev, "SET_ADDRESS %d\n", ++ UGETW(ctrl.wValue)); ++#endif ++ dwc_usb3_set_address(pcd, UGETW(ctrl.wValue)); ++ pcd->ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ if (UGETW(ctrl.wValue)) ++ pcd->state = DWC_STATE_ADDRESSED; ++ else ++ pcd->state = DWC_STATE_DEFAULT; ++ } ++} ++ ++/** ++ * This routine stalls EP0. ++ */ ++static void ep0_do_stall(dwc_usb3_pcd_t *pcd, int err_val) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ dwc_usb3_pcd_ep_t *ep0 = pcd->ep0; ++ ++ dwc_print3(pcd->usb3_dev, "req %02x.%02x protocol STALL; err %d\n", ++ ctrl.bmRequestType, ctrl.bRequest, err_val); ++ ep0->dwc_ep.is_in = 0; ++ dwc_usb3_pcd_ep_set_stall(pcd, ep0); ++ ep0->dwc_ep.stopped = 1; ++ pcd->ep0state = EP0_IDLE; ++ dwc_usb3_pcd_ep0_out_start(pcd); ++} ++ ++/** ++ * Clear the EP halt (STALL), and if there are pending requests start ++ * the transfer. ++ */ ++static void do_clear_halt(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ ++ if (ep->dwc_ep.stall_clear_flag == 0) { ++ if (ep != pcd->ep0) ++ dwc_usb3_pcd_ep_clear_stall(pcd, ep); ++ ++ if (ep->dwc_ep.stopped) { ++ ep->dwc_ep.stopped = 0; ++ ++ /* If there is a request in the EP queue start it */ ++ if (ep != pcd->ep0 && ep->dwc_ep.is_in) ++ dwc_usb3_gadget_start_next_request(pcd, ep); ++ } ++ } else { ++ dwc_usb3_dis_usb2_suspend(pcd); ++ ++ /* Clear sequence number using DEPCFG */ ++ if (ep->dwc_ep.is_in) { ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ dwc_usb3_dep_cfg(pcd, ep_reg, ep->dwc_ep.param0in, ++ ep->dwc_ep.param1in, 0); ++ } else { ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ dwc_usb3_dep_cfg(pcd, ep_reg, ep->dwc_ep.param0out, ++ ep->dwc_ep.param1out, 0); ++ } ++ ++ dwc_usb3_ena_usb2_suspend(pcd); ++ } ++ ++ /* Start Control Status Phase */ ++ pcd->ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++} ++ ++/** ++ * This routine handles the Get Descriptor request for the BOS descriptor ++ * and the OTG descriptor, and passes all other requests to the Gadget driver. ++ */ ++static void do_get_descriptor(dwc_usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ u8 desc_type = UGETW(ctrl.wValue) >> 8; ++ int ret; ++ ++#if defined(CONFIG_USB_OTG_DWC) || !defined(__linux__) || !defined(DWC_BOS_IN_GADGET) ++ u16 max = UGETW(ctrl.wLength); ++ u8 *buf = pcd->ep0_status_buf; ++ int length; ++#endif ++ ++#ifdef DEBUG_EP0 ++ dwc_debug5(pcd->usb3_dev, "GET_DESCRIPTOR %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, UGETW(ctrl.wValue), ++ UGETW(ctrl.wIndex), UGETW(ctrl.wLength)); ++#endif ++ ++ switch (desc_type) { ++ ++#ifdef CONFIG_USB_OTG_DWC ++ case UDESC_OTG: ++ dwc_debug0(pcd->usb3_dev, "\nGET_DESCRIPTOR(OTG)\n\n"); ++ buf[0] = 5; ++ buf[1] = UDESC_OTG; ++ if (pcd->speed == USB_SPEED_SUPER) ++ buf[2] = 0xd; ++ else ++ buf[2] = 0x7; ++ ++ buf[3] = 0; ++ if (pcd->speed == USB_SPEED_SUPER) ++ buf[4] = 0x3; ++ else ++ buf[4] = 0x2; ++ ++ length = 5; ++ dwc_usb3_pcd_ep0_data_stage(pcd, length < max ? length : max); ++ break; ++#endif ++ ++ case UDESC_BOS: ++ dwc_debug0(pcd->usb3_dev, "\nGET_DESCRIPTOR(BOS)\n\n"); ++ if (pcd->speed != USB_SPEED_SUPER && ++ pcd->usb3_dev->core_params->nobos) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++#if !defined(__linux__) || !defined(DWC_BOS_IN_GADGET) ++ memcpy(buf, &bos_desc, sizeof(bos_desc)); ++ buf += sizeof(bos_desc); ++ ++ if (pcd->usb3_dev->core_params->besl) { ++ u32 d = UGETDW(cap1_desc.bmAttributes); ++ ++ d |= USB_20_EXT_BESL | USB_20_EXT_BASELINE_BESL_VALID | ++ USB_20_EXT_DEEP_BESL_VALID; ++ d &= ~(USB_20_EXT_BASELINE_BESL_BITS | ++ USB_20_EXT_DEEP_BESL_BITS); ++ d |= pcd->usb3_dev->core_params->baseline_besl << ++ USB_20_EXT_BASELINE_BESL_SHIFT; ++ d |= pcd->usb3_dev->core_params->deep_besl << ++ USB_20_EXT_DEEP_BESL_SHIFT; ++ USETDW(cap1_desc.bmAttributes, d); ++ } ++ ++ if (pcd->usb3_dev->core_params->lpmctl == 0) ++ USETDW(cap1_desc.bmAttributes, 0); ++ ++ memcpy(buf, &cap1_desc, sizeof(cap1_desc)); ++ buf += sizeof(cap1_desc); ++ memcpy(buf, &cap2_desc, sizeof(cap2_desc)); ++ ++#ifdef DWC_OPTIONAL_UUID ++ buf += sizeof(cap2_desc); ++ memcpy(buf, &cap3_desc, sizeof(cap3_desc)); ++#endif ++ length = UGETW(bos_desc.wTotalLength); ++ dwc_usb3_pcd_ep0_data_stage(pcd, length < max ? length : max); ++ break; ++#else ++ /* FALL THROUGH */ ++#endif ++ ++ default: ++ /* Call the Gadget driver's setup routine */ ++ ret = dwc_usb3_gadget_setup(pcd, &ctrl); ++ if (ret < 0) ++ ep0_do_stall(pcd, ret); ++ break; ++ } ++} ++ ++/** ++ * This routine processes the GET_STATUS Setup Commands. ++ */ ++static void do_get_status(dwc_usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ u8 *status = pcd->ep0_status_buf; ++ dwc_usb3_pcd_ep_t *ep; ++ int length; ++ ++#ifdef DEBUG_EP0 ++ dwc_debug5(pcd->usb3_dev, "GET_STATUS %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, UGETW(ctrl.wValue), ++ UGETW(ctrl.wIndex), UGETW(ctrl.wLength)); ++#endif ++ ++ if (UGETW(ctrl.wLength) != 2 ++#ifdef CONFIG_USB_OTG_DWC ++ && UGETW(ctrl.wIndex) != 0xf000 ++#endif ++ ) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) { ++ case UT_DEVICE: ++#ifdef CONFIG_USB_OTG_DWC ++ /* HNP Polling */ ++ if (UGETW(ctrl.wIndex) == 0xf000) ++ *status = pcd->wants_host ? 1 : 0; ++ else ++#endif ++ { ++ *status = 1; /* Self powered */ ++ ++ if (pcd->speed == USB_SPEED_SUPER) { ++ if (pcd->state == DWC_STATE_CONFIGURED) { ++ if (dwc_usb3_u1_enabled(pcd)) ++ *status |= 1 << 2; ++ ++ if (dwc_usb3_u2_enabled(pcd)) ++ *status |= 1 << 3; ++ ++ *status |= pcd->ltm_enable << 4; ++ } ++ } else { ++ *status |= pcd->remote_wakeup_enable << 1; ++ } ++ } ++ ++ dwc_debug1(pcd->usb3_dev, "GET_STATUS(Device)=%02x\n", *status); ++ *(status + 1) = 0; ++ break; ++ ++ case UT_INTERFACE: ++ *status = 0; ++ if (pcd->usb3_dev->core_params->wakeup) ++ *status |= 1; ++ *status |= pcd->remote_wakeup_enable << 1; ++ dwc_debug2(pcd->usb3_dev, "GET_STATUS(Interface %d)=%02x\n", ++ UGETW(ctrl.wIndex), *status); ++ *(status + 1) = 0; ++ break; ++ ++ case UT_ENDPOINT: ++ ep = dwc_usb3_pcd_get_ep_by_addr(pcd, UGETW(ctrl.wIndex)); ++ ++ /* @todo check for EP stall */ ++ *status = ep->dwc_ep.stopped; ++ dwc_debug2(pcd->usb3_dev, "GET_STATUS(Endpoint %d)=%02x\n", ++ UGETW(ctrl.wIndex), *status); ++ *(status + 1) = 0; ++ break; ++ ++ default: ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++#ifdef CONFIG_USB_OTG_DWC ++ if (UGETW(ctrl.wIndex) == 0xf000) ++ length = 1; ++ else ++#endif ++ length = 2; ++ dwc_usb3_pcd_ep0_data_stage(pcd, length); ++} ++ ++/** ++ * This routine processes the SET_FEATURE Setup Commands. ++ */ ++static void do_set_feature(dwc_usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ dwc_usb3_pcd_ep_t *ep; ++ int ret; ++ ++#ifdef DEBUG_EP0 ++ dwc_debug5(pcd->usb3_dev, "SET_FEATURE %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, UGETW(ctrl.wValue), ++ UGETW(ctrl.wIndex), UGETW(ctrl.wLength)); ++#endif ++ ++ switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) { ++ case UT_DEVICE: ++ switch (UGETW(ctrl.wValue)) { ++ case UF_DEVICE_REMOTE_WAKEUP: ++ pcd->remote_wakeup_enable = 1; ++ break; ++ ++ case UF_TEST_MODE: ++ /* Setup the Test Mode tasklet to do the Test ++ * Packet generation after the SETUP Status ++ * phase has completed. */ ++ pcd->test_mode = UGETW(ctrl.wIndex) >> 8; ++ dwc_usb3_task_schedule(&pcd->test_mode_tasklet); ++ break; ++ ++ case UF_DEVICE_B_HNP_ENABLE: ++ dwc_debug0(pcd->usb3_dev, ++ "SET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); ++#ifdef CONFIG_USB_OTG_DWC ++ if (pcd->wants_host) { ++ pcd->b_hnp_enable = 0; ++ pcd->wants_host = 0; ++ dwc_usb3_start_hnp(pcd); ++ } else { ++ pcd->b_hnp_enable = 1; ++ } ++#endif ++ break; ++ ++ case UOTG_NTF_HOST_REL: ++ dwc_debug0(pcd->usb3_dev, ++ "SET_FEATURE: USB_NTF_HOST_REL\n"); ++#ifdef CONFIG_USB_OTG_DWC ++ dwc_usb3_host_release(pcd); ++#endif ++ break; ++ ++ case UOTG_B3_RSP_ENABLE: ++ dwc_debug0(pcd->usb3_dev, ++ "SET_FEATURE: USB_B3_RSP_ENABLE\n"); ++ break; ++ ++ case UF_DEVICE_A_HNP_SUPPORT: ++ /* RH port supports HNP */ ++ dwc_debug0(pcd->usb3_dev, ++ "SET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n"); ++ break; ++ ++ case UF_DEVICE_A_ALT_HNP_SUPPORT: ++ /* other RH port does */ ++ dwc_debug0(pcd->usb3_dev, ++ "SET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); ++ break; ++ ++ case UF_U1_ENABLE: ++ dwc_debug0(pcd->usb3_dev, "SET_FEATURE: UF_U1_ENABLE\n"); ++ if (pcd->speed != USB_SPEED_SUPER || ++ pcd->state != DWC_STATE_CONFIGURED) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ if (pcd->usb3_dev->core_params->pwrctl & 1) ++ dwc_usb3_enable_u1(pcd); ++ break; ++ ++ case UF_U2_ENABLE: ++ dwc_debug0(pcd->usb3_dev, "SET_FEATURE: UF_U2_ENABLE\n"); ++ if (pcd->speed != USB_SPEED_SUPER || ++ pcd->state != DWC_STATE_CONFIGURED) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ if (pcd->usb3_dev->core_params->pwrctl & 2) ++ dwc_usb3_enable_u2(pcd); ++ break; ++ ++ case UF_LTM_ENABLE: ++ dwc_debug0(pcd->usb3_dev, ++ "SET_FEATURE: UF_LTM_ENABLE\n"); ++ if (pcd->speed != USB_SPEED_SUPER || ++ pcd->state != DWC_STATE_CONFIGURED || ++ UGETW(ctrl.wIndex) != 0) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ pcd->ltm_enable = 1; ++ pcd->send_lpm = 1; ++ break; ++ ++ default: ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ break; ++ ++ case UT_INTERFACE: ++ /* if FUNCTION_SUSPEND ... */ ++ if (UGETW(ctrl.wValue) == 0) { ++ /* if Function Remote Wake Enabled ... */ ++ if (UGETW(ctrl.wIndex) >> 8 & 2) ++ pcd->remote_wakeup_enable = 1; ++ else ++ pcd->remote_wakeup_enable = 0; ++ ++ /* if Function Low Power Suspend ... */ ++ // TODO ++ ++ break; ++ } ++ ++ ret = dwc_usb3_gadget_setup(pcd, &ctrl); ++ if (ret < 0) ++ ep0_do_stall(pcd, ret); ++ return; ++ ++ case UT_ENDPOINT: ++ ep = dwc_usb3_pcd_get_ep_by_addr(pcd, UGETW(ctrl.wIndex)); ++ if (UGETW(ctrl.wValue) != UF_ENDPOINT_HALT) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ ep->dwc_ep.stopped = 1; ++ dwc_usb3_pcd_ep_set_stall(pcd, ep); ++ break; ++ } ++ ++ pcd->ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++} ++ ++/** ++ * This routine processes the CLEAR_FEATURE Setup Commands. ++ */ ++static void do_clear_feature(dwc_usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ dwc_usb3_pcd_ep_t *ep; ++ ++#ifdef DEBUG_EP0 ++ dwc_debug5(pcd->usb3_dev, "CLEAR_FEATURE %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, UGETW(ctrl.wValue), ++ UGETW(ctrl.wIndex), UGETW(ctrl.wLength)); ++#endif ++ ++ switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) { ++ case UT_DEVICE: ++ switch (UGETW(ctrl.wValue)) { ++ case UF_DEVICE_REMOTE_WAKEUP: ++ pcd->remote_wakeup_enable = 0; ++ break; ++ ++ case UF_TEST_MODE: ++ /* @todo Add CLEAR_FEATURE for TEST modes. */ ++ break; ++ ++ case UF_U1_ENABLE: ++ dwc_debug0(pcd->usb3_dev, ++ "CLEAR_FEATURE: UF_U1_ENABLE\n"); ++ if (pcd->speed != USB_SPEED_SUPER || ++ pcd->state != DWC_STATE_CONFIGURED) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ dwc_usb3_disable_u1(pcd); ++ break; ++ ++ case UF_U2_ENABLE: ++ dwc_debug0(pcd->usb3_dev, ++ "CLEAR_FEATURE: UF_U2_ENABLE\n"); ++ if (pcd->speed != USB_SPEED_SUPER || ++ pcd->state != DWC_STATE_CONFIGURED) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ dwc_usb3_disable_u2(pcd); ++ break; ++ ++ case UF_LTM_ENABLE: ++ dwc_debug0(pcd->usb3_dev, ++ "CLEAR_FEATURE: UF_LTM_ENABLE\n"); ++ if (pcd->speed != USB_SPEED_SUPER || ++ pcd->state != DWC_STATE_CONFIGURED || ++ UGETW(ctrl.wIndex) != 0) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ pcd->ltm_enable = 0; ++ pcd->send_lpm = 1; ++ break; ++ ++ default: ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ break; ++ ++ case UT_INTERFACE: ++ /* if FUNCTION_SUSPEND ... */ ++ if (UGETW(ctrl.wValue) == 0) { ++ /* if Function Remote Wake Enabled ... */ ++ if (UGETW(ctrl.wIndex) >> 8 & 2) { ++ pcd->remote_wakeup_enable = 0; ++ } ++ ++ /* if Function Low Power Suspend ... */ ++ // TODO ++ ++ break; ++ } ++ ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ ++ case UT_ENDPOINT: ++ ep = dwc_usb3_pcd_get_ep_by_addr(pcd, UGETW(ctrl.wIndex)); ++ if (UGETW(ctrl.wValue) != UF_ENDPOINT_HALT) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ do_clear_halt(pcd, ep); ++ return; ++ } ++ ++ pcd->ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++} ++ ++/** ++ * This routine processes SETUP commands. The USB Command processing is ++ * done in two places - the first being the PCD and the second being the ++ * Gadget driver (for example, the File-Backed Storage Gadget driver). ++ * ++ * <table> ++ * <tr><td> Command </td><td> Driver </td><td> Description </td></tr> ++ * ++ * <tr><td> GET_STATUS </td><td> PCD </td><td> Command is processed ++ * as defined in chapter 9 of the USB 2.0 Specification. </td></tr> ++ * ++ * <tr><td> SET_FEATURE </td><td> PCD / Gadget driver </td><td> Device ++ * and Endpoint requests are processed by the PCD. Interface requests ++ * are passed to the Gadget driver. </td></tr> ++ * ++ * <tr><td> CLEAR_FEATURE </td><td> PCD </td><td> Device and Endpoint ++ * requests are processed by the PCD. Interface requests are ignored. ++ * The only Endpoint feature handled is ENDPOINT_HALT. </td></tr> ++ * ++ * <tr><td> SET_ADDRESS </td><td> PCD </td><td> Program the DCFG register ++ * with device address received. </td></tr> ++ * ++ * <tr><td> GET_DESCRIPTOR </td><td> Gadget driver </td><td> Return the ++ * requested descriptor. </td></tr> ++ * ++ * <tr><td> SET_DESCRIPTOR </td><td> Gadget driver </td><td> Optional - ++ * not implemented by any of the existing Gadget drivers. </td></tr> ++ * ++ * <tr><td> GET_CONFIGURATION </td><td> Gadget driver </td><td> Return ++ * the current configuration. </td></tr> ++ * ++ * <tr><td> SET_CONFIGURATION </td><td> Gadget driver </td><td> Disable ++ * all EPs and enable EPs for new configuration. </td></tr> ++ * ++ * <tr><td> GET_INTERFACE </td><td> Gadget driver </td><td> Return the ++ * current interface. </td></tr> ++ * ++ * <tr><td> SET_INTERFACE </td><td> Gadget driver </td><td> Disable all ++ * EPs and enable EPs for new interface. </td></tr> ++ * </table> ++ * ++ * When the SETUP Phase Done interrupt occurs, the generic SETUP commands ++ * are processed by this routine. Calling the Gadget driver's ++ * dwc_usb3_gadget_setup() routine from here processes the gadget-specific ++ * SETUP commands. ++ */ ++void dwc_usb3_pcd_do_setup(dwc_usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ dwc_usb3_pcd_ep_t *ep0 = pcd->ep0; ++ u16 wvalue, wlength; ++ int ret; ++ ++ dwc_debug2(pcd->usb3_dev, "%s(%lx)\n", __func__, (unsigned long)pcd); ++ wvalue = UGETW(ctrl.wValue); ++ wlength = UGETW(ctrl.wLength); ++#if 0 ++#ifdef DEBUG_EP0 ++ dwc_debug0(pcd->usb3_dev, "\n"); ++ dwc_print1(pcd->usb3_dev, "setup_pkt[0]=0x%08x\n", ++ pcd->ep0_setup_pkt->d32[0]); ++ dwc_print1(pcd->usb3_dev, "setup_pkt[1]=0x%08x\n", ++ pcd->ep0_setup_pkt->d32[1]); ++ dwc_print5(pcd->usb3_dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, wvalue, ++ UGETW(ctrl.wIndex), wlength); ++ dwc_debug0(pcd->usb3_dev, "\n"); ++#endif ++#endif ++ /* Clean up the request queue */ ++ dwc_usb3_gadget_request_nuke(pcd, ep0); ++ ep0->dwc_ep.stopped = 0; ++ ep0->dwc_ep.three_stage = 1; ++ ++ if (ctrl.bmRequestType & UE_DIR_IN) { ++ ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_DATA_PHASE; ++ } else { ++ ep0->dwc_ep.is_in = 0; ++ pcd->ep0state = EP0_OUT_DATA_PHASE; ++ } ++ ++ if (wlength == 0) { ++ ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_GADGET; ++ ep0->dwc_ep.three_stage = 0; ++ } ++ ++ if ((UT_GET_TYPE(ctrl.bmRequestType)) != UT_STANDARD) { ++ /* handle non-standard (class/vendor) requests ++ * in the gadget driver ++ */ ++ ret = dwc_usb3_gadget_setup(pcd, &ctrl); ++ if (ret < 0) ++ ep0_do_stall(pcd, ret); ++ return; ++ } ++ ++ /* @todo NGS: Handle bad setup packet? */ ++ ++/////////////////////////////////////////// ++//// --- Standard Request handling --- //// ++ ++ switch (ctrl.bRequest) { ++ case UR_GET_STATUS: ++ do_get_status(pcd); ++ break; ++ ++ case UR_CLEAR_FEATURE: ++ do_clear_feature(pcd); ++ break; ++ ++ case UR_SET_FEATURE: ++ do_set_feature(pcd); ++ break; ++ ++ case UR_SET_ADDRESS: ++ do_set_address(pcd); ++ break; ++ ++ case UR_SET_INTERFACE: ++ dwc_debug0(pcd->usb3_dev, "USB_REQ_SET_INTERFACE\n"); ++#ifndef DWC_UTE ++ dwc_usb3_clr_eps_enabled(pcd); ++ ++# ifdef DWC_STAR_9000463548_WORKAROUND ++ pcd->configuring = 1; ++# endif ++#endif ++ ret = dwc_usb3_gadget_setup(pcd, &ctrl); ++ if (ret < 0) { ++#ifndef DWC_UTE ++# ifdef DWC_STAR_9000463548_WORKAROUND ++ pcd->configuring = 0; ++# endif ++#endif ++ ep0_do_stall(pcd, ret); ++ return; ++ } ++ ++ break; ++ ++ case UR_SET_CONFIG: ++ dwc_debug0(pcd->usb3_dev, "USB_REQ_SET_CONFIGURATION\n"); ++#ifdef DWC_UTE ++ if (wvalue != 0) ++ dwc_usb3_ute_config(pcd->usb3_dev); ++#endif ++ dwc_usb3_clr_eps_enabled(pcd); ++ ++#ifdef DWC_STAR_9000463548_WORKAROUND ++ pcd->configuring = 1; ++#endif ++ if (pcd->ltm_enable) ++ pcd->send_lpm = 1; ++ ++ ret = dwc_usb3_gadget_setup(pcd, &ctrl); ++ if (ret >= 0) { ++ if (wvalue != 0) ++ pcd->state = DWC_STATE_CONFIGURED; ++ else ++ pcd->state = DWC_STATE_ADDRESSED; ++ } else { ++ ep0_do_stall(pcd, ret); ++#ifdef DWC_STAR_9000463548_WORKAROUND ++ pcd->configuring = 0; ++#endif ++ return; ++ } ++ ++ /* Must wait until SetConfig before accepting U1/U2 link ++ * control, otherwise we have problems with VIA hubs ++ */ ++ if (pcd->usb3_dev->core_params->pwrctl & 1) ++ dwc_usb3_accept_u1(pcd); ++ if (pcd->usb3_dev->core_params->pwrctl & 2) ++ dwc_usb3_accept_u2(pcd); ++ ++ pcd->ltm_enable = 0; ++ break; ++ ++ case UR_GET_DESCRIPTOR: ++ do_get_descriptor(pcd); ++ break; ++ ++ case UR_SET_SEL: ++ dwc_debug0(pcd->usb3_dev, "USB_REQ_SET_SEL\n"); ++ ++ /* For now this is a no-op */ ++ dwc_usb3_pcd_ep0_data_stage(pcd, DWC_STATUS_BUF_SIZE < wlength ? ++ DWC_STATUS_BUF_SIZE : wlength); ++ break; ++ ++ case UR_SET_ISOC_DELAY: ++ dwc_debug0(pcd->usb3_dev, "USB_REQ_SET_ISOC_DELAY\n"); ++ ++ /* For now this is a no-op */ ++ pcd->ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ break; ++ ++ default: ++ /* Call the Gadget driver's setup routine */ ++ ret = dwc_usb3_gadget_setup(pcd, &ctrl); ++ if (ret < 0) ++ ep0_do_stall(pcd, ret); ++ break; ++ } ++} ++ ++/** ++ * This routine starts the Zero-Length Packet for the IN status phase of a ++ * control write transfer. ++ */ ++static void setup_in_status_phase(dwc_usb3_pcd_t *pcd, void *buf, ++ dwc_dma_t dma) ++{ ++ dwc_usb3_pcd_ep_t *ep0 = pcd->ep0; ++ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ ++ if (pcd->ep0state == EP0_STALL) { ++ dwc_debug0(pcd->usb3_dev, "EP0 STALLED\n"); ++ return; ++ } ++ ++ ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_STATUS_PHASE; ++ ++ dwc_debug0(pcd->usb3_dev, "EP0 IN ZLP\n"); ++ ++ pcd->ep0_req->dwc_req.buf[0] = buf; ++ pcd->ep0_req->dwc_req.bufdma[0] = dma; ++ pcd->ep0_req->dwc_req.length = 0; ++ pcd->ep0_req->dwc_req.actual = 0; ++ dwc_usb3_pcd_ep0_start_transfer(pcd, pcd->ep0_req); ++} ++ ++/** ++ * This routine starts the Zero-Length Packet for the OUT status phase of a ++ * control read transfer. ++ */ ++static void setup_out_status_phase(dwc_usb3_pcd_t *pcd, void *buf, ++ dwc_dma_t dma) ++{ ++ dwc_usb3_pcd_ep_t *ep0 = pcd->ep0; ++ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ ++ if (pcd->ep0state == EP0_STALL) { ++ dwc_debug0(pcd->usb3_dev, "EP0 STALLED\n"); ++ return; ++ } ++ ++ ep0->dwc_ep.is_in = 0; ++ pcd->ep0state = EP0_OUT_STATUS_PHASE; ++ ++ dwc_debug0(pcd->usb3_dev, "EP0 OUT ZLP\n"); ++ ++ pcd->ep0_req->dwc_req.buf[0] = buf; ++ pcd->ep0_req->dwc_req.bufdma[0] = dma; ++ pcd->ep0_req->dwc_req.length = 0; ++ pcd->ep0_req->dwc_req.actual = 0; ++ dwc_usb3_pcd_ep0_start_transfer(pcd, pcd->ep0_req); ++} ++ ++#ifdef DEBUG_EP0 ++/** ++ * This routine prints the ep0 state for debug purposes. ++ */ ++void dwc_usb3_print_ep0_state(dwc_usb3_pcd_t *pcd) ++{ ++#ifdef DEBUG ++ char *str; ++ ++ switch (pcd->ep0state) { ++ case EP0_IDLE: ++ str = "EP0_IDLE"; ++ break; ++ case EP0_IN_DATA_PHASE: ++ str = "EP0_IN_DATA_PHASE"; ++ break; ++ case EP0_OUT_DATA_PHASE: ++ str = "EP0_OUT_DATA_PHASE"; ++ break; ++ case EP0_IN_WAIT_GADGET: ++ str = "EP0_IN_WAIT_GADGET"; ++ break; ++ case EP0_OUT_WAIT_GADGET: ++ str = "EP0_OUT_WAIT_GADGET"; ++ break; ++ case EP0_IN_WAIT_NRDY: ++ str = "EP0_IN_WAIT_NRDY"; ++ break; ++ case EP0_OUT_WAIT_NRDY: ++ str = "EP0_OUT_WAIT_NRDY"; ++ break; ++ case EP0_IN_STATUS_PHASE: ++ str = "EP0_IN_STATUS_PHASE"; ++ break; ++ case EP0_OUT_STATUS_PHASE: ++ str = "EP0_OUT_STATUS_PHASE"; ++ break; ++ case EP0_STALL: ++ str = "EP0_STALL"; ++ break; ++ default: ++ str = "EP0_INVALID"; ++ } ++ ++ dwc_debug2(pcd->usb3_dev, "%s(%d)\n", str, pcd->ep0state); ++#endif ++} ++#endif ++ ++/** ++ * This routine completes the ep0 control transfer. ++ */ ++static int ep0_complete_request(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_req_t *req, ++ dwc_usb3_dma_desc_t *desc, int status) ++{ ++ dwc_usb3_pcd_ep_t *ep = pcd->ep0; ++ int is_last = 0; ++ ++ dwc_debug4(pcd->usb3_dev, "%s(%lx,%lx,%d)\n", __func__, ++ (unsigned long)req, (unsigned long)desc, status); ++ ++ if (pcd->ep0_status_pending && !req) { ++ if (ep->dwc_ep.is_in) { ++#ifdef DEBUG_EP0 ++ dwc_debug0(pcd->usb3_dev, ++ "Do setup OUT status phase\n"); ++#endif ++ pcd->ep0->dwc_ep.is_in = 0; ++ pcd->ep0state = EP0_OUT_WAIT_NRDY; ++ } else { ++#ifdef DEBUG_EP0 ++ dwc_debug0(pcd->usb3_dev, "Do setup IN status phase\n"); ++#endif ++ pcd->ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ } ++ ++ pcd->ep0_status_pending = 0; ++ return 1; ++ } ++ ++ if (!req) ++ return 0; ++ ++ dwc_debug1(pcd->usb3_dev, "req=%lx\n", (unsigned long)req); ++ ++ if (pcd->ep0state == EP0_OUT_STATUS_PHASE || ++ pcd->ep0state == EP0_IN_STATUS_PHASE) { ++ is_last = 1; ++ ++ } else if (ep->dwc_ep.is_in) { ++#ifdef DEBUG_EP0 ++ dwc_debug4(pcd->usb3_dev, ++ "IN len=%d actual=%d xfrcnt=%d trbrsp=0x%02x\n", ++ req->dwc_req.length, req->dwc_req.actual, ++ dwc_usb3_get_xfercnt(desc), ++ dwc_usb3_get_xfersts(desc)); ++#endif ++ if (dwc_usb3_get_xfercnt(desc) == 0) { ++ /* Is a Zero Len Packet needed? */ ++ if (req->dwc_req.flags & DWC_PCD_REQ_ZERO) { ++#ifdef DEBUG_EP0 ++ dwc_debug0(pcd->usb3_dev, "Setup Rx ZLP\n"); ++#endif ++ req->dwc_req.flags &= ~DWC_PCD_REQ_ZERO; ++ } ++ ++ pcd->ep0->dwc_ep.is_in = 0; ++ pcd->ep0state = EP0_OUT_WAIT_NRDY; ++ } ++ } else { ++#ifdef DEBUG_EP0 ++ dwc_debug4(pcd->usb3_dev, ++ "OUT len=%d actual=%d xfrcnt=%d trbrsp=0x%02x\n", ++ req->dwc_req.length, req->dwc_req.actual, ++ dwc_usb3_get_xfercnt(desc), ++ dwc_usb3_get_xfersts(desc)); ++#endif ++ /* Is a Zero Len Packet needed? */ ++ if (req->dwc_req.flags & DWC_PCD_REQ_ZERO) { ++#ifdef DEBUG_EP0 ++ dwc_debug0(pcd->usb3_dev, "Setup Tx ZLP\n"); ++#endif ++ req->dwc_req.flags &= ~DWC_PCD_REQ_ZERO; ++ } ++ ++ pcd->ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ } ++ ++ /* Complete the request */ ++ if (is_last) { ++ dwc_debug2(pcd->usb3_dev, "is_last len=%d actual=%d\n", ++ req->dwc_req.length, req->dwc_req.actual); ++ dwc_usb3_pcd_request_done(pcd, ep, req, status); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * This routine handles EP0 Control transfers. ++ * ++ * The state of the control tranfers are tracked in <code>ep0state</code>. ++ */ ++static void dwc_usb3_handle_ep0(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_req_t *req, ++ u32 event) ++{ ++ dwc_usb3_pcd_ep_t *ep0 = pcd->ep0; ++ dwc_usb3_dma_desc_t *desc; ++ u32 byte_count, len; ++ int status; ++ ++#ifdef DEBUG_EP0 ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ dwc_usb3_print_ep0_state(pcd); ++#endif ++ dwc_debug0(pcd->usb3_dev, "HANDLE EP0\n"); ++ ++ switch (pcd->ep0state) { ++ case EP0_IN_DATA_PHASE: ++ if (!req) ++ req = pcd->ep0_req; ++ desc = dwc_usb3_ep0_in_desc(pcd); ++ dwc_debug1(pcd->usb3_dev, "req=%lx\n", (unsigned long)req); ++#ifdef DEBUG_EP0 ++ dwc_debug5(pcd->usb3_dev, ++ "DATA_IN EP%d-%s: type=%d mps=%d trb.status=0x%08x\n", ++ ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"), ++ ep0->dwc_ep.type, ep0->dwc_ep.maxpacket, desc->status); ++#endif ++ if (dwc_usb3_is_hwo(desc)) { ++ dwc_print3(pcd->usb3_dev, ++ "### %s, EP%d-%s HWO bit set 1! ###\n", ++ __func__, ep0->dwc_ep.num, ep0->dwc_ep.is_in ? ++ "IN" : "OUT"); ++ goto out; ++ } ++ ++ status = dwc_usb3_get_xfersts(desc); ++ if (status & DWC_TRBRSP_SETUP_PEND) { ++ /* Start of a new Control transfer */ ++ dwc_debug0(pcd->usb3_dev, "IN SETUP PENDING\n"); ++ desc->status = 0; ++ } ++ ++ byte_count = req->dwc_req.length - dwc_usb3_get_xfercnt(desc); ++ req->dwc_req.actual += byte_count; ++ req->dwc_req.buf[0] += byte_count; ++ req->dwc_req.bufdma[0] += byte_count; ++ dwc_debug3(pcd->usb3_dev, "length=%d byte_count=%d actual=%d\n", ++ req->dwc_req.length, byte_count, req->dwc_req.actual); ++ ++ if (req->dwc_req.actual < req->dwc_req.length) { ++#ifdef DEBUG_EP0 ++ dwc_usb3_dump_dbgregs(pcd->usb3_dev); ++#endif ++ dwc_debug0(pcd->usb3_dev, "IN CONTINUE\n"); ++ //dwc_usb3_pcd_ep0_continue_transfer(pcd, req); ++ //dwc_debug0(pcd->usb3_dev, "CONTINUE TRANSFER\n"); ++ dwc_debug0(pcd->usb3_dev, "Stall EP0\n"); ++ ep0->dwc_ep.is_in = 0; ++ dwc_usb3_pcd_ep_set_stall(pcd, ep0); ++ ep0->dwc_ep.stopped = 1; ++ pcd->ep0state = EP0_IDLE; ++ dwc_usb3_pcd_ep0_out_start(pcd); ++ ++ } else if (ep0->dwc_ep.send_zlp) { ++ dwc_debug0(pcd->usb3_dev, "IN ZLP\n"); ++ dwc_usb3_pcd_ep0_continue_transfer(pcd, req); ++ ep0->dwc_ep.send_zlp = 0; ++ dwc_debug0(pcd->usb3_dev, "CONTINUE TRANSFER\n"); ++ ++ } else { ++ dwc_debug0(pcd->usb3_dev, "IN COMPLETE\n"); ++ /* This sets ep0state = EP0_IN/OUT_WAIT_NRDY */ ++ ep0_complete_request(pcd, req, desc, 0); ++ dwc_debug0(pcd->usb3_dev, "COMPLETE TRANSFER\n"); ++ } ++ ++ break; ++ ++ case EP0_OUT_DATA_PHASE: ++ if (!req) ++ req = pcd->ep0_req; ++ desc = dwc_usb3_ep0_out_desc(pcd); ++ dwc_debug1(pcd->usb3_dev, "req=%lx\n", (unsigned long)req); ++#ifdef DEBUG_EP0 ++ dwc_debug5(pcd->usb3_dev, ++ "DATA_OUT EP%d-%s: type=%d mps=%d trb.status=0x%08x\n", ++ ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"), ++ ep0->dwc_ep.type, ep0->dwc_ep.maxpacket, desc->status); ++#endif ++ if (dwc_usb3_is_hwo(desc)) { ++ dwc_print3(pcd->usb3_dev, ++ "### %s, EP%d-%s HWO bit set 2! ###\n", ++ __func__, ep0->dwc_ep.num, ep0->dwc_ep.is_in ? ++ "IN" : "OUT"); ++ goto out; ++ } ++ ++ status = dwc_usb3_get_xfersts(desc); ++ if (status & DWC_TRBRSP_SETUP_PEND) { ++ /* Start of a new Control transfer */ ++ dwc_debug0(pcd->usb3_dev, "OUT SETUP PENDING\n"); ++ } ++ ++ len = (req->dwc_req.length + ep0->dwc_ep.maxpacket - 1) & ++ ~(ep0->dwc_ep.maxpacket - 1); ++ byte_count = len - dwc_usb3_get_xfercnt(desc); ++ req->dwc_req.actual += byte_count; ++ req->dwc_req.buf[0] += byte_count; ++ req->dwc_req.bufdma[0] += byte_count; ++ dwc_debug3(pcd->usb3_dev, "length=%d byte_count=%d actual=%d\n", ++ req->dwc_req.length, byte_count, req->dwc_req.actual); ++ ++ /*if (req->dwc_req.actual < req->dwc_req.length) { ++ dwc_debug0(pcd->usb3_dev, "OUT CONTINUE\n"); ++ dwc_usb3_pcd_ep0_continue_transfer(pcd, req); ++ dwc_debug0(pcd->usb3_dev, "CONTINUE TRANSFER\n"); ++ ++ } else*/ if (ep0->dwc_ep.send_zlp) { ++ dwc_debug0(pcd->usb3_dev, "OUT ZLP\n"); ++ dwc_usb3_pcd_ep0_continue_transfer(pcd, req); ++ ep0->dwc_ep.send_zlp = 0; ++ dwc_debug0(pcd->usb3_dev, "CONTINUE TRANSFER\n"); ++ ++ } else { ++ dwc_debug0(pcd->usb3_dev, "OUT COMPLETE\n"); ++ /* This sets ep0state = EP0_IN/OUT_WAIT_NRDY */ ++ ep0_complete_request(pcd, req, desc, 0); ++ dwc_debug0(pcd->usb3_dev, "COMPLETE TRANSFER\n"); ++ } ++ ++ break; ++ ++ case EP0_IN_WAIT_GADGET: ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ break; ++ ++ case EP0_OUT_WAIT_GADGET: ++ pcd->ep0state = EP0_OUT_WAIT_NRDY; ++ break; ++ ++ case EP0_IN_WAIT_NRDY: ++ case EP0_OUT_WAIT_NRDY: ++ if (ep0->dwc_ep.is_in) ++ setup_in_status_phase(pcd, dwc_usb3_ep0_setup_pkt(pcd), ++ dwc_usb3_ep0_setup_pkt_dma(pcd)); ++ else ++ setup_out_status_phase(pcd, dwc_usb3_ep0_setup_pkt(pcd), ++ dwc_usb3_ep0_setup_pkt_dma(pcd)); ++ break; ++ ++ case EP0_IN_STATUS_PHASE: ++ case EP0_OUT_STATUS_PHASE: ++ if (ep0->dwc_ep.is_in) ++ desc = dwc_usb3_ep0_in_desc(pcd); ++ else ++ desc = dwc_usb3_ep0_out_desc(pcd); ++#ifdef DEBUG_EP0 ++ dwc_debug2(pcd->usb3_dev, "STATUS EP%d-%s\n", ep0->dwc_ep.num, ++ (ep0->dwc_ep.is_in ? "IN" : "OUT")); ++#endif ++ ep0_complete_request(pcd, req, desc, 0); ++ pcd->ep0state = EP0_IDLE; ++ ep0->dwc_ep.stopped = 1; ++ ep0->dwc_ep.is_in = 0; /* OUT for next SETUP */ ++ ++ if (pcd->send_lpm) { ++ pcd->send_lpm = 0; ++ ++#if 0 // This is only for testing ++ dwc_usb3_xmit_ltm(pcd, ++ 32 << DWC_DGCMDPAR_BELT_VALUE_SHIFT | ++ DWC_LATENCY_VALUE_MULT_32768 << ++ DWC_DGCMDPAR_BELT_SCALE_SHIFT); ++#endif ++ } ++ ++ /* Prepare for more SETUP Packets */ ++ dwc_usb3_pcd_ep0_out_start(pcd); ++ break; ++ ++ case EP0_STALL: ++ dwc_error0(pcd->usb3_dev, "EP0 STALLed, should not get here\n"); ++ break; ++ ++ case EP0_IDLE: ++ dwc_error0(pcd->usb3_dev, "EP0 IDLE, should not get here\n"); ++ break; ++ } ++out: ++#ifdef DEBUG_EP0 ++ dwc_usb3_print_ep0_state(pcd); ++#endif ++ return; ++} ++ ++/** ++ * This routine handles EP0 transfers. ++ * ++ * This routine gets the request corresponding to the current EP0 transfer. If ++ * EP0 is in IDLE state, it calls dwc_usb3_do_setup() to begin processing ++ * the next Setup request, otherwise it calls dwc_usb3_handle_ep0() to handle ++ * the next stage of the current transfer. ++ */ ++void dwc_usb3_handle_ep0_xfer(dwc_usb3_pcd_t *pcd, u32 event) ++{ ++ dwc_usb3_pcd_ep_t *ep0 = pcd->ep0; ++ dwc_usb3_pcd_req_t *req = NULL; ++ ++#ifdef DEBUG_EP0 ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++#endif ++ req = dwc_usb3_gadget_get_request(pcd, ep0); ++ ++ if (pcd->ep0state == EP0_IDLE) { ++#ifdef DEBUG_EP0 ++ dwc_usb3_print_ep0_state(pcd); ++ dwc_debug2(pcd->usb3_dev, "IDLE EP%d-%s\n", ep0->dwc_ep.num, ++ (ep0->dwc_ep.is_in ? "IN" : "OUT")); ++#endif ++ pcd->request_config = 0; ++ dwc_usb3_gadget_do_setup(pcd); ++ } else { ++ dwc_usb3_handle_ep0(pcd, req, event); ++ } ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/hiusb3.c b/drivers/usb/gadget/udc/hiudc3/hiusb3.c +new file mode 100644 +index 0000000..85e981f +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/hiusb3.c +@@ -0,0 +1,64 @@ ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/dma-mapping.h> ++#include <linux/types.h> ++#include "os_dev.h" ++//#include "xhci.h" ++//#include "hiusb3.h" ++ ++ ++MODULE_LICENSE("Dual MIT/GPL"); ++ ++static struct resource hiusb_xhci_res[] = { ++ [0] = { ++ .start = CONFIG_HIUSB_XHCI_REG_BASE_ADDRESS, ++ .end = CONFIG_HIUSB_XHCI_REG_BASE_ADDRESS ++ + CONFIG_HIUSB_XHCI_REG_BASE_ADDRESS_LEN - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = CONFIG_HIUSB_XHCI_IRQ_NUMBER, ++ .end = CONFIG_HIUSB_XHCI_IRQ_NUMBER, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++static void usb_xhci_platdev_release(struct device *dev) ++{ ++ /* These don't need to do anything because the ++ pdev structures are statically allocated. */ ++} ++ ++//u64 usb_dmamask = DMA_BIT_MASK(32); ++ ++static struct platform_device hiusb_xhci_platdev = { ++ .name = "dwc_usb3", ++ .id = -1, ++ .dev = { ++ .init_name = "dwc_usb3", ++ .platform_data = NULL, ++ .dma_mask = DMA_BIT_MASK(32), ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ .release = usb_xhci_platdev_release, ++ }, ++ .num_resources = ARRAY_SIZE(hiusb_xhci_res), ++ .resource = hiusb_xhci_res, ++}; ++ ++static int __init xhci_device_init(void) ++{ ++// if (usb_disabled()) ++ // return -ENODEV; ++ int reg; ++// reg = readl(IO_ADDRESS(0x11080430)); ++ printk("\n########################################################%s,%d\n",__func__,__LINE__); ++ return 0; ++ return platform_device_register(&hiusb_xhci_platdev); ++} ++ ++static void __exit xhci_device_exit(void) ++{ ++ platform_device_unregister(&hiusb_xhci_platdev); ++} ++ ++module_init(xhci_device_init); ++module_exit(xhci_device_exit); +diff --git a/drivers/usb/gadget/udc/hiudc3/hw.h b/drivers/usb/gadget/udc/hiudc3/hw.h +new file mode 100644 +index 0000000..5967a13 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/hw.h +@@ -0,0 +1,1967 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/hw.h $ ++ * $Revision: #65 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef _DWC_USB3_REGS_H_ ++#define _DWC_USB3_REGS_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @file ++ * ++ * This file contains the data structures for accessing the DWC_usb3 core ++ * registers and DMA descriptor fields. ++ * ++ * The application interfaces with the HS OTG core by reading from and ++ * writing to the Control and Status Register (CSR) space through the ++ * AHB Slave interface. These registers are 32 bits wide, and the ++ * addresses are 32-bit-block aligned. ++ * CSRs are classified as follows: ++ * - Core Global Registers ++ * - Device Global Registers ++ * - Device Endpoint Specific Registers ++ */ ++ ++ ++/****************************************************************************/ ++/* Core Global Registers */ ++ ++/** ++ * This enum represents the bit fields of the Core SoC Bus Configuration 0 ++ * Register (GSBUSCFG0). ++ */ ++typedef enum gsbuscfg0_data { ++ /** Bus Burst Len <i>Access: R_W</i>. ++ * - 0: single ++ * - 1: incr ++ * - 3: incr4 ++ * - 7: incr8 ++ * - 15: incr16 ++ * - 31: incr32 (non-AHB mode only) ++ * - 63: incr64 (non-AHB mode only) ++ * - 127: incr128 (non-AHB mode only) ++ * - 255: incr256 (non-AHB mode only) ++ */ ++ DWC_SBUSCFG0_HBURSTLEN_BITS = 0x000000ff, ++ DWC_SBUSCFG0_HBURSTLEN_SHIFT = 0, ++ ++ DWC_SBUSCFG0_INT_DMA_BURST_SINGLE = 0, ++ DWC_SBUSCFG0_INT_DMA_BURST_INCR = 1, ++ DWC_SBUSCFG0_INT_DMA_BURST_INCR4 = 3, ++ DWC_SBUSCFG0_INT_DMA_BURST_INCR8 = 7, ++ DWC_SBUSCFG0_INT_DMA_BURST_INCR16 = 15, ++ DWC_SBUSCFG0_INT_DMA_BURST_INCR32 = 31, ++ DWC_SBUSCFG0_INT_DMA_BURST_INCR64 = 63, ++ DWC_SBUSCFG0_INT_DMA_BURST_INCR128 = 127, ++ DWC_SBUSCFG0_INT_DMA_BURST_INCR256 = 255, ++ ++ /** Descriptor Write is Posted <i>Access: R_W</i> */ ++ DWC_SBUSCFG0_DES_WR_POST_BIT = 0x00000100, ++ DWC_SBUSCFG0_DES_WR_POST_SHIFT = 8, ++ ++ /** Data Write is Posted <i>Access: R_W</i> */ ++ DWC_SBUSCFG0_DAT_WR_POST_BIT = 0x00000200, ++ DWC_SBUSCFG0_DAT_WR_POST_SHIFT = 9, ++ ++ /** Descriptor Access is Big-Endian <i>Access: R_W</i> */ ++ DWC_SBUSCFG0_DES_BIG_END_BIT = 0x00000400, ++ DWC_SBUSCFG0_DES_BIG_END_SHIFT = 10, ++ ++ /** Data Access is Big-Endian <i>Access: R_W</i> */ ++ DWC_SBUSCFG0_DAT_BIG_END_BIT = 0x00000800, ++ DWC_SBUSCFG0_DAT_BIG_END_SHIFT = 11, ++ ++ /** Store and Forward Mode <i>Access: R_W</i> */ ++ DWC_SBUSCFG0_STORE_AND_FORWARD_BIT = 0x00001000, ++ DWC_SBUSCFG0_STORE_AND_FORWARD_SHIFT = 12, ++ ++ /** Force Single Request <i>Access: R_W</i> */ ++ DWC_SBUSCFG0_SING_REQ_BIT = 0x00004000, ++ DWC_SBUSCFG0_SING_REQ_SHIFT = 14, ++ ++ /** Descriptor Readback Enable <i>Access: R_W</i> */ ++ DWC_SBUSCFG0_READ_AFTER_WRITE_BIT = 0x00008000, ++ DWC_SBUSCFG0_READ_AFTER_WRITE_SHIFT = 15, ++ ++ /** Descriptor Write Request Info <i>Access: R_W</i> */ ++ DWC_SBUSCFG0_DES_WR_REQ_INFO_BITS = 0x000f0000, ++ DWC_SBUSCFG0_DES_WR_REQ_INFO_SHIFT = 16, ++ ++ /** Data Write Request Info <i>Access: R_W</i> */ ++ DWC_SBUSCFG0_DAT_WR_REQ_INFO_BITS = 0x00f00000, ++ DWC_SBUSCFG0_DAT_WR_REQ_INFO_SHIFT = 20, ++ ++ /** Descriptor Read Request Info <i>Access: R_W</i> */ ++ DWC_SBUSCFG0_DES_RD_REQ_INFO_BITS = 0x0f000000, ++ DWC_SBUSCFG0_DES_RD_REQ_INFO_SHIFT = 24, ++ ++ /** Data Read Request Info <i>Access: R_W</i> */ ++ DWC_SBUSCFG0_DAT_RD_REQ_INFO_BITS = 0xf0000000, ++ DWC_SBUSCFG0_DAT_RD_REQ_INFO_SHIFT = 28, ++} gsbuscfg0_data_t; ++ ++/** ++ * This enum represents the bit fields of the Core SoC Bus Configuration 1 ++ * Register (GSBUSCFG1). ++ */ ++typedef enum gsbuscfg1_data { ++ /** OCP Address Space For Descriptor <i>Access: R_W</i> */ ++ DWC_SBUSCFG1_DES_ADDR_SPC_BITS = 0x0000000f, ++ DWC_SBUSCFG1_DES_ADDR_SPC_SHIFT = 0, ++ ++ /** OCP Address Space For Data <i>Access: R_W</i> */ ++ DWC_SBUSCFG1_DAT_ADDR_SPC_BITS = 0x000000f0, ++ DWC_SBUSCFG1_DAT_ADDR_SPC_SHIFT = 4, ++} gsbuscfg1_data_t; ++ ++/** ++ * This enum represents the bit fields of the Core Tx Threshold Control ++ * Register (GTXTHRCFG). ++ */ ++typedef enum gtxthrcfg_data { ++ /** Maximum Tx Burst Size <i>Access: R_W</i> */ ++ DWC_TXTHRCTL_USB_MAX_TX_BURST_SIZE_BITS = 0x00ff0000, ++ DWC_TXTHRCTL_USB_MAX_TX_BURST_SIZE_SHIFT = 16, ++ ++ /** Tx Multi-Packet Threshold Count <i>Access: R_W</i> */ ++ DWC_TXTHRCTL_USB_TX_PKT_CNT_BITS = 0x1f000000, ++ DWC_TXTHRCTL_USB_TX_PKT_CNT_SHIFT = 24, ++ ++ /** Tx Multi-Packet Threshold Enable <i>Access: R_W</i> */ ++ DWC_TXTHRCTL_USB_TX_PKT_CNT_EN_BIT = 0x20000000, ++ DWC_TXTHRCTL_USB_TX_PKT_CNT_EN_SHIFT = 29, ++} gtxthrcfg_data_t; ++ ++/** ++ * This enum represents the bit fields of the Core Rx Threshold Control ++ * Register (GRXTHRCFG). ++ */ ++typedef enum grxthrcfg_data { ++ /** Maximum Rx Burst Size <i>Access: R_W</i> */ ++ DWC_RXTHRCTL_USB_MAX_RX_BURST_SIZE_BITS = 0x00f80000, ++ DWC_RXTHRCTL_USB_MAX_RX_BURST_SIZE_SHIFT = 19, ++ ++ /** Rx Multi-Packet Threshold Count <i>Access: R_W</i> */ ++ DWC_RXTHRCTL_USB_RX_PKT_CNT_BITS = 0x0f000000, ++ DWC_RXTHRCTL_USB_RX_PKT_CNT_SHIFT = 24, ++ ++ /** Rx Multi-Packet Threshold Enable <i>Access: R_W</i> */ ++ DWC_RXTHRCTL_USB_RX_PKT_CNT_EN_BIT = 0x20000000, ++ DWC_RXTHRCTL_USB_RX_PKT_CNT_EN_SHIFT = 29, ++} grxthrcfg_data_t; ++ ++/** ++ * This enum represents the bit fields of the Core Control ++ * Register (GCTL). ++ */ ++typedef enum gctl_data { ++ /** Disable Clock Gating <i>Access: R_W</i> */ ++ DWC_GCTL_DSBL_CLCK_GTNG_BIT = 0x00000001, ++ DWC_GCTL_DSBL_CLCK_GTNG_SHIFT = 0, ++ ++ /** Global Hibernation Enable <i>Access: R_W</i> */ ++ DWC_GCTL_GBL_HIBER_EN_BIT = 0x00000002, ++ DWC_GCTL_GBL_HIBER_EN_SHIFT = 1, ++ ++ /** Disable Scrambling <i>Access: R_W</i> */ ++ DWC_GCTL_DIS_SCRAMBLE_BIT = 0x00000008, ++ DWC_GCTL_DIS_SCRAMBLE_SHIFT = 3, ++ ++ /** Scale-down Mode <i>Access: R_W</i> */ ++ DWC_GCTL_SCALE_DOWN_BITS = 0x00000030, ++ DWC_GCTL_SCALE_DOWN_SHIFT = 4, ++ ++ /** RAM Clock Select <i>Access: R_W</i> */ ++ DWC_GCTL_RAM_CLK_SEL_BITS = 0x000000c0, ++ DWC_GCTL_RAM_CLK_SEL_SHIFT = 6, ++ ++ /** Debug Attach <i>Access: R_W</i> */ ++ DWC_GCTL_DEBUG_ATTACH_BIT = 0x00000100, ++ DWC_GCTL_DEBUG_ATTACH_SHIFT = 8, ++ ++ /** Disable U1/U2 Timer Scaledown <i>Access: R_W</i> */ ++ DWC_GCTL_U1U2_TIMER_SCALE_BIT = 0x00000200, ++ DWC_GCTL_U1U2_TIMER_SCALE_SHIFT = 9, ++ ++ /** SOF ITP SYNC <i>Access: R_W</i> */ ++ DWC_GCTL_SOFITPSYNC_BIT = 0x00000400, ++ DWC_GCTL_SOFITPSYNC_SHIFT = 10, ++ ++ /** Core Soft Reset <i>Access: R_W</i> */ ++ DWC_GCTL_CORE_SOFT_RST_BIT = 0x00000800, ++ DWC_GCTL_CORE_SOFT_RST_SHIFT = 11, ++ ++ /** Port Capability Direction <i>Access: R_W</i> */ ++ DWC_GCTL_PRT_CAP_DIR_BITS = 0x00003000, ++ DWC_GCTL_PRT_CAP_DIR_SHIFT = 12, ++ ++ /** Port Capability Values */ ++ DWC_GCTL_PRT_CAP_HOST = 1, ++ DWC_GCTL_PRT_CAP_DEVICE = 2, ++ DWC_GCTL_PRT_CAP_OTG = 3, ++ ++ /** Frame Scale Down <i>Access: R_W</i> */ ++ DWC_GCTL_FRMSCLDWN_BITS = 0x0000c000, ++ DWC_GCTL_FRMSCLDWN_SHIFT = 14, ++ ++ /** U2 Reset ECN <i>Access: R_W</i> */ ++ DWC_GCTL_U2RSTECN_BIT = 0x00010000, ++ DWC_GCTL_U2RSTECN_SHIFT = 16, ++ ++ /** Bypass SetAddress <i>Access: R_W</i> */ ++ DWC_GCTL_BYPSSETADDR_BIT = 0x00020000, ++ DWC_GCTL_BYPSSETADDR_SHIFT = 17, ++ ++ /** Master Filter Bypass <i>Access: R_W</i> */ ++ DWC_GCTL_MASTERFILTBYPASS_BIT = 0x00040000, ++ DWC_GCTL_MASTERFILTBYPASS_SHIFT = 18, ++ ++ /** Power Down Scale <i>Access: R_W</i> */ ++ DWC_GCTL_PWR_DN_SCALE_BITS = 0xfff80000, ++ DWC_GCTL_PWR_DN_SCALE_SHIFT = 19, ++} gctl_data_t; ++ ++/** ++ * This enum represents the bit fields of the Core Interrupt Mask ++ * Register (GEVTEN). ++ */ ++typedef enum gevten_data { ++ /** ULPI Carkit Event Enable <i>Access: R_W</i> */ ++ DWC_GEVTEN_ULPI_CK_EVT_EN_BIT = 0x00000001, ++ DWC_GEVTEN_ULPI_CK_EVT_SHIFT = 0, ++ ++ /** I2C Event Enable <i>Access: R_W</i> */ ++ DWC_GEVTEN_I2C_EVT_EN_BIT = 0x00000002, ++ DWC_GEVTEN_I2C_EVT_EN_SHIFT = 1, ++} gevten_data_t; ++ ++/** ++ * This enum represents the bit fields of the Core Status ++ * Register (GSTS). ++ */ ++typedef enum gsts_data { ++ /** Current Mode <i>Access: RO</i>. ++ * - 0: Device Mode ++ * - 1: Host Mode ++ * - 2: DRD Mode ++ */ ++ DWC_GSTS_CURMODE_BITS = 0x00000003, ++ DWC_GSTS_CURMODE_SHIFT = 0, ++ ++ DWC_GSTS_DEVICE_MODE = 0, ++ DWC_GSTS_HOST_MODE = 1, ++ DWC_GSTS_DRD_MODE = 2, ++ ++ /** Bus Error Address Valid <i>Access: RO</i> */ ++ DWC_GSTS_BUS_ERR_ADDR_VLD_BIT = 0x00000010, ++ DWC_GSTS_BUS_ERR_ADDR_VLD_SHIFT = 4, ++ ++ /** CSR Timeout */ ++ DWC_GSTS_CSR_TIMEOUT_BIT = 0x00000020, ++ DWC_GSTS_CSR_TIMEOUT_SHIFT = 5, ++ ++ /** Device Interrupt Pending */ ++ DWC_GSTS_DEV_EVT_PENDING_BIT = 0x00000040, ++ DWC_GSTS_DEV_EVT_PENDING_SHIFT = 6, ++ ++ /** Host Interrupt Pending */ ++ DWC_GSTS_HOST_EVT_PENDING_BIT = 0x00000080, ++ DWC_GSTS_HOST_EVT_PENDING_SHIFT = 7, ++ ++ /** ADP Interrupt Pending */ ++ DWC_GSTS_ADP_EVT_PENDING_BIT = 0x00000100, ++ DWC_GSTS_ADP_EVT_PENDING_SHIFT = 8, ++ ++ /** BC Interrupt Pending */ ++ DWC_GSTS_BC_EVT_PENDING_BIT = 0x00000200, ++ DWC_GSTS_BC_EVT_PENDING_SHIFT = 9, ++ ++ /** OTG Interrupt Pending */ ++ DWC_GSTS_OTG_EVT_PENDING_BIT = 0x00000400, ++ DWC_GSTS_OTG_EVT_PENDING_SHIFT = 10, ++ ++ /** SSIC Interrupt Pending */ ++ DWC_GSTS_SSIC_IP_BIT = 0x00000800, ++ DWC_GSTS_SSIC_IP_SHIFT = 11, ++ ++ /** Current BELT Value <i>Access: RO</i> */ ++ DWC_GSTS_CBELT_BITS = 0xfff00000, ++ DWC_GSTS_CBELT_SHIFT = 20, ++} gsts_data_t; ++ ++/** ++ * This enum represents the bit fields of the Hardware Parameters 0 ++ * Register (GHWPARAMS0). ++ */ ++typedef enum ghwparams0_data { ++ DWC_HWP0_MODE_BITS = 0x00000007, ++ DWC_HWP0_MODE_SHIFT = 0, ++ ++ DWC_HWP0_MBUS_TYPE_BITS = 0x00000038, ++ DWC_HWP0_MBUS_TYPE_SHIFT = 3, ++ ++ DWC_HWP0_SBUS_TYPE_BITS = 0x000000c0, ++ DWC_HWP0_SBUS_TYPE_SHIFT = 6, ++ ++ DWC_HWP0_MDWIDTH_BITS = 0x0000ff00, ++ DWC_HWP0_MDWIDTH_SHIFT = 8, ++ ++ DWC_HWP0_SDWIDTH_BITS = 0x00ff0000, ++ DWC_HWP0_SDWIDTH_SHIFT = 16, ++ ++ DWC_HWP0_AWIDTH_BITS = 0x3f000000, ++ DWC_HWP0_AWIDTH_SHIFT = 24, ++} ghwparams0_data_t; ++ ++/** ++ * This enum represents the bit fields of the Hardware Parameters 1 ++ * Register (GHWPARAMS1). ++ */ ++typedef enum ghwparams1_data { ++ DWC_HWP1_IDWIDTH_M1_BITS = 0x00000007, ++ DWC_HWP1_IDWIDTH_M1_SHIFT = 0, ++ ++ DWC_HWP1_BURSTWIDTH_M1_BITS = 0x00000038, ++ DWC_HWP1_BURSTWIDTH_M1_SHIFT = 3, ++ ++ DWC_HWP1_DATAINFOWIDTH_BITS = 0x000001c0, ++ DWC_HWP1_DATAINFOWIDTH_SHIFT = 6, ++ ++ DWC_HWP1_REQINFOWIDTH_BITS = 0x00000e00, ++ DWC_HWP1_REQINFOWIDTH_SHIFT = 9, ++ ++ DWC_HWP1_ASPACEWIDTH_BITS = 0x00007000, ++ DWC_HWP1_ASPACEWIDTH_SHIFT = 12, ++ ++ DWC_HWP1_DEV_NUM_INT_BITS = 0x001f8000, ++ DWC_HWP1_DEV_NUM_INT_SHIFT = 15, ++ ++ DWC_HWP1_NUM_RAMS_BITS = 0x00600000, ++ DWC_HWP1_NUM_RAMS_SHIFT = 21, ++ ++ DWC_HWP1_SPRAM_TYP_BIT = 0x00800000, ++ DWC_HWP1_SPRAM_TYP_SHIFT = 23, ++ ++ DWC_HWP1_EN_PWROPT_BITS = 0x03000000, ++ DWC_HWP1_EN_PWROPT_SHIFT = 24, ++ ++ DWC_EN_PWROPT_NONE = 0, ++ DWC_EN_PWROPT_CLK_GATING_ONLY = 1, ++ DWC_EN_PWROPT_HIBERNATION = 2, ++ ++ DWC_HWP1_MAC_PHY_CLKS_SYNC_BIT = 0x04000000, ++ DWC_HWP1_MAC_PHY_CLKS_SYNC_SHIFT = 26, ++ ++ DWC_HWP1_MAC_RAM_CLKS_SYNC_BIT = 0x08000000, ++ DWC_HWP1_MAC_RAM_CLKS_SYNC_SHIFT = 27, ++ ++ DWC_HWP1_RAM_BUS_CLKS_SYNC_BIT = 0x10000000, ++ DWC_HWP1_RAM_BUS_CLKS_SYNC_SHIFT = 28, ++ ++ DWC_HWP1_RM_OPT_FEATURES_BIT = 0x40000000, ++ DWC_HWP1_RM_OPT_FEATURES_SHIFT = 30, ++} ghwparams1_data_t; ++ ++/** ++ * This enum represents the bit fields of the Hardware Parameters 2 ++ * Register (GHWPARAMS2). ++ */ ++typedef enum ghwparams2_data { ++ DWC_HWP2_USERID_BITS = 0xffffffff, ++ DWC_HWP2_USERID_SHIFT = 0, ++} ghwparams2_data_t; ++ ++/** ++ * This enum represents the bit fields of the Hardware Parameters 3 ++ * Register (GHWPARAMS3). ++ */ ++typedef enum ghwparams3_data { ++ DWC_HWP3_SSPHY_IFC_BITS = 0x00000003, ++ DWC_HWP3_SSPHY_IFC_SHIFT = 0, ++ ++ DWC_HWP3_HSPHY_IFC_BITS = 0x0000000c, ++ DWC_HWP3_HSPHY_IFC_SHIFT = 2, ++ ++ DWC_HWP3_FSPHY_IFC_BITS = 0x00000030, ++ DWC_HWP3_FSPHY_IFC_SHIFT = 4, ++ ++ DWC_HWP3_HSPHY_DWIDTH_BITS = 0x000000c0, ++ DWC_HWP3_HSPHY_DWIDTH_SHIFT = 6, ++ ++ DWC_HWP3_VEND_CTL_IFC_BIT = 0x00000400, ++ DWC_HWP3_VEND_CTL_IFC_SHIFT = 10, ++ ++ DWC_HWP3_ULPI_CARKIT_BIT = 0x00000800, ++ DWC_HWP3_ULPI_CARKIT_SHIFT = 11, ++ ++ DWC_HWP3_NUM_EPS_BITS = 0x0003f000, ++ DWC_HWP3_NUM_EPS_SHIFT = 12, ++ ++ DWC_HWP3_NUM_IN_EPS_BITS = 0x007c0000, ++ DWC_HWP3_NUM_IN_EPS_SHIFT = 18, ++ ++ DWC_HWP3_TOT_XFR_RSRC_BITS = 0x7f800000, ++ DWC_HWP3_TOT_XFR_RSRC_SHIFT = 23, ++} ghwparams3_data_t; ++ ++/** ++ * This enum represents the bit fields of the Hardware Parameters 4 ++ * Register (GHWPARAMS4). ++ */ ++typedef enum ghwparams4_data { ++ DWC_HWP4_TRBS_PER_XFER_BITS = 0x0000003f, ++ DWC_HWP4_TRBS_PER_XFER_SHIFT = 0, ++ ++ DWC_HWP4_HIBER_SPAD_BITS = 0x0001e000, ++ DWC_HWP4_HIBER_SPAD_SHIFT = 13, ++ ++ DWC_HWP4_NUM_SS_USB_INST_BITS = 0x001e0000, ++ DWC_HWP4_NUM_SS_USB_INST_SHIFT = 17, ++ ++ DWC_HWP4_EN_ISOC_SUPT_BIT = 0x00800000, ++ DWC_HWP4_EN_ISOC_SUPT_SHIFT = 23, ++ ++ DWC_HWP4_BMU_PTL_DEPTH_BITS = 0x0f000000, ++ DWC_HWP4_BMU_PTL_DEPTH_SHIFT = 24, ++ ++ DWC_HWP4_BMU_LSP_DEPTH_BITS = 0xf0000000, ++ DWC_HWP4_BMU_LSP_DEPTH_SHIFT = 28, ++} ghwparams4_data_t; ++ ++/** ++ * This enum represents the bit fields of the Hardware Parameters 5 ++ * Register (GHWPARAMS5). ++ */ ++typedef enum ghwparams5_data { ++ DWC_HWP5_BMU_BUSGM_DEPTH_BITS = 0x0000000f, ++ DWC_HWP5_BMU_BUSGM_DEPTH_SHIFT = 0, ++ ++ DWC_HWP5_RXQ_FIFO_DEPTH_BITS = 0x000003f0, ++ DWC_HWP5_RXQ_FIFO_DEPTH_SHIFT = 4, ++ ++ DWC_HWP5_TXQ_FIFO_DEPTH_BITS = 0x0000fc00, ++ DWC_HWP5_TXQ_FIFO_DEPTH_SHIFT = 10, ++ ++ DWC_HWP5_DWQ_FIFO_DEPTH_BITS = 0x003f0000, ++ DWC_HWP5_DWQ_FIFO_DEPTH_SHIFT = 16, ++ ++ DWC_HWP5_DFQ_FIFO_DEPTH_BITS = 0x0fc00000, ++ DWC_HWP5_DFQ_FIFO_DEPTH_SHIFT = 22, ++} ghwparams5_data_t; ++ ++/** ++ * This enum represents the bit fields of the Hardware Parameters 6 ++ * Register (GHWPARAMS6). ++ */ ++typedef enum ghwparams6_data { ++ DWC_HWP6_PSQ_FIFO_DEPTH_BITS = 0x0000003f, ++ DWC_HWP6_PSQ_FIFO_DEPTH_SHIFT = 0, ++ ++ DWC_HWP6_EN_DBG_PORTS_BIT = 0x00000040, ++ DWC_HWP6_EN_DBG_PORTS_SHIFT = 6, ++ ++ DWC_HWP6_EN_FPGA_BIT = 0x00000080, ++ DWC_HWP6_EN_FPGA_SHIFT = 7, ++ ++ DWC_HWP6_EN_SRP_BIT = 0x00000400, ++ DWC_HWP6_EN_SRP_SHIFT = 10, ++ ++ DWC_HWP6_EN_HNP_BIT = 0x00000800, ++ DWC_HWP6_EN_HNP_SHIFT = 11, ++ ++ DWC_HWP6_EN_ADP_BIT = 0x00001000, ++ DWC_HWP6_EN_ADP_SHIFT = 12, ++ ++ DWC_HWP6_EN_OTG_BIT = 0x00002000, ++ DWC_HWP6_EN_OTG_SHIFT = 13, ++ ++ DWC_HWP6_EN_BC_BIT = 0x00004000, ++ DWC_HWP6_EN_BC_SHIFT = 14, ++ ++ DWC_HWP6_EN_BUS_FILTERS_BIT = 0x00008000, ++ DWC_HWP6_EN_BUS_FILTERS_SHIFT = 15, ++ ++ DWC_HWP6_RAM0_DEPTH_BITS = 0xffff0000, ++ DWC_HWP6_RAM0_DEPTH_SHIFT = 16, ++} ghwparams6_data_t; ++ ++/** ++ * This enum represents the bit fields of the Hardware Parameters 7 ++ * Register (GHWPARAMS7). ++ */ ++typedef enum ghwparams7_data { ++ DWC_HWP7_RAM1_DEPTH_BITS = 0x0000ffff, ++ DWC_HWP7_RAM1_DEPTH_SHIFT = 0, ++ ++ DWC_HWP7_RAM2_DEPTH_BITS = 0xffff0000, ++ DWC_HWP7_RAM2_DEPTH_SHIFT = 16, ++} ghwparams7_data_t; ++ ++/** ++ * This enum represents the bit fields of the Hardware Parameters 8 ++ * Register (GHWPARAMS8). ++ */ ++typedef enum ghwparams8_data { ++ DWC_HWP8_DCACHE_DEPTH_BITS = 0xffffffff, ++ DWC_HWP8_DCACHE_DEPTH_SHIFT = 0, ++} ghwparams8_data_t; ++ ++/** ++ * This enum represents the bit fields of the Debug Queue/FIFO Space ++ * Register (GDBGFIFOSPACE). ++ */ ++typedef enum gdbgfifospace_data { ++ /** FIFO/Queue Select <i>Access: R_W</i> */ ++ DWC_DBGFIFOSPACE_FIFO_QUEUE_SEL_BITS = 0x000000ff, ++ DWC_DBGFIFOSPACE_FIFO_QUEUE_SEL_SHIFT = 0, ++ ++ /* 0 - 31 TxFIFO Number */ ++ /* 32 - 63 RxFIFO Number */ ++ /* 64 - 95 TxReqQ Number */ ++ /* 96 - 127 RxReqQ Number */ ++ /* 128 - 159 RxInfoQ Number */ ++ /* 160 DescFetchQ */ ++ /* 161 EventQ */ ++ /* 162 ProtocolStatusQ */ ++ ++ /** Space Available <i>Access: R</i> */ ++ DWC_DBGFIFOSPACE_SPACE_AVAIL_BITS = 0xffff0000, ++ DWC_DBGFIFOSPACE_SPACE_AVAIL_SHIFT = 16, ++} gdbgfifospace_data_t; ++ ++/** ++ * This enum represents the bit fields of the Debug LTSSM ++ * Register (GDBGLTSSM). ++ */ ++typedef enum gdbgltssm_data { ++ /** Pipe Status <i>Access: R</i> */ ++ DWC_DBGLTSSM_PIPE_STATUS_BITS = 0x0003ffff, ++ DWC_DBGLTSSM_PIPE_STATUS_SHIFT = 0, ++ ++ /** LTDB SubState <i>Access: R</i> */ ++ DWC_DBGLTSSM_LTDB_SUB_STATE_BITS = 0x003c0000, ++ DWC_DBGLTSSM_LTDB_SUB_STATE_SHIFT = 18, ++ ++ /** LTDB State <i>Access: R</i> */ ++ DWC_DBGLTSSM_LTDB_STATE_BITS = 0x03c00000, ++ DWC_DBGLTSSM_LTDB_STATE_SHIFT = 22, ++ ++ /** LTDB Timeout <i>Access: R</i> */ ++ DWC_DBGLTSSM_LTDB_TIMEOUT_BIT = 0x04000000, ++ DWC_DBGLTSSM_LTDB_TIMEOUT_SHIFT = 26, ++} gdbgltssm_data_t; ++ ++/** ++ * This enum represents the bit fields of the Core RMMI PHY Control ++ * Registers (GUSB3RMMICTLn). ++ */ ++typedef enum gusb3rmmictl_data { ++ DWC_RMMICTL_MPHY_STATE_BITS = 0x0e000000, ++ DWC_RMMICTL_MPHY_STATE_SHIFT = 25, ++ ++#define DWC_MPHY_STATE_DISABLED 0 ++#define DWC_MPHY_STATE_HIBERN8 1 ++#define DWC_MPHY_STATE_SLEEP 2 ++#define DWC_MPHY_STATE_STALL 3 ++#define DWC_MPHY_STATE_PWM_BURST 4 ++#define DWC_MPHY_STATE_HS_BURST 5 ++#define DWC_MPHY_STATE_LINE_CFG 6 ++#define DWC_MPHY_STATE_LINE_RESET 7 ++ ++ DWC_RMMICTL_AUTO_EXIT_RRAP_BIT = 0x10000000, ++ DWC_RMMICTL_AUTO_EXIT_RRAP_SHIFT = 28, ++ ++ DWC_RMMICTL_AUTO_ROM_RRAP_BIT = 0x20000000, ++ DWC_RMMICTL_AUTO_ROM_RRAP_SHIFT = 29, ++ ++ DWC_RMMICTL_AUTO_EXIT_H8_BIT = 0x40000000, ++ DWC_RMMICTL_AUTO_EXIT_H8_SHIFT = 30, ++ ++ DWC_RMMICTL_AUTO_ROM_H8_BIT = 0x80000000, ++ DWC_RMMICTL_AUTO_ROM_H8_SHIFT = 31, ++} gusb3rmmictl_data_t; ++ ++/** ++ * This enum represents the bit fields of the Core USB2 PHY Configuration ++ * Registers (GUSB2PHYCFGn). ++ */ ++typedef enum gusb2phycfg_data { ++ /** HS/FS Timeout Calibration <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_TOUT_CAL_BITS = 0x00000007, ++ DWC_USB2PHYCFG_TOUT_CAL_SHIFT = 0, ++ ++ /** UTMI+ PHY Intf Width (8-bit/16-bit) SelecT <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_16B_PHY_IF_BIT = 0x00000008, ++ DWC_USB2PHYCFG_16B_PHY_IF_SHIFT = 3, ++ /*--------*/ ++ /** ULPI DDR Select <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_DDR_SEL_BIT = 0x00000008, ++ DWC_USB2PHYCFG_DDR_SEL_SHIFT = 3, ++ ++ /** UTMI+ / ULPI Select <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_UTMI_ULPI_BIT = 0x00000010, ++ DWC_USB2PHYCFG_UTMI_ULPI_SHIFT = 4, ++ ++ /** Full-speed Serial Interface Select <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_FSINTF_BIT = 0x00000020, ++ DWC_USB2PHYCFG_FSINTF_SHIFT = 5, ++ ++ /** Suspend USB2 Phy <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_SUS_PHY_BIT = 0x00000040, ++ DWC_USB2PHYCFG_SUS_PHY_SHIFT = 6, ++ ++ /** USB2.0 HS PHY/USB1.1 FS Serial Xcvr Select <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_PHY_SEL_BIT = 0x00000080, ++ DWC_USB2PHYCFG_PHY_SEL_SHIFT = 7, ++ ++ /** Enable UTMI Sleep <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_ENBL_SLP_M_BIT = 0x00000100, ++ DWC_USB2PHYCFG_ENBL_SLP_M_SHIFT = 8, ++ ++ /** USB2.0 Turnaround Time <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_USB_TRD_TIM_BITS = 0x00003c00, ++ DWC_USB2PHYCFG_USB_TRD_TIM_SHIFT = 10, ++ ++ /** PHY Low-power Clock Select <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_PHY_LPWR_CLK_SEL_BIT = 0x00004000, ++ DWC_USB2PHYCFG_PHY_LPWR_CLK_SEL_SHIFT = 14, ++ ++ /** ULPI Auto Resume <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_ULPI_AUTO_RES_BIT = 0x00008000, ++ DWC_USB2PHYCFG_ULPI_AUTO_RES_SHIFT = 15, ++ ++ /** ULPI Clock SuspendM <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_ULPI_CLK_SUS_M_BIT = 0x00010000, ++ DWC_USB2PHYCFG_ULPI_CLK_SUS_M_SHIFT = 16, ++ ++ /** ULPI External Vbus Drive <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_ULPI_EXT_VBUS_DRV_BIT = 0x00020000, ++ DWC_USB2PHYCFG_ULPI_EXT_VBUS_DRV_SHIFT = 17, ++ ++ /** ULPI External Vbus Indicator <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_ULPI_EXT_VBUS_IND_BIT = 0x00040000, ++ DWC_USB2PHYCFG_ULPI_EXT_VBUS_IND_SHIFT = 18, ++ ++ /** PHY Interrupt Number <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_PHY_INTR_NUM_BITS = 0x01f80000, ++ DWC_USB2PHYCFG_PHY_INTR_NUM_SHIFT = 19, ++ ++ /** OTG Interrupt Number <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_OTG_INTR_NUM_BITS = 0x7e000000, ++ DWC_USB2PHYCFG_OTG_INTR_NUM_SHIFT = 25, ++ ++ /** PHY Soft Reset <i>Access: R_W</i> */ ++ DWC_USB2PHYCFG_PHY_SOFT_RST_BIT = 0x80000000, ++ DWC_USB2PHYCFG_PHY_SOFT_RST_SHIFT = 31, ++} gusb2phycfg_data_t; ++ ++/** ++ * This enum represents the bit fields in the USB2 I2C Control ++ * Registers (GUSB2I2CCTLn). ++ */ ++typedef enum gusb2i2cctl_data { ++ /** All bits are reserved */ ++ DWC_USB2I2C_RSVD_BITS = 0xffffffff, ++ DWC_USB2I2C_RSVD_SHIFT = 0, ++} gusb2i2cctl_data_t; ++ ++/** ++ * This enum represents the bit fields in the USB2 Phy Vendor Control ++ * Registers (GUSB2PHYACCn). ++ */ ++typedef enum gusb2phyacc_data { ++ /** Register Data <i>Access: R_W</i> */ ++ DWC_USB2PHY_REGDATA_BITS = 0x000000ff, ++ DWC_USB2PHY_REGDATA_SHIFT = 0, ++ ++ /** UTMI+ Vendor Ctrl Register Address <i>Access: R_W</i> */ ++ DWC_USB2PHY_VCTRL_BITS = 0x0000ff00, ++ DWC_USB2PHY_VCTRL_SHIFT = 8, ++ /*--------*/ ++ /** ULPI Extended Register Address <i>Access: R_W</i> */ ++ DWC_USB2PHY_EXTREGADDR_BITS = 0x00003f00, ++ DWC_USB2PHY_EXTREGADDR_SHIFT = 8, ++ ++ /** Register Address <i>Access: R_W</i> */ ++ DWC_USB2PHY_REGADDR_BITS = 0x003f0000, ++ DWC_USB2PHY_REGADDR_SHIFT = 16, ++ ++ /** Register Write <i>Access: R_W</i> */ ++ DWC_USB2PHY_REGWR_BIT = 0x00400000, ++ DWC_USB2PHY_REGWR_SHIFT = 22, ++ ++ /** VStatus Busy <i>Access: RO</i> */ ++ DWC_USB2PHY_VSTSBSY_BIT = 0x00800000, ++ DWC_USB2PHY_VSTSBSY_SHIFT = 23, ++ ++ /** VStatus Done <i>Access: R_SS_SC</i> */ ++ DWC_USB2PHY_VSTSDONE_BIT = 0x01000000, ++ DWC_USB2PHY_VSTSDONE_SHIFT = 24, ++ ++ /** New Register Request <i>Access: R_WS_SC</i> */ ++ DWC_USB2PHY_NEWREGREQ_BIT = 0x02000000, ++ DWC_USB2PHY_NEWREGREQ_SHIFT = 25, ++ ++ /** Disable ULPI Drivers <i>Access: R_WS_SC</i> */ ++ DWC_USB2PHY_DIS_ULPI_DRVR_BIT = 0x04000000, ++ DWC_USB2PHY_DIS_ULPI_DRVR_SHIFT = 26, ++} gusb2phyacc_data_t; ++ ++/** ++ * This enum represents the bit fields of the USB3 Pipe Control ++ * Registers (GUSB3PIPECTLn). ++ */ ++typedef enum gusb3pipectl_data { ++ /** Elastic Buffer Mode <i>Access: R_W</i> */ ++ DWC_PIPECTL_ELAS_BUF_MODE_BIT = 0x00000001, ++ DWC_PIPECTL_ELAS_BUF_MODE_SHIFT = 0, ++ ++ /** Tx De-Emphasis <i>Access: R_W</i> */ ++ DWC_PIPECTL_TX_DEMPH_BITS = 0x00000006, ++ DWC_PIPECTL_TX_DEMPH_SHIFT = 1, ++ ++ /** Tx Margin <i>Access: R_W</i> */ ++ DWC_PIPECTL_TX_MARGIN_BITS = 0x00000038, ++ DWC_PIPECTL_TX_MARGIN_SHIFT = 3, ++ ++ /** Tx Swing <i>Access: R_W</i> */ ++ DWC_PIPECTL_TX_SWING_BIT = 0x00000040, ++ DWC_PIPECTL_TX_SWING_SHIFT = 6, ++ ++ /** USB3 SSIC Enable <i>Access: R_W</i> */ ++ DWC_PIPECTL_SSIC_EN_BIT = 0x00000080, ++ DWC_PIPECTL_SSIC_EN_SHIFT = 7, ++ ++ /** LFPS Filter <i>Access: R_W</i> */ ++ DWC_PIPECTL_LFPS_FILTER_BIT = 0x00000200, ++ DWC_PIPECTL_LFPS_FILTER_SHIFT = 9, ++ ++ /** P3 Exit Signal In P2 <i>Access: R_W</i> */ ++ DWC_PIPECTL_P3_EX_SIG_P2_BIT = 0x00000400, ++ DWC_PIPECTL_P3_EX_SIG_P2_SHIFT = 10, ++ ++ /** P3-P2 Transitions OK <i>Access: R_W</i> */ ++ DWC_PIPECTL_P3_P2_TRAN_OK_BIT = 0x00000800, ++ DWC_PIPECTL_P3_P2_TRAN_OK_SHIFT = 11, ++ ++ /** LFPS P0 Align <i>Access: R_W</i> */ ++ DWC_PIPECTL_LFPS_P0_ALGN_BIT = 0x00001000, ++ DWC_PIPECTL_LFPS_P0_ALGN_SHIFT = 12, ++ ++ /** Pipe Data Width <i>Access: R_W</i> */ ++ DWC_PIPECTL_DATA_WIDTH_BITS = 0x00018000, ++ DWC_PIPECTL_DATA_WIDTH_SHIFT = 15, ++ ++ /** Suspend USB3 Phy <i>Access: R_W</i> */ ++ DWC_PIPECTL_SUS_PHY_BIT = 0x00020000, ++ DWC_PIPECTL_SUS_PHY_SHIFT = 17, ++ ++ /** PHY Soft Reset <i>Access: R_W</i> */ ++ DWC_PIPECTL_PHY_SOFT_RST_BIT = 0x80000000, ++ DWC_PIPECTL_PHY_SOFT_RST_SHIFT = 31, ++} gusb3pipectl_data_t; ++ ++/** ++ * This enum represents the bit fields in the FIFO Size Registers. ++ */ ++typedef enum gfifosize_data { ++ /** Depth <i>Access: R_W</i> */ ++ DWC_FIFOSZ_DEPTH_BITS = 0x0000ffff, ++ DWC_FIFOSZ_DEPTH_SHIFT = 0, ++ ++ /** Starting Address <i>Access: RO or R_W</i> */ ++ DWC_FIFOSZ_STARTADDR_BITS = 0xffff0000, ++ DWC_FIFOSZ_STARTADDR_SHIFT = 16, ++} gfifosize_data_t; ++ ++/** ++ * This enum represents the bit fields of the Event Buffer Size ++ * Registers (GEVENTSIZn). ++ */ ++typedef enum geventsiz_data { ++ /** Event Buffer Size <i>Access: R_W</i> */ ++ DWC_EVENTSIZ_SIZ_BITS = 0x0000ffff, ++ DWC_EVENTSIZ_SIZ_SHIFT = 0, ++ ++ /** Event Interrupt Mask (1 == disable) <i>Access: R_W</i> */ ++ DWC_EVENTSIZ_INT_MSK_BIT = 0x80000000, ++ DWC_EVENTSIZ_INT_MSK_SHIFT = 31, ++} geventsiz_data_t; ++ ++/** ++ * This enum represents the bit fields of the Event Buffer Count ++ * Registers (GEVENTCNTn). ++ */ ++typedef enum geventcnt_data { ++ /** Event Count <i>Access: R_W</i> */ ++ DWC_EVENTCNT_CNT_BITS = 0x0000ffff, ++ DWC_EVENTCNT_CNT_SHIFT = 0, ++} geventcnt_data_t; ++ ++/** ++ * This enum represents the bit fields of a generic Event Buffer entry. ++ */ ++typedef enum gevent_data { ++ /** Non-Endpoint Specific Event flag */ ++ DWC_EVENT_NON_EP_BIT = 0x01, ++ DWC_EVENT_NON_EP_SHIFT = 0, ++ ++ /** Non-Endpoint Specific Event Type */ ++ DWC_EVENT_INTTYPE_BITS = 0xfe, ++ DWC_EVENT_INTTYPE_SHIFT = 1, ++ ++ /** Non-Endpoint Specific Event Type values */ ++ DWC_EVENT_DEV_INT = 0, /** @< */ ++ DWC_EVENT_OTG_INT = 1, /** @< */ ++ DWC_EVENT_CARKIT_INT = 3, /** @< */ ++ DWC_EVENT_I2C_INT = 4, ++} gevent_data_t; ++ ++/** ++ * This enum represents the non-generic bit fields of an Event Buffer entry ++ * for Device Specific events (DEVT). ++ */ ++typedef enum devt_data { ++ /** Device Specific Event Type */ ++ DWC_DEVT_BITS = 0x00000f00, ++ DWC_DEVT_SHIFT = 8, ++ ++ /** Device Specific Event Type values */ ++ DWC_DEVT_DISCONN = 0, /** @< */ ++ DWC_DEVT_USBRESET = 1, /** @< */ ++ DWC_DEVT_CONNDONE = 2, /** @< */ ++ DWC_DEVT_ULST_CHNG = 3, /** @< */ ++ DWC_DEVT_WKUP = 4, /** @< */ ++ DWC_DEVT_HIBER_REQ = 5, /** @< */ ++ DWC_DEVT_U3_L2L1_SUSP = 6, /** @< */ ++ DWC_DEVT_SOF = 7, /** @< */ ++ DWC_DEVT_ERRATICERR = 9, /** @< */ ++ DWC_DEVT_CMD_CMPL = 10, /** @< */ ++ DWC_DEVT_OVERFLOW = 11, /** @< */ ++ DWC_DEVT_VNDR_DEV_TST_RCVD = 12, /** @< */ ++ DWC_DEVT_INACT_TIMEOUT_RCVD = 13, ++ ++ /** Event Information */ ++ DWC_DEVT_EVT_INFO_BITS = 0xffff0000, ++ DWC_DEVT_EVT_INFO_SHIFT = 16, ++ ++ /** USB/Link State */ ++ DWC_DEVT_ULST_STATE_BITS = 0x000f0000, ++ DWC_DEVT_ULST_STATE_SHIFT = 16, ++ ++ /** USB/Link State values in SS */ ++ DWC_LINK_STATE_U0 = 0, /** @< */ ++ DWC_LINK_STATE_U1 = 1, /** @< */ ++ DWC_LINK_STATE_U2 = 2, /** @< */ ++ DWC_LINK_STATE_U3 = 3, /** @< */ ++ DWC_LINK_STATE_SS_DIS = 4, /** @< */ ++ DWC_LINK_STATE_RX_DET = 5, /** @< */ ++ DWC_LINK_STATE_SS_INACT = 6, /** @< */ ++ DWC_LINK_STATE_POLL = 7, /** @< */ ++ DWC_LINK_STATE_RECOV = 8, /** @< */ ++ DWC_LINK_STATE_HRESET = 9, /** @< */ ++ DWC_LINK_STATE_CMPLY = 10, /** @< */ ++ DWC_LINK_STATE_LPBK = 11, /** @< */ ++ DWC_LINK_STATE_RESET = 14, /** @< */ ++ DWC_LINK_STATE_RESUME = 15, ++ ++ /** USB/Link State values in HS/FS/LS */ ++ DWC_LINK_STATE_ON = 0, /** @< */ ++ DWC_LINK_STATE_SLEEP = 2, /** @< */ ++ DWC_LINK_STATE_SUSPEND = 3, /** @< */ ++ DWC_LINK_STATE_EARLY_SUSPEND = 5, ++ ++ DWC_DEVT_ULST_SS_BIT = 0x00100000, ++ DWC_DEVT_ULST_SS_SHIFT = 20, ++ ++#define DWC_DEVT_HIBER_STATE_BITS DWC_DEVT_ULST_STATE_BITS ++#define DWC_DEVT_HIBER_STATE_SHIFT DWC_DEVT_ULST_STATE_SHIFT ++ ++#define DWC_DEVT_HIBER_SS_BIT DWC_DEVT_ULST_SS_BIT ++#define DWC_DEVT_HIBER_SS_SHIFT DWC_DEVT_ULST_SS_SHIFT ++ ++ DWC_DEVT_HIBER_HIRD_BITS = 0x0f000000, ++ DWC_DEVT_HIBER_HIRD_SHIFT = 24, ++} devt_data_t; ++ ++/** ++ * This enum represents the bit fields of an Event Buffer entry for ++ * Endpoint Specific events (DEPEVT). ++ */ ++typedef enum depevt_data { ++ /** Endpoint Number */ ++ DWC_DEPEVT_EPNUM_BITS = 0x0000003e, ++ DWC_DEPEVT_EPNUM_SHIFT = 1, ++ ++ /** Endpoint Event Type */ ++ DWC_DEPEVT_INTTYPE_BITS = 0x000003c0, ++ DWC_DEPEVT_INTTYPE_SHIFT = 6, ++ ++ /** Endpoint Event Type values */ ++ DWC_DEPEVT_XFER_CMPL = 1, /** @< */ ++ DWC_DEPEVT_XFER_IN_PROG = 2, /** @< */ ++ DWC_DEPEVT_XFER_NRDY = 3, /** @< */ ++ DWC_DEPEVT_FIFOXRUN = 4, /** @< */ ++ DWC_DEPEVT_STRM_EVT = 6, /** @< */ ++ DWC_DEPEVT_EPCMD_CMPL = 7, ++ ++ /** Event Status for Start Xfer Command */ ++ DWC_DEPEVT_NO_MORE_RSCS_BIT = 0x00001000, ++ DWC_DEPEVT_NO_MORE_RSCS_SHIFT = 12, ++ DWC_DEPEVT_ISOC_TIME_PASSED_BIT = 0x00002000, ++ DWC_DEPEVT_ISOC_TIME_PASSED_SHIFT = 13, ++ ++ /** Event Status for Stream Event */ ++ DWC_DEPEVT_STRM_EVT_BITS = 0x0000f000, ++ DWC_DEPEVT_STRM_EVT_SHIFT = 12, ++ ++ /** Stream Event Status values */ ++ DWC_DEPEVT_STRM_FOUND = 1, /** @< */ ++ DWC_DEPEVT_STRM_NOT_FOUND = 2, ++ ++ /** Event Status for Xfer Complete or Xfer In Progress Event */ ++ DWC_DEPEVT_BUS_ERR_BIT = 0x00001000, ++ DWC_DEPEVT_BUS_ERR_SHIFT = 12, ++ DWC_DEPEVT_SHORT_PKT_BIT = 0x00002000, ++ DWC_DEPEVT_SHORT_PKT_SHIFT = 13, ++ DWC_DEPEVT_IOC_BIT = 0x00004000, ++ DWC_DEPEVT_IOC_SHIFT = 14, ++ DWC_DEPEVT_LST_BIT = 0x00008000, ++ DWC_DEPEVT_LST_SHIFT = 15, ++#define DWC_DEPEVT_MISSED_ISOC_BIT DWC_DEPEVT_LST_BIT ++#define DWC_DEPEVT_MISSED_ISOC_SHIFT DWC_DEPEVT_LST_SHIFT ++ ++ /** Event Status for Xfer Not Ready Event */ ++ DWC_DEPEVT_CTRL_BITS = 0x00003000, ++ DWC_DEPEVT_CTRL_SHIFT = 12, ++ DWC_DEPEVT_XFER_ACTIVE_BIT = 0x00008000, ++ DWC_DEPEVT_XFER_ACTIVE_SHIFT = 15, ++ ++ /** Xfer Not Ready Event Status values */ ++ DWC_DEPEVT_CTRL_SETUP = 0, /** @< */ ++ DWC_DEPEVT_CTRL_DATA = 1, /** @< */ ++ DWC_DEPEVT_CTRL_STATUS = 2, ++ ++ /** Stream ID */ ++ DWC_DEPEVT_STRM_ID_BITS = 0xffff0000, ++ DWC_DEPEVT_STRM_ID_SHIFT = 16, ++ ++ /** Isoc uFrame Number (for Xfer Not Ready on Isoc EP) */ ++ DWC_DEPEVT_ISOC_UFRAME_NUM_BITS = 0xffff0000, ++ DWC_DEPEVT_ISOC_UFRAME_NUM_SHIFT = 16, ++ ++ /** Xfer Resource Index (for Start Xfer Command) */ ++ DWC_DEPEVT_XFER_RSC_IDX_BITS = 0x007f0000, ++ DWC_DEPEVT_XFER_RSC_IDX_SHIFT = 16, ++ ++ /** Current Data Sequence Number (for Get Endpoint State Command) */ ++ DWC_DEPEVT_CUR_DAT_SEQ_NUM_BITS = 0x001f0000, ++ DWC_DEPEVT_CUR_DAT_SEQ_NUM_SHIFT = 16, ++ ++ /** Flow Control State (for Get Endpoint State Command) */ ++ DWC_DEPEVT_FLOW_CTRL_BIT = 0x00200000, ++ DWC_DEPEVT_FLOW_CTRL_SHIFT = 21, ++} depevt_data_t; ++ ++/** ++ * This enum represents the non-generic bit fields of an Event Buffer entry ++ * for other Core events (GEVT). ++ */ ++typedef enum gevt_data { ++ /** PHY Port Number */ ++ DWC_GINT_PHY_PORT_BITS = 0xf00, ++ DWC_GINT_PHY_PORT_SHIFT = 8, ++} gevt_data_t; ++ ++/** ++ * This struct represents the 32-bit register fields of the Event Buffer ++ * Registers (GEVENTBUFn). ++ */ ++typedef struct geventbuf_data { ++ /** Event Buffer Address Register Low Word */ ++ volatile u32 geventadr_lo; ++ ++ /** Event Buffer Address Register High Word */ ++ volatile u32 geventadr_hi; ++ ++ /** Event Buffer Size Register. ++ * Fields defined in enum @ref geventsiz_data. */ ++ volatile u32 geventsiz; ++ ++ /** Event Buffer Count Register. ++ * Fields defined in enum @ref geventcnt_data. */ ++ volatile u32 geventcnt; ++} geventbuf_data_t; ++ ++/** ++ * Core Global Registers <i>Offsets 100h-5FCh</i>. ++ * ++ * The dwc_usb3_core_global_regs structure defines the size ++ * and relative field offsets for the Core Global Registers. ++ */ ++typedef struct dwc_usb3_core_global_regs { ++ ++#define DWC_CORE_GLOBAL_REG_OFFSET 0x100 ++ ++ /** Core BIU Configuration 0 Register <i>Offset: 100h</i>. ++ * Fields defined in enum @ref gsbuscfg0_data. */ ++ volatile u32 gsbuscfg0; ++ ++ /** Core BIU Configuration 1 Register <i>Offset: 104h</i>. ++ * Fields defined in enum @ref gsbuscfg1_data. */ ++ volatile u32 gsbuscfg1; ++ ++ /** Core Tx Threshold Control Register <i>Offset: 108h</i>. ++ * Fields defined in enum @ref gtxthrcfg_data. */ ++ volatile u32 gtxthrcfg; ++ ++ /** Core Threshold Control Register <i>Offset: 10Ch</i>. ++ * Fields defined in enum @ref grxthrcfg_data. */ ++ volatile u32 grxthrcfg; ++ ++ /** Core Control Register <i>Offset: 110h</i>. ++ * Fields defined in enum @ref gctl_data. */ ++ volatile u32 gctl; ++ ++ /** Core Interrupt Mask Register <i>Offset: 114h</i>. ++ * Fields defined in enum @ref gevten_data. */ ++ volatile u32 gevten; ++ ++ /** Core Status Register <i>Offset: 118h</i>. ++ * Fields defined in enum @ref gsts_data. */ ++ volatile u32 gsts; ++ ++ /** Core User Control 1 Register <i>Offset: 11Ch</i> */ ++ volatile u32 guctl1; ++ ++ /** Synopsys ID Register <i>Offset: 120h</i> */ ++ volatile u32 gsnpsid; ++ ++ /** General Purpose I/O Register <i>Offset: 124h</i> */ ++ volatile u32 ggpio; ++ ++ /** User ID Register <i>Offset: 128h</i> */ ++ volatile u32 guid; ++ ++ /** Core User Control Register <i>Offset: 12Ch</i> */ ++ volatile u32 guctl; ++ ++ /** Bus Error Address Register <i>Offset: 130h</i> */ ++ volatile u32 gbuserraddrlo; ++ ++ /** Bus Error Address Register <i>Offset: 134h</i> */ ++ volatile u32 gbuserraddrhi; ++ ++ /** SS Port to Bus Instance Mapping Register <i>Offset: 138h</i> */ ++ volatile u32 gprtbimap_lo; ++ ++ /** SS Port to Bus Instance Mapping Register <i>Offset: 13Ch</i> */ ++ volatile u32 gprtbimap_hi; ++ ++ /** Hardware Parameter 0 Register <i>Offset: 140h</i>. ++ * Fields defined in enum @ref ghwparams0_data. */ ++ volatile u32 ghwparams0; ++ ++ /** Hardware Parameter 1 Register <i>Offset: 144h</i>. ++ * Fields defined in enum @ref ghwparams1_data. */ ++ volatile u32 ghwparams1; ++ ++ /** Hardware Parameter 2 Register <i>Offset: 148h</i>. ++ * Fields defined in enum @ref ghwparams2_data. */ ++ volatile u32 ghwparams2; ++ ++ /** Hardware Parameter 3 Register <i>Offset: 14Ch</i>. ++ * Fields defined in enum @ref ghwparams3_data. */ ++ volatile u32 ghwparams3; ++ ++ /** Hardware Parameter 4 Register <i>Offset: 150h</i>. ++ * Fields defined in enum @ref ghwparams4_data. */ ++ volatile u32 ghwparams4; ++ ++ /** Hardware Parameter 5 Register <i>Offset: 154h</i>. ++ * Fields defined in enum @ref ghwparams5_data. */ ++ volatile u32 ghwparams5; ++ ++ /** Hardware Parameter 6 Register <i>Offset: 158h</i>. ++ * Fields defined in enum @ref ghwparams6_data. */ ++ volatile u32 ghwparams6; ++ ++ /** Hardware Parameter 7 Register <i>Offset: 15Ch</i>. ++ * Fields defined in enum @ref ghwparams7_data. */ ++ volatile u32 ghwparams7; ++ ++ /** Debug Queue/FIFO Space Register <i>Offset: 160h</i>. ++ * Fields defined in enum @ref gdbgfifospace_data. */ ++ volatile u32 gdbgfifospace; ++ ++ /** Debug LTSSM Register <i>Offset: 164h</i>. ++ * Fields defined in enum @ref gdbgltssm_data */ ++ volatile u32 gdbgltssm; ++ ++ /** Debug LNMCC Register <i>Offset: 168h</i> */ ++ volatile u32 gdbglnmcc; ++ ++ /** Debug BMU Register <i>Offset: 16Ch</i> */ ++ volatile u32 gdbgbmu; ++ ++ /** Debug LSP Mux Register <i>Offset: 170h</i> */ ++ volatile u32 gdbglspmux; ++ ++ /** Debug LSP Register <i>Offset: 174h</i> */ ++ volatile u32 gdbglsp; ++ ++ /** Debug EP Info 0 Register <i>Offset: 178h</i> */ ++ volatile u32 gdbgepinfo0; ++ ++ /** Debug EP Info 1 Register <i>Offset: 17Ch</i> */ ++ volatile u32 gdbgepinfo1; ++ ++ /** HS Port to Bus Instance Mapping Register <i>Offset: 180h</i> */ ++ volatile u32 gprtbimap_hs_lo; ++ ++ /** HS Port to Bus Instance Mapping Register <i>Offset: 184h</i> */ ++ volatile u32 gprtbimap_hs_hi; ++ ++ /** FS Port to Bus Instance Mapping Register <i>Offset: 188h</i> */ ++ volatile u32 gprtbimap_fs_lo; ++ ++ /** FS Port to Bus Instance Mapping Register <i>Offset: 18Ch</i> */ ++ volatile u32 gprtbimap_fs_hi; ++ ++ /** reserved <i>Offset: 190h-1BCh</i> */ ++ volatile u32 reserved4[12]; ++ ++ /** Global RMMI PHY Control Register <i>Offset: 1C0h-200h</i> */ ++ volatile u32 gusb3rmmictl[16]; ++ ++ /** USB2 Configuration Registers <i>Offset: 200h-23Ch</i>. ++ * Fields defined in enum @ref gusb2phycfg_data. */ ++ volatile u32 gusb2phycfg[16]; ++ ++ /** USB2 I2C Access Registers <i>Offset: 240h-27Ch</i>. ++ * Fields defined in enum @ref gusb2i2cctl_data. */ ++ volatile u32 gusb2i2cctl[16]; ++ ++ /** USB2 PHY Vendor Control Registers <i>Offset: 280h-2BCh</i>. ++ * Fields defined in enum @ref gusb2phyacc_data. */ ++ volatile u32 gusb2phyacc[16]; ++ ++ /** USB3 Pipe Control Registers <i>Offset: 2C0h-2FCh</i>. ++ * Fields defined in enum @ref gusb3pipectl_data. */ ++ volatile u32 gusb3pipectl[16]; ++ ++ /** Transmit FIFO Size Registers <i>Offset: 300h-37Ch</i>. ++ * Fields defined in enum @ref gfifosize_data. */ ++ volatile u32 gtxfifosiz[32]; ++ ++ /** Receive FIFO Size Registers <i>Offset: 380h-3FC0h</i>. ++ * Fields defined in enum @ref gfifosize_data. */ ++ volatile u32 grxfifosiz[32]; ++ ++ /** Event Buffer Registers <i>Offset: 400h-5FCh</i>. ++ * Fields defined in struct @ref geventbuf_data. */ ++ struct geventbuf_data geventbuf[32]; ++ ++ /** Hardware Parameter 8 Register <i>Offset: 600h</i>. ++ * Fields defined in enum @ref ghwparams8_data. */ ++ volatile u32 ghwparams8; ++} dwc_usb3_core_global_regs_t; ++ ++ ++/****************************************************************************/ ++/* Device Global Registers */ ++ ++/** ++ * This enum represents the bit fields in the Device Configuration ++ * Register (DCFG). ++ */ ++typedef enum dcfg_data { ++ /** Device Speed <i>Access: R_W</i> */ ++ DWC_DCFG_DEVSPD_BITS = 0x000007, ++ DWC_DCFG_DEVSPD_SHIFT = 0, ++ ++ /** Device Speed values */ ++ DWC_SPEED_HS_PHY_30MHZ_OR_60MHZ = 0, /** @< */ ++ DWC_SPEED_FS_PHY_30MHZ_OR_60MHZ = 1, /** @< */ ++ DWC_SPEED_LS_PHY_6MHZ = 2, /** @< */ ++ DWC_SPEED_FS_PHY_48MHZ = 3, /** @< */ ++ DWC_SPEED_SS_PHY_125MHZ_OR_250MHZ = 4, ++ ++ /** Device Address <i>Access: R_W</i> */ ++ DWC_DCFG_DEVADDR_BITS = 0x0003f8, ++ DWC_DCFG_DEVADDR_SHIFT = 3, ++ ++ /** Periodic Frame Interval <i>Access: R_W</i> */ ++ DWC_DCFG_PER_FR_INTVL_BITS = 0x000c00, ++ DWC_DCFG_PER_FR_INTVL_SHIFT = 10, ++ ++ /** Periodic Frame Interval values */ ++ DWC_DCFG_PER_FR_INTVL_80 = 0, /** @< */ ++ DWC_DCFG_PER_FR_INTVL_85 = 1, /** @< */ ++ DWC_DCFG_PER_FR_INTVL_90 = 2, /** @< */ ++ DWC_DCFG_PER_FR_INTVL_95 = 3, ++ ++ /** Device Interrupt Number <i>Access: R_W</i> */ ++ DWC_DCFG_DEV_INTR_NUM_BITS = 0x01f000, ++ DWC_DCFG_DEV_INTR_NUM_SHIFT = 12, ++ ++ /** Number of Receive Buffers <i>Access: R_W</i> */ ++ DWC_DCFG_NUM_RCV_BUF_BITS = 0x3e0000, ++ DWC_DCFG_NUM_RCV_BUF_SHIFT = 17, ++ ++ /** LPM Capable <i>Access: R_W</i> */ ++ DWC_DCFG_LPM_CAP_BIT = 0x400000, ++ DWC_DCFG_LPM_CAP_SHIFT = 22, ++} dcfg_data_t; ++ ++/** ++ * This enum represents the bit fields in the Device Control ++ * Register (DCTL). ++ */ ++typedef enum dctl_data { ++ /** Soft Disconnect <i>Access: R_W</i> */ ++ DWC_DCTL_SFT_DISCONN_BIT = 0x00000001, ++ DWC_DCTL_SFT_DISCONN_SHIFT = 0, ++ ++ /** Test Control <i>Access: R_W</i> */ ++ DWC_DCTL_TSTCTL_BITS = 0x0000001e, ++ DWC_DCTL_TSTCTL_SHIFT = 1, ++ ++ /** USB/Link State Change Request <i>Access: R_W</i> */ ++ DWC_DCTL_ULST_CHNG_REQ_BITS = 0x000001e0, ++ DWC_DCTL_ULST_CHNG_REQ_SHIFT = 5, ++ ++ /** Requested Link State Transition/Action In SS Mode */ ++ DWC_LINK_STATE_REQ_NO_ACTION = 0, /** @< */ ++ DWC_LINK_STATE_REQ_SS_DISABLED = 4, /** @< */ ++ DWC_LINK_STATE_REQ_RX_DETECT = 5, /** @< */ ++ DWC_LINK_STATE_REQ_INACTIVE = 6, /** @< */ ++ DWC_LINK_STATE_REQ_RECOVERY = 8, /** @< */ ++ DWC_LINK_STATE_REQ_COMPLIANCE = 10, /** @< */ ++ DWC_LINK_STATE_REQ_LOOPBACK = 11, /** @< */ ++ DWC_LINK_STATE_REQ_HOST_MODE_ONLY = 15, ++ ++ /** Requested Link State Transition/Action In HS/FS/LS Mode */ ++ DWC_LINK_STATE_REQ_REMOTE_WAKEUP = 8, ++ ++ /** U1/U2 control <i>Access: R_W</i> */ ++ DWC_DCTL_ACCEPT_U1_EN_BIT = 0x00000200, ++ DWC_DCTL_ACCEPT_U1_EN_SHIFT = 9, ++ DWC_DCTL_INIT_U1_EN_BIT = 0x00000400, ++ DWC_DCTL_INIT_U1_EN_SHIFT = 10, ++ DWC_DCTL_ACCEPT_U2_EN_BIT = 0x00000800, ++ DWC_DCTL_ACCEPT_U2_EN_SHIFT = 11, ++ DWC_DCTL_INIT_U2_EN_BIT = 0x00001000, ++ DWC_DCTL_INIT_U2_EN_SHIFT = 12, ++ ++ /** Controller Save State <i>Access: R_W</i> */ ++ DWC_DCTL_CSS_BIT = 0x00010000, ++ DWC_DCTL_CSS_SHIFT = 16, ++ ++ /** Controller Restore State <i>Access: R_W</i> */ ++ DWC_DCTL_CRS_BIT = 0x00020000, ++ DWC_DCTL_CRS_SHIFT = 17, ++ ++ /** L1 Hibernation Enable <i>Access: R_W</i> */ ++ DWC_DCTL_L1_HIBER_EN_BIT = 0x00040000, ++ DWC_DCTL_L1_HIBER_EN_RES_SHIFT = 18, ++ ++ /** Keep Connect (for hibernation) <i>Access: R_W</i> */ ++ DWC_DCTL_KEEP_CONNECT_BIT = 0x00080000, ++ DWC_DCTL_KEEP_CONNECT_SHIFT = 19, ++ ++ /** LPM NYET Response Threshold <i>Access: R_W</i> */ ++ DWC_DCTL_LPM_NYET_THRESH_BITS = 0x00f00000, ++ DWC_DCTL_LPM_NYET_THRESH_SHIFT = 20, ++ ++ /** LPM Response <i>Access: R_W</i> */ ++ DWC_DCTL_APP_L1_RES_BIT = 0x00800000, ++ DWC_DCTL_APP_L1_RES_SHIFT = 23, ++ ++ /* HIRD Threshold <i>Access: R_W</i> */ ++ DWC_DCTL_HIRD_THR_BITS = 0x1f000000, ++ DWC_DCTL_HIRD_THR_SHIFT = 24, ++ ++ /** Light Soft Reset <i>Access: R_W</i> */ ++ DWC_DCTL_LSFT_RST_BIT = 0x20000000, ++ DWC_DCTL_LSFT_RST_SHIFT = 29, ++ ++ /** Core Soft Reset <i>Access: R_W</i> */ ++ DWC_DCTL_CSFT_RST_BIT = 0x40000000, ++ DWC_DCTL_CSFT_RST_SHIFT = 30, ++ ++ /** Run/Stop <i>Access: R_W</i> */ ++ DWC_DCTL_RUN_STOP_BIT = 0x80000000, ++ DWC_DCTL_RUN_STOP_SHIFT = 31, ++} dctl_data_t; ++ ++/** ++ * This enum represents the bit fields of the Device Event Enable ++ * Register (DEVTEN). ++ */ ++typedef enum devten_data { ++ /** Disconnect Detected Event Enable <i>Access: R_W</i> */ ++ DWC_DEVTEN_DISCONN_BIT = 0x0001, ++ DWC_DEVTEN_DISCONN_SHIFT = 0, ++ ++ /** USB Reset Enable <i>Access: R_W</i> */ ++ DWC_DEVTEN_USBRESET_BIT = 0x0002, ++ DWC_DEVTEN_USBRESET_SHIFT = 1, ++ ++ /** Connect Done Enable <i>Access: R_W</i> */ ++ DWC_DEVTEN_CONNDONE_BIT = 0x0004, ++ DWC_DEVTEN_CONNDONE_SHIFT = 2, ++ ++ /** USB/Link State Change Event Enable <i>Access: R_W</i> */ ++ DWC_DEVTEN_ULST_CHNG_BIT = 0x0008, ++ DWC_DEVTEN_ULST_CHNG_SHIFT = 3, ++ ++ /** Resume/Remote-Wakeup Event Enable <i>Access: R_W</i> */ ++ DWC_DEVTEN_WKUP_BIT = 0x0010, ++ DWC_DEVTEN_WKUP_SHIFT = 4, ++ ++ /** Hibernation Request Event Enable <i>Access: R_W</i> */ ++ DWC_DEVTEN_HIBER_REQ_BIT = 0x0020, ++ DWC_DEVTEN_HIBER_REQ_SHIFT = 5, ++ ++ /** End of Periodic Frame Event Enable <i>Access: R_W</i> */ ++ DWC_DEVTEN_U3_L2L1_SUSP_BIT = 0x0040, ++ DWC_DEVTEN_U3_L2L1_SUSP_SHIFT = 6, ++ ++ /** Start of (Micro)Frame Enable <i>Access: R_W</i> */ ++ DWC_DEVTEN_SOF_BIT = 0x0080, ++ DWC_DEVTEN_SOF_SHIFT = 7, ++ ++ /** Erratic Error Event Enable <i>Access: R_W</i> */ ++ DWC_DEVTEN_ERRATICERR_BIT = 0x0200, ++ DWC_DEVTEN_ERRATICERR_SHIFT = 9, ++ ++ /** U2 Inactivity Timeout Enable <i>Access: R_W</i> */ ++ DWC_DEVTEN_INACT_TIMEOUT_BIT = 0x2000, ++ DWC_DEVTEN_INACT_TIMEOUT_SHIFT = 13, ++} devten_data_t; ++ ++/** ++ * This enum represents the bit fields in the Device Status ++ * Register (DSTS). ++ */ ++typedef enum dsts_data { ++ /** Connected Speed <i>Access: RO</i>. ++ * (see enum @ref dcfg_data for values) */ ++ DWC_DSTS_CONNSPD_BITS = 0x00000007, ++ DWC_DSTS_CONNSPD_SHIFT = 0, ++ ++ /** (Micro)Frame Number of Received SOF <i>Access: RO</i> */ ++ DWC_DSTS_SOF_FN_BITS = 0x0001fff8, ++ DWC_DSTS_SOF_FN_SHIFT = 3, ++ ++ /** RX Fifo Empty <i>Access: RO</i> */ ++ DWC_DSTS_RXFIFO_EMPTY_BIT = 0x00020000, ++ DWC_DSTS_RXFIFO_EMPTY_SHIFT = 17, ++ ++ /** USB/Link State <i>Access: RO</i> */ ++ DWC_DSTS_USBLNK_STATE_BITS = 0x003c0000, ++ DWC_DSTS_USBLNK_STATE_SHIFT = 18, ++ ++ /** USB/Link State values same as for devt_data_t */ ++ ++ /** Device Controller Halted <i>Access: RO</i> */ ++ DWC_DSTS_DEV_CTRL_HLT_BIT = 0x00400000, ++ DWC_DSTS_DEV_CTRL_HLT_SHIFT = 22, ++ ++ /** Core Idle <i>Access: RO</i> */ ++ DWC_DSTS_CORE_IDLE_BIT = 0x00800000, ++ DWC_DSTS_CORE_IDLE_SHIFT = 23, ++ ++ /** Save State Status <i>Access: RO</i> */ ++ DWC_DSTS_SSS_BIT = 0x01000000, ++ DWC_DSTS_SSS_SHIFT = 24, ++ ++ /** Restore State Status <i>Access: RO</i> */ ++ DWC_DSTS_RSS_BIT = 0x02000000, ++ DWC_DSTS_RSS_SHIFT = 25, ++ ++ /** Save/Restore Error <i>Access: RO</i> */ ++ DWC_DSTS_SRE_BIT = 0x10000000, ++ DWC_DSTS_SRE_SHIFT = 28, ++ ++ /** Link-state Not Ready <i>Access: RO</i> */ ++ DWC_DSTS_LNR_BIT = 0x20000000, ++ DWC_DSTS_LNR_SHIFT = 29, ++} dsts_data_t; ++ ++/** ++ * This enum represents the bit fields in the Device Generic Command Parameter ++ * Register (DGCMDPARn) for the various commands. ++ */ ++typedef enum dgcmdpar_data { ++ /** Periodic Parameters - for DWC_DGCMD_SET_PERIODIC_PARAMS command */ ++ DWC_DGCMD_PER_PARAM_SEL_BITS = 0x000003ff, ++ DWC_DGCMD_PER_PARAM_SEL_SHIFT = 0, ++ ++ /** Host Role Request - for DWC_DGCMD_XMIT_HOST_ROLE_REQUEST command */ ++ DWC_DGCMDPAR_HOST_ROLE_REQ_BITS = 0x00000003, ++ DWC_DGCMDPAR_HOST_ROLE_REQ_SHIFT = 0, ++ ++ /** RSP Phase values - for DWC_DGCMD_XMIT_HOST_ROLE_REQUEST (older ++ * cores) or DWC_DGCMD_HOST_ROLE_REQ_DEV_NOTIF (newer cores) */ ++ DWC_DGCMDPAR_HOST_ROLE_REQ_INITIATE = 1, /** @< */ ++ DWC_DGCMDPAR_HOST_ROLE_REQ_CONFIRM = 2, ++ ++ /** Notification Type - for DWC_DGCMD_XMIT_DEV_NOTIF command */ ++ DWC_DGCMDPAR_DEV_NOTIF_TYPE_BITS = 0x0000000f, ++ DWC_DGCMDPAR_DEV_NOTIF_TYPE_SHIFT = 0, ++ ++ /** Notification Type values - for DWC_DGCMD_XMIT_DEV_NOTIF command */ ++ DWC_DGCMD_FUNCTION_WAKE_DEV_NOTIF = 1, /** @< */ ++ DWC_DGCMD_LATENCY_TOL_DEV_NOTIF = 2, /** @< */ ++ DWC_DGCMD_BUS_INTVL_ADJ_DEV_NOTIF = 3, /** @< */ ++ DWC_DGCMD_HOST_ROLE_REQ_DEV_NOTIF = 4, ++ ++ /** Notification Parameters - for DWC_DGCMD_XMIT_DEV_NOTIF command */ ++ DWC_DGCMDPAR_DEV_NOTIF_PARAM_BITS = 0xfffffff0, ++ DWC_DGCMDPAR_DEV_NOTIF_PARAM_SHIFT = 4, ++ ++ /** Best Effort Latency Tolerance Value - for ++ * DWC_DGCMD_LATENCY_TOL_DEV_NOTIF command type */ ++ DWC_DGCMDPAR_BELT_VALUE_BITS = 0x000003ff, ++ DWC_DGCMDPAR_BELT_VALUE_SHIFT = 0, ++ ++ /** Best Effort Latency Tolerance Scale - for ++ * DWC_DGCMD_LATENCY_TOL_DEV_NOTIF command type */ ++ DWC_DGCMDPAR_BELT_SCALE_BITS = 0x00000c00, ++ DWC_DGCMDPAR_BELT_SCALE_SHIFT = 10, ++ ++ /** Latency Scale values (ns) */ ++ DWC_LATENCY_VALUE_MULT_1024 = 1, /** @< */ ++ DWC_LATENCY_VALUE_MULT_32768 = 2, /** @< */ ++ DWC_LATENCY_VALUE_MULT_1048576 = 3, ++} dgcmdpar_data_t; ++ ++/** ++ * This enum represents the bit fields in the Device Generic Command ++ * Register (DGCMDn). ++ */ ++typedef enum dgcmd_data { ++ /** Command Type <i>Access: R_W</i> */ ++ DWC_DGCMD_TYP_BITS = 0x0ff, ++ DWC_DGCMD_TYP_SHIFT = 0, ++ ++ /** Command Type values */ ++ DWC_DGCMD_SET_PERIODIC_PARAMS = 2, /** @< */ ++ DWC_DGCMD_XMIT_FUNC_WAKE_DEV_NOTIF = 3, /** @< */ ++ DWC_DGCMD_SET_SCRATCHPAD_ARRAY_ADR_LO = 4, /** @< */ ++ DWC_DGCMD_SET_SCRATCHPAD_ARRAY_ADR_HI = 5, /** @< */ ++ DWC_DGCMD_XMIT_HOST_ROLE_REQUEST = 6, /** @< */ ++ DWC_DGCMD_XMIT_DEV_NOTIF = 7, /** @< */ ++ DWC_DGCMD_SELECTED_FIFO_FLUSH = 9, /** @< */ ++ DWC_DGCMD_ALL_FIFO_FLUSH = 10, /** @< */ ++ DWC_DGCMD_SET_EP_NRDY = 12, /** @< */ ++ DWC_DGCMD_RUN_SOC_BUS_LOOPBK_TST = 16, ++ ++ /** Command Interrupt on Complete <i>Access: R_W</i> */ ++ DWC_DGCMD_IOC_BIT = 0x100, ++ DWC_DGCMD_IOC_SHIFT = 8, ++ ++ /** Command Active <i>Access: R_W</i> */ ++ DWC_DGCMD_ACT_BIT = 0x400, ++ DWC_DGCMD_ACT_SHIFT = 10, ++ ++ /** Command Status <i>Access: R_W</i> */ ++ DWC_DGCMD_STS_BITS = 0xf000, ++ DWC_DGCMD_STS_SHIFT = 12, ++ ++ /** Command Status values */ ++ DWC_DGCMD_STS_ERROR = 15, ++} dgcmd_data_t; ++ ++/** ++ * This enum represents the bit fields in the Device Endpoint Mapping ++ * Registers (DEPMAPn). ++ */ ++typedef enum depmap_data { ++ /** Resource Number <i>Access: R_W / RO</i> */ ++ DWC_EPMAP_RES_NUM_BITS = 0x1f, ++ DWC_EPMAP_RES_NUM_SHIFT = 0, ++} depmap_data_t; ++ ++/** ++ * Device Global Registers <i>Offsets 700h-7FCh</i>. ++ * ++ * The following structures define the size and relative field offsets ++ * for the Device Mode Global Registers. ++ */ ++typedef struct dwc_usb3_dev_global_regs { ++ ++#define DWC_DEV_GLOBAL_REG_OFFSET 0x700 ++ ++ /** Device Configuration Register <i>Offset: 700h</i>. ++ * Fields defined in enum @ref dcfg_data. */ ++ volatile u32 dcfg; ++ ++ /** Device Control Register <i>Offset: 704h</i>. ++ * Fields defined in enum @ref dctl_data. */ ++ volatile u32 dctl; ++ ++ /** Device All Endpoints Interrupt Mask Register <i>Offset: 708h</i>. ++ * Fields defined in enum @ref devten_data. */ ++ volatile u32 devten; ++ ++ /** Device Status Register <i>Offset: 70Ch</i>. ++ * Fields defined in enum @ref dsts_data. */ ++ volatile u32 dsts; ++ ++ /** Device Generic Command Parameter Register <i>Offset: 710h</i>. ++ * Fields defined in enum @ref dgcmdpar_data. */ ++ volatile u32 dgcmdpar; ++ ++ /** Device Generic Command Register <i>Offset: 714h</i>. ++ * Fields defined in enum @ref dgcmd_data. */ ++ volatile u32 dgcmd; ++ ++ /** reserved <i>Offset: 718h-71Ch</i> */ ++ volatile u32 reserved[2]; ++ ++ /** Device Active Logical Endpoint Enable Register <i>Offset: 720h</i>. ++ * One bit per logical endpoint, bit0=EP0 ... bit31=EP31. */ ++ volatile u32 dalepena; ++} dwc_usb3_dev_global_regs_t; ++ ++ ++/****************************************************************************/ ++/* Device Endpoint Specific Registers */ ++ ++/** ++ * This enum represents the bit fields in the Device Endpoint Command ++ * Parameter 1 Register (DEPCMDPAR1n) for the Set Endpoint Configuration ++ * (DEPCFG) command. ++ */ ++typedef enum depcfgpar1_data { ++ /** Interrupt number */ ++ DWC_EPCFG1_INTRNUM_BITS = 0x0000003f, ++ DWC_EPCFG1_INTRNUM_SHIFT = 0, ++ ++ /** Stream Completed */ ++ DWC_EPCFG1_XFER_CMPL_BIT = 0x00000100, ++ DWC_EPCFG1_XFER_CMPL_SHIFT = 8, ++ ++ /** Stream In Progress */ ++ DWC_EPCFG1_XFER_IN_PROG_BIT = 0x00000200, ++ DWC_EPCFG1_XFER_IN_PROG_SHIFT = 9, ++ ++ /** Stream Not Ready */ ++ DWC_EPCFG1_XFER_NRDY_BIT = 0x00000400, ++ DWC_EPCFG1_XFER_NRDY_SHIFT = 10, ++ ++ /** Rx FIFO Underrun / Tx FIFO Overrun */ ++ DWC_EPCFG1_FIFOXRUN_BIT = 0x00000800, ++ DWC_EPCFG1_FIFOXRUN_SHIFT = 11, ++ ++ /** Back-to-Back Setup Packets Received */ ++ DWC_EPCFG1_SETUP_PNDG_BIT = 0x00001000, ++ DWC_EPCFG1_SETUP_PNDG_SHIFT = 12, ++ ++ /** Endpoint Command Complete */ ++ DWC_EPCFG1_EPCMD_CMPL_BIT = 0x00002000, ++ DWC_EPCFG1_EPCMD_CMPL_SHIFT = 13, ++ ++ /** Endpoint EBC Mode */ ++ DWC_EPCFG1_EBC_MODE_BIT = 0x00008000, ++ DWC_EPCFG1_EBC_MODE_SHIFT = 15, ++ ++ /** Endpoint bInterval */ ++ DWC_EPCFG1_BINTERVAL_BITS = 0x00ff0000, ++ DWC_EPCFG1_BINTERVAL_SHIFT = 16, ++ ++ /** Endpoint Stream Capability */ ++ DWC_EPCFG1_STRM_CAP_BIT = 0x01000000, ++ DWC_EPCFG1_STRM_CAP_SHIFT = 24, ++ ++ /** Endpoint Direction */ ++ DWC_EPCFG1_EP_DIR_BIT = 0x02000000, ++ DWC_EPCFG1_EP_DIR_SHIFT = 25, ++ ++ /** Endpoint Number */ ++ DWC_EPCFG1_EP_NUM_BITS = 0x3c000000, ++ DWC_EPCFG1_EP_NUM_SHIFT = 26, ++} depcfgpar1_data_t; ++ ++/** ++ * This enum represents the bit fields in the Device Endpoint Command ++ * Parameter 0 Register (DEPCMDPAR0n) for the Set Endpoint Configuration ++ * (DWC_EPCMD_SET_EP_CFG) command. ++ */ ++typedef enum depcfgpar0_data { ++ /** Endpoint Type <i>Access: R_W</i> */ ++ DWC_EPCFG0_EPTYPE_BITS = 0x00000006, ++ DWC_EPCFG0_EPTYPE_SHIFT = 1, ++ ++ /** Endpoint Type values */ ++ DWC_USB3_EP_TYPE_CONTROL = 0, /** @< */ ++ DWC_USB3_EP_TYPE_ISOC = 1, /** @< */ ++ DWC_USB3_EP_TYPE_BULK = 2, /** @< */ ++ DWC_USB3_EP_TYPE_INTR = 3, ++ ++ /** Maximum Packet Size <i>Access: R_W</i> */ ++ DWC_EPCFG0_MPS_BITS = 0x00003ff8, ++ DWC_EPCFG0_MPS_SHIFT = 3, ++ ++ /** Flow Control State <i>Access: R_W</i> */ ++ DWC_EPCFG0_FLOW_CTRL_STATE_BIT = 0x00010000, ++ DWC_EPCFG0_FLOW_CTRL_STATE_SHIFT = 16, ++ ++ /** Tx Fifo Number (IN endpoints only) <i>Access: R_W</i> */ ++ DWC_EPCFG0_TXFNUM_BITS = 0x003e0000, ++ DWC_EPCFG0_TXFNUM_SHIFT = 17, ++ ++ /** Burst Size <i>Access: R_W</i> */ ++ DWC_EPCFG0_BRSTSIZ_BITS = 0x03c00000, ++ DWC_EPCFG0_BRSTSIZ_SHIFT = 22, ++ ++ /** Data Sequence Num (old) <i>Access: R_W</i> */ ++ DWC_EPCFG0_DSNUM_BITS = 0x7c000000, ++ DWC_EPCFG0_DSNUM_SHIFT = 26, ++ ++ /** Ignore Data Sequence Num (old) <i>Access: R_W</i> */ ++ DWC_EPCFG0_IGN_DSNUM_BIT = 0x80000000, ++ DWC_EPCFG0_IGN_DSNUM_SHIFT = 31, ++ ++ /** Config Action (new) <i>Access: R_W</i> */ ++ DWC_EPCFG0_CFG_ACTION_BITS = 0xc0000000, ++ DWC_EPCFG0_CFG_ACTION_SHIFT = 30, ++ ++ /** Config Action values (new) */ ++ DWC_CFG_ACTION_INIT = 0, /** @< */ ++ DWC_CFG_ACTION_RESTORE = 1, /** @< */ ++ DWC_CFG_ACTION_MODIFY = 2, ++} depcfgpar0_data_t; ++ ++/** ++ * This enum represents the bit fields in the Device Endpoint Command ++ * Register (DEPCMDn). ++ */ ++typedef enum depcmd_data { ++ /** Command Type <i>Access: R_W</i> */ ++ DWC_EPCMD_TYP_BITS = 0x0ff, ++ DWC_EPCMD_TYP_SHIFT = 0, ++ ++ /** Command Type values */ ++ DWC_EPCMD_SET_EP_CFG = 1, /** @< */ ++ DWC_EPCMD_SET_XFER_CFG = 2, /** @< */ ++ DWC_EPCMD_GET_EP_STATE = 3, /** @< */ ++ DWC_EPCMD_SET_STALL = 4, /** @< */ ++ DWC_EPCMD_CLR_STALL = 5, /** @< */ ++ DWC_EPCMD_START_XFER = 6, /** @< */ ++ DWC_EPCMD_UPDATE_XFER = 7, /** @< */ ++ DWC_EPCMD_END_XFER = 8, /** @< */ ++ DWC_EPCMD_START_NEW_CFG = 9, ++ ++ /** Command Interrupt on Complete <i>Access: R_W</i> */ ++ DWC_EPCMD_IOC_BIT = 0x100, ++ DWC_EPCMD_IOC_SHIFT = 8, ++ ++ /** Command Active <i>Access: R_W</i> */ ++ DWC_EPCMD_ACT_BIT = 0x400, ++ DWC_EPCMD_ACT_SHIFT = 10, ++ ++ /** High Priority / Force RM Bit <i>Access: R_W</i> */ ++ DWC_EPCMD_HP_FRM_BIT = 0x800, ++ DWC_EPCMD_HP_FRM_SHIFT = 11, ++ ++ /** Command Completion Status <i>Access: R_W</i> */ ++ DWC_EPCMD_CMPL_STS_BITS = 0xf000, ++ DWC_EPCMD_CMPL_STS_SHIFT = 12, ++ ++ /** Stream Number or uFrame (input) <i>Access: R_W</i> */ ++ DWC_EPCMD_STR_NUM_OR_UF_BITS = 0xffff0000, ++ DWC_EPCMD_STR_NUM_OR_UF_SHIFT = 16, ++ ++ /** Transfer Resource Index (output) <i>Access: R_W</i> */ ++ DWC_EPCMD_XFER_RSRC_IDX_BITS = 0x007f0000, ++ DWC_EPCMD_XFER_RSRC_IDX_SHIFT = 16, ++} depcmd_data_t; ++ ++/** ++ * Device Endpoint Specific Registers <i>Offsets 800h-9ECh for OUT, ++ * 810h-9FCh for IN</i>. ++ * There will be one set of endpoint registers per logical endpoint ++ * implemented. ++ */ ++typedef struct dwc_usb3_dev_ep_regs { ++ ++#define DWC_DEV_OUT_EP_REG_OFFSET 0x800 ++#define DWC_DEV_IN_EP_REG_OFFSET 0x810 ++#define DWC_EP_REG_OFFSET 0x20 ++ ++ /** Device Endpoint Command Parameter 2 Register <i>Offset: 800h/810h + ++ * (ep_num * 20h) + 00h</i> */ ++ volatile u32 depcmdpar2; ++ ++ /** Device Endpoint Command Parameter 1 Register <i>Offset: 800h/810h + ++ * (ep_num * 20h) + 04h</i> */ ++ volatile u32 depcmdpar1; ++ ++ /** Device Endpoint Command Parameter 0 Register <i>Offset: 800h/810h + ++ * (ep_num * 20h) + 08h</i> */ ++ volatile u32 depcmdpar0; ++ ++ /** Device Endpoint Command Register <i>Offset: 800h/810h + ++ * (ep_num * 20h) + 0Ch</i>. ++ * Fields defined in enum @ref depcmd_data. */ ++ volatile u32 depcmd; ++ ++ /** reserved <i>Offset: 800h/810h + ++ * (ep_num * 20h) + 10h-1Ch</i> */ ++ volatile u32 reserved[4]; ++} dwc_usb3_dev_ep_regs_t; ++ ++ ++/****************************************************************************/ ++/* DMA Descriptor Specific Structures */ ++ ++/** ++ * This enum represents the bit fields in the DMA Descriptor ++ * Status quadlet. ++ */ ++typedef enum desc_sts_data { ++ /** Transfer Count */ ++ DWC_DSCSTS_XFRCNT_BITS = 0x00ffffff, ++ DWC_DSCSTS_XFRCNT_SHIFT = 0, ++ ++ /** Packet Count Minus 1 (for HS IN transfers) */ ++ DWC_DSCSTS_PCM1_BITS = 0x03000000, ++ DWC_DSCSTS_PCM1_SHIFT = 24, ++ ++ /** Transfer Request Block Response */ ++ DWC_DSCSTS_TRBRSP_BITS = 0xf0000000, ++ DWC_DSCSTS_TRBRSP_SHIFT = 28, ++ ++ /** Response values */ ++ DWC_TRBRSP_MISSED_ISOC_IN = 1, /** @< */ ++ DWC_TRBRSP_SETUP_PEND = 2, /** @< */ ++ DWC_TRBRSP_XFER_IN_PROG = 4, ++} desc_sts_data_t; ++ ++/** ++ * This enum represents the bit fields in the DMA Descriptor ++ * Control quadlet. ++ */ ++typedef enum desc_ctl_data { ++ /** Hardware-Owned bit */ ++ DWC_DSCCTL_HWO_BIT = 0x00000001, ++ DWC_DSCCTL_HWO_SHIFT = 0, ++ ++ /** Last Descriptor bit */ ++ DWC_DSCCTL_LST_BIT = 0x00000002, ++ DWC_DSCCTL_LST_SHIFT = 1, ++ ++ /** Chain Buffer bit */ ++ DWC_DSCCTL_CHN_BIT = 0x00000004, ++ DWC_DSCCTL_CHN_SHIFT = 2, ++ ++ /** Continue on Short Packet bit */ ++ DWC_DSCCTL_CSP_BIT = 0x00000008, ++ DWC_DSCCTL_CSP_SHIFT = 3, ++ ++ /** Transfer Request Block Control field */ ++ DWC_DSCCTL_TRBCTL_BITS = 0x000003f0, ++ DWC_DSCCTL_TRBCTL_SHIFT = 4, ++ ++ /** Transfer Request Block Control types */ ++ DWC_DSCCTL_TRBCTL_NORMAL = 1, /** @< */ ++ DWC_DSCCTL_TRBCTL_SETUP = 2, /** @< */ ++ DWC_DSCCTL_TRBCTL_STATUS_2 = 3, /** @< */ ++ DWC_DSCCTL_TRBCTL_STATUS_3 = 4, /** @< */ ++ DWC_DSCCTL_TRBCTL_CTLDATA_1ST = 5, /** @< */ ++ DWC_DSCCTL_TRBCTL_ISOC_1ST = 6, /** @< */ ++ DWC_DSCCTL_TRBCTL_ISOC = 7, /** @< */ ++ DWC_DSCCTL_TRBCTL_LINK = 8, ++ ++ /** Interrupt on Short Packet bit */ ++ DWC_DSCCTL_ISP_BIT = 0x00000400, ++ DWC_DSCCTL_ISP_SHIFT = 10, ++#define DWC_DSCCTL_IMI_BIT DWC_DSCCTL_ISP_BIT ++#define DWC_DSCCTL_IMI_SHIFT DWC_DSCCTL_ISP_SHIFT ++ ++ /** Interrupt on Completion bit */ ++ DWC_DSCCTL_IOC_BIT = 0x00000800, ++ DWC_DSCCTL_IOC_SHIFT = 11, ++ ++ /** Stream ID / SOF Number */ ++ DWC_DSCCTL_STRMID_SOFN_BITS = 0x3fffc000, ++ DWC_DSCCTL_STRMID_SOFN_SHIFT = 14, ++} desc_ctl_data_t; ++ ++/** ++ * DMA Descriptor structure ++ * ++ * DMA Descriptor structure contains 4 quadlets: ++ * Buffer Pointer Low address, Buffer Pointer High address, Status, and Control. ++ */ ++typedef struct dwc_usb3_dma_desc { ++ /** Buffer Pointer - Low address quadlet */ ++ u32 bptl; ++ ++ /** Buffer Pointer - High address quadlet */ ++ u32 bpth; ++ ++ /** Status quadlet. Fields defined in enum @ref desc_sts_data. */ ++ u32 status; ++ ++ /** Control quadlet. Fields defined in enum @ref desc_ctl_data. */ ++ u32 control; ++} dwc_usb3_dma_desc_t; ++ ++#ifdef SSIC ++ ++/* SSIC Registers */ ++typedef struct dwc_usb3_ssic_regs { ++ ++#define DWC_SSIC_REG_OFFSET 0xc40 ++ ++ volatile u32 sctl[16]; ++ volatile u32 sevt[16]; ++ volatile u32 sevten[16]; ++ volatile u32 gscfg; ++ volatile u32 gsser; ++ volatile u32 gsdbg; ++} dwc_usb3_ssic_regs_t; ++ ++typedef enum sevten_data { ++ DWC_SEVTEN_ROM_INIT_CMPLT_EN_BIT = 0x00000001, ++ DWC_SEVTEN_ROM_INIT_CMPLT_EN_SHIFT = 0, ++ ++ DWC_SEVTEN_LACC_CMPLT_EN_BIT = 0x00000002, ++ DWC_SEVTEN_LACC_CMPLT_EN_SHIFT = 1, ++ ++ DWC_SEVTEN_RCMD_RES_RCVD_EN_BIT = 0x00000004, ++ DWC_SEVTEN_RCMD_RES_RCVD_EN_SHIFT = 2, ++ ++ DWC_SEVTEN_RCMD_RES_SENT_EN_BIT = 0x00000008, ++ DWC_SEVTEN_RCMD_RES_SENT_EN_SHIFT = 3, ++ ++ DWC_SEVTEN_MPHY_ST_CHNGD_EN_BIT = 0x00000010, ++ DWC_SEVTEN_MPHY_ST_CHNGD_EN_SHIFT = 4, ++ ++ DWC_SEVTEN_OK_STRT_RRAP_EN_BIT = 0x00000020, ++ DWC_SEVTEN_OK_STRT_RRAP_EN_SHIFT = 5, ++ ++ DWC_SEVTEN_RRAP_ERROR_EN_BIT = 0x00000040, ++ DWC_SEVTEN_RRAP_ERROR_EN_SHIFT = 6, ++} sevten_data_t; ++ ++typedef enum sevt_data { ++ DWC_SEVT_ROM_INIT_CMPLT_BIT = 0x00000001, ++ DWC_SEVT_ROM_INIT_CMPLT_SHIFT = 0, ++ ++ DWC_SEVT_LACC_CMPLT_BIT = 0x00000002, ++ DWC_SEVT_LACC_CMPLT_SHIFT = 1, ++ ++ DWC_SEVT_RCMD_RES_RCVD_BIT = 0x00000004, ++ DWC_SEVT_RCMD_RES_RCVD_SHIFT = 2, ++ ++ DWC_SEVT_RCMD_RES_SENT_BIT = 0x00000008, ++ DWC_SEVT_RCMD_RES_SENT_SHIFT = 3, ++ ++ DWC_SEVT_MPHY_ST_CHNG_BIT = 0x00000010, ++ DWC_SEVT_MPHY_ST_CHNG_SHIFT = 4, ++ ++ DWC_SEVT_OK_STRT_RRAP_BIT = 0x00000020, ++ DWC_SEVT_OK_STRT_RRAP_SHIFT = 5, ++ ++ DWC_SEVT_RRAP_ERROR_BIT = 0x00000040, ++ DWC_SEVT_RRAP_ERROR_SHIFT = 6, ++ ++ DWC_SEVT_RACC_RESULT_BITS = 0x00000300, ++ DWC_SEVT_RACC_RESULT_SHIFT = 8, ++ ++ DWC_SEVT_LACC_RESULT_BIT = 0x00000400, ++ DWC_SEVT_LACC_RESULT_SHIFT = 10, ++ ++ DWC_SEVT_READ_RCVD_BIT = 0x00000800, ++ DWC_SEVT_READ_RCVD_SHIFT = 11, ++ ++ DWC_SEVT_RUADDR_RCVD_BITS = 0x0000f000, ++ DWC_SEVT_RUADDR_RCVD_SHIFT = 12, ++ ++ DWC_SEVT_RLADDR_RCVD_BITS = 0x00ff0000, ++ DWC_SEVT_RLADDR_RCVD_SHIFT = 16, ++ ++ DWC_SEVT_RDATA_RCVD_BITS = 0xff000000, ++ DWC_SEVT_RDATA_RCVD_SHIFT = 24, ++} sevt_data_t; ++ ++typedef enum sctl_data { ++ DWC_SCTL_GO_ACC_BIT = 0x00000001, ++ DWC_SCTL_GO_ACC_SHIFT = 0, ++ ++ DWC_SCTL_RACC_BIT = 0x00000002, ++ DWC_SCTL_RACC_SHIFT = 1, ++ ++ DWC_SCTL_RD_WR_N_BIT = 0x00000004, ++ DWC_SCTL_RD_WR_N_SHIFT = 2, ++ ++ DWC_SCTL_BCW_BIT = 0x00000008, ++ DWC_SCTL_BCW_SHIFT = 3, ++ ++ DWC_SCTL_IN_LN_CFG_BIT = 0x00000010, ++ DWC_SCTL_IN_LN_CFG_SHIFT = 4, ++ ++ DWC_SCTL_CFG_DONE_BIT = 0x00000020, ++ DWC_SCTL_CFG_DEON_SHIFT = 5, ++ ++ DWC_SCTL_CBS_ACC_BIT = 0x00000040, ++ DWC_SCTL_CBS_ACC_SHIFT = 6, ++ ++ DWC_SCTL_RXS_ACC_BIT = 0x00000080, ++ DWC_SCTL_RXS_ACC_SHIFT = 7, ++ ++ DWC_SCTL_EAID_BITS = 0x0000f000, ++ DWC_SCTL_EAID_SHIFT = 12, ++ ++ DWC_SCTL_AID_BITS = 0x00ff0000, ++ DWC_SCTL_AID_SHIFT = 16, ++ ++ DWC_SCTL_ADATA_BITS = 0xff000000, ++ DWC_SCTL_ADATA_SHIFT = 24, ++} sctl_data_t; ++ ++#endif /* SSIC */ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_USB3_REGS_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/linux_defs.h b/drivers/usb/gadget/udc/hiudc3/linux_defs.h +new file mode 100644 +index 0000000..b013072 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/linux_defs.h +@@ -0,0 +1,162 @@ ++#ifndef _DWC_LINUX_DEFS_H_ ++#define _DWC_LINUX_DEFS_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @file ++ * ++ * This file contains OS-specific includes and definitions. ++ * ++ */ ++ ++#define DWC_DRIVER_VERSION "2.90b - November 2014" ++#define DWC_DRIVER_DESC "SS USB3 Controller driver" ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/sysfs.h> ++#include <linux/errno.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/ctype.h> ++#include <linux/string.h> ++#include <linux/dma-mapping.h> ++#include <linux/jiffies.h> ++#include <linux/delay.h> ++#include <linux/timer.h> ++#include <linux/kthread.h> ++#include <linux/workqueue.h> ++#include <linux/freezer.h> ++#include <linux/stat.h> ++#include <linux/pci.h> ++ ++#include <linux/version.h> ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++# include <linux/irq.h> ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++# include <linux/usb/ch9.h> ++#else ++# include <linux/usb_ch9.h> ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) ++# include <linux/usb/gadget.h> ++# include <linux/usb/otg.h> ++#else ++# include <linux/usb_gadget.h> ++# include <linux/usb_otg.h> ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++# include <asm/irq.h> ++#endif ++ ++# include <asm/unaligned.h> ++# include <asm/param.h> ++# include <asm/io.h> ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) ++typedef int gfp_t; ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) ++# define IRQF_SHARED SA_SHIRQ ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0) ++# define DWC_BOS_IN_GADGET ++#endif ++ ++/** @name Error Codes */ ++/** @{ */ ++#define DWC_E_INVALID EINVAL ++#define DWC_E_NO_MEMORY ENOMEM ++#define DWC_E_NO_DEVICE ENODEV ++#define DWC_E_NOT_SUPPORTED EOPNOTSUPP ++#define DWC_E_TIMEOUT ETIMEDOUT ++#define DWC_E_BUSY EBUSY ++#define DWC_E_AGAIN EAGAIN ++#define DWC_E_ABORT ECONNABORTED ++#define DWC_E_SHUTDOWN ESHUTDOWN ++#define DWC_E_NO_DATA ENODATA ++#define DWC_E_DISCONNECT ECONNRESET ++#define DWC_E_UNKNOWN EINVAL ++#define DWC_E_NO_STREAM_RES ENOSR ++#define DWC_E_COMMUNICATION ECOMM ++#define DWC_E_OVERFLOW EOVERFLOW ++#define DWC_E_PROTOCOL EPROTO ++#define DWC_E_IN_PROGRESS EINPROGRESS ++#define DWC_E_PIPE EPIPE ++#define DWC_E_IO EIO ++#define DWC_E_NO_SPACE ENOSPC ++#define DWC_E_DOMAIN EDOM ++/** @} */ ++ ++/** Compiler 'packed' attribute for structs */ ++#define UPACKED __attribute__ ((__packed__)) ++ ++/** @{ */ ++/** Type for DMA addresses */ ++typedef dma_addr_t dwc_dma_t; ++#define DWC_DMA_ADDR_INVALID (~(dwc_dma_t)0) ++/** @} */ ++ ++/** ++ * The number of DMA Descriptors (TRBs) to allocate for each endpoint type. ++ * NOTE: The driver currently supports more than 1 TRB for Isoc EPs only. ++ * So the values for Bulk and Intr must be 1. ++ */ ++#define DWC_NUM_BULK_TRBS 1 ++#define DWC_NUM_INTR_TRBS 1 ++#define DWC_NUM_ISOC_TRBS 256 ++ ++/** ++ * These parameters may be specified when loading the module. They define how ++ * the DWC_usb3 controller should be configured. The parameter values are passed ++ * to the CIL initialization routine dwc_usb3_pcd_common_init(). ++ */ ++typedef struct dwc_usb3_core_params { ++ int burst; ++ int newcore; ++ int phy; ++ int wakeup; ++ int pwrctl; ++ int lpmctl; ++ int phyctl; ++ int usb2mode; ++ int hibernate; ++ int hiberdisc; ++ int clkgatingen; ++ int ssdisquirk; ++ int nobos; ++ int loop; ++ int nump; ++ int newcsr; ++ int rxfsz; ++ int txfsz[16]; ++ int txfsz_cnt; ++ int baseline_besl; ++ int deep_besl; ++ int besl; ++ int ebc; ++} dwc_usb3_core_params_t; ++ ++extern dwc_usb3_core_params_t dwc_usb3_module_params; ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_LINUX_DEFS_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/linux_dev.h b/drivers/usb/gadget/udc/hiudc3/linux_dev.h +new file mode 100644 +index 0000000..fb30136 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/linux_dev.h +@@ -0,0 +1,165 @@ ++#ifndef _DWC_LINUX_DEV_H_ ++#define _DWC_LINUX_DEV_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** @file ++ */ ++ ++/** Wrapper function for _handshake(), shows the source line for any failure */ ++#define handshake(_dev_, _ptr_, _mask_, _done_) ({ \ ++ int _retval_ = _handshake(_dev_, _ptr_, _mask_, _done_); \ ++ if (!_retval_) \ ++ dwc_error2(_dev_, "handshake failed in %s():%d\n", \ ++ __func__, __LINE__); \ ++ _retval_; \ ++}) ++ ++/** Takes a usb req pointer and returns the associated pcd req pointer */ ++#define dwc_usb3_get_pcd_req(usbreq) \ ++ container_of((usbreq), dwc_usb3_pcd_req_t, usb_req) ++ ++/** Takes a usb ep pointer and returns the associated pcd ep pointer */ ++#define dwc_usb3_get_pcd_ep(usbep) \ ++ container_of((usbep), dwc_usb3_pcd_ep_t, usb_ep) ++ ++/** @{ */ ++/** ++ * Register read/write. ++ */ ++static inline u32 dwc_rd32(struct dwc_usb3_device *dev, ++ volatile u32 __iomem *adr) ++{ ++ return readl(adr); ++} ++ ++static inline void dwc_wr32(struct dwc_usb3_device *dev, ++ volatile u32 __iomem *adr, u32 val) ++{ ++ writel(val, adr); ++} ++/** @} */ ++ ++/** @{ */ ++/** ++ * Non-sleeping delays. ++ */ ++#define dwc_udelay(dev, us) udelay(us) ++#define dwc_mdelay(dev, ms) mdelay(ms) ++/** @} */ ++ ++/** ++ * Sleeping delay. ++ */ ++#define dwc_msleep(dev, ms) msleep(ms) ++ ++/** ++ * Debugging support - vanishes in non-debug builds. ++ */ ++ ++/** Prefix string for print macros. */ ++#define USB3_DWC "dwc_usb3: " ++#ifdef DEBUG ++//# define dwc_debug(dev, x...) printk(KERN_DEBUG USB3_DWC x ) ++# define dwc_debug(dev, x...) printk(KERN_ERR USB3_DWC x ) ++#else ++# define dwc_debug(dev, x...) printk(KERN_ERR USB3_DWC x ) ++//# define dwc_debug(dev, x...) do {} while (0) ++#endif /* DEBUG */ ++ ++# define dwc_debug0(dev, fmt) dwc_debug(dev, fmt) ++# define dwc_debug1(dev, fmt, a) dwc_debug(dev, fmt, a) ++# define dwc_debug2(dev, fmt, a, b) dwc_debug(dev, fmt, a, b) ++# define dwc_debug3(dev, fmt, a, b, c) dwc_debug(dev, fmt, a, b, c) ++# define dwc_debug4(dev, fmt, a, b, c, d) dwc_debug(dev, fmt, a, b, c, d) ++# define dwc_debug5(dev, fmt, a, b, c, d, e) \ ++ dwc_debug(dev, fmt, a, b, c, d, e) ++# define dwc_debug6(dev, fmt, a, b, c, d, e, f) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f) ++# define dwc_debug7(dev, fmt, a, b, c, d, e, f, g) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f, g) ++# define dwc_debug8(dev, fmt, a, b, c, d, e, f, g, h) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f, g, h) ++# define dwc_debug9(dev, fmt, a, b, c, d, e, f, g, h, i) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f, g, h, i) ++# define dwc_debug10(dev, fmt, a, b, c, d, e, f, g, h, i, j) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f, g, h, i, j) ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++# define dwc_isocdbg(dev, x...) printk(USB3_DWC x ) ++#else ++# define dwc_isocdbg(dev, x...) do {} while (0) ++#endif ++ ++# define dwc_isocdbg0(dev, fmt) dwc_isocdbg(dev, fmt) ++# define dwc_isocdbg1(dev, fmt, a) dwc_isocdbg(dev, fmt, a) ++# define dwc_isocdbg2(dev, fmt, a, b) dwc_isocdbg(dev, fmt, a, b) ++# define dwc_isocdbg3(dev, fmt, a, b, c) dwc_isocdbg(dev, fmt, a, b, c) ++# define dwc_isocdbg4(dev, fmt, a, b, c, d) \ ++ dwc_isocdbg(dev, fmt, a, b, c, d) ++# define dwc_isocdbg5(dev, fmt, a, b, c, d, e) \ ++ dwc_isocdbg(dev, fmt, a, b, c, d, e) ++# define dwc_isocdbg6(dev, fmt, a, b, c, d, e, f) \ ++ dwc_isocdbg(dev, fmt, a, b, c, d, e, f) ++ ++/** ++ * Print an Error message. ++ */ ++#define dwc_error(dev, x...) printk(KERN_ERR USB3_DWC x ) ++ ++#define dwc_error0(dev, fmt) dwc_error(dev, fmt) ++#define dwc_error1(dev, fmt, a) dwc_error(dev, fmt, a) ++#define dwc_error2(dev, fmt, a, b) dwc_error(dev, fmt, a, b) ++#define dwc_error3(dev, fmt, a, b, c) dwc_error(dev, fmt, a, b, c) ++#define dwc_error4(dev, fmt, a, b, c, d) dwc_error(dev, fmt, a, b, c, d) ++ ++/** ++ * Print a Warning message. ++ */ ++#define dwc_warn(dev, x...) printk(KERN_WARNING USB3_DWC x ) ++ ++#define dwc_warn0(dev, fmt) dwc_warn(dev, fmt) ++#define dwc_warn1(dev, fmt, a) dwc_warn(dev, fmt, a) ++#define dwc_warn2(dev, fmt, a, b) dwc_warn(dev, fmt, a, b) ++#define dwc_warn3(dev, fmt, a, b, c) dwc_warn(dev, fmt, a, b, c) ++#define dwc_warn4(dev, fmt, a, b, c, d) dwc_warn(dev, fmt, a, b, c, d) ++ ++/** ++ * Print an Informational message (normal but significant). ++ */ ++#define dwc_info(dev, x...) printk(KERN_INFO USB3_DWC x ) ++ ++#define dwc_info0(dev, fmt) dwc_info(dev, fmt) ++#define dwc_info1(dev, fmt, a) dwc_info(dev, fmt, a) ++#define dwc_info2(dev, fmt, a, b) dwc_info(dev, fmt, a, b) ++#define dwc_info3(dev, fmt, a, b, c) dwc_info(dev, fmt, a, b, c) ++#define dwc_info4(dev, fmt, a, b, c, d) dwc_info(dev, fmt, a, b, c, d) ++ ++/** ++ * Basic message printing. ++ */ ++#define dwc_print(dev, x...) 0 ++//#define dwc_print(dev, x...) printk(USB3_DWC x ) ++ ++#define dwc_print0(dev, fmt) dwc_print(dev, fmt) ++#define dwc_print1(dev, fmt, a) dwc_print(dev, fmt, a) ++#define dwc_print2(dev, fmt, a, b) dwc_print(dev, fmt, a, b) ++#define dwc_print3(dev, fmt, a, b, c) dwc_print(dev, fmt, a, b, c) ++#define dwc_print4(dev, fmt, a, b, c, d) dwc_print(dev, fmt, a, b, c, d) ++#define dwc_print5(dev, fmt, a, b, c, d, e) \ ++ dwc_print(dev, fmt, a, b, c, d, e) ++ ++extern int dwc_usb3_gadget_init(struct dwc_usb3_device *usb3_dev, struct device *dev); ++extern void dwc_usb3_gadget_remove(struct dwc_usb3_device *usb3_dev, struct device *dev); ++extern int dwc_usb3_create_dev_files(struct device *dev); ++extern void dwc_usb3_remove_dev_files(struct device *dev); ++extern int dwc_usb3_wakeup(struct usb_gadget *gadget); ++extern int dwc_wait_pme_thread(void *data); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_LINUX_DEV_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/linux_gadget.c b/drivers/usb/gadget/udc/hiudc3/linux_gadget.c +new file mode 100644 +index 0000000..d6e2656 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/linux_gadget.c +@@ -0,0 +1,2693 @@ ++/** @file ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++#ifdef DWC_UTE ++# include "ute_if.h" ++#endif ++ ++#ifdef CONFIG_USB_OTG_DWC ++extern void dwc_otg3_set_peripheral(struct usb_otg *otg, int yes); ++extern int otg_host_release(struct usb_otg *otg); ++extern int otg_start_rsp(struct usb_otg *otg); ++#endif ++ ++#define dwc_wait_if_hibernating(_pcd_, _flags_) \ ++{ \ ++ int _temp_ = (_pcd_)->usb3_dev->hibernate; \ ++ while (_temp_ >= DWC_HIBER_SLEEPING && \ ++ _temp_ != DWC_HIBER_WAIT_U0 && \ ++ _temp_ != DWC_HIBER_SS_DIS_QUIRK) { \ ++ spin_unlock_irqrestore(&(_pcd_)->lock, (_flags_)); \ ++ msleep(1); \ ++ spin_lock_irqsave(&(_pcd_)->lock, (_flags_)); \ ++ _temp_ = (_pcd_)->usb3_dev->hibernate; \ ++ } \ ++} ++ ++/** ++ * NOTE: These strings MUST reflect the number and type of endpoints that the ++ * core was configured with in CoreConsultant, and their intended usage. In ++ * particular, the type (bulk/int/iso) determines how many TRBs will be ++ * allocated for each endpoint. See gadget_init_eps() and trb_alloc() below. ++ * ++ * Naming convention is described in drivers/usb/gadget/epautoconf.c ++ */ ++#if 0 ++static const char *g_ep_names[] = { "ep0", "ep1out", "ep1in", "ep2out", ++ "ep2in", "ep3out", "ep3in", "ep4out", "ep4in", "ep5out", "ep5in", ++ "ep6out", "ep6in", "ep7out", "ep7in", "ep8out", "ep8in", "ep9out", ++ "ep9in", "ep10out", "ep10in", "ep11out", "ep11in", "ep12out", "ep12in", ++ "ep13out", "ep13in", "ep14out", "ep14in", "ep15out", "ep15in" }; ++#endif ++#if 0 ++static const char *g_ep_names[] = { "ep0", "ep1out", "ep1in", "ep2out", ++ "ep3in", "ep2in", "ep4in", "ep4out", "ep5out", "ep5in", ++ "ep6out", "ep6in", "ep7out", "ep7in", "ep8out", "ep8in", "ep9out", ++ "ep9in", "ep10out", "ep10in", "ep11out", "ep11in", "ep12out", "ep12in", ++ "ep13out", "ep13in", "ep14out", "ep14in", "ep15out", "ep15in" }; ++#endif ++static const char *g_ep_names[] = { "ep0", "ep1out", "ep1in", "ep2out", ++ "ep2in", "ep3in", "ep4in", "ep4out", "ep4in", "ep5out", "ep5in", ++ "ep6out", "ep6in", "ep7out", "ep7in", "ep8out", "ep8in", "ep9out", ++ "ep9in", "ep10out", "ep10in", "ep11out", "ep11in", "ep12out", "ep12in", ++ "ep13out", "ep13in", "ep14out", "ep14in", "ep15out", "ep15in" }; ++/*========================================================================*/ ++/* ++ * Linux Gadget API related functions ++ */ ++ ++/* Gadget wrapper */ ++ ++static struct gadget_wrapper { ++ dwc_usb3_pcd_t *pcd; ++ ++ char ep_names[DWC_MAX_EPS * 2 - 1][16]; ++ ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ ++ dwc_usb3_pcd_req_t ep0_req; ++ ++ dwc_usb3_pcd_ep_t ep0; ++ dwc_usb3_pcd_ep_t out_ep[DWC_MAX_EPS - 1]; ++ dwc_usb3_pcd_ep_t in_ep[DWC_MAX_EPS - 1]; ++} *gadget_wrapper; ++ ++ ++/** ++ * Passes a Setup request to the Gadget driver ++ */ ++static int gadget_setup(dwc_usb3_pcd_t *pcd, void *data) ++{ ++ int retval = -DWC_E_NOT_SUPPORTED; ++ ++ dwc_debug(pcd->usb3_dev, "%s()\n", __func__); ++ ++ if (!gadget_wrapper) { ++ dwc_warn(pcd->usb3_dev, "%s null wrapper!\n", __func__); ++ return -DWC_E_INVALID; ++ } ++ ++ if (gadget_wrapper->driver && gadget_wrapper->driver->setup) { ++ spin_unlock(&pcd->lock); ++ retval = gadget_wrapper->driver->setup(&gadget_wrapper->gadget, ++ (struct usb_ctrlrequest *)data); ++ spin_lock(&pcd->lock); ++ if (retval == -ENOTSUPP) ++ retval = -DWC_E_NOT_SUPPORTED; ++ else if (retval < 0) ++ retval = -DWC_E_INVALID; ++ } ++ ++ return retval; ++} ++ ++/***************************************************************************** ++ * Gadget notification functions ++ * ++ * These functions receive notifications from the PCD when certain events ++ * occur which the Gadget must be aware of. ++ * ++ * These functions *must* have the exact names and parameters shown here, ++ * since they are part of the API between the PCD and the Gadget. ++ * ++ *****************************************************************************/ ++ ++/** ++ * This function receives Connect notifications from the PCD ++ */ ++int dwc_usb3_gadget_connect(dwc_usb3_pcd_t *pcd, int speed) ++{ ++ dwc_debug(pcd->usb3_dev, "%s()\n", __func__); ++ ++ if (!gadget_wrapper) { ++ dwc_warn(pcd->usb3_dev, "%s null wrapper!\n", __func__); ++ return -DWC_E_INVALID; ++ } ++ ++ gadget_wrapper->gadget.speed = speed; ++ pcd->ep0->usb_ep.maxpacket = pcd->ep0->dwc_ep.maxpacket; ++ ++ return 0; ++} ++ ++/** ++ * This function receives Disconnect notifications from the PCD ++ */ ++int dwc_usb3_gadget_disconnect(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_debug(pcd->usb3_dev, "%s()\n", __func__); ++ ++ if (!gadget_wrapper) { ++ dwc_warn(pcd->usb3_dev, "%s null wrapper!\n", __func__); ++ return -DWC_E_INVALID; ++ } ++ ++ if (gadget_wrapper->driver && gadget_wrapper->driver->disconnect) { ++ spin_unlock(&pcd->lock); ++ gadget_wrapper->driver->disconnect(&gadget_wrapper->gadget); ++ spin_lock(&pcd->lock); ++ } ++ ++ return 0; ++} ++ ++/** ++ * This function receives Setup request notifications from the PCD ++ */ ++int dwc_usb3_gadget_setup(dwc_usb3_pcd_t *pcd, usb_device_request_t *ctrl) ++{ ++ int ret; ++ ++ dwc_debug(pcd->usb3_dev, "%s()\n", __func__); ++ ++ ret = gadget_setup(pcd, ctrl); ++ ++ /* @todo This is a g_file_storage gadget driver specific ++ * workaround: a DELAYED_STATUS result from the fsg_setup ++ * routine will result in the gadget queueing a EP0 IN status ++ * phase for a two-stage control transfer. Exactly the same as ++ * a SET_CONFIGURATION/SET_INTERFACE except that this is a class ++ * specific request. Need a generic way to know when the gadget ++ * driver will queue the status phase. Can we assume when we ++ * call the gadget driver setup() function that it will always ++ * queue and require the following flag? Need to look into ++ * this. ++ */ ++ if (ret >= 256 + 999) ++ pcd->request_config = 1; ++ ++ return ret; ++} ++ ++/** ++ * This function receives Suspend notifications from the PCD ++ */ ++int dwc_usb3_gadget_suspend(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_debug(pcd->usb3_dev, "%s()\n", __func__); ++ ++ if (!gadget_wrapper) { ++ dwc_warn(pcd->usb3_dev, "%s null wrapper!\n", __func__); ++ return -DWC_E_INVALID; ++ } ++ ++ if (gadget_wrapper->driver && gadget_wrapper->driver->suspend) { ++ spin_unlock(&pcd->lock); ++ gadget_wrapper->driver->suspend(&gadget_wrapper->gadget); ++ spin_lock(&pcd->lock); ++ } ++ ++ return 0; ++} ++ ++/** ++ * This function receives Resume notifications from the PCD ++ */ ++int dwc_usb3_gadget_resume(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_debug(pcd->usb3_dev, "%s()\n", __func__); ++ ++ if (!gadget_wrapper) { ++ dwc_warn(pcd->usb3_dev, "%s null wrapper!\n", __func__); ++ return -DWC_E_INVALID; ++ } ++ ++ if (gadget_wrapper->driver && gadget_wrapper->driver->resume) { ++ spin_unlock(&pcd->lock); ++ gadget_wrapper->driver->resume(&gadget_wrapper->gadget); ++ spin_lock(&pcd->lock); ++ } ++ ++ return 0; ++} ++ ++static void map_buffers(struct device *dev, struct usb_request *usb_req, ++ dwc_usb3_pcd_ep_t *pcd_ep, int *req_flags) ++{ ++ if (dwc_usb3_pcd_ep_is_in(pcd_ep)) { ++# ifdef DWC_TEST_ISOC_CHAIN ++ if (dwc_usb3_pcd_ep_type(pcd_ep) == UE_ISOCHRONOUS) { ++ if (usb_req->length > 58 && usb_req->buf2[0]) { ++ usb_req->dma2[0] = dma_map_single(dev, ++ usb_req->buf2[0], 29, DMA_TO_DEVICE); ++ } ++ if (usb_req->length > 58) { ++ usb_req->dma = dma_map_single(dev, usb_req->buf, ++ usb_req->length - 58, DMA_TO_DEVICE); ++ } else { ++ usb_req->dma = dma_map_single(dev, usb_req->buf, ++ usb_req->length, DMA_TO_DEVICE); ++ } ++ if (usb_req->length > 58 && usb_req->buf2[1]) { ++ usb_req->dma2[1] = dma_map_single(dev, ++ usb_req->buf2[1], 29, DMA_TO_DEVICE); ++ } ++ } else ++# endif ++ usb_req->dma = dma_map_single(dev, usb_req->buf, ++ usb_req->length, DMA_TO_DEVICE); ++ *req_flags |= DWC_PCD_REQ_MAP_DMA | DWC_PCD_REQ_IN; ++ } else { ++# ifdef DWC_TEST_ISOC_CHAIN ++ if (dwc_usb3_pcd_ep_type(pcd_ep) == UE_ISOCHRONOUS) { ++ if (usb_req->length > 58 && usb_req->buf2[0]) { ++ usb_req->dma2[0] = dma_map_single(dev, ++ usb_req->buf2[0], 29, DMA_FROM_DEVICE); ++ } ++ if (usb_req->length > 58) { ++ usb_req->dma = dma_map_single(dev, usb_req->buf, ++ usb_req->length - 58, DMA_FROM_DEVICE); ++ } else { ++ usb_req->dma = dma_map_single(dev, usb_req->buf, ++ usb_req->length, DMA_FROM_DEVICE); ++ } ++ if (usb_req->length > 58 && usb_req->buf2[1]) { ++ usb_req->dma2[1] = dma_map_single(dev, ++ usb_req->buf2[1], 29, DMA_FROM_DEVICE); ++ } ++ } else ++# endif ++ usb_req->dma = dma_map_single(dev, usb_req->buf, ++ usb_req->length, DMA_FROM_DEVICE); ++ *req_flags |= DWC_PCD_REQ_MAP_DMA; ++ } ++} ++ ++static void unmap_buffers(struct device *dev, struct usb_request *usb_req, ++ dwc_usb3_pcd_ep_t *pcd_ep, int *req_flags) ++{ ++ if (*req_flags & DWC_PCD_REQ_IN) { ++# ifdef DWC_TEST_ISOC_CHAIN ++ if (dwc_usb3_pcd_ep_type(pcd_ep) == UE_ISOCHRONOUS) { ++ if (usb_req->dma2[1] != DWC_DMA_ADDR_INVALID) { ++ dma_unmap_single(dev, usb_req->dma2[1], 29, ++ DMA_TO_DEVICE); ++ } ++ if (usb_req->dma2[1] != DWC_DMA_ADDR_INVALID || ++ usb_req->dma2[0] != DWC_DMA_ADDR_INVALID) { ++ dma_unmap_single(dev, usb_req->dma, ++ usb_req->length - 58, DMA_TO_DEVICE); ++ } else { ++ dma_unmap_single(dev, usb_req->dma, ++ usb_req->length, DMA_TO_DEVICE); ++ } ++ if (usb_req->dma2[0] != DWC_DMA_ADDR_INVALID) { ++ dma_unmap_single(dev, usb_req->dma2[0], 29, ++ DMA_TO_DEVICE); ++ } ++ } else ++# endif ++ dma_unmap_single(dev, usb_req->dma, usb_req->length, ++ DMA_TO_DEVICE); ++ } else { ++# ifdef DWC_TEST_ISOC_CHAIN ++ if (dwc_usb3_pcd_ep_type(pcd_ep) == UE_ISOCHRONOUS) { ++ if (usb_req->dma2[1] != DWC_DMA_ADDR_INVALID) { ++ dma_unmap_single(dev, usb_req->dma2[1], 29, ++ DMA_FROM_DEVICE); ++ } ++ if (usb_req->dma2[1] != DWC_DMA_ADDR_INVALID || ++ usb_req->dma2[0] != DWC_DMA_ADDR_INVALID) { ++ dma_unmap_single(dev, usb_req->dma, ++ usb_req->length - 58, DMA_FROM_DEVICE); ++ } else { ++ dma_unmap_single(dev, usb_req->dma, ++ usb_req->length, DMA_FROM_DEVICE); ++ } ++ if (usb_req->dma2[0] != DWC_DMA_ADDR_INVALID) { ++ dma_unmap_single(dev, usb_req->dma2[0], 29, ++ DMA_FROM_DEVICE); ++ } ++ } else ++# endif ++ dma_unmap_single(dev, usb_req->dma, usb_req->length, ++ DMA_FROM_DEVICE); ++ } ++ ++ *req_flags &= ~(DWC_PCD_REQ_MAP_DMA | DWC_PCD_REQ_IN); ++ ++# ifdef DWC_TEST_ISOC_CHAIN ++ usb_req->dma2[0] = DWC_DMA_ADDR_INVALID; ++ usb_req->dma2[1] = DWC_DMA_ADDR_INVALID; ++# endif ++ usb_req->dma = DWC_DMA_ADDR_INVALID; ++} ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++static void dbg_databuf(dwc_usb3_pcd_t *pcd, struct usb_request *usb_req, ++ dwc_usb3_pcd_ep_t *pcd_ep, u32 actual, ++ dma_addr_t dma ++#ifdef DWC_TEST_ISOC_CHAIN ++ , dma_addr_t dma2[2] ++#endif ++ ) ++{ ++ if (dwc_usb3_pcd_ep_type(pcd_ep) == UE_ISOCHRONOUS) { ++ unsigned char *buf = (unsigned char *)usb_req->buf; ++# ifdef DWC_TEST_ISOC_CHAIN ++ unsigned char *buf0 = (unsigned char *)usb_req->buf2[0]; ++ unsigned char *buf1 = (unsigned char *)usb_req->buf2[1]; ++# endif ++ if (buf) { ++ if (actual >= 4) { ++# ifdef DWC_TEST_ISOC_CHAIN ++ dwc_isocdbg(pcd->usb3_dev, ++ "%p %1lx %1lx %1lx bdata: %02x %02x %02x " ++ "%02x .. %02x %02x %02x %02x .. %02x " ++ "%02x %02x %02x\n", ++ buf, ++ (unsigned long)dma, ++ (unsigned long)dma2[0], ++ (unsigned long)dma2[1], ++ buf[0], buf[1], buf[2], buf[3], ++ buf0[0], buf0[1], buf0[2], buf0[3], ++ buf1[0], buf1[1], buf1[2], buf1[3]); ++# else ++ dwc_isocdbg(pcd->usb3_dev, ++ "%p %1lx bdata: %02x %02x %02x %02x\n", ++ buf, ++ (unsigned long)dma, ++ buf[0], buf[1], buf[2], buf[3]); ++# endif ++ } else if (actual > 0) { ++ dwc_isocdbg(pcd->usb3_dev, ++ "%p %1lx bdata: %02x ...\n", ++ buf, ++ (unsigned long)dma, buf[0]); ++ } else { ++ dwc_isocdbg(pcd->usb3_dev, ++ "%p %1lx bdata: 0-len!\n", ++ buf, ++ (unsigned long)dma); ++ } ++ } else { ++ dwc_isocdbg(pcd->usb3_dev, "bdata: buf NULL!\n"); ++ } ++ } ++} ++#endif ++ ++/** ++ * This function receives Transfer Complete notifications from the PCD ++ */ ++int dwc_usb3_gadget_complete(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep, ++ dwc_usb3_pcd_req_t *pcd_req, int status) ++{ ++ struct usb_ep *usb_ep = &pcd_ep->usb_ep; ++ struct usb_request *usb_req = &pcd_req->usb_req; ++ u32 actual = pcd_req->dwc_req.actual; ++ int *req_flags = &pcd_req->dwc_req.flags; ++ dma_addr_t dma; ++#ifdef DWC_TEST_ISOC_CHAIN ++ dma_addr_t dma2[2]; ++#endif ++ ++ /* Remove the request from the queue */ ++ list_del_init(&pcd_req->entry); ++ ++ /* Save DMA address for debug */ ++ dma = usb_req->dma; ++#ifdef DWC_TEST_ISOC_CHAIN ++ dma2[0] = usb_req->dma2[0]; ++ dma2[1] = usb_req->dma2[1]; ++#endif ++ ++ /* Unmap DMA */ ++ if (*req_flags & DWC_PCD_REQ_MAP_DMA) { ++ dwc_debug(pcd->usb3_dev, "DMA unmap req %p\n", usb_req); ++ unmap_buffers(pcd->usb3_dev->dev, usb_req, pcd_ep, req_flags); ++ } ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ dbg_databuf(pcd, usb_req, pcd_ep, actual, dma ++# ifdef DWC_TEST_ISOC_CHAIN ++ , dma2 ++# endif ++ ); ++#endif ++ ++ if (usb_req->complete) { ++ usb_req->status = status; ++ usb_req->actual = actual; ++ spin_unlock(&pcd->lock); ++ usb_req->complete(usb_ep, usb_req); ++ spin_lock(&pcd->lock); ++ } ++ ++ if (pcd->request_pending > 0) ++ --pcd->request_pending; ++ ++ return 0; ++} ++ ++/** ++ * This function allows overriding the standard Control transfer handling ++ * (currently only done by the axs101 test gadget) ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++void dwc_usb3_gadget_do_setup(dwc_usb3_pcd_t *pcd) ++{ ++ /* Call the standard Control transfer handler */ ++ dwc_usb3_pcd_do_setup(pcd); ++} ++ ++/* ++ * Gadget EP ops ++ */ ++ ++/** ++ * This function fills in an I/O Request for an EP. ++ */ ++static void fill_req(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *req, int numbuf, char **buf, ++ dwc_dma_t *bufdma, u32 *buflen, u32 stream, int req_flags) ++{ ++ int i; ++ ++ dwc_debug(pcd->usb3_dev, "req_flags=0x%1x\n", req_flags); ++ ++ req->dwc_req.length = 0; ++ req->dwc_req.numbuf = numbuf; ++ ++ for (i = 0; i < numbuf; i++) { ++ req->dwc_req.buf[i] = buf[i]; ++ req->dwc_req.bufdma[i] = bufdma[i]; ++ req->dwc_req.buflen[i] = buflen[i]; ++ req->dwc_req.length += buflen[i]; ++ } ++ ++ req->dwc_req.actual = 0; ++ req->dwc_req.stream = stream; ++ req->dwc_req.flags = req_flags; ++ ++ /* For EP0 IN without premature status, zlp is required? */ ++ if (ep == pcd->ep0 && ep->dwc_ep.is_in) { ++ dwc_debug(pcd->usb3_dev, "%d-OUT ZLP\n", ep->dwc_ep.num); ++ //req->dwc_req.flags |= DWC_PCD_REQ_ZERO; ++ } ++} ++ ++/** ++ * This function enables an EP ++ */ ++static int ep_enable(struct usb_ep *usb_ep, ++ const struct usb_endpoint_descriptor *ep_desc) ++{ ++ dwc_usb3_pcd_ep_t *pcd_ep; ++ dwc_usb3_pcd_t *pcd; ++ dwc_usb3_dma_desc_t *desc; ++ dma_addr_t desc_dma; ++ int num, dir, type, num_trbs, maxpacket, link; ++ int retval = 0; ++ unsigned long flags; ++ ss_endpoint_companion_descriptor_t ep_comp = { 0, }; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0) ++ int maxstreams = 0, mult = 0, maxburst = 0; ++#endif ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(KERN_DEBUG USB3_DWC "%s(%p,%p)\n", __func__, usb_ep, ep_desc); ++#endif ++ if (!gadget_wrapper) { ++ printk(KERN_WARNING USB3_DWC "%s null wrapper!\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!usb_ep || !ep_desc || ++ ep_desc->bDescriptorType != USB_DT_ENDPOINT) { ++ printk(KERN_WARNING USB3_DWC ++ "%s, bad ep or descriptor %p %p %d!\n", ++ __func__, usb_ep, ep_desc, ++ ep_desc ? ep_desc->bDescriptorType : 0); ++ return -EINVAL; ++ } ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(USB3_DWC "usb_ep->name = %s\n", usb_ep->name); ++#endif ++ if (usb_ep == &gadget_wrapper->ep0.usb_ep) { ++ printk(KERN_WARNING USB3_DWC "%s called for EP0!\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!ep_desc->wMaxPacketSize) { ++ printk(KERN_WARNING USB3_DWC "%s, zero %s wMaxPacketSize!\n", ++ __func__, usb_ep->name); ++ return -ERANGE; ++ } ++ ++ if (!gadget_wrapper->driver || ++ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { ++ printk(KERN_WARNING USB3_DWC "%s, bogus device state!\n", ++ __func__); ++ return -ESHUTDOWN; ++ } ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(KERN_DEBUG USB3_DWC "usb_ep->maxpacket = %d\n", ++ le16_to_cpu(ep_desc->wMaxPacketSize)); ++#endif ++ pcd = gadget_wrapper->pcd; ++ pcd_ep = dwc_usb3_get_pcd_ep(usb_ep); ++ ++ if (pcd_ep->dwc_ep.phys >= DWC_MAX_PHYS_EP) { ++ dwc_warn(pcd->usb3_dev, "%s, bad %s phys EP # %d!\n", ++ __func__, usb_ep->name, pcd_ep->dwc_ep.phys); ++ return -ERANGE; ++ } ++ ++ /* Free any existing TRB allocation for this EP */ ++ dwc_usb3_pcd_trb_free(pcd_ep); ++ ++ num = UE_GET_ADDR(ep_desc->bEndpointAddress); ++ dir = UE_GET_DIR(ep_desc->bEndpointAddress); ++ type = UE_GET_XFERTYPE(ep_desc->bmAttributes); ++ link = 0; ++ ++ /* Allocate the number of TRBs based on EP type */ ++ switch (type) { ++ case UE_INTERRUPT: ++ num_trbs = DWC_NUM_INTR_TRBS; ++ break; ++ case UE_ISOCHRONOUS: ++ num_trbs = DWC_NUM_ISOC_TRBS; ++ link = 1; ++ break; ++ default: ++ num_trbs = DWC_NUM_BULK_TRBS; ++ break; ++ } ++ ++ dwc_debug(pcd->usb3_dev, "ep%d-%s=%p phys=%d pcd_ep=%p\n", ++ num, dir == UE_DIR_IN ? "IN" : "OUT", usb_ep, ++ pcd_ep->dwc_ep.phys, pcd_ep); ++ ++ /* Set the TRB allocation for this EP */ ++ desc = dwc_usb3_pcd_trb_alloc(pcd_ep, num_trbs, type, ++ ep_desc->bInterval - 1, link, ++ &desc_dma); ++ if (!desc) ++ return -ENOMEM; ++ ++ maxpacket = le16_to_cpu(ep_desc->wMaxPacketSize); ++ ++ spin_lock_irqsave(&pcd->lock, flags); ++ dwc_wait_if_hibernating(pcd, flags); ++ pcd->usb3_dev->hiber_cnt++; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0) ++ if (gadget_wrapper->gadget.speed == USB_SPEED_SUPER) { ++ if (usb_ep->comp_desc) { ++ if (type == UE_ISOCHRONOUS) ++ mult = usb_ep->comp_desc->bmAttributes; ++ maxburst = usb_ep->comp_desc->bMaxBurst; ++ if (type == UE_BULK) ++ maxstreams = usb_ep->comp_desc->bmAttributes; ++ } ++ } else { ++ if (type == UE_ISOCHRONOUS) ++ mult = maxpacket >> 11 & 3; ++ } ++ ++ if (mult > 2 || maxburst > 15 || maxstreams > 16) { ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ dwc_usb3_pcd_trb_free(pcd_ep); ++ return -EINVAL; ++ } ++ ++ ep_comp.bmAttributes = USSE_SET_MAX_STREAMS(0, maxstreams); ++ ep_comp.bmAttributes = USSE_SET_MAX_PACKET_NUM(ep_comp.bmAttributes, ++ mult); ++ ep_comp.bMaxBurst = maxburst; ++#else ++ if (type == UE_BULK) ++ ep_comp.bmAttributes = USSE_SET_MAX_STREAMS(0, ++ usb_ep->numstreams); ++ if (type == UE_ISOCHRONOUS) ++ ep_comp.bmAttributes = USSE_SET_MAX_PACKET_NUM(0, usb_ep->mult); ++ ++ ep_comp.bMaxBurst = usb_ep->maxburst; ++#endif ++ ++ retval = dwc_usb3_pcd_ep_enable(pcd, pcd_ep, ++ (usb_endpoint_descriptor_t *)ep_desc, &ep_comp); ++ if (!retval) ++ usb_ep->maxpacket = maxpacket; ++ ++ if (gadget_wrapper->gadget.speed == USB_SPEED_HIGH) { ++ if (usb_ep->maxpacket == 5120) ++ usb_ep->maxpacket = 3072; ++ else if (usb_ep->maxpacket == 3072) ++ usb_ep->maxpacket = 2048; ++ } ++ ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ ++ if (retval) { ++ dwc_usb3_pcd_trb_free(pcd_ep); ++ retval = -ENOMEM; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function disables an EP ++ */ ++static int ep_disable(struct usb_ep *usb_ep) ++{ ++ int retval = 0; ++ dwc_usb3_pcd_ep_t *pcd_ep; ++ dwc_usb3_pcd_t *pcd; ++ unsigned long flags; ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(KERN_DEBUG USB3_DWC "%s()\n", __func__); ++#endif ++ if (!gadget_wrapper) { ++ printk(KERN_WARNING USB3_DWC "%s null wrapper!\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!usb_ep) { ++ printk(KERN_WARNING USB3_DWC "EP not enabled!\n"); ++ return -EINVAL; ++ } ++ ++ pcd = gadget_wrapper->pcd; ++ ++ spin_lock_irqsave(&pcd->lock, flags); ++ dwc_wait_if_hibernating(pcd, flags); ++ pcd->usb3_dev->hiber_cnt++; ++ ++ pcd_ep = dwc_usb3_get_pcd_ep(usb_ep); ++ retval = dwc_usb3_pcd_ep_disable(pcd, pcd_ep); ++ ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ ++ /* Free any existing TRB allocation for this EP */ ++ dwc_usb3_pcd_trb_free(pcd_ep); ++ ++ return retval ? -EINVAL : 0; ++} ++ ++/** ++ * This function allocates a request object to use with the specified USB EP. ++ * ++ * @param usb_ep The USB EP to be used with with the request. ++ * @param gfp_flags The GFP_* flags to use. ++ */ ++static struct usb_request *alloc_request(struct usb_ep *usb_ep, gfp_t gfp_flags) ++{ ++ dwc_usb3_pcd_req_t *pcd_req; ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(KERN_DEBUG USB3_DWC "%s(%p,%d)\n", __func__, usb_ep, gfp_flags); ++#endif ++ if (!usb_ep) { ++ printk(USB3_DWC "%s() %s\n", __func__, "Invalid EP!\n"); ++ return NULL; ++ } ++ ++ pcd_req = kmalloc(sizeof(dwc_usb3_pcd_req_t), gfp_flags); ++ if (!pcd_req) { ++ printk(USB3_DWC "%s() pcd request allocation failed!\n", ++ __func__); ++ return NULL; ++ } ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ //printk(USB3_DWC "%s() allocated %p\n", __func__, pcd_req); ++#endif ++ memset(pcd_req, 0, sizeof(dwc_usb3_pcd_req_t)); ++ pcd_req->usb_req.dma = DWC_DMA_ADDR_INVALID; ++ ++#ifdef DWC_TEST_ISOC_CHAIN ++ pcd_req->usb_req.dma2[0] = DWC_DMA_ADDR_INVALID; ++ pcd_req->usb_req.dma2[1] = DWC_DMA_ADDR_INVALID; ++#endif ++ return &pcd_req->usb_req; ++} ++ ++/** ++ * This function frees a request object. ++ * ++ * @param usb_ep The USB EP associated with the request. ++ * @param usb_req The request being freed. ++ */ ++static void free_request(struct usb_ep *usb_ep, struct usb_request *usb_req) ++{ ++ dwc_usb3_pcd_req_t *pcd_req; ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(KERN_DEBUG USB3_DWC "%s(%p,%p)\n", __func__, usb_ep, usb_req); ++#endif ++ if (!usb_ep || !usb_req) { ++ printk(KERN_WARNING USB3_DWC "%s() %s\n", __func__, ++ "Invalid ep or req argument!"); ++ return; ++ } ++ ++ pcd_req = dwc_usb3_get_pcd_req(usb_req); ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ //printk(USB3_DWC "%s() freed %p\n", __func__, pcd_req); ++#endif ++ kfree(pcd_req); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) ++/** ++ * This function allocates an I/O buffer to be used for a transfer ++ * to/from the specified USB EP. ++ * ++ * @param usb_ep The USB EP to be used with with the request. ++ * @param bytes The desired number of bytes for the buffer. ++ * @param dma Pointer to the buffer's DMA address; must be valid. ++ * @param gfp_flags The GFP_* flags to use. ++ * @return Address of a new buffer or null if buffer could not be ++ * allocated. ++ */ ++static void *alloc_buffer(struct usb_ep *usb_ep, unsigned bytes, ++ dma_addr_t *dma, gfp_t gfp_flags) ++{ ++ gfp_t gfp_flags = GFP_KERNEL | GFP_DMA32; ++ void *buf; ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(KERN_DEBUG USB3_DWC "%s(%p,%d,%p,%0x)\n", __func__, ++ usb_ep, bytes, dma, gfp_flags); ++#endif ++ /* Check dword alignment */ ++ if ((bytes & 0x3) != 0) { ++ printk(KERN_WARNING USB3_DWC ++ "%s() Buffer size is not a multiple of DWORD size (%d)\n", ++ __func__, bytes); ++ } ++ ++ buf = dma_alloc_coherent(NULL, bytes, dma, gfp_flags); ++ if (buf) { ++ /* Check dword alignment */ ++ if (((unsigned long)buf & 0x3) != 0) { ++ printk(KERN_WARNING USB3_DWC ++ "%s() Buffer is not DWORD aligned (%p)\n", ++ __func__, buf); ++ } ++ } ++ ++ return buf; ++} ++ ++/** ++ * This function frees an I/O buffer that was allocated by alloc_buffer. ++ * ++ * @param usb_ep The USB EP associated with the buffer. ++ * @param buf Address of the buffer. ++ * @param dma The buffer's DMA address. ++ * @param bytes The number of bytes of the buffer. ++ */ ++static void free_buffer(struct usb_ep *usb_ep, void *buf, ++ dma_addr_t dma, unsigned bytes) ++{ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(KERN_DEBUG USB3_DWC "%s(%p,%p,%0x,%d)\n", __func__, ++ usb_ep, buf, dma, bytes); ++#endif ++ dma_free_coherent(NULL, bytes, buf, dma); ++} ++#endif ++ ++/** ++ * This function queues a request to a USB EP ++ */ ++static int ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req, ++ gfp_t gfp_flags) ++{ ++ int is_atomic = 0; ++ int retval = 0; ++ int req_flags = 0; ++ dwc_usb3_pcd_ep_t *pcd_ep; ++ dwc_usb3_pcd_req_t *pcd_req; ++ int numbufs; ++ char *bufs[3]; ++ dma_addr_t bufdmas[3]; ++ u32 buflens[3]; ++ dwc_usb3_pcd_t *pcd; ++ unsigned long flags; ++ ++#ifdef DEBUG ++ printk(KERN_DEBUG USB3_DWC "%s(%p,%p,%d)\n", __func__, ++ usb_ep, usb_req, gfp_flags); ++#endif ++ if (!gadget_wrapper) { ++ printk(KERN_WARNING USB3_DWC "%s null wrapper!\n", __func__); ++ return -EINVAL; ++ } ++ ++#ifdef DEBUG ++ printk(KERN_DEBUG USB3_DWC "pcd=%p\n", gadget_wrapper->pcd); ++#endif ++ if (!usb_req || !usb_req->complete || !usb_req->buf) { ++ printk(KERN_WARNING USB3_DWC "%s, bad params\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!usb_ep) { ++ printk(KERN_WARNING USB3_DWC "%s, bad ep\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!gadget_wrapper->driver || ++ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { ++ printk(KERN_DEBUG USB3_DWC "gadget.speed=%d\n", ++ gadget_wrapper->gadget.speed); ++ printk(KERN_WARNING USB3_DWC "%s, bogus device state\n", ++ __func__); ++ return -ESHUTDOWN; ++ } ++ ++ if (!gadget_wrapper->pcd) { ++ printk(KERN_WARNING USB3_DWC ++ "%s, gadget_wrapper->pcd is NULL!\n", __func__); ++ return -EINVAL; ++ } ++ ++ pcd_ep = dwc_usb3_get_pcd_ep(usb_ep); ++ pcd_req = dwc_usb3_get_pcd_req(usb_req); ++ pcd = dwc_usb3_pcd_ep_to_pcd(pcd_ep); ++ ++#ifdef DEBUG ++ printk(KERN_DEBUG USB3_DWC "%s 0x%p queue req 0x%p, len %d buf 0x%p\n", ++ usb_ep->name, pcd_ep, usb_req, usb_req->length, usb_req->buf); ++#endif ++ usb_req->status = -EINPROGRESS; ++ usb_req->actual = 0; ++ ++ if (gfp_flags == GFP_ATOMIC) ++ is_atomic = 1; ++ ++ if (usb_req->zero) ++ req_flags |= DWC_PCD_REQ_ZERO; ++ ++ if (usb_req->length != 0 && usb_req->dma == DWC_DMA_ADDR_INVALID) { ++ dwc_debug(pcd->usb3_dev, "DMA map req %p\n", usb_req); ++ dwc_debug(pcd->usb3_dev, "dev=%p\n", pcd->usb3_dev->dev); ++ map_buffers(pcd->usb3_dev->dev, usb_req, pcd_ep, &req_flags); ++ } ++ ++ bufs[0] = usb_req->buf; ++ bufdmas[0] = usb_req->dma; ++ buflens[0] = usb_req->length; ++ numbufs = 1; ++ ++#ifdef DWC_TEST_ISOC_CHAIN ++ if (dwc_usb3_pcd_ep_type(pcd_ep) == UE_ISOCHRONOUS) { ++ buflens[0] = usb_req->length - 58; ++ ++ bufs[1] = usb_req->buf2[0]; ++ bufdmas[1] = usb_req->dma2[0]; ++ buflens[1] = 29; ++ ++ bufs[2] = usb_req->buf2[1]; ++ bufdmas[2] = usb_req->dma2[1]; ++ buflens[2] = 29; ++ ++ numbufs = 3; ++ } ++#endif ++ if (dwc_usb3_pcd_ep_num(pcd_ep) != 0 && !pcd_ep->dwc_ep.usb_ep_desc) { ++ dwc_debug(pcd->usb3_dev, "%s, bad ep!\n", __func__); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&pcd->lock, flags); ++ dwc_wait_if_hibernating(pcd, flags); ++ pcd->usb3_dev->hiber_cnt++; ++ ++ dwc_debug(pcd->usb3_dev, "%s: EP%d-%s %p stream %d req %p\n", ++ __func__, dwc_usb3_pcd_ep_num(pcd_ep), ++ dwc_usb3_pcd_ep_is_in(pcd_ep) ? "IN" : "OUT", ++ pcd_ep, usb_req->stream_id, pcd_req); ++ ++ dwc_debug(pcd->usb3_dev, "%s(%p,%p,%p,%p,%lx)\n", __func__, pcd, ++ usb_ep, usb_req, bufs[0], (unsigned long)bufdmas[0]); ++ ++ INIT_LIST_HEAD(&pcd_req->entry); ++ ++ fill_req(pcd, pcd_ep, pcd_req, numbufs, bufs, bufdmas, buflens, ++ usb_req->stream_id, req_flags); ++ ++ if (!list_empty(&pcd_ep->dwc_ep.queue)) { ++ /* If queue is not empty, then don't start the transfer, unless ++ * this is an Isoc transfer. But we still queue the request. ++ */ ++ dwc_debug(pcd->usb3_dev, ++ " q not empty %d, stopped %d, avail %d, istart %d\n", ++ pcd->request_pending, pcd_ep->dwc_ep.stopped, ++ pcd_ep->dwc_ep.desc_avail, ++ pcd_ep->dwc_ep.xfer_started); ++ ++ if (!pcd_ep->dwc_ep.stopped && ++ pcd_ep->dwc_ep.type == UE_ISOCHRONOUS && ++ pcd_ep->dwc_ep.desc_avail > 0 && ++ pcd_ep->dwc_ep.xfer_started) ++ goto do_start; ++ ++ } else if (pcd_ep->dwc_ep.stopped || ++ (pcd_ep != pcd->ep0 && pcd_ep->dwc_ep.desc_avail <= 0) || ++ (pcd_ep->dwc_ep.type == UE_ISOCHRONOUS && ++ !pcd_ep->dwc_ep.xfer_started)) { ++ /* If EP is stopped, or there is no TRB available, or this ++ * is an Isoc transfer and the EP is not started, then don't ++ * start the transfer. But we still queue the request. ++ */ ++ dwc_debug(pcd->usb3_dev, ++ " q empty %d, stopped %d, avail %d, istart %d\n", ++ pcd->request_pending, pcd_ep->dwc_ep.stopped, ++ pcd_ep->dwc_ep.desc_avail, pcd_ep->dwc_ep.xfer_started); ++ } else { ++do_start: ++ /* Setup and start the transfer */ ++ dwc_usb3_pcd_fill_trbs(pcd, pcd_ep, pcd_req); ++ retval = dwc_usb3_pcd_ep_submit_req(pcd, pcd_ep, pcd_req, ++ req_flags); ++ } ++ ++ if (!retval) { ++ list_add_tail(&pcd_req->entry, &pcd_ep->dwc_ep.queue); ++ ++pcd->request_pending; ++ } ++ ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ ++ /* dwc_usb3_pcd_ep_submit_req() can return positive values for ++ * Control transfers, don't propagate those from this function ++ */ ++ if (retval < 0) ++ return -EINVAL; ++ return 0; ++} ++ ++/** ++ * This function dequeues a request from a USB EP ++ */ ++static int ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) ++{ ++ dwc_usb3_pcd_req_t *pcd_req = NULL; ++ dwc_usb3_pcd_ep_t *pcd_ep; ++ dwc_usb3_pcd_t *pcd; ++ struct list_head *list_item; ++ unsigned long flags; ++ ++#ifdef DEBUG ++ printk(KERN_DEBUG USB3_DWC "%s(%p,%p)\n", __func__, usb_ep, usb_req); ++#endif ++ if (!gadget_wrapper) { ++ printk(KERN_WARNING USB3_DWC "%s null wrapper!\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!usb_ep || !usb_req) { ++ printk(KERN_WARNING USB3_DWC "%s, bad argument!\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!gadget_wrapper->driver || ++ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { ++ printk(KERN_WARNING USB3_DWC "%s, bogus device state!\n", ++ __func__); ++ return -ESHUTDOWN; ++ } ++ ++ if (!gadget_wrapper->pcd) { ++ printk(KERN_WARNING USB3_DWC ++ "%s, gadget_wrapper->pcd is NULL!\n", __func__); ++ return -EINVAL; ++ } ++ ++ pcd = gadget_wrapper->pcd; ++ ++ spin_lock_irqsave(&pcd->lock, flags); ++ dwc_wait_if_hibernating(pcd, flags); ++ pcd->usb3_dev->hiber_cnt++; ++ ++ pcd_ep = dwc_usb3_get_pcd_ep(usb_ep); ++ ++ if (dwc_usb3_pcd_ep_num(pcd_ep) != 0 && !pcd_ep->dwc_ep.usb_ep_desc) { ++ //dwc_warn(pcd->usb3_dev, "%s, bad pcd_ep!\n", __func__); ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ return -EINVAL; ++ } ++ ++ /* make sure it's actually queued on this EP */ ++ list_for_each (list_item, &pcd_ep->dwc_ep.queue) { ++ pcd_req = list_entry(list_item, dwc_usb3_pcd_req_t, entry); ++ if (&pcd_req->usb_req == usb_req) ++ break; ++ } ++ ++ if (!pcd_req) { ++ dwc_warn(pcd->usb3_dev, "%s, no request in queue!\n", __func__); ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ return 0; ++ } ++ ++ dwc_usb3_pcd_ep_cancel_req(pcd, pcd_ep, pcd_req, usb_req->stream_id); ++ ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ return 0; ++} ++ ++/** ++ * This routine allocates coherent DMA memory. It is used by the PCD to ++ * allocate memory for TRBs. The block of memory returned must have a start ++ * address aligned to a 16-byte boundary. ++ * ++ * @param ep PCD EP that memory block will be associated with. ++ * @param size Size of memory block to allocate, in bytes. ++ * @param mem_dma_ret DMA address of allocated memory block is returned ++ * through this pointer. ++ * @return Address of allocated memory block, or NULL if allocation ++ * fails. ++ */ ++void *dwc_usb3_gadget_alloc_dma(dwc_usb3_pcd_ep_t *ep, int size, ++ dwc_dma_t *mem_dma_ret) ++{ ++ void *mem; ++ ++ mem = dma_alloc_coherent(NULL, size, mem_dma_ret, ++ GFP_ATOMIC | GFP_DMA32); ++ if (!mem) ++ return NULL; ++ memset(mem, 0, size); ++ return mem; ++} ++ ++/** ++ * This routine frees DMA memory allocated by dwc_usb3_gadget_alloc_dma(). ++ * ++ * @param ep PCD EP that memory block is associated with. ++ * @param size Size of memory block to free, in bytes. ++ * @param mem Address of memory block. ++ * @param mem_dma DMA address of memory block. ++ */ ++void dwc_usb3_gadget_free_dma(dwc_usb3_pcd_ep_t *ep, int size, void *mem, ++ dwc_dma_t mem_dma) ++{ ++ dma_free_coherent(NULL, size, mem, mem_dma); ++} ++ ++/** ++ * This routine returns the PCD request corresponding to the current transfer ++ * request for an endpoint. The current transfer request is the first request ++ * submitted that has not been completed yet. ++ */ ++dwc_usb3_pcd_req_t *dwc_usb3_gadget_get_request(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_usb3_pcd_req_t *req; ++ ++ /* Check for a pending request */ ++ if (list_empty(&ep->dwc_ep.queue)) { ++ dwc_debug(pcd->usb3_dev, "%s(%p), ep->dwc_ep.queue empty!\n", ++ __func__, ep); ++ return NULL; ++ } ++ ++ req = list_first_entry(&ep->dwc_ep.queue, dwc_usb3_pcd_req_t, entry); ++ return req; ++} ++ ++/** ++ * This function checks the EP request queue, if the queue is not empty then ++ * the next transfer is started. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ep EP to operate on. ++ */ ++void dwc_usb3_gadget_start_next_request(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_usb3_pcd_req_t *req = NULL; ++ struct list_head *list_item; ++ ++ dwc_debug(pcd->usb3_dev, "%s(%p)\n", __func__, ep); ++ ++ if (list_empty(&ep->dwc_ep.queue)) { ++ dwc_debug(pcd->usb3_dev, "start_next EP%d-%s: queue empty\n", ++ ep->dwc_ep.num, ep->dwc_ep.is_in ? "IN" : "OUT"); ++ return; ++ } ++ ++ list_for_each (list_item, &ep->dwc_ep.queue) { ++ req = list_entry(list_item, dwc_usb3_pcd_req_t, entry); ++ if (!(req->dwc_req.flags & DWC_PCD_REQ_STARTED)) { ++ if (ep->dwc_ep.desc_avail <= 0) { ++ dwc_debug(pcd->usb3_dev, ++ "start_next EP%d-%s: no TRB avail\n", ++ ep->dwc_ep.num, ep->dwc_ep.is_in ? ++ "IN" : "OUT"); ++ return; ++ } ++ ++ dwc_debug(pcd->usb3_dev, "start_next EP%d-%s: OK\n", ++ ep->dwc_ep.num, ++ ep->dwc_ep.is_in ? "IN" : "OUT"); ++ ++ /* Setup and start the transfer */ ++ //dwc_debug(pcd->usb3_dev, ++ // " -> starting xfer (start_next_req) %s %s\n", ++ // ep->dwc_ep.ep.name, ++ // ep->dwc_ep.is_in ? "IN" : "OUT"); ++ dwc_usb3_pcd_fill_trbs(pcd, ep, req); ++ dwc_usb3_pcd_ep_start_transfer(pcd, ep, req, 0); ++ return; ++ } ++ } ++ ++ dwc_debug(pcd->usb3_dev, "start_next EP%d-%s: all req active\n", ++ ep->dwc_ep.num, ep->dwc_ep.is_in ? "IN" : "OUT"); ++} ++ ++/** ++ * This function terminates all the requests in the EP request queue. ++ */ ++void dwc_usb3_gadget_request_nuke(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_usb3_pcd_req_t *req; ++ ++ dwc_debug(pcd->usb3_dev, "%s(%p)\n", __func__, ep); ++ ep->dwc_ep.stopped = 1; ++ ++ /* called with irqs blocked?? */ ++ while (!list_empty(&ep->dwc_ep.queue)) { ++ req = list_first_entry(&ep->dwc_ep.queue, dwc_usb3_pcd_req_t, ++ entry); ++ dwc_usb3_pcd_request_done(pcd, ep, req, -DWC_E_SHUTDOWN); ++ } ++ ++ if (ep != pcd->ep0) { ++ ep->dwc_ep.desc_idx = 0; ++ ep->dwc_ep.hiber_desc_idx = 0; ++ } ++} ++ ++/** ++ * This function marks all requests in an EP queue as not started. ++ */ ++void dwc_usb3_gadget_set_ep_not_started(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_usb3_pcd_req_t *req; ++ struct list_head *list_item; ++ ++ list_for_each (list_item, &ep->dwc_ep.queue) { ++ req = list_entry(list_item, dwc_usb3_pcd_req_t, entry); ++ if (req->dwc_req.flags & DWC_PCD_REQ_STARTED) ++ req->dwc_req.flags &= ~DWC_PCD_REQ_STARTED; ++ if (req->dwc_req.trb) { ++ dwc_usb3_disable_desc(req->dwc_req.trb); ++ (req->dwc_req.trb)->bpth = ep->dwc_ep.desc_avail; ++ ep->dwc_ep.desc_avail++; ++ req->dwc_req.trb = NULL; ++ } ++ ++ } ++} ++ ++/** ++ * Start an Isoc EP running at the proper interval, after receiving the initial ++ * XferNrdy event. ++ */ ++void dwc_usb3_gadget_isoc_ep_start(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ u32 event) ++{ ++ dwc_usb3_pcd_req_t *req = NULL; ++ struct list_head *list_item; ++ ++ dwc_debug(pcd->usb3_dev, "%s(%p,%p,%x)\n", __func__, pcd, ep, event); ++ dwc_isocdbg(pcd->usb3_dev, "%s(%08x)\n", __func__, event); ++ ++ if (list_empty(&ep->dwc_ep.queue)) { ++ dwc_debug(pcd->usb3_dev, "%s(%p), ep->dwc_ep.queue empty!\n", ++ __func__, ep); ++ return; ++ } ++ ++ if (ep->dwc_ep.desc_avail <= 0) { ++ dwc_print(pcd->usb3_dev, "EP%d-%s: no TRB avail!\n", ++ ep->dwc_ep.num, ep->dwc_ep.is_in ? "IN" : "OUT"); ++ return; ++ } ++ ++ /* If need to restart after hibernation, handle that and return */ ++ if (dwc_usb3_pcd_isoc_ep_hiber_restart(pcd, ep)) ++ return; ++ ++ /* ++ * Start the next queued transfer at the target uFrame ++ */ ++ ++ list_for_each (list_item, &ep->dwc_ep.queue) { ++ req = list_entry(list_item, dwc_usb3_pcd_req_t, entry); ++ if (req->dwc_req.flags & DWC_PCD_REQ_STARTED) ++ req = NULL; ++ else ++ break; ++ } ++ ++ dwc_debug(pcd->usb3_dev, "req=%p\n", req); ++ if (!req) { ++ dwc_print(pcd->usb3_dev, "EP%d-%s: no requests to start!\n", ++ ep->dwc_ep.num, ep->dwc_ep.is_in ? "IN" : "OUT"); ++ return; ++ } ++ ++ dwc_usb3_pcd_fill_trbs(pcd, ep, req); ++ dwc_usb3_pcd_ep_start_transfer(pcd, ep, req, event); ++ ++ /* ++ * Now start any remaining queued transfers ++ */ ++ ++ while (!list_is_last(list_item, &ep->dwc_ep.queue)) { ++ list_item = list_item->next; ++ req = list_entry(list_item, dwc_usb3_pcd_req_t, entry); ++ if (!(req->dwc_req.flags & DWC_PCD_REQ_STARTED)) { ++ if (ep->dwc_ep.desc_avail <= 0) { ++ dwc_debug(pcd->usb3_dev, ++ "start_next EP%d-%s: no TRB avail!\n", ++ ep->dwc_ep.num, ep->dwc_ep.is_in ? ++ "IN" : "OUT"); ++ return; ++ } ++ ++ dwc_usb3_pcd_fill_trbs(pcd, ep, req); ++ dwc_usb3_pcd_ep_start_transfer(pcd, ep, req, 0); ++ } ++ } ++} ++ ++/** ++ * This function sets/clears halt on a USB EP ++ */ ++static int ep_set_halt(struct usb_ep *usb_ep, int value) ++{ ++ dwc_usb3_pcd_ep_t *pcd_ep; ++ dwc_usb3_pcd_t *pcd; ++ unsigned long flags; ++ ++ if (!gadget_wrapper) { ++ printk(KERN_WARNING USB3_DWC "%s null wrapper!\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!usb_ep) { ++ printk(KERN_WARNING USB3_DWC "%s, bad usb_ep!\n", __func__); ++ return -EINVAL; ++ } ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(KERN_DEBUG USB3_DWC "HALT %s %d\n", usb_ep->name, value); ++#endif ++ ++ pcd = gadget_wrapper->pcd; ++ ++ spin_lock_irqsave(&pcd->lock, flags); ++ dwc_wait_if_hibernating(pcd, flags); ++ pcd->usb3_dev->hiber_cnt++; ++ ++ pcd_ep = dwc_usb3_get_pcd_ep(usb_ep); ++ dwc_debug(pcd->usb3_dev, "pcd_ep=%p\n", pcd_ep); ++ dwc_debug(pcd->usb3_dev, "pcd->ep0=%p\n", pcd->ep0); ++ dwc_debug(pcd->usb3_dev, "epnum=%d is_in=%d\n", ++ dwc_usb3_pcd_ep_num(pcd_ep), dwc_usb3_pcd_ep_is_in(pcd_ep)); ++ ++ if ((!pcd_ep->dwc_ep.usb_ep_desc && dwc_usb3_pcd_ep_num(pcd_ep) != 0) || ++ (pcd_ep->dwc_ep.usb_ep_desc && dwc_usb3_pcd_ep_type(pcd_ep) ++ == UE_ISOCHRONOUS)) { ++ dwc_warn(pcd->usb3_dev, "%s, bad pcd_ep!\n", __func__); ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ return -EINVAL; ++ } ++ ++ if (!list_empty(&pcd_ep->dwc_ep.queue)) { ++ dwc_warn(pcd->usb3_dev, "%s, Xfer in process!\n", __func__); ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ return -EAGAIN; ++ } ++ ++ dwc_usb3_pcd_ep_set_halt(pcd, pcd_ep, value); ++ ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ return 0; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) ++static int ep_set_wedge(struct usb_ep *usb_ep) ++{ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(KERN_DEBUG USB3_DWC "WEDGE %s\n", usb_ep->name); ++#endif ++ return ep_set_halt(usb_ep, 3); ++} ++#endif ++ ++static struct usb_ep_ops pcd_ep_ops = { ++ .enable = ep_enable, ++ .disable = ep_disable, ++ ++ .alloc_request = alloc_request, ++ .free_request = free_request, ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) ++ .alloc_buffer = alloc_buffer, ++ .free_buffer = free_buffer, ++#endif ++ .queue = ep_queue, ++ .dequeue = ep_dequeue, ++ ++ .set_halt = ep_set_halt, ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) ++ .set_wedge = ep_set_wedge, ++#endif ++ .fifo_status = NULL, ++ .fifo_flush = NULL, ++}; ++ ++ ++/* Gadget ops */ ++ ++#ifdef CONFIG_USB_OTG_DWC ++ ++void dwc_usb3_start_hnp(dwc_usb3_pcd_t *pcd) ++{ ++ struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ struct usb_otg *otg; ++ ++ dwc_debug(pcd->usb3_dev, "%s()\n", __func__); ++ if (IS_ERR(phy) || !phy) { ++ dwc_info(pcd->usb3_dev, "NO PHY!!\n"); ++ if (!IS_ERR(phy)) ++ usb_put_phy(phy); ++ return; ++ } ++ ++ otg = phy->otg; ++ if (!otg) { ++ dwc_info(pcd->usb3_dev, "NO OTG!!\n"); ++ usb_put_phy(phy); ++ return; ++ } ++ ++ otg_start_hnp(otg); ++ usb_put_phy(phy); ++} ++ ++void dwc_usb3_host_release(dwc_usb3_pcd_t *pcd) ++{ ++ struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ struct usb_otg *otg; ++ ++ dwc_debug(pcd->usb3_dev, "%s()\n", __func__); ++ if (IS_ERR(phy) || !phy) { ++ dwc_info(pcd->usb3_dev, "NO PHY!!\n"); ++ if (!IS_ERR(phy)) ++ usb_put_phy(phy); ++ return; ++ } ++ ++ otg = phy->otg; ++ if (!otg) { ++ dwc_info(pcd->usb3_dev, "NO OTG!!\n"); ++ usb_put_phy(phy); ++ return; ++ } ++ ++ otg_host_release(otg); ++ usb_put_phy(phy); ++} ++ ++static int pcd_stop_peripheral(struct usb_gadget *gadget, ++ struct usb_gadget_driver *driver) ++{ ++ struct gadget_wrapper *d; ++ dwc_usb3_pcd_t *pcd; ++ unsigned long flags; ++ u32 temp; ++ int count = 0; ++ ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ pcd = d->pcd; ++ ++ spin_lock_irqsave(&pcd->lock, flags); ++ ++ dwc_usb3_pcd_stop(pcd); ++ ++ /* Disable device interrupts */ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->devten, 0); ++ ++ /* Clear Run/Stop bit */ ++ temp = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dctl); ++ temp &= ~DWC_DCTL_RUN_STOP_BIT; ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dctl, temp); ++ ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ ++ /* Wait for core stopped */ ++ do { ++ msleep(1); ++ temp = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dsts); ++ } while (!(temp & DWC_DSTS_DEV_CTRL_HLT_BIT) && (++count < 30)); ++ ++ return 0; ++} ++ ++static int pcd_start_peripheral(struct usb_gadget *gadget, ++ struct usb_gadget_driver *driver) ++{ ++ struct gadget_wrapper *d; ++ dwc_usb3_pcd_t *pcd; ++ unsigned long flags; ++ u32 temp; ++ ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ pcd = d->pcd; ++ ++ spin_lock_irqsave(&pcd->lock, flags); ++ ++ dwc_usb3_pcd_device_init(pcd->usb3_dev, 0, 0); ++ ++ /* Enable Device mode interrupts */ ++ dwc_usb3_enable_device_interrupts(pcd->usb3_dev); ++ ++ /* Set Run/Stop bit, and Keep-Connect bit if hibernation enabled */ ++ temp = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dctl); ++ temp |= DWC_DCTL_RUN_STOP_BIT; ++ ++ if (pcd->usb3_dev->core_params->hibernate && ++ (pcd->usb3_dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT) ++ temp |= DWC_DCTL_KEEP_CONNECT_BIT; ++ ++ dwc_wr32(pcd->usb3_dev, &pcd->dev_global_regs->dctl, temp); ++ pcd->wants_host = 0; ++ ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ ++ return 0; ++} ++ ++static int pcd_send_hrr(struct usb_gadget *gadget, int is_init) ++{ ++ struct gadget_wrapper *d; ++ dwc_usb3_pcd_t *pcd; ++ unsigned long flags; ++ u32 param; ++ ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ pcd = d->pcd; ++ ++ spin_lock_irqsave(&pcd->lock, flags); ++ ++ param = is_init ? DWC_DGCMDPAR_HOST_ROLE_REQ_INITIATE : ++ DWC_DGCMDPAR_HOST_ROLE_REQ_CONFIRM; ++ dwc_usb3_xmit_host_role_request(pcd, param); ++ ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ ++ return 0; ++} ++ ++#endif /* CONFIG_USB_OTG_DWC */ ++ ++/** ++ * This function returns the current USB frame number. ++ * ++ * @param gadget The gadget context. ++ */ ++static int dwc_get_frame(struct usb_gadget *gadget) ++{ ++ struct gadget_wrapper *d; ++ dwc_usb3_pcd_t *pcd; ++ dwc_usb3_device_t *dev; ++ unsigned long flags; ++ int ret; ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(KERN_DEBUG USB3_DWC "%s()\n", __func__); ++#endif ++ if (!gadget) ++ return -ENODEV; ++ ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ pcd = d->pcd; ++ dev = pcd->usb3_dev; ++ ++ spin_lock_irqsave(&pcd->lock, flags); ++ dwc_wait_if_hibernating(pcd, flags); ++ pcd->usb3_dev->hiber_cnt++; ++ ++ ret = dwc_usb3_pcd_get_frame_number(pcd); ++ ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ return ret; ++} ++ ++/** ++ * This function sends a remote wakeup to the host. ++ * ++ * @param gadget The gadget context. ++ */ ++int dwc_usb3_wakeup(struct usb_gadget *gadget) ++{ ++ struct gadget_wrapper *d; ++ dwc_usb3_pcd_t *pcd; ++ dwc_usb3_device_t *dev; ++ unsigned long timeout, flags; ++ int state; ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(KERN_DEBUG USB3_DWC "%s()\n", __func__); ++#endif ++ if (!gadget) ++ return -ENODEV; ++ ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ pcd = d->pcd; ++ dev = pcd->usb3_dev; ++ ++ if (!pcd->remote_wakeup_enable) { ++ dwc_info(dev, "hardware not enabled for wakeup\n"); ++ return -ENOTSUPP; ++ } ++ ++ spin_lock_irqsave(&pcd->lock, flags); ++ ++ if (dev->hibernate != DWC_HIBER_AWAKE) { ++ state = dwc_exit_hibernation(pcd, 1); ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ printk(USB3_DWC "dwc_exit_hibernation() returned %d\n", state); ++ return 0; ++ } ++ ++ dwc_wait_if_hibernating(pcd, flags); ++ pcd->usb3_dev->hiber_cnt++; ++ ++ state = dwc_usb3_pcd_get_link_state(pcd); ++ pcd->link_state = state; ++ dwc_usb3_pcd_set_link_state(pcd, DWC_LINK_STATE_REQ_REMOTE_WAKEUP); ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ ++ dwc_info(dev, "link state before: %d\n", state); ++#if 0 ++ /* Clear DCTL bits after 2 ms */ ++ msleep(2); ++ ++ spin_lock_irqsave(&pcd->lock, flags); ++ dwc_usb3_pcd_set_link_state(pcd, 0); ++ spin_unlock_irqrestore(&pcd->lock, flags); ++#endif ++ /* Wait 500 ms for link state to go to U0 */ ++ timeout = jiffies + msecs_to_jiffies(500); ++ ++ while (!time_after(jiffies, timeout)) { ++ spin_lock_irqsave(&pcd->lock, flags); ++ state = dwc_usb3_pcd_get_link_state(pcd); ++ pcd->link_state = state; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ if (state == DWC_LINK_STATE_U0) ++ break; ++ else ++ msleep(1); ++ } ++ ++ dwc_info(dev, "link state after: %d\n", state); ++ ++ if (state != DWC_LINK_STATE_U0) { ++ spin_lock_irqsave(&pcd->lock, flags); ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ return -ETIMEDOUT; ++ } ++ ++ /* Send function remote wake notification */ ++ spin_lock_irqsave(&pcd->lock, flags); ++ dwc_usb3_pcd_remote_wake(pcd, 0); ++ pcd->wkup_rdy = 0; ++ pcd->usb3_dev->hiber_cnt--; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ dwc_info(dev, "remote wake sent\n"); ++ ++ return 0; ++} ++ ++static int dwc_usb3_pullup(struct usb_gadget *gadget, int is_on) ++{ ++ return 0; ++} ++ ++static const struct usb_gadget_ops pcd_ops = { ++ .get_frame = dwc_get_frame, ++ .wakeup = dwc_usb3_wakeup, ++ .pullup = dwc_usb3_pullup, ++ // current versions must always be self-powered ++ ++#ifdef CONFIG_USB_OTG_DWC ++ .udc_start = pcd_start_peripheral, ++ .udc_stop = pcd_stop_peripheral, ++ ++ .send_hrr = pcd_send_hrr, ++#endif ++}; ++ ++ ++/*=======================================================================*/ ++ ++/** ++ * Initialize the Linux gadget specific parts of the default Control EP (EP0). ++ */ ++static void gadget_init_ep0(dwc_usb3_pcd_t *pcd, struct gadget_wrapper *d) ++{ ++ dwc_usb3_pcd_ep_t *pcd_ep; ++ struct usb_ep *ep0; ++ ++ dwc_debug(pcd->usb3_dev, "%s()\n", __func__); ++ ++ d->gadget.ep0 = &d->ep0.usb_ep; ++ INIT_LIST_HEAD(&d->gadget.ep0->ep_list); ++ ++ pcd_ep = &d->ep0; ++ ep0 = &pcd_ep->usb_ep; ++ dwc_debug(pcd->usb3_dev, "ep0=%p\n", ep0); ++ ++ INIT_LIST_HEAD(&pcd_ep->dwc_ep.queue); ++ ++ /* Init the usb_ep structure */ ++ ep0->name = d->ep_names[0]; ++ ep0->ops = (struct usb_ep_ops *)&pcd_ep_ops; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) ++ ep0->maxpacket = DWC_MAX_EP0_SIZE; ++#else ++ usb_ep_set_maxpacket_limit(ep0, DWC_MAX_EP0_SIZE); ++#endif ++ ++ dwc_debug(pcd->usb3_dev, "EP0 name=%s\n", ep0->name); ++ dwc_debug(pcd->usb3_dev, "ep0 eplist: %p(%p,%p) = %p(%p,%p)\n", ++ &d->gadget.ep0->ep_list, d->gadget.ep0->ep_list.prev, ++ d->gadget.ep0->ep_list.next, &ep0->ep_list, ++ ep0->ep_list.prev, ep0->ep_list.next); ++} ++ ++/** ++ * Initialize the Linux gadget specific parts of the non-EP0 EPs. ++ */ ++static int gadget_init_eps(dwc_usb3_pcd_t *pcd, struct gadget_wrapper *d) ++{ ++ dwc_usb3_pcd_ep_t *pcd_ep; ++ struct usb_ep *ep; ++ const char *name, *tmp, *ttmp; ++ int i, num, ep_in, ep_out; ++ ++ dwc_debug(pcd->usb3_dev, "%s()\n", __func__); ++ ++ INIT_LIST_HEAD(&d->gadget.ep_list); ++ ++ for (i = 1, ep_in = 0, ep_out = 0; i < ARRAY_SIZE(d->ep_names); i++) { ++ name = d->ep_names[i]; ++ if (!name || !name[0]) ++ break; ++ ++ /* Find '-' in e.g. "ep1in-bulk" */ ++ tmp = strrchr(name, '-'); ++ if (!tmp) ++ /* If no '-' then find end of string */ ++ tmp = name + strlen(name); ++ ++ /* If not 0-len string then back up 1 char */ ++ if (tmp != name) ++ tmp--; ++ ++ /* Get the EP number */ ++ num = 0; ++ ttmp = tmp; ++ ++ if (*tmp == 't') { ++ /* If "out", back up to the number before the 'o' */ ++ if (tmp >= name + 3) ++ tmp -= 3; ++ } else if (*tmp == 'n') { ++ /* If "in", back up to the number before the 'i' */ ++ if (tmp >= name + 2) ++ tmp -= 2; ++ } ++ ++ if (isdigit(*tmp)) { ++ /* If numeric, handle 1 or 2 digits */ ++ if (tmp > name && isdigit(*(tmp - 1))) ++ num = simple_strtol(tmp - 1, NULL, 10); ++ else ++ num = simple_strtol(tmp, NULL, 10); ++ } ++ ++ dwc_debug(pcd->usb3_dev, "num=%d\n", num); ++ tmp = ttmp; ++ if (num < 1 || num >= DWC_MAX_EPS) ++ goto bad; ++ ++ /* If e.g. "ep1" or "ep1out" then OUT ep */ ++ if ((isdigit(*tmp) || *tmp == 't') && (ep_out < 2)) { ++ ep_out++; ++ pcd_ep = &d->out_ep[ep_out - 1]; ++ ep = &pcd_ep->usb_ep; ++ dwc_debug(pcd->usb3_dev, ++ "ep%d-OUT=%p name=%s phys=%d pcd_ep=%p\n", ++ num, ep, name, ep_out, pcd_ep); ++ dwc_error(pcd->usb3_dev, ++ "ep%d-OUT=%p name=%s phys=%d pcd_ep=%p\n", ++ num, ep, name, ep_out, pcd_ep); ++ ++ pcd_ep->dwc_ep.num = num; ++ INIT_LIST_HEAD(&pcd_ep->dwc_ep.queue); ++ ++ /* Init the usb_ep structure */ ++ ep->name = name; ++ ep->ops = (struct usb_ep_ops *)&pcd_ep_ops; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) ++ ep->maxpacket = DWC_MAX_PACKET_SIZE; ++#else ++ usb_ep_set_maxpacket_limit(ep, DWC_MAX_PACKET_SIZE); ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0) ++ ep->max_streams = 15; ++#else ++ ep->numstreams = 0; ++#endif ++ ep->mult = 0; ++ ep->maxburst = 0; ++ ++ dwc_debug(pcd->usb3_dev, ++ "ep%d-OUT eplist pre: %p(%p,%p) = %p(%p,%p)\n", ++ num, &d->gadget.ep_list, d->gadget.ep_list.prev, ++ d->gadget.ep_list.next, &ep->ep_list, ++ ep->ep_list.prev, ep->ep_list.next); ++ list_add_tail(&ep->ep_list, &d->gadget.ep_list); ++ dwc_debug(pcd->usb3_dev, ++ "ep%d-OUT eplist post: %p(%p,%p) = %p(%p,%p)\n", ++ num, &d->gadget.ep_list, d->gadget.ep_list.prev, ++ d->gadget.ep_list.next, &ep->ep_list, ++ ep->ep_list.prev, ep->ep_list.next); ++ } ++ ++ /* If e.g. "ep1" or "ep1in" then IN ep */ ++ if ((isdigit(*tmp) || *tmp == 'n')&&(ep_in < 4)) { ++ ep_in++; ++ pcd_ep = &d->in_ep[ep_in - 1]; ++ ep = &pcd_ep->usb_ep; ++ dwc_debug(pcd->usb3_dev, ++ "ep%d-IN=%p name=%s phys=%d pcd_ep=%p\n", ++ num, ep, name, ep_in, pcd_ep); ++ dwc_error(pcd->usb3_dev, ++ "ep%d-IN=%p name=%s phys=%d pcd_ep=%p\n", ++ num, ep, name, ep_in, pcd_ep); ++ ++ pcd_ep->dwc_ep.num = num; ++ INIT_LIST_HEAD(&pcd_ep->dwc_ep.queue); ++ /* Init the usb_ep structure */ ++ ep->name = name; ++ ep->ops = (struct usb_ep_ops *)&pcd_ep_ops; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) ++ ep->maxpacket = DWC_MAX_PACKET_SIZE; ++#else ++ usb_ep_set_maxpacket_limit(ep, DWC_MAX_PACKET_SIZE); ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0) ++ ep->max_streams = 15; ++#else ++ ep->numstreams = 0; ++#endif ++ ep->mult = 0; ++ ep->maxburst = 0; ++ ++ dwc_debug(pcd->usb3_dev, ++ "ep%d-IN eplist pre: %p(%p,%p) = %p(%p,%p)\n", ++ num, &d->gadget.ep_list, d->gadget.ep_list.prev, ++ d->gadget.ep_list.next, &ep->ep_list, ++ ep->ep_list.prev, ep->ep_list.next); ++ list_add_tail(&ep->ep_list, &d->gadget.ep_list); ++ dwc_debug(pcd->usb3_dev, ++ "ep%d-IN eplist post: %p(%p,%p) = %p(%p,%p)\n", ++ num, &d->gadget.ep_list, d->gadget.ep_list.prev, ++ d->gadget.ep_list.next, &ep->ep_list, ++ ep->ep_list.prev, ep->ep_list.next); ++ } ++ ++ if (!isdigit(*tmp) && *tmp != 't' && *tmp != 'n') { ++bad: ++ dwc_debug(pcd->usb3_dev, "ep%d ????\n", num); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * Free any descriptor allocations made for each non-EP0 EP. ++ */ ++static void gadget_free_ep_allocations(dwc_usb3_pcd_t *pcd, ++ struct gadget_wrapper *d) ++{ ++ dwc_usb3_pcd_ep_t *pcd_ep; ++ int i; ++ ++ for (i = DWC_MAX_EPS - 1; i > 0; i--) { ++ pcd_ep = &d->in_ep[i - 1]; ++ dwc_debug(pcd->usb3_dev, "physpair%d-IN=%p\n", i, pcd_ep); ++ dwc_usb3_pcd_trb_free(pcd_ep); ++ ++ pcd_ep = &d->out_ep[i - 1]; ++ dwc_debug(pcd->usb3_dev, "physpair%d-OUT=%p\n", i, pcd_ep); ++ dwc_usb3_pcd_trb_free(pcd_ep); ++ } ++} ++ ++/** ++ * This function releases the Gadget device. ++ * Required by device_unregister(). ++ * ++ * @param dev The device context. ++ */ ++static void gadget_release(struct device *dev) ++{ ++ /* @todo Should this do something? Should it free the PCD? ++ */ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ printk(KERN_DEBUG USB3_DWC "%s()\n", __func__); ++#endif ++} ++ ++ ++#ifdef DWC_UTE ++/*=======================================================================*/ ++/* ++ * UTE support functions ++ */ ++ ++/** ++ * Return the PCD pointer for a given gadget instance. ++ */ ++dwc_usb3_pcd_t *dwc_usb3_get_pcd_instance(unsigned devnum) ++{ ++ if (!gadget_wrapper) { ++ printk(KERN_WARNING USB3_DWC "%s null wrapper!\n", __func__); ++ return NULL; ++ } ++ ++ return gadget_wrapper->pcd; ++} ++ ++/** ++ * Set mapping of a USB EP to a physical EP, by changing the contents of the ++ * gadget instance's ep_names[] array. ++ */ ++int dwc_usb3_set_usb_ep_map(unsigned devnum, unsigned phys_ep_num, ++ unsigned usb_ep_num) ++{ ++ unsigned usb_ep, usb_dir, usb_type; ++ char *dirstr, *typestr, *buf; ++ static char *ep_type_str[] = { "ctrl", "iso", "bulk", "int" }; ++ ++ if (!gadget_wrapper) { ++ printk(KERN_WARNING USB3_DWC "%s null wrapper!\n", __func__); ++ return -ENODEV; ++ } ++ ++ /* Phys EP 0 & 1 cannot be remapped */ ++ if (phys_ep_num < 2 || phys_ep_num >= DWC_MAX_PHYS_EP) ++ return -EINVAL; ++ ++ /* ep_names[] has a single entry for both EP0 IN & OUT */ ++ phys_ep_num--; ++ ++ /* These fields are defined by UTE */ ++ usb_ep = usb_ep_num & 0xf; ++ usb_dir = usb_ep_num >> 4 & 0x3; ++ usb_type = usb_ep_num >> 6 & 0x7; ++ ++ /* Cannot remap the default Control EP */ ++ if (usb_ep == 0) ++ return -EINVAL; ++ ++ switch (usb_dir) { ++ case 0: ++ dirstr = "out"; ++ break; ++ case 1: ++ dirstr = "in"; ++ break; ++ default: ++ /* USB3 does not have bidirectional physical EPs */ ++ return -EINVAL; ++ } ++ ++ buf = gadget_wrapper->ep_names[phys_ep_num]; ++ ++ if (usb_type >= 4) { ++ snprintf(buf, sizeof(gadget_wrapper->ep_names[0]), ++ "ep%u%s", usb_ep, dirstr); ++ } else { ++ typestr = ep_type_str[usb_type]; ++ snprintf(buf, sizeof(gadget_wrapper->ep_names[0]), ++ "ep%u%s-%s", usb_ep, dirstr, typestr); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Get mapping of a USB EP to a physical EP, by reading the contents of the ++ * gadget instance's ep_names[] array. ++ */ ++int dwc_usb3_get_usb_ep_map(unsigned devnum, unsigned phys_ep_num, ++ unsigned *usb_ep_num_ret) ++{ ++ unsigned usb_ep, usb_dir, usb_type; ++ char dirstr[16], typestr[16], *dashp, *buf; ++ int ret; ++ ++ if (!gadget_wrapper) { ++ printk(KERN_WARNING USB3_DWC "%s null wrapper!\n", __func__); ++ return -ENODEV; ++ } ++ ++ /* Phys EP 0 & 1 cannot be remapped */ ++ if (phys_ep_num < 2 || phys_ep_num >= DWC_MAX_PHYS_EP) ++ return -EINVAL; ++ ++ /* ep_names[] has a single entry for both EP0 IN & OUT */ ++ phys_ep_num--; ++ ++ buf = gadget_wrapper->ep_names[phys_ep_num]; ++ printk("phys EP %d, config=\"%s\"\n", phys_ep_num, buf); ++ usb_ep=16; ++ dirstr[0] = 0; ++ typestr[0] = 0; ++ dashp = strchr(buf, '-'); ++ if (dashp) { ++ *dashp = ' '; ++ ret = sscanf(buf, "ep%u%15s %15s", &usb_ep, dirstr, typestr); ++ *dashp = '-'; ++ printk("sscanf() ret=%d\n", ret); ++ if (ret != 3) ++ return -EINVAL; ++ } else { ++ ret = sscanf(buf, "ep%u%15s", &usb_ep, dirstr); ++ printk("sscanf() ret=%d\n", ret); ++ if (ret != 2) ++ return -EINVAL; ++ } ++ ++ if (usb_ep > 15) ++ return -ERANGE; ++ ++ if (strcmp(dirstr, "out") == 0) ++ usb_dir = 0; ++ else if (strcmp(dirstr, "in") == 0) ++ usb_dir = 1; ++ else ++ return -ERANGE; ++ ++ if (strcmp(typestr, "ctrl") == 0) ++ usb_type = 0; ++ else if (strcmp(typestr, "iso") == 0) ++ usb_type = 1; ++ else if (strcmp(typestr, "bulk") == 0) ++ usb_type = 2; ++ else if (strcmp(typestr, "int") == 0) ++ usb_type = 3; ++ else ++ usb_type = 4; ++ ++ *usb_ep_num_ret = usb_ep | usb_dir << 4 | usb_type << 6; ++ return 0; ++} ++ ++/** ++ * Activate the changes made by the UTE to the core's features. ++ */ ++void dwc_usb3_ute_config(dwc_usb3_device_t *usb3_dev) ++{ ++ dwc_usb3_pcd_t *pcd = &usb3_dev->pcd; ++ int i, cnt, txsz[DWC_MAX_TX_FIFOS]; ++ ++ dwc_debug(usb3_dev, "%s()\n", __func__); ++ ++ if (!gadget_wrapper) { ++ dwc_warn(usb3_dev, "%s null wrapper!\n", __func__); ++ return; ++ } ++ ++ if (pcd->ute_change) { ++ /* Free any remaining descriptor allocations made for ++ * non-EP0 EPs ++ */ ++ gadget_free_ep_allocations(pcd, gadget_wrapper); ++ ++ /* Set the Tx FIFO sizes */ ++ for (i = 0, cnt = 0; i < pcd->num_in_eps + 1; i++) { ++ txsz[i] = pcd->txf_size[i]; ++ if (txsz[i]) ++ cnt++; ++ } ++ if (cnt) ++ dwc_usb3_set_tx_fifo_size(usb3_dev, txsz); ++ ++ /* Set the Rx FIFO size */ ++ if (pcd->rxf_size) ++ dwc_usb3_set_rx_fifo_size(usb3_dev, pcd->rxf_size); ++ ++ /* Re-initialize non-EP0 EPs to pick up any mapping changes */ ++ if (gadget_init_eps(pcd, gadget_wrapper)) { ++ dwc_error(usb3_dev, "%s, gadget_init_eps error!\n", ++ __func__); ++ } ++ ++ pcd->ute_change = 0; ++ } ++} ++ ++/** ++ * Reset usb endpoint mapping to it's default state. ++ */ ++int dwc_usb3_reset_usb_ep_map(unsigned devnum) ++{ ++ struct gadget_wrapper *d; ++ dwc_usb3_pcd_t *pcd; ++ int i; ++ ++ if (!gadget_wrapper) { ++ printk(KERN_WARNING USB3_DWC "%s null wrapper!\n", __func__); ++ return -ENODEV; ++ } ++ ++ d = gadget_wrapper; ++ pcd = d->pcd; ++ ++ for (i = 0; i < ARRAY_SIZE(g_ep_names) && ++ i < ARRAY_SIZE(d->ep_names) && ++ i < pcd->num_in_eps + pcd->num_out_eps + 1; i++) { ++ strncpy(d->ep_names[i], g_ep_names[i], ++ sizeof(d->ep_names[0]) - 1); ++ d->ep_names[i][sizeof(d->ep_names[0]) - 1] = 0; ++ dwc_debug(pcd->usb3_dev, "~phys EP%d name=%s\n", i, d->ep_names[i]); ++ } ++ ++ return gadget_init_eps(pcd, d); ++} ++ ++int dwc_usb3_switch_speed(unsigned devnum, int speed) ++{ ++ int ret = 0; ++ struct gadget_wrapper *d; ++ dwc_usb3_pcd_t *pcd; ++ dwc_usb3_device_t *usb3_dev; ++ //u32 temp; ++ ++ if (!gadget_wrapper) { ++ printk(KERN_WARNING USB3_DWC "%s null wrapper!\n", __func__); ++ return -ENODEV; ++ } ++ ++ d = gadget_wrapper; ++ pcd = d->pcd; ++ usb3_dev = pcd->usb3_dev; ++ /* ++ temp = dwc_rd32(usb3_dev, &pcd->dev_global_regs->dctl); ++ temp &= ~DWC_DCTL_RUN_STOP_BIT; ++ dwc_wr32(usb3_dev, &pcd->dev_global_regs->dctl, temp); ++ ++ dwc_udelay(usb3_dev, 1500); */ ++ /* Soft-reset the core */ ++ /* temp = dwc_rd32(usb3_dev, &pcd->dev_global_regs->dctl); ++ temp &= ~DWC_DCTL_RUN_STOP_BIT; ++ temp |= DWC_DCTL_CSFT_RST_BIT; ++ dwc_wr32(usb3_dev, &pcd->dev_global_regs->dctl, temp); */ ++ ++ /* Wait for core to come out of reset */ ++ /* do { ++ dwc_udelay(usb3_dev, 1); ++ temp = dwc_rd32(usb3_dev, &pcd->dev_global_regs->dctl); ++ } while (temp & DWC_DCTL_CSFT_RST_BIT); ++ ++ dwc_mdelay(usb3_dev, 2); */ ++ ++ if (speed == 3) { ++ /* Set device speed feature to Super Speed */ ++ speed = 0; ++ } ++ usb3_dev->core_params->usb2mode = speed; ++ ++ dwc_usb3_pcd_device_init(usb3_dev, 1, 0); ++ ++ return ret; ++} ++ ++int dwc_usb3_get_dev_speed(unsigned devnum) ++{ ++ struct gadget_wrapper *d; ++ dwc_usb3_pcd_t *pcd; ++ int ret; ++ ++ if (!gadget_wrapper) { ++ printk(KERN_WARNING USB3_DWC "%s null wrapper!\n", __func__); ++ return -ENODEV; ++ } ++ ++ d = gadget_wrapper; ++ pcd = d->pcd; ++ ++ ret = pcd->usb3_dev->core_params->usb2mode; ++ if (ret == 0) { ++ /* If speed feature is set to Super Speed */ ++ ret = 3; ++ } ++ return ret; ++} ++ ++#endif /* DWC_UTE */ ++ ++/** ++ * This function initializes the Gadget portion of the driver. ++ * ++ * @param usb3_dev Programming view of DWC_usb3 controller. ++ * @param dev The device context. ++ */ ++int dwc_usb3_gadget_init(dwc_usb3_device_t *usb3_dev, struct device *dev) ++{ ++ dwc_usb3_pcd_t *pcd = &usb3_dev->pcd; ++ int retval = -ENOMEM; ++ int hiberbufs = 0; ++ dma_addr_t dma_addr = 0; ++ int i; ++ ++ dwc_debug(usb3_dev, "%s()\n", __func__); ++ dwc_debug(usb3_dev, "pcd=%p\n", pcd); ++ ++ gadget_wrapper = kmalloc(sizeof(struct gadget_wrapper), GFP_KERNEL); ++ if (!gadget_wrapper) ++ goto out1; ++ ++ memset(gadget_wrapper, 0, sizeof(*gadget_wrapper)); ++ gadget_wrapper->pcd = pcd; ++ ++ for (i = 0; i < ARRAY_SIZE(g_ep_names) && ++ i < ARRAY_SIZE(gadget_wrapper->ep_names) && ++ i < pcd->num_in_eps + pcd->num_out_eps + 1; i++) { ++ strncpy(gadget_wrapper->ep_names[i], g_ep_names[i], ++ sizeof(gadget_wrapper->ep_names[0]) - 1); ++ gadget_wrapper->ep_names[i][sizeof(gadget_wrapper->ep_names[0]) - 1] = 0; ++ dwc_debug(usb3_dev, "~phys EP%d name=%s\n", i, ++ gadget_wrapper->ep_names[i]); ++ } ++ ++ gadget_wrapper->gadget.name = "dwc_usb3_pcd"; ++ dwc_debug(usb3_dev, "gadget.name=%s\n", gadget_wrapper->gadget.name); ++ ++#ifdef CONFIG_USB_OTG_DWC ++ gadget_wrapper->gadget.is_otg = 1; ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) ++ strcpy(gadget_wrapper->gadget.dev.bus_id, "gadget"); ++#else ++ dev_set_name(&gadget_wrapper->gadget.dev, "%s", "gadget"); ++#endif ++ gadget_wrapper->gadget.dev.parent = dev; ++ gadget_wrapper->gadget.dev.release = gadget_release; ++ gadget_wrapper->gadget.speed = USB_SPEED_UNKNOWN; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) ++ gadget_wrapper->gadget.is_dualspeed = 1; ++#else ++ gadget_wrapper->gadget.max_speed = USB_SPEED_SUPER; ++#endif ++ gadget_wrapper->gadget.ops = &pcd_ops; ++ gadget_wrapper->driver = NULL; ++ dwc_debug(usb3_dev, "gadget=%p ops=%p\n", &gadget_wrapper->gadget, ++ gadget_wrapper->gadget.ops); ++ ++ /* Set the PCD's EP0 request pointer to the wrapper's request */ ++ pcd->ep0_req = &gadget_wrapper->ep0_req; ++ ++ /* Set the PCD's EP array pointers to the wrapper's EPs */ ++ pcd->ep0 = &gadget_wrapper->ep0; ++ for (i = 0; i < DWC_MAX_EPS - 1; i++) { ++ pcd->out_ep[i] = &gadget_wrapper->out_ep[i]; ++ pcd->in_ep[i] = &gadget_wrapper->in_ep[i]; ++ } ++ ++ /* Allocate the EP0 packet buffers */ ++ pcd->ep0_setup_pkt = dma_alloc_coherent(NULL, ++ sizeof(*pcd->ep0_setup_pkt) * 5, ++ &pcd->ep0_setup_pkt_dma, GFP_KERNEL | GFP_DMA32); ++ if (!pcd->ep0_setup_pkt) ++ goto out2; ++ ++ pcd->ep0_status_buf = dma_alloc_coherent(NULL, DWC_STATUS_BUF_SIZE, ++ &pcd->ep0_status_buf_dma, GFP_KERNEL | GFP_DMA32); ++ if (!pcd->ep0_status_buf) ++ goto out3; ++ ++ /* Allocate the EP0 DMA descriptors */ ++ pcd->ep0_setup_desc = dma_alloc_coherent(NULL, ++ sizeof(dwc_usb3_dma_desc_t), &pcd->ep0_setup_desc_dma, ++ GFP_KERNEL | GFP_DMA32); ++ if (!pcd->ep0_setup_desc) ++ goto out4; ++ ++ pcd->ep0_in_desc = dma_alloc_coherent(NULL, sizeof(dwc_usb3_dma_desc_t), ++ &pcd->ep0_in_desc_dma, GFP_KERNEL | GFP_DMA32); ++ if (!pcd->ep0_in_desc) ++ goto out5; ++ ++ pcd->ep0_out_desc = dma_alloc_coherent(NULL, ++ sizeof(dwc_usb3_dma_desc_t), &pcd->ep0_out_desc_dma, ++ GFP_KERNEL | GFP_DMA32); ++ if (!pcd->ep0_out_desc) ++ goto out6; ++ ++ /* If hibernation is supported */ ++ if (usb3_dev->core_params->hibernate && ++ (usb3_dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT) { ++ hiberbufs = usb3_dev->hwparams4 >> DWC_HWP4_HIBER_SPAD_SHIFT & ++ DWC_HWP4_HIBER_SPAD_BITS >> DWC_HWP4_HIBER_SPAD_SHIFT; ++ if (hiberbufs) { ++ /* Allocate scratch buffer pointer array */ ++ pcd->hiber_scratchpad_array = ++ dma_alloc_coherent(NULL, ++ sizeof(*pcd->hiber_scratchpad_array), ++ &pcd->hiber_scratchpad_array_dma, ++ GFP_KERNEL | GFP_DMA32); ++ if (!pcd->hiber_scratchpad_array) { ++ dwc_debug(usb3_dev, ++ "%s hibernation array allocation error\n", ++ __func__); ++ goto out7; ++ } ++ } ++ ++ /* Allocate scratch buffers */ ++ for (i = 0; i < hiberbufs; i++) { ++ pcd->hiber_scratchpad[i] = ++ dma_alloc_coherent(NULL, 4096, &dma_addr, ++ GFP_KERNEL | GFP_DMA32); ++ if (!pcd->hiber_scratchpad[i]) { ++ dwc_debug(usb3_dev, ++ "%s hibernation buf allocation error\n", ++ __func__); ++ while (i-- > 0) { ++ dma_addr = (dma_addr_t)pcd-> ++ hiber_scratchpad_array->dma_addr[i]; ++ dma_free_coherent(NULL, 4096, ++ pcd->hiber_scratchpad[i], ++ dma_addr); ++ pcd->hiber_scratchpad[i] = NULL; ++ } ++ ++ goto out8; ++ } ++ ++ pcd->hiber_scratchpad_array->dma_addr[i] = (u64)dma_addr; ++ } ++ } ++ ++ /* Initialize all the EP structures */ ++ gadget_init_ep0(pcd, gadget_wrapper); ++ retval = gadget_init_eps(pcd, gadget_wrapper); ++ if (retval) { ++ dwc_debug(usb3_dev, "%s gadget_init_eps error\n", __func__); ++ goto out9; ++ } ++ ++ /* Register the gadget device */ ++ retval = device_register(&gadget_wrapper->gadget.dev); ++ if (retval) { ++ dwc_debug(usb3_dev, "%s cannot register gadget device\n", ++ __func__); ++ goto out10; ++ } ++ ++ return 0; ++ ++out10: ++ gadget_free_ep_allocations(pcd, gadget_wrapper); ++out9: ++ for (i = hiberbufs - 1; i >= 0; i--) { ++ if (pcd->hiber_scratchpad[i]) { ++ dma_addr = (dma_addr_t) ++ pcd->hiber_scratchpad_array->dma_addr[i]; ++ dma_free_coherent(NULL, 4096, pcd->hiber_scratchpad[i], ++ dma_addr); ++ pcd->hiber_scratchpad[i] = NULL; ++ } ++ } ++out8: ++ if (hiberbufs) ++ dma_free_coherent(NULL, sizeof(*pcd->hiber_scratchpad_array), ++ pcd->hiber_scratchpad_array, ++ pcd->hiber_scratchpad_array_dma); ++out7: ++ dma_free_coherent(NULL, sizeof(dwc_usb3_dma_desc_t), pcd->ep0_out_desc, ++ pcd->ep0_out_desc_dma); ++out6: ++ dma_free_coherent(NULL, sizeof(dwc_usb3_dma_desc_t), pcd->ep0_in_desc, ++ pcd->ep0_in_desc_dma); ++out5: ++ dma_free_coherent(NULL, sizeof(dwc_usb3_dma_desc_t), ++ pcd->ep0_setup_desc, pcd->ep0_setup_desc_dma); ++out4: ++ dma_free_coherent(NULL, DWC_STATUS_BUF_SIZE, pcd->ep0_status_buf, ++ pcd->ep0_status_buf_dma); ++out3: ++ dma_free_coherent(NULL, sizeof(*pcd->ep0_setup_pkt) * 5, ++ pcd->ep0_setup_pkt, pcd->ep0_setup_pkt_dma); ++out2: ++ kfree(gadget_wrapper); ++ gadget_wrapper = NULL; ++out1: ++ return retval; ++} ++ ++/** ++ * Cleanup the Gadget. ++ * ++ * @param usb3_dev Programming view of DWC_usb3 controller. ++ * @param dev The device context. ++ */ ++void dwc_usb3_gadget_remove(dwc_usb3_device_t *usb3_dev, struct device *dev) ++{ ++ dwc_usb3_pcd_t *pcd = &usb3_dev->pcd; ++ int hiberbufs, i; ++ void *addr; ++ dma_addr_t dma_addr; ++ ++ dwc_debug(usb3_dev, "%s()\n", __func__); ++ dwc_debug(usb3_dev, "pcd=%p\n", pcd); ++ ++ if (!gadget_wrapper) { ++ dwc_warn(usb3_dev, "%s null wrapper!\n", __func__); ++ return; ++ } ++ ++ /* start with the driver above us */ ++ if (gadget_wrapper->driver) { ++ /* should have been done already by driver model core */ ++ dwc_warn(usb3_dev, "driver '%s' is still registered!\n", ++ gadget_wrapper->driver->driver.name); ++ usb_gadget_unregister_driver(gadget_wrapper->driver); ++ } ++ ++ device_unregister(&gadget_wrapper->gadget.dev); ++ gadget_free_ep_allocations(pcd, gadget_wrapper); ++ ++ /* If hibernation is supported */ ++ if (usb3_dev->core_params->hibernate && ++ (usb3_dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT) { ++ hiberbufs = usb3_dev->hwparams4 >> DWC_HWP4_HIBER_SPAD_SHIFT & ++ DWC_HWP4_HIBER_SPAD_BITS >> DWC_HWP4_HIBER_SPAD_SHIFT; ++ ++ /* Free hibernation scratch buffers */ ++ for (i = hiberbufs - 1; i >= 0; i--) { ++ addr = pcd->hiber_scratchpad[i]; ++ dma_addr = (dma_addr_t)pcd->hiber_scratchpad_array->dma_addr[i]; ++ pcd->hiber_scratchpad[i] = NULL; ++ if (addr) ++ dma_free_coherent(NULL, 4096, addr, dma_addr); ++ } ++ ++ if (hiberbufs) ++ dma_free_coherent(NULL, ++ sizeof(*pcd->hiber_scratchpad_array), ++ pcd->hiber_scratchpad_array, ++ pcd->hiber_scratchpad_array_dma); ++ } ++ ++ dma_free_coherent(NULL, sizeof(dwc_usb3_dma_desc_t), pcd->ep0_out_desc, ++ pcd->ep0_out_desc_dma); ++ dma_free_coherent(NULL, sizeof(dwc_usb3_dma_desc_t), pcd->ep0_in_desc, ++ pcd->ep0_in_desc_dma); ++ dma_free_coherent(NULL, sizeof(dwc_usb3_dma_desc_t), pcd->ep0_setup_desc, ++ pcd->ep0_setup_desc_dma); ++ dma_free_coherent(NULL, DWC_STATUS_BUF_SIZE, pcd->ep0_status_buf, ++ pcd->ep0_status_buf_dma); ++ dma_free_coherent(NULL, sizeof(*pcd->ep0_setup_pkt) * 5, ++ pcd->ep0_setup_pkt, pcd->ep0_setup_pkt_dma); ++ ++ kfree(gadget_wrapper); ++ gadget_wrapper = NULL; ++} ++ ++/** ++ * This function registers a gadget driver with the PCD. ++ * ++ * When a driver is successfully registered, it will receive control ++ * requests including set_configuration(), which enables non-control ++ * requests. Then usb traffic follows until a disconnect is reported. ++ * Then a host may connect again, or the driver might get unbound. ++ * ++ * @param driver The driver being registered. ++ * @param bind The gadget driver's bind function. ++ */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++int usb_gadget_register_driver(struct usb_gadget_driver *driver) ++#else ++int usb_gadget_probe_driver(struct usb_gadget_driver *driver ++# if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) ++ , int (*bind)(struct usb_gadget *) ++# endif ++ ) ++#endif ++{ ++ dwc_usb3_pcd_t *pcd; ++ int retval = -ENODEV; ++#ifdef CONFIG_USB_OTG_DWC ++ struct usb_phy *phy; ++ struct usb_otg *otg; ++#endif ++ ++ printk(KERN_DEBUG USB3_DWC "%s(%p)\n", __func__, driver); ++ printk(KERN_DEBUG USB3_DWC "gdt_wrp=%p\n", gadget_wrapper); ++ ++ if (!gadget_wrapper) ++ goto out; ++ ++ pcd = gadget_wrapper->pcd; ++ printk(KERN_DEBUG USB3_DWC "pcd=%p\n", pcd); ++ ++ if (!pcd) { ++ printk(KERN_DEBUG USB3_DWC "ENODEV\n"); ++ goto out; ++ } ++ ++ if (!driver || ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) ++ driver->speed == USB_SPEED_UNKNOWN || ++#else ++ driver->max_speed == USB_SPEED_UNKNOWN || ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) || \ ++ LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) ++ !driver->bind || ++#endif ++ !driver->unbind || ++ !driver->disconnect || !driver->setup) { ++ printk(KERN_DEBUG USB3_DWC "EINVAL\n"); ++ retval = -EINVAL; ++ goto out; ++ } ++ ++ printk(KERN_DEBUG USB3_DWC "registering gadget driver '%s'\n", ++ driver->driver.name); ++ ++ if (gadget_wrapper->driver) { ++ printk(KERN_DEBUG USB3_DWC "EBUSY (%p)\n", ++ gadget_wrapper->driver); ++ retval = -EBUSY; ++ goto out; ++ } ++ ++#ifdef CONFIG_USB_OTG_DWC ++ /* Check that the otg transceiver driver is loaded */ ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ if (IS_ERR(phy) || !phy) { ++ printk(KERN_DEBUG USB3_DWC "OTG PHY not available!\n"); ++ if (!IS_ERR(phy)) ++ usb_put_phy(phy); ++ goto out; ++ } ++ ++ otg = phy->otg; ++ usb_put_phy(phy); ++ if (!otg) { ++ printk(KERN_DEBUG USB3_DWC "OTG not available!\n"); ++ goto out; ++ } ++#endif ++ ++ /* hook up the driver */ ++ gadget_wrapper->driver = driver; ++ gadget_wrapper->gadget.dev.driver = &driver->driver; ++ pcd->gadget = &gadget_wrapper->gadget; ++ ++ dwc_debug(pcd->usb3_dev, "bind to driver %s\n", driver->driver.name); ++ dwc_debug(pcd->usb3_dev, "&gadget_gadget_wrapper->gadget = %p\n", ++ &gadget_wrapper->gadget); ++ dwc_debug(pcd->usb3_dev, "gadget_gadget_wrapper->driver = %p\n", ++ gadget_wrapper->driver); ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++ retval = driver->bind(&gadget_wrapper->gadget); ++#else ++# if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) ++ retval = driver->bind(&gadget_wrapper->gadget, ++ gadget_wrapper->driver); ++# else ++ retval = bind(&gadget_wrapper->gadget); ++# endif ++#endif ++ if (retval) { ++ dwc_error(pcd->usb3_dev, "bind to driver %s --> error %d\n", ++ driver->driver.name, retval); ++ gadget_wrapper->driver = NULL; ++ gadget_wrapper->gadget.dev.driver = NULL; ++ pcd->gadget = NULL; ++ goto out; ++ } ++ ++#ifdef CONFIG_USB_OTG_DWC ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ if (!IS_ERR(phy)) { ++ if (phy && phy->otg) ++ otg_set_peripheral(phy->otg, &gadget_wrapper->gadget); ++ usb_put_phy(phy); ++ } ++#endif ++ ++ printk(KERN_DEBUG USB3_DWC "registered gadget driver '%s'\n", ++ driver->driver.name); ++out: ++#ifdef CONFIG_USB_OTG_DWC ++ /* Switch otg to host mode now that gadget is bound */ ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ if (!IS_ERR(phy)) { ++ if (phy && phy->otg) ++ dwc_otg3_set_peripheral(phy->otg, 0); ++ usb_put_phy(phy); ++ } ++#endif ++ return retval; ++} ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++EXPORT_SYMBOL_GPL(usb_gadget_register_driver); ++#else ++EXPORT_SYMBOL_GPL(usb_gadget_probe_driver); ++#endif ++ ++/** ++ * This function unregisters a gadget driver ++ * ++ * @param driver The driver being unregistered. ++ */ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++#ifdef CONFIG_USB_OTG_DWC ++ struct usb_phy *phy; ++ struct usb_otg *otg; ++#endif ++ ++ printk(KERN_DEBUG USB3_DWC "%s(%p)\n", __func__, driver); ++ printk(KERN_DEBUG USB3_DWC "unregistering gadget driver '%s'\n", ++ driver->driver.name); ++ ++ if (!gadget_wrapper) ++ return -ENODEV; ++ ++ if (!gadget_wrapper->pcd) { ++ printk(KERN_DEBUG USB3_DWC "%s Return(%d): pcd==NULL\n", ++ __func__, -ENODEV); ++ return -ENODEV; ++ } ++ ++ if (!gadget_wrapper->driver || driver != gadget_wrapper->driver) { ++ printk(KERN_DEBUG USB3_DWC "%s Return(%d): driver?\n", ++ __func__, -EINVAL); ++ return -EINVAL; ++ } ++ ++#ifdef CONFIG_USB_OTG_DWC ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ otg = phy->otg; ++ otg_set_peripheral(otg, NULL); ++ usb_put_phy(phy); ++#endif ++ ++ driver->disconnect(&gadget_wrapper->gadget); ++ driver->unbind(&gadget_wrapper->gadget); ++ gadget_wrapper->driver = NULL; ++ gadget_wrapper->gadget.dev.driver = NULL; ++ gadget_wrapper->pcd->gadget = NULL; ++ ++ printk(KERN_DEBUG USB3_DWC "unregistered gadget driver '%s'\n", ++ driver->driver.name); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); ++ ++//#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) ++int udc_attach_driver(const char *name, struct usb_gadget_driver *driver) ++{ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(udc_attach_driver); ++//#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0) ++void usb_gadget_set_state(struct usb_gadget *gadget, ++ enum usb_device_state state) ++{ ++ gadget->state = state; ++} ++EXPORT_SYMBOL_GPL(usb_gadget_set_state); ++#endif ++ ++MODULE_DESCRIPTION(DWC_DRIVER_DESC); ++MODULE_AUTHOR("Synopsys Inc."); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/usb/gadget/udc/hiudc3/linux_hiber.c b/drivers/usb/gadget/udc/hiudc3/linux_hiber.c +new file mode 100644 +index 0000000..8e77e97 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/linux_hiber.c +@@ -0,0 +1,282 @@ ++/** @file ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++void dwc_usb3_task_schedule(struct tasklet_struct *tasklet) ++{ ++ tasklet_schedule(tasklet); ++} ++ ++/** ++ * Helper routine for dwc_wait_pme_thread() ++ */ ++static void dwc_wait_for_link_up(dwc_usb3_pcd_t *pcd) ++{ ++ unsigned long flags; ++ u32 state; ++ int count = 5; ++ ++ while (count-- > 0) { ++ msleep(20); ++ spin_lock_irqsave(&pcd->lock, flags); ++ state = dwc_usb3_pcd_get_link_state(pcd); ++ pcd->link_state = state; ++ if (state == DWC_LINK_STATE_U0) { ++ dwc_debug(pcd->usb3_dev, ++ "Exiting from hibernation 2\n"); ++ pcd->usb3_dev->hiber_wait_connect = 0; ++ ++ /* Perform the steps in Section 9.1.3 "Initialization on ++ * Connect Done" in [DWSS]. ++ */ ++ dwc_usb3_handle_connect_done_intr(pcd); ++ dwc_exit_hibernation_after_connect(pcd, 0); ++ count = 0; ++ } ++ ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ } ++} ++ ++/** ++ * Kernel thread that monitors the usb3_dev->hibernate variable. usb3_dev-> ++ * hibernate is set to 1 or 2 from the interrupt handler when the core is ++ * requesting to enter hibernation. This thread checks whether it is safe to ++ * do so (usb3_dev->hiber_cnt == 0) and then sets usb3_dev->hibernate to 3 and ++ * puts the core into hibernation. usb3_dev->hibernate >= 3 tells the rest of ++ * the driver that it cannot access the hardware. ++ * ++ * This thread also handles some housekeeping work that must be done after the ++ * core has exited from hibernation. ++ * ++ * Note that this is NOT the way a "real" device would enter hibernation. This ++ * code is ONLY for testing hibernation on the Synopsys HAPS platform. ++ */ ++int dwc_wait_pme_thread(void *data) ++{ ++ dwc_usb3_pcd_t *pcd = (dwc_usb3_pcd_t *)data; ++ dwc_usb3_device_t *dev = pcd->usb3_dev; ++ unsigned long flags; ++ u32 state, temp; ++ int i; ++ int cnt = 0; ++ ++ dwc_debug(dev, "%s(%p)\n", __func__, data); ++ ++ /* Allow the thread to be killed by a signal, but set the signal mask ++ * to block everything but INT, TERM, KILL, and USR1. */ ++ allow_signal(SIGINT); ++ allow_signal(SIGTERM); ++ allow_signal(SIGKILL); ++ allow_signal(SIGUSR1); ++ ++ set_user_nice(current, -10); ++ ++ /* Allow the thread to be frozen */ ++ set_freezable(); ++ ++ for (;;) { ++ spin_lock_irqsave(&pcd->lock, flags); ++ state = dev->hibernate; ++ ++ if (dev->hiber_cnt == 0) { ++ if (state == DWC_HIBER_ENTER_NOSAVE || ++ state == DWC_HIBER_ENTER_SAVE) { ++ dwc_enter_hibernation(pcd, ++ state == DWC_HIBER_ENTER_SAVE); ++ state = DWC_HIBER_SLEEPING; ++ } ++ } ++ ++ if (state != DWC_HIBER_SLEEPING && dev->snpsid >= 0x5533230a && ++ dev->hiber_wait_u0) { ++ /* Handle waiting for U0 after requesting link state ++ * RECOVERY, because we don't have the link state ++ * change event enabled. We also do this in ++ * dwc_usb3_irq() in case an event comes first. ++ */ ++ temp = dwc_usb3_pcd_get_link_state(pcd); ++ pcd->link_state = temp; ++ ++ if (cnt-- == 0) { ++ cnt = 500; ++ dwc_debug1(pcd->usb3_dev, ++ "thread WAIT_U0 state=%d\n", temp); ++ } ++ ++ if (temp == DWC_LINK_STATE_U0) { ++ dwc_debug0(dev, ++ "thread WAIT_U0 setting speed\n"); ++ pcd->speed = dwc_usb3_get_device_speed(pcd); ++ if (pcd->remote_wakeup_enable) ++ dwc_usb3_pcd_remote_wake(pcd, 0); ++ dev->hiber_wait_u0 = 0; ++ } ++ } ++ ++ if (state == DWC_HIBER_WAIT_LINK_UP) { ++ dev->hibernate = DWC_HIBER_AWAKE; ++ state = DWC_HIBER_AWAKE; ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ dwc_wait_for_link_up(pcd); ++ spin_lock_irqsave(&pcd->lock, flags); ++ } ++ ++ if (state == DWC_HIBER_SS_DIS_QUIRK) { ++ for (i = 0; i < 500; i++) { ++ if (dev->hibernate != DWC_HIBER_SS_DIS_QUIRK) { ++ dwc_info(dev, ++ "breaking loop after %d ms\n", ++ i); ++ break; ++ } ++ ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ msleep(1); ++ spin_lock_irqsave(&pcd->lock, flags); ++ } ++ ++ if (dev->hibernate == DWC_HIBER_SS_DIS_QUIRK) { ++ temp = dwc_usb3_pcd_get_link_state(pcd); ++ pcd->link_state = temp; ++ if (temp == DWC_LINK_STATE_SS_DIS) ++ dwc_enter_hibernation(pcd, 0); ++ else ++ dev->hibernate = DWC_HIBER_AWAKE; ++ } ++ } ++ ++ spin_unlock_irqrestore(&pcd->lock, flags); ++ if (kthread_should_stop()) ++ break; ++ msleep(1); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Fake PME interrupt handler, called from dwc_usb3_irq() in cil_intr.c if ++ * the core is in hibernation and any interrupt is received. The custom PCIe ++ * "gasket" on the HAPS platform generates an interrupt when the core requests ++ * to exit hibernation. ++ * ++ * This function reads the Debug register at offset 16 in the gasket, to make ++ * sure the core is requesting to exit hibernation. If so, it brings the core ++ * out of hibernation. ++ * ++ * Note that this is NOT the way a "real" device would exit hibernation. This ++ * code is ONLY for testing hibernation on the Synopsys HAPS platform. ++ */ ++int dwc_usb3_handle_pme_intr(dwc_usb3_device_t *dev) ++{ ++ u32 temp, test1, test2; ++ u8 __iomem *base_addr; ++ int ret; ++ ++ dwc_debug(dev, "%s()\n", __func__); ++ ++ if (dev->gasket_base) ++ base_addr = (u8 __iomem *)dev->gasket_base; ++ else ++ base_addr = (u8 __iomem *)dev->base + dev->gasket_ofs; ++ ++ if ((dev->hwparams3 & DWC_HWP3_SSPHY_IFC_BITS) == 0) { ++ test1 = 0x40; ++ test2 = 0x80; ++ } else { ++ test1 = 0x44; ++ test2 = 0x88; ++ } ++ ++ temp = dwc_rd32(dev, (volatile u32 __iomem *)(base_addr + 16)); ++ if ((temp & test1) != 0) { ++ dwc_debug(dev, "calling dwc_exit_hibernation() Debug=%01x\n", ++ temp); ++ ret = dwc_exit_hibernation(&dev->pcd, (temp & test2) != 0); ++ if (ret) ++ dwc_debug(dev, "dwc_exit_hibernation() returned %d\n", ++ ret); ++ } ++ ++ return 1; ++} ++ ++/** ++ * Routine to enable and disable power to the core. The custom PCIe "gasket" on ++ * the HAPS platform provides this capability through the R1 register at offset ++ * 4. ++ * ++ * Note that this is NOT the way a "real" device would control the power. This ++ * code is ONLY for testing hibernation on the Synopsys HAPS platform. ++ */ ++void dwc_usb3_power_ctl(dwc_usb3_device_t *dev, int on) ++{ ++ u32 temp, test; ++ u8 __iomem *base_addr; ++ int cnt; ++ ++ dwc_debug(dev, "%s(%1x)\n", __func__, on); ++ ++ if (dev->gasket_base) ++ base_addr = (u8 __iomem *)dev->gasket_base; ++ else ++ base_addr = (u8 __iomem *)dev->base + dev->gasket_ofs; ++ ++ if ((dev->hwparams3 & DWC_HWP3_SSPHY_IFC_BITS) == 0) ++ test = 0x30; ++ else ++ test = 0x33; ++ ++ if (on) { ++ temp = dwc_rd32(dev, (volatile u32 __iomem *)(base_addr + 4)); ++ dwc_debug(dev, "R1=%01x before write\n", temp); ++ temp &= ~0x3000; ++ dwc_wr32(dev, (volatile u32 __iomem *)(base_addr + 4), temp); ++ temp = dwc_rd32(dev, (volatile u32 __iomem *)(base_addr + 4)); ++ dwc_debug(dev, "R1=%01x after write\n", temp); ++ ++ /* Wait until both PMUs confirm that they have entered D0 */ ++ dwc_debug(dev, "Asked for D0 state, waiting for response\n"); ++ cnt = 0; ++ do { ++ udelay(1); ++ temp = dwc_rd32(dev, (volatile u32 __iomem *) ++ (base_addr + 16)); ++ if (++cnt > 10000000) { ++ cnt = 0; ++ dwc_debug(dev, "%01x\n", temp); ++ //break; ++ } ++ } while ((temp & test) != 0); ++ } else { ++ temp = dwc_rd32(dev, (volatile u32 __iomem *)(base_addr + 4)); ++ dwc_debug(dev, "R1=%01x before write\n", temp); ++ temp |= 0x3000; ++ dwc_wr32(dev, (volatile u32 __iomem *)(base_addr + 4), temp); ++ temp = dwc_rd32(dev, (volatile u32 __iomem *)(base_addr + 4)); ++ dwc_debug(dev, "R1=%01x after write\n", temp); ++ ++ /* Wait until both PMUs confirm that they have entered D3 */ ++ dwc_debug(dev, "Asked for D3 state, waiting for response\n"); ++ ++ cnt = 0; ++ do { ++ udelay(1); ++ temp = dwc_rd32(dev, (volatile u32 __iomem *) ++ (base_addr + 16)); ++ if (++cnt > 10000000) { ++ cnt = 0; ++ dwc_debug(dev, "%01x\n", temp); ++ //break; ++ } ++ } while ((temp & test) != test); ++ } ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/linux_pci.c b/drivers/usb/gadget/udc/hiudc3/linux_pci.c +new file mode 100644 +index 0000000..fd27cfa +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/linux_pci.c +@@ -0,0 +1,689 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/linux/linux_pci.c $ ++ * $Revision: #16 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * The driver module provides the initialization and cleanup entry ++ * points for the DWC_usb3 driver. This module will be dynamically installed ++ * after Linux is booted using the insmod command. When the module is ++ * installed, the driver_init function is called. When the module is ++ * removed (using rmmod), the driver_cleanup function is called. ++ * ++ * This module also defines a data structure for the driver, which is ++ * used in conjunction with the standard pci_dev structure. These ++ * structures allow the USB3 driver to comply with the standard Linux driver ++ * model in which devices and drivers are registered with a bus driver. This ++ * has the benefit that Linux can expose attributes of the driver and device ++ * in its special sysfs file system. Users can then read or write files in ++ * this file system to perform diagnostics on the driver components or the ++ * device. ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++#ifdef CONFIG_USB_OTG_DWC ++extern int dwc_otg3_irq(struct usb_otg *otg); ++extern void dwc_otg3_set_peripheral(struct usb_otg *otg, int yes); ++#endif ++ ++#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 ++#define PCI_DEVICE_ID_SYNOPSYS_SITKA 0xabcd ++#define PCI_DEVICE_ID_SYNOPSYS_HAPS_AXI 0xabce ++ ++static const char dwc_driver_name[] = "dwc_usb3"; ++ ++/* ++ * Hook to override the default Phy configuration in dwc_usb3_pcd_device_init() ++ * with a HAPS-specific one ++ */ ++static void haps_phy_config_hook(struct dwc_usb3_device *dev, int soft_reset, ++ int restore) ++{ ++ dwc_usb3_core_global_regs_t __iomem *global_regs = ++ dev->core_global_regs; ++ u32 temp; ++ ++ switch (dev->core_params->phy) { ++ case 3: // 16-bit UTMI+ SNPS Phy ++ temp = dwc_rd32(dev, &global_regs->gusb2phycfg[0]); ++ temp &= ~DWC_USB2PHYCFG_USB_TRD_TIM_BITS; ++ temp |= DWC_USB2PHYCFG_16B_PHY_IF_BIT; ++ temp |= 5 << DWC_USB2PHYCFG_USB_TRD_TIM_SHIFT; ++ dwc_wr32(dev, &global_regs->gusb2phycfg[0], temp); ++ break; ++ case 2: // 8-bit UTMI+ / ULPI TI or SNPS Phy ++ case 1: // old 8-bit UTMI+ SNPS Phy ++ temp = dwc_rd32(dev, &global_regs->gusb2phycfg[0]); ++ temp &= ~DWC_USB2PHYCFG_USB_TRD_TIM_BITS; ++ temp &= ~DWC_USB2PHYCFG_16B_PHY_IF_BIT; ++ temp |= 9 << DWC_USB2PHYCFG_USB_TRD_TIM_SHIFT; ++ dwc_wr32(dev, &global_regs->gusb2phycfg[0], temp); ++ break; ++ default: // RocketIO Phy ++ if (dev->core_params->usb2mode == 0) { ++ /* Set rx-eq, differential swing */ ++ dwc_wr32(dev, (volatile u32 __iomem *) ++ (dev->base + dev->gasket_ofs + 8), 0x41); ++#ifdef LECROY ++ /* Rx-detect for LeCroy */ ++ dwc_wr32(dev, (volatile u32 __iomem *) ++ (dev->base + dev->gasket_ofs + 4), 0x200); ++#else ++ dwc_wr32(dev, (volatile u32 __iomem *) ++ (dev->base + dev->gasket_ofs + 4), 0); ++#endif ++ } ++ } ++} ++ ++/** ++ * This function is the top level interrupt handler for the Common ++ * (Core and Device) interrupts. ++ */ ++static irqreturn_t dwc_usb3_common_irq(int irq, void *dev ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ , struct pt_regs *regs ++#endif ++ ) ++{ ++ dwc_usb3_device_t *usb3_dev = dev; ++ int retval = 0; ++ ++#ifdef CONFIG_USB_OTG_DWC ++ u32 gsts = 0; ++ int temp = usb3_dev->hibernate; ++ ++ /* Skip OTG IRQ handler if in hibernation */ ++ if (temp < DWC_HIBER_SLEEPING) { ++ gsts = dwc_rd32(usb3_dev, &usb3_dev->core_global_regs->gsts); ++ } else { ++ dwc_info(usb3_dev, "%s() possible OTG IRQ in hibernation\n", ++ __func__); ++ retval = 1; ++ } ++ ++ if (gsts & DWC_GSTS_OTG_EVT_PENDING_BIT || ++ gsts & DWC_GSTS_ADP_EVT_PENDING_BIT || ++ gsts & DWC_GSTS_BC_EVT_PENDING_BIT) { ++ struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ ++ if (!IS_ERR(phy)) { ++ dwc_info(usb3_dev, "%s() OTG IRQ\n", __func__); ++ dwc_info(usb3_dev, "gsts = %08x\n", gsts); ++ if (phy && phy->otg) ++ dwc_otg3_irq(phy->otg); ++ else ++ dwc_info(usb3_dev, "OTG IRQ but no OTG\n"); ++ usb_put_phy(phy); ++ } else { ++ dwc_info(usb3_dev, "%s() OTG IRQ but no PHY\n", ++ __func__); ++ } ++ ++ retval = 1; ++ } ++ ++ if (temp >= DWC_HIBER_SLEEPING || (gsts & DWC_GSTS_DEV_EVT_PENDING_BIT)) ++#endif ++ { ++ spin_lock(&usb3_dev->pcd.lock); ++ retval = dwc_usb3_irq(usb3_dev, irq); ++ spin_unlock(&usb3_dev->pcd.lock); ++ } ++ ++ return IRQ_RETVAL(retval); ++} ++ ++#ifdef DWC_UTE ++static void dwc_usb3_save_fifosiz_def_vals(dwc_usb3_device_t *dev) ++{ ++ unsigned i, size; ++ dwc_usb3_pcd_t *pcd; ++ ++ if (!dev) ++ return; ++ ++ pcd = &dev->pcd; ++ ++ for (i = 0; i < pcd->num_in_eps + 1; i++) { ++ size = dwc_rd32(dev, &dev->core_global_regs->gtxfifosiz[i]) & ++ DWC_FIFOSZ_DEPTH_BITS; ++ dwc_print(dev, "Saving %d TxFIFO default size: %d\n", i, size); ++ pcd->def_txf_size[i] = size; ++ } ++ ++ size = dwc_rd32(dev, &dev->core_global_regs->grxfifosiz[0]) & ++ DWC_FIFOSZ_DEPTH_BITS; ++ pcd->def_rxf_size = size; ++ ++ dwc_print(dev, "Saving RxFIFO default size: %d\n", size); ++} ++#endif ++ ++/** ++ * This function is called when a pci_dev is unregistered with the ++ * dwc_usb3_driver. This happens, for example, when the rmmod command is ++ * executed. The device may or may not be electrically present. If it is ++ * present, the driver stops device processing. Any resources used on behalf ++ * of this device are freed. ++ * ++ * @param dev pci_dev struct ++ */ ++static void dwc_usb3_driver_remove(struct pci_dev *dev) ++{ ++ dwc_usb3_device_t *usb3_dev = pci_get_drvdata(dev); ++ u32 *event_buf; ++ dwc_dma_t event_buf_dma; ++ ++#ifdef CONFIG_USB_OTG_DWC ++ struct usb_phy *phy; ++#endif ++ ++ dev_dbg(&dev->dev, "%s(%p)\n", __func__, dev); ++ ++ if (!usb3_dev) { ++ /* Memory allocation for the dwc_usb3_device failed */ ++ dev_dbg(&dev->dev, "%s: usb3_dev NULL\n", __func__); ++ goto disable; ++ } ++ ++#ifdef CONFIG_USB_OTG_DWC ++ /* Switch otg to peripheral mode */ ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ if (!IS_ERR(phy)) { ++ if (phy && phy->otg) ++ dwc_otg3_set_peripheral(phy->otg, 1); ++ usb_put_phy(phy); ++ } ++#endif ++ ++ /* ++ * Free the IRQ ++ */ ++ if (usb3_dev->cmn_irq_installed) { ++ usb3_dev->cmn_irq_installed = 0; ++ free_irq(dev->irq, usb3_dev); ++ } ++ ++ if (usb3_dev->pme_thread) { ++ kthread_stop(usb3_dev->pme_thread); ++ usb3_dev->pme_thread = NULL; ++ } ++ ++ if (usb3_dev->pcd_initialized) { ++ usb3_dev->pcd_initialized = 0; ++ dwc_usb3_pcd_remove(usb3_dev); ++ } ++ ++ if (usb3_dev->gadget_initialized) { ++ usb3_dev->gadget_initialized = 0; ++ dwc_usb3_gadget_remove(usb3_dev, &dev->dev); ++ } ++ ++ if (usb3_dev->cmn_initialized) { ++ usb3_dev->cmn_initialized = 0; ++ dwc_usb3_pcd_common_remove(usb3_dev); ++ } ++ ++ event_buf = usb3_dev->event_buf[0]; ++ event_buf_dma = usb3_dev->event_buf_dma[0]; ++ if (event_buf) { ++ dwc_usb3_dis_flush_eventbuf_intr(usb3_dev, 0); ++ usb3_dev->event_buf[0] = NULL; ++ dma_free_coherent(NULL, DWC_EVENT_BUF_SIZE * sizeof(u32), ++ event_buf, event_buf_dma); ++ } ++ ++ if (usb3_dev->sysfs_initialized) { ++ dwc_usb3_remove_dev_files(&dev->dev); ++ usb3_dev->sysfs_initialized = 0; ++ } ++ ++ /* ++ * Clear the drvdata pointer. ++ */ ++ pci_set_drvdata(dev, NULL); ++ ++ /* ++ * Return the memory. ++ */ ++ if (usb3_dev->base) ++ iounmap((void __iomem *)usb3_dev->base); ++ if (usb3_dev->rsrc_start) ++ release_mem_region(usb3_dev->rsrc_start, usb3_dev->rsrc_len); ++ if (usb3_dev->gasket_base) ++ iounmap((void __iomem *)usb3_dev->gasket_base); ++ if (usb3_dev->gasket_start) ++ release_mem_region(usb3_dev->gasket_start, usb3_dev->gasket_len); ++ ++ kfree(usb3_dev); ++ ++disable: ++ //pci_disable_device(dev); ++ return; ++} ++ ++/** ++ * This function is called when a pci_dev is bound to a ++ * dwc_usb3_driver. It creates the driver components required to ++ * control the device (CIL and PCD) and it initializes the ++ * device. The driver components are stored in a dwc_usb3_device ++ * structure. A reference to the dwc_usb3_device is saved in the ++ * pci_dev. This allows the driver to access the dwc_usb3_device ++ * structure on subsequent calls to driver methods for this device. ++ * ++ * @param dev pci_dev struct ++ * @param id pci_dev_id struct ++ */ ++static int dwc_usb3_driver_probe(struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ dwc_usb3_device_t *usb3_dev; ++ u32 addr_ofs = 0xc000; ++ int retval = 0; ++ ++#ifdef CONFIG_USB_OTG_DWC ++ struct usb_phy *phy; ++ struct usb_otg *otg; ++#endif ++ ++ printk(KERN_DEBUG "%s: driver_probe()\n", dwc_driver_name); ++ dev_dbg(&dev->dev, "dwc_usb3_driver_probe(%p)\n", dev); ++ ++ dev_dbg(&dev->dev, "start=0x%08x\n", ++ (unsigned)pci_resource_start(dev, 0)); ++ dev_dbg(&dev->dev, "len=0x%08x\n", (unsigned)pci_resource_len(dev, 0)); ++ ++#ifndef DWC_BAR2_GASKET_REG ++ if (id->device == PCI_DEVICE_ID_SYNOPSYS_HAPS_AXI) ++#endif ++ { ++ dev_dbg(&dev->dev, "start2=0x%08x\n", ++ (unsigned)pci_resource_start(dev, 2)); ++ dev_dbg(&dev->dev, "len2=0x%08x\n", ++ (unsigned)pci_resource_len(dev, 2)); ++ } ++ ++ if (!id) { ++ dev_err(&dev->dev, "id parameter NULL!\n"); ++ return -EINVAL; ++ } ++ ++ if (pci_enable_device(dev) < 0) { ++ dev_err(&dev->dev, "pci_enable_device() failed!\n"); ++ return -ENODEV; ++ } ++ ++ dev->current_state = PCI_D0; ++ dev->dev.power.power_state = PMSG_ON; ++ ++ if (!dev->irq) { ++ dev_err(&dev->dev, "no IRQ for PCI device!\n"); ++ retval = -ENODEV; ++ goto fail; ++ } ++ ++ usb3_dev = kmalloc(sizeof(dwc_usb3_device_t), GFP_KERNEL); ++ if (!usb3_dev) { ++ dev_err(&dev->dev, "kmalloc of dwc_usb3_device failed!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ memset(usb3_dev, 0, sizeof(*usb3_dev)); ++ usb3_dev->dev = &dev->dev; ++ usb3_dev->rsrc_start = pci_resource_start(dev, 0); ++ usb3_dev->rsrc_len = pci_resource_len(dev, 0); ++ ++#ifndef DWC_BAR2_GASKET_REG ++ if (id->device == PCI_DEVICE_ID_SYNOPSYS_HAPS_AXI) ++#endif ++ { ++ usb3_dev->gasket_start = pci_resource_start(dev, 2); ++ usb3_dev->gasket_len = pci_resource_len(dev, 2); ++ } ++ ++ /* ++ * Initialize driver data to point to the global DWC_usb3 ++ * Device structure. ++ */ ++ pci_set_drvdata(dev, usb3_dev); ++ dev_dbg(&dev->dev, "dwc_usb3_device=0x%p\n", usb3_dev); ++ ++ if (!usb3_dev->rsrc_start || !usb3_dev->rsrc_len) { ++ dev_err(&dev->dev, "bad PCI resource!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++#ifndef DWC_BAR2_GASKET_REG ++ if (id->device == PCI_DEVICE_ID_SYNOPSYS_HAPS_AXI) ++#endif ++ { ++ if (!usb3_dev->gasket_start || !usb3_dev->gasket_len) { ++ dev_err(&dev->dev, "bad PCI resource 2!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ } ++ ++ /* ++ * Map the DWC_usb3 Core memory into virtual address space. ++ */ ++ if (!request_mem_region(usb3_dev->rsrc_start, usb3_dev->rsrc_len, ++ "usb3")) { ++ dev_err(&dev->dev, "request_mem_region() failed!\n"); ++ ++ /* Flag for dwc_usb3_driver_remove() that we failed */ ++ usb3_dev->rsrc_start = 0; ++ ++ retval = -EBUSY; ++ goto fail; ++ } ++ ++ usb3_dev->base = ioremap_nocache(usb3_dev->rsrc_start, ++ usb3_dev->rsrc_len); ++ if (!usb3_dev->base) { ++ dev_err(&dev->dev, "ioremap_nocache() failed!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ dev_dbg(&dev->dev, "base=%p\n", usb3_dev->base); ++ ++#ifndef DWC_BAR2_GASKET_REG ++ if (id->device == PCI_DEVICE_ID_SYNOPSYS_HAPS_AXI) ++#endif ++ { ++ if (!request_mem_region(usb3_dev->gasket_start, ++ usb3_dev->gasket_len, "usb3")) { ++ dev_err(&dev->dev, "request_mem_region() 2 failed!\n"); ++ ++ /* Flag for dwc_usb3_driver_remove() that we failed */ ++ usb3_dev->gasket_start = 0; ++ ++ retval = -EBUSY; ++ goto fail; ++ } ++ ++ usb3_dev->gasket_base = ioremap_nocache(usb3_dev->gasket_start, ++ usb3_dev->gasket_len); ++ if (!usb3_dev->gasket_base) { ++ dev_err(&dev->dev, "ioremap_nocache() 2 failed!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ dev_dbg(&dev->dev, "gasket_base=%p\n", usb3_dev->gasket_base); ++ } ++ ++ if (dwc_usb3_module_params.newcsr) ++ usb3_dev->gasket_ofs = 0xf000; ++ else ++ usb3_dev->gasket_ofs = 0x80000; ++ ++#ifdef CONFIG_USB_OTG_DWC ++ /* Switch otg to peripheral mode */ ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ if (IS_ERR(phy) || !phy) { ++ dev_err(&dev->dev, "OTG PHY not available!\n"); ++ if (!IS_ERR(phy)) ++ usb_put_phy(phy); ++ retval = -ENODEV; ++ goto fail; ++ } ++ ++ otg = phy->otg; ++ if (!otg) { ++ dev_err(&dev->dev, "OTG not available!\n"); ++ usb_put_phy(phy); ++ retval = -ENODEV; ++ goto fail; ++ } ++ ++ phy->io_priv = (void __iomem *)usb3_dev->base; ++ dwc_otg3_set_peripheral(otg, 1); ++ usb_put_phy(phy); ++#endif ++ ++ retval = dwc_usb3_create_dev_files(&dev->dev); ++ if (retval) { ++ dev_err(&dev->dev, "sysfs initialization failed!\n"); ++ goto fail; ++ } ++ ++ usb3_dev->sysfs_initialized = 1; ++ ++ /* ++ * Checks that this device is really a DWC_usb3 controller. Also saves ++ * the SNPSID register value in usb3_dev->snpsid for later use by the ++ * PCD. ++ */ ++ retval = dwc_usb3_pcd_check_snpsid(usb3_dev, addr_ofs); ++ if (retval) { ++ dev_err(&dev->dev, "bad value for SNPSID!\n"); ++ goto fail; ++ } ++ ++ if (dwc_usb3_module_params.newcore && usb3_dev->snpsid < 0x5533109a) ++ usb3_dev->snpsid = 0x5533109a; ++ ++ /* ++ * Up to 32 Event Buffers are supported by the hardware, ++ * but we only use 1 ++ */ ++ usb3_dev->event_buf[0] = dma_alloc_coherent(NULL, ++ DWC_EVENT_BUF_SIZE * sizeof(u32), ++ &usb3_dev->event_buf_dma[0], ++ GFP_KERNEL | GFP_DMA32); ++ if (!usb3_dev->event_buf[0]) { ++ dev_err(&dev->dev, "allocation of event_buf failed!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ /* ++ * Add our hook to override the default Phy register setup ++ */ ++ usb3_dev->phy_config_hook = haps_phy_config_hook; ++ ++ /* ++ * Initialize the DWC_usb3 Core. ++ */ ++ retval = dwc_usb3_pcd_common_init(usb3_dev, usb3_dev->base + addr_ofs, ++ &dwc_usb3_module_params); ++ if (retval) { ++ dev_err(&dev->dev, "CIL initialization failed!\n"); ++ goto fail; ++ } ++ ++ usb3_dev->cmn_initialized = 1; ++ ++#ifdef DWC_UTE ++ dwc_usb3_save_fifosiz_def_vals(usb3_dev); ++#endif ++ ++ spin_lock_init(&usb3_dev->pcd.lock); ++ ++ /* ++ * Initialize the Gadget ++ */ ++ retval = dwc_usb3_gadget_init(usb3_dev, &dev->dev); ++ if (retval) { ++ dev_err(&dev->dev, "gadget initialization failed!\n"); ++ goto fail; ++ } ++ ++ usb3_dev->gadget_initialized = 1; ++ ++ /* ++ * Initialize the PCD ++ */ ++ retval = dwc_usb3_pcd_init(usb3_dev); ++ if (retval) { ++ dev_err(&dev->dev, "PCD initialization failed!\n"); ++ goto fail; ++ } ++ ++ usb3_dev->pcd_initialized = 1; ++ ++ /* Allocate the test mode tasklet */ ++ tasklet_init(&usb3_dev->pcd.test_mode_tasklet, ++ dwc_usb3_pcd_do_test_mode, ++ (unsigned long)&usb3_dev->pcd); ++ ++ /* Start the hibernation thread */ ++ usb3_dev->pme_thread = kthread_run(dwc_wait_pme_thread, &usb3_dev->pcd, ++ "pmethr"); ++ if (IS_ERR(usb3_dev->pme_thread)) { ++ retval = PTR_ERR(usb3_dev->pme_thread); ++ usb3_dev->pme_thread = NULL; ++ goto fail; ++ } ++ ++ /* ++ * Install the interrupt handler for the common interrupts. ++ */ ++ dev_dbg(&dev->dev, "registering (common) handler for irq%d\n", ++ dev->irq); ++ retval = request_irq(dev->irq, dwc_usb3_common_irq, ++ IRQF_SHARED | IRQF_DISABLED, ++ dwc_driver_name, usb3_dev); ++ if (retval) { ++ dev_err(&dev->dev, "request of irq%d failed!\n", dev->irq); ++ goto fail; ++ } ++ ++ usb3_dev->cmn_irq_installed = 1; ++#if 0 ++ if (dwc_usb3_module_params.hibernate && ++ (usb3_dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&usb3_dev->pcd.lock, flags); ++ dwc_enter_hibernation(&usb3_dev->pcd, 0); ++ spin_unlock_irqrestore(&usb3_dev->pcd.lock, flags); ++ } ++#endif ++ return 0; ++ ++fail: ++ dwc_usb3_driver_remove(dev); ++ return retval; ++} ++ ++static const struct pci_device_id pci_ids[] = { ++ { ++ /* The Synopsys PCIe card */ ++ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, ++ PCI_DEVICE_ID_SYNOPSYS_SITKA), ++ .driver_data = (unsigned long)0xdeadbeef, ++ }, ++ { ++ /* The Synopsys HAPS PCIe card with AXI, with gasket registers ++ * moved to 2nd BAR ++ */ ++ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, ++ PCI_DEVICE_ID_SYNOPSYS_HAPS_AXI), ++ .driver_data = (unsigned long)0xdeadbeef, ++ }, ++ { 0, } /* end: all zeroes */ ++}; ++MODULE_DEVICE_TABLE(pci, pci_ids); ++ ++/** ++ * This structure defines the methods to be called by a bus driver ++ * during the lifecycle of a device on that bus. Both drivers and ++ * devices are registered with a bus driver. The bus driver matches ++ * devices to drivers based on information in the device and driver ++ * structures. ++ * ++ * The probe function is called when the bus driver matches a device ++ * to this driver. The remove function is called when a device is ++ * unregistered with the bus driver. ++ */ ++static struct pci_driver dwc_usb3_driver = { ++ .name = (char *)dwc_driver_name, ++ .id_table = pci_ids, ++ .probe = dwc_usb3_driver_probe, ++ .remove = dwc_usb3_driver_remove, ++ .driver = { ++ .name = (char *)dwc_driver_name, ++ }, ++}; ++ ++/** ++ * This function is called when the DWC_usb3 driver is loaded into the kernel ++ * with the insmod command. It registers the dwc_usb3_driver structure with the ++ * appropriate bus driver. This will cause the dwc_usb3_driver_probe function ++ * to be called. In addition, the bus driver will automatically expose ++ * attributes defined for the device and driver in the special sysfs file ++ * system. ++ */ ++static int __init dwc_usb3_driver_init(void) ++{ ++ int retval; ++ ++ printk(KERN_INFO "%s: %s version %s\n", dwc_driver_name, ++ DWC_DRIVER_DESC, DWC_DRIVER_VERSION); ++ ++ retval = pci_register_driver(&dwc_usb3_driver); ++ if (retval < 0) { ++ printk(KERN_ERR "%s retval=%d\n", __func__, retval); ++ return retval; ++ } ++ ++ printk(KERN_INFO "%s: module installed\n", dwc_driver_name); ++ return retval; ++} ++module_init(dwc_usb3_driver_init); ++ ++/** ++ * This function is called when the DWC_usb3 driver is removed from the kernel ++ * with the rmmod command. The driver unregisters itself with its bus driver. ++ * ++ */ ++static void __exit dwc_usb3_driver_exit(void) ++{ ++ printk(KERN_DEBUG "%s: driver_exit()\n", dwc_driver_name); ++ ++ pci_unregister_driver(&dwc_usb3_driver); ++ ++ printk(KERN_INFO "%s: module removed\n", dwc_driver_name); ++} ++module_exit(dwc_usb3_driver_exit); +diff --git a/drivers/usb/gadget/udc/hiudc3/linux_plat.c b/drivers/usb/gadget/udc/hiudc3/linux_plat.c +new file mode 100644 +index 0000000..3102844 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/linux_plat.c +@@ -0,0 +1,701 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/linux/linux_plat.c $ ++ * $Revision: #16 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * The driver module provides the initialization and cleanup entry ++ * points for the DWC_usb3 driver. This module will be dynamically installed ++ * after Linux is booted using the insmod command. When the module is ++ * installed, the driver_init function is called. When the module is ++ * removed (using rmmod), the driver_cleanup function is called. ++ * ++ * This module also defines a data structure for the driver, which is ++ * used in conjunction with the standard pci_dev structure. These ++ * structures allow the USB3 driver to comply with the standard Linux driver ++ * model in which devices and drivers are registered with a bus driver. This ++ * has the benefit that Linux can expose attributes of the driver and device ++ * in its special sysfs file system. Users can then read or write files in ++ * this file system to perform diagnostics on the driver components or the ++ * device. ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++#include <linux/of.h> ++#include <mach/io.h> ++//#include "../../host/hiusb.h" ++ ++#ifndef __devinit ++# define __devinit ++# define __devexit ++# define __devexit_p(x) x ++#endif ++ ++#ifdef CONFIG_USB_OTG_DWC ++extern int dwc_otg3_irq(struct usb_otg *otg); ++extern void dwc_otg3_set_peripheral(struct usb_otg *otg, int yes); ++#endif ++ ++//#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) ++//#if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0) ++/* Enable this to replace the DWC3 driver in recent Linux kernels */ ++# define DWC_REPLACE_DWC3 ++//#endif ++ ++/* Enable this to support the Synopsys Xplorer platform */ ++//#define DWC_XPLORER ++ ++static const char dwc_driver_name[] = "dwc_usb3"; ++ ++/* ++ * Hook to override the default Phy configuration in dwc_usb3_pcd_device_init() ++ * with a HAPS-specific one ++ */ ++static void haps_phy_config_hook(struct dwc_usb3_device *dev, int soft_reset, ++ int restore) ++{ ++ dwc_usb3_core_global_regs_t __iomem *global_regs = ++ dev->core_global_regs; ++ u32 temp; ++ ++ switch (dev->core_params->phy) { ++ case 3: // 16-bit UTMI+ SNPS Phy ++ temp = dwc_rd32(dev, &global_regs->gusb2phycfg[0]); ++ temp &= ~DWC_USB2PHYCFG_USB_TRD_TIM_BITS; ++ temp |= DWC_USB2PHYCFG_16B_PHY_IF_BIT; ++ temp |= 5 << DWC_USB2PHYCFG_USB_TRD_TIM_SHIFT; ++ dwc_wr32(dev, &global_regs->gusb2phycfg[0], temp); ++ break; ++ case 2: // 8-bit UTMI+ / ULPI TI or SNPS Phy ++ case 1: // old 8-bit UTMI+ SNPS Phy ++ temp = dwc_rd32(dev, &global_regs->gusb2phycfg[0]); ++ temp &= ~DWC_USB2PHYCFG_USB_TRD_TIM_BITS; ++ temp &= ~DWC_USB2PHYCFG_16B_PHY_IF_BIT; ++ temp |= 9 << DWC_USB2PHYCFG_USB_TRD_TIM_SHIFT; ++ dwc_wr32(dev, &global_regs->gusb2phycfg[0], temp); ++ break; ++ default: // RocketIO Phy ++ if (dev->core_params->usb2mode == 0) { ++ /* Set rx-eq, differential swing */ ++ dwc_wr32(dev, (volatile u32 __iomem *) ++ (dev->base + dev->gasket_ofs + 8), 0x41); ++#ifdef LECROY ++ /* Rx-detect for LeCroy */ ++ dwc_wr32(dev, (volatile u32 __iomem *) ++ (dev->base + dev->gasket_ofs + 4), 0x200); ++#else ++ dwc_wr32(dev, (volatile u32 __iomem *) ++ (dev->base + dev->gasket_ofs + 4), 0); ++#endif ++ } ++ } ++} ++ ++/** ++ * This function is the top level interrupt handler for the Common ++ * (Core and Device) interrupts. ++ */ ++static irqreturn_t dwc_usb3_common_irq(int irq, void *dev ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ , struct pt_regs *regs ++#endif ++ ) ++{ ++ dwc_usb3_device_t *usb3_dev = dev; ++ int retval = 0; ++ ++#ifdef CONFIG_USB_OTG_DWC ++ u32 gsts = 0; ++ int temp = usb3_dev->hibernate; ++ ++ /* Skip OTG IRQ handler if in hibernation */ ++ if (temp < DWC_HIBER_SLEEPING) { ++ gsts = dwc_rd32(usb3_dev, &usb3_dev->core_global_regs->gsts); ++ } else { ++ dwc_info(usb3_dev, "%s() possible OTG IRQ in hibernation\n", ++ __func__); ++ retval = 1; ++ } ++ ++ if (gsts & DWC_GSTS_OTG_EVT_PENDING_BIT || ++ gsts & DWC_GSTS_ADP_EVT_PENDING_BIT || ++ gsts & DWC_GSTS_BC_EVT_PENDING_BIT) { ++ struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ ++ if (!IS_ERR(phy)) { ++ dwc_info(usb3_dev, "%s() OTG IRQ\n", __func__); ++ dwc_info(usb3_dev, "gsts = %08x\n", gsts); ++ if (phy && phy->otg) ++ dwc_otg3_irq(phy->otg); ++ else ++ dwc_info(usb3_dev, "OTG IRQ but no OTG\n"); ++ usb_put_phy(phy); ++ } else { ++ dwc_info(usb3_dev, "%s() OTG IRQ but no PHY\n", ++ __func__); ++ } ++ ++ retval = 1; ++ } ++ ++ if (temp >= DWC_HIBER_SLEEPING || (gsts & DWC_GSTS_DEV_EVT_PENDING_BIT)) ++#endif ++ { ++ spin_lock(&usb3_dev->pcd.lock); ++ retval = dwc_usb3_irq(usb3_dev, irq); ++ spin_unlock(&usb3_dev->pcd.lock); ++ } ++ ++ return IRQ_RETVAL(retval); ++} ++ ++#ifdef DWC_UTE ++static void dwc_usb3_save_fifosiz_def_vals(dwc_usb3_device_t *dev) ++{ ++ unsigned i, size; ++ dwc_usb3_pcd_t *pcd; ++ ++ if (!dev) ++ return; ++ ++ pcd = &dev->pcd; ++ ++ for (i = 0; i < pcd->num_in_eps + 1; i++) { ++ size = dwc_rd32(dev, &dev->core_global_regs->gtxfifosiz[i]) & ++ DWC_FIFOSZ_DEPTH_BITS; ++ dwc_print(dev, "Saving %d TxFIFO default size: %d\n", i, size); ++ pcd->def_txf_size[i] = size; ++ } ++ ++ size = dwc_rd32(dev, &dev->core_global_regs->grxfifosiz[0]) & ++ DWC_FIFOSZ_DEPTH_BITS; ++ pcd->def_rxf_size = size; ++ ++ dwc_print(dev, "Saving RxFIFO default size: %d\n", size); ++} ++#endif ++ ++/** ++ * This function is called when a platform_device is unregistered with the ++ * dwc_usb3_driver. This happens, for example, when the rmmod command is ++ * executed. The device may or may not be electrically present. If it is ++ * present, the driver stops device processing. Any resources used on behalf ++ * of this device are freed. ++ * ++ * @param dev platform_device struct ++ */ ++static int dwc_usb3_platform_remove(struct platform_device *dev) ++{ ++ dwc_usb3_device_t *usb3_dev = platform_get_drvdata(dev); ++ u32 *event_buf; ++ dwc_dma_t event_buf_dma; ++ ++#ifdef CONFIG_USB_OTG_DWC ++ struct usb_phy *phy; ++#endif ++ ++ dev_dbg(&dev->dev, "%s(%p)\n", __func__, dev); ++ ++ if (!usb3_dev) { ++ /* Memory allocation for the dwc_usb3_device failed */ ++ dev_dbg(&dev->dev, "%s: usb3_dev NULL\n", __func__); ++ goto disable; ++ } ++ ++#ifdef CONFIG_USB_OTG_DWC ++ /* Switch otg to peripheral mode */ ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ if (!IS_ERR(phy)) { ++ if (phy && phy->otg) ++ dwc_otg3_set_peripheral(phy->otg, 1); ++ usb_put_phy(phy); ++ } ++#endif ++ ++ /* ++ * Free the IRQ ++ */ ++ if (usb3_dev->cmn_irq_installed) { ++ usb3_dev->cmn_irq_installed = 0; ++ free_irq(usb3_dev->irq, usb3_dev); ++ } ++ ++ if (usb3_dev->pme_thread) { ++ kthread_stop(usb3_dev->pme_thread); ++ usb3_dev->pme_thread = NULL; ++ } ++ ++ if (usb3_dev->pcd_initialized) { ++ usb3_dev->pcd_initialized = 0; ++ dwc_usb3_pcd_remove(usb3_dev); ++ } ++ ++ if (usb3_dev->gadget_initialized) { ++ usb3_dev->gadget_initialized = 0; ++ dwc_usb3_gadget_remove(usb3_dev, &dev->dev); ++ } ++ ++ if (usb3_dev->cmn_initialized) { ++ usb3_dev->cmn_initialized = 0; ++ dwc_usb3_pcd_common_remove(usb3_dev); ++ } ++ ++ event_buf = usb3_dev->event_buf[0]; ++ event_buf_dma = usb3_dev->event_buf_dma[0]; ++ if (event_buf) { ++ dwc_usb3_dis_flush_eventbuf_intr(usb3_dev, 0); ++ usb3_dev->event_buf[0] = NULL; ++ dma_free_coherent(NULL, DWC_EVENT_BUF_SIZE * sizeof(u32), ++ event_buf, event_buf_dma); ++ } ++ ++ if (usb3_dev->sysfs_initialized) { ++ dwc_usb3_remove_dev_files(&dev->dev); ++ usb3_dev->sysfs_initialized = 0; ++ } ++ ++ /* ++ * Clear the drvdata pointer. ++ */ ++ platform_set_drvdata(dev, NULL); ++ ++ /* ++ * Return the memory. ++ */ ++ if (usb3_dev->base) ++ iounmap((void __iomem *)usb3_dev->base); ++ if (usb3_dev->rsrc_start) ++ release_mem_region(usb3_dev->rsrc_start, usb3_dev->rsrc_len); ++ if (usb3_dev->gasket_base) ++ iounmap((void __iomem *)usb3_dev->gasket_base); ++ if (usb3_dev->gasket_start) ++ release_mem_region(usb3_dev->gasket_start, usb3_dev->gasket_len); ++ ++ kfree(usb3_dev); ++ ++disable: ++ return 0; ++} ++ ++static u64 dwc3_vexpress_dma_mask = DMA_BIT_MASK(32); ++ ++/** ++ * This function is called when a platform_device is bound to a ++ * dwc_usb3_driver. It creates the driver components required to ++ * control the device (CIL and PCD) and it initializes the ++ * device. The driver components are stored in a dwc_usb3_device ++ * structure. A reference to the dwc_usb3_device is saved in the ++ * platform_device. This allows the driver to access the dwc_usb3_device ++ * structure on subsequent calls to driver methods for this device. ++ * ++ * @param dev platform_device struct ++ */ ++static int __devinit dwc_usb3_platform_probe(struct platform_device *dev) ++{ ++#if 0 ++ int reg; ++ reg = dwc_rd32(dev, IO_ADDRESS(0x10180430)); ++ reg = dwc_rd32(dev, IO_ADDRESS(0x1018c110)); ++ reg &= ~(3 << 12); ++ reg |= (2 << 12); ++ dwc_wr32(dev, IO_ADDRESS(0x1018c110), reg); ++#endif ++ dwc_usb3_device_t *usb3_dev; ++ struct resource *res_mem; ++ u32 addr_ofs = 0xc000; ++ int irq, retval = 0; ++ ++#ifdef DWC_BAR2_GASKET_REG ++ struct resource *res_mem2; ++#endif ++ ++#ifdef CONFIG_USB_OTG_DWC ++ struct usb_phy *phy; ++ struct usb_otg *otg; ++#endif ++ ++ printk(KERN_DEBUG "%s: platform_probe()\n", dwc_driver_name); ++ dev_dbg(&dev->dev, "dwc_usb3_platform_probe(%p)\n", dev); ++ ++ if (!dev->dev.dma_mask) ++ dev->dev.dma_mask = &dwc3_vexpress_dma_mask; ++ ++ irq = platform_get_irq(dev, 0); ++ if (irq < 0) { ++ dev_err(&dev->dev, "no irq provided"); ++ return irq; ++ } ++ ++ res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ if (!res_mem) { ++ dev_err(&dev->dev, "no memory resource provided"); ++ return -ENXIO; ++ } ++ ++#ifdef DWC_BAR2_GASKET_REG ++ res_mem2 = platform_get_resource(dev, IORESOURCE_MEM, 2); ++ if (!res_mem2) { ++ dev_err(&dev->dev, "no memory resource 2 provided"); ++ return -ENXIO; ++ } ++#endif ++ ++ usb3_dev = kmalloc(sizeof(dwc_usb3_device_t), GFP_KERNEL); ++ if (!usb3_dev) { ++ dev_err(&dev->dev, "kmalloc of dwc_usb3_device failed!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ memset(usb3_dev, 0, sizeof(*usb3_dev)); ++ usb3_dev->dev = &dev->dev; ++ usb3_dev->irq = irq; ++ ++ /* ++ * Initialize driver data to point to the global DWC_usb3 ++ * Device structure. ++ */ ++ platform_set_drvdata(dev, usb3_dev); ++ dev_dbg(&dev->dev, "dwc_usb3_device=0x%p\n", usb3_dev); ++ ++ /* ++ * Map the DWC_usb3 Core memory into virtual address space. ++ */ ++#if 0 ++ if (!request_mem_region(res_mem->start, resource_size(res_mem), ++ "usb3")) { ++ dev_err(&dev->dev, "request_mem_region() failed!\n"); ++ retval = -EBUSY; ++ goto fail; ++ } ++#endif ++ usb3_dev->rsrc_start = res_mem->start; ++ usb3_dev->rsrc_len = resource_size(res_mem); ++ ++ usb3_dev->base = ioremap_nocache(usb3_dev->rsrc_start, ++ usb3_dev->rsrc_len); ++ if (!usb3_dev->base) { ++ dev_err(&dev->dev, "ioremap_nocache() failed!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ dev_dbg(&dev->dev, "base=%p\n", usb3_dev->base); ++ ++#ifdef DWC_BAR2_GASKET_REG ++ if (!request_mem_region(res_mem2->start, resource_size(res_mem2), ++ "usb3")) { ++ dev_err(&dev->dev, "request_mem_region() 2 failed!\n"); ++ retval = -EBUSY; ++ goto fail; ++ } ++ ++ usb3_dev->gasket_start = res_mem2->start; ++ usb3_dev->gasket_len = resource_size(res_mem2); ++ ++ usb3_dev->gasket_base = ioremap_nocache(usb3_dev->gasket_start, ++ usb3_dev->gasket_len); ++ if (!usb3_dev->gasket_base) { ++ dev_err(&dev->dev, "ioremap_nocache() 2 failed!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ dev_dbg(&dev->dev, "gasket_base=%p\n", usb3_dev->gasket_base); ++#endif ++ ++ if (dwc_usb3_module_params.newcsr) ++ usb3_dev->gasket_ofs = 0xf000; ++ else ++ usb3_dev->gasket_ofs = 0x80000; ++ ++#ifdef CONFIG_USB_OTG_DWC ++ /* Switch otg to peripheral mode */ ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ if (IS_ERR(phy) || !phy) { ++ dev_err(&dev->dev, "OTG PHY not available!\n"); ++ if (!IS_ERR(phy)) ++ usb_put_phy(phy); ++ retval = -ENODEV; ++ goto fail; ++ } ++ ++ otg = phy->otg; ++ if (!otg) { ++ dev_err(&dev->dev, "OTG not available!\n"); ++ usb_put_phy(phy); ++ retval = -ENODEV; ++ goto fail; ++ } ++ ++ phy->io_priv = (void __iomem *)usb3_dev->base; ++ dwc_otg3_set_peripheral(otg, 1); ++ usb_put_phy(phy); ++#endif ++ ++ retval = dwc_usb3_create_dev_files(&dev->dev); ++ if (retval) { ++ dev_err(&dev->dev, "sysfs initialization failed!\n"); ++ goto fail; ++ } ++ ++ usb3_dev->sysfs_initialized = 1; ++ ++ /* ++ * Checks that this device is really a DWC_usb3 controller. Also saves ++ * the SNPSID register value in usb3_dev->snpsid for later use by the ++ * PCD. ++ */ ++ retval = dwc_usb3_pcd_check_snpsid(usb3_dev, addr_ofs); ++ if (retval) { ++ dev_err(&dev->dev, "bad value for SNPSID!\n"); ++ goto fail; ++ } ++ ++ if (dwc_usb3_module_params.newcore && usb3_dev->snpsid < 0x5533109a) ++ usb3_dev->snpsid = 0x5533109a; ++ ++ /* ++ * Up to 32 Event Buffers are supported by the hardware, ++ * but we only use 1 ++ */ ++ usb3_dev->event_buf[0] = dma_alloc_coherent(NULL, ++ DWC_EVENT_BUF_SIZE * sizeof(u32), ++ &usb3_dev->event_buf_dma[0], ++ GFP_KERNEL | GFP_DMA32); ++ if (!usb3_dev->event_buf[0]) { ++ dev_err(&dev->dev, "allocation of event_buf failed!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ /* ++ * Add our hook to override the default Phy register setup ++ */ ++ usb3_dev->phy_config_hook = haps_phy_config_hook; ++ ++ /* ++ * Initialize the DWC_usb3 Core. ++ */ ++ retval = dwc_usb3_pcd_common_init(usb3_dev, usb3_dev->base + addr_ofs, ++ &dwc_usb3_module_params); ++ if (retval) { ++ dev_err(&dev->dev, "CIL initialization failed!\n"); ++ goto fail; ++ } ++ ++ usb3_dev->cmn_initialized = 1; ++ ++#ifdef DWC_UTE ++ dwc_usb3_save_fifosiz_def_vals(usb3_dev); ++#endif ++ ++ spin_lock_init(&usb3_dev->pcd.lock); ++ ++ /* ++ * Initialize the Gadget ++ */ ++ retval = dwc_usb3_gadget_init(usb3_dev, &dev->dev); ++ if (retval) { ++ dev_err(&dev->dev, "gadget initialization failed!\n"); ++ goto fail; ++ } ++ ++ usb3_dev->gadget_initialized = 1; ++ ++ /* ++ * Initialize the PCD ++ */ ++ retval = dwc_usb3_pcd_init(usb3_dev); ++ if (retval) { ++ dev_err(&dev->dev, "PCD initialization failed!\n"); ++ goto fail; ++ } ++ ++ usb3_dev->pcd_initialized = 1; ++ ++ /* Allocate the test mode tasklet */ ++ tasklet_init(&usb3_dev->pcd.test_mode_tasklet, ++ dwc_usb3_pcd_do_test_mode, ++ (unsigned long)&usb3_dev->pcd); ++ ++ /* Start the hibernation thread */ ++ usb3_dev->pme_thread = kthread_run(dwc_wait_pme_thread, &usb3_dev->pcd, ++ "pmethr"); ++ if (IS_ERR(usb3_dev->pme_thread)) { ++ retval = PTR_ERR(usb3_dev->pme_thread); ++ usb3_dev->pme_thread = NULL; ++ goto fail; ++ } ++ ++ /* ++ * Install the interrupt handler for the common interrupts. ++ */ ++ dev_dbg(&dev->dev, "registering (common) handler for irq%d\n", ++ usb3_dev->irq); ++ retval = request_irq(usb3_dev->irq, dwc_usb3_common_irq, ++ IRQF_SHARED | IRQF_DISABLED, ++ dwc_driver_name, usb3_dev); ++ if (retval) { ++ dev_err(&dev->dev, "request of irq%d failed!\n", usb3_dev->irq); ++ goto fail; ++ } ++ ++ usb3_dev->cmn_irq_installed = 1; ++#if 0 ++ if (dwc_usb3_module_params.hibernate && ++ (usb3_dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&usb3_dev->pcd.lock, flags); ++ dwc_enter_hibernation(&usb3_dev->pcd, 0); ++ spin_unlock_irqrestore(&usb3_dev->pcd.lock, flags); ++ } ++#endif ++ return 0; ++ ++fail: ++ dwc_usb3_platform_remove(dev); ++ return retval; ++} ++ ++#ifdef DWC_REPLACE_DWC3 ++ ++#ifdef CONFIG_OF ++static const struct of_device_id of_dwc_usb3_match[] = { ++#if 0 ++#ifdef DWC_XPLORER ++ { ++ .compatible = "snps,xplorer-dwusb3" ++ }, ++#else ++ { ++ .compatible = "synopsys,dwc3" ++ }, ++#endif ++#endif ++ { .compatible = "dwc_usb3", }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, of_dwc_usb3_match); ++#endif ++ ++static struct platform_driver dwc_usb3_platform_driver = { ++ .probe = dwc_usb3_platform_probe, ++ .remove = dwc_usb3_platform_remove, ++ .driver = { ++#ifdef DWC_XPLORER ++ .name = "xplorer-dwc3", ++#else ++ .name = (char *)dwc_driver_name, ++// .name = "dwc3", ++#endif ++ .of_match_table = of_dwc_usb3_match, ++ }, ++}; ++ ++module_platform_driver(dwc_usb3_platform_driver); ++ ++#ifdef DWC_XPLORER ++MODULE_ALIAS("platform:xplorer-dwc3"); ++#else ++MODULE_ALIAS("platform:dwc3"); ++#endif ++ ++#else /* DWC_REPLACE_DWC3 */ ++ ++static const struct platform_device_id dwc_usb3_platform_ids[] = { ++ { "dwc-usb3-platform", 0 }, ++ { "dwc_usb3", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(platform, dwc_usb3_platform_ids); ++ ++static struct platform_driver dwc_usb3_platform_driver = { ++ .id_table = dwc_usb3_platform_ids, ++ .probe = dwc_usb3_platform_probe, ++ .remove = __devexit_p(dwc_usb3_platform_remove), ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "dwc_usb3", ++ } ++}; ++ ++/** ++ * This function is called when the DWC_usb3 driver is loaded into the kernel ++ * with the insmod command. It registers the dwc_usb3_driver structure with the ++ * appropriate bus driver. This will cause the dwc_usb3_platform_probe function ++ * to be called. In addition, the bus driver will automatically expose ++ * attributes defined for the device and driver in the special sysfs file ++ * system. ++ */ ++static int __init dwc_usb3_driver_init(void) ++{ ++ int retval; ++ ++ printk("\n###%s,%d,reg(c110)=0x%x\n",__func__,__LINE__,readl((IO_ADDRESS(0x1018c110)))); ++ printk("\n###%s,%d\n",__func__,__LINE__); ++ printk(KERN_INFO "%s: %s version %s\n", dwc_driver_name, ++ DWC_DRIVER_DESC, DWC_DRIVER_VERSION); ++ ++ retval = platform_driver_register(&dwc_usb3_platform_driver); ++ if (retval < 0) { ++ printk(KERN_ERR "%s retval=%d\n", __func__, retval); ++ return retval; ++ } ++ ++ printk(KERN_INFO "%s: module installed\n", dwc_driver_name); ++ return retval; ++} ++module_init(dwc_usb3_driver_init); ++ ++/** ++ * This function is called when the DWC_usb3 driver is removed from the kernel ++ * with the rmmod command. The driver unregisters itself with its bus driver. ++ * ++ */ ++static void __exit dwc_usb3_driver_exit(void) ++{ ++ printk(KERN_DEBUG "%s: driver_exit()\n", dwc_driver_name); ++ ++ platform_driver_unregister(&dwc_usb3_platform_driver); ++ ++ printk(KERN_INFO "%s: module removed\n", dwc_driver_name); ++} ++module_exit(dwc_usb3_driver_exit); ++#endif +diff --git a/drivers/usb/gadget/udc/hiudc3/linux_sysfs.c b/drivers/usb/gadget/udc/hiudc3/linux_sysfs.c +new file mode 100644 +index 0000000..5502de1 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/linux_sysfs.c +@@ -0,0 +1,469 @@ ++/** @file ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0) ++# undef VERIFY_OCTAL_PERMISSIONS ++# define VERIFY_OCTAL_PERMISSIONS(perms) \ ++ (BUILD_BUG_ON_ZERO((perms) < 0) + \ ++ BUILD_BUG_ON_ZERO((perms) > 0777) + \ ++ /* User perms >= group perms >= other perms */ \ ++ BUILD_BUG_ON_ZERO(((perms) >> 6) < (((perms) >> 3) & 7)) + \ ++ BUILD_BUG_ON_ZERO((((perms) >> 3) & 7) < ((perms) & 7)) + \ ++ (perms)) ++#endif ++ ++#ifdef CONFIG_USB_OTG_DWC ++extern int otg_start_rsp(struct usb_otg *otg); ++extern int otg_end_session(struct usb_otg *otg); ++#endif ++ ++/*-------------------------------------------------------------------------*/ ++/* Encapsulate the module parameter settings */ ++ ++dwc_usb3_core_params_t dwc_usb3_module_params = { ++ .burst = 1, ++ .newcore = 0, ++ .phy = 2, ++ .wakeup = 0, ++#ifdef DWC_STAR_9000446947_WORKAROUND ++ .pwrctl = 0, ++#else ++# if defined(DWC_STAR_9000449814_WORKAROUND) || \ ++ defined(DWC_STAR_9000459034_WORKAROUND) ++ .pwrctl = 2, ++# else ++ .pwrctl = 3, ++# endif ++#endif ++ .lpmctl = 1, ++ .phyctl = 1, ++ .usb2mode = 0, ++ .hibernate = 0, ++ .hiberdisc = 1, ++ .clkgatingen = 1, ++ .ssdisquirk = 1, ++ .nobos = 0, ++ .loop = 0, ++ .nump = 16, ++ .newcsr = 1, ++ .rxfsz = 0, ++ .txfsz = { 0, }, ++ .txfsz_cnt = 0, ++ .besl = 0, ++ .baseline_besl = 0, ++ .deep_besl = 0, ++ .ebc = 0, ++}; ++ ++module_param_named(burst, dwc_usb3_module_params.burst, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(burst, "Enable gadget to set USB 3.0 max burst size " ++ "(0=no, 1=yes) (default=yes)"); ++ ++module_param_named(new, dwc_usb3_module_params.newcore, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(new, "Force new core behavior [rev >= 1.09a] (0=no, 1=yes) " ++ "(default=no)"); ++ ++module_param_named(phy, dwc_usb3_module_params.phy, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(phy, "Select PHY type (0=RocketIO, 1=old-Synopsys, " ++ "2=TI/Synopsys-8bit-UTMI/ULPI 3=Synopsys-16bit-UTMI) " ++ "(default=2)"); ++ ++module_param_named(wakeup, dwc_usb3_module_params.wakeup, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(wakeup, "Enable remote wakeup (0=no, 1=yes) (default=no)"); ++ ++module_param_named(pwrctl, dwc_usb3_module_params.pwrctl, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(pwrctl, "Enable U1/U2 power states (bit0=U1, bit1=U2) " ++ "(default=U1+U2)"); ++ ++module_param_named(lpmctl, dwc_usb3_module_params.lpmctl, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(lpmctl, "Enable LPM power control (0=no, 1=AppL1Res-0, " ++ "2=AppL1Res-1) (default=1)"); ++ ++module_param_named(phyctl, dwc_usb3_module_params.phyctl, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(phyctl, "Enable PHY suspend (0=no, 1=yes) (default=yes)"); ++ ++module_param_named(usb2mode, dwc_usb3_module_params.usb2mode, int, ++ S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(usb2mode, "Force the core to connect in USB 2.0 mode at the " ++ "given speed (0=no, 1=FS, 2=HS) (default=no)"); ++ ++module_param_named(hibernate, dwc_usb3_module_params.hibernate, int, ++ S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(hibernate, "Enable hibernation mode (0=no, 1=yes) " ++ "(default=no)"); ++ ++module_param_named(hiberdisc, dwc_usb3_module_params.hiberdisc, int, ++ S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(hiberdisc, "Enter hibernation when disconnected " ++ "(0=no, 1=yes) (default=yes)"); ++ ++module_param_named(clkgatingen, dwc_usb3_module_params.clkgatingen, int, ++ S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(clkgatingen, "Enable clock gating (0=no, 1=yes) " ++ "(default=yes)"); ++ ++module_param_named(ssdisquirk, dwc_usb3_module_params.ssdisquirk, int, ++ S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(ssdisquirk, "Enable SS_DIS Quirk (0=no, 1=yes) (default=yes)"); ++ ++module_param_named(nobos, dwc_usb3_module_params.nobos, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(nobos, "Fail GetDescriptor(BOS) request at USB2 speeds " ++ "(0=no, 1=yes) (default=no)"); ++ ++module_param_named(nump, dwc_usb3_module_params.nump, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(nump, "Set NUMP to given value (1-16) (default=16)"); ++ ++module_param_named(newcsr, dwc_usb3_module_params.newcsr, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(newcsr, "Use a newer HAPS bitfile with a 64K PCI BAR size " ++ "instead of the older 1MB PCI BAR size (0=no, 1=yes) " ++ "(default=yes)"); ++ ++module_param_named(loop, dwc_usb3_module_params.loop, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(loop, "Number of times to loop in reset (for debug only)"); ++ ++module_param_named(rxfsz, dwc_usb3_module_params.rxfsz, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(rxfsz, "Size of Rx FIFO in bytes"); ++ ++module_param_array_named(txfsz, dwc_usb3_module_params.txfsz, int, ++ &dwc_usb3_module_params.txfsz_cnt, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(txfsz, "Size of Tx FIFOs in bytes"); ++ ++module_param_named(besl, dwc_usb3_module_params.besl, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(besl, "Enable besl support"); ++ ++module_param_named(baseline_besl, dwc_usb3_module_params.baseline_besl, int, ++ S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(baseline_besl, "Set the baseline besl value"); ++ ++module_param_named(deep_besl, dwc_usb3_module_params.deep_besl, int, ++ S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(deep_besl, "Set the deep besl value"); ++ ++module_param_named(ebc, dwc_usb3_module_params.ebc, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(ebc, "Enable EBC support on all Bulk endpoints"); ++ ++static dwc_usb3_device_t *get_usb3_device(struct device *dev) ++{ ++#ifdef DWC_PLATFORM_DEV ++ struct platform_device *device = ++ container_of(dev, struct platform_device, dev); ++ dwc_usb3_device_t *usb3_dev = platform_get_drvdata(device); ++#else ++ struct pci_dev *device = container_of(dev, struct pci_dev, dev); ++ dwc_usb3_device_t *usb3_dev = pci_get_drvdata(device); ++#endif ++ ++ return usb3_dev; ++} ++ ++static ssize_t show_wakeup(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ dwc_usb3_device_t *usb3_dev = get_usb3_device(dev); ++ ++ return sprintf(buf, "%d\n", usb3_dev->pcd.wkup_rdy); ++} ++ ++static ssize_t store_wakeup(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ dwc_usb3_device_t *usb3_dev = get_usb3_device(dev); ++ int ret; ++ ++ ret = dwc_usb3_wakeup(usb3_dev->pcd.gadget); ++ ++ return ret < 0 ? ret : count; ++} ++ ++/* /sys/module/dwc_usb3/drivers/pci:dwc_usb3/nnnn:nn:nn.n/wakeup */ ++static DEVICE_ATTR(wakeup, 0666, show_wakeup, store_wakeup); ++ ++static ssize_t show_disrupt(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", 0); ++} ++ ++static ssize_t store_disrupt(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ dwc_usb3_device_t *usb3_dev = get_usb3_device(dev); ++ unsigned long tmp = 0; ++ ssize_t rc; ++ unsigned long flags; ++ ++ sscanf(buf, "%ld", &tmp); ++ rc = strnlen(buf, count); ++ printk(USB3_DWC "disrupt: %ld ms\n", tmp); ++ ++ spin_lock_irqsave(&usb3_dev->pcd.lock, flags); ++ mdelay(tmp); ++ spin_unlock_irqrestore(&usb3_dev->pcd.lock, flags); ++ return rc; ++} ++ ++/* /sys/module/dwc_usb3/drivers/pci:dwc_usb3/nnnn:nn:nn.n/disrupt */ ++static DEVICE_ATTR(disrupt, 0666, show_disrupt, store_disrupt); ++ ++static ssize_t show_hiber(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", 0); ++} ++ ++static ssize_t store_hiber(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ dwc_usb3_device_t *usb3_dev = get_usb3_device(dev); ++ int tmp = 0; ++ ssize_t rc; ++ unsigned long flags; ++ ++ sscanf(buf, "%d", &tmp); ++ rc = strnlen(buf, count); ++ printk(USB3_DWC "hibernate: save_state=%d\n", tmp); ++ ++ spin_lock_irqsave(&usb3_dev->pcd.lock, flags); ++ dwc_enter_hibernation(&usb3_dev->pcd, tmp); ++ spin_unlock_irqrestore(&usb3_dev->pcd.lock, flags); ++ return rc; ++} ++ ++/* /sys/module/dwc_usb3/drivers/pci:dwc_usb3/nnnn:nn:nn.n/hibernate */ ++static DEVICE_ATTR(hibernate, 0666, show_hiber, store_hiber); ++ ++static ssize_t show_restore(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ return sprintf(buf, "%d\n", 0); ++} ++ ++static ssize_t store_restore(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ dwc_usb3_device_t *usb3_dev = get_usb3_device(dev); ++ int tmp = 0; ++ ssize_t rc; ++ unsigned long flags; ++ ++ sscanf(buf, "%d", &tmp); ++ rc = strnlen(buf, count); ++ printk(USB3_DWC "restore: restore_state=%d\n", tmp); ++ ++ spin_lock_irqsave(&usb3_dev->pcd.lock, flags); ++ tmp = dwc_exit_hibernation(&usb3_dev->pcd, tmp); ++ spin_unlock_irqrestore(&usb3_dev->pcd.lock, flags); ++ printk(USB3_DWC "dwc_exit_hibernation() returned %d\n", tmp); ++ return rc; ++} ++ ++/* /sys/module/dwc_usb3/drivers/pci:dwc_usb3/nnnn:nn:nn.n/restore */ ++static DEVICE_ATTR(restore, 0666, show_restore, store_restore); ++ ++#ifdef CONFIG_USB_OTG_DWC ++ ++static ssize_t store_srp(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct usb_phy *phy; ++ struct usb_otg *otg; ++ ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ if (IS_ERR(phy) || !phy) { ++ if (!IS_ERR(phy)) ++ usb_put_phy(phy); ++ return count; ++ } ++ ++ otg = phy->otg; ++ if (!otg) { ++ usb_put_phy(phy); ++ return count; ++ } ++ ++ otg_start_srp(otg); ++ usb_put_phy(phy); ++ return count; ++} ++static DEVICE_ATTR(srp, 0222, NULL, store_srp); ++ ++static ssize_t store_end(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct usb_phy *phy; ++ struct usb_otg *otg; ++ ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ if (IS_ERR(phy) || !phy) { ++ if (!IS_ERR(phy)) ++ usb_put_phy(phy); ++ return count; ++ } ++ ++ otg = phy->otg; ++ if (!otg) { ++ usb_put_phy(phy); ++ return count; ++ } ++ ++ otg_end_session(otg); ++ usb_put_phy(phy); ++ return count; ++} ++static DEVICE_ATTR(end, 0222, NULL, store_end); ++ ++static ssize_t store_hnp(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ dwc_usb3_device_t *usb3_dev = get_usb3_device(dev); ++ ++ if (usb3_dev->pcd.b_hnp_enable) { ++ dev_info(dev, "b_hnp_enable is TRUE\n"); ++ usb3_dev->pcd.b_hnp_enable = 0; ++ usb3_dev->pcd.wants_host = 0; ++ dwc_usb3_start_hnp(&usb3_dev->pcd); ++ } else { ++ dev_info(dev, "b_hnp_enable is FALSE\n"); ++ usb3_dev->pcd.wants_host = 1; ++ /* TODO if we don't receive the SET_FEATURE within 4 secs, ++ * reset this value ++ */ ++ } ++ return count; ++} ++static DEVICE_ATTR(hnp, 0222, NULL, store_hnp); ++ ++static ssize_t store_rsp(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct usb_phy *phy; ++ struct usb_otg *otg; ++ ++ phy = usb_get_phy(USB_PHY_TYPE_USB3); ++ if (IS_ERR(phy) || !phy) { ++ if (!IS_ERR(phy)) ++ usb_put_phy(phy); ++ return count; ++ } ++ ++ otg = phy->otg; ++ if (!otg) { ++ usb_put_phy(phy); ++ return count; ++ } ++ ++ otg_start_rsp(otg); ++ usb_put_phy(phy); ++ return count; ++} ++static DEVICE_ATTR(rsp, 0222, NULL, store_rsp); ++ ++#endif /* CONFIG_USB_OTG_DWC */ ++ ++static ssize_t show_lpm_nyet(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ dwc_usb3_device_t *usb3_dev = get_usb3_device(dev); ++ dwc_usb3_dev_global_regs_t __iomem *regs = ++ usb3_dev->pcd.dev_global_regs; ++ u32 dctl = dwc_rd32(usb3_dev, ®s->dctl); ++ u32 nyet = (dctl & DWC_DCTL_LPM_NYET_THRESH_BITS) >> ++ DWC_DCTL_LPM_NYET_THRESH_SHIFT; ++ ++ return sprintf(buf, "0x%x\n", nyet); ++} ++ ++static ssize_t store_lpm_nyet(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ dwc_usb3_device_t *usb3_dev = get_usb3_device(dev); ++ dwc_usb3_dev_global_regs_t __iomem *regs = ++ usb3_dev->pcd.dev_global_regs; ++ u32 dctl = dwc_rd32(usb3_dev, ®s->dctl); ++ u32 value = simple_strtol(buf, NULL, 16); ++ ++ value = (value << DWC_DCTL_LPM_NYET_THRESH_SHIFT) & ++ DWC_DCTL_LPM_NYET_THRESH_BITS; ++ ++ dctl &= ~(DWC_DCTL_LPM_NYET_THRESH_BITS); ++ dctl |= value; ++ ++ dwc_wr32(usb3_dev, ®s->dctl, dctl); ++ return count; ++} ++ ++/* /sys/module/dwc_usb3/drivers/pci:dwc_usb3/nnnn:nn:nn.n/wakeup */ ++static DEVICE_ATTR(lpm_nyet, 0666, show_lpm_nyet, store_lpm_nyet); ++ ++void dwc_usb3_remove_dev_files(struct device *dev) ++{ ++#ifdef CONFIG_USB_OTG_DWC ++ device_remove_file(dev, &dev_attr_end); ++ device_remove_file(dev, &dev_attr_srp); ++ device_remove_file(dev, &dev_attr_rsp); ++ device_remove_file(dev, &dev_attr_hnp); ++#endif ++ device_remove_file(dev, &dev_attr_restore); ++ device_remove_file(dev, &dev_attr_hibernate); ++ device_remove_file(dev, &dev_attr_disrupt); ++ device_remove_file(dev, &dev_attr_wakeup); ++ device_remove_file(dev, &dev_attr_lpm_nyet); ++} ++ ++int dwc_usb3_create_dev_files(struct device *dev) ++{ ++ int retval; ++ ++ retval = device_create_file(dev, &dev_attr_wakeup); ++ if (retval) ++ goto fail; ++ ++ retval = device_create_file(dev, &dev_attr_disrupt); ++ if (retval) ++ goto fail; ++ ++ retval = device_create_file(dev, &dev_attr_hibernate); ++ if (retval) ++ goto fail; ++ ++ retval = device_create_file(dev, &dev_attr_restore); ++ if (retval) ++ goto fail; ++ ++#ifdef CONFIG_USB_OTG_DWC ++ retval = device_create_file(dev, &dev_attr_hnp); ++ if (retval) ++ goto fail; ++ ++ retval = device_create_file(dev, &dev_attr_rsp); ++ if (retval) ++ goto fail; ++ ++ retval = device_create_file(dev, &dev_attr_srp); ++ if (retval) ++ goto fail; ++ ++ retval = device_create_file(dev, &dev_attr_end); ++ if (retval) ++ goto fail; ++#endif ++ ++ retval = device_create_file(dev, &dev_attr_lpm_nyet); ++ if (retval) ++ goto fail; ++ ++ return 0; ++ ++fail: ++ dev_err(dev, "Failed to create one or more sysfs files!!\n"); ++ return retval; ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/no_os/no_os_defs.h b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_defs.h +new file mode 100644 +index 0000000..54d2af2 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_defs.h +@@ -0,0 +1,363 @@ ++#ifndef _DWC_NO_OS_DEFS_H_ ++#define _DWC_NO_OS_DEFS_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @file ++ * ++ * This file contains OS-specific includes and definitions. ++ * ++ */ ++ ++#undef linux ++#undef __linux ++#undef __linux__ ++ ++#ifdef LINUXTEST ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/errno.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/ctype.h> ++#include <linux/string.h> ++#include <linux/dma-mapping.h> ++#include <linux/jiffies.h> ++#include <linux/delay.h> ++#include <linux/timer.h> ++#include <linux/kthread.h> ++#include <linux/workqueue.h> ++#include <linux/freezer.h> ++#include <linux/stat.h> ++#include <linux/pci.h> ++ ++#include <linux/version.h> ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++# include <linux/irq.h> ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++# include <asm/irq.h> ++#endif ++ ++#include <asm/io.h> ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) ++typedef int gfp_t; ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) ++# define IRQF_SHARED SA_SHIRQ ++#endif ++ ++/** @{ */ ++/** Data type for DMA addresses */ ++typedef dma_addr_t dwc_dma_t; ++#define DWC_DMA_ADDR_INVALID (~(dwc_dma_t)0) ++/** @} */ ++ ++/** Compiler 'packed' attribute */ ++#define UPACKED __attribute__ ((__packed__)) ++ ++/** Compiler 'aligned(16)' attribute */ ++#define UALIGNED16 __attribute__ ((__aligned__(16))) ++ ++#define dwc_init_spinlock(d, p) spin_lock_init(p) ++#define dwc_acquire_spinlock(d, p) spin_lock(p) ++#define dwc_release_spinlock(d, p) spin_unlock(p) ++#define dwc_acquire_spinlock_irq(d, p, f) spin_lock_irqsave(p, f) ++#define dwc_release_spinlock_irq(d, p, f) spin_unlock_irqrestore(p, f) ++ ++#else /* !LINUXTEST */ ++ ++#include <stdint.h> ++#include <stddef.h> ++#include <stdarg.h> ++#include <stdio.h> // for printf ++#include <string.h> // for memcpy ++ ++/** @{ */ ++/** Data types needed by the PCD */ ++typedef uint64_t u64, u_int64_t; ++typedef uint32_t u32, u_int32_t; ++typedef uint16_t u16, u_int16_t; ++typedef uint8_t u8, u_int8_t; ++ ++typedef int64_t s64; ++typedef int32_t s32; ++typedef int16_t s16; ++typedef int8_t s8; ++ ++typedef unsigned long u_long; ++typedef unsigned int u_int; ++typedef unsigned short u_short; ++typedef unsigned char u_char; ++/** @} */ ++ ++/** @{ */ ++/** Data type for DMA addresses */ ++typedef unsigned long dwc_dma_t; ++#define DWC_DMA_ADDR_INVALID (~(dwc_dma_t)0) ++/** @} */ ++ ++/** Compiler 'packed' attribute */ ++#define UPACKED __attribute__ ((__packed__)) ++ ++/** Compiler 'aligned(16)' attribute */ ++#define UALIGNED16 __attribute__ ((__aligned__(16))) ++ ++/** I/O memory attribute for pointers. Needed for Linux "sparse" tool. */ ++#define __iomem /* */ ++ ++#define KERN_DEBUG "" /* debug messages */ ++#define KERN_INFO "" /* informational messages */ ++#define KERN_WARNING "" /* warning messages */ ++#define KERN_ERR "" /* error messages */ ++ ++// from /usr/include/asm-x86_64/errno.h ++#define EIO 5 /* I/O error */ ++#define EAGAIN 11 /* Try again */ ++#define ENOMEM 12 /* Out of memory */ ++#define EBUSY 16 /* Device or resource busy */ ++#define ENODEV 19 /* No such device */ ++#define EINVAL 22 /* Invalid argument */ ++#define ENOSPC 28 /* No space left on device */ ++#define EPIPE 32 /* Broken pipe */ ++#define EDOM 33 /* Math argument out of domain of func */ ++#define ENODATA 61 /* No data available */ ++#define ENOSR 63 /* Out of streams resources */ ++#define ECOMM 70 /* Communication error on send */ ++#define EPROTO 71 /* Protocol error */ ++#define EOVERFLOW 75 /* Value too large for defined data type */ ++#define ERESTART 85 /* Interrupted system call should be restarted */ ++#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ ++#define ECONNABORTED 103 /* Software caused connection abort */ ++#define ECONNRESET 104 /* Connection reset by peer */ ++#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ ++#define ETIMEDOUT 110 /* Connection timed out */ ++#define EINPROGRESS 115 /* Operation now in progress */ ++ ++/** Write memory barrier macro */ ++#define wmb() do {} while (0) ++ ++#define interrupt_disable() 0 ++#define interrupt_enable() do {} while (0) ++ ++#define dwc_init_spinlock(d, p) do {} while (0) ++#define dwc_acquire_spinlock(d, p) do {} while (0) ++#define dwc_release_spinlock(d, p) do {} while (0) ++#define dwc_acquire_spinlock_irq(d, p, f) do { (f) = interrupt_disable(); } while (0) ++#define dwc_release_spinlock_irq(d, p, f) do { if (f) interrupt_enable(); } while (0) ++ ++struct task_struct { ++ int dummy; ++}; ++ ++struct tasklet_struct { ++ int dummy; ++}; ++ ++#endif /* !LINUXTEST */ ++ ++#include "usb.h" ++#include "dwc_list.h" ++ ++/** @name Error Codes */ ++/** @{ */ ++#define DWC_E_INVALID EINVAL ++#define DWC_E_NO_MEMORY ENOMEM ++#define DWC_E_NO_DEVICE ENODEV ++#define DWC_E_NOT_SUPPORTED EOPNOTSUPP ++#define DWC_E_TIMEOUT ETIMEDOUT ++#define DWC_E_BUSY EBUSY ++#define DWC_E_AGAIN EAGAIN ++#define DWC_E_RESTART ERESTART ++#define DWC_E_ABORT ECONNABORTED ++#define DWC_E_SHUTDOWN ESHUTDOWN ++#define DWC_E_NO_DATA ENODATA ++#define DWC_E_DISCONNECT ECONNRESET ++#define DWC_E_UNKNOWN EINVAL ++#define DWC_E_NO_STREAM_RES ENOSR ++#define DWC_E_COMMUNICATION ECOMM ++#define DWC_E_OVERFLOW EOVERFLOW ++#define DWC_E_PROTOCOL EPROTO ++#define DWC_E_IN_PROGRESS EINPROGRESS ++#define DWC_E_PIPE EPIPE ++#define DWC_E_IO EIO ++#define DWC_E_NO_SPACE ENOSPC ++#define DWC_E_DOMAIN EDOM ++/** @} */ ++ ++/** ++ * The number of DMA Descriptors (TRBs) to allocate for each endpoint type. ++ * NOTE: The driver currently supports more than 1 TRB for Isoc EPs only. ++ * So the values for Bulk and Intr must be 1. ++ */ ++#define DWC_NUM_BULK_TRBS 1 ++#define DWC_NUM_INTR_TRBS 1 ++#define DWC_NUM_ISOC_TRBS 32 ++ ++/** ++ * These parameters may be specified when loading the module. They define how ++ * the DWC_usb3 controller should be configured. The parameter values are passed ++ * to the CIL initialization routine dwc_usb3_pcd_common_init(). ++ */ ++typedef struct dwc_usb3_core_params { ++ int burst; ++ int newcore; ++ int phy; ++ int wakeup; ++ int pwrctl; ++ int lpmctl; ++ int phyctl; ++ int usb2mode; ++ int hibernate; ++ int hiberdisc; ++ int clkgatingen; ++ int ssdisquirk; ++ int nobos; ++ int loop; ++ int nump; ++ int newcsr; ++ int rxfsz; ++ int txfsz[16]; ++ int txfsz_cnt; ++ int baseline_besl; ++ int deep_besl; ++ int besl; ++ int ebc; ++} dwc_usb3_core_params_t; ++ ++// linux/usb/gadget.h ++ ++/** ++ * Platform-specific USB endpoint ++ */ ++typedef struct usb_ep { ++ const void *desc; ++ const void *comp_desc; ++ unsigned maxpacket:16; ++ u8 address; ++} usb_ep_t; ++ ++/** ++ * Platform-specific USB request ++ */ ++typedef struct usb_request { ++ void *buf; ++ unsigned length; ++ dwc_dma_t dma; ++ ++ unsigned stream_id:16; ++ unsigned zero:1; ++ ++ void (*complete)(usb_ep_t *ep, struct usb_request *req); ++ ++ int status; ++ unsigned actual; ++} usb_request_t; ++ ++/** ++ */ ++static inline void dwc_usb3_task_schedule(struct tasklet_struct *tasklet) ++{ ++#ifdef LINUXTEST ++ tasklet_schedule(tasklet); ++#endif ++} ++ ++/* Make the following structure type definitions "packed" if using a Microsoft ++ * compiler. The UPACKED attribute (defined above) embedded in the structure ++ * type definitions does the same thing for GCC. Other compilers may need ++ * something different. ++ */ ++#ifdef _MSC_VER ++#include <pshpack1.h> ++#endif ++ ++/** ++ */ ++typedef struct fs_config_desc_st { ++ usb_config_descriptor_t config_desc; ++ usb_interface_descriptor_t intf_desc; ++ usb_endpoint_descriptor_t bulk_in_ep_desc; ++ usb_endpoint_descriptor_t bulk_out_ep_desc; ++} UPACKED fs_config_desc_t; ++ ++/** ++ */ ++typedef struct hs_config_desc_st { ++ usb_config_descriptor_t config_desc; ++ usb_interface_descriptor_t intf_desc; ++ usb_endpoint_descriptor_t bulk_in_ep_desc; ++ usb_endpoint_descriptor_t bulk_out_ep_desc; ++} UPACKED hs_config_desc_t; ++ ++/** ++ */ ++typedef struct ss_config_desc_st { ++ usb_config_descriptor_t config_desc; ++ usb_interface_descriptor_t intf_desc; ++ usb_endpoint_descriptor_t bulk_in_ep_desc; ++ ss_endpoint_companion_descriptor_t bulk_in_ss_ep_comp_desc; ++ usb_endpoint_descriptor_t bulk_out_ep_desc; ++ ss_endpoint_companion_descriptor_t bulk_out_ss_ep_comp_desc; ++} UPACKED ss_config_desc_t; ++ ++/* Stop packing structure type definitions */ ++#ifdef _MSC_VER ++#include <poppack.h> ++#endif ++ ++/* These structures are defined in no_os_ep0.c */ ++extern fs_config_desc_t fs_config_desc; ++extern hs_config_desc_t hs_config_desc; ++extern ss_config_desc_t ss_config_desc; ++ ++/** ++ * Function driver API routines ++ */ ++ ++struct dwc_usb3_device; /* so the simulation code can include just this file */ ++ ++extern dwc_usb3_core_params_t usb3ss_module_params; ++ ++#ifndef LINUXTEST ++extern struct dwc_usb3_device *dwc_usb3_driver_init(u32 base_addr_dwc); ++extern void dwc_usb3_driver_remove(void); ++extern void dwc_usb3_common_irq(int irq, void *dev); ++#endif ++ ++extern usb_ep_t *dwc_usb3_ep_enable(struct dwc_usb3_device *usb3_dev, const void *epdesc, ++ const void *epcomp); ++extern int dwc_usb3_ep_disable(struct dwc_usb3_device *usb3_dev, usb_ep_t *usb_ep); ++extern usb_request_t *dwc_usb3_alloc_request(struct dwc_usb3_device *usb3_dev, usb_ep_t *usb_ep); ++extern void dwc_usb3_free_request(struct dwc_usb3_device *usb3_dev, usb_ep_t *usb_ep, ++ usb_request_t *usb_req); ++extern int dwc_usb3_ep_queue(struct dwc_usb3_device *usb3_dev, usb_ep_t *usb_ep, ++ usb_request_t *usb_req); ++extern int dwc_usb3_ep_dequeue(struct dwc_usb3_device *usb3_dev, usb_ep_t *usb_ep, ++ usb_request_t *usb_req); ++extern int dwc_usb3_wait_pme(struct dwc_usb3_device *usb3_dev); ++extern int dwc_usb3_handle_pme_intr(struct dwc_usb3_device *usb3_dev); ++ ++extern int dwc_usb3_function_init(struct dwc_usb3_device *usb3_dev); ++extern void dwc_usb3_function_remove(struct dwc_usb3_device *usb3_dev); ++extern int dwc_usb3_function_connect(struct dwc_usb3_device *usb3_dev, int speed); ++extern int dwc_usb3_function_disconnect(struct dwc_usb3_device *usb3_dev); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_NO_OS_DEFS_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/no_os/no_os_dev.h b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_dev.h +new file mode 100644 +index 0000000..0daeb86 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_dev.h +@@ -0,0 +1,198 @@ ++#ifndef _DWC_NO_OS_DEV_H_ ++#define _DWC_NO_OS_DEV_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** @file ++ */ ++ ++/** Wrapper routine for _handshake() */ ++#define handshake(_dev_, _ptr_, _mask_, _done_) \ ++ _handshake(_dev_, _ptr_, _mask_, _done_) ++ ++/** Takes a usb req pointer and returns the associated pcd req pointer */ ++#define dwc_usb3_get_pcd_req(usbreq) \ ++ ((dwc_usb3_pcd_req_t *)((char *)(usbreq) - \ ++ offsetof(struct dwc_usb3_pcd_req, usb_req))) ++ ++/** Takes a usb ep pointer and returns the associated pcd ep pointer */ ++#define dwc_usb3_get_pcd_ep(usbep) \ ++ ((dwc_usb3_pcd_ep_t *)((char *)(usbep) - \ ++ offsetof(struct dwc_usb3_pcd_ep, usb_ep))) ++ ++#ifdef LINUXTEST ++ ++#define printf printk ++ ++/** @{ */ ++/** ++ * Register read/write. ++ */ ++static inline u32 dwc_rd32(struct dwc_usb3_device *dev, volatile u32 __iomem *adr) ++{ ++ return readl(adr); ++} ++ ++static inline void dwc_wr32(struct dwc_usb3_device *dev, volatile u32 __iomem *adr, u32 val) ++{ ++ writel(val, adr); ++} ++/** @} */ ++ ++/** @{ */ ++/** ++ * Non-sleeping delays. ++ */ ++#define dwc_udelay(dev, us) udelay(us) ++#define dwc_mdelay(dev, ms) mdelay(ms) ++/** @} */ ++ ++/** ++ * Sleeping delay. ++ */ ++#define dwc_msleep(dev, ms) msleep(ms) ++ ++#else /* !LINUXTEST */ ++ ++/** @{ */ ++/** ++ * Register read/write. ++ */ ++static inline u32 dwc_rd32(struct dwc_usb3_device *dev, volatile u32 __iomem *adr) ++{ ++ return *adr; ++} ++ ++static inline void dwc_wr32(struct dwc_usb3_device *dev, volatile u32 __iomem *adr, u32 val) ++{ ++ *adr = val; ++} ++/** @} */ ++ ++/** @{ */ ++/** ++ * Non-sleeping delays. ++ */ ++#define dwc_udelay(dev, us) do {} while (0) ++#define dwc_mdelay(dev, ms) do {} while (0) ++/** @} */ ++ ++/** ++ * Sleeping delay. ++ */ ++#define dwc_msleep(dev, ms) do {} while (0) ++ ++#endif /* LINUXTEST */ ++ ++/* ++ * Debugging support - vanishes in non-debug builds. ++ */ ++ ++/** Prefix string for print macros */ ++#define USB3_DWC "dwc_usb3: " ++ ++/** @{ */ ++/** Print a debug message */ ++#ifdef DEBUG ++# define dwc_debug(dev, x...) printf(KERN_DEBUG USB3_DWC x ) ++#else ++# define dwc_debug(dev, x...) do {} while (0) ++#endif /* DEBUG */ ++ ++# define dwc_debug0(dev, fmt) dwc_debug(dev, fmt) ++# define dwc_debug1(dev, fmt, a) dwc_debug(dev, fmt, a) ++# define dwc_debug2(dev, fmt, a, b) dwc_debug(dev, fmt, a, b) ++# define dwc_debug3(dev, fmt, a, b, c) dwc_debug(dev, fmt, a, b, c) ++# define dwc_debug4(dev, fmt, a, b, c, d) dwc_debug(dev, fmt, a, b, c, d) ++# define dwc_debug5(dev, fmt, a, b, c, d, e) \ ++ dwc_debug(dev, fmt, a, b, c, d, e) ++# define dwc_debug6(dev, fmt, a, b, c, d, e, f) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f) ++# define dwc_debug7(dev, fmt, a, b, c, d, e, f, g) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f, g) ++# define dwc_debug8(dev, fmt, a, b, c, d, e, f, g, h) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f, g, h) ++# define dwc_debug9(dev, fmt, a, b, c, d, e, f, g, h, i) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f, g, h, i) ++# define dwc_debug10(dev, fmt, a, b, c, d, e, f, g, h, i, j) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f, g, h, i, j) ++/** @} */ ++ ++/** @{ */ ++/** Print an isochronous debug message */ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++# define dwc_isocdbg(dev, x...) printf(KERN_DEBUG USB3_DWC x ) ++#else ++# define dwc_isocdbg(dev, x...) do {} while (0) ++#endif ++ ++# define dwc_isocdbg0(dev, fmt) dwc_isocdbg(dev, fmt) ++# define dwc_isocdbg1(dev, fmt, a) dwc_isocdbg(dev, fmt, a) ++# define dwc_isocdbg2(dev, fmt, a, b) dwc_isocdbg(dev, fmt, a, b) ++# define dwc_isocdbg3(dev, fmt, a, b, c) dwc_isocdbg(dev, fmt, a, b, c) ++# define dwc_isocdbg4(dev, fmt, a, b, c, d) \ ++ dwc_isocdbg(dev, fmt, a, b, c, d) ++# define dwc_isocdbg5(dev, fmt, a, b, c, d, e) \ ++ dwc_isocdbg(dev, fmt, a, b, c, d, e) ++# define dwc_isocdbg6(dev, fmt, a, b, c, d, e, f) \ ++ dwc_isocdbg(dev, fmt, a, b, c, d, e, f) ++/** @} */ ++ ++/** @{ */ ++/** Print an Error message */ ++#define dwc_error(dev, x...) printf(KERN_ERR USB3_DWC x ) ++ ++#define dwc_error0(dev, fmt) dwc_error(dev, fmt) ++#define dwc_error1(dev, fmt, a) dwc_error(dev, fmt, a) ++#define dwc_error2(dev, fmt, a, b) dwc_error(dev, fmt, a, b) ++#define dwc_error3(dev, fmt, a, b, c) dwc_error(dev, fmt, a, b, c) ++#define dwc_error4(dev, fmt, a, b, c, d) dwc_error(dev, fmt, a, b, c, d) ++/** @} */ ++ ++/** @{ */ ++/** Print a Warning message */ ++#define dwc_warn(dev, x...) printf(KERN_WARNING USB3_DWC x ) ++ ++#define dwc_warn0(dev, fmt) dwc_warn(dev, fmt) ++#define dwc_warn1(dev, fmt, a) dwc_warn(dev, fmt, a) ++#define dwc_warn2(dev, fmt, a, b) dwc_warn(dev, fmt, a, b) ++#define dwc_warn3(dev, fmt, a, b, c) dwc_warn(dev, fmt, a, b, c) ++#define dwc_warn4(dev, fmt, a, b, c, d) dwc_warn(dev, fmt, a, b, c, d) ++/** @} */ ++ ++/** @{ */ ++/** Print an Informational message (normal but significant) */ ++#define dwc_info(dev, x...) printf(KERN_INFO USB3_DWC x ) ++ ++#define dwc_info0(dev, fmt) dwc_info(dev, fmt) ++#define dwc_info1(dev, fmt, a) dwc_info(dev, fmt, a) ++#define dwc_info2(dev, fmt, a, b) dwc_info(dev, fmt, a, b) ++#define dwc_info3(dev, fmt, a, b, c) dwc_info(dev, fmt, a, b, c) ++#define dwc_info4(dev, fmt, a, b, c, d) dwc_info(dev, fmt, a, b, c, d) ++/** @} */ ++ ++/** @{ */ ++/** Basic message printing */ ++#define dwc_print(dev, x...) printf(USB3_DWC x ) ++ ++#define dwc_print0(dev, fmt) dwc_print(dev, fmt) ++#define dwc_print1(dev, fmt, a) dwc_print(dev, fmt, a) ++#define dwc_print2(dev, fmt, a, b) dwc_print(dev, fmt, a, b) ++#define dwc_print3(dev, fmt, a, b, c) dwc_print(dev, fmt, a, b, c) ++#define dwc_print4(dev, fmt, a, b, c, d) dwc_print(dev, fmt, a, b, c, d) ++#define dwc_print5(dev, fmt, a, b, c, d, e) \ ++ dwc_print(dev, fmt, a, b, c, d, e) ++/** @} */ ++ ++extern int dwc_usb3_gadget_init(struct dwc_usb3_device *usb3_dev); ++extern void dwc_usb3_gadget_remove(struct dwc_usb3_device *usb3_dev); ++extern int dwc_usb3_no_os_setup(dwc_usb3_pcd_t *pcd, usb_device_request_t *ctrl); ++extern int dwc_usb3_function_setup(dwc_usb3_pcd_t *pcd, usb_device_request_t *ctrl); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_NO_OS_DEV_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/no_os/no_os_ep0.c b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_ep0.c +new file mode 100644 +index 0000000..26bab51 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_ep0.c +@@ -0,0 +1,560 @@ ++/** @file ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++#ifdef DWC_UTE ++#include "ute_if.h" ++#endif ++ ++/*=======================================================================*/ ++/* ++ * EP0 routines ++ */ ++ ++#ifdef DWC_MASS_STORAGE_GADGET ++# define DWC_VENDOR_ID 0x16c3 ++# define DWC_PRODUCT_ID 0x1234 ++#else ++/* We simulate the Linux gadget-zero device */ ++//# define DWC_VENDOR_ID 0x0525 ++//# define DWC_PRODUCT_ID 0xa4a0 ++# define DWC_VENDOR_ID 0x1e08 ++# define DWC_PRODUCT_ID 0x0001 ++#endif ++ ++/* Make the following structure type definitions "packed" if using a Microsoft ++ * compiler. The UPACKED attribute (defined in no_os_defs.h) embedded in the ++ * structure type definitions does the same thing for GCC. Other compilers may ++ * need something different. ++ */ ++#ifdef _MSC_VER ++#include <pshpack1.h> ++#endif ++ ++/* The language string always has string index 0 */ ++static struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord wString[1]; ++} UPACKED language_string = { ++ 4, /* bLength (size of string array + 2) */ ++ UDESC_STRING, /* bDescriptorType */ ++ { /* wString[] */ ++ UCONSTW(0x0409), /* US English */ ++ /* others can be added here */ ++ }, ++}; ++ ++static struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord wString[8]; ++} UPACKED manuf_string = { ++ 18, /* bLength (size of string array + 2) */ ++ UDESC_STRING, /* bDescriptorType */ ++ { /* wString[] */ ++ UCONSTW('S'), UCONSTW('y'), UCONSTW('n'), UCONSTW('o'), ++ UCONSTW('p'), UCONSTW('s'), UCONSTW('y'), UCONSTW('s'), ++ }, ++}; ++#define DWC_STRING_MANUFACTURER 1 ++ ++static struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord wString[8]; ++} UPACKED product_string = { ++ 18, /* bLength (size of string array + 2) */ ++ UDESC_STRING, /* bDescriptorType */ ++ { /* wString[] */ ++ UCONSTW('D'), UCONSTW('W'), UCONSTW('C'), UCONSTW(' '), ++ UCONSTW('U'), UCONSTW('S'), UCONSTW('B'), UCONSTW('3'), ++ }, ++}; ++#define DWC_STRING_PRODUCT 2 ++ ++static struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord wString[10]; ++} UPACKED serial_string = { ++ 22, /* bLength (size of string array + 2) */ ++ UDESC_STRING, /* bDescriptorType */ ++ { /* wString[] */ ++ UCONSTW('0'), UCONSTW('1'), UCONSTW('2'), UCONSTW('3'), ++ UCONSTW('4'), UCONSTW('5'), UCONSTW('6'), UCONSTW('7'), ++ UCONSTW('8'), UCONSTW('9'), ++ }, ++}; ++#define DWC_STRING_SERIAL 3 ++ ++/* Stop packing structure type definitions */ ++#ifdef _MSC_VER ++#include <poppack.h> ++#endif ++ ++/* These standard USB descriptor types are defined in usb.h */ ++ ++static usb_device_descriptor_t device_desc = { ++ USB_DEVICE_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_DEVICE, /* bDescriptorType */ ++ ++ UCONSTW(0), /* bcdUSB (filled in later) */ ++ ++#ifdef DWC_MASS_STORAGE_GADGET ++ UDCLASS_IN_INTERFACE, /* bDeviceClass */ ++#else ++ UDCLASS_VENDOR, /* bDeviceClass */ ++#endif ++ 0, /* bDeviceSubClass */ ++ 0, /* bDeviceProtocol */ ++ 0, /* bMaxPacketSize */ ++ ++ UCONSTW(DWC_VENDOR_ID), /* idVendor */ ++ UCONSTW(DWC_PRODUCT_ID), /* idProduct */ ++ UCONSTW(0xffff), /* bcdDevice */ ++ ++ DWC_STRING_MANUFACTURER, /* iManufacturer */ ++ DWC_STRING_PRODUCT, /* iProduct */ ++ DWC_STRING_SERIAL, /* iSerialNumber */ ++ ++ 1, /* bNumConfigurations */ ++}; ++ ++static usb_device_qualifier_t dev_qualifier = { ++ USB_DEVICE_QUALIFIER_SIZE, /* bLength */ ++ UDESC_DEVICE_QUALIFIER, /* bDescriptorType */ ++ ++ UCONSTW(0), /* bcdUSB (filled in later) */ ++ ++#ifdef DWC_MASS_STORAGE_GADGET ++ UDCLASS_IN_INTERFACE, /* bDeviceClass */ ++#else ++ UDCLASS_VENDOR, /* bDeviceClass */ ++#endif ++ 0, /* bDeviceSubClass */ ++ 0, /* bDeviceProtocol */ ++ 0, /* bMaxPacketSize0 */ ++ 1, /* bNumConfigurations */ ++ 0, /* bReserved */ ++}; ++ ++/* These application-specific config descriptor types are defined in ++ * no_os_defs.h ++ */ ++ ++fs_config_desc_t fs_config_desc = { ++ /* config descriptor */ ++ { ++ USB_CONFIG_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_CONFIG, /* bDescriptorType */ ++ UCONSTW(0), /* wTotalLength (filled in later) */ ++ 1, /* bNumInterface */ ++ 1, /* bConfigurationValue */ ++ 0, /* iConfiguration */ ++ UC_ATT_ONE | UC_ATT_SELFPOWER, /* bmAttributes */ ++ 100 / UC_POWER_FACTOR, /* bMaxPower (100 ma) */ ++ }, ++ /* interface descriptor */ ++ { ++ USB_INTERFACE_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_INTERFACE, /* bDescriptorType */ ++ 0, /* bInterfaceNumber */ ++ 0, /* bAlternateSetting */ ++ 2, /* bNumEndpoints */ ++ ++#ifdef DWC_MASS_STORAGE_GADGET ++ UICLASS_MASS, /* bInterfaceClass */ ++ UISUBCLASS_SCSI, /* bInterfaceSubClass */ ++ UIPROTO_MASS_BBB, /* bInterfaceProtocol */ ++#else ++ UICLASS_VENDOR, /* bInterfaceClass */ ++ 0, /* bInterfaceSubClass */ ++ 0, /* bInterfaceProtocol */ ++#endif ++ 0, /* iInterface */ ++ }, ++ /* bulk IN endpoint descriptor */ ++ { ++ USB_ENDPOINT_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_ENDPOINT, /* bDescriptorType */ ++ 1 | UE_DIR_IN, /* bEndpointAddress */ ++ UE_BULK, /* bmAttributes */ ++ UCONSTW(64), /* wMaxPacketSize */ ++ 0, /* bInterval */ ++ }, ++ /* bulk OUT endpoint descriptor */ ++ { ++ USB_ENDPOINT_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_ENDPOINT, /* bDescriptorType */ ++ 2 | UE_DIR_OUT, /* bEndpointAddress */ ++ UE_BULK, /* bmAttributes */ ++ UCONSTW(64), /* wMaxPacketSize */ ++ 0, /* bInterval */ ++ }, ++}; ++ ++hs_config_desc_t hs_config_desc = { ++ /* config descriptor */ ++ { ++ USB_CONFIG_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_CONFIG, /* bDescriptorType */ ++ UCONSTW(0), /* wTotalLength (filled in later) */ ++ 1, /* bNumInterface */ ++ 1, /* bConfigurationValue */ ++ 0, /* iConfiguration */ ++ UC_ATT_ONE | UC_ATT_SELFPOWER, /* bmAttributes */ ++ 100 / UC_POWER_FACTOR, /* bMaxPower (100 ma) */ ++ }, ++ /* interface descriptor */ ++ { ++ USB_INTERFACE_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_INTERFACE, /* bDescriptorType */ ++ 0, /* bInterfaceNumber */ ++ 0, /* bAlternateSetting */ ++ 2, /* bNumEndpoints */ ++ ++#ifdef DWC_MASS_STORAGE_GADGET ++ UICLASS_MASS, /* bInterfaceClass */ ++ UISUBCLASS_SCSI, /* bInterfaceSubClass */ ++ UIPROTO_MASS_BBB, /* bInterfaceProtocol */ ++#else ++ UICLASS_VENDOR, /* bInterfaceClass */ ++ 0, /* bInterfaceSubClass */ ++ 0, /* bInterfaceProtocol */ ++#endif ++ 0, /* iInterface */ ++ }, ++ /* bulk IN endpoint descriptor */ ++ { ++ USB_ENDPOINT_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_ENDPOINT, /* bDescriptorType */ ++ 1 | UE_DIR_IN, /* bEndpointAddress */ ++ UE_BULK, /* bmAttributes */ ++ UCONSTW(512), /* wMaxPacketSize */ ++ 0, /* bInterval */ ++ }, ++ /* bulk OUT endpoint descriptor */ ++ { ++ USB_ENDPOINT_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_ENDPOINT, /* bDescriptorType */ ++ 2 | UE_DIR_OUT, /* bEndpointAddress */ ++ UE_BULK, /* bmAttributes */ ++ UCONSTW(512), /* wMaxPacketSize */ ++ 1, /* bInterval (every uframe) */ ++ }, ++}; ++ ++ss_config_desc_t ss_config_desc = { ++ /* config descriptor */ ++ { ++ USB_CONFIG_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_CONFIG, /* bDescriptorType */ ++ UCONSTW(0), /* wTotalLength (filled in later) */ ++ 1, /* bNumInterface */ ++ 1, /* bConfigurationValue */ ++ 0, /* iConfiguration */ ++ UC_ATT_ONE | UC_ATT_SELFPOWER, /* bmAttributes */ ++ 100 / UC_POWER_FACTOR, /* bMaxPower (100 ma) */ ++ }, ++ /* interface descriptor */ ++ { ++ USB_INTERFACE_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_INTERFACE, /* bDescriptorType */ ++ 0, /* bInterfaceNumber */ ++ 0, /* bAlternateSetting */ ++ 2, /* bNumEndpoints */ ++ ++#ifdef DWC_MASS_STORAGE_GADGET ++ UICLASS_MASS, /* bInterfaceClass */ ++ UISUBCLASS_SCSI, /* bInterfaceSubClass */ ++ UIPROTO_MASS_BBB, /* bInterfaceProtocol */ ++#else ++ UICLASS_VENDOR, /* bInterfaceClass */ ++ 0, /* bInterfaceSubClass */ ++ 0, /* bInterfaceProtocol */ ++#endif ++ 0, /* iInterface */ ++ }, ++ /* bulk IN endpoint descriptor */ ++ { ++ USB_ENDPOINT_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_ENDPOINT, /* bDescriptorType */ ++ 1 | UE_DIR_IN, /* bEndpointAddress */ ++ UE_BULK, /* bmAttributes */ ++ UCONSTW(1024), /* wMaxPacketSize */ ++ 0, /* bInterval */ ++ }, ++ /* bulk IN companion descriptor */ ++ { ++ USB_SS_ENDPOINT_COMPANION_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_SS_USB_COMPANION, /* bDescriptorType */ ++ 15, /* bMaxBurst */ ++ 0, /* bmAttributes */ ++ UCONSTW(0), /* wBytesPerInterval */ ++ }, ++ /* bulk OUT endpoint descriptor */ ++ { ++ USB_ENDPOINT_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_ENDPOINT, /* bDescriptorType */ ++ 2 | UE_DIR_OUT, /* bEndpointAddress */ ++ UE_BULK, /* bmAttributes */ ++ UCONSTW(1024), /* wMaxPacketSize */ ++ 0, /* bInterval */ ++ }, ++ /* bulk OUT companion descriptor */ ++ { ++ USB_SS_ENDPOINT_COMPANION_DESCRIPTOR_SIZE, /* bLength */ ++ UDESC_SS_USB_COMPANION, /* bDescriptorType */ ++ 15, /* bMaxBurst */ ++ 0, /* bmAttributes */ ++ UCONSTW(0), /* wBytesPerInterval */ ++ }, ++}; ++ ++/** ++ * This routine handles Get Descriptor requests. ++ */ ++static int no_os_get_descriptor(dwc_usb3_pcd_t *pcd, usb_device_request_t *ctrl) ++{ ++ u8 desc_type = UGETW(ctrl->wValue) >> 8; ++ u8 desc_idx = UGETW(ctrl->wValue); ++ u16 max = UGETW(ctrl->wLength); ++ u8 *buf = pcd->ep0_status_buf; ++ u16 len; ++ ++#ifdef DEBUG_EP0 ++ dwc_debug5(pcd->usb3_dev, "GET_DESCRIPTOR %02x.%02x v%04x i%04x l%04x\n", ++ ctrl->bmRequestType, ctrl->bRequest, UGETW(ctrl->wValue), ++ UGETW(ctrl->wIndex), UGETW(ctrl->wLength)); ++#endif ++ switch (desc_type) { ++ case UDESC_DEVICE: ++ if (desc_idx) ++ return -DWC_E_NOT_SUPPORTED; ++ ++ switch (pcd->speed) { ++ case USB_SPEED_SUPER: ++ USETW(device_desc.bcdUSB, 0x300); ++ device_desc.bMaxPacketSize = 9; ++ break; ++ ++ case USB_SPEED_HIGH: ++ USETW(device_desc.bcdUSB, 0x200); ++ device_desc.bMaxPacketSize = 64; ++ break; ++ ++ default: ++ USETW(device_desc.bcdUSB, 0x200); ++ device_desc.bMaxPacketSize = 64; ++ break; ++ } ++ ++ memcpy(buf, &device_desc, sizeof(device_desc)); ++ len = sizeof(device_desc); ++ break; ++ ++ case UDESC_CONFIG: ++ if (desc_idx) ++ return -DWC_E_NOT_SUPPORTED; ++ ++ switch (pcd->speed) { ++ case USB_SPEED_SUPER: ++ USETW(ss_config_desc.config_desc.wTotalLength, ++ sizeof(ss_config_desc)); ++ memcpy(buf, &ss_config_desc, sizeof(ss_config_desc)); ++ len = sizeof(ss_config_desc); ++ break; ++ ++ case USB_SPEED_HIGH: ++ USETW(hs_config_desc.config_desc.wTotalLength, ++ sizeof(hs_config_desc)); ++ memcpy(buf, &hs_config_desc, sizeof(hs_config_desc)); ++ len = sizeof(hs_config_desc); ++ break; ++ ++ default: ++ USETW(fs_config_desc.config_desc.wTotalLength, ++ sizeof(fs_config_desc)); ++ memcpy(buf, &fs_config_desc, sizeof(fs_config_desc)); ++ len = sizeof(fs_config_desc); ++ break; ++ } ++ ++ break; ++ ++ case UDESC_STRING: ++ switch (desc_idx) { ++ case 0: ++ memcpy(buf, &language_string, sizeof(language_string)); ++ len = sizeof(language_string); ++ break; ++ ++ case DWC_STRING_MANUFACTURER: ++ memcpy(buf, &manuf_string, sizeof(manuf_string)); ++ len = sizeof(manuf_string); ++ break; ++ ++ case DWC_STRING_PRODUCT: ++ memcpy(buf, &product_string, sizeof(product_string)); ++ len = sizeof(product_string); ++ break; ++ ++ case DWC_STRING_SERIAL: ++ memcpy(buf, &serial_string, sizeof(serial_string)); ++ len = sizeof(serial_string); ++ break; ++ ++ default: ++ return -DWC_E_NOT_SUPPORTED; ++ } ++ ++ break; ++ ++ case UDESC_DEVICE_QUALIFIER: ++ if (desc_idx || pcd->speed == USB_SPEED_SUPER) ++ return -DWC_E_NOT_SUPPORTED; ++ ++ USETW(dev_qualifier.bcdUSB, 0x200); ++ dev_qualifier.bMaxPacketSize0 = 9; ++ memcpy(buf, &dev_qualifier, sizeof(dev_qualifier)); ++ len = sizeof(dev_qualifier); ++ break; ++ ++ case UDESC_OTHER_SPEED_CONFIGURATION: ++ if (desc_idx || pcd->speed == USB_SPEED_SUPER) ++ return -DWC_E_NOT_SUPPORTED; ++ ++ switch (pcd->speed) { ++ case USB_SPEED_HIGH: ++ ss_config_desc.config_desc.bDescriptorType = ++ UDESC_OTHER_SPEED_CONFIGURATION; ++ USETW(ss_config_desc.config_desc.wTotalLength, ++ sizeof(ss_config_desc)); ++ memcpy(buf, &ss_config_desc, sizeof(ss_config_desc)); ++ ss_config_desc.config_desc.bDescriptorType = UDESC_CONFIG; ++ len = sizeof(ss_config_desc); ++ break; ++ ++ default: ++ hs_config_desc.config_desc.bDescriptorType = ++ UDESC_OTHER_SPEED_CONFIGURATION; ++ USETW(hs_config_desc.config_desc.wTotalLength, ++ sizeof(hs_config_desc)); ++ memcpy(buf, &hs_config_desc, sizeof(hs_config_desc)); ++ hs_config_desc.config_desc.bDescriptorType = UDESC_CONFIG; ++ len = sizeof(hs_config_desc); ++ break; ++ } ++ ++ break; ++ ++ default: ++ return -DWC_E_NOT_SUPPORTED; ++ } ++ ++ pcd->ep0state = EP0_IN_DATA_PHASE; ++ pcd->ep0_req->dwc_req.length = len < max ? len : max; ++ pcd->ep0_status_pending = 1; ++ pcd->ep0_req->dwc_req.buf[0] = pcd->ep0_status_buf; ++ pcd->ep0_req->dwc_req.bufdma[0] = pcd->ep0_status_buf_dma; ++ pcd->ep0_req->dwc_req.actual = 0; ++ dwc_usb3_pcd_ep0_start_transfer(pcd, pcd->ep0_req); ++ ++ return 0; ++} ++ ++/** ++ * This routine processes SETUP commands. The USB Command processing is ++ * done in two places - the first being the PCD and the second being the ++ * Gadget driver (for example, the File-Backed Storage Gadget driver). ++ * ++ * <table> ++ * <tr><td> Command </td><td> Driver </td><td> Description </td></tr> ++ * ++ * <tr><td> SET_FEATURE </td><td> PCD / Gadget driver </td><td> Device ++ * and Endpoint requests are processed by the PCD. Interface requests ++ * are passed to the Gadget driver. </td></tr> ++ * ++ * <tr><td> CLEAR_FEATURE </td><td> PCD </td><td> Device and Endpoint ++ * requests are processed by the PCD. Interface requests are ignored. ++ * The only Endpoint feature handled is ENDPOINT_HALT. </td></tr> ++ * ++ * <tr><td> GET_DESCRIPTOR </td><td> Gadget driver </td><td> Return the ++ * requested descriptor. </td></tr> ++ * ++ * <tr><td> SET_DESCRIPTOR </td><td> Gadget driver </td><td> Optional - ++ * not implemented by any of the existing Gadget drivers. </td></tr> ++ * ++ * <tr><td> GET_CONFIGURATION </td><td> Gadget driver </td><td> Return ++ * the current configuration. </td></tr> ++ * ++ * <tr><td> SET_CONFIGURATION </td><td> Gadget driver </td><td> Disable ++ * all EPs and enable EPs for new configuration. </td></tr> ++ * ++ * <tr><td> GET_INTERFACE </td><td> Gadget driver </td><td> Return the ++ * current interface. </td></tr> ++ * ++ * <tr><td> SET_INTERFACE </td><td> Gadget driver </td><td> Disable all ++ * EPs and enable EPs for new interface. </td></tr> ++ * </table> ++ * ++ * When the SETUP Phase Done interrupt occurs, the generic SETUP commands ++ * are processed by dwc_usb3_do_setup(). Calling the gadget driver's ++ * dwc_usb3_gadget_setup() routine from dwc_usb3_do_setup() in turn calls ++ * this routine to process the gadget-specific SETUP commands. Any requests ++ * not handled here are passed to the Function Driver's ++ * dwc_usb3_function_setup() routine. ++ */ ++int dwc_usb3_no_os_setup(dwc_usb3_pcd_t *pcd, usb_device_request_t *ctrl) ++{ ++ dwc_debug2(pcd->usb3_dev, "%s(%lx)\n", __func__, (unsigned long)pcd); ++ ++ if ((UT_GET_TYPE(ctrl->bmRequestType)) != UT_STANDARD) { ++ /* handle non-standard (class/vendor) requests ++ * in the Function Driver ++ */ ++ return dwc_usb3_function_setup(pcd, ctrl); ++ } ++ ++ /* @todo NGS: Handle bad setup packet? */ ++ ++/////////////////////////////////////////// ++//// --- Standard Request handling --- //// ++ ++ switch (ctrl->bRequest) { ++ case UR_SET_FEATURE: ++ return dwc_usb3_function_setup(pcd, ctrl); ++ ++ case UR_SET_INTERFACE: ++ return dwc_usb3_function_setup(pcd, ctrl); ++ ++ case UR_GET_INTERFACE: ++ return dwc_usb3_function_setup(pcd, ctrl); ++ ++ case UR_SET_CONFIG: ++ return dwc_usb3_function_setup(pcd, ctrl); ++ ++ case UR_GET_CONFIG: ++ return dwc_usb3_function_setup(pcd, ctrl); ++ ++ case UR_SYNCH_FRAME: ++ return dwc_usb3_function_setup(pcd, ctrl); ++ ++ case UR_GET_DESCRIPTOR: ++ return no_os_get_descriptor(pcd, ctrl); ++ ++ default: ++ /* Call the Function Driver setup routines */ ++ return dwc_usb3_function_setup(pcd, ctrl); ++ } ++ ++ return 0; ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/no_os/no_os_gadget.c b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_gadget.c +new file mode 100644 +index 0000000..5bd2c6a +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_gadget.c +@@ -0,0 +1,733 @@ ++/** @file ++ * ++ * Application-specific Function Driver interface ++ * ++ * Simple applications may wish to implement their entire Function Driver here. ++ * ++ * More complex applications, or environments with an existing Function Driver ++ * framework (such as the Linux Gadget framework) will implement the interface ++ * to their Function Driver here. ++ * ++ * See the linux/ subdirectory for an example implementation of a Linux Gadget ++ * framework interface. ++ * ++ * For historical reasons, the PCD API uses the Linux term "gadget" to refer to ++ * a Function Driver. ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++/** Driver context struct - defined in no_os_init.c */ ++extern dwc_usb3_device_t g_usb3_dev; ++ ++/** @{ */ ++/** PCD request pool - defined in no_os_init.c */ ++extern dwc_usb3_pcd_req_t g_pcd_req[]; ++extern u32 g_pcd_req_bm; ++/** @} */ ++ ++#ifndef LINUXTEST ++/** @{ */ ++/** DMA descriptor (TRB) pools for non-EP0 EPs - defined in no_os_init.c */ ++extern dwc_usb3_dma_desc_t g_out_trb_pool[][DWC_NUM_ISOC_TRBS + 1]; ++extern dwc_usb3_dma_desc_t g_in_trb_pool[][DWC_NUM_ISOC_TRBS + 1]; ++/** @} */ ++#endif ++ ++/****************************************************************************** ++ * Function Driver notification routines ++ * ++ * These routines receive notifications from the PCD when certain events occur ++ * which the Function Driver may need to be aware of. ++ * ++ * These routines *must* have the exact names and parameters shown here, ++ * because they are part of the API between the PCD and the Function Driver. ++ ******************************************************************************/ ++ ++/** ++ * This routine receives Connect notifications from the PCD ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param speed Speed of the connection (as defined in usb.h). ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_connect(dwc_usb3_pcd_t *pcd, int speed) ++{ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ dwc_usb3_function_connect(pcd->usb3_dev, speed); ++ return 0; ++} ++ ++/** ++ * This routine receives Disconnect notifications from the PCD ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_disconnect(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ dwc_usb3_function_disconnect(pcd->usb3_dev); ++ return 0; ++} ++ ++/** ++ * This routine receives Suspend notifications from the PCD ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_suspend(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ return 0; ++} ++ ++/** ++ * This routine receives Resume notifications from the PCD ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_resume(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ return 0; ++} ++ ++/** ++ * This routine receives Setup request notifications from the PCD ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ctrl Pointer to the Setup packet for the request. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_setup(dwc_usb3_pcd_t *pcd, usb_device_request_t *ctrl) ++{ ++ int ret; ++ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ ++ ret = dwc_usb3_no_os_setup(pcd, ctrl); ++ ++ return ret; ++} ++ ++/** ++ * This routine receives Transfer Complete notifications from the PCD ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param pcd_ep PCD EP for the transfer. ++ * @param pcd_req PCD request for the transfer. ++ * @param status Transfer status, 0 for success else negative error code. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_complete(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep, ++ dwc_usb3_pcd_req_t *pcd_req, int status) ++{ ++ usb_ep_t *usb_ep = &pcd_ep->usb_ep; ++ usb_request_t *usb_req = &pcd_req->usb_req; ++ u32 actual = pcd_req->dwc_req.actual; ++ ++ dwc_debug2(pcd->usb3_dev, "%s(%lx)\n", __func__, (unsigned long)pcd_ep); ++ ++ /* Remove the request from the queue */ ++ DWC_CIRCLEQ_REMOVE_INIT(&pcd_ep->dwc_ep.queue, pcd_req, entry); ++ ++ usb_req->status = status; ++ usb_req->actual = actual; ++ ++ if (usb_req->complete) { ++ dwc_release_spinlock(pcd->usb3_dev, &pcd->lock); ++ usb_req->complete(usb_ep, usb_req); ++ dwc_acquire_spinlock(pcd->usb3_dev, &pcd->lock); ++ } ++ ++ if (pcd->request_pending > 0) ++ --pcd->request_pending; ++ ++ return 0; ++} ++ ++/** ++ * This routine allows overriding the standard Control transfer handling ++ * (currently only done by the axs101 test gadget) ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++void dwc_usb3_gadget_do_setup(dwc_usb3_pcd_t *pcd) ++{ ++ /* Call the standard Control transfer handler */ ++ dwc_usb3_pcd_do_setup(pcd); ++} ++ ++/****************************************************************************** ++ * Function Driver callback routines ++ * ++ * The PCD calls these routines when it needs something from the Function ++ * Driver. ++ * ++ * These routines *must* have the exact names and parameters shown here, ++ * because they are part of the API between the PCD and the Function Driver. ++ ******************************************************************************/ ++ ++/** ++ * This routine allocates coherent DMA memory. It is used by the PCD to ++ * allocate memory for TRBs. The block of memory returned must have a start ++ * address aligned to a 16-byte boundary. ++ * ++ * This routine may be called from interrupt context, so it must be able to ++ * allocate coherent DMA memory in that context. One strategy would be to ++ * preallocate a block of memory at initialization time, and hand out chunks ++ * from that block in this routine. ++ * ++ * @param pcd_ep PCD EP that memory block will be associated with. ++ * @param size Size of memory block to allocate, in bytes. ++ * @param mem_dma_ret Physical address of allocated memory block is returned ++ * through this pointer. ++ * @return Address of allocated memory block, or NULL if allocation ++ * fails. ++ */ ++void *dwc_usb3_gadget_alloc_dma(dwc_usb3_pcd_ep_t *pcd_ep, int size, dwc_dma_t *mem_dma_ret) ++{ ++ void *mem; ++ dwc_dma_t mem_dma; ++ ++ dwc_debug2(pcd_ep->dwc_ep.pcd->usb3_dev, "%s(%lx)\n", __func__, (unsigned long)pcd_ep); ++ ++ if (!pcd_ep->dwc_ep.num) { ++ dwc_error0(pcd_ep->dwc_ep.pcd->usb3_dev, "USB epnum is 0 for pcd_ep\n"); ++ return NULL; ++ } ++ ++#ifdef LINUXTEST ++ mem = dma_alloc_coherent(g_usb3_dev.dev, size, &mem_dma, GFP_ATOMIC | GFP_DMA32); ++ if (!mem) ++ return NULL; ++#else ++ if (pcd_ep->dwc_ep.is_in) ++ mem = (void *)&g_in_trb_pool[pcd_ep->dwc_ep.num - 1]; ++ else ++ mem = (void *)&g_out_trb_pool[pcd_ep->dwc_ep.num - 1]; ++ ++ mem_dma = (dwc_dma_t)mem; ++#endif ++ *mem_dma_ret = mem_dma; ++ ++ return mem; ++} ++ ++/** ++ * This routine frees DMA memory allocated by dwc_usb3_gadget_alloc_dma(). ++ * ++ * @param pcd_ep PCD EP that memory block is associated with. ++ * @param size Size of memory block to free, in bytes. ++ * @param mem Address of memory block. ++ * @param mem_dma Physical address of memory block. ++ */ ++void dwc_usb3_gadget_free_dma(dwc_usb3_pcd_ep_t *pcd_ep, int size, void *mem, dwc_dma_t mem_dma) ++{ ++ dwc_debug2(pcd_ep->dwc_ep.pcd->usb3_dev, "%s(%lx)\n", __func__, (unsigned long)pcd_ep); ++ ++#ifdef LINUXTEST ++ dma_free_coherent(g_usb3_dev.dev, size, mem, mem_dma); ++#endif ++} ++ ++/** ++ * This routine returns the PCD request corresponding to the current transfer ++ * request for an endpoint. The current transfer request is the first request ++ * submitted that has not been completed yet. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param pcd_ep PCD EP to operate on. ++ * @return Pointer to PCD request, or NULL if no request available. ++ */ ++dwc_usb3_pcd_req_t *dwc_usb3_gadget_get_request(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep) ++{ ++ dwc_usb3_pcd_req_t *pcd_req; ++ ++ dwc_debug2(pcd->usb3_dev, "%s(%lx)\n", __func__, (unsigned long)pcd_ep); ++ ++ /* Check for a pending request */ ++ if (DWC_CIRCLEQ_EMPTY(&pcd_ep->dwc_ep.queue)) { ++ dwc_debug0(pcd->usb3_dev, "ep->dwc_ep.queue empty\n"); ++ return NULL; ++ } ++ ++ pcd_req = DWC_CIRCLEQ_FIRST(&pcd_ep->dwc_ep.queue); ++ return pcd_req; ++} ++ ++/** ++ * This routine checks to see if there is another transfer request waiting ++ * on an endpoint that has not been started yet. If so then that transfer is ++ * started. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param pcd_ep PCD EP to operate on. ++ */ ++void dwc_usb3_gadget_start_next_request(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep) ++{ ++ dwc_usb3_pcd_req_t *pcd_req = NULL; ++ ++ dwc_debug2(pcd->usb3_dev, "%s(%lx)\n", __func__, (unsigned long)pcd_ep); ++ ++ if (DWC_CIRCLEQ_EMPTY(&pcd_ep->dwc_ep.queue)) { ++ dwc_debug2(pcd->usb3_dev, "start_next EP%d-%s: queue empty\n", ++ pcd_ep->dwc_ep.num, pcd_ep->dwc_ep.is_in ? "IN" : "OUT"); ++ return; ++ } ++ ++ DWC_CIRCLEQ_FOREACH(pcd_req, &pcd_ep->dwc_ep.queue, entry) { ++ if (!(pcd_req->dwc_req.flags & DWC_PCD_REQ_STARTED)) { ++ dwc_debug2(pcd->usb3_dev, "start_next EP%d-%s: OK\n", ++ pcd_ep->dwc_ep.num, pcd_ep->dwc_ep.is_in ? "IN" : "OUT"); ++ ++ /* Setup and start the transfer */ ++ dwc_usb3_pcd_fill_trbs(pcd, pcd_ep, pcd_req); ++ dwc_usb3_pcd_ep_start_transfer(pcd, pcd_ep, pcd_req, 0); ++ return; ++ } ++ } ++ ++ dwc_debug2(pcd->usb3_dev, "start_next EP%d-%s: all req active\n", ++ pcd_ep->dwc_ep.num, pcd_ep->dwc_ep.is_in ? "IN" : "OUT"); ++} ++ ++/** ++ * Start an Isoc EP running at the proper interval, after receiving the initial ++ * XferNrdy event. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param pcd_ep EP to operate on. ++ * @param event Event data containing the XferNrdy microframe. ++ */ ++void dwc_usb3_gadget_isoc_ep_start(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep, u32 event) ++{ ++ dwc_usb3_pcd_req_t *pcd_req = NULL; ++ ++ dwc_debug3(pcd->usb3_dev, "%s(%lx,%x)\n", __func__, (unsigned long)pcd_ep, event); ++ dwc_isocdbg2(pcd->usb3_dev, "%s(%08x)\n", __func__, event); ++ ++ if (DWC_CIRCLEQ_EMPTY(&pcd_ep->dwc_ep.queue)) { ++ dwc_debug2(pcd->usb3_dev, "%s(%lx), ep->dwc_ep.queue empty\n", ++ __func__, (unsigned long)pcd_ep); ++ return; ++ } ++ ++ if (pcd_ep->dwc_ep.desc_avail <= 0) { ++ dwc_debug2(pcd->usb3_dev, "EP%d-%s: no TRB avail\n", ++ pcd_ep->dwc_ep.num, pcd_ep->dwc_ep.is_in ? "IN" : "OUT"); ++ return; ++ } ++ ++ /* If need to restart after hibernation, handle that and return */ ++ if (dwc_usb3_pcd_isoc_ep_hiber_restart(pcd, pcd_ep)) ++ return; ++ ++ /* ++ * Start the next queued transfer at the target uFrame ++ */ ++ ++ DWC_CIRCLEQ_FOREACH(pcd_req, &pcd_ep->dwc_ep.queue, entry) { ++ if (pcd_req->dwc_req.flags & DWC_PCD_REQ_STARTED) ++ pcd_req = NULL; ++ else ++ break; ++ } ++ ++ dwc_debug1(pcd->usb3_dev, "req=%lx\n", (unsigned long)pcd_req); ++ if (!pcd_req) { ++ dwc_debug2(pcd->usb3_dev, "EP%d-%s: no requests to start\n", ++ pcd_ep->dwc_ep.num, pcd_ep->dwc_ep.is_in ? "IN" : "OUT"); ++ return; ++ } ++ ++ dwc_usb3_pcd_fill_trbs(pcd, pcd_ep, pcd_req); ++ dwc_usb3_pcd_ep_start_transfer(pcd, pcd_ep, pcd_req, event); ++ ++ /* ++ * Now start any remaining queued transfers ++ */ ++ ++ while (pcd_req != DWC_CIRCLEQ_LAST(&pcd_ep->dwc_ep.queue)) { ++ pcd_req = DWC_CIRCLEQ_NEXT(pcd_req, entry); ++ if (!(pcd_req->dwc_req.flags & DWC_PCD_REQ_STARTED)) { ++ dwc_usb3_pcd_fill_trbs(pcd, pcd_ep, pcd_req); ++ dwc_usb3_pcd_ep_start_transfer(pcd, pcd_ep, pcd_req, 0); ++ } ++ } ++} ++ ++/** ++ * This routine terminates all requests which are pending on an endpoint. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param pcd_ep EP to operate on. ++ */ ++void dwc_usb3_gadget_request_nuke(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep) ++{ ++ dwc_usb3_pcd_req_t *pcd_req; ++ ++ dwc_debug2(pcd->usb3_dev, "%s(%lx)\n", __func__, (unsigned long)pcd_ep); ++ pcd_ep->dwc_ep.stopped = 1; ++ ++ while (!DWC_CIRCLEQ_EMPTY(&pcd_ep->dwc_ep.queue)) { ++ pcd_req = DWC_CIRCLEQ_FIRST(&pcd_ep->dwc_ep.queue); ++ dwc_usb3_pcd_request_done(pcd, pcd_ep, pcd_req, -DWC_E_SHUTDOWN); ++ } ++ ++ if (pcd_ep != pcd->ep0) ++ pcd_ep->dwc_ep.hiber_desc_idx = 0; ++} ++ ++/** ++ * This routine marks all pending requests for an EP as not started. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param pcd_ep EP to operate on. ++ */ ++void dwc_usb3_gadget_set_ep_not_started(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep) ++{ ++ dwc_usb3_pcd_req_t *pcd_req; ++ ++ dwc_debug2(pcd->usb3_dev, "%s(%lx)\n", __func__, (unsigned long)pcd_ep); ++ ++ DWC_CIRCLEQ_FOREACH(pcd_req, &pcd_ep->dwc_ep.queue, entry) { ++ if (pcd_req->dwc_req.flags & DWC_PCD_REQ_STARTED) ++ pcd_req->dwc_req.flags &= ~DWC_PCD_REQ_STARTED; ++ } ++} ++ ++/****************************************************************************** ++ * Function Driver user-defined routines ++ * ++ * These routines are completely user-defined, and can implement whatever is ++ * required for the application's Function Driver. ++ * ++ * The example code here implements a simple interface which allows external ++ * code to implement the application's Function Driver by calling these ++ * routines. ++ ******************************************************************************/ ++ ++/** ++ * This routine enables a USB endpoint ++ * ++ * @param usb3_dev Programming view of DWC_usb3 device. ++ * @param epdesc USB endpoint descriptor for the EP. ++ * @param epcomp USB SS endpoint companion descriptor for the EP. ++ * @return Pointer to USB EP context, or NULL on failure. ++ */ ++usb_ep_t *dwc_usb3_ep_enable(struct dwc_usb3_device *usb3_dev, const void *epdesc, const void *epcomp) ++{ ++ const usb_endpoint_descriptor_t *ep_desc = epdesc; ++ const ss_endpoint_companion_descriptor_t *ep_comp = epcomp; ++ dwc_usb3_pcd_t *pcd = &usb3_dev->pcd; ++ dwc_usb3_pcd_ep_t *pcd_ep; ++ dwc_usb3_dma_desc_t *desc; ++ dwc_dma_t desc_dma; ++ usb_ep_t *usb_ep; ++ int ep_num, ep_dir, ep_type; ++ int link, num_trbs, retval; ++ ++ dwc_debug1(usb3_dev, "%s()\n", __func__); ++ ++ ep_num = UE_GET_ADDR(ep_desc->bEndpointAddress); ++ ep_dir = UE_GET_DIR(ep_desc->bEndpointAddress); ++ ep_type = UE_GET_XFERTYPE(ep_desc->bmAttributes); ++ link = 0; ++ ++ if (ep_num < 1 || ep_num >= DWC_MAX_EPS) ++ return NULL; ++ ++ if (ep_dir == UE_DIR_IN) ++ pcd_ep = pcd->in_ep[ep_num - 1]; ++ else ++ pcd_ep = pcd->out_ep[ep_num - 1]; ++ usb_ep = &pcd_ep->usb_ep; ++ ++ /* Allocate the number of TRBs based on EP type */ ++ switch (ep_type) { ++ case UE_INTERRUPT: ++ num_trbs = DWC_NUM_INTR_TRBS; ++ break; ++ case UE_ISOCHRONOUS: ++ num_trbs = DWC_NUM_ISOC_TRBS; ++ link = 1; ++ break; ++ default: ++ num_trbs = DWC_NUM_BULK_TRBS; ++ break; ++ } ++ ++ dwc_debug5(usb3_dev, "ep%d-%s=%lx phys=%d pcd_ep=%lx\n", ep_num, ++ ep_dir == UE_DIR_IN ? "IN" : "OUT", (unsigned long)usb_ep, ++ pcd_ep->dwc_ep.phys, (unsigned long)pcd_ep); ++ ++ /* Set the TRB allocation for this EP */ ++ desc = dwc_usb3_pcd_trb_alloc(pcd_ep, num_trbs, ep_type, ep_desc->bInterval - 1, ++ link, &desc_dma); ++ if (!desc) ++ return NULL; ++ ++ /* Call the PCD API routine to enable the endpoint */ ++ retval = dwc_usb3_pcd_ep_enable(pcd, pcd_ep, ep_desc, ep_comp); ++ if (retval) { ++ dwc_usb3_pcd_trb_free(pcd_ep); ++ return NULL; ++ } ++ ++ usb_ep->maxpacket = UGETW(ep_desc->wMaxPacketSize); ++ usb_ep->desc = epdesc; ++ ++ return usb_ep; ++} ++ ++/** ++ * This routine disables a USB endpoint ++ * ++ * @param usb3_dev Programming view of DWC_usb3 device. ++ * @param usb_ep USB EP to disable. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_ep_disable(struct dwc_usb3_device *usb3_dev, usb_ep_t *usb_ep) ++{ ++ dwc_usb3_pcd_t *pcd = &usb3_dev->pcd; ++ dwc_usb3_pcd_ep_t *pcd_ep; ++ int retval; ++ ++ dwc_debug2(usb3_dev, "%s(%lx)\n", __func__, (unsigned long)usb_ep); ++ pcd_ep = dwc_usb3_get_pcd_ep(usb_ep); ++ ++ /* Call the PCD API routine to disable the endpoint */ ++ retval = dwc_usb3_pcd_ep_disable(pcd, pcd_ep); ++ ++ dwc_usb3_pcd_trb_free(pcd_ep); ++ ++ return retval; ++} ++ ++/** ++ * This routine allocates a USB request object to use with the specified USB ++ * endpoint. The contents of a USB request are defined by the Function Driver, ++ * and are opaque to the PCD. The USB request is embedded inside of an enclosing ++ * PCD request object (see the definition of struct dwc_usb3_pcd_req in pcd.h). ++ * One request object is needed for each transfer that is submitted to the PCD. ++ * ++ * @param usb3_dev Programming view of DWC_usb3 device. ++ * @param usb_ep USB EP for the request. ++ */ ++usb_request_t *dwc_usb3_alloc_request(struct dwc_usb3_device *usb3_dev, usb_ep_t *usb_ep) ++{ ++ dwc_usb3_pcd_req_t *pcd_req; ++ unsigned int req_idx; ++ ++ dwc_debug2(usb3_dev, "%s(%lx)\n", __func__, (unsigned long)usb_ep); ++ ++ for (req_idx = 0; req_idx < 32; req_idx++) { ++ if (g_pcd_req_bm & (1 << req_idx)) { ++ g_pcd_req_bm &= ~(1 << req_idx); ++ pcd_req = &g_pcd_req[req_idx]; ++ memset(pcd_req, 0, sizeof(*pcd_req)); ++ pcd_req->usb_req.dma = DWC_DMA_ADDR_INVALID; ++ return &pcd_req->usb_req; ++ } ++ } ++ ++ return NULL; ++} ++ ++/** ++ * This routine frees a USB request object ++ * ++ * @param usb3_dev Programming view of DWC_usb3 device. ++ * @param usb_ep USB EP for the request. ++ * @param usb_req USB request to be freed. ++ */ ++void dwc_usb3_free_request(struct dwc_usb3_device *usb3_dev, usb_ep_t *usb_ep, usb_request_t *usb_req) ++{ ++ dwc_usb3_pcd_req_t *pcd_req = dwc_usb3_get_pcd_req(usb_req); ++ unsigned int req_idx = pcd_req - &g_pcd_req[0]; ++ ++ dwc_debug2(usb3_dev, "%s(%lx)\n", __func__, (unsigned long)usb_ep); ++ ++ if (req_idx >= 32) { ++ dwc_error1(usb3_dev, "Bad request index %u\n", req_idx); ++ return; ++ } ++ ++ g_pcd_req_bm |= 1 << req_idx; ++} ++ ++/** ++ * This routine submits a transfer request for a USB endpoint. The example code ++ * maintains a queue of requests for each endpoint, so that the application can ++ * start another transfer on the same endpoint without having to wait for the ++ * first transfer to complete. ++ * ++ * @param usb3_dev Programming view of DWC_usb3 device. ++ * @param usb_ep USB EP for the transfer. ++ * @param usb_req USB request for the transfer. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_ep_queue(struct dwc_usb3_device *usb3_dev, usb_ep_t *usb_ep, usb_request_t *usb_req) ++{ ++ dwc_usb3_pcd_t *pcd = &usb3_dev->pcd; ++ dwc_usb3_pcd_ep_t *pcd_ep = dwc_usb3_get_pcd_ep(usb_ep); ++ dwc_usb3_pcd_req_t *pcd_req = dwc_usb3_get_pcd_req(usb_req); ++ int retval = 0, req_flags = 0; ++ ++ dwc_debug2(usb3_dev, "%s(%lx)\n", __func__, (unsigned long)usb_ep); ++ ++ usb_req->status = -DWC_E_IN_PROGRESS; ++ usb_req->actual = 0; ++ ++ if (usb_req->zero) ++ req_flags |= DWC_PCD_REQ_ZERO; ++ ++ pcd_req->dwc_req.buf[0] = usb_req->buf; ++ pcd_req->dwc_req.bufdma[0] = usb_req->dma; ++ pcd_req->dwc_req.buflen[0] = usb_req->length; ++ pcd_req->dwc_req.numbuf = 1; ++ pcd_req->dwc_req.length = usb_req->length; ++ pcd_req->dwc_req.actual = 0; ++ pcd_req->dwc_req.stream = usb_req->stream_id; ++ pcd_req->dwc_req.flags = req_flags; ++ ++ DWC_CIRCLEQ_INIT_ENTRY(pcd_req, entry); ++#if 1 ++ if (!DWC_CIRCLEQ_EMPTY(&pcd_ep->dwc_ep.queue)) { ++ /* If queue is not empty, and this is not an Isoc transfer, then ++ * don't submit the transfer. But we still queue the request so ++ * it can be submitted later. ++ */ ++ dwc_debug4(usb3_dev, "q !empty, pend %d, stop %d, avail %d, start %d\n", ++ pcd->request_pending, pcd_ep->dwc_ep.stopped, ++ pcd_ep->dwc_ep.desc_avail, pcd_ep->dwc_ep.xfer_started); ++ ++ if (!pcd_ep->dwc_ep.stopped && pcd_ep->dwc_ep.type == UE_ISOCHRONOUS && ++ pcd_ep->dwc_ep.desc_avail > 0 && pcd_ep->dwc_ep.xfer_started) ++ goto do_start; ++ } else ++#endif ++ if (pcd_ep->dwc_ep.stopped || (pcd_ep != pcd->ep0 && pcd_ep->dwc_ep.desc_avail <= 0) || ++ (pcd_ep->dwc_ep.type == UE_ISOCHRONOUS && !pcd_ep->dwc_ep.xfer_started)) { ++ /* If EP is stopped, or there is no TRB available, or this ++ * is an Isoc transfer and the EP is not started, then don't ++ * submit the transfer. But we still queue the request so it ++ * can be submitted later. ++ */ ++ dwc_debug4(usb3_dev, ++ " q empty, pend %d, stop %d, avail %d, start %d\n", ++// " pend %d, stop %d, avail %d, start %d\n", ++ pcd->request_pending, pcd_ep->dwc_ep.stopped, ++ pcd_ep->dwc_ep.desc_avail, pcd_ep->dwc_ep.xfer_started); ++ } else { ++do_start: ++ /* Call the PCD API routine to set up the request TRBs */ ++ dwc_usb3_pcd_fill_trbs(pcd, pcd_ep, pcd_req); ++ ++ /* Call the PCD API routine to submit the transfer request */ ++ retval = dwc_usb3_pcd_ep_submit_req(pcd, pcd_ep, pcd_req, req_flags); ++ } ++ ++ if (!retval) { ++ DWC_CIRCLEQ_INSERT_TAIL(&pcd_ep->dwc_ep.queue, pcd_req, entry); ++ ++pcd->request_pending; ++ } ++ ++ /* dwc_usb3_pcd_ep_submit_req() can return positive values for ++ * Control transfers, don't propagate those from this function ++ */ ++ return retval < 0 ? retval : 0; ++} ++ ++/** ++ * This routine cancels a transfer request for a USB endpoint. This is only ++ * needed in exceptional cases, a normal transfer completion does not require ++ * this. ++ * ++ * @param usb3_dev Programming view of DWC_usb3 device. ++ * @param usb_ep USB EP for the transfer. ++ * @param usb_req USB request for the transfer. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_ep_dequeue(struct dwc_usb3_device *usb3_dev, usb_ep_t *usb_ep, usb_request_t *usb_req) ++{ ++ dwc_usb3_pcd_t *pcd = &usb3_dev->pcd; ++ dwc_usb3_pcd_ep_t *pcd_ep = dwc_usb3_get_pcd_ep(usb_ep); ++ dwc_usb3_pcd_req_t *pcd_req; ++ ++ dwc_debug2(usb3_dev, "%s(%lx)\n", __func__, (unsigned long)usb_ep); ++ ++ if (dwc_usb3_pcd_ep_num(pcd_ep) != 0 && !pcd_ep->dwc_ep.usb_ep_desc) { ++ dwc_warn1(usb3_dev, "%s, bad pcd_ep\n", __func__); ++ return -DWC_E_INVALID; ++ } ++ ++ /* Make sure it's actually queued on this EP */ ++ DWC_CIRCLEQ_FOREACH(pcd_req, &pcd_ep->dwc_ep.queue, entry) { ++ if (&pcd_req->usb_req == usb_req) ++ break; ++ } ++ ++ if (!pcd_req) { ++ dwc_warn1(usb3_dev, "%s, no request in queue\n", __func__); ++ return 0; ++ } ++ ++ /* Call the PCD API routine to cancel the transfer request */ ++ dwc_usb3_pcd_ep_cancel_req(pcd, pcd_ep, pcd_req, usb_req->stream_id); ++ ++ return 0; ++} ++ ++/** ++ * Function Driver initialization routine ++ * ++ * @param usb3_dev Programming view of DWC_usb3 device. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_init(dwc_usb3_device_t *usb3_dev) ++{ ++ dwc_usb3_pcd_t *pcd = &usb3_dev->pcd; ++ dwc_usb3_pcd_ep_t *ep; ++ int i; ++ ++ dwc_debug1(usb3_dev, "%s()\n", __func__); ++ ++ ep = pcd->ep0; ++ ep->dwc_ep.num = 0; ++ DWC_CIRCLEQ_INIT(&ep->dwc_ep.queue); ++ ++ for (i = 0; i < pcd->num_out_eps; i++) { ++ ep = pcd->out_ep[i]; ++ ep->dwc_ep.num = i + 1; ++ DWC_CIRCLEQ_INIT(&ep->dwc_ep.queue); ++ } ++ ++ for (i = 0; i < pcd->num_in_eps; i++) { ++ ep = pcd->in_ep[i]; ++ ep->dwc_ep.num = i + 1; ++ DWC_CIRCLEQ_INIT(&ep->dwc_ep.queue); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Function Driver removal routine ++ * ++ * @param usb3_dev Programming view of DWC_usb3 device. ++ */ ++void dwc_usb3_gadget_remove(dwc_usb3_device_t *usb3_dev) ++{ ++ dwc_debug1(usb3_dev, "%s()\n", __func__); ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/no_os/no_os_hiber.c b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_hiber.c +new file mode 100644 +index 0000000..995eb40 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_hiber.c +@@ -0,0 +1,136 @@ ++/** @file ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++/** ++ * Helper routine for dwc_wait_pme_thread() ++ */ ++static void dwc_wait_for_link_up(dwc_usb3_pcd_t *pcd) ++{ ++ unsigned long flags; ++ u32 temp; ++ int count = 5; ++ ++ while (count-- > 0) { ++ dwc_msleep(pcd->usb3_dev, 20); ++ dwc_acquire_spinlock_irq(pcd->usb3_dev, &pcd->lock, flags); ++ temp = dwc_usb3_pcd_get_link_state(pcd); ++ if (temp == 0) { ++ dwc_info0(pcd->usb3_dev, "Exiting from hibernation 2\n"); ++ pcd->usb3_dev->hiber_wait_connect = 0; ++ ++ /* Perform the steps in Section 9.1.3 "Initialization on ++ * Connect Done" in [DWSS]. ++ */ ++ dwc_usb3_handle_connect_done_intr(pcd); ++ dwc_exit_hibernation_after_connect(pcd, 0); ++ count = 0; ++ } ++ ++ dwc_release_spinlock_irq(pcd->usb3_dev, &pcd->lock, flags); ++ } ++} ++ ++/** ++ * Routine that monitors the dev->hibernate variable and the 'debug' ++ * register in the FPGA. dev->hibernate is set to 1 or 2 from the interrupt ++ * handler when the core is requesting to enter hibernation. This routine ++ * checks whether it is safe to do so (dev->hiber_cnt == 0) and then sets ++ * dev->hibernate to 3 and puts the core into hibernation. dev->hibernate >= 3 ++ * tells the rest of the driver that it cannot access the hardware. ++ * ++ * When the core is in hibernation, this routine should be called periodically ++ * to read the 'debug' register to see if the core is requesting to exit ++ * hibernation. If so, it brings the core out of hibernation. ++ * ++ * Note that this is NOT the way a "real" device would enter and exit ++ * hibernation. This code is ONLY for testing hibernation on the Synopsys ++ * HAPS platform. ++ */ ++int dwc_usb3_wait_pme(dwc_usb3_device_t *dev) ++{ ++ dwc_usb3_pcd_t *pcd = &dev->pcd; ++ unsigned long flags; ++ u32 temp; ++ int state, i; ++ ++ dwc_acquire_spinlock_irq(dev, &pcd->lock, flags); ++ temp = dev->hibernate; ++ ++ if (dev->hiber_cnt == 0) { ++ if (temp == DWC_HIBER_ENTER_NOSAVE || temp == DWC_HIBER_ENTER_SAVE) { ++ dwc_enter_hibernation(pcd, temp == DWC_HIBER_ENTER_SAVE); ++ temp = DWC_HIBER_SLEEPING; ++ } ++ } ++ ++ if (temp == DWC_HIBER_WAIT_LINK_UP) { ++ dev->hibernate = DWC_HIBER_AWAKE; ++ temp = DWC_HIBER_AWAKE; ++ dwc_release_spinlock_irq(dev, &pcd->lock, flags); ++ dwc_wait_for_link_up(pcd); ++ dwc_acquire_spinlock_irq(dev, &pcd->lock, flags); ++ } ++ ++ if (temp == DWC_HIBER_WAIT_U0) { ++ state = dwc_usb3_pcd_get_link_state(pcd); ++ if (state == 0) { ++ dev->hibernate = DWC_HIBER_AWAKE; ++ temp = DWC_HIBER_AWAKE; ++ } ++ } ++ ++ if (temp == DWC_HIBER_SS_DIS_QUIRK) { ++ for (i = 0; i < 500; i++) { ++ temp = dev->hibernate; ++ if (temp != DWC_HIBER_SS_DIS_QUIRK) { ++ dwc_debug1(dev, "breaking loop after %d ms\n", i); ++ break; ++ } ++ ++ dwc_release_spinlock_irq(dev, &pcd->lock, flags); ++ dwc_msleep(dev, 1); ++ dwc_acquire_spinlock_irq(dev, &pcd->lock, flags); ++ } ++ ++ if (temp == DWC_HIBER_SS_DIS_QUIRK) { ++ if (dwc_usb3_pcd_get_link_state(pcd) == DWC_LINK_STATE_SS_DIS) { ++ dwc_enter_hibernation(pcd, 0); ++ temp = DWC_HIBER_SLEEPING; ++ } else { ++ dev->hibernate = DWC_HIBER_AWAKE; ++ temp = DWC_HIBER_AWAKE; ++ } ++ } ++ } ++ ++ dwc_release_spinlock_irq(dev, &pcd->lock, flags); ++ dwc_msleep(dev, 1); ++ ++ return temp; ++} ++ ++int dwc_usb3_handle_pme_intr(dwc_usb3_device_t *dev) ++{ ++ int ret; ++ ++ dwc_debug1(dev, "%s()\n", __func__); ++ ++ dwc_debug0(dev, "calling dwc_exit_hibernation()\n"); ++ ret = dwc_exit_hibernation(&dev->pcd, 1); ++ if (ret) ++ dwc_info1(dev, "dwc_exit_hibernation() returned %d\n", ret); ++ ++ return 1; ++} ++ ++void dwc_usb3_power_ctl(dwc_usb3_device_t *dev, int on) ++{ ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/no_os/no_os_init.c b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_init.c +new file mode 100644 +index 0000000..939b323 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_init.c +@@ -0,0 +1,674 @@ ++/** @file ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++#ifdef LINUXTEST ++#define DWC_DRIVER_VERSION "2.90b - November 2014" ++#define DWC_DRIVER_DESC "SS USB3 Controller driver" ++ ++static const char dwc_driver_name[] = "dwc_usb3"; ++#endif ++ ++/** ++ * The simulation environment doesn't have a malloc() function, so all data ++ * structures are statically allocated ++ */ ++ ++/** Driver context struct */ ++dwc_usb3_device_t g_usb3_dev; ++ ++/** @{ */ ++/** Endpoint context structs */ ++static dwc_usb3_pcd_ep_t g_ep0; ++static dwc_usb3_pcd_ep_t g_out_ep[DWC_MAX_EPS - 1]; ++static dwc_usb3_pcd_ep_t g_in_ep[DWC_MAX_EPS - 1]; ++/** @} */ ++ ++/** EP0 PCD request */ ++static dwc_usb3_pcd_req_t g_ep0_req; ++ ++/** @{ */ ++/** PCD request pool */ ++dwc_usb3_pcd_req_t g_pcd_req[32]; ++u32 g_pcd_req_bm; ++/** @} */ ++ ++/** Driver options struct, default values are defined here */ ++dwc_usb3_core_params_t usb3ss_module_params = { ++ .burst = 1, ++ .newcore = 0, ++ .phy = 2, ++ .wakeup = 0, ++ .pwrctl = 3, ++ .lpmctl = 1, ++ .phyctl = 1, ++ .usb2mode = 0, ++ .hibernate = 0, ++ .hiberdisc = 1, ++ .clkgatingen = 0, ++ .ssdisquirk = 0, ++ .nobos = 0, ++ .loop = 0, ++ .nump = 16, ++ .newcsr = 0, ++ .rxfsz = 0, ++ .txfsz = { 0, }, ++ .txfsz_cnt = 0, ++}; ++ ++#ifndef LINUXTEST ++/*** The following data structures must be in coherent DMA capable memory. ++ *** All memory in the simulation environment is such, and physical address ++ *** == virtual address, so we can just allocate these statically. ++ ***/ ++ ++/** Event buffer */ ++static u32 g_event_buf[DWC_EVENT_BUF_SIZE]; ++ ++/** EP0 SETUP packet buffer */ ++static char g_ep0_setup_pkt[8]; ++ ++/** Data packet buffer used to return data for GET_STATUS and ++ * GET_DESCRIPTOR requests, up to 512 bytes in length ++ */ ++static u8 g_ep0_status_buf[DWC_STATUS_BUF_SIZE]; ++ ++/** DMA descriptor (TRB) for EP0 SETUP packets */ ++static UALIGNED16 dwc_usb3_dma_desc_t g_ep0_setup_desc; ++ ++/** DMA descriptor (TRB) for EP0 Data Out and Status Out phases */ ++static UALIGNED16 dwc_usb3_dma_desc_t g_ep0_out_desc; ++ ++/** DMA descriptor (TRB) for EP0 Data In and Status In phases */ ++static UALIGNED16 dwc_usb3_dma_desc_t g_ep0_in_desc; ++ ++/** @{ */ ++/** DMA descriptor (TRB) pools for non-EP0 EPs */ ++UALIGNED16 dwc_usb3_dma_desc_t g_out_trb_pool[DWC_MAX_EPS - 1][DWC_NUM_ISOC_TRBS + 1]; ++UALIGNED16 dwc_usb3_dma_desc_t g_in_trb_pool[DWC_MAX_EPS - 1][DWC_NUM_ISOC_TRBS + 1]; ++/** @} */ ++ ++/** @{ */ ++/** Scratchpad buffers for hibernation support */ ++static char g_hiber_scratchpad[15][64]; ++static struct dwc_hiber_scratchpad_array g_hiber_scratchpad_array; ++/** @} */ ++#endif ++ ++#ifdef LINUXTEST ++module_param_named(newcsr, usb3ss_module_params.newcsr, int, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(newcsr, "'gasket' has new, smaller CSR space (0=no, 1=yes)"); ++#endif ++ ++/* ++ * Hook to override the default Phy configuration in dwc_usb3_pcd_device_init() ++ * with a HAPS-specific one ++ */ ++static void haps_phy_config_hook(struct dwc_usb3_device *dev, int soft_reset, ++ int restore) ++{ ++ dwc_usb3_core_global_regs_t __iomem *global_regs = ++ dev->core_global_regs; ++ u32 temp; ++ ++ switch (dev->core_params->phy) { ++ case 3: // 16-bit UTMI+ SNPS Phy ++ temp = dwc_rd32(dev, &global_regs->gusb2phycfg[0]); ++ temp &= ~DWC_USB2PHYCFG_USB_TRD_TIM_BITS; ++ temp |= DWC_USB2PHYCFG_16B_PHY_IF_BIT; ++ temp |= 5 << DWC_USB2PHYCFG_USB_TRD_TIM_SHIFT; ++ dwc_wr32(dev, &global_regs->gusb2phycfg[0], temp); ++ break; ++ case 2: // 8-bit UTMI+ / ULPI TI or SNPS Phy ++ case 1: // old 8-bit UTMI+ SNPS Phy ++ temp = dwc_rd32(dev, &global_regs->gusb2phycfg[0]); ++ temp &= ~DWC_USB2PHYCFG_USB_TRD_TIM_BITS; ++ temp &= ~DWC_USB2PHYCFG_16B_PHY_IF_BIT; ++ temp |= 9 << DWC_USB2PHYCFG_USB_TRD_TIM_SHIFT; ++ dwc_wr32(dev, &global_regs->gusb2phycfg[0], temp); ++ break; ++ default: // RocketIO Phy ++ if (dev->core_params->usb2mode == 0) { ++ /* Set rx-eq, differential swing */ ++ dwc_wr32(dev, (volatile u32 __iomem *) ++ (dev->base + dev->gasket_ofs + 8), 0x41); ++#ifdef LECROY ++ /* Rx-detect for LeCroy */ ++ dwc_wr32(dev, (volatile u32 __iomem *) ++ (dev->base + dev->gasket_ofs + 4), 0x200); ++#else ++ dwc_wr32(dev, (volatile u32 __iomem *) ++ (dev->base + dev->gasket_ofs + 4), 0); ++#endif ++ } ++ } ++} ++ ++#ifdef LINUXTEST ++static irqreturn_t dwc_usb3_common_irq(int irq, void *dev ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ , struct pt_regs *regs ++#endif ++ ) ++{ ++ dwc_usb3_device_t *usb3_dev = dev; ++ int retval; ++ ++ dwc_acquire_spinlock(usb3_dev, &usb3_dev->pcd.lock); ++ retval = dwc_usb3_irq(usb3_dev, irq); ++ dwc_release_spinlock(usb3_dev, &usb3_dev->pcd.lock); ++ ++ return IRQ_RETVAL(retval); ++} ++#else ++/** ++ * This routine is the top level interrupt handler for the Common ++ * (Core and Device) interrupts ++ */ ++void dwc_usb3_common_irq(int irq, void *dev) ++{ ++ dwc_usb3_device_t *usb3_dev = dev; ++ ++ dwc_acquire_spinlock(usb3_dev, &usb3_dev->pcd.lock); ++ dwc_usb3_irq(usb3_dev, irq); ++ dwc_release_spinlock(usb3_dev, &usb3_dev->pcd.lock); ++} ++#endif ++ ++/** ++ * This routine de-initializes the driver ++ */ ++#ifdef LINUXTEST ++static void dwc_usb3_driver_remove(struct pci_dev *dev) ++#else ++void dwc_usb3_driver_remove(void) ++#endif ++{ ++ dwc_usb3_device_t *usb3_dev = &g_usb3_dev; ++ u32 *event_buf; ++ dwc_dma_t event_buf_dma; ++ ++ dwc_debug0(usb3_dev, "usb3ss_driver_remove()\n"); ++ ++ if (usb3_dev->cmn_irq_installed) { ++ usb3_dev->cmn_irq_installed = 0; ++#ifdef LINUXTEST ++ free_irq(dev->irq, usb3_dev); ++#endif ++ } ++ ++ if (usb3_dev->pcd_initialized) { ++ usb3_dev->pcd_initialized = 0; ++ dwc_usb3_pcd_remove(usb3_dev); ++ } ++ ++ if (usb3_dev->gadget_initialized) { ++ usb3_dev->gadget_initialized = 0; ++ dwc_usb3_function_remove(usb3_dev); ++ dwc_usb3_gadget_remove(usb3_dev); ++ } ++ ++ if (usb3_dev->cmn_initialized) { ++ usb3_dev->cmn_initialized = 0; ++ dwc_usb3_pcd_common_remove(usb3_dev); ++ } ++ ++#ifdef LINUXTEST ++ if (usb3_dev->pcd.ep0_setup_pkt) ++ dma_free_coherent(&dev->dev, ++ sizeof(*usb3_dev->pcd.ep0_setup_pkt) * 5, ++ usb3_dev->pcd.ep0_setup_pkt, ++ usb3_dev->pcd.ep0_setup_pkt_dma); ++ ++ if (usb3_dev->pcd.ep0_status_buf) ++ dma_free_coherent(&dev->dev, ++ DWC_STATUS_BUF_SIZE, ++ usb3_dev->pcd.ep0_status_buf, ++ usb3_dev->pcd.ep0_status_buf_dma); ++ ++ if (usb3_dev->pcd.ep0_in_desc) ++ dma_free_coherent(&dev->dev, ++ sizeof(dwc_usb3_dma_desc_t), ++ usb3_dev->pcd.ep0_in_desc, ++ usb3_dev->pcd.ep0_in_desc_dma); ++ ++ if (usb3_dev->pcd.ep0_out_desc) ++ dma_free_coherent(&dev->dev, ++ sizeof(dwc_usb3_dma_desc_t), ++ usb3_dev->pcd.ep0_out_desc, ++ usb3_dev->pcd.ep0_out_desc_dma); ++ ++ if (usb3_dev->pcd.ep0_setup_desc) ++ dma_free_coherent(&dev->dev, ++ sizeof(dwc_usb3_dma_desc_t), ++ usb3_dev->pcd.ep0_setup_desc, ++ usb3_dev->pcd.ep0_setup_desc_dma); ++#endif ++ ++ event_buf = usb3_dev->event_buf[0]; ++ event_buf_dma = usb3_dev->event_buf_dma[0]; ++ if (event_buf) { ++ dwc_usb3_dis_flush_eventbuf_intr(usb3_dev, 0); ++ usb3_dev->event_buf[0] = NULL; ++#ifdef LINUXTEST ++ dma_free_coherent(&dev->dev, DWC_EVENT_BUF_SIZE * sizeof(u32), ++ event_buf, event_buf_dma); ++#endif ++ } ++ ++ if (usb3_dev->sysfs_initialized) ++ usb3_dev->sysfs_initialized = 0; ++ ++#ifdef LINUXTEST ++ /* ++ * Clear the drvdata pointer. ++ */ ++ pci_set_drvdata(dev, NULL); ++ ++ /* ++ * Return the memory. ++ */ ++ if (usb3_dev->base) ++ iounmap(usb3_dev->base); ++ if (usb3_dev->rsrc_start) ++ release_mem_region(usb3_dev->rsrc_start, usb3_dev->rsrc_len); ++ ++ //pci_disable_device(dev); ++#endif ++ ++ return; ++} ++ ++/** ++ * This routine initializes the driver ++ * ++ * This routine must be called in a context that allows <strong>dwc_msleep() ++ * </strong> to be used, because that function is called while waiting for the ++ * core to come out of reset. ++ */ ++#ifdef LINUXTEST ++static int dwc_usb3_driver_probe(struct pci_dev *dev, ++ const struct pci_device_id *id) ++#else ++dwc_usb3_device_t *dwc_usb3_driver_init(u32 base_addr_dwc) ++#endif ++{ ++ dwc_usb3_device_t *usb3_dev = &g_usb3_dev; ++ u32 addr_ofs = 0xc000; ++ int retval, i; ++ ++ dwc_debug0(usb3_dev, "usb3ss_driver_init()\n"); ++ ++ memset(usb3_dev, 0, sizeof(*usb3_dev)); ++ dwc_init_spinlock(usb3_dev, &usb3_dev->pcd.lock); ++ ++#ifdef LINUXTEST ++ dev_dbg(&dev->dev, "start=0x%08x\n", (unsigned)pci_resource_start(dev, 0)); ++ dev_dbg(&dev->dev, "len=0x%08x\n", (unsigned)pci_resource_len(dev, 0)); ++ ++ if (!id) { ++ dev_err(&dev->dev, "id parameter NULL!\n"); ++ return -EINVAL; ++ } ++ ++ if (pci_enable_device(dev) < 0) { ++ dev_err(&dev->dev, "pci_enable_device() failed!\n"); ++ return -ENODEV; ++ } ++ ++ dev->current_state = PCI_D0; ++ dev->dev.power.power_state = PMSG_ON; ++ ++ if (!dev->irq) { ++ dev_err(&dev->dev, "no IRQ for PCI device!\n"); ++ retval = -ENODEV; ++ goto fail; ++ } ++ ++ usb3_dev->dev = &dev->dev; ++ pci_set_drvdata(dev, usb3_dev); ++ ++ dev_dbg(&dev->dev, "dwc_usb3_device=%lx\n", (unsigned long)usb3_dev); ++ ++ usb3_dev->rsrc_start = pci_resource_start(dev, 0); ++ usb3_dev->rsrc_len = pci_resource_len(dev, 0); ++ ++ if (!usb3_dev->rsrc_start || !usb3_dev->rsrc_len) { ++ dev_err(&dev->dev, "bad PCI resource!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ /* ++ * Map the DWC_usb3 Core memory into virtual address space. ++ */ ++ if (!request_mem_region(usb3_dev->rsrc_start, ++ usb3_dev->rsrc_len, "usb3")) { ++ dev_err(&dev->dev, "request_mem_region() failed!\n"); ++ ++ /* Flag for dwc_usb3_driver_remove() that we failed */ ++ usb3_dev->rsrc_start = 0; ++ ++ retval = -EBUSY; ++ goto fail; ++ } ++ ++ usb3_dev->base = ioremap_nocache(usb3_dev->rsrc_start, ++ usb3_dev->rsrc_len); ++ if (!usb3_dev->base) { ++ dev_err(&dev->dev, "ioremap_nocache() failed!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ if (usb3ss_module_params.newcsr) ++ usb3_dev->gasket_ofs = 0xf000; ++ else ++ usb3_dev->gasket_ofs = 0x80000; ++ ++ dev_dbg(&dev->dev, "base=%lx\n", (unsigned long)usb3_dev->base); ++ ++ usb3_dev->sysfs_initialized = 1; ++#else ++ usb3_dev->base = (volatile u8 __iomem *)(long)base_addr_dwc; ++#endif ++ ++ /* ++ * Checks that this device is really a DWC_usb3 controller. Also saves ++ * the SNPSID register value in usb3_dev->snpsid for later use by the ++ * PCD. ++ */ ++ retval = dwc_usb3_pcd_check_snpsid(usb3_dev, addr_ofs); ++ if (retval) { ++ dwc_error0(&dev->dev, "bad value for SNPSID!\n"); ++ goto fail; ++ } ++ ++ if (usb3ss_module_params.newcore && usb3_dev->snpsid < 0x5533109a) ++ usb3_dev->snpsid = 0x5533109a; ++ ++#ifdef LINUXTEST ++ retval = -ENOMEM; ++ ++ /* ++ * Up to 32 Event Buffers are supported by the hardware, but we only ++ * use 1 ++ */ ++ usb3_dev->event_buf[0] = dma_alloc_coherent(&dev->dev, ++ DWC_EVENT_BUF_SIZE * sizeof(u32), ++ &usb3_dev->event_buf_dma[0], ++ GFP_KERNEL | GFP_DMA32); ++ if (!usb3_dev->event_buf[0]) { ++ dev_err(&dev->dev, "allocation of event_buf failed!\n"); ++ goto fail; ++ } ++ ++ /* ++ * Allocate all the data structures that must be in DMA memory ++ */ ++ usb3_dev->pcd.ep0_setup_desc = dma_alloc_coherent( ++ &dev->dev, sizeof(dwc_usb3_dma_desc_t), ++ &usb3_dev->pcd.ep0_setup_desc_dma, ++ GFP_KERNEL | GFP_DMA32); ++ if (!usb3_dev->pcd.ep0_setup_desc) ++ goto fail; ++ ++ usb3_dev->pcd.ep0_out_desc = dma_alloc_coherent( ++ &dev->dev, sizeof(dwc_usb3_dma_desc_t), ++ &usb3_dev->pcd.ep0_out_desc_dma, ++ GFP_KERNEL | GFP_DMA32); ++ if (!usb3_dev->pcd.ep0_out_desc) ++ goto fail; ++ ++ usb3_dev->pcd.ep0_in_desc = dma_alloc_coherent( ++ &dev->dev, sizeof(dwc_usb3_dma_desc_t), ++ &usb3_dev->pcd.ep0_in_desc_dma, ++ GFP_KERNEL | GFP_DMA32); ++ if (!usb3_dev->pcd.ep0_in_desc) ++ goto fail; ++ ++ usb3_dev->pcd.ep0_status_buf = dma_alloc_coherent( ++ &dev->dev, DWC_STATUS_BUF_SIZE, ++ &usb3_dev->pcd.ep0_status_buf_dma, ++ GFP_KERNEL | GFP_DMA32); ++ if (!usb3_dev->pcd.ep0_status_buf) ++ goto fail; ++ ++ usb3_dev->pcd.ep0_setup_pkt = dma_alloc_coherent( ++ &dev->dev, sizeof(*usb3_dev->pcd.ep0_setup_pkt) * 5, ++ &usb3_dev->pcd.ep0_setup_pkt_dma, ++ GFP_KERNEL | GFP_DMA32); ++ if (!usb3_dev->pcd.ep0_setup_pkt) ++ goto fail; ++#else ++ /* ++ * Up to 32 Event Buffers are supported by the hardware, but we only ++ * use 1 ++ */ ++ usb3_dev->event_buf[0] = g_event_buf; ++ usb3_dev->event_buf_dma[0] = (dwc_dma_t)g_event_buf; ++ ++ /* ++ * "allocate" all the data structures that must be in DMA memory ++ */ ++ usb3_dev->pcd.ep0_setup_desc = &g_ep0_setup_desc; ++ usb3_dev->pcd.ep0_setup_desc_dma = (dwc_dma_t)&g_ep0_setup_desc; ++ usb3_dev->pcd.ep0_out_desc = &g_ep0_out_desc; ++ usb3_dev->pcd.ep0_out_desc_dma = (dwc_dma_t)&g_ep0_out_desc; ++ usb3_dev->pcd.ep0_in_desc = &g_ep0_in_desc; ++ usb3_dev->pcd.ep0_in_desc_dma = (dwc_dma_t)&g_ep0_in_desc; ++ usb3_dev->pcd.ep0_status_buf = g_ep0_status_buf; ++ usb3_dev->pcd.ep0_status_buf_dma = (dwc_dma_t)g_ep0_status_buf; ++ usb3_dev->pcd.ep0_setup_pkt = (union dwc_setup_pkt *)g_ep0_setup_pkt; ++ usb3_dev->pcd.ep0_setup_pkt_dma = (dwc_dma_t)g_ep0_setup_pkt; ++ ++ for (i = 0; i < 15; i++) { ++ g_hiber_scratchpad_array.dma_addr[i] = (u64)(long)&g_hiber_scratchpad[i]; ++ usb3_dev->pcd.hiber_scratchpad[i] = &g_hiber_scratchpad[i]; ++ } ++ ++ usb3_dev->pcd.hiber_scratchpad_array = &g_hiber_scratchpad_array; ++ usb3_dev->pcd.hiber_scratchpad_array_dma = (dwc_dma_t)&g_hiber_scratchpad_array; ++#endif ++ ++ /* ++ * Now "allocate" the remaining data structures ++ */ ++ usb3_dev->pcd.ep0_req = &g_ep0_req; ++ usb3_dev->pcd.ep0 = &g_ep0; ++ for (i = 0; i < DWC_MAX_EPS - 1; i++) { ++ usb3_dev->pcd.out_ep[i] = &g_out_ep[i]; ++ usb3_dev->pcd.in_ep[i] = &g_in_ep[i]; ++ } ++ ++ g_pcd_req_bm = 0xffffffff; ++ ++ /* ++ * Add our hook to override the default Phy register setup ++ */ ++ usb3_dev->phy_config_hook = haps_phy_config_hook; ++ ++ /* ++ * Initialize the DWC_usb3 core ++ */ ++ retval = dwc_usb3_pcd_common_init(usb3_dev, usb3_dev->base + addr_ofs, ++ &usb3ss_module_params); ++ if (retval) { ++ dwc_error0(usb3_dev, "CIL initialization failed!\n"); ++ goto fail; ++ } ++ ++ usb3_dev->cmn_initialized = 1; ++ ++ /* ++ * Initialize the Function Driver interface ++ */ ++ retval = dwc_usb3_gadget_init(usb3_dev); ++ if (retval) { ++ dwc_error0(usb3_dev, "gadget initialization failed!\n"); ++ goto fail; ++ } ++ ++ /* ++ * Initialize the Function Driver ++ */ ++ retval = dwc_usb3_function_init(usb3_dev); ++ if (retval) { ++ dwc_error0(usb3_dev, "loopback gadget initialization failed!\n"); ++ dwc_usb3_gadget_remove(usb3_dev); ++ goto fail; ++ } ++ ++ usb3_dev->gadget_initialized = 1; ++ ++ /* ++ * Initialize the PCD ++ */ ++ retval = dwc_usb3_pcd_init(usb3_dev); ++ if (retval) { ++ dwc_error0(usb3_dev, "PCD initialization failed!\n"); ++ goto fail; ++ } ++ ++ usb3_dev->pcd_initialized = 1; ++ ++#ifdef LINUXTEST ++ /* ++ * Install the interrupt handler for the common interrupts. ++ */ ++ dev_dbg(&dev->dev, "registering (common) handler for irq%d\n", ++ dev->irq); ++ retval = request_irq(dev->irq, dwc_usb3_common_irq, ++ IRQF_SHARED | IRQF_DISABLED, ++ dwc_driver_name, usb3_dev); ++ if (retval) { ++ dev_err(&dev->dev, "request of irq%d failed!\n", dev->irq); ++ retval = -EBUSY; ++ goto fail; ++ } ++#else ++# if 0 // Interrupt handler is hooked up before this routine is called ++ // Register interrupt service routine for DWC interrupt ++ interrupt_priority_set(USB3SS_DWC_INT, 5); ++ interrupt_target_set(USB3SS_DWC_INT, CPU0, 1); ++ intRegister(USB3SS_DWC_INT, usb3ss_common_irq, usb3_dev); ++ interrupt_enable(USB3SS_DWC_INT, 1); ++# endif ++#endif ++ usb3_dev->cmn_irq_installed = 1; ++ ++#ifdef LINUXTEST ++ return 0; ++#else ++ return usb3_dev; ++#endif ++ ++fail: ++#ifdef LINUXTEST ++ dwc_usb3_driver_remove(dev); ++ ++ return retval; ++#else ++ dwc_usb3_driver_remove(); ++ ++ return NULL; ++#endif ++} ++ ++#ifdef LINUXTEST ++ ++#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 ++#define PCI_DEVICE_ID_SYNOPSYS_SITKA 0xabcd ++ ++static const struct pci_device_id pci_ids[] = { ++ { ++ /* The Synopsys PCIe card */ ++ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, ++ PCI_DEVICE_ID_SYNOPSYS_SITKA), ++ .driver_data = (unsigned long)0xdeadbeef, ++ }, ++ { 0, } /* end: all zeroes */ ++}; ++MODULE_DEVICE_TABLE(pci, pci_ids); ++ ++/** ++ * This structure defines the methods to be called by a bus driver ++ * during the lifecycle of a device on that bus. Both drivers and ++ * devices are registered with a bus driver. The bus driver matches ++ * devices to drivers based on information in the device and driver ++ * structures. ++ * ++ * The probe routine is called when the bus driver matches a device ++ * to this driver. The remove routine is called when a device is ++ * unregistered with the bus driver. ++ */ ++static struct pci_driver dwc_usb3_driver = { ++ .name = (char *)dwc_driver_name, ++ .id_table = pci_ids, ++ .probe = dwc_usb3_driver_probe, ++ .remove = dwc_usb3_driver_remove, ++ .driver = { ++ .name = (char *)dwc_driver_name, ++ }, ++}; ++ ++/** ++ * This routine is called when the DWC_usb3 driver is loaded into the kernel ++ * with the insmod command. It registers the dwc_usb3_driver structure with the ++ * appropriate bus driver. This will cause the dwc_usb3_driver_probe routine ++ * to be called. In addition, the bus driver will automatically expose ++ * attributes defined for the device and driver in the special sysfs file ++ * system. ++ */ ++static int __init dwc_usb3_driver_init(void) ++{ ++ int retval = 0; ++ ++ printk(KERN_INFO "%s: %s version %s\n", dwc_driver_name, ++ DWC_DRIVER_DESC, DWC_DRIVER_VERSION); ++ retval = pci_register_driver(&dwc_usb3_driver); ++ ++ if (retval < 0) { ++ printk(KERN_ERR "%s retval=%d\n", __func__, retval); ++ return retval; ++ } ++ ++ printk(KERN_INFO "%s: module installed\n", dwc_driver_name); ++ return retval; ++} ++module_init(dwc_usb3_driver_init); ++ ++/** ++ * This routine is called when the DWC_usb3 driver is removed from the kernel ++ * with the rmmod command. The driver unregisters itself with its bus driver. ++ * ++ */ ++static void __exit dwc_usb3_driver_exit(void) ++{ ++ printk(KERN_DEBUG "%s: driver_exit()\n", dwc_driver_name); ++ ++ pci_unregister_driver(&dwc_usb3_driver); ++ ++ printk(KERN_INFO "%s: module removed\n", dwc_driver_name); ++} ++module_exit(dwc_usb3_driver_exit); ++ ++MODULE_DESCRIPTION(DWC_DRIVER_DESC); ++MODULE_AUTHOR("Synopsys Inc."); ++MODULE_LICENSE("GPL"); ++ ++#else /* !LINUXTEST */ ++ ++int main(int argc, char *argv[]) ++{ ++ return 0; ++} ++ ++#endif +diff --git a/drivers/usb/gadget/udc/hiudc3/no_os/no_os_src_sink_lpbk.c b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_src_sink_lpbk.c +new file mode 100644 +index 0000000..66f3efa +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/no_os/no_os_src_sink_lpbk.c +@@ -0,0 +1,485 @@ ++/** @file ++ * ++ * Loopback/source-sink Function Driver which uses the application-specific ++ * interface in no_os_gadget.c ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++#define DWC_BUF_SIZ (1024 * 1024) ++ ++/** Driver context struct - defined in no_os_init.c */ ++extern dwc_usb3_device_t g_usb3_dev; ++ ++#ifndef LINUXTEST ++/** @{ */ ++/** Transfer buffers - 1MB each */ ++static char g_in_buf[DWC_BUF_SIZ]; ++static char g_out_buf[DWC_BUF_SIZ]; ++/** @} */ ++#endif ++ ++/** ++ * The FPGA configuration is limited to a maximum transfer size of 128K by ++ * default, so allocate 8 128K requests for each 1MB buffer. ++ * NOTE: DWC_NUM_REQ * DWC_REQ_SIZ must be <= DWC_BUF_SIZ ! ++ */ ++/** @{ */ ++#define DWC_NUM_REQ 8 ++#define DWC_REQ_SIZ (128 * 1024) ++/** @} */ ++ ++/** ++ * Function Driver context struct ++ */ ++typedef struct dwc_usb3_loopbk { ++ usb_ep_t *in_ep, *out_ep; ++ char *in_buf, *out_buf; ++ dwc_dma_t in_dma, out_dma; ++ int speed, maxp; ++ u8 cfg, ifc, next0, src_sink; ++} dwc_usb3_loopbk_t; ++ ++/** ++ * The Function Driver context ++ */ ++static dwc_usb3_loopbk_t loopbk; ++ ++/** ++ * Function Driver transfer complete callback routine ++ */ ++static void loopbk_complete(usb_ep_t *ep, usb_request_t *req) ++{ ++ int ret; ++ ++ dwc_debug1(&g_usb3_dev, "%s()\n", __func__); ++ ++ switch (req->status) { ++ case 0: ++ if (loopbk.src_sink) { ++ if (ep == loopbk.out_ep) { ++ dwc_debug0(&g_usb3_dev, "OUT req, requeuing\n"); ++ req->length = DWC_REQ_SIZ; ++ ret = dwc_usb3_ep_queue(&g_usb3_dev, loopbk.out_ep, req); ++ if (ret == 0) ++ return; ++ dwc_error0(&g_usb3_dev, "Failed to requeue OUT req\n"); ++ } else { ++ dwc_debug0(&g_usb3_dev, "IN req, requeuing\n"); ++ req->length = DWC_REQ_SIZ; ++ ret = dwc_usb3_ep_queue(&g_usb3_dev, loopbk.in_ep, req); ++ if (ret == 0) ++ return; ++ dwc_error0(&g_usb3_dev, "Failed to requeue IN req\n"); ++ } ++ } else { ++ if (ep == loopbk.out_ep) { ++ dwc_debug0(&g_usb3_dev, "OUT req, requeuing on IN\n"); ++ ++ /* Handle 0-length marker packet */ ++ if (req->actual && (req->actual & (loopbk.maxp - 1)) == 0) ++ loopbk.next0 = 1; ++ ++ /* Echo packet back to host on IN EP */ ++ req->length = req->actual; ++ ret = dwc_usb3_ep_queue(&g_usb3_dev, loopbk.in_ep, req); ++ if (ret == 0) ++ return; ++ loopbk.next0 = 0; ++ dwc_error0(&g_usb3_dev, "Failed to queue IN req, requeuing on OUT\n"); ++ } else { ++ dwc_debug0(&g_usb3_dev, "IN req, requeuing on OUT\n"); ++ } ++ ++ /* Handle 0-length marker packet */ ++ if (loopbk.next0) { ++ loopbk.next0 = 0; ++ req->length = 0; ++ ret = dwc_usb3_ep_queue(&g_usb3_dev, loopbk.in_ep, req); ++ if (ret == 0) ++ return; ++ dwc_error0(&g_usb3_dev, "Failed to queue IN 0-length req, requeuing on OUT\n"); ++ } ++ ++ /* Requeue for a future OUT EP transfer */ ++ req->length = DWC_REQ_SIZ; ++ ret = dwc_usb3_ep_queue(&g_usb3_dev, loopbk.out_ep, req); ++ if (ret == 0) ++ return; ++ dwc_error0(&g_usb3_dev, "Failed to queue OUT req\n"); ++ } ++ ++ dwc_usb3_free_request(&g_usb3_dev, ep, req); ++ break; ++ ++ default: ++ dwc_error0(&g_usb3_dev, "Bad completion status\n"); ++ ++ /* Requeue for a future OUT EP transfer */ ++ req->length = DWC_REQ_SIZ; ++ ret = dwc_usb3_ep_queue(&g_usb3_dev, loopbk.out_ep, req); ++ if (ret == 0) ++ return; ++ dwc_error0(&g_usb3_dev, "Failed to queue OUT req\n"); ++ /* FALL-THRU */ ++ ++ case -DWC_E_SHUTDOWN: ++ dwc_debug0(&g_usb3_dev, "Shutdown status\n"); ++ dwc_usb3_free_request(&g_usb3_dev, ep, req); ++ break; ++ } ++} ++ ++/** ++ * Function Driver EP enable routine ++ */ ++static int enable_eps(dwc_usb3_device_t *usb3_dev, dwc_usb3_loopbk_t *loopbk) ++{ ++ usb_ep_t *in_ep, *out_ep; ++ ++ switch (loopbk->speed) { ++ case USB_SPEED_SUPER: ++ in_ep = dwc_usb3_ep_enable(usb3_dev, &ss_config_desc.bulk_in_ep_desc, ++ &ss_config_desc.bulk_in_ss_ep_comp_desc); ++ if (!in_ep) ++ return -DWC_E_INVALID; ++ ++ out_ep = dwc_usb3_ep_enable(usb3_dev, &ss_config_desc.bulk_out_ep_desc, ++ &ss_config_desc.bulk_out_ss_ep_comp_desc); ++ if (!out_ep) { ++ dwc_usb3_ep_disable(usb3_dev, in_ep); ++ return -DWC_E_INVALID; ++ } ++ ++ break; ++ ++ case USB_SPEED_HIGH: ++ in_ep = dwc_usb3_ep_enable(usb3_dev, &hs_config_desc.bulk_in_ep_desc, NULL); ++ if (!in_ep) ++ return -DWC_E_INVALID; ++ ++ out_ep = dwc_usb3_ep_enable(usb3_dev, &hs_config_desc.bulk_out_ep_desc, NULL); ++ if (!out_ep) { ++ dwc_usb3_ep_disable(usb3_dev, in_ep); ++ return -DWC_E_INVALID; ++ } ++ ++ break; ++ ++ case USB_SPEED_FULL: ++ in_ep = dwc_usb3_ep_enable(usb3_dev, &fs_config_desc.bulk_in_ep_desc, NULL); ++ if (!in_ep) ++ return -DWC_E_INVALID; ++ ++ out_ep = dwc_usb3_ep_enable(usb3_dev, &fs_config_desc.bulk_out_ep_desc, NULL); ++ if (!out_ep) { ++ dwc_usb3_ep_disable(usb3_dev, in_ep); ++ return -DWC_E_INVALID; ++ } ++ ++ break; ++ ++ default: ++ return -DWC_E_INVALID; ++ } ++ ++ loopbk->in_ep = in_ep; ++ loopbk->out_ep = out_ep; ++ ++ return 0; ++} ++ ++/** ++ * Function Driver EP disable routine ++ */ ++static void disable_eps(dwc_usb3_device_t *usb3_dev, dwc_usb3_loopbk_t *loopbk) ++{ ++ if (loopbk->out_ep) { ++ dwc_usb3_ep_disable(usb3_dev, loopbk->out_ep); ++ loopbk->out_ep = NULL; ++ } ++ ++ if (loopbk->in_ep) { ++ dwc_usb3_ep_disable(usb3_dev, loopbk->in_ep); ++ loopbk->in_ep = NULL; ++ } ++} ++ ++/** ++ * Function Driver SET_INTERFACE routine ++ */ ++static int set_interface(dwc_usb3_device_t *usb3_dev, dwc_usb3_loopbk_t *loopbk, int alt) ++{ ++ usb_request_t *req; ++ int i, ret = 0; ++ ++ if (alt == -1) ++ goto cleanup; ++ ++ /* Already set? */ ++ if (loopbk->out_ep) ++ return 0; ++ ++ ret = enable_eps(usb3_dev, loopbk); ++ if (ret) ++ return ret; ++ ++ ret = -DWC_E_NO_MEMORY; ++ ++ if (loopbk->src_sink) { ++ for (i = 0; i < DWC_NUM_REQ; i++) { ++ req = dwc_usb3_alloc_request(usb3_dev, loopbk->in_ep); ++ if (!req) ++ goto cleanup; ++ req->buf = loopbk->in_buf + DWC_REQ_SIZ * i; ++ req->dma = loopbk->in_dma + DWC_REQ_SIZ * i; ++ req->length = DWC_REQ_SIZ; ++ req->complete = loopbk_complete; ++ ret = dwc_usb3_ep_queue(usb3_dev, loopbk->in_ep, req); ++ if (ret) ++ goto cleanup; ++ } ++ } ++ ++ for (i = 0; i < DWC_NUM_REQ; i++) { ++ req = dwc_usb3_alloc_request(usb3_dev, loopbk->out_ep); ++ if (!req) ++ goto cleanup; ++ req->buf = loopbk->out_buf + DWC_REQ_SIZ * i; ++ req->dma = loopbk->out_dma + DWC_REQ_SIZ * i; ++ req->length = DWC_REQ_SIZ; ++ req->complete = loopbk_complete; ++ ret = dwc_usb3_ep_queue(usb3_dev, loopbk->out_ep, req); ++ if (ret) ++ goto cleanup; ++ } ++ ++ return 0; ++ ++cleanup: ++ /* disable_eps() will eventually dequeue all requests queued on each EP, ++ * and call the ->complete routine with -DWC_E_SHUTDOWN status for each ++ * one. That in turn will free the request. So all cleanup is done for ++ * us by this one call. ++ */ ++ disable_eps(usb3_dev, loopbk); ++ loopbk->next0 = 0; ++ return ret; ++} ++ ++/** ++ * This routine handles Function Driver specific Setup requests. Generic ++ * requests are handled in ep0.c and no_os_ep0.c. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ctrl Pointer to the Setup packet for the request. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_function_setup(dwc_usb3_pcd_t *pcd, usb_device_request_t *ctrl) ++{ ++ u16 windex = UGETW(ctrl->wIndex); ++ u16 wvalue = UGETW(ctrl->wValue); ++ u16 wlength = UGETW(ctrl->wLength); ++ int len = 0, ret = 0; ++ ++ switch (ctrl->bRequest) { ++ case UR_SET_FEATURE: ++ dwc_debug0(pcd->usb3_dev, "USB_REQ_SET_FEATURE\n"); ++ ++ /* We don't need to do anything for this */ ++ pcd->ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ return 0; ++ ++ case UR_SET_INTERFACE: ++ dwc_debug0(pcd->usb3_dev, "USB_REQ_SET_INTERFACE\n"); ++ if (!loopbk.cfg || windex) { ++ dwc_error2(pcd->usb3_dev, "cfg=%x wIndex=%x\n", loopbk.cfg, windex); ++ return -DWC_E_NOT_SUPPORTED; ++ } ++ ++ dwc_debug1(pcd->usb3_dev, "ifc=%x\n", wvalue); ++ ++ /* If interface has changed, disable the old EPs and enable the new ones */ ++ if (loopbk.ifc != wvalue) { ++ set_interface(pcd->usb3_dev, &loopbk, -1); ++ ret = set_interface(pcd->usb3_dev, &loopbk, wvalue); ++ if (ret) ++ return ret; ++ } ++ ++ loopbk.ifc = wvalue; ++ pcd->ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ return 0; ++ ++ case UR_GET_INTERFACE: ++ dwc_debug0(pcd->usb3_dev, "USB_REQ_GET_INTERFACE\n"); ++ if (!loopbk.cfg) { ++ dwc_error1(pcd->usb3_dev, "cfg=%x\n", loopbk.cfg); ++ return -DWC_E_NOT_SUPPORTED; ++ } ++ ++ if (windex) { ++ dwc_error1(pcd->usb3_dev, "wIndex=%x\n", windex); ++ return -DWC_E_DOMAIN; ++ } ++ ++ pcd->ep0_status_buf[0] = loopbk.ifc; ++ len = 1; ++ break; ++ ++ case UR_SET_CONFIG: ++ dwc_debug0(pcd->usb3_dev, "USB_REQ_SET_CONFIG\n"); ++ if (wvalue != 0 && wvalue != 1) { // we only have one configuration ++ dwc_error1(pcd->usb3_dev, "wValue=%x\n", wvalue); ++ return -DWC_E_NOT_SUPPORTED; ++ } ++ ++ /* If config already set, clear it and disable the EPs */ ++ if (loopbk.cfg) { ++ loopbk.cfg = 0; ++ loopbk.ifc = 0; ++ set_interface(pcd->usb3_dev, &loopbk, -1); ++ } ++ ++ /* If new config is 1, enable the EPs for interface 0 */ ++ if (wvalue) { ++ loopbk.cfg = wvalue; ++ loopbk.ifc = 0; ++ ret = set_interface(pcd->usb3_dev, &loopbk, 0); ++ if (ret) ++ loopbk.cfg = 0; ++ } ++ ++ dwc_debug1(pcd->usb3_dev, "cfg=%x\n", loopbk.cfg); ++ if (ret) ++ return ret; ++ pcd->ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ return 0; ++ ++ case UR_GET_CONFIG: ++ dwc_debug0(pcd->usb3_dev, "USB_REQ_GET_CONFIG\n"); ++ pcd->ep0_status_buf[0] = loopbk.cfg; ++ len = 1; ++ break; ++ ++ case UR_SYNCH_FRAME: ++ dwc_debug0(pcd->usb3_dev, "USB_REQ_SYNCH_FRAME\n"); ++ ++ /* We don't need to do anything for this */ ++ pcd->ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ return 0; ++ ++ default: ++ dwc_debug0(pcd->usb3_dev, "Unknown request!\n"); ++ return -DWC_E_NOT_SUPPORTED; ++ } ++ ++ /* Start the data phase for 3-stage transfers */ ++ pcd->ep0state = EP0_IN_DATA_PHASE; ++ dwc_usb3_pcd_ep0_data_stage(pcd, len < wlength ? len : wlength); ++ ++ return 0; ++} ++ ++/** ++ * Function Driver CONNECT routine ++ */ ++int dwc_usb3_function_connect(struct dwc_usb3_device *usb3_dev, int speed) ++{ ++ loopbk.speed = speed; ++ ++ switch (speed) { ++ case USB_SPEED_SUPER: ++ loopbk.maxp = 1024; ++ break; ++ ++ case USB_SPEED_HIGH: ++ loopbk.maxp = 512; ++ break; ++ ++ case USB_SPEED_FULL: ++ loopbk.maxp = 64; ++ break; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Function Driver DISCONNECT routine ++ */ ++int dwc_usb3_function_disconnect(struct dwc_usb3_device *usb3_dev) ++{ ++ return 0; ++} ++ ++/** ++ * Function Driver initialization routine ++ * ++ * @param usb3_dev Programming view of DWC_usb3 device. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_function_init(dwc_usb3_device_t *usb3_dev) ++{ ++ dwc_debug1(usb3_dev, "%s()\n", __func__); ++ ++ loopbk.src_sink = 0; /* for now */ ++ ++#ifndef LINUXTEST ++ if (loopbk.src_sink) { ++ loopbk.in_buf = g_in_buf; ++ loopbk.in_dma = (dwc_dma_t)g_in_buf; ++ } ++ ++ loopbk.out_buf = g_out_buf; ++ loopbk.out_dma = (dwc_dma_t)g_out_buf; ++ ++ return 0; ++#else ++ if (loopbk.src_sink) { ++ loopbk.in_buf = dma_alloc_coherent(usb3_dev->dev, DWC_BUF_SIZ, ++ &loopbk.in_dma, GFP_KERNEL | GFP_DMA32); ++ if (!loopbk.in_buf) ++ goto out0; ++ } ++ ++ loopbk.out_buf = dma_alloc_coherent(usb3_dev->dev, DWC_BUF_SIZ, ++ &loopbk.out_dma, GFP_KERNEL | GFP_DMA32); ++ if (!loopbk.out_buf) ++ goto out1; ++ ++ return 0; ++ ++out1: ++ if (loopbk.src_sink) ++ dma_free_coherent(usb3_dev->dev, DWC_BUF_SIZ, loopbk.in_buf, loopbk.in_dma); ++out0: ++ return -DWC_E_NO_MEMORY; ++#endif ++} ++ ++/** ++ * Function Driver removal routine ++ * ++ * @param usb3_dev Programming view of DWC_usb3 device. ++ */ ++void dwc_usb3_function_remove(dwc_usb3_device_t *usb3_dev) ++{ ++ dwc_debug1(usb3_dev, "%s()\n", __func__); ++ ++#ifdef LINUXTEST ++ dma_free_coherent(usb3_dev->dev, DWC_BUF_SIZ, loopbk.out_buf, loopbk.out_dma); ++ if (loopbk.src_sink) ++ dma_free_coherent(usb3_dev->dev, DWC_BUF_SIZ, loopbk.in_buf, loopbk.in_dma); ++#endif ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/os_defs.h b/drivers/usb/gadget/udc/hiudc3/os_defs.h +new file mode 100644 +index 0000000..b013072 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/os_defs.h +@@ -0,0 +1,162 @@ ++#ifndef _DWC_LINUX_DEFS_H_ ++#define _DWC_LINUX_DEFS_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @file ++ * ++ * This file contains OS-specific includes and definitions. ++ * ++ */ ++ ++#define DWC_DRIVER_VERSION "2.90b - November 2014" ++#define DWC_DRIVER_DESC "SS USB3 Controller driver" ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/sysfs.h> ++#include <linux/errno.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/ctype.h> ++#include <linux/string.h> ++#include <linux/dma-mapping.h> ++#include <linux/jiffies.h> ++#include <linux/delay.h> ++#include <linux/timer.h> ++#include <linux/kthread.h> ++#include <linux/workqueue.h> ++#include <linux/freezer.h> ++#include <linux/stat.h> ++#include <linux/pci.h> ++ ++#include <linux/version.h> ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++# include <linux/irq.h> ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++# include <linux/usb/ch9.h> ++#else ++# include <linux/usb_ch9.h> ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) ++# include <linux/usb/gadget.h> ++# include <linux/usb/otg.h> ++#else ++# include <linux/usb_gadget.h> ++# include <linux/usb_otg.h> ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++# include <asm/irq.h> ++#endif ++ ++# include <asm/unaligned.h> ++# include <asm/param.h> ++# include <asm/io.h> ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) ++typedef int gfp_t; ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) ++# define IRQF_SHARED SA_SHIRQ ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0) ++# define DWC_BOS_IN_GADGET ++#endif ++ ++/** @name Error Codes */ ++/** @{ */ ++#define DWC_E_INVALID EINVAL ++#define DWC_E_NO_MEMORY ENOMEM ++#define DWC_E_NO_DEVICE ENODEV ++#define DWC_E_NOT_SUPPORTED EOPNOTSUPP ++#define DWC_E_TIMEOUT ETIMEDOUT ++#define DWC_E_BUSY EBUSY ++#define DWC_E_AGAIN EAGAIN ++#define DWC_E_ABORT ECONNABORTED ++#define DWC_E_SHUTDOWN ESHUTDOWN ++#define DWC_E_NO_DATA ENODATA ++#define DWC_E_DISCONNECT ECONNRESET ++#define DWC_E_UNKNOWN EINVAL ++#define DWC_E_NO_STREAM_RES ENOSR ++#define DWC_E_COMMUNICATION ECOMM ++#define DWC_E_OVERFLOW EOVERFLOW ++#define DWC_E_PROTOCOL EPROTO ++#define DWC_E_IN_PROGRESS EINPROGRESS ++#define DWC_E_PIPE EPIPE ++#define DWC_E_IO EIO ++#define DWC_E_NO_SPACE ENOSPC ++#define DWC_E_DOMAIN EDOM ++/** @} */ ++ ++/** Compiler 'packed' attribute for structs */ ++#define UPACKED __attribute__ ((__packed__)) ++ ++/** @{ */ ++/** Type for DMA addresses */ ++typedef dma_addr_t dwc_dma_t; ++#define DWC_DMA_ADDR_INVALID (~(dwc_dma_t)0) ++/** @} */ ++ ++/** ++ * The number of DMA Descriptors (TRBs) to allocate for each endpoint type. ++ * NOTE: The driver currently supports more than 1 TRB for Isoc EPs only. ++ * So the values for Bulk and Intr must be 1. ++ */ ++#define DWC_NUM_BULK_TRBS 1 ++#define DWC_NUM_INTR_TRBS 1 ++#define DWC_NUM_ISOC_TRBS 256 ++ ++/** ++ * These parameters may be specified when loading the module. They define how ++ * the DWC_usb3 controller should be configured. The parameter values are passed ++ * to the CIL initialization routine dwc_usb3_pcd_common_init(). ++ */ ++typedef struct dwc_usb3_core_params { ++ int burst; ++ int newcore; ++ int phy; ++ int wakeup; ++ int pwrctl; ++ int lpmctl; ++ int phyctl; ++ int usb2mode; ++ int hibernate; ++ int hiberdisc; ++ int clkgatingen; ++ int ssdisquirk; ++ int nobos; ++ int loop; ++ int nump; ++ int newcsr; ++ int rxfsz; ++ int txfsz[16]; ++ int txfsz_cnt; ++ int baseline_besl; ++ int deep_besl; ++ int besl; ++ int ebc; ++} dwc_usb3_core_params_t; ++ ++extern dwc_usb3_core_params_t dwc_usb3_module_params; ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_LINUX_DEFS_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/os_dev.h b/drivers/usb/gadget/udc/hiudc3/os_dev.h +new file mode 100644 +index 0000000..1c5cd83 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/os_dev.h +@@ -0,0 +1,176 @@ ++#ifndef _DWC_LINUX_DEV_H_ ++#define _DWC_LINUX_DEV_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#if defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3516AV200) ++#ifdef readl ++#undef readl ++#undef readl_relaxed ++#undef writel ++#undef writel_relaxed ++#define readl hi_readl ++#define readl_relaxed hi_readl_relaxed ++#define writel hi_writel ++#define writel_relaxed hi_writel_relaxed ++#endif /* readl */ ++#endif /* defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3516AV200)*/ ++ ++/** @file ++ */ ++ ++/** Wrapper function for _handshake(), shows the source line for any failure */ ++#define handshake(_dev_, _ptr_, _mask_, _done_) ({ \ ++ int _retval_ = _handshake(_dev_, _ptr_, _mask_, _done_); \ ++ if (!_retval_) \ ++ dwc_error2(_dev_, "handshake failed in %s():%d\n", \ ++ __func__, __LINE__); \ ++ _retval_; \ ++}) ++ ++/** Takes a usb req pointer and returns the associated pcd req pointer */ ++#define dwc_usb3_get_pcd_req(usbreq) \ ++ container_of((usbreq), dwc_usb3_pcd_req_t, usb_req) ++ ++/** Takes a usb ep pointer and returns the associated pcd ep pointer */ ++#define dwc_usb3_get_pcd_ep(usbep) \ ++ container_of((usbep), dwc_usb3_pcd_ep_t, usb_ep) ++ ++/** @{ */ ++/** ++ * Register read/write. ++ */ ++static inline u32 dwc_rd32(struct dwc_usb3_device *dev, ++ volatile u32 __iomem *adr) ++{ ++ return readl(adr); ++} ++ ++static inline void dwc_wr32(struct dwc_usb3_device *dev, ++ volatile u32 __iomem *adr, u32 val) ++{ ++ writel(val, adr); ++} ++/** @} */ ++ ++/** @{ */ ++/** ++ * Non-sleeping delays. ++ */ ++#define dwc_udelay(dev, us) udelay(us) ++#define dwc_mdelay(dev, ms) mdelay(ms) ++/** @} */ ++ ++/** ++ * Sleeping delay. ++ */ ++#define dwc_msleep(dev, ms) msleep(ms) ++ ++/** ++ * Debugging support - vanishes in non-debug builds. ++ */ ++ ++/** Prefix string for print macros. */ ++#define USB3_DWC "dwc_usb3: " ++/* #define DEBUG */ ++#ifdef DEBUG ++# define dwc_debug(dev, x...) printk(KERN_DEBUG USB3_DWC x ) ++#else ++# define dwc_debug(dev, x...) do {} while (0) ++#endif /* DEBUG */ ++ ++# define dwc_debug0(dev, fmt) dwc_debug(dev, fmt) ++# define dwc_debug1(dev, fmt, a) dwc_debug(dev, fmt, a) ++# define dwc_debug2(dev, fmt, a, b) dwc_debug(dev, fmt, a, b) ++# define dwc_debug3(dev, fmt, a, b, c) dwc_debug(dev, fmt, a, b, c) ++# define dwc_debug4(dev, fmt, a, b, c, d) dwc_debug(dev, fmt, a, b, c, d) ++# define dwc_debug5(dev, fmt, a, b, c, d, e) \ ++ dwc_debug(dev, fmt, a, b, c, d, e) ++# define dwc_debug6(dev, fmt, a, b, c, d, e, f) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f) ++# define dwc_debug7(dev, fmt, a, b, c, d, e, f, g) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f, g) ++# define dwc_debug8(dev, fmt, a, b, c, d, e, f, g, h) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f, g, h) ++# define dwc_debug9(dev, fmt, a, b, c, d, e, f, g, h, i) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f, g, h, i) ++# define dwc_debug10(dev, fmt, a, b, c, d, e, f, g, h, i, j) \ ++ dwc_debug(dev, fmt, a, b, c, d, e, f, g, h, i, j) ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++# define dwc_isocdbg(dev, x...) printk(USB3_DWC x ) ++#else ++# define dwc_isocdbg(dev, x...) do {} while (0) ++#endif ++ ++# define dwc_isocdbg0(dev, fmt) dwc_isocdbg(dev, fmt) ++# define dwc_isocdbg1(dev, fmt, a) dwc_isocdbg(dev, fmt, a) ++# define dwc_isocdbg2(dev, fmt, a, b) dwc_isocdbg(dev, fmt, a, b) ++# define dwc_isocdbg3(dev, fmt, a, b, c) dwc_isocdbg(dev, fmt, a, b, c) ++# define dwc_isocdbg4(dev, fmt, a, b, c, d) \ ++ dwc_isocdbg(dev, fmt, a, b, c, d) ++# define dwc_isocdbg5(dev, fmt, a, b, c, d, e) \ ++ dwc_isocdbg(dev, fmt, a, b, c, d, e) ++# define dwc_isocdbg6(dev, fmt, a, b, c, d, e, f) \ ++ dwc_isocdbg(dev, fmt, a, b, c, d, e, f) ++ ++/** ++ * Print an Error message. ++ */ ++#define dwc_error(dev, x...) printk(KERN_ERR USB3_DWC x ) ++ ++#define dwc_error0(dev, fmt) dwc_error(dev, fmt) ++#define dwc_error1(dev, fmt, a) dwc_error(dev, fmt, a) ++#define dwc_error2(dev, fmt, a, b) dwc_error(dev, fmt, a, b) ++#define dwc_error3(dev, fmt, a, b, c) dwc_error(dev, fmt, a, b, c) ++#define dwc_error4(dev, fmt, a, b, c, d) dwc_error(dev, fmt, a, b, c, d) ++ ++/** ++ * Print a Warning message. ++ */ ++#define dwc_warn(dev, x...) printk(KERN_WARNING USB3_DWC x ) ++ ++#define dwc_warn0(dev, fmt) dwc_warn(dev, fmt) ++#define dwc_warn1(dev, fmt, a) dwc_warn(dev, fmt, a) ++#define dwc_warn2(dev, fmt, a, b) dwc_warn(dev, fmt, a, b) ++#define dwc_warn3(dev, fmt, a, b, c) dwc_warn(dev, fmt, a, b, c) ++#define dwc_warn4(dev, fmt, a, b, c, d) dwc_warn(dev, fmt, a, b, c, d) ++ ++/** ++ * Print an Informational message (normal but significant). ++ */ ++#define dwc_info(dev, x...) printk(KERN_INFO USB3_DWC x ) ++ ++#define dwc_info0(dev, fmt) dwc_info(dev, fmt) ++#define dwc_info1(dev, fmt, a) dwc_info(dev, fmt, a) ++#define dwc_info2(dev, fmt, a, b) dwc_info(dev, fmt, a, b) ++#define dwc_info3(dev, fmt, a, b, c) dwc_info(dev, fmt, a, b, c) ++#define dwc_info4(dev, fmt, a, b, c, d) dwc_info(dev, fmt, a, b, c, d) ++ ++/** ++ * Basic message printing. ++ */ ++#define dwc_print(dev, x...) printk(USB3_DWC x ) ++ ++#define dwc_print0(dev, fmt) dwc_print(dev, fmt) ++#define dwc_print1(dev, fmt, a) dwc_print(dev, fmt, a) ++#define dwc_print2(dev, fmt, a, b) dwc_print(dev, fmt, a, b) ++#define dwc_print3(dev, fmt, a, b, c) dwc_print(dev, fmt, a, b, c) ++#define dwc_print4(dev, fmt, a, b, c, d) dwc_print(dev, fmt, a, b, c, d) ++#define dwc_print5(dev, fmt, a, b, c, d, e) \ ++ dwc_print(dev, fmt, a, b, c, d, e) ++ ++extern int dwc_usb3_gadget_init(struct dwc_usb3_device *usb3_dev, struct device *dev); ++extern void dwc_usb3_gadget_remove(struct dwc_usb3_device *usb3_dev, struct device *dev); ++extern int dwc_usb3_create_dev_files(struct device *dev); ++extern void dwc_usb3_remove_dev_files(struct device *dev); ++extern int dwc_usb3_wakeup(struct usb_gadget *gadget); ++extern int dwc_wait_pme_thread(void *data); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_LINUX_DEV_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/pcd.c b/drivers/usb/gadget/udc/hiudc3/pcd.c +new file mode 100644 +index 0000000..6bf93aa +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/pcd.c +@@ -0,0 +1,1992 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/pcd.c $ ++ * $Revision: #110 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * This file implements the Peripheral Controller Driver. ++ * ++ * The Peripheral Controller Driver (PCD) is responsible for translating ++ * requests from the Function Driver into the appropriate actions on the ++ * DWC_usb3 controller. It isolates the Function Driver from the specifics ++ * of the controller by providing an API to the Function Driver. ++ * ++ * The Peripheral Controller Driver for Linux will implement the Gadget API, ++ * so that the existing Gadget drivers can be used. (Gadget Driver is the ++ * Linux terminology for a Function Driver.) ++ * ++ * The Linux Gadget API is defined in the header file ++ * <code><linux/usb/gadget.h></code>. The USB EP operations API is defined ++ * in the structure <code>usb_ep_ops</code> and the USB Controller API is ++ * defined in the structure <code>usb_gadget_ops</code>. ++ * ++ * An important function of the PCD is managing interrupts generated by the ++ * DWC_usb3 controller. The implementation of the DWC_usb3 device mode ++ * interrupt service routines is in pcd_intr.c. ++ */ ++/* ++ * todo Add Device Mode test modes (Test J mode, Test K mode, etc). ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++#ifdef DWC_UTE ++# include "ute_if.h" ++#endif ++ ++/** ++ * This routine allocates the TRBs for an EP. ++ * ++ * @param ep The EP for the allocation. ++ * @param num_trbs Number of TRBs to allocate. ++ * @param trb_type Type of the TRB. ++ * @param iso_intvl bInterval if this is an Isoc EP. ++ * @param link True if the TRBs should be linked in a circular chain ++ * (only supported for Isoc EPs for now). ++ * @param trbs_dma_ret The DMA address of the allocation is returned through ++ * this pointer. ++ * @return The address of the allocated memory, or NULL if the ++ * allocation fails. ++ */ ++dwc_usb3_dma_desc_t *dwc_usb3_pcd_trb_alloc(dwc_usb3_pcd_ep_t *ep, int num_trbs, ++ int trb_type, int iso_intvl, int link, dwc_dma_t *trbs_dma_ret) ++{ ++ dwc_usb3_dma_desc_t *trbs, *cur_trb; ++ dwc_dma_t trbs_dma = 0; ++ int size, i; ++#ifdef DWC_TEST_ISOC_CHAIN ++ int j; ++#endif ++ ++#ifdef DWC_TEST_ISOC_CHAIN ++ size = num_trbs * 3 * 16; ++ if (link) ++ size += 16 * 3; ++#else ++ size = num_trbs * 16; ++ if (link) ++ size += 16; ++#endif ++ ++ trbs = cur_trb = dwc_usb3_gadget_alloc_dma(ep, size, &trbs_dma); ++ if (!trbs) ++ return NULL; ++ ++ /* Now initialize the TRBs */ ++ for (i = 0; i < num_trbs; i++, cur_trb++) { ++ if (trb_type == UE_ISOCHRONOUS) { ++#ifdef DWC_ISOC_INTR_MODERATION ++ /* ++ * For small intervals, only set the IOC bit in every ++ * 8th TRB, for interrupt moderation purposes ++ */ ++ if (iso_intvl > 3 || (i & 7) == 7 || i == num_trbs - 1) ++#endif ++ dwc_usb3_fill_desc(cur_trb, 0, 0, 0, ++ DWC_DSCCTL_TRBCTL_ISOC_1ST, ++ DWC_DSCCTL_IOC_BIT | ++ DWC_DSCCTL_IMI_BIT | ++ DWC_DSCCTL_CSP_BIT, 0); ++#ifdef DWC_ISOC_INTR_MODERATION ++ else ++ dwc_usb3_fill_desc(cur_trb, 0, 0, 0, ++ DWC_DSCCTL_TRBCTL_ISOC_1ST, ++ DWC_DSCCTL_IMI_BIT | ++ DWC_DSCCTL_CSP_BIT, 0); ++#endif ++#ifdef DWC_TEST_ISOC_CHAIN ++ /* Add 2 more TRBs per entry, chain them to the 1st */ ++ dwc_usb3_start_desc_chain(cur_trb); ++ cur_trb++; ++ ++ for (j = 0; j < 2; j++, cur_trb++) ++ dwc_usb3_fill_desc(cur_trb, 0, 0, 0, ++ DWC_DSCCTL_TRBCTL_ISOC, ++ DWC_DSCCTL_IMI_BIT | ++ DWC_DSCCTL_CSP_BIT | ++ DWC_DSCCTL_CHN_BIT, 0); ++ cur_trb--; ++ dwc_usb3_end_desc_chain(cur_trb); ++#endif ++ } ++ ++ /* For types other than Isoc, the TRBs are initialized just ++ * before the transfer is started. ++ */ ++ } ++ ++ if (link) { ++ dwc_usb3_fill_desc(cur_trb, trbs_dma, 0, 0, ++ DWC_DSCCTL_TRBCTL_LINK, 0, 1); ++ } ++ ++ /* Init the pcd_ep structure */ ++ ep->dwc_ep.dma_desc = trbs; ++ ep->dwc_ep.dma_desc_dma = trbs_dma; ++ ep->dwc_ep.desc_size = size; ++ ep->dwc_ep.desc_link = link; ++ ep->dwc_ep.num_desc = num_trbs; ++ ep->dwc_ep.desc_avail = num_trbs; ++ ep->dwc_ep.desc_idx = 0; ++ ep->dwc_ep.hiber_desc_idx = 0; ++ ++ if (trbs_dma_ret) ++ *trbs_dma_ret = trbs_dma; ++ return trbs; ++} ++ ++/** ++ * This routine frees the TRBs allocated by dwc_usb3_pcd_trb_alloc(). ++ * ++ * @param ep The EP for the allocation. ++ */ ++void dwc_usb3_pcd_trb_free(dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_usb3_dma_desc_t *trbs; ++ dwc_dma_t trbs_dma; ++ int size; ++ ++ if (ep->dwc_ep.dma_desc) { ++ trbs = ep->dwc_ep.dma_desc; ++ trbs_dma = ep->dwc_ep.dma_desc_dma; ++ size = ep->dwc_ep.desc_size; ++ ep->dwc_ep.dma_desc = NULL; ++ ep->dwc_ep.dma_desc_dma = 0; ++ ++ dwc_usb3_gadget_free_dma(ep, size, trbs, trbs_dma); ++ } ++} ++ ++/** ++ * This routine assigns and fills in the TRBs for a request. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ep The EP for the transfer. ++ * @param req The request that needs the TRBs. ++ */ ++void dwc_usb3_pcd_fill_trbs(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *req) ++{ ++ dwc_usb3_dma_desc_t *desc; ++ dwc_dma_t desc_dma; ++ u32 len, tlen, pkts, ctrl; ++ int i; ++ ++ if (ep == pcd->ep0) ++ return; ++ ++ /* Get the next DMA Descriptor (TRB) for this EP */ ++ desc = ep->dwc_ep.dma_desc + ep->dwc_ep.desc_idx * req->dwc_req.numbuf; ++ desc_dma = (dwc_dma_t)((unsigned long)ep->dwc_ep.dma_desc_dma + ++ (unsigned long)ep->dwc_ep.desc_idx * req->dwc_req.numbuf * 16); ++ ++ if (++ep->dwc_ep.desc_idx >= ep->dwc_ep.num_desc) ++ ep->dwc_ep.desc_idx = 0; ++ ep->dwc_ep.desc_avail--; ++ ++ req->dwc_req.trb = desc; ++ req->dwc_req.trbdma = desc_dma; ++ ++ pkts = 0; ++ ++ if (ep->dwc_ep.is_in) { ++ /* For IN, TRB length is just xfer length */ ++ len = req->dwc_req.length; ++ ++ if (ep->dwc_ep.type == UE_ISOCHRONOUS && ++ pcd->speed == USB_SPEED_HIGH) { ++ pkts = (len + ep->dwc_ep.maxpacket - 1) ++ / ep->dwc_ep.maxpacket; ++ if (pkts) ++ pkts--; ++ } ++ } else { ++ /* For OUT, TRB length must be multiple of maxpacket */ ++ if ((ep->dwc_ep.type == UE_ISOCHRONOUS || ++ ep->dwc_ep.type == UE_INTERRUPT) && ++ ep->dwc_ep.maxpacket != 1024) ++ /* Might not be power of 2, so use (expensive?) ++ * divide/multiply ++ */ ++ len = ((req->dwc_req.length + ep->dwc_ep.maxpacket - 1) ++ / ep->dwc_ep.maxpacket) * ep->dwc_ep.maxpacket; ++ else ++ /* Must be power of 2, use cheap AND */ ++ len = (req->dwc_req.length + ep->dwc_ep.maxpacket - 1) ++ & ~(ep->dwc_ep.maxpacket - 1); ++ ++ req->dwc_req.length = len; ++ } ++ ++ /* DMA Descriptor Setup */ ++ for (i = 0; i < req->dwc_req.numbuf; i++, desc++) { ++ if (ep->dwc_ep.type == UE_ISOCHRONOUS) { ++ if (i != req->dwc_req.numbuf - 1) { ++ tlen = req->dwc_req.buflen[i]; ++ len -= tlen; ++ } else { ++ tlen = len; ++ } ++ ++ if (i == 0) ++ tlen |= pkts << DWC_DSCSTS_PCM1_SHIFT; ++ ++ dwc_usb3_fill_desc(desc, req->dwc_req.bufdma[i], tlen, ++ 0, 0, 0, i != 0); ++ } else { ++ if (i != req->dwc_req.numbuf - 1) { ++ ctrl = 0; ++ tlen = req->dwc_req.buflen[i]; ++ len -= tlen; ++ } else { ++ ctrl = DWC_DSCCTL_LST_BIT; ++ tlen = len; ++ } ++ ++ dwc_usb3_fill_desc(desc, req->dwc_req.bufdma[i], tlen, ++ req->dwc_req.stream, ++ DWC_DSCCTL_TRBCTL_NORMAL, ctrl, ++ i != 0); ++ } ++ } ++ ++ /* Must do this last! */ ++ desc = req->dwc_req.trb; ++ dwc_usb3_enable_desc(desc); ++} ++ ++/** ++ * This routine configures EP0 OUT to receive SETUP packets and configures ++ * EP0 IN for transmitting packets. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param restore True if restoring endpoint state after hibernation. ++ */ ++void dwc_usb3_ep0_activate(dwc_usb3_pcd_t *pcd, int restore) ++{ ++ u32 diepcfg0, doepcfg0, diepcfg1, doepcfg1; ++ u32 diepcfg2 = 0, doepcfg2 = 0; ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ ++ diepcfg0 = DWC_USB3_EP_TYPE_CONTROL << DWC_EPCFG0_EPTYPE_SHIFT; ++ diepcfg1 = DWC_EPCFG1_XFER_CMPL_BIT | DWC_EPCFG1_XFER_IN_PROG_BIT | ++ DWC_EPCFG1_XFER_NRDY_BIT | DWC_EPCFG1_EP_DIR_BIT; ++ ++ doepcfg0 = DWC_USB3_EP_TYPE_CONTROL << DWC_EPCFG0_EPTYPE_SHIFT; ++ doepcfg1 = DWC_EPCFG1_XFER_CMPL_BIT | DWC_EPCFG1_XFER_IN_PROG_BIT | ++ DWC_EPCFG1_XFER_NRDY_BIT; ++ ++ /* Default to MPS of 512 (will reconfigure after ConnectDone event) */ ++ diepcfg0 |= 512 << DWC_EPCFG0_MPS_SHIFT; ++ doepcfg0 |= 512 << DWC_EPCFG0_MPS_SHIFT; ++ ++#ifdef DWC_UTE ++ pcd->ep0->dwc_ep.tx_fifo_num = pcd->txf_map[1]; ++#endif ++ diepcfg0 |= pcd->ep0->dwc_ep.tx_fifo_num << DWC_EPCFG0_TXFNUM_SHIFT; ++ ++ if (restore) { ++ diepcfg0 |= DWC_CFG_ACTION_RESTORE ++ << DWC_EPCFG0_CFG_ACTION_SHIFT; ++ diepcfg2 = pcd->ep0_in_save_state; ++ dwc_debug1(pcd->usb3_dev, "IN restore state=%08x\n", diepcfg2); ++ doepcfg0 |= DWC_CFG_ACTION_RESTORE ++ << DWC_EPCFG0_CFG_ACTION_SHIFT; ++ doepcfg2 = pcd->ep0_out_save_state; ++ dwc_debug1(pcd->usb3_dev, "OUT restore state=%08x\n", doepcfg2); ++ } ++ ++ /* ++ * Issue "DEPCFG" command to EP0-OUT ++ */ ++ ++ ep_reg = &pcd->out_ep_regs[0]; ++ dwc_usb3_dis_usb2_suspend(pcd); ++ ++ /* If core is version 1.09a or later */ ++ if ((pcd->usb3_dev->snpsid & 0xffff) >= 0x109a) { ++ /* Must issue DEPSTRTNEWCFG command first */ ++ dwc_usb3_dep_startnewcfg(pcd, ep_reg, 0); ++ } ++ ++ dwc_usb3_dep_cfg(pcd, ep_reg, doepcfg0, doepcfg1, doepcfg2); ++ ++ /* ++ * Issue "DEPSTRMCFG" command to EP0-OUT ++ */ ++ ++ /* One stream */ ++ dwc_usb3_dep_xfercfg(pcd, ep_reg, 1); ++ ++ /* ++ * Issue "DEPCFG" command to EP0-IN ++ */ ++ ++ ep_reg = &pcd->in_ep_regs[0]; ++ dwc_usb3_dep_cfg(pcd, ep_reg, diepcfg0, diepcfg1, diepcfg2); ++ ++ /* ++ * Issue "DEPSTRMCFG" command to EP0-IN ++ */ ++ ++ /* One stream */ ++ dwc_usb3_dep_xfercfg(pcd, ep_reg, 1); ++ ++ dwc_usb3_ena_usb2_suspend(pcd); ++ pcd->ep0->dwc_ep.active = 1; ++} ++ ++/** ++ * This routine activates an EP. The Device EP control registers for the EP ++ * are configured as defined in the EP structure. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ep The EP to activate. ++ * @param restore True if restoring endpoint state after hibernation. ++ */ ++void dwc_usb3_ep_activate(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ int restore) ++{ ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg, *ep0_reg; ++ u32 depcfg0, depcfg1, depcfg2 = 0; ++ ++ dwc_debug3(pcd->usb3_dev, "%s() EP%d-%s\n", __func__, ep->dwc_ep.num, ++ (ep->dwc_ep.is_in ? "IN" : "OUT")); ++ ++ ep->dwc_ep.hiber_desc_idx = 0; ++ ++#ifdef DWC_STAR_9000463548_WORKAROUND ++ if (pcd->configuring) ++ goto skip; ++#endif ++ /* ++ * Get the appropriate EP registers ++ */ ++ if (ep->dwc_ep.is_in) ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ else ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ ++ dwc_usb3_dis_usb2_suspend(pcd); ++ ++ /* If this is first EP enable (ie. start of a new configuration) */ ++ if (!pcd->eps_enabled) { ++ ++#ifdef DWC_STAR_9000463548_WORKAROUND ++ dwc_usb3_dev_ep_regs_t __iomem *eptmp_reg; ++ dwc_usb3_pcd_ep_t *eptmp; ++ int i; ++ ++ /* For the workaround, we must wait for all EndXfers on all EPs ++ * to complete before continuing ++ */ ++ for (i = 0; i < pcd->num_in_eps; i++) { ++ eptmp = pcd->in_ep[i]; ++ dwc_print3(pcd->usb3_dev, "DWC IN EP%d=%lx tri-in=%d\n", ++ i, (unsigned long)eptmp, eptmp->dwc_ep.tri_in); ++ if (eptmp->dwc_ep.tri_in) { ++ eptmp_reg = eptmp->dwc_ep.in_ep_reg; ++ eptmp->dwc_ep.condition = 0; ++ dwc_usb3_dep_wait_endxfer(pcd, eptmp_reg, ++ &eptmp->dwc_ep.condition); ++ eptmp->dwc_ep.tri_in = 0; ++ } ++ } ++ ++ for (i = 0; i < pcd->num_out_eps; i++) { ++ eptmp = pcd->out_ep[i]; ++ dwc_print3(pcd->usb3_dev, ++ "DWC OUT EP%d=%lx tri-out=%d\n", ++ i, (unsigned long)eptmp, eptmp->dwc_ep.tri_out); ++ if (eptmp->dwc_ep.tri_out) { ++ eptmp_reg = eptmp->dwc_ep.out_ep_reg; ++ eptmp->dwc_ep.condition = 0; ++ dwc_usb3_dep_wait_endxfer(pcd, eptmp_reg, ++ &eptmp->dwc_ep.condition); ++ eptmp->dwc_ep.tri_out = 0; ++ } ++ } ++#endif ++ pcd->eps_enabled = 1; ++ ++ /* NOTE: When setting a new configuration, we must issue a ++ * "DEPCFG" command to physical EP1 (logical EP0-IN) first. ++ * This resets the core's Tx FIFO mapping table ++ */ ++ depcfg0 = DWC_USB3_EP_TYPE_CONTROL << DWC_EPCFG0_EPTYPE_SHIFT; ++ depcfg0 |= DWC_CFG_ACTION_MODIFY << DWC_EPCFG0_CFG_ACTION_SHIFT; ++ depcfg1 = DWC_EPCFG1_XFER_CMPL_BIT | DWC_EPCFG1_XFER_IN_PROG_BIT ++ | DWC_EPCFG1_XFER_NRDY_BIT | DWC_EPCFG1_EP_DIR_BIT; ++ ++ switch (pcd->speed) { ++ case USB_SPEED_SUPER: ++ depcfg0 |= 512 << DWC_EPCFG0_MPS_SHIFT; ++ break; ++ ++ case USB_SPEED_HIGH: ++ case USB_SPEED_FULL: ++ depcfg0 |= 64 << DWC_EPCFG0_MPS_SHIFT; ++ break; ++ ++ case USB_SPEED_LOW: ++ depcfg0 |= 8 << DWC_EPCFG0_MPS_SHIFT; ++ break; ++ } ++ ++ ep0_reg = &pcd->in_ep_regs[0]; ++ dwc_usb3_dep_cfg(pcd, ep0_reg, depcfg0, depcfg1, 0); ++ ++ /* If core is version 1.09a or later */ ++ if ((pcd->usb3_dev->snpsid & 0xffff) >= 0x109a) { ++ /* Must issue DEPSTRTNEWCFG command first */ ++ ep0_reg = &pcd->out_ep_regs[0]; ++ dwc_usb3_dep_startnewcfg(pcd, ep0_reg, 2); ++ } ++ } ++ ++ /* ++ * Issue "DEPCFG" command to EP ++ */ ++ depcfg0 = ep->dwc_ep.type << DWC_EPCFG0_EPTYPE_SHIFT; ++ depcfg0 |= ep->dwc_ep.maxpacket << DWC_EPCFG0_MPS_SHIFT; ++ ++ if (ep->dwc_ep.is_in) { ++#ifdef DWC_UTE ++ ep->dwc_ep.tx_fifo_num = pcd->txf_map[ep->dwc_ep.phys]; ++#endif ++ depcfg0 |= ep->dwc_ep.tx_fifo_num << DWC_EPCFG0_TXFNUM_SHIFT; ++ } ++ ++ if (pcd->usb3_dev->core_params->burst) { ++ dwc_debug1(pcd->usb3_dev, "Setting maxburst to %u\n", ++ ep->dwc_ep.maxburst); ++ depcfg0 |= ep->dwc_ep.maxburst << DWC_EPCFG0_BRSTSIZ_SHIFT; ++ } ++ ++ if (restore) { ++ depcfg0 |= DWC_CFG_ACTION_RESTORE ++ << DWC_EPCFG0_CFG_ACTION_SHIFT; ++ depcfg2 = ep->dwc_ep.save_state; ++ } ++ ++ depcfg1 = ep->dwc_ep.num << DWC_EPCFG1_EP_NUM_SHIFT; ++ if (ep->dwc_ep.is_in) ++ depcfg1 |= DWC_EPCFG1_EP_DIR_BIT; ++ ++ depcfg1 |= DWC_EPCFG1_XFER_CMPL_BIT; ++ depcfg1 |= DWC_EPCFG1_XFER_IN_PROG_BIT; ++ depcfg1 |= DWC_EPCFG1_XFER_NRDY_BIT; ++ dwc_isocdbg1(pcd->usb3_dev, "Setting bInterval-1 to %u\n", ++ ep->dwc_ep.intvl); ++ depcfg1 |= ep->dwc_ep.intvl << DWC_EPCFG1_BINTERVAL_SHIFT; ++ ++ if (ep->dwc_ep.num_streams) { ++ dwc_debug0(pcd->usb3_dev, "Setting stream-capable bit\n"); ++ depcfg1 |= DWC_EPCFG1_STRM_CAP_BIT; ++ } ++ ++ if (pcd->usb3_dev->core_params->ebc) { ++ if (pcd->speed == USB_SPEED_SUPER || ++ pcd->speed == USB_SPEED_HIGH) { ++ dwc_debug0(pcd->usb3_dev, "Setting EBC enable bit\n"); ++ depcfg1 |= DWC_EPCFG1_EBC_MODE_BIT; ++ } ++ } ++ ++ /* Save the DEPCFG parameters for later */ ++ if (ep->dwc_ep.is_in) { ++ ep->dwc_ep.param0in = depcfg0 & ~DWC_EPCFG0_CFG_ACTION_BITS; ++ ep->dwc_ep.param1in = depcfg1; ++ } else { ++ ep->dwc_ep.param0out = depcfg0 & ~DWC_EPCFG0_CFG_ACTION_BITS; ++ ep->dwc_ep.param1out = depcfg1; ++ } ++ ++ dwc_usb3_dep_cfg(pcd, ep_reg, depcfg0, depcfg1, depcfg2); ++ ++ /* ++ * Issue "DEPSTRMCFG" command to EP ++ */ ++ ++ /* If this EP hasn't been enabled yet in this configuration */ ++ if (!ep->dwc_ep.ena_once) { ++ ep->dwc_ep.ena_once = 1; ++ ++ /* One stream */ ++ dwc_debug0(pcd->usb3_dev, "Setting 1 stream resource\n"); ++ dwc_usb3_dep_xfercfg(pcd, ep_reg, 1); ++ } ++ ++ dwc_usb3_ena_usb2_suspend(pcd); ++ ++#ifdef DWC_STAR_9000463548_WORKAROUND ++skip: ++#endif ++ /* Enable EP in DALEPENA reg */ ++ dwc_usb3_enable_ep(pcd, ep); ++ ++ ep->dwc_ep.active = 1; ++ ep->dwc_ep.stall_clear_flag = 0; ++} ++ ++/** ++ * This routine deactivates an EP. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ep The EP to deactivate. ++ */ ++static void ep_deactivate(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ u8 tri; ++ ++ dwc_debug3(pcd->usb3_dev, "%s() EP%d-%s\n", __func__, ep->dwc_ep.num, ++ (ep->dwc_ep.is_in ? "IN" : "OUT")); ++ /* ++ * Get the appropriate EP registers ++ */ ++ if (ep->dwc_ep.is_in) { ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ tri = ep->dwc_ep.tri_in; ++ ep->dwc_ep.tri_in = 0; ++ } else { ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ tri = ep->dwc_ep.tri_out; ++ ep->dwc_ep.tri_out = 0; ++ } ++ ++ dwc_print2(pcd->usb3_dev, "end: DWC EP=%lx tri=%d\n", ++ (unsigned long)ep, tri); ++ dwc_usb3_dis_usb2_suspend(pcd); ++ ++ /* Execute clear stall command */ ++ dwc_usb3_dep_cstall(pcd, ep_reg, 0); ++ ++ if (tri) { ++#ifdef DWC_STAR_9000463548_WORKAROUND ++ /* For the workaround, we wait until the EP is re-enabled ++ * before waiting for the end transfer to complete ++ */ ++ dwc_usb3_dep_endxfer_nowait(pcd, ep_reg, tri - 1, ++ DWC_ENDXFER_FORCE); ++ if (ep->dwc_ep.is_in) ++ ep->dwc_ep.tri_in = tri; ++ else ++ ep->dwc_ep.tri_out = tri; ++#else ++ /* Execute end transfer command */ ++ ep->dwc_ep.condition = 0; ++ dwc_usb3_dep_endxfer(pcd, ep_reg, tri - 1, DWC_ENDXFER_FORCE, ++ &ep->dwc_ep.condition); ++#endif ++ } ++ ++ dwc_usb3_ena_usb2_suspend(pcd); ++ ep->dwc_ep.xfer_started = 0; ++ ++ /* Disable EP in DALEPENA reg */ ++ dwc_usb3_disable_ep(pcd, ep); ++ ++ ep->dwc_ep.active = 0; ++} ++ ++/** ++ * This routine sets up a SETUP stage transfer for EP0 and starts the transfer. ++ * ++ * @param pcd Programming view of the PCD. ++ */ ++void dwc_usb3_pcd_ep0_out_start(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ dwc_usb3_dma_desc_t *desc; ++ dwc_dma_t desc_dma; ++ u8 tri; ++ ++ /* Get the SETUP packet DMA Descriptor (TRB) */ ++ desc = dwc_usb3_ep0_setup_desc(pcd); ++ desc_dma = dwc_usb3_ep0_setup_desc_dma(pcd); ++ ++ /* DMA Descriptor setup */ ++ dwc_usb3_fill_desc(desc, dwc_usb3_ep0_setup_pkt_dma(pcd), ++ pcd->ep0->dwc_ep.maxpacket, 0, ++ DWC_DSCCTL_TRBCTL_SETUP, DWC_DSCCTL_LST_BIT, 1); ++ dwc_debug5(pcd->usb3_dev, ++ "%s() desc=0x%08lx xfercnt=%u bptr=0x%08x:%08x\n", ++ __func__, (unsigned long)desc, dwc_usb3_get_xfercnt(desc), ++ desc->bpth, desc->bptl); ++#ifdef VERBOSE ++ dwc_debug4(pcd->usb3_dev, "0x%08x 0x%08x 0x%08x 0x%08x\n", ++ *((unsigned *)desc), *((unsigned *)desc + 1), ++ *((unsigned *)desc + 2), *((unsigned *)desc + 3)); ++#endif ++ ++ ep_reg = &pcd->out_ep_regs[0]; ++ dwc_usb3_dis_usb2_suspend(pcd); ++ ++ /* Issue "DEPSTRTXFER" command to EP0-OUT */ ++ wmb(); ++ tri = dwc_usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ++ pcd->ep0->dwc_ep.tri_out = tri + 1; ++ ++ dwc_usb3_ena_usb2_suspend(pcd); ++} ++ ++/** ++ * This routine sets up a data/status stage transfer for EP0 and starts the ++ * transfer. If pcd->ep0->dwc_ep.is_in is 0 it will be an OUT transfer, ++ * otherwise it will be an IN transfer. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param req The request to start. ++ */ ++void dwc_usb3_pcd_ep0_start_transfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_req_t *req) ++{ ++ dwc_usb3_pcd_ep_t *ep0 = pcd->ep0; ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ dwc_usb3_dma_desc_t *desc; ++ dwc_dma_t desc_dma; ++ u32 desc_type, len; ++ u8 tri; ++ ++ dwc_debug7(pcd->usb3_dev, ++ "%s(): ep%d-%s req=%lx xfer_len=%d xfer_cnt=%d xfer_buf=%lx\n", ++ __func__, ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"), ++ (unsigned long)req, req->dwc_req.length, req->dwc_req.actual, ++ (unsigned long)req->dwc_req.buf[0]); ++ ++ /* Get the DMA Descriptor (TRB) for this request */ ++ if (ep0->dwc_ep.is_in) { ++ req->dwc_req.trb = dwc_usb3_ep0_in_desc(pcd); ++ req->dwc_req.trbdma = dwc_usb3_ep0_in_desc_dma(pcd); ++ } else { ++ req->dwc_req.trb = dwc_usb3_ep0_out_desc(pcd); ++ req->dwc_req.trbdma = dwc_usb3_ep0_out_desc_dma(pcd); ++ } ++ ++ desc = req->dwc_req.trb; ++ desc_dma = req->dwc_req.trbdma; ++ dwc_usb3_dis_usb2_suspend(pcd); ++ ++ if (ep0->dwc_ep.is_in) { ++ /* ++ * Start DMA on EP0-IN ++ */ ++ ep_reg = ep0->dwc_ep.in_ep_reg; ++ ++ /* DMA Descriptor (TRB) setup */ ++ len = req->dwc_req.length; ++ ++ dwc_debug1(pcd->usb3_dev, "IN EP0STATE=%d\n", pcd->ep0state); ++ ++ if (pcd->ep0state == EP0_IN_STATUS_PHASE) { ++ if (ep0->dwc_ep.three_stage) ++ desc_type = DWC_DSCCTL_TRBCTL_STATUS_3; ++ else ++ desc_type = DWC_DSCCTL_TRBCTL_STATUS_2; ++ } else { ++ desc_type = DWC_DSCCTL_TRBCTL_CTLDATA_1ST; ++ } ++ ++ dwc_usb3_fill_desc(desc, req->dwc_req.bufdma[0], ++ len, 0, desc_type, DWC_DSCCTL_LST_BIT, 1); ++ dwc_debug4(pcd->usb3_dev, ++ "IN desc=0x%08lx xferlen=%u bptr=0x%08x:%08x\n", ++ (unsigned long)desc, dwc_usb3_get_xfercnt(desc), ++ desc->bpth, desc->bptl); ++#ifdef VERBOSE ++ dwc_debug4(pcd->usb3_dev, "0x%08x 0x%08x 0x%08x 0x%08x\n", ++ *((unsigned *)desc), *((unsigned *)desc + 1), ++ *((unsigned *)desc + 2), *((unsigned *)desc + 3)); ++#endif ++ /* Issue "DEPSTRTXFER" command to EP0-IN */ ++ wmb(); ++ tri = dwc_usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ++ ep0->dwc_ep.tri_in = tri + 1; ++ } else { ++ /* ++ * Start DMA on EP0-OUT ++ */ ++ ep_reg = ep0->dwc_ep.out_ep_reg; ++ ++ /* DMA Descriptor (TRB) setup */ ++ len = (req->dwc_req.length + ep0->dwc_ep.maxpacket - 1) & ++ ~(ep0->dwc_ep.maxpacket - 1); ++ ++ dwc_debug1(pcd->usb3_dev, "OUT EP0STATE=%d\n", pcd->ep0state); ++ ++ if (pcd->ep0state == EP0_OUT_STATUS_PHASE) { ++ if (ep0->dwc_ep.three_stage) ++ desc_type = DWC_DSCCTL_TRBCTL_STATUS_3; ++ else ++ desc_type = DWC_DSCCTL_TRBCTL_STATUS_2; ++ } else { ++ desc_type = DWC_DSCCTL_TRBCTL_CTLDATA_1ST; ++ } ++ ++ dwc_usb3_fill_desc(desc, req->dwc_req.bufdma[0], ++ len, 0, desc_type, DWC_DSCCTL_LST_BIT, 1); ++ dwc_debug4(pcd->usb3_dev, ++ "OUT desc=0x%08lx xferlen=%u bptr=0x%08x:%08x\n", ++ (unsigned long)desc, dwc_usb3_get_xfercnt(desc), ++ desc->bpth, desc->bptl); ++#ifdef VERBOSE ++ dwc_debug4(pcd->usb3_dev, "0x%08x 0x%08x 0x%08x 0x%08x\n", ++ *((unsigned *)desc), *((unsigned *)desc + 1), ++ *((unsigned *)desc + 2), *((unsigned *)desc + 3)); ++#endif ++ /* Issue "DEPSTRTXFER" command to EP0-OUT */ ++ wmb(); ++ tri = dwc_usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ++ ep0->dwc_ep.tri_out = tri + 1; ++ } ++ ++ dwc_usb3_ena_usb2_suspend(pcd); ++} ++ ++/** ++ * This routine continues control IN transfers started by ep0_start_transfer, ++ * when the transfer does not fit in a single request. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param req The request to continue. ++ */ ++void dwc_usb3_pcd_ep0_continue_transfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_req_t *req) ++{ ++ dwc_usb3_pcd_ep_t *ep0 = pcd->ep0; ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ dwc_usb3_dma_desc_t *desc; ++ dwc_dma_t desc_dma; ++ u8 tri; ++ ++ /* Currently the EP0 buffer size in the gadget is at least 256 bytes, ++ * and all control transfers are smaller than that, so this routine is ++ * never called to continue a transfer. However it can be called to ++ * send a 0-length packet after the end of a transfer, so the code here ++ * only supports that case. ++ */ ++ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ ++ if (ep0->dwc_ep.is_in) { ++ desc = dwc_usb3_ep0_in_desc(pcd); ++ desc_dma = dwc_usb3_ep0_in_desc_dma(pcd); ++ ep_reg = ep0->dwc_ep.in_ep_reg; ++ ++ /* DMA Descriptor Setup */ ++ dwc_usb3_fill_desc(desc, req->dwc_req.bufdma[0], 0, 0, ++ DWC_DSCCTL_TRBCTL_NORMAL, DWC_DSCCTL_LST_BIT, ++ 1); ++ ++ /* Make sure all writes to TRB have completed */ ++ wmb(); ++ ++ dwc_usb3_dis_usb2_suspend(pcd); ++ tri = dwc_usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ++ ep0->dwc_ep.tri_in = tri + 1; ++ dwc_usb3_ena_usb2_suspend(pcd); ++ } ++} ++ ++/** ++ * This routine does the setup for a data transfer for an EP and starts ++ * the transfer. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ep The EP to start the transfer on. ++ * @param req The request to start. ++ * @param event If non-zero, this is the first transfer for an Isoc EP, so we ++ * must calculate the starting uFrame and do a startxfer instead ++ * of an updatexfer. ++ */ ++void dwc_usb3_pcd_ep_start_transfer(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *req, u32 event) ++{ ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ dwc_usb3_dma_desc_t *desc; ++ dwc_dma_t desc_dma; ++ u32 dsts; ++ u16 current_uf, intvl, mask, now, target_uf = 0; ++ u8 tri; ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ u32 dcfg; ++#endif ++ ++ dwc_debug10(pcd->usb3_dev, ++ "%s(): ep%d-%s (%d phys) %lx max_pkt=%d req=%lx" ++ " xfer_len=%d xfer_cnt=%d xfer_buf=%lx\n", ++ __func__, ep->dwc_ep.num, (ep->dwc_ep.is_in ? "IN" : "OUT"), ++ ep->dwc_ep.phys, (unsigned long)ep, ep->dwc_ep.maxpacket, ++ (unsigned long)req, req->dwc_req.length, ++ req->dwc_req.actual, (unsigned long)req->dwc_req.buf[0]); ++ ++ ep->dwc_ep.hiber_desc_idx = 0; ++ ++ /* If first transfer for Isoc */ ++ if (event) { ++ /* Get the uFrame of the host request */ ++ current_uf = event >> DWC_DEPEVT_ISOC_UFRAME_NUM_SHIFT & ++ DWC_DEPEVT_ISOC_UFRAME_NUM_BITS >> ++ DWC_DEPEVT_ISOC_UFRAME_NUM_SHIFT; ++ ++ /* Get the EP's interval */ ++ intvl = 1 << ep->dwc_ep.intvl; ++ ++ /* Get the EP's interval mask */ ++ mask = ~(intvl - 1); ++ ++ dsts = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dsts); ++ now = dsts >> DWC_DSTS_SOF_FN_SHIFT & ++ DWC_DSTS_SOF_FN_BITS >> DWC_DSTS_SOF_FN_SHIFT; ++ if (now < (current_uf & 0x3fff)) ++ now += 0x4000; ++ now += current_uf & 0xc000; ++ ++ /* Calculate a start time which is 2 or 4 intervals in the ++ * future ++ */ ++ target_uf = current_uf & mask; ++again: ++#ifdef SELA_PLATFORM ++ target_uf += intvl; ++#else ++ if (intvl <= 8) ++ target_uf += intvl << 2; ++ else ++ target_uf += intvl << 1; ++#endif ++ dwc_isocdbg3(pcd->usb3_dev, "tgt:%1x now:%1x tgt-now:%1x\n", ++ target_uf, now, target_uf - now); ++ if (target_uf - now >= 0x8000U) ++ goto again; ++ ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ dcfg = dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dcfg); ++ dwc_debug6(pcd->usb3_dev, ++ "dcfg:0x%1x dsts:0x%1x uf:0x%1x" ++ " intvl:0x%1x cur_uf:0x%1x tgt_uf:0x%1x\n", ++ dcfg, dsts, dsts >> 3 & 0x3fff, intvl, current_uf, ++ target_uf); ++ dwc_isocdbg4(pcd->usb3_dev, ++ "now:%1x bIvl:%1x ivl:%1x(u)f tgt:%1x\n", ++ now, ep->dwc_ep.intvl + 1, intvl, target_uf); ++#endif ++ /* Make sure 'target_uf' is non-zero so the code below knows ++ * that this is the first Isoc xfer. It will decrement the ++ * value by 1 before using it ++ */ ++ target_uf++; ++ } ++ ++ ep->dwc_ep.send_zlp = 0; ++ req->dwc_req.flags |= DWC_PCD_REQ_STARTED; ++ desc = req->dwc_req.trb; ++ desc_dma = req->dwc_req.trbdma; ++ dwc_usb3_dis_usb2_suspend(pcd); ++ wmb(); ++ ++ if (ep->dwc_ep.is_in) { ++ /* ++ * Start DMA on EPn-IN ++ */ ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ dwc_debug4(pcd->usb3_dev, ++ "IN desc=0x%08lx xferlen=%u bptr=0x%08x:%08x\n", ++ (unsigned long)desc, dwc_usb3_get_xfercnt(desc), ++ desc->bpth, desc->bptl); ++#ifdef VERBOSE ++ dwc_debug5(pcd->usb3_dev, "%08x %08x %08x %08x (%08x)\n", ++ *((unsigned *)desc), *((unsigned *)desc + 1), ++ *((unsigned *)desc + 2), *((unsigned *)desc + 3), ++ (unsigned)desc_dma); ++#endif ++ /* If Isoc */ ++ if (ep->dwc_ep.type == UE_ISOCHRONOUS) { ++ if (ep->dwc_ep.xfer_started) { ++ /* Issue "DEPUPDTXFER" command to EP */ ++ dwc_usb3_dep_updatexfer(pcd, ep_reg, ++ ep->dwc_ep.tri_in - 1); ++ } else if (target_uf) { ++ /* Issue "DEPSTRTXFER" command to EP */ ++ tri = dwc_usb3_dep_startxfer(pcd, ep_reg, ++ desc_dma, ++ target_uf - 1); ++ ep->dwc_ep.tri_in = tri + 1; ++ ep->dwc_ep.xfer_started = 1; ++ } else { ++ //dwc_print0(pcd->usb3_dev, ++ // "Not starting isoc IN!\n"); ++ } ++ } else { ++ if (ep->dwc_ep.xfer_started) { ++ /* Issue "DEPUPDTXFER" command to EP */ ++ dwc_usb3_dep_updatexfer(pcd, ep_reg, ++ ep->dwc_ep.tri_in - 1); ++ } else { ++ /* Issue "DEPSTRTXFER" command to EP */ ++ tri = dwc_usb3_dep_startxfer(pcd, ep_reg, ++ desc_dma, ++ req->dwc_req.stream); ++ ep->dwc_ep.tri_in = tri + 1; ++ ep->dwc_ep.xfer_started = 1; ++ } ++ } ++ } else { ++ /* ++ * Start DMA on EPn-OUT ++ */ ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ dwc_debug4(pcd->usb3_dev, ++ "OUT desc=0x%08lx xferlen=%u bptr=0x%08x:%08x\n", ++ (unsigned long)desc, dwc_usb3_get_xfercnt(desc), ++ desc->bpth, desc->bptl); ++#ifdef VERBOSE ++ dwc_debug5(pcd->usb3_dev, "%08x %08x %08x %08x (%08x)\n", ++ *((unsigned *)desc), *((unsigned *)desc + 1), ++ *((unsigned *)desc + 2), *((unsigned *)desc + 3), ++ (unsigned)desc_dma); ++#endif ++ /* If Isoc */ ++ if (ep->dwc_ep.type == UE_ISOCHRONOUS) { ++ if (ep->dwc_ep.xfer_started) { ++ /* Issue "DEPUPDTXFER" command to EP */ ++ dwc_usb3_dep_updatexfer(pcd, ep_reg, ++ ep->dwc_ep.tri_out - 1); ++ } else if (target_uf) { ++ /* Issue "DEPSTRTXFER" command to EP */ ++ tri = dwc_usb3_dep_startxfer(pcd, ep_reg, ++ desc_dma, ++ target_uf - 1); ++ ep->dwc_ep.tri_out = tri + 1; ++ ep->dwc_ep.xfer_started = 1; ++ } else { ++ //dwc_print0(pcd->usb3_dev, ++ // "Not starting isoc OUT!\n"); ++ } ++ } else { ++ if (ep->dwc_ep.xfer_started) { ++ /* Issue "DEPUPDTXFER" command to EP */ ++ dwc_usb3_dep_updatexfer(pcd, ep_reg, ++ ep->dwc_ep.tri_out - 1); ++ } else { ++ /* Issue "DEPSTRTXFER" command to EP */ ++ tri = dwc_usb3_dep_startxfer(pcd, ep_reg, ++ desc_dma, ++ req->dwc_req.stream); ++ ep->dwc_ep.tri_out = tri + 1; ++ ep->dwc_ep.xfer_started = 1; ++ } ++ } ++ } ++ ++ dwc_usb3_ena_usb2_suspend(pcd); ++} ++ ++/** ++ * For restart after hibernation, we need to restart the transfer with the ++ * address of the TRB that was last active before the hibernation. That address ++ * was saved in the <em>hiber_desc_idx</em> field of struct dwc_ep by the ++ * hibernation wakeup code. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ep The EP to restart the transfer on. ++ * @return 1 if a transfer was restarted, 0 if not. ++ */ ++int dwc_usb3_pcd_isoc_ep_hiber_restart(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ dwc_usb3_dma_desc_t *desc; ++ dwc_dma_t desc_dma; ++ int owned; ++ u8 *tri; ++ ++ /* Need to restart after hibernation? */ ++ owned = ep->dwc_ep.hiber_desc_idx - 1; ++ if (owned < 0) ++ return 0; ++ ++ if (ep->dwc_ep.is_in) { ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ tri = &ep->dwc_ep.tri_in; ++ } else { ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ tri = &ep->dwc_ep.tri_out; ++ } ++ ++ dwc_debug0(pcd->usb3_dev, "Restarting Isoc xfer\n"); ++ desc = ep->dwc_ep.dma_desc + owned; ++ desc_dma = (dwc_dma_t) ++ ((unsigned long)ep->dwc_ep.dma_desc_dma + owned * 16); ++ dwc_debug1(pcd->usb3_dev, "desc=%08lx\n", (unsigned long)desc); ++ ++#ifdef VERBOSE ++ dwc_debug5(pcd->usb3_dev, "%08x %08x %08x %08x (%08x)\n", ++ *((unsigned *)desc), *((unsigned *)desc + 1), ++ *((unsigned *)desc + 2), *((unsigned *)desc + 3), ++ (unsigned)desc_dma); ++#endif ++ ++ dwc_usb3_dis_usb2_suspend(pcd); ++ wmb(); ++ *tri = dwc_usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0) + 1; ++ dwc_usb3_ena_usb2_suspend(pcd); ++ ++ return 1; ++} ++ ++/** ++ * Stop any active xfer on a non-EP0 endpoint. ++ */ ++static void dwc_usb3_stop_xfer(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ ++ if (ep->dwc_ep.is_in) { ++ if (ep->dwc_ep.active && ep->dwc_ep.tri_in) { ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ ep->dwc_ep.condition = 0; ++ dwc_usb3_dep_endxfer(pcd, ep_reg, ep->dwc_ep.tri_in - 1, ++ DWC_ENDXFER_FORCE, ++ &ep->dwc_ep.condition); ++ ep->dwc_ep.tri_in = 0; ++ } ++ } else { ++ if (ep->dwc_ep.active && ep->dwc_ep.tri_out) { ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ ep->dwc_ep.condition = 0; ++ dwc_usb3_dep_endxfer(pcd, ep_reg, ep->dwc_ep.tri_out - 1, ++ DWC_ENDXFER_FORCE, ++ &ep->dwc_ep.condition); ++ ep->dwc_ep.tri_out = 0; ++ } ++ } ++} ++ ++/** ++ * Stop any active xfers on the non-EP0 endpoints. ++ */ ++void dwc_usb3_stop_all_xfers(dwc_usb3_pcd_t *pcd) ++{ ++ int i; ++ dwc_usb3_pcd_ep_t *ep; ++ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ dwc_usb3_dis_usb2_suspend(pcd); ++ ++ /* Stop any active xfers on the non-EP0 IN endpoints */ ++ printk("\n####%s,%d,pcd->num_in_eps=0x%x,pcd->num_out_eps=0x%x\n",__func__,__LINE__,pcd->num_in_eps,pcd->num_out_eps); ++ for (i = pcd->num_in_eps; i > 0; i--) { ++ ep = pcd->in_ep[i - 1]; ++ dwc_debug3(pcd->usb3_dev, "DWC IN EP%d=%lx tri-in=%d\n", ++ i, (unsigned long)ep, ep->dwc_ep.tri_in); ++ dwc_error(pcd->usb3_dev, "DWC IN EP%d=%lx tri-in=%d\n", ++ i, (unsigned long)ep, ep->dwc_ep.tri_in); ++ dwc_usb3_stop_xfer(pcd, ep); ++ dwc_usb3_gadget_request_nuke(pcd, ep); ++ ep->dwc_ep.xfer_started = 0; ++ } ++ ++ /* Stop any active xfers on the non-EP0 OUT endpoints */ ++ for (i = pcd->num_out_eps; i > 0; i--) { ++ ep = pcd->out_ep[i - 1]; ++ dwc_debug3(pcd->usb3_dev, "DWC OUT EP%d=%lx tri-out=%d\n", ++ i, (unsigned long)ep, ep->dwc_ep.tri_out); ++ dwc_error(pcd->usb3_dev, "DWC OUT EP%d=%lx tri-out=%d\n", ++ i, (unsigned long)ep, ep->dwc_ep.tri_out); ++ dwc_usb3_stop_xfer(pcd, ep); ++ dwc_usb3_gadget_request_nuke(pcd, ep); ++ ep->dwc_ep.xfer_started = 0; ++ } ++ ++ dwc_usb3_ena_usb2_suspend(pcd); ++} ++ ++/** ++ * This routine completes the request for the EP. If there are additional ++ * requests for the EP in the queue they will be started. ++ */ ++static int dwc_usb3_ep_complete_request(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *req, u32 event) ++{ ++ int is_last = 0, ret = 0; ++ int now_uf, evt_uf, i; ++ dwc_usb3_dma_desc_t *desc; ++ u32 byte_count; ++#if defined(DEBUG) || defined(ISOC_DEBUG) ++ dwc_usb3_device_t *dev = pcd->usb3_dev; ++ u32 bmudbg; ++ static u32 old0, old1, old2, old3, old4; ++#endif ++ ++ dwc_debug1(dev, "%s()\n", __func__); ++ ++ ep->dwc_ep.send_zlp = 0; ++ desc = req->dwc_req.trb; ++ dwc_debug2(dev, "req=%lx desc=%lx\n", (unsigned long)req, ++ (unsigned long)desc); ++ ++ if (!desc) { ++ dwc_isocdbg3(dev, "### %s, EP%d-%s request TRB is NULL! ###\n", ++ __func__, ep->dwc_ep.num, ep->dwc_ep.is_in ? ++ "IN" : "OUT"); ++ return ret; ++ } ++ ++ if (!(req->dwc_req.flags & DWC_PCD_REQ_STARTED)) { ++ dwc_isocdbg3(dev, "### %s, EP%d-%s request not started! ###\n", ++ __func__, ep->dwc_ep.num, ep->dwc_ep.is_in ? ++ "IN" : "OUT"); ++ if (ep->dwc_ep.type == UE_ISOCHRONOUS && ++ (event & DWC_DEPEVT_INTTYPE_BITS) == ++ DWC_DEPEVT_XFER_IN_PROG << DWC_DEPEVT_INTTYPE_SHIFT && ++ ep->dwc_ep.xfer_started == 0) { ++ is_last = 1; ++ goto done; ++ } ++ return ret; ++ } ++ ++ if (dwc_usb3_is_hwo(desc)) { ++ dwc_isocdbg3(dev, "### %s, EP%d-%s HWO bit set! ###\n", ++ __func__, ep->dwc_ep.num, ep->dwc_ep.is_in ? ++ "IN" : "OUT"); ++ return ret; ++ } ++ ++ if (ep->dwc_ep.type == UE_ISOCHRONOUS) { ++ now_uf = dwc_usb3_get_frame(pcd); ++ evt_uf = dwc_usb3_get_eventsofn(event); ++ dwc_isocdbg6(dev, ++ "ep:%1x evt:%08x frm:%04x now:%04x trb:%1lx len:%04x\n", ++ ep->dwc_ep.num, event, evt_uf, now_uf, ++ ((unsigned long)req->dwc_req.trbdma - ++ (unsigned long)ep->dwc_ep.dma_desc_dma) / ++ (req->dwc_req.numbuf * 16), dwc_usb3_get_xfercnt(desc)); ++ /*dwc_isocdbg4(dev, "this trb: %08x %08x %08x %08x\n", ++ *((unsigned *)desc), *((unsigned *)desc + 1), ++ *((unsigned *)desc + 2), *((unsigned *)desc + 3)); ++ dwc_isocdbg4(dev, "next trb: %08x %08x %08x %08x\n", ++ *((unsigned *)desc + 4), *((unsigned *)desc + 5), ++ *((unsigned *)desc + 6), *((unsigned *)desc + 7));*/ ++ } ++ ++ if (ep->dwc_ep.is_in) { /* IN endpoint */ ++ for (i = 0; i < req->dwc_req.numbuf; i++, desc++) { ++ req->dwc_req.actual += req->dwc_req.length; ++ if (i == req->dwc_req.numbuf - 1) ++ is_last = 1; ++ } ++ ++ if (i) ++ desc--; ++ dwc_debug3(dev, "IN len=%d cnt=%d rem=%d\n", ++ req->dwc_req.length, req->dwc_req.actual, ++ dwc_usb3_get_xfercnt(desc)); ++ ++ } else { /* OUT endpoint */ ++ for (i = 0; i < req->dwc_req.numbuf; i++, desc++) { ++ byte_count = req->dwc_req.length - ++ dwc_usb3_get_xfercnt(desc); ++ req->dwc_req.actual += byte_count; ++ } ++ ++ if (i) ++ desc--; ++ dwc_debug3(dev, "OUT len=%d cnt=%d rem=%d\n", ++ req->dwc_req.length, req->dwc_req.actual, ++ dwc_usb3_get_xfercnt(desc)); ++ is_last = 1; ++ } ++done: ++ if ((event & DWC_DEPEVT_INTTYPE_BITS) == ++ DWC_DEPEVT_XFER_CMPL << DWC_DEPEVT_INTTYPE_SHIFT) { ++ if (ep->dwc_ep.is_in) ++ ep->dwc_ep.tri_in = 0; ++ else ++ ep->dwc_ep.tri_out = 0; ++ } ++ ++ /* Complete the request */ ++ if (is_last) { ++ dwc_usb3_pcd_request_done(pcd, ep, req, 0); ++ //dwc_info2(dev, "ep_complete_request - start req %d-%s\n", ++ // ep->dwc_ep.num, ep->dwc_ep.is_in ? "IN" : "OUT"); ++ if (ep->dwc_ep.type != UE_ISOCHRONOUS || ++ ep->dwc_ep.xfer_started) ++ /* If there is a request in the queue start it. */ ++ dwc_usb3_gadget_start_next_request(pcd, ep); ++ ++#ifdef DWC_ISOC_INTR_MODERATION ++ /* Handle Isoc interrupt moderation */ ++ if (ep->dwc_ep.type == UE_ISOCHRONOUS && ++ ep->dwc_ep.xfer_started && !dwc_usb3_is_ioc(desc)) ++ /* Tell caller we want to process next TRB */ ++ ret = 1; ++#endif ++ } else { ++ dwc_print2(dev, "### EP%d-%s is_last not set! ###\n", ++ ep->dwc_ep.num, ep->dwc_ep.is_in ? "IN" : "OUT"); ++ } ++ ++ return ret; ++} ++ ++/** ++ * This routine handles non-EP0 transfers. ++ * ++ * This routine gets the request corresponding to the completed transfer ++ * and then calls the core routine for handling the completion. ++ */ ++void dwc_usb3_complete_request(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ u32 event) ++{ ++ dwc_usb3_pcd_req_t *req; ++ dwc_usb3_dma_desc_t *desc; ++ int ret; ++ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ dwc_debug1(pcd->usb3_dev, "Requests %d\n", pcd->request_pending); ++ ++ req = dwc_usb3_gadget_get_request(pcd, ep); ++ if (!req) { ++ dwc_print2(pcd->usb3_dev, "%s(%lx), ep->dwc_ep.queue empty!\n", ++ __func__, (unsigned long)ep); ++ return; ++ } ++next: ++ ret = dwc_usb3_ep_complete_request(pcd, ep, req, event); ++ dwc_debug1(pcd->usb3_dev, ++ "dwc_usb3_ep_complete_request() returned %d\n", ret); ++ if (!ret) ++ return; ++ ++ req = dwc_usb3_gadget_get_request(pcd, ep); ++ if (!req) ++ return; ++ ++ if (ret < 0) { ++ /* Isoc restart - mark all requests in queue as not started */ ++ dwc_usb3_gadget_set_ep_not_started(pcd, ep); ++ } else { ++ /* ep_complete_request() wants to process next TRB */ ++ dwc_debug1(pcd->usb3_dev, "Requests2 %d\n", ++ pcd->request_pending); ++ desc = req->dwc_req.trb; ++ if (desc && (req->dwc_req.flags & DWC_PCD_REQ_STARTED) && ++ !dwc_usb3_is_hwo(desc)) { ++ dwc_debug0(pcd->usb3_dev, "Processing next TRB\n"); ++ goto next; ++ } ++ } ++} ++ ++/** ++ * Set the EP STALL. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ep The EP to set the stall on. ++ */ ++void dwc_usb3_pcd_ep_set_stall(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ ++ dwc_info2(pcd->usb3_dev, "%s(%lx)\n", __func__, (unsigned long)ep); ++ dwc_info2(pcd->usb3_dev, "ep_num=%d is_in=%d\n", ++ ep->dwc_ep.num, ep->dwc_ep.is_in); ++ ++ if (ep->dwc_ep.is_in) ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ else ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ ++ dwc_usb3_dis_usb2_suspend(pcd); ++ dwc_usb3_dep_sstall(pcd, ep_reg); ++ dwc_usb3_ena_usb2_suspend(pcd); ++} ++ ++/** ++ * Clear the EP STALL. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ep The EP to clear the stall on. ++ */ ++void dwc_usb3_pcd_ep_clear_stall(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ dwc_debug2(pcd->usb3_dev, "ep_num=%d is_in=%d\n", ++ ep->dwc_ep.num, ep->dwc_ep.is_in); ++ ++ if (ep->dwc_ep.is_in) ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ else ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ ++ dwc_usb3_dis_usb2_suspend(pcd); ++ dwc_usb3_dep_cstall(pcd, ep_reg, 0); ++ dwc_usb3_ena_usb2_suspend(pcd); ++} ++ ++/** ++ * This routine returns a pointer to Out EP struct with number ep_num. ++ */ ++dwc_usb3_pcd_ep_t *dwc_usb3_get_out_ep(dwc_usb3_pcd_t *pcd, u32 ep_num) ++{ ++ //dwc_debug2(pcd->usb3_dev, "%s(%d)\n", __func__, ep_num); ++ ++ if (ep_num == 0) ++ return pcd->ep0; ++ else ++ return pcd->out_ep[ep_num - 1]; ++} ++ ++/** ++ * This routine returns a pointer to In EP struct with number ep_num. ++ */ ++dwc_usb3_pcd_ep_t *dwc_usb3_get_in_ep(dwc_usb3_pcd_t *pcd, u32 ep_num) ++{ ++ //dwc_debug2(pcd->usb3_dev, "%s(%d)\n", __func__, ep_num); ++ ++ if (ep_num == 0) ++ return pcd->ep0; ++ else ++ return pcd->in_ep[ep_num - 1]; ++} ++ ++/** ++ * This routine gets a pointer to an EP from the wIndex address value of the ++ * control request. ++ */ ++dwc_usb3_pcd_ep_t *dwc_usb3_pcd_get_ep_by_addr(dwc_usb3_pcd_t *pcd, u16 index) ++{ ++ u32 ep_num = UE_GET_ADDR(index); ++ ++ //dwc_debug2(pcd->usb3_dev, "%s(%d)\n", __func__, index); ++ ++ if (UE_GET_DIR(index) == UE_DIR_IN) ++ return dwc_usb3_get_in_ep(pcd, ep_num); ++ else ++ return dwc_usb3_get_out_ep(pcd, ep_num); ++} ++ ++/* USB Endpoint Operations */ ++/* ++ * The following sections briefly describe the behavior of the Gadget API ++ * endpoint operations implemented in the DWC_usb3 driver software. Detailed ++ * descriptions of the generic behavior of each of these routines can be ++ * found in the Linux header file include/linux/usb_gadget.h. ++ * ++ * The Gadget API provides wrapper routines for each of the function ++ * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper ++ * routine, which then calls the underlying PCD routine. The following ++ * sections are named according to the wrapper routines. Within each ++ * section, the corresponding DWC_usb3 PCD routine name is specified. ++ * ++ */ ++ ++/** ++ * This routine is called by the Function Driver for each EP (except EP0) to ++ * be configured for the current configuration (SET_CONFIGURATION). ++ * ++ * This routine initializes the dwc_usb3_ep_t data structure, and then ++ * calls dwc_usb3_ep_activate. ++ */ ++int dwc_usb3_pcd_ep_enable(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ const usb_endpoint_descriptor_t *ep_desc, ++ const ss_endpoint_companion_descriptor_t *ep_comp) ++{ ++ int num, dir; ++ u16 maxpacket; ++ u8 type; ++ ++ dwc_debug5(pcd->usb3_dev, "%s(%lx,%lx,%lx,%lx)\n", __func__, ++ (unsigned long)pcd, (unsigned long)ep, ++ (unsigned long)ep_desc, (unsigned long)ep_comp); ++ dwc_debug2(pcd->usb3_dev, "ep=%lx is_in=%d\n", (unsigned long)ep, ++ ep->dwc_ep.is_in); ++ ++ if (ep->dwc_ep.usb_ep_desc) { ++ dwc_print1(pcd->usb3_dev, "%s, bad ep or descriptor!\n", ++ __func__); ++ return -DWC_E_INVALID; ++ } ++ ++ ep->dwc_ep.usb_ep_desc = ep_desc; ++ ++ /* ++ * Activate the EP ++ */ ++ ep->dwc_ep.stopped = 0; ++ ++ num = UE_GET_ADDR(ep_desc->bEndpointAddress); ++ if (ep->dwc_ep.num != num) { ++ dwc_print3(pcd->usb3_dev, ++ "%s, EP num mismatch, is %d asked %d!\n", ++ __func__, ep->dwc_ep.num, num); ++ } ++ ++ dir = UE_GET_DIR(ep_desc->bEndpointAddress); ++ if (ep->dwc_ep.is_in != (dir == UE_DIR_IN)) { ++ dwc_print3(pcd->usb3_dev, ++ "%s, EP dir mismatch, is %d asked %d!\n", ++ __func__, ep->dwc_ep.is_in, dir == UE_DIR_IN); ++ } ++ ++ type = UE_GET_XFERTYPE(ep_desc->bmAttributes); ++ maxpacket = UGETW(ep_desc->wMaxPacketSize); ++ ++ ep->dwc_ep.type = type; ++ ep->dwc_ep.maxpacket = maxpacket & 0x7ff; ++ ep->dwc_ep.intvl = 0; ++ ep->dwc_ep.mult = 0; ++ ep->dwc_ep.maxburst = 0; ++ ep->dwc_ep.num_streams = 0; ++ ep->dwc_ep.xfer_started = 0; ++ ++ if (pcd->speed == USB_SPEED_SUPER && ep_comp) ++ ep->dwc_ep.maxburst = ep_comp->bMaxBurst; ++ ++ switch (type) { ++ case UE_ISOCHRONOUS: ++ if (pcd->speed == USB_SPEED_SUPER && ep_comp) { ++ ep->dwc_ep.mult = USSE_GET_MAX_PACKET_NUM( ++ ep_comp->bmAttributes) + 1; ++ /* 3 packets at most */ ++ if (ep->dwc_ep.mult > 3) ++ return -DWC_E_INVALID; ++ } ++ /* FALL THRU */ ++ case UE_INTERRUPT: ++ if (pcd->speed == USB_SPEED_SUPER) { ++ ep->dwc_ep.intvl = ep_desc->bInterval - 1; ++ break; ++ } ++ ++ ep->dwc_ep.intvl = ep_desc->bInterval - 1; ++ ++ /* ++ * Bits 12:11 specify number of _additional_ ++ * packets per microframe. ++ */ ++ ep->dwc_ep.mult = (maxpacket >> 11 & 3) + 1; ++ ++ /* 3 packets at most */ ++ if (ep->dwc_ep.mult > 3) ++ return -DWC_E_INVALID; ++ ++ break; ++ ++ case UE_BULK: ++ if (pcd->speed == USB_SPEED_SUPER && ep_comp) ++ ep->dwc_ep.num_streams = ++ USSE_GET_MAX_STREAMS(ep_comp->bmAttributes); ++ ++ /* Set initial data PID */ ++ ep->dwc_ep.data_pid_start = 0; ++ break; ++ } ++ ++ dwc_debug5(pcd->usb3_dev, ++ "type=%u maxpkt=%u mult=%u maxbst=%u numstrm=%u\n", ++ type, maxpacket, ep->dwc_ep.mult, ep->dwc_ep.maxburst, ++ ep->dwc_ep.num_streams); ++ ++ dwc_usb3_ep_activate(pcd, ep, 0); ++ return 0; ++} ++ ++/** ++ * This routine is called when an EP (except EP0) is disabled due to ++ * disconnect or change in configuration. Any pending requests will ++ * terminate with a status of -ESHUTDOWN. ++ * ++ * This routine modifies the dwc_usb3_ep_t data structure for this EP, ++ * and then calls ep_deactivate. ++ */ ++int dwc_usb3_pcd_ep_disable(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_debug2(pcd->usb3_dev, "%s(%lx)\n", __func__, (unsigned long)ep); ++ dwc_debug2(pcd->usb3_dev, "ep=%lx is_in=%d\n", (unsigned long)ep, ++ ep->dwc_ep.is_in); ++ ++ if (!ep->dwc_ep.usb_ep_desc) ++ return -DWC_E_INVALID; ++ ++ ep_deactivate(ep->dwc_ep.pcd, ep); ++ dwc_usb3_gadget_request_nuke(pcd, ep); ++ ep->dwc_ep.usb_ep_desc = NULL; ++ ++ return 0; ++} ++ ++/** ++ * This routine submits an I/O Request to an EP. ++ * ++ * - When the request completes the request's completion callback is called ++ * to return the request to the driver. ++ * - An EP, except control EPs, may have multiple requests pending. ++ * - Once submitted the request cannot be examined or modified. ++ * - Each request is turned into one or more packets. ++ * - A BULK EP can queue any amount of data; the transfer is packetized. ++ * - Zero-length packets are specified with the request 'zero' flag. ++ */ ++int dwc_usb3_pcd_ep_submit_req(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *req, int req_flags) ++{ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ ++ /* EP0 Transfer? */ ++ if (ep == pcd->ep0) { ++ switch (pcd->ep0state) { ++ case EP0_IN_DATA_PHASE: ++ dwc_debug1(pcd->usb3_dev, "%s ep0: EP0_IN_DATA_PHASE\n", ++ __func__); ++ break; ++ ++ case EP0_OUT_DATA_PHASE: ++ dwc_debug1(pcd->usb3_dev, ++ "%s ep0: EP0_OUT_DATA_PHASE\n", __func__); ++ if (pcd->request_config) { ++ /* Complete STATUS PHASE */ ++ ep->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ return 1; ++ } ++ ++ break; ++ ++ case EP0_IN_WAIT_GADGET: ++ dwc_debug1(pcd->usb3_dev, ++ "%s ep0: EP0_IN_WAIT_GADGET\n", __func__); ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ return 2; ++ ++ case EP0_OUT_WAIT_GADGET: ++ dwc_debug1(pcd->usb3_dev, ++ "%s ep0: EP0_OUT_WAIT_GADGET\n", __func__); ++ pcd->ep0state = EP0_OUT_WAIT_NRDY; ++ return 3; ++ ++ case EP0_IN_WAIT_NRDY: ++ dwc_debug1(pcd->usb3_dev, "%s ep0: EP0_IN_WAIT_NRDY\n", ++ __func__); ++ pcd->ep0state = EP0_IN_STATUS_PHASE; ++ break; ++ ++ case EP0_OUT_WAIT_NRDY: ++ dwc_debug1(pcd->usb3_dev, "%s ep0: EP0_OUT_WAIT_NRDY\n", ++ __func__); ++ pcd->ep0state = EP0_OUT_STATUS_PHASE; ++ break; ++ ++ default: ++ dwc_print2(pcd->usb3_dev, "%s ep0: odd state %d!\n", ++ __func__, pcd->ep0state); ++ return -DWC_E_SHUTDOWN; ++ } ++ ++ ep->dwc_ep.send_zlp = 0; ++ ++ if ((req_flags & DWC_PCD_REQ_ZERO) && ++ req->dwc_req.length != 0 && ++ (req->dwc_req.length & ++ (ep->dwc_ep.maxpacket - 1)) == 0) { ++ ep->dwc_ep.send_zlp = 1; ++ } ++ ++ dwc_usb3_pcd_ep0_start_transfer(pcd, req); ++ ++#ifdef DWC_STAR_9000463548_WORKAROUND ++ if (pcd->configuring) { ++ dwc_usb3_pcd_ep_t *ept; ++ int i; ++ ++ pcd->configuring = 0; ++ ++ for (i = 0; i < pcd->num_in_eps; i++) { ++ ept = pcd->in_ep[i]; ++ if (ept->dwc_ep.active) ++ dwc_usb3_ep_activate(pcd, ept, 0); ++ } ++ ++ for (i = 0; i < pcd->num_out_eps; i++) { ++ ept = pcd->out_ep[i]; ++ if (ept->dwc_ep.active) ++ dwc_usb3_ep_activate(pcd, ept, 0); ++ } ++ } ++#endif ++ } else { ++ /* Setup and start the Transfer */ ++ dwc_usb3_pcd_ep_start_transfer(pcd, ep, req, 0); ++ } ++ ++ return 0; ++} ++ ++/** ++ * This routine cancels an I/O request from an EP. ++ */ ++void dwc_usb3_pcd_ep_cancel_req(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *req, u32 stream) ++{ ++ dwc_debug4(pcd->usb3_dev, "%s(%lx,%lx) stream %d\n", __func__, ++ (unsigned long)ep, (unsigned long)req, stream); ++ ++ dwc_usb3_dis_usb2_suspend(pcd); ++ dwc_usb3_stop_xfer(pcd, ep); ++ dwc_usb3_ena_usb2_suspend(pcd); ++ ++ ep->dwc_ep.xfer_started = 0; ++ dwc_usb3_pcd_request_done(pcd, ep, req, -DWC_E_DISCONNECT); ++} ++ ++/** ++ * usb_ep_set_halt stalls an endpoint. ++ * ++ * usb_ep_clear_halt clears an endpoint stall and resets its data toggle. ++ * ++ * Both of these routines are implemented with the same underlying routine. ++ * The behavior depends on the value argument. ++ * ++ * @param pcd The PCD structure. ++ * @param ep The endpoint to set halt or clear halt. ++ * @param value - 0 means clear_halt. ++ * - 1 means set_halt. ++ * - 2 means clear stall lock flag. ++ * - 3 means set stall lock flag. ++ */ ++void dwc_usb3_pcd_ep_set_halt(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ int value) ++{ ++ dwc_debug3(pcd->usb3_dev, "%s(%lx,%d)\n", __func__, ++ (unsigned long)ep, value); ++ ++ if (value == 0) { ++ ep->dwc_ep.stall_clear_flag = 0; ++ if (ep != pcd->ep0) ++ dwc_usb3_pcd_ep_clear_stall(pcd, ep); ++ ++ if (ep->dwc_ep.stopped) { ++ ep->dwc_ep.stopped = 0; ++ ++ /* If there is a request in the EP queue start it */ ++ if (ep != pcd->ep0 && ep->dwc_ep.is_in) ++ dwc_usb3_gadget_start_next_request(pcd, ep); ++ } ++ } else if (value == 1) { ++stall: ++ if (ep == pcd->ep0) { ++ ep->dwc_ep.is_in = 0; ++ dwc_usb3_pcd_ep_set_stall(pcd, ep); ++ pcd->ep0state = EP0_STALL; ++ } else { ++ dwc_usb3_pcd_ep_set_stall(pcd, ep); ++ } ++ ++ ep->dwc_ep.stopped = 1; ++ ++ } else if (value == 2) { ++ ep->dwc_ep.stall_clear_flag = 0; ++ ++ } else if (value == 3) { ++ ep->dwc_ep.stall_clear_flag = 1; ++ goto stall; ++ } ++} ++ ++/** ++ * This routine completes a request. It calls the request callback. ++ */ ++void dwc_usb3_pcd_request_done(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *req, int status) ++{ ++ unsigned stopped = ep->dwc_ep.stopped; ++ ++ dwc_debug4(pcd->usb3_dev, "%s(%lx,%lx,%d)\n", __func__, ++ (unsigned long)ep, (unsigned long)req, status); ++ ++// printk("\n###%s,%d,=0x%x\n",__func__,__LINE__,req->dwc_req); ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ if (ep != pcd->ep0) { ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ req->dwc_req.flags &= ~DWC_PCD_REQ_STARTED; ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ if (req->dwc_req.trb) { ++ ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ dwc_usb3_disable_desc(req->dwc_req.trb); ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ ep->dwc_ep.desc_avail++; ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ req->dwc_req.trb = NULL; ++ } ++ } ++ ++ /* don't modify queue heads during completion callback */ ++ ep->dwc_ep.stopped = 1; ++ ++ dwc_usb3_gadget_complete(pcd, ep, req, status); ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ ++ //req->dwc_req.actual = 0; ++ ep->dwc_ep.stopped = stopped; ++// printk("\n###%s,%d\n",__func__,__LINE__); ++} ++ ++/** ++ * This routine is called when the Device is disconnected. It stops any ++ * active requests and informs the Function Driver of the disconnect. ++ */ ++void dwc_usb3_pcd_stop(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_usb3_pcd_ep_t *ep; ++ int i; ++ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ ++ /* don't disconnect drivers more than once */ ++ if (pcd->state == DWC_STATE_UNCONNECTED) { ++ dwc_debug1(pcd->usb3_dev, "%s() Already Disconnected\n", ++ __func__); ++ } else { ++ pcd->state = DWC_STATE_UNCONNECTED; ++ ++ /* report disconnect; the driver is already quiesced */ ++ dwc_usb3_gadget_disconnect(pcd); ++ } ++ ++ dwc_usb3_dis_usb2_suspend(pcd); ++ ++ /* kill any outstanding requests, prevent new request submissions */ ++ for (i = 0; i < pcd->num_in_eps; i++) { ++ ep = pcd->in_ep[i]; ++ dwc_usb3_stop_xfer(pcd, ep); ++ dwc_usb3_gadget_request_nuke(pcd, ep); ++ ep->dwc_ep.xfer_started = 0; ++ } ++ ++ for (i = 0; i < pcd->num_out_eps; i++) { ++ ep = pcd->out_ep[i]; ++ dwc_usb3_stop_xfer(pcd, ep); ++ dwc_usb3_gadget_request_nuke(pcd, ep); ++ ep->dwc_ep.xfer_started = 0; ++ } ++ ++ dwc_usb3_ena_usb2_suspend(pcd); ++} ++ ++/** ++ * Gets the current USB frame number. ++ */ ++int dwc_usb3_pcd_get_frame_number(dwc_usb3_pcd_t *pcd) ++{ ++ return dwc_usb3_get_frame(pcd); ++} ++ ++/** ++ * Initialize the PCD EP structures to their default state. ++ * ++ * @param pcd The PCD structure. ++ */ ++static void pcd_epinit(dwc_usb3_pcd_t *pcd) ++{ ++ int num_out_eps = pcd->num_out_eps; ++ int num_in_eps = pcd->num_in_eps; ++ int ep_cntr, i; ++ dwc_usb3_pcd_ep_t *ep; ++ ++ dwc_debug2(pcd->usb3_dev, "%s(%lx)\n", __func__, (unsigned long)pcd); ++ dwc_debug1(pcd->usb3_dev, "num_out_eps=%d\n", num_out_eps); ++ dwc_debug1(pcd->usb3_dev, "num_in_eps=%d\n", num_in_eps); ++ ++ /* ++ * Initialize the EP0 structure ++ */ ++ ep = pcd->ep0; ++ ++ /* Init EP structure */ ++ ep->dwc_ep.dma_desc = NULL; ++ ep->dwc_ep.dma_desc_dma = 0; ++ ep->dwc_ep.usb_ep_desc = NULL; ++ ep->dwc_ep.pcd = pcd; ++ ep->dwc_ep.stopped = 1; ++ ep->dwc_ep.is_in = 0; ++ ep->dwc_ep.active = 0; ++ ep->dwc_ep.phys = 0; ++ ep->dwc_ep.num = 0; ++#ifdef DWC_UTE ++ pcd->txf_map[1] = 0; ++#endif ++ ep->dwc_ep.tx_fifo_num = 0; ++ ep->dwc_ep.out_ep_reg = &pcd->out_ep_regs[0]; ++ ep->dwc_ep.in_ep_reg = &pcd->in_ep_regs[0]; ++ ++ ep->dwc_ep.type = DWC_USB3_EP_TYPE_CONTROL; ++ ep->dwc_ep.maxpacket = DWC_MAX_EP0_SIZE; ++ ep->dwc_ep.send_zlp = 0; ++ ep->dwc_ep.queue_sof = 0; ++ ++ pcd->ep0_req->dwc_req.buf[0] = NULL; ++ pcd->ep0_req->dwc_req.bufdma[0] = 0; ++ pcd->ep0_req->dwc_req.buflen[0] = 0; ++ pcd->ep0_req->dwc_req.length = 0; ++ pcd->ep0_req->dwc_req.actual = 0; ++ ++ /* ++ * Initialize the EP1-n structures ++ */ ++ ep_cntr = 0; ++ ++ for (i = 1; ep_cntr < num_out_eps; i++) { ++ dwc_debug2(pcd->usb3_dev, ++ "initializing EP%d-OUT (out_ep[%d])\n", i, ep_cntr); ++ ep = pcd->out_ep[ep_cntr]; ++ ep_cntr++; ++ ++ /* Init EP structure - but don't overwrite '.num', the gadget ++ * has already set that ++ */ ++ ep->dwc_ep.dma_desc = NULL; ++ ep->dwc_ep.dma_desc_dma = 0; ++ ep->dwc_ep.usb_ep_desc = NULL; ++ ep->dwc_ep.pcd = pcd; ++ ep->dwc_ep.stopped = 1; ++ ep->dwc_ep.is_in = 0; ++ ep->dwc_ep.active = 0; ++ ep->dwc_ep.phys = ep_cntr << 1; ++ ep->dwc_ep.tx_fifo_num = 0; ++ ep->dwc_ep.out_ep_reg = &pcd->out_ep_regs[ep_cntr]; ++ ++ /* Control until EP is activated */ ++ ep->dwc_ep.type = DWC_USB3_EP_TYPE_CONTROL; ++ ep->dwc_ep.maxpacket = DWC_MAX_EP0_SIZE; ++ ep->dwc_ep.send_zlp = 0; ++ ep->dwc_ep.queue_sof = 0; ++ } ++ ++ ep_cntr = 0; ++ ++ for (i = 1; ep_cntr < num_in_eps; i++) { ++ dwc_debug2(pcd->usb3_dev, ++ "initializing EP%d-IN (in_ep[%d])\n", i, ep_cntr); ++ ep = pcd->in_ep[ep_cntr]; ++ ep_cntr++; ++ ++ /* Init EP structure - but don't overwrite '.num', the gadget ++ * has already set that ++ */ ++ ep->dwc_ep.dma_desc = NULL; ++ ep->dwc_ep.dma_desc_dma = 0; ++ ep->dwc_ep.usb_ep_desc = NULL; ++ ep->dwc_ep.pcd = pcd; ++ ep->dwc_ep.stopped = 1; ++ ep->dwc_ep.is_in = 1; ++ ep->dwc_ep.active = 0; ++ ep->dwc_ep.phys = ep_cntr << 1 | 1; ++#ifdef DWC_UTE ++ pcd->txf_map[ep_cntr << 1 | 1] = ep_cntr; ++#endif ++ ep->dwc_ep.tx_fifo_num = ep_cntr; ++ ep->dwc_ep.in_ep_reg = &pcd->in_ep_regs[ep_cntr]; ++ ++ /* Control until EP is activated */ ++ ep->dwc_ep.type = DWC_USB3_EP_TYPE_CONTROL; ++ ep->dwc_ep.maxpacket = DWC_MAX_EP0_SIZE; ++ ep->dwc_ep.send_zlp = 0; ++ ep->dwc_ep.queue_sof = 0; ++ } ++ ++ pcd->ep0state = EP0_IDLE; ++ pcd->ep0->dwc_ep.maxpacket = DWC_MAX_EP0_SIZE; ++ pcd->ep0->dwc_ep.type = DWC_USB3_EP_TYPE_CONTROL; ++} ++ ++/** ++ * Initialize the PCD portion of the driver. ++ * ++ * This routine should be called after dwc_usb3_pcd_common_init() and any ++ * platform-specific initialization routines have been called. ++ * ++ * This routine must be called in a context that allows <em>dwc_msleep()</em> ++ * to be used, because that function is called while waiting for the core to ++ * come out of reset. ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ */ ++int dwc_usb3_pcd_init(dwc_usb3_device_t *dev) ++{ ++ dwc_debug1(dev, "%s()\n", __func__); ++ ++ dev->pcd.usb3_dev = dev; ++ dev->pcd.speed = USB_SPEED_UNKNOWN; ++ ++ /* ++ * Initialize EP structures ++ */ ++ pcd_epinit(&dev->pcd); ++ ++ /* ++ * Initialize the Core (also enables interrupts and sets Run/Stop bit) ++ */ ++ dwc_usb3_pcd_device_init(dev, 1, 0); ++ ++ return 0; ++} ++ ++/** ++ * Deinitialize the PCD portion of the driver. ++ * ++ * This routine should be called before any platform-specific deinitialization ++ * routines and dwc_usb3_pcd_common_remove() are called. ++ * ++ * @param dev Programming view of DWC_usb3 controller. ++ */ ++void dwc_usb3_pcd_remove(dwc_usb3_device_t *dev) ++{ ++ dwc_debug1(dev, "%s()\n", __func__); ++ ++ dwc_usb3_pcd_device_remove(dev); ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/pcd.h b/drivers/usb/gadget/udc/hiudc3/pcd.h +new file mode 100644 +index 0000000..a71bc86 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/pcd.h +@@ -0,0 +1,658 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/pcd.h $ ++ * $Revision: #71 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef _DWC_PCD_H_ ++#define _DWC_PCD_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @file ++ * ++ * This file contains the structures, constants, and interfaces for ++ * the Perpherial Contoller Driver (PCD). ++ * ++ * The Peripheral Controller Driver (PCD) for Linux will implement the ++ * Gadget API, so that the existing Gadget drivers can be used. For ++ * the Mass Storage function, the File-backed USB Storage Gadget (FBS) ++ * driver will be used. The FBS driver supports the Control-Bulk (CB), ++ * Control-Bulk-Interrupt (CBI), and Bulk-Only transports. ++ * ++ */ ++ ++/** Maximum number of Tx FIFOs. Depends on the RTL configuration. No way to ++ * probe the value at runtime ++ */ ++#define DWC_MAX_TX_FIFOS 16 ++ ++/** Maximum number of physical EPs. Depends on the RTL configuration. No way to ++ * probe the value at runtime ++ */ ++#define DWC_MAX_PHYS_EP 32 ++ ++/** Maximum number of data buffers per TRB. OS/application specific */ ++#define DWC_MAX_DATA_BUFS 13 ++ ++/** Maximum number of EPs, defined by USB spec */ ++#define DWC_MAX_EPS 16 ++ ++/** Maxpacket size for EP0, defined by USB3 spec */ ++#define DWC_MAX_EP0_SIZE 512 ++ ++/** Maxpacket size for any EP, defined by USB3 spec */ ++#define DWC_MAX_PACKET_SIZE 1024 ++ ++/** ++ * States of EP0 ++ */ ++typedef enum ep0_state { ++ EP0_IDLE, ++ EP0_IN_DATA_PHASE, ++ EP0_OUT_DATA_PHASE, ++ EP0_IN_WAIT_GADGET, ++ EP0_OUT_WAIT_GADGET, ++ EP0_IN_WAIT_NRDY, ++ EP0_OUT_WAIT_NRDY, ++ EP0_IN_STATUS_PHASE, ++ EP0_OUT_STATUS_PHASE, ++ EP0_STALL, ++} ep0state_e; ++ ++typedef enum pcd_state { ++ DWC_STATE_UNCONNECTED, /* no host */ ++ DWC_STATE_DEFAULT, ++ DWC_STATE_ADDRESSED, ++ DWC_STATE_CONFIGURED, ++} pcdstate_e; ++ ++struct dwc_usb3_device; ++struct dwc_usb3_pcd; ++ ++/** ++ * The <code>dwc_req</code> structure represents the state of a single ++ * transfer request when acting in device mode. It contains the data items ++ * needed for a request to be started and completed. ++ */ ++typedef struct dwc_req { ++ dwc_usb3_dma_desc_t *trb; /**< TRB or TRB chain for this req */ ++ dwc_dma_t trbdma; /**< DMA address of TRB or TRB chain */ ++ u32 length; /**< total length of data bufs */ ++ u32 actual; /**< actual amt of data transferred */ ++ u32 stream; /**< stream # of this request */ ++ ++ int flags; /**< request flags - bits 8-31 are OS-specific */ ++#define DWC_PCD_REQ_ZERO 0x001 ++#define DWC_PCD_REQ_STARTED 0x002 ++#define DWC_PCD_REQ_MAP_DMA 0x100 ++#define DWC_PCD_REQ_IN 0x200 ++ ++ int numbuf; /**< number of data bufs */ ++ char *buf[DWC_MAX_DATA_BUFS]; /**< data buffers */ ++ dwc_dma_t bufdma[DWC_MAX_DATA_BUFS]; /**< DMA addrs of data bufs */ ++ u32 buflen[DWC_MAX_DATA_BUFS]; /**< length of data bufs */ ++} dwc_req_t; ++ ++/** DWC_usb3 request structure. ++ * This structure is used to form a list of requests. ++ */ ++typedef struct dwc_usb3_pcd_req { ++ /** DWC_usb3 request data */ ++ dwc_req_t dwc_req; ++ ++ /* ==== The rest is OS-specific ==== */ ++ ++ /** List entry for EP queue */ ++#if defined(__linux__) || defined(SELA_PLATFORM) ++ struct list_head entry; ++#else ++ DWC_CIRCLEQ_ENTRY(dwc_usb3_pcd_req) entry; ++#endif ++ /** USB request */ ++ struct usb_request usb_req; ++} dwc_usb3_pcd_req_t; ++ ++/** ++ * The <code>dwc_ep</code> structure represents the state of a single EP ++ * when acting in device mode. It contains the data items needed for an EP ++ * to be activated and transfer packets. ++ */ ++typedef struct dwc_ep { ++ /** Pointer to PCD */ ++ struct dwc_usb3_pcd *pcd; ++ ++ /** Pointer to OUT EP register */ ++ dwc_usb3_dev_ep_regs_t __iomem *out_ep_reg; ++ ++ /** Pointer to IN EP register */ ++ dwc_usb3_dev_ep_regs_t __iomem *in_ep_reg; ++ ++ /** Physical EP number */ ++ u8 phys; ++ ++ /** USB EP number */ ++ u8 num; ++ ++ /** EP type: 0 - Control, 1 - ISOC, 2 - BULK, 3 - INTR */ ++ u8 type; ++ ++ /** 'bInterval' value for Isoc EPs */ ++ u8 intvl; ++ ++ /** Max Packet bytes */ ++ u16 maxpacket; ++ ++ /** 'mult' value for SS Isoc EPs */ ++ u8 mult; ++ ++ /** Max burst size for SS EPs (0 - 15, actual burst is 1 - 16) */ ++ u8 maxburst; ++ ++ /** Number of streams for SS Bulk EPs (0 - 16, actual number is 2^n) */ ++ u8 num_streams; ++ ++ /** Tx FIFO # for IN EPs */ ++ u8 tx_fifo_num; ++ ++ /** @{ */ ++ /** Transfer Resource Index from the Start Transfer command */ ++ u8 tri_out; ++ u8 tri_in; ++ /** @} */ ++ ++ /** @{ */ ++ /** Status of the queue */ ++ unsigned int stopped : 1; ++ unsigned int disabling : 1; ++ unsigned int queue_sof : 1; ++ /** @} */ ++ ++ /** @{ */ ++ /** Send ZLP */ ++ unsigned int send_zlp : 1; ++ ++ /** Stall clear flag */ ++ unsigned int stall_clear_flag : 1; ++ ++ /** True if 3-stage control transfer */ ++ unsigned int three_stage : 1; ++ ++ /** True if transfer has been started on EP */ ++ unsigned int xfer_started : 1; ++ /** @} */ ++ ++ /** EP direction 0 = OUT */ ++ unsigned int is_in : 1; ++ ++ /** EP active */ ++ unsigned int active : 1; ++ ++ /** True if TRB array has a link TRB at the end */ ++ unsigned int desc_link : 1; ++ ++ /** DATA start PID for INTR and BULK EP */ ++ unsigned int data_pid_start : 1; ++ ++ /** EP has been enabled for this configuration */ ++ unsigned int ena_once : 1; ++ ++ /** EP was in stalled state when entering hibernation */ ++ unsigned int stalled_save : 1; ++ ++ /** @{ */ ++ /** Saved parameters from the last DEPCFG for this EP. Used when ++ * resetting the sequence number ++ */ ++ u32 param0out; ++ u32 param1out; ++ u32 param0in; ++ u32 param1in; ++ /** @} */ ++ ++ /** EP state, saved across core hibernation */ ++ u32 save_state; ++ ++ /** Pointer to USB EP descriptor */ ++ const usb_endpoint_descriptor_t *usb_ep_desc; ++ ++ /** @{ */ ++ /** Array of DMA descriptors (TRBs) for this EP */ ++ dwc_usb3_dma_desc_t *dma_desc; ++ dwc_dma_t dma_desc_dma; ++ int desc_size; ++ int num_desc; ++ /** @} */ ++ ++ /** Number of DMA descriptors available */ ++ int desc_avail; ++ ++ /** Index to next free DMA descriptor in array */ ++ int desc_idx; ++ ++ /** Index to DMA descriptor that was active before hibernation */ ++ int hiber_desc_idx; ++ ++ /* ==== The rest is OS-specific ==== */ ++ ++ /** Condition variable for EPCMD_CMPL interrupt */ ++ u32 condition; ++ ++ /** Queue of dwc_usb3_pcd_reqs */ ++#if defined(__linux__) || defined(SELA_PLATFORM) ++ struct list_head queue; ++#else ++ DWC_CIRCLEQ_HEAD(circleq, dwc_usb3_pcd_req) queue; ++#endif ++} dwc_ep_t; ++ ++/** PCD EP structure. ++ * This structure describes an EP, there is an array of EP pointers in the ++ * PCD structure. ++ */ ++typedef struct dwc_usb3_pcd_ep { ++ /** DWC_usb3 EP data */ ++ dwc_ep_t dwc_ep; ++ ++ /* ==== The rest is OS-specific ==== */ ++ ++ /** USB EP */ ++ struct usb_ep usb_ep; ++} dwc_usb3_pcd_ep_t; ++ ++/** @{ */ ++/** PCD EP accessor functions */ ++#define dwc_usb3_pcd_ep_to_pcd(pcd_ep) ((pcd_ep)->dwc_ep.pcd) ++#define dwc_usb3_pcd_ep_num(pcd_ep) ((pcd_ep)->dwc_ep.num) ++#define dwc_usb3_pcd_ep_type(pcd_ep) ((pcd_ep)->dwc_ep.type) ++#define dwc_usb3_pcd_ep_is_in(pcd_ep) ((pcd_ep)->dwc_ep.is_in) ++/** @} */ ++ ++struct dwc_hiber_scratchpad_array { ++ u64 dma_addr[15]; ++}; ++ ++/** DWC_usb3 PCD Structure. ++ * This structure encapsulates the data for the dwc_usb3 PCD. ++ */ ++typedef struct dwc_usb3_pcd { ++ /** The DWC otg device pointer */ ++ struct dwc_usb3_device *usb3_dev; ++ ++ /** USB3 link state */ ++ int link_state; ++ ++ /** State of the device */ ++ pcdstate_e state; ++ ++ /** State of EP0 */ ++ ep0state_e ep0state; ++ ++ /** EP0 Status Request is pending */ ++ unsigned int ep0_status_pending : 1; ++ ++ /** Indicates when SET CONFIGURATION Request is in process */ ++ unsigned int request_config : 1; ++ ++ /** State of the Remote Wakeup Enable */ ++ unsigned int remote_wakeup_enable : 1; ++ ++ /** State of the Latency Tolerance Messaging Enable */ ++ unsigned int ltm_enable : 1; ++ ++ /** True if we should send an LPM notification after the status stage */ ++ unsigned int send_lpm : 1; ++ ++ /** True if ready for remote wakeup request from user */ ++ unsigned int wkup_rdy : 1; ++ ++ /** True if we have enabled some EPs */ ++ unsigned int eps_enabled : 1; ++ ++ /** True if UTE has made some config changes */ ++ unsigned int ute_change : 1; ++ ++#ifdef DWC_STAR_9000463548_WORKAROUND ++ unsigned int configuring : 1; ++#endif ++ ++#ifdef CONFIG_USB_OTG_DWC ++ /** Set when user writes to 'hnp' sysfs attribute */ ++ unsigned int wants_host : 1; ++ ++ /** For set feature (b_hnp_enable) */ ++ unsigned int b_hnp_enable : 1; ++#endif ++ /** EP0 */ ++ dwc_usb3_pcd_ep_t *ep0; ++ ++ /** Array of OUT EPs (not including EP0) */ ++ dwc_usb3_pcd_ep_t *out_ep[DWC_MAX_EPS - 1]; ++ ++ /** Array of IN EPs (not including EP0) */ ++ dwc_usb3_pcd_ep_t *in_ep[DWC_MAX_EPS - 1]; ++ ++ /** Pointer to device Global registers. ++ * Device Global Registers starting at offset 700h ++ */ ++ dwc_usb3_dev_global_regs_t __iomem *dev_global_regs; ++ ++ /** Device Logical OUT EP-Specific Registers 800h-9ECh */ ++ dwc_usb3_dev_ep_regs_t __iomem *out_ep_regs; ++ ++ /** Device Logical IN EP-Specific Registers 810h-9FCh */ ++ dwc_usb3_dev_ep_regs_t __iomem *in_ep_regs; ++ ++ /** @{ */ ++ /** Scratchpad buffers for hibernation support */ ++ void *hiber_scratchpad[15]; ++ struct dwc_hiber_scratchpad_array *hiber_scratchpad_array; ++ dwc_dma_t hiber_scratchpad_array_dma; ++ /** @} */ ++ ++ /** @{ */ ++ /** EP0 state, saved across core hibernation */ ++ u32 ep0_out_save_state; ++ u32 ep0_in_save_state; ++ /** @} */ ++ ++ /** 'dummy' request, for EP0 only */ ++ dwc_usb3_pcd_req_t *ep0_req; ++ ++#ifdef DWC_UTE ++ /** size of Rx FIFO, requested by UTE */ ++ unsigned rxf_size; ++ ++ /** size of Tx FIFOs, requested by UTE */ ++ unsigned txf_size[DWC_MAX_TX_FIFOS]; ++ ++ /** mapping of Tx FIFO for each physical EP, requested by UTE */ ++ unsigned txf_map[DWC_MAX_PHYS_EP]; ++ ++ /** Rx FIFO default size */ ++ unsigned def_rxf_size; ++ ++ /** Tx FIFOs, default size */ ++ unsigned def_txf_size[DWC_MAX_TX_FIFOS]; ++#endif ++ ++ /** @{ */ ++ /** Thresholding enable flags and length variables */ ++ u16 rx_thr_en; ++ u16 iso_tx_thr_en; ++ u16 non_iso_tx_thr_en; ++ u16 rx_thr_length; ++ u16 tx_thr_length; ++ /** @} */ ++ ++ /** Device configuration information */ ++ u8 speed; /**< Device Speed - 0:Unk 1:LS 2:FS 3:HS 4:Var 5:SS */ ++ u8 num_out_eps; /**< Number # of Rx EP range: 0-15 except ep0 */ ++ u8 num_in_eps; /**< Number # of Tx EP range: 0-15 except ep0 */ ++ ++ /** The TEST mode to enter when test_mode_tasklet is executed */ ++ u8 test_mode; ++ ++ /** Count of pending Requests */ ++ unsigned request_pending; ++ ++ /* ==== The rest is OS-specific ==== */ ++#ifdef SELA_PLATFORM ++ /** Limit the number of EPs to speed up simulation */ ++ int max_in_eps, max_out_eps; ++ ++ /** Pointers for passing event codes back to the test code */ ++ int *dev_event, *ep_event; ++#endif ++#if defined(__linux__) || defined(LINUXTEST) ++ /** The spinlock for the PCD */ ++ spinlock_t lock; ++#endif ++#ifdef __linux__ ++ /** The associated gadget */ ++ struct usb_gadget *gadget; ++#endif ++ /** Tasklet to defer starting of TEST mode transmissions until ++ * Status Phase has been completed ++ */ ++ struct tasklet_struct test_mode_tasklet; ++ ++ /** ++ * Pointers to the DMA Descriptors for EP0 Control transfers ++ * (virtual and physical) ++ */ ++ ++ /** @{ */ ++ /** Descriptor for SETUP packets */ ++ dwc_usb3_dma_desc_t *ep0_setup_desc; ++ dwc_dma_t ep0_setup_desc_dma; ++ /** @} */ ++ ++ /** @{ */ ++ /** Descriptor for Data Out or Status Out phases */ ++ dwc_usb3_dma_desc_t *ep0_out_desc; ++ dwc_dma_t ep0_out_desc_dma; ++ /** @} */ ++ ++ /** @{ */ ++ /** Descriptor for Data In or Status In phases */ ++ dwc_usb3_dma_desc_t *ep0_in_desc; ++ dwc_dma_t ep0_in_desc_dma; ++ /** @} */ ++ ++ /** @{ */ ++ /** Data packet buffer used to return data for GET_STATUS and ++ * GET_DESCRIPTOR(BOS) up to 512 bytes in length ++ */ ++ u8 *ep0_status_buf; ++ dwc_dma_t ep0_status_buf_dma; ++#define DWC_STATUS_BUF_SIZE 512 ++ /** @} */ ++ ++ /** @{ */ ++ /** SETUP packet buffer for EP0 */ ++ union dwc_setup_pkt { ++ usb_device_request_t req; ++ u32 d32[2]; ++ char d8[8]; ++ } *ep0_setup_pkt; ++ dwc_dma_t ep0_setup_pkt_dma; ++ /** @} */ ++} dwc_usb3_pcd_t; ++ ++/** @{ */ ++/** PCD accessor functions */ ++#define dwc_usb3_ep0_setup_desc(pcd) (pcd)->ep0_setup_desc ++#define dwc_usb3_ep0_setup_desc_dma(pcd) (pcd)->ep0_setup_desc_dma ++#define dwc_usb3_ep0_out_desc(pcd) (pcd)->ep0_out_desc ++#define dwc_usb3_ep0_out_desc_dma(pcd) (pcd)->ep0_out_desc_dma ++#define dwc_usb3_ep0_in_desc(pcd) (pcd)->ep0_in_desc ++#define dwc_usb3_ep0_in_desc_dma(pcd) (pcd)->ep0_in_desc_dma ++#define dwc_usb3_ep0_setup_pkt(pcd) (pcd)->ep0_setup_pkt ++#define dwc_usb3_ep0_setup_pkt_dma(pcd) (pcd)->ep0_setup_pkt_dma ++/** @} */ ++ ++extern void dwc_usb3_ep0_activate(dwc_usb3_pcd_t *pcd, int restore); ++extern void dwc_usb3_ep_activate(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ int restore); ++extern void dwc_usb3_stop_all_xfers(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_complete_request(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep, u32 event); ++extern dwc_usb3_pcd_ep_t *dwc_usb3_get_out_ep(dwc_usb3_pcd_t *pcd, u32 ep_num); ++extern dwc_usb3_pcd_ep_t *dwc_usb3_get_in_ep(dwc_usb3_pcd_t *pcd, u32 ep_num); ++extern void dwc_usb3_handle_ep0_xfer(dwc_usb3_pcd_t *pcd, u32 event); ++#ifdef DEBUG_EP0 ++extern void dwc_usb3_print_ep0_state(dwc_usb3_pcd_t *pcd); ++#endif ++extern void dwc_usb3_handle_ep_intr(dwc_usb3_pcd_t *pcd, int physep, u32 event); ++extern int dwc_usb3_handle_dev_intr(dwc_usb3_pcd_t *pcd, u32 event); ++extern void dwc_usb3_handle_connect_done_intr(dwc_usb3_pcd_t *pcd); ++extern void dwc_enter_hibernation(dwc_usb3_pcd_t *pcd, int save_state); ++extern void dwc_exit_hibernation_after_connect(dwc_usb3_pcd_t *pcd, ++ int connected); ++extern int dwc_exit_hibernation(dwc_usb3_pcd_t *pcd, int restore_state); ++extern int dwc_usb3_handle_pme_intr(struct dwc_usb3_device *dev); ++extern void dwc_usb3_power_ctl(struct dwc_usb3_device *dev, int on); ++ ++/** @addtogroup init_api_grp */ ++/** @{ */ ++extern int dwc_usb3_pcd_init(struct dwc_usb3_device *dev); ++extern void dwc_usb3_pcd_remove(struct dwc_usb3_device *dev); ++/** @} */ ++ ++/** @addtogroup trb_api_grp TRB API Routines ++ * ++ * These routines handle the allocation, deallocation, and setup of TRBs. ++ */ ++/** @{ */ ++extern dwc_usb3_dma_desc_t *dwc_usb3_pcd_trb_alloc(dwc_usb3_pcd_ep_t *ep, ++ int num_trbs, int trb_type, int iso_intvl, int link, ++ dwc_dma_t *trbs_dma_ret); ++extern void dwc_usb3_pcd_trb_free(dwc_usb3_pcd_ep_t *ep /*, int num_trbs, int link, ++ void *trbs, dwc_dma_t trbs_dma*/); ++extern void dwc_usb3_pcd_fill_trbs(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *req); ++/** @} */ ++ ++/** @addtogroup ep_api_grp Endpoint API Routines ++ * ++ * These routines handle all the functionality required for configuring, ++ * enabling, controlling, and submitting transfers to an endpoint\n\n ++ * Note: For Control endpoint 0, only the submit_req, cancel_req, request_done, ++ * and set_halt routines are used; the remaining functionality is handled either ++ * by the @ref ep0_api_grp below or internally by the PCD. ++ */ ++/** @{ */ ++extern int dwc_usb3_pcd_ep_enable(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ const usb_endpoint_descriptor_t *ep_desc, ++ const ss_endpoint_companion_descriptor_t *ep_comp); ++extern int dwc_usb3_pcd_ep_disable(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep); ++extern int dwc_usb3_pcd_ep_submit_req(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *req, int req_flags); ++extern void dwc_usb3_pcd_ep_cancel_req(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *req, u32 stream); ++extern void dwc_usb3_pcd_request_done(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *req, int status); ++extern void dwc_usb3_pcd_ep_start_transfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *req, u32 event); ++extern void dwc_usb3_pcd_ep_set_stall(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep); ++extern void dwc_usb3_pcd_ep_clear_stall(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep); ++extern void dwc_usb3_pcd_ep_set_halt(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ int value); ++/** @} */ ++ ++/** @addtogroup ep0_api_grp Control Endpoint 0 API Routines ++ * ++ * These routines are only used for Control endpoint 0. ++ */ ++/** @{ */ ++extern void dwc_usb3_pcd_do_setup(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_pcd_ep0_out_start(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_pcd_ep0_start_transfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_req_t *req); ++extern void dwc_usb3_pcd_ep0_continue_transfer(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_req_t *req); ++extern void dwc_usb3_pcd_ep0_data_stage(dwc_usb3_pcd_t *pcd, int length); ++/** @} */ ++ ++/** @addtogroup misc_api_grp Miscellaneous API Routines ++ * ++ * These are miscellaneous routines that don't fit into any of the other ++ * categories. ++ */ ++/** @{ */ ++extern dwc_usb3_pcd_ep_t *dwc_usb3_pcd_get_ep_by_addr(dwc_usb3_pcd_t *pcd, ++ u16 index); ++extern int dwc_usb3_pcd_get_frame_number(dwc_usb3_pcd_t *pcd); ++extern int dwc_usb3_pcd_isoc_ep_hiber_restart(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep); ++extern void dwc_usb3_pcd_stop(dwc_usb3_pcd_t *pcd); ++/** @} */ ++ ++/** @addtogroup gadget_notif_grp Function Driver notification routines ++ * ++ * These routines receive notifications from the PCD when certain events occur ++ * which the Function Driver may need to be aware of. ++ */ ++/** @{ */ ++extern int dwc_usb3_gadget_connect(dwc_usb3_pcd_t *pcd, int speed); ++extern int dwc_usb3_gadget_disconnect(dwc_usb3_pcd_t *pcd); ++extern int dwc_usb3_gadget_suspend(dwc_usb3_pcd_t *pcd); ++extern int dwc_usb3_gadget_resume(dwc_usb3_pcd_t *pcd); ++extern int dwc_usb3_gadget_setup(dwc_usb3_pcd_t *pcd, ++ usb_device_request_t *ctrl); ++extern int dwc_usb3_gadget_complete(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *ep, ++ dwc_usb3_pcd_req_t *pcd_req, int status); ++extern void dwc_usb3_gadget_do_setup(dwc_usb3_pcd_t *pcd); ++/** @} */ ++ ++/** @addtogroup gadget_callbk_grp Function Driver callback routines ++ * ++ * The PCD calls these routines when it needs something from the Function ++ * Driver. ++ */ ++/** @{ */ ++extern void *dwc_usb3_gadget_alloc_dma(dwc_usb3_pcd_ep_t *ep, int size, ++ dwc_dma_t *mem_dma_ret); ++extern void dwc_usb3_gadget_free_dma(dwc_usb3_pcd_ep_t *ep, int size, void *mem, ++ dwc_dma_t mem_dma); ++extern dwc_usb3_pcd_req_t *dwc_usb3_gadget_get_request(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep); ++extern void dwc_usb3_gadget_start_next_request(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep); ++extern void dwc_usb3_gadget_isoc_ep_start(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep, u32 event); ++extern void dwc_usb3_gadget_request_nuke(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep); ++extern void dwc_usb3_gadget_set_ep_not_started(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep); ++/** @} */ ++ ++/* OS-specific routines called from core code */ ++extern void dwc_usb3_task_schedule(struct tasklet_struct *tasklet); ++ ++#ifdef CONFIG_USB_OTG_DWC ++extern void dwc_usb3_start_hnp(dwc_usb3_pcd_t *pcd); ++extern void dwc_usb3_host_release(dwc_usb3_pcd_t *pcd); ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_PCD_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/pcd_hiber.c b/drivers/usb/gadget/udc/hiudc3/pcd_hiber.c +new file mode 100644 +index 0000000..d401ef1 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/pcd_hiber.c +@@ -0,0 +1,679 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/pcd_hiber.c $ ++ * $Revision: #16 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * This file contains the PCD hibernation code. ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++/** ++ * This routine sends the core into hibernation, saving the core's runtime ++ * state if requested. ++ */ ++void dwc_enter_hibernation(dwc_usb3_pcd_t *pcd, int save_state) ++{ ++ dwc_usb3_device_t *dev = pcd->usb3_dev; ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ dwc_usb3_pcd_ep_t *ep; ++ int num_in_eps, num_out_eps, i; ++ u32 temp; ++ ++ dwc_debug2(dev, "%s(%d)\n", __func__, save_state); ++ num_in_eps = pcd->num_in_eps; ++ num_out_eps = pcd->num_out_eps; ++ ++ dev->hiber_wait_u0 = 0; ++ dwc_usb3_dis_usb2_suspend(pcd); ++ ++ /* Issue an "End Transfer" command for all active transfers with the ++ * ForceRM field set to 0, including the default control endpoint 0. ++ */ ++ ep = pcd->ep0; ++ if (ep->dwc_ep.active) { ++ if (ep->dwc_ep.tri_in) { ++ dwc_debug0(dev, "EndXfer on phys EP1 (IN)\n"); ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ ep->dwc_ep.condition = 0; ++ dwc_usb3_dep_endxfer(pcd, ep_reg, ep->dwc_ep.tri_in - 1, ++ 0, &ep->dwc_ep.condition); ++ ep->dwc_ep.tri_in = 0; ++ } ++ ++ if (ep->dwc_ep.tri_out) { ++ dwc_debug0(dev, "EndXfer on phys EP0 (OUT)\n"); ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ ep->dwc_ep.condition = 0; ++ dwc_usb3_dep_endxfer(pcd, ep_reg, ep->dwc_ep.tri_out - 1, ++ 0, &ep->dwc_ep.condition); ++ ep->dwc_ep.tri_out = 0; ++ } ++ } ++ ++ for (i = 0; i < num_in_eps; i++) { ++ ep = pcd->in_ep[i]; ++ if (ep->dwc_ep.active && ep->dwc_ep.tri_in) { ++ dwc_debug1(dev, "EndXfer on phys EP%d (IN)\n", ++ i * 2 + 3); ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ ep->dwc_ep.condition = 0; ++ dwc_usb3_dep_endxfer(pcd, ep_reg, ep->dwc_ep.tri_in - 1, ++ 0, &ep->dwc_ep.condition); ++ ep->dwc_ep.tri_in = 0; ++ } ++ ep->dwc_ep.xfer_started = 0; ++ } ++ ++ for (i = 0; i < num_out_eps; i++) { ++ ep = pcd->out_ep[i]; ++ if (ep->dwc_ep.active && ep->dwc_ep.tri_out) { ++ dwc_debug1(dev, "EndXfer on phys EP%d (OUT)\n", ++ i * 2 + 2); ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ ep->dwc_ep.condition = 0; ++ dwc_usb3_dep_endxfer(pcd, ep_reg, ep->dwc_ep.tri_out - 1, ++ 0, &ep->dwc_ep.condition); ++ ep->dwc_ep.tri_out = 0; ++ } ++ ep->dwc_ep.xfer_started = 0; ++ } ++ ++ if (save_state) { ++ dwc_debug0(dev, "Saving state - 1\n"); ++ ++ /* Issue a "Get Endpoint State" endpoint command for each active ++ * endpoint, and save the bits that are returned for use after ++ * coming out of hibernation. ++ * ++ * In addition, software must remember if the endpoint is ++ * currently in a Halted state. The endpoint is in a Halted ++ * state if software has issued a "Set STALL" command and has ++ * not issued a "Clear STALL" command. ++ */ ++ ep = pcd->ep0; ++ if (ep->dwc_ep.active) { ++ dwc_debug0(dev, "Saving state of phys EP1 (IN)\n"); ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ pcd->ep0_in_save_state = ++ dwc_usb3_dep_getepstate(pcd, ep_reg); ++ dwc_debug1(dev, "IN save state=%08x\n", ++ pcd->ep0_in_save_state); ++ dwc_debug0(dev, "Saving state of phys EP0 (OUT)\n"); ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ pcd->ep0_out_save_state = ++ dwc_usb3_dep_getepstate(pcd, ep_reg); ++ dwc_debug1(dev, "OUT save state=%08x\n", ++ pcd->ep0_out_save_state); ++ ep->dwc_ep.stalled_save = ep->dwc_ep.stopped; ++ } ++ ++ for (i = 0; i < num_in_eps; i++) { ++ ep = pcd->in_ep[i]; ++ if (ep->dwc_ep.active) { ++ dwc_debug1(dev, ++ "Saving state of phys EP%d (IN)\n", ++ i * 2 + 3); ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ ep->dwc_ep.save_state = ++ dwc_usb3_dep_getepstate(pcd, ep_reg); ++ ep->dwc_ep.stalled_save = ep->dwc_ep.stopped; ++ } ++ } ++ ++ for (i = 0; i < num_out_eps; i++) { ++ ep = pcd->out_ep[i]; ++ if (ep->dwc_ep.active) { ++ dwc_debug1(dev, ++ "Saving state of phys EP%d (OUT)\n", ++ i * 2 + 2); ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ ep->dwc_ep.save_state = ++ dwc_usb3_dep_getepstate(pcd, ep_reg); ++ ep->dwc_ep.stalled_save = ep->dwc_ep.stopped; ++ } ++ } ++ } else { ++ pcd->ep0state = EP0_IDLE; ++ } ++ ++ dwc_usb3_ena_usb2_suspend(pcd); ++ dev->hibernate = DWC_HIBER_SLEEPING; ++ ++ /* Set DCTL.RunStop to 0, DCTL.KeepConnect to 1 (or 0 if disconnected), ++ * and wait for DSTS.Halted to be set to 1. Software must service any ++ * events that are generated while it is waiting for Halted to be set ++ * to 1. ++ */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ temp &= ~DWC_DCTL_RUN_STOP_BIT; ++ if (!save_state) ++ temp &= ~DWC_DCTL_KEEP_CONNECT_BIT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, temp); ++ ++ dwc_debug0(dev, "Cleared Run/Stop bit, waiting for Halt bit\n"); ++ i = 25000; ++ do { ++ if (!dwc_usb3_handle_event(dev)) ++ dwc_udelay(dev, 1); ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dsts); ++ if (--i == 0) { ++ /* If there is data stuck in the Rx FIFO, then the ++ * controller will never set the DevCtrlHlt bit. To ++ * handle that case, after about 25ms flush any data ++ * that remains in the Rx FIFO. ++ */ ++ if (!(temp & DWC_DSTS_RXFIFO_EMPTY_BIT)) ++ dwc_usb3_flush_fifo(pcd, 0); // 0 = Rx FIFO ++ } ++ } while (!(temp & DWC_DSTS_DEV_CTRL_HLT_BIT)); ++ ++ /* Unconditionally save GUCTL/GUCTL1 */ ++ dev->guctl_save = dwc_rd32(dev, &dev->core_global_regs->guctl); ++ dev->guctl1_save = dwc_rd32(dev, &dev->core_global_regs->guctl1); ++ ++ if (save_state) { ++ dwc_debug0(dev, "Saving state - 2\n"); ++ ++ /* Read the D* registers (DCTL, DCFG, DEVTEN) and G* registers ++ * (GSBUSCFG0/1, GCTL, GTXTHRCFG, GRXTHRCFG, GTXFIFOSIZn, ++ * GRXFIFOSIZ0, GUSB3PIPECTL0, GUSB2PHYCFG0) and save their ++ * state. ++ */ ++ dev->dcfg_save = dwc_rd32(dev, &pcd->dev_global_regs->dcfg); ++ dwc_debug1(dev, "DCFG=%08x\n", dev->dcfg_save); ++ dev->dctl_save = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ dev->gtxfifosiz0_save = ++ dwc_rd32(dev, &dev->core_global_regs->gtxfifosiz[0]); ++ dev->gtxfifosiz1_save = ++ dwc_rd32(dev, &dev->core_global_regs->gtxfifosiz[1]); ++ dev->gtxfifosiz2_save = ++ dwc_rd32(dev, &dev->core_global_regs->gtxfifosiz[2]); ++ dev->gtxfifosiz3_save = ++ dwc_rd32(dev, &dev->core_global_regs->gtxfifosiz[3]); ++ dev->grxfifosiz0_save = ++ dwc_rd32(dev, &dev->core_global_regs->grxfifosiz[0]); ++ dwc_usb3_set_scratchpad_buf_array(pcd, ++ pcd->hiber_scratchpad_array_dma); ++ ++ /* Set the DCTL.CSS bit and wait for the save state process to ++ * complete by polling for DSTS.SSS to equal 0. ++ */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dsts); ++ dwc_debug1(dev, "DSTS before=%1x\n", temp); ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ dwc_debug1(dev, "DCTL before=%1x\n", temp); ++ temp |= DWC_DCTL_CSS_BIT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, temp); ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ dwc_debug1(dev, "DCTL after=%1x\n", temp); ++ ++ dwc_debug0(dev, "Set CSS bit, waiting for SSS bit clear\n"); ++ do { ++ dwc_udelay(dev, 1); ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dsts); ++ } while (temp & DWC_DSTS_SSS_BIT); ++ dwc_debug1(dev, "DSTS after=%1x\n", temp); ++#if 0 ++ /* If the save failed, the DSTS.SRE field will indicate an ++ * error. ++ */ ++ if (temp & DWC_DSTS_SRE_BIT) { ++ dwc_error0(dev, "### Save state failed! ###\n"); ++ ++ /* What should we do here ?? */ ++ return; ++ } ++#endif ++ } ++ ++ /* Communicate with the power controller to set the power state to D3 */ ++ dwc_usb3_power_ctl(dev, 0); ++ dev->pme_ready = 1; ++ ++ dwc_info0(dev, "In D3\n"); ++ ++ /* Remove core well power */ ++ // This is "faked" by the FPGA ++} ++ ++/** ++ * This routine restarts any transfer that was in progress on an EP when the ++ * core entered hibernation. ++ */ ++static void dwc_reenable_xfer_and_restart(dwc_usb3_pcd_t *pcd, ++ dwc_usb3_pcd_ep_t *ep) ++{ ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++ dwc_usb3_dma_desc_t *desc; ++ dwc_dma_t desc_dma; ++ u8 *tri; ++ int i, owned; ++ ++ dwc_debug1(pcd->usb3_dev, "%s()\n", __func__); ++ dwc_debug2(pcd->usb3_dev, "phys EP%d-%s\n", ep->dwc_ep.phys, ++ ep->dwc_ep.is_in ? "IN" : "OUT"); ++ desc = ep->dwc_ep.dma_desc; ++ ++ /* Find the first non-hw-owned TRB */ ++ for (i = 0; i < ep->dwc_ep.num_desc; i++, desc++) { ++ if (!dwc_usb3_is_hwo(desc) && ++ !(dwc_usb3_get_xfersts(desc) & DWC_TRBRSP_XFER_IN_PROG)) { ++ dwc_debug1(pcd->usb3_dev, ++ "Found non-hw-owned TRB at %d\n", i); ++ break; ++ } ++ } ++ ++ if (i == ep->dwc_ep.num_desc) ++ desc = ep->dwc_ep.dma_desc; ++ ++ /* Find the first hw-owned TRB */ ++ for (i = 0, owned = -1; i < ep->dwc_ep.num_desc; i++) { ++ /* If status == 4, this TRB's xfer was in progress prior to ++ * entering hibernation ++ */ ++ if (dwc_usb3_get_xfersts(desc) & DWC_TRBRSP_XFER_IN_PROG) { ++ dwc_debug1(pcd->usb3_dev, ++ "Found in-progress TRB at %d\n", i); ++ ++ /* Set HWO back to 1 so the xfer can continue */ ++ desc->control |= DWC_DSCCTL_HWO_BIT; ++ owned = i; ++ break; ++ } ++ ++ /* Save the index of the first TRB with the HWO bit set */ ++ if (dwc_usb3_is_hwo(desc)) { ++ dwc_debug1(pcd->usb3_dev, "Found hw-owned TRB at %d\n", ++ i); ++ ++ owned = i; ++ break; ++ } ++ ++ desc++; ++ if (desc == ep->dwc_ep.dma_desc + ep->dwc_ep.num_desc) ++ desc = ep->dwc_ep.dma_desc; ++ } ++ ++ wmb(); ++ ep->dwc_ep.hiber_desc_idx = 0; ++ ++ if (owned < 0) ++ /* No TRB had HWO bit set, fine */ ++ return; ++ ++ dwc_debug1(pcd->usb3_dev, "desc=%08lx\n", (unsigned long)desc); ++ ++ /* Restart of Isoc EPs is deferred until the host polls the EP */ ++ if (ep->dwc_ep.type == UE_ISOCHRONOUS) { ++ dwc_debug0(pcd->usb3_dev, ++ "Deferring restart until host polls\n"); ++ ep->dwc_ep.hiber_desc_idx = owned + 1; ++ return; ++ } ++ ++#ifdef VERBOSE ++ dwc_debug4(pcd->usb3_dev, "%08x %08x %08x %08x\n", ++ *((unsigned *)desc), *((unsigned *)desc + 1), ++ *((unsigned *)desc + 2), *((unsigned *)desc + 3)); ++#endif ++ /* Now restart at the first TRB found with HWO set */ ++ if (ep->dwc_ep.is_in) { ++ ep_reg = ep->dwc_ep.in_ep_reg; ++ tri = &ep->dwc_ep.tri_in; ++ } else { ++ ep_reg = ep->dwc_ep.out_ep_reg; ++ tri = &ep->dwc_ep.tri_out; ++ } ++ ++ dwc_debug0(pcd->usb3_dev, "Restarting xfer\n"); ++ desc = ep->dwc_ep.dma_desc + owned; ++ desc_dma = (dwc_dma_t) ++ ((unsigned long)ep->dwc_ep.dma_desc_dma + owned * 16); ++ dwc_debug1(pcd->usb3_dev, "desc=%08lx\n", (unsigned long)desc); ++ ++#ifdef VERBOSE ++ dwc_debug5(pcd->usb3_dev, "%08x %08x %08x %08x (%08x)\n", ++ *((unsigned *)desc), *((unsigned *)desc + 1), ++ *((unsigned *)desc + 2), *((unsigned *)desc + 3), ++ (unsigned)desc_dma); ++#endif ++ ++ dwc_usb3_dis_usb2_suspend(pcd); ++ *tri = dwc_usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0) + 1; ++ dwc_usb3_ena_usb2_suspend(pcd); ++} ++ ++/** ++ * This routine finishes exiting from hibernation once the device is connected. ++ */ ++void dwc_exit_hibernation_after_connect(dwc_usb3_pcd_t *pcd, int connected) ++{ ++ dwc_usb3_pcd_ep_t *ep; ++ int num_in_eps, num_out_eps, i; ++ u32 temp; ++ ++ dwc_debug2(pcd->usb3_dev, "%s(%d)\n", __func__, connected); ++ ++ /* Perform the steps in Section 9.1.5 "Initialization on ++ * SetConfiguration or SetInterface Command" in [DWSS]. ++ * ++ * While issuing the DEPCFG commands, set each endpoint's sequence ++ * number and flow control state to the value read during the save. ++ * ++ * If the endpoint was in the Halted state prior to entering ++ * hibernation, software must issue the "Set STALL" endpoint command ++ * to put the endpoint back into the Halted state. ++ */ ++ num_in_eps = pcd->num_in_eps; ++ num_out_eps = pcd->num_out_eps; ++ ++ pcd->eps_enabled = 0; ++ ++ ep = pcd->ep0; ++ if (ep->dwc_ep.active) ++ /* Setting STALL on EP0 caused problems */ ++ ep->dwc_ep.stalled_save = 0; ++ ++ for (i = 0; i < num_in_eps; i++) { ++ ep = pcd->in_ep[i]; ++ if (ep->dwc_ep.active) { ++ ep->dwc_ep.ena_once = 0; ++ dwc_debug1(pcd->usb3_dev, "Activating phys EP%d (IN)\n", ++ i * 2 + 3); ++ dwc_usb3_ep_activate(pcd, ep, 1); ++ if (ep->dwc_ep.stalled_save) { ++ ep->dwc_ep.stalled_save = 0; ++ dwc_debug1(pcd->usb3_dev, ++ "Stalling phys EP%d (IN)\n", ++ i * 2 + 3); ++ dwc_usb3_pcd_ep_set_stall(pcd, ep); ++ ep->dwc_ep.stopped = 1; ++ } ++ } ++ } ++ ++ for (i = 0; i < num_out_eps; i++) { ++ ep = pcd->out_ep[i]; ++ if (ep->dwc_ep.active) { ++ ep->dwc_ep.ena_once = 0; ++ dwc_debug1(pcd->usb3_dev, ++ "Activating phys EP%d (OUT)\n", i * 2 + 2); ++ dwc_usb3_ep_activate(pcd, ep, 1); ++ if (ep->dwc_ep.stalled_save) { ++ ep->dwc_ep.stalled_save = 0; ++ dwc_debug1(pcd->usb3_dev, ++ "Stalling phys EP%d (OUT)\n", ++ i * 2 + 2); ++ dwc_usb3_pcd_ep_set_stall(pcd, ep); ++ ep->dwc_ep.stopped = 1; ++ } ++ } ++ } ++ ++ /* (In this step, software re-configures the existing endpoints and ++ * starts their transfers). ++ * ++ * When software issued the EndXfer command with the ForceRM field set ++ * to '0' prior to entering hibernation, the core may have written back ++ * an active TRB for the transfer, setting the HWO bit to '0'. Software ++ * must ensure that the TRB is valid and set the HWO bit back to '1' ++ * prior to re-starting the transfer in this step. ++ */ ++ for (i = 0; i < num_in_eps; i++) { ++ ep = pcd->in_ep[i]; ++ if (ep->dwc_ep.active) { ++ dwc_debug1(pcd->usb3_dev, ++ "Restarting xfer on phys EP%d (IN)\n", ++ i * 2 + 3); ++ dwc_reenable_xfer_and_restart(pcd, ep); ++ } ++ } ++ ++ for (i = 0; i < num_out_eps; i++) { ++ ep = pcd->out_ep[i]; ++ if (ep->dwc_ep.active) { ++ dwc_debug1(pcd->usb3_dev, ++ "Restarting xfer on phys EP%d (OUT)\n", ++ i * 2 + 2); ++ dwc_reenable_xfer_and_restart(pcd, ep); ++ } ++ } ++ ++ /* If the core is already connected to the host (DSTS.USBLnkSt == 0, 2, ++ * 3, 14, or 15), set DCTL.ULStChngReq to '8' as described in Table 7-47 ++ * of [DWSS]. ++ * ++ * If the host initiated resume, this step completes the transition to ++ * U0. If the host did not initiate resume, this step causes the device ++ * to initiate resume (remote wakeup). ++ */ ++ if (connected) { ++ dwc_debug0(pcd->usb3_dev, "Already connected," ++ " requesting link state Recovery\n"); ++ dwc_usb3_pcd_set_link_state(pcd, DWC_LINK_STATE_REQ_RECOVERY); ++ //dwc_udelay(pcd->usb3_dev, 1); ++ //dwc_usb3_pcd_set_link_state(pcd, 0); ++ pcd->usb3_dev->hiber_wait_u0 = 1; ++ pcd->usb3_dev->hibernate = DWC_HIBER_WAIT_U0; ++ temp = dwc_usb3_pcd_get_link_state(pcd); ++ dwc_debug1(pcd->usb3_dev, "Link state %d\n", temp); ++ pcd->link_state = temp; ++ } else { ++ pcd->usb3_dev->hiber_wait_u0 = 0; ++ } ++} ++ ++/** ++ * This routine wakes the core from hibernation. ++ */ ++int dwc_exit_hibernation(dwc_usb3_pcd_t *pcd, int restore_state) ++{ ++ dwc_usb3_device_t *dev = pcd->usb3_dev; ++ u32 temp; ++ ++ dwc_debug2(dev, "%s(%d)\n", __func__, restore_state); ++ ++ /* Enable core well power */ ++ // This is "faked" by the FPGA ++ ++ /* Communicate with the power controller to set the power state to D0 */ ++ dwc_usb3_power_ctl(dev, 1); ++ dev->pme_ready = 0; ++ ++ dwc_info0(dev, "In D0\n"); ++ dev->hibernate = DWC_HIBER_AWAKE; ++ ++ /* Unconditionally restore GUCTL/GUCTL1 */ ++ dwc_wr32(dev, &dev->core_global_regs->guctl, dev->guctl_save); ++ dwc_wr32(dev, &dev->core_global_regs->guctl1, dev->guctl1_save); ++ ++ /* If the reset value of GSBUSCFG0/1 is not correct, write these ++ * registers with the desired values. ++ */ ++ if (dev->program_gsbuscfg) { ++ dwc_wr32(dev, &dev->core_global_regs->gsbuscfg0, ++ dev->gsbuscfg0); ++ dwc_wr32(dev, &dev->core_global_regs->gsbuscfg1, ++ dev->gsbuscfg1); ++ } ++ ++ if (restore_state) { ++ dwc_debug0(dev, "Restoring state\n"); ++ ++ /* Issue a "Set Scratchpad Buffer Array" device generic command ++ * and wait for completion by polling the DGCMD.CmdAct bit. ++ */ ++ dwc_usb3_set_scratchpad_buf_array(pcd, ++ pcd->hiber_scratchpad_array_dma); ++ ++ /* Write '1' to DCTL.CRS to start the restore process and wait ++ * for completion by polling the DSTS.RSS bit. ++ */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dsts); ++ dwc_debug1(dev, "DSTS before=%1x\n", temp); ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ dwc_debug1(dev, "DCTL before=%1x\n", temp); ++ temp |= DWC_DCTL_CRS_BIT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, temp); ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ dwc_debug1(dev, "DCTL after=%1x\n", temp); ++ ++ dwc_debug0(dev, "Set CRS bit, waiting for RSS bit clear\n"); ++ do { ++ dwc_udelay(dev, 1); ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dsts); ++ } while (temp & DWC_DSTS_RSS_BIT); ++ dwc_debug1(dev, "DSTS after=%1x\n", temp); ++#if 0 ++ /* If the restore failed, the DSTS.SRE field will indicate an ++ * error. ++ */ ++ if (temp & DWC_DSTS_SRE_BIT) { ++ dwc_error0(dev, "### Restore state failed! ###\n"); ++ ++ /* What should we do here ?? */ ++ return 1; ++ } ++#endif ++ /* (Restore the remaining D* and G* registers) */ ++ dwc_wr32(dev, &pcd->dev_global_regs->dcfg, dev->dcfg_save); ++ dwc_debug1(dev, "DCFG=%08x\n", dev->dcfg_save); ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dcfg); ++ dwc_debug1(dev, "DCFG read back %08x\n", temp); ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, dev->dctl_save); ++ dwc_wr32(dev, &dev->core_global_regs->gtxfifosiz[0], ++ dev->gtxfifosiz0_save); ++ dwc_wr32(dev, &dev->core_global_regs->gtxfifosiz[1], ++ dev->gtxfifosiz1_save); ++ dwc_wr32(dev, &dev->core_global_regs->gtxfifosiz[2], ++ dev->gtxfifosiz2_save); ++ dwc_wr32(dev, &dev->core_global_regs->gtxfifosiz[3], ++ dev->gtxfifosiz3_save); ++ dwc_wr32(dev, &dev->core_global_regs->grxfifosiz[0], ++ dev->grxfifosiz0_save); ++ } ++ ++ /* Configure the core as described in [DWSS] Section 9.1.1 "Device ++ * Power-On or Soft Reset," excluding the first step (Soft Reset). ++ */ ++ dwc_usb3_pcd_device_init(dev, 0, restore_state); ++ ++#ifdef CONFIG_USB_OTG_DWC ++ /* Enable Device mode interrupts */ ++ dwc_usb3_enable_device_interrupts(dev); ++ ++ /* Set Run/Stop bit, and Keep-Connect bit if hibernation enabled */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ temp |= DWC_DCTL_RUN_STOP_BIT; ++ if (dev->core_params->hibernate && ++ (dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT) ++ temp |= DWC_DCTL_KEEP_CONNECT_BIT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, temp); ++#endif ++ ++ /* ++ * If the LNR (Link-state Not Ready) field of DSTS is set to 1, ++ * continue polling the DSTS register until LNR=0. ++ */ ++ for (;;) { ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dsts); ++ if (temp & DWC_DSTS_LNR_BIT) ++ dwc_udelay(dev, 1); ++ else ++ break; ++ } ++ ++ /* Read the DSTS register to see the current link state. */ ++ temp = dwc_usb3_pcd_get_link_state(pcd); ++ pcd->link_state = temp; ++ ++ if (temp > DWC_LINK_STATE_U3 && temp != DWC_LINK_STATE_EARLY_SUSPEND && ++ temp != DWC_LINK_STATE_RESUME) { ++ /* If the core is not connected to the host, wait for a Connect ++ * Done event. ++ */ ++ if (temp != DWC_LINK_STATE_RESET) { ++ dev->hiber_wait_connect = 1; ++ ++ if (temp == DWC_LINK_STATE_SS_DIS && ++ /*pcd->speed != USB_SPEED_SUPER &&*/ ++ dev->core_params->ssdisquirk) { ++ dwc_debug0(dev, ++ "Link state SS_DIS, enabling quirk\n"); ++ dev->hibernate = DWC_HIBER_SS_DIS_QUIRK; ++ return DWC_HIBER_SS_DIS_QUIRK; ++ } ++ ++ dwc_debug1(dev, "Link state %d, waiting for connect" ++ " before exiting from hibernation\n", temp); ++ } else { ++ /* If the DSTS.USBLnkSt equals 14, it means a USB reset ++ * was received while the core was entering or exiting ++ * hibernation. Prior to performing the steps in ++ * sections 9.1.2 and 9.1.3, software must write Resume ++ * (8) into the DCTL.ULStChngReq field. ++ */ ++ dwc_debug0(dev, "USB Reset received during hibernation," ++ " requesting link state Recovery\n"); ++ dwc_usb3_pcd_set_link_state(pcd, ++ DWC_LINK_STATE_REQ_RECOVERY); ++ //dwc_mdelay(dev, 1); ++ //dwc_usb3_pcd_set_link_state(pcd, 0); ++ dwc_usb3_set_address(pcd, 0); ++ dev->hibernate = DWC_HIBER_WAIT_LINK_UP; ++ return DWC_HIBER_WAIT_LINK_UP; ++ } ++ } else { ++ dwc_debug1(dev, "Link state %d, exiting from hibernation\n", ++ temp); ++ dev->hiber_wait_connect = 0; ++ ++ /* Perform the steps in Section 9.1.3 "Initialization on ++ * Connect Done" in [DWSS]. ++ */ ++ dwc_usb3_handle_connect_done_intr(pcd); ++ ++ dwc_exit_hibernation_after_connect(pcd, 1); ++ } ++ ++ return 0; ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/pcd_intr.c b/drivers/usb/gadget/udc/hiudc3/pcd_intr.c +new file mode 100644 +index 0000000..fbdc652 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/pcd_intr.c +@@ -0,0 +1,691 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/pcd_intr.c $ ++ * $Revision: #114 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * This file contains the implementation of the PCD Interrupt handlers. ++ * ++ * The PCD handles the device interrupts. Many conditions can cause a ++ * device interrupt. When an interrupt occurs, the device interrupt ++ * service routine determines the cause of the interrupt and ++ * dispatches handling to the appropriate routine. These interrupt ++ * handling routines are described below. ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++#ifdef DWC_UTE ++# include "ute_if.h" ++#endif ++ ++/** ++ * This interrupt indicates that the USB link state has changed to L2, U3, or ++ * (if L1 Hibernation is enabled) L1, and software intervention is required. ++ */ ++static int handle_hiber_req_intr(dwc_usb3_pcd_t *pcd, u32 event) ++{ ++ int hird, is_superspeed; ++ u32 state; ++ ++ dwc_print0(pcd->usb3_dev, "HIBERNATION REQUEST\n"); ++ ++ is_superspeed = !!(event & DWC_DEVT_HIBER_SS_BIT); ++ state = event >> DWC_DEVT_HIBER_STATE_SHIFT & ++ DWC_DEVT_HIBER_STATE_BITS >> DWC_DEVT_HIBER_STATE_SHIFT; ++ dwc_print2(pcd->usb3_dev, "state=%x, is_superspeed=%d\n", ++ state, is_superspeed); ++ ++ /* TODO: Workaround */ ++ if (!(state == DWC_LINK_STATE_U3 || ++ //state == DWC_LINK_STATE_SS_DIS || ++ (!is_superspeed && (state == DWC_LINK_STATE_SLEEP)))) { ++ dwc_print0(pcd->usb3_dev, "HIBERNATION not handled\n"); ++ return 1; ++ } /* End workaround */ ++ ++ hird = event >> DWC_DEVT_HIBER_HIRD_SHIFT & ++ DWC_DEVT_HIBER_HIRD_BITS >> DWC_DEVT_HIBER_HIRD_SHIFT; ++ dwc_debug5(pcd->usb3_dev, "%s(%u), state=%d hird=%d ss=%d\n", ++ __func__, event, state, hird, is_superspeed); ++ ++ /* Enter hibernation if supported */ ++ if (pcd->usb3_dev->core_params->hibernate && ++ (pcd->usb3_dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT) { ++ /* Tell kernel thread to save state and enter hibernation */ ++ pcd->usb3_dev->hibernate = DWC_HIBER_ENTER_SAVE; ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * This interrupt indicates that the device has been disconnected. ++ */ ++static int handle_disconnect_intr(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_usb3_device_t *dev = pcd->usb3_dev; ++ u32 temp; ++ ++ dwc_print0(dev, "DISCONNECT\n"); ++ ++ /* Must set DCTL[8:5] to 5, according to 2.40a and later databook. ++ * Assuming here this will be harmless on earlier cores. ++ */ ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ temp &= ~DWC_DCTL_ULST_CHNG_REQ_BITS; ++ temp |= DWC_LINK_STATE_REQ_RX_DETECT << DWC_DCTL_ULST_CHNG_REQ_SHIFT; ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, temp); ++ ++ dwc_usb3_clr_eps_enabled(pcd); ++ dwc_usb3_pcd_stop(pcd); ++ ++ /* Enter hibernation if supported */ ++ if (dev->core_params->hibernate && dev->core_params->hiberdisc && ++ (dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT) { ++ /* Tell kernel thread to enter hibernation */ ++ dev->hibernate = DWC_HIBER_ENTER_NOSAVE; ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * This interrupt occurs when a USB Reset is detected. When the USB Reset ++ * Interrupt occurs, all transfers are stopped and the device state is set ++ * to DEFAULT. ++ */ ++static void handle_usb_reset_intr(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_usb3_pcd_ep_t *ep; ++ int i; ++ ++ dwc_print0(pcd->usb3_dev, "USB RESET\n"); ++ ++ /* If UsbReset comes without Disconnect first, fake it, because the ++ * gadget may need to see a disconnect first. The Synopsys UAS gadget ++ * needs this. ++ */ ++#if 1 ++ if (pcd->state != DWC_STATE_UNCONNECTED) { ++// dwc_print0(pcd->usb3_dev, "fake DISCONNECT\n"); ++ dwc_usb3_clr_eps_enabled(pcd); ++ dwc_usb3_pcd_stop(pcd); ++ } else ++#endif ++ { ++ /* Stop any active xfers on the non-EP0 endpoints */ ++ dwc_usb3_stop_all_xfers(pcd); ++ dwc_usb3_clr_eps_enabled(pcd); ++ } ++ ++ /* Clear stall on each EP */ ++ for (i = 0; i < pcd->num_in_eps; i++) { ++ ep = pcd->in_ep[i]; ++ if (ep->dwc_ep.active && ep->dwc_ep.stopped) ++ dwc_usb3_pcd_ep_clear_stall(pcd, ep); ++ } ++ for (i = 0; i < pcd->num_out_eps; i++) { ++ ep = pcd->out_ep[i]; ++ if (ep->dwc_ep.active && ep->dwc_ep.stopped) ++ dwc_usb3_pcd_ep_clear_stall(pcd, ep); ++ } ++ ++#ifndef SELA_PLATFORM_NOCTL ++ /* Set Device Address to 0 */ ++ dwc_usb3_set_address(pcd, 0); ++#endif ++ ++ pcd->remote_wakeup_enable = 0; ++ pcd->ltm_enable = 0; ++} ++ ++/** ++ * This interrupt occurs when a Connect Done is detected. ++ * Read the device status register and set the device speed in the data ++ * structure. Set up EP0 to receive SETUP packets. ++ */ ++void dwc_usb3_handle_connect_done_intr(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_usb3_device_t *dev = pcd->usb3_dev; ++ dwc_usb3_pcd_ep_t *ep0 = pcd->ep0; ++ int speed; ++#ifndef SELA_PLATFORM_NOCTL ++ u32 temp; ++ u32 diepcfg0, doepcfg0, diepcfg1, doepcfg1; ++ dwc_usb3_dev_ep_regs_t __iomem *ep_reg; ++#endif ++ ++#ifndef SELA_PLATFORM_NOCTL ++ /* The timing on reconnect after hibernation is so tight that we ++ * cannot afford the overhead of printing this to the dmesg log! ++ */ ++ if (!dev->core_params->hibernate || ++ (dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) != ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT) ++ dwc_print0(dev, "CONNECT\n"); ++ ++// printk("\n###%s,%d\n",__func__,__LINE__); ++# ifdef DEBUG_EP0 ++ dwc_usb3_print_ep0_state(pcd); ++# endif ++#endif /* SELA_PLATFORM_NOCTL */ ++ ++ ep0->dwc_ep.stopped = 0; ++ speed = dwc_usb3_get_device_speed(pcd); ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ pcd->speed = speed; ++ ++#ifndef SELA_PLATFORM_NOCTL ++# ifdef DWC_STAR_9000483510_WORKAROUND ++ if (speed == USB_SPEED_SUPER) ++ handle_usb_reset_intr(pcd); ++# endif ++ ++ /* If LPM enable was requested */ ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ if (dev->core_params->lpmctl) { ++ temp = dwc_rd32(dev, &pcd->dev_global_regs->dctl); ++ ++ /* HIRD threshold must be set to 0 when in SS */ ++ temp &= ~DWC_DCTL_HIRD_THR_BITS; ++ ++ /* If not SS */ ++ if (speed != USB_SPEED_SUPER) { ++ /* If hibernation enabled and non-default threshold */ ++ if (dev->core_params->hibernate && ++ (dev->hwparams1 & DWC_HWP1_EN_PWROPT_BITS) == ++ DWC_EN_PWROPT_HIBERNATION << DWC_HWP1_EN_PWROPT_SHIFT && ++ dev->hird_thresh) { ++ /* Set requested threshold value */ ++ temp |= dev->hird_thresh << DWC_DCTL_HIRD_THR_SHIFT & ++ DWC_DCTL_HIRD_THR_BITS; ++ } else { ++ /* Set default threshold value */ ++ temp |= 0x1c << DWC_DCTL_HIRD_THR_SHIFT; ++ } ++ } ++ ++ dwc_wr32(dev, &pcd->dev_global_regs->dctl, temp); ++ } ++ ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ /* Set the MPS of EP0 based on the connection speed */ ++ switch (speed) { ++ case USB_SPEED_SUPER: ++ pcd->ep0->dwc_ep.maxpacket = 512; ++ break; ++ ++ case USB_SPEED_HIGH: ++ case USB_SPEED_FULL: ++ pcd->ep0->dwc_ep.maxpacket = 64; ++ break; ++ ++ case USB_SPEED_LOW: ++ pcd->ep0->dwc_ep.maxpacket = 8; ++ break; ++ } ++ ++ diepcfg0 = DWC_USB3_EP_TYPE_CONTROL << DWC_EPCFG0_EPTYPE_SHIFT; ++ diepcfg0 |= DWC_CFG_ACTION_MODIFY << DWC_EPCFG0_CFG_ACTION_SHIFT; ++ diepcfg1 = DWC_EPCFG1_XFER_CMPL_BIT | DWC_EPCFG1_XFER_IN_PROG_BIT | ++ DWC_EPCFG1_XFER_NRDY_BIT | DWC_EPCFG1_EP_DIR_BIT; ++ ++ doepcfg0 = DWC_USB3_EP_TYPE_CONTROL << DWC_EPCFG0_EPTYPE_SHIFT; ++ doepcfg0 |= DWC_CFG_ACTION_MODIFY << DWC_EPCFG0_CFG_ACTION_SHIFT; ++ doepcfg1 = DWC_EPCFG1_XFER_CMPL_BIT | DWC_EPCFG1_XFER_IN_PROG_BIT | ++ DWC_EPCFG1_XFER_NRDY_BIT; ++ ++ diepcfg0 |= pcd->ep0->dwc_ep.maxpacket << DWC_EPCFG0_MPS_SHIFT; ++ doepcfg0 |= pcd->ep0->dwc_ep.maxpacket << DWC_EPCFG0_MPS_SHIFT; ++ ++# ifdef DWC_UTE ++ ep0->dwc_ep.tx_fifo_num = pcd->txf_map[0]; ++# endif ++ diepcfg0 |= ep0->dwc_ep.tx_fifo_num << DWC_EPCFG0_TXFNUM_SHIFT; ++ ++ dwc_usb3_dis_usb2_suspend(pcd); ++ ++ /* Issue "DEPCFG" command to EP0-OUT */ ++ ep_reg = &pcd->out_ep_regs[0]; ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ dwc_usb3_dep_cfg(pcd, ep_reg, doepcfg0, doepcfg1, 0); ++ ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ /* Issue "DEPCFG" command to EP0-IN */ ++ ep_reg = &pcd->in_ep_regs[0]; ++ dwc_usb3_dep_cfg(pcd, ep_reg, diepcfg0, diepcfg1, 0); ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ ++ dwc_usb3_ena_usb2_suspend(pcd); ++// printk("\n###%s,%d\n",__func__,__LINE__); ++#endif /* SELA_PLATFORM_NOCTL */ ++ ++ if (pcd->state == DWC_STATE_UNCONNECTED) ++ pcd->state = DWC_STATE_DEFAULT; ++ ++ /* Inform the gadget of the connection and the speed */ ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ dwc_usb3_gadget_connect(pcd, speed); ++// printk("\n###%s,%d\n",__func__,__LINE__); ++ ++ if (dev->hiber_wait_connect) { ++ /* Already did 'Perform the steps in Section 9.1.3 ++ * "Initialization on Connect Done" in [DWSS]'. ++ */ ++ ++ dwc_debug0(dev, "Hibernation wait connect\n"); ++ dev->hiber_wait_connect = 0; ++ dwc_exit_hibernation_after_connect(pcd, 0); ++ } ++} ++ ++/** ++ * This interrupt indicates that the USB link state has changed. ++ */ ++static void handle_link_status_change_intr(dwc_usb3_pcd_t *pcd) ++{ ++ int state; ++ int speed; ++ ++ if (pcd->usb3_dev->snpsid >= 0x5533230a) ++ return; ++ ++ state = dwc_usb3_pcd_get_link_state(pcd); ++ speed = dwc_usb3_get_device_speed(pcd); ++ pcd->speed = speed; ++ dwc_debug2(pcd->usb3_dev, "LINK state=%d speed=%d\n", state, speed); ++ ++ switch (state) { ++ case DWC_LINK_STATE_U0: ++ if (pcd->usb3_dev->hiber_wait_u0) { ++ pcd->speed = speed; ++ if (pcd->remote_wakeup_enable) ++ dwc_usb3_pcd_remote_wake(pcd, 0); ++ pcd->usb3_dev->hiber_wait_u0 = 0; ++ } ++ ++ /* If transitioning from 3->0 */ ++ if (pcd->link_state == DWC_LINK_STATE_U3) { ++ dwc_debug0(pcd->usb3_dev, ++ "Enabling function remote wake\n"); ++ pcd->wkup_rdy = 1; ++ } else { ++ pcd->wkup_rdy = 0; ++ } ++ ++ pcd->link_state = state; ++ break; ++ ++ case DWC_LINK_STATE_U3: ++ /* If transitioning to 3 */ ++ if (pcd->link_state != DWC_LINK_STATE_U3) ++ dwc_usb3_gadget_suspend(pcd); ++ /* FALL-THRU */ ++ ++ default: ++ pcd->link_state = state; ++ pcd->wkup_rdy = 0; ++ break; ++ } ++} ++ ++/** ++ * This interrupt indicates that the DWC_usb3 controller has detected a ++ * resume or remote wakeup sequence. ++ */ ++static void handle_wakeup_detected_intr(dwc_usb3_pcd_t *pcd) ++{ ++ int state; ++ ++ dwc_debug0(pcd->usb3_dev, ++ "++Resume or Remote Wakeup Detected Interrupt++\n"); ++ dwc_debug1(pcd->usb3_dev, "DSTS=0x%01x\n", ++ dwc_rd32(pcd->usb3_dev, &pcd->dev_global_regs->dsts)); ++ state = dwc_usb3_pcd_get_link_state(pcd); ++ pcd->link_state = state; ++ ++ if (state == DWC_LINK_STATE_U0) ++ dwc_usb3_gadget_resume(pcd); ++} ++ ++/** ++ * This interrupt indicates that a U3/L2-L1 Suspend event has occurred. ++ */ ++static void handle_u3_l2l1_susp_intr(dwc_usb3_pcd_t *pcd) ++{ ++ int state; ++ ++ if (pcd->usb3_dev->snpsid < 0x5533230a) ++ return; ++ ++ state = dwc_usb3_pcd_get_link_state(pcd); ++ dwc_debug1(pcd->usb3_dev, "LINK state=%d\n", state); ++ ++ switch (state) { ++ case DWC_LINK_STATE_U0: ++ /* If transitioning from 3->0 */ ++ if (pcd->link_state == DWC_LINK_STATE_U3) { ++ dwc_debug0(pcd->usb3_dev, ++ "Enabling function remote wake\n"); ++ pcd->wkup_rdy = 1; ++ } else { ++ pcd->wkup_rdy = 0; ++ } ++ ++ pcd->link_state = state; ++ break; ++ ++ case DWC_LINK_STATE_U3: ++ /* If transitioning to 3 */ ++ if (pcd->link_state != DWC_LINK_STATE_U3) ++ dwc_usb3_gadget_suspend(pcd); ++ /* FALL-THRU */ ++ ++ default: ++ pcd->link_state = state; ++ pcd->wkup_rdy = 0; ++ break; ++ } ++} ++ ++/** ++ * This routine handles the SOF Interrupts. At this time the SOF Interrupt ++ * is disabled. ++ */ ++static void handle_sof_intr(dwc_usb3_pcd_t *pcd) ++{ ++ dwc_debug0(pcd->usb3_dev, "SOF\n"); ++} ++ ++/** ++ * This interrupt indicates that an EP has a pending interrupt. ++ */ ++void dwc_usb3_handle_ep_intr(dwc_usb3_pcd_t *pcd, int physep, u32 event) ++{ ++ dwc_usb3_pcd_ep_t *ep; ++ int epnum, is_in, temp; ++ char *dir; ++ ++ dwc_debug4(pcd->usb3_dev, "%s(%lx,%d,0x%08x)\n", __func__, ++ (unsigned long)pcd, physep, event); ++ ++ /* Physical Out EPs are even, physical In EPs are odd */ ++ is_in = physep & 1; ++ epnum = physep >> 1 & 0xf; ++ ++ /* Get EP pointer */ ++ if (is_in) { ++ ep = dwc_usb3_get_in_ep(pcd, epnum); ++ dir = "IN"; ++ } else { ++ ep = dwc_usb3_get_out_ep(pcd, epnum); ++ dir = "OUT"; ++ } ++ ++ dwc_debug1(pcd->usb3_dev, "%s EP intr\n", dir); ++ ++#ifdef VERBOSE ++ dwc_debug4(pcd->usb3_dev, "EP%d-%s: type=%d mps=%d\n", ++ ep->dwc_ep.num, (ep->dwc_ep.is_in ? "IN" : "OUT"), ++ ep->dwc_ep.type, ep->dwc_ep.maxpacket); ++#endif ++#ifdef SELA_PLATFORM ++ if (pcd->ep_event) { ++ int evt = event >> DWC_DEPEVT_INTTYPE_SHIFT & ++ DWC_DEPEVT_INTTYPE_BITS >> DWC_DEPEVT_INTTYPE_SHIFT; ++ evt |= epnum << 8 | is_in << 15; ++ *pcd->ep_event = evt; ++ } ++#endif ++ ++ temp = pcd->usb3_dev->hibernate; ++ if (temp >= DWC_HIBER_SLEEPING && temp != DWC_HIBER_WAIT_U0 && ++ temp != DWC_HIBER_SS_DIS_QUIRK) { ++ dwc_info3(pcd->usb3_dev, ++ "EP%d-%s: got event 0x%08x while hibernating\n", ++ ep->dwc_ep.num, (ep->dwc_ep.is_in ? "IN" : "OUT"), ++ event); ++ return; ++ } ++ ++ switch (event & DWC_DEPEVT_INTTYPE_BITS) { ++ case DWC_DEPEVT_XFER_CMPL << DWC_DEPEVT_INTTYPE_SHIFT: ++#ifdef VERBOSE ++ dwc_debug2(pcd->usb3_dev, "[EP%d] %s xfer complete\n", ++ epnum, dir); ++#endif ++ ep->dwc_ep.xfer_started = 0; ++ ++ if (ep->dwc_ep.type != UE_ISOCHRONOUS) { ++ /* Complete the transfer */ ++ if (epnum == 0) ++ dwc_usb3_handle_ep0_xfer(pcd, event); ++ else ++ dwc_usb3_complete_request(pcd, ep, event); ++ } else { ++ dwc_print2(pcd->usb3_dev, ++ "[EP%d] %s xfer complete for ISOC EP!\n", ++ epnum, dir); ++ } ++ ++ break; ++ ++ case DWC_DEPEVT_XFER_IN_PROG << DWC_DEPEVT_INTTYPE_SHIFT: ++#ifdef VERBOSE ++ dwc_debug2(pcd->usb3_dev, "[EP%d] %s xfer in progress\n", ++ epnum, dir); ++#endif ++ if (ep->dwc_ep.type == UE_ISOCHRONOUS) { ++#if 0 ++ u32 now_uf, evt_uf; ++ ++ /* Don't complete transfer if it's way in the past */ ++ now_uf = dwc_usb3_get_frame(pcd); ++ evt_uf = dwc_usb3_get_eventsofn(event); ++ now_uf &= DWC_DEPEVT_ISOC_UFRAME_NUM_BITS >> ++ DWC_DEPEVT_ISOC_UFRAME_NUM_SHIFT; ++ if (now_uf - evt_uf < 0x8000) ++#endif ++ /* Complete the transfer */ ++ dwc_usb3_complete_request(pcd, ep, event); ++ } else { ++// dwc_print2(pcd->usb3_dev, ++// "[EP%d] %s xfer in progress for non-ISOC EP!\n", ++// epnum, dir); ++ ++ /* Complete the transfer */ ++ if (epnum == 0) ++ dwc_usb3_handle_ep0_xfer(pcd, event); ++ else ++ dwc_usb3_complete_request(pcd, ep, event); ++ } ++ ++ break; ++ ++ case DWC_DEPEVT_XFER_NRDY << DWC_DEPEVT_INTTYPE_SHIFT: ++ dwc_debug2(pcd->usb3_dev, "[EP%d] %s xfer not ready\n", ++ epnum, dir); ++ ++ if (epnum == 0) { ++ switch (pcd->ep0state) { ++ case EP0_IN_WAIT_GADGET: ++ case EP0_IN_WAIT_NRDY: ++ if (is_in) ++ dwc_usb3_handle_ep0_xfer(pcd, event); ++ break; ++ case EP0_OUT_WAIT_GADGET: ++ case EP0_OUT_WAIT_NRDY: ++ if (!is_in) ++ dwc_usb3_handle_ep0_xfer(pcd, event); ++ break; ++ default: ++ break; ++ } ++ } else if (ep->dwc_ep.type == UE_ISOCHRONOUS) { ++ dwc_isocdbg2(pcd->usb3_dev, ++ "[EP%d] %s xfer not ready for ISOC EP!\n", ++ epnum, dir); ++ if (!ep->dwc_ep.xfer_started) ++ dwc_usb3_gadget_isoc_ep_start(pcd, ep, event); ++ } ++ ++ break; ++ ++ case DWC_DEPEVT_FIFOXRUN << DWC_DEPEVT_INTTYPE_SHIFT: ++ dwc_error2(pcd->usb3_dev, "[EP%d] %s FIFO Underrun Error!\n", ++ epnum, dir); ++ break; ++ ++ case DWC_DEPEVT_EPCMD_CMPL << DWC_DEPEVT_INTTYPE_SHIFT: ++ dwc_error2(pcd->usb3_dev, "[EP%d] %s Command Complete!\n", ++ epnum, dir); ++ break; ++ ++ default: ++ dwc_error2(pcd->usb3_dev, "[EP%d] %s Unknown event!\n", ++ epnum, dir); ++ break; ++ } ++} ++ ++/** ++ * PCD interrupt handler. ++ * ++ * The PCD handles the device interrupts. Many conditions can cause a ++ * device interrupt. When an interrupt occurs, the device interrupt ++ * service routine determines the cause of the interrupt and ++ * dispatches handling to the appropriate routine. ++ */ ++int dwc_usb3_handle_dev_intr(dwc_usb3_pcd_t *pcd, u32 event) ++{ ++ u32 dint = event >> DWC_DEVT_SHIFT & DWC_DEVT_BITS >> DWC_DEVT_SHIFT; ++ int temp, ret = 0; ++ ++#ifdef VERBOSE ++ dwc_debug2(pcd->usb3_dev, "%s() event=%08x\n", __func__, event); ++#endif ++#ifdef SELA_PLATFORM ++ if (pcd->dev_event) ++ *pcd->dev_event = dint; ++#endif ++ ++ temp = pcd->usb3_dev->hibernate; ++ if (temp >= DWC_HIBER_SLEEPING && temp != DWC_HIBER_WAIT_U0 && ++ temp != DWC_HIBER_SS_DIS_QUIRK) { ++ dwc_info1(pcd->usb3_dev, ++ "Device: got event 0x%08x while hibernating\n", ++ event); ++ return 0; ++ } ++ ++// printk("\n#####%s,%d,dint=0x%x\n",__func__,__LINE__,dint); ++ switch (dint) { ++ case DWC_DEVT_DISCONN: ++ dwc_debug0(pcd->usb3_dev, "Disconnect\n"); ++ printk("Disconnect\n"); ++ ret = handle_disconnect_intr(pcd); ++ break; ++ ++ case DWC_DEVT_USBRESET: ++ dwc_debug0(pcd->usb3_dev, "USB Reset\n"); ++ printk("USB Reset\n"); ++ handle_usb_reset_intr(pcd); ++ break; ++ ++ case DWC_DEVT_CONNDONE: ++ dwc_debug0(pcd->usb3_dev, "Connect Done\n"); ++ printk("Connect Done\n"); ++ dwc_usb3_handle_connect_done_intr(pcd); ++ break; ++ ++ case DWC_DEVT_ULST_CHNG: ++ printk("Link Status Change\n"); ++ handle_link_status_change_intr(pcd); ++ break; ++ ++ case DWC_DEVT_WKUP: ++ dwc_debug0(pcd->usb3_dev, "Wakeup\n"); ++ printk("Wakeup\n"); ++ handle_wakeup_detected_intr(pcd); ++ break; ++ ++ case DWC_DEVT_HIBER_REQ: ++ dwc_debug0(pcd->usb3_dev, "Hibernation Request\n"); ++ printk("Hibernation Request\n"); ++ ret = handle_hiber_req_intr(pcd, event); ++ break; ++ ++ case DWC_DEVT_U3_L2L1_SUSP: ++ dwc_debug0(pcd->usb3_dev, "U3/L2-L1 Suspend Event\n"); ++ printk("U3/L2-L1 Suspend Event\n"); ++ handle_u3_l2l1_susp_intr(pcd); ++ break; ++ ++ case DWC_DEVT_SOF: ++ dwc_debug0(pcd->usb3_dev, "Start of Frame\n"); ++ printk("Start of Frame\n"); ++ handle_sof_intr(pcd); ++ break; ++ ++ case DWC_DEVT_ERRATICERR: ++ dwc_debug0(pcd->usb3_dev, "Erratic Error\n"); ++ printk("Erratic Error\n"); ++ break; ++ ++ case DWC_DEVT_CMD_CMPL: ++ dwc_debug0(pcd->usb3_dev, "Command Complete\n"); ++ printk("Command Complete\n"); ++ break; ++ ++ case DWC_DEVT_OVERFLOW: ++ dwc_debug0(pcd->usb3_dev, "Overflow\n"); ++ printk("Overflow\n"); ++ break; ++ ++ default: ++ dwc_debug0(pcd->usb3_dev, "Unknown event!\n"); ++ printk("Unknown event!\n"); ++ break; ++ } ++ ++ return ret; ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/usb.h b/drivers/usb/gadget/udc/hiudc3/usb.h +new file mode 100644 +index 0000000..3317776 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/usb.h +@@ -0,0 +1,961 @@ ++/* ++ * Copyright (c) 1998 The NetBSD Foundation, Inc. ++ * All rights reserved. ++ * ++ * This code is derived from software contributed to The NetBSD Foundation ++ * by Lennart Augustsson (lennart@augustsson.net) at ++ * Carlstedt Research & Technology. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * This product includes software developed by the NetBSD ++ * Foundation, Inc. and its contributors. ++ * 4. Neither the name of The NetBSD Foundation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS ++ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS ++ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++/* Modified by Synopsys, Inc, 12/12/2007 */ ++ ++ ++#ifndef _USB_H_ ++#define _USB_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @file ++ */ ++ ++/* ++ * The USB records contain some unaligned little-endian word ++ * components. The U[SG]ETW macros take care of both the alignment ++ * and endian problem and should always be used to access non-byte ++ * values. ++ */ ++typedef u_int8_t uByte; ++typedef u_int8_t uWord[2]; ++typedef u_int8_t uDWord[4]; ++ ++#define USETW2(w,h,l) ((w)[0] = (u_int8_t)(l), (w)[1] = (u_int8_t)(h)) ++#define UCONSTW(x) { (x) & 0xff, ((x) >> 8) & 0xff } ++#define UCONSTDW(x) { (x) & 0xff, ((x) >> 8) & 0xff, \ ++ ((x) >> 16) & 0xff, ((x) >> 24) & 0xff } ++ ++#if 1 ++#define UGETW(w) ((w)[0] | ((w)[1] << 8)) ++#define USETW(w,v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8)) ++#define UGETDW(w) ((w)[0] | ((w)[1] << 8) | ((w)[2] << 16) | ((w)[3] << 24)) ++#define USETDW(w,v) ((w)[0] = (u_int8_t)(v), \ ++ (w)[1] = (u_int8_t)((v) >> 8), \ ++ (w)[2] = (u_int8_t)((v) >> 16), \ ++ (w)[3] = (u_int8_t)((v) >> 24)) ++#else ++/* ++ * On little-endian machines that can handle unaligned accesses ++ * (e.g. i386) these macros can be replaced by the following. ++ */ ++#define UGETW(w) (*(u_int16_t *)(w)) ++#define USETW(w,v) (*(u_int16_t *)(w) = (v)) ++#define UGETDW(w) (*(u_int32_t *)(w)) ++#define USETDW(w,v) (*(u_int32_t *)(w) = (v)) ++#endif ++ ++/* ++ * Macros for accessing UAS IU fields, which are big-endian ++ */ ++#define IUSETW2(w,h,l) ((w)[0] = (u_int8_t)(h), (w)[1] = (u_int8_t)(l)) ++#define IUCONSTW(x) { ((x) >> 8) & 0xff, (x) & 0xff } ++#define IUCONSTDW(x) { ((x) >> 24) & 0xff, ((x) >> 16) & 0xff, \ ++ ((x) >> 8) & 0xff, (x) & 0xff } ++#define IUGETW(w) (((w)[0] << 8) | (w)[1]) ++#define IUSETW(w,v) ((w)[0] = (u_int8_t)((v) >> 8), (w)[1] = (u_int8_t)(v)) ++#define IUGETDW(w) (((w)[0] << 24) | ((w)[1] << 16) | ((w)[2] << 8) | (w)[3]) ++#define IUSETDW(w,v) ((w)[0] = (u_int8_t)((v) >> 24), \ ++ (w)[1] = (u_int8_t)((v) >> 16), \ ++ (w)[2] = (u_int8_t)((v) >> 8), \ ++ (w)[3] = (u_int8_t)(v)) ++ ++//#define UPACKED __attribute__ ((__packed__)) ++ ++typedef struct { ++ uByte bmRequestType; ++ uByte bRequest; ++ uWord wValue; ++ uWord wIndex; ++ uWord wLength; ++} UPACKED usb_device_request_t; ++ ++#define UT_GET_DIR(a) ((a) & 0x80) ++#define UT_WRITE 0x00 ++#define UT_READ 0x80 ++ ++#define UT_GET_TYPE(a) ((a) & 0x60) ++#define UT_STANDARD 0x00 ++#define UT_CLASS 0x20 ++#define UT_VENDOR 0x40 ++ ++#define UT_GET_RECIPIENT(a) ((a) & 0x1f) ++#define UT_DEVICE 0x00 ++#define UT_INTERFACE 0x01 ++#define UT_ENDPOINT 0x02 ++#define UT_OTHER 0x03 ++ ++#define UT_READ_DEVICE (UT_READ | UT_STANDARD | UT_DEVICE) ++#define UT_READ_INTERFACE (UT_READ | UT_STANDARD | UT_INTERFACE) ++#define UT_READ_ENDPOINT (UT_READ | UT_STANDARD | UT_ENDPOINT) ++#define UT_WRITE_DEVICE (UT_WRITE | UT_STANDARD | UT_DEVICE) ++#define UT_WRITE_INTERFACE (UT_WRITE | UT_STANDARD | UT_INTERFACE) ++#define UT_WRITE_ENDPOINT (UT_WRITE | UT_STANDARD | UT_ENDPOINT) ++#define UT_READ_CLASS_DEVICE (UT_READ | UT_CLASS | UT_DEVICE) ++#define UT_READ_CLASS_INTERFACE (UT_READ | UT_CLASS | UT_INTERFACE) ++#define UT_READ_CLASS_OTHER (UT_READ | UT_CLASS | UT_OTHER) ++#define UT_READ_CLASS_ENDPOINT (UT_READ | UT_CLASS | UT_ENDPOINT) ++#define UT_WRITE_CLASS_DEVICE (UT_WRITE | UT_CLASS | UT_DEVICE) ++#define UT_WRITE_CLASS_INTERFACE (UT_WRITE | UT_CLASS | UT_INTERFACE) ++#define UT_WRITE_CLASS_OTHER (UT_WRITE | UT_CLASS | UT_OTHER) ++#define UT_WRITE_CLASS_ENDPOINT (UT_WRITE | UT_CLASS | UT_ENDPOINT) ++#define UT_READ_VENDOR_DEVICE (UT_READ | UT_VENDOR | UT_DEVICE) ++#define UT_READ_VENDOR_INTERFACE (UT_READ | UT_VENDOR | UT_INTERFACE) ++#define UT_READ_VENDOR_OTHER (UT_READ | UT_VENDOR | UT_OTHER) ++#define UT_READ_VENDOR_ENDPOINT (UT_READ | UT_VENDOR | UT_ENDPOINT) ++#define UT_WRITE_VENDOR_DEVICE (UT_WRITE | UT_VENDOR | UT_DEVICE) ++#define UT_WRITE_VENDOR_INTERFACE (UT_WRITE | UT_VENDOR | UT_INTERFACE) ++#define UT_WRITE_VENDOR_OTHER (UT_WRITE | UT_VENDOR | UT_OTHER) ++#define UT_WRITE_VENDOR_ENDPOINT (UT_WRITE | UT_VENDOR | UT_ENDPOINT) ++ ++/* Requests */ ++#define UR_GET_STATUS 0x00 ++#define USTAT_STANDARD_STATUS 0x00 ++#define WUSTAT_WUSB_FEATURE 0x01 ++#define WUSTAT_CHANNEL_INFO 0x02 ++#define WUSTAT_RECEIVED_DATA 0x03 ++#define WUSTAT_MAS_AVAILABILITY 0x04 ++#define WUSTAT_CURRENT_TRANSMIT_POWER 0x05 ++#define UR_CLEAR_FEATURE 0x01 ++#define UR_SET_FEATURE 0x03 ++#define UR_SET_AND_TEST_FEATURE 0x0c ++#define UR_SET_ADDRESS 0x05 ++#define UR_GET_DESCRIPTOR 0x06 ++#define UDESC_DEVICE 0x01 ++#define UDESC_CONFIG 0x02 ++#define UDESC_STRING 0x03 ++#define UDESC_INTERFACE 0x04 ++#define UDESC_ENDPOINT 0x05 ++#define UDESC_SS_USB_COMPANION 0x30 ++#define UDESC_DEVICE_QUALIFIER 0x06 ++#define UDESC_OTHER_SPEED_CONFIGURATION 0x07 ++#define UDESC_INTERFACE_POWER 0x08 ++#define UDESC_OTG 0x09 ++#define WUDESC_SECURITY 0x0c ++#define WUDESC_KEY 0x0d ++#define WUD_GET_KEY_INDEX(_wValue_) ((_wValue_) & 0xf) ++#define WUD_GET_KEY_TYPE(_wValue_) (((_wValue_) & 0x30) >> 4) ++#define WUD_KEY_TYPE_ASSOC 0x01 ++#define WUD_KEY_TYPE_GTK 0x02 ++#define WUD_GET_KEY_ORIGIN(_wValue_) (((_wValue_) & 0x40) >> 6) ++#define WUD_KEY_ORIGIN_HOST 0x00 ++#define WUD_KEY_ORIGIN_DEVICE 0x01 ++#define WUDESC_ENCRYPTION_TYPE 0x0e ++#define WUDESC_BOS 0x0f ++#define WUDESC_DEVICE_CAPABILITY 0x10 ++#define WUDESC_WIRELESS_ENDPOINT_COMPANION 0x11 ++#define UDESC_BOS 0x0f ++#define UDESC_DEVICE_CAPABILITY 0x10 ++#define UDESC_CS_DEVICE 0x21 /* class specific */ ++#define UDESC_CS_CONFIG 0x22 ++#define UDESC_CS_STRING 0x23 ++#define UDESC_CS_INTERFACE 0x24 ++#define UDESC_CS_ENDPOINT 0x25 ++#define UDESC_HUB 0x29 ++#define UR_SET_DESCRIPTOR 0x07 ++#define UR_GET_CONFIG 0x08 ++#define UR_SET_CONFIG 0x09 ++#define UR_GET_INTERFACE 0x0a ++#define UR_SET_INTERFACE 0x0b ++#define UR_SYNCH_FRAME 0x0c ++#define UR_SET_SEL 0x30 ++#define UR_SET_ISOC_DELAY 0x31 ++#define WUR_SET_ENCRYPTION 0x0d ++#define WUR_GET_ENCRYPTION 0x0e ++#define WUR_SET_HANDSHAKE 0x0f ++#define WUR_GET_HANDSHAKE 0x10 ++#define WUR_SET_CONNECTION 0x11 ++#define WUR_SET_SECURITY_DATA 0x12 ++#define WUR_GET_SECURITY_DATA 0x13 ++#define WUR_SET_WUSB_DATA 0x14 ++#define WUDATA_DRPIE_INFO 0x01 ++#define WUDATA_TRANSMIT_DATA 0x02 ++#define WUDATA_TRANSMIT_PARAMS 0x03 ++#define WUDATA_RECEIVE_PARAMS 0x04 ++#define WUDATA_TRANSMIT_POWER 0x05 ++#define WUR_LOOPBACK_DATA_WRITE 0x15 ++#define WUR_LOOPBACK_DATA_READ 0x16 ++#define WUR_SET_INTERFACE_DS 0x17 ++ ++/* Feature numbers */ ++#define UF_ENDPOINT_HALT 0 ++#define UF_DEVICE_REMOTE_WAKEUP 1 ++#define UF_TEST_MODE 2 ++#define UF_DEVICE_B_HNP_ENABLE 3 ++#define UF_DEVICE_A_HNP_SUPPORT 4 ++#define UF_DEVICE_A_ALT_HNP_SUPPORT 5 ++#define WUF_WUSB 3 ++#define WUF_TX_DRPIE 0x0 ++#define WUF_DEV_XMIT_PACKET 0x1 ++#define WUF_COUNT_PACKETS 0x2 ++#define WUF_CAPTURE_PACKETS 0x3 ++#define UF_FUNCTION_SUSPEND 0 ++#define UF_U1_ENABLE 48 ++#define UF_U2_ENABLE 49 ++#define UF_LTM_ENABLE 50 ++ ++/* Class requests from the USB 2.0 hub spec, table 11-15 */ ++#define UCR_CLEAR_HUB_FEATURE (0x2000 | UR_CLEAR_FEATURE) ++#define UCR_CLEAR_PORT_FEATURE (0x2300 | UR_CLEAR_FEATURE) ++#define UCR_GET_HUB_DESCRIPTOR (0xa000 | UR_GET_DESCRIPTOR) ++#define UCR_GET_HUB_STATUS (0xa000 | UR_GET_STATUS) ++#define UCR_GET_PORT_STATUS (0xa300 | UR_GET_STATUS) ++#define UCR_SET_HUB_FEATURE (0x2000 | UR_SET_FEATURE) ++#define UCR_SET_PORT_FEATURE (0x2300 | UR_SET_FEATURE) ++#define UCR_SET_AND_TEST_PORT_FEATURE (0xa300 | UR_SET_AND_TEST_FEATURE) ++ ++#ifdef _MSC_VER ++#include <pshpack1.h> ++#endif ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDescriptorSubtype; ++} UPACKED usb_descriptor_t; ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++} UPACKED usb_descriptor_header_t; ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord bcdUSB; ++#define UD_USB_2_0 0x0200 ++#define UD_IS_USB2(d) (UGETW((d)->bcdUSB) >= UD_USB_2_0) ++ uByte bDeviceClass; ++ uByte bDeviceSubClass; ++ uByte bDeviceProtocol; ++ uByte bMaxPacketSize; ++ /* The fields below are not part of the initial descriptor. */ ++ uWord idVendor; ++ uWord idProduct; ++ uWord bcdDevice; ++ uByte iManufacturer; ++ uByte iProduct; ++ uByte iSerialNumber; ++ uByte bNumConfigurations; ++} UPACKED usb_device_descriptor_t; ++#define USB_DEVICE_DESCRIPTOR_SIZE 18 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord wTotalLength; ++ uByte bNumInterface; ++ uByte bConfigurationValue; ++ uByte iConfiguration; ++#define UC_ATT_ONE (1 << 7) /* must be set */ ++#define UC_ATT_SELFPOWER (1 << 6) /* self powered */ ++#define UC_ATT_WAKEUP (1 << 5) /* can wakeup */ ++#define UC_ATT_BATTERY (1 << 4) /* battery powered */ ++ uByte bmAttributes; ++#define UC_BUS_POWERED 0x80 ++#define UC_SELF_POWERED 0x40 ++#define UC_REMOTE_WAKEUP 0x20 ++ uByte bMaxPower; /* max current in 2 mA units */ ++#define UC_POWER_FACTOR 2 ++} UPACKED usb_config_descriptor_t; ++#define USB_CONFIG_DESCRIPTOR_SIZE 9 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bInterfaceNumber; ++ uByte bAlternateSetting; ++ uByte bNumEndpoints; ++ uByte bInterfaceClass; ++ uByte bInterfaceSubClass; ++ uByte bInterfaceProtocol; ++ uByte iInterface; ++} UPACKED usb_interface_descriptor_t; ++#define USB_INTERFACE_DESCRIPTOR_SIZE 9 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bEndpointAddress; ++#define UE_GET_DIR(a) ((a) & 0x80) ++#define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7)) ++#define UE_DIR_IN 0x80 ++#define UE_DIR_OUT 0x00 ++#define UE_ADDR 0x0f ++#define UE_GET_ADDR(a) ((a) & UE_ADDR) ++ uByte bmAttributes; ++#define UE_XFERTYPE 0x03 ++#define UE_CONTROL 0x00 ++#define UE_ISOCHRONOUS 0x01 ++#define UE_BULK 0x02 ++#define UE_INTERRUPT 0x03 ++#define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE) ++#define UE_ISO_TYPE 0x0c ++#define UE_ISO_ASYNC 0x04 ++#define UE_ISO_ADAPT 0x08 ++#define UE_ISO_SYNC 0x0c ++#define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE) ++ uWord wMaxPacketSize; ++ uByte bInterval; ++} UPACKED usb_endpoint_descriptor_t; ++#define USB_ENDPOINT_DESCRIPTOR_SIZE 7 ++ ++typedef struct ss_endpoint_companion_descriptor { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bMaxBurst; ++#define USSE_GET_MAX_STREAMS(a) ((a) & 0x1f) ++#define USSE_SET_MAX_STREAMS(a, b) ((a) | ((b) & 0x1f)) ++#define USSE_GET_MAX_PACKET_NUM(a) ((a) & 0x03) ++#define USSE_SET_MAX_PACKET_NUM(a, b) ((a) | ((b) & 0x03)) ++ uByte bmAttributes; ++ uWord wBytesPerInterval; ++} UPACKED ss_endpoint_companion_descriptor_t; ++#define USB_SS_ENDPOINT_COMPANION_DESCRIPTOR_SIZE 6 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord bString[127]; ++} UPACKED usb_string_descriptor_t; ++#define USB_MAX_STRING_LEN 128 ++#define USB_LANGUAGE_TABLE 0 /* # of the string language id table */ ++ ++/* Hub specific request */ ++#define UR_GET_BUS_STATE 0x02 ++#define UR_CLEAR_TT_BUFFER 0x08 ++#define UR_RESET_TT 0x09 ++#define UR_GET_TT_STATE 0x0a ++#define UR_STOP_TT 0x0b ++ ++/* Hub features */ ++#define UHF_C_HUB_LOCAL_POWER 0 ++#define UHF_C_HUB_OVER_CURRENT 1 ++#define UHF_PORT_CONNECTION 0 ++#define UHF_PORT_ENABLE 1 ++#define UHF_PORT_SUSPEND 2 ++#define UHF_PORT_OVER_CURRENT 3 ++#define UHF_PORT_RESET 4 ++#define UHF_PORT_L1 5 ++#define UHF_PORT_POWER 8 ++#define UHF_PORT_LOW_SPEED 9 ++#define UHF_PORT_HIGH_SPEED 10 ++#define UHF_C_PORT_CONNECTION 16 ++#define UHF_C_PORT_ENABLE 17 ++#define UHF_C_PORT_SUSPEND 18 ++#define UHF_C_PORT_OVER_CURRENT 19 ++#define UHF_C_PORT_RESET 20 ++#define UHF_C_PORT_L1 23 ++#define UHF_PORT_TEST 21 ++#define UHF_PORT_INDICATOR 22 ++ ++typedef struct { ++ uByte bDescLength; ++ uByte bDescriptorType; ++ uByte bNbrPorts; ++ uWord wHubCharacteristics; ++#define UHD_PWR 0x0003 ++#define UHD_PWR_GANGED 0x0000 ++#define UHD_PWR_INDIVIDUAL 0x0001 ++#define UHD_PWR_NO_SWITCH 0x0002 ++#define UHD_COMPOUND 0x0004 ++#define UHD_OC 0x0018 ++#define UHD_OC_GLOBAL 0x0000 ++#define UHD_OC_INDIVIDUAL 0x0008 ++#define UHD_OC_NONE 0x0010 ++#define UHD_TT_THINK 0x0060 ++#define UHD_TT_THINK_8 0x0000 ++#define UHD_TT_THINK_16 0x0020 ++#define UHD_TT_THINK_24 0x0040 ++#define UHD_TT_THINK_32 0x0060 ++#define UHD_PORT_IND 0x0080 ++ uByte bPwrOn2PwrGood; /* delay in 2 ms units */ ++#define UHD_PWRON_FACTOR 2 ++ uByte bHubContrCurrent; ++ uByte DeviceRemovable[32]; /* max 255 ports */ ++#define UHD_NOT_REMOV(desc, i) \ ++ (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1) ++ /* deprecated */ uByte PortPowerCtrlMask[1]; ++} UPACKED usb_hub_descriptor_t; ++#define USB_HUB_DESCRIPTOR_SIZE 9 /* includes deprecated PortPowerCtrlMask */ ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord bcdUSB; ++ uByte bDeviceClass; ++ uByte bDeviceSubClass; ++ uByte bDeviceProtocol; ++ uByte bMaxPacketSize0; ++ uByte bNumConfigurations; ++ uByte bReserved; ++} UPACKED usb_device_qualifier_t; ++#define USB_DEVICE_QUALIFIER_SIZE 10 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bmAttributes; ++#define UOTG_SRP 0x01 ++#define UOTG_HNP 0x02 ++} UPACKED usb_otg_descriptor_t; ++ ++/* OTG feature selectors */ ++#define UOTG_B_HNP_ENABLE 3 ++#define UOTG_A_HNP_SUPPORT 4 ++#define UOTG_A_ALT_HNP_SUPPORT 5 ++#define UOTG_NTF_HOST_REL 51 ++#define UOTG_B3_RSP_ENABLE 52 ++ ++typedef struct { ++ uWord wStatus; ++/* Device status flags */ ++#define UDS_SELF_POWERED 0x0001 ++#define UDS_REMOTE_WAKEUP 0x0002 ++/* Endpoint status flags */ ++#define UES_HALT 0x0001 ++} UPACKED usb_status_t; ++ ++typedef struct { ++ uWord wHubStatus; ++#define UHS_LOCAL_POWER 0x0001 ++#define UHS_OVER_CURRENT 0x0002 ++ uWord wHubChange; ++} UPACKED usb_hub_status_t; ++ ++typedef struct { ++ uWord wPortStatus; ++#define UPS_CURRENT_CONNECT_STATUS 0x0001 ++#define UPS_PORT_ENABLED 0x0002 ++#define UPS_SUSPEND 0x0004 ++#define UPS_OVERCURRENT_INDICATOR 0x0008 ++#define UPS_RESET 0x0010 ++#define UPS_PORT_POWER 0x0100 ++#define UPS_LOW_SPEED 0x0200 ++#define UPS_HIGH_SPEED 0x0400 ++#define UPS_PORT_TEST 0x0800 ++#define UPS_PORT_INDICATOR 0x1000 ++ uWord wPortChange; ++#define UPS_C_CONNECT_STATUS 0x0001 ++#define UPS_C_PORT_ENABLED 0x0002 ++#define UPS_C_SUSPEND 0x0004 ++#define UPS_C_OVERCURRENT_INDICATOR 0x0008 ++#define UPS_C_PORT_RESET 0x0010 ++} UPACKED usb_port_status_t; ++ ++#ifdef _MSC_VER ++#include <poppack.h> ++#endif ++ ++/* Device class codes */ ++#define UDCLASS_IN_INTERFACE 0x00 ++#define UDCLASS_COMM 0x02 ++#define UDCLASS_HUB 0x09 ++#define UDSUBCLASS_HUB 0x00 ++#define UDPROTO_FSHUB 0x00 ++#define UDPROTO_HSHUBSTT 0x01 ++#define UDPROTO_HSHUBMTT 0x02 ++#define UDCLASS_DIAGNOSTIC 0xdc ++#define UDCLASS_WIRELESS 0xe0 ++#define UDSUBCLASS_RF 0x01 ++#define UDPROTO_BLUETOOTH 0x01 ++#define UDCLASS_VENDOR 0xff ++ ++/* Interface class codes */ ++#define UICLASS_UNSPEC 0x00 ++ ++#define UICLASS_AUDIO 0x01 ++#define UISUBCLASS_AUDIOCONTROL 1 ++#define UISUBCLASS_AUDIOSTREAM 2 ++#define UISUBCLASS_MIDISTREAM 3 ++ ++#define UICLASS_CDC 0x02 /* communication */ ++#define UISUBCLASS_DIRECT_LINE_CONTROL_MODEL 1 ++#define UISUBCLASS_ABSTRACT_CONTROL_MODEL 2 ++#define UISUBCLASS_TELEPHONE_CONTROL_MODEL 3 ++#define UISUBCLASS_MULTICHANNEL_CONTROL_MODEL 4 ++#define UISUBCLASS_CAPI_CONTROLMODEL 5 ++#define UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL 6 ++#define UISUBCLASS_ATM_NETWORKING_CONTROL_MODEL 7 ++#define UIPROTO_CDC_AT 1 ++ ++#define UICLASS_HID 0x03 ++#define UISUBCLASS_BOOT 1 ++#define UIPROTO_BOOT_KEYBOARD 1 ++ ++#define UICLASS_PHYSICAL 0x05 ++ ++#define UICLASS_IMAGE 0x06 ++ ++#define UICLASS_PRINTER 0x07 ++#define UISUBCLASS_PRINTER 1 ++#define UIPROTO_PRINTER_UNI 1 ++#define UIPROTO_PRINTER_BI 2 ++#define UIPROTO_PRINTER_1284 3 ++ ++#define UICLASS_MASS 0x08 ++#define UISUBCLASS_RBC 1 ++#define UISUBCLASS_SFF8020I 2 ++#define UISUBCLASS_QIC157 3 ++#define UISUBCLASS_UFI 4 ++#define UISUBCLASS_SFF8070I 5 ++#define UISUBCLASS_SCSI 6 ++#define UIPROTO_MASS_CBI_I 0 ++#define UIPROTO_MASS_CBI 1 ++#define UIPROTO_MASS_BBB_OLD 2 /* Not in the spec anymore */ ++#define UIPROTO_MASS_BBB 80 /* 'P' for the Iomega Zip drive */ ++ ++#define UICLASS_HUB 0x09 ++#define UISUBCLASS_HUB 0 ++#define UIPROTO_FSHUB 0 ++#define UIPROTO_HSHUBSTT 0 /* Yes, same as previous */ ++#define UIPROTO_HSHUBMTT 1 ++ ++#define UICLASS_CDC_DATA 0x0a ++#define UISUBCLASS_DATA 0 ++#define UIPROTO_DATA_ISDNBRI 0x30 /* Physical iface */ ++#define UIPROTO_DATA_HDLC 0x31 /* HDLC */ ++#define UIPROTO_DATA_TRANSPARENT 0x32 /* Transparent */ ++#define UIPROTO_DATA_Q921M 0x50 /* Management for Q921 */ ++#define UIPROTO_DATA_Q921 0x51 /* Data for Q921 */ ++#define UIPROTO_DATA_Q921TM 0x52 /* TEI multiplexer for Q921 */ ++#define UIPROTO_DATA_V42BIS 0x90 /* Data compression */ ++#define UIPROTO_DATA_Q931 0x91 /* Euro-ISDN */ ++#define UIPROTO_DATA_V120 0x92 /* V.24 rate adaption */ ++#define UIPROTO_DATA_CAPI 0x93 /* CAPI 2.0 commands */ ++#define UIPROTO_DATA_HOST_BASED 0xfd /* Host based driver */ ++#define UIPROTO_DATA_PUF 0xfe /* see Prot. Unit Func. Desc.*/ ++#define UIPROTO_DATA_VENDOR 0xff /* Vendor specific */ ++ ++#define UICLASS_SMARTCARD 0x0b ++ ++/*#define UICLASS_FIRM_UPD 0x0c*/ ++ ++#define UICLASS_SECURITY 0x0d ++ ++#define UICLASS_DIAGNOSTIC 0xdc ++ ++#define UICLASS_WIRELESS 0xe0 ++#define UISUBCLASS_RF 0x01 ++#define UIPROTO_BLUETOOTH 0x01 ++ ++#define UICLASS_APPL_SPEC 0xfe ++#define UISUBCLASS_FIRMWARE_DOWNLOAD 1 ++#define UISUBCLASS_IRDA 2 ++#define UIPROTO_IRDA 0 ++ ++#define UICLASS_VENDOR 0xff ++ ++#define USB_HUB_MAX_DEPTH 5 ++ ++/* ++ * Minimum time a device needs to be powered down to go through ++ * a power cycle. XXX Are these time in the spec? ++ */ ++#define USB_POWER_DOWN_TIME 200 /* ms */ ++#define USB_PORT_POWER_DOWN_TIME 100 /* ms */ ++ ++#if 0 ++/* These are the values from the spec. */ ++#define USB_PORT_RESET_DELAY 10 /* ms */ ++#define USB_PORT_ROOT_RESET_DELAY 50 /* ms */ ++#define USB_PORT_RESET_RECOVERY 10 /* ms */ ++#define USB_PORT_POWERUP_DELAY 100 /* ms */ ++#define USB_SET_ADDRESS_SETTLE 2 /* ms */ ++#define USB_RESUME_DELAY (20*5) /* ms */ ++#define USB_RESUME_WAIT 10 /* ms */ ++#define USB_RESUME_RECOVERY 10 /* ms */ ++#define USB_EXTRA_POWER_UP_TIME 0 /* ms */ ++#else ++/* Allow for marginal (i.e. non-conforming) devices. */ ++#define USB_PORT_RESET_DELAY 50 /* ms */ ++#define USB_PORT_ROOT_RESET_DELAY 250 /* ms */ ++#define USB_PORT_RESET_RECOVERY 250 /* ms */ ++#define USB_PORT_POWERUP_DELAY 300 /* ms */ ++#define USB_SET_ADDRESS_SETTLE 10 /* ms */ ++#define USB_RESUME_DELAY (50*5) /* ms */ ++#define USB_RESUME_WAIT 50 /* ms */ ++#define USB_RESUME_RECOVERY 50 /* ms */ ++#define USB_EXTRA_POWER_UP_TIME 20 /* ms */ ++#endif ++ ++#define USB_MIN_POWER 100 /* mA */ ++#define USB_MAX_POWER 500 /* mA */ ++ ++#define USB_BUS_RESET_DELAY 100 /* ms XXX?*/ ++ ++#define USB_UNCONFIG_NO 0 ++#define USB_UNCONFIG_INDEX (-1) ++ ++/*** ioctl() related stuff ***/ ++ ++struct usb_ctl_request { ++ int ucr_addr; ++ usb_device_request_t ucr_request; ++ void *ucr_data; ++ int ucr_flags; ++#define USBD_SHORT_XFER_OK 0x04 /* allow short reads */ ++ int ucr_actlen; /* actual length transferred */ ++}; ++ ++struct usb_alt_interface { ++ int uai_config_index; ++ int uai_interface_index; ++ int uai_alt_no; ++}; ++ ++#define USB_CURRENT_CONFIG_INDEX (-1) ++#define USB_CURRENT_ALT_INDEX (-1) ++ ++struct usb_config_desc { ++ int ucd_config_index; ++ usb_config_descriptor_t ucd_desc; ++}; ++ ++struct usb_interface_desc { ++ int uid_config_index; ++ int uid_interface_index; ++ int uid_alt_index; ++ usb_interface_descriptor_t uid_desc; ++}; ++ ++struct usb_endpoint_desc { ++ int ued_config_index; ++ int ued_interface_index; ++ int ued_alt_index; ++ int ued_endpoint_index; ++ usb_endpoint_descriptor_t ued_desc; ++}; ++ ++struct usb_full_desc { ++ int ufd_config_index; ++ u_int ufd_size; ++ u_char *ufd_data; ++}; ++ ++struct usb_string_desc { ++ int usd_string_index; ++ int usd_language_id; ++ usb_string_descriptor_t usd_desc; ++}; ++ ++struct usb_ctl_report_desc { ++ int ucrd_size; ++ u_char ucrd_data[1024]; /* filled data size will vary */ ++}; ++ ++typedef struct { u_int32_t cookie; } usb_event_cookie_t; ++ ++#define USB_MAX_DEVNAMES 4 ++#define USB_MAX_DEVNAMELEN 16 ++struct usb_device_info { ++ u_int8_t udi_bus; ++ u_int8_t udi_addr; /* device address */ ++ usb_event_cookie_t udi_cookie; ++ char udi_product[USB_MAX_STRING_LEN]; ++ char udi_vendor[USB_MAX_STRING_LEN]; ++ char udi_release[8]; ++ u_int16_t udi_productNo; ++ u_int16_t udi_vendorNo; ++ u_int16_t udi_releaseNo; ++ u_int8_t udi_class; ++ u_int8_t udi_subclass; ++ u_int8_t udi_protocol; ++ u_int8_t udi_config; ++ u_int8_t udi_speed; ++#define USB_SPEED_UNKNOWN 0 ++#define USB_SPEED_LOW 1 ++#define USB_SPEED_FULL 2 ++#define USB_SPEED_HIGH 3 ++#define USB_SPEED_VARIABLE 4 ++#define USB_SPEED_SUPER 5 ++ int udi_power; /* power consumption in mA, 0 if selfpowered */ ++ int udi_nports; ++ char udi_devnames[USB_MAX_DEVNAMES][USB_MAX_DEVNAMELEN]; ++ u_int8_t udi_ports[16];/* hub only: addresses of devices on ports */ ++#define USB_PORT_ENABLED 0xff ++#define USB_PORT_SUSPENDED 0xfe ++#define USB_PORT_POWERED 0xfd ++#define USB_PORT_DISABLED 0xfc ++}; ++ ++struct usb_ctl_report { ++ int ucr_report; ++ u_char ucr_data[1024]; /* filled data size will vary */ ++}; ++ ++struct usb_device_stats { ++ u_long uds_requests[4]; /* indexed by transfer type UE_* */ ++}; ++ ++#define WUSB_MIN_IE 0x80 ++#define WUSB_WCTA_IE 0x80 ++#define WUSB_WCONNECTACK_IE 0x81 ++#define WUSB_WHOSTINFO_IE 0x82 ++#define WUHI_GET_CA(_bmAttributes_) ((_bmAttributes_) & 0x3) ++#define WUHI_CA_RECONN 0x00 ++#define WUHI_CA_LIMITED 0x01 ++#define WUHI_CA_ALL 0x03 ++#define WUHI_GET_MLSI(_bmAttributes_) (((_bmAttributes_) & 0x38) >> 3) ++#define WUSB_WCHCHANGEANNOUNCE_IE 0x83 ++#define WUSB_WDEV_DISCONNECT_IE 0x84 ++#define WUSB_WHOST_DISCONNECT_IE 0x85 ++#define WUSB_WRELEASE_CHANNEL_IE 0x86 ++#define WUSB_WWORK_IE 0x87 ++#define WUSB_WCHANNEL_STOP_IE 0x88 ++#define WUSB_WDEV_KEEPALIVE_IE 0x89 ++#define WUSB_WISOCH_DISCARD_IE 0x8A ++#define WUSB_WRESETDEVICE_IE 0x8B ++#define WUSB_WXMIT_PACKET_ADJUST_IE 0x8C ++#define WUSB_MAX_IE 0x8C ++ ++/* Device Notification Types */ ++ ++#define WUSB_DN_MIN 0x01 ++#define WUSB_DN_CONNECT 0x01 ++# define WUSB_DA_OLDCONN 0x00 ++# define WUSB_DA_NEWCONN 0x01 ++# define WUSB_DA_SELF_BEACON 0x02 ++# define WUSB_DA_DIR_BEACON 0x04 ++# define WUSB_DA_NO_BEACON 0x06 ++#define WUSB_DN_DISCONNECT 0x02 ++#define WUSB_DN_EPRDY 0x03 ++#define WUSB_DN_MASAVAILCHANGED 0x04 ++#define WUSB_DN_REMOTEWAKEUP 0x05 ++#define WUSB_DN_SLEEP 0x06 ++#define WUSB_DN_ALIVE 0x07 ++#define WUSB_DN_MAX 0x07 ++ ++#ifdef _MSC_VER ++#include <pshpack1.h> ++#endif ++ ++/* WUSB Handshake Data. Used during the SET/GET HANDSHAKE requests */ ++typedef struct wusb_hndshk_data { ++ uByte bMessageNumber; ++ uByte bStatus; ++ uByte tTKID[3]; ++ uByte bReserved; ++ uByte CDID[16]; ++ uByte Nonce[16]; ++ uByte MIC[8]; ++} UPACKED wusb_hndshk_data_t; ++#define WUSB_HANDSHAKE_LEN_FOR_MIC 38 ++ ++/* WUSB Connection Context */ ++typedef struct wusb_conn_context { ++ uByte CHID [16]; ++ uByte CDID [16]; ++ uByte CK [16]; ++} UPACKED wusb_conn_context_t; ++ ++/* WUSB Security Descriptor */ ++typedef struct wusb_security_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord wTotalLength; ++ uByte bNumEncryptionTypes; ++} UPACKED wusb_security_desc_t; ++ ++/* WUSB Encryption Type Descriptor */ ++typedef struct wusb_encrypt_type_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ ++ uByte bEncryptionType; ++#define WUETD_UNSECURE 0 ++#define WUETD_WIRED 1 ++#define WUETD_CCM_1 2 ++#define WUETD_RSA_1 3 ++ ++ uByte bEncryptionValue; ++ uByte bAuthKeyIndex; ++} UPACKED wusb_encrypt_type_desc_t; ++ ++/* WUSB Key Descriptor */ ++typedef struct wusb_key_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte tTKID[3]; ++ uByte bReserved; ++ uByte KeyData[1]; /* variable length */ ++} UPACKED wusb_key_desc_t; ++ ++/* WUSB BOS Descriptor (Binary device Object Store) */ ++typedef struct wusb_bos_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord wTotalLength; ++ uByte bNumDeviceCaps; ++} UPACKED wusb_bos_desc_t; ++ ++#define USB_DEVICE_CAPABILITY_20_EXTENSION 0x02 ++typedef struct usb_dev_cap_20_ext_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDevCapabilityType; ++#define USB_20_EXT_LPM 0x02 ++#define USB_20_EXT_BESL 0x04 ++#define USB_20_EXT_BASELINE_BESL_VALID 0x08 ++#define USB_20_EXT_DEEP_BESL_VALID 0x10 ++#define USB_20_EXT_BASELINE_BESL_BITS 0x00f00 ++#define USB_20_EXT_BASELINE_BESL_SHIFT 8 ++#define USB_20_EXT_DEEP_BESL_BITS 0x0f000 ++#define USB_20_EXT_DEEP_BESL_SHIFT 12 ++ uDWord bmAttributes; ++} UPACKED usb_dev_cap_20_ext_desc_t; ++ ++#define USB_DEVICE_CAPABILITY_SS_USB 0x03 ++typedef struct usb_dev_cap_ss_usb { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDevCapabilityType; ++#define USB_DC_SS_USB_LTM_CAPABLE 0x02 ++ uByte bmAttributes; ++#define USB_DC_SS_USB_SPEED_SUPPORT_LOW 0x01 ++#define USB_DC_SS_USB_SPEED_SUPPORT_FULL 0x02 ++#define USB_DC_SS_USB_SPEED_SUPPORT_HIGH 0x04 ++#define USB_DC_SS_USB_SPEED_SUPPORT_SS 0x08 ++ uWord wSpeedsSupported; ++ uByte bFunctionalitySupport; ++ uByte bU1DevExitLat; ++ uWord wU2DevExitLat; ++} UPACKED usb_dev_cap_ss_usb_t; ++ ++#define USB_DEVICE_CAPABILITY_CONTAINER_ID 0x04 ++typedef struct usb_dev_cap_container_id { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDevCapabilityType; ++ uByte bReserved; ++ uByte containerID[16]; ++} UPACKED usb_dev_cap_container_id_t; ++ ++/* Device Capability Type Codes */ ++#define WUSB_DEVICE_CAPABILITY_WIRELESS_USB 0x01 ++ ++/* Device Capability Descriptor */ ++typedef struct wusb_dev_cap_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDevCapabilityType; ++ uByte caps[1]; /* Variable length */ ++} UPACKED wusb_dev_cap_desc_t; ++ ++/* Device Capability Descriptor */ ++typedef struct wusb_dev_cap_uwb_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDevCapabilityType; ++ uByte bmAttributes; ++ uWord wPHYRates; /* Bitmap */ ++ uByte bmTFITXPowerInfo; ++ uByte bmFFITXPowerInfo; ++ uWord bmBandGroup; ++ uByte bReserved; ++} UPACKED wusb_dev_cap_uwb_desc_t; ++ ++/* Wireless USB Endpoint Companion Descriptor */ ++typedef struct wusb_endpoint_companion_desc { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bMaxBurst; ++ uByte bMaxSequence; ++ uWord wMaxStreamDelay; ++ uWord wOverTheAirPacketSize; ++ uByte bOverTheAirInterval; ++ uByte bmCompAttributes; ++} UPACKED wusb_endpoint_companion_desc_t; ++ ++/* Wireless USB Numeric Association M1 Data Structure */ ++typedef struct wusb_m1_data { ++ uByte version; ++ uWord langId; ++ uByte deviceFriendlyNameLength; ++ uByte sha_256_m3[32]; ++ uByte deviceFriendlyName[256]; ++} UPACKED wusb_m1_data_t; ++ ++typedef struct wusb_m2_data { ++ uByte version; ++ uWord langId; ++ uByte hostFriendlyNameLength; ++ uByte pkh[384]; ++ uByte hostFriendlyName[256]; ++} UPACKED wusb_m2_data_t; ++ ++typedef struct wusb_m3_data { ++ uByte pkd[384]; ++ uByte nd; ++} UPACKED wusb_m3_data_t; ++ ++typedef struct wusb_m4_data { ++ uDWord _attributeTypeIdAndLength_1; ++ uWord associationTypeId; ++ ++ uDWord _attributeTypeIdAndLength_2; ++ uWord associationSubTypeId; ++ ++ uDWord _attributeTypeIdAndLength_3; ++ uDWord length; ++ ++ uDWord _attributeTypeIdAndLength_4; ++ uDWord associationStatus; ++ ++ uDWord _attributeTypeIdAndLength_5; ++ uByte chid[16]; ++ ++ uDWord _attributeTypeIdAndLength_6; ++ uByte cdid[16]; ++ ++ uDWord _attributeTypeIdAndLength_7; ++ uByte bandGroups[2]; ++} UPACKED wusb_m4_data_t; ++ ++#ifdef _MSC_VER ++#include <poppack.h> ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _USB_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/ute_if.c b/drivers/usb/gadget/udc/hiudc3/ute_if.c +new file mode 100644 +index 0000000..0e00835 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/ute_if.c +@@ -0,0 +1,574 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/linux/ute_if.c $ ++ * $Revision: #11 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++#include "dwc_pcd_cfi.h" ++#include "ute_if.h" ++ ++static struct cfi_ft_lst pcd_feats = { ++ .hdr = { ++ //.totlen = (set at runtime), ++ .cfiver = 0x100, ++ .coreID = 4, ++ //.ftcount = (set at runtime), ++ }, ++ .desc[0] = { /* Rx FIFO size */ ++ .ftparm = { ++ .fid = UTE_FID_RXFIFO_SIZE, ++ .paramcount = 1, ++ .param1 = 0xffffffff, /* shared */ ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "rxfsz", ++ }, ++ .desc[1] = { /* Tx FIFO 0 size */ ++ .ftparm = { ++ .fid = UTE_FID_TXFIFO_SIZE, ++ .paramcount = 1, ++ .param1 = 0, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "txfsz0", ++ }, ++ .desc[2] = { /* Tx FIFO 1 size */ ++ .ftparm = { ++ .fid = UTE_FID_TXFIFO_SIZE, ++ .paramcount = 1, ++ .param1 = 1, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "txfsiz1", ++ }, ++ .desc[3] = { /* Tx FIFO 2 size */ ++ .ftparm = { ++ .fid = UTE_FID_TXFIFO_SIZE, ++ .paramcount = 1, ++ .param1 = 2, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "txfsz2", ++ }, ++ .desc[4] = { /* Tx FIFO 3 size */ ++ .ftparm = { ++ .fid = UTE_FID_TXFIFO_SIZE, ++ .paramcount = 1, ++ .param1 = 3, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "txfsz3", ++ }, ++ .desc[5] = { /* FIFO 0 physical EP mapping */ ++ .ftparm = { ++ .fid = UTE_FID_TXFIFO_MAP, ++ .paramcount = 1, ++ .param1 = 0, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "txfmap0", ++ }, ++ .desc[6] = { /* FIFO 1 physical EP mapping */ ++ .ftparm = { ++ .fid = UTE_FID_TXFIFO_MAP, ++ .paramcount = 1, ++ .param1 = 1, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "txfmap1", ++ }, ++ .desc[7] = { /* FIFO 2 physical EP mapping */ ++ .ftparm = { ++ .fid = UTE_FID_TXFIFO_MAP, ++ .paramcount = 1, ++ .param1 = 2, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "txfmap2", ++ }, ++ .desc[8] = { /* FIFO 3 physical EP mapping */ ++ .ftparm = { ++ .fid = UTE_FID_TXFIFO_MAP, ++ .paramcount = 1, ++ .param1 = 3, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "txfmap3", ++ }, ++ .desc[9] = { /* Physical OUT EP 1 USB EP mapping */ ++ .ftparm = { ++ .fid = UTE_FID_EP_MAP, ++ .paramcount = 1, ++ .param1 = 0x01, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "epmap1-out", ++ }, ++ .desc[10] = { /* Physical IN EP 1 USB EP mapping */ ++ .ftparm = { ++ .fid = UTE_FID_EP_MAP, ++ .paramcount = 1, ++ .param1 = 0x11, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "epmap1-in", ++ }, ++ .desc[11] = { /* Physical OUT EP 2 USB EP mapping */ ++ .ftparm = { ++ .fid = UTE_FID_EP_MAP, ++ .paramcount = 1, ++ .param1 = 0x02, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "epmap2-out", ++ }, ++ .desc[12] = { /* Physical IN EP 2 USB EP mapping */ ++ .ftparm = { ++ .fid = UTE_FID_EP_MAP, ++ .paramcount = 1, ++ .param1 = 0x12, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "epmap2-in", ++ }, ++ .desc[13] = { /* Physical OUT EP 3 USB EP mapping */ ++ .ftparm = { ++ .fid = UTE_FID_EP_MAP, ++ .paramcount = 1, ++ .param1 = 0x03, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "epmap3-out", ++ }, ++ .desc[14] = { /* Physical IN EP 3 USB EP mapping */ ++ .ftparm = { ++ .fid = UTE_FID_EP_MAP, ++ .paramcount = 1, ++ .param1 = 0x13, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "epmap3-in", ++ }, ++ .desc[15] = { /* Super Speed */ ++ .ftparm = { ++ .fid = UTE_FID_DEV_SPEED, ++ .paramcount = 1, ++ .param1 = 0xffffffff, ++ }, ++ //.fdlen = sizeof(struct cfi_feature_desc), ++ .dlen = 4, ++ .bmattr = FT_ATTR_RW, ++ .ftname = "dev speed", ++ }, ++}; ++ ++static void *cficb_get_ft_list(struct cfiobject *cfi) ++{ ++ return cfi->features; ++} ++ ++static int cficb_get_ft_value(struct cfiobject *cfi, ++ struct cfi_ft_params *params, ++ unsigned long valptr) ++{ ++ dwc_usb3_pcd_t *pcd; ++ dwc_usb3_device_t *dev; ++ dwc_usb3_core_global_regs_t __iomem *global_regs; ++ unsigned val, index, ram_width; ++ int ret = 0; ++ ++ printk(KERN_DEBUG USB3_DWC "%s(%p,%p,%1lx)\n", __func__, ++ cfi, params, valptr); ++ pcd = cfi->pcd; ++ printk(KERN_DEBUG USB3_DWC "pcd=%p\n", pcd); ++ dev = pcd->usb3_dev; ++ if (params) { ++ printk(KERN_DEBUG USB3_DWC "params->param1=%1x ->fid=%1d\n", ++ params->param1, params->fid); ++ } else { ++ printk(KERN_ERR USB3_DWC "params=NULL!\n"); ++ return -EINVAL; ++ } ++ if (!valptr) { ++ printk(KERN_ERR USB3_DWC "valptr=NULL!\n"); ++ return -EINVAL; ++ } ++ ++ switch (params->fid) { ++ case UTE_FID_RXFIFO_SIZE: ++ /* get Rx FIFO size */ ++ /* val is FIFO size in bytes */ ++ if (params->param1 != 0xffffffff) { ++ ret = -EINVAL; ++ } else { ++ global_regs = dev->core_global_regs; ++ val = dwc_rd32(dev, &global_regs->grxfifosiz[0]); ++ val = (val >> DWC_FIFOSZ_DEPTH_SHIFT) & ++ (DWC_FIFOSZ_DEPTH_BITS >> DWC_FIFOSZ_DEPTH_SHIFT); ++ ram_width = ++ ((dev->hwparams0 >> DWC_HWP0_MDWIDTH_SHIFT) & ++ (DWC_HWP0_MDWIDTH_BITS >> DWC_HWP0_MDWIDTH_SHIFT)) ++ / 8; ++ val *= ram_width; ++ *(unsigned *)valptr = val; ++ } ++ break; ++ ++ case UTE_FID_TXFIFO_SIZE: ++ /* get Tx FIFO size */ ++ /* param1 is Tx FIFO #, val is FIFO size in bytes */ ++ if (params->param1 >= DWC_MAX_TX_FIFOS) { ++ ret = -EINVAL; ++ } else { ++ global_regs = dev->core_global_regs; ++ val = dwc_rd32(dev, ++ &global_regs->gtxfifosiz[params->param1]); ++ val = (val >> DWC_FIFOSZ_DEPTH_SHIFT) & ++ (DWC_FIFOSZ_DEPTH_BITS >> DWC_FIFOSZ_DEPTH_SHIFT); ++ ram_width = ++ ((dev->hwparams0 >> DWC_HWP0_MDWIDTH_SHIFT) & ++ (DWC_HWP0_MDWIDTH_BITS >> DWC_HWP0_MDWIDTH_SHIFT)) ++ / 8; ++ val *= ram_width; ++ *(unsigned *)valptr = val; ++ } ++ break; ++ ++ case UTE_FID_RXFIFO_MAP: ++ /* Rx FIFO mapping not possible for USB3 core */ ++ ret = -EINVAL; ++ break; ++ ++ case UTE_FID_TXFIFO_MAP: ++ /* get Tx FIFO mapping */ ++ /* param1 is Tx FIFO #, val is physical EP # */ ++ if (params->param1 >= DWC_MAX_TX_FIFOS) { ++ ret = -EINVAL; ++ } else { ++ /* find matching FIFO # in EP array */ ++ for (index = 1; index < DWC_MAX_PHYS_EP; index += 2) { ++ if (pcd->txf_map[index] == params->param1) ++ break; ++ } ++ if (index >= DWC_MAX_PHYS_EP) { ++ ret = -EINVAL; ++ } else { ++ /* convert EP array index to physical EP # */ ++ /* Note: have to drop the direction bit, UTE ++ * doesn't expect it to be set ++ */ ++ val = (index >> 1) & 0xf; ++ *(unsigned *)valptr = val; ++ } ++ } ++ break; ++ ++ case UTE_FID_EP_MAP: ++ /* get EP mapping */ ++ /* param1 is physical EP #, val is USB EP # */ ++ index = (params->param1 & 0xf) << 1 | ++ ((params->param1 >> 4) & 0x1); ++ ret = dwc_usb3_get_usb_ep_map(0, index, &val); ++ if (!ret) ++ *(unsigned *)valptr = val; ++ break; ++ case UTE_FID_DEV_SPEED: ++ /* get current device speed */ ++ val = dwc_usb3_get_dev_speed(0); ++ *(unsigned *)valptr = val; ++ break; ++ ++ default: ++ ret = -EINVAL; ++ } ++ ++ if (ret == 0) ++ printk(KERN_DEBUG USB3_DWC "*valptr=%1x\n", ++ *(unsigned *)valptr); ++ return ret; ++} ++ ++static void reset_txfifosize_ft_value(struct cfiobject *cfi) ++{ ++ int i; ++ dwc_usb3_pcd_t *pcd = cfi->pcd; ++ ++ for (i = 0; i < DWC_MAX_TX_FIFOS; i++) { ++ pcd->txf_size[i] = pcd->def_txf_size[i]; ++ } ++} ++ ++static void reset_txfifomap_ft_value(struct cfiobject *cfi) ++{ ++ int i; ++ dwc_usb3_pcd_t *pcd = cfi->pcd; ++ ++ for (i = 0; i < pcd->num_in_eps + 1; i++) { ++ pcd->txf_map[(i << 1) | 1] = i; ++ } ++} ++ ++static int cficb_reset_ft_value(struct cfiobject *cfi, ++ struct cfi_ft_params *params) ++{ ++ int ret = 0; ++ dwc_usb3_pcd_t *pcd = cfi->pcd; ++ ++ printk(KERN_DEBUG USB3_DWC "%s(%p,%p)\n", __func__, cfi, params); ++ ++ if (params) { ++ printk(KERN_DEBUG USB3_DWC "params->param1=%1x ->fid=%1d\n", ++ params->param1, params->fid); ++ } else { ++ printk(KERN_ERR USB3_DWC "params=NULL!\n"); ++ return -EINVAL; ++ } ++ ++ switch (params->fid) { ++ case UTE_FID_EP_MAP: ++ if (params->param1 == 0) ++ ret = dwc_usb3_reset_usb_ep_map(0); ++ break; ++ case UTE_FID_RXFIFO_SIZE: ++ if (params->param1 == 0 && pcd->rxf_size != 0) { ++ pcd->rxf_size = pcd->def_rxf_size; ++ pcd->ute_change = 1; ++ } ++ break; ++ case UTE_FID_TXFIFO_SIZE: ++ if (params->param1 == 0) { ++ reset_txfifosize_ft_value(cfi); ++ pcd->ute_change = 1; ++ } ++ break; ++ case UTE_FID_TXFIFO_MAP: ++ if (params->param1) { ++ reset_txfifomap_ft_value(cfi); ++ pcd->ute_change = 1; ++ } ++ break; ++ default: ++ ret = -ENOTSUPP; ++ break; ++ } ++ ++ if (ret == 0) ++ pcd->ute_change = 1; ++ ++ return ret; ++} ++ ++ ++static int cficb_set_ft_value(struct cfiobject *cfi, ++ struct cfi_ft_params *params, ++ unsigned long valptr) ++{ ++ dwc_usb3_pcd_t *pcd; ++ unsigned val, index; ++ int ret = 0; ++ ++ printk(KERN_DEBUG USB3_DWC "%s(%p,%p,%1lx)\n", __func__, ++ cfi, params, valptr); ++ pcd = cfi->pcd; ++ printk(KERN_DEBUG USB3_DWC "pcd=%p\n", pcd); ++ if (params) { ++ printk(KERN_DEBUG USB3_DWC "params->param1=%1x ->fid=%1d\n", ++ params->param1, params->fid); ++ } else { ++ printk(KERN_ERR USB3_DWC "params=NULL!\n"); ++ return -EINVAL; ++ } ++ if (valptr) { ++ printk(KERN_DEBUG USB3_DWC "*valptr=%1x\n", ++ *(unsigned *)valptr); ++ } else { ++ printk(KERN_ERR USB3_DWC "valptr=NULL!\n"); ++ return -EINVAL; ++ } ++ ++ switch (params->fid) { ++ case UTE_FID_RXFIFO_SIZE: ++ /* set Rx FIFO size */ ++ /* val is FIFO size in bytes */ ++ val = *(unsigned *)valptr; ++ if (params->param1 != 0xffffffff) ++ ret = -EINVAL; ++ else ++ pcd->rxf_size = val; ++ break; ++ ++ case UTE_FID_TXFIFO_SIZE: ++ /* set Tx FIFO size */ ++ /* param1 is Tx FIFO #, val is FIFO size in bytes */ ++ val = *(unsigned *)valptr; ++ if (params->param1 >= DWC_MAX_TX_FIFOS) ++ ret = -EINVAL; ++ else ++ pcd->txf_size[params->param1] = val; ++ break; ++ ++ case UTE_FID_RXFIFO_MAP: ++ /* Rx FIFO mapping not possible for USB3 core */ ++ ret = -EINVAL; ++ break; ++ ++ case UTE_FID_TXFIFO_MAP: ++ /* set Tx FIFO mapping */ ++ /* param1 is Tx FIFO #, val is physical EP # */ ++ val = *(unsigned *)valptr; ++ /* convert physical EP # to EP array index */ ++ /* Note: have to force the direction bit to 1, UTE ++ * doesn't set it ++ */ ++ index = ((val & 0xf) << 1) | 1; ++ if (params->param1 >= DWC_MAX_TX_FIFOS || ++ index >= DWC_MAX_PHYS_EP) ++ ret = -EINVAL; ++ else ++ pcd->txf_map[index] = params->param1; ++ break; ++ ++ case UTE_FID_EP_MAP: ++ /* set EP mapping */ ++ /* param1 is physical EP #, val is USB EP # */ ++ val = *(unsigned *)valptr; ++ index = (params->param1 & 0xf) << 1 | ++ ((params->param1 >> 4) & 0x1); ++ ret = dwc_usb3_set_usb_ep_map(0, index, val); ++ break; ++ case UTE_FID_DEV_SPEED: ++ index = params->param1; ++ printk("UTE_FID_DEV_SPEED is received: new speed %d", index); ++ ret = dwc_usb3_switch_speed(0, index); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ if (ret == 0) { ++ pcd->ute_change = 1; ++ printk(KERN_DEBUG USB3_DWC "Set UTE change bit\n"); ++ } ++ ++ return ret; ++} ++ ++static struct cfiobject *cfi_create_object(dwc_usb3_pcd_t *pcd) ++{ ++ struct cfiobject *cfi; ++ ++ /* Set the ftcount field in the features header */ ++ pcd_feats.hdr.ftcount = 16; ++ ++ /* Calculate the total length of the feature descriptors for all ++ * features plus the features header descriptor length, and write it ++ * to the totlen field in the features header ++ */ ++ pcd_feats.hdr.totlen = sizeof(struct cfi_features_hdr) + ++ sizeof(struct cfi_feature_desc) * pcd_feats.hdr.ftcount; ++ ++ /* Allocate the cfi object */ ++ cfi = kmalloc(sizeof(*cfi), GFP_KERNEL); ++ if (!cfi) ++ return NULL; ++ memset(cfi, 0, sizeof(*cfi)); ++ ++ /* Assign the PCD pointer */ ++ cfi->pcd = pcd; ++ ++ /* Assign the features pointer */ ++ cfi->features = &pcd_feats; ++ ++ /* Assign the callbacks */ ++ cfi->ops.get_ft_list = cficb_get_ft_list; ++ cfi->ops.get_ft_value = cficb_get_ft_value; ++ cfi->ops.set_ft_value = cficb_set_ft_value; ++ cfi->ops.reset_ft_value = cficb_reset_ft_value; ++ ++ return cfi; ++} ++ ++static void *dwc_cfi_get_object(void) ++{ ++ struct cfiobject *cfi; ++ dwc_usb3_pcd_t *pcd; ++ ++ pcd = dwc_usb3_get_pcd_instance(0); ++ if (!pcd) ++ return NULL; ++ ++ cfi = cfi_create_object(pcd); ++ if (!cfi) ++ return NULL; ++ ++ return cfi; ++}; ++EXPORT_SYMBOL(dwc_cfi_get_object); +diff --git a/drivers/usb/gadget/udc/hiudc3/ute_if.h b/drivers/usb/gadget/udc/hiudc3/ute_if.h +new file mode 100644 +index 0000000..ec05269 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/ute_if.h +@@ -0,0 +1,60 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/linux/ute_if.h $ ++ * $Revision: #10 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef _DWC_UTE_IFC_H_ ++#define _DWC_UTE_IFC_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** @file ++ * ++ */ ++ ++extern dwc_usb3_pcd_t *dwc_usb3_get_pcd_instance(unsigned devnum); ++extern int dwc_usb3_set_usb_ep_map(unsigned devnum, unsigned phys_ep_num, ++ unsigned usb_ep_num); ++extern int dwc_usb3_get_usb_ep_map(unsigned devnum, unsigned phys_ep_num, ++ unsigned *usb_ep_num_ret); ++extern void dwc_usb3_ute_config(dwc_usb3_device_t *usb3_dev); ++ ++extern int dwc_usb3_reset_usb_ep_map(unsigned devnum); ++extern int dwc_usb3_switch_speed(unsigned devnum, int speed); ++extern int dwc_usb3_get_dev_speed(unsigned devnum); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_UTE_IFC_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/win/win_defs.h b/drivers/usb/gadget/udc/hiudc3/win/win_defs.h +new file mode 100644 +index 0000000..042614f +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/win/win_defs.h +@@ -0,0 +1,145 @@ ++#ifndef _DWC_WIN_DEFS_H_ ++#define _DWC_WIN_DEFS_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * @file ++ * ++ * This file contains OS-specific includes and definitions. ++ * ++ */ ++ ++#include <stdio.h> ++#include <stdlib.h> ++#include <errno.h> ++#include <string.h> ++#include <sys/types.h> ++ ++typedef unsigned long long u64, u_int64_t; ++typedef unsigned long u_long; ++typedef unsigned int u32, u_int32_t, u_int; ++typedef unsigned short u16, u_int16_t, u_short; ++typedef unsigned char u8, u_int8_t, u_char; ++ ++typedef long long s64; ++typedef int s32; ++typedef short s16; ++typedef char s8; ++ ++/** Type for DMA addresses */ ++typedef unsigned long dwc_dma_t; ++#define DWC_DMA_ADDR_INVALID (~(dwc_dma_t)0) ++ ++/** Compiler 'packed' attribute for structs */ ++#define UPACKED /* */ ++ ++#define __iomem /* */ ++#define __func__ __FUNCTION__ ++ ++#define KERN_DEBUG "" /* debug messages */ ++#define KERN_INFO "" /* informational messages */ ++#define KERN_WARNING "" /* warning messages */ ++#define KERN_ERR "" /* error messages */ ++ ++#define ESHUTDOWN 1001 ++ ++#include "dwc_list.h" ++ ++/** @name Error Codes */ ++#define DWC_E_INVALID EINVAL ++#define DWC_E_NO_MEMORY ENOMEM ++#define DWC_E_NO_DEVICE ENODEV ++#define DWC_E_NOT_SUPPORTED EOPNOTSUPP ++#define DWC_E_TIMEOUT ETIMEDOUT ++#define DWC_E_BUSY EBUSY ++#define DWC_E_AGAIN EAGAIN ++#define DWC_E_ABORT ECONNABORTED ++#define DWC_E_SHUTDOWN ESHUTDOWN ++#define DWC_E_NO_DATA ENODATA ++#define DWC_E_DISCONNECT ECONNRESET ++#define DWC_E_UNKNOWN EINVAL ++#define DWC_E_NO_STREAM_RES ENOSR ++#define DWC_E_COMMUNICATION ECOMM ++#define DWC_E_OVERFLOW EOVERFLOW ++#define DWC_E_PROTOCOL EPROTO ++#define DWC_E_IN_PROGRESS EINPROGRESS ++#define DWC_E_PIPE EPIPE ++#define DWC_E_IO EIO ++#define DWC_E_NO_SPACE ENOSPC ++#define DWC_E_DOMAIN EDOM ++ ++/** ++ * The number of DMA Descriptors (TRBs) to allocate for each endpoint type. ++ * NOTE: The driver currently supports more than 1 TRB for Isoc EPs only. ++ * So the values for Bulk and Intr must be 1. ++ */ ++#define DWC_NUM_BULK_TRBS 1 ++#define DWC_NUM_INTR_TRBS 1 ++#define DWC_NUM_ISOC_TRBS 32 ++ ++/** ++ * These parameters may be specified when loading the module. They define how ++ * the DWC_usb3 controller should be configured. The parameter values are passed ++ * to the CIL initialization routine dwc_usb3_pcd_common_init(). ++ */ ++typedef struct dwc_usb3_core_params { ++ int burst; ++ int newcore; ++ int phy; ++ int wakeup; ++ int pwrctl; ++ int lpmctl; ++ int phyctl; ++ int usb2mode; ++ int hibernate; ++ int hiberdisc; ++ int clkgatingen; ++ int ssdisquirk; ++ int nobos; ++ int loop; ++ int nump; ++ int newcsr; ++ int rxfsz; ++ int txfsz[16]; ++ int txfsz_cnt; ++ int baseline_besl; ++ int deep_besl; ++ int besl; ++ int ebc; ++} dwc_usb3_core_params_t; ++ ++#define wmb() do {} while (0) ++ ++#define interrupt_disable() 0 ++#define interrupt_enable() do {} while (0) ++ ++#define spin_lock_init(p) do {} while (0) ++#define spin_lock(p) do {} while (0) ++#define spin_unlock(p) do {} while (0) ++#define spin_lock_irqsave(p, f) do { (f) = interrupt_disable(); } while (0) ++#define spin_unlock_irqrestore(p, f) do { if (f) interrupt_enable(); } while (0) ++ ++struct task_struct { ++ int dummy; ++}; ++ ++struct tasklet_struct { ++ int dummy; ++}; ++ ++struct usb_ep { ++ int dummy; ++}; ++ ++struct usb_request { ++ int dummy; ++}; ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_WIN_DEFS_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/win/win_dev.h b/drivers/usb/gadget/udc/hiudc3/win/win_dev.h +new file mode 100644 +index 0000000..c20e5f2 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/win/win_dev.h +@@ -0,0 +1,183 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/DWC_usb3/driver/win/win_dev.h $ ++ * $Revision: #9 $ ++ * $Date: 2014/11/11 $ ++ * $Change: 2664766 $ ++ * ++ * Synopsys SS USB3 Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef _DWC_WIN_DEV_H_ ++#define _DWC_WIN_DEV_H_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** @file ++ */ ++ ++/** Wrapper function for _handshake() */ ++#define handshake(_dev_, _ptr_, _mask_, _done_) \ ++ _handshake(_dev_, _ptr_, _mask_, _done_) ++ ++/** Takes a usb req pointer and returns the associated pcd req pointer */ ++#define dwc_usb3_get_pcd_req(usbreq) \ ++ ((dwc_usb3_pcd_req_t *)((char *)(usbreq) - \ ++ offsetof(struct dwc_usb3_pcd_req, usb_req))) ++ ++/** Takes a usb ep pointer and returns the associated pcd ep pointer */ ++#define dwc_usb3_get_pcd_ep(usbep) \ ++ ((dwc_usb3_pcd_ep_t *)((char *)(usbep) - \ ++ offsetof(struct dwc_usb3_pcd_ep, usb_ep))) ++ ++/** ++ * Register read/write. ++ */ ++static __inline u32 dwc_rd32(struct dwc_usb3_device *dev, volatile u32 *adr) ++{ ++ return *adr; ++} ++ ++static __inline void dwc_wr32(struct dwc_usb3_device *dev, volatile u32 *adr, u32 val) ++{ ++ *adr = val; ++} ++ ++/** ++ * Non-sleeping delays. ++ */ ++#define dwc_udelay(dev, us) do {} while (0) ++#define dwc_mdelay(dev, ms) do {} while (0) ++ ++/** ++ * Sleeping delay. ++ */ ++#define dwc_msleep(dev, ms) do {} while (0) ++ ++/** ++ * Debugging support - vanishes in non-debug builds. ++ */ ++ ++/** Prefix string for print macros. */ ++#define USB3_DWC "dwc_usb3: " ++ ++//#ifdef DEBUG ++# define dwc_debug printf ++//#else ++//# define dwc_debug(dev, x) do {} while (0) ++//#endif /* DEBUG */ ++ ++# define dwc_debug0(dev, fmt) dwc_debug(fmt) ++# define dwc_debug1(dev, fmt, a) dwc_debug(fmt, a) ++# define dwc_debug2(dev, fmt, a, b) dwc_debug(fmt, a, b) ++# define dwc_debug3(dev, fmt, a, b, c) dwc_debug(fmt, a, b, c) ++# define dwc_debug4(dev, fmt, a, b, c, d) dwc_debug(fmt, a, b, c, d) ++# define dwc_debug5(dev, fmt, a, b, c, d, e) \ ++ dwc_debug(fmt, a, b, c, d, e) ++# define dwc_debug6(dev, fmt, a, b, c, d, e, f) \ ++ dwc_debug(fmt, a, b, c, d, e, f) ++# define dwc_debug7(dev, fmt, a, b, c, d, e, f, g) \ ++ dwc_debug(fmt, a, b, c, d, e, f, g) ++# define dwc_debug8(dev, fmt, a, b, c, d, e, f, g, h) \ ++ dwc_debug(fmt, a, b, c, d, e, f, g, h) ++# define dwc_debug9(dev, fmt, a, b, c, d, e, f, g, h, i) \ ++ dwc_debug(fmt, a, b, c, d, e, f, g, h, i) ++# define dwc_debug10(dev, fmt, a, b, c, d, e, f, g, h, i, j) \ ++ dwc_debug(fmt, a, b, c, d, e, f, g, h, i, j) ++ ++//#if defined(DEBUG) || defined(ISOC_DEBUG) ++# define dwc_isocdbg printf ++//#else ++//# define dwc_isocdbg(dev, x) do {} while (0) ++//#endif ++ ++# define dwc_isocdbg0(dev, fmt) dwc_isocdbg(fmt) ++# define dwc_isocdbg1(dev, fmt, a) dwc_isocdbg(fmt, a) ++# define dwc_isocdbg2(dev, fmt, a, b) dwc_isocdbg(fmt, a, b) ++# define dwc_isocdbg3(dev, fmt, a, b, c) dwc_isocdbg(fmt, a, b, c) ++# define dwc_isocdbg4(dev, fmt, a, b, c, d) \ ++ dwc_isocdbg(fmt, a, b, c, d) ++# define dwc_isocdbg5(dev, fmt, a, b, c, d, e) \ ++ dwc_isocdbg(fmt, a, b, c, d, e) ++# define dwc_isocdbg6(dev, fmt, a, b, c, d, e, f) \ ++ dwc_isocdbg(fmt, a, b, c, d, e, f) ++ ++/** ++ * Print an Error message. ++ */ ++#define dwc_error printf ++ ++#define dwc_error0(dev, fmt) dwc_error(fmt) ++#define dwc_error1(dev, fmt, a) dwc_error(fmt, a) ++#define dwc_error2(dev, fmt, a, b) dwc_error(fmt, a, b) ++#define dwc_error3(dev, fmt, a, b, c) dwc_error(fmt, a, b, c) ++#define dwc_error4(dev, fmt, a, b, c, d) dwc_error(fmt, a, b, c, d) ++ ++/** ++ * Print a Warning message. ++ */ ++#define dwc_warn printf ++ ++#define dwc_warn0(dev, fmt) dwc_warn(fmt) ++#define dwc_warn1(dev, fmt, a) dwc_warn(fmt, a) ++#define dwc_warn2(dev, fmt, a, b) dwc_warn(fmt, a, b) ++#define dwc_warn3(dev, fmt, a, b, c) dwc_warn(fmt, a, b, c) ++#define dwc_warn4(dev, fmt, a, b, c, d) dwc_warn(fmt, a, b, c, d) ++ ++/** ++ * Print an Informational message (normal but significant). ++ */ ++#define dwc_info printf ++ ++#define dwc_info0(dev, fmt) dwc_info(fmt) ++#define dwc_info1(dev, fmt, a) dwc_info(fmt, a) ++#define dwc_info2(dev, fmt, a, b) dwc_info(fmt, a, b) ++#define dwc_info3(dev, fmt, a, b, c) dwc_info(fmt, a, b, c) ++#define dwc_info4(dev, fmt, a, b, c, d) dwc_info(fmt, a, b, c, d) ++ ++/** ++ * Basic message printing. ++ */ ++#define dwc_print printf ++ ++#define dwc_print0(dev, fmt) dwc_print(fmt) ++#define dwc_print1(dev, fmt, a) dwc_print(fmt, a) ++#define dwc_print2(dev, fmt, a, b) dwc_print(fmt, a, b) ++#define dwc_print3(dev, fmt, a, b, c) dwc_print(fmt, a, b, c) ++#define dwc_print4(dev, fmt, a, b, c, d) dwc_print(fmt, a, b, c, d) ++#define dwc_print5(dev, fmt, a, b, c, d, e) \ ++ dwc_print(fmt, a, b, c, d, e) ++ ++extern int dwc_usb3_gadget_init(struct dwc_usb3_device *usb3_dev, struct device *dev); ++extern void dwc_usb3_gadget_remove(struct dwc_usb3_device *usb3_dev, struct device *dev); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _DWC_WIN_DEV_H_ */ +diff --git a/drivers/usb/gadget/udc/hiudc3/win/win_gadget.c b/drivers/usb/gadget/udc/hiudc3/win/win_gadget.c +new file mode 100644 +index 0000000..a15da82 +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/win/win_gadget.c +@@ -0,0 +1,205 @@ ++/** @file ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++int main(int argc, char *argv[]) ++{ ++ return 0; ++} ++ ++/****************************************************************************** ++ * Function driver notification routines ++ * ++ * These routines receive notifications from the PCD when certain events ++ * occur which the Function driver may need to be aware of. ++ * ++ * These routines *must* have the exact names and parameters shown here, ++ * because they are part of the API between the PCD and the Function driver. ++ ******************************************************************************/ ++ ++/** ++ * This routine receives Connect notifications from the PCD ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param speed Speed of the connection (as defined in usb.h) ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_connect(dwc_usb3_pcd_t *pcd, int speed) ++{ ++ return 0; ++} ++ ++/** ++ * This routine receives Disconnect notifications from the PCD ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_disconnect(dwc_usb3_pcd_t *pcd) ++{ ++ return 0; ++} ++ ++/** ++ * This routine receives Setup request notifications from the PCD ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param ctrl Pointer to the Setup packet for the request. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_setup(dwc_usb3_pcd_t *pcd, usb_device_request_t *ctrl) ++{ ++ return 0; ++} ++ ++/** ++ * This routine receives Suspend notifications from the PCD ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_suspend(dwc_usb3_pcd_t *pcd) ++{ ++ return 0; ++} ++ ++/** ++ * This routine receives Resume notifications from the PCD ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_resume(dwc_usb3_pcd_t *pcd) ++{ ++ return 0; ++} ++ ++/** ++ * This routine receives Transfer Complete notifications from the PCD ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param pcd_ep PCD EP for the transfer. ++ * @param pcd_req PCD request for the transfer. ++ * @param status Transfer status, 0 for success else negative error code. ++ * @return 0 for success, else negative error code. ++ */ ++int dwc_usb3_gadget_complete(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep, ++ dwc_usb3_pcd_req_t *pcd_req, int status) ++{ ++ return 0; ++} ++ ++/** ++ * This routine allows overriding the standard Control transfer handling ++ * (currently only done by the axs101 test gadget) ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ */ ++void dwc_usb3_gadget_do_setup(dwc_usb3_pcd_t *pcd) ++{ ++ /* TODO */ ++} ++ ++/****************************************************************************** ++ * Function driver callback routines ++ * ++ * The PCD calls these routines when it needs something from the Function ++ * driver. ++ * ++ * These routines *must* have the exact names and parameters shown here, ++ * because they are part of the API between the PCD and the Function driver. ++ ******************************************************************************/ ++ ++/** ++ * This routine allocates coherent DMA memory. It is used by the PCD to ++ * allocate memory for TRBs. The block of memory returned must have a start ++ * address aligned to a 16-byte boundary. ++ * ++ * @param pcd_ep PCD EP that memory block will be associated with. ++ * @param size Size of memory block to allocate, in bytes. ++ * @param mem_dma_ret Physical address of allocated memory block is returned ++ * through this pointer. ++ * @return Address of allocated memory block, or NULL if allocation ++ * fails. ++ */ ++void *dwc_usb3_gadget_alloc_dma(dwc_usb3_pcd_ep_t *pcd_ep, int size, dwc_dma_t *mem_dma_ret) ++{ ++ return NULL; ++} ++ ++/** ++ * This routine frees DMA memory allocated by dwc_usb3_gadget_alloc_dma(). ++ * ++ * @param pcd_ep PCD EP that memory block is associated with. ++ * @param size Size of memory block to free, in bytes. ++ * @param mem Address of memory block. ++ * @param mem_dma Physical address of memory block. ++ */ ++void dwc_usb3_gadget_free_dma(dwc_usb3_pcd_ep_t *pcd_ep, int size, void *mem, dwc_dma_t mem_dma) ++{ ++} ++ ++/** ++ * This routine returns the PCD request corresponding to the current transfer ++ * request for an endpoint. The current transfer request is the first request ++ * submitted that has not been completed yet. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param pcd_ep PCD EP to operate on. ++ * @return Pointer to PCD request, or NULL if no request available. ++ */ ++dwc_usb3_pcd_req_t *dwc_usb3_gadget_get_request(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep) ++{ ++ return NULL; ++} ++ ++/** ++ * This routine checks to see if there is another transfer request waiting ++ * on an endpoint that has not been started yet. If so then that transfer is ++ * started. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param pcd_ep PCD EP to operate on. ++ */ ++void dwc_usb3_gadget_start_next_request(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep) ++{ ++} ++ ++/** ++ * This routine terminates all requests which are pending on an endpoint. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param pcd_ep EP to operate on. ++ */ ++void dwc_usb3_gadget_request_nuke(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep) ++{ ++} ++ ++/** ++ * This routine marks all pending requests for an EP as not started. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param pcd_ep EP to operate on. ++ */ ++void dwc_usb3_gadget_set_ep_not_started(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep) ++{ ++} ++ ++/** ++ * Start an Isoc EP running at the proper interval, after receiving the initial ++ * XferNrdy event. ++ * ++ * @param pcd Programming view of DWC_usb3 peripheral controller. ++ * @param pcd_ep EP to operate on. ++ * @param event Event data containing the XferNrdy microframe. ++ */ ++void dwc_usb3_gadget_isoc_ep_start(dwc_usb3_pcd_t *pcd, dwc_usb3_pcd_ep_t *pcd_ep, u32 event) ++{ ++} +diff --git a/drivers/usb/gadget/udc/hiudc3/win/win_hiber.c b/drivers/usb/gadget/udc/hiudc3/win/win_hiber.c +new file mode 100644 +index 0000000..303f08c +--- /dev/null ++++ b/drivers/usb/gadget/udc/hiudc3/win/win_hiber.c +@@ -0,0 +1,35 @@ ++/** @file ++ */ ++ ++#include "os_defs.h" ++#include "hw.h" ++#include "usb.h" ++#include "pcd.h" ++#include "dev.h" ++#include "os_dev.h" ++#include "cil.h" ++ ++void dwc_usb3_task_schedule(struct tasklet_struct *tasklet) ++{ ++} ++ ++/** ++ * Helper routine for dwc_wait_pme_thread() ++ */ ++static void dwc_wait_for_link_up(dwc_usb3_pcd_t *pcd) ++{ ++} ++ ++int dwc_wait_pme_thread(void *data) ++{ ++ return 0; ++} ++ ++int dwc_usb3_handle_pme_intr(dwc_usb3_device_t *dev) ++{ ++ return 0; ++} ++ ++void dwc_usb3_power_ctl(dwc_usb3_device_t *dev, int on) ++{ ++} +diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c +index f205465..382d8b6 100644 +--- a/drivers/usb/gadget/udc/udc-core.c ++++ b/drivers/usb/gadget/udc/udc-core.c +@@ -400,7 +400,15 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri + driver->unbind(udc->gadget); + goto err1; + } +- usb_gadget_connect(udc->gadget); ++ /* ++ * HACK: The Android gadget driver disconnects the gadget ++ * on bind and expects the gadget to stay disconnected until ++ * it calls usb_gadget_connect when userspace is ready. Remove ++ * the call to usb_gadget_connect bellow to avoid enabling the ++ * pullup before userspace is ready. ++ * ++ * usb_gadget_connect(udc->gadget); ++ */ + + kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); + return 0; +diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig +index a3ca137..aa0fd91 100644 +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -50,6 +50,14 @@ config USB_XHCI_RCAR + Say 'Y' to enable the support for the xHCI host controller + found in Renesas R-Car ARM SoCs. + ++config USB_XHCI_HISILICON ++ tristate "xHCI support for Hisilicon SoCs" ++ select USB_XHCI_PLATFORM ++ ---help--- ++ Say 'Y' to enable the support for the xHCI host controller ++ found in Hisilicon ARM SOCs. When you select the option, usb ++ driver can be loaded. ++ + endif # USB_XHCI_HCD + + config USB_EHCI_HCD +diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c +index 3df32fa..9dadcba 100644 +--- a/drivers/usb/host/ehci-hcd.c ++++ b/drivers/usb/host/ehci-hcd.c +@@ -109,6 +109,9 @@ MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications"); + #include "ehci.h" + #include "pci-quirks.h" + ++#ifdef CONFIG_ARCH_HI3516CV300 ++void usb2_low_power(int); ++#endif + static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE], + struct ehci_tt *tt); + +@@ -777,7 +780,9 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) + continue; + pstatus = ehci_readl(ehci, + &ehci->regs->port_status[i]); +- ++#ifdef CONFIG_ARCH_HI3516CV300 ++ usb2_low_power(pstatus); ++#endif + if (pstatus & PORT_OWNER) + continue; + if (!(test_bit(i, &ehci->suspended_ports) && +diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c +index ecd5d6a..bf45624 100644 +--- a/drivers/usb/host/ehci-hub.c ++++ b/drivers/usb/host/ehci-hub.c +@@ -30,7 +30,21 @@ + #include <linux/usb/otg.h> + + #define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) ++#if defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) \ ++ || defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) \ ++ || defined(CONFIG_ARCH_HI3516AV200) ++#define BASE_REG 0x120100b4 ++#define USBPHY_PORT0_TREQ (0x1 << 2) ++#endif ++#ifdef CONFIG_ARCH_HI3531D ++#define BASE_REG 0x12040000 ++#define USBPHY_PORT0_TREQ 0x0134 ++#endif + ++#ifdef CONFIG_ARCH_HI3516CV300 ++#define BASE_REG 0x120100b8 ++#define USBPHY_PORT0_TREQ (0x1 << 14) ++#endif + #ifdef CONFIG_PM + + static int persist_enabled_on_companion(struct usb_device *udev, void *unused) +@@ -1219,7 +1233,32 @@ int ehci_hub_control( + ehci->reset_done [wIndex] = jiffies + + msecs_to_jiffies (50); + } ++ ++ if (!(temp & PORT_CONNECT) && !PORT_USB11 (temp)) ++ mdelay(100); ++ ++#if defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556)\ ++ || defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3516CV300) || defined(CONFIG_ARCH_HI3516AV200) \ ++ || defined(CONFIG_ARCH_HI3531D) ++ if (ehci_readl(ehci, status_reg) == 0x1005) { ++ unsigned int reg, reg1; ++ void __iomem *base_reg; ++ ++ base_reg = ioremap_nocache(BASE_REG, 0x4); ++ ++ ehci_writel(ehci, temp, status_reg); ++ reg = reg1 = ehci_readl(ehci, base_reg); ++ reg1 |= USBPHY_PORT0_TREQ; ++ ehci_writel(ehci, reg1, base_reg); ++ ehci_writel(ehci, reg, base_reg); ++ ++ iounmap(base_reg); ++ } else ++ ehci_writel(ehci, temp, status_reg); ++ ++#else + ehci_writel(ehci, temp, status_reg); ++#endif + break; + + /* For downstream facing ports (these): one hub port is put +diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c +index 2f5b9ce..2ea6968 100644 +--- a/drivers/usb/host/ehci-platform.c ++++ b/drivers/usb/host/ehci-platform.c +@@ -43,7 +43,8 @@ + struct ehci_platform_priv { + struct clk *clks[EHCI_MAX_CLKS]; + struct reset_control *rst; +- struct phy *phy; ++ struct phy **phys; ++ int num_phys; + }; + + static const char hcd_name[] = "ehci-platform"; +@@ -78,7 +79,7 @@ static int ehci_platform_power_on(struct platform_device *dev) + { + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); +- int clk, ret; ++ int clk, ret, phy_num; + + for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { + ret = clk_prepare_enable(priv->clks[clk]); +@@ -86,20 +87,24 @@ static int ehci_platform_power_on(struct platform_device *dev) + goto err_disable_clks; + } + +- if (priv->phy) { +- ret = phy_init(priv->phy); +- if (ret) +- goto err_disable_clks; +- +- ret = phy_power_on(priv->phy); ++ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { ++ ret = phy_init(priv->phys[phy_num]); + if (ret) + goto err_exit_phy; ++ ret = phy_power_on(priv->phys[phy_num]); ++ if (ret) { ++ phy_exit(priv->phys[phy_num]); ++ goto err_exit_phy; ++ } + } + + return 0; + + err_exit_phy: +- phy_exit(priv->phy); ++ while (--phy_num >= 0) { ++ phy_power_off(priv->phys[phy_num]); ++ phy_exit(priv->phys[phy_num]); ++ } + err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(priv->clks[clk]); +@@ -111,11 +116,11 @@ static void ehci_platform_power_off(struct platform_device *dev) + { + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); +- int clk; ++ int clk, phy_num; + +- if (priv->phy) { +- phy_power_off(priv->phy); +- phy_exit(priv->phy); ++ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { ++ phy_power_off(priv->phys[phy_num]); ++ phy_exit(priv->phys[phy_num]); + } + + for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) +@@ -143,7 +148,7 @@ static int ehci_platform_probe(struct platform_device *dev) + struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ehci_platform_priv *priv; + struct ehci_hcd *ehci; +- int err, irq, clk = 0; ++ int err, irq, phy_num, clk = 0; + + if (usb_disabled()) + return -ENODEV; +@@ -190,12 +195,24 @@ static int ehci_platform_probe(struct platform_device *dev) + if (of_property_read_bool(dev->dev.of_node, "big-endian")) + ehci->big_endian_mmio = ehci->big_endian_desc = 1; + +- priv->phy = devm_phy_get(&dev->dev, "usb"); +- if (IS_ERR(priv->phy)) { +- err = PTR_ERR(priv->phy); +- if (err == -EPROBE_DEFER) +- goto err_put_hcd; +- priv->phy = NULL; ++ priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, ++ "phys", "#phy-cells"); ++ ++ if (priv->num_phys > 0) { ++ priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, ++ sizeof(struct phy *), GFP_KERNEL); ++ if (!priv->phys) ++ return -ENOMEM; ++ } else ++ priv->num_phys = 0; ++ ++ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { ++ priv->phys[phy_num] = devm_of_phy_get_by_index( ++ &dev->dev, dev->dev.of_node, phy_num); ++ if (IS_ERR(priv->phys[phy_num])) { ++ err = PTR_ERR(priv->phys[phy_num]); ++ goto err_put_hcd; ++ } + } + + for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { +@@ -311,7 +328,7 @@ static int ehci_platform_remove(struct platform_device *dev) + return 0; + } + +-#ifdef CONFIG_PM ++#ifdef CONFIG_PM_SLEEP + + static int ehci_platform_suspend(struct device *dev) + { +@@ -349,10 +366,7 @@ static int ehci_platform_resume(struct device *dev) + return 0; + } + +-#else /* !CONFIG_PM */ +-#define ehci_platform_suspend NULL +-#define ehci_platform_resume NULL +-#endif /* CONFIG_PM */ ++#endif /* CONFIG_PM_SLEEP */ + + static const struct of_device_id vt8500_ehci_ids[] = { + { .compatible = "via,vt8500-ehci", }, +@@ -368,10 +382,8 @@ static const struct platform_device_id ehci_platform_table[] = { + }; + MODULE_DEVICE_TABLE(platform, ehci_platform_table); + +-static const struct dev_pm_ops ehci_platform_pm_ops = { +- .suspend = ehci_platform_suspend, +- .resume = ehci_platform_resume, +-}; ++static SIMPLE_DEV_PM_OPS(ehci_platform_pm_ops, ehci_platform_suspend, ++ ehci_platform_resume); + + static struct platform_driver ehci_platform_driver = { + .id_table = ehci_platform_table, +diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c +index d664eda..098de14 100644 +--- a/drivers/usb/host/ohci-hcd.c ++++ b/drivers/usb/host/ohci-hcd.c +@@ -97,7 +97,9 @@ static void io_watchdog_func(unsigned long _ohci); + #define IRQ_NOTMINE IRQ_NONE + #endif + +- ++#ifdef CONFIG_ARCH_HI3516CV300 ++void usb2_low_power(int); ++#endif + /* Some boards misreport power switching/overcurrent */ + static bool distrust_firmware = 1; + module_param (distrust_firmware, bool, 0); +@@ -231,7 +233,6 @@ static int ohci_urb_enqueue ( + /* Start up the I/O watchdog timer, if it's not running */ + if (!timer_pending(&ohci->io_watchdog) && + list_empty(&ohci->eds_in_use)) { +- ohci->prev_frame_no = ohci_frame_no(ohci); + mod_timer(&ohci->io_watchdog, + jiffies + IO_WATCHDOG_DELAY); + } +@@ -729,7 +730,6 @@ static void io_watchdog_func(unsigned long _ohci) + u32 head; + struct ed *ed; + struct td *td, *td_start, *td_next; +- unsigned frame_no; + unsigned long flags; + + spin_lock_irqsave(&ohci->lock, flags); +@@ -745,7 +745,6 @@ static void io_watchdog_func(unsigned long _ohci) + if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) { + if (ohci->prev_donehead) { + ohci_err(ohci, "HcDoneHead not written back; disabled\n"); +- died: + usb_hc_died(ohci_to_hcd(ohci)); + ohci_dump(ohci); + ohci_shutdown(ohci_to_hcd(ohci)); +@@ -806,35 +805,7 @@ static void io_watchdog_func(unsigned long _ohci) + ohci_work(ohci); + + if (ohci->rh_state == OHCI_RH_RUNNING) { +- +- /* +- * Sometimes a controller just stops working. We can tell +- * by checking that the frame counter has advanced since +- * the last time we ran. +- * +- * But be careful: Some controllers violate the spec by +- * stopping their frame counter when no ports are active. +- */ +- frame_no = ohci_frame_no(ohci); +- if (frame_no == ohci->prev_frame_no) { +- int active_cnt = 0; +- int i; +- unsigned tmp; +- +- for (i = 0; i < ohci->num_ports; ++i) { +- tmp = roothub_portstatus(ohci, i); +- /* Enabled and not suspended? */ +- if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS)) +- ++active_cnt; +- } +- +- if (active_cnt > 0) { +- ohci_err(ohci, "frame counter not updating; disabled\n"); +- goto died; +- } +- } + if (!list_empty(&ohci->eds_in_use)) { +- ohci->prev_frame_no = frame_no; + ohci->prev_wdh_cnt = ohci->wdh_cnt; + ohci->prev_donehead = ohci_readl(ohci, + &ohci->regs->donehead); +@@ -900,6 +871,16 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) + } + + if (ints & OHCI_INTR_RHSC) { ++#ifdef CONFIG_ARCH_HI3516CV300 ++ unsigned i = ohci->num_ports; ++ ++ while (i--) { ++ int pstatus; ++ ++ pstatus = roothub_portstatus (ohci, i); ++ usb2_low_power(pstatus); ++ } ++#endif + ohci_dbg(ohci, "rhsc\n"); + ohci->next_statechange = jiffies + STATECHANGE_DELAY; + ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC, +diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c +index 4369299..00a6b0f 100644 +--- a/drivers/usb/host/ohci-platform.c ++++ b/drivers/usb/host/ohci-platform.c +@@ -38,7 +38,8 @@ + struct ohci_platform_priv { + struct clk *clks[OHCI_MAX_CLKS]; + struct reset_control *rst; +- struct phy *phy; ++ struct phy **phys; ++ int num_phys; + }; + + static const char hcd_name[] = "ohci-platform"; +@@ -61,7 +62,7 @@ static int ohci_platform_power_on(struct platform_device *dev) + { + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); +- int clk, ret; ++ int clk, ret, phy_num; + + for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) { + ret = clk_prepare_enable(priv->clks[clk]); +@@ -69,20 +70,24 @@ static int ohci_platform_power_on(struct platform_device *dev) + goto err_disable_clks; + } + +- if (priv->phy) { +- ret = phy_init(priv->phy); +- if (ret) +- goto err_disable_clks; +- +- ret = phy_power_on(priv->phy); ++ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { ++ ret = phy_init(priv->phys[phy_num]); + if (ret) + goto err_exit_phy; ++ ret = phy_power_on(priv->phys[phy_num]); ++ if (ret) { ++ phy_exit(priv->phys[phy_num]); ++ goto err_exit_phy; ++ } + } + + return 0; + + err_exit_phy: +- phy_exit(priv->phy); ++ while (--phy_num >= 0) { ++ phy_power_off(priv->phys[phy_num]); ++ phy_exit(priv->phys[phy_num]); ++ } + err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(priv->clks[clk]); +@@ -94,11 +99,11 @@ static void ohci_platform_power_off(struct platform_device *dev) + { + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); +- int clk; ++ int clk, phy_num; + +- if (priv->phy) { +- phy_power_off(priv->phy); +- phy_exit(priv->phy); ++ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { ++ phy_power_off(priv->phys[phy_num]); ++ phy_exit(priv->phys[phy_num]); + } + + for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) +@@ -127,7 +132,7 @@ static int ohci_platform_probe(struct platform_device *dev) + struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); + struct ohci_platform_priv *priv; + struct ohci_hcd *ohci; +- int err, irq, clk = 0; ++ int err, irq, phy_num, clk = 0; + + if (usb_disabled()) + return -ENODEV; +@@ -175,12 +180,24 @@ static int ohci_platform_probe(struct platform_device *dev) + if (of_property_read_bool(dev->dev.of_node, "big-endian")) + ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC; + +- priv->phy = devm_phy_get(&dev->dev, "usb"); +- if (IS_ERR(priv->phy)) { +- err = PTR_ERR(priv->phy); +- if (err == -EPROBE_DEFER) ++ priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, ++ "phys", "#phy-cells"); ++ ++ if (priv->num_phys > 0) { ++ priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, ++ sizeof(struct phy *), GFP_KERNEL); ++ if (!priv->phys) ++ return -ENOMEM; ++ } else ++ priv->num_phys = 0; ++ ++ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { ++ priv->phys[phy_num] = devm_of_phy_get_by_index( ++ &dev->dev, dev->dev.of_node, phy_num); ++ if (IS_ERR(priv->phys[phy_num])) { ++ err = PTR_ERR(priv->phys[phy_num]); + goto err_put_hcd; +- priv->phy = NULL; ++ } + } + + for (clk = 0; clk < OHCI_MAX_CLKS; clk++) { +@@ -298,7 +315,7 @@ static int ohci_platform_remove(struct platform_device *dev) + return 0; + } + +-#ifdef CONFIG_PM ++#ifdef CONFIG_PM_SLEEP + + static int ohci_platform_suspend(struct device *dev) + { +@@ -335,11 +352,7 @@ static int ohci_platform_resume(struct device *dev) + ohci_resume(hcd, false); + return 0; + } +- +-#else /* !CONFIG_PM */ +-#define ohci_platform_suspend NULL +-#define ohci_platform_resume NULL +-#endif /* CONFIG_PM */ ++#endif /* CONFIG_PM_SLEEP */ + + static const struct of_device_id ohci_platform_ids[] = { + { .compatible = "generic-ohci", }, +@@ -353,10 +366,8 @@ static const struct platform_device_id ohci_platform_table[] = { + }; + MODULE_DEVICE_TABLE(platform, ohci_platform_table); + +-static const struct dev_pm_ops ohci_platform_pm_ops = { +- .suspend = ohci_platform_suspend, +- .resume = ohci_platform_resume, +-}; ++static SIMPLE_DEV_PM_OPS(ohci_platform_pm_ops, ohci_platform_suspend, ++ ohci_platform_resume); + + static struct platform_driver ohci_platform_driver = { + .id_table = ohci_platform_table, +diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h +index 59f4245..0548f5c 100644 +--- a/drivers/usb/host/ohci.h ++++ b/drivers/usb/host/ohci.h +@@ -421,7 +421,6 @@ struct ohci_hcd { + + // there are also chip quirks/bugs in init logic + +- unsigned prev_frame_no; + unsigned wdh_cnt, prev_wdh_cnt; + u32 prev_donehead; + struct timer_list io_watchdog; +diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h +index 54f386f..8a6354b 100644 +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -33,6 +33,19 @@ + #include "xhci-ext-caps.h" + #include "pci-quirks.h" + ++#if defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3516AV200) ++#ifdef readl ++#undef readl ++#undef readl_relaxed ++#undef writel ++#undef writel_relaxed ++#define readl hi_readl ++#define readl_relaxed hi_readl_relaxed ++#define writel hi_writel ++#define writel_relaxed hi_writel_relaxed ++#endif /* readl */ ++#endif /* defined(CONFIG_ARCH_HI3519) || defined(CONFIG_ARCH_HI3519V101) || defined(CONFIG_ARCH_HI3516AV200) */ ++ + /* xHCI PCI Configuration Registers */ + #define XHCI_SBRN_OFFSET (0x60) + +diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig +index 0cd1f44..77ef4bb 100644 +--- a/drivers/usb/phy/Kconfig ++++ b/drivers/usb/phy/Kconfig +@@ -6,6 +6,14 @@ menu "USB Physical Layer drivers" + config USB_PHY + def_bool n + ++config USB_OTG_WAKELOCK ++ bool "Hold a wakelock when USB connected" ++ depends on WAKELOCK ++ select USB_OTG_UTILS ++ help ++ Select this to automatically hold a wakelock when USB is ++ connected, preventing suspend. ++ + # + # USB Transceiver Drivers + # +@@ -213,4 +221,13 @@ config USB_ULPI_VIEWPORT + Provides read/write operations to the ULPI phy register set for + controllers with a viewport register (e.g. Chipidea/ARC controllers). + ++config DUAL_ROLE_USB_INTF ++ bool "Generic DUAL ROLE sysfs interface" ++ depends on SYSFS && USB_PHY ++ help ++ A generic sysfs interface to track and change the state of ++ dual role usb phys. The usb phy drivers can register to ++ this interface to expose it capabilities to the userspace ++ and thereby allowing userspace to change the port mode. ++ + endmenu +diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile +index 75f2bba..5cd7871 100644 +--- a/drivers/usb/phy/Makefile ++++ b/drivers/usb/phy/Makefile +@@ -3,6 +3,8 @@ + # + obj-$(CONFIG_USB_PHY) += phy.o + obj-$(CONFIG_OF) += of.o ++obj-$(CONFIG_USB_OTG_WAKELOCK) += otg-wakelock.o ++obj-$(CONFIG_DUAL_ROLE_USB_INTF) += class-dual-role.o + + # transceiver drivers, keep the list sorted + +diff --git a/drivers/usb/phy/class-dual-role.c b/drivers/usb/phy/class-dual-role.c +new file mode 100644 +index 0000000..51fcb54 +--- /dev/null ++++ b/drivers/usb/phy/class-dual-role.c +@@ -0,0 +1,529 @@ ++/* ++ * class-dual-role.c ++ * ++ * Copyright (C) 2015 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/ctype.h> ++#include <linux/device.h> ++#include <linux/usb/class-dual-role.h> ++#include <linux/err.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/stat.h> ++#include <linux/types.h> ++ ++#define DUAL_ROLE_NOTIFICATION_TIMEOUT 2000 ++ ++static ssize_t dual_role_store_property(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count); ++static ssize_t dual_role_show_property(struct device *dev, ++ struct device_attribute *attr, ++ char *buf); ++ ++#define DUAL_ROLE_ATTR(_name) \ ++{ \ ++ .attr = { .name = #_name }, \ ++ .show = dual_role_show_property, \ ++ .store = dual_role_store_property, \ ++} ++ ++static struct device_attribute dual_role_attrs[] = { ++ DUAL_ROLE_ATTR(supported_modes), ++ DUAL_ROLE_ATTR(mode), ++ DUAL_ROLE_ATTR(power_role), ++ DUAL_ROLE_ATTR(data_role), ++ DUAL_ROLE_ATTR(powers_vconn), ++}; ++ ++struct class *dual_role_class; ++EXPORT_SYMBOL_GPL(dual_role_class); ++ ++static struct device_type dual_role_dev_type; ++ ++static char *kstrdupcase(const char *str, gfp_t gfp, bool to_upper) ++{ ++ char *ret, *ustr; ++ ++ ustr = ret = kmalloc(strlen(str) + 1, gfp); ++ ++ if (!ret) ++ return NULL; ++ ++ while (*str) ++ *ustr++ = to_upper ? toupper(*str++) : tolower(*str++); ++ ++ *ustr = 0; ++ ++ return ret; ++} ++ ++static void dual_role_changed_work(struct work_struct *work) ++{ ++ struct dual_role_phy_instance *dual_role = ++ container_of(work, struct dual_role_phy_instance, ++ changed_work); ++ ++ dev_dbg(&dual_role->dev, "%s\n", __func__); ++ kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE); ++} ++ ++void dual_role_instance_changed(struct dual_role_phy_instance *dual_role) ++{ ++ dev_dbg(&dual_role->dev, "%s\n", __func__); ++ pm_wakeup_event(&dual_role->dev, DUAL_ROLE_NOTIFICATION_TIMEOUT); ++ schedule_work(&dual_role->changed_work); ++} ++EXPORT_SYMBOL_GPL(dual_role_instance_changed); ++ ++int dual_role_get_property(struct dual_role_phy_instance *dual_role, ++ enum dual_role_property prop, ++ unsigned int *val) ++{ ++ return dual_role->desc->get_property(dual_role, prop, val); ++} ++EXPORT_SYMBOL_GPL(dual_role_get_property); ++ ++int dual_role_set_property(struct dual_role_phy_instance *dual_role, ++ enum dual_role_property prop, ++ const unsigned int *val) ++{ ++ if (!dual_role->desc->set_property) ++ return -ENODEV; ++ ++ return dual_role->desc->set_property(dual_role, prop, val); ++} ++EXPORT_SYMBOL_GPL(dual_role_set_property); ++ ++int dual_role_property_is_writeable(struct dual_role_phy_instance *dual_role, ++ enum dual_role_property prop) ++{ ++ if (!dual_role->desc->property_is_writeable) ++ return -ENODEV; ++ ++ return dual_role->desc->property_is_writeable(dual_role, prop); ++} ++EXPORT_SYMBOL_GPL(dual_role_property_is_writeable); ++ ++static void dual_role_dev_release(struct device *dev) ++{ ++ struct dual_role_phy_instance *dual_role = ++ container_of(dev, struct dual_role_phy_instance, dev); ++ pr_debug("device: '%s': %s\n", dev_name(dev), __func__); ++ kfree(dual_role); ++} ++ ++static struct dual_role_phy_instance *__must_check ++__dual_role_register(struct device *parent, ++ const struct dual_role_phy_desc *desc) ++{ ++ struct device *dev; ++ struct dual_role_phy_instance *dual_role; ++ int rc; ++ ++ dual_role = kzalloc(sizeof(*dual_role), GFP_KERNEL); ++ if (!dual_role) ++ return ERR_PTR(-ENOMEM); ++ ++ dev = &dual_role->dev; ++ ++ device_initialize(dev); ++ ++ dev->class = dual_role_class; ++ dev->type = &dual_role_dev_type; ++ dev->parent = parent; ++ dev->release = dual_role_dev_release; ++ dev_set_drvdata(dev, dual_role); ++ dual_role->desc = desc; ++ ++ rc = dev_set_name(dev, "%s", desc->name); ++ if (rc) ++ goto dev_set_name_failed; ++ ++ INIT_WORK(&dual_role->changed_work, dual_role_changed_work); ++ ++ rc = device_init_wakeup(dev, true); ++ if (rc) ++ goto wakeup_init_failed; ++ ++ rc = device_add(dev); ++ if (rc) ++ goto device_add_failed; ++ ++ dual_role_instance_changed(dual_role); ++ ++ return dual_role; ++ ++device_add_failed: ++ device_init_wakeup(dev, false); ++wakeup_init_failed: ++dev_set_name_failed: ++ put_device(dev); ++ kfree(dual_role); ++ ++ return ERR_PTR(rc); ++} ++ ++static void dual_role_instance_unregister(struct dual_role_phy_instance ++ *dual_role) ++{ ++ cancel_work_sync(&dual_role->changed_work); ++ device_init_wakeup(&dual_role->dev, false); ++ device_unregister(&dual_role->dev); ++} ++ ++static void devm_dual_role_release(struct device *dev, void *res) ++{ ++ struct dual_role_phy_instance **dual_role = res; ++ ++ dual_role_instance_unregister(*dual_role); ++} ++ ++struct dual_role_phy_instance *__must_check ++devm_dual_role_instance_register(struct device *parent, ++ const struct dual_role_phy_desc *desc) ++{ ++ struct dual_role_phy_instance **ptr, *dual_role; ++ ++ ptr = devres_alloc(devm_dual_role_release, sizeof(*ptr), GFP_KERNEL); ++ ++ if (!ptr) ++ return ERR_PTR(-ENOMEM); ++ dual_role = __dual_role_register(parent, desc); ++ if (IS_ERR(dual_role)) { ++ devres_free(ptr); ++ } else { ++ *ptr = dual_role; ++ devres_add(parent, ptr); ++ } ++ return dual_role; ++} ++EXPORT_SYMBOL_GPL(devm_dual_role_instance_register); ++ ++static int devm_dual_role_match(struct device *dev, void *res, void *data) ++{ ++ struct dual_role_phy_instance **r = res; ++ ++ if (WARN_ON(!r || !*r)) ++ return 0; ++ ++ return *r == data; ++} ++ ++void devm_dual_role_instance_unregister(struct device *dev, ++ struct dual_role_phy_instance ++ *dual_role) ++{ ++ int rc; ++ ++ rc = devres_release(dev, devm_dual_role_release, ++ devm_dual_role_match, dual_role); ++ WARN_ON(rc); ++} ++EXPORT_SYMBOL_GPL(devm_dual_role_instance_unregister); ++ ++void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role) ++{ ++ return dual_role->drv_data; ++} ++EXPORT_SYMBOL_GPL(dual_role_get_drvdata); ++ ++/***************** Device attribute functions **************************/ ++ ++/* port type */ ++static char *supported_modes_text[] = { ++ "ufp dfp", "dfp", "ufp" ++}; ++ ++/* current mode */ ++static char *mode_text[] = { ++ "ufp", "dfp", "none" ++}; ++ ++/* Power role */ ++static char *pr_text[] = { ++ "source", "sink", "none" ++}; ++ ++/* Data role */ ++static char *dr_text[] = { ++ "host", "device", "none" ++}; ++ ++/* Vconn supply */ ++static char *vconn_supply_text[] = { ++ "n", "y" ++}; ++ ++static ssize_t dual_role_show_property(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ssize_t ret = 0; ++ struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev); ++ const ptrdiff_t off = attr - dual_role_attrs; ++ unsigned int value; ++ ++ if (off == DUAL_ROLE_PROP_SUPPORTED_MODES) { ++ value = dual_role->desc->supported_modes; ++ } else { ++ ret = dual_role_get_property(dual_role, off, &value); ++ ++ if (ret < 0) { ++ if (ret == -ENODATA) ++ dev_dbg(dev, ++ "driver has no data for `%s' property\n", ++ attr->attr.name); ++ else if (ret != -ENODEV) ++ dev_err(dev, ++ "driver failed to report `%s' property: %zd\n", ++ attr->attr.name, ret); ++ return ret; ++ } ++ } ++ ++ if (off == DUAL_ROLE_PROP_SUPPORTED_MODES) { ++ BUILD_BUG_ON(DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL != ++ ARRAY_SIZE(supported_modes_text)); ++ if (value < DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL) ++ return snprintf(buf, PAGE_SIZE, "%s\n", ++ supported_modes_text[value]); ++ else ++ return -EIO; ++ } else if (off == DUAL_ROLE_PROP_MODE) { ++ BUILD_BUG_ON(DUAL_ROLE_PROP_MODE_TOTAL != ++ ARRAY_SIZE(mode_text)); ++ if (value < DUAL_ROLE_PROP_MODE_TOTAL) ++ return snprintf(buf, PAGE_SIZE, "%s\n", ++ mode_text[value]); ++ else ++ return -EIO; ++ } else if (off == DUAL_ROLE_PROP_PR) { ++ BUILD_BUG_ON(DUAL_ROLE_PROP_PR_TOTAL != ARRAY_SIZE(pr_text)); ++ if (value < DUAL_ROLE_PROP_PR_TOTAL) ++ return snprintf(buf, PAGE_SIZE, "%s\n", ++ pr_text[value]); ++ else ++ return -EIO; ++ } else if (off == DUAL_ROLE_PROP_DR) { ++ BUILD_BUG_ON(DUAL_ROLE_PROP_DR_TOTAL != ARRAY_SIZE(dr_text)); ++ if (value < DUAL_ROLE_PROP_DR_TOTAL) ++ return snprintf(buf, PAGE_SIZE, "%s\n", ++ dr_text[value]); ++ else ++ return -EIO; ++ } else if (off == DUAL_ROLE_PROP_VCONN_SUPPLY) { ++ BUILD_BUG_ON(DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL != ++ ARRAY_SIZE(vconn_supply_text)); ++ if (value < DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL) ++ return snprintf(buf, PAGE_SIZE, "%s\n", ++ vconn_supply_text[value]); ++ else ++ return -EIO; ++ } else ++ return -EIO; ++} ++ ++static ssize_t dual_role_store_property(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ ssize_t ret; ++ struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev); ++ const ptrdiff_t off = attr - dual_role_attrs; ++ unsigned int value; ++ int total, i; ++ char *dup_buf, **text_array; ++ bool result = false; ++ ++ dup_buf = kstrdupcase(buf, GFP_KERNEL, false); ++ switch (off) { ++ case DUAL_ROLE_PROP_MODE: ++ total = DUAL_ROLE_PROP_MODE_TOTAL; ++ text_array = mode_text; ++ break; ++ case DUAL_ROLE_PROP_PR: ++ total = DUAL_ROLE_PROP_PR_TOTAL; ++ text_array = pr_text; ++ break; ++ case DUAL_ROLE_PROP_DR: ++ total = DUAL_ROLE_PROP_DR_TOTAL; ++ text_array = dr_text; ++ break; ++ case DUAL_ROLE_PROP_VCONN_SUPPLY: ++ ret = strtobool(dup_buf, &result); ++ value = result; ++ if (!ret) ++ goto setprop; ++ default: ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ for (i = 0; i <= total; i++) { ++ if (i == total) { ++ ret = -ENOTSUPP; ++ goto error; ++ } ++ if (!strncmp(*(text_array + i), dup_buf, ++ strlen(*(text_array + i)))) { ++ value = i; ++ break; ++ } ++ } ++ ++setprop: ++ ret = dual_role->desc->set_property(dual_role, off, &value); ++ ++error: ++ kfree(dup_buf); ++ ++ if (ret < 0) ++ return ret; ++ ++ return count; ++} ++ ++static umode_t dual_role_attr_is_visible(struct kobject *kobj, ++ struct attribute *attr, int attrno) ++{ ++ struct device *dev = container_of(kobj, struct device, kobj); ++ struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev); ++ umode_t mode = S_IRUSR | S_IRGRP | S_IROTH; ++ int i; ++ ++ if (attrno == DUAL_ROLE_PROP_SUPPORTED_MODES) ++ return mode; ++ ++ for (i = 0; i < dual_role->desc->num_properties; i++) { ++ int property = dual_role->desc->properties[i]; ++ ++ if (property == attrno) { ++ if (dual_role->desc->property_is_writeable && ++ dual_role_property_is_writeable(dual_role, property) ++ > 0) ++ mode |= S_IWUSR; ++ ++ return mode; ++ } ++ } ++ ++ return 0; ++} ++ ++static struct attribute *__dual_role_attrs[ARRAY_SIZE(dual_role_attrs) + 1]; ++ ++static struct attribute_group dual_role_attr_group = { ++ .attrs = __dual_role_attrs, ++ .is_visible = dual_role_attr_is_visible, ++}; ++ ++static const struct attribute_group *dual_role_attr_groups[] = { ++ &dual_role_attr_group, ++ NULL, ++}; ++ ++void dual_role_init_attrs(struct device_type *dev_type) ++{ ++ int i; ++ ++ dev_type->groups = dual_role_attr_groups; ++ ++ for (i = 0; i < ARRAY_SIZE(dual_role_attrs); i++) ++ __dual_role_attrs[i] = &dual_role_attrs[i].attr; ++} ++ ++int dual_role_uevent(struct device *dev, struct kobj_uevent_env *env) ++{ ++ struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev); ++ int ret = 0, j; ++ char *prop_buf; ++ char *attrname; ++ ++ dev_dbg(dev, "uevent\n"); ++ ++ if (!dual_role || !dual_role->desc) { ++ dev_dbg(dev, "No dual_role phy yet\n"); ++ return ret; ++ } ++ ++ dev_dbg(dev, "DUAL_ROLE_NAME=%s\n", dual_role->desc->name); ++ ++ ret = add_uevent_var(env, "DUAL_ROLE_NAME=%s", dual_role->desc->name); ++ if (ret) ++ return ret; ++ ++ prop_buf = (char *)get_zeroed_page(GFP_KERNEL); ++ if (!prop_buf) ++ return -ENOMEM; ++ ++ for (j = 0; j < dual_role->desc->num_properties; j++) { ++ struct device_attribute *attr; ++ char *line; ++ ++ attr = &dual_role_attrs[dual_role->desc->properties[j]]; ++ ++ ret = dual_role_show_property(dev, attr, prop_buf); ++ if (ret == -ENODEV || ret == -ENODATA) { ++ ret = 0; ++ continue; ++ } ++ ++ if (ret < 0) ++ goto out; ++ line = strnchr(prop_buf, PAGE_SIZE, '\n'); ++ if (line) ++ *line = 0; ++ ++ attrname = kstrdupcase(attr->attr.name, GFP_KERNEL, true); ++ if (!attrname) ++ ret = -ENOMEM; ++ ++ dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf); ++ ++ ret = add_uevent_var(env, "DUAL_ROLE_%s=%s", attrname, ++ prop_buf); ++ kfree(attrname); ++ if (ret) ++ goto out; ++ } ++ ++out: ++ free_page((unsigned long)prop_buf); ++ ++ return ret; ++} ++ ++/******************* Module Init ***********************************/ ++ ++static int __init dual_role_class_init(void) ++{ ++ dual_role_class = class_create(THIS_MODULE, "dual_role_usb"); ++ ++ if (IS_ERR(dual_role_class)) ++ return PTR_ERR(dual_role_class); ++ ++ dual_role_class->dev_uevent = dual_role_uevent; ++ dual_role_init_attrs(&dual_role_dev_type); ++ ++ return 0; ++} ++ ++static void __exit dual_role_class_exit(void) ++{ ++ class_destroy(dual_role_class); ++} ++ ++subsys_initcall(dual_role_class_init); ++module_exit(dual_role_class_exit); +diff --git a/drivers/usb/phy/otg-wakelock.c b/drivers/usb/phy/otg-wakelock.c +new file mode 100644 +index 0000000..479376b +--- /dev/null ++++ b/drivers/usb/phy/otg-wakelock.c +@@ -0,0 +1,173 @@ ++/* ++ * otg-wakelock.c ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/device.h> ++#include <linux/err.h> ++#include <linux/module.h> ++#include <linux/notifier.h> ++#include <linux/wakelock.h> ++#include <linux/spinlock.h> ++#include <linux/usb/otg.h> ++ ++#define TEMPORARY_HOLD_TIME 2000 ++ ++static bool enabled = true; ++static struct usb_phy *otgwl_xceiv; ++static struct notifier_block otgwl_nb; ++ ++/* ++ * otgwl_spinlock is held while the VBUS lock is grabbed or dropped and the ++ * held field is updated to match. ++ */ ++ ++static DEFINE_SPINLOCK(otgwl_spinlock); ++ ++/* ++ * Only one lock, but since these 3 fields are associated with each other... ++ */ ++ ++struct otgwl_lock { ++ char name[40]; ++ struct wake_lock wakelock; ++ bool held; ++}; ++ ++/* ++ * VBUS present lock. Also used as a timed lock on charger ++ * connect/disconnect and USB host disconnect, to allow the system ++ * to react to the change in power. ++ */ ++ ++static struct otgwl_lock vbus_lock; ++ ++static void otgwl_hold(struct otgwl_lock *lock) ++{ ++ if (!lock->held) { ++ wake_lock(&lock->wakelock); ++ lock->held = true; ++ } ++} ++ ++static void otgwl_temporary_hold(struct otgwl_lock *lock) ++{ ++ wake_lock_timeout(&lock->wakelock, ++ msecs_to_jiffies(TEMPORARY_HOLD_TIME)); ++ lock->held = false; ++} ++ ++static void otgwl_drop(struct otgwl_lock *lock) ++{ ++ if (lock->held) { ++ wake_unlock(&lock->wakelock); ++ lock->held = false; ++ } ++} ++ ++static void otgwl_handle_event(unsigned long event) ++{ ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&otgwl_spinlock, irqflags); ++ ++ if (!enabled) { ++ otgwl_drop(&vbus_lock); ++ spin_unlock_irqrestore(&otgwl_spinlock, irqflags); ++ return; ++ } ++ ++ switch (event) { ++ case USB_EVENT_VBUS: ++ case USB_EVENT_ENUMERATED: ++ otgwl_hold(&vbus_lock); ++ break; ++ ++ case USB_EVENT_NONE: ++ case USB_EVENT_ID: ++ case USB_EVENT_CHARGER: ++ otgwl_temporary_hold(&vbus_lock); ++ break; ++ ++ default: ++ break; ++ } ++ ++ spin_unlock_irqrestore(&otgwl_spinlock, irqflags); ++} ++ ++static int otgwl_otg_notifications(struct notifier_block *nb, ++ unsigned long event, void *unused) ++{ ++ otgwl_handle_event(event); ++ return NOTIFY_OK; ++} ++ ++static int set_enabled(const char *val, const struct kernel_param *kp) ++{ ++ int rv = param_set_bool(val, kp); ++ ++ if (rv) ++ return rv; ++ ++ if (otgwl_xceiv) ++ otgwl_handle_event(otgwl_xceiv->last_event); ++ ++ return 0; ++} ++ ++static struct kernel_param_ops enabled_param_ops = { ++ .set = set_enabled, ++ .get = param_get_bool, ++}; ++ ++module_param_cb(enabled, &enabled_param_ops, &enabled, 0644); ++MODULE_PARM_DESC(enabled, "enable wakelock when VBUS present"); ++ ++static int __init otg_wakelock_init(void) ++{ ++ int ret; ++ struct usb_phy *phy; ++ ++ phy = usb_get_phy(USB_PHY_TYPE_USB2); ++ ++ if (IS_ERR(phy)) { ++ pr_err("%s: No USB transceiver found\n", __func__); ++ return PTR_ERR(phy); ++ } ++ otgwl_xceiv = phy; ++ ++ snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s", ++ dev_name(otgwl_xceiv->dev)); ++ wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND, ++ vbus_lock.name); ++ ++ otgwl_nb.notifier_call = otgwl_otg_notifications; ++ ret = usb_register_notifier(otgwl_xceiv, &otgwl_nb); ++ ++ if (ret) { ++ pr_err("%s: usb_register_notifier on transceiver %s" ++ " failed\n", __func__, ++ dev_name(otgwl_xceiv->dev)); ++ otgwl_xceiv = NULL; ++ wake_lock_destroy(&vbus_lock.wakelock); ++ return ret; ++ } ++ ++ otgwl_handle_event(otgwl_xceiv->last_event); ++ return ret; ++} ++ ++late_initcall(otg_wakelock_init); +diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c +index cb84f69..313f09a 100644 +--- a/drivers/vhost/scsi.c ++++ b/drivers/vhost/scsi.c +@@ -1251,7 +1251,7 @@ tcm_vhost_send_evt(struct vhost_scsi *vs, + * lun[4-7] need to be zero according to virtio-scsi spec. + */ + evt->event.lun[0] = 0x01; +- evt->event.lun[1] = tpg->tport_tpgt & 0xFF; ++ evt->event.lun[1] = tpg->tport_tpgt; + if (lun->unpacked_lun >= 256) + evt->event.lun[2] = lun->unpacked_lun >> 8 | 0x40 ; + evt->event.lun[3] = lun->unpacked_lun & 0xFF; +@@ -2122,12 +2122,12 @@ tcm_vhost_make_tpg(struct se_wwn *wwn, + struct tcm_vhost_tport, tport_wwn); + + struct tcm_vhost_tpg *tpg; +- unsigned long tpgt; ++ u16 tpgt; + int ret; + + if (strstr(name, "tpgt_") != name) + return ERR_PTR(-EINVAL); +- if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX) ++ if (kstrtou16(name + 5, 10, &tpgt) || tpgt >= VHOST_SCSI_MAX_TARGET) + return ERR_PTR(-EINVAL); + + tpg = kzalloc(sizeof(struct tcm_vhost_tpg), GFP_KERNEL); +diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig +index 8bf495f..49fcbb1 100644 +--- a/drivers/video/Kconfig ++++ b/drivers/video/Kconfig +@@ -31,6 +31,7 @@ source "drivers/video/fbdev/Kconfig" + endmenu + + source "drivers/video/backlight/Kconfig" ++source "drivers/video/adf/Kconfig" + + config VGASTATE + tristate +diff --git a/drivers/video/Makefile b/drivers/video/Makefile +index 9ad3c17..1a8c4ce 100644 +--- a/drivers/video/Makefile ++++ b/drivers/video/Makefile +@@ -1,6 +1,7 @@ + obj-$(CONFIG_VGASTATE) += vgastate.o + obj-$(CONFIG_HDMI) += hdmi.o + ++obj-$(CONFIG_ADF) += adf/ + obj-$(CONFIG_VT) += console/ + obj-$(CONFIG_LOGO) += logo/ + obj-y += backlight/ +diff --git a/drivers/video/adf/Kconfig b/drivers/video/adf/Kconfig +new file mode 100644 +index 0000000..33858b7 +--- /dev/null ++++ b/drivers/video/adf/Kconfig +@@ -0,0 +1,14 @@ ++menuconfig ADF ++ depends on SYNC ++ depends on DMA_SHARED_BUFFER ++ tristate "Atomic Display Framework" ++ ++menuconfig ADF_FBDEV ++ depends on ADF ++ depends on FB ++ tristate "Helper for implementing the fbdev API in ADF drivers" ++ ++menuconfig ADF_MEMBLOCK ++ depends on ADF ++ depends on HAVE_MEMBLOCK ++ tristate "Helper for using memblocks as buffers in ADF drivers" +diff --git a/drivers/video/adf/Makefile b/drivers/video/adf/Makefile +new file mode 100644 +index 0000000..78d0915 +--- /dev/null ++++ b/drivers/video/adf/Makefile +@@ -0,0 +1,15 @@ ++ccflags-y := -Idrivers/staging/android ++ ++CFLAGS_adf.o := -I$(src) ++ ++obj-$(CONFIG_ADF) += adf.o \ ++ adf_client.o \ ++ adf_fops.o \ ++ adf_format.o \ ++ adf_sysfs.o ++ ++obj-$(CONFIG_COMPAT) += adf_fops32.o ++ ++obj-$(CONFIG_ADF_FBDEV) += adf_fbdev.o ++ ++obj-$(CONFIG_ADF_MEMBLOCK) += adf_memblock.o +diff --git a/drivers/video/adf/adf.c b/drivers/video/adf/adf.c +new file mode 100644 +index 0000000..42c30c0 +--- /dev/null ++++ b/drivers/video/adf/adf.c +@@ -0,0 +1,1188 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * adf_modeinfo_{set_name,set_vrefresh} modified from ++ * drivers/gpu/drm/drm_modes.c ++ * adf_format_validate_yuv modified from framebuffer_check in ++ * drivers/gpu/drm/drm_crtc.c ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/device.h> ++#include <linux/idr.h> ++#include <linux/highmem.h> ++#include <linux/memblock.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++ ++#include <video/adf_format.h> ++ ++#include "sw_sync.h" ++#include "sync.h" ++ ++#include "adf.h" ++#include "adf_fops.h" ++#include "adf_sysfs.h" ++ ++#define CREATE_TRACE_POINTS ++#include "adf_trace.h" ++ ++#define ADF_SHORT_FENCE_TIMEOUT (1 * MSEC_PER_SEC) ++#define ADF_LONG_FENCE_TIMEOUT (10 * MSEC_PER_SEC) ++ ++static DEFINE_IDR(adf_devices); ++ ++static void adf_fence_wait(struct adf_device *dev, struct sync_fence *fence) ++{ ++ /* sync_fence_wait() dumps debug information on timeout. Experience ++ has shown that if the pipeline gets stuck, a short timeout followed ++ by a longer one provides useful information for debugging. */ ++ int err = sync_fence_wait(fence, ADF_SHORT_FENCE_TIMEOUT); ++ if (err >= 0) ++ return; ++ ++ if (err == -ETIME) ++ err = sync_fence_wait(fence, ADF_LONG_FENCE_TIMEOUT); ++ ++ if (err < 0) ++ dev_warn(&dev->base.dev, "error waiting on fence: %d\n", err); ++} ++ ++void adf_buffer_cleanup(struct adf_buffer *buf) ++{ ++ size_t i; ++ for (i = 0; i < ARRAY_SIZE(buf->dma_bufs); i++) ++ if (buf->dma_bufs[i]) ++ dma_buf_put(buf->dma_bufs[i]); ++ ++ if (buf->acquire_fence) ++ sync_fence_put(buf->acquire_fence); ++} ++ ++void adf_buffer_mapping_cleanup(struct adf_buffer_mapping *mapping, ++ struct adf_buffer *buf) ++{ ++ /* calling adf_buffer_mapping_cleanup() is safe even if mapping is ++ uninitialized or partially-initialized, as long as it was ++ zeroed on allocation */ ++ size_t i; ++ for (i = 0; i < ARRAY_SIZE(mapping->sg_tables); i++) { ++ if (mapping->sg_tables[i]) ++ dma_buf_unmap_attachment(mapping->attachments[i], ++ mapping->sg_tables[i], DMA_TO_DEVICE); ++ if (mapping->attachments[i]) ++ dma_buf_detach(buf->dma_bufs[i], ++ mapping->attachments[i]); ++ } ++} ++ ++void adf_post_cleanup(struct adf_device *dev, struct adf_pending_post *post) ++{ ++ size_t i; ++ ++ if (post->state) ++ dev->ops->state_free(dev, post->state); ++ ++ for (i = 0; i < post->config.n_bufs; i++) { ++ adf_buffer_mapping_cleanup(&post->config.mappings[i], ++ &post->config.bufs[i]); ++ adf_buffer_cleanup(&post->config.bufs[i]); ++ } ++ ++ kfree(post->config.custom_data); ++ kfree(post->config.mappings); ++ kfree(post->config.bufs); ++ kfree(post); ++} ++ ++static void adf_sw_advance_timeline(struct adf_device *dev) ++{ ++#ifdef CONFIG_SW_SYNC ++ sw_sync_timeline_inc(dev->timeline, 1); ++#else ++ BUG(); ++#endif ++} ++ ++static void adf_post_work_func(struct kthread_work *work) ++{ ++ struct adf_device *dev = ++ container_of(work, struct adf_device, post_work); ++ struct adf_pending_post *post, *next; ++ struct list_head saved_list; ++ ++ mutex_lock(&dev->post_lock); ++ memcpy(&saved_list, &dev->post_list, sizeof(saved_list)); ++ list_replace_init(&dev->post_list, &saved_list); ++ mutex_unlock(&dev->post_lock); ++ ++ list_for_each_entry_safe(post, next, &saved_list, head) { ++ int i; ++ ++ for (i = 0; i < post->config.n_bufs; i++) { ++ struct sync_fence *fence = ++ post->config.bufs[i].acquire_fence; ++ if (fence) ++ adf_fence_wait(dev, fence); ++ } ++ ++ dev->ops->post(dev, &post->config, post->state); ++ ++ if (dev->ops->advance_timeline) ++ dev->ops->advance_timeline(dev, &post->config, ++ post->state); ++ else ++ adf_sw_advance_timeline(dev); ++ ++ list_del(&post->head); ++ if (dev->onscreen) ++ adf_post_cleanup(dev, dev->onscreen); ++ dev->onscreen = post; ++ } ++} ++ ++void adf_attachment_free(struct adf_attachment_list *attachment) ++{ ++ list_del(&attachment->head); ++ kfree(attachment); ++} ++ ++struct adf_event_refcount *adf_obj_find_event_refcount(struct adf_obj *obj, ++ enum adf_event_type type) ++{ ++ struct rb_root *root = &obj->event_refcount; ++ struct rb_node **new = &(root->rb_node); ++ struct rb_node *parent = NULL; ++ struct adf_event_refcount *refcount; ++ ++ while (*new) { ++ refcount = container_of(*new, struct adf_event_refcount, node); ++ parent = *new; ++ ++ if (refcount->type > type) ++ new = &(*new)->rb_left; ++ else if (refcount->type < type) ++ new = &(*new)->rb_right; ++ else ++ return refcount; ++ } ++ ++ refcount = kzalloc(sizeof(*refcount), GFP_KERNEL); ++ if (!refcount) ++ return NULL; ++ refcount->type = type; ++ ++ rb_link_node(&refcount->node, parent, new); ++ rb_insert_color(&refcount->node, root); ++ return refcount; ++} ++ ++/** ++ * adf_event_get - increase the refcount for an event ++ * ++ * @obj: the object that produces the event ++ * @type: the event type ++ * ++ * ADF will call the object's set_event() op if needed. ops are allowed ++ * to sleep, so adf_event_get() must NOT be called from an atomic context. ++ * ++ * Returns 0 if successful, or -%EINVAL if the object does not support the ++ * requested event type. ++ */ ++int adf_event_get(struct adf_obj *obj, enum adf_event_type type) ++{ ++ struct adf_event_refcount *refcount; ++ int old_refcount; ++ int ret; ++ ++ ret = adf_obj_check_supports_event(obj, type); ++ if (ret < 0) ++ return ret; ++ ++ mutex_lock(&obj->event_lock); ++ ++ refcount = adf_obj_find_event_refcount(obj, type); ++ if (!refcount) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ old_refcount = refcount->refcount++; ++ ++ if (old_refcount == 0) { ++ obj->ops->set_event(obj, type, true); ++ trace_adf_event_enable(obj, type); ++ } ++ ++done: ++ mutex_unlock(&obj->event_lock); ++ return ret; ++} ++EXPORT_SYMBOL(adf_event_get); ++ ++/** ++ * adf_event_put - decrease the refcount for an event ++ * ++ * @obj: the object that produces the event ++ * @type: the event type ++ * ++ * ADF will call the object's set_event() op if needed. ops are allowed ++ * to sleep, so adf_event_put() must NOT be called from an atomic context. ++ * ++ * Returns 0 if successful, -%EINVAL if the object does not support the ++ * requested event type, or -%EALREADY if the refcount is already 0. ++ */ ++int adf_event_put(struct adf_obj *obj, enum adf_event_type type) ++{ ++ struct adf_event_refcount *refcount; ++ int old_refcount; ++ int ret; ++ ++ ret = adf_obj_check_supports_event(obj, type); ++ if (ret < 0) ++ return ret; ++ ++ ++ mutex_lock(&obj->event_lock); ++ ++ refcount = adf_obj_find_event_refcount(obj, type); ++ if (!refcount) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ old_refcount = refcount->refcount--; ++ ++ if (WARN_ON(old_refcount == 0)) { ++ refcount->refcount++; ++ ret = -EALREADY; ++ } else if (old_refcount == 1) { ++ obj->ops->set_event(obj, type, false); ++ trace_adf_event_disable(obj, type); ++ } ++ ++done: ++ mutex_unlock(&obj->event_lock); ++ return ret; ++} ++EXPORT_SYMBOL(adf_event_put); ++ ++/** ++ * adf_vsync_wait - wait for a vsync event on a display interface ++ * ++ * @intf: the display interface ++ * @timeout: timeout in jiffies (0 = wait indefinitely) ++ * ++ * adf_vsync_wait() may sleep, so it must NOT be called from an atomic context. ++ * ++ * This function returns -%ERESTARTSYS if it is interrupted by a signal. ++ * If @timeout == 0 then this function returns 0 on vsync. If @timeout > 0 then ++ * this function returns the number of remaining jiffies or -%ETIMEDOUT on ++ * timeout. ++ */ ++int adf_vsync_wait(struct adf_interface *intf, long timeout) ++{ ++ ktime_t timestamp; ++ int ret; ++ unsigned long flags; ++ ++ read_lock_irqsave(&intf->vsync_lock, flags); ++ timestamp = intf->vsync_timestamp; ++ read_unlock_irqrestore(&intf->vsync_lock, flags); ++ ++ adf_vsync_get(intf); ++ if (timeout) { ++ ret = wait_event_interruptible_timeout(intf->vsync_wait, ++ !ktime_equal(timestamp, ++ intf->vsync_timestamp), ++ msecs_to_jiffies(timeout)); ++ if (ret == 0 && ktime_equal(timestamp, intf->vsync_timestamp)) ++ ret = -ETIMEDOUT; ++ } else { ++ ret = wait_event_interruptible(intf->vsync_wait, ++ !ktime_equal(timestamp, ++ intf->vsync_timestamp)); ++ } ++ adf_vsync_put(intf); ++ ++ return ret; ++} ++EXPORT_SYMBOL(adf_vsync_wait); ++ ++static void adf_event_queue(struct adf_obj *obj, struct adf_event *event) ++{ ++ struct adf_file *file; ++ unsigned long flags; ++ ++ trace_adf_event(obj, event->type); ++ ++ spin_lock_irqsave(&obj->file_lock, flags); ++ ++ list_for_each_entry(file, &obj->file_list, head) ++ if (test_bit(event->type, file->event_subscriptions)) ++ adf_file_queue_event(file, event); ++ ++ spin_unlock_irqrestore(&obj->file_lock, flags); ++} ++ ++/** ++ * adf_event_notify - notify userspace of a driver-private event ++ * ++ * @obj: the ADF object that produced the event ++ * @event: the event ++ * ++ * adf_event_notify() may be called safely from an atomic context. It will ++ * copy @event if needed, so @event may point to a variable on the stack. ++ * ++ * Drivers must NOT call adf_event_notify() for vsync and hotplug events. ++ * ADF provides adf_vsync_notify() and ++ * adf_hotplug_notify_{connected,disconnected}() for these events. ++ */ ++int adf_event_notify(struct adf_obj *obj, struct adf_event *event) ++{ ++ if (WARN_ON(event->type == ADF_EVENT_VSYNC || ++ event->type == ADF_EVENT_HOTPLUG)) ++ return -EINVAL; ++ ++ adf_event_queue(obj, event); ++ return 0; ++} ++EXPORT_SYMBOL(adf_event_notify); ++ ++/** ++ * adf_vsync_notify - notify ADF of a display interface's vsync event ++ * ++ * @intf: the display interface ++ * @timestamp: the time the vsync occurred ++ * ++ * adf_vsync_notify() may be called safely from an atomic context. ++ */ ++void adf_vsync_notify(struct adf_interface *intf, ktime_t timestamp) ++{ ++ unsigned long flags; ++ struct adf_vsync_event event; ++ ++ write_lock_irqsave(&intf->vsync_lock, flags); ++ intf->vsync_timestamp = timestamp; ++ write_unlock_irqrestore(&intf->vsync_lock, flags); ++ ++ wake_up_interruptible_all(&intf->vsync_wait); ++ ++ event.base.type = ADF_EVENT_VSYNC; ++ event.base.length = sizeof(event); ++ event.timestamp = ktime_to_ns(timestamp); ++ adf_event_queue(&intf->base, &event.base); ++} ++EXPORT_SYMBOL(adf_vsync_notify); ++ ++void adf_hotplug_notify(struct adf_interface *intf, bool connected, ++ struct drm_mode_modeinfo *modelist, size_t n_modes) ++{ ++ unsigned long flags; ++ struct adf_hotplug_event event; ++ struct drm_mode_modeinfo *old_modelist; ++ ++ write_lock_irqsave(&intf->hotplug_modelist_lock, flags); ++ old_modelist = intf->modelist; ++ intf->hotplug_detect = connected; ++ intf->modelist = modelist; ++ intf->n_modes = n_modes; ++ write_unlock_irqrestore(&intf->hotplug_modelist_lock, flags); ++ ++ kfree(old_modelist); ++ ++ event.base.length = sizeof(event); ++ event.base.type = ADF_EVENT_HOTPLUG; ++ event.connected = connected; ++ adf_event_queue(&intf->base, &event.base); ++} ++ ++/** ++ * adf_hotplug_notify_connected - notify ADF of a display interface being ++ * connected to a display ++ * ++ * @intf: the display interface ++ * @modelist: hardware modes supported by display ++ * @n_modes: length of modelist ++ * ++ * @modelist is copied as needed, so it may point to a variable on the stack. ++ * ++ * adf_hotplug_notify_connected() may NOT be called safely from an atomic ++ * context. ++ * ++ * Returns 0 on success or error code (<0) on error. ++ */ ++int adf_hotplug_notify_connected(struct adf_interface *intf, ++ struct drm_mode_modeinfo *modelist, size_t n_modes) ++{ ++ struct drm_mode_modeinfo *modelist_copy; ++ ++ if (n_modes > ADF_MAX_MODES) ++ return -ENOMEM; ++ ++ modelist_copy = kzalloc(sizeof(modelist_copy[0]) * n_modes, ++ GFP_KERNEL); ++ if (!modelist_copy) ++ return -ENOMEM; ++ memcpy(modelist_copy, modelist, sizeof(modelist_copy[0]) * n_modes); ++ ++ adf_hotplug_notify(intf, true, modelist_copy, n_modes); ++ return 0; ++} ++EXPORT_SYMBOL(adf_hotplug_notify_connected); ++ ++/** ++ * adf_hotplug_notify_disconnected - notify ADF of a display interface being ++ * disconnected from a display ++ * ++ * @intf: the display interface ++ * ++ * adf_hotplug_notify_disconnected() may be called safely from an atomic ++ * context. ++ */ ++void adf_hotplug_notify_disconnected(struct adf_interface *intf) ++{ ++ adf_hotplug_notify(intf, false, NULL, 0); ++} ++EXPORT_SYMBOL(adf_hotplug_notify_disconnected); ++ ++static int adf_obj_init(struct adf_obj *obj, enum adf_obj_type type, ++ struct idr *idr, struct adf_device *parent, ++ const struct adf_obj_ops *ops, const char *fmt, va_list args) ++{ ++ int ret; ++ ++ if (ops && ops->supports_event && !ops->set_event) { ++ pr_err("%s: %s implements supports_event but not set_event\n", ++ __func__, adf_obj_type_str(type)); ++ return -EINVAL; ++ } ++ ++ ret = idr_alloc(idr, obj, 0, 0, GFP_KERNEL); ++ if (ret < 0) { ++ pr_err("%s: allocating object id failed: %d\n", __func__, ret); ++ return ret; ++ } ++ obj->id = ret; ++ ++ vscnprintf(obj->name, sizeof(obj->name), fmt, args); ++ ++ obj->type = type; ++ obj->ops = ops; ++ obj->parent = parent; ++ mutex_init(&obj->event_lock); ++ obj->event_refcount = RB_ROOT; ++ spin_lock_init(&obj->file_lock); ++ INIT_LIST_HEAD(&obj->file_list); ++ return 0; ++} ++ ++static void adf_obj_destroy(struct adf_obj *obj, struct idr *idr) ++{ ++ struct rb_node *node = rb_first(&obj->event_refcount); ++ ++ while (node) { ++ struct adf_event_refcount *refcount = ++ container_of(node, struct adf_event_refcount, ++ node); ++ rb_erase(&refcount->node, &obj->event_refcount); ++ kfree(refcount); ++ node = rb_first(&obj->event_refcount); ++ } ++ ++ mutex_destroy(&obj->event_lock); ++ idr_remove(idr, obj->id); ++} ++ ++/** ++ * adf_device_init - initialize ADF-internal data for a display device ++ * and create sysfs entries ++ * ++ * @dev: the display device ++ * @parent: the device's parent device ++ * @ops: the device's associated ops ++ * @fmt: formatting string for the display device's name ++ * ++ * @fmt specifies the device's sysfs filename and the name returned to ++ * userspace through the %ADF_GET_DEVICE_DATA ioctl. ++ * ++ * Returns 0 on success or error code (<0) on failure. ++ */ ++int adf_device_init(struct adf_device *dev, struct device *parent, ++ const struct adf_device_ops *ops, const char *fmt, ...) ++{ ++ int ret; ++ va_list args; ++ ++ if (!ops->validate || !ops->post) { ++ pr_err("%s: device must implement validate and post\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (!ops->complete_fence && !ops->advance_timeline) { ++ if (!IS_ENABLED(CONFIG_SW_SYNC)) { ++ pr_err("%s: device requires sw_sync but it is not enabled in the kernel\n", ++ __func__); ++ return -EINVAL; ++ } ++ } else if (!(ops->complete_fence && ops->advance_timeline)) { ++ pr_err("%s: device must implement both complete_fence and advance_timeline, or implement neither\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ memset(dev, 0, sizeof(*dev)); ++ ++ va_start(args, fmt); ++ ret = adf_obj_init(&dev->base, ADF_OBJ_DEVICE, &adf_devices, dev, ++ &ops->base, fmt, args); ++ va_end(args); ++ if (ret < 0) ++ return ret; ++ ++ dev->dev = parent; ++ dev->ops = ops; ++ idr_init(&dev->overlay_engines); ++ idr_init(&dev->interfaces); ++ mutex_init(&dev->client_lock); ++ INIT_LIST_HEAD(&dev->post_list); ++ mutex_init(&dev->post_lock); ++ init_kthread_worker(&dev->post_worker); ++ INIT_LIST_HEAD(&dev->attached); ++ INIT_LIST_HEAD(&dev->attach_allowed); ++ ++ dev->post_thread = kthread_run(kthread_worker_fn, ++ &dev->post_worker, dev->base.name); ++ if (IS_ERR(dev->post_thread)) { ++ ret = PTR_ERR(dev->post_thread); ++ dev->post_thread = NULL; ++ ++ pr_err("%s: failed to run config posting thread: %d\n", ++ __func__, ret); ++ goto err; ++ } ++ init_kthread_work(&dev->post_work, adf_post_work_func); ++ ++ ret = adf_device_sysfs_init(dev); ++ if (ret < 0) ++ goto err; ++ ++ return 0; ++ ++err: ++ adf_device_destroy(dev); ++ return ret; ++} ++EXPORT_SYMBOL(adf_device_init); ++ ++/** ++ * adf_device_destroy - clean up ADF-internal data for a display device ++ * ++ * @dev: the display device ++ */ ++void adf_device_destroy(struct adf_device *dev) ++{ ++ struct adf_attachment_list *entry, *next; ++ ++ idr_destroy(&dev->interfaces); ++ idr_destroy(&dev->overlay_engines); ++ ++ if (dev->post_thread) { ++ flush_kthread_worker(&dev->post_worker); ++ kthread_stop(dev->post_thread); ++ } ++ ++ if (dev->onscreen) ++ adf_post_cleanup(dev, dev->onscreen); ++ adf_device_sysfs_destroy(dev); ++ list_for_each_entry_safe(entry, next, &dev->attach_allowed, head) { ++ adf_attachment_free(entry); ++ } ++ list_for_each_entry_safe(entry, next, &dev->attached, head) { ++ adf_attachment_free(entry); ++ } ++ mutex_destroy(&dev->post_lock); ++ mutex_destroy(&dev->client_lock); ++ ++ if (dev->timeline) ++ sync_timeline_destroy(&dev->timeline->obj); ++ ++ adf_obj_destroy(&dev->base, &adf_devices); ++} ++EXPORT_SYMBOL(adf_device_destroy); ++ ++/** ++ * adf_interface_init - initialize ADF-internal data for a display interface ++ * and create sysfs entries ++ * ++ * @intf: the display interface ++ * @dev: the interface's "parent" display device ++ * @type: interface type (see enum @adf_interface_type) ++ * @idx: which interface of type @type; ++ * e.g. interface DSI.1 -> @type=%ADF_INTF_TYPE_DSI, @idx=1 ++ * @flags: informational flags (bitmask of %ADF_INTF_FLAG_* values) ++ * @ops: the interface's associated ops ++ * @fmt: formatting string for the display interface's name ++ * ++ * @dev must have previously been initialized with adf_device_init(). ++ * ++ * @fmt affects the name returned to userspace through the ++ * %ADF_GET_INTERFACE_DATA ioctl. It does not affect the sysfs filename, ++ * which is derived from @dev's name. ++ * ++ * Returns 0 on success or error code (<0) on failure. ++ */ ++int adf_interface_init(struct adf_interface *intf, struct adf_device *dev, ++ enum adf_interface_type type, u32 idx, u32 flags, ++ const struct adf_interface_ops *ops, const char *fmt, ...) ++{ ++ int ret; ++ va_list args; ++ const u32 allowed_flags = ADF_INTF_FLAG_PRIMARY | ++ ADF_INTF_FLAG_EXTERNAL; ++ ++ if (dev->n_interfaces == ADF_MAX_INTERFACES) { ++ pr_err("%s: parent device %s has too many interfaces\n", ++ __func__, dev->base.name); ++ return -ENOMEM; ++ } ++ ++ if (type >= ADF_INTF_MEMORY && type <= ADF_INTF_TYPE_DEVICE_CUSTOM) { ++ pr_err("%s: invalid interface type %u\n", __func__, type); ++ return -EINVAL; ++ } ++ ++ if (flags & ~allowed_flags) { ++ pr_err("%s: invalid interface flags 0x%X\n", __func__, ++ flags & ~allowed_flags); ++ return -EINVAL; ++ } ++ ++ memset(intf, 0, sizeof(*intf)); ++ ++ va_start(args, fmt); ++ ret = adf_obj_init(&intf->base, ADF_OBJ_INTERFACE, &dev->interfaces, ++ dev, ops ? &ops->base : NULL, fmt, args); ++ va_end(args); ++ if (ret < 0) ++ return ret; ++ ++ intf->type = type; ++ intf->idx = idx; ++ intf->flags = flags; ++ intf->ops = ops; ++ intf->dpms_state = DRM_MODE_DPMS_OFF; ++ init_waitqueue_head(&intf->vsync_wait); ++ rwlock_init(&intf->vsync_lock); ++ rwlock_init(&intf->hotplug_modelist_lock); ++ ++ ret = adf_interface_sysfs_init(intf); ++ if (ret < 0) ++ goto err; ++ dev->n_interfaces++; ++ ++ return 0; ++ ++err: ++ adf_obj_destroy(&intf->base, &dev->interfaces); ++ return ret; ++} ++EXPORT_SYMBOL(adf_interface_init); ++ ++/** ++ * adf_interface_destroy - clean up ADF-internal data for a display interface ++ * ++ * @intf: the display interface ++ */ ++void adf_interface_destroy(struct adf_interface *intf) ++{ ++ struct adf_device *dev = adf_interface_parent(intf); ++ struct adf_attachment_list *entry, *next; ++ ++ mutex_lock(&dev->client_lock); ++ list_for_each_entry_safe(entry, next, &dev->attach_allowed, head) { ++ if (entry->attachment.interface == intf) { ++ adf_attachment_free(entry); ++ dev->n_attach_allowed--; ++ } ++ } ++ list_for_each_entry_safe(entry, next, &dev->attached, head) { ++ if (entry->attachment.interface == intf) { ++ adf_device_detach_op(dev, ++ entry->attachment.overlay_engine, intf); ++ adf_attachment_free(entry); ++ dev->n_attached--; ++ } ++ } ++ kfree(intf->modelist); ++ adf_interface_sysfs_destroy(intf); ++ adf_obj_destroy(&intf->base, &dev->interfaces); ++ dev->n_interfaces--; ++ mutex_unlock(&dev->client_lock); ++} ++EXPORT_SYMBOL(adf_interface_destroy); ++ ++static bool adf_overlay_engine_has_custom_formats( ++ const struct adf_overlay_engine_ops *ops) ++{ ++ size_t i; ++ for (i = 0; i < ops->n_supported_formats; i++) ++ if (!adf_format_is_standard(ops->supported_formats[i])) ++ return true; ++ return false; ++} ++ ++/** ++ * adf_overlay_engine_init - initialize ADF-internal data for an ++ * overlay engine and create sysfs entries ++ * ++ * @eng: the overlay engine ++ * @dev: the overlay engine's "parent" display device ++ * @ops: the overlay engine's associated ops ++ * @fmt: formatting string for the overlay engine's name ++ * ++ * @dev must have previously been initialized with adf_device_init(). ++ * ++ * @fmt affects the name returned to userspace through the ++ * %ADF_GET_OVERLAY_ENGINE_DATA ioctl. It does not affect the sysfs filename, ++ * which is derived from @dev's name. ++ * ++ * Returns 0 on success or error code (<0) on failure. ++ */ ++int adf_overlay_engine_init(struct adf_overlay_engine *eng, ++ struct adf_device *dev, ++ const struct adf_overlay_engine_ops *ops, const char *fmt, ...) ++{ ++ int ret; ++ va_list args; ++ ++ if (!ops->supported_formats) { ++ pr_err("%s: overlay engine must support at least one format\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (ops->n_supported_formats > ADF_MAX_SUPPORTED_FORMATS) { ++ pr_err("%s: overlay engine supports too many formats\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (adf_overlay_engine_has_custom_formats(ops) && ++ !dev->ops->validate_custom_format) { ++ pr_err("%s: overlay engine has custom formats but parent device %s does not implement validate_custom_format\n", ++ __func__, dev->base.name); ++ return -EINVAL; ++ } ++ ++ memset(eng, 0, sizeof(*eng)); ++ ++ va_start(args, fmt); ++ ret = adf_obj_init(&eng->base, ADF_OBJ_OVERLAY_ENGINE, ++ &dev->overlay_engines, dev, &ops->base, fmt, args); ++ va_end(args); ++ if (ret < 0) ++ return ret; ++ ++ eng->ops = ops; ++ ++ ret = adf_overlay_engine_sysfs_init(eng); ++ if (ret < 0) ++ goto err; ++ ++ return 0; ++ ++err: ++ adf_obj_destroy(&eng->base, &dev->overlay_engines); ++ return ret; ++} ++EXPORT_SYMBOL(adf_overlay_engine_init); ++ ++/** ++ * adf_interface_destroy - clean up ADF-internal data for an overlay engine ++ * ++ * @eng: the overlay engine ++ */ ++void adf_overlay_engine_destroy(struct adf_overlay_engine *eng) ++{ ++ struct adf_device *dev = adf_overlay_engine_parent(eng); ++ struct adf_attachment_list *entry, *next; ++ ++ mutex_lock(&dev->client_lock); ++ list_for_each_entry_safe(entry, next, &dev->attach_allowed, head) { ++ if (entry->attachment.overlay_engine == eng) { ++ adf_attachment_free(entry); ++ dev->n_attach_allowed--; ++ } ++ } ++ list_for_each_entry_safe(entry, next, &dev->attached, head) { ++ if (entry->attachment.overlay_engine == eng) { ++ adf_device_detach_op(dev, eng, ++ entry->attachment.interface); ++ adf_attachment_free(entry); ++ dev->n_attached--; ++ } ++ } ++ adf_overlay_engine_sysfs_destroy(eng); ++ adf_obj_destroy(&eng->base, &dev->overlay_engines); ++ mutex_unlock(&dev->client_lock); ++} ++EXPORT_SYMBOL(adf_overlay_engine_destroy); ++ ++struct adf_attachment_list *adf_attachment_find(struct list_head *list, ++ struct adf_overlay_engine *eng, struct adf_interface *intf) ++{ ++ struct adf_attachment_list *entry; ++ list_for_each_entry(entry, list, head) { ++ if (entry->attachment.interface == intf && ++ entry->attachment.overlay_engine == eng) ++ return entry; ++ } ++ return NULL; ++} ++ ++int adf_attachment_validate(struct adf_device *dev, ++ struct adf_overlay_engine *eng, struct adf_interface *intf) ++{ ++ struct adf_device *intf_dev = adf_interface_parent(intf); ++ struct adf_device *eng_dev = adf_overlay_engine_parent(eng); ++ ++ if (intf_dev != dev) { ++ dev_err(&dev->base.dev, "can't attach interface %s belonging to device %s\n", ++ intf->base.name, intf_dev->base.name); ++ return -EINVAL; ++ } ++ ++ if (eng_dev != dev) { ++ dev_err(&dev->base.dev, "can't attach overlay engine %s belonging to device %s\n", ++ eng->base.name, eng_dev->base.name); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * adf_attachment_allow - add a new entry to the list of allowed ++ * attachments ++ * ++ * @dev: the parent device ++ * @eng: the overlay engine ++ * @intf: the interface ++ * ++ * adf_attachment_allow() indicates that the underlying display hardware allows ++ * @intf to scan out @eng's output. It is intended to be called at ++ * driver initialization for each supported overlay engine + interface pair. ++ * ++ * Returns 0 on success, -%EALREADY if the entry already exists, or -errno on ++ * any other failure. ++ */ ++int adf_attachment_allow(struct adf_device *dev, ++ struct adf_overlay_engine *eng, struct adf_interface *intf) ++{ ++ int ret; ++ struct adf_attachment_list *entry = NULL; ++ ++ ret = adf_attachment_validate(dev, eng, intf); ++ if (ret < 0) ++ return ret; ++ ++ mutex_lock(&dev->client_lock); ++ ++ if (dev->n_attach_allowed == ADF_MAX_ATTACHMENTS) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ if (adf_attachment_find(&dev->attach_allowed, eng, intf)) { ++ ret = -EALREADY; ++ goto done; ++ } ++ ++ entry = kzalloc(sizeof(*entry), GFP_KERNEL); ++ if (!entry) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ entry->attachment.interface = intf; ++ entry->attachment.overlay_engine = eng; ++ list_add_tail(&entry->head, &dev->attach_allowed); ++ dev->n_attach_allowed++; ++ ++done: ++ mutex_unlock(&dev->client_lock); ++ if (ret < 0) ++ kfree(entry); ++ ++ return ret; ++} ++EXPORT_SYMBOL(adf_attachment_allow); ++ ++/** ++ * adf_obj_type_str - string representation of an adf_obj_type ++ * ++ * @type: the object type ++ */ ++const char *adf_obj_type_str(enum adf_obj_type type) ++{ ++ switch (type) { ++ case ADF_OBJ_OVERLAY_ENGINE: ++ return "overlay engine"; ++ ++ case ADF_OBJ_INTERFACE: ++ return "interface"; ++ ++ case ADF_OBJ_DEVICE: ++ return "device"; ++ ++ default: ++ return "unknown"; ++ } ++} ++EXPORT_SYMBOL(adf_obj_type_str); ++ ++/** ++ * adf_interface_type_str - string representation of an adf_interface's type ++ * ++ * @intf: the interface ++ */ ++const char *adf_interface_type_str(struct adf_interface *intf) ++{ ++ switch (intf->type) { ++ case ADF_INTF_DSI: ++ return "DSI"; ++ ++ case ADF_INTF_eDP: ++ return "eDP"; ++ ++ case ADF_INTF_DPI: ++ return "DPI"; ++ ++ case ADF_INTF_VGA: ++ return "VGA"; ++ ++ case ADF_INTF_DVI: ++ return "DVI"; ++ ++ case ADF_INTF_HDMI: ++ return "HDMI"; ++ ++ case ADF_INTF_MEMORY: ++ return "memory"; ++ ++ default: ++ if (intf->type >= ADF_INTF_TYPE_DEVICE_CUSTOM) { ++ if (intf->ops && intf->ops->type_str) ++ return intf->ops->type_str(intf); ++ return "custom"; ++ } ++ return "unknown"; ++ } ++} ++EXPORT_SYMBOL(adf_interface_type_str); ++ ++/** ++ * adf_event_type_str - string representation of an adf_event_type ++ * ++ * @obj: ADF object that produced the event ++ * @type: event type ++ */ ++const char *adf_event_type_str(struct adf_obj *obj, enum adf_event_type type) ++{ ++ switch (type) { ++ case ADF_EVENT_VSYNC: ++ return "vsync"; ++ ++ case ADF_EVENT_HOTPLUG: ++ return "hotplug"; ++ ++ default: ++ if (type >= ADF_EVENT_DEVICE_CUSTOM) { ++ if (obj->ops && obj->ops->event_type_str) ++ return obj->ops->event_type_str(obj, type); ++ return "custom"; ++ } ++ return "unknown"; ++ } ++} ++EXPORT_SYMBOL(adf_event_type_str); ++ ++/** ++ * adf_format_str - string representation of an ADF/DRM fourcc format ++ * ++ * @format: format fourcc ++ * @buf: target buffer for the format's string representation ++ */ ++void adf_format_str(u32 format, char buf[ADF_FORMAT_STR_SIZE]) ++{ ++ buf[0] = format & 0xFF; ++ buf[1] = (format >> 8) & 0xFF; ++ buf[2] = (format >> 16) & 0xFF; ++ buf[3] = (format >> 24) & 0xFF; ++ buf[4] = '\0'; ++} ++EXPORT_SYMBOL(adf_format_str); ++ ++/** ++ * adf_format_validate_yuv - validate the number and size of planes in buffers ++ * with a custom YUV format. ++ * ++ * @dev: ADF device performing the validation ++ * @buf: buffer to validate ++ * @num_planes: expected number of planes ++ * @hsub: expected horizontal chroma subsampling factor, in pixels ++ * @vsub: expected vertical chroma subsampling factor, in pixels ++ * @cpp: expected bytes per pixel for each plane (length @num_planes) ++ * ++ * adf_format_validate_yuv() is intended to be called as a helper from @dev's ++ * validate_custom_format() op. ++ * ++ * Returns 0 if @buf has the expected number of planes and each plane ++ * has sufficient size, or -EINVAL otherwise. ++ */ ++int adf_format_validate_yuv(struct adf_device *dev, struct adf_buffer *buf, ++ u8 num_planes, u8 hsub, u8 vsub, u8 cpp[]) ++{ ++ u8 i; ++ ++ if (num_planes != buf->n_planes) { ++ char format_str[ADF_FORMAT_STR_SIZE]; ++ adf_format_str(buf->format, format_str); ++ dev_err(&dev->base.dev, "%u planes expected for format %s but %u planes provided\n", ++ num_planes, format_str, buf->n_planes); ++ return -EINVAL; ++ } ++ ++ if (buf->w == 0 || buf->w % hsub) { ++ dev_err(&dev->base.dev, "bad buffer width %u\n", buf->w); ++ return -EINVAL; ++ } ++ ++ if (buf->h == 0 || buf->h % vsub) { ++ dev_err(&dev->base.dev, "bad buffer height %u\n", buf->h); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < num_planes; i++) { ++ u32 width = buf->w / (i != 0 ? hsub : 1); ++ u32 height = buf->h / (i != 0 ? vsub : 1); ++ u8 cpp = adf_format_plane_cpp(buf->format, i); ++ u32 last_line_size; ++ ++ if (buf->pitch[i] < (u64) width * cpp) { ++ dev_err(&dev->base.dev, "plane %u pitch is shorter than buffer width (pitch = %u, width = %u, bpp = %u)\n", ++ i, buf->pitch[i], width, cpp * 8); ++ return -EINVAL; ++ } ++ ++ switch (dev->ops->quirks.buffer_padding) { ++ case ADF_BUFFER_PADDED_TO_PITCH: ++ last_line_size = buf->pitch[i]; ++ break; ++ ++ case ADF_BUFFER_UNPADDED: ++ last_line_size = width * cpp; ++ break; ++ ++ default: ++ BUG(); ++ } ++ ++ if ((u64) (height - 1) * buf->pitch[i] + last_line_size + ++ buf->offset[i] > buf->dma_bufs[i]->size) { ++ dev_err(&dev->base.dev, "plane %u buffer too small (height = %u, pitch = %u, offset = %u, size = %zu)\n", ++ i, height, buf->pitch[i], ++ buf->offset[i], buf->dma_bufs[i]->size); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(adf_format_validate_yuv); ++ ++/** ++ * adf_modeinfo_set_name - sets the name of a mode from its display resolution ++ * ++ * @mode: mode ++ * ++ * adf_modeinfo_set_name() fills in @mode->name in the format ++ * "[hdisplay]x[vdisplay](i)". It is intended to help drivers create ++ * ADF/DRM-style modelists from other mode formats. ++ */ ++void adf_modeinfo_set_name(struct drm_mode_modeinfo *mode) ++{ ++ bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; ++ ++ snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d%s", ++ mode->hdisplay, mode->vdisplay, ++ interlaced ? "i" : ""); ++} ++EXPORT_SYMBOL(adf_modeinfo_set_name); ++ ++/** ++ * adf_modeinfo_set_vrefresh - sets the vrefresh of a mode from its other ++ * timing data ++ * ++ * @mode: mode ++ * ++ * adf_modeinfo_set_vrefresh() calculates @mode->vrefresh from ++ * @mode->{h,v}display and @mode->flags. It is intended to help drivers ++ * create ADF/DRM-style modelists from other mode formats. ++ */ ++void adf_modeinfo_set_vrefresh(struct drm_mode_modeinfo *mode) ++{ ++ int refresh = 0; ++ unsigned int calc_val; ++ ++ if (mode->vrefresh > 0) ++ return; ++ ++ if (mode->htotal <= 0 || mode->vtotal <= 0) ++ return; ++ ++ /* work out vrefresh the value will be x1000 */ ++ calc_val = (mode->clock * 1000); ++ calc_val /= mode->htotal; ++ refresh = (calc_val + mode->vtotal / 2) / mode->vtotal; ++ ++ if (mode->flags & DRM_MODE_FLAG_INTERLACE) ++ refresh *= 2; ++ if (mode->flags & DRM_MODE_FLAG_DBLSCAN) ++ refresh /= 2; ++ if (mode->vscan > 1) ++ refresh /= mode->vscan; ++ ++ mode->vrefresh = refresh; ++} ++EXPORT_SYMBOL(adf_modeinfo_set_vrefresh); ++ ++static int __init adf_init(void) ++{ ++ int err; ++ ++ err = adf_sysfs_init(); ++ if (err < 0) ++ return err; ++ ++ return 0; ++} ++ ++static void __exit adf_exit(void) ++{ ++ adf_sysfs_destroy(); ++} ++ ++module_init(adf_init); ++module_exit(adf_exit); +diff --git a/drivers/video/adf/adf.h b/drivers/video/adf/adf.h +new file mode 100644 +index 0000000..3bcf1fa +--- /dev/null ++++ b/drivers/video/adf/adf.h +@@ -0,0 +1,71 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __VIDEO_ADF_ADF_H ++#define __VIDEO_ADF_ADF_H ++ ++#include <linux/idr.h> ++#include <linux/list.h> ++#include <video/adf.h> ++#include "sync.h" ++ ++struct adf_event_refcount { ++ struct rb_node node; ++ enum adf_event_type type; ++ int refcount; ++}; ++ ++void adf_buffer_cleanup(struct adf_buffer *buf); ++void adf_buffer_mapping_cleanup(struct adf_buffer_mapping *mapping, ++ struct adf_buffer *buf); ++void adf_post_cleanup(struct adf_device *dev, struct adf_pending_post *post); ++ ++struct adf_attachment_list *adf_attachment_find(struct list_head *list, ++ struct adf_overlay_engine *eng, struct adf_interface *intf); ++int adf_attachment_validate(struct adf_device *dev, ++ struct adf_overlay_engine *eng, struct adf_interface *intf); ++void adf_attachment_free(struct adf_attachment_list *attachment); ++ ++struct adf_event_refcount *adf_obj_find_event_refcount(struct adf_obj *obj, ++ enum adf_event_type type); ++ ++static inline int adf_obj_check_supports_event(struct adf_obj *obj, ++ enum adf_event_type type) ++{ ++ if (!obj->ops || !obj->ops->supports_event) ++ return -EOPNOTSUPP; ++ if (!obj->ops->supports_event(obj, type)) ++ return -EINVAL; ++ return 0; ++} ++ ++static inline int adf_device_attach_op(struct adf_device *dev, ++ struct adf_overlay_engine *eng, struct adf_interface *intf) ++{ ++ if (!dev->ops->attach) ++ return 0; ++ ++ return dev->ops->attach(dev, eng, intf); ++} ++ ++static inline int adf_device_detach_op(struct adf_device *dev, ++ struct adf_overlay_engine *eng, struct adf_interface *intf) ++{ ++ if (!dev->ops->detach) ++ return 0; ++ ++ return dev->ops->detach(dev, eng, intf); ++} ++ ++#endif /* __VIDEO_ADF_ADF_H */ +diff --git a/drivers/video/adf/adf_client.c b/drivers/video/adf/adf_client.c +new file mode 100644 +index 0000000..8061d8e +--- /dev/null ++++ b/drivers/video/adf/adf_client.c +@@ -0,0 +1,811 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/kthread.h> ++#include <linux/mutex.h> ++#include <linux/slab.h> ++ ++#include "sw_sync.h" ++ ++#include <video/adf.h> ++#include <video/adf_client.h> ++#include <video/adf_format.h> ++ ++#include "adf.h" ++ ++static inline bool vsync_active(u8 state) ++{ ++ return state == DRM_MODE_DPMS_ON || state == DRM_MODE_DPMS_STANDBY; ++} ++ ++/** ++ * adf_interface_blank - set interface's DPMS state ++ * ++ * @intf: the interface ++ * @state: one of %DRM_MODE_DPMS_* ++ * ++ * Returns 0 on success or -errno on failure. ++ */ ++int adf_interface_blank(struct adf_interface *intf, u8 state) ++{ ++ struct adf_device *dev = adf_interface_parent(intf); ++ u8 prev_state; ++ bool disable_vsync; ++ bool enable_vsync; ++ int ret = 0; ++ struct adf_event_refcount *vsync_refcount; ++ ++ if (!intf->ops || !intf->ops->blank) ++ return -EOPNOTSUPP; ++ ++ if (state > DRM_MODE_DPMS_OFF) ++ return -EINVAL; ++ ++ mutex_lock(&dev->client_lock); ++ if (state != DRM_MODE_DPMS_ON) ++ flush_kthread_worker(&dev->post_worker); ++ mutex_lock(&intf->base.event_lock); ++ ++ vsync_refcount = adf_obj_find_event_refcount(&intf->base, ++ ADF_EVENT_VSYNC); ++ if (!vsync_refcount) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ prev_state = intf->dpms_state; ++ if (prev_state == state) { ++ ret = -EBUSY; ++ goto done; ++ } ++ ++ disable_vsync = vsync_active(prev_state) && ++ !vsync_active(state) && ++ vsync_refcount->refcount; ++ enable_vsync = !vsync_active(prev_state) && ++ vsync_active(state) && ++ vsync_refcount->refcount; ++ ++ if (disable_vsync) ++ intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC, ++ false); ++ ++ ret = intf->ops->blank(intf, state); ++ if (ret < 0) { ++ if (disable_vsync) ++ intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC, ++ true); ++ goto done; ++ } ++ ++ if (enable_vsync) ++ intf->base.ops->set_event(&intf->base, ADF_EVENT_VSYNC, ++ true); ++ ++ intf->dpms_state = state; ++done: ++ mutex_unlock(&intf->base.event_lock); ++ mutex_unlock(&dev->client_lock); ++ return ret; ++} ++EXPORT_SYMBOL(adf_interface_blank); ++ ++/** ++ * adf_interface_blank - get interface's current DPMS state ++ * ++ * @intf: the interface ++ * ++ * Returns one of %DRM_MODE_DPMS_*. ++ */ ++u8 adf_interface_dpms_state(struct adf_interface *intf) ++{ ++ struct adf_device *dev = adf_interface_parent(intf); ++ u8 dpms_state; ++ ++ mutex_lock(&dev->client_lock); ++ dpms_state = intf->dpms_state; ++ mutex_unlock(&dev->client_lock); ++ ++ return dpms_state; ++} ++EXPORT_SYMBOL(adf_interface_dpms_state); ++ ++/** ++ * adf_interface_current_mode - get interface's current display mode ++ * ++ * @intf: the interface ++ * @mode: returns the current mode ++ */ ++void adf_interface_current_mode(struct adf_interface *intf, ++ struct drm_mode_modeinfo *mode) ++{ ++ struct adf_device *dev = adf_interface_parent(intf); ++ ++ mutex_lock(&dev->client_lock); ++ memcpy(mode, &intf->current_mode, sizeof(*mode)); ++ mutex_unlock(&dev->client_lock); ++} ++EXPORT_SYMBOL(adf_interface_current_mode); ++ ++/** ++ * adf_interface_modelist - get interface's modelist ++ * ++ * @intf: the interface ++ * @modelist: storage for the modelist (optional) ++ * @n_modes: length of @modelist ++ * ++ * If @modelist is not NULL, adf_interface_modelist() will copy up to @n_modes ++ * modelist entries into @modelist. ++ * ++ * Returns the length of the modelist. ++ */ ++size_t adf_interface_modelist(struct adf_interface *intf, ++ struct drm_mode_modeinfo *modelist, size_t n_modes) ++{ ++ unsigned long flags; ++ size_t retval; ++ ++ read_lock_irqsave(&intf->hotplug_modelist_lock, flags); ++ if (modelist) ++ memcpy(modelist, intf->modelist, sizeof(modelist[0]) * ++ min(n_modes, intf->n_modes)); ++ retval = intf->n_modes; ++ read_unlock_irqrestore(&intf->hotplug_modelist_lock, flags); ++ ++ return retval; ++} ++EXPORT_SYMBOL(adf_interface_modelist); ++ ++/** ++ * adf_interface_set_mode - set interface's display mode ++ * ++ * @intf: the interface ++ * @mode: the new mode ++ * ++ * Returns 0 on success or -errno on failure. ++ */ ++int adf_interface_set_mode(struct adf_interface *intf, ++ struct drm_mode_modeinfo *mode) ++{ ++ struct adf_device *dev = adf_interface_parent(intf); ++ int ret = 0; ++ ++ if (!intf->ops || !intf->ops->modeset) ++ return -EOPNOTSUPP; ++ ++ mutex_lock(&dev->client_lock); ++ flush_kthread_worker(&dev->post_worker); ++ ++ ret = intf->ops->modeset(intf, mode); ++ if (ret < 0) ++ goto done; ++ ++ memcpy(&intf->current_mode, mode, sizeof(*mode)); ++done: ++ mutex_unlock(&dev->client_lock); ++ return ret; ++} ++EXPORT_SYMBOL(adf_interface_set_mode); ++ ++/** ++ * adf_interface_screen_size - get size of screen connected to interface ++ * ++ * @intf: the interface ++ * @width_mm: returns the screen width in mm ++ * @height_mm: returns the screen width in mm ++ * ++ * Returns 0 on success or -errno on failure. ++ */ ++int adf_interface_get_screen_size(struct adf_interface *intf, u16 *width_mm, ++ u16 *height_mm) ++{ ++ struct adf_device *dev = adf_interface_parent(intf); ++ int ret; ++ ++ if (!intf->ops || !intf->ops->screen_size) ++ return -EOPNOTSUPP; ++ ++ mutex_lock(&dev->client_lock); ++ ret = intf->ops->screen_size(intf, width_mm, height_mm); ++ mutex_unlock(&dev->client_lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL(adf_interface_get_screen_size); ++ ++/** ++ * adf_overlay_engine_supports_format - returns whether a format is in an ++ * overlay engine's supported list ++ * ++ * @eng: the overlay engine ++ * @format: format fourcc ++ */ ++bool adf_overlay_engine_supports_format(struct adf_overlay_engine *eng, ++ u32 format) ++{ ++ size_t i; ++ for (i = 0; i < eng->ops->n_supported_formats; i++) ++ if (format == eng->ops->supported_formats[i]) ++ return true; ++ ++ return false; ++} ++EXPORT_SYMBOL(adf_overlay_engine_supports_format); ++ ++static int adf_buffer_validate(struct adf_buffer *buf) ++{ ++ struct adf_overlay_engine *eng = buf->overlay_engine; ++ struct device *dev = &eng->base.dev; ++ struct adf_device *parent = adf_overlay_engine_parent(eng); ++ u8 hsub, vsub, num_planes, cpp[ADF_MAX_PLANES], i; ++ ++ if (!adf_overlay_engine_supports_format(eng, buf->format)) { ++ char format_str[ADF_FORMAT_STR_SIZE]; ++ adf_format_str(buf->format, format_str); ++ dev_err(dev, "unsupported format %s\n", format_str); ++ return -EINVAL; ++ } ++ ++ if (!adf_format_is_standard(buf->format)) ++ return parent->ops->validate_custom_format(parent, buf); ++ ++ hsub = adf_format_horz_chroma_subsampling(buf->format); ++ vsub = adf_format_vert_chroma_subsampling(buf->format); ++ num_planes = adf_format_num_planes(buf->format); ++ for (i = 0; i < num_planes; i++) ++ cpp[i] = adf_format_plane_cpp(buf->format, i); ++ ++ return adf_format_validate_yuv(parent, buf, num_planes, hsub, vsub, ++ cpp); ++} ++ ++static int adf_buffer_map(struct adf_device *dev, struct adf_buffer *buf, ++ struct adf_buffer_mapping *mapping) ++{ ++ int ret = 0; ++ size_t i; ++ ++ for (i = 0; i < buf->n_planes; i++) { ++ struct dma_buf_attachment *attachment; ++ struct sg_table *sg_table; ++ ++ attachment = dma_buf_attach(buf->dma_bufs[i], dev->dev); ++ if (IS_ERR(attachment)) { ++ ret = PTR_ERR(attachment); ++ dev_err(&dev->base.dev, "attaching plane %zu failed: %d\n", ++ i, ret); ++ goto done; ++ } ++ mapping->attachments[i] = attachment; ++ ++ sg_table = dma_buf_map_attachment(attachment, DMA_TO_DEVICE); ++ if (IS_ERR(sg_table)) { ++ ret = PTR_ERR(sg_table); ++ dev_err(&dev->base.dev, "mapping plane %zu failed: %d", ++ i, ret); ++ goto done; ++ } else if (!sg_table) { ++ ret = -ENOMEM; ++ dev_err(&dev->base.dev, "mapping plane %zu failed\n", ++ i); ++ goto done; ++ } ++ mapping->sg_tables[i] = sg_table; ++ } ++ ++done: ++ if (ret < 0) ++ adf_buffer_mapping_cleanup(mapping, buf); ++ ++ return ret; ++} ++ ++static struct sync_fence *adf_sw_complete_fence(struct adf_device *dev) ++{ ++ struct sync_pt *pt; ++ struct sync_fence *complete_fence; ++ ++ if (!dev->timeline) { ++ dev->timeline = sw_sync_timeline_create(dev->base.name); ++ if (!dev->timeline) ++ return ERR_PTR(-ENOMEM); ++ dev->timeline_max = 1; ++ } ++ ++ dev->timeline_max++; ++ pt = sw_sync_pt_create(dev->timeline, dev->timeline_max); ++ if (!pt) ++ goto err_pt_create; ++ complete_fence = sync_fence_create(dev->base.name, pt); ++ if (!complete_fence) ++ goto err_fence_create; ++ ++ return complete_fence; ++ ++err_fence_create: ++ sync_pt_free(pt); ++err_pt_create: ++ dev->timeline_max--; ++ return ERR_PTR(-ENOSYS); ++} ++ ++/** ++ * adf_device_post - flip to a new set of buffers ++ * ++ * @dev: device targeted by the flip ++ * @intfs: interfaces targeted by the flip ++ * @n_intfs: number of targeted interfaces ++ * @bufs: description of buffers displayed ++ * @n_bufs: number of buffers displayed ++ * @custom_data: driver-private data ++ * @custom_data_size: size of driver-private data ++ * ++ * adf_device_post() will copy @intfs, @bufs, and @custom_data, so they may ++ * point to variables on the stack. adf_device_post() also takes its own ++ * reference on each of the dma-bufs in @bufs. The adf_device_post_nocopy() ++ * variant transfers ownership of these resources to ADF instead. ++ * ++ * On success, returns a sync fence which signals when the buffers are removed ++ * from the screen. On failure, returns ERR_PTR(-errno). ++ */ ++struct sync_fence *adf_device_post(struct adf_device *dev, ++ struct adf_interface **intfs, size_t n_intfs, ++ struct adf_buffer *bufs, size_t n_bufs, void *custom_data, ++ size_t custom_data_size) ++{ ++ struct adf_interface **intfs_copy = NULL; ++ struct adf_buffer *bufs_copy = NULL; ++ void *custom_data_copy = NULL; ++ struct sync_fence *ret; ++ size_t i; ++ ++ intfs_copy = kzalloc(sizeof(intfs_copy[0]) * n_intfs, GFP_KERNEL); ++ if (!intfs_copy) ++ return ERR_PTR(-ENOMEM); ++ ++ bufs_copy = kzalloc(sizeof(bufs_copy[0]) * n_bufs, GFP_KERNEL); ++ if (!bufs_copy) { ++ ret = ERR_PTR(-ENOMEM); ++ goto err_alloc; ++ } ++ ++ custom_data_copy = kzalloc(custom_data_size, GFP_KERNEL); ++ if (!custom_data_copy) { ++ ret = ERR_PTR(-ENOMEM); ++ goto err_alloc; ++ } ++ ++ for (i = 0; i < n_bufs; i++) { ++ size_t j; ++ for (j = 0; j < bufs[i].n_planes; j++) ++ get_dma_buf(bufs[i].dma_bufs[j]); ++ } ++ ++ memcpy(intfs_copy, intfs, sizeof(intfs_copy[0]) * n_intfs); ++ memcpy(bufs_copy, bufs, sizeof(bufs_copy[0]) * n_bufs); ++ memcpy(custom_data_copy, custom_data, custom_data_size); ++ ++ ret = adf_device_post_nocopy(dev, intfs_copy, n_intfs, bufs_copy, ++ n_bufs, custom_data_copy, custom_data_size); ++ if (IS_ERR(ret)) ++ goto err_post; ++ ++ return ret; ++ ++err_post: ++ for (i = 0; i < n_bufs; i++) { ++ size_t j; ++ for (j = 0; j < bufs[i].n_planes; j++) ++ dma_buf_put(bufs[i].dma_bufs[j]); ++ } ++err_alloc: ++ kfree(custom_data_copy); ++ kfree(bufs_copy); ++ kfree(intfs_copy); ++ return ret; ++} ++EXPORT_SYMBOL(adf_device_post); ++ ++/** ++ * adf_device_post_nocopy - flip to a new set of buffers ++ * ++ * adf_device_post_nocopy() has the same behavior as adf_device_post(), ++ * except ADF does not copy @intfs, @bufs, or @custom_data, and it does ++ * not take an extra reference on the dma-bufs in @bufs. ++ * ++ * @intfs, @bufs, and @custom_data must point to buffers allocated by ++ * kmalloc(). On success, ADF takes ownership of these buffers and the dma-bufs ++ * in @bufs, and will kfree()/dma_buf_put() them when they are no longer needed. ++ * On failure, adf_device_post_nocopy() does NOT take ownership of these ++ * buffers or the dma-bufs, and the caller must clean them up. ++ * ++ * adf_device_post_nocopy() is mainly intended for implementing ADF's ioctls. ++ * Clients may find the nocopy variant useful in limited cases, but most should ++ * call adf_device_post() instead. ++ */ ++struct sync_fence *adf_device_post_nocopy(struct adf_device *dev, ++ struct adf_interface **intfs, size_t n_intfs, ++ struct adf_buffer *bufs, size_t n_bufs, ++ void *custom_data, size_t custom_data_size) ++{ ++ struct adf_pending_post *cfg; ++ struct adf_buffer_mapping *mappings; ++ struct sync_fence *ret; ++ size_t i; ++ int err; ++ ++ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); ++ if (!cfg) ++ return ERR_PTR(-ENOMEM); ++ ++ mappings = kzalloc(sizeof(mappings[0]) * n_bufs, GFP_KERNEL); ++ if (!mappings) { ++ ret = ERR_PTR(-ENOMEM); ++ goto err_alloc; ++ } ++ ++ mutex_lock(&dev->client_lock); ++ ++ for (i = 0; i < n_bufs; i++) { ++ err = adf_buffer_validate(&bufs[i]); ++ if (err < 0) { ++ ret = ERR_PTR(err); ++ goto err_buf; ++ } ++ ++ err = adf_buffer_map(dev, &bufs[i], &mappings[i]); ++ if (err < 0) { ++ ret = ERR_PTR(err); ++ goto err_buf; ++ } ++ } ++ ++ INIT_LIST_HEAD(&cfg->head); ++ cfg->config.n_bufs = n_bufs; ++ cfg->config.bufs = bufs; ++ cfg->config.mappings = mappings; ++ cfg->config.custom_data = custom_data; ++ cfg->config.custom_data_size = custom_data_size; ++ ++ err = dev->ops->validate(dev, &cfg->config, &cfg->state); ++ if (err < 0) { ++ ret = ERR_PTR(err); ++ goto err_buf; ++ } ++ ++ mutex_lock(&dev->post_lock); ++ ++ if (dev->ops->complete_fence) ++ ret = dev->ops->complete_fence(dev, &cfg->config, ++ cfg->state); ++ else ++ ret = adf_sw_complete_fence(dev); ++ ++ if (IS_ERR(ret)) ++ goto err_fence; ++ ++ list_add_tail(&cfg->head, &dev->post_list); ++ queue_kthread_work(&dev->post_worker, &dev->post_work); ++ mutex_unlock(&dev->post_lock); ++ mutex_unlock(&dev->client_lock); ++ kfree(intfs); ++ return ret; ++ ++err_fence: ++ mutex_unlock(&dev->post_lock); ++ ++err_buf: ++ for (i = 0; i < n_bufs; i++) ++ adf_buffer_mapping_cleanup(&mappings[i], &bufs[i]); ++ ++ mutex_unlock(&dev->client_lock); ++ kfree(mappings); ++ ++err_alloc: ++ kfree(cfg); ++ return ret; ++} ++EXPORT_SYMBOL(adf_device_post_nocopy); ++ ++static void adf_attachment_list_to_array(struct adf_device *dev, ++ struct list_head *src, struct adf_attachment *dst, size_t size) ++{ ++ struct adf_attachment_list *entry; ++ size_t i = 0; ++ ++ if (!dst) ++ return; ++ ++ list_for_each_entry(entry, src, head) { ++ if (i == size) ++ return; ++ dst[i] = entry->attachment; ++ i++; ++ } ++} ++ ++/** ++ * adf_device_attachments - get device's list of active attachments ++ * ++ * @dev: the device ++ * @attachments: storage for the attachment list (optional) ++ * @n_attachments: length of @attachments ++ * ++ * If @attachments is not NULL, adf_device_attachments() will copy up to ++ * @n_attachments entries into @attachments. ++ * ++ * Returns the length of the active attachment list. ++ */ ++size_t adf_device_attachments(struct adf_device *dev, ++ struct adf_attachment *attachments, size_t n_attachments) ++{ ++ size_t retval; ++ ++ mutex_lock(&dev->client_lock); ++ adf_attachment_list_to_array(dev, &dev->attached, attachments, ++ n_attachments); ++ retval = dev->n_attached; ++ mutex_unlock(&dev->client_lock); ++ ++ return retval; ++} ++EXPORT_SYMBOL(adf_device_attachments); ++ ++/** ++ * adf_device_attachments_allowed - get device's list of allowed attachments ++ * ++ * @dev: the device ++ * @attachments: storage for the attachment list (optional) ++ * @n_attachments: length of @attachments ++ * ++ * If @attachments is not NULL, adf_device_attachments_allowed() will copy up to ++ * @n_attachments entries into @attachments. ++ * ++ * Returns the length of the allowed attachment list. ++ */ ++size_t adf_device_attachments_allowed(struct adf_device *dev, ++ struct adf_attachment *attachments, size_t n_attachments) ++{ ++ size_t retval; ++ ++ mutex_lock(&dev->client_lock); ++ adf_attachment_list_to_array(dev, &dev->attach_allowed, attachments, ++ n_attachments); ++ retval = dev->n_attach_allowed; ++ mutex_unlock(&dev->client_lock); ++ ++ return retval; ++} ++EXPORT_SYMBOL(adf_device_attachments_allowed); ++ ++/** ++ * adf_device_attached - return whether an overlay engine and interface are ++ * attached ++ * ++ * @dev: the parent device ++ * @eng: the overlay engine ++ * @intf: the interface ++ */ ++bool adf_device_attached(struct adf_device *dev, struct adf_overlay_engine *eng, ++ struct adf_interface *intf) ++{ ++ struct adf_attachment_list *attachment; ++ ++ mutex_lock(&dev->client_lock); ++ attachment = adf_attachment_find(&dev->attached, eng, intf); ++ mutex_unlock(&dev->client_lock); ++ ++ return attachment != NULL; ++} ++EXPORT_SYMBOL(adf_device_attached); ++ ++/** ++ * adf_device_attach_allowed - return whether the ADF device supports attaching ++ * an overlay engine and interface ++ * ++ * @dev: the parent device ++ * @eng: the overlay engine ++ * @intf: the interface ++ */ ++bool adf_device_attach_allowed(struct adf_device *dev, ++ struct adf_overlay_engine *eng, struct adf_interface *intf) ++{ ++ struct adf_attachment_list *attachment; ++ ++ mutex_lock(&dev->client_lock); ++ attachment = adf_attachment_find(&dev->attach_allowed, eng, intf); ++ mutex_unlock(&dev->client_lock); ++ ++ return attachment != NULL; ++} ++EXPORT_SYMBOL(adf_device_attach_allowed); ++/** ++ * adf_device_attach - attach an overlay engine to an interface ++ * ++ * @dev: the parent device ++ * @eng: the overlay engine ++ * @intf: the interface ++ * ++ * Returns 0 on success, -%EINVAL if attaching @intf and @eng is not allowed, ++ * -%EALREADY if @intf and @eng are already attached, or -errno on any other ++ * failure. ++ */ ++int adf_device_attach(struct adf_device *dev, struct adf_overlay_engine *eng, ++ struct adf_interface *intf) ++{ ++ int ret; ++ struct adf_attachment_list *attachment = NULL; ++ ++ ret = adf_attachment_validate(dev, eng, intf); ++ if (ret < 0) ++ return ret; ++ ++ mutex_lock(&dev->client_lock); ++ ++ if (dev->n_attached == ADF_MAX_ATTACHMENTS) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ if (!adf_attachment_find(&dev->attach_allowed, eng, intf)) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ if (adf_attachment_find(&dev->attached, eng, intf)) { ++ ret = -EALREADY; ++ goto done; ++ } ++ ++ ret = adf_device_attach_op(dev, eng, intf); ++ if (ret < 0) ++ goto done; ++ ++ attachment = kzalloc(sizeof(*attachment), GFP_KERNEL); ++ if (!attachment) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ attachment->attachment.interface = intf; ++ attachment->attachment.overlay_engine = eng; ++ list_add_tail(&attachment->head, &dev->attached); ++ dev->n_attached++; ++ ++done: ++ mutex_unlock(&dev->client_lock); ++ if (ret < 0) ++ kfree(attachment); ++ ++ return ret; ++} ++EXPORT_SYMBOL(adf_device_attach); ++ ++/** ++ * adf_device_detach - detach an overlay engine from an interface ++ * ++ * @dev: the parent device ++ * @eng: the overlay engine ++ * @intf: the interface ++ * ++ * Returns 0 on success, -%EINVAL if @intf and @eng are not attached, ++ * or -errno on any other failure. ++ */ ++int adf_device_detach(struct adf_device *dev, struct adf_overlay_engine *eng, ++ struct adf_interface *intf) ++{ ++ int ret; ++ struct adf_attachment_list *attachment; ++ ++ ret = adf_attachment_validate(dev, eng, intf); ++ if (ret < 0) ++ return ret; ++ ++ mutex_lock(&dev->client_lock); ++ ++ attachment = adf_attachment_find(&dev->attached, eng, intf); ++ if (!attachment) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ ret = adf_device_detach_op(dev, eng, intf); ++ if (ret < 0) ++ goto done; ++ ++ adf_attachment_free(attachment); ++ dev->n_attached--; ++done: ++ mutex_unlock(&dev->client_lock); ++ return ret; ++} ++EXPORT_SYMBOL(adf_device_detach); ++ ++/** ++ * adf_interface_simple_buffer_alloc - allocate a simple buffer ++ * ++ * @intf: target interface ++ * @w: width in pixels ++ * @h: height in pixels ++ * @format: format fourcc ++ * @dma_buf: returns the allocated buffer ++ * @offset: returns the byte offset of the allocated buffer's first pixel ++ * @pitch: returns the allocated buffer's pitch ++ * ++ * See &struct adf_simple_buffer_alloc for a description of simple buffers and ++ * their limitations. ++ * ++ * Returns 0 on success or -errno on failure. ++ */ ++int adf_interface_simple_buffer_alloc(struct adf_interface *intf, u16 w, u16 h, ++ u32 format, struct dma_buf **dma_buf, u32 *offset, u32 *pitch) ++{ ++ if (!intf->ops || !intf->ops->alloc_simple_buffer) ++ return -EOPNOTSUPP; ++ ++ if (!adf_format_is_rgb(format)) ++ return -EINVAL; ++ ++ return intf->ops->alloc_simple_buffer(intf, w, h, format, dma_buf, ++ offset, pitch); ++} ++EXPORT_SYMBOL(adf_interface_simple_buffer_alloc); ++ ++/** ++ * adf_interface_simple_post - flip to a single buffer ++ * ++ * @intf: interface targeted by the flip ++ * @buf: buffer to display ++ * ++ * adf_interface_simple_post() can be used generically for simple display ++ * configurations, since the client does not need to provide any driver-private ++ * configuration data. ++ * ++ * adf_interface_simple_post() has the same copying semantics as ++ * adf_device_post(). ++ * ++ * On success, returns a sync fence which signals when the buffer is removed ++ * from the screen. On failure, returns ERR_PTR(-errno). ++ */ ++struct sync_fence *adf_interface_simple_post(struct adf_interface *intf, ++ struct adf_buffer *buf) ++{ ++ size_t custom_data_size = 0; ++ void *custom_data = NULL; ++ struct sync_fence *ret; ++ ++ if (intf->ops && intf->ops->describe_simple_post) { ++ int err; ++ ++ custom_data = kzalloc(ADF_MAX_CUSTOM_DATA_SIZE, GFP_KERNEL); ++ if (!custom_data) { ++ ret = ERR_PTR(-ENOMEM); ++ goto done; ++ } ++ ++ err = intf->ops->describe_simple_post(intf, buf, custom_data, ++ &custom_data_size); ++ if (err < 0) { ++ ret = ERR_PTR(err); ++ goto done; ++ } ++ } ++ ++ ret = adf_device_post(adf_interface_parent(intf), &intf, 1, buf, 1, ++ custom_data, custom_data_size); ++done: ++ kfree(custom_data); ++ return ret; ++} ++EXPORT_SYMBOL(adf_interface_simple_post); +diff --git a/drivers/video/adf/adf_fbdev.c b/drivers/video/adf/adf_fbdev.c +new file mode 100644 +index 0000000..a5b53bc +--- /dev/null ++++ b/drivers/video/adf/adf_fbdev.c +@@ -0,0 +1,665 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/vmalloc.h> ++ ++#include <video/adf.h> ++#include <video/adf_client.h> ++#include <video/adf_fbdev.h> ++#include <video/adf_format.h> ++ ++#include "adf.h" ++ ++struct adf_fbdev_format { ++ u32 fourcc; ++ u32 bpp; ++ u32 r_length; ++ u32 g_length; ++ u32 b_length; ++ u32 a_length; ++ u32 r_offset; ++ u32 g_offset; ++ u32 b_offset; ++ u32 a_offset; ++}; ++ ++static const struct adf_fbdev_format format_table[] = { ++ {DRM_FORMAT_RGB332, 8, 3, 3, 2, 0, 5, 2, 0, 0}, ++ {DRM_FORMAT_BGR233, 8, 3, 3, 2, 0, 0, 3, 5, 0}, ++ ++ {DRM_FORMAT_XRGB4444, 16, 4, 4, 4, 0, 8, 4, 0, 0}, ++ {DRM_FORMAT_XBGR4444, 16, 4, 4, 4, 0, 0, 4, 8, 0}, ++ {DRM_FORMAT_RGBX4444, 16, 4, 4, 4, 0, 12, 8, 4, 0}, ++ {DRM_FORMAT_BGRX4444, 16, 4, 4, 4, 0, 0, 4, 8, 0}, ++ ++ {DRM_FORMAT_ARGB4444, 16, 4, 4, 4, 4, 8, 4, 0, 12}, ++ {DRM_FORMAT_ABGR4444, 16, 4, 4, 4, 4, 0, 4, 8, 12}, ++ {DRM_FORMAT_RGBA4444, 16, 4, 4, 4, 4, 12, 8, 4, 0}, ++ {DRM_FORMAT_BGRA4444, 16, 4, 4, 4, 4, 0, 4, 8, 0}, ++ ++ {DRM_FORMAT_XRGB1555, 16, 5, 5, 5, 0, 10, 5, 0, 0}, ++ {DRM_FORMAT_XBGR1555, 16, 5, 5, 5, 0, 0, 5, 10, 0}, ++ {DRM_FORMAT_RGBX5551, 16, 5, 5, 5, 0, 11, 6, 1, 0}, ++ {DRM_FORMAT_BGRX5551, 16, 5, 5, 5, 0, 1, 6, 11, 0}, ++ ++ {DRM_FORMAT_ARGB1555, 16, 5, 5, 5, 1, 10, 5, 0, 15}, ++ {DRM_FORMAT_ABGR1555, 16, 5, 5, 5, 1, 0, 5, 10, 15}, ++ {DRM_FORMAT_RGBA5551, 16, 5, 5, 5, 1, 11, 6, 1, 0}, ++ {DRM_FORMAT_BGRA5551, 16, 5, 5, 5, 1, 1, 6, 11, 0}, ++ ++ {DRM_FORMAT_RGB565, 16, 5, 6, 5, 0, 11, 5, 0, 0}, ++ {DRM_FORMAT_BGR565, 16, 5, 6, 5, 0, 0, 5, 11, 0}, ++ ++ {DRM_FORMAT_RGB888, 24, 8, 8, 8, 0, 16, 8, 0, 0}, ++ {DRM_FORMAT_BGR888, 24, 8, 8, 8, 0, 0, 8, 16, 0}, ++ ++ {DRM_FORMAT_XRGB8888, 32, 8, 8, 8, 0, 16, 8, 0, 0}, ++ {DRM_FORMAT_XBGR8888, 32, 8, 8, 8, 0, 0, 8, 16, 0}, ++ {DRM_FORMAT_RGBX8888, 32, 8, 8, 8, 0, 24, 16, 8, 0}, ++ {DRM_FORMAT_BGRX8888, 32, 8, 8, 8, 0, 8, 16, 24, 0}, ++ ++ {DRM_FORMAT_ARGB8888, 32, 8, 8, 8, 8, 16, 8, 0, 24}, ++ {DRM_FORMAT_ABGR8888, 32, 8, 8, 8, 8, 0, 8, 16, 24}, ++ {DRM_FORMAT_RGBA8888, 32, 8, 8, 8, 8, 24, 16, 8, 0}, ++ {DRM_FORMAT_BGRA8888, 32, 8, 8, 8, 8, 8, 16, 24, 0}, ++ ++ {DRM_FORMAT_XRGB2101010, 32, 10, 10, 10, 0, 20, 10, 0, 0}, ++ {DRM_FORMAT_XBGR2101010, 32, 10, 10, 10, 0, 0, 10, 20, 0}, ++ {DRM_FORMAT_RGBX1010102, 32, 10, 10, 10, 0, 22, 12, 2, 0}, ++ {DRM_FORMAT_BGRX1010102, 32, 10, 10, 10, 0, 2, 12, 22, 0}, ++ ++ {DRM_FORMAT_ARGB2101010, 32, 10, 10, 10, 2, 20, 10, 0, 30}, ++ {DRM_FORMAT_ABGR2101010, 32, 10, 10, 10, 2, 0, 10, 20, 30}, ++ {DRM_FORMAT_RGBA1010102, 32, 10, 10, 10, 2, 22, 12, 2, 0}, ++ {DRM_FORMAT_BGRA1010102, 32, 10, 10, 10, 2, 2, 12, 22, 0}, ++}; ++ ++static u32 drm_fourcc_from_fb_var(struct fb_var_screeninfo *var) ++{ ++ size_t i; ++ for (i = 0; i < ARRAY_SIZE(format_table); i++) { ++ const struct adf_fbdev_format *f = &format_table[i]; ++ if (var->red.length == f->r_length && ++ var->red.offset == f->r_offset && ++ var->green.length == f->g_length && ++ var->green.offset == f->g_offset && ++ var->blue.length == f->b_length && ++ var->blue.offset == f->b_offset && ++ var->transp.length == f->a_length && ++ (var->transp.length == 0 || ++ var->transp.offset == f->a_offset)) ++ return f->fourcc; ++ } ++ ++ return 0; ++} ++ ++static const struct adf_fbdev_format *fbdev_format_info(u32 format) ++{ ++ size_t i; ++ for (i = 0; i < ARRAY_SIZE(format_table); i++) { ++ const struct adf_fbdev_format *f = &format_table[i]; ++ if (f->fourcc == format) ++ return f; ++ } ++ ++ BUG(); ++} ++ ++void adf_modeinfo_to_fb_videomode(const struct drm_mode_modeinfo *mode, ++ struct fb_videomode *vmode) ++{ ++ memset(vmode, 0, sizeof(*vmode)); ++ ++ vmode->refresh = mode->vrefresh; ++ ++ vmode->xres = mode->hdisplay; ++ vmode->yres = mode->vdisplay; ++ ++ vmode->pixclock = mode->clock ? KHZ2PICOS(mode->clock) : 0; ++ vmode->left_margin = mode->htotal - mode->hsync_end; ++ vmode->right_margin = mode->hsync_start - mode->hdisplay; ++ vmode->upper_margin = mode->vtotal - mode->vsync_end; ++ vmode->lower_margin = mode->vsync_start - mode->vdisplay; ++ vmode->hsync_len = mode->hsync_end - mode->hsync_start; ++ vmode->vsync_len = mode->vsync_end - mode->vsync_start; ++ ++ vmode->sync = 0; ++ if (mode->flags & DRM_MODE_FLAG_PHSYNC) ++ vmode->sync |= FB_SYNC_HOR_HIGH_ACT; ++ if (mode->flags & DRM_MODE_FLAG_PVSYNC) ++ vmode->sync |= FB_SYNC_VERT_HIGH_ACT; ++ if (mode->flags & DRM_MODE_FLAG_PCSYNC) ++ vmode->sync |= FB_SYNC_COMP_HIGH_ACT; ++ if (mode->flags & DRM_MODE_FLAG_BCAST) ++ vmode->sync |= FB_SYNC_BROADCAST; ++ ++ vmode->vmode = 0; ++ if (mode->flags & DRM_MODE_FLAG_INTERLACE) ++ vmode->vmode |= FB_VMODE_INTERLACED; ++ if (mode->flags & DRM_MODE_FLAG_DBLSCAN) ++ vmode->vmode |= FB_VMODE_DOUBLE; ++} ++EXPORT_SYMBOL(adf_modeinfo_to_fb_videomode); ++ ++void adf_modeinfo_from_fb_videomode(const struct fb_videomode *vmode, ++ struct drm_mode_modeinfo *mode) ++{ ++ memset(mode, 0, sizeof(*mode)); ++ ++ mode->hdisplay = vmode->xres; ++ mode->hsync_start = mode->hdisplay + vmode->right_margin; ++ mode->hsync_end = mode->hsync_start + vmode->hsync_len; ++ mode->htotal = mode->hsync_end + vmode->left_margin; ++ ++ mode->vdisplay = vmode->yres; ++ mode->vsync_start = mode->vdisplay + vmode->lower_margin; ++ mode->vsync_end = mode->vsync_start + vmode->vsync_len; ++ mode->vtotal = mode->vsync_end + vmode->upper_margin; ++ ++ mode->clock = vmode->pixclock ? PICOS2KHZ(vmode->pixclock) : 0; ++ ++ mode->flags = 0; ++ if (vmode->sync & FB_SYNC_HOR_HIGH_ACT) ++ mode->flags |= DRM_MODE_FLAG_PHSYNC; ++ if (vmode->sync & FB_SYNC_VERT_HIGH_ACT) ++ mode->flags |= DRM_MODE_FLAG_PVSYNC; ++ if (vmode->sync & FB_SYNC_COMP_HIGH_ACT) ++ mode->flags |= DRM_MODE_FLAG_PCSYNC; ++ if (vmode->sync & FB_SYNC_BROADCAST) ++ mode->flags |= DRM_MODE_FLAG_BCAST; ++ if (vmode->vmode & FB_VMODE_INTERLACED) ++ mode->flags |= DRM_MODE_FLAG_INTERLACE; ++ if (vmode->vmode & FB_VMODE_DOUBLE) ++ mode->flags |= DRM_MODE_FLAG_DBLSCAN; ++ ++ if (vmode->refresh) ++ mode->vrefresh = vmode->refresh; ++ else ++ adf_modeinfo_set_vrefresh(mode); ++ ++ if (vmode->name) ++ strlcpy(mode->name, vmode->name, sizeof(mode->name)); ++ else ++ adf_modeinfo_set_name(mode); ++} ++EXPORT_SYMBOL(adf_modeinfo_from_fb_videomode); ++ ++static int adf_fbdev_post(struct adf_fbdev *fbdev) ++{ ++ struct adf_buffer buf; ++ struct sync_fence *complete_fence; ++ int ret = 0; ++ ++ memset(&buf, 0, sizeof(buf)); ++ buf.overlay_engine = fbdev->eng; ++ buf.w = fbdev->info->var.xres; ++ buf.h = fbdev->info->var.yres; ++ buf.format = fbdev->format; ++ buf.dma_bufs[0] = fbdev->dma_buf; ++ buf.offset[0] = fbdev->offset + ++ fbdev->info->var.yoffset * fbdev->pitch + ++ fbdev->info->var.xoffset * ++ (fbdev->info->var.bits_per_pixel / 8); ++ buf.pitch[0] = fbdev->pitch; ++ buf.n_planes = 1; ++ ++ complete_fence = adf_interface_simple_post(fbdev->intf, &buf); ++ if (IS_ERR(complete_fence)) { ++ ret = PTR_ERR(complete_fence); ++ goto done; ++ } ++ ++ sync_fence_put(complete_fence); ++done: ++ return ret; ++} ++ ++static const u16 vga_palette[][3] = { ++ {0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0xAAAA}, ++ {0x0000, 0xAAAA, 0x0000}, ++ {0x0000, 0xAAAA, 0xAAAA}, ++ {0xAAAA, 0x0000, 0x0000}, ++ {0xAAAA, 0x0000, 0xAAAA}, ++ {0xAAAA, 0x5555, 0x0000}, ++ {0xAAAA, 0xAAAA, 0xAAAA}, ++ {0x5555, 0x5555, 0x5555}, ++ {0x5555, 0x5555, 0xFFFF}, ++ {0x5555, 0xFFFF, 0x5555}, ++ {0x5555, 0xFFFF, 0xFFFF}, ++ {0xFFFF, 0x5555, 0x5555}, ++ {0xFFFF, 0x5555, 0xFFFF}, ++ {0xFFFF, 0xFFFF, 0x5555}, ++ {0xFFFF, 0xFFFF, 0xFFFF}, ++}; ++ ++static int adf_fb_alloc(struct adf_fbdev *fbdev) ++{ ++ int ret; ++ ++ ret = adf_interface_simple_buffer_alloc(fbdev->intf, ++ fbdev->default_xres_virtual, ++ fbdev->default_yres_virtual, ++ fbdev->default_format, ++ &fbdev->dma_buf, &fbdev->offset, &fbdev->pitch); ++ if (ret < 0) { ++ dev_err(fbdev->info->dev, "allocating fb failed: %d\n", ret); ++ return ret; ++ } ++ ++ fbdev->vaddr = dma_buf_vmap(fbdev->dma_buf); ++ if (!fbdev->vaddr) { ++ ret = -ENOMEM; ++ dev_err(fbdev->info->dev, "vmapping fb failed\n"); ++ goto err_vmap; ++ } ++ fbdev->info->fix.line_length = fbdev->pitch; ++ fbdev->info->var.xres_virtual = fbdev->default_xres_virtual; ++ fbdev->info->var.yres_virtual = fbdev->default_yres_virtual; ++ fbdev->info->fix.smem_len = fbdev->dma_buf->size; ++ fbdev->info->screen_base = fbdev->vaddr; ++ ++ return 0; ++ ++err_vmap: ++ dma_buf_put(fbdev->dma_buf); ++ return ret; ++} ++ ++static void adf_fb_destroy(struct adf_fbdev *fbdev) ++{ ++ dma_buf_vunmap(fbdev->dma_buf, fbdev->vaddr); ++ dma_buf_put(fbdev->dma_buf); ++} ++ ++static void adf_fbdev_set_format(struct adf_fbdev *fbdev, u32 format) ++{ ++ size_t i; ++ const struct adf_fbdev_format *info = fbdev_format_info(format); ++ for (i = 0; i < ARRAY_SIZE(vga_palette); i++) { ++ u16 r = vga_palette[i][0]; ++ u16 g = vga_palette[i][1]; ++ u16 b = vga_palette[i][2]; ++ ++ r >>= (16 - info->r_length); ++ g >>= (16 - info->g_length); ++ b >>= (16 - info->b_length); ++ ++ fbdev->pseudo_palette[i] = ++ (r << info->r_offset) | ++ (g << info->g_offset) | ++ (b << info->b_offset); ++ ++ if (info->a_length) { ++ u16 a = BIT(info->a_length) - 1; ++ fbdev->pseudo_palette[i] |= (a << info->a_offset); ++ } ++ } ++ ++ fbdev->info->var.bits_per_pixel = adf_format_bpp(format); ++ fbdev->info->var.red.length = info->r_length; ++ fbdev->info->var.red.offset = info->r_offset; ++ fbdev->info->var.green.length = info->g_length; ++ fbdev->info->var.green.offset = info->g_offset; ++ fbdev->info->var.blue.length = info->b_length; ++ fbdev->info->var.blue.offset = info->b_offset; ++ fbdev->info->var.transp.length = info->a_length; ++ fbdev->info->var.transp.offset = info->a_offset; ++ fbdev->format = format; ++} ++ ++static void adf_fbdev_fill_modelist(struct adf_fbdev *fbdev) ++{ ++ struct drm_mode_modeinfo *modelist; ++ struct fb_videomode fbmode; ++ size_t n_modes, i; ++ int ret = 0; ++ ++ n_modes = adf_interface_modelist(fbdev->intf, NULL, 0); ++ modelist = kzalloc(sizeof(modelist[0]) * n_modes, GFP_KERNEL); ++ if (!modelist) { ++ dev_warn(fbdev->info->dev, "allocating new modelist failed; keeping old modelist\n"); ++ return; ++ } ++ adf_interface_modelist(fbdev->intf, modelist, n_modes); ++ ++ fb_destroy_modelist(&fbdev->info->modelist); ++ ++ for (i = 0; i < n_modes; i++) { ++ adf_modeinfo_to_fb_videomode(&modelist[i], &fbmode); ++ ret = fb_add_videomode(&fbmode, &fbdev->info->modelist); ++ if (ret < 0) ++ dev_warn(fbdev->info->dev, "adding mode %s to modelist failed: %d\n", ++ modelist[i].name, ret); ++ } ++ ++ kfree(modelist); ++} ++ ++/** ++ * adf_fbdev_open - default implementation of fbdev open op ++ */ ++int adf_fbdev_open(struct fb_info *info, int user) ++{ ++ struct adf_fbdev *fbdev = info->par; ++ int ret; ++ ++ mutex_lock(&fbdev->refcount_lock); ++ ++ if (unlikely(fbdev->refcount == UINT_MAX)) { ++ ret = -EMFILE; ++ goto done; ++ } ++ ++ if (!fbdev->refcount) { ++ struct drm_mode_modeinfo mode; ++ struct fb_videomode fbmode; ++ struct adf_device *dev = adf_interface_parent(fbdev->intf); ++ ++ ret = adf_device_attach(dev, fbdev->eng, fbdev->intf); ++ if (ret < 0 && ret != -EALREADY) ++ goto done; ++ ++ ret = adf_fb_alloc(fbdev); ++ if (ret < 0) ++ goto done; ++ ++ adf_interface_current_mode(fbdev->intf, &mode); ++ adf_modeinfo_to_fb_videomode(&mode, &fbmode); ++ fb_videomode_to_var(&fbdev->info->var, &fbmode); ++ ++ adf_fbdev_set_format(fbdev, fbdev->default_format); ++ adf_fbdev_fill_modelist(fbdev); ++ } ++ ++ ret = adf_fbdev_post(fbdev); ++ if (ret < 0) { ++ if (!fbdev->refcount) ++ adf_fb_destroy(fbdev); ++ goto done; ++ } ++ ++ fbdev->refcount++; ++done: ++ mutex_unlock(&fbdev->refcount_lock); ++ return ret; ++} ++EXPORT_SYMBOL(adf_fbdev_open); ++ ++/** ++ * adf_fbdev_release - default implementation of fbdev release op ++ */ ++int adf_fbdev_release(struct fb_info *info, int user) ++{ ++ struct adf_fbdev *fbdev = info->par; ++ mutex_lock(&fbdev->refcount_lock); ++ BUG_ON(!fbdev->refcount); ++ fbdev->refcount--; ++ if (!fbdev->refcount) ++ adf_fb_destroy(fbdev); ++ mutex_unlock(&fbdev->refcount_lock); ++ return 0; ++} ++EXPORT_SYMBOL(adf_fbdev_release); ++ ++/** ++ * adf_fbdev_check_var - default implementation of fbdev check_var op ++ */ ++int adf_fbdev_check_var(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ struct adf_fbdev *fbdev = info->par; ++ bool valid_format = true; ++ u32 format = drm_fourcc_from_fb_var(var); ++ u32 pitch = var->xres_virtual * var->bits_per_pixel / 8; ++ ++ if (!format) { ++ dev_dbg(info->dev, "%s: unrecognized format\n", __func__); ++ valid_format = false; ++ } ++ ++ if (valid_format && var->grayscale) { ++ dev_dbg(info->dev, "%s: grayscale modes not supported\n", ++ __func__); ++ valid_format = false; ++ } ++ ++ if (valid_format && var->nonstd) { ++ dev_dbg(info->dev, "%s: nonstandard formats not supported\n", ++ __func__); ++ valid_format = false; ++ } ++ ++ if (valid_format && !adf_overlay_engine_supports_format(fbdev->eng, ++ format)) { ++ char format_str[ADF_FORMAT_STR_SIZE]; ++ adf_format_str(format, format_str); ++ dev_dbg(info->dev, "%s: format %s not supported by overlay engine %s\n", ++ __func__, format_str, fbdev->eng->base.name); ++ valid_format = false; ++ } ++ ++ if (valid_format && pitch > fbdev->pitch) { ++ dev_dbg(info->dev, "%s: fb pitch too small for var (pitch = %u, xres_virtual = %u, bits_per_pixel = %u)\n", ++ __func__, fbdev->pitch, var->xres_virtual, ++ var->bits_per_pixel); ++ valid_format = false; ++ } ++ ++ if (valid_format && var->yres_virtual > fbdev->default_yres_virtual) { ++ dev_dbg(info->dev, "%s: fb height too small for var (h = %u, yres_virtual = %u)\n", ++ __func__, fbdev->default_yres_virtual, ++ var->yres_virtual); ++ valid_format = false; ++ } ++ ++ if (valid_format) { ++ var->activate = info->var.activate; ++ var->height = info->var.height; ++ var->width = info->var.width; ++ var->accel_flags = info->var.accel_flags; ++ var->rotate = info->var.rotate; ++ var->colorspace = info->var.colorspace; ++ /* userspace can't change these */ ++ } else { ++ /* if any part of the format is invalid then fixing it up is ++ impractical, so save just the modesetting bits and ++ overwrite everything else */ ++ struct fb_videomode mode; ++ fb_var_to_videomode(&mode, var); ++ memcpy(var, &info->var, sizeof(*var)); ++ fb_videomode_to_var(var, &mode); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(adf_fbdev_check_var); ++ ++/** ++ * adf_fbdev_set_par - default implementation of fbdev set_par op ++ */ ++int adf_fbdev_set_par(struct fb_info *info) ++{ ++ struct adf_fbdev *fbdev = info->par; ++ struct adf_interface *intf = fbdev->intf; ++ struct fb_videomode vmode; ++ struct drm_mode_modeinfo mode; ++ int ret; ++ u32 format = drm_fourcc_from_fb_var(&info->var); ++ ++ fb_var_to_videomode(&vmode, &info->var); ++ adf_modeinfo_from_fb_videomode(&vmode, &mode); ++ ret = adf_interface_set_mode(intf, &mode); ++ if (ret < 0) ++ return ret; ++ ++ ret = adf_fbdev_post(fbdev); ++ if (ret < 0) ++ return ret; ++ ++ if (format != fbdev->format) ++ adf_fbdev_set_format(fbdev, format); ++ ++ return 0; ++} ++EXPORT_SYMBOL(adf_fbdev_set_par); ++ ++/** ++ * adf_fbdev_blank - default implementation of fbdev blank op ++ */ ++int adf_fbdev_blank(int blank, struct fb_info *info) ++{ ++ struct adf_fbdev *fbdev = info->par; ++ struct adf_interface *intf = fbdev->intf; ++ u8 dpms_state; ++ ++ switch (blank) { ++ case FB_BLANK_UNBLANK: ++ dpms_state = DRM_MODE_DPMS_ON; ++ break; ++ case FB_BLANK_NORMAL: ++ dpms_state = DRM_MODE_DPMS_STANDBY; ++ break; ++ case FB_BLANK_VSYNC_SUSPEND: ++ dpms_state = DRM_MODE_DPMS_SUSPEND; ++ break; ++ case FB_BLANK_HSYNC_SUSPEND: ++ dpms_state = DRM_MODE_DPMS_STANDBY; ++ break; ++ case FB_BLANK_POWERDOWN: ++ dpms_state = DRM_MODE_DPMS_OFF; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return adf_interface_blank(intf, dpms_state); ++} ++EXPORT_SYMBOL(adf_fbdev_blank); ++ ++/** ++ * adf_fbdev_pan_display - default implementation of fbdev pan_display op ++ */ ++int adf_fbdev_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ struct adf_fbdev *fbdev = info->par; ++ return adf_fbdev_post(fbdev); ++} ++EXPORT_SYMBOL(adf_fbdev_pan_display); ++ ++/** ++ * adf_fbdev_mmap - default implementation of fbdev mmap op ++ */ ++int adf_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma) ++{ ++ struct adf_fbdev *fbdev = info->par; ++ ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ return dma_buf_mmap(fbdev->dma_buf, vma, 0); ++} ++EXPORT_SYMBOL(adf_fbdev_mmap); ++ ++/** ++ * adf_fbdev_init - initialize helper to wrap ADF device in fbdev API ++ * ++ * @fbdev: the fbdev helper ++ * @interface: the ADF interface that will display the framebuffer ++ * @eng: the ADF overlay engine that will scan out the framebuffer ++ * @xres_virtual: the virtual width of the framebuffer ++ * @yres_virtual: the virtual height of the framebuffer ++ * @format: the format of the framebuffer ++ * @fbops: the device's fbdev ops ++ * @fmt: formatting for the framebuffer identification string ++ * @...: variable arguments ++ * ++ * @format must be a standard, non-indexed RGB format, i.e., ++ * adf_format_is_rgb(@format) && @format != @DRM_FORMAT_C8. ++ * ++ * Returns 0 on success or -errno on failure. ++ */ ++int adf_fbdev_init(struct adf_fbdev *fbdev, struct adf_interface *interface, ++ struct adf_overlay_engine *eng, ++ u16 xres_virtual, u16 yres_virtual, u32 format, ++ struct fb_ops *fbops, const char *fmt, ...) ++{ ++ struct adf_device *parent = adf_interface_parent(interface); ++ struct device *dev = &parent->base.dev; ++ u16 width_mm, height_mm; ++ va_list args; ++ int ret; ++ ++ if (!adf_format_is_rgb(format) || ++ format == DRM_FORMAT_C8) { ++ dev_err(dev, "fbdev helper does not support format %u\n", ++ format); ++ return -EINVAL; ++ } ++ ++ memset(fbdev, 0, sizeof(*fbdev)); ++ fbdev->intf = interface; ++ fbdev->eng = eng; ++ fbdev->info = framebuffer_alloc(0, dev); ++ if (!fbdev->info) { ++ dev_err(dev, "allocating framebuffer device failed\n"); ++ return -ENOMEM; ++ } ++ mutex_init(&fbdev->refcount_lock); ++ fbdev->default_xres_virtual = xres_virtual; ++ fbdev->default_yres_virtual = yres_virtual; ++ fbdev->default_format = format; ++ ++ fbdev->info->flags = FBINFO_FLAG_DEFAULT; ++ ret = adf_interface_get_screen_size(interface, &width_mm, &height_mm); ++ if (ret < 0) { ++ width_mm = 0; ++ height_mm = 0; ++ } ++ fbdev->info->var.width = width_mm; ++ fbdev->info->var.height = height_mm; ++ fbdev->info->var.activate = FB_ACTIVATE_VBL; ++ va_start(args, fmt); ++ vsnprintf(fbdev->info->fix.id, sizeof(fbdev->info->fix.id), fmt, args); ++ va_end(args); ++ fbdev->info->fix.type = FB_TYPE_PACKED_PIXELS; ++ fbdev->info->fix.visual = FB_VISUAL_TRUECOLOR; ++ fbdev->info->fix.xpanstep = 1; ++ fbdev->info->fix.ypanstep = 1; ++ INIT_LIST_HEAD(&fbdev->info->modelist); ++ fbdev->info->fbops = fbops; ++ fbdev->info->pseudo_palette = fbdev->pseudo_palette; ++ fbdev->info->par = fbdev; ++ ++ ret = register_framebuffer(fbdev->info); ++ if (ret < 0) { ++ dev_err(dev, "registering framebuffer failed: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(adf_fbdev_init); ++ ++/** ++ * adf_fbdev_destroy - destroy helper to wrap ADF device in fbdev API ++ * ++ * @fbdev: the fbdev helper ++ */ ++void adf_fbdev_destroy(struct adf_fbdev *fbdev) ++{ ++ unregister_framebuffer(fbdev->info); ++ BUG_ON(fbdev->refcount); ++ mutex_destroy(&fbdev->refcount_lock); ++ framebuffer_release(fbdev->info); ++} ++EXPORT_SYMBOL(adf_fbdev_destroy); +diff --git a/drivers/video/adf/adf_fops.c b/drivers/video/adf/adf_fops.c +new file mode 100644 +index 0000000..7fbf33e +--- /dev/null ++++ b/drivers/video/adf/adf_fops.c +@@ -0,0 +1,957 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/bitops.h> ++#include <linux/circ_buf.h> ++#include <linux/fs.h> ++#include <linux/module.h> ++#include <linux/poll.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++ ++#include <video/adf_client.h> ++#include <video/adf_format.h> ++ ++#include "sw_sync.h" ++#include "sync.h" ++ ++#include "adf.h" ++#include "adf_fops.h" ++#include "adf_sysfs.h" ++ ++#ifdef CONFIG_COMPAT ++#include "adf_fops32.h" ++#endif ++ ++static int adf_obj_set_event(struct adf_obj *obj, struct adf_file *file, ++ struct adf_set_event __user *arg) ++{ ++ struct adf_set_event data; ++ bool enabled; ++ unsigned long flags; ++ int err; ++ ++ if (copy_from_user(&data, arg, sizeof(data))) ++ return -EFAULT; ++ ++ err = adf_obj_check_supports_event(obj, data.type); ++ if (err < 0) ++ return err; ++ ++ spin_lock_irqsave(&obj->file_lock, flags); ++ if (data.enabled) ++ enabled = test_and_set_bit(data.type, ++ file->event_subscriptions); ++ else ++ enabled = test_and_clear_bit(data.type, ++ file->event_subscriptions); ++ spin_unlock_irqrestore(&obj->file_lock, flags); ++ ++ if (data.enabled == enabled) ++ return -EALREADY; ++ ++ if (data.enabled) ++ adf_event_get(obj, data.type); ++ else ++ adf_event_put(obj, data.type); ++ ++ return 0; ++} ++ ++static int adf_obj_copy_custom_data_to_user(struct adf_obj *obj, ++ void __user *dst, size_t *dst_size) ++{ ++ void *custom_data; ++ size_t custom_data_size; ++ int ret; ++ ++ if (!obj->ops || !obj->ops->custom_data) { ++ dev_dbg(&obj->dev, "%s: no custom_data op\n", __func__); ++ return 0; ++ } ++ ++ custom_data = kzalloc(ADF_MAX_CUSTOM_DATA_SIZE, GFP_KERNEL); ++ if (!custom_data) ++ return -ENOMEM; ++ ++ ret = obj->ops->custom_data(obj, custom_data, &custom_data_size); ++ if (ret < 0) ++ goto done; ++ ++ if (copy_to_user(dst, custom_data, min(*dst_size, custom_data_size))) { ++ ret = -EFAULT; ++ goto done; ++ } ++ *dst_size = custom_data_size; ++ ++done: ++ kfree(custom_data); ++ return ret; ++} ++ ++static int adf_eng_get_data(struct adf_overlay_engine *eng, ++ struct adf_overlay_engine_data __user *arg) ++{ ++ struct adf_device *dev = adf_overlay_engine_parent(eng); ++ struct adf_overlay_engine_data data; ++ size_t n_supported_formats; ++ u32 *supported_formats = NULL; ++ int ret = 0; ++ ++ if (copy_from_user(&data, arg, sizeof(data))) ++ return -EFAULT; ++ ++ strlcpy(data.name, eng->base.name, sizeof(data.name)); ++ ++ if (data.n_supported_formats > ADF_MAX_SUPPORTED_FORMATS) ++ return -EINVAL; ++ ++ n_supported_formats = data.n_supported_formats; ++ data.n_supported_formats = eng->ops->n_supported_formats; ++ ++ if (n_supported_formats) { ++ supported_formats = kzalloc(n_supported_formats * ++ sizeof(supported_formats[0]), GFP_KERNEL); ++ if (!supported_formats) ++ return -ENOMEM; ++ } ++ ++ memcpy(supported_formats, eng->ops->supported_formats, ++ sizeof(u32) * min(n_supported_formats, ++ eng->ops->n_supported_formats)); ++ ++ mutex_lock(&dev->client_lock); ++ ret = adf_obj_copy_custom_data_to_user(&eng->base, arg->custom_data, ++ &data.custom_data_size); ++ mutex_unlock(&dev->client_lock); ++ ++ if (ret < 0) ++ goto done; ++ ++ if (copy_to_user(arg, &data, sizeof(data))) { ++ ret = -EFAULT; ++ goto done; ++ } ++ ++ if (supported_formats && copy_to_user(arg->supported_formats, ++ supported_formats, ++ n_supported_formats * sizeof(supported_formats[0]))) ++ ret = -EFAULT; ++ ++done: ++ kfree(supported_formats); ++ return ret; ++} ++ ++static int adf_buffer_import(struct adf_device *dev, ++ struct adf_buffer_config __user *cfg, struct adf_buffer *buf) ++{ ++ struct adf_buffer_config user_buf; ++ size_t i; ++ int ret = 0; ++ ++ if (copy_from_user(&user_buf, cfg, sizeof(user_buf))) ++ return -EFAULT; ++ ++ memset(buf, 0, sizeof(*buf)); ++ ++ if (user_buf.n_planes > ADF_MAX_PLANES) { ++ dev_err(&dev->base.dev, "invalid plane count %u\n", ++ user_buf.n_planes); ++ return -EINVAL; ++ } ++ ++ buf->overlay_engine = idr_find(&dev->overlay_engines, ++ user_buf.overlay_engine); ++ if (!buf->overlay_engine) { ++ dev_err(&dev->base.dev, "invalid overlay engine id %u\n", ++ user_buf.overlay_engine); ++ return -ENOENT; ++ } ++ ++ buf->w = user_buf.w; ++ buf->h = user_buf.h; ++ buf->format = user_buf.format; ++ for (i = 0; i < user_buf.n_planes; i++) { ++ buf->dma_bufs[i] = dma_buf_get(user_buf.fd[i]); ++ if (IS_ERR(buf->dma_bufs[i])) { ++ ret = PTR_ERR(buf->dma_bufs[i]); ++ dev_err(&dev->base.dev, "importing dma_buf fd %d failed: %d\n", ++ user_buf.fd[i], ret); ++ buf->dma_bufs[i] = NULL; ++ goto done; ++ } ++ buf->offset[i] = user_buf.offset[i]; ++ buf->pitch[i] = user_buf.pitch[i]; ++ } ++ buf->n_planes = user_buf.n_planes; ++ ++ if (user_buf.acquire_fence >= 0) { ++ buf->acquire_fence = sync_fence_fdget(user_buf.acquire_fence); ++ if (!buf->acquire_fence) { ++ dev_err(&dev->base.dev, "getting fence fd %d failed\n", ++ user_buf.acquire_fence); ++ ret = -EINVAL; ++ goto done; ++ } ++ } ++ ++done: ++ if (ret < 0) ++ adf_buffer_cleanup(buf); ++ return ret; ++} ++ ++static int adf_device_post_config(struct adf_device *dev, ++ struct adf_post_config __user *arg) ++{ ++ struct sync_fence *complete_fence; ++ int complete_fence_fd; ++ struct adf_buffer *bufs = NULL; ++ struct adf_interface **intfs = NULL; ++ size_t n_intfs, n_bufs, i; ++ void *custom_data = NULL; ++ size_t custom_data_size; ++ int ret = 0; ++ ++ complete_fence_fd = get_unused_fd(); ++ if (complete_fence_fd < 0) ++ return complete_fence_fd; ++ ++ if (get_user(n_intfs, &arg->n_interfaces)) { ++ ret = -EFAULT; ++ goto err_get_user; ++ } ++ ++ if (n_intfs > ADF_MAX_INTERFACES) { ++ ret = -EINVAL; ++ goto err_get_user; ++ } ++ ++ if (get_user(n_bufs, &arg->n_bufs)) { ++ ret = -EFAULT; ++ goto err_get_user; ++ } ++ ++ if (n_bufs > ADF_MAX_BUFFERS) { ++ ret = -EINVAL; ++ goto err_get_user; ++ } ++ ++ if (get_user(custom_data_size, &arg->custom_data_size)) { ++ ret = -EFAULT; ++ goto err_get_user; ++ } ++ ++ if (custom_data_size > ADF_MAX_CUSTOM_DATA_SIZE) { ++ ret = -EINVAL; ++ goto err_get_user; ++ } ++ ++ if (n_intfs) { ++ intfs = kmalloc(sizeof(intfs[0]) * n_intfs, GFP_KERNEL); ++ if (!intfs) { ++ ret = -ENOMEM; ++ goto err_get_user; ++ } ++ } ++ ++ for (i = 0; i < n_intfs; i++) { ++ u32 intf_id; ++ if (get_user(intf_id, &arg->interfaces[i])) { ++ ret = -EFAULT; ++ goto err_get_user; ++ } ++ ++ intfs[i] = idr_find(&dev->interfaces, intf_id); ++ if (!intfs[i]) { ++ ret = -EINVAL; ++ goto err_get_user; ++ } ++ } ++ ++ if (n_bufs) { ++ bufs = kzalloc(sizeof(bufs[0]) * n_bufs, GFP_KERNEL); ++ if (!bufs) { ++ ret = -ENOMEM; ++ goto err_get_user; ++ } ++ } ++ ++ for (i = 0; i < n_bufs; i++) { ++ ret = adf_buffer_import(dev, &arg->bufs[i], &bufs[i]); ++ if (ret < 0) { ++ memset(&bufs[i], 0, sizeof(bufs[i])); ++ goto err_import; ++ } ++ } ++ ++ if (custom_data_size) { ++ custom_data = kzalloc(custom_data_size, GFP_KERNEL); ++ if (!custom_data) { ++ ret = -ENOMEM; ++ goto err_import; ++ } ++ ++ if (copy_from_user(custom_data, arg->custom_data, ++ custom_data_size)) { ++ ret = -EFAULT; ++ goto err_import; ++ } ++ } ++ ++ if (put_user(complete_fence_fd, &arg->complete_fence)) { ++ ret = -EFAULT; ++ goto err_import; ++ } ++ ++ complete_fence = adf_device_post_nocopy(dev, intfs, n_intfs, bufs, ++ n_bufs, custom_data, custom_data_size); ++ if (IS_ERR(complete_fence)) { ++ ret = PTR_ERR(complete_fence); ++ goto err_import; ++ } ++ ++ sync_fence_install(complete_fence, complete_fence_fd); ++ return 0; ++ ++err_import: ++ for (i = 0; i < n_bufs; i++) ++ adf_buffer_cleanup(&bufs[i]); ++ ++err_get_user: ++ kfree(custom_data); ++ kfree(bufs); ++ kfree(intfs); ++ put_unused_fd(complete_fence_fd); ++ return ret; ++} ++ ++static int adf_intf_simple_post_config(struct adf_interface *intf, ++ struct adf_simple_post_config __user *arg) ++{ ++ struct adf_device *dev = intf->base.parent; ++ struct sync_fence *complete_fence; ++ int complete_fence_fd; ++ struct adf_buffer buf; ++ int ret = 0; ++ ++ complete_fence_fd = get_unused_fd(); ++ if (complete_fence_fd < 0) ++ return complete_fence_fd; ++ ++ ret = adf_buffer_import(dev, &arg->buf, &buf); ++ if (ret < 0) ++ goto err_import; ++ ++ if (put_user(complete_fence_fd, &arg->complete_fence)) { ++ ret = -EFAULT; ++ goto err_put_user; ++ } ++ ++ complete_fence = adf_interface_simple_post(intf, &buf); ++ if (IS_ERR(complete_fence)) { ++ ret = PTR_ERR(complete_fence); ++ goto err_put_user; ++ } ++ ++ sync_fence_install(complete_fence, complete_fence_fd); ++ return 0; ++ ++err_put_user: ++ adf_buffer_cleanup(&buf); ++err_import: ++ put_unused_fd(complete_fence_fd); ++ return ret; ++} ++ ++static int adf_intf_simple_buffer_alloc(struct adf_interface *intf, ++ struct adf_simple_buffer_alloc __user *arg) ++{ ++ struct adf_simple_buffer_alloc data; ++ struct dma_buf *dma_buf; ++ int ret = 0; ++ ++ if (copy_from_user(&data, arg, sizeof(data))) ++ return -EFAULT; ++ ++ data.fd = get_unused_fd_flags(O_CLOEXEC); ++ if (data.fd < 0) ++ return data.fd; ++ ++ ret = adf_interface_simple_buffer_alloc(intf, data.w, data.h, ++ data.format, &dma_buf, &data.offset, &data.pitch); ++ if (ret < 0) ++ goto err_alloc; ++ ++ if (copy_to_user(arg, &data, sizeof(*arg))) { ++ ret = -EFAULT; ++ goto err_copy; ++ } ++ ++ fd_install(data.fd, dma_buf->file); ++ return 0; ++ ++err_copy: ++ dma_buf_put(dma_buf); ++ ++err_alloc: ++ put_unused_fd(data.fd); ++ return ret; ++} ++ ++static int adf_copy_attachment_list_to_user( ++ struct adf_attachment_config __user *to, size_t n_to, ++ struct adf_attachment *from, size_t n_from) ++{ ++ struct adf_attachment_config *temp; ++ size_t n = min(n_to, n_from); ++ size_t i; ++ int ret = 0; ++ ++ if (!n) ++ return 0; ++ ++ temp = kzalloc(n * sizeof(temp[0]), GFP_KERNEL); ++ if (!temp) ++ return -ENOMEM; ++ ++ for (i = 0; i < n; i++) { ++ temp[i].interface = from[i].interface->base.id; ++ temp[i].overlay_engine = from[i].overlay_engine->base.id; ++ } ++ ++ if (copy_to_user(to, temp, n * sizeof(to[0]))) { ++ ret = -EFAULT; ++ goto done; ++ } ++ ++done: ++ kfree(temp); ++ return ret; ++} ++ ++static int adf_device_get_data(struct adf_device *dev, ++ struct adf_device_data __user *arg) ++{ ++ struct adf_device_data data; ++ size_t n_attach; ++ struct adf_attachment *attach = NULL; ++ size_t n_allowed_attach; ++ struct adf_attachment *allowed_attach = NULL; ++ int ret = 0; ++ ++ if (copy_from_user(&data, arg, sizeof(data))) ++ return -EFAULT; ++ ++ if (data.n_attachments > ADF_MAX_ATTACHMENTS || ++ data.n_allowed_attachments > ADF_MAX_ATTACHMENTS) ++ return -EINVAL; ++ ++ strlcpy(data.name, dev->base.name, sizeof(data.name)); ++ ++ if (data.n_attachments) { ++ attach = kzalloc(data.n_attachments * sizeof(attach[0]), ++ GFP_KERNEL); ++ if (!attach) ++ return -ENOMEM; ++ } ++ n_attach = adf_device_attachments(dev, attach, data.n_attachments); ++ ++ if (data.n_allowed_attachments) { ++ allowed_attach = kzalloc(data.n_allowed_attachments * ++ sizeof(allowed_attach[0]), GFP_KERNEL); ++ if (!allowed_attach) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ } ++ n_allowed_attach = adf_device_attachments_allowed(dev, allowed_attach, ++ data.n_allowed_attachments); ++ ++ mutex_lock(&dev->client_lock); ++ ret = adf_obj_copy_custom_data_to_user(&dev->base, arg->custom_data, ++ &data.custom_data_size); ++ mutex_unlock(&dev->client_lock); ++ ++ if (ret < 0) ++ goto done; ++ ++ ret = adf_copy_attachment_list_to_user(arg->attachments, ++ data.n_attachments, attach, n_attach); ++ if (ret < 0) ++ goto done; ++ ++ ret = adf_copy_attachment_list_to_user(arg->allowed_attachments, ++ data.n_allowed_attachments, allowed_attach, ++ n_allowed_attach); ++ if (ret < 0) ++ goto done; ++ ++ data.n_attachments = n_attach; ++ data.n_allowed_attachments = n_allowed_attach; ++ ++ if (copy_to_user(arg, &data, sizeof(data))) ++ ret = -EFAULT; ++ ++done: ++ kfree(allowed_attach); ++ kfree(attach); ++ return ret; ++} ++ ++static int adf_device_handle_attachment(struct adf_device *dev, ++ struct adf_attachment_config __user *arg, bool attach) ++{ ++ struct adf_attachment_config data; ++ struct adf_overlay_engine *eng; ++ struct adf_interface *intf; ++ ++ if (copy_from_user(&data, arg, sizeof(data))) ++ return -EFAULT; ++ ++ eng = idr_find(&dev->overlay_engines, data.overlay_engine); ++ if (!eng) { ++ dev_err(&dev->base.dev, "invalid overlay engine id %u\n", ++ data.overlay_engine); ++ return -EINVAL; ++ } ++ ++ intf = idr_find(&dev->interfaces, data.interface); ++ if (!intf) { ++ dev_err(&dev->base.dev, "invalid interface id %u\n", ++ data.interface); ++ return -EINVAL; ++ } ++ ++ if (attach) ++ return adf_device_attach(dev, eng, intf); ++ else ++ return adf_device_detach(dev, eng, intf); ++} ++ ++static int adf_intf_set_mode(struct adf_interface *intf, ++ struct drm_mode_modeinfo __user *arg) ++{ ++ struct drm_mode_modeinfo mode; ++ ++ if (copy_from_user(&mode, arg, sizeof(mode))) ++ return -EFAULT; ++ ++ return adf_interface_set_mode(intf, &mode); ++} ++ ++static int adf_intf_get_data(struct adf_interface *intf, ++ struct adf_interface_data __user *arg) ++{ ++ struct adf_device *dev = adf_interface_parent(intf); ++ struct adf_interface_data data; ++ struct drm_mode_modeinfo *modelist; ++ size_t modelist_size; ++ int err; ++ int ret = 0; ++ unsigned long flags; ++ ++ if (copy_from_user(&data, arg, sizeof(data))) ++ return -EFAULT; ++ ++ strlcpy(data.name, intf->base.name, sizeof(data.name)); ++ ++ data.type = intf->type; ++ data.id = intf->idx; ++ data.flags = intf->flags; ++ ++ err = adf_interface_get_screen_size(intf, &data.width_mm, ++ &data.height_mm); ++ if (err < 0) { ++ data.width_mm = 0; ++ data.height_mm = 0; ++ } ++ ++ modelist = kmalloc(sizeof(modelist[0]) * ADF_MAX_MODES, GFP_KERNEL); ++ if (!modelist) ++ return -ENOMEM; ++ ++ mutex_lock(&dev->client_lock); ++ read_lock_irqsave(&intf->hotplug_modelist_lock, flags); ++ data.hotplug_detect = intf->hotplug_detect; ++ modelist_size = min(data.n_available_modes, intf->n_modes) * ++ sizeof(intf->modelist[0]); ++ memcpy(modelist, intf->modelist, modelist_size); ++ data.n_available_modes = intf->n_modes; ++ read_unlock_irqrestore(&intf->hotplug_modelist_lock, flags); ++ ++ if (copy_to_user(arg->available_modes, modelist, modelist_size)) { ++ ret = -EFAULT; ++ goto done; ++ } ++ ++ data.dpms_state = intf->dpms_state; ++ memcpy(&data.current_mode, &intf->current_mode, ++ sizeof(intf->current_mode)); ++ ++ ret = adf_obj_copy_custom_data_to_user(&intf->base, arg->custom_data, ++ &data.custom_data_size); ++done: ++ mutex_unlock(&dev->client_lock); ++ kfree(modelist); ++ ++ if (ret < 0) ++ return ret; ++ ++ if (copy_to_user(arg, &data, sizeof(data))) ++ ret = -EFAULT; ++ ++ return ret; ++} ++ ++static inline long adf_obj_custom_ioctl(struct adf_obj *obj, unsigned int cmd, ++ unsigned long arg) ++{ ++ if (obj->ops && obj->ops->ioctl) ++ return obj->ops->ioctl(obj, cmd, arg); ++ return -ENOTTY; ++} ++ ++static long adf_overlay_engine_ioctl(struct adf_overlay_engine *eng, ++ struct adf_file *file, unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case ADF_SET_EVENT: ++ return adf_obj_set_event(&eng->base, file, ++ (struct adf_set_event __user *)arg); ++ ++ case ADF_GET_OVERLAY_ENGINE_DATA: ++ return adf_eng_get_data(eng, ++ (struct adf_overlay_engine_data __user *)arg); ++ ++ case ADF_BLANK: ++ case ADF_POST_CONFIG: ++ case ADF_SET_MODE: ++ case ADF_GET_DEVICE_DATA: ++ case ADF_GET_INTERFACE_DATA: ++ case ADF_SIMPLE_POST_CONFIG: ++ case ADF_SIMPLE_BUFFER_ALLOC: ++ case ADF_ATTACH: ++ case ADF_DETACH: ++ return -EINVAL; ++ ++ default: ++ return adf_obj_custom_ioctl(&eng->base, cmd, arg); ++ } ++} ++ ++static long adf_interface_ioctl(struct adf_interface *intf, ++ struct adf_file *file, unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case ADF_SET_EVENT: ++ return adf_obj_set_event(&intf->base, file, ++ (struct adf_set_event __user *)arg); ++ ++ case ADF_BLANK: ++ return adf_interface_blank(intf, arg); ++ ++ case ADF_SET_MODE: ++ return adf_intf_set_mode(intf, ++ (struct drm_mode_modeinfo __user *)arg); ++ ++ case ADF_GET_INTERFACE_DATA: ++ return adf_intf_get_data(intf, ++ (struct adf_interface_data __user *)arg); ++ ++ case ADF_SIMPLE_POST_CONFIG: ++ return adf_intf_simple_post_config(intf, ++ (struct adf_simple_post_config __user *)arg); ++ ++ case ADF_SIMPLE_BUFFER_ALLOC: ++ return adf_intf_simple_buffer_alloc(intf, ++ (struct adf_simple_buffer_alloc __user *)arg); ++ ++ case ADF_POST_CONFIG: ++ case ADF_GET_DEVICE_DATA: ++ case ADF_GET_OVERLAY_ENGINE_DATA: ++ case ADF_ATTACH: ++ case ADF_DETACH: ++ return -EINVAL; ++ ++ default: ++ return adf_obj_custom_ioctl(&intf->base, cmd, arg); ++ } ++} ++ ++static long adf_device_ioctl(struct adf_device *dev, struct adf_file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case ADF_SET_EVENT: ++ return adf_obj_set_event(&dev->base, file, ++ (struct adf_set_event __user *)arg); ++ ++ case ADF_POST_CONFIG: ++ return adf_device_post_config(dev, ++ (struct adf_post_config __user *)arg); ++ ++ case ADF_GET_DEVICE_DATA: ++ return adf_device_get_data(dev, ++ (struct adf_device_data __user *)arg); ++ ++ case ADF_ATTACH: ++ return adf_device_handle_attachment(dev, ++ (struct adf_attachment_config __user *)arg, ++ true); ++ ++ case ADF_DETACH: ++ return adf_device_handle_attachment(dev, ++ (struct adf_attachment_config __user *)arg, ++ false); ++ ++ case ADF_BLANK: ++ case ADF_SET_MODE: ++ case ADF_GET_INTERFACE_DATA: ++ case ADF_GET_OVERLAY_ENGINE_DATA: ++ case ADF_SIMPLE_POST_CONFIG: ++ case ADF_SIMPLE_BUFFER_ALLOC: ++ return -EINVAL; ++ ++ default: ++ return adf_obj_custom_ioctl(&dev->base, cmd, arg); ++ } ++} ++ ++static int adf_file_open(struct inode *inode, struct file *file) ++{ ++ struct adf_obj *obj; ++ struct adf_file *fpriv = NULL; ++ unsigned long flags; ++ int ret = 0; ++ ++ obj = adf_obj_sysfs_find(iminor(inode)); ++ if (!obj) ++ return -ENODEV; ++ ++ dev_dbg(&obj->dev, "opening %s\n", dev_name(&obj->dev)); ++ ++ if (!try_module_get(obj->parent->ops->owner)) { ++ dev_err(&obj->dev, "getting owner module failed\n"); ++ return -ENODEV; ++ } ++ ++ fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL); ++ if (!fpriv) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ INIT_LIST_HEAD(&fpriv->head); ++ fpriv->obj = obj; ++ init_waitqueue_head(&fpriv->event_wait); ++ ++ file->private_data = fpriv; ++ ++ if (obj->ops && obj->ops->open) { ++ ret = obj->ops->open(obj, inode, file); ++ if (ret < 0) ++ goto done; ++ } ++ ++ spin_lock_irqsave(&obj->file_lock, flags); ++ list_add_tail(&fpriv->head, &obj->file_list); ++ spin_unlock_irqrestore(&obj->file_lock, flags); ++ ++done: ++ if (ret < 0) { ++ kfree(fpriv); ++ module_put(obj->parent->ops->owner); ++ } ++ return ret; ++} ++ ++static int adf_file_release(struct inode *inode, struct file *file) ++{ ++ struct adf_file *fpriv = file->private_data; ++ struct adf_obj *obj = fpriv->obj; ++ enum adf_event_type event_type; ++ unsigned long flags; ++ ++ if (obj->ops && obj->ops->release) ++ obj->ops->release(obj, inode, file); ++ ++ spin_lock_irqsave(&obj->file_lock, flags); ++ list_del(&fpriv->head); ++ spin_unlock_irqrestore(&obj->file_lock, flags); ++ ++ for_each_set_bit(event_type, fpriv->event_subscriptions, ++ ADF_EVENT_TYPE_MAX) { ++ adf_event_put(obj, event_type); ++ } ++ ++ kfree(fpriv); ++ module_put(obj->parent->ops->owner); ++ ++ dev_dbg(&obj->dev, "released %s\n", dev_name(&obj->dev)); ++ return 0; ++} ++ ++long adf_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct adf_file *fpriv = file->private_data; ++ struct adf_obj *obj = fpriv->obj; ++ long ret = -EINVAL; ++ ++ dev_dbg(&obj->dev, "%s ioctl %u\n", dev_name(&obj->dev), _IOC_NR(cmd)); ++ ++ switch (obj->type) { ++ case ADF_OBJ_OVERLAY_ENGINE: ++ ret = adf_overlay_engine_ioctl(adf_obj_to_overlay_engine(obj), ++ fpriv, cmd, arg); ++ break; ++ ++ case ADF_OBJ_INTERFACE: ++ ret = adf_interface_ioctl(adf_obj_to_interface(obj), fpriv, cmd, ++ arg); ++ break; ++ ++ case ADF_OBJ_DEVICE: ++ ret = adf_device_ioctl(adf_obj_to_device(obj), fpriv, cmd, arg); ++ break; ++ } ++ ++ return ret; ++} ++ ++static inline bool adf_file_event_available(struct adf_file *fpriv) ++{ ++ int head = fpriv->event_head; ++ int tail = fpriv->event_tail; ++ return CIRC_CNT(head, tail, sizeof(fpriv->event_buf)) != 0; ++} ++ ++void adf_file_queue_event(struct adf_file *fpriv, struct adf_event *event) ++{ ++ int head = fpriv->event_head; ++ int tail = fpriv->event_tail; ++ size_t space = CIRC_SPACE(head, tail, sizeof(fpriv->event_buf)); ++ size_t space_to_end = ++ CIRC_SPACE_TO_END(head, tail, sizeof(fpriv->event_buf)); ++ ++ if (space < event->length) { ++ dev_dbg(&fpriv->obj->dev, ++ "insufficient buffer space for event %u\n", ++ event->type); ++ return; ++ } ++ ++ if (space_to_end >= event->length) { ++ memcpy(fpriv->event_buf + head, event, event->length); ++ } else { ++ memcpy(fpriv->event_buf + head, event, space_to_end); ++ memcpy(fpriv->event_buf, (u8 *)event + space_to_end, ++ event->length - space_to_end); ++ } ++ ++ smp_wmb(); ++ fpriv->event_head = (fpriv->event_head + event->length) & ++ (sizeof(fpriv->event_buf) - 1); ++ wake_up_interruptible_all(&fpriv->event_wait); ++} ++ ++static ssize_t adf_file_copy_to_user(struct adf_file *fpriv, ++ char __user *buffer, size_t buffer_size) ++{ ++ int head, tail; ++ u8 *event_buf; ++ size_t cnt, cnt_to_end, copy_size = 0; ++ ssize_t ret = 0; ++ unsigned long flags; ++ ++ event_buf = kmalloc(min(buffer_size, sizeof(fpriv->event_buf)), ++ GFP_KERNEL); ++ if (!event_buf) ++ return -ENOMEM; ++ ++ spin_lock_irqsave(&fpriv->obj->file_lock, flags); ++ ++ if (!adf_file_event_available(fpriv)) ++ goto out; ++ ++ head = fpriv->event_head; ++ tail = fpriv->event_tail; ++ ++ cnt = CIRC_CNT(head, tail, sizeof(fpriv->event_buf)); ++ cnt_to_end = CIRC_CNT_TO_END(head, tail, sizeof(fpriv->event_buf)); ++ copy_size = min(buffer_size, cnt); ++ ++ if (cnt_to_end >= copy_size) { ++ memcpy(event_buf, fpriv->event_buf + tail, copy_size); ++ } else { ++ memcpy(event_buf, fpriv->event_buf + tail, cnt_to_end); ++ memcpy(event_buf + cnt_to_end, fpriv->event_buf, ++ copy_size - cnt_to_end); ++ } ++ ++ fpriv->event_tail = (fpriv->event_tail + copy_size) & ++ (sizeof(fpriv->event_buf) - 1); ++ ++out: ++ spin_unlock_irqrestore(&fpriv->obj->file_lock, flags); ++ if (copy_size) { ++ if (copy_to_user(buffer, event_buf, copy_size)) ++ ret = -EFAULT; ++ else ++ ret = copy_size; ++ } ++ kfree(event_buf); ++ return ret; ++} ++ ++ssize_t adf_file_read(struct file *filp, char __user *buffer, ++ size_t count, loff_t *offset) ++{ ++ struct adf_file *fpriv = filp->private_data; ++ int err; ++ ++ err = wait_event_interruptible(fpriv->event_wait, ++ adf_file_event_available(fpriv)); ++ if (err < 0) ++ return err; ++ ++ return adf_file_copy_to_user(fpriv, buffer, count); ++} ++ ++unsigned int adf_file_poll(struct file *filp, struct poll_table_struct *wait) ++{ ++ struct adf_file *fpriv = filp->private_data; ++ unsigned int mask = 0; ++ ++ poll_wait(filp, &fpriv->event_wait, wait); ++ ++ if (adf_file_event_available(fpriv)) ++ mask |= POLLIN | POLLRDNORM; ++ ++ return mask; ++} ++ ++const struct file_operations adf_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = adf_file_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = adf_file_compat_ioctl, ++#endif ++ .open = adf_file_open, ++ .release = adf_file_release, ++ .llseek = default_llseek, ++ .read = adf_file_read, ++ .poll = adf_file_poll, ++}; +diff --git a/drivers/video/adf/adf_fops.h b/drivers/video/adf/adf_fops.h +new file mode 100644 +index 0000000..90a3a74 +--- /dev/null ++++ b/drivers/video/adf/adf_fops.h +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __VIDEO_ADF_ADF_FOPS_H ++#define __VIDEO_ADF_ADF_FOPS_H ++ ++#include <linux/bitmap.h> ++#include <linux/fs.h> ++ ++extern const struct file_operations adf_fops; ++ ++struct adf_file { ++ struct list_head head; ++ struct adf_obj *obj; ++ ++ DECLARE_BITMAP(event_subscriptions, ADF_EVENT_TYPE_MAX); ++ u8 event_buf[4096]; ++ int event_head; ++ int event_tail; ++ wait_queue_head_t event_wait; ++}; ++ ++void adf_file_queue_event(struct adf_file *file, struct adf_event *event); ++long adf_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg); ++ ++#endif /* __VIDEO_ADF_ADF_FOPS_H */ +diff --git a/drivers/video/adf/adf_fops32.c b/drivers/video/adf/adf_fops32.c +new file mode 100644 +index 0000000..d299a81 +--- /dev/null ++++ b/drivers/video/adf/adf_fops32.c +@@ -0,0 +1,217 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/uaccess.h> ++#include <video/adf.h> ++ ++#include "adf_fops.h" ++#include "adf_fops32.h" ++ ++long adf_compat_post_config(struct file *file, ++ struct adf_post_config32 __user *arg) ++{ ++ struct adf_post_config32 cfg32; ++ struct adf_post_config __user *cfg; ++ int ret; ++ ++ if (copy_from_user(&cfg32, arg, sizeof(cfg32))) ++ return -EFAULT; ++ ++ cfg = compat_alloc_user_space(sizeof(*cfg)); ++ if (!access_ok(VERIFY_WRITE, cfg, sizeof(*cfg))) ++ return -EFAULT; ++ ++ if (put_user(cfg32.n_interfaces, &cfg->n_interfaces) || ++ put_user(compat_ptr(cfg32.interfaces), ++ &cfg->interfaces) || ++ put_user(cfg32.n_bufs, &cfg->n_bufs) || ++ put_user(compat_ptr(cfg32.bufs), &cfg->bufs) || ++ put_user(cfg32.custom_data_size, ++ &cfg->custom_data_size) || ++ put_user(compat_ptr(cfg32.custom_data), ++ &cfg->custom_data)) ++ return -EFAULT; ++ ++ ret = adf_file_ioctl(file, ADF_POST_CONFIG, (unsigned long)cfg); ++ if (ret < 0) ++ return ret; ++ ++ if (copy_in_user(&arg->complete_fence, &cfg->complete_fence, ++ sizeof(cfg->complete_fence))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++long adf_compat_get_device_data(struct file *file, ++ struct adf_device_data32 __user *arg) ++{ ++ struct adf_device_data32 data32; ++ struct adf_device_data __user *data; ++ int ret; ++ ++ if (copy_from_user(&data32, arg, sizeof(data32))) ++ return -EFAULT; ++ ++ data = compat_alloc_user_space(sizeof(*data)); ++ if (!access_ok(VERIFY_WRITE, data, sizeof(*data))) ++ return -EFAULT; ++ ++ if (put_user(data32.n_attachments, &data->n_attachments) || ++ put_user(compat_ptr(data32.attachments), ++ &data->attachments) || ++ put_user(data32.n_allowed_attachments, ++ &data->n_allowed_attachments) || ++ put_user(compat_ptr(data32.allowed_attachments), ++ &data->allowed_attachments) || ++ put_user(data32.custom_data_size, ++ &data->custom_data_size) || ++ put_user(compat_ptr(data32.custom_data), ++ &data->custom_data)) ++ return -EFAULT; ++ ++ ret = adf_file_ioctl(file, ADF_GET_DEVICE_DATA, (unsigned long)data); ++ if (ret < 0) ++ return ret; ++ ++ if (copy_in_user(arg->name, data->name, sizeof(arg->name)) || ++ copy_in_user(&arg->n_attachments, &data->n_attachments, ++ sizeof(arg->n_attachments)) || ++ copy_in_user(&arg->n_allowed_attachments, ++ &data->n_allowed_attachments, ++ sizeof(arg->n_allowed_attachments)) || ++ copy_in_user(&arg->custom_data_size, ++ &data->custom_data_size, ++ sizeof(arg->custom_data_size))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++long adf_compat_get_interface_data(struct file *file, ++ struct adf_interface_data32 __user *arg) ++{ ++ struct adf_interface_data32 data32; ++ struct adf_interface_data __user *data; ++ int ret; ++ ++ if (copy_from_user(&data32, arg, sizeof(data32))) ++ return -EFAULT; ++ ++ data = compat_alloc_user_space(sizeof(*data)); ++ if (!access_ok(VERIFY_WRITE, data, sizeof(*data))) ++ return -EFAULT; ++ ++ if (put_user(data32.n_available_modes, &data->n_available_modes) || ++ put_user(compat_ptr(data32.available_modes), ++ &data->available_modes) || ++ put_user(data32.custom_data_size, ++ &data->custom_data_size) || ++ put_user(compat_ptr(data32.custom_data), ++ &data->custom_data)) ++ return -EFAULT; ++ ++ ret = adf_file_ioctl(file, ADF_GET_INTERFACE_DATA, (unsigned long)data); ++ if (ret < 0) ++ return ret; ++ ++ if (copy_in_user(arg->name, data->name, sizeof(arg->name)) || ++ copy_in_user(&arg->type, &data->type, ++ sizeof(arg->type)) || ++ copy_in_user(&arg->id, &data->id, sizeof(arg->id)) || ++ copy_in_user(&arg->flags, &data->flags, ++ sizeof(arg->flags)) || ++ copy_in_user(&arg->dpms_state, &data->dpms_state, ++ sizeof(arg->dpms_state)) || ++ copy_in_user(&arg->hotplug_detect, ++ &data->hotplug_detect, ++ sizeof(arg->hotplug_detect)) || ++ copy_in_user(&arg->width_mm, &data->width_mm, ++ sizeof(arg->width_mm)) || ++ copy_in_user(&arg->height_mm, &data->height_mm, ++ sizeof(arg->height_mm)) || ++ copy_in_user(&arg->current_mode, &data->current_mode, ++ sizeof(arg->current_mode)) || ++ copy_in_user(&arg->n_available_modes, ++ &data->n_available_modes, ++ sizeof(arg->n_available_modes)) || ++ copy_in_user(&arg->custom_data_size, ++ &data->custom_data_size, ++ sizeof(arg->custom_data_size))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++long adf_compat_get_overlay_engine_data(struct file *file, ++ struct adf_overlay_engine_data32 __user *arg) ++{ ++ struct adf_overlay_engine_data32 data32; ++ struct adf_overlay_engine_data __user *data; ++ int ret; ++ ++ if (copy_from_user(&data32, arg, sizeof(data32))) ++ return -EFAULT; ++ ++ data = compat_alloc_user_space(sizeof(*data)); ++ if (!access_ok(VERIFY_WRITE, data, sizeof(*data))) ++ return -EFAULT; ++ ++ if (put_user(data32.n_supported_formats, &data->n_supported_formats) || ++ put_user(compat_ptr(data32.supported_formats), ++ &data->supported_formats) || ++ put_user(data32.custom_data_size, ++ &data->custom_data_size) || ++ put_user(compat_ptr(data32.custom_data), ++ &data->custom_data)) ++ return -EFAULT; ++ ++ ret = adf_file_ioctl(file, ADF_GET_OVERLAY_ENGINE_DATA, ++ (unsigned long)data); ++ if (ret < 0) ++ return ret; ++ ++ if (copy_in_user(arg->name, data->name, sizeof(arg->name)) || ++ copy_in_user(&arg->n_supported_formats, ++ &data->n_supported_formats, ++ sizeof(arg->n_supported_formats)) || ++ copy_in_user(&arg->custom_data_size, ++ &data->custom_data_size, ++ sizeof(arg->custom_data_size))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++long adf_file_compat_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ switch (cmd) { ++ case ADF_POST_CONFIG32: ++ return adf_compat_post_config(file, compat_ptr(arg)); ++ ++ case ADF_GET_DEVICE_DATA32: ++ return adf_compat_get_device_data(file, compat_ptr(arg)); ++ ++ case ADF_GET_INTERFACE_DATA32: ++ return adf_compat_get_interface_data(file, compat_ptr(arg)); ++ ++ case ADF_GET_OVERLAY_ENGINE_DATA32: ++ return adf_compat_get_overlay_engine_data(file, ++ compat_ptr(arg)); ++ ++ default: ++ return adf_file_ioctl(file, cmd, arg); ++ } ++} +diff --git a/drivers/video/adf/adf_fops32.h b/drivers/video/adf/adf_fops32.h +new file mode 100644 +index 0000000..64034ce +--- /dev/null ++++ b/drivers/video/adf/adf_fops32.h +@@ -0,0 +1,78 @@ ++#ifndef __VIDEO_ADF_ADF_FOPS32_H ++#define __VIDEO_ADF_ADF_FOPS32_H ++ ++#include <linux/compat.h> ++#include <linux/ioctl.h> ++ ++#include <video/adf.h> ++ ++#define ADF_POST_CONFIG32 \ ++ _IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config32) ++#define ADF_GET_DEVICE_DATA32 \ ++ _IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data32) ++#define ADF_GET_INTERFACE_DATA32 \ ++ _IOR(ADF_IOCTL_TYPE, 5, struct adf_interface_data32) ++#define ADF_GET_OVERLAY_ENGINE_DATA32 \ ++ _IOR(ADF_IOCTL_TYPE, 6, struct adf_overlay_engine_data32) ++ ++struct adf_post_config32 { ++ compat_size_t n_interfaces; ++ compat_uptr_t interfaces; ++ ++ compat_size_t n_bufs; ++ compat_uptr_t bufs; ++ ++ compat_size_t custom_data_size; ++ compat_uptr_t custom_data; ++ ++ __s32 complete_fence; ++}; ++ ++struct adf_device_data32 { ++ char name[ADF_NAME_LEN]; ++ ++ compat_size_t n_attachments; ++ compat_uptr_t attachments; ++ ++ compat_size_t n_allowed_attachments; ++ compat_uptr_t allowed_attachments; ++ ++ compat_size_t custom_data_size; ++ compat_uptr_t custom_data; ++}; ++ ++struct adf_interface_data32 { ++ char name[ADF_NAME_LEN]; ++ ++ __u8 type; ++ __u32 id; ++ /* e.g. type=ADF_INTF_TYPE_DSI, id=1 => DSI.1 */ ++ __u32 flags; ++ ++ __u8 dpms_state; ++ __u8 hotplug_detect; ++ __u16 width_mm; ++ __u16 height_mm; ++ ++ struct drm_mode_modeinfo current_mode; ++ compat_size_t n_available_modes; ++ compat_uptr_t available_modes; ++ ++ compat_size_t custom_data_size; ++ compat_uptr_t custom_data; ++}; ++ ++struct adf_overlay_engine_data32 { ++ char name[ADF_NAME_LEN]; ++ ++ compat_size_t n_supported_formats; ++ compat_uptr_t supported_formats; ++ ++ compat_size_t custom_data_size; ++ compat_uptr_t custom_data; ++}; ++ ++long adf_file_compat_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg); ++ ++#endif /* __VIDEO_ADF_ADF_FOPS32_H */ +diff --git a/drivers/video/adf/adf_format.c b/drivers/video/adf/adf_format.c +new file mode 100644 +index 0000000..e3f22c7 +--- /dev/null ++++ b/drivers/video/adf/adf_format.c +@@ -0,0 +1,280 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * modified from drivers/gpu/drm/drm_crtc.c ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/export.h> ++#include <linux/kernel.h> ++#include <drm/drm_fourcc.h> ++#include <video/adf_format.h> ++ ++bool adf_format_is_standard(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_C8: ++ case DRM_FORMAT_RGB332: ++ case DRM_FORMAT_BGR233: ++ case DRM_FORMAT_XRGB4444: ++ case DRM_FORMAT_XBGR4444: ++ case DRM_FORMAT_RGBX4444: ++ case DRM_FORMAT_BGRX4444: ++ case DRM_FORMAT_ARGB4444: ++ case DRM_FORMAT_ABGR4444: ++ case DRM_FORMAT_RGBA4444: ++ case DRM_FORMAT_BGRA4444: ++ case DRM_FORMAT_XRGB1555: ++ case DRM_FORMAT_XBGR1555: ++ case DRM_FORMAT_RGBX5551: ++ case DRM_FORMAT_BGRX5551: ++ case DRM_FORMAT_ARGB1555: ++ case DRM_FORMAT_ABGR1555: ++ case DRM_FORMAT_RGBA5551: ++ case DRM_FORMAT_BGRA5551: ++ case DRM_FORMAT_RGB565: ++ case DRM_FORMAT_BGR565: ++ case DRM_FORMAT_RGB888: ++ case DRM_FORMAT_BGR888: ++ case DRM_FORMAT_XRGB8888: ++ case DRM_FORMAT_XBGR8888: ++ case DRM_FORMAT_RGBX8888: ++ case DRM_FORMAT_BGRX8888: ++ case DRM_FORMAT_ARGB8888: ++ case DRM_FORMAT_ABGR8888: ++ case DRM_FORMAT_RGBA8888: ++ case DRM_FORMAT_BGRA8888: ++ case DRM_FORMAT_XRGB2101010: ++ case DRM_FORMAT_XBGR2101010: ++ case DRM_FORMAT_RGBX1010102: ++ case DRM_FORMAT_BGRX1010102: ++ case DRM_FORMAT_ARGB2101010: ++ case DRM_FORMAT_ABGR2101010: ++ case DRM_FORMAT_RGBA1010102: ++ case DRM_FORMAT_BGRA1010102: ++ case DRM_FORMAT_YUYV: ++ case DRM_FORMAT_YVYU: ++ case DRM_FORMAT_UYVY: ++ case DRM_FORMAT_VYUY: ++ case DRM_FORMAT_AYUV: ++ case DRM_FORMAT_NV12: ++ case DRM_FORMAT_NV21: ++ case DRM_FORMAT_NV16: ++ case DRM_FORMAT_NV61: ++ case DRM_FORMAT_YUV410: ++ case DRM_FORMAT_YVU410: ++ case DRM_FORMAT_YUV411: ++ case DRM_FORMAT_YVU411: ++ case DRM_FORMAT_YUV420: ++ case DRM_FORMAT_YVU420: ++ case DRM_FORMAT_YUV422: ++ case DRM_FORMAT_YVU422: ++ case DRM_FORMAT_YUV444: ++ case DRM_FORMAT_YVU444: ++ return true; ++ default: ++ return false; ++ } ++} ++EXPORT_SYMBOL(adf_format_is_standard); ++ ++bool adf_format_is_rgb(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_C8: ++ case DRM_FORMAT_RGB332: ++ case DRM_FORMAT_BGR233: ++ case DRM_FORMAT_XRGB1555: ++ case DRM_FORMAT_XBGR1555: ++ case DRM_FORMAT_RGBX5551: ++ case DRM_FORMAT_BGRX5551: ++ case DRM_FORMAT_ARGB1555: ++ case DRM_FORMAT_ABGR1555: ++ case DRM_FORMAT_RGBA5551: ++ case DRM_FORMAT_BGRA5551: ++ case DRM_FORMAT_RGB565: ++ case DRM_FORMAT_BGR565: ++ case DRM_FORMAT_RGB888: ++ case DRM_FORMAT_BGR888: ++ case DRM_FORMAT_XRGB8888: ++ case DRM_FORMAT_XBGR8888: ++ case DRM_FORMAT_RGBX8888: ++ case DRM_FORMAT_BGRX8888: ++ case DRM_FORMAT_XRGB2101010: ++ case DRM_FORMAT_XBGR2101010: ++ case DRM_FORMAT_RGBX1010102: ++ case DRM_FORMAT_BGRX1010102: ++ case DRM_FORMAT_ARGB2101010: ++ case DRM_FORMAT_ABGR2101010: ++ case DRM_FORMAT_RGBA1010102: ++ case DRM_FORMAT_BGRA1010102: ++ case DRM_FORMAT_ARGB8888: ++ case DRM_FORMAT_ABGR8888: ++ case DRM_FORMAT_RGBA8888: ++ case DRM_FORMAT_BGRA8888: ++ return true; ++ ++ default: ++ return false; ++ } ++} ++EXPORT_SYMBOL(adf_format_is_rgb); ++ ++u8 adf_format_num_planes(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_YUV410: ++ case DRM_FORMAT_YVU410: ++ case DRM_FORMAT_YUV411: ++ case DRM_FORMAT_YVU411: ++ case DRM_FORMAT_YUV420: ++ case DRM_FORMAT_YVU420: ++ case DRM_FORMAT_YUV422: ++ case DRM_FORMAT_YVU422: ++ case DRM_FORMAT_YUV444: ++ case DRM_FORMAT_YVU444: ++ return 3; ++ case DRM_FORMAT_NV12: ++ case DRM_FORMAT_NV21: ++ case DRM_FORMAT_NV16: ++ case DRM_FORMAT_NV61: ++ return 2; ++ default: ++ return 1; ++ } ++} ++EXPORT_SYMBOL(adf_format_num_planes); ++ ++u8 adf_format_bpp(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_C8: ++ case DRM_FORMAT_RGB332: ++ case DRM_FORMAT_BGR233: ++ return 8; ++ ++ case DRM_FORMAT_XRGB1555: ++ case DRM_FORMAT_XBGR1555: ++ case DRM_FORMAT_RGBX5551: ++ case DRM_FORMAT_BGRX5551: ++ case DRM_FORMAT_ARGB1555: ++ case DRM_FORMAT_ABGR1555: ++ case DRM_FORMAT_RGBA5551: ++ case DRM_FORMAT_BGRA5551: ++ case DRM_FORMAT_RGB565: ++ case DRM_FORMAT_BGR565: ++ return 16; ++ ++ case DRM_FORMAT_RGB888: ++ case DRM_FORMAT_BGR888: ++ return 24; ++ ++ case DRM_FORMAT_XRGB8888: ++ case DRM_FORMAT_XBGR8888: ++ case DRM_FORMAT_RGBX8888: ++ case DRM_FORMAT_BGRX8888: ++ case DRM_FORMAT_XRGB2101010: ++ case DRM_FORMAT_XBGR2101010: ++ case DRM_FORMAT_RGBX1010102: ++ case DRM_FORMAT_BGRX1010102: ++ case DRM_FORMAT_ARGB2101010: ++ case DRM_FORMAT_ABGR2101010: ++ case DRM_FORMAT_RGBA1010102: ++ case DRM_FORMAT_BGRA1010102: ++ case DRM_FORMAT_ARGB8888: ++ case DRM_FORMAT_ABGR8888: ++ case DRM_FORMAT_RGBA8888: ++ case DRM_FORMAT_BGRA8888: ++ return 32; ++ ++ default: ++ pr_debug("%s: unsupported pixel format %u\n", __func__, format); ++ return 0; ++ } ++} ++EXPORT_SYMBOL(adf_format_bpp); ++ ++u8 adf_format_plane_cpp(u32 format, int plane) ++{ ++ if (plane >= adf_format_num_planes(format)) ++ return 0; ++ ++ switch (format) { ++ case DRM_FORMAT_YUYV: ++ case DRM_FORMAT_YVYU: ++ case DRM_FORMAT_UYVY: ++ case DRM_FORMAT_VYUY: ++ return 2; ++ case DRM_FORMAT_NV12: ++ case DRM_FORMAT_NV21: ++ case DRM_FORMAT_NV16: ++ case DRM_FORMAT_NV61: ++ return plane ? 2 : 1; ++ case DRM_FORMAT_YUV410: ++ case DRM_FORMAT_YVU410: ++ case DRM_FORMAT_YUV411: ++ case DRM_FORMAT_YVU411: ++ case DRM_FORMAT_YUV420: ++ case DRM_FORMAT_YVU420: ++ case DRM_FORMAT_YUV422: ++ case DRM_FORMAT_YVU422: ++ case DRM_FORMAT_YUV444: ++ case DRM_FORMAT_YVU444: ++ return 1; ++ default: ++ return adf_format_bpp(format) / 8; ++ } ++} ++EXPORT_SYMBOL(adf_format_plane_cpp); ++ ++u8 adf_format_horz_chroma_subsampling(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_YUV411: ++ case DRM_FORMAT_YVU411: ++ case DRM_FORMAT_YUV410: ++ case DRM_FORMAT_YVU410: ++ return 4; ++ case DRM_FORMAT_YUYV: ++ case DRM_FORMAT_YVYU: ++ case DRM_FORMAT_UYVY: ++ case DRM_FORMAT_VYUY: ++ case DRM_FORMAT_NV12: ++ case DRM_FORMAT_NV21: ++ case DRM_FORMAT_NV16: ++ case DRM_FORMAT_NV61: ++ case DRM_FORMAT_YUV422: ++ case DRM_FORMAT_YVU422: ++ case DRM_FORMAT_YUV420: ++ case DRM_FORMAT_YVU420: ++ return 2; ++ default: ++ return 1; ++ } ++} ++EXPORT_SYMBOL(adf_format_horz_chroma_subsampling); ++ ++u8 adf_format_vert_chroma_subsampling(u32 format) ++{ ++ switch (format) { ++ case DRM_FORMAT_YUV410: ++ case DRM_FORMAT_YVU410: ++ return 4; ++ case DRM_FORMAT_YUV420: ++ case DRM_FORMAT_YVU420: ++ case DRM_FORMAT_NV12: ++ case DRM_FORMAT_NV21: ++ return 2; ++ default: ++ return 1; ++ } ++} ++EXPORT_SYMBOL(adf_format_vert_chroma_subsampling); +diff --git a/drivers/video/adf/adf_memblock.c b/drivers/video/adf/adf_memblock.c +new file mode 100644 +index 0000000..ab583f8 +--- /dev/null ++++ b/drivers/video/adf/adf_memblock.c +@@ -0,0 +1,160 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/dma-buf.h> ++#include <linux/highmem.h> ++#include <linux/memblock.h> ++#include <linux/slab.h> ++ ++struct adf_memblock_pdata { ++ phys_addr_t base; ++}; ++ ++static struct sg_table *adf_memblock_map(struct dma_buf_attachment *attach, ++ enum dma_data_direction direction) ++{ ++ struct adf_memblock_pdata *pdata = attach->dmabuf->priv; ++ unsigned long pfn = PFN_DOWN(pdata->base); ++ struct page *page = pfn_to_page(pfn); ++ struct sg_table *table; ++ int nents, ret; ++ ++ table = kzalloc(sizeof(*table), GFP_KERNEL); ++ if (!table) ++ return ERR_PTR(-ENOMEM); ++ ++ ret = sg_alloc_table(table, 1, GFP_KERNEL); ++ if (ret < 0) ++ goto err_alloc; ++ ++ sg_set_page(table->sgl, page, attach->dmabuf->size, 0); ++ ++ nents = dma_map_sg(attach->dev, table->sgl, 1, direction); ++ if (!nents) { ++ ret = -EINVAL; ++ goto err_map; ++ } ++ ++ return table; ++ ++err_map: ++ sg_free_table(table); ++err_alloc: ++ kfree(table); ++ return ERR_PTR(ret); ++} ++ ++static void adf_memblock_unmap(struct dma_buf_attachment *attach, ++ struct sg_table *table, enum dma_data_direction direction) ++{ ++ dma_unmap_sg(attach->dev, table->sgl, 1, direction); ++ sg_free_table(table); ++} ++ ++static void __init_memblock adf_memblock_release(struct dma_buf *buf) ++{ ++ struct adf_memblock_pdata *pdata = buf->priv; ++ int err = memblock_free(pdata->base, buf->size); ++ ++ if (err < 0) ++ pr_warn("%s: freeing memblock failed: %d\n", __func__, err); ++ kfree(pdata); ++} ++ ++static void *adf_memblock_do_kmap(struct dma_buf *buf, unsigned long pgoffset, ++ bool atomic) ++{ ++ struct adf_memblock_pdata *pdata = buf->priv; ++ unsigned long pfn = PFN_DOWN(pdata->base) + pgoffset; ++ struct page *page = pfn_to_page(pfn); ++ ++ if (atomic) ++ return kmap_atomic(page); ++ else ++ return kmap(page); ++} ++ ++static void *adf_memblock_kmap_atomic(struct dma_buf *buf, ++ unsigned long pgoffset) ++{ ++ return adf_memblock_do_kmap(buf, pgoffset, true); ++} ++ ++static void adf_memblock_kunmap_atomic(struct dma_buf *buf, ++ unsigned long pgoffset, void *vaddr) ++{ ++ kunmap_atomic(vaddr); ++} ++ ++static void *adf_memblock_kmap(struct dma_buf *buf, unsigned long pgoffset) ++{ ++ return adf_memblock_do_kmap(buf, pgoffset, false); ++} ++ ++static void adf_memblock_kunmap(struct dma_buf *buf, unsigned long pgoffset, ++ void *vaddr) ++{ ++ kunmap(vaddr); ++} ++ ++static int adf_memblock_mmap(struct dma_buf *buf, struct vm_area_struct *vma) ++{ ++ struct adf_memblock_pdata *pdata = buf->priv; ++ ++ return remap_pfn_range(vma, vma->vm_start, PFN_DOWN(pdata->base), ++ vma->vm_end - vma->vm_start, vma->vm_page_prot); ++} ++ ++struct dma_buf_ops adf_memblock_ops = { ++ .map_dma_buf = adf_memblock_map, ++ .unmap_dma_buf = adf_memblock_unmap, ++ .release = adf_memblock_release, ++ .kmap_atomic = adf_memblock_kmap_atomic, ++ .kunmap_atomic = adf_memblock_kunmap_atomic, ++ .kmap = adf_memblock_kmap, ++ .kunmap = adf_memblock_kunmap, ++ .mmap = adf_memblock_mmap, ++}; ++ ++/** ++ * adf_memblock_export - export a memblock reserved area as a dma-buf ++ * ++ * @base: base physical address ++ * @size: memblock size ++ * @flags: mode flags for the dma-buf's file ++ * ++ * @base and @size must be page-aligned. ++ * ++ * Returns a dma-buf on success or ERR_PTR(-errno) on failure. ++ */ ++struct dma_buf *adf_memblock_export(phys_addr_t base, size_t size, int flags) ++{ ++ struct adf_memblock_pdata *pdata; ++ struct dma_buf *buf; ++ ++ if (PAGE_ALIGN(base) != base || PAGE_ALIGN(size) != size) ++ return ERR_PTR(-EINVAL); ++ ++ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) ++ return ERR_PTR(-ENOMEM); ++ ++ pdata->base = base; ++ buf = dma_buf_export(pdata, &adf_memblock_ops, size, flags, NULL); ++ if (IS_ERR(buf)) ++ kfree(pdata); ++ ++ return buf; ++} ++EXPORT_SYMBOL(adf_memblock_export); +diff --git a/drivers/video/adf/adf_sysfs.c b/drivers/video/adf/adf_sysfs.c +new file mode 100644 +index 0000000..8c659c7 +--- /dev/null ++++ b/drivers/video/adf/adf_sysfs.c +@@ -0,0 +1,296 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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 <video/adf_client.h> ++ ++#include "adf.h" ++#include "adf_fops.h" ++#include "adf_sysfs.h" ++ ++static struct class *adf_class; ++static int adf_major; ++static DEFINE_IDR(adf_minors); ++ ++#define dev_to_adf_interface(p) \ ++ adf_obj_to_interface(container_of(p, struct adf_obj, dev)) ++ ++static ssize_t dpms_state_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct adf_interface *intf = dev_to_adf_interface(dev); ++ return scnprintf(buf, PAGE_SIZE, "%u\n", ++ adf_interface_dpms_state(intf)); ++} ++ ++static ssize_t dpms_state_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct adf_interface *intf = dev_to_adf_interface(dev); ++ u8 dpms_state; ++ int err; ++ ++ err = kstrtou8(buf, 0, &dpms_state); ++ if (err < 0) ++ return err; ++ ++ err = adf_interface_blank(intf, dpms_state); ++ if (err < 0) ++ return err; ++ ++ return count; ++} ++ ++static ssize_t current_mode_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct adf_interface *intf = dev_to_adf_interface(dev); ++ struct drm_mode_modeinfo mode; ++ ++ adf_interface_current_mode(intf, &mode); ++ ++ if (mode.name[0]) { ++ return scnprintf(buf, PAGE_SIZE, "%s\n", mode.name); ++ } else { ++ bool interlaced = !!(mode.flags & DRM_MODE_FLAG_INTERLACE); ++ return scnprintf(buf, PAGE_SIZE, "%ux%u%s\n", mode.hdisplay, ++ mode.vdisplay, interlaced ? "i" : ""); ++ } ++} ++ ++static ssize_t type_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct adf_interface *intf = dev_to_adf_interface(dev); ++ return scnprintf(buf, PAGE_SIZE, "%s\n", ++ adf_interface_type_str(intf)); ++} ++ ++static ssize_t vsync_timestamp_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct adf_interface *intf = dev_to_adf_interface(dev); ++ ktime_t timestamp; ++ unsigned long flags; ++ ++ read_lock_irqsave(&intf->vsync_lock, flags); ++ memcpy(×tamp, &intf->vsync_timestamp, sizeof(timestamp)); ++ read_unlock_irqrestore(&intf->vsync_lock, flags); ++ ++ return scnprintf(buf, PAGE_SIZE, "%llu\n", ktime_to_ns(timestamp)); ++} ++ ++static ssize_t hotplug_detect_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct adf_interface *intf = dev_to_adf_interface(dev); ++ return scnprintf(buf, PAGE_SIZE, "%u\n", intf->hotplug_detect); ++} ++ ++static struct device_attribute adf_interface_attrs[] = { ++ __ATTR(dpms_state, S_IRUGO|S_IWUSR, dpms_state_show, dpms_state_store), ++ __ATTR_RO(current_mode), ++ __ATTR_RO(hotplug_detect), ++ __ATTR_RO(type), ++ __ATTR_RO(vsync_timestamp), ++}; ++ ++int adf_obj_sysfs_init(struct adf_obj *obj, struct device *parent) ++{ ++ int ret = idr_alloc(&adf_minors, obj, 0, 0, GFP_KERNEL); ++ if (ret < 0) { ++ pr_err("%s: allocating adf minor failed: %d\n", __func__, ++ ret); ++ return ret; ++ } ++ ++ obj->minor = ret; ++ obj->dev.parent = parent; ++ obj->dev.class = adf_class; ++ obj->dev.devt = MKDEV(adf_major, obj->minor); ++ ++ ret = device_register(&obj->dev); ++ if (ret < 0) { ++ pr_err("%s: registering adf object failed: %d\n", __func__, ++ ret); ++ goto err_device_register; ++ } ++ ++ return 0; ++ ++err_device_register: ++ idr_remove(&adf_minors, obj->minor); ++ return ret; ++} ++ ++static char *adf_device_devnode(struct device *dev, umode_t *mode, ++ kuid_t *uid, kgid_t *gid) ++{ ++ struct adf_obj *obj = container_of(dev, struct adf_obj, dev); ++ return kasprintf(GFP_KERNEL, "adf%d", obj->id); ++} ++ ++static char *adf_interface_devnode(struct device *dev, umode_t *mode, ++ kuid_t *uid, kgid_t *gid) ++{ ++ struct adf_obj *obj = container_of(dev, struct adf_obj, dev); ++ struct adf_interface *intf = adf_obj_to_interface(obj); ++ struct adf_device *parent = adf_interface_parent(intf); ++ return kasprintf(GFP_KERNEL, "adf-interface%d.%d", ++ parent->base.id, intf->base.id); ++} ++ ++static char *adf_overlay_engine_devnode(struct device *dev, umode_t *mode, ++ kuid_t *uid, kgid_t *gid) ++{ ++ struct adf_obj *obj = container_of(dev, struct adf_obj, dev); ++ struct adf_overlay_engine *eng = adf_obj_to_overlay_engine(obj); ++ struct adf_device *parent = adf_overlay_engine_parent(eng); ++ return kasprintf(GFP_KERNEL, "adf-overlay-engine%d.%d", ++ parent->base.id, eng->base.id); ++} ++ ++static void adf_noop_release(struct device *dev) ++{ ++} ++ ++static struct device_type adf_device_type = { ++ .name = "adf_device", ++ .devnode = adf_device_devnode, ++ .release = adf_noop_release, ++}; ++ ++static struct device_type adf_interface_type = { ++ .name = "adf_interface", ++ .devnode = adf_interface_devnode, ++ .release = adf_noop_release, ++}; ++ ++static struct device_type adf_overlay_engine_type = { ++ .name = "adf_overlay_engine", ++ .devnode = adf_overlay_engine_devnode, ++ .release = adf_noop_release, ++}; ++ ++int adf_device_sysfs_init(struct adf_device *dev) ++{ ++ dev->base.dev.type = &adf_device_type; ++ dev_set_name(&dev->base.dev, "%s", dev->base.name); ++ return adf_obj_sysfs_init(&dev->base, dev->dev); ++} ++ ++int adf_interface_sysfs_init(struct adf_interface *intf) ++{ ++ struct adf_device *parent = adf_interface_parent(intf); ++ size_t i, j; ++ int ret; ++ ++ intf->base.dev.type = &adf_interface_type; ++ dev_set_name(&intf->base.dev, "%s-interface%d", parent->base.name, ++ intf->base.id); ++ ++ ret = adf_obj_sysfs_init(&intf->base, &parent->base.dev); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < ARRAY_SIZE(adf_interface_attrs); i++) { ++ ret = device_create_file(&intf->base.dev, ++ &adf_interface_attrs[i]); ++ if (ret < 0) { ++ dev_err(&intf->base.dev, "creating sysfs attribute %s failed: %d\n", ++ adf_interface_attrs[i].attr.name, ret); ++ goto err; ++ } ++ } ++ ++ return 0; ++ ++err: ++ for (j = 0; j < i; j++) ++ device_remove_file(&intf->base.dev, &adf_interface_attrs[j]); ++ return ret; ++} ++ ++int adf_overlay_engine_sysfs_init(struct adf_overlay_engine *eng) ++{ ++ struct adf_device *parent = adf_overlay_engine_parent(eng); ++ ++ eng->base.dev.type = &adf_overlay_engine_type; ++ dev_set_name(&eng->base.dev, "%s-overlay-engine%d", parent->base.name, ++ eng->base.id); ++ ++ return adf_obj_sysfs_init(&eng->base, &parent->base.dev); ++} ++ ++struct adf_obj *adf_obj_sysfs_find(int minor) ++{ ++ return idr_find(&adf_minors, minor); ++} ++ ++void adf_obj_sysfs_destroy(struct adf_obj *obj) ++{ ++ idr_remove(&adf_minors, obj->minor); ++ device_unregister(&obj->dev); ++} ++ ++void adf_device_sysfs_destroy(struct adf_device *dev) ++{ ++ adf_obj_sysfs_destroy(&dev->base); ++} ++ ++void adf_interface_sysfs_destroy(struct adf_interface *intf) ++{ ++ size_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(adf_interface_attrs); i++) ++ device_remove_file(&intf->base.dev, &adf_interface_attrs[i]); ++ adf_obj_sysfs_destroy(&intf->base); ++} ++ ++void adf_overlay_engine_sysfs_destroy(struct adf_overlay_engine *eng) ++{ ++ adf_obj_sysfs_destroy(&eng->base); ++} ++ ++int adf_sysfs_init(void) ++{ ++ struct class *class; ++ int ret; ++ ++ class = class_create(THIS_MODULE, "adf"); ++ if (IS_ERR(class)) { ++ ret = PTR_ERR(class); ++ pr_err("%s: creating class failed: %d\n", __func__, ret); ++ return ret; ++ } ++ ++ ret = register_chrdev(0, "adf", &adf_fops); ++ if (ret < 0) { ++ pr_err("%s: registering device failed: %d\n", __func__, ret); ++ goto err_chrdev; ++ } ++ ++ adf_class = class; ++ adf_major = ret; ++ return 0; ++ ++err_chrdev: ++ class_destroy(adf_class); ++ return ret; ++} ++ ++void adf_sysfs_destroy(void) ++{ ++ idr_destroy(&adf_minors); ++ class_destroy(adf_class); ++} +diff --git a/drivers/video/adf/adf_sysfs.h b/drivers/video/adf/adf_sysfs.h +new file mode 100644 +index 0000000..0613ac3 +--- /dev/null ++++ b/drivers/video/adf/adf_sysfs.h +@@ -0,0 +1,33 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __VIDEO_ADF_ADF_SYSFS_H ++#define __VIDEO_ADF_ADF_SYSFS_H ++ ++struct adf_device; ++struct adf_interface; ++struct adf_overlay_engine; ++ ++int adf_device_sysfs_init(struct adf_device *dev); ++void adf_device_sysfs_destroy(struct adf_device *dev); ++int adf_interface_sysfs_init(struct adf_interface *intf); ++void adf_interface_sysfs_destroy(struct adf_interface *intf); ++int adf_overlay_engine_sysfs_init(struct adf_overlay_engine *eng); ++void adf_overlay_engine_sysfs_destroy(struct adf_overlay_engine *eng); ++struct adf_obj *adf_obj_sysfs_find(int minor); ++ ++int adf_sysfs_init(void); ++void adf_sysfs_destroy(void); ++ ++#endif /* __VIDEO_ADF_ADF_SYSFS_H */ +diff --git a/drivers/video/adf/adf_trace.h b/drivers/video/adf/adf_trace.h +new file mode 100644 +index 0000000..3cb2a84 +--- /dev/null ++++ b/drivers/video/adf/adf_trace.h +@@ -0,0 +1,93 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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. ++ * ++ */ ++ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM adf ++ ++#if !defined(__VIDEO_ADF_ADF_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) ++#define __VIDEO_ADF_ADF_TRACE_H ++ ++#include <linux/tracepoint.h> ++#include <video/adf.h> ++ ++TRACE_EVENT(adf_event, ++ TP_PROTO(struct adf_obj *obj, enum adf_event_type type), ++ TP_ARGS(obj, type), ++ ++ TP_STRUCT__entry( ++ __string(name, obj->name) ++ __field(enum adf_event_type, type) ++ __array(char, type_str, 32) ++ ), ++ TP_fast_assign( ++ __assign_str(name, obj->name); ++ __entry->type = type; ++ strlcpy(__entry->type_str, adf_event_type_str(obj, type), ++ sizeof(__entry->type_str)); ++ ), ++ TP_printk("obj=%s type=%u (%s)", ++ __get_str(name), ++ __entry->type, ++ __entry->type_str) ++); ++ ++TRACE_EVENT(adf_event_enable, ++ TP_PROTO(struct adf_obj *obj, enum adf_event_type type), ++ TP_ARGS(obj, type), ++ ++ TP_STRUCT__entry( ++ __string(name, obj->name) ++ __field(enum adf_event_type, type) ++ __array(char, type_str, 32) ++ ), ++ TP_fast_assign( ++ __assign_str(name, obj->name); ++ __entry->type = type; ++ strlcpy(__entry->type_str, adf_event_type_str(obj, type), ++ sizeof(__entry->type_str)); ++ ), ++ TP_printk("obj=%s type=%u (%s)", ++ __get_str(name), ++ __entry->type, ++ __entry->type_str) ++); ++ ++TRACE_EVENT(adf_event_disable, ++ TP_PROTO(struct adf_obj *obj, enum adf_event_type type), ++ TP_ARGS(obj, type), ++ ++ TP_STRUCT__entry( ++ __string(name, obj->name) ++ __field(enum adf_event_type, type) ++ __array(char, type_str, 32) ++ ), ++ TP_fast_assign( ++ __assign_str(name, obj->name); ++ __entry->type = type; ++ strlcpy(__entry->type_str, adf_event_type_str(obj, type), ++ sizeof(__entry->type_str)); ++ ), ++ TP_printk("obj=%s type=%u (%s)", ++ __get_str(name), ++ __entry->type, ++ __entry->type_str) ++); ++ ++#endif /* __VIDEO_ADF_ADF_TRACE_H */ ++ ++#undef TRACE_INCLUDE_PATH ++#undef TRACE_INCLUDE_FILE ++#define TRACE_INCLUDE_PATH . ++#define TRACE_INCLUDE_FILE adf_trace ++#include <trace/define_trace.h> +diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c +index e76a9b3..5e505d4 100644 +--- a/drivers/w1/masters/ds2482.c ++++ b/drivers/w1/masters/ds2482.c +@@ -18,6 +18,8 @@ + #include <linux/slab.h> + #include <linux/i2c.h> + #include <linux/delay.h> ++#include <linux/gpio.h> ++#include <linux/platform_data/ds2482.h> + #include <asm/delay.h> + + #include "../w1.h" +@@ -84,7 +86,8 @@ static const u8 ds2482_chan_rd[8] = + static int ds2482_probe(struct i2c_client *client, + const struct i2c_device_id *id); + static int ds2482_remove(struct i2c_client *client); +- ++static int ds2482_suspend(struct device *dev); ++static int ds2482_resume(struct device *dev); + + /** + * Driver data (common to all clients) +@@ -94,10 +97,16 @@ static const struct i2c_device_id ds2482_id[] = { + { } + }; + ++static const struct dev_pm_ops ds2482_pm_ops = { ++ .suspend = ds2482_suspend, ++ .resume = ds2482_resume, ++}; ++ + static struct i2c_driver ds2482_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ds2482", ++ .pm = &ds2482_pm_ops, + }, + .probe = ds2482_probe, + .remove = ds2482_remove, +@@ -119,6 +128,7 @@ struct ds2482_w1_chan { + struct ds2482_data { + struct i2c_client *client; + struct mutex access_lock; ++ int slpz_gpio; + + /* 1-wire interface(s) */ + int w1_count; /* 1 or 8 */ +@@ -444,11 +454,31 @@ static u8 ds2482_w1_set_pullup(void *data, int delay) + return retval; + } + ++static int ds2482_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct ds2482_data *data = i2c_get_clientdata(client); ++ ++ if (data->slpz_gpio >= 0) ++ gpio_set_value(data->slpz_gpio, 0); ++ return 0; ++} ++ ++static int ds2482_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct ds2482_data *data = i2c_get_clientdata(client); ++ ++ if (data->slpz_gpio >= 0) ++ gpio_set_value(data->slpz_gpio, 1); ++ return 0; ++} + + static int ds2482_probe(struct i2c_client *client, + const struct i2c_device_id *id) + { + struct ds2482_data *data; ++ struct ds2482_platform_data *pdata; + int err = -ENODEV; + int temp1; + int idx; +@@ -515,6 +545,16 @@ static int ds2482_probe(struct i2c_client *client, + } + } + ++ pdata = client->dev.platform_data; ++ data->slpz_gpio = pdata ? pdata->slpz_gpio : -1; ++ ++ if (data->slpz_gpio >= 0) { ++ err = gpio_request_one(data->slpz_gpio, GPIOF_OUT_INIT_HIGH, ++ "ds2482.slpz"); ++ if (err < 0) ++ goto exit_w1_remove; ++ } ++ + return 0; + + exit_w1_remove: +@@ -539,6 +579,11 @@ static int ds2482_remove(struct i2c_client *client) + w1_remove_master_device(&data->w1_ch[idx].w1_bm); + } + ++ if (data->slpz_gpio >= 0) { ++ gpio_set_value(data->slpz_gpio, 0); ++ gpio_free(data->slpz_gpio); ++ } ++ + /* Free the memory */ + kfree(data); + return 0; +diff --git a/fs/9p/acl.c b/fs/9p/acl.c +index 8482f2d..d3f5d48 100644 +--- a/fs/9p/acl.c ++++ b/fs/9p/acl.c +@@ -320,32 +320,26 @@ static int v9fs_xattr_set_acl(struct dentry *dentry, const char *name, + case ACL_TYPE_ACCESS: + name = POSIX_ACL_XATTR_ACCESS; + if (acl) { +- umode_t mode = inode->i_mode; +- retval = posix_acl_equiv_mode(acl, &mode); +- if (retval < 0) ++ struct iattr iattr; ++ ++ retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl); ++ if (retval) + goto err_out; +- else { +- struct iattr iattr; +- if (retval == 0) { +- /* +- * ACL can be represented +- * by the mode bits. So don't +- * update ACL. +- */ +- acl = NULL; +- value = NULL; +- size = 0; +- } +- /* Updte the mode bits */ +- iattr.ia_mode = ((mode & S_IALLUGO) | +- (inode->i_mode & ~S_IALLUGO)); +- iattr.ia_valid = ATTR_MODE; +- /* FIXME should we update ctime ? +- * What is the following setxattr update the +- * mode ? ++ if (!acl) { ++ /* ++ * ACL can be represented ++ * by the mode bits. So don't ++ * update ACL. + */ +- v9fs_vfs_setattr_dotl(dentry, &iattr); ++ value = NULL; ++ size = 0; + } ++ iattr.ia_valid = ATTR_MODE; ++ /* FIXME should we update ctime ? ++ * What is the following setxattr update the ++ * mode ? ++ */ ++ v9fs_vfs_setattr_dotl(dentry, &iattr); + } + break; + case ACL_TYPE_DEFAULT: +diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c +index a2f6e9a..2cec576 100644 +--- a/fs/9p/vfs_inode.c ++++ b/fs/9p/vfs_inode.c +@@ -1129,6 +1129,10 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) + if (S_ISREG(dentry->d_inode->i_mode)) + filemap_write_and_wait(dentry->d_inode->i_mapping); + ++ retval = setattr_killpriv(dentry, iattr); ++ if (retval) ++ return retval; ++ + retval = p9_client_wstat(fid, &wstat); + if (retval < 0) + return retval; +diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c +index 092d20c..3ab6527 100644 +--- a/fs/9p/vfs_inode_dotl.c ++++ b/fs/9p/vfs_inode_dotl.c +@@ -582,6 +582,10 @@ int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr) + if (S_ISREG(inode->i_mode)) + filemap_write_and_wait(inode->i_mapping); + ++ retval = setattr_killpriv(dentry, iattr); ++ if (retval) ++ return retval; ++ + retval = p9_client_setattr(fid, &p9attr); + if (retval < 0) + return retval; +diff --git a/fs/Kconfig b/fs/Kconfig +index 664991a..6ccbee1 100644 +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -191,6 +191,7 @@ source "fs/hfsplus/Kconfig" + source "fs/befs/Kconfig" + source "fs/bfs/Kconfig" + source "fs/efs/Kconfig" ++source "fs/yaffs2/Kconfig" + source "fs/jffs2/Kconfig" + # UBIFS File system configuration + source "fs/ubifs/Kconfig" +diff --git a/fs/Makefile b/fs/Makefile +index da0bbb4..4b5887c 100644 +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -126,3 +126,4 @@ obj-y += exofs/ # Multiple modules + obj-$(CONFIG_CEPH_FS) += ceph/ + obj-$(CONFIG_PSTORE) += pstore/ + obj-$(CONFIG_EFIVAR_FS) += efivarfs/ ++obj-$(CONFIG_YAFFS_FS) += yaffs2/ +diff --git a/fs/aio.c b/fs/aio.c +index 58caa7e..41e6a77 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -1325,7 +1325,8 @@ static ssize_t aio_setup_vectored_rw(struct kiocb *kiocb, + int rw, char __user *buf, + unsigned long *nr_segs, + struct iovec **iovec, +- bool compat) ++ bool compat, ++ struct iov_iter *iter) + { + ssize_t ret; + +@@ -1346,20 +1347,26 @@ static ssize_t aio_setup_vectored_rw(struct kiocb *kiocb, + + /* ki_nbytes now reflect bytes instead of segs */ + kiocb->ki_nbytes = ret; ++ iov_iter_init(iter, rw, *iovec, *nr_segs, kiocb->ki_nbytes); + return 0; + } + + static ssize_t aio_setup_single_vector(struct kiocb *kiocb, + int rw, char __user *buf, + unsigned long *nr_segs, +- struct iovec *iovec) ++ struct iovec *iovec, ++ struct iov_iter *iter) + { ++ if (kiocb->ki_nbytes > MAX_RW_COUNT) ++ kiocb->ki_nbytes = MAX_RW_COUNT; ++ + if (unlikely(!access_ok(!rw, buf, kiocb->ki_nbytes))) + return -EFAULT; + + iovec->iov_base = buf; + iovec->iov_len = kiocb->ki_nbytes; + *nr_segs = 1; ++ iov_iter_init(iter, rw, iovec, *nr_segs, kiocb->ki_nbytes); + return 0; + } + +@@ -1406,9 +1413,9 @@ rw_common: + ret = (opcode == IOCB_CMD_PREADV || + opcode == IOCB_CMD_PWRITEV) + ? aio_setup_vectored_rw(req, rw, buf, &nr_segs, +- &iovec, compat) ++ &iovec, compat, &iter) + : aio_setup_single_vector(req, rw, buf, &nr_segs, +- iovec); ++ iovec, &iter); + if (!ret) + ret = rw_verify_area(rw, file, &req->ki_pos, req->ki_nbytes); + if (ret < 0) { +@@ -1430,10 +1437,9 @@ rw_common: + file_start_write(file); + + if (iter_op) { +- iov_iter_init(&iter, rw, iovec, nr_segs, req->ki_nbytes); + ret = iter_op(req, &iter); + } else { +- ret = rw_op(req, iovec, nr_segs, req->ki_pos); ++ ret = rw_op(req, iter.iov, iter.nr_segs, req->ki_pos); + } + + if (rw == WRITE) +diff --git a/fs/attr.c b/fs/attr.c +index 6530ced..2052234 100644 +--- a/fs/attr.c ++++ b/fs/attr.c +@@ -167,6 +167,28 @@ void setattr_copy(struct inode *inode, const struct iattr *attr) + } + EXPORT_SYMBOL(setattr_copy); + ++ /** ++ * setattr_killpriv - remove extended privilege attributes from a file ++ * @dentry: Directory entry passed to the setattr operation ++ * @iattr: New attributes pased to the setattr operation ++ * ++ * All filesystems that can carry extended privilege attributes ++ * should call this from their setattr operation *after* validating ++ * the attribute changes. ++ * ++ * It does nothing if !(iattr->ia_valid & ATTR_KILL_PRIV), so ++ * it is not necessary to call it in that case. ++ */ ++int setattr_killpriv(struct dentry *dentry, struct iattr *iattr) ++{ ++ if (!(iattr->ia_valid & ATTR_KILL_PRIV)) ++ return 0; ++ ++ iattr->ia_valid &= ~ATTR_KILL_PRIV; ++ return security_inode_killpriv(dentry); ++} ++EXPORT_SYMBOL(setattr_killpriv); ++ + /** + * notify_change - modify attributes of a filesytem object + * @dentry: object affected +@@ -217,13 +239,13 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de + if (!(ia_valid & ATTR_MTIME_SET)) + attr->ia_mtime = now; + if (ia_valid & ATTR_KILL_PRIV) { +- attr->ia_valid &= ~ATTR_KILL_PRIV; +- ia_valid &= ~ATTR_KILL_PRIV; + error = security_inode_need_killpriv(dentry); +- if (error > 0) +- error = security_inode_killpriv(dentry); +- if (error) ++ if (error < 0) + return error; ++ if (error == 0) { ++ attr->ia_valid &= ~ATTR_KILL_PRIV; ++ ia_valid &= ~ATTR_KILL_PRIV; ++ } + } + + /* +diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c +index 9a0124a..fb3e64d 100644 +--- a/fs/btrfs/acl.c ++++ b/fs/btrfs/acl.c +@@ -83,11 +83,9 @@ static int __btrfs_set_acl(struct btrfs_trans_handle *trans, + case ACL_TYPE_ACCESS: + name = POSIX_ACL_XATTR_ACCESS; + if (acl) { +- ret = posix_acl_equiv_mode(acl, &inode->i_mode); +- if (ret < 0) ++ ret = posix_acl_update_mode(inode, &inode->i_mode, &acl); ++ if (ret) + return ret; +- if (ret == 0) +- acl = NULL; + } + ret = 0; + break; +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index edaa617..793419c 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -4697,6 +4697,10 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr) + if (err) + return err; + ++ err = setattr_killpriv(dentry, attr); ++ if (err) ++ return err; ++ + if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) { + err = btrfs_setsize(inode, attr); + if (err) +diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c +index 5bd853b..6a4a3e2 100644 +--- a/fs/ceph/acl.c ++++ b/fs/ceph/acl.c +@@ -108,11 +108,9 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type) + case ACL_TYPE_ACCESS: + name = POSIX_ACL_XATTR_ACCESS; + if (acl) { +- ret = posix_acl_equiv_mode(acl, &new_mode); +- if (ret < 0) ++ ret = posix_acl_update_mode(inode, &new_mode, &acl); ++ if (ret) + goto out; +- if (ret == 0) +- acl = NULL; + } + break; + case ACL_TYPE_DEFAULT: +diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c +index 7a1df90..b0e9198 100644 +--- a/fs/ceph/inode.c ++++ b/fs/ceph/inode.c +@@ -1712,6 +1712,10 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) + if (err != 0) + return err; + ++ err = setattr_killpriv(dentry, attr); ++ if (err != 0) ++ return err; ++ + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETATTR, + USE_AUTH_MDS); + if (IS_ERR(req)) +diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c +index 0c3ce46..0eba18c 100644 +--- a/fs/cifs/inode.c ++++ b/fs/cifs/inode.c +@@ -2151,6 +2151,10 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) + mapping_set_error(inode->i_mapping, rc); + rc = 0; + ++ rc = setattr_killpriv(direntry, attrs); ++ if (rc) ++ goto out; ++ + if (attrs->ia_valid & ATTR_SIZE) { + rc = cifs_set_file_size(inode, attrs, xid, full_path); + if (rc != 0) +@@ -2273,6 +2277,12 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) + return rc; + } + ++ rc = setattr_killpriv(direntry, attrs); ++ if (rc) { ++ free_xid(xid); ++ return rc; ++ } ++ + full_path = build_path_from_dentry(direntry); + if (full_path == NULL) { + rc = -ENOMEM; +diff --git a/fs/dcache.c b/fs/dcache.c +index d0539a4..2ffac07 100644 +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -2886,6 +2886,13 @@ restart: + + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { + struct mount *parent = ACCESS_ONCE(mnt->mnt_parent); ++ /* Escaped? */ ++ if (dentry != vfsmnt->mnt_root) { ++ bptr = *buffer; ++ blen = *buflen; ++ error = 3; ++ break; ++ } + /* Global root? */ + if (mnt != parent) { + dentry = ACCESS_ONCE(mnt->mnt_mountpoint); +diff --git a/fs/eventpoll.c b/fs/eventpoll.c +index 7bcfff9..1c6bba7 100644 +--- a/fs/eventpoll.c ++++ b/fs/eventpoll.c +@@ -34,6 +34,7 @@ + #include <linux/mutex.h> + #include <linux/anon_inodes.h> + #include <linux/device.h> ++#include <linux/freezer.h> + #include <asm/uaccess.h> + #include <asm/io.h> + #include <asm/mman.h> +@@ -1637,7 +1638,8 @@ fetch_events: + } + + spin_unlock_irqrestore(&ep->lock, flags); +- if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS)) ++ if (!freezable_schedule_hrtimeout_range(to, slack, ++ HRTIMER_MODE_ABS)) + timed_out = 1; + + spin_lock_irqsave(&ep->lock, flags); +diff --git a/fs/exec.c b/fs/exec.c +index b7a5f46..969c650 100644 +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -199,11 +199,21 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, + + if (write) { + unsigned long size = bprm->vma->vm_end - bprm->vma->vm_start; ++ unsigned long ptr_size; + struct rlimit *rlim; + + acct_arg_size(bprm, size / PAGE_SIZE); + + /* ++ * Since the stack will hold pointers to the strings, we ++ * must account for them as well. ++ */ ++ ptr_size = (bprm->argc + bprm->envc) * sizeof(void *); ++ if (ptr_size > ULONG_MAX - size) ++ goto fail; ++ size += ptr_size; ++ ++ /* + * We've historically supported up to 32 pages (ARG_MAX) + * of argument strings even with small stacks + */ +@@ -218,13 +228,15 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, + * to work from. + */ + rlim = current->signal->rlim; +- if (size > ACCESS_ONCE(rlim[RLIMIT_STACK].rlim_cur) / 4) { +- put_page(page); +- return NULL; +- } ++ if (size > READ_ONCE(rlim[RLIMIT_STACK].rlim_cur) / 4) ++ goto fail; + } + + return page; ++ ++fail: ++ put_page(page); ++ return NULL; + } + + static void put_arg_page(struct page *page) +diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c +index 27695e6..d6aeb84 100644 +--- a/fs/ext2/acl.c ++++ b/fs/ext2/acl.c +@@ -193,15 +193,11 @@ ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type) + case ACL_TYPE_ACCESS: + name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; + if (acl) { +- error = posix_acl_equiv_mode(acl, &inode->i_mode); +- if (error < 0) ++ error = posix_acl_update_mode(inode, &inode->i_mode, &acl); ++ if (error) + return error; +- else { +- inode->i_ctime = CURRENT_TIME_SEC; +- mark_inode_dirty(inode); +- if (error == 0) +- acl = NULL; +- } ++ inode->i_ctime = CURRENT_TIME_SEC; ++ mark_inode_dirty(inode); + } + break; + +diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c +index 36d35c3..9e245af 100644 +--- a/fs/ext2/inode.c ++++ b/fs/ext2/inode.c +@@ -1551,6 +1551,10 @@ int ext2_setattr(struct dentry *dentry, struct iattr *iattr) + if (error) + return error; + ++ error = setattr_killpriv(dentry, iattr); ++ if (error) ++ return error; ++ + if (is_quota_modification(inode, iattr)) + dquot_initialize(inode); + if ((iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid)) || +diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c +index 2c6ccc4..ec4dffa 100644 +--- a/fs/ext3/inode.c ++++ b/fs/ext3/inode.c +@@ -3248,6 +3248,10 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr) + if (error) + return error; + ++ error = setattr_killpriv(dentry, attr); ++ if (error) ++ return error; ++ + if (is_quota_modification(inode, attr)) + dquot_initialize(inode); + if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) || +diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c +index d40c8db..87d9bbf 100644 +--- a/fs/ext4/acl.c ++++ b/fs/ext4/acl.c +@@ -201,15 +201,11 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type, + case ACL_TYPE_ACCESS: + name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS; + if (acl) { +- error = posix_acl_equiv_mode(acl, &inode->i_mode); +- if (error < 0) ++ error = posix_acl_update_mode(inode, &inode->i_mode, &acl); ++ if (error) + return error; +- else { +- inode->i_ctime = ext4_current_time(inode); +- ext4_mark_inode_dirty(handle, inode); +- if (error == 0) +- acl = NULL; +- } ++ inode->i_ctime = ext4_current_time(inode); ++ ext4_mark_inode_dirty(handle, inode); + } + break; + +diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h +index c55a1fa..a2f4422 100644 +--- a/fs/ext4/ext4.h ++++ b/fs/ext4/ext4.h +@@ -2085,7 +2085,8 @@ extern int ext4_mb_add_groupinfo(struct super_block *sb, + ext4_group_t i, struct ext4_group_desc *desc); + extern int ext4_group_add_blocks(handle_t *handle, struct super_block *sb, + ext4_fsblk_t block, unsigned long count); +-extern int ext4_trim_fs(struct super_block *, struct fstrim_range *); ++extern int ext4_trim_fs(struct super_block *, struct fstrim_range *, ++ unsigned long blkdev_flags); + + /* inode.c */ + struct buffer_head *ext4_getblk(handle_t *, struct inode *, ext4_lblk_t, int); +diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c +index d418431..e770c1e 100644 +--- a/fs/ext4/ext4_jbd2.c ++++ b/fs/ext4/ext4_jbd2.c +@@ -88,13 +88,13 @@ int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle) + return 0; + } + ++ err = handle->h_err; + if (!handle->h_transaction) { +- err = jbd2_journal_stop(handle); +- return handle->h_err ? handle->h_err : err; ++ rc = jbd2_journal_stop(handle); ++ return err ? err : rc; + } + + sb = handle->h_transaction->t_journal->j_private; +- err = handle->h_err; + rc = jbd2_journal_stop(handle); + + if (!err) +diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c +index b5fcb1a..c56f72f 100644 +--- a/fs/ext4/extents.c ++++ b/fs/ext4/extents.c +@@ -4792,12 +4792,6 @@ static long ext4_zero_range(struct file *file, loff_t offset, + else + max_blocks -= lblk; + +- flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT | +- EXT4_GET_BLOCKS_CONVERT_UNWRITTEN | +- EXT4_EX_NOCACHE; +- if (mode & FALLOC_FL_KEEP_SIZE) +- flags |= EXT4_GET_BLOCKS_KEEP_SIZE; +- + mutex_lock(&inode->i_mutex); + + /* +@@ -4814,15 +4808,28 @@ static long ext4_zero_range(struct file *file, loff_t offset, + ret = inode_newsize_ok(inode, new_size); + if (ret) + goto out_mutex; +- /* +- * If we have a partial block after EOF we have to allocate +- * the entire block. +- */ +- if (partial_end) +- max_blocks += 1; + } + ++ flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT; ++ if (mode & FALLOC_FL_KEEP_SIZE) ++ flags |= EXT4_GET_BLOCKS_KEEP_SIZE; ++ ++ /* Preallocate the range including the unaligned edges */ ++ if (partial_begin || partial_end) { ++ ret = ext4_alloc_file_blocks(file, ++ round_down(offset, 1 << blkbits) >> blkbits, ++ (round_up((offset + len), 1 << blkbits) - ++ round_down(offset, 1 << blkbits)) >> blkbits, ++ new_size, flags, mode); ++ if (ret) ++ goto out_mutex; ++ ++ } ++ ++ /* Zero range excluding the unaligned edges */ + if (max_blocks > 0) { ++ flags |= (EXT4_GET_BLOCKS_CONVERT_UNWRITTEN | ++ EXT4_EX_NOCACHE); + + /* Now release the pages and zero block aligned part of pages*/ + truncate_pagecache_range(inode, start, end - 1); +diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c +index 777f743..fc46254 100644 +--- a/fs/ext4/inode.c ++++ b/fs/ext4/inode.c +@@ -4481,6 +4481,10 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) + if (error) + return error; + ++ error = setattr_killpriv(dentry, attr); ++ if (error) ++ return error; ++ + if (is_quota_modification(inode, attr)) + dquot_initialize(inode); + if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) || +diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c +index bfda18a..e75462a 100644 +--- a/fs/ext4/ioctl.c ++++ b/fs/ext4/ioctl.c +@@ -587,11 +587,13 @@ resizefs_out: + return err; + } + ++ case FIDTRIM: + case FITRIM: + { + struct request_queue *q = bdev_get_queue(sb->s_bdev); + struct fstrim_range range; + int ret = 0; ++ int flags = cmd == FIDTRIM ? BLKDEV_DISCARD_SECURE : 0; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; +@@ -599,13 +601,15 @@ resizefs_out: + if (!blk_queue_discard(q)) + return -EOPNOTSUPP; + ++ if ((flags & BLKDEV_DISCARD_SECURE) && !blk_queue_secdiscard(q)) ++ return -EOPNOTSUPP; + if (copy_from_user(&range, (struct fstrim_range __user *)arg, + sizeof(range))) + return -EFAULT; + + range.minlen = max((unsigned int)range.minlen, + q->limits.discard_granularity); +- ret = ext4_trim_fs(sb, &range); ++ ret = ext4_trim_fs(sb, &range, flags); + if (ret < 0) + return ret; + +diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c +index 99c8e38..07b13c1 100644 +--- a/fs/ext4/mballoc.c ++++ b/fs/ext4/mballoc.c +@@ -2744,7 +2744,8 @@ int ext4_mb_release(struct super_block *sb) + } + + static inline int ext4_issue_discard(struct super_block *sb, +- ext4_group_t block_group, ext4_grpblk_t cluster, int count) ++ ext4_group_t block_group, ext4_grpblk_t cluster, int count, ++ unsigned long flags) + { + ext4_fsblk_t discard_block; + +@@ -2753,7 +2754,7 @@ static inline int ext4_issue_discard(struct super_block *sb, + count = EXT4_C2B(EXT4_SB(sb), count); + trace_ext4_discard_blocks(sb, + (unsigned long long) discard_block, count); +- return sb_issue_discard(sb, discard_block, count, GFP_NOFS, 0); ++ return sb_issue_discard(sb, discard_block, count, GFP_NOFS, flags); + } + + /* +@@ -2775,7 +2776,7 @@ static void ext4_free_data_callback(struct super_block *sb, + if (test_opt(sb, DISCARD)) { + err = ext4_issue_discard(sb, entry->efd_group, + entry->efd_start_cluster, +- entry->efd_count); ++ entry->efd_count, 0); + if (err && err != -EOPNOTSUPP) + ext4_msg(sb, KERN_WARNING, "discard request in" + " group:%d block:%d count:%d failed" +@@ -4821,7 +4822,8 @@ do_more: + * them with group lock_held + */ + if (test_opt(sb, DISCARD)) { +- err = ext4_issue_discard(sb, block_group, bit, count); ++ err = ext4_issue_discard(sb, block_group, bit, count, ++ 0); + if (err && err != -EOPNOTSUPP) + ext4_msg(sb, KERN_WARNING, "discard request in" + " group:%d block:%d count:%lu failed" +@@ -5016,13 +5018,15 @@ error_return: + * @count: number of blocks to TRIM + * @group: alloc. group we are working with + * @e4b: ext4 buddy for the group ++ * @blkdev_flags: flags for the block device + * + * Trim "count" blocks starting at "start" in the "group". To assure that no + * one will allocate those blocks, mark it as used in buddy bitmap. This must + * be called with under the group lock. + */ + static int ext4_trim_extent(struct super_block *sb, int start, int count, +- ext4_group_t group, struct ext4_buddy *e4b) ++ ext4_group_t group, struct ext4_buddy *e4b, ++ unsigned long blkdev_flags) + __releases(bitlock) + __acquires(bitlock) + { +@@ -5043,7 +5047,7 @@ __acquires(bitlock) + */ + mb_mark_used(e4b, &ex); + ext4_unlock_group(sb, group); +- ret = ext4_issue_discard(sb, group, start, count); ++ ret = ext4_issue_discard(sb, group, start, count, blkdev_flags); + ext4_lock_group(sb, group); + mb_free_blocks(NULL, e4b, start, ex.fe_len); + return ret; +@@ -5056,6 +5060,7 @@ __acquires(bitlock) + * @start: first group block to examine + * @max: last group block to examine + * @minblocks: minimum extent block count ++ * @blkdev_flags: flags for the block device + * + * ext4_trim_all_free walks through group's buddy bitmap searching for free + * extents. When the free block is found, ext4_trim_extent is called to TRIM +@@ -5070,7 +5075,7 @@ __acquires(bitlock) + static ext4_grpblk_t + ext4_trim_all_free(struct super_block *sb, ext4_group_t group, + ext4_grpblk_t start, ext4_grpblk_t max, +- ext4_grpblk_t minblocks) ++ ext4_grpblk_t minblocks, unsigned long blkdev_flags) + { + void *bitmap; + ext4_grpblk_t next, count = 0, free_count = 0; +@@ -5103,7 +5108,8 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group, + + if ((next - start) >= minblocks) { + ret = ext4_trim_extent(sb, start, +- next - start, group, &e4b); ++ next - start, group, &e4b, ++ blkdev_flags); + if (ret && ret != -EOPNOTSUPP) + break; + ret = 0; +@@ -5145,6 +5151,7 @@ out: + * ext4_trim_fs() -- trim ioctl handle function + * @sb: superblock for filesystem + * @range: fstrim_range structure ++ * @blkdev_flags: flags for the block device + * + * start: First Byte to trim + * len: number of Bytes to trim from start +@@ -5153,7 +5160,8 @@ out: + * start to start+len. For each such a group ext4_trim_all_free function + * is invoked to trim all free space. + */ +-int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) ++int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range, ++ unsigned long blkdev_flags) + { + struct ext4_group_info *grp; + ext4_group_t group, first_group, last_group; +@@ -5209,7 +5217,7 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) + + if (grp->bb_free >= minlen) { + cnt = ext4_trim_all_free(sb, group, first_cluster, +- end, minlen); ++ end, minlen, blkdev_flags); + if (cnt < 0) { + ret = cnt; + break; +diff --git a/fs/ext4/super.c b/fs/ext4/super.c +index bf03846..86dc226 100644 +--- a/fs/ext4/super.c ++++ b/fs/ext4/super.c +@@ -304,6 +304,8 @@ static void __save_error_info(struct super_block *sb, const char *func, + struct ext4_super_block *es = EXT4_SB(sb)->s_es; + + EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS; ++ if (bdev_read_only(sb->s_bdev)) ++ return; + es->s_state |= cpu_to_le16(EXT4_ERROR_FS); + es->s_last_error_time = cpu_to_le32(get_seconds()); + strncpy(es->s_last_error_func, func, sizeof(es->s_last_error_func)); +diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c +index 83b9b5a..f12d5c5 100644 +--- a/fs/f2fs/acl.c ++++ b/fs/f2fs/acl.c +@@ -207,12 +207,10 @@ static int __f2fs_set_acl(struct inode *inode, int type, + case ACL_TYPE_ACCESS: + name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; + if (acl) { +- error = posix_acl_equiv_mode(acl, &inode->i_mode); +- if (error < 0) ++ error = posix_acl_update_mode(inode, &inode->i_mode, &acl); ++ if (error) + return error; + set_acl_inode(fi, inode->i_mode); +- if (error == 0) +- acl = NULL; + } + break; + +diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c +index 8e68bb6..c9371d2 100644 +--- a/fs/f2fs/file.c ++++ b/fs/f2fs/file.c +@@ -560,6 +560,10 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) + if (err) + return err; + ++ err = setattr_killpriv(dentry, attr); ++ if (err) ++ return err; ++ + if (attr->ia_valid & ATTR_SIZE) { + err = f2fs_convert_inline_data(inode, attr->ia_size, NULL); + if (err) +diff --git a/fs/fat/cache.c b/fs/fat/cache.c +index 91ad9e1..5d38492 100644 +--- a/fs/fat/cache.c ++++ b/fs/fat/cache.c +@@ -8,9 +8,7 @@ + * May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers. + */ + +-#include <linux/fs.h> + #include <linux/slab.h> +-#include <linux/buffer_head.h> + #include "fat.h" + + /* this must be > 0. */ +@@ -303,15 +301,59 @@ static int fat_bmap_cluster(struct inode *inode, int cluster) + return dclus; + } + +-int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, +- unsigned long *mapped_blocks, int create) ++int fat_get_mapped_cluster(struct inode *inode, sector_t sector, ++ sector_t last_block, ++ unsigned long *mapped_blocks, sector_t *bmap) + { + struct super_block *sb = inode->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); ++ int cluster, offset; ++ ++ cluster = sector >> (sbi->cluster_bits - sb->s_blocksize_bits); ++ offset = sector & (sbi->sec_per_clus - 1); ++ cluster = fat_bmap_cluster(inode, cluster); ++ if (cluster < 0) ++ return cluster; ++ else if (cluster) { ++ *bmap = fat_clus_to_blknr(sbi, cluster) + offset; ++ *mapped_blocks = sbi->sec_per_clus - offset; ++ if (*mapped_blocks > last_block - sector) ++ *mapped_blocks = last_block - sector; ++ } ++ ++ return 0; ++} ++ ++static int is_exceed_eof(struct inode *inode, sector_t sector, ++ sector_t *last_block, int create) ++{ ++ struct super_block *sb = inode->i_sb; + const unsigned long blocksize = sb->s_blocksize; + const unsigned char blocksize_bits = sb->s_blocksize_bits; ++ ++ *last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits; ++ if (sector >= *last_block) { ++ if (!create) ++ return 1; ++ ++ /* ++ * ->mmu_private can access on only allocation path. ++ * (caller must hold ->i_mutex) ++ */ ++ *last_block = (MSDOS_I(inode)->mmu_private + (blocksize - 1)) ++ >> blocksize_bits; ++ if (sector >= *last_block) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, ++ unsigned long *mapped_blocks, int create, bool from_bmap) ++{ ++ struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); + sector_t last_block; +- int cluster, offset; + + *phys = 0; + *mapped_blocks = 0; +@@ -323,31 +365,16 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, + return 0; + } + +- last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits; +- if (sector >= last_block) { +- if (!create) ++ if (!from_bmap) { ++ if (is_exceed_eof(inode, sector, &last_block, create)) + return 0; +- +- /* +- * ->mmu_private can access on only allocation path. +- * (caller must hold ->i_mutex) +- */ +- last_block = (MSDOS_I(inode)->mmu_private + (blocksize - 1)) +- >> blocksize_bits; ++ } else { ++ last_block = inode->i_blocks >> ++ (inode->i_sb->s_blocksize_bits - 9); + if (sector >= last_block) + return 0; + } + +- cluster = sector >> (sbi->cluster_bits - sb->s_blocksize_bits); +- offset = sector & (sbi->sec_per_clus - 1); +- cluster = fat_bmap_cluster(inode, cluster); +- if (cluster < 0) +- return cluster; +- else if (cluster) { +- *phys = fat_clus_to_blknr(sbi, cluster) + offset; +- *mapped_blocks = sbi->sec_per_clus - offset; +- if (*mapped_blocks > last_block - sector) +- *mapped_blocks = last_block - sector; +- } +- return 0; ++ return fat_get_mapped_cluster(inode, sector, last_block, mapped_blocks, ++ phys); + } +diff --git a/fs/fat/dir.c b/fs/fat/dir.c +index 3963ede..bfd5d55 100644 +--- a/fs/fat/dir.c ++++ b/fs/fat/dir.c +@@ -13,13 +13,9 @@ + * Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de> + */ + +-#include <linux/module.h> + #include <linux/slab.h> +-#include <linux/time.h> +-#include <linux/buffer_head.h> + #include <linux/compat.h> + #include <linux/uaccess.h> +-#include <linux/kernel.h> + #include "fat.h" + + /* +@@ -95,7 +91,7 @@ next: + + *bh = NULL; + iblock = *pos >> sb->s_blocksize_bits; +- err = fat_bmap(dir, iblock, &phys, &mapped_blocks, 0); ++ err = fat_bmap(dir, iblock, &phys, &mapped_blocks, 0, false); + if (err || !phys) + return -1; /* beyond EOF or error */ + +@@ -614,9 +610,9 @@ parse_record: + int status = fat_parse_long(inode, &cpos, &bh, &de, + &unicode, &nr_slots); + if (status < 0) { +- ctx->pos = cpos; ++ bh = NULL; + ret = status; +- goto out; ++ goto end_of_dir; + } else if (status == PARSE_INVALID) + goto record_end; + else if (status == PARSE_NOT_LONGNAME) +@@ -658,8 +654,9 @@ parse_record: + fill_len = short_len; + + start_filldir: +- if (!fake_offset) +- ctx->pos = cpos - (nr_slots + 1) * sizeof(struct msdos_dir_entry); ++ ctx->pos = cpos - (nr_slots + 1) * sizeof(struct msdos_dir_entry); ++ if (fake_offset && ctx->pos < 2) ++ ctx->pos = 2; + + if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) { + if (!dir_emit_dot(file, ctx)) +@@ -685,14 +682,19 @@ record_end: + fake_offset = 0; + ctx->pos = cpos; + goto get_new; ++ + end_of_dir: +- ctx->pos = cpos; ++ if (fake_offset && cpos < 2) ++ ctx->pos = 2; ++ else ++ ctx->pos = cpos; + fill_failed: + brelse(bh); + if (unicode) + __putname(unicode); + out: + mutex_unlock(&sbi->s_lock); ++ + return ret; + } + +@@ -702,10 +704,12 @@ static int fat_readdir(struct file *file, struct dir_context *ctx) + } + + #define FAT_IOCTL_FILLDIR_FUNC(func, dirent_type) \ +-static int func(void *__buf, const char *name, int name_len, \ ++static int func(void *_ctx, const char *name, int name_len, \ + loff_t offset, u64 ino, unsigned int d_type) \ + { \ +- struct fat_ioctl_filldir_callback *buf = __buf; \ ++ struct dir_context * ctx = (struct dir_context *)_ctx; \ ++ struct fat_ioctl_filldir_callback *buf = \ ++ container_of(ctx, struct fat_ioctl_filldir_callback, ctx); \ + struct dirent_type __user *d1 = buf->dirent; \ + struct dirent_type __user *d2 = d1 + 1; \ + \ +@@ -766,7 +770,7 @@ static int fat_ioctl_readdir(struct inode *inode, struct file *file, + + buf.dirent = dirent; + buf.result = 0; +- mutex_lock(&inode->i_mutex); ++ inode_lock(inode); + buf.ctx.pos = file->f_pos; + ret = -ENOENT; + if (!IS_DEADDIR(inode)) { +@@ -774,12 +778,392 @@ static int fat_ioctl_readdir(struct inode *inode, struct file *file, + short_only, both ? &buf : NULL); + file->f_pos = buf.ctx.pos; + } ++ inode_unlock(inode); ++ if (ret >= 0) ++ ret = buf.result; ++ return ret; ++} ++ ++/* ++ * This is the "fatfilldirall_t" function type, ++ * used by fat_ioctl_filldirall to let ++ * the kernel specify what kind of dirent layout it wants to have. ++ * This allows the kernel to read directories into kernel space or ++ * to have different dirent layouts depending on the binary type. ++ */ ++typedef int (*fatfilldirall_t)(void *__buf, const char *name, ++ int name_len, loff_t offset, u64 ino, ++ unsigned int d_type, struct msdos_dir_entry *de, ++ char *d_createtime); ++struct fatdirall_context { ++ const fatfilldirall_t actor; ++ loff_t pos; ++}; ++ ++struct fat_ioctl_filldirall_callback { ++ struct fatdirall_context ctx; ++ struct fat_direntall __user *current_dir; ++ struct fat_direntall __user *previous; ++ int count; ++ int usecount; ++ int error; ++ int result; ++ const char *longname; ++ int long_len; ++ const char *shortname; ++ int short_len; ++}; ++ ++static inline bool fat_dir_emit(struct fatdirall_context *ctx, ++ const char *name, int namelen, ++ u64 ino, unsigned type, ++ struct msdos_dir_entry *de, ++ char *d_createtime) ++{ ++ return ctx->actor(ctx, name, namelen, ctx->pos, ino, ++ type, de, d_createtime) == 0; ++} ++static inline bool fat_dir_emit_dot(struct file *file, ++ struct fatdirall_context *ctx, ++ struct msdos_dir_entry *de, ++ char *d_createtime) ++{ ++ return ctx->actor(ctx, ".", 1, ctx->pos, ++ file->f_path.dentry->d_inode->i_ino, ++ DT_DIR, de, d_createtime) == 0; ++} ++static inline bool fat_dir_emit_dotdot(struct file *file, ++ struct fatdirall_context *ctx, ++ struct msdos_dir_entry *de, ++ char *d_createtime) ++{ ++ return ctx->actor(ctx, "..", 2, ctx->pos, ++ parent_ino(file->f_path.dentry), ++ DT_DIR, de, d_createtime) == 0; ++} ++ ++static inline bool fat_dir_emit_dots(struct file *file, ++ struct fatdirall_context *ctx, ++ struct msdos_dir_entry *de, ++ char *d_createtime) ++{ ++ if (ctx->pos == 0) { ++ if (!fat_dir_emit_dot(file, ctx, de, d_createtime)) ++ return false; ++ ctx->pos = 1; ++ } ++ if (ctx->pos == 1) { ++ if (!fat_dir_emit_dotdot(file, ctx, de, d_createtime)) ++ return false; ++ ctx->pos = 2; ++ } ++ return true; ++} ++ ++ ++static int __fat_readdirall(struct inode *inode, struct file *file, ++ struct fatdirall_context *ctx, int short_only, ++ struct fat_ioctl_filldirall_callback *both) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct msdos_sb_info *sbi = MSDOS_SB(sb); ++ struct buffer_head *bh; ++ struct msdos_dir_entry *de; ++ unsigned char nr_slots; ++ wchar_t *unicode = NULL; ++ unsigned char bufname[FAT_MAX_SHORT_SIZE]; ++ int isvfat = sbi->options.isvfat; ++ const char *fill_name = NULL; ++ int fake_offset = 0; ++ loff_t cpos; ++ int short_len = 0, fill_len = 0; ++ int ret = 0; ++ char d_createtime[8]; ++ ++ mutex_lock(&sbi->s_lock); ++ ++ cpos = ctx->pos; ++ /* Fake . and .. for the root directory. */ ++ if (inode->i_ino == MSDOS_ROOT_INO) { ++ if (!fat_dir_emit_dots(file, ctx, NULL, NULL)) ++ goto out; ++ if (ctx->pos == 2) { ++ fake_offset = 1; ++ cpos = 0; ++ } ++ } ++ if (cpos & (sizeof(struct msdos_dir_entry) - 1)) { ++ ret = -ENOENT; ++ goto out; ++ } ++ ++ bh = NULL; ++get_new: ++ if (fat_get_entry(inode, &cpos, &bh, &de) == -1) ++ goto end_of_dir; ++parse_record: ++ nr_slots = 0; ++ /* ++ * Check for long filename entry, but if short_only, we don't ++ * need to parse long filename. ++ */ ++ if (isvfat && !short_only) { ++ if (de->name[0] == DELETED_FLAG) ++ goto record_end; ++ if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) ++ goto record_end; ++ if (de->attr != ATTR_EXT && IS_FREE(de->name)) ++ goto record_end; ++ } else { ++ if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name)) ++ goto record_end; ++ } ++ ++ if (isvfat && de->attr == ATTR_EXT) { ++ int status = fat_parse_long(inode, &cpos, &bh, &de, ++ &unicode, &nr_slots); ++ if (status < 0) { ++ ctx->pos = cpos; ++ ret = status; ++ goto out; ++ } else if (status == PARSE_INVALID) ++ goto record_end; ++ else if (status == PARSE_NOT_LONGNAME) ++ goto parse_record; ++ else if (status == PARSE_EOF) ++ goto end_of_dir; ++ ++ if (nr_slots) { ++ void *longname = unicode + FAT_MAX_UNI_CHARS; ++ int size = PATH_MAX - FAT_MAX_UNI_SIZE; ++ int len = fat_uni_to_x8(sb, unicode, longname, size); ++ ++ fill_name = longname; ++ fill_len = len; ++ ++ short_len = fat_parse_short(sb, de, bufname, ++ sbi->options.dotsOK); ++ if (short_len == 0) ++ goto record_end; ++ ++ /* hack for fat_ioctl_filldir() */ ++ both->longname = fill_name; ++ both->long_len = fill_len; ++ both->shortname = bufname; ++ both->short_len = short_len; ++ fill_name = NULL; ++ fill_len = 0; ++ goto start_filldir; ++ } ++ } ++ ++ short_len = fat_parse_short(sb, de, bufname, sbi->options.dotsOK); ++ if (short_len == 0) ++ goto record_end; ++ ++ fill_name = bufname; ++ fill_len = short_len; ++ ++start_filldir: ++ if (!fake_offset) ++ ctx->pos = cpos - (nr_slots + 1) ++ * sizeof(struct msdos_dir_entry); ++ ++ memset(d_createtime, 0, 8); ++ fat_time_fat2str(sbi, d_createtime, de->ctime, ++ de->cdate, de->ctime_cs); ++ ++ if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) { ++ if (!fat_dir_emit_dot(file, ctx, de, d_createtime)) ++ goto fill_failed; ++ } else if (!memcmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) { ++ if (!fat_dir_emit_dotdot(file, ctx, de, d_createtime)) ++ goto fill_failed; ++ } else { ++ unsigned long inum; ++ loff_t i_pos = fat_make_i_pos(sb, bh, de); ++ struct inode *tmp = fat_iget(sb, i_pos); ++ ++ if (tmp) { ++ inum = tmp->i_ino; ++ iput(tmp); ++ } else ++ inum = iunique(sb, MSDOS_ROOT_INO); ++ if (!fat_dir_emit(ctx, fill_name, fill_len, inum, ++ (de->attr & ATTR_DIR) ? DT_DIR : DT_REG, ++ de, d_createtime)) ++ goto fill_failed; ++ } ++ ++record_end: ++ fake_offset = 0; ++ ctx->pos = cpos; ++ goto get_new; ++end_of_dir: ++ ctx->pos = cpos; ++fill_failed: ++ brelse(bh); ++ if (unicode) ++ __putname(unicode); ++out: ++ mutex_unlock(&sbi->s_lock); ++ return ret; ++} ++ ++static int fat_ioctl_filldirall(void *__buf, const char *name, ++ int name_len, loff_t offset, ++ u64 ino, unsigned int d_type, ++ struct msdos_dir_entry *de, ++ char *d_createtime) ++{ ++ struct fat_direntall __user *dirent; ++ struct fat_ioctl_filldirall_callback *buf; ++ unsigned long d_ino; ++ int reclen = 0; ++ const char *longname = NULL; ++ int long_len = 0; ++ const char *shortname = NULL; ++ int short_len = 0; ++ ++ buf = (struct fat_ioctl_filldirall_callback *) __buf; ++ ++ if (name != NULL) { ++ reclen = ALIGN(offsetof(struct fat_direntall, d_name) ++ + name_len + 2, sizeof(long)); ++ } else { ++ longname = buf->longname; ++ long_len = buf->long_len; ++ shortname = buf->shortname; ++ short_len = buf->short_len; ++ reclen = ALIGN(offsetof(struct fat_direntall, d_name) ++ + long_len + 2, sizeof(long)); ++ } ++ ++ buf->error = -EINVAL; /* only used if we fail.. */ ++ ++ if (reclen >= buf->count) ++ return -EINVAL; ++ ++ d_ino = ino; ++ ++ if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { ++ buf->error = -EOVERFLOW; ++ return -EOVERFLOW; ++ } ++ ++ dirent = buf->previous; ++ ++ if (dirent) { ++ if (__put_user(offset, &dirent->d_off)) ++ goto efault; ++ } ++ ++ dirent = buf->current_dir; ++ ++ if (__put_user(d_ino, &dirent->d_ino)) ++ goto efault; ++ ++ if (__put_user(reclen, &dirent->d_reclen)) ++ goto efault; ++ ++ if (name != NULL) { ++ if (copy_to_user(dirent->d_name, name, name_len)) ++ goto efault; ++ if (__put_user(0, dirent->d_name + name_len)) ++ goto efault; ++ } else { ++ if (copy_to_user(dirent->d_name, longname, long_len)) ++ goto efault; ++ if (__put_user(0, dirent->d_name + long_len)) ++ goto efault; ++ } ++ ++ if (__put_user(d_type, &dirent->d_type)) ++ goto efault; ++ ++ if (de != NULL) { ++ u64 u_size = 0; ++ if (copy_to_user(&dirent->d_size, &u_size, sizeof(u64))) ++ goto efault; ++ if (copy_to_user(&dirent->d_size, &de->size, sizeof(u32))) ++ goto efault; ++ } ++ ++ if (d_createtime != NULL) { ++ if (copy_to_user(dirent->d_createtime, d_createtime, 8)) ++ goto efault; ++ } ++ buf->previous = dirent; ++ dirent = (void __user *)dirent + reclen; ++ buf->current_dir = dirent; ++ buf->count -= reclen; ++ buf->usecount += reclen; ++ return 0; ++efault: ++ buf->error = -EFAULT; ++ return -EFAULT; ++} ++ ++ ++static int fat_ioctl_readdirall(struct inode *inode, struct file *file, ++ void __user *dirent, ++ int short_only, int both) ++{ ++ struct fat_ioctl_filldirall_callback buf = { ++ .ctx.actor = fat_ioctl_filldirall, ++ }; ++ ++ struct fat_direntall_buf __user *userbuf = dirent; ++ int ret; ++ ++ buf.current_dir = &(userbuf->direntall); ++ buf.previous = NULL; ++ buf.error = 0; ++ buf.result = 0; ++ buf.usecount = 0; ++ ++ if (get_user(buf.count, &(userbuf->d_count))) ++ return -EFAULT; ++ ++ mutex_lock(&inode->i_mutex); ++ buf.ctx.pos = file->f_pos; ++ ret = -ENOENT; ++ if (!IS_DEADDIR(inode)) { ++ ret = __fat_readdirall(inode, file, &buf.ctx, ++ short_only, both ? &buf : NULL); ++ file->f_pos = buf.ctx.pos; ++ } + mutex_unlock(&inode->i_mutex); ++ ++ if (__put_user(buf.usecount, &(userbuf->d_usecount))) ++ return -EFAULT; + if (ret >= 0) + ret = buf.result; + return ret; + } + ++static int fat_dir_ioctl_readdirall(struct file *filp, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct inode *inode = filp->f_path.dentry->d_inode; ++ struct fat_direntall_buf __user *direntallbuf; ++ int short_only, both; ++ ++ direntallbuf = (struct fat_direntall_buf __user *)arg; ++ ++ if (!access_ok(VERIFY_WRITE, direntallbuf, ++ sizeof(struct fat_direntall_buf))) ++ return -EFAULT; ++ if (put_user(0, &(direntallbuf->direntall.d_reclen))) ++ return -EFAULT; ++ if (put_user(0, &(direntallbuf->d_usecount))) ++ return -EFAULT; ++ short_only = 0; ++ both = 1; ++ return fat_ioctl_readdirall(inode, filp, direntallbuf, ++ short_only, both); ++} ++ + static long fat_dir_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) + { +@@ -787,6 +1171,9 @@ static long fat_dir_ioctl(struct file *filp, unsigned int cmd, + struct __fat_dirent __user *d1 = (struct __fat_dirent __user *)arg; + int short_only, both; + ++ if (VFAT_IOCTL_READDIR_ALL == cmd) ++ return fat_dir_ioctl_readdirall(filp, cmd, arg); ++ + switch (cmd) { + case VFAT_IOCTL_READDIR_SHORT: + short_only = 1; +diff --git a/fs/fat/fat.h b/fs/fat/fat.h +index e0c4ba3..6cb4ba3 100644 +--- a/fs/fat/fat.h ++++ b/fs/fat/fat.h +@@ -2,11 +2,8 @@ + #define _FAT_H + + #include <linux/buffer_head.h> +-#include <linux/string.h> + #include <linux/nls.h> +-#include <linux/fs.h> + #include <linux/hash.h> +-#include <linux/mutex.h> + #include <linux/ratelimit.h> + #include <linux/msdos_fs.h> + +@@ -66,7 +63,7 @@ struct msdos_sb_info { + unsigned short sec_per_clus; /* sectors/cluster */ + unsigned short cluster_bits; /* log2(cluster_size) */ + unsigned int cluster_size; /* cluster size */ +- unsigned char fats, fat_bits; /* number of FATs, FAT bits (12 or 16) */ ++ unsigned char fats, fat_bits; /* number of FATs, FAT bits (12,16 or 32) */ + unsigned short fat_start; + unsigned long fat_length; /* FAT start & length (sec.) */ + unsigned long dir_start; +@@ -90,7 +87,7 @@ struct msdos_sb_info { + unsigned int vol_id; /*volume ID*/ + + int fatent_shift; +- struct fatent_operations *fatent_ops; ++ const struct fatent_operations *fatent_ops; + struct inode *fat_inode; + struct inode *fsinfo_inode; + +@@ -288,8 +285,11 @@ static inline void fatwchar_to16(__u8 *dst, const wchar_t *src, size_t len) + extern void fat_cache_inval_inode(struct inode *inode); + extern int fat_get_cluster(struct inode *inode, int cluster, + int *fclus, int *dclus); ++extern int fat_get_mapped_cluster(struct inode *inode, sector_t sector, ++ sector_t last_block, ++ unsigned long *mapped_blocks, sector_t *bmap); + extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, +- unsigned long *mapped_blocks, int create); ++ unsigned long *mapped_blocks, int create, bool from_bmap); + + /* fat/dir.c */ + extern const struct file_operations fat_dir_operations; +@@ -370,6 +370,7 @@ extern int fat_file_fsync(struct file *file, loff_t start, loff_t end, + int datasync); + + /* fat/inode.c */ ++extern int fat_block_truncate_page(struct inode *inode, loff_t from); + extern void fat_attach(struct inode *inode, loff_t i_pos); + extern void fat_detach(struct inode *inode); + extern struct inode *fat_iget(struct super_block *sb, loff_t i_pos); +@@ -386,6 +387,7 @@ static inline unsigned long fat_dir_hash(int logstart) + { + return hash_32(logstart, FAT_HASH_BITS); + } ++extern int fat_add_cluster(struct inode *inode); + + /* fat/misc.c */ + extern __printf(3, 4) __cold +@@ -407,6 +409,8 @@ extern void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts, + __le16 __time, __le16 __date, u8 time_cs); + extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts, + __le16 *time, __le16 *date, u8 *time_cs); ++extern void fat_time_fat2str(struct msdos_sb_info *sbi, char *d_createtime, ++ __le16 __time, __le16 __date, u8 time_cs); + extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs); + + int fat_cache_init(void); +diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c +index 260705c..1d9a8c4 100644 +--- a/fs/fat/fatent.c ++++ b/fs/fat/fatent.c +@@ -3,9 +3,6 @@ + * Released under GPL v2. + */ + +-#include <linux/module.h> +-#include <linux/fs.h> +-#include <linux/msdos_fs.h> + #include <linux/blkdev.h> + #include "fat.h" + +@@ -102,7 +99,7 @@ err: + static int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent, + int offset, sector_t blocknr) + { +- struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; ++ const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; + + WARN_ON(blocknr < MSDOS_SB(sb)->fat_start); + fatent->fat_inode = MSDOS_SB(sb)->fat_inode; +@@ -249,7 +246,7 @@ static int fat32_ent_next(struct fat_entry *fatent) + return 0; + } + +-static struct fatent_operations fat12_ops = { ++static const struct fatent_operations fat12_ops = { + .ent_blocknr = fat12_ent_blocknr, + .ent_set_ptr = fat12_ent_set_ptr, + .ent_bread = fat12_ent_bread, +@@ -258,7 +255,7 @@ static struct fatent_operations fat12_ops = { + .ent_next = fat12_ent_next, + }; + +-static struct fatent_operations fat16_ops = { ++static const struct fatent_operations fat16_ops = { + .ent_blocknr = fat_ent_blocknr, + .ent_set_ptr = fat16_ent_set_ptr, + .ent_bread = fat_ent_bread, +@@ -267,7 +264,7 @@ static struct fatent_operations fat16_ops = { + .ent_next = fat16_ent_next, + }; + +-static struct fatent_operations fat32_ops = { ++static const struct fatent_operations fat32_ops = { + .ent_blocknr = fat_ent_blocknr, + .ent_set_ptr = fat32_ent_set_ptr, + .ent_bread = fat_ent_bread, +@@ -323,7 +320,7 @@ static inline int fat_ent_update_ptr(struct super_block *sb, + int offset, sector_t blocknr) + { + struct msdos_sb_info *sbi = MSDOS_SB(sb); +- struct fatent_operations *ops = sbi->fatent_ops; ++ const struct fatent_operations *ops = sbi->fatent_ops; + struct buffer_head **bhs = fatent->bhs; + + /* Is this fatent's blocks including this entry? */ +@@ -352,7 +349,7 @@ int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry) + { + struct super_block *sb = inode->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); +- struct fatent_operations *ops = sbi->fatent_ops; ++ const struct fatent_operations *ops = sbi->fatent_ops; + int err, offset; + sector_t blocknr; + +@@ -410,7 +407,7 @@ int fat_ent_write(struct inode *inode, struct fat_entry *fatent, + int new, int wait) + { + struct super_block *sb = inode->i_sb; +- struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; ++ const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; + int err; + + ops->ent_put(fatent, new); +@@ -435,7 +432,7 @@ static inline int fat_ent_next(struct msdos_sb_info *sbi, + static inline int fat_ent_read_block(struct super_block *sb, + struct fat_entry *fatent) + { +- struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; ++ const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; + sector_t blocknr; + int offset; + +@@ -466,7 +463,7 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) + { + struct super_block *sb = inode->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); +- struct fatent_operations *ops = sbi->fatent_ops; ++ const struct fatent_operations *ops = sbi->fatent_ops; + struct fat_entry fatent, prev_ent; + struct buffer_head *bhs[MAX_BUF_PER_PAGE]; + int i, count, err, nr_bhs, idx_clus; +@@ -554,7 +551,7 @@ int fat_free_clusters(struct inode *inode, int cluster) + { + struct super_block *sb = inode->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); +- struct fatent_operations *ops = sbi->fatent_ops; ++ const struct fatent_operations *ops = sbi->fatent_ops; + struct fat_entry fatent; + struct buffer_head *bhs[MAX_BUF_PER_PAGE]; + int i, err, nr_bhs; +@@ -639,7 +636,7 @@ EXPORT_SYMBOL_GPL(fat_free_clusters); + static void fat_ent_reada(struct super_block *sb, struct fat_entry *fatent, + unsigned long reada_blocks) + { +- struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; ++ const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; + sector_t blocknr; + int i, offset; + +@@ -652,7 +649,7 @@ static void fat_ent_reada(struct super_block *sb, struct fat_entry *fatent, + int fat_count_free_clusters(struct super_block *sb) + { + struct msdos_sb_info *sbi = MSDOS_SB(sb); +- struct fatent_operations *ops = sbi->fatent_ops; ++ const struct fatent_operations *ops = sbi->fatent_ops; + struct fat_entry fatent; + unsigned long reada_blocks, reada_mask, cur_block; + int err = 0, free; +diff --git a/fs/fat/file.c b/fs/fat/file.c +index 85f79a8..25543e6 100644 +--- a/fs/fat/file.c ++++ b/fs/fat/file.c +@@ -10,22 +10,23 @@ + #include <linux/module.h> + #include <linux/compat.h> + #include <linux/mount.h> +-#include <linux/time.h> +-#include <linux/buffer_head.h> +-#include <linux/writeback.h> +-#include <linux/backing-dev.h> + #include <linux/blkdev.h> ++#include <linux/backing-dev.h> + #include <linux/fsnotify.h> + #include <linux/security.h> ++#include <linux/falloc.h> + #include "fat.h" + ++static long fat_fallocate(struct file *file, int mode, ++ loff_t offset, loff_t len); ++ + static int fat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr) + { + u32 attr; + +- mutex_lock(&inode->i_mutex); ++ inode_lock(inode); + attr = fat_make_attrs(inode); +- mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); + + return put_user(attr, user_attr); + } +@@ -46,7 +47,7 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr) + err = mnt_want_write_file(file); + if (err) + goto out; +- mutex_lock(&inode->i_mutex); ++ inode_lock(inode); + + /* + * ATTR_VOLUME and ATTR_DIR cannot be changed; this also +@@ -108,7 +109,7 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr) + fat_save_attrs(inode, attr); + mark_inode_dirty(inode); + out_unlock_inode: +- mutex_unlock(&inode->i_mutex); ++ inode_unlock(inode); + mnt_drop_write_file(file); + out: + return err; +@@ -170,8 +171,6 @@ int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) + + const struct file_operations fat_file_operations = { + .llseek = generic_file_llseek, +- .read = new_sync_read, +- .write = new_sync_write, + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, + .mmap = generic_file_mmap, +@@ -182,6 +181,7 @@ const struct file_operations fat_file_operations = { + #endif + .fsync = fat_file_fsync, + .splice_read = generic_file_splice_read, ++ .fallocate = fat_fallocate, + }; + + static int fat_cont_expand(struct inode *inode, loff_t size) +@@ -220,6 +220,62 @@ out: + return err; + } + ++/* ++ * Preallocate space for a file. This implements fat's fallocate file ++ * operation, which gets called from sys_fallocate system call. User ++ * space requests len bytes at offset. If FALLOC_FL_KEEP_SIZE is set ++ * we just allocate clusters without zeroing them out. Otherwise we ++ * allocate and zero out clusters via an expanding truncate. ++ */ ++static long fat_fallocate(struct file *file, int mode, ++ loff_t offset, loff_t len) ++{ ++ int nr_cluster; /* Number of clusters to be allocated */ ++ loff_t mm_bytes; /* Number of bytes to be allocated for file */ ++ loff_t ondisksize; /* block aligned on-disk size in bytes*/ ++ struct inode *inode = file->f_mapping->host; ++ struct super_block *sb = inode->i_sb; ++ struct msdos_sb_info *sbi = MSDOS_SB(sb); ++ int err = 0; ++ ++ /* No support for hole punch or other fallocate flags. */ ++ if (mode & ~FALLOC_FL_KEEP_SIZE) ++ return -EOPNOTSUPP; ++ ++ /* No support for dir */ ++ if (!S_ISREG(inode->i_mode)) ++ return -EOPNOTSUPP; ++ ++ inode_lock(inode); ++ if (mode & FALLOC_FL_KEEP_SIZE) { ++ ondisksize = inode->i_blocks << 9; ++ if ((offset + len) <= ondisksize) ++ goto error; ++ ++ /* First compute the number of clusters to be allocated */ ++ mm_bytes = offset + len - ondisksize; ++ nr_cluster = (mm_bytes + (sbi->cluster_size - 1)) >> ++ sbi->cluster_bits; ++ ++ /* Start the allocation.We are not zeroing out the clusters */ ++ while (nr_cluster-- > 0) { ++ err = fat_add_cluster(inode); ++ if (err) ++ goto error; ++ } ++ } else { ++ if ((offset + len) <= i_size_read(inode)) ++ goto error; ++ ++ /* This is just an expanding truncate */ ++ err = fat_cont_expand(inode, (offset + len)); ++ } ++ ++error: ++ inode_unlock(inode); ++ return err; ++} ++ + /* Free all clusters after the skip'th cluster. */ + static int fat_free(struct inode *inode, int skip) + { +@@ -311,7 +367,7 @@ void fat_truncate_blocks(struct inode *inode, loff_t offset) + + int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) + { +- struct inode *inode = dentry->d_inode; ++ struct inode *inode = d_inode(dentry); + generic_fillattr(inode, stat); + stat->blksize = MSDOS_SB(inode->i_sb)->cluster_size; + +@@ -380,10 +436,22 @@ static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode) + /* valid file mode bits */ + #define FAT_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXUGO) + ++ ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++void reset_mmu_private(struct inode *inode, loff_t offset) ++{ ++ /**set the inode mmu_private**/ ++ MSDOS_I(inode)->mmu_private = offset; ++ /**set the inode time**/ ++ inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; ++} ++#endif ++ ++ + int fat_setattr(struct dentry *dentry, struct iattr *attr) + { + struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); +- struct inode *inode = dentry->d_inode; ++ struct inode *inode = d_inode(dentry); + unsigned int ia_valid; + int error; + +@@ -408,6 +476,7 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) + * hole before it. XXX: this is no longer true with new truncate + * sequence. + */ ++#if !defined(CONFIG_ARCH_HI3559) && !defined(CONFIG_ARCH_HI3556) + if (attr->ia_valid & ATTR_SIZE) { + inode_dio_wait(inode); + +@@ -418,6 +487,7 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) + attr->ia_valid &= ~ATTR_SIZE; + } + } ++#endif + + if (((attr->ia_valid & ATTR_UID) && + (!uid_eq(attr->ia_uid, sbi->options.fs_uid))) || +@@ -443,8 +513,14 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) + } + + if (attr->ia_valid & ATTR_SIZE) { ++ error = fat_block_truncate_page(inode, attr->ia_size); ++ if (error) ++ goto out; + down_write(&MSDOS_I(inode)->truncate_lock); + truncate_setsize(inode, attr->ia_size); ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++ reset_mmu_private(inode, attr->ia_size); ++#endif + fat_truncate_blocks(inode, attr->ia_size); + up_write(&MSDOS_I(inode)->truncate_lock); + } +diff --git a/fs/fat/inode.c b/fs/fat/inode.c +index 756aead..41ea0da 100644 +--- a/fs/fat/inode.c ++++ b/fs/fat/inode.c +@@ -11,22 +11,15 @@ + */ + + #include <linux/module.h> +-#include <linux/init.h> +-#include <linux/time.h> +-#include <linux/slab.h> +-#include <linux/seq_file.h> + #include <linux/pagemap.h> + #include <linux/mpage.h> +-#include <linux/buffer_head.h> +-#include <linux/mount.h> +-#include <linux/aio.h> + #include <linux/vfs.h> ++#include <linux/seq_file.h> + #include <linux/parser.h> + #include <linux/uio.h> +-#include <linux/writeback.h> +-#include <linux/log2.h> +-#include <linux/hash.h> ++#include <linux/aio.h> + #include <linux/blkdev.h> ++#include <linux/backing-dev.h> + #include <asm/unaligned.h> + #include "fat.h" + +@@ -101,7 +94,7 @@ static struct fat_floppy_defaults { + }, + }; + +-static int fat_add_cluster(struct inode *inode) ++int fat_add_cluster(struct inode *inode) + { + int err, cluster; + +@@ -123,10 +116,10 @@ static inline int __fat_get_block(struct inode *inode, sector_t iblock, + struct super_block *sb = inode->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); + unsigned long mapped_blocks; +- sector_t phys; ++ sector_t phys, last_block; + int err, offset; + +- err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create); ++ err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create, false); + if (err) + return err; + if (phys) { +@@ -143,8 +136,14 @@ static inline int __fat_get_block(struct inode *inode, sector_t iblock, + return -EIO; + } + ++ last_block = inode->i_blocks >> (sb->s_blocksize_bits - 9); + offset = (unsigned long)iblock & (sbi->sec_per_clus - 1); +- if (!offset) { ++ /* ++ * allocate a cluster according to the following. ++ * 1) no more available blocks ++ * 2) not part of fallocate region ++ */ ++ if (!offset && !(iblock < last_block)) { + /* TODO: multiple cluster allocation would be desirable. */ + err = fat_add_cluster(inode); + if (err) +@@ -156,7 +155,7 @@ static inline int __fat_get_block(struct inode *inode, sector_t iblock, + *max_blocks = min(mapped_blocks, *max_blocks); + MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits; + +- err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create); ++ err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create, false); + if (err) + return err; + +@@ -246,8 +245,7 @@ static int fat_write_end(struct file *file, struct address_space *mapping, + return err; + } + +-static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, +- struct iov_iter *iter, ++static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, + loff_t offset) + { + struct file *file = iocb->ki_filp; +@@ -256,7 +254,7 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, + size_t count = iov_iter_count(iter); + ssize_t ret; + +- if (rw == WRITE) { ++ if (iov_iter_rw(iter) == WRITE) { + /* + * FIXME: blockdev_direct_IO() doesn't use ->write_begin(), + * so we need to update the ->mmu_private to block boundary. +@@ -276,24 +274,61 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, + * condition of fat_get_block() and ->truncate(). + */ + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, fat_get_block); +- if (ret < 0 && (rw & WRITE)) ++ if (ret < 0 && iov_iter_rw(iter) == WRITE) + fat_write_failed(mapping, offset + count); + + return ret; + } + ++static int fat_get_block_bmap(struct inode *inode, sector_t iblock, ++ struct buffer_head *bh_result, int create) ++{ ++ struct super_block *sb = inode->i_sb; ++ unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; ++ int err; ++ sector_t bmap; ++ unsigned long mapped_blocks; ++ ++ BUG_ON(create != 0); ++ ++ err = fat_bmap(inode, iblock, &bmap, &mapped_blocks, create, true); ++ if (err) ++ return err; ++ ++ if (bmap) { ++ map_bh(bh_result, sb, bmap); ++ max_blocks = min(mapped_blocks, max_blocks); ++ } ++ ++ bh_result->b_size = max_blocks << sb->s_blocksize_bits; ++ ++ return 0; ++} ++ + static sector_t _fat_bmap(struct address_space *mapping, sector_t block) + { + sector_t blocknr; + + /* fat_get_cluster() assumes the requested blocknr isn't truncated. */ + down_read(&MSDOS_I(mapping->host)->truncate_lock); +- blocknr = generic_block_bmap(mapping, block, fat_get_block); ++ blocknr = generic_block_bmap(mapping, block, fat_get_block_bmap); + up_read(&MSDOS_I(mapping->host)->truncate_lock); + + return blocknr; + } + ++/* ++ * fat_block_truncate_page() zeroes out a mapping from file offset `from' ++ * up to the end of the block which corresponds to `from'. ++ * This is required during truncate to physically zeroout the tail end ++ * of that block so it doesn't yield old data if the file is later grown. ++ * Also, avoid causing failure from fsx for cases of "data past EOF" ++ */ ++int fat_block_truncate_page(struct inode *inode, loff_t from) ++{ ++ return block_truncate_page(inode->i_mapping, from, fat_get_block); ++} ++ + static const struct address_space_operations fat_aops = { + .readpage = fat_readpage, + .readpages = fat_readpages, +@@ -446,6 +481,24 @@ static int fat_calc_dir_size(struct inode *inode) + return 0; + } + ++static int fat_validate_dir(struct inode *dir) ++{ ++ struct super_block *sb = dir->i_sb; ++ ++ if (dir->i_nlink < 2) { ++ /* Directory should have "."/".." entries at least. */ ++ fat_fs_error(sb, "corrupted directory (invalid entries)"); ++ return -EIO; ++ } ++ if (MSDOS_I(dir)->i_start == 0 || ++ MSDOS_I(dir)->i_start == MSDOS_SB(sb)->root_cluster) { ++ /* Directory should point valid cluster. */ ++ fat_fs_error(sb, "corrupted directory (invalid i_start)"); ++ return -EIO; ++ } ++ return 0; ++} ++ + /* doesn't deal with root inode */ + int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) + { +@@ -472,6 +525,10 @@ int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) + MSDOS_I(inode)->mmu_private = inode->i_size; + + set_nlink(inode, fat_subdirs(inode)); ++ ++ error = fat_validate_dir(inode); ++ if (error < 0) ++ return error; + } else { /* not a directory */ + inode->i_generation |= 1; + inode->i_mode = fat_make_mode(sbi, de->attr, +@@ -550,13 +607,46 @@ out: + + EXPORT_SYMBOL_GPL(fat_build_inode); + ++static int __fat_write_inode(struct inode *inode, int wait); ++ ++static void fat_free_eofblocks(struct inode *inode) ++{ ++ /* Release unwritten fallocated blocks on inode eviction. */ ++ if ((inode->i_blocks << 9) > ++ round_up(MSDOS_I(inode)->mmu_private, ++ MSDOS_SB(inode->i_sb)->cluster_size)) { ++ int err; ++ ++#if !defined(CONFIG_ARCH_HI3559) && !defined(CONFIG_ARCH_HI3556) ++ fat_truncate_blocks(inode, MSDOS_I(inode)->mmu_private); ++#endif ++ ++ /* Fallocate results in updating the i_start/iogstart ++ * for the zero byte file. So, make it return to ++ * original state during evict and commit it to avoid ++ * any corruption on the next access to the cluster ++ * chain for the file. ++ */ ++ err = __fat_write_inode(inode, inode_needs_sync(inode)); ++ if (err) { ++ fat_msg(inode->i_sb, KERN_WARNING, "Failed to " ++ "update on disk inode for unused " ++ "fallocated blocks, inode could be " ++ "corrupted. Please run fsck"); ++ } ++ ++ } ++} ++ + static void fat_evict_inode(struct inode *inode) + { + truncate_inode_pages_final(&inode->i_data); + if (!inode->i_nlink) { + inode->i_size = 0; + fat_truncate_blocks(inode, 0); +- } ++ } else ++ fat_free_eofblocks(inode); ++ + invalidate_inode_buffers(inode); + clear_inode(inode); + fat_cache_inval_inode(inode); +@@ -568,7 +658,7 @@ static void fat_set_state(struct super_block *sb, + { + struct buffer_head *bh; + struct fat_boot_sector *b; +- struct msdos_sb_info *sbi = sb->s_fs_info; ++ struct msdos_sb_info *sbi = MSDOS_SB(sb); + + /* do not change any thing if mounted read only */ + if ((sb->s_flags & MS_RDONLY) && !force) +@@ -674,7 +764,7 @@ static int __init fat_init_inodecache(void) + fat_inode_cachep = kmem_cache_create("fat_inode_cache", + sizeof(struct msdos_inode_info), + 0, (SLAB_RECLAIM_ACCOUNT| +- SLAB_MEM_SPREAD), ++ SLAB_MEM_SPREAD|SLAB_ACCOUNT), + init_once); + if (fat_inode_cachep == NULL) + return -ENOMEM; +@@ -1143,7 +1233,12 @@ static int parse_options(struct super_block *sb, char *options, int is_vfat, + case Opt_time_offset: + if (match_int(&args[0], &option)) + return -EINVAL; +- if (option < -12 * 60 || option > 12 * 60) ++ /* ++ * GMT+-12 zones may have DST corrections so at least ++ * 13 hours difference is needed. Make the limit 24 ++ * just in case someone invents something unusual. ++ */ ++ if (option < -24 * 60 || option > 24 * 60) + return -EINVAL; + opts->tz_set = 1; + opts->time_offset = option; +@@ -1266,10 +1361,19 @@ out: + return 0; + } + ++static void fat_dummy_inode_init(struct inode *inode) ++{ ++ /* Initialize this dummy inode to work as no-op. */ ++ MSDOS_I(inode)->mmu_private = 0; ++ MSDOS_I(inode)->i_start = 0; ++ MSDOS_I(inode)->i_logstart = 0; ++ MSDOS_I(inode)->i_attrs = 0; ++ MSDOS_I(inode)->i_pos = 0; ++} ++ + static int fat_read_root(struct inode *inode) + { +- struct super_block *sb = inode->i_sb; +- struct msdos_sb_info *sbi = MSDOS_SB(sb); ++ struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); + int error; + + MSDOS_I(inode)->i_pos = MSDOS_ROOT_INO; +@@ -1711,12 +1815,13 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, + fat_inode = new_inode(sb); + if (!fat_inode) + goto out_fail; +- MSDOS_I(fat_inode)->i_pos = 0; ++ fat_dummy_inode_init(fat_inode); + sbi->fat_inode = fat_inode; + + fsinfo_inode = new_inode(sb); + if (!fsinfo_inode) + goto out_fail; ++ fat_dummy_inode_init(fsinfo_inode); + fsinfo_inode->i_ino = MSDOS_FSINFO_INO; + sbi->fsinfo_inode = fsinfo_inode; + insert_inode_hash(fsinfo_inode); +diff --git a/fs/fat/misc.c b/fs/fat/misc.c +index d8da2d2..0287b52 100644 +--- a/fs/fat/misc.c ++++ b/fs/fat/misc.c +@@ -266,6 +266,37 @@ void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts, + } + EXPORT_SYMBOL_GPL(fat_time_unix2fat); + ++void fat_time_fat2str(struct msdos_sb_info *sbi, char *d_createtime, ++ __le16 __time, __le16 __date, u8 time_cs) ++{ ++ u16 time = le16_to_cpu(__time), date = le16_to_cpu(__date); ++ time_t day, month, year; ++ ++ year = date >> 9; ++ month = max(1, (date >> 5) & 0xf); ++ day = max(1, date & 0x1f); ++ ++ d_createtime[0] = year; ++ d_createtime[1] = month; ++ d_createtime[2] = day; ++ d_createtime[3] = (time >> 11); /*hour*/ ++ d_createtime[4] = ((time >> 5) & 0x3f); /*min*/ ++ d_createtime[5] = (time & 0x1f); /*second 2s*/ ++ ++ ++ if (!sbi->options.tz_set) ++ d_createtime[4] += sys_tz.tz_minuteswest; ++ else ++ d_createtime[4] -= sbi->options.time_offset; ++ ++ if (time_cs) { ++ /*second 1s*/ ++ d_createtime[5] += (time_cs / 100); ++ } ++} ++EXPORT_SYMBOL_GPL(fat_time_fat2str); ++ ++ + int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs) + { + int i, err = 0; +diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c +index a783b0e..b7e2b33 100644 +--- a/fs/fat/namei_msdos.c ++++ b/fs/fat/namei_msdos.c +@@ -7,8 +7,6 @@ + */ + + #include <linux/module.h> +-#include <linux/time.h> +-#include <linux/buffer_head.h> + #include "fat.h" + + /* Characters that are undesirable in an MS-DOS file name */ +@@ -310,7 +308,7 @@ out: + static int msdos_rmdir(struct inode *dir, struct dentry *dentry) + { + struct super_block *sb = dir->i_sb; +- struct inode *inode = dentry->d_inode; ++ struct inode *inode = d_inode(dentry); + struct fat_slot_info sinfo; + int err; + +@@ -404,7 +402,7 @@ out: + /***** Unlink a file */ + static int msdos_unlink(struct inode *dir, struct dentry *dentry) + { +- struct inode *inode = dentry->d_inode; ++ struct inode *inode = d_inode(dentry); + struct super_block *sb = inode->i_sb; + struct fat_slot_info sinfo; + int err; +@@ -442,8 +440,8 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name, + int err, old_attrs, is_dir, update_dotdot, corrupt = 0; + + old_sinfo.bh = sinfo.bh = dotdot_bh = NULL; +- old_inode = old_dentry->d_inode; +- new_inode = new_dentry->d_inode; ++ old_inode = d_inode(old_dentry); ++ new_inode = d_inode(new_dentry); + + err = fat_scan(old_dir, old_name, &old_sinfo); + if (err) { +diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c +index b8b92c2..7092584 100644 +--- a/fs/fat/namei_vfat.c ++++ b/fs/fat/namei_vfat.c +@@ -16,10 +16,8 @@ + */ + + #include <linux/module.h> +-#include <linux/jiffies.h> + #include <linux/ctype.h> + #include <linux/slab.h> +-#include <linux/buffer_head.h> + #include <linux/namei.h> + #include "fat.h" + +@@ -35,7 +33,7 @@ static int vfat_revalidate_shortname(struct dentry *dentry) + { + int ret = 1; + spin_lock(&dentry->d_lock); +- if (dentry->d_time != dentry->d_parent->d_inode->i_version) ++ if (dentry->d_time != d_inode(dentry->d_parent)->i_version) + ret = 0; + spin_unlock(&dentry->d_lock); + return ret; +@@ -47,7 +45,7 @@ static int vfat_revalidate(struct dentry *dentry, unsigned int flags) + return -ECHILD; + + /* This is not negative dentry. Always valid. */ +- if (dentry->d_inode) ++ if (d_really_is_positive(dentry)) + return 1; + return vfat_revalidate_shortname(dentry); + } +@@ -67,7 +65,7 @@ static int vfat_revalidate_ci(struct dentry *dentry, unsigned int flags) + * positive dentry isn't good idea. So it's unsupported like + * rename("filename", "FILENAME") for now. + */ +- if (dentry->d_inode) ++ if (d_really_is_positive(dentry)) + return 1; + + /* +@@ -803,7 +801,7 @@ out: + + static int vfat_rmdir(struct inode *dir, struct dentry *dentry) + { +- struct inode *inode = dentry->d_inode; ++ struct inode *inode = d_inode(dentry); + struct super_block *sb = dir->i_sb; + struct fat_slot_info sinfo; + int err; +@@ -834,7 +832,7 @@ out: + + static int vfat_unlink(struct inode *dir, struct dentry *dentry) + { +- struct inode *inode = dentry->d_inode; ++ struct inode *inode = d_inode(dentry); + struct super_block *sb = dir->i_sb; + struct fat_slot_info sinfo; + int err; +@@ -917,8 +915,8 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct super_block *sb = old_dir->i_sb; + + old_sinfo.bh = sinfo.bh = dotdot_bh = NULL; +- old_inode = old_dentry->d_inode; +- new_inode = new_dentry->d_inode; ++ old_inode = d_inode(old_dentry); ++ new_inode = d_inode(new_dentry); + mutex_lock(&MSDOS_SB(sb)->s_lock); + err = vfat_find(old_dir, &old_dentry->d_name, &old_sinfo); + if (err) +diff --git a/fs/fat/nfs.c b/fs/fat/nfs.c +index 93e1493..eb19265 100644 +--- a/fs/fat/nfs.c ++++ b/fs/fat/nfs.c +@@ -266,7 +266,7 @@ struct inode *fat_rebuild_parent(struct super_block *sb, int parent_logstart) + * Find the parent for a directory that is not currently connected to + * the filesystem root. + * +- * On entry, the caller holds child_dir->d_inode->i_mutex. ++ * On entry, the caller holds d_inode(child_dir)->i_mutex. + */ + static struct dentry *fat_get_parent(struct dentry *child_dir) + { +@@ -276,7 +276,7 @@ static struct dentry *fat_get_parent(struct dentry *child_dir) + struct inode *parent_inode = NULL; + struct msdos_sb_info *sbi = MSDOS_SB(sb); + +- if (!fat_get_dotdot_entry(child_dir->d_inode, &bh, &de)) { ++ if (!fat_get_dotdot_entry(d_inode(child_dir), &bh, &de)) { + int parent_logstart = fat_get_start(sbi, de); + parent_inode = fat_dget(sb, parent_logstart); + if (!parent_inode && sbi->options.nfs == FAT_NFS_NOSTALE_RO) +diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c +index 2d609a5..4bc7cbe 100644 +--- a/fs/fs-writeback.c ++++ b/fs/fs-writeback.c +@@ -1172,7 +1172,7 @@ void __mark_inode_dirty(struct inode *inode, int flags) + if ((inode->i_state & flags) == flags) + return; + +- if (unlikely(block_dump)) ++ if (unlikely(block_dump > 1)) + block_dump___mark_inode_dirty(inode); + + spin_lock(&inode->i_lock); +diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c +index f2bbb85..3f00d67 100644 +--- a/fs/fuse/dev.c ++++ b/fs/fuse/dev.c +@@ -20,6 +20,7 @@ + #include <linux/swap.h> + #include <linux/splice.h> + #include <linux/aio.h> ++#include <linux/freezer.h> + + MODULE_ALIAS_MISCDEV(FUSE_MINOR); + MODULE_ALIAS("devname:fuse"); +@@ -464,7 +465,10 @@ __acquires(fc->lock) + * Wait it out. + */ + spin_unlock(&fc->lock); +- wait_event(req->waitq, req->state == FUSE_REQ_FINISHED); ++ ++ while (req->state != FUSE_REQ_FINISHED) ++ wait_event_freezable(req->waitq, ++ req->state == FUSE_REQ_FINISHED); + spin_lock(&fc->lock); + + if (!req->aborted) +diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c +index dbab798..40d2409 100644 +--- a/fs/fuse/dir.c ++++ b/fs/fuse/dir.c +@@ -1693,9 +1693,10 @@ int fuse_flush_times(struct inode *inode, struct fuse_file *ff) + * vmtruncate() doesn't allow for this case, so do the rlimit checking + * and the actual truncation by hand. + */ +-int fuse_do_setattr(struct inode *inode, struct iattr *attr, ++int fuse_do_setattr(struct dentry *entry, struct iattr *attr, + struct file *file) + { ++ struct inode *inode = entry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_req *req; +@@ -1714,6 +1715,10 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, + if (err) + return err; + ++ err = setattr_killpriv(entry, attr); ++ if (err) ++ return err; ++ + if (attr->ia_valid & ATTR_OPEN) { + if (fc->atomic_o_trunc) + return 0; +@@ -1809,15 +1814,13 @@ error: + + static int fuse_setattr(struct dentry *entry, struct iattr *attr) + { +- struct inode *inode = entry->d_inode; +- +- if (!fuse_allow_current_process(get_fuse_conn(inode))) ++ if (!fuse_allow_current_process(get_fuse_conn(entry->d_inode))) + return -EACCES; + + if (attr->ia_valid & ATTR_FILE) +- return fuse_do_setattr(inode, attr, attr->ia_file); ++ return fuse_do_setattr(entry, attr, attr->ia_file); + else +- return fuse_do_setattr(inode, attr, NULL); ++ return fuse_do_setattr(entry, attr, NULL); + } + + static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, +diff --git a/fs/fuse/file.c b/fs/fuse/file.c +index caa8d95..ffdc363 100644 +--- a/fs/fuse/file.c ++++ b/fs/fuse/file.c +@@ -2855,7 +2855,8 @@ static void fuse_do_truncate(struct file *file) + attr.ia_file = file; + attr.ia_valid |= ATTR_FILE; + +- fuse_do_setattr(inode, &attr, file); ++ /* XXX Is file->f_dentry->d_inode always the same as inode? */ ++ fuse_do_setattr(file->f_dentry, &attr, file); + } + + static inline loff_t fuse_round_up(loff_t off) +diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h +index e8e47a6..163de1f 100644 +--- a/fs/fuse/fuse_i.h ++++ b/fs/fuse/fuse_i.h +@@ -894,7 +894,7 @@ bool fuse_write_update_size(struct inode *inode, loff_t pos); + int fuse_flush_times(struct inode *inode, struct fuse_file *ff); + int fuse_write_inode(struct inode *inode, struct writeback_control *wbc); + +-int fuse_do_setattr(struct inode *inode, struct iattr *attr, ++int fuse_do_setattr(struct dentry *entry, struct iattr *attr, + struct file *file); + + #endif /* _FS_FUSE_I_H */ +diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c +index 7b31430..88e66aa 100644 +--- a/fs/gfs2/acl.c ++++ b/fs/gfs2/acl.c +@@ -79,17 +79,11 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type) + if (type == ACL_TYPE_ACCESS) { + umode_t mode = inode->i_mode; + +- error = posix_acl_equiv_mode(acl, &mode); +- if (error < 0) ++ error = posix_acl_update_mode(inode, &inode->i_mode, &acl); ++ if (error) + return error; +- +- if (error == 0) +- acl = NULL; +- +- if (mode != inode->i_mode) { +- inode->i_mode = mode; ++ if (mode != inode->i_mode) + mark_inode_dirty(inode); +- } + } + + if (acl) { +diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c +index c4ed823..23ba265 100644 +--- a/fs/gfs2/inode.c ++++ b/fs/gfs2/inode.c +@@ -1786,6 +1786,10 @@ static int gfs2_setattr(struct dentry *dentry, struct iattr *attr) + if (error) + goto out; + ++ error = setattr_killpriv(dentry, attr); ++ if (error) ++ goto out; ++ + if (attr->ia_valid & ATTR_SIZE) + error = gfs2_setattr_size(inode, attr->ia_size); + else if (attr->ia_valid & (ATTR_UID | ATTR_GID)) +diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c +index d0929bc..817f7a5 100644 +--- a/fs/hfs/inode.c ++++ b/fs/hfs/inode.c +@@ -620,6 +620,10 @@ int hfs_inode_setattr(struct dentry *dentry, struct iattr * attr) + return hsb->s_quiet ? 0 : error; + } + ++ error = setattr_killpriv(dentry, attr); ++ if (error) ++ return error; ++ + if (attr->ia_valid & ATTR_MODE) { + /* Only the 'w' bits can ever change and only all together. */ + if (attr->ia_mode & S_IWUSR) +diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c +index 0cf786f..12549bc 100644 +--- a/fs/hfsplus/inode.c ++++ b/fs/hfsplus/inode.c +@@ -251,6 +251,10 @@ static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr) + if (error) + return error; + ++ error = setattr_killpriv(dentry, attr); ++ if (error) ++ return error; ++ + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + inode_dio_wait(inode); +diff --git a/fs/hfsplus/posix_acl.c b/fs/hfsplus/posix_acl.c +index df0c9af..71b3087 100644 +--- a/fs/hfsplus/posix_acl.c ++++ b/fs/hfsplus/posix_acl.c +@@ -68,8 +68,8 @@ int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl, + case ACL_TYPE_ACCESS: + xattr_name = POSIX_ACL_XATTR_ACCESS; + if (acl) { +- err = posix_acl_equiv_mode(acl, &inode->i_mode); +- if (err < 0) ++ err = posix_acl_update_mode(inode, &inode->i_mode, &acl); ++ if (err) + return err; + } + err = 0; +diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c +index 2f7a3c0..f9f86f8 100644 +--- a/fs/jffs2/acl.c ++++ b/fs/jffs2/acl.c +@@ -235,9 +235,10 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type) + case ACL_TYPE_ACCESS: + xprefix = JFFS2_XPREFIX_ACL_ACCESS; + if (acl) { +- umode_t mode = inode->i_mode; +- rc = posix_acl_equiv_mode(acl, &mode); +- if (rc < 0) ++ umode_t mode; ++ ++ rc = posix_acl_update_mode(inode, &mode, &acl); ++ if (rc) + return rc; + if (inode->i_mode != mode) { + struct iattr attr; +@@ -249,8 +250,6 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type) + if (rc < 0) + return rc; + } +- if (rc == 0) +- acl = NULL; + } + break; + case ACL_TYPE_DEFAULT: +diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c +index 601afd1..b260789 100644 +--- a/fs/jffs2/fs.c ++++ b/fs/jffs2/fs.c +@@ -197,6 +197,10 @@ int jffs2_setattr(struct dentry *dentry, struct iattr *iattr) + if (rc) + return rc; + ++ rc = setattr_killpriv(dentry, iattr); ++ if (rc) ++ return rc; ++ + rc = jffs2_do_setattr(inode, iattr); + if (!rc && (iattr->ia_valid & ATTR_MODE)) + rc = posix_acl_chmod(inode, inode->i_mode); +diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c +index 0c8ca83..9fad9f4 100644 +--- a/fs/jfs/acl.c ++++ b/fs/jfs/acl.c +@@ -84,13 +84,11 @@ static int __jfs_set_acl(tid_t tid, struct inode *inode, int type, + case ACL_TYPE_ACCESS: + ea_name = POSIX_ACL_XATTR_ACCESS; + if (acl) { +- rc = posix_acl_equiv_mode(acl, &inode->i_mode); +- if (rc < 0) ++ rc = posix_acl_update_mode(inode, &inode->i_mode, &acl); ++ if (rc) + return rc; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); +- if (rc == 0) +- acl = NULL; + } + break; + case ACL_TYPE_DEFAULT: +diff --git a/fs/jfs/file.c b/fs/jfs/file.c +index 33aa0cc..4008313 100644 +--- a/fs/jfs/file.c ++++ b/fs/jfs/file.c +@@ -107,6 +107,10 @@ int jfs_setattr(struct dentry *dentry, struct iattr *iattr) + if (rc) + return rc; + ++ rc = setattr_killpriv(dentry, iattr); ++ if (rc) ++ return rc; ++ + if (is_quota_modification(inode, iattr)) + dquot_initialize(inode); + if ((iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid)) || +diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c +index 9852176..6a70fc5 100644 +--- a/fs/kernfs/inode.c ++++ b/fs/kernfs/inode.c +@@ -135,6 +135,23 @@ int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr) + if (error) + goto out; + ++ /* ++ * If we need to remove privileges, drop the mutex to do that ++ * first and then re-validate the remaining changes. ++ */ ++ if (iattr->ia_valid & ATTR_KILL_PRIV) { ++ mutex_unlock(&kernfs_mutex); ++ ++ error = setattr_killpriv(dentry, iattr); ++ if (error) ++ return error; ++ ++ mutex_lock(&kernfs_mutex); ++ error = inode_change_ok(inode, iattr); ++ if (error) ++ goto out; ++ } ++ + error = __kernfs_setattr(kn, iattr); + if (error) + goto out; +diff --git a/fs/libfs.c b/fs/libfs.c +index 005843c..10afa14 100644 +--- a/fs/libfs.c ++++ b/fs/libfs.c +@@ -375,6 +375,10 @@ int simple_setattr(struct dentry *dentry, struct iattr *iattr) + if (error) + return error; + ++ error = setattr_killpriv(dentry, iattr); ++ if (error) ++ return error; ++ + if (iattr->ia_valid & ATTR_SIZE) + truncate_setsize(inode, iattr->ia_size); + setattr_copy(inode, iattr); +diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c +index 00689a8..5446180 100644 +--- a/fs/nfs/inode.c ++++ b/fs/nfs/inode.c +@@ -496,7 +496,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) + { + struct inode *inode = dentry->d_inode; + struct nfs_fattr *fattr; +- int error = -ENOMEM; ++ int error; + + nfs_inc_stats(inode, NFSIOS_VFSSETATTR); + +@@ -524,9 +524,17 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) + nfs_wb_all(inode); + } + ++ /* XXX Can we assume the server's permission checks are sufficient? */ ++ error = setattr_killpriv(dentry, attr); ++ if (error) ++ goto out; ++ + fattr = nfs_alloc_fattr(); +- if (fattr == NULL) ++ if (fattr == NULL) { ++ error = -ENOMEM; + goto out; ++ } ++ + /* + * Return any delegations if we're going to change ACLs + */ +diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c +index 7e8282d..eefcacf 100644 +--- a/fs/ocfs2/acl.c ++++ b/fs/ocfs2/acl.c +@@ -241,20 +241,16 @@ int ocfs2_set_acl(handle_t *handle, + case ACL_TYPE_ACCESS: + name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS; + if (acl) { +- umode_t mode = inode->i_mode; +- ret = posix_acl_equiv_mode(acl, &mode); +- if (ret < 0) +- return ret; +- else { +- if (ret == 0) +- acl = NULL; ++ umode_t mode; + +- ret = ocfs2_acl_set_mode(inode, di_bh, +- handle, mode); +- if (ret) +- return ret; ++ ret = posix_acl_update_mode(inode, &mode, &acl); ++ if (ret) ++ return ret; + +- } ++ ret = ocfs2_acl_set_mode(inode, di_bh, ++ handle, mode); ++ if (ret) ++ return ret; + } + break; + case ACL_TYPE_DEFAULT: +diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c +index e6e8d64..8d2e7b2 100644 +--- a/fs/ocfs2/file.c ++++ b/fs/ocfs2/file.c +@@ -1139,7 +1139,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) + attr->ia_valid &= ~ATTR_SIZE; + + #define OCFS2_VALID_ATTRS (ATTR_ATIME | ATTR_MTIME | ATTR_CTIME | ATTR_SIZE \ +- | ATTR_GID | ATTR_UID | ATTR_MODE) ++ | ATTR_GID | ATTR_UID | ATTR_MODE | ATTR_KILL_PRIV) + if (!(attr->ia_valid & OCFS2_VALID_ATTRS)) + return 0; + +@@ -1147,6 +1147,10 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) + if (status) + return status; + ++ status = setattr_killpriv(dentry, attr); ++ if (status) ++ return status; ++ + if (is_quota_modification(inode, attr)) + dquot_initialize(inode); + size_change = S_ISREG(inode->i_mode) && attr->ia_valid & ATTR_SIZE; +diff --git a/fs/open.c b/fs/open.c +index 4a8a355..200dca8 100644 +--- a/fs/open.c ++++ b/fs/open.c +@@ -231,8 +231,7 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) + return -EINVAL; + + /* Return error if mode is not supported */ +- if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | +- FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE)) ++ if (mode & ~FALLOC_FL_SUPPORTED_MASK) + return -EOPNOTSUPP; + + /* Punch hole and zero range are mutually exclusive */ +@@ -249,6 +248,9 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) + if ((mode & FALLOC_FL_COLLAPSE_RANGE) && + (mode & ~FALLOC_FL_COLLAPSE_RANGE)) + return -EINVAL; ++ if ((mode & FALLOC_FL_INSERT_RANGE) && ++ (mode & ~FALLOC_FL_INSERT_RANGE)) ++ return -EINVAL; + + if (!(file->f_mode & FMODE_WRITE)) + return -EBADF; +@@ -295,6 +297,8 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) + + sb_start_write(inode->i_sb); + ret = file->f_op->fallocate(file, mode, offset, len); ++ if (ret == 0) ++ fsnotify_modify(file); + sb_end_write(inode->i_sb); + return ret; + } +diff --git a/fs/pipe.c b/fs/pipe.c +index 21981e5..e3ba6c3 100644 +--- a/fs/pipe.c ++++ b/fs/pipe.c +@@ -39,6 +39,12 @@ unsigned int pipe_max_size = 1048576; + */ + unsigned int pipe_min_size = PAGE_SIZE; + ++/* Maximum allocatable pages per user. Hard limit is unset by default, soft ++ * matches default values. ++ */ ++unsigned long pipe_user_pages_hard; ++unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR; ++ + /* + * We use a start+len construction, which provides full use of the + * allocated memory. +@@ -585,20 +591,49 @@ pipe_fasync(int fd, struct file *filp, int on) + return retval; + } + ++static void account_pipe_buffers(struct pipe_inode_info *pipe, ++ unsigned long old, unsigned long new) ++{ ++ atomic_long_add(new - old, &pipe->user->pipe_bufs); ++} ++ ++static bool too_many_pipe_buffers_soft(struct user_struct *user) ++{ ++ return pipe_user_pages_soft && ++ atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft; ++} ++ ++static bool too_many_pipe_buffers_hard(struct user_struct *user) ++{ ++ return pipe_user_pages_hard && ++ atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard; ++} ++ + struct pipe_inode_info *alloc_pipe_info(void) + { + struct pipe_inode_info *pipe; + + pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL); + if (pipe) { +- pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * PIPE_DEF_BUFFERS, GFP_KERNEL); ++ unsigned long pipe_bufs = PIPE_DEF_BUFFERS; ++ struct user_struct *user = get_current_user(); ++ ++ if (!too_many_pipe_buffers_hard(user)) { ++ if (too_many_pipe_buffers_soft(user)) ++ pipe_bufs = 1; ++ pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * pipe_bufs, GFP_KERNEL); ++ } ++ + if (pipe->bufs) { + init_waitqueue_head(&pipe->wait); + pipe->r_counter = pipe->w_counter = 1; +- pipe->buffers = PIPE_DEF_BUFFERS; ++ pipe->buffers = pipe_bufs; ++ pipe->user = user; ++ account_pipe_buffers(pipe, 0, pipe_bufs); + mutex_init(&pipe->mutex); + return pipe; + } ++ free_uid(user); + kfree(pipe); + } + +@@ -609,6 +644,8 @@ void free_pipe_info(struct pipe_inode_info *pipe) + { + int i; + ++ account_pipe_buffers(pipe, pipe->buffers, 0); ++ free_uid(pipe->user); + for (i = 0; i < pipe->buffers; i++) { + struct pipe_buffer *buf = pipe->bufs + i; + if (buf->ops) +@@ -999,6 +1036,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages) + memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer)); + } + ++ account_pipe_buffers(pipe, pipe->buffers, nr_pages); + pipe->curbuf = 0; + kfree(pipe->bufs); + pipe->bufs = bufs; +@@ -1070,6 +1108,11 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) + if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) { + ret = -EPERM; + goto out; ++ } else if ((too_many_pipe_buffers_hard(pipe->user) || ++ too_many_pipe_buffers_soft(pipe->user)) && ++ !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) { ++ ret = -EPERM; ++ goto out; + } + ret = pipe_set_size(pipe, nr_pages); + break; +diff --git a/fs/pnode.c b/fs/pnode.c +index aae331a..18e56fc 100644 +--- a/fs/pnode.c ++++ b/fs/pnode.c +@@ -198,10 +198,15 @@ static struct mount *next_group(struct mount *m, struct mount *origin) + + /* all accesses are serialized by namespace_sem */ + static struct user_namespace *user_ns; +-static struct mount *last_dest, *last_source, *dest_master; ++static struct mount *last_dest, *first_source, *last_source, *dest_master; + static struct mountpoint *mp; + static struct hlist_head *list; + ++static inline bool peers(struct mount *m1, struct mount *m2) ++{ ++ return m1->mnt_group_id == m2->mnt_group_id && m1->mnt_group_id; ++} ++ + static int propagate_one(struct mount *m) + { + struct mount *child; +@@ -212,24 +217,26 @@ static int propagate_one(struct mount *m) + /* skip if mountpoint isn't covered by it */ + if (!is_subdir(mp->m_dentry, m->mnt.mnt_root)) + return 0; +- if (m->mnt_group_id == last_dest->mnt_group_id) { ++ if (peers(m, last_dest)) { + type = CL_MAKE_SHARED; + } else { + struct mount *n, *p; ++ bool done; + for (n = m; ; n = p) { + p = n->mnt_master; +- if (p == dest_master || IS_MNT_MARKED(p)) { +- while (last_dest->mnt_master != p) { +- last_source = last_source->mnt_master; +- last_dest = last_source->mnt_parent; +- } +- if (n->mnt_group_id != last_dest->mnt_group_id) { +- last_source = last_source->mnt_master; +- last_dest = last_source->mnt_parent; +- } ++ if (p == dest_master || IS_MNT_MARKED(p)) + break; +- } + } ++ do { ++ struct mount *parent = last_source->mnt_parent; ++ if (last_source == first_source) ++ break; ++ done = parent->mnt_master == p; ++ if (done && peers(n, parent)) ++ break; ++ last_source = last_source->mnt_master; ++ } while (!done); ++ + type = CL_SLAVE; + /* beginning of peer group among the slaves? */ + if (IS_MNT_SHARED(m)) +@@ -280,6 +287,7 @@ int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp, + */ + user_ns = current->nsproxy->mnt_ns->user_ns; + last_dest = dest_mnt; ++ first_source = source_mnt; + last_source = source_mnt; + mp = dest_mp; + list = tree_list; +diff --git a/fs/posix_acl.c b/fs/posix_acl.c +index 0855f77..94e6826 100644 +--- a/fs/posix_acl.c ++++ b/fs/posix_acl.c +@@ -594,6 +594,37 @@ no_acl: + } + EXPORT_SYMBOL_GPL(posix_acl_create); + ++/** ++ * posix_acl_update_mode - update mode in set_acl ++ * ++ * Update the file mode when setting an ACL: compute the new file permission ++ * bits based on the ACL. In addition, if the ACL is equivalent to the new ++ * file mode, set *acl to NULL to indicate that no ACL should be set. ++ * ++ * As with chmod, clear the setgit bit if the caller is not in the owning group ++ * or capable of CAP_FSETID (see inode_change_ok). ++ * ++ * Called from set_acl inode operations. ++ */ ++int posix_acl_update_mode(struct inode *inode, umode_t *mode_p, ++ struct posix_acl **acl) ++{ ++ umode_t mode = inode->i_mode; ++ int error; ++ ++ error = posix_acl_equiv_mode(*acl, &mode); ++ if (error < 0) ++ return error; ++ if (error == 0) ++ *acl = NULL; ++ if (!in_group_p(inode->i_gid) && ++ !capable_wrt_inode_uidgid(inode, CAP_FSETID)) ++ mode &= ~S_ISGID; ++ *mode_p = mode; ++ return 0; ++} ++EXPORT_SYMBOL(posix_acl_update_mode); ++ + /* + * Fix up the uids and gids in posix acl extended attributes in place. + */ +@@ -869,11 +900,10 @@ int simple_set_acl(struct inode *inode, struct posix_acl *acl, int type) + int error; + + if (type == ACL_TYPE_ACCESS) { +- error = posix_acl_equiv_mode(acl, &inode->i_mode); +- if (error < 0) +- return 0; +- if (error == 0) +- acl = NULL; ++ error = posix_acl_update_mode(inode, ++ &inode->i_mode, &acl); ++ if (error) ++ return error; + } + + inode->i_ctime = CURRENT_TIME; +diff --git a/fs/proc/base.c b/fs/proc/base.c +index 7dc3ea8..bbda576 100644 +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -752,7 +752,8 @@ static ssize_t environ_read(struct file *file, char __user *buf, + int ret = 0; + struct mm_struct *mm = file->private_data; + +- if (!mm) ++ /* Ensure the process spawned far enough to have an environment. */ ++ if (!mm || !mm->env_end) + return 0; + + page = (char *)__get_free_page(GFP_TEMPORARY); +@@ -2601,8 +2602,8 @@ static const struct pid_entry tgid_base_stuff[] = { + ONE("cgroup", S_IRUGO, proc_cgroup_show), + #endif + ONE("oom_score", S_IRUGO, proc_oom_score), +- REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adj_operations), +- REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations), ++ REG("oom_adj", S_IRUSR, proc_oom_adj_operations), ++ REG("oom_score_adj", S_IRUSR, proc_oom_score_adj_operations), + #ifdef CONFIG_AUDITSYSCALL + REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), + REG("sessionid", S_IRUGO, proc_sessionid_operations), +@@ -2946,8 +2947,8 @@ static const struct pid_entry tid_base_stuff[] = { + ONE("cgroup", S_IRUGO, proc_cgroup_show), + #endif + ONE("oom_score", S_IRUGO, proc_oom_score), +- REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adj_operations), +- REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations), ++ REG("oom_adj", S_IRUSR, proc_oom_adj_operations), ++ REG("oom_score_adj", S_IRUSR, proc_oom_score_adj_operations), + #ifdef CONFIG_AUDITSYSCALL + REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), + REG("sessionid", S_IRUGO, proc_sessionid_operations), +diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c +index f92d5dd..f81d63d 100644 +--- a/fs/proc/proc_sysctl.c ++++ b/fs/proc/proc_sysctl.c +@@ -666,7 +666,7 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx) + ctl_dir = container_of(head, struct ctl_dir, header); + + if (!dir_emit_dots(file, ctx)) +- return 0; ++ goto out; + + pos = 2; + +@@ -676,6 +676,7 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx) + break; + } + } ++out: + sysctl_head_finish(head); + return 0; + } +diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c +index 69aa378..23c697e 100644 +--- a/fs/proc/task_mmu.c ++++ b/fs/proc/task_mmu.c +@@ -111,6 +111,56 @@ static void release_task_mempolicy(struct proc_maps_private *priv) + } + #endif + ++static void seq_print_vma_name(struct seq_file *m, struct vm_area_struct *vma) ++{ ++ const char __user *name = vma_get_anon_name(vma); ++ struct mm_struct *mm = vma->vm_mm; ++ ++ unsigned long page_start_vaddr; ++ unsigned long page_offset; ++ unsigned long num_pages; ++ unsigned long max_len = NAME_MAX; ++ int i; ++ ++ page_start_vaddr = (unsigned long)name & PAGE_MASK; ++ page_offset = (unsigned long)name - page_start_vaddr; ++ num_pages = DIV_ROUND_UP(page_offset + max_len, PAGE_SIZE); ++ ++ seq_puts(m, "[anon:"); ++ ++ for (i = 0; i < num_pages; i++) { ++ int len; ++ int write_len; ++ const char *kaddr; ++ long pages_pinned; ++ struct page *page; ++ ++ pages_pinned = get_user_pages(current, mm, page_start_vaddr, ++ 1, 0, 0, &page, NULL); ++ if (pages_pinned < 1) { ++ seq_puts(m, "<fault>]"); ++ return; ++ } ++ ++ kaddr = (const char *)kmap(page); ++ len = min(max_len, PAGE_SIZE - page_offset); ++ write_len = strnlen(kaddr + page_offset, len); ++ seq_write(m, kaddr + page_offset, write_len); ++ kunmap(page); ++ put_page(page); ++ ++ /* if strnlen hit a null terminator then we're done */ ++ if (write_len != len) ++ break; ++ ++ max_len -= len; ++ page_offset = 0; ++ page_start_vaddr += PAGE_SIZE; ++ } ++ ++ seq_putc(m, ']'); ++} ++ + static void vma_stop(struct proc_maps_private *priv) + { + struct mm_struct *mm = priv->mm; +@@ -346,6 +396,12 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) + seq_pad(m, ' '); + seq_printf(m, "[stack:%d]", tid); + } ++ goto done; ++ } ++ ++ if (vma_get_anon_name(vma)) { ++ seq_pad(m, ' '); ++ seq_print_vma_name(m, vma); + } + } + +@@ -602,6 +658,12 @@ static int show_smap(struct seq_file *m, void *v, int is_pid) + + show_map_vma(m, vma, is_pid); + ++ if (vma_get_anon_name(vma)) { ++ seq_puts(m, "Name: "); ++ seq_print_vma_name(m, vma); ++ seq_putc(m, '\n'); ++ } ++ + seq_printf(m, + "Size: %8lu kB\n" + "Rss: %8lu kB\n" +@@ -712,6 +774,7 @@ enum clear_refs_types { + CLEAR_REFS_ANON, + CLEAR_REFS_MAPPED, + CLEAR_REFS_SOFT_DIRTY, ++ CLEAR_REFS_MM_HIWATER_RSS, + CLEAR_REFS_LAST, + }; + +@@ -826,6 +889,18 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, + .mm = mm, + .private = &cp, + }; ++ ++ if (type == CLEAR_REFS_MM_HIWATER_RSS) { ++ /* ++ * Writing 5 to /proc/pid/clear_refs resets the peak ++ * resident set size to this mm's current rss value. ++ */ ++ down_write(&mm->mmap_sem); ++ reset_mm_hiwater_rss(mm); ++ up_write(&mm->mmap_sem); ++ goto out_mm; ++ } ++ + down_read(&mm->mmap_sem); + if (type == CLEAR_REFS_SOFT_DIRTY) { + for (vma = mm->mmap; vma; vma = vma->vm_next) { +@@ -868,6 +943,7 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, + mmu_notifier_invalidate_range_end(mm, 0, -1); + flush_tlb_mm(mm); + up_read(&mm->mmap_sem); ++out_mm: + mmput(mm); + } + put_task_struct(task); +diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig +index 983d951..916b8e2 100644 +--- a/fs/pstore/Kconfig ++++ b/fs/pstore/Kconfig +@@ -21,6 +21,16 @@ config PSTORE_CONSOLE + When the option is enabled, pstore will log all kernel + messages, even if no oops or panic happened. + ++config PSTORE_PMSG ++ bool "Log user space messages" ++ depends on PSTORE ++ help ++ When the option is enabled, pstore will export a character ++ interface /dev/pmsg0 to log user space messages. On reboot ++ data can be retrieved from /sys/fs/pstore/pmsg-ramoops-[ID]. ++ ++ If unsure, say N. ++ + config PSTORE_FTRACE + bool "Persistent function tracer" + depends on PSTORE +diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile +index 4c9095c..e647d8e 100644 +--- a/fs/pstore/Makefile ++++ b/fs/pstore/Makefile +@@ -7,5 +7,7 @@ obj-y += pstore.o + pstore-objs += inode.o platform.o + obj-$(CONFIG_PSTORE_FTRACE) += ftrace.o + ++obj-$(CONFIG_PSTORE_PMSG) += pmsg.o ++ + ramoops-objs += ram.o ram_core.o + obj-$(CONFIG_PSTORE_RAM) += ramoops.o +diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c +index fafb7a0..6dce93c 100644 +--- a/fs/pstore/inode.c ++++ b/fs/pstore/inode.c +@@ -316,32 +316,38 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, + + switch (type) { + case PSTORE_TYPE_DMESG: +- sprintf(name, "dmesg-%s-%lld%s", psname, id, +- compressed ? ".enc.z" : ""); ++ scnprintf(name, sizeof(name), "dmesg-%s-%lld%s", ++ psname, id, compressed ? ".enc.z" : ""); + break; + case PSTORE_TYPE_CONSOLE: +- sprintf(name, "console-%s-%lld", psname, id); ++ scnprintf(name, sizeof(name), "console-%s", psname); + break; + case PSTORE_TYPE_FTRACE: +- sprintf(name, "ftrace-%s-%lld", psname, id); ++ scnprintf(name, sizeof(name), "ftrace-%s", psname); + break; + case PSTORE_TYPE_MCE: +- sprintf(name, "mce-%s-%lld", psname, id); ++ scnprintf(name, sizeof(name), "mce-%s-%lld", psname, id); + break; + case PSTORE_TYPE_PPC_RTAS: +- sprintf(name, "rtas-%s-%lld", psname, id); ++ scnprintf(name, sizeof(name), "rtas-%s-%lld", psname, id); + break; + case PSTORE_TYPE_PPC_OF: +- sprintf(name, "powerpc-ofw-%s-%lld", psname, id); ++ scnprintf(name, sizeof(name), "powerpc-ofw-%s-%lld", ++ psname, id); + break; + case PSTORE_TYPE_PPC_COMMON: +- sprintf(name, "powerpc-common-%s-%lld", psname, id); ++ scnprintf(name, sizeof(name), "powerpc-common-%s-%lld", ++ psname, id); ++ break; ++ case PSTORE_TYPE_PMSG: ++ scnprintf(name, sizeof(name), "pmsg-%s-%lld", psname, id); + break; + case PSTORE_TYPE_UNKNOWN: +- sprintf(name, "unknown-%s-%lld", psname, id); ++ scnprintf(name, sizeof(name), "unknown-%s-%lld", psname, id); + break; + default: +- sprintf(name, "type%d-%s-%lld", type, psname, id); ++ scnprintf(name, sizeof(name), "type%d-%s-%lld", ++ type, psname, id); + break; + } + +diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h +index 3b3d305..c36ba2c 100644 +--- a/fs/pstore/internal.h ++++ b/fs/pstore/internal.h +@@ -45,6 +45,12 @@ extern void pstore_register_ftrace(void); + static inline void pstore_register_ftrace(void) {} + #endif + ++#ifdef CONFIG_PSTORE_PMSG ++extern void pstore_register_pmsg(void); ++#else ++static inline void pstore_register_pmsg(void) {} ++#endif ++ + extern struct pstore_info *psinfo; + + extern void pstore_set_kmsg_bytes(int); +diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c +index 0a9b72c..15ee78c 100644 +--- a/fs/pstore/platform.c ++++ b/fs/pstore/platform.c +@@ -447,6 +447,7 @@ int pstore_register(struct pstore_info *psi) + if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) { + pstore_register_console(); + pstore_register_ftrace(); ++ pstore_register_pmsg(); + } + + if (pstore_update_ms >= 0) { +diff --git a/fs/pstore/pmsg.c b/fs/pstore/pmsg.c +new file mode 100644 +index 0000000..5a2f05a +--- /dev/null ++++ b/fs/pstore/pmsg.c +@@ -0,0 +1,116 @@ ++/* ++ * Copyright 2014 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/cdev.h> ++#include <linux/device.h> ++#include <linux/fs.h> ++#include <linux/uaccess.h> ++#include <linux/vmalloc.h> ++#include "internal.h" ++ ++static DEFINE_MUTEX(pmsg_lock); ++#define PMSG_MAX_BOUNCE_BUFFER_SIZE (2*PAGE_SIZE) ++ ++static ssize_t write_pmsg(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ size_t i, buffer_size; ++ char *buffer; ++ ++ if (!count) ++ return 0; ++ ++ if (!access_ok(VERIFY_READ, buf, count)) ++ return -EFAULT; ++ ++ buffer_size = count; ++ if (buffer_size > PMSG_MAX_BOUNCE_BUFFER_SIZE) ++ buffer_size = PMSG_MAX_BOUNCE_BUFFER_SIZE; ++ buffer = vmalloc(buffer_size); ++ if (!buffer) ++ return -ENOMEM; ++ ++ mutex_lock(&pmsg_lock); ++ for (i = 0; i < count; ) { ++ size_t c = min(count - i, buffer_size); ++ u64 id; ++ long ret; ++ ++ ret = __copy_from_user(buffer, buf + i, c); ++ if (unlikely(ret != 0)) { ++ mutex_unlock(&pmsg_lock); ++ vfree(buffer); ++ return -EFAULT; ++ } ++ psinfo->write_buf(PSTORE_TYPE_PMSG, 0, &id, 0, buffer, 0, c, ++ psinfo); ++ ++ i += c; ++ } ++ ++ mutex_unlock(&pmsg_lock); ++ vfree(buffer); ++ return count; ++} ++ ++static const struct file_operations pmsg_fops = { ++ .owner = THIS_MODULE, ++ .llseek = noop_llseek, ++ .write = write_pmsg, ++}; ++ ++static struct class *pmsg_class; ++static int pmsg_major; ++#define PMSG_NAME "pmsg" ++#undef pr_fmt ++#define pr_fmt(fmt) PMSG_NAME ": " fmt ++ ++static char *pmsg_devnode(struct device *dev, umode_t *mode) ++{ ++ if (mode) ++ *mode = 0220; ++ return NULL; ++} ++ ++void pstore_register_pmsg(void) ++{ ++ struct device *pmsg_device; ++ ++ pmsg_major = register_chrdev(0, PMSG_NAME, &pmsg_fops); ++ if (pmsg_major < 0) { ++ pr_err("register_chrdev failed\n"); ++ goto err; ++ } ++ ++ pmsg_class = class_create(THIS_MODULE, PMSG_NAME); ++ if (IS_ERR(pmsg_class)) { ++ pr_err("device class file already in use\n"); ++ goto err_class; ++ } ++ pmsg_class->devnode = pmsg_devnode; ++ ++ pmsg_device = device_create(pmsg_class, NULL, MKDEV(pmsg_major, 0), ++ NULL, "%s%d", PMSG_NAME, 0); ++ if (IS_ERR(pmsg_device)) { ++ pr_err("failed to create device\n"); ++ goto err_device; ++ } ++ return; ++ ++err_device: ++ class_destroy(pmsg_class); ++err_class: ++ unregister_chrdev(pmsg_major, PMSG_NAME); ++err: ++ return; ++} +diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c +index 5fa3424..c28dc68 100644 +--- a/fs/pstore/ram.c ++++ b/fs/pstore/ram.c +@@ -51,6 +51,10 @@ static ulong ramoops_ftrace_size = MIN_MEM_SIZE; + module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400); + MODULE_PARM_DESC(ftrace_size, "size of ftrace log"); + ++static ulong ramoops_pmsg_size = MIN_MEM_SIZE; ++module_param_named(pmsg_size, ramoops_pmsg_size, ulong, 0400); ++MODULE_PARM_DESC(pmsg_size, "size of user space message log"); ++ + static ulong mem_address; + module_param(mem_address, ulong, 0400); + MODULE_PARM_DESC(mem_address, +@@ -82,12 +86,14 @@ struct ramoops_context { + struct persistent_ram_zone **przs; + struct persistent_ram_zone *cprz; + struct persistent_ram_zone *fprz; ++ struct persistent_ram_zone *mprz; + phys_addr_t phys_addr; + unsigned long size; + unsigned int memtype; + size_t record_size; + size_t console_size; + size_t ftrace_size; ++ size_t pmsg_size; + int dump_oops; + struct persistent_ram_ecc_info ecc_info; + unsigned int max_dump_cnt; +@@ -96,6 +102,7 @@ struct ramoops_context { + unsigned int dump_read_cnt; + unsigned int console_read_cnt; + unsigned int ftrace_read_cnt; ++ unsigned int pmsg_read_cnt; + struct pstore_info pstore; + }; + +@@ -109,6 +116,7 @@ static int ramoops_pstore_open(struct pstore_info *psi) + cxt->dump_read_cnt = 0; + cxt->console_read_cnt = 0; + cxt->ftrace_read_cnt = 0; ++ cxt->pmsg_read_cnt = 0; + return 0; + } + +@@ -162,6 +170,12 @@ static void ramoops_read_kmsg_hdr(char *buffer, struct timespec *time, + } + } + ++static bool prz_ok(struct persistent_ram_zone *prz) ++{ ++ return !!prz && !!(persistent_ram_old_size(prz) + ++ persistent_ram_ecc_string(prz, NULL, 0)); ++} ++ + static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, + int *count, struct timespec *time, + char **buf, bool *compressed, +@@ -175,13 +189,16 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, + prz = ramoops_get_next_prz(cxt->przs, &cxt->dump_read_cnt, + cxt->max_dump_cnt, id, type, + PSTORE_TYPE_DMESG, 1); +- if (!prz) ++ if (!prz_ok(prz)) + prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt, + 1, id, type, PSTORE_TYPE_CONSOLE, 0); +- if (!prz) ++ if (!prz_ok(prz)) + prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt, + 1, id, type, PSTORE_TYPE_FTRACE, 0); +- if (!prz) ++ if (!prz_ok(prz)) ++ prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt, ++ 1, id, type, PSTORE_TYPE_PMSG, 0); ++ if (!prz_ok(prz)) + return 0; + + size = persistent_ram_old_size(prz); +@@ -244,6 +261,11 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, + return -ENOMEM; + persistent_ram_write(cxt->fprz, buf, size); + return 0; ++ } else if (type == PSTORE_TYPE_PMSG) { ++ if (!cxt->mprz) ++ return -ENOMEM; ++ persistent_ram_write(cxt->mprz, buf, size); ++ return 0; + } + + if (type != PSTORE_TYPE_DMESG) +@@ -301,6 +323,9 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count, + case PSTORE_TYPE_FTRACE: + prz = cxt->fprz; + break; ++ case PSTORE_TYPE_PMSG: ++ prz = cxt->mprz; ++ break; + default: + return -EINVAL; + } +@@ -411,6 +436,12 @@ static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt, + return 0; + } + ++void notrace ramoops_console_write_buf(const char *buf, size_t size) ++{ ++ struct ramoops_context *cxt = &oops_cxt; ++ persistent_ram_write(cxt->cprz, buf, size); ++} ++ + static int ramoops_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -427,7 +458,7 @@ static int ramoops_probe(struct platform_device *pdev) + goto fail_out; + + if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size && +- !pdata->ftrace_size)) { ++ !pdata->ftrace_size && !pdata->pmsg_size)) { + pr_err("The memory size and the record/console size must be " + "non-zero\n"); + goto fail_out; +@@ -439,6 +470,8 @@ static int ramoops_probe(struct platform_device *pdev) + pdata->console_size = rounddown_pow_of_two(pdata->console_size); + if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size)) + pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size); ++ if (pdata->pmsg_size && !is_power_of_2(pdata->pmsg_size)) ++ pdata->pmsg_size = rounddown_pow_of_two(pdata->pmsg_size); + + cxt->size = pdata->mem_size; + cxt->phys_addr = pdata->mem_address; +@@ -446,12 +479,14 @@ static int ramoops_probe(struct platform_device *pdev) + cxt->record_size = pdata->record_size; + cxt->console_size = pdata->console_size; + cxt->ftrace_size = pdata->ftrace_size; ++ cxt->pmsg_size = pdata->pmsg_size; + cxt->dump_oops = pdata->dump_oops; + cxt->ecc_info = pdata->ecc_info; + + paddr = cxt->phys_addr; + +- dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size; ++ dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size ++ - cxt->pmsg_size; + err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz); + if (err) + goto fail_out; +@@ -466,13 +501,9 @@ static int ramoops_probe(struct platform_device *pdev) + if (err) + goto fail_init_fprz; + +- if (!cxt->przs && !cxt->cprz && !cxt->fprz) { +- pr_err("memory size too small, minimum is %zu\n", +- cxt->console_size + cxt->record_size + +- cxt->ftrace_size); +- err = -EINVAL; +- goto fail_cnt; +- } ++ err = ramoops_init_prz(dev, cxt, &cxt->mprz, &paddr, cxt->pmsg_size, 0); ++ if (err) ++ goto fail_init_mprz; + + cxt->pstore.data = cxt; + /* +@@ -517,7 +548,9 @@ fail_buf: + kfree(cxt->pstore.buf); + fail_clear: + cxt->pstore.bufsize = 0; +-fail_cnt: ++ cxt->max_dump_cnt = 0; ++ kfree(cxt->mprz); ++fail_init_mprz: + kfree(cxt->fprz); + fail_init_fprz: + kfree(cxt->cprz); +@@ -576,6 +609,7 @@ static void ramoops_register_dummy(void) + dummy_data->record_size = record_size; + dummy_data->console_size = ramoops_console_size; + dummy_data->ftrace_size = ramoops_ftrace_size; ++ dummy_data->pmsg_size = ramoops_pmsg_size; + dummy_data->dump_oops = dump_oops; + /* + * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC +diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c +index a7eec98..a458c12 100644 +--- a/fs/reiserfs/inode.c ++++ b/fs/reiserfs/inode.c +@@ -3316,6 +3316,10 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) + if (error) + return error; + ++ error = setattr_killpriv(dentry, attr); ++ if (error) ++ return error; ++ + /* must be turned off for recursive notify_change calls */ + ia_valid = attr->ia_valid &= ~(ATTR_KILL_SUID|ATTR_KILL_SGID); + +diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c +index 4b34b9d..9b1824f 100644 +--- a/fs/reiserfs/xattr_acl.c ++++ b/fs/reiserfs/xattr_acl.c +@@ -246,13 +246,9 @@ __reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode, + case ACL_TYPE_ACCESS: + name = POSIX_ACL_XATTR_ACCESS; + if (acl) { +- error = posix_acl_equiv_mode(acl, &inode->i_mode); +- if (error < 0) ++ error = posix_acl_update_mode(inode, &inode->i_mode, &acl); ++ if (error) + return error; +- else { +- if (error == 0) +- acl = NULL; +- } + } + break; + case ACL_TYPE_DEFAULT: +diff --git a/fs/super.c b/fs/super.c +index eae088f..cdf6dec 100644 +--- a/fs/super.c ++++ b/fs/super.c +@@ -769,7 +769,7 @@ static void do_emergency_remount(struct work_struct *work) + struct super_block *sb, *p = NULL; + + spin_lock(&sb_lock); +- list_for_each_entry(sb, &super_blocks, s_list) { ++ list_for_each_entry_reverse(sb, &super_blocks, s_list) { + if (hlist_unhashed(&sb->s_instances)) + continue; + sb->s_count++; +diff --git a/fs/timerfd.c b/fs/timerfd.c +index b46ffa9..21ea542 100644 +--- a/fs/timerfd.c ++++ b/fs/timerfd.c +@@ -40,6 +40,7 @@ struct timerfd_ctx { + short unsigned settime_flags; /* to show in fdinfo */ + struct rcu_head rcu; + struct list_head clist; ++ spinlock_t cancel_lock; + bool might_cancel; + }; + +@@ -112,7 +113,7 @@ void timerfd_clock_was_set(void) + rcu_read_unlock(); + } + +-static void timerfd_remove_cancel(struct timerfd_ctx *ctx) ++static void __timerfd_remove_cancel(struct timerfd_ctx *ctx) + { + if (ctx->might_cancel) { + ctx->might_cancel = false; +@@ -122,6 +123,13 @@ static void timerfd_remove_cancel(struct timerfd_ctx *ctx) + } + } + ++static void timerfd_remove_cancel(struct timerfd_ctx *ctx) ++{ ++ spin_lock(&ctx->cancel_lock); ++ __timerfd_remove_cancel(ctx); ++ spin_unlock(&ctx->cancel_lock); ++} ++ + static bool timerfd_canceled(struct timerfd_ctx *ctx) + { + if (!ctx->might_cancel || ctx->moffs.tv64 != KTIME_MAX) +@@ -132,6 +140,7 @@ static bool timerfd_canceled(struct timerfd_ctx *ctx) + + static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags) + { ++ spin_lock(&ctx->cancel_lock); + if ((ctx->clockid == CLOCK_REALTIME || + ctx->clockid == CLOCK_REALTIME_ALARM) && + (flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) { +@@ -141,9 +150,10 @@ static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags) + list_add_rcu(&ctx->clist, &cancel_list); + spin_unlock(&cancel_lock); + } +- } else if (ctx->might_cancel) { +- timerfd_remove_cancel(ctx); ++ } else { ++ __timerfd_remove_cancel(ctx); + } ++ spin_unlock(&ctx->cancel_lock); + } + + static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) +@@ -394,6 +404,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) + return -ENOMEM; + + init_waitqueue_head(&ctx->wqh); ++ spin_lock_init(&ctx->cancel_lock); + ctx->clockid = clockid; + + if (isalarm(ctx)) +@@ -414,7 +425,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) + return ufd; + } + +-static int do_timerfd_settime(int ufd, int flags, ++static int do_timerfd_settime(int ufd, int flags, + const struct itimerspec *new, + struct itimerspec *old) + { +diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c +index b5b593c..73d2e87 100644 +--- a/fs/ubifs/file.c ++++ b/fs/ubifs/file.c +@@ -1269,6 +1269,10 @@ int ubifs_setattr(struct dentry *dentry, struct iattr *attr) + if (err) + return err; + ++ err = setattr_killpriv(dentry, attr); ++ if (err) ++ return err; ++ + if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size < inode->i_size) + /* Truncation to a smaller size */ + err = do_truncation(c, inode, attr); +diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c +index a65fa5d..90d6d8a 100644 +--- a/fs/xfs/xfs_acl.c ++++ b/fs/xfs/xfs_acl.c +@@ -244,7 +244,8 @@ xfs_set_mode(struct inode *inode, umode_t mode) + iattr.ia_mode = mode; + iattr.ia_ctime = current_fs_time(inode->i_sb); + +- error = xfs_setattr_nonsize(XFS_I(inode), &iattr, XFS_ATTR_NOACL); ++ error = xfs_setattr_nonsize(NULL, XFS_I(inode), &iattr, ++ XFS_ATTR_NOACL); + } + + return error; +@@ -286,16 +287,11 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type) + return error; + + if (type == ACL_TYPE_ACCESS) { +- umode_t mode = inode->i_mode; +- error = posix_acl_equiv_mode(acl, &mode); +- +- if (error <= 0) { +- acl = NULL; +- +- if (error < 0) +- return error; +- } ++ umode_t mode; + ++ error = posix_acl_update_mode(inode, &mode, &acl); ++ if (error) ++ return error; + error = xfs_set_mode(inode, mode); + if (error) + return error; +diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c +index 62db83a..ae64625 100644 +--- a/fs/xfs/xfs_attr_list.c ++++ b/fs/xfs/xfs_attr_list.c +@@ -205,8 +205,10 @@ xfs_attr_shortform_list(xfs_attr_list_context_t *context) + sbp->namelen, + sbp->valuelen, + &sbp->name[sbp->namelen]); +- if (error) ++ if (error) { ++ kmem_free(sbuf); + return error; ++ } + if (context->seen_enough) + break; + cursor->offset++; +@@ -454,14 +456,13 @@ xfs_attr3_leaf_list_int( + args.rmtblkcnt = xfs_attr3_rmt_blocks( + args.dp->i_mount, valuelen); + retval = xfs_attr_rmtval_get(&args); +- if (retval) +- return retval; +- retval = context->put_listent(context, +- entry->flags, +- name_rmt->name, +- (int)name_rmt->namelen, +- valuelen, +- args.value); ++ if (!retval) ++ retval = context->put_listent(context, ++ entry->flags, ++ name_rmt->name, ++ (int)name_rmt->namelen, ++ valuelen, ++ args.value); + kmem_free(args.value); + } else { + retval = context->put_listent(context, +diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c +index ba8b158..a1295b0 100644 +--- a/fs/xfs/xfs_file.c ++++ b/fs/xfs/xfs_file.c +@@ -879,7 +879,7 @@ xfs_file_fallocate( + + iattr.ia_valid = ATTR_SIZE; + iattr.ia_size = new_size; +- error = xfs_setattr_size(ip, &iattr); ++ error = xfs_setattr_size(NULL, ip, &iattr); + } + + out_unlock: +diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c +index 24c926b..3e0dc56 100644 +--- a/fs/xfs/xfs_ioctl.c ++++ b/fs/xfs/xfs_ioctl.c +@@ -714,7 +714,7 @@ xfs_ioc_space( + iattr.ia_valid = ATTR_SIZE; + iattr.ia_size = bf->l_start; + +- error = xfs_setattr_size(ip, &iattr); ++ error = xfs_setattr_size(NULL, ip, &iattr); + if (!error) + clrprealloc = true; + break; +diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c +index d2273d2..129d03c 100644 +--- a/fs/xfs/xfs_iops.c ++++ b/fs/xfs/xfs_iops.c +@@ -527,6 +527,7 @@ xfs_setattr_time( + + int + xfs_setattr_nonsize( ++ struct dentry *dentry, + struct xfs_inode *ip, + struct iattr *iattr, + int flags) +@@ -554,6 +555,11 @@ xfs_setattr_nonsize( + error = inode_change_ok(inode, iattr); + if (error) + return error; ++ ++ ++ error = setattr_killpriv(dentry, iattr); ++ if (error) ++ return error; + } + + ASSERT((mask & ATTR_SIZE) == 0); +@@ -734,6 +740,7 @@ out_dqrele: + */ + int + xfs_setattr_size( ++ struct dentry *dentry, + struct xfs_inode *ip, + struct iattr *iattr) + { +@@ -777,9 +784,13 @@ xfs_setattr_size( + * Use the regular setattr path to update the timestamps. + */ + iattr->ia_valid &= ~ATTR_SIZE; +- return xfs_setattr_nonsize(ip, iattr, 0); ++ return xfs_setattr_nonsize(dentry, ip, iattr, 0); + } + ++ error = setattr_killpriv(dentry, iattr); ++ if (error) ++ return error; ++ + /* + * Make sure that the dquots are attached to the inode. + */ +@@ -966,10 +977,10 @@ xfs_vn_setattr( + + if (iattr->ia_valid & ATTR_SIZE) { + xfs_ilock(ip, XFS_IOLOCK_EXCL); +- error = xfs_setattr_size(ip, iattr); ++ error = xfs_setattr_size(dentry, ip, iattr); + xfs_iunlock(ip, XFS_IOLOCK_EXCL); + } else { +- error = xfs_setattr_nonsize(ip, iattr, 0); ++ error = xfs_setattr_nonsize(dentry, ip, iattr, 0); + } + + return error; +diff --git a/fs/xfs/xfs_iops.h b/fs/xfs/xfs_iops.h +index 1c34e43..04cd5da 100644 +--- a/fs/xfs/xfs_iops.h ++++ b/fs/xfs/xfs_iops.h +@@ -32,8 +32,14 @@ extern void xfs_setup_inode(struct xfs_inode *); + */ + #define XFS_ATTR_NOACL 0x01 /* Don't call posix_acl_chmod */ + +-extern int xfs_setattr_nonsize(struct xfs_inode *ip, struct iattr *vap, +- int flags); +-extern int xfs_setattr_size(struct xfs_inode *ip, struct iattr *vap); ++/* ++ * XXX Several callers have to pass dentry = NULL and this should ++ * work but it's really ugly. ++ */ ++extern int xfs_setattr_nonsize(struct dentry *dentry, ++ struct xfs_inode *ip, struct iattr *vap, ++ int flags); + ++extern int xfs_setattr_size(struct dentry *dentry, ++ struct xfs_inode *ip, struct iattr *vap); + #endif /* __XFS_IOPS_H__ */ +diff --git a/fs/yaffs2/Kconfig b/fs/yaffs2/Kconfig +new file mode 100644 +index 0000000..408570f +--- /dev/null ++++ b/fs/yaffs2/Kconfig +@@ -0,0 +1,171 @@ ++# ++# yaffs file system configurations ++# ++ ++config YAFFS_FS ++ tristate "yaffs2 file system support" ++ default n ++ depends on MTD_BLOCK ++ select YAFFS_YAFFS1 ++ select YAFFS_YAFFS2 ++ help ++ yaffs2, or Yet Another Flash File System, is a file system ++ optimised for NAND Flash chips. ++ ++ To compile the yaffs2 file system support as a module, choose M ++ here: the module will be called yaffs2. ++ ++ If unsure, say N. ++ ++ Further information on yaffs2 is available at ++ <http://www.aleph1.co.uk/yaffs/>. ++ ++config YAFFS_YAFFS1 ++ bool "512 byte / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable yaffs1 support -- yaffs for 512 byte / page devices ++ ++ Not needed for 2K-page devices. ++ ++ If unsure, say Y. ++ ++config YAFFS_9BYTE_TAGS ++ bool "Use older-style on-NAND data format with pageStatus byte" ++ depends on YAFFS_YAFFS1 ++ default n ++ help ++ ++ Older-style on-NAND data format has a "pageStatus" byte to record ++ chunk/page state. This byte is zero when the page is discarded. ++ Choose this option if you have existing on-NAND data using this ++ format that you need to continue to support. New data written ++ also uses the older-style format. Note: Use of this option ++ generally requires that MTD's oob layout be adjusted to use the ++ older-style format. See notes on tags formats and MTD versions ++ in yaffs_mtdif1.c. ++ ++ If unsure, say N. ++ ++config YAFFS_DOES_ECC ++ bool "Lets yaffs do its own ECC" ++ depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This enables yaffs to use its own ECC functions instead of using ++ the ones from the generic MTD-NAND driver. ++ ++ If unsure, say N. ++ ++config YAFFS_ECC_WRONG_ORDER ++ bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" ++ depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This makes yaffs_ecc.c use the same ecc byte order as Steven ++ Hill's nand_ecc.c. If not set, then you get the same ecc byte ++ order as SmartMedia. ++ ++ If unsure, say N. ++ ++config YAFFS_YAFFS2 ++ bool "2048 byte (or larger) / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable yaffs2 support -- yaffs for >= 2K bytes per page devices ++ ++ If unsure, say Y. ++ ++config YAFFS_AUTO_YAFFS2 ++ bool "Autoselect yaffs2 format" ++ depends on YAFFS_YAFFS2 ++ default y ++ help ++ Without this, you need to explicitely use yaffs2 as the file ++ system type. With this, you can say "yaffs" and yaffs or yaffs2 ++ will be used depending on the device page size (yaffs on ++ 512-byte page devices, yaffs2 on 2K page devices). ++ ++ If unsure, say Y. ++ ++config YAFFS_DISABLE_TAGS_ECC ++ bool "Disable yaffs from doing ECC on tags by default" ++ depends on YAFFS_FS && YAFFS_YAFFS2 ++ default n ++ help ++ This defaults yaffs to using its own ECC calculations on tags instead of ++ just relying on the MTD. ++ This behavior can also be overridden with tags_ecc_on and ++ tags_ecc_off mount options. ++ ++ If unsure, say N. ++ ++config YAFFS_ALWAYS_CHECK_CHUNK_ERASED ++ bool "Force chunk erase check" ++ depends on YAFFS_FS ++ default n ++ help ++ Normally yaffs only checks chunks before writing until an erased ++ chunk is found. This helps to detect any partially written ++ chunks that might have happened due to power loss. ++ ++ Enabling this forces on the test that chunks are erased in flash ++ before writing to them. This takes more time but is potentially ++ a bit more secure. ++ ++ Suggest setting Y during development and ironing out driver ++ issues etc. Suggest setting to N if you want faster writing. ++ ++ If unsure, say Y. ++ ++config YAFFS_EMPTY_LOST_AND_FOUND ++ bool "Empty lost and found on boot" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is enabled then the contents of lost and found is ++ automatically dumped at mount. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BLOCK_REFRESHING ++ bool "Disable yaffs2 block refreshing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then block refreshing is disabled. ++ Block refreshing infrequently refreshes the oldest block in ++ a yaffs2 file system. This mechanism helps to refresh flash to ++ mitigate against data loss. This is particularly useful for MLC. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BACKGROUND ++ bool "Disable yaffs2 background processing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then background processing is disabled. ++ Background processing makes many foreground activities faster. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BAD_BLOCK_MARKING ++ bool "Disable yaffs2 bad block marking" ++ depends on YAFFS_FS ++ default n ++ help ++ Useful during early flash bring up to prevent problems causing ++ lots of bad block marking. ++ ++ If unsure, say N. ++ ++config YAFFS_XATTR ++ bool "Enable yaffs2 xattr support" ++ depends on YAFFS_FS ++ default y ++ help ++ If this is set then yaffs2 will provide xattr support. ++ If unsure, say Y. +diff --git a/fs/yaffs2/Makefile b/fs/yaffs2/Makefile +new file mode 100644 +index 0000000..7a10847 +--- /dev/null ++++ b/fs/yaffs2/Makefile +@@ -0,0 +1,17 @@ ++# ++# Makefile for the linux YAFFS filesystem routines. ++# ++ ++obj-$(CONFIG_YAFFS_FS) += yaffs.o ++ ++yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o ++yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o ++yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o ++yaffs-y += yaffs_mtdif.o ++yaffs-y += yaffs_nameval.o yaffs_attribs.o ++yaffs-y += yaffs_allocator.o ++yaffs-y += yaffs_yaffs1.o ++yaffs-y += yaffs_yaffs2.o ++yaffs-y += yaffs_bitmap.o ++yaffs-y += yaffs_summary.o ++yaffs-y += yaffs_verify.o +diff --git a/fs/yaffs2/yaffs_allocator.c b/fs/yaffs2/yaffs_allocator.c +new file mode 100644 +index 0000000..611061f +--- /dev/null ++++ b/fs/yaffs2/yaffs_allocator.c +@@ -0,0 +1,356 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_allocator.h" ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yportenv.h" ++ ++/* ++ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks ++ * of approx 100 objects that are themn allocated singly. ++ * This is basically a simplified slab allocator. ++ * ++ * We don't use the Linux slab allocator because slab does not allow ++ * us to dump all the objects in one hit when we do a umount and tear ++ * down all the tnodes and objects. slab requires that we first free ++ * the individual objects. ++ * ++ * Once yaffs has been mainlined I shall try to motivate for a change ++ * to slab to provide the extra features we need here. ++ */ ++ ++struct yaffs_tnode_list { ++ struct yaffs_tnode_list *next; ++ struct yaffs_tnode *tnodes; ++}; ++ ++struct yaffs_obj_list { ++ struct yaffs_obj_list *next; ++ struct yaffs_obj *objects; ++}; ++ ++struct yaffs_allocator { ++ int n_tnodes_created; ++ struct yaffs_tnode *free_tnodes; ++ int n_free_tnodes; ++ struct yaffs_tnode_list *alloc_tnode_list; ++ ++ int n_obj_created; ++ struct list_head free_objs; ++ int n_free_objects; ++ ++ struct yaffs_obj_list *allocated_obj_list; ++}; ++ ++static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode_list *tmp; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ while (allocator->alloc_tnode_list) { ++ tmp = allocator->alloc_tnode_list->next; ++ ++ kfree(allocator->alloc_tnode_list->tnodes); ++ kfree(allocator->alloc_tnode_list); ++ allocator->alloc_tnode_list = tmp; ++ } ++ ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator->alloc_tnode_list = NULL; ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ int i; ++ struct yaffs_tnode *new_tnodes; ++ u8 *mem; ++ struct yaffs_tnode *curr; ++ struct yaffs_tnode *next; ++ struct yaffs_tnode_list *tnl; ++ ++ if (!allocator) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_tnodes < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); ++ mem = (u8 *) new_tnodes; ++ ++ if (!new_tnodes) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: Could not allocate Tnodes"); ++ return YAFFS_FAIL; ++ } ++ ++ /* New hookup for wide tnodes */ ++ for (i = 0; i < n_tnodes - 1; i++) { ++ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; ++ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; ++ curr->internal[0] = next; ++ } ++ ++ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; ++ curr->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = (struct yaffs_tnode *)mem; ++ ++ allocator->n_free_tnodes += n_tnodes; ++ allocator->n_tnodes_created += n_tnodes; ++ ++ /* Now add this bunch of tnodes to a list for freeing up. ++ * NB If we can't add this to the management list it isn't fatal ++ * but it just means we can't free this bunch of tnodes later. ++ */ ++ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); ++ if (!tnl) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Could not add tnodes to management list"); ++ return YAFFS_FAIL; ++ } else { ++ tnl->tnodes = new_tnodes; ++ tnl->next = allocator->alloc_tnode_list; ++ allocator->alloc_tnode_list = tnl; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (!allocator) { ++ BUG(); ++ return NULL; ++ } ++ ++ /* If there are none left make more */ ++ if (!allocator->free_tnodes) ++ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); ++ ++ if (allocator->free_tnodes) { ++ tn = allocator->free_tnodes; ++ allocator->free_tnodes = allocator->free_tnodes->internal[0]; ++ allocator->n_free_tnodes--; ++ } ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ if (tn) { ++ tn->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = tn; ++ allocator->n_free_tnodes++; ++ } ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++/*--------------- yaffs_obj alloaction ------------------------ ++ * ++ * Free yaffs_objs are stored in a list using obj->siblings. ++ * The blocks of allocated objects are stored in a linked list. ++ */ ++ ++static void yaffs_init_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator->allocated_obj_list = NULL; ++ INIT_LIST_HEAD(&allocator->free_objs); ++ allocator->n_free_objects = 0; ++} ++ ++static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ struct yaffs_obj_list *tmp; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ while (allocator->allocated_obj_list) { ++ tmp = allocator->allocated_obj_list->next; ++ kfree(allocator->allocated_obj_list->objects); ++ kfree(allocator->allocated_obj_list); ++ allocator->allocated_obj_list = tmp; ++ } ++ ++ INIT_LIST_HEAD(&allocator->free_objs); ++ allocator->n_free_objects = 0; ++ allocator->n_obj_created = 0; ++} ++ ++static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ int i; ++ struct yaffs_obj *new_objs; ++ struct yaffs_obj_list *list; ++ ++ if (!allocator) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_obj < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); ++ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); ++ ++ if (!new_objs || !list) { ++ kfree(new_objs); ++ new_objs = NULL; ++ kfree(list); ++ list = NULL; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Could not allocate more objects"); ++ return YAFFS_FAIL; ++ } ++ ++ /* Hook them into the free list */ ++ for (i = 0; i < n_obj; i++) ++ list_add(&new_objs[i].siblings, &allocator->free_objs); ++ ++ allocator->n_free_objects += n_obj; ++ allocator->n_obj_created += n_obj; ++ ++ /* Now add this bunch of Objects to a list for freeing up. */ ++ ++ list->objects = new_objs; ++ list->next = allocator->allocated_obj_list; ++ allocator->allocated_obj_list = list; ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct list_head *lh; ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return obj; ++ } ++ ++ /* If there are none left make more */ ++ if (list_empty(&allocator->free_objs)) ++ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); ++ ++ if (!list_empty(&allocator->free_objs)) { ++ lh = allocator->free_objs.next; ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ list_del_init(lh); ++ allocator->n_free_objects--; ++ } ++ ++ return obj; ++} ++ ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ /* Link into the free list. */ ++ list_add(&obj->siblings, &allocator->free_objs); ++ allocator->n_free_objects++; ++} ++ ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ ++ if (!dev->allocator) { ++ BUG(); ++ return; ++ } ++ ++ yaffs_deinit_raw_tnodes(dev); ++ yaffs_deinit_raw_objs(dev); ++ kfree(dev->allocator); ++ dev->allocator = NULL; ++} ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator; ++ ++ if (dev->allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); ++ if (allocator) { ++ dev->allocator = allocator; ++ yaffs_init_raw_tnodes(dev); ++ yaffs_init_raw_objs(dev); ++ } ++} +diff --git a/fs/yaffs2/yaffs_allocator.h b/fs/yaffs2/yaffs_allocator.h +new file mode 100644 +index 0000000..a8cc322 +--- /dev/null ++++ b/fs/yaffs2/yaffs_allocator.h +@@ -0,0 +1,30 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ALLOCATOR_H__ ++#define __YAFFS_ALLOCATOR_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_attribs.c b/fs/yaffs2/yaffs_attribs.c +new file mode 100644 +index 0000000..711941f +--- /dev/null ++++ b/fs/yaffs2/yaffs_attribs.c +@@ -0,0 +1,132 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) ++#define IATTR_UID ia_uid ++#define IATTR_GID ia_gid ++#else ++#define IATTR_UID ia_uid.val ++#define IATTR_GID ia_gid.val ++#endif ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) ++{ ++ obj->yst_uid = oh->yst_uid; ++ obj->yst_gid = oh->yst_gid; ++ obj->yst_atime = oh->yst_atime; ++ obj->yst_mtime = oh->yst_mtime; ++ obj->yst_ctime = oh->yst_ctime; ++ obj->yst_rdev = oh->yst_rdev; ++} ++ ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) ++{ ++ oh->yst_uid = obj->yst_uid; ++ oh->yst_gid = obj->yst_gid; ++ oh->yst_atime = obj->yst_atime; ++ oh->yst_mtime = obj->yst_mtime; ++ oh->yst_ctime = obj->yst_ctime; ++ oh->yst_rdev = obj->yst_rdev; ++ ++} ++ ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) ++{ ++ obj->yst_mtime = Y_CURRENT_TIME; ++ if (do_a) ++ obj->yst_atime = obj->yst_mtime; ++ if (do_c) ++ obj->yst_ctime = obj->yst_mtime; ++} ++ ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) ++{ ++ yaffs_load_current_time(obj, 1, 1); ++ obj->yst_rdev = rdev; ++ obj->yst_uid = uid; ++ obj->yst_gid = gid; ++} ++ ++static loff_t yaffs_get_file_size(struct yaffs_obj *obj) ++{ ++ YCHAR *alias = NULL; ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return obj->variant.file_variant.file_size; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = obj->variant.symlink_variant.alias; ++ if (!alias) ++ return 0; ++ return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); ++ default: ++ return 0; ++ } ++} ++ ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = attr->ia_valid; ++ ++ if (valid & ATTR_MODE) ++ obj->yst_mode = attr->ia_mode; ++ if (valid & ATTR_UID) ++ obj->yst_uid = attr->IATTR_UID; ++ if (valid & ATTR_GID) ++ obj->yst_gid = attr->IATTR_GID; ++ ++ if (valid & ATTR_ATIME) ++ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); ++ if (valid & ATTR_CTIME) ++ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); ++ if (valid & ATTR_MTIME) ++ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); ++ ++ if (valid & ATTR_SIZE) ++ yaffs_resize_file(obj, attr->ia_size); ++ ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ return YAFFS_OK; ++ ++} ++ ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = 0; ++ ++ attr->ia_mode = obj->yst_mode; ++ valid |= ATTR_MODE; ++ attr->IATTR_UID = obj->yst_uid; ++ valid |= ATTR_UID; ++ attr->IATTR_GID = obj->yst_gid; ++ valid |= ATTR_GID; ++ ++ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; ++ valid |= ATTR_ATIME; ++ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; ++ valid |= ATTR_CTIME; ++ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; ++ valid |= ATTR_MTIME; ++ ++ attr->ia_size = yaffs_get_file_size(obj); ++ valid |= ATTR_SIZE; ++ ++ attr->ia_valid = valid; ++ ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_attribs.h b/fs/yaffs2/yaffs_attribs.h +new file mode 100644 +index 0000000..5b21b08 +--- /dev/null ++++ b/fs/yaffs2/yaffs_attribs.h +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ATTRIBS_H__ ++#define __YAFFS_ATTRIBS_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_bitmap.c b/fs/yaffs2/yaffs_bitmap.c +new file mode 100644 +index 0000000..4440e93 +--- /dev/null ++++ b/fs/yaffs2/yaffs_bitmap.c +@@ -0,0 +1,97 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_bitmap.h" ++#include "yaffs_trace.h" ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "BlockBits block %d is not valid", ++ blk); ++ BUG(); ++ } ++ return dev->chunk_bits + ++ (dev->chunk_bit_stride * (blk - dev->internal_start_block)); ++} ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block || ++ chunk < 0 || chunk >= dev->param.chunks_per_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Chunk Id (%d:%d) invalid", ++ blk, chunk); ++ BUG(); ++ } ++} ++ ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ memset(blk_bits, 0, dev->chunk_bit_stride); ++} ++ ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); ++} ++ ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ blk_bits[chunk / 8] |= (1 << (chunk & 7)); ++} ++ ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; ++} ++ ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++) { ++ if (*blk_bits) ++ return 1; ++ blk_bits++; ++ } ++ return 0; ++} ++ ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ int n = 0; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) ++ n += hweight8(*blk_bits); ++ ++ return n; ++} +diff --git a/fs/yaffs2/yaffs_bitmap.h b/fs/yaffs2/yaffs_bitmap.h +new file mode 100644 +index 0000000..e26b37d +--- /dev/null ++++ b/fs/yaffs2/yaffs_bitmap.h +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++#ifndef __YAFFS_BITMAP_H__ ++#define __YAFFS_BITMAP_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_checkptrw.c b/fs/yaffs2/yaffs_checkptrw.c +new file mode 100644 +index 0000000..16ee1e0 +--- /dev/null ++++ b/fs/yaffs2/yaffs_checkptrw.c +@@ -0,0 +1,466 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_checkptrw.h" ++#include "yaffs_getblockinfo.h" ++ ++struct yaffs_checkpt_chunk_hdr { ++ int version; ++ int seq; ++ u32 sum; ++ u32 xor; ++} ; ++ ++ ++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) ++{ ++ return chunk - dev->chunk_offset; ++} ++ ++static int apply_block_offset(struct yaffs_dev *dev, int block) ++{ ++ return block - dev->block_offset; ++} ++ ++static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_chunk_hdr hdr; ++ ++ hdr.version = YAFFS_CHECKPOINT_VERSION; ++ hdr.seq = dev->checkpt_page_seq; ++ hdr.sum = dev->checkpt_sum; ++ hdr.xor = dev->checkpt_xor; ++ ++ dev->checkpt_byte_offs = sizeof(hdr); ++ ++ memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); ++} ++ ++static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_chunk_hdr hdr; ++ ++ memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); ++ ++ dev->checkpt_byte_offs = sizeof(hdr); ++ ++ return hdr.version == YAFFS_CHECKPOINT_VERSION && ++ hdr.seq == dev->checkpt_page_seq && ++ hdr.sum == dev->checkpt_sum && ++ hdr.xor == dev->checkpt_xor; ++} ++ ++static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) ++{ ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpt blocks_avail = %d", blocks_avail); ++ ++ return (blocks_avail <= 0) ? 0 : 1; ++} ++ ++static int yaffs_checkpt_erase(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (!dev->drv.drv_erase_fn) ++ return 0; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checking blocks %d to %d", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ int offset_i = apply_block_offset(dev, i); ++ int result; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "erasing checkpt block %d", i); ++ ++ dev->n_erasures++; ++ ++ result = dev->drv.drv_erase_fn(dev, offset_i); ++ if(result) { ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += ++ dev->param.chunks_per_block; ++ } else { ++ dev->drv.drv_mark_bad_fn(dev, offset_i); ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ } ++ } ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ return 1; ++} ++ ++static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) ++{ ++ int i; ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block: erased %d reserved %d avail %d next %d ", ++ dev->n_erased_blocks, dev->param.n_reserved_blocks, ++ blocks_avail, dev->checkpt_next_block); ++ ++ if (dev->checkpt_next_block >= 0 && ++ dev->checkpt_next_block <= dev->internal_end_block && ++ blocks_avail > 0) { ++ ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ struct yaffs_block_info *bi; ++ ++ bi = yaffs_get_block_info(dev, i); ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ dev->checkpt_next_block = i + 1; ++ dev->checkpt_cur_block = i; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block %d", i); ++ return; ++ } ++ } ++ } ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_ext_tags tags; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: start: blocks %d next %d", ++ dev->blocks_in_checkpt, dev->checkpt_next_block); ++ ++ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ int chunk = i * dev->param.chunks_per_block; ++ enum yaffs_block_state state; ++ u32 seq; ++ ++ dev->tagger.read_chunk_tags_fn(dev, ++ apply_chunk_offset(dev, chunk), ++ NULL, &tags); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", ++ i, (int) state, ++ tags.obj_id, tags.seq_number, ++ tags.ecc_result); ++ ++ if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ continue; ++ ++ dev->tagger.query_block_fn(dev, ++ apply_block_offset(dev, i), ++ &state, &seq); ++ if (state == YAFFS_BLOCK_STATE_DEAD) ++ continue; ++ ++ /* Right kind of block */ ++ dev->checkpt_next_block = tags.obj_id; ++ dev->checkpt_cur_block = i; ++ dev->checkpt_block_list[dev->blocks_in_checkpt] = i; ++ dev->blocks_in_checkpt++; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "found checkpt block %d", i); ++ return; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) ++{ ++ int i; ++ ++ dev->checkpt_open_write = writing; ++ ++ /* Got the functions we need? */ ++ if (!dev->tagger.write_chunk_tags_fn || ++ !dev->tagger.read_chunk_tags_fn || ++ !dev->drv.drv_erase_fn || ++ !dev->drv.drv_mark_bad_fn) ++ return 0; ++ ++ if (writing && !yaffs2_checkpt_space_ok(dev)) ++ return 0; ++ ++ if (!dev->checkpt_buffer) ++ dev->checkpt_buffer = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ dev->checkpt_page_seq = 0; ++ dev->checkpt_byte_count = 0; ++ dev->checkpt_sum = 0; ++ dev->checkpt_xor = 0; ++ dev->checkpt_cur_block = -1; ++ dev->checkpt_cur_chunk = -1; ++ dev->checkpt_next_block = dev->internal_start_block; ++ ++ if (writing) { ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ yaffs2_checkpt_init_chunk_hdr(dev); ++ return yaffs_checkpt_erase(dev); ++ } ++ ++ /* Opening for a read */ ++ /* Set to a value that will kick off a read */ ++ dev->checkpt_byte_offs = dev->data_bytes_per_chunk; ++ /* A checkpoint block list of 1 checkpoint block per 16 block is ++ * (hopefully) going to be way more than we need */ ++ dev->blocks_in_checkpt = 0; ++ dev->checkpt_max_blocks = ++ (dev->internal_end_block - dev->internal_start_block) / 16 + 2; ++ if (!dev->checkpt_block_list) ++ dev->checkpt_block_list = ++ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); ++ ++ if (!dev->checkpt_block_list) ++ return 0; ++ ++ for (i = 0; i < dev->checkpt_max_blocks; i++) ++ dev->checkpt_block_list[i] = -1; ++ ++ return 1; ++} ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) ++{ ++ u32 composite_sum; ++ ++ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); ++ *sum = composite_sum; ++ return 1; ++} ++ ++static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) ++{ ++ int chunk; ++ int offset_chunk; ++ struct yaffs_ext_tags tags; ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_erased_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ if (dev->checkpt_cur_block < 0) ++ return 0; ++ ++ tags.is_deleted = 0; ++ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ ++ tags.chunk_id = dev->checkpt_page_seq + 1; ++ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; ++ tags.n_bytes = dev->data_bytes_per_chunk; ++ if (dev->checkpt_cur_chunk == 0) { ++ /* First chunk we write for the block? Set block state to ++ checkpoint */ ++ struct yaffs_block_info *bi = ++ yaffs_get_block_info(dev, dev->checkpt_cur_block); ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ dev->blocks_in_checkpt++; ++ } ++ ++ chunk = ++ dev->checkpt_cur_block * dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", ++ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, ++ tags.obj_id, tags.chunk_id); ++ ++ offset_chunk = apply_chunk_offset(dev, chunk); ++ ++ dev->n_page_writes++; ++ ++ dev->tagger.write_chunk_tags_fn(dev, offset_chunk, ++ dev->checkpt_buffer, &tags); ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { ++ dev->checkpt_cur_chunk = 0; ++ dev->checkpt_cur_block = -1; ++ } ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ ++ yaffs2_checkpt_init_chunk_hdr(dev); ++ ++ ++ return 1; ++} ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) ++{ ++ int i = 0; ++ int ok = 1; ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (!dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes && ok) { ++ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) ++ ok = yaffs2_checkpt_flush_buffer(dev); ++ } ++ ++ return i; ++} ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) ++{ ++ int i = 0; ++ struct yaffs_ext_tags tags; ++ int chunk; ++ int offset_chunk; ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes) { ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ /* Bail out if we can't find a checpoint block */ ++ if (dev->checkpt_cur_block < 0) ++ break; ++ ++ chunk = dev->checkpt_cur_block * ++ dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ offset_chunk = apply_chunk_offset(dev, chunk); ++ dev->n_page_reads++; ++ ++ /* Read in the next chunk */ ++ dev->tagger.read_chunk_tags_fn(dev, ++ offset_chunk, ++ dev->checkpt_buffer, ++ &tags); ++ ++ /* Bail out if the chunk is corrupted. */ ++ if (tags.chunk_id != (dev->checkpt_page_seq + 1) || ++ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || ++ tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ break; ++ ++ /* Bail out if it is not a checkpoint chunk. */ ++ if(!yaffs2_checkpt_check_chunk_hdr(dev)) ++ break; ++ ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ ++ if (dev->checkpt_cur_chunk >= ++ dev->param.chunks_per_block) ++ dev->checkpt_cur_block = -1; ++ ++ } ++ ++ *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ } ++ ++ return i; /* Number of bytes read */ ++} ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->checkpt_open_write) { ++ if (dev->checkpt_byte_offs != ++ sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) ++ yaffs2_checkpt_flush_buffer(dev); ++ } else if (dev->checkpt_block_list) { ++ for (i = 0; ++ i < dev->blocks_in_checkpt && ++ dev->checkpt_block_list[i] >= 0; i++) { ++ int blk = dev->checkpt_block_list[i]; ++ struct yaffs_block_info *bi = NULL; ++ ++ if (dev->internal_start_block <= blk && ++ blk <= dev->internal_end_block) ++ bi = yaffs_get_block_info(dev, blk); ++ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ } ++ } ++ ++ dev->n_free_chunks -= ++ dev->blocks_in_checkpt * dev->param.chunks_per_block; ++ dev->n_erased_blocks -= dev->blocks_in_checkpt; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", ++ dev->checkpt_byte_count); ++ ++ if (dev->checkpt_buffer) ++ return 1; ++ else ++ return 0; ++} ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) ++{ ++ /* Erase the checkpoint data */ ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint invalidate of %d blocks", ++ dev->blocks_in_checkpt); ++ ++ return yaffs_checkpt_erase(dev); ++} +diff --git a/fs/yaffs2/yaffs_checkptrw.h b/fs/yaffs2/yaffs_checkptrw.h +new file mode 100644 +index 0000000..cdbaba7 +--- /dev/null ++++ b/fs/yaffs2/yaffs_checkptrw.h +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_CHECKPTRW_H__ ++#define __YAFFS_CHECKPTRW_H__ ++ ++#include "yaffs_guts.h" ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev); ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_ecc.c b/fs/yaffs2/yaffs_ecc.c +new file mode 100644 +index 0000000..9294107 +--- /dev/null ++++ b/fs/yaffs2/yaffs_ecc.c +@@ -0,0 +1,281 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two ++ * such ECC blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_ecc.h" ++ ++/* Table generated by gen-ecc.c ++ * Using a table means we do not have to calculate p1..p4 and p1'..p4' ++ * for each byte of data. These are instead provided in a table in bits7..2. ++ * Bit 0 of each entry indicates whether the entry has an odd or even parity, ++ * and therefore this bytes influence on the line parity. ++ */ ++ ++static const unsigned char column_parity_table[] = { ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++}; ++ ++ ++/* Calculate the ECC for a 256-byte block of data */ ++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) ++{ ++ unsigned int i; ++ unsigned char col_parity = 0; ++ unsigned char line_parity = 0; ++ unsigned char line_parity_prime = 0; ++ unsigned char t; ++ unsigned char b; ++ ++ for (i = 0; i < 256; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ } ++ ++ ecc[2] = (~col_parity) | 0x03; ++ ++ t = 0; ++ if (line_parity & 0x80) ++ t |= 0x80; ++ if (line_parity_prime & 0x80) ++ t |= 0x40; ++ if (line_parity & 0x40) ++ t |= 0x20; ++ if (line_parity_prime & 0x40) ++ t |= 0x10; ++ if (line_parity & 0x20) ++ t |= 0x08; ++ if (line_parity_prime & 0x20) ++ t |= 0x04; ++ if (line_parity & 0x10) ++ t |= 0x02; ++ if (line_parity_prime & 0x10) ++ t |= 0x01; ++ ecc[1] = ~t; ++ ++ t = 0; ++ if (line_parity & 0x08) ++ t |= 0x80; ++ if (line_parity_prime & 0x08) ++ t |= 0x40; ++ if (line_parity & 0x04) ++ t |= 0x20; ++ if (line_parity_prime & 0x04) ++ t |= 0x10; ++ if (line_parity & 0x02) ++ t |= 0x08; ++ if (line_parity_prime & 0x02) ++ t |= 0x04; ++ if (line_parity & 0x01) ++ t |= 0x02; ++ if (line_parity_prime & 0x01) ++ t |= 0x01; ++ ecc[0] = ~t; ++ ++} ++ ++/* Correct the ECC on a 256 byte block of data */ ++ ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc) ++{ ++ unsigned char d0, d1, d2; /* deltas */ ++ ++ d0 = read_ecc[0] ^ test_ecc[0]; ++ d1 = read_ecc[1] ^ test_ecc[1]; ++ d2 = read_ecc[2] ^ test_ecc[2]; ++ ++ if ((d0 | d1 | d2) == 0) ++ return 0; /* no error */ ++ ++ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && ++ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && ++ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { ++ /* Single bit (recoverable) error in data */ ++ ++ unsigned byte; ++ unsigned bit; ++ ++ bit = byte = 0; ++ ++ if (d1 & 0x80) ++ byte |= 0x80; ++ if (d1 & 0x20) ++ byte |= 0x40; ++ if (d1 & 0x08) ++ byte |= 0x20; ++ if (d1 & 0x02) ++ byte |= 0x10; ++ if (d0 & 0x80) ++ byte |= 0x08; ++ if (d0 & 0x20) ++ byte |= 0x04; ++ if (d0 & 0x08) ++ byte |= 0x02; ++ if (d0 & 0x02) ++ byte |= 0x01; ++ ++ if (d2 & 0x80) ++ bit |= 0x04; ++ if (d2 & 0x20) ++ bit |= 0x02; ++ if (d2 & 0x08) ++ bit |= 0x01; ++ ++ data[byte] ^= (1 << bit); ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ read_ecc[0] = test_ecc[0]; ++ read_ecc[1] = test_ecc[1]; ++ read_ecc[2] = test_ecc[2]; ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++ ++} ++ ++/* ++ * ECCxxxOther does ECC calcs on arbitrary n bytes of data ++ */ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc_other) ++{ ++ unsigned int i; ++ unsigned char col_parity = 0; ++ unsigned line_parity = 0; ++ unsigned line_parity_prime = 0; ++ unsigned char b; ++ ++ for (i = 0; i < n_bytes; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { ++ /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ ++ } ++ ++ ecc_other->col_parity = (col_parity >> 2) & 0x3f; ++ ecc_other->line_parity = line_parity; ++ ecc_other->line_parity_prime = line_parity_prime; ++} ++ ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc) ++{ ++ unsigned char delta_col; /* column parity delta */ ++ unsigned delta_line; /* line parity delta */ ++ unsigned delta_line_prime; /* line parity delta */ ++ unsigned bit; ++ ++ delta_col = read_ecc->col_parity ^ test_ecc->col_parity; ++ delta_line = read_ecc->line_parity ^ test_ecc->line_parity; ++ delta_line_prime = ++ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; ++ ++ if ((delta_col | delta_line | delta_line_prime) == 0) ++ return 0; /* no error */ ++ ++ if (delta_line == ~delta_line_prime && ++ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { ++ /* Single bit (recoverable) error in data */ ++ ++ bit = 0; ++ ++ if (delta_col & 0x20) ++ bit |= 0x04; ++ if (delta_col & 0x08) ++ bit |= 0x02; ++ if (delta_col & 0x02) ++ bit |= 0x01; ++ ++ if (delta_line >= n_bytes) ++ return -1; ++ ++ data[delta_line] ^= (1 << bit); ++ ++ return 1; /* corrected */ ++ } ++ ++ if ((hweight32(delta_line) + ++ hweight32(delta_line_prime) + ++ hweight8(delta_col)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ *read_ecc = *test_ecc; ++ return 1; /* corrected */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++} +diff --git a/fs/yaffs2/yaffs_ecc.h b/fs/yaffs2/yaffs_ecc.h +new file mode 100644 +index 0000000..17d47bd +--- /dev/null ++++ b/fs/yaffs2/yaffs_ecc.h +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. ++ * Thus, two such ECC blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#ifndef __YAFFS_ECC_H__ ++#define __YAFFS_ECC_H__ ++ ++struct yaffs_ecc_other { ++ unsigned char col_parity; ++ unsigned line_parity; ++ unsigned line_parity_prime; ++}; ++ ++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc); ++ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc); ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc); ++#endif +diff --git a/fs/yaffs2/yaffs_getblockinfo.h b/fs/yaffs2/yaffs_getblockinfo.h +new file mode 100644 +index 0000000..8fd0802 +--- /dev/null ++++ b/fs/yaffs2/yaffs_getblockinfo.h +@@ -0,0 +1,35 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GETBLOCKINFO_H__ ++#define __YAFFS_GETBLOCKINFO_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++ ++/* Function to manipulate block info */ ++static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev ++ *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs: get_block_info block %d is not valid", ++ blk); ++ BUG(); ++ } ++ return &dev->block_info[blk - dev->internal_start_block]; ++} ++ ++#endif +diff --git a/fs/yaffs2/yaffs_guts.c b/fs/yaffs2/yaffs_guts.c +new file mode 100644 +index 0000000..89fb2a9 +--- /dev/null ++++ b/fs/yaffs2/yaffs_guts.c +@@ -0,0 +1,5140 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++#include "yaffs_guts.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_tagsmarshall.h" ++#include "yaffs_nand.h" ++#include "yaffs_yaffs1.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_verify.h" ++#include "yaffs_nand.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_nameval.h" ++#include "yaffs_allocator.h" ++#include "yaffs_attribs.h" ++#include "yaffs_summary.h" ++ ++/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ ++#define YAFFS_GC_GOOD_ENOUGH 2 ++#define YAFFS_GC_PASSIVE_THRESHOLD 4 ++ ++#include "yaffs_ecc.h" ++ ++/* Forward declarations */ ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 *buffer, int n_bytes, int use_reserve); ++ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, ++ int buffer_size); ++ ++/* Function to calculate chunk and offset */ ++ ++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 *offset_out) ++{ ++ int chunk; ++ u32 offset; ++ ++ chunk = (u32) (addr >> dev->chunk_shift); ++ ++ if (dev->chunk_div == 1) { ++ /* easy power of 2 case */ ++ offset = (u32) (addr & dev->chunk_mask); ++ } else { ++ /* Non power-of-2 case */ ++ ++ loff_t chunk_base; ++ ++ chunk /= dev->chunk_div; ++ ++ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; ++ offset = (u32) (addr - chunk_base); ++ } ++ ++ *chunk_out = chunk; ++ *offset_out = offset; ++} ++ ++/* Function to return the number of shifts for a power of 2 greater than or ++ * equal to the given number ++ * Note we don't try to cater for all possible numbers and this does not have to ++ * be hellishly efficient. ++ */ ++ ++static inline u32 calc_shifts_ceiling(u32 x) ++{ ++ int extra_bits; ++ int shifts; ++ ++ shifts = extra_bits = 0; ++ ++ while (x > 1) { ++ if (x & 1) ++ extra_bits++; ++ x >>= 1; ++ shifts++; ++ } ++ ++ if (extra_bits) ++ shifts++; ++ ++ return shifts; ++} ++ ++/* Function to return the number of shifts to get a 1 in bit 0 ++ */ ++ ++static inline u32 calc_shifts(u32 x) ++{ ++ u32 shifts; ++ ++ shifts = 0; ++ ++ if (!x) ++ return 0; ++ ++ while (!(x & 1)) { ++ x >>= 1; ++ shifts++; ++ } ++ ++ return shifts; ++} ++ ++/* ++ * Temporary buffer manipulations. ++ */ ++ ++static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) ++{ ++ int i; ++ u8 *buf = (u8 *) 1; ++ ++ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); ++ ++ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { ++ dev->temp_buffer[i].in_use = 0; ++ buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ dev->temp_buffer[i].buffer = buf; ++ } ++ ++ return buf ? YAFFS_OK : YAFFS_FAIL; ++} ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) ++{ ++ int i; ++ ++ dev->temp_in_use++; ++ if (dev->temp_in_use > dev->max_temp) ++ dev->max_temp = dev->temp_in_use; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].in_use == 0) { ++ dev->temp_buffer[i].in_use = 1; ++ return dev->temp_buffer[i].buffer; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); ++ /* ++ * If we got here then we have to allocate an unmanaged one ++ * This is not good. ++ */ ++ ++ dev->unmanaged_buffer_allocs++; ++ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); ++ ++} ++ ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) ++{ ++ int i; ++ ++ dev->temp_in_use--; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].buffer == buffer) { ++ dev->temp_buffer[i].in_use = 0; ++ return; ++ } ++ } ++ ++ if (buffer) { ++ /* assume it is an unmanaged one. */ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, ++ "Releasing unmanaged temp buffer"); ++ kfree(buffer); ++ dev->unmanaged_buffer_deallocs++; ++ } ++ ++} ++ ++/* ++ * Functions for robustisizing TODO ++ * ++ */ ++ ++static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, ++ const struct yaffs_ext_tags *tags) ++{ ++ (void) dev; ++ (void) nand_chunk; ++ (void) data; ++ (void) tags; ++} ++ ++static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, ++ const struct yaffs_ext_tags *tags) ++{ ++ (void) dev; ++ (void) nand_chunk; ++ (void) tags; ++} ++ ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ if (!bi->gc_prioritise) { ++ bi->gc_prioritise = 1; ++ dev->has_pending_prioritised_gc = 1; ++ bi->chunk_error_strikes++; ++ ++ if (bi->chunk_error_strikes > 3) { ++ bi->needs_retiring = 1; /* Too many stikes, so retire */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Block struck out"); ++ ++ } ++ } ++} ++ ++static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, ++ int erased_ok) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs_handle_chunk_error(dev, bi); ++ ++ if (erased_ok) { ++ /* Was an actual write failure, ++ * so mark the block for retirement.*/ ++ bi->needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d needs retiring", flash_block); ++ } ++ ++ /* Delete the chunk */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++} ++ ++/* ++ * Verification code ++ */ ++ ++/* ++ * Simple hash function. Needs to have a reasonable spread ++ */ ++ ++static inline int yaffs_hash_fn(int n) ++{ ++ if (n < 0) ++ n = -n; ++ return n % YAFFS_NOBJECT_BUCKETS; ++} ++ ++/* ++ * Access functions to useful fake objects. ++ * Note that root might have a presence in NAND if permissions are set. ++ */ ++ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) ++{ ++ return dev->root_dir; ++} ++ ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) ++{ ++ return dev->lost_n_found; ++} ++ ++/* ++ * Erased NAND checking functions ++ */ ++ ++int yaffs_check_ff(u8 *buffer, int n_bytes) ++{ ++ /* Horrible, slow implementation */ ++ while (n_bytes--) { ++ if (*buffer != 0xff) ++ return 0; ++ buffer++; ++ } ++ return 1; ++} ++ ++static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int retval = YAFFS_OK; ++ u8 *data = yaffs_get_temp_buffer(dev); ++ struct yaffs_ext_tags tags; ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); ++ ++ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) ++ retval = YAFFS_FAIL; ++ ++ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || ++ tags.chunk_used) { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not erased", nand_chunk); ++ retval = YAFFS_FAIL; ++ } ++ ++ yaffs_release_temp_buffer(dev, data); ++ ++ return retval; ++ ++} ++ ++static int yaffs_verify_chunk_written(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = YAFFS_OK; ++ struct yaffs_ext_tags temp_tags; ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); ++ if (memcmp(buffer, data, dev->data_bytes_per_chunk) || ++ temp_tags.obj_id != tags->obj_id || ++ temp_tags.chunk_id != tags->chunk_id || ++ temp_tags.n_bytes != tags->n_bytes) ++ retval = YAFFS_FAIL; ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ return retval; ++} ++ ++ ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) ++{ ++ int reserved_chunks; ++ int reserved_blocks = dev->param.n_reserved_blocks; ++ int checkpt_blocks; ++ ++ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); ++ ++ reserved_chunks = ++ (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; ++ ++ return (dev->n_free_chunks > (reserved_chunks + n_chunks)); ++} ++ ++static int yaffs_find_alloc_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_block_info *bi; ++ ++ if (dev->n_erased_blocks < 1) { ++ /* Hoosterman we've got a problem. ++ * Can't get space to gc ++ */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: no more erased blocks"); ++ ++ return -1; ++ } ++ ++ /* Find an empty block. */ ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ dev->alloc_block_finder++; ++ if (dev->alloc_block_finder < dev->internal_start_block ++ || dev->alloc_block_finder > dev->internal_end_block) { ++ dev->alloc_block_finder = dev->internal_start_block; ++ } ++ ++ bi = yaffs_get_block_info(dev, dev->alloc_block_finder); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->seq_number++; ++ bi->seq_number = dev->seq_number; ++ dev->n_erased_blocks--; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Allocated block %d, seq %d, %d left" , ++ dev->alloc_block_finder, dev->seq_number, ++ dev->n_erased_blocks); ++ return dev->alloc_block_finder; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs tragedy: no more erased blocks, but there should have been %d", ++ dev->n_erased_blocks); ++ ++ return -1; ++} ++ ++static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, ++ struct yaffs_block_info **block_ptr) ++{ ++ int ret_val; ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block < 0) { ++ /* Get next block to allocate off */ ++ dev->alloc_block = yaffs_find_alloc_block(dev); ++ dev->alloc_page = 0; ++ } ++ ++ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { ++ /* No space unless we're allowed to use the reserve. */ ++ return -1; ++ } ++ ++ if (dev->n_erased_blocks < dev->param.n_reserved_blocks ++ && dev->alloc_page == 0) ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); ++ ++ /* Next page please.... */ ++ if (dev->alloc_block >= 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ ++ ret_val = (dev->alloc_block * dev->param.chunks_per_block) + ++ dev->alloc_page; ++ bi->pages_in_use++; ++ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); ++ ++ dev->alloc_page++; ++ ++ dev->n_free_chunks--; ++ ++ /* If the block is full set the state to full */ ++ if (dev->alloc_page >= dev->param.chunks_per_block) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ if (block_ptr) ++ *block_ptr = bi; ++ ++ return ret_val; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); ++ ++ return -1; ++} ++ ++static int yaffs_get_erased_chunks(struct yaffs_dev *dev) ++{ ++ int n; ++ ++ n = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ if (dev->alloc_block > 0) ++ n += (dev->param.chunks_per_block - dev->alloc_page); ++ ++ return n; ++ ++} ++ ++/* ++ * yaffs_skip_rest_of_block() skips over the rest of the allocation block ++ * if we don't want to write to it. ++ */ ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev) ++{ ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block > 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ } ++} ++ ++static int yaffs_write_new_chunk(struct yaffs_dev *dev, ++ const u8 *data, ++ struct yaffs_ext_tags *tags, int use_reserver) ++{ ++ int attempts = 0; ++ int write_ok = 0; ++ int chunk; ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ do { ++ struct yaffs_block_info *bi = 0; ++ int erased_ok = 0; ++ ++ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); ++ if (chunk < 0) { ++ /* no space */ ++ break; ++ } ++ ++ /* First check this chunk is erased, if it needs ++ * checking. The checking policy (unless forced ++ * always on) is as follows: ++ * ++ * Check the first page we try to write in a block. ++ * If the check passes then we don't need to check any ++ * more. If the check fails, we check again... ++ * If the block has been erased, we don't need to check. ++ * ++ * However, if the block has been prioritised for gc, ++ * then we think there might be something odd about ++ * this block and stop using it. ++ * ++ * Rationale: We should only ever see chunks that have ++ * not been erased if there was a partially written ++ * chunk due to power loss. This checking policy should ++ * catch that case with very few checks and thus save a ++ * lot of checks that are most likely not needed. ++ * ++ * Mods to the above ++ * If an erase check fails or the write fails we skip the ++ * rest of the block. ++ */ ++ ++ /* let's give it a try */ ++ attempts++; ++ ++ if (dev->param.always_check_erased) ++ bi->skip_erased_check = 0; ++ ++ if (!bi->skip_erased_check) { ++ erased_ok = yaffs_check_chunk_erased(dev, chunk); ++ if (erased_ok != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs chunk %d was not erased", ++ chunk); ++ ++ /* If not erased, delete this one, ++ * skip rest of block and ++ * try another chunk */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++ continue; ++ } ++ } ++ ++ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); ++ ++ if (!bi->skip_erased_check) ++ write_ok = ++ yaffs_verify_chunk_written(dev, chunk, data, tags); ++ ++ if (write_ok != YAFFS_OK) { ++ /* Clean up aborted write, skip to next block and ++ * try another chunk */ ++ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); ++ continue; ++ } ++ ++ bi->skip_erased_check = 1; ++ ++ /* Copy the data into the robustification buffer */ ++ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); ++ ++ } while (write_ok != YAFFS_OK && ++ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); ++ ++ if (!write_ok) ++ chunk = -1; ++ ++ if (attempts > 1) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs write required %d attempts", ++ attempts); ++ dev->n_retried_writes += (attempts - 1); ++ } ++ ++ return chunk; ++} ++ ++/* ++ * Block retiring for handling a broken block. ++ */ ++ ++static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { ++ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to mark bad and erase block %d", ++ flash_block); ++ } else { ++ struct yaffs_ext_tags tags; ++ int chunk_id = ++ flash_block * dev->param.chunks_per_block; ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ memset(&tags, 0, sizeof(tags)); ++ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; ++ if (dev->tagger.write_chunk_tags_fn(dev, chunk_id - ++ dev->chunk_offset, ++ buffer, ++ &tags) != YAFFS_OK) ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to write bad block marker to block %d", ++ flash_block); ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ } ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ bi->gc_prioritise = 0; ++ bi->needs_retiring = 0; ++ ++ dev->n_retired_blocks++; ++} ++ ++/*---------------- Name handling functions ------------*/ ++ ++static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, ++ const YCHAR *oh_name, int buff_size) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ if (dev->param.auto_unicode) { ++ if (*oh_name) { ++ /* It is an ASCII name, do an ASCII to ++ * unicode conversion */ ++ const char *ascii_oh_name = (const char *)oh_name; ++ int n = buff_size - 1; ++ while (n > 0 && *ascii_oh_name) { ++ *name = *ascii_oh_name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ strncpy(name, oh_name + 1, buff_size - 1); ++ } ++ } else { ++#else ++ (void) dev; ++ { ++#endif ++ strncpy(name, oh_name, buff_size - 1); ++ } ++} ++ ++static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, ++ const YCHAR *name) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ ++ int is_ascii; ++ const YCHAR *w; ++ ++ if (dev->param.auto_unicode) { ++ ++ is_ascii = 1; ++ w = name; ++ ++ /* Figure out if the name will fit in ascii character set */ ++ while (is_ascii && *w) { ++ if ((*w) & 0xff00) ++ is_ascii = 0; ++ w++; ++ } ++ ++ if (is_ascii) { ++ /* It is an ASCII name, so convert unicode to ascii */ ++ char *ascii_oh_name = (char *)oh_name; ++ int n = YAFFS_MAX_NAME_LENGTH - 1; ++ while (n > 0 && *name) { ++ *ascii_oh_name = *name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ /* Unicode name, so save starting at the second YCHAR */ ++ *oh_name = 0; ++ strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); ++ } ++ } else { ++#else ++ dev = dev; ++ { ++#endif ++ strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); ++ } ++} ++ ++static u16 yaffs_calc_name_sum(const YCHAR *name) ++{ ++ u16 sum = 0; ++ u16 i = 1; ++ ++ if (!name) ++ return 0; ++ ++ while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { ++ ++ /* 0x1f mask is case insensitive */ ++ sum += ((*name) & 0x1f) * i; ++ i++; ++ name++; ++ } ++ return sum; ++} ++ ++ ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ memset(obj->short_name, 0, sizeof(obj->short_name)); ++ ++ if (name && !name[0]) { ++ yaffs_fix_null_name(obj, obj->short_name, ++ YAFFS_SHORT_NAME_LENGTH); ++ name = obj->short_name; ++ } else if (name && ++ strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= ++ YAFFS_SHORT_NAME_LENGTH) { ++ strcpy(obj->short_name, name); ++ } ++ ++ obj->sum = yaffs_calc_name_sum(name); ++} ++ ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ memset(tmp_name, 0, sizeof(tmp_name)); ++ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_set_obj_name(obj, tmp_name); ++#else ++ yaffs_set_obj_name(obj, oh->name); ++#endif ++} ++ ++loff_t yaffs_max_file_size(struct yaffs_dev *dev) ++{ ++ if(sizeof(loff_t) < 8) ++ return YAFFS_MAX_FILE_SIZE_32; ++ else ++ return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; ++} ++ ++/*-------------------- TNODES ------------------- ++ ++ * List of spare tnodes ++ * The list is hooked together using the first pointer ++ * in the tnode. ++ */ ++ ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); ++ ++ if (tn) { ++ memset(tn, 0, dev->tnode_size); ++ dev->n_tnodes++; ++ } ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ yaffs_free_raw_tnode(dev, tn); ++ dev->n_tnodes--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ yaffs_deinit_raw_tnodes_and_objs(dev); ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++} ++ ++static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos, unsigned val) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 mask; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ val >>= dev->chunk_grp_bits; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ mask = dev->tnode_mask << bit_in_word; ++ ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val << bit_in_word)); ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++; ++ mask = ++ dev->tnode_mask >> bit_in_word; ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val >> bit_in_word)); ++ } ++} ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 val; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ val = map[word_in_map] >> bit_in_word; ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++; ++ val |= (map[word_in_map] << bit_in_word); ++ } ++ ++ val &= dev->tnode_mask; ++ val <<= dev->chunk_grp_bits; ++ ++ return val; ++} ++ ++/* ------------------- End of individual tnode manipulation -----------------*/ ++ ++/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ ++ * The look up tree is represented by the top tnode and the number of top_level ++ * in the tree. 0 means only the level 0 tnode is in the tree. ++ */ ++ ++/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id) ++{ ++ struct yaffs_tnode *tn = file_struct->top; ++ u32 i; ++ int required_depth; ++ int level = file_struct->top_level; ++ ++ (void) dev; ++ ++ /* Check sane level and chunk Id */ ++ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (i) { ++ i >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) ++ return NULL; /* Not tall enough, so we can't find it */ ++ ++ /* Traverse down to level 0 */ ++ while (level > 0 && tn) { ++ tn = tn->internal[(chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (level - 1) * ++ YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK]; ++ level--; ++ } ++ ++ return tn; ++} ++ ++/* add_find_tnode_0 finds the level 0 tnode if it exists, ++ * otherwise first expands the tree. ++ * This happens in two steps: ++ * 1. If the tree isn't tall enough, then make it taller. ++ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. ++ * ++ * Used when modifying the tree. ++ * ++ * If the tn argument is NULL, then a fresh tnode will be added otherwise the ++ * specified tn will be plugged into the ttree. ++ */ ++ ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn) ++{ ++ int required_depth; ++ int i; ++ int l; ++ struct yaffs_tnode *tn; ++ u32 x; ++ ++ /* Check sane level and page Id */ ++ if (file_struct->top_level < 0 || ++ file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) { ++ /* Not tall enough, gotta make the tree taller */ ++ for (i = file_struct->top_level; i < required_depth; i++) { ++ ++ tn = yaffs_get_tnode(dev); ++ ++ if (tn) { ++ tn->internal[0] = file_struct->top; ++ file_struct->top = tn; ++ file_struct->top_level++; ++ } else { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: no more tnodes"); ++ return NULL; ++ } ++ } ++ } ++ ++ /* Traverse down to level 0, adding anything we need */ ++ ++ l = file_struct->top_level; ++ tn = file_struct->top; ++ ++ if (l > 0) { ++ while (l > 0 && tn) { ++ x = (chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK; ++ ++ if ((l > 1) && !tn->internal[x]) { ++ /* Add missing non-level-zero tnode */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } else if (l == 1) { ++ /* Looking from level 1 at level 0 */ ++ if (passed_tn) { ++ /* If we already have one, release it */ ++ if (tn->internal[x]) ++ yaffs_free_tnode(dev, ++ tn->internal[x]); ++ tn->internal[x] = passed_tn; ++ ++ } else if (!tn->internal[x]) { ++ /* Don't have one, none passed in */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } ++ } ++ ++ tn = tn->internal[x]; ++ l--; ++ } ++ } else { ++ /* top is level 0 */ ++ if (passed_tn) { ++ memcpy(tn, passed_tn, ++ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); ++ yaffs_free_tnode(dev, passed_tn); ++ } ++ } ++ ++ return tn; ++} ++ ++static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, ++ int chunk_obj) ++{ ++ return (tags->chunk_id == chunk_obj && ++ tags->obj_id == obj_id && ++ !tags->is_deleted) ? 1 : 0; ++ ++} ++ ++static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, ++ struct yaffs_ext_tags *tags, int obj_id, ++ int inode_chunk) ++{ ++ int j; ++ ++ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { ++ if (yaffs_check_chunk_bit ++ (dev, the_chunk / dev->param.chunks_per_block, ++ the_chunk % dev->param.chunks_per_block)) { ++ ++ if (dev->chunk_grp_size == 1) ++ return the_chunk; ++ else { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ tags); ++ if (yaffs_tags_match(tags, ++ obj_id, inode_chunk)) { ++ /* found it; */ ++ return the_chunk; ++ } ++ } ++ } ++ the_chunk++; ++ } ++ return -1; ++} ++ ++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /*Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ int ret_val = -1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (!tn) ++ return ret_val; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ return ret_val; ++} ++ ++static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /* Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ struct yaffs_dev *dev = in->my_dev; ++ int ret_val = -1; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (!tn) ++ return ret_val; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ ++ /* Delete the entry in the filestructure (if found) */ ++ if (ret_val != -1) ++ yaffs_load_tnode_0(dev, tn, inode_chunk, 0); ++ ++ return ret_val; ++} ++ ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan) ++{ ++ /* NB in_scan is zero unless scanning. ++ * For forward scanning, in_scan is > 0; ++ * for backward scanning in_scan is < 0 ++ * ++ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. ++ */ ++ ++ struct yaffs_tnode *tn; ++ struct yaffs_dev *dev = in->my_dev; ++ int existing_cunk; ++ struct yaffs_ext_tags existing_tags; ++ struct yaffs_ext_tags new_tags; ++ unsigned existing_serial, new_serial; ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { ++ /* Just ignore an attempt at putting a chunk into a non-file ++ * during scanning. ++ * If it is not during Scanning then something went wrong! ++ */ ++ if (!in_scan) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy:attempt to put data chunk into a non-file" ++ ); ++ BUG(); ++ } ++ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ ++ tn = yaffs_add_find_tnode_0(dev, ++ &in->variant.file_variant, ++ inode_chunk, NULL); ++ if (!tn) ++ return YAFFS_FAIL; ++ ++ if (!nand_chunk) ++ /* Dummy insert, bail now */ ++ return YAFFS_OK; ++ ++ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ if (in_scan != 0) { ++ /* If we're scanning then we need to test for duplicates ++ * NB This does not need to be efficient since it should only ++ * happen when the power fails during a write, then only one ++ * chunk should ever be affected. ++ * ++ * Correction for YAFFS2: This could happen quite a lot and we ++ * need to think about efficiency! TODO ++ * Update: For backward scanning we don't need to re-read tags ++ * so this is quite cheap. ++ */ ++ ++ if (existing_cunk > 0) { ++ /* NB Right now existing chunk will not be real ++ * chunk_id if the chunk group size > 1 ++ * thus we have to do a FindChunkInFile to get the ++ * real chunk id. ++ * ++ * We have a duplicate now we need to decide which ++ * one to use: ++ * ++ * Backwards scanning YAFFS2: The old one is what ++ * we use, dump the new one. ++ * YAFFS1: Get both sets of tags and compare serial ++ * numbers. ++ */ ++ ++ if (in_scan > 0) { ++ /* Only do this for forward scanning */ ++ yaffs_rd_chunk_tags_nand(dev, ++ nand_chunk, ++ NULL, &new_tags); ++ ++ /* Do a proper find */ ++ existing_cunk = ++ yaffs_find_chunk_in_file(in, inode_chunk, ++ &existing_tags); ++ } ++ ++ if (existing_cunk <= 0) { ++ /*Hoosterman - how did this happen? */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: existing chunk < 0 in scan" ++ ); ++ ++ } ++ ++ /* NB The deleted flags should be false, otherwise ++ * the chunks will not be loaded during a scan ++ */ ++ ++ if (in_scan > 0) { ++ new_serial = new_tags.serial_number; ++ existing_serial = existing_tags.serial_number; ++ } ++ ++ if ((in_scan > 0) && ++ (existing_cunk <= 0 || ++ ((existing_serial + 1) & 3) == new_serial)) { ++ /* Forward scanning. ++ * Use new ++ * Delete the old one and drop through to ++ * update the tnode ++ */ ++ yaffs_chunk_del(dev, existing_cunk, 1, ++ __LINE__); ++ } else { ++ /* Backward scanning or we want to use the ++ * existing one ++ * Delete the new one and return early so that ++ * the tnode isn't changed ++ */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ } ++ ++ } ++ ++ if (existing_cunk == 0) ++ in->n_data_chunks++; ++ ++ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); ++ ++ return YAFFS_OK; ++} ++ ++static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) ++{ ++ struct yaffs_block_info *the_block; ++ unsigned block_no; ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); ++ ++ block_no = chunk / dev->param.chunks_per_block; ++ the_block = yaffs_get_block_info(dev, block_no); ++ if (the_block) { ++ the_block->soft_del_pages++; ++ dev->n_free_chunks++; ++ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); ++ } ++} ++ ++/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all ++ * the chunks in the file. ++ * All soft deleting does is increment the block's softdelete count and pulls ++ * the chunk out of the tnode. ++ * Thus, essentially this is the same as DeleteWorker except that the chunks ++ * are soft deleted. ++ */ ++ ++static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, ++ u32 level, int chunk_offset) ++{ ++ int i; ++ int the_chunk; ++ int all_done = 1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tn) ++ return 1; ++ ++ if (level > 0) { ++ for (i = YAFFS_NTNODES_INTERNAL - 1; ++ all_done && i >= 0; ++ i--) { ++ if (tn->internal[i]) { ++ all_done = ++ yaffs_soft_del_worker(in, ++ tn->internal[i], ++ level - 1, ++ (chunk_offset << ++ YAFFS_TNODES_INTERNAL_BITS) ++ + i); ++ if (all_done) { ++ yaffs_free_tnode(dev, ++ tn->internal[i]); ++ tn->internal[i] = NULL; ++ } else { ++ /* Can this happen? */ ++ } ++ } ++ } ++ return (all_done) ? 1 : 0; ++ } ++ ++ /* level 0 */ ++ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk) { ++ yaffs_soft_del_chunk(dev, the_chunk); ++ yaffs_load_tnode_0(dev, tn, i, 0); ++ } ++ } ++ return 1; ++} ++ ++static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_obj *parent; ++ ++ yaffs_verify_obj_in_dir(obj); ++ parent = obj->parent; ++ ++ yaffs_verify_dir(parent); ++ ++ if (dev && dev->param.remove_obj_fn) ++ dev->param.remove_obj_fn(obj); ++ ++ list_del_init(&obj->siblings); ++ obj->parent = NULL; ++ ++ yaffs_verify_dir(parent); ++} ++ ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) ++{ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a null pointer directory" ++ ); ++ BUG(); ++ return; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a non-directory" ++ ); ++ BUG(); ++ } ++ ++ if (obj->siblings.prev == NULL) { ++ /* Not initialised */ ++ BUG(); ++ } ++ ++ yaffs_verify_dir(directory); ++ ++ yaffs_remove_obj_from_dir(obj); ++ ++ /* Now add it */ ++ list_add(&obj->siblings, &directory->variant.dir_variant.children); ++ obj->parent = directory; ++ ++ if (directory == obj->my_dev->unlinked_dir ++ || directory == obj->my_dev->del_dir) { ++ obj->unlinked = 1; ++ obj->my_dev->n_unlinked_files++; ++ obj->rename_allowed = 0; ++ } ++ ++ yaffs_verify_dir(directory); ++ yaffs_verify_obj_in_dir(obj); ++} ++ ++static int yaffs_change_obj_name(struct yaffs_obj *obj, ++ struct yaffs_obj *new_dir, ++ const YCHAR *new_name, int force, int shadows) ++{ ++ int unlink_op; ++ int del_op; ++ struct yaffs_obj *existing_target; ++ ++ if (new_dir == NULL) ++ new_dir = obj->parent; /* use the old directory */ ++ ++ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_change_obj_name: new_dir is not a directory" ++ ); ++ BUG(); ++ } ++ ++ unlink_op = (new_dir == obj->my_dev->unlinked_dir); ++ del_op = (new_dir == obj->my_dev->del_dir); ++ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ ++ /* If the object is a file going into the unlinked directory, ++ * then it is OK to just stuff it in since duplicate names are OK. ++ * else only proceed if the new name does not exist and we're putting ++ * it into a directory. ++ */ ++ if (!(unlink_op || del_op || force || ++ shadows > 0 || !existing_target) || ++ new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ return YAFFS_FAIL; ++ ++ yaffs_set_obj_name(obj, new_name); ++ obj->dirty = 1; ++ yaffs_add_obj_to_dir(new_dir, obj); ++ ++ if (unlink_op) ++ obj->unlinked = 1; ++ ++ /* If it is a deletion then we mark it as a shrink for gc */ ++ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++/*------------------------ Short Operations Cache ------------------------------ ++ * In many situations where there is no high level buffering a lot of ++ * reads might be short sequential reads, and a lot of writes may be short ++ * sequential writes. eg. scanning/writing a jpeg file. ++ * In these cases, a short read/write cache can provide a huge perfomance ++ * benefit with dumb-as-a-rock code. ++ * In Linux, the page cache provides read buffering and the short op cache ++ * provides write buffering. ++ * ++ * There are a small number (~10) of cache chunks per device so that we don't ++ * need a very intelligent search. ++ */ ++ ++static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ struct yaffs_cache *cache; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ for (i = 0; i < n_caches; i++) { ++ cache = &dev->cache[i]; ++ if (cache->object == obj && cache->dirty) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static void yaffs_flush_single_cache(struct yaffs_cache *cache, int discard) ++{ ++ ++ if (!cache || cache->locked) ++ return; ++ ++ /* Write it out and free it up if need be.*/ ++ if (cache->dirty) { ++ yaffs_wr_data_obj(cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, ++ 1); ++ ++ cache->dirty = 0; ++ } ++ ++ if (discard) ++ cache->object = NULL; ++} ++ ++static void yaffs_flush_file_cache(struct yaffs_obj *obj, int discard) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ struct yaffs_cache *cache; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ if (n_caches < 1) ++ return; ++ ++ ++ /* Find the chunks for this object and flush them. */ ++ for (i = 0; i < n_caches; i++) { ++ cache = &dev->cache[i]; ++ if (cache->object == obj) ++ yaffs_flush_single_cache(cache, discard); ++ } ++ ++} ++ ++ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard) ++{ ++ struct yaffs_obj *obj; ++ int n_caches = dev->param.n_caches; ++ int i; ++ ++ /* Find a dirty object in the cache and flush it... ++ * until there are no further dirty objects. ++ */ ++ do { ++ obj = NULL; ++ for (i = 0; i < n_caches && !obj; i++) { ++ if (dev->cache[i].object && dev->cache[i].dirty) ++ obj = dev->cache[i].object; ++ } ++ if (obj) ++ yaffs_flush_file_cache(obj, discard); ++ } while (obj); ++ ++} ++ ++/* Grab us an unused cache chunk for use. ++ * First look for an empty one. ++ * Then look for the least recently used non-dirty one. ++ * Then look for the least recently used dirty one...., flush and look again. ++ */ ++static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->param.n_caches > 0) { ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (!dev->cache[i].object) ++ return &dev->cache[i]; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) ++{ ++ struct yaffs_cache *cache; ++ int usage; ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return NULL; ++ ++ /* First look for an unused cache */ ++ ++ cache = yaffs_grab_chunk_worker(dev); ++ ++ if (cache) ++ return cache; ++ ++ /* ++ * Thery were all in use. ++ * Find the LRU cache and flush it if it is dirty. ++ */ ++ ++ usage = -1; ++ cache = NULL; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object && ++ !dev->cache[i].locked && ++ (dev->cache[i].last_use < usage || !cache)) { ++ usage = dev->cache[i].last_use; ++ cache = &dev->cache[i]; ++ } ++ } ++ ++#if 1 ++ yaffs_flush_single_cache(cache, 1); ++#else ++ yaffs_flush_file_cache(cache->object, 1); ++ cache = yaffs_grab_chunk_worker(dev); ++#endif ++ ++ return cache; ++} ++ ++/* Find a cached chunk */ ++static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, ++ int chunk_id) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return NULL; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == obj && ++ dev->cache[i].chunk_id == chunk_id) { ++ dev->cache_hits++; ++ ++ return &dev->cache[i]; ++ } ++ } ++ return NULL; ++} ++ ++/* Mark the chunk for the least recently used algorithym */ ++static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, ++ int is_write) ++{ ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return; ++ ++ if (dev->cache_last_use < 0 || ++ dev->cache_last_use > 100000000) { ++ /* Reset the cache usages */ ++ for (i = 1; i < dev->param.n_caches; i++) ++ dev->cache[i].last_use = 0; ++ ++ dev->cache_last_use = 0; ++ } ++ dev->cache_last_use++; ++ cache->last_use = dev->cache_last_use; ++ ++ if (is_write) ++ cache->dirty = 1; ++} ++ ++/* Invalidate a single cache page. ++ * Do this when a whole page gets written, ++ * ie the short cache for this page is no longer valid. ++ */ ++static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) ++{ ++ struct yaffs_cache *cache; ++ ++ if (object->my_dev->param.n_caches > 0) { ++ cache = yaffs_find_chunk_cache(object, chunk_id); ++ ++ if (cache) ++ cache->object = NULL; ++ } ++} ++ ++/* Invalidate all the cache pages associated with this object ++ * Do this whenever ther file is deleted or resized. ++ */ ++static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.n_caches > 0) { ++ /* Invalidate it. */ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == in) ++ dev->cache[i].object = NULL; ++ } ++ } ++} ++ ++static void yaffs_unhash_obj(struct yaffs_obj *obj) ++{ ++ int bucket; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ /* If it is still linked into the bucket list, free from the list */ ++ if (!list_empty(&obj->hash_link)) { ++ list_del_init(&obj->hash_link); ++ bucket = yaffs_hash_fn(obj->obj_id); ++ dev->obj_bucket[bucket].count--; ++ } ++} ++ ++/* FreeObject frees up a Object and puts it back on the free list */ ++static void yaffs_free_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ if (!obj) { ++ BUG(); ++ return; ++ } ++ dev = obj->my_dev; ++ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", ++ obj, obj->my_inode); ++ if (obj->parent) ++ BUG(); ++ if (!list_empty(&obj->siblings)) ++ BUG(); ++ ++ if (obj->my_inode) { ++ /* We're still hooked up to a cached inode. ++ * Don't delete now, but mark for later deletion ++ */ ++ obj->defered_free = 1; ++ return; ++ } ++ ++ yaffs_unhash_obj(obj); ++ ++ yaffs_free_raw_obj(dev, obj); ++ dev->n_obj--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj) ++{ ++ if (obj->defered_free) ++ yaffs_free_obj(obj); ++} ++ ++static int yaffs_generic_obj_del(struct yaffs_obj *in) ++{ ++ /* Iinvalidate the file's data in the cache, without flushing. */ ++ yaffs_invalidate_whole_cache(in); ++ ++ if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { ++ /* Move to unlinked directory so we have a deletion record */ ++ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, ++ 0); ++ } ++ ++ yaffs_remove_obj_from_dir(in); ++ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); ++ in->hdr_chunk = 0; ++ ++ yaffs_free_obj(in); ++ return YAFFS_OK; ++ ++} ++ ++static void yaffs_soft_del_file(struct yaffs_obj *obj) ++{ ++ if (!obj->deleted || ++ obj->variant_type != YAFFS_OBJECT_TYPE_FILE || ++ obj->soft_del) ++ return; ++ ++ if (obj->n_data_chunks <= 0) { ++ /* Empty file with no duplicate object headers, ++ * just delete it immediately */ ++ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: Deleting empty file %d", ++ obj->obj_id); ++ yaffs_generic_obj_del(obj); ++ } else { ++ yaffs_soft_del_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant. ++ file_variant.top_level, 0); ++ obj->soft_del = 1; ++ } ++} ++ ++/* Pruning removes any part of the file structure tree that is beyond the ++ * bounds of the file (ie that does not point to chunks). ++ * ++ * A file should only get pruned when its size is reduced. ++ * ++ * Before pruning, the chunks must be pulled from the tree and the ++ * level 0 tnode entries must be zeroed out. ++ * Could also use this for file deletion, but that's probably better handled ++ * by a special case. ++ * ++ * This function is recursive. For levels > 0 the function is called again on ++ * any sub-tree. For level == 0 we just check if the sub-tree has data. ++ * If there is no data in a subtree then it is pruned. ++ */ ++ ++static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, ++ struct yaffs_tnode *tn, u32 level, ++ int del0) ++{ ++ int i; ++ int has_data; ++ ++ if (!tn) ++ return tn; ++ ++ has_data = 0; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) { ++ tn->internal[i] = ++ yaffs_prune_worker(dev, ++ tn->internal[i], ++ level - 1, ++ (i == 0) ? del0 : 1); ++ } ++ ++ if (tn->internal[i]) ++ has_data++; ++ } ++ } else { ++ int tnode_size_u32 = dev->tnode_size / sizeof(u32); ++ u32 *map = (u32 *) tn; ++ ++ for (i = 0; !has_data && i < tnode_size_u32; i++) { ++ if (map[i]) ++ has_data++; ++ } ++ } ++ ++ if (has_data == 0 && del0) { ++ /* Free and return NULL */ ++ yaffs_free_tnode(dev, tn); ++ tn = NULL; ++ } ++ return tn; ++} ++ ++static int yaffs_prune_tree(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct) ++{ ++ int i; ++ int has_data; ++ int done = 0; ++ struct yaffs_tnode *tn; ++ ++ if (file_struct->top_level < 1) ++ return YAFFS_OK; ++ ++ file_struct->top = ++ yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); ++ ++ /* Now we have a tree with all the non-zero branches NULL but ++ * the height is the same as it was. ++ * Let's see if we can trim internal tnodes to shorten the tree. ++ * We can do this if only the 0th element in the tnode is in use ++ * (ie all the non-zero are NULL) ++ */ ++ ++ while (file_struct->top_level && !done) { ++ tn = file_struct->top; ++ ++ has_data = 0; ++ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) ++ has_data++; ++ } ++ ++ if (!has_data) { ++ file_struct->top = tn->internal[0]; ++ file_struct->top_level--; ++ yaffs_free_tnode(dev, tn); ++ } else { ++ done = 1; ++ } ++ } ++ ++ return YAFFS_OK; ++} ++ ++/*-------------------- End of File Structure functions.-------------------*/ ++ ++/* alloc_empty_obj gets us a clean Object.*/ ++static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); ++ ++ if (!obj) ++ return obj; ++ ++ dev->n_obj++; ++ ++ /* Now sweeten it up... */ ++ ++ memset(obj, 0, sizeof(struct yaffs_obj)); ++ obj->being_created = 1; ++ ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; ++ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; ++ INIT_LIST_HEAD(&(obj->hard_links)); ++ INIT_LIST_HEAD(&(obj->hash_link)); ++ INIT_LIST_HEAD(&obj->siblings); ++ ++ /* Now make the directory sane */ ++ if (dev->root_dir) { ++ obj->parent = dev->root_dir; ++ list_add(&(obj->siblings), ++ &dev->root_dir->variant.dir_variant.children); ++ } ++ ++ /* Add it to the lost and found directory. ++ * NB Can't put root or lost-n-found in lost-n-found so ++ * check if lost-n-found exists first ++ */ ++ if (dev->lost_n_found) ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ ++ obj->being_created = 0; ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return obj; ++} ++ ++static int yaffs_find_nice_bucket(struct yaffs_dev *dev) ++{ ++ int i; ++ int l = 999; ++ int lowest = 999999; ++ ++ /* Search for the shortest list or one that ++ * isn't too long. ++ */ ++ ++ for (i = 0; i < 10 && lowest > 4; i++) { ++ dev->bucket_finder++; ++ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; ++ if (dev->obj_bucket[dev->bucket_finder].count < lowest) { ++ lowest = dev->obj_bucket[dev->bucket_finder].count; ++ l = dev->bucket_finder; ++ } ++ } ++ ++ return l; ++} ++ ++static int yaffs_new_obj_id(struct yaffs_dev *dev) ++{ ++ int bucket = yaffs_find_nice_bucket(dev); ++ int found = 0; ++ struct list_head *i; ++ u32 n = (u32) bucket; ++ ++ /* ++ * Now find an object value that has not already been taken ++ * by scanning the list, incrementing each time by number of buckets. ++ */ ++ while (!found) { ++ found = 1; ++ n += YAFFS_NOBJECT_BUCKETS; ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* Check if this value is already taken. */ ++ if (i && list_entry(i, struct yaffs_obj, ++ hash_link)->obj_id == n) ++ found = 0; ++ } ++ } ++ return n; ++} ++ ++static void yaffs_hash_obj(struct yaffs_obj *in) ++{ ++ int bucket = yaffs_hash_fn(in->obj_id); ++ struct yaffs_dev *dev = in->my_dev; ++ ++ list_add(&in->hash_link, &dev->obj_bucket[bucket].list); ++ dev->obj_bucket[bucket].count++; ++} ++ ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) ++{ ++ int bucket = yaffs_hash_fn(number); ++ struct list_head *i; ++ struct yaffs_obj *in; ++ ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* Look if it is in the list */ ++ in = list_entry(i, struct yaffs_obj, hash_link); ++ if (in->obj_id == number) { ++ /* Don't show if it is defered free */ ++ if (in->defered_free) ++ return NULL; ++ return in; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (number < 0) ++ number = yaffs_new_obj_id(dev); ++ ++ if (type == YAFFS_OBJECT_TYPE_FILE) { ++ tn = yaffs_get_tnode(dev); ++ if (!tn) ++ return NULL; ++ } ++ ++ the_obj = yaffs_alloc_empty_obj(dev); ++ if (!the_obj) { ++ if (tn) ++ yaffs_free_tnode(dev, tn); ++ return NULL; ++ } ++ ++ the_obj->fake = 0; ++ the_obj->rename_allowed = 1; ++ the_obj->unlink_allowed = 1; ++ the_obj->obj_id = number; ++ yaffs_hash_obj(the_obj); ++ the_obj->variant_type = type; ++ yaffs_load_current_time(the_obj, 1, 1); ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ the_obj->variant.file_variant.file_size = 0; ++ the_obj->variant.file_variant.scanned_size = 0; ++ the_obj->variant.file_variant.shrink_size = ++ yaffs_max_file_size(dev); ++ the_obj->variant.file_variant.top_level = 0; ++ the_obj->variant.file_variant.top = tn; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* No action required */ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* todo this should not happen */ ++ break; ++ } ++ return the_obj; ++} ++ ++static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, ++ int number, u32 mode) ++{ ++ ++ struct yaffs_obj *obj = ++ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); ++ ++ if (!obj) ++ return NULL; ++ ++ obj->fake = 1; /* it is fake so it might not use NAND */ ++ obj->rename_allowed = 0; ++ obj->unlink_allowed = 0; ++ obj->deleted = 0; ++ obj->unlinked = 0; ++ obj->yst_mode = mode; ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; /* Not a valid chunk. */ ++ return obj; ++ ++} ++ ++ ++static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++ yaffs_init_raw_tnodes_and_objs(dev); ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ INIT_LIST_HEAD(&dev->obj_bucket[i].list); ++ dev->obj_bucket[i].count = 0; ++ } ++} ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ ++ if (number > 0) ++ the_obj = yaffs_find_by_number(dev, number); ++ ++ if (!the_obj) ++ the_obj = yaffs_new_obj(dev, number, type); ++ ++ return the_obj; ++ ++} ++ ++YCHAR *yaffs_clone_str(const YCHAR *str) ++{ ++ YCHAR *new_str = NULL; ++ int len; ++ ++ if (!str) ++ str = _Y(""); ++ ++ len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); ++ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); ++ if (new_str) { ++ strncpy(new_str, str, len); ++ new_str[len] = 0; ++ } ++ return new_str; ++ ++} ++/* ++ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new ++ * link (ie. name) is created or deleted in the directory. ++ * ++ * ie. ++ * create dir/a : update dir's mtime/ctime ++ * rm dir/a: update dir's mtime/ctime ++ * modify dir/a: don't update dir's mtimme/ctime ++ * ++ * This can be handled immediately or defered. Defering helps reduce the number ++ * of updates when many files in a directory are changed within a brief period. ++ * ++ * If the directory updating is defered then yaffs_update_dirty_dirs must be ++ * called periodically. ++ */ ++ ++static void yaffs_update_parent(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ if (!obj) ++ return; ++ dev = obj->my_dev; ++ obj->dirty = 1; ++ yaffs_load_current_time(obj, 0, 1); ++ if (dev->param.defered_dir_update) { ++ struct list_head *link = &obj->variant.dir_variant.dirty; ++ ++ if (list_empty(link)) { ++ list_add(link, &dev->dirty_dirs); ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Added object %d to dirty directories", ++ obj->obj_id); ++ } ++ ++ } else { ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev) ++{ ++ struct list_head *link; ++ struct yaffs_obj *obj; ++ struct yaffs_dir_var *d_s; ++ union yaffs_obj_var *o_v; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); ++ ++ while (!list_empty(&dev->dirty_dirs)) { ++ link = dev->dirty_dirs.next; ++ list_del_init(link); ++ ++ d_s = list_entry(link, struct yaffs_dir_var, dirty); ++ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); ++ obj = list_entry(o_v, struct yaffs_obj, variant); ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", ++ obj->obj_id); ++ ++ if (obj->dirty) ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++/* ++ * Mknod (create) a new object. ++ * equiv_obj only has meaning for a hard link; ++ * alias_str only has meaning for a symlink. ++ * rdev only has meaning for devices (a subset of special objects) ++ */ ++ ++static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, ++ struct yaffs_obj *parent, ++ const YCHAR *name, ++ u32 mode, ++ u32 uid, ++ u32 gid, ++ struct yaffs_obj *equiv_obj, ++ const YCHAR *alias_str, u32 rdev) ++{ ++ struct yaffs_obj *in; ++ YCHAR *str = NULL; ++ struct yaffs_dev *dev = parent->my_dev; ++ ++ /* Check if the entry exists. ++ * If it does then fail the call since we don't want a dup. */ ++ if (yaffs_find_by_name(parent, name)) ++ return NULL; ++ ++ if (type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ str = yaffs_clone_str(alias_str); ++ if (!str) ++ return NULL; ++ } ++ ++ in = yaffs_new_obj(dev, -1, type); ++ ++ if (!in) { ++ kfree(str); ++ return NULL; ++ } ++ ++ in->hdr_chunk = 0; ++ in->valid = 1; ++ in->variant_type = type; ++ ++ in->yst_mode = mode; ++ ++ yaffs_attribs_init(in, gid, uid, rdev); ++ ++ in->n_data_chunks = 0; ++ ++ yaffs_set_obj_name(in, name); ++ in->dirty = 1; ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ in->my_dev = parent->my_dev; ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant.alias = str; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant.hardlink_variant.equiv_obj = equiv_obj; ++ in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; ++ list_add(&in->hard_links, &equiv_obj->hard_links); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* do nothing */ ++ break; ++ } ++ ++ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { ++ /* Could not create the object header, fail */ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ if (in) ++ yaffs_update_parent(parent); ++ ++ return in; ++} ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, ++ uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, ++ u32 mode, u32 uid, u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, ++ mode, uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, u32 rdev) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, ++ uid, gid, NULL, NULL, rdev); ++} ++ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, const YCHAR *alias) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, ++ uid, gid, NULL, alias, 0); ++} ++ ++/* yaffs_link_obj returns the object id of the equivalent object.*/ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, ++ struct yaffs_obj *equiv_obj) ++{ ++ /* Get the real object in case we were fed a hard link obj */ ++ equiv_obj = yaffs_get_equivalent_obj(equiv_obj); ++ ++ if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, ++ parent, name, 0, 0, 0, ++ equiv_obj, NULL, 0)) ++ return equiv_obj; ++ ++ return NULL; ++ ++} ++ ++ ++ ++/*---------------------- Block Management and Page Allocation -------------*/ ++ ++static void yaffs_deinit_blocks(struct yaffs_dev *dev) ++{ ++ if (dev->block_info_alt && dev->block_info) ++ vfree(dev->block_info); ++ else ++ kfree(dev->block_info); ++ ++ dev->block_info_alt = 0; ++ ++ dev->block_info = NULL; ++ ++ if (dev->chunk_bits_alt && dev->chunk_bits) ++ vfree(dev->chunk_bits); ++ else ++ kfree(dev->chunk_bits); ++ dev->chunk_bits_alt = 0; ++ dev->chunk_bits = NULL; ++} ++ ++static int yaffs_init_blocks(struct yaffs_dev *dev) ++{ ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ dev->block_info = NULL; ++ dev->chunk_bits = NULL; ++ dev->alloc_block = -1; /* force it to get a new one */ ++ ++ /* If the first allocation strategy fails, thry the alternate one */ ++ dev->block_info = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); ++ if (!dev->block_info) { ++ dev->block_info = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_info)); ++ dev->block_info_alt = 1; ++ } else { ++ dev->block_info_alt = 0; ++ } ++ ++ if (!dev->block_info) ++ goto alloc_error; ++ ++ /* Set up dynamic blockinfo stuff. Round up bytes. */ ++ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; ++ dev->chunk_bits = ++ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); ++ if (!dev->chunk_bits) { ++ dev->chunk_bits = ++ vmalloc(dev->chunk_bit_stride * n_blocks); ++ dev->chunk_bits_alt = 1; ++ } else { ++ dev->chunk_bits_alt = 0; ++ } ++ if (!dev->chunk_bits) ++ goto alloc_error; ++ ++ ++ memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); ++ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); ++ return YAFFS_OK; ++ ++alloc_error: ++ yaffs_deinit_blocks(dev); ++ return YAFFS_FAIL; ++} ++ ++ ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); ++ int erased_ok = 0; ++ int i; ++ ++ /* If the block is still healthy erase it and mark as clean. ++ * If the block has had a data failure, then retire it. ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, ++ "yaffs_block_became_dirty block %d state %d %s", ++ block_no, bi->block_state, ++ (bi->needs_retiring) ? "needs retiring" : ""); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DIRTY; ++ ++ /* If this is the block being garbage collected then stop gc'ing */ ++ if (block_no == dev->gc_block) ++ dev->gc_block = 0; ++ ++ /* If this block is currently the best candidate for gc ++ * then drop as a candidate */ ++ if (block_no == dev->gc_dirtiest) { ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ } ++ ++ if (!bi->needs_retiring) { ++ yaffs2_checkpt_invalidate(dev); ++ erased_ok = yaffs_erase_block(dev, block_no); ++ if (!erased_ok) { ++ dev->n_erase_failures++; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Erasure failed %d", block_no); ++ } ++ } ++ ++ /* Verify erasure if needed */ ++ if (erased_ok && ++ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || ++ !yaffs_skip_verification(dev))) { ++ for (i = 0; i < dev->param.chunks_per_block; i++) { ++ if (!yaffs_check_chunk_erased(dev, ++ block_no * dev->param.chunks_per_block + i)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ ">>Block %d erasure supposedly OK, but chunk %d not erased", ++ block_no, i); ++ } ++ } ++ } ++ ++ if (!erased_ok) { ++ /* We lost a block of free space */ ++ dev->n_free_chunks -= dev->param.chunks_per_block; ++ yaffs_retire_block(dev, block_no); ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d retired", block_no); ++ return; ++ } ++ ++ /* Clean it up... */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ bi->seq_number = 0; ++ dev->n_erased_blocks++; ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ bi->has_shrink_hdr = 0; ++ bi->skip_erased_check = 1; /* Clean, so no need to check */ ++ bi->gc_prioritise = 0; ++ bi->has_summary = 0; ++ ++ yaffs_clear_chunk_bits(dev, block_no); ++ ++ yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); ++} ++ ++static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, ++ int old_chunk, u8 *buffer) ++{ ++ int new_chunk; ++ int mark_flash = 1; ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj *object; ++ int matching_chunk; ++ int ret_val = YAFFS_OK; ++ ++ memset(&tags, 0, sizeof(tags)); ++ yaffs_rd_chunk_tags_nand(dev, old_chunk, ++ buffer, &tags); ++ object = yaffs_find_by_number(dev, tags.obj_id); ++ ++ yaffs_trace(YAFFS_TRACE_GC_DETAIL, ++ "Collecting chunk in block %d, %d %d %d ", ++ dev->gc_chunk, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ ++ if (object && !yaffs_skip_verification(dev)) { ++ if (tags.chunk_id == 0) ++ matching_chunk = ++ object->hdr_chunk; ++ else if (object->soft_del) ++ /* Defeat the test */ ++ matching_chunk = old_chunk; ++ else ++ matching_chunk = ++ yaffs_find_chunk_in_file ++ (object, tags.chunk_id, ++ NULL); ++ ++ if (old_chunk != matching_chunk) ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "gc: page in gc mismatch: %d %d %d %d", ++ old_chunk, ++ matching_chunk, ++ tags.obj_id, ++ tags.chunk_id); ++ } ++ ++ if (!object) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "page %d in gc has no object: %d %d %d ", ++ old_chunk, ++ tags.obj_id, tags.chunk_id, ++ tags.n_bytes); ++ } ++ ++ if (object && ++ object->deleted && ++ object->soft_del && tags.chunk_id != 0) { ++ /* Data chunk in a soft deleted file, ++ * throw it away. ++ * It's a soft deleted data chunk, ++ * No need to copy this, just forget ++ * about it and fix up the object. ++ */ ++ ++ /* Free chunks already includes ++ * softdeleted chunks, how ever this ++ * chunk is going to soon be really ++ * deleted which will increment free ++ * chunks. We have to decrement free ++ * chunks so this works out properly. ++ */ ++ dev->n_free_chunks--; ++ bi->soft_del_pages--; ++ ++ object->n_data_chunks--; ++ if (object->n_data_chunks <= 0) { ++ /* remeber to clean up obj */ ++ dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; ++ dev->n_clean_ups++; ++ } ++ mark_flash = 0; ++ } else if (object) { ++ /* It's either a data chunk in a live ++ * file or an ObjectHeader, so we're ++ * interested in it. ++ * NB Need to keep the ObjectHeaders of ++ * deleted files until the whole file ++ * has been deleted off ++ */ ++ tags.serial_number++; ++ dev->n_gc_copies++; ++ ++ if (tags.chunk_id == 0) { ++ /* It is an object Id, ++ * We need to nuke the ++ * shrinkheader flags since its ++ * work is done. ++ * Also need to clean up ++ * shadowing. ++ */ ++ struct yaffs_obj_hdr *oh; ++ oh = (struct yaffs_obj_hdr *) buffer; ++ ++ oh->is_shrink = 0; ++ tags.extra_is_shrink = 0; ++ oh->shadows_obj = 0; ++ oh->inband_shadowed_obj_id = 0; ++ tags.extra_shadows = 0; ++ ++ /* Update file size */ ++ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { ++ yaffs_oh_size_load(oh, ++ object->variant.file_variant.file_size); ++ tags.extra_file_size = ++ object->variant.file_variant.file_size; ++ } ++ ++ yaffs_verify_oh(object, oh, &tags, 1); ++ new_chunk = ++ yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); ++ } else { ++ new_chunk = ++ yaffs_write_new_chunk(dev, buffer, &tags, 1); ++ } ++ ++ if (new_chunk < 0) { ++ ret_val = YAFFS_FAIL; ++ } else { ++ ++ /* Now fix up the Tnodes etc. */ ++ ++ if (tags.chunk_id == 0) { ++ /* It's a header */ ++ object->hdr_chunk = new_chunk; ++ object->serial = tags.serial_number; ++ } else { ++ /* It's a data chunk */ ++ yaffs_put_chunk_in_file(object, tags.chunk_id, ++ new_chunk, 0); ++ } ++ } ++ } ++ if (ret_val == YAFFS_OK) ++ yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); ++ return ret_val; ++} ++ ++static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) ++{ ++ int old_chunk; ++ int ret_val = YAFFS_OK; ++ int i; ++ int is_checkpt_block; ++ int max_copies; ++ int chunks_before = yaffs_get_erased_chunks(dev); ++ int chunks_after; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); ++ ++ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d, in use %d, shrink %d, whole_block %d", ++ block, bi->pages_in_use, bi->has_shrink_hdr, ++ whole_block); ++ ++ /*yaffs_verify_free_chunks(dev); */ ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; ++ ++ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ ++ ++ dev->gc_disable = 1; ++ ++ yaffs_summary_gc(dev, block); ++ ++ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d that has no chunks in use", ++ block); ++ yaffs_block_became_dirty(dev, block); ++ } else { ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ yaffs_verify_blk(dev, bi, block); ++ ++ max_copies = (whole_block) ? dev->param.chunks_per_block : 5; ++ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; ++ ++ for (/* init already done */ ; ++ ret_val == YAFFS_OK && ++ dev->gc_chunk < dev->param.chunks_per_block && ++ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && ++ max_copies > 0; ++ dev->gc_chunk++, old_chunk++) { ++ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { ++ /* Page is in use and might need to be copied */ ++ max_copies--; ++ ret_val = yaffs_gc_process_chunk(dev, bi, ++ old_chunk, buffer); ++ } ++ } ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ ++ yaffs_verify_collected_blk(dev, bi, block); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ /* ++ * The gc did not complete. Set block state back to FULL ++ * because checkpointing does not restore gc. ++ */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } else { ++ /* The gc completed. */ ++ /* Do any required cleanups */ ++ for (i = 0; i < dev->n_clean_ups; i++) { ++ /* Time to delete the file too */ ++ struct yaffs_obj *object = ++ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); ++ if (object) { ++ yaffs_free_tnode(dev, ++ object->variant.file_variant.top); ++ object->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: About to finally delete object %d", ++ object->obj_id); ++ yaffs_generic_obj_del(object); ++ object->my_dev->n_deleted_files--; ++ } ++ ++ } ++ chunks_after = yaffs_get_erased_chunks(dev); ++ if (chunks_before >= chunks_after) ++ yaffs_trace(YAFFS_TRACE_GC, ++ "gc did not increase free chunks before %d after %d", ++ chunks_before, chunks_after); ++ dev->gc_block = 0; ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ dev->gc_disable = 0; ++ ++ return ret_val; ++} ++ ++/* ++ * find_gc_block() selects the dirtiest block (or close enough) ++ * for garbage collection. ++ */ ++ ++static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, ++ int aggressive, int background) ++{ ++ int i; ++ int iterations; ++ unsigned selected = 0; ++ int prioritised = 0; ++ int prioritised_exist = 0; ++ struct yaffs_block_info *bi; ++ int threshold; ++ ++ /* First let's see if we need to grab a prioritised block */ ++ if (dev->has_pending_prioritised_gc && !aggressive) { ++ dev->gc_dirtiest = 0; ++ bi = dev->block_info; ++ for (i = dev->internal_start_block; ++ i <= dev->internal_end_block && !selected; i++) { ++ ++ if (bi->gc_prioritise) { ++ prioritised_exist = 1; ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ selected = i; ++ prioritised = 1; ++ } ++ } ++ bi++; ++ } ++ ++ /* ++ * If there is a prioritised block and none was selected then ++ * this happened because there is at least one old dirty block ++ * gumming up the works. Let's gc the oldest dirty block. ++ */ ++ ++ if (prioritised_exist && ++ !selected && dev->oldest_dirty_block > 0) ++ selected = dev->oldest_dirty_block; ++ ++ if (!prioritised_exist) /* None found, so we can clear this */ ++ dev->has_pending_prioritised_gc = 0; ++ } ++ ++ /* If we're doing aggressive GC then we are happy to take a less-dirty ++ * block, and search harder. ++ * else (leasurely gc), then we only bother to do this if the ++ * block has only a few pages in use. ++ */ ++ ++ if (!selected) { ++ int pages_used; ++ int n_blocks = ++ dev->internal_end_block - dev->internal_start_block + 1; ++ if (aggressive) { ++ threshold = dev->param.chunks_per_block; ++ iterations = n_blocks; ++ } else { ++ int max_threshold; ++ ++ if (background) ++ max_threshold = dev->param.chunks_per_block / 2; ++ else ++ max_threshold = dev->param.chunks_per_block / 8; ++ ++ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ ++ threshold = background ? (dev->gc_not_done + 2) * 2 : 0; ++ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ if (threshold > max_threshold) ++ threshold = max_threshold; ++ ++ iterations = n_blocks / 16 + 1; ++ if (iterations > 100) ++ iterations = 100; ++ } ++ ++ for (i = 0; ++ i < iterations && ++ (dev->gc_dirtiest < 1 || ++ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); ++ i++) { ++ dev->gc_block_finder++; ++ if (dev->gc_block_finder < dev->internal_start_block || ++ dev->gc_block_finder > dev->internal_end_block) ++ dev->gc_block_finder = ++ dev->internal_start_block; ++ ++ bi = yaffs_get_block_info(dev, dev->gc_block_finder); ++ ++ pages_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ pages_used < dev->param.chunks_per_block && ++ (dev->gc_dirtiest < 1 || ++ pages_used < dev->gc_pages_in_use) && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ dev->gc_dirtiest = dev->gc_block_finder; ++ dev->gc_pages_in_use = pages_used; ++ } ++ } ++ ++ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) ++ selected = dev->gc_dirtiest; ++ } ++ ++ /* ++ * If nothing has been selected for a while, try the oldest dirty ++ * because that's gumming up the works. ++ */ ++ ++ if (!selected && dev->param.is_yaffs2 && ++ dev->gc_not_done >= (background ? 10 : 20)) { ++ yaffs2_find_oldest_dirty_seq(dev); ++ if (dev->oldest_dirty_block > 0) { ++ selected = dev->oldest_dirty_block; ++ dev->gc_dirtiest = selected; ++ dev->oldest_dirty_gc_count++; ++ bi = yaffs_get_block_info(dev, selected); ++ dev->gc_pages_in_use = ++ bi->pages_in_use - bi->soft_del_pages; ++ } else { ++ dev->gc_not_done = 0; ++ } ++ } ++ ++ if (selected) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC Selected block %d with %d free, prioritised:%d", ++ selected, ++ dev->param.chunks_per_block - dev->gc_pages_in_use, ++ prioritised); ++ ++ dev->n_gc_blocks++; ++ if (background) ++ dev->bg_gcs++; ++ ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ dev->gc_not_done = 0; ++ if (dev->refresh_skip > 0) ++ dev->refresh_skip--; ++ } else { ++ dev->gc_not_done++; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", ++ dev->gc_block_finder, dev->gc_not_done, threshold, ++ dev->gc_dirtiest, dev->gc_pages_in_use, ++ dev->oldest_dirty_block, background ? " bg" : ""); ++ } ++ ++ return selected; ++} ++ ++/* New garbage collector ++ * If we're very low on erased blocks then we do aggressive garbage collection ++ * otherwise we do "leasurely" garbage collection. ++ * Aggressive gc looks further (whole array) and will accept less dirty blocks. ++ * Passive gc only inspects smaller areas and only accepts more dirty blocks. ++ * ++ * The idea is to help clear out space in a more spread-out manner. ++ * Dunno if it really does anything useful. ++ */ ++static int yaffs_check_gc(struct yaffs_dev *dev, int background) ++{ ++ int aggressive = 0; ++ int gc_ok = YAFFS_OK; ++ int max_tries = 0; ++ int min_erased; ++ int erased_chunks; ++ int checkpt_block_adjust; ++ ++ if (dev->param.gc_control_fn && ++ (dev->param.gc_control_fn(dev) & 1) == 0) ++ return YAFFS_OK; ++ ++ if (dev->gc_disable) ++ /* Bail out so we don't get recursive gc */ ++ return YAFFS_OK; ++ ++ /* This loop should pass the first time. ++ * Only loops here if the collection does not increase space. ++ */ ++ ++ do { ++ max_tries++; ++ ++ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); ++ ++ min_erased = ++ dev->param.n_reserved_blocks + checkpt_block_adjust + 1; ++ erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ /* If we need a block soon then do aggressive gc. */ ++ if (dev->n_erased_blocks < min_erased) ++ aggressive = 1; ++ else { ++ if (!background ++ && erased_chunks > (dev->n_free_chunks / 4)) ++ break; ++ ++ if (dev->gc_skip > 20) ++ dev->gc_skip = 20; ++ if (erased_chunks < dev->n_free_chunks / 2 || ++ dev->gc_skip < 1 || background) ++ aggressive = 0; ++ else { ++ dev->gc_skip--; ++ break; ++ } ++ } ++ ++ dev->gc_skip = 5; ++ ++ /* If we don't already have a block being gc'd then see if we ++ * should start another */ ++ ++ if (dev->gc_block < 1 && !aggressive) { ++ dev->gc_block = yaffs2_find_refresh_block(dev); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ if (dev->gc_block < 1) { ++ dev->gc_block = ++ yaffs_find_gc_block(dev, aggressive, background); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ if (dev->gc_block > 0) { ++ dev->all_gcs++; ++ if (!aggressive) ++ dev->passive_gc_count++; ++ ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC n_erased_blocks %d aggressive %d", ++ dev->n_erased_blocks, aggressive); ++ ++ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); ++ } ++ ++ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && ++ dev->gc_block > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", ++ dev->n_erased_blocks, max_tries, ++ dev->gc_block); ++ } ++ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && ++ (dev->gc_block > 0) && (max_tries < 2)); ++ ++ return aggressive ? gc_ok : YAFFS_OK; ++} ++ ++/* ++ * yaffs_bg_gc() ++ * Garbage collects. Intended to be called from a background thread. ++ * Returns non-zero if at least half the free chunks are erased. ++ */ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) ++{ ++ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); ++ ++ yaffs_check_gc(dev, 1); ++ return erased_chunks > dev->n_free_chunks / 2; ++} ++ ++/*-------------------- Data file manipulation -----------------*/ ++ ++static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) ++{ ++ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); ++ ++ if (nand_chunk >= 0) ++ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, ++ buffer, NULL); ++ else { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not found zero instead", ++ nand_chunk); ++ /* get sane (zero) data if you read a hole */ ++ memset(buffer, 0, in->my_dev->data_bytes_per_chunk); ++ return 0; ++ } ++ ++} ++ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn) ++{ ++ int block; ++ int page; ++ struct yaffs_ext_tags tags; ++ struct yaffs_block_info *bi; ++ ++ if (chunk_id <= 0) ++ return; ++ ++ dev->n_deletions++; ++ block = chunk_id / dev->param.chunks_per_block; ++ page = chunk_id % dev->param.chunks_per_block; ++ ++ if (!yaffs_check_chunk_bit(dev, block, page)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Deleting invalid chunk %d", chunk_id); ++ ++ bi = yaffs_get_block_info(dev, block); ++ ++ yaffs2_update_oldest_dirty_seq(dev, block, bi); ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, ++ "line %d delete of chunk %d", ++ lyn, chunk_id); ++ ++ if (!dev->param.is_yaffs2 && mark_flash && ++ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { ++ ++ memset(&tags, 0, sizeof(tags)); ++ tags.is_deleted = 1; ++ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); ++ yaffs_handle_chunk_update(dev, chunk_id, &tags); ++ } else { ++ dev->n_unmarked_deletions++; ++ } ++ ++ /* Pull out of the management area. ++ * If the whole block became dirty, this will kick off an erasure. ++ */ ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || ++ bi->block_state == YAFFS_BLOCK_STATE_FULL || ++ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ dev->n_free_chunks++; ++ yaffs_clear_chunk_bit(dev, block, page); ++ bi->pages_in_use--; ++ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && ++ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ yaffs_block_became_dirty(dev, block); ++ } ++ } ++} ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 *buffer, int n_bytes, int use_reserve) ++{ ++ /* Find old chunk Need to do this to get serial number ++ * Write new one and patch into tree. ++ * Invalidate old tags. ++ */ ++ ++ int prev_chunk_id; ++ struct yaffs_ext_tags prev_tags; ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ yaffs_check_gc(dev, 0); ++ ++ /* Get the previous chunk at this location in the file if it exists. ++ * If it does not exist then put a zero into the tree. This creates ++ * the tnode now, rather than later when it is harder to clean up. ++ */ ++ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); ++ if (prev_chunk_id < 1 && ++ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) ++ return 0; ++ ++ /* Set up new tags */ ++ memset(&new_tags, 0, sizeof(new_tags)); ++ ++ new_tags.chunk_id = inode_chunk; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = ++ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; ++ new_tags.n_bytes = n_bytes; ++ ++ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Writing %d bytes to chunk!!!!!!!!!", ++ n_bytes); ++ BUG(); ++ } ++ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); ++ ++ if (new_chunk_id > 0) { ++ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ yaffs_verify_file_sane(in); ++ } ++ return new_chunk_id; ++ ++} ++ ++ ++ ++static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, ++ const YCHAR *name, const void *value, int size, ++ int flags) ++{ ++ struct yaffs_xattr_mod xmod; ++ int result; ++ ++ xmod.set = set; ++ xmod.name = name; ++ xmod.data = value; ++ xmod.size = size; ++ xmod.flags = flags; ++ xmod.result = -ENOSPC; ++ ++ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); ++ ++ if (result > 0) ++ return xmod.result; ++ else ++ return -ENOSPC; ++} ++ ++static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, ++ struct yaffs_xattr_mod *xmod) ++{ ++ int retval = 0; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ char *x_buffer = buffer + x_offs; ++ ++ if (xmod->set) ++ retval = ++ nval_set(x_buffer, x_size, xmod->name, xmod->data, ++ xmod->size, xmod->flags); ++ else ++ retval = nval_del(x_buffer, x_size, xmod->name); ++ ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ xmod->result = retval; ++ ++ return retval; ++} ++ ++static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, ++ void *value, int size) ++{ ++ char *buffer = NULL; ++ int result; ++ struct yaffs_ext_tags tags; ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ char *x_buffer; ++ int retval = 0; ++ ++ if (obj->hdr_chunk < 1) ++ return -ENODATA; ++ ++ /* If we know that the object has no xattribs then don't do all the ++ * reading and parsing. ++ */ ++ if (obj->xattr_known && !obj->has_xattr) { ++ if (name) ++ return -ENODATA; ++ else ++ return 0; ++ } ++ ++ buffer = (char *)yaffs_get_temp_buffer(dev); ++ if (!buffer) ++ return -ENOMEM; ++ ++ result = ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ retval = -ENOENT; ++ else { ++ x_buffer = buffer + x_offs; ++ ++ if (!obj->xattr_known) { ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ } ++ ++ if (name) ++ retval = nval_get(x_buffer, x_size, name, value, size); ++ else ++ retval = nval_list(x_buffer, x_size, value, size); ++ } ++ yaffs_release_temp_buffer(dev, (u8 *) buffer); ++ return retval; ++} ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, ++ const void *value, int size, int flags) ++{ ++ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); ++} ++ ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); ++} ++ ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, ++ int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, name, value, size); ++} ++ ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); ++} ++ ++static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) ++{ ++ u8 *buf; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ ++ if (!in || !in->lazy_loaded || in->hdr_chunk < 1) ++ return; ++ ++ dev = in->my_dev; ++ in->lazy_loaded = 0; ++ buf = yaffs_get_temp_buffer(dev); ++ ++ result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); ++ oh = (struct yaffs_obj_hdr *)buf; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ yaffs_set_obj_name_from_oh(in, oh); ++ ++ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ in->variant.symlink_variant.alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant.symlink_variant.alias) ++ alloc_failed = 1; /* Not returned */ ++ } ++ yaffs_release_temp_buffer(dev, buf); ++} ++ ++/* UpdateObjectHeader updates the header on NAND for an object. ++ * If name is not NULL, then that new name is used. ++ */ ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, ++ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) ++{ ++ ++ struct yaffs_block_info *bi; ++ struct yaffs_dev *dev = in->my_dev; ++ int prev_chunk_id; ++ int ret_val = 0; ++ int result = 0; ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_ext_tags old_tags; ++ const YCHAR *alias = NULL; ++ u8 *buffer = NULL; ++ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ struct yaffs_obj_hdr *oh = NULL; ++ loff_t file_size = 0; ++ ++ strcpy(old_name, _Y("silly old name")); ++ ++ if (in->fake && in != dev->root_dir && !force && !xmod) ++ return ret_val; ++ ++ yaffs_check_gc(dev, 0); ++ yaffs_check_obj_details_loaded(in); ++ ++ buffer = yaffs_get_temp_buffer(in->my_dev); ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ prev_chunk_id = in->hdr_chunk; ++ ++ if (prev_chunk_id > 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, ++ buffer, &old_tags); ++ ++ yaffs_verify_oh(in, oh, &old_tags, 0); ++ memcpy(old_name, oh->name, sizeof(oh->name)); ++ memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); ++ } else { ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ } ++ ++ oh->type = in->variant_type; ++ oh->yst_mode = in->yst_mode; ++ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; ++ ++ yaffs_load_attribs_oh(oh, in); ++ ++ if (in->parent) ++ oh->parent_obj_id = in->parent->obj_id; ++ else ++ oh->parent_obj_id = 0; ++ ++ if (name && *name) { ++ memset(oh->name, 0, sizeof(oh->name)); ++ yaffs_load_oh_from_name(dev, oh->name, name); ++ } else if (prev_chunk_id > 0) { ++ memcpy(oh->name, old_name, sizeof(oh->name)); ++ } else { ++ memset(oh->name, 0, sizeof(oh->name)); ++ } ++ ++ oh->is_shrink = is_shrink; ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Should not happen */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && ++ oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) ++ file_size = in->variant.file_variant.file_size; ++ yaffs_oh_size_load(oh, file_size); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ oh->equiv_id = in->variant.hardlink_variant.equiv_id; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = in->variant.symlink_variant.alias; ++ if (!alias) ++ alias = _Y("no alias"); ++ strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); ++ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; ++ break; ++ } ++ ++ /* process any xattrib modifications */ ++ if (xmod) ++ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); ++ ++ /* Tags */ ++ memset(&new_tags, 0, sizeof(new_tags)); ++ in->serial++; ++ new_tags.chunk_id = 0; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = in->serial; ++ ++ /* Add extra info for file header */ ++ new_tags.extra_available = 1; ++ new_tags.extra_parent_id = oh->parent_obj_id; ++ new_tags.extra_file_size = file_size; ++ new_tags.extra_is_shrink = oh->is_shrink; ++ new_tags.extra_equiv_id = oh->equiv_id; ++ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; ++ new_tags.extra_obj_type = in->variant_type; ++ yaffs_verify_oh(in, oh, &new_tags, 1); ++ ++ /* Create new chunk in NAND */ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, ++ (prev_chunk_id > 0) ? 1 : 0); ++ ++ if (buffer) ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ if (new_chunk_id < 0) ++ return new_chunk_id; ++ ++ in->hdr_chunk = new_chunk_id; ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ if (!yaffs_obj_cache_dirty(in)) ++ in->dirty = 0; ++ ++ /* If this was a shrink, then mark the block ++ * that the chunk lives on */ ++ if (is_shrink) { ++ bi = yaffs_get_block_info(in->my_dev, ++ new_chunk_id / ++ in->my_dev->param.chunks_per_block); ++ bi->has_shrink_hdr = 1; ++ } ++ ++ ++ return new_chunk_id; ++} ++ ++/*--------------------- File read/write ------------------------ ++ * Read and write have very similar structures. ++ * In general the read/write has three parts to it ++ * An incomplete chunk to start with (if the read/write is not chunk-aligned) ++ * Some complete chunks ++ * An incomplete chunk to end off with ++ * ++ * Curve-balls: the first chunk might also be the last chunk. ++ */ ++ ++int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) ++{ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ struct yaffs_cache *cache; ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ chunk++; ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ if ((start + n) < dev->data_bytes_per_chunk) ++ n_copy = n; ++ else ++ n_copy = dev->data_bytes_per_chunk - start; ++ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ /* If the chunk is already in the cache or it is less than ++ * a whole chunk or we're using inband tags then use the cache ++ * (if there is caching) else bypass the cache. ++ */ ++ if (cache || n_copy != dev->data_bytes_per_chunk || ++ dev->param.inband_tags) { ++ if (dev->param.n_caches > 0) { ++ ++ /* If we can't find the data in the cache, ++ * then load it up. */ ++ ++ if (!cache) { ++ cache = ++ yaffs_grab_chunk_cache(in->my_dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ cache->n_bytes = 0; ++ } ++ ++ yaffs_use_cache(dev, cache, 0); ++ ++ cache->locked = 1; ++ ++ memcpy(buffer, &cache->data[start], n_copy); ++ ++ cache->locked = 0; ++ } else { ++ /* Read into the local buffer then copy.. */ ++ ++ u8 *local_buffer = ++ yaffs_get_temp_buffer(dev); ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ ++ memcpy(buffer, &local_buffer[start], n_copy); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ } else { ++ /* A full chunk. Read directly into the buffer. */ ++ yaffs_rd_data_obj(in, chunk, buffer); ++ } ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ return n_done; ++} ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_through) ++{ ++ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ int n_writeback; ++ loff_t start_write = offset; ++ int chunk_written = 0; ++ u32 n_bytes_read; ++ loff_t chunk_start; ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0 && chunk_written >= 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ ++ if (((loff_t)chunk) * ++ dev->data_bytes_per_chunk + start != offset || ++ start >= dev->data_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "AddrToChunk of offset %lld gives chunk %d start %d", ++ offset, chunk, start); ++ } ++ chunk++; /* File pos to chunk in file offset */ ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ ++ if ((start + n) < dev->data_bytes_per_chunk) { ++ n_copy = n; ++ ++ /* Now calculate how many bytes to write back.... ++ * If we're overwriting and not writing to then end of ++ * file then we need to write back as much as was there ++ * before. ++ */ ++ ++ chunk_start = (((loff_t)(chunk - 1)) * ++ dev->data_bytes_per_chunk); ++ ++ if (chunk_start > in->variant.file_variant.file_size) ++ n_bytes_read = 0; /* Past end of file */ ++ else ++ n_bytes_read = ++ in->variant.file_variant.file_size - ++ chunk_start; ++ ++ if (n_bytes_read > dev->data_bytes_per_chunk) ++ n_bytes_read = dev->data_bytes_per_chunk; ++ ++ n_writeback = ++ (n_bytes_read > ++ (start + n)) ? n_bytes_read : (start + n); ++ ++ if (n_writeback < 0 || ++ n_writeback > dev->data_bytes_per_chunk) ++ BUG(); ++ ++ } else { ++ n_copy = dev->data_bytes_per_chunk - start; ++ n_writeback = dev->data_bytes_per_chunk; ++ } ++ ++ if (n_copy != dev->data_bytes_per_chunk || ++ !dev->param.cache_bypass_aligned || ++ dev->param.inband_tags) { ++ /* An incomplete start or end chunk (or maybe both ++ * start and end chunk), or we're using inband tags, ++ * or we're forcing writes through the cache, ++ * so we want to use the cache buffers. ++ */ ++ if (dev->param.n_caches > 0) { ++ struct yaffs_cache *cache; ++ ++ /* If we can't find the data in the cache, then ++ * load the cache */ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ if (!cache && ++ yaffs_check_alloc_available(dev, 1)) { ++ cache = yaffs_grab_chunk_cache(dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ } else if (cache && ++ !cache->dirty && ++ !yaffs_check_alloc_available(dev, ++ 1)) { ++ /* Drop the cache if it was a read cache ++ * item and no space check has been made ++ * for it. ++ */ ++ cache = NULL; ++ } ++ ++ if (cache) { ++ yaffs_use_cache(dev, cache, 1); ++ cache->locked = 1; ++ ++ memcpy(&cache->data[start], buffer, ++ n_copy); ++ ++ cache->locked = 0; ++ cache->n_bytes = n_writeback; ++ ++ if (write_through) { ++ chunk_written = ++ yaffs_wr_data_obj ++ (cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, 1); ++ cache->dirty = 0; ++ } ++ } else { ++ chunk_written = -1; /* fail write */ ++ } ++ } else { ++ /* An incomplete start or end chunk (or maybe ++ * both start and end chunk). Read into the ++ * local buffer then copy over and write back. ++ */ ++ ++ u8 *local_buffer = yaffs_get_temp_buffer(dev); ++ ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ memcpy(&local_buffer[start], buffer, n_copy); ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, ++ local_buffer, ++ n_writeback, 0); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ } else { ++ /* A full chunk. Write directly from the buffer. */ ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, buffer, ++ dev->data_bytes_per_chunk, 0); ++ ++ /* Since we've overwritten the cached data, ++ * we better invalidate it. */ ++ yaffs_invalidate_chunk_cache(in, chunk); ++ } ++ ++ if (chunk_written >= 0) { ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ } ++ ++ /* Update file object */ ++ ++ if ((start_write + n_done) > in->variant.file_variant.file_size) ++ in->variant.file_variant.file_size = (start_write + n_done); ++ ++ in->dirty = 1; ++ return n_done; ++} ++ ++int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_through) ++{ ++ yaffs2_handle_hole(in, offset); ++ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); ++} ++ ++/* ---------------------- File resizing stuff ------------------ */ ++ ++static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size) ++{ ++ ++ struct yaffs_dev *dev = in->my_dev; ++ loff_t old_size = in->variant.file_variant.file_size; ++ int i; ++ int chunk_id; ++ u32 dummy; ++ int last_del; ++ int start_del; ++ ++ if (old_size > 0) ++ yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); ++ else ++ last_del = 0; ++ ++ yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, ++ &start_del, &dummy); ++ last_del++; ++ start_del++; ++ ++ /* Delete backwards so that we don't end up with holes if ++ * power is lost part-way through the operation. ++ */ ++ for (i = last_del; i >= start_del; i--) { ++ /* NB this could be optimised somewhat, ++ * eg. could retrieve the tags and write them without ++ * using yaffs_chunk_del ++ */ ++ ++ chunk_id = yaffs_find_del_file_chunk(in, i, NULL); ++ ++ if (chunk_id < 1) ++ continue; ++ ++ if (chunk_id < ++ (dev->internal_start_block * dev->param.chunks_per_block) || ++ chunk_id >= ++ ((dev->internal_end_block + 1) * ++ dev->param.chunks_per_block)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Found daft chunk_id %d for %d", ++ chunk_id, i); ++ } else { ++ in->n_data_chunks--; ++ yaffs_chunk_del(dev, chunk_id, 1, __LINE__); ++ } ++ } ++} ++ ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) ++{ ++ int new_full; ++ u32 new_partial; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); ++ ++ yaffs_prune_chunks(obj, new_size); ++ ++ if (new_partial != 0) { ++ int last_chunk = 1 + new_full; ++ u8 *local_buffer = yaffs_get_temp_buffer(dev); ++ ++ /* Rewrite the last chunk with its new size and zero pad */ ++ yaffs_rd_data_obj(obj, last_chunk, local_buffer); ++ memset(local_buffer + new_partial, 0, ++ dev->data_bytes_per_chunk - new_partial); ++ ++ yaffs_wr_data_obj(obj, last_chunk, local_buffer, ++ new_partial, 1); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ ++ obj->variant.file_variant.file_size = new_size; ++ ++ yaffs_prune_tree(dev, &obj->variant.file_variant); ++} ++ ++int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) ++{ ++ struct yaffs_dev *dev = in->my_dev; ++ loff_t old_size = in->variant.file_variant.file_size; ++ ++ yaffs_flush_file_cache(in, 1); ++ yaffs_invalidate_whole_cache(in); ++ ++ yaffs_check_gc(dev, 0); ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ if (new_size == old_size) ++ return YAFFS_OK; ++ ++ if (new_size > old_size) { ++ yaffs2_handle_hole(in, new_size); ++ in->variant.file_variant.file_size = new_size; ++ } else { ++ /* new_size < old_size */ ++ yaffs_resize_file_down(in, new_size); ++ } ++ ++ /* Write a new object header to reflect the resize. ++ * show we've shrunk the file, if need be ++ * Do this only if the file is not in the deleted directories ++ * and is not shadowed. ++ */ ++ if (in->parent && ++ !in->is_shadowed && ++ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ in->parent->obj_id != YAFFS_OBJECTID_DELETED) ++ yaffs_update_oh(in, NULL, 0, 0, 0, NULL); ++ ++ return YAFFS_OK; ++} ++ ++int yaffs_flush_file(struct yaffs_obj *in, ++ int update_time, ++ int data_sync, ++ int discard_cache) ++{ ++ if (!in->dirty) ++ return YAFFS_OK; ++ ++ yaffs_flush_file_cache(in, discard_cache); ++ ++ if (data_sync) ++ return YAFFS_OK; ++ ++ if (update_time) ++ yaffs_load_current_time(in, 0, 0); ++ ++ return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? ++ YAFFS_OK : YAFFS_FAIL; ++} ++ ++ ++/* yaffs_del_file deletes the whole file data ++ * and the inode associated with the file. ++ * It does not delete the links associated with the file. ++ */ ++static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) ++{ ++ int ret_val; ++ int del_now = 0; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!in->my_inode) ++ del_now = 1; ++ ++ if (del_now) { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->del_dir, ++ _Y("deleted"), 0, 0); ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: immediate deletion of file %d", ++ in->obj_id); ++ in->deleted = 1; ++ in->my_dev->n_deleted_files++; ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ yaffs_soft_del_file(in); ++ } else { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++ return ret_val; ++} ++ ++static int yaffs_del_file(struct yaffs_obj *in) ++{ ++ int ret_val = YAFFS_OK; ++ int deleted; /* Need to cache value on stack if in is freed */ ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ ++ if (in->n_data_chunks > 0) { ++ /* Use soft deletion if there is data in the file. ++ * That won't be the case if it has been resized to zero. ++ */ ++ if (!in->unlinked) ++ ret_val = yaffs_unlink_file_if_needed(in); ++ ++ deleted = in->deleted; ++ ++ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { ++ in->deleted = 1; ++ deleted = 1; ++ in->my_dev->n_deleted_files++; ++ yaffs_soft_del_file(in); ++ } ++ return deleted ? YAFFS_OK : YAFFS_FAIL; ++ } else { ++ /* The file has no data chunks so we toss it immediately */ ++ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); ++ in->variant.file_variant.top = NULL; ++ yaffs_generic_obj_del(in); ++ ++ return YAFFS_OK; ++ } ++} ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj) ++{ ++ return (obj && ++ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && ++ !(list_empty(&obj->variant.dir_variant.children)); ++} ++ ++static int yaffs_del_dir(struct yaffs_obj *obj) ++{ ++ /* First check that the directory is empty. */ ++ if (yaffs_is_non_empty_dir(obj)) ++ return YAFFS_FAIL; ++ ++ return yaffs_generic_obj_del(obj); ++} ++ ++static int yaffs_del_symlink(struct yaffs_obj *in) ++{ ++ kfree(in->variant.symlink_variant.alias); ++ in->variant.symlink_variant.alias = NULL; ++ ++ return yaffs_generic_obj_del(in); ++} ++ ++static int yaffs_del_link(struct yaffs_obj *in) ++{ ++ /* remove this hardlink from the list associated with the equivalent ++ * object ++ */ ++ list_del_init(&in->hard_links); ++ return yaffs_generic_obj_del(in); ++} ++ ++int yaffs_del_obj(struct yaffs_obj *obj) ++{ ++ int ret_val = -1; ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ ret_val = yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!list_empty(&obj->variant.dir_variant.dirty)) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Remove object %d from dirty directories", ++ obj->obj_id); ++ list_del_init(&obj->variant.dir_variant.dirty); ++ } ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ ret_val = yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ ret_val = yaffs_del_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ ret_val = yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ ret_val = 0; ++ break; /* should not happen. */ ++ } ++ return ret_val; ++} ++ ++ ++static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir, ++ struct yaffs_obj *to_dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_add_obj_to_dir(to_dir, obj); ++ } ++} ++ ++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, ++ enum yaffs_obj_type type) ++{ ++ /* Tear down the old variant */ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ /* Nuke file data */ ++ yaffs_resize_file(obj, 0); ++ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Put the children in lost and found. */ ++ yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found); ++ if (!list_empty(&obj->variant.dir_variant.dirty)) ++ list_del_init(&obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ /* Nuke symplink data */ ++ kfree(obj->variant.symlink_variant.alias); ++ obj->variant.symlink_variant.alias = NULL; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ list_del_init(&obj->hard_links); ++ break; ++ default: ++ break; ++ } ++ ++ memset(&obj->variant, 0, sizeof(obj->variant)); ++ ++ /*Set up new variant if the memset is not enough. */ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ default: ++ break; ++ } ++ ++ obj->variant_type = type; ++ ++ return obj; ++ ++} ++ ++static int yaffs_unlink_worker(struct yaffs_obj *obj) ++{ ++ int del_now = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (!obj->my_inode) ++ del_now = 1; ++ ++ yaffs_update_parent(obj->parent); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ return yaffs_del_link(obj); ++ } else if (!list_empty(&obj->hard_links)) { ++ /* Curve ball: We're unlinking an object that has a hardlink. ++ * ++ * This problem arises because we are not strictly following ++ * The Linux link/inode model. ++ * ++ * We can't really delete the object. ++ * Instead, we do the following: ++ * - Select a hardlink. ++ * - Unhook it from the hard links ++ * - Move it from its parent directory so that the rename works. ++ * - Rename the object to the hardlink's name. ++ * - Delete the hardlink ++ */ ++ ++ struct yaffs_obj *hl; ++ struct yaffs_obj *parent; ++ int ret_val; ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ hl = list_entry(obj->hard_links.next, struct yaffs_obj, ++ hard_links); ++ ++ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); ++ parent = hl->parent; ++ ++ list_del_init(&hl->hard_links); ++ ++ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); ++ ++ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); ++ ++ if (ret_val == YAFFS_OK) ++ ret_val = yaffs_generic_obj_del(hl); ++ ++ return ret_val; ++ ++ } else if (del_now) { ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ list_del_init(&obj->variant.dir_variant.dirty); ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ return yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ return YAFFS_FAIL; ++ } ++ } else if (yaffs_is_non_empty_dir(obj)) { ++ return YAFFS_FAIL; ++ } else { ++ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++} ++ ++static int yaffs_unlink_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->unlink_allowed) ++ return yaffs_unlink_worker(obj); ++ ++ return YAFFS_FAIL; ++} ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) ++{ ++ struct yaffs_obj *obj; ++ ++ obj = yaffs_find_by_name(dir, name); ++ return yaffs_unlink_obj(obj); ++} ++ ++/* Note: ++ * If old_name is NULL then we take old_dir as the object to be renamed. ++ */ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, ++ struct yaffs_obj *new_dir, const YCHAR *new_name) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *existing_target = NULL; ++ int force = 0; ++ int result; ++ struct yaffs_dev *dev; ++ ++ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ dev = old_dir->my_dev; ++ ++#ifdef CONFIG_YAFFS_CASE_INSENSITIVE ++ /* Special case for case insemsitive systems. ++ * While look-up is case insensitive, the name isn't. ++ * Therefore we might want to change x.txt to X.txt ++ */ ++ if (old_dir == new_dir && ++ old_name && new_name && ++ strcmp(old_name, new_name) == 0) ++ force = 1; ++#endif ++ ++ if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ /* ENAMETOOLONG */ ++ return YAFFS_FAIL; ++ ++ if (old_name) ++ obj = yaffs_find_by_name(old_dir, old_name); ++ else{ ++ obj = old_dir; ++ old_dir = obj->parent; ++ } ++ ++ if (obj && obj->rename_allowed) { ++ /* Now handle an existing target, if there is one */ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ if (yaffs_is_non_empty_dir(existing_target)) { ++ return YAFFS_FAIL; /* ENOTEMPTY */ ++ } else if (existing_target && existing_target != obj) { ++ /* Nuke the target first, using shadowing, ++ * but only if it isn't the same object. ++ * ++ * Note we must disable gc here otherwise it can mess ++ * up the shadowing. ++ * ++ */ ++ dev->gc_disable = 1; ++ yaffs_change_obj_name(obj, new_dir, new_name, force, ++ existing_target->obj_id); ++ existing_target->is_shadowed = 1; ++ yaffs_unlink_obj(existing_target); ++ dev->gc_disable = 0; ++ } ++ ++ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); ++ ++ yaffs_update_parent(old_dir); ++ if (new_dir != old_dir) ++ yaffs_update_parent(new_dir); ++ ++ return result; ++ } ++ return YAFFS_FAIL; ++} ++ ++/*----------------------- Initialisation Scanning ---------------------- */ ++ ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning) ++{ ++ struct yaffs_obj *obj; ++ ++ if (backward_scanning) { ++ /* Handle YAFFS2 case (backward scanning) ++ * If the shadowed object exists then ignore. ++ */ ++ obj = yaffs_find_by_number(dev, obj_id); ++ if (obj) ++ return; ++ } ++ ++ /* Let's create it (if it does not exist) assuming it is a file so that ++ * it can do shrinking etc. ++ * We put it in unlinked dir to be cleaned up after the scanning ++ */ ++ obj = ++ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); ++ if (!obj) ++ return; ++ obj->is_shadowed = 1; ++ yaffs_add_obj_to_dir(dev->unlinked_dir, obj); ++ obj->variant.file_variant.shrink_size = 0; ++ obj->valid = 1; /* So that we don't read any other info. */ ++} ++ ++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) ++{ ++ struct list_head *lh; ++ struct list_head *save; ++ struct yaffs_obj *hl; ++ struct yaffs_obj *in; ++ ++ list_for_each_safe(lh, save, hard_list) { ++ hl = list_entry(lh, struct yaffs_obj, hard_links); ++ in = yaffs_find_by_number(dev, ++ hl->variant.hardlink_variant.equiv_id); ++ ++ if (in) { ++ /* Add the hardlink pointers */ ++ hl->variant.hardlink_variant.equiv_obj = in; ++ list_add(&hl->hard_links, &in->hard_links); ++ } else { ++ /* Todo Need to report/handle this better. ++ * Got a problem... hardlink to a non-existant object ++ */ ++ hl->variant.hardlink_variant.equiv_obj = NULL; ++ INIT_LIST_HEAD(&hl->hard_links); ++ } ++ } ++} ++ ++static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) ++{ ++ /* ++ * Sort out state of unlinked and deleted objects after scanning. ++ */ ++ struct list_head *i; ++ struct list_head *n; ++ struct yaffs_obj *l; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Soft delete all the unlinked files */ ++ list_for_each_safe(i, n, ++ &dev->unlinked_dir->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++ ++ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++} ++ ++/* ++ * This code iterates through all the objects making sure that they are rooted. ++ * Any unrooted objects are re-rooted in lost+found. ++ * An object needs to be in one of: ++ * - Directly under deleted, unlinked ++ * - Directly or indirectly under root. ++ * ++ * Note: ++ * This code assumes that we don't ever change the current relationships ++ * between directories: ++ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL ++ * lost-n-found->parent == root_dir ++ * ++ * This fixes the problem where directories might have inadvertently been ++ * deleted leaving the object "hanging" without being rooted in the ++ * directory tree. ++ */ ++ ++static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ return (obj == dev->del_dir || ++ obj == dev->unlinked_dir || obj == dev->root_dir); ++} ++ ++static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_obj *parent; ++ int i; ++ struct list_head *lh; ++ struct list_head *n; ++ int depth_limit; ++ int hanging; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Iterate through the objects in each hash entry, ++ * looking at each object. ++ * Make sure it is rooted. ++ */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ parent = obj->parent; ++ ++ if (yaffs_has_null_parent(dev, obj)) { ++ /* These directories are not hanging */ ++ hanging = 0; ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ hanging = 1; ++ } else if (yaffs_has_null_parent(dev, parent)) { ++ hanging = 0; ++ } else { ++ /* ++ * Need to follow the parent chain to ++ * see if it is hanging. ++ */ ++ hanging = 0; ++ depth_limit = 100; ++ ++ while (parent != dev->root_dir && ++ parent->parent && ++ parent->parent->variant_type == ++ YAFFS_OBJECT_TYPE_DIRECTORY && ++ depth_limit > 0) { ++ parent = parent->parent; ++ depth_limit--; ++ } ++ if (parent != dev->root_dir) ++ hanging = 1; ++ } ++ if (hanging) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Hanging object %d moved to lost and found", ++ obj->obj_id); ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ } ++ } ++ } ++} ++ ++/* ++ * Delete directory contents for cleaning up lost and found. ++ */ ++static void yaffs_del_dir_contents(struct yaffs_obj *dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ BUG(); ++ ++ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) ++ yaffs_del_dir_contents(obj); ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Deleting lost_found object %d", ++ obj->obj_id); ++ yaffs_unlink_obj(obj); ++ } ++} ++ ++static void yaffs_empty_l_n_f(struct yaffs_dev *dev) ++{ ++ yaffs_del_dir_contents(dev->lost_n_found); ++} ++ ++ ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, ++ const YCHAR *name) ++{ ++ int sum; ++ struct list_head *i; ++ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; ++ struct yaffs_obj *l; ++ ++ if (!name) ++ return NULL; ++ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: null pointer directory" ++ ); ++ BUG(); ++ return NULL; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: non-directory" ++ ); ++ BUG(); ++ } ++ ++ sum = yaffs_calc_name_sum(name); ++ ++ list_for_each(i, &directory->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ ++ if (l->parent != directory) ++ BUG(); ++ ++ yaffs_check_obj_details_loaded(l); ++ ++ /* Special case for lost-n-found */ ++ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) ++ return l; ++ } else if (l->sum == sum || l->hdr_chunk <= 0) { ++ /* LostnFound chunk called Objxxx ++ * Do a real check ++ */ ++ yaffs_get_obj_name(l, buffer, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) ++ return l; ++ } ++ } ++ return NULL; ++} ++ ++/* GetEquivalentObject dereferences any hard links to get to the ++ * actual object. ++ */ ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ obj = obj->variant.hardlink_variant.equiv_obj; ++ yaffs_check_obj_details_loaded(obj); ++ } ++ return obj; ++} ++ ++/* ++ * A note or two on object names. ++ * * If the object name is missing, we then make one up in the form objnnn ++ * ++ * * ASCII names are stored in the object header's name field from byte zero ++ * * Unicode names are historically stored starting from byte zero. ++ * ++ * Then there are automatic Unicode names... ++ * The purpose of these is to save names in a way that can be read as ++ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII ++ * system to share files. ++ * ++ * These automatic unicode are stored slightly differently... ++ * - If the name can fit in the ASCII character space then they are saved as ++ * ascii names as per above. ++ * - If the name needs Unicode then the name is saved in Unicode ++ * starting at oh->name[1]. ++ ++ */ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, ++ int buffer_size) ++{ ++ /* Create an object name if we could not find one. */ ++ if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { ++ YCHAR local_name[20]; ++ YCHAR num_string[20]; ++ YCHAR *x = &num_string[19]; ++ unsigned v = obj->obj_id; ++ num_string[19] = 0; ++ while (v > 0) { ++ x--; ++ *x = '0' + (v % 10); ++ v /= 10; ++ } ++ /* make up a name */ ++ strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); ++ strcat(local_name, x); ++ strncpy(name, local_name, buffer_size - 1); ++ } ++} ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) ++{ ++ memset(name, 0, buffer_size * sizeof(YCHAR)); ++ yaffs_check_obj_details_loaded(obj); ++ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); ++ } else if (obj->short_name[0]) { ++ strcpy(name, obj->short_name); ++ } else if (obj->hdr_chunk > 0) { ++ int result; ++ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); ++ ++ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; ++ ++ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); ++ ++ if (obj->hdr_chunk > 0) { ++ result = yaffs_rd_chunk_tags_nand(obj->my_dev, ++ obj->hdr_chunk, ++ buffer, NULL); ++ } ++ yaffs_load_name_from_oh(obj->my_dev, name, oh->name, ++ buffer_size); ++ ++ yaffs_release_temp_buffer(obj->my_dev, buffer); ++ } ++ ++ yaffs_fix_null_name(obj, name, buffer_size); ++ ++ return strnlen(name, YAFFS_MAX_NAME_LENGTH); ++} ++ ++loff_t yaffs_get_obj_length(struct yaffs_obj *obj) ++{ ++ /* Dereference any hard linking */ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ return obj->variant.file_variant.file_size; ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ if (!obj->variant.symlink_variant.alias) ++ return 0; ++ return strnlen(obj->variant.symlink_variant.alias, ++ YAFFS_MAX_ALIAS_LENGTH); ++ } else { ++ /* Only a directory should drop through to here */ ++ return obj->my_dev->data_bytes_per_chunk; ++ } ++} ++ ++int yaffs_get_obj_link_count(struct yaffs_obj *obj) ++{ ++ int count = 0; ++ struct list_head *i; ++ ++ if (!obj->unlinked) ++ count++; /* the object itself */ ++ ++ list_for_each(i, &obj->hard_links) ++ count++; /* add the hard links; */ ++ ++ return count; ++} ++ ++int yaffs_get_obj_inode(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ return obj->obj_id; ++} ++ ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ return DT_DIR; ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return DT_LNK; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ if (S_ISFIFO(obj->yst_mode)) ++ return DT_FIFO; ++ if (S_ISCHR(obj->yst_mode)) ++ return DT_CHR; ++ if (S_ISBLK(obj->yst_mode)) ++ return DT_BLK; ++ if (S_ISSOCK(obj->yst_mode)) ++ return DT_SOCK; ++ return DT_REG; ++ break; ++ default: ++ return DT_REG; ++ break; ++ } ++} ++ ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) ++ return yaffs_clone_str(obj->variant.symlink_variant.alias); ++ else ++ return yaffs_clone_str(_Y("")); ++} ++ ++/*--------------------------- Initialisation code -------------------------- */ ++ ++static int yaffs_check_dev_fns(struct yaffs_dev *dev) ++{ ++ struct yaffs_driver *drv = &dev->drv; ++ struct yaffs_tags_handler *tagger = &dev->tagger; ++ ++ /* Common functions, gotta have */ ++ if (!drv->drv_read_chunk_fn || ++ !drv->drv_write_chunk_fn || ++ !drv->drv_erase_fn) ++ return 0; ++ ++ if (dev->param.is_yaffs2 && ++ (!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn)) ++ return 0; ++ ++ /* Install the default tags marshalling functions if needed. */ ++ yaffs_tags_compat_install(dev); ++ yaffs_tags_marshall_install(dev); ++ ++ /* Check we now have the marshalling functions required. */ ++ if (!tagger->write_chunk_tags_fn || ++ !tagger->read_chunk_tags_fn || ++ !tagger->query_block_fn || ++ !tagger->mark_bad_fn) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs_create_initial_dir(struct yaffs_dev *dev) ++{ ++ /* Initialise the unlinked, deleted, root and lost+found directories */ ++ dev->lost_n_found = dev->root_dir = NULL; ++ dev->unlinked_dir = dev->del_dir = NULL; ++ dev->unlinked_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); ++ dev->del_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); ++ dev->root_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, ++ YAFFS_ROOT_MODE | S_IFDIR); ++ dev->lost_n_found = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, ++ YAFFS_LOSTNFOUND_MODE | S_IFDIR); ++ ++ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir ++ && dev->del_dir) { ++ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); ++ return YAFFS_OK; ++ } ++ return YAFFS_FAIL; ++} ++ ++/* Low level init. ++ * Typically only used by yaffs_guts_initialise, but also used by the ++ * Low level yaffs driver tests. ++ */ ++ ++int yaffs_guts_ll_init(struct yaffs_dev *dev) ++{ ++ ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()"); ++ ++ if (!dev) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Need a device" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ if (dev->ll_init) ++ return YAFFS_OK; ++ ++ dev->internal_start_block = dev->param.start_block; ++ dev->internal_end_block = dev->param.end_block; ++ dev->block_offset = 0; ++ dev->chunk_offset = 0; ++ dev->n_free_chunks = 0; ++ ++ dev->gc_block = 0; ++ ++ if (dev->param.start_block == 0) { ++ dev->internal_start_block = dev->param.start_block + 1; ++ dev->internal_end_block = dev->param.end_block + 1; ++ dev->block_offset = 1; ++ dev->chunk_offset = dev->param.chunks_per_block; ++ } ++ ++ /* Check geometry parameters. */ ++ ++ if ((!dev->param.inband_tags && dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 1024) || ++ (!dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 512) || ++ (dev->param.inband_tags && !dev->param.is_yaffs2) || ++ dev->param.chunks_per_block < 2 || ++ dev->param.n_reserved_blocks < 2 || ++ dev->internal_start_block <= 0 || ++ dev->internal_end_block <= 0 || ++ dev->internal_end_block <= ++ (dev->internal_start_block + dev->param.n_reserved_blocks + 2) ++ ) { ++ /* otherwise it is too small */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", ++ dev->param.total_bytes_per_chunk, ++ dev->param.is_yaffs2 ? "2" : "", ++ dev->param.inband_tags); ++ return YAFFS_FAIL; ++ } ++ ++ /* Sort out space for inband tags, if required */ ++ if (dev->param.inband_tags) ++ dev->data_bytes_per_chunk = ++ dev->param.total_bytes_per_chunk - ++ sizeof(struct yaffs_packed_tags2_tags_only); ++ else ++ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; ++ ++ /* Got the right mix of functions? */ ++ if (!yaffs_check_dev_fns(dev)) { ++ /* Function missing */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "device function(s) missing or wrong"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ if (yaffs_init_nand(dev) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); ++ return YAFFS_FAIL; ++ } ++ ++ return YAFFS_OK; ++} ++ ++ ++int yaffs_guts_format_dev(struct yaffs_dev *dev) ++{ ++ int i; ++ enum yaffs_block_state state; ++ u32 dummy; ++ ++ if(yaffs_guts_ll_init(dev) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ if(dev->is_mounted) ++ return YAFFS_FAIL; ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ yaffs_query_init_block_state(dev, i, &state, &dummy); ++ if (state != YAFFS_BLOCK_STATE_DEAD) ++ yaffs_erase_block(dev, i); ++ } ++ ++ return YAFFS_OK; ++} ++ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev) ++{ ++ int init_failed = 0; ++ unsigned x; ++ int bits; ++ ++ if(yaffs_guts_ll_init(dev) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ if (dev->is_mounted) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); ++ return YAFFS_FAIL; ++ } ++ ++ dev->is_mounted = 1; ++ ++ /* OK now calculate a few things for the device */ ++ ++ /* ++ * Calculate all the chunk size manipulation numbers: ++ */ ++ x = dev->data_bytes_per_chunk; ++ /* We always use dev->chunk_shift and dev->chunk_div */ ++ dev->chunk_shift = calc_shifts(x); ++ x >>= dev->chunk_shift; ++ dev->chunk_div = x; ++ /* We only use chunk mask if chunk_div is 1 */ ++ dev->chunk_mask = (1 << dev->chunk_shift) - 1; ++ ++ /* ++ * Calculate chunk_grp_bits. ++ * We need to find the next power of 2 > than internal_end_block ++ */ ++ ++ x = dev->param.chunks_per_block * (dev->internal_end_block + 1); ++ ++ bits = calc_shifts_ceiling(x); ++ ++ /* Set up tnode width if wide tnodes are enabled. */ ++ if (!dev->param.wide_tnodes_disabled) { ++ /* bits must be even so that we end up with 32-bit words */ ++ if (bits & 1) ++ bits++; ++ if (bits < 16) ++ dev->tnode_width = 16; ++ else ++ dev->tnode_width = bits; ++ } else { ++ dev->tnode_width = 16; ++ } ++ ++ dev->tnode_mask = (1 << dev->tnode_width) - 1; ++ ++ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), ++ * so if the bitwidth of the ++ * chunk range we're using is greater than 16 we need ++ * to figure out chunk shift and chunk_grp_size ++ */ ++ ++ if (bits <= dev->tnode_width) ++ dev->chunk_grp_bits = 0; ++ else ++ dev->chunk_grp_bits = bits - dev->tnode_width; ++ ++ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; ++ if (dev->tnode_size < sizeof(struct yaffs_tnode)) ++ dev->tnode_size = sizeof(struct yaffs_tnode); ++ ++ dev->chunk_grp_size = 1 << dev->chunk_grp_bits; ++ ++ if (dev->param.chunks_per_block < dev->chunk_grp_size) { ++ /* We have a problem because the soft delete won't work if ++ * the chunk group size > chunks per block. ++ * This can be remedied by using larger "virtual blocks". ++ */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ /* Finished verifying the device, continue with initialisation */ ++ ++ /* More device initialisation */ ++ dev->all_gcs = 0; ++ dev->passive_gc_count = 0; ++ dev->oldest_dirty_gc_count = 0; ++ dev->bg_gcs = 0; ++ dev->gc_block_finder = 0; ++ dev->buffered_block = -1; ++ dev->doing_buffered_block_rewrite = 0; ++ dev->n_deleted_files = 0; ++ dev->n_bg_deletions = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_ecc_fixed = 0; ++ dev->n_ecc_unfixed = 0; ++ dev->n_tags_ecc_fixed = 0; ++ dev->n_tags_ecc_unfixed = 0; ++ dev->n_erase_failures = 0; ++ dev->n_erased_blocks = 0; ++ dev->gc_disable = 0; ++ dev->has_pending_prioritised_gc = 1; ++ /* Assume the worst for now, will get fixed on first GC */ ++ INIT_LIST_HEAD(&dev->dirty_dirs); ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ ++ /* Initialise temporary buffers and caches. */ ++ if (!yaffs_init_tmp_buffers(dev)) ++ init_failed = 1; ++ ++ dev->cache = NULL; ++ dev->gc_cleanup_list = NULL; ++ ++ if (!init_failed && dev->param.n_caches > 0) { ++ int i; ++ void *buf; ++ int cache_bytes = ++ dev->param.n_caches * sizeof(struct yaffs_cache); ++ ++ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) ++ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; ++ ++ dev->cache = kmalloc(cache_bytes, GFP_NOFS); ++ ++ buf = (u8 *) dev->cache; ++ ++ if (dev->cache) ++ memset(dev->cache, 0, cache_bytes); ++ ++ for (i = 0; i < dev->param.n_caches && buf; i++) { ++ dev->cache[i].object = NULL; ++ dev->cache[i].last_use = 0; ++ dev->cache[i].dirty = 0; ++ dev->cache[i].data = buf = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ } ++ if (!buf) ++ init_failed = 1; ++ ++ dev->cache_last_use = 0; ++ } ++ ++ dev->cache_hits = 0; ++ ++ if (!init_failed) { ++ dev->gc_cleanup_list = ++ kmalloc(dev->param.chunks_per_block * sizeof(u32), ++ GFP_NOFS); ++ if (!dev->gc_cleanup_list) ++ init_failed = 1; ++ } ++ ++ if (dev->param.is_yaffs2) ++ dev->param.use_header_file_size = 1; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && dev->param.is_yaffs2 && ++ !dev->param.disable_summary && ++ !yaffs_summary_init(dev)) ++ init_failed = 1; ++ ++ if (!init_failed) { ++ /* Now scan the flash. */ ++ if (dev->param.is_yaffs2) { ++ if (yaffs2_checkpt_restore(dev)) { ++ yaffs_check_obj_details_loaded(dev->root_dir); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | ++ YAFFS_TRACE_MOUNT, ++ "yaffs: restored from checkpoint" ++ ); ++ } else { ++ ++ /* Clean up the mess caused by an aborted ++ * checkpoint load then scan backwards. ++ */ ++ yaffs_deinit_blocks(dev); ++ ++ yaffs_deinit_tnodes_and_objs(dev); ++ ++ dev->n_erased_blocks = 0; ++ dev->n_free_chunks = 0; ++ dev->alloc_block = -1; ++ dev->alloc_page = -1; ++ dev->n_deleted_files = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_bg_deletions = 0; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed ++ && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && !yaffs2_scan_backwards(dev)) ++ init_failed = 1; ++ } ++ } else if (!yaffs1_scan(dev)) { ++ init_failed = 1; ++ } ++ ++ yaffs_strip_deleted_objs(dev); ++ yaffs_fix_hanging_objs(dev); ++ if (dev->param.empty_lost_n_found) ++ yaffs_empty_l_n_f(dev); ++ } ++ ++ if (init_failed) { ++ /* Clean up the mess */ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() aborted."); ++ ++ yaffs_deinitialise(dev); ++ return YAFFS_FAIL; ++ } ++ ++ /* Zero out stats */ ++ dev->n_page_reads = 0; ++ dev->n_page_writes = 0; ++ dev->n_erasures = 0; ++ dev->n_gc_copies = 0; ++ dev->n_retried_writes = 0; ++ ++ dev->n_retired_blocks = 0; ++ ++ yaffs_verify_free_chunks(dev); ++ yaffs_verify_blocks(dev); ++ ++ /* Clean up any aborted checkpoint data */ ++ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() done."); ++ return YAFFS_OK; ++} ++ ++void yaffs_deinitialise(struct yaffs_dev *dev) ++{ ++ if (dev->is_mounted) { ++ int i; ++ ++ yaffs_deinit_blocks(dev); ++ yaffs_deinit_tnodes_and_objs(dev); ++ yaffs_summary_deinit(dev); ++ ++ if (dev->param.n_caches > 0 && dev->cache) { ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ kfree(dev->cache[i].data); ++ dev->cache[i].data = NULL; ++ } ++ ++ kfree(dev->cache); ++ dev->cache = NULL; ++ } ++ ++ kfree(dev->gc_cleanup_list); ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ kfree(dev->temp_buffer[i].buffer); ++ dev->temp_buffer[i].buffer = NULL; ++ } ++ ++ kfree(dev->checkpt_buffer); ++ dev->checkpt_buffer = NULL; ++ kfree(dev->checkpt_block_list); ++ dev->checkpt_block_list = NULL; ++ ++ dev->is_mounted = 0; ++ ++ yaffs_deinit_nand(dev); ++ } ++} ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev) ++{ ++ int n_free = 0; ++ int b; ++ struct yaffs_block_info *blk; ++ ++ blk = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ switch (blk->block_state) { ++ case YAFFS_BLOCK_STATE_EMPTY: ++ case YAFFS_BLOCK_STATE_ALLOCATING: ++ case YAFFS_BLOCK_STATE_COLLECTING: ++ case YAFFS_BLOCK_STATE_FULL: ++ n_free += ++ (dev->param.chunks_per_block - blk->pages_in_use + ++ blk->soft_del_pages); ++ break; ++ default: ++ break; ++ } ++ blk++; ++ } ++ return n_free; ++} ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev) ++{ ++ /* This is what we report to the outside world */ ++ int n_free; ++ int n_dirty_caches; ++ int blocks_for_checkpt; ++ int i; ++ ++ n_free = dev->n_free_chunks; ++ n_free += dev->n_deleted_files; ++ ++ /* Now count and subtract the number of dirty chunks in the cache. */ ++ ++ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].dirty) ++ n_dirty_caches++; ++ } ++ ++ n_free -= n_dirty_caches; ++ ++ n_free -= ++ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); ++ ++ /* Now figure checkpoint space and report that... */ ++ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); ++ ++ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); ++ ++ if (n_free < 0) ++ n_free = 0; ++ ++ return n_free; ++} ++ ++ ++ ++/* ++ * Marshalling functions to get loff_t file sizes into and out of ++ * object headers. ++ */ ++void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) ++{ ++ oh->file_size_low = (fsize & 0xFFFFFFFF); ++ oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); ++} ++ ++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) ++{ ++ loff_t retval; ++ ++ if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) ++ retval = (((loff_t) oh->file_size_high) << 32) | ++ (((loff_t) oh->file_size_low) & 0xFFFFFFFF); ++ else ++ retval = (loff_t) oh->file_size_low; ++ ++ return retval; ++} ++ ++ ++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]) ++{ ++ int i; ++ struct yaffs_block_info *bi; ++ int s; ++ ++ for(i = 0; i < 10; i++) ++ bs[i] = 0; ++ ++ for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ bi = yaffs_get_block_info(dev, i); ++ s = bi->block_state; ++ if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN) ++ bs[0]++; ++ else ++ bs[s]++; ++ } ++} +diff --git a/fs/yaffs2/yaffs_guts.h b/fs/yaffs2/yaffs_guts.h +new file mode 100644 +index 0000000..231f8ac +--- /dev/null ++++ b/fs/yaffs2/yaffs_guts.h +@@ -0,0 +1,1010 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GUTS_H__ ++#define __YAFFS_GUTS_H__ ++ ++#include "yportenv.h" ++ ++#define YAFFS_OK 1 ++#define YAFFS_FAIL 0 ++ ++/* Give us a Y=0x59, ++ * Give us an A=0x41, ++ * Give us an FF=0xff ++ * Give us an S=0x53 ++ * And what have we got... ++ */ ++#define YAFFS_MAGIC 0x5941ff53 ++ ++/* ++ * Tnodes form a tree with the tnodes in "levels" ++ * Levels greater than 0 hold 8 slots which point to other tnodes. ++ * Those at level 0 hold 16 slots which point to chunks in NAND. ++ * ++ * A maximum level of 8 thust supports files of size up to: ++ * ++ * 2^(3*MAX_LEVEL+4) ++ * ++ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives ++ * a maximum file size of around 512Gbytees with 2k chunks. ++ */ ++#define YAFFS_NTNODES_LEVEL0 16 ++#define YAFFS_TNODES_LEVEL0_BITS 4 ++#define YAFFS_TNODES_LEVEL0_MASK 0xf ++ ++#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) ++#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) ++#define YAFFS_TNODES_INTERNAL_MASK 0x7 ++#define YAFFS_TNODES_MAX_LEVEL 8 ++#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \ ++ YAFFS_TNODES_INTERNAL_BITS * \ ++ YAFFS_TNODES_MAX_LEVEL) ++#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1) ++ ++#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff ++ ++/* Constants for YAFFS1 mode */ ++#define YAFFS_BYTES_PER_SPARE 16 ++#define YAFFS_BYTES_PER_CHUNK 512 ++#define YAFFS_CHUNK_SIZE_SHIFT 9 ++#define YAFFS_CHUNKS_PER_BLOCK 32 ++#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) ++ ++#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 ++#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 ++ ++ ++ ++#define YAFFS_ALLOCATION_NOBJECTS 100 ++#define YAFFS_ALLOCATION_NTNODES 100 ++#define YAFFS_ALLOCATION_NLINKS 100 ++ ++#define YAFFS_NOBJECT_BUCKETS 256 ++ ++#define YAFFS_OBJECT_SPACE 0x40000 ++#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) ++ ++/* Binary data version stamps */ ++#define YAFFS_SUMMARY_VERSION 1 ++#define YAFFS_CHECKPOINT_VERSION 7 ++ ++#ifdef CONFIG_YAFFS_UNICODE ++#define YAFFS_MAX_NAME_LENGTH 127 ++#define YAFFS_MAX_ALIAS_LENGTH 79 ++#else ++#define YAFFS_MAX_NAME_LENGTH 255 ++#define YAFFS_MAX_ALIAS_LENGTH 159 ++#endif ++ ++#define YAFFS_SHORT_NAME_LENGTH 15 ++ ++/* Some special object ids for pseudo objects */ ++#define YAFFS_OBJECTID_ROOT 1 ++#define YAFFS_OBJECTID_LOSTNFOUND 2 ++#define YAFFS_OBJECTID_UNLINKED 3 ++#define YAFFS_OBJECTID_DELETED 4 ++ ++/* Fake object Id for summary data */ ++#define YAFFS_OBJECTID_SUMMARY 0x10 ++ ++/* Pseudo object ids for checkpointing */ ++#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 ++#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 ++ ++#define YAFFS_MAX_SHORT_OP_CACHES 20 ++ ++#define YAFFS_N_TEMP_BUFFERS 6 ++ ++/* We limit the number attempts at sucessfully saving a chunk of data. ++ * Small-page devices have 32 pages per block; large-page devices have 64. ++ * Default to something in the order of 5 to 10 blocks worth of chunks. ++ */ ++#define YAFFS_WR_ATTEMPTS (5*64) ++ ++/* Sequence numbers are used in YAFFS2 to determine block allocation order. ++ * The range is limited slightly to help distinguish bad numbers from good. ++ * This also allows us to perhaps in the future use special numbers for ++ * special purposes. ++ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, ++ * and is a larger number than the lifetime of a 2GB device. ++ */ ++#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 ++#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 ++ ++/* Special sequence number for bad block that failed to be marked bad */ ++#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 ++ ++/* ChunkCache is used for short read/write operations.*/ ++struct yaffs_cache { ++ struct yaffs_obj *object; ++ int chunk_id; ++ int last_use; ++ int dirty; ++ int n_bytes; /* Only valid if the cache is dirty */ ++ int locked; /* Can't push out or flush while locked. */ ++ u8 *data; ++}; ++ ++/* yaffs1 tags structures in RAM ++ * NB This uses bitfield. Bitfields should not straddle a u32 boundary ++ * otherwise the structure size will get blown out. ++ */ ++ ++struct yaffs_tags { ++ u32 chunk_id:20; ++ u32 serial_number:2; ++ u32 n_bytes_lsb:10; ++ u32 obj_id:18; ++ u32 ecc:12; ++ u32 n_bytes_msb:2; ++}; ++ ++union yaffs_tags_union { ++ struct yaffs_tags as_tags; ++ u8 as_bytes[8]; ++}; ++ ++ ++/* Stuff used for extended tags in YAFFS2 */ ++ ++enum yaffs_ecc_result { ++ YAFFS_ECC_RESULT_UNKNOWN, ++ YAFFS_ECC_RESULT_NO_ERROR, ++ YAFFS_ECC_RESULT_FIXED, ++ YAFFS_ECC_RESULT_UNFIXED ++}; ++ ++enum yaffs_obj_type { ++ YAFFS_OBJECT_TYPE_UNKNOWN, ++ YAFFS_OBJECT_TYPE_FILE, ++ YAFFS_OBJECT_TYPE_SYMLINK, ++ YAFFS_OBJECT_TYPE_DIRECTORY, ++ YAFFS_OBJECT_TYPE_HARDLINK, ++ YAFFS_OBJECT_TYPE_SPECIAL ++}; ++ ++#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL ++ ++struct yaffs_ext_tags { ++ unsigned chunk_used; /* Status of the chunk: used or unused */ ++ unsigned obj_id; /* If 0 this is not used */ ++ unsigned chunk_id; /* If 0 this is a header, else a data chunk */ ++ unsigned n_bytes; /* Only valid for data chunks */ ++ ++ /* The following stuff only has meaning when we read */ ++ enum yaffs_ecc_result ecc_result; ++ unsigned block_bad; ++ ++ /* YAFFS 1 stuff */ ++ unsigned is_deleted; /* The chunk is marked deleted */ ++ unsigned serial_number; /* Yaffs1 2-bit serial number */ ++ ++ /* YAFFS2 stuff */ ++ unsigned seq_number; /* The sequence number of this block */ ++ ++ /* Extra info if this is an object header (YAFFS2 only) */ ++ ++ unsigned extra_available; /* Extra info available if not zero */ ++ unsigned extra_parent_id; /* The parent object */ ++ unsigned extra_is_shrink; /* Is it a shrink header? */ ++ unsigned extra_shadows; /* Does this shadow another object? */ ++ ++ enum yaffs_obj_type extra_obj_type; /* What object type? */ ++ ++ loff_t extra_file_size; /* Length if it is a file */ ++ unsigned extra_equiv_id; /* Equivalent object for a hard link */ ++}; ++ ++/* Spare structure for YAFFS1 */ ++struct yaffs_spare { ++ u8 tb0; ++ u8 tb1; ++ u8 tb2; ++ u8 tb3; ++ u8 page_status; /* set to 0 to delete the chunk */ ++ u8 block_status; ++ u8 tb4; ++ u8 tb5; ++ u8 ecc1[3]; ++ u8 tb6; ++ u8 tb7; ++ u8 ecc2[3]; ++}; ++ ++/*Special structure for passing through to mtd */ ++struct yaffs_nand_spare { ++ struct yaffs_spare spare; ++ int eccres1; ++ int eccres2; ++}; ++ ++/* Block data in RAM */ ++ ++enum yaffs_block_state { ++ YAFFS_BLOCK_STATE_UNKNOWN = 0, ++ ++ YAFFS_BLOCK_STATE_SCANNING, ++ /* Being scanned */ ++ ++ YAFFS_BLOCK_STATE_NEEDS_SCAN, ++ /* The block might have something on it (ie it is allocating or full, ++ * perhaps empty) but it needs to be scanned to determine its true ++ * state. ++ * This state is only valid during scanning. ++ * NB We tolerate empty because the pre-scanner might be incapable of ++ * deciding ++ * However, if this state is returned on a YAFFS2 device, ++ * then we expect a sequence number ++ */ ++ ++ YAFFS_BLOCK_STATE_EMPTY, ++ /* This block is empty */ ++ ++ YAFFS_BLOCK_STATE_ALLOCATING, ++ /* This block is partially allocated. ++ * At least one page holds valid data. ++ * This is the one currently being used for page ++ * allocation. Should never be more than one of these. ++ * If a block is only partially allocated at mount it is treated as ++ * full. ++ */ ++ ++ YAFFS_BLOCK_STATE_FULL, ++ /* All the pages in this block have been allocated. ++ * If a block was only partially allocated when mounted we treat ++ * it as fully allocated. ++ */ ++ ++ YAFFS_BLOCK_STATE_DIRTY, ++ /* The block was full and now all chunks have been deleted. ++ * Erase me, reuse me. ++ */ ++ ++ YAFFS_BLOCK_STATE_CHECKPOINT, ++ /* This block is assigned to holding checkpoint data. */ ++ ++ YAFFS_BLOCK_STATE_COLLECTING, ++ /* This block is being garbage collected */ ++ ++ YAFFS_BLOCK_STATE_DEAD ++ /* This block has failed and is not in use */ ++}; ++ ++#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) ++ ++struct yaffs_block_info { ++ ++ s32 soft_del_pages:10; /* number of soft deleted pages */ ++ s32 pages_in_use:10; /* number of pages in use */ ++ u32 block_state:4; /* One of the above block states. */ ++ /* NB use unsigned because enum is sometimes ++ * an int */ ++ u32 needs_retiring:1; /* Data has failed on this block, */ ++ /*need to get valid data off and retire*/ ++ u32 skip_erased_check:1;/* Skip the erased check on this block */ ++ u32 gc_prioritise:1; /* An ECC check or blank check has failed. ++ Block should be prioritised for GC */ ++ u32 chunk_error_strikes:3; /* How many times we've had ecc etc ++ failures on this block and tried to reuse it */ ++ u32 has_summary:1; /* The block has a summary */ ++ ++ u32 has_shrink_hdr:1; /* This block has at least one shrink header */ ++ u32 seq_number; /* block sequence number for yaffs2 */ ++ ++}; ++ ++/* -------------------------- Object structure -------------------------------*/ ++/* This is the object structure as stored on NAND */ ++ ++struct yaffs_obj_hdr { ++ enum yaffs_obj_type type; ++ ++ /* Apply to everything */ ++ int parent_obj_id; ++ u16 sum_no_longer_used; /* checksum of name. No longer used */ ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ /* The following apply to all object types except for hard links */ ++ u32 yst_mode; /* protection */ ++ ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++ ++ /* File size applies to files only */ ++ u32 file_size_low; ++ ++ /* Equivalent object id applies to hard links only. */ ++ int equiv_id; ++ ++ /* Alias is for symlinks only. */ ++ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; ++ ++ u32 yst_rdev; /* stuff for block and char devices (major/min) */ ++ ++ u32 win_ctime[2]; ++ u32 win_atime[2]; ++ u32 win_mtime[2]; ++ ++ u32 inband_shadowed_obj_id; ++ u32 inband_is_shrink; ++ ++ u32 file_size_high; ++ u32 reserved[1]; ++ int shadows_obj; /* This object header shadows the ++ specified object if > 0 */ ++ ++ /* is_shrink applies to object headers written when wemake a hole. */ ++ u32 is_shrink; ++ ++}; ++ ++/*--------------------------- Tnode -------------------------- */ ++ ++struct yaffs_tnode { ++ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; ++}; ++ ++/*------------------------ Object -----------------------------*/ ++/* An object can be one of: ++ * - a directory (no data, has children links ++ * - a regular file (data.... not prunes :->). ++ * - a symlink [symbolic link] (the alias). ++ * - a hard link ++ */ ++ ++struct yaffs_file_var { ++ loff_t file_size; ++ loff_t scanned_size; ++ loff_t shrink_size; ++ int top_level; ++ struct yaffs_tnode *top; ++}; ++ ++struct yaffs_dir_var { ++ struct list_head children; /* list of child links */ ++ struct list_head dirty; /* Entry for list of dirty directories */ ++}; ++ ++struct yaffs_symlink_var { ++ YCHAR *alias; ++}; ++ ++struct yaffs_hardlink_var { ++ struct yaffs_obj *equiv_obj; ++ u32 equiv_id; ++}; ++ ++union yaffs_obj_var { ++ struct yaffs_file_var file_variant; ++ struct yaffs_dir_var dir_variant; ++ struct yaffs_symlink_var symlink_variant; ++ struct yaffs_hardlink_var hardlink_variant; ++}; ++ ++struct yaffs_obj { ++ u8 deleted:1; /* This should only apply to unlinked files. */ ++ u8 soft_del:1; /* it has also been soft deleted */ ++ u8 unlinked:1; /* An unlinked file.*/ ++ u8 fake:1; /* A fake object has no presence on NAND. */ ++ u8 rename_allowed:1; /* Some objects cannot be renamed. */ ++ u8 unlink_allowed:1; ++ u8 dirty:1; /* the object needs to be written to flash */ ++ u8 valid:1; /* When the file system is being loaded up, this ++ * object might be created before the data ++ * is available ++ * ie. file data chunks encountered before ++ * the header. ++ */ ++ u8 lazy_loaded:1; /* This object has been lazy loaded and ++ * is missing some detail */ ++ ++ u8 defered_free:1; /* Object is removed from NAND, but is ++ * still in the inode cache. ++ * Free of object is defered. ++ * until the inode is released. ++ */ ++ u8 being_created:1; /* This object is still being created ++ * so skip some verification checks. */ ++ u8 is_shadowed:1; /* This object is shadowed on the way ++ * to being renamed. */ ++ ++ u8 xattr_known:1; /* We know if this has object has xattribs ++ * or not. */ ++ u8 has_xattr:1; /* This object has xattribs. ++ * Only valid if xattr_known. */ ++ ++ u8 serial; /* serial number of chunk in NAND.*/ ++ u16 sum; /* sum of the name to speed searching */ ++ ++ struct yaffs_dev *my_dev; /* The device I'm on */ ++ ++ struct list_head hash_link; /* list of objects in hash bucket */ ++ ++ struct list_head hard_links; /* hard linked object chain*/ ++ ++ /* directory structure stuff */ ++ /* also used for linking up the free list */ ++ struct yaffs_obj *parent; ++ struct list_head siblings; ++ ++ /* Where's my object header in NAND? */ ++ int hdr_chunk; ++ ++ int n_data_chunks; /* Number of data chunks for this file. */ ++ ++ u32 obj_id; /* the object id value */ ++ ++ u32 yst_mode; ++ ++ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; ++ ++#ifdef CONFIG_YAFFS_WINCE ++ u32 win_ctime[2]; ++ u32 win_mtime[2]; ++ u32 win_atime[2]; ++#else ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++#endif ++ ++ u32 yst_rdev; ++ ++ void *my_inode; ++ ++ enum yaffs_obj_type variant_type; ++ ++ union yaffs_obj_var variant; ++ ++}; ++ ++struct yaffs_obj_bucket { ++ struct list_head list; ++ int count; ++}; ++ ++/* yaffs_checkpt_obj holds the definition of an object as dumped ++ * by checkpointing. ++ */ ++ ++struct yaffs_checkpt_obj { ++ int struct_type; ++ u32 obj_id; ++ u32 parent_id; ++ int hdr_chunk; ++ enum yaffs_obj_type variant_type:3; ++ u8 deleted:1; ++ u8 soft_del:1; ++ u8 unlinked:1; ++ u8 fake:1; ++ u8 rename_allowed:1; ++ u8 unlink_allowed:1; ++ u8 serial; ++ int n_data_chunks; ++ loff_t size_or_equiv_obj; ++}; ++ ++/*--------------------- Temporary buffers ---------------- ++ * ++ * These are chunk-sized working buffers. Each device has a few. ++ */ ++ ++struct yaffs_buffer { ++ u8 *buffer; ++ int in_use; ++}; ++ ++/*----------------- Device ---------------------------------*/ ++ ++struct yaffs_param { ++ const YCHAR *name; ++ ++ /* ++ * Entry parameters set up way early. Yaffs sets up the rest. ++ * The structure should be zeroed out before use so that unused ++ * and default values are zero. ++ */ ++ ++ int inband_tags; /* Use unband tags */ ++ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to ++ be a power of 2 */ ++ int chunks_per_block; /* does not need to be a power of 2 */ ++ int spare_bytes_per_chunk; /* spare area size */ ++ int start_block; /* Start block we're allowed to use */ ++ int end_block; /* End block we're allowed to use */ ++ int n_reserved_blocks; /* Tuneable so that we can reduce ++ * reserved blocks on NOR and RAM. */ ++ ++ int n_caches; /* If <= 0, then short op caching is disabled, ++ * else the number of short op caches. ++ */ ++ int cache_bypass_aligned; /* If non-zero then bypass the cache for ++ * aligned writes. ++ */ ++ ++ int use_nand_ecc; /* Flag to decide whether or not to use ++ * NAND driver ECC on data (yaffs1) */ ++ int tags_9bytes; /* Use 9 byte tags */ ++ int no_tags_ecc; /* Flag to decide whether or not to do ECC ++ * on packed tags (yaffs2) */ ++ ++ int is_yaffs2; /* Use yaffs2 mode on this device */ ++ ++ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ ++ ++ int refresh_period; /* How often to check for a block refresh */ ++ ++ /* Checkpoint control. Can be set before or after initialisation */ ++ u8 skip_checkpt_rd; ++ u8 skip_checkpt_wr; ++ ++ int enable_xattr; /* Enable xattribs */ ++ ++ int max_objects; /* ++ * Set to limit the number of objects created. ++ * 0 = no limit. ++ */ ++ ++ /* The remove_obj_fn function must be supplied by OS flavours that ++ * need it. ++ * yaffs direct uses it to implement the faster readdir. ++ * Linux uses it to protect the directory during unlocking. ++ */ ++ void (*remove_obj_fn) (struct yaffs_obj *obj); ++ ++ /* Callback to mark the superblock dirty */ ++ void (*sb_dirty_fn) (struct yaffs_dev *dev); ++ ++ /* Callback to control garbage collection. */ ++ unsigned (*gc_control_fn) (struct yaffs_dev *dev); ++ ++ /* Debug control flags. Don't use unless you know what you're doing */ ++ int use_header_file_size; /* Flag to determine if we should use ++ * file sizes from the header */ ++ int disable_lazy_load; /* Disable lazy loading on this device */ ++ int wide_tnodes_disabled; /* Set to disable wide tnodes */ ++ int disable_soft_del; /* yaffs 1 only: Set to disable the use of ++ * softdeletion. */ ++ ++ int defered_dir_update; /* Set to defer directory updates */ ++ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ int auto_unicode; ++#endif ++ int always_check_erased; /* Force chunk erased check always on */ ++ ++ int disable_summary; ++ int disable_bad_block_marking; ++ ++}; ++ ++struct yaffs_driver { ++ int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, int data_len, ++ const u8 *oob, int oob_len); ++ int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, ++ u8 *data, int data_len, ++ u8 *oob, int oob_len, ++ enum yaffs_ecc_result *ecc_result); ++ int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_initialise_fn) (struct yaffs_dev *dev); ++ int (*drv_deinitialise_fn) (struct yaffs_dev *dev); ++}; ++ ++struct yaffs_tags_handler { ++ int (*write_chunk_tags_fn) (struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ const struct yaffs_ext_tags *tags); ++ int (*read_chunk_tags_fn) (struct yaffs_dev *dev, ++ int nand_chunk, u8 *data, ++ struct yaffs_ext_tags *tags); ++ ++ int (*query_block_fn) (struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number); ++ int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no); ++}; ++ ++struct yaffs_dev { ++ struct yaffs_param param; ++ struct yaffs_driver drv; ++ struct yaffs_tags_handler tagger; ++ ++ /* Context storage. Holds extra OS specific data for this device */ ++ ++ void *os_context; ++ void *driver_context; ++ ++ struct list_head dev_list; ++ ++ int ll_init; ++ /* Runtime parameters. Set up by YAFFS. */ ++ int data_bytes_per_chunk; ++ ++ /* Non-wide tnode stuff */ ++ u16 chunk_grp_bits; /* Number of bits that need to be resolved if ++ * the tnodes are not wide enough. ++ */ ++ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ ++ ++ /* Stuff to support wide tnodes */ ++ u32 tnode_width; ++ u32 tnode_mask; ++ u32 tnode_size; ++ ++ /* Stuff for figuring out file offset to chunk conversions */ ++ u32 chunk_shift; /* Shift value */ ++ u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ ++ u32 chunk_mask; /* Mask to use for power-of-2 case */ ++ ++ int is_mounted; ++ int read_only; ++ int is_checkpointed; ++ ++ /* Stuff to support block offsetting to support start block zero */ ++ int internal_start_block; ++ int internal_end_block; ++ int block_offset; ++ int chunk_offset; ++ ++ /* Runtime checkpointing stuff */ ++ int checkpt_page_seq; /* running sequence number of checkpt pages */ ++ int checkpt_byte_count; ++ int checkpt_byte_offs; ++ u8 *checkpt_buffer; ++ int checkpt_open_write; ++ int blocks_in_checkpt; ++ int checkpt_cur_chunk; ++ int checkpt_cur_block; ++ int checkpt_next_block; ++ int *checkpt_block_list; ++ int checkpt_max_blocks; ++ u32 checkpt_sum; ++ u32 checkpt_xor; ++ ++ int checkpoint_blocks_required; /* Number of blocks needed to store ++ * current checkpoint set */ ++ ++ /* Block Info */ ++ struct yaffs_block_info *block_info; ++ u8 *chunk_bits; /* bitmap of chunks in use */ ++ u8 block_info_alt:1; /* allocated using alternative alloc */ ++ u8 chunk_bits_alt:1; /* allocated using alternative alloc */ ++ int chunk_bit_stride; /* Number of bytes of chunk_bits per block. ++ * Must be consistent with chunks_per_block. ++ */ ++ ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int alloc_block_finder; /* Used to search for next allocation block */ ++ ++ /* Object and Tnode memory management */ ++ void *allocator; ++ int n_obj; ++ int n_tnodes; ++ ++ int n_hardlinks; ++ ++ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; ++ u32 bucket_finder; ++ ++ int n_free_chunks; ++ ++ /* Garbage collection control */ ++ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ ++ u32 n_clean_ups; ++ ++ unsigned has_pending_prioritised_gc; /* We think this device might ++ have pending prioritised gcs */ ++ unsigned gc_disable; ++ unsigned gc_block_finder; ++ unsigned gc_dirtiest; ++ unsigned gc_pages_in_use; ++ unsigned gc_not_done; ++ unsigned gc_block; ++ unsigned gc_chunk; ++ unsigned gc_skip; ++ struct yaffs_summary_tags *gc_sum_tags; ++ ++ /* Special directories */ ++ struct yaffs_obj *root_dir; ++ struct yaffs_obj *lost_n_found; ++ ++ int buffered_block; /* Which block is buffered here? */ ++ int doing_buffered_block_rewrite; ++ ++ struct yaffs_cache *cache; ++ int cache_last_use; ++ ++ /* Stuff for background deletion and unlinked files. */ ++ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted ++ files live. */ ++ struct yaffs_obj *del_dir; /* Directory where deleted objects are ++ sent to disappear. */ ++ struct yaffs_obj *unlinked_deletion; /* Current file being ++ background deleted. */ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* Temporary buffer management */ ++ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; ++ int max_temp; ++ int temp_in_use; ++ int unmanaged_buffer_allocs; ++ int unmanaged_buffer_deallocs; ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently ++ allocating block */ ++ unsigned oldest_dirty_seq; ++ unsigned oldest_dirty_block; ++ ++ /* Block refreshing */ ++ int refresh_skip; /* A skip down counter. ++ * Refresh happens when this gets to zero. */ ++ ++ /* Dirty directory handling */ ++ struct list_head dirty_dirs; /* List of dirty directories */ ++ ++ /* Summary */ ++ int chunks_per_summary; ++ struct yaffs_summary_tags *sum_tags; ++ ++ /* Statistics */ ++ u32 n_page_writes; ++ u32 n_page_reads; ++ u32 n_erasures; ++ u32 n_bad_queries; ++ u32 n_bad_markings; ++ u32 n_erase_failures; ++ u32 n_gc_copies; ++ u32 all_gcs; ++ u32 passive_gc_count; ++ u32 oldest_dirty_gc_count; ++ u32 n_gc_blocks; ++ u32 bg_gcs; ++ u32 n_retried_writes; ++ u32 n_retired_blocks; ++ u32 n_ecc_fixed; ++ u32 n_ecc_unfixed; ++ u32 n_tags_ecc_fixed; ++ u32 n_tags_ecc_unfixed; ++ u32 n_deletions; ++ u32 n_unmarked_deletions; ++ u32 refresh_count; ++ u32 cache_hits; ++ u32 tags_used; ++ u32 summary_used; ++ ++}; ++ ++/* The CheckpointDevice structure holds the device information that changes ++ *at runtime and must be preserved over unmount/mount cycles. ++ */ ++struct yaffs_checkpt_dev { ++ int struct_type; ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int n_free_chunks; ++ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently ++ * allocating block */ ++ ++}; ++ ++struct yaffs_checkpt_validity { ++ int struct_type; ++ u32 magic; ++ u32 version; ++ u32 head; ++}; ++ ++struct yaffs_shadow_fixer { ++ int obj_id; ++ int shadowed_id; ++ struct yaffs_shadow_fixer *next; ++}; ++ ++/* Structure for doing xattr modifications */ ++struct yaffs_xattr_mod { ++ int set; /* If 0 then this is a deletion */ ++ const YCHAR *name; ++ const void *data; ++ int size; ++ int flags; ++ int result; ++}; ++ ++/*----------------------- YAFFS Functions -----------------------*/ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev); ++void yaffs_deinitialise(struct yaffs_dev *dev); ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, ++ struct yaffs_obj *new_dir, const YCHAR * new_name); ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); ++int yaffs_del_obj(struct yaffs_obj *obj); ++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, ++ enum yaffs_obj_type type); ++ ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); ++loff_t yaffs_get_obj_length(struct yaffs_obj *obj); ++int yaffs_get_obj_inode(struct yaffs_obj *obj); ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj); ++int yaffs_get_obj_link_count(struct yaffs_obj *obj); ++ ++/* File operations */ ++int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, ++ int n_bytes); ++int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid); ++ ++int yaffs_flush_file(struct yaffs_obj *in, ++ int update_time, ++ int data_sync, ++ int discard_cache); ++ ++/* Flushing and checkpointing */ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard); ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev); ++int yaffs_checkpoint_restore(struct yaffs_dev *dev); ++ ++/* Directory operations */ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, ++ u32 mode, u32 uid, u32 gid); ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, ++ const YCHAR *name); ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); ++ ++/* Link operations */ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, ++ struct yaffs_obj *equiv_obj); ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); ++ ++/* Symlink operations */ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, const YCHAR *alias); ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); ++ ++/* Special inodes (fifos, sockets and devices) */ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, u32 rdev); ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, ++ const void *value, int size, int flags); ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, ++ int size); ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); ++ ++/* Special directories */ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj); ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev); ++ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); ++ ++/* Debug dump */ ++int yaffs_dump_obj(struct yaffs_obj *obj); ++ ++void yaffs_guts_test(struct yaffs_dev *dev); ++int yaffs_guts_ll_init(struct yaffs_dev *dev); ++ ++ ++/* A few useful functions to be used within the core files*/ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn); ++int yaffs_check_ff(u8 *buffer, int n_bytes); ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type); ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan); ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh); ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); ++YCHAR *yaffs_clone_str(const YCHAR *str); ++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, ++ int force, int is_shrink, int shadows, ++ struct yaffs_xattr_mod *xop); ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning); ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn); ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev); ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id); ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos); ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj); ++ ++int yaffs_guts_format_dev(struct yaffs_dev *dev); ++ ++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 *offset_out); ++/* ++ * Marshalling functions to get loff_t file sizes into aand out of ++ * object headers. ++ */ ++void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize); ++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh); ++loff_t yaffs_max_file_size(struct yaffs_dev *dev); ++ ++/* ++ * Debug function to count number of blocks in each state ++ * NB Needs to be called with correct number of integers ++ */ ++ ++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]); ++ ++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_linux.h b/fs/yaffs2/yaffs_linux.h +new file mode 100644 +index 0000000..c20ab14 +--- /dev/null ++++ b/fs/yaffs2/yaffs_linux.h +@@ -0,0 +1,48 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_LINUX_H__ ++#define __YAFFS_LINUX_H__ ++ ++#include "yportenv.h" ++ ++struct yaffs_linux_context { ++ struct list_head context_list; /* List of these we have mounted */ ++ struct yaffs_dev *dev; ++ struct super_block *super; ++ struct task_struct *bg_thread; /* Background thread for this device */ ++ int bg_running; ++ struct mutex gross_lock; /* Gross locking mutex*/ ++ u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size ++ * at compile time so we have to allocate it. ++ */ ++ struct list_head search_contexts; ++ struct task_struct *readdir_process; ++ unsigned mount_id; ++ int dirty; ++}; ++ ++#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) ++#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++#define WRITE_SIZE_STR "writesize" ++#define WRITE_SIZE(mtd) ((mtd)->writesize) ++#else ++#define WRITE_SIZE_STR "oobblock" ++#define WRITE_SIZE(mtd) ((mtd)->oobblock) ++#endif ++ ++#endif +diff --git a/fs/yaffs2/yaffs_mtdif.c b/fs/yaffs2/yaffs_mtdif.c +new file mode 100644 +index 0000000..1858b4f +--- /dev/null ++++ b/fs/yaffs2/yaffs_mtdif.c +@@ -0,0 +1,310 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_mtdif.h" ++ ++#include "linux/mtd/mtd.h" ++#include "linux/types.h" ++#include "linux/time.h" ++#include "linux/mtd/nand.h" ++#include "linux/kernel.h" ++#include "linux/version.h" ++#include "linux/types.h" ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) ++#include "uapi/linux/major.h" ++#endif ++ ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_linux.h" ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) ++#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) ++#define mtd_erase(m, ei) (m)->erase(m, ei) ++#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) ++#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) ++#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) ++#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) ++#endif ++ ++ ++ ++int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ u32 addr = ++ ((loff_t) block_no) * dev->param.total_bytes_per_chunk * ++ dev->param.chunks_per_block; ++ struct erase_info ei; ++ int retval = 0; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd_erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++ ++static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, int data_len, ++ const u8 *oob, int oob_len) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ loff_t addr; ++ struct mtd_oob_ops ops; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", ++ dev, nand_chunk, data, data_len, oob, oob_len); ++ ++ if (!data || !data_len) { ++ data = NULL; ++ data_len = 0; ++ } ++ ++ if (!oob || !oob_len) { ++ oob = NULL; ++ oob_len = 0; ++ } ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.len = (data) ? data_len : 0; ++ ops.ooblen = oob_len; ++ ops.datbuf = (u8 *)data; ++ ops.oobbuf = (u8 *)oob; ++ ++ retval = mtd_write_oob(mtd, addr, &ops); ++ if (retval) { ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "write_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ } ++ return retval ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, ++ u8 *data, int data_len, ++ u8 *oob, int oob_len, ++ enum yaffs_ecc_result *ecc_result) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ loff_t addr; ++ struct mtd_oob_ops ops; ++ int retval; ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.len = (data) ? data_len : 0; ++ ops.ooblen = oob_len; ++ ops.datbuf = data; ++ ops.oobbuf = oob; ++ ++#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) ++ /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; ++ * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. ++ */ ++ ops.len = (ops.datbuf) ? ops.len : ops.ooblen; ++#endif ++ /* Read page and oob using MTD. ++ * Check status and determine ECC result. ++ */ ++ retval = mtd_read_oob(mtd, addr, &ops); ++ if (retval) ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "read_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ ++ switch (retval) { ++ case 0: ++ /* no error */ ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ ++ case -EUCLEAN: ++ /* MTD's ECC fixed the data */ ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ break; ++ ++ case -EBADMSG: ++ default: ++ /* MTD's ECC could not fix the data */ ++ dev->n_ecc_unfixed++; ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ return YAFFS_FAIL; ++ } ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ ++ loff_t addr; ++ struct erase_info ei; ++ int retval = 0; ++ u32 block_size; ++ ++ block_size = dev->param.total_bytes_per_chunk * ++ dev->param.chunks_per_block; ++ addr = ((loff_t) block_no) * block_size; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = block_size; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd_erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); ++ ++ retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); ++ ++ retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_initialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} ++ ++static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} ++ ++ ++void yaffs_mtd_drv_install(struct yaffs_dev *dev) ++{ ++ struct yaffs_driver *drv = &dev->drv; ++ ++ drv->drv_write_chunk_fn = yaffs_mtd_write; ++ drv->drv_read_chunk_fn = yaffs_mtd_read; ++ drv->drv_erase_fn = yaffs_mtd_erase; ++ drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; ++ drv->drv_check_bad_fn = yaffs_mtd_check_bad; ++ drv->drv_initialise_fn = yaffs_mtd_initialise; ++ drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; ++} ++ ++ ++struct mtd_info * yaffs_get_mtd_device(dev_t sdev) ++{ ++ struct mtd_info *mtd; ++ ++ mtd = yaffs_get_mtd_device(sdev); ++ ++ /* Check it's an mtd device..... */ ++ if (MAJOR(sdev) != MTD_BLOCK_MAJOR) ++ return NULL; /* This isn't an mtd device */ ++ ++ /* Check it's NAND */ ++ if (mtd->type != MTD_NANDFLASH) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: MTD device is not NAND it's type %d", ++ mtd->type); ++ return NULL; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); ++ yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); ++ yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ++ yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); ++#else ++ yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); ++#endif ++ ++ return mtd; ++} ++ ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) ++{ ++ if (yaffs_version == 2) { ++ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || ++ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && ++ !inband_tags) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not have the right page sizes" ++ ); ++ return -1; ++ } ++ } else { ++ if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || ++ mtd->oobsize != YAFFS_BYTES_PER_SPARE) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not support have the right page sizes" ++ ); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++void yaffs_put_mtd_device(struct mtd_info *mtd) ++{ ++ if(mtd) ++ put_mtd_device(mtd); ++} +diff --git a/fs/yaffs2/yaffs_mtdif.h b/fs/yaffs2/yaffs_mtdif.h +new file mode 100644 +index 0000000..9cff224 +--- /dev/null ++++ b/fs/yaffs2/yaffs_mtdif.h +@@ -0,0 +1,25 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_MTDIF_H__ ++#define __YAFFS_MTDIF_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_mtd_drv_install(struct yaffs_dev *dev); ++struct mtd_info * yaffs_get_mtd_device(dev_t sdev); ++void yaffs_put_mtd_device(struct mtd_info *mtd); ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); ++#endif +diff --git a/fs/yaffs2/yaffs_nameval.c b/fs/yaffs2/yaffs_nameval.c +new file mode 100644 +index 0000000..4bdf4ed +--- /dev/null ++++ b/fs/yaffs2/yaffs_nameval.c +@@ -0,0 +1,208 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This simple implementation of a name-value store assumes a small number of ++* values and fits into a small finite buffer. ++ * ++ * Each attribute is stored as a record: ++ * sizeof(int) bytes record size. ++ * strnlen+1 bytes name null terminated. ++ * nbytes value. ++ * ---------- ++ * total size stored in record size ++ * ++ * This code has not been tested with unicode yet. ++ */ ++ ++#include "yaffs_nameval.h" ++ ++#include "yportenv.h" ++ ++static int nval_find(const char *xb, int xb_size, const YCHAR *name, ++ int *exist_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), ++ name, size)) { ++ if (exist_size) ++ *exist_size = size; ++ return pos; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ if (exist_size) ++ *exist_size = 0; ++ return -ENODATA; ++} ++ ++static int nval_used(const char *xb, int xb_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return pos; ++} ++ ++int nval_del(char *xb, int xb_size, const YCHAR *name) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos < 0 || pos >= xb_size) ++ return -ENODATA; ++ ++ /* Find size, shift rest over this record, ++ * then zero out the rest of buffer */ ++ memcpy(&size, xb + pos, sizeof(int)); ++ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); ++ memset(xb + (xb_size - size), 0, size); ++ return 0; ++} ++ ++int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, ++ int bsize, int flags) ++{ ++ int pos; ++ int namelen = strnlen(name, xb_size); ++ int reclen; ++ int size_exist = 0; ++ int space; ++ int start; ++ ++ pos = nval_find(xb, xb_size, name, &size_exist); ++ ++ if (flags & XATTR_CREATE && pos >= 0) ++ return -EEXIST; ++ if (flags & XATTR_REPLACE && pos < 0) ++ return -ENODATA; ++ ++ start = nval_used(xb, xb_size); ++ space = xb_size - start + size_exist; ++ ++ reclen = (sizeof(int) + namelen + 1 + bsize); ++ ++ if (reclen > space) ++ return -ENOSPC; ++ ++ if (pos >= 0) { ++ nval_del(xb, xb_size, name); ++ start = nval_used(xb, xb_size); ++ } ++ ++ pos = start; ++ ++ memcpy(xb + pos, &reclen, sizeof(int)); ++ pos += sizeof(int); ++ strncpy((YCHAR *) (xb + pos), name, reclen); ++ pos += (namelen + 1); ++ memcpy(xb + pos, buf, bsize); ++ return 0; ++} ++ ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos >= 0 && pos < xb_size) { ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ pos += sizeof(int); /* advance past record length */ ++ size -= sizeof(int); ++ ++ /* Advance over name string */ ++ while (xb[pos] && size > 0 && pos < xb_size) { ++ pos++; ++ size--; ++ } ++ /*Advance over NUL */ ++ pos++; ++ size--; ++ ++ /* If bsize is zero then this is a size query. ++ * Return the size, but don't copy. ++ */ ++ if (!bsize) ++ return size; ++ ++ if (size <= bsize) { ++ memcpy(buf, xb + pos, size); ++ return size; ++ } ++ } ++ if (pos >= 0) ++ return -ERANGE; ++ ++ return -ENODATA; ++} ++ ++int nval_list(const char *xb, int xb_size, char *buf, int bsize) ++{ ++ int pos = 0; ++ int size; ++ int name_len; ++ int ncopied = 0; ++ int filled = 0; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > sizeof(int) && ++ size <= xb_size && ++ (pos + size) < xb_size && ++ !filled) { ++ pos += sizeof(int); ++ size -= sizeof(int); ++ name_len = strnlen((YCHAR *) (xb + pos), size); ++ if (ncopied + name_len + 1 < bsize) { ++ memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); ++ buf += name_len; ++ *buf = '\0'; ++ buf++; ++ if (sizeof(YCHAR) > 1) { ++ *buf = '\0'; ++ buf++; ++ } ++ ncopied += (name_len + 1); ++ } else { ++ filled = 1; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return ncopied; ++} ++ ++int nval_hasvalues(const char *xb, int xb_size) ++{ ++ return nval_used(xb, xb_size) > 0; ++} +diff --git a/fs/yaffs2/yaffs_nameval.h b/fs/yaffs2/yaffs_nameval.h +new file mode 100644 +index 0000000..951e64f +--- /dev/null ++++ b/fs/yaffs2/yaffs_nameval.h +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __NAMEVAL_H__ ++#define __NAMEVAL_H__ ++ ++#include "yportenv.h" ++ ++int nval_del(char *xb, int xb_size, const YCHAR * name); ++int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, ++ int bsize, int flags); ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize); ++int nval_list(const char *xb, int xb_size, char *buf, int bsize); ++int nval_hasvalues(const char *xb, int xb_size); ++#endif +diff --git a/fs/yaffs2/yaffs_nand.c b/fs/yaffs2/yaffs_nand.c +new file mode 100644 +index 0000000..0d8499b +--- /dev/null ++++ b/fs/yaffs2/yaffs_nand.c +@@ -0,0 +1,122 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_nand.h" ++#include "yaffs_tagscompat.h" ++ ++#include "yaffs_getblockinfo.h" ++#include "yaffs_summary.h" ++ ++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) ++{ ++ return chunk - dev->chunk_offset; ++} ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 *buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ struct yaffs_ext_tags local_tags; ++ int flash_chunk = apply_chunk_offset(dev, nand_chunk); ++ ++ dev->n_page_reads++; ++ ++ /* If there are no tags provided use local tags. */ ++ if (!tags) ++ tags = &local_tags; ++ ++ result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags); ++ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { ++ ++ struct yaffs_block_info *bi; ++ bi = yaffs_get_block_info(dev, ++ nand_chunk / ++ dev->param.chunks_per_block); ++ yaffs_handle_chunk_error(dev, bi); ++ } ++ return result; ++} ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ int flash_chunk = apply_chunk_offset(dev, nand_chunk); ++ ++ dev->n_page_writes++; ++ ++ if (!tags) { ++ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ tags->seq_number = dev->seq_number; ++ tags->chunk_used = 1; ++ yaffs_trace(YAFFS_TRACE_WRITE, ++ "Writing chunk %d tags %d %d", ++ nand_chunk, tags->obj_id, tags->chunk_id); ++ ++ result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk, ++ buffer, tags); ++ ++ yaffs_summary_add(dev, tags, nand_chunk); ++ ++ return result; ++} ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ block_no -= dev->block_offset; ++ dev->n_bad_markings++; ++ ++ if (dev->param.disable_bad_block_marking) ++ return YAFFS_OK; ++ ++ return dev->tagger.mark_bad_fn(dev, block_no); ++} ++ ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ block_no -= dev->block_offset; ++ return dev->tagger.query_block_fn(dev, block_no, state, seq_number); ++} ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ int result; ++ ++ block_no -= dev->block_offset; ++ dev->n_erasures++; ++ result = dev->drv.drv_erase_fn(dev, block_no); ++ return result; ++} ++ ++int yaffs_init_nand(struct yaffs_dev *dev) ++{ ++ if (dev->drv.drv_initialise_fn) ++ return dev->drv.drv_initialise_fn(dev); ++ return YAFFS_OK; ++} ++ ++int yaffs_deinit_nand(struct yaffs_dev *dev) ++{ ++ if (dev->drv.drv_deinitialise_fn) ++ return dev->drv.drv_deinitialise_fn(dev); ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_nand.h b/fs/yaffs2/yaffs_nand.h +new file mode 100644 +index 0000000..804e97a +--- /dev/null ++++ b/fs/yaffs2/yaffs_nand.h +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_NAND_H__ ++#define __YAFFS_NAND_H__ ++#include "yaffs_guts.h" ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 *buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ unsigned *seq_number); ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); ++ ++int yaffs_init_nand(struct yaffs_dev *dev); ++int yaffs_deinit_nand(struct yaffs_dev *dev); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_packedtags1.c b/fs/yaffs2/yaffs_packedtags1.c +new file mode 100644 +index 0000000..dd9a331 +--- /dev/null ++++ b/fs/yaffs2/yaffs_packedtags1.c +@@ -0,0 +1,56 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags1.h" ++#include "yportenv.h" ++ ++static const u8 all_ff[20] = { ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t) ++{ ++ pt->chunk_id = t->chunk_id; ++ pt->serial_number = t->serial_number; ++ pt->n_bytes = t->n_bytes; ++ pt->obj_id = t->obj_id; ++ pt->ecc = 0; ++ pt->deleted = (t->is_deleted) ? 0 : 1; ++ pt->unused_stuff = 0; ++ pt->should_be_ff = 0xffffffff; ++} ++ ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt) ++{ ++ ++ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { ++ t->block_bad = 0; ++ if (pt->should_be_ff != 0xffffffff) ++ t->block_bad = 1; ++ t->chunk_used = 1; ++ t->obj_id = pt->obj_id; ++ t->chunk_id = pt->chunk_id; ++ t->n_bytes = pt->n_bytes; ++ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ t->is_deleted = (pt->deleted) ? 0 : 1; ++ t->serial_number = pt->serial_number; ++ } else { ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ } ++} +diff --git a/fs/yaffs2/yaffs_packedtags1.h b/fs/yaffs2/yaffs_packedtags1.h +new file mode 100644 +index 0000000..3015d58 +--- /dev/null ++++ b/fs/yaffs2/yaffs_packedtags1.h +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS1_H__ ++#define __YAFFS_PACKEDTAGS1_H__ ++ ++#include "yaffs_guts.h" ++ ++struct yaffs_packed_tags1 { ++ u32 chunk_id:20; ++ u32 serial_number:2; ++ u32 n_bytes:10; ++ u32 obj_id:18; ++ u32 ecc:12; ++ u32 deleted:1; ++ u32 unused_stuff:1; ++ unsigned should_be_ff; ++ ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt); ++#endif +diff --git a/fs/yaffs2/yaffs_packedtags2.c b/fs/yaffs2/yaffs_packedtags2.c +new file mode 100644 +index 0000000..e1d18cc +--- /dev/null ++++ b/fs/yaffs2/yaffs_packedtags2.c +@@ -0,0 +1,197 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags2.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++/* This code packs a set of extended tags into a binary structure for ++ * NAND storage ++ */ ++ ++/* Some of the information is "extra" struff which can be packed in to ++ * speed scanning ++ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. ++ */ ++ ++/* Extra flags applied to chunk_id */ ++ ++#define EXTRA_HEADER_INFO_FLAG 0x80000000 ++#define EXTRA_SHRINK_FLAG 0x40000000 ++#define EXTRA_SHADOWS_FLAG 0x20000000 ++#define EXTRA_SPARE_FLAGS 0x10000000 ++ ++#define ALL_EXTRA_FLAGS 0xf0000000 ++ ++/* Also, the top 4 bits of the object Id are set to the object type. */ ++#define EXTRA_OBJECT_TYPE_SHIFT (28) ++#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) ++ ++static void yaffs_dump_packed_tags2_tags_only( ++ const struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "packed tags obj %d chunk %d byte %d seq %d", ++ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); ++} ++ ++static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) ++{ ++ yaffs_dump_packed_tags2_tags_only(&pt->t); ++} ++ ++static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", ++ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, ++ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, ++ t->seq_number); ++ ++} ++ ++static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t) ++{ ++ if (t->chunk_id != 0 || !t->extra_available) ++ return 0; ++ ++ /* Check if the file size is too long to store */ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE && ++ (t->extra_file_size >> 31) != 0) ++ return 0; ++ return 1; ++} ++ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, ++ const struct yaffs_ext_tags *t) ++{ ++ ptt->chunk_id = t->chunk_id; ++ ptt->seq_number = t->seq_number; ++ ptt->n_bytes = t->n_bytes; ++ ptt->obj_id = t->obj_id; ++ ++ /* Only store extra tags for object headers. ++ * If it is a file then only store if the file size is short\ ++ * enough to fit. ++ */ ++ if (yaffs_check_tags_extra_packable(t)) { ++ /* Store the extra header info instead */ ++ /* We save the parent object in the chunk_id */ ++ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; ++ if (t->extra_is_shrink) ++ ptt->chunk_id |= EXTRA_SHRINK_FLAG; ++ if (t->extra_shadows) ++ ptt->chunk_id |= EXTRA_SHADOWS_FLAG; ++ ++ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ ptt->n_bytes = t->extra_equiv_id; ++ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ ptt->n_bytes = (unsigned) t->extra_file_size; ++ else ++ ptt->n_bytes = 0; ++ } ++ ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc) ++{ ++ yaffs_pack_tags2_tags_only(&pt->t, t); ++ ++ if (tags_ecc) ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &pt->ecc); ++} ++ ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ ++ if (ptt->seq_number == 0xffffffff) ++ return; ++ ++ t->block_bad = 0; ++ t->chunk_used = 1; ++ t->obj_id = ptt->obj_id; ++ t->chunk_id = ptt->chunk_id; ++ t->n_bytes = ptt->n_bytes; ++ t->is_deleted = 0; ++ t->serial_number = 0; ++ t->seq_number = ptt->seq_number; ++ ++ /* Do extra header info stuff */ ++ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { ++ t->chunk_id = 0; ++ t->n_bytes = 0; ++ ++ t->extra_available = 1; ++ t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); ++ t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; ++ t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; ++ t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; ++ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ t->extra_equiv_id = ptt->n_bytes; ++ else ++ t->extra_file_size = ptt->n_bytes; ++ } ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc) ++{ ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ if (pt->t.seq_number != 0xffffffff && tags_ecc) { ++ /* Chunk is in use and we need to do ECC */ ++ ++ struct yaffs_ecc_other ecc; ++ int result; ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &ecc); ++ result = ++ yaffs_ecc_correct_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &pt->ecc, &ecc); ++ switch (result) { ++ case 0: ++ ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ case 1: ++ ecc_result = YAFFS_ECC_RESULT_FIXED; ++ break; ++ case -1: ++ ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ break; ++ default: ++ ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ } ++ } ++ yaffs_unpack_tags2_tags_only(t, &pt->t); ++ ++ t->ecc_result = ecc_result; ++ ++ yaffs_dump_packed_tags2(pt); ++ yaffs_dump_tags2(t); ++} +diff --git a/fs/yaffs2/yaffs_packedtags2.h b/fs/yaffs2/yaffs_packedtags2.h +new file mode 100644 +index 0000000..675e719 +--- /dev/null ++++ b/fs/yaffs2/yaffs_packedtags2.h +@@ -0,0 +1,47 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS2_H__ ++#define __YAFFS_PACKEDTAGS2_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_ecc.h" ++ ++struct yaffs_packed_tags2_tags_only { ++ unsigned seq_number; ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++struct yaffs_packed_tags2 { ++ struct yaffs_packed_tags2_tags_only t; ++ struct yaffs_ecc_other ecc; ++}; ++ ++/* Full packed tags with ECC, used for oob tags */ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc); ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc); ++ ++/* Only the tags part (no ECC for use with inband tags */ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *pt); ++#endif +diff --git a/fs/yaffs2/yaffs_summary.c b/fs/yaffs2/yaffs_summary.c +new file mode 100644 +index 0000000..3c9e723 +--- /dev/null ++++ b/fs/yaffs2/yaffs_summary.c +@@ -0,0 +1,312 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Summaries write the useful part of the tags for the chunks in a block into an ++ * an array which is written to the last n chunks of the block. ++ * Reading the summaries gives all the tags for the block in one read. Much ++ * faster. ++ * ++ * Chunks holding summaries are marked with tags making it look like ++ * they are part of a fake file. ++ * ++ * The summary could also be used during gc. ++ * ++ */ ++ ++#include "yaffs_summary.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_bitmap.h" ++ ++/* ++ * The summary is built up in an array of summary tags. ++ * This gets written to the last one or two (maybe more) chunks in a block. ++ * A summary header is written as the first part of each chunk of summary data. ++ * The summary header must match or the summary is rejected. ++ */ ++ ++/* Summary tags don't need the sequence number because that is redundant. */ ++struct yaffs_summary_tags { ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++/* Summary header */ ++struct yaffs_summary_header { ++ unsigned version; /* Must match current version */ ++ unsigned block; /* Must be this block */ ++ unsigned seq; /* Must be this sequence number */ ++ unsigned sum; /* Just add up all the bytes in the tags */ ++}; ++ ++ ++static void yaffs_summary_clear(struct yaffs_dev *dev) ++{ ++ if (!dev->sum_tags) ++ return; ++ memset(dev->sum_tags, 0, dev->chunks_per_summary * ++ sizeof(struct yaffs_summary_tags)); ++} ++ ++ ++void yaffs_summary_deinit(struct yaffs_dev *dev) ++{ ++ kfree(dev->sum_tags); ++ dev->sum_tags = NULL; ++ kfree(dev->gc_sum_tags); ++ dev->gc_sum_tags = NULL; ++ dev->chunks_per_summary = 0; ++} ++ ++int yaffs_summary_init(struct yaffs_dev *dev) ++{ ++ int sum_bytes; ++ int chunks_used; /* Number of chunks used by summary */ ++ int sum_tags_bytes; ++ ++ sum_bytes = dev->param.chunks_per_block * ++ sizeof(struct yaffs_summary_tags); ++ ++ chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ ++ (dev->data_bytes_per_chunk - ++ sizeof(struct yaffs_summary_header)); ++ ++ dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; ++ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); ++ dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); ++ if (!dev->sum_tags || !dev->gc_sum_tags) { ++ yaffs_summary_deinit(dev); ++ return YAFFS_FAIL; ++ } ++ ++ yaffs_summary_clear(dev); ++ ++ return YAFFS_OK; ++} ++ ++static unsigned yaffs_summary_sum(struct yaffs_dev *dev) ++{ ++ u8 *sum_buffer = (u8 *)dev->sum_tags; ++ int i; ++ unsigned sum = 0; ++ ++ i = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ while (i > 0) { ++ sum += *sum_buffer; ++ sum_buffer++; ++ i--; ++ } ++ ++ return sum; ++} ++ ++static int yaffs_summary_write(struct yaffs_dev *dev, int blk) ++{ ++ struct yaffs_ext_tags tags; ++ u8 *buffer; ++ u8 *sum_buffer = (u8 *)dev->sum_tags; ++ int n_bytes; ++ int chunk_in_nand; ++ int chunk_in_block; ++ int result; ++ int this_tx; ++ struct yaffs_summary_header hdr; ++ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ ++ buffer = yaffs_get_temp_buffer(dev); ++ n_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ memset(&tags, 0, sizeof(struct yaffs_ext_tags)); ++ tags.obj_id = YAFFS_OBJECTID_SUMMARY; ++ tags.chunk_id = 1; ++ chunk_in_block = dev->chunks_per_summary; ++ chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + ++ dev->chunks_per_summary; ++ hdr.version = YAFFS_SUMMARY_VERSION; ++ hdr.block = blk; ++ hdr.seq = bi->seq_number; ++ hdr.sum = yaffs_summary_sum(dev); ++ ++ do { ++ this_tx = n_bytes; ++ if (this_tx > sum_bytes_per_chunk) ++ this_tx = sum_bytes_per_chunk; ++ memcpy(buffer, &hdr, sizeof(hdr)); ++ memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); ++ tags.n_bytes = this_tx + sizeof(hdr); ++ result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, ++ buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ break; ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ dev->n_free_chunks--; ++ ++ n_bytes -= this_tx; ++ sum_buffer += this_tx; ++ chunk_in_nand++; ++ chunk_in_block++; ++ tags.chunk_id++; ++ } while (result == YAFFS_OK && n_bytes > 0); ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ ++ if (result == YAFFS_OK) ++ bi->has_summary = 1; ++ ++ ++ return result; ++} ++ ++int yaffs_summary_read(struct yaffs_dev *dev, ++ struct yaffs_summary_tags *st, ++ int blk) ++{ ++ struct yaffs_ext_tags tags; ++ u8 *buffer; ++ u8 *sum_buffer = (u8 *)st; ++ int n_bytes; ++ int chunk_id; ++ int chunk_in_nand; ++ int chunk_in_block; ++ int result; ++ int this_tx; ++ struct yaffs_summary_header hdr; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); ++ int sum_tags_bytes; ++ ++ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ buffer = yaffs_get_temp_buffer(dev); ++ n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; ++ chunk_in_block = dev->chunks_per_summary; ++ chunk_in_nand = blk * dev->param.chunks_per_block + ++ dev->chunks_per_summary; ++ chunk_id = 1; ++ do { ++ this_tx = n_bytes; ++ if (this_tx > sum_bytes_per_chunk) ++ this_tx = sum_bytes_per_chunk; ++ result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, ++ buffer, &tags); ++ ++ if (tags.chunk_id != chunk_id || ++ tags.obj_id != YAFFS_OBJECTID_SUMMARY || ++ tags.chunk_used == 0 || ++ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || ++ tags.n_bytes != (this_tx + sizeof(hdr))) ++ result = YAFFS_FAIL; ++ if (result != YAFFS_OK) ++ break; ++ ++ if (st == dev->sum_tags) { ++ /* If we're scanning then update the block info */ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ } ++ memcpy(&hdr, buffer, sizeof(hdr)); ++ memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); ++ n_bytes -= this_tx; ++ sum_buffer += this_tx; ++ chunk_in_nand++; ++ chunk_in_block++; ++ chunk_id++; ++ } while (result == YAFFS_OK && n_bytes > 0); ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ if (result == YAFFS_OK) { ++ /* Verify header */ ++ if (hdr.version != YAFFS_SUMMARY_VERSION || ++ hdr.seq != bi->seq_number || ++ hdr.sum != yaffs_summary_sum(dev)) ++ result = YAFFS_FAIL; ++ } ++ ++ if (st == dev->sum_tags && result == YAFFS_OK) ++ bi->has_summary = 1; ++ ++ return result; ++} ++ ++int yaffs_summary_add(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_nand) ++{ ++ struct yaffs_packed_tags2_tags_only tags_only; ++ struct yaffs_summary_tags *sum_tags; ++ int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; ++ int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; ++ ++ if (!dev->sum_tags) ++ return YAFFS_OK; ++ ++ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { ++ yaffs_pack_tags2_tags_only(&tags_only, tags); ++ sum_tags = &dev->sum_tags[chunk_in_block]; ++ sum_tags->chunk_id = tags_only.chunk_id; ++ sum_tags->n_bytes = tags_only.n_bytes; ++ sum_tags->obj_id = tags_only.obj_id; ++ ++ if (chunk_in_block == dev->chunks_per_summary - 1) { ++ /* Time to write out the summary */ ++ yaffs_summary_write(dev, block_in_nand); ++ yaffs_summary_clear(dev); ++ yaffs_skip_rest_of_block(dev); ++ } ++ } ++ return YAFFS_OK; ++} ++ ++int yaffs_summary_fetch(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block) ++{ ++ struct yaffs_packed_tags2_tags_only tags_only; ++ struct yaffs_summary_tags *sum_tags; ++ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { ++ sum_tags = &dev->sum_tags[chunk_in_block]; ++ tags_only.chunk_id = sum_tags->chunk_id; ++ tags_only.n_bytes = sum_tags->n_bytes; ++ tags_only.obj_id = sum_tags->obj_id; ++ yaffs_unpack_tags2_tags_only(tags, &tags_only); ++ return YAFFS_OK; ++ } ++ return YAFFS_FAIL; ++} ++ ++void yaffs_summary_gc(struct yaffs_dev *dev, int blk) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ int i; ++ ++ if (!bi->has_summary) ++ return; ++ ++ for (i = dev->chunks_per_summary; ++ i < dev->param.chunks_per_block; ++ i++) { ++ if (yaffs_check_chunk_bit(dev, blk, i)) { ++ yaffs_clear_chunk_bit(dev, blk, i); ++ bi->pages_in_use--; ++ dev->n_free_chunks++; ++ } ++ } ++} +diff --git a/fs/yaffs2/yaffs_summary.h b/fs/yaffs2/yaffs_summary.h +new file mode 100644 +index 0000000..be141d0 +--- /dev/null ++++ b/fs/yaffs2/yaffs_summary.h +@@ -0,0 +1,37 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_SUMMARY_H__ ++#define __YAFFS_SUMMARY_H__ ++ ++#include "yaffs_packedtags2.h" ++ ++ ++int yaffs_summary_init(struct yaffs_dev *dev); ++void yaffs_summary_deinit(struct yaffs_dev *dev); ++ ++int yaffs_summary_add(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block); ++int yaffs_summary_fetch(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block); ++int yaffs_summary_read(struct yaffs_dev *dev, ++ struct yaffs_summary_tags *st, ++ int blk); ++void yaffs_summary_gc(struct yaffs_dev *dev, int blk); ++ ++ ++#endif +diff --git a/fs/yaffs2/yaffs_tagscompat.c b/fs/yaffs2/yaffs_tagscompat.c +new file mode 100644 +index 0000000..092430b +--- /dev/null ++++ b/fs/yaffs2/yaffs_tagscompat.c +@@ -0,0 +1,381 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_ecc.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_trace.h" ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); ++ ++ ++/********** Tags ECC calculations *********/ ++ ++ ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags) ++{ ++ /* Calculate an ecc */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ unsigned i, j; ++ unsigned ecc = 0; ++ unsigned bit = 0; ++ ++ tags->ecc = 0; ++ ++ for (i = 0; i < 8; i++) { ++ for (j = 1; j & 0xff; j <<= 1) { ++ bit++; ++ if (b[i] & j) ++ ecc ^= bit; ++ } ++ } ++ tags->ecc = ecc; ++} ++ ++int yaffs_check_tags_ecc(struct yaffs_tags *tags) ++{ ++ unsigned ecc = tags->ecc; ++ ++ yaffs_calc_tags_ecc(tags); ++ ++ ecc ^= tags->ecc; ++ ++ if (ecc && ecc <= 64) { ++ /* TODO: Handle the failure better. Retire? */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ ++ ecc--; ++ ++ b[ecc / 8] ^= (1 << (ecc & 7)); ++ ++ /* Now recvalc the ecc */ ++ yaffs_calc_tags_ecc(tags); ++ ++ return 1; /* recovered error */ ++ } else if (ecc) { ++ /* Wierd ecc failure value */ ++ /* TODO Need to do somethiong here */ ++ return -1; /* unrecovered error */ ++ } ++ return 0; ++} ++ ++/********** Tags **********/ ++ ++static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ ++ yaffs_calc_tags_ecc(tags_ptr); ++ ++ spare_ptr->tb0 = tu->as_bytes[0]; ++ spare_ptr->tb1 = tu->as_bytes[1]; ++ spare_ptr->tb2 = tu->as_bytes[2]; ++ spare_ptr->tb3 = tu->as_bytes[3]; ++ spare_ptr->tb4 = tu->as_bytes[4]; ++ spare_ptr->tb5 = tu->as_bytes[5]; ++ spare_ptr->tb6 = tu->as_bytes[6]; ++ spare_ptr->tb7 = tu->as_bytes[7]; ++} ++ ++static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, ++ struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ int result; ++ ++ tu->as_bytes[0] = spare_ptr->tb0; ++ tu->as_bytes[1] = spare_ptr->tb1; ++ tu->as_bytes[2] = spare_ptr->tb2; ++ tu->as_bytes[3] = spare_ptr->tb3; ++ tu->as_bytes[4] = spare_ptr->tb4; ++ tu->as_bytes[5] = spare_ptr->tb5; ++ tu->as_bytes[6] = spare_ptr->tb6; ++ tu->as_bytes[7] = spare_ptr->tb7; ++ ++ result = yaffs_check_tags_ecc(tags_ptr); ++ if (result > 0) ++ dev->n_tags_ecc_fixed++; ++ else if (result < 0) ++ dev->n_tags_ecc_unfixed++; ++} ++ ++static void yaffs_spare_init(struct yaffs_spare *spare) ++{ ++ memset(spare, 0xff, sizeof(struct yaffs_spare)); ++} ++ ++static int yaffs_wr_nand(struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ struct yaffs_spare *spare) ++{ ++ int data_size = dev->data_bytes_per_chunk; ++ ++ return dev->drv.drv_write_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *) spare, sizeof(*spare)); ++} ++ ++static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, ++ struct yaffs_spare *spare, ++ enum yaffs_ecc_result *ecc_result, ++ int correct_errors) ++{ ++ int ret_val; ++ struct yaffs_spare local_spare; ++ int data_size; ++ int spare_size; ++ int ecc_result1, ecc_result2; ++ u8 calc_ecc[3]; ++ ++ if (!spare) { ++ /* If we don't have a real spare, then we use a local one. */ ++ /* Need this for the calculation of the ecc */ ++ spare = &local_spare; ++ } ++ data_size = dev->data_bytes_per_chunk; ++ spare_size = sizeof(struct yaffs_spare); ++ ++ if (dev->param.use_nand_ecc) ++ return dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *) spare, spare_size, ++ ecc_result); ++ ++ ++ /* Handle the ECC at this level. */ ++ ++ ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *)spare, spare_size, ++ NULL); ++ if (!data || !correct_errors) ++ return ret_val; ++ ++ /* Do ECC correction if needed. */ ++ yaffs_ecc_calc(data, calc_ecc); ++ ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc); ++ yaffs_ecc_calc(&data[256], calc_ecc); ++ ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc); ++ ++ if (ecc_result1 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result1 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result2 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result2 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result1 || ecc_result2) { ++ /* We had a data problem on this page */ ++ yaffs_handle_rd_data_error(dev, nand_chunk); ++ } ++ ++ if (ecc_result1 < 0 || ecc_result2 < 0) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ else if (ecc_result1 > 0 || ecc_result2 > 0) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ else ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ return ret_val; ++} ++ ++/* ++ * Functions for robustisizing ++ */ ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ ++ /* Mark the block for retirement */ ++ yaffs_get_block_info(dev, flash_block + dev->block_offset)-> ++ needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>>Block %d marked for retirement", ++ flash_block); ++ ++ /* TODO: ++ * Just do a garbage collection on the affected block ++ * then retire the block ++ * NB recursion ++ */ ++} ++ ++static int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, const struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ ++ yaffs_spare_init(&spare); ++ ++ if (ext_tags->is_deleted) ++ spare.page_status = 0; ++ else { ++ tags.obj_id = ext_tags->obj_id; ++ tags.chunk_id = ext_tags->chunk_id; ++ ++ tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; ++ else ++ tags.n_bytes_msb = 3; ++ ++ tags.serial_number = ext_tags->serial_number; ++ ++ if (!dev->param.use_nand_ecc && data) { ++ yaffs_ecc_calc(data, spare.ecc1); ++ yaffs_ecc_calc(&data[256], spare.ecc2); ++ } ++ ++ yaffs_load_tags_to_spare(&spare, &tags); ++ } ++ return yaffs_wr_nand(dev, nand_chunk, data, &spare); ++} ++ ++static int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ int deleted; ++ ++ if (!init) { ++ memset(&spare_ff, 0xff, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ if (!yaffs_rd_chunk_nand(dev, nand_chunk, ++ data, &spare, &ecc_result, 1)) ++ return YAFFS_FAIL; ++ ++ /* ext_tags may be NULL */ ++ if (!ext_tags) ++ return YAFFS_OK; ++ ++ deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; ++ ++ ext_tags->is_deleted = deleted; ++ ext_tags->ecc_result = ecc_result; ++ ext_tags->block_bad = 0; /* We're reading it */ ++ /* therefore it is not a bad block */ ++ ext_tags->chunk_used = ++ memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; ++ ++ if (ext_tags->chunk_used) { ++ yaffs_get_tags_from_spare(dev, &spare, &tags); ++ ext_tags->obj_id = tags.obj_id; ++ ext_tags->chunk_id = tags.chunk_id; ++ ext_tags->n_bytes = tags.n_bytes_lsb; ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ ext_tags->n_bytes |= ++ (((unsigned)tags.n_bytes_msb) << 10); ++ ++ ext_tags->serial_number = tags.serial_number; ++ } ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_spare spare; ++ ++ memset(&spare, 0xff, sizeof(struct yaffs_spare)); ++ ++ spare.block_status = 'Y'; ++ ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, ++ &spare); ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, ++ NULL, &spare); ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ struct yaffs_spare spare0, spare1; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ enum yaffs_ecc_result dummy; ++ ++ if (!init) { ++ memset(&spare_ff, 0xff, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ *seq_number = 0; ++ ++ /* Look for bad block markers in the first two chunks */ ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, ++ NULL, &spare0, &dummy, 0); ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, ++ NULL, &spare1, &dummy, 0); ++ ++ if (hweight8(spare0.block_status & spare1.block_status) < 7) ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ else ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; ++ ++ return YAFFS_OK; ++} ++ ++void yaffs_tags_compat_install(struct yaffs_dev *dev) ++{ ++ if(dev->param.is_yaffs2) ++ return; ++ if(!dev->tagger.write_chunk_tags_fn) ++ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr; ++ if(!dev->tagger.read_chunk_tags_fn) ++ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd; ++ if(!dev->tagger.query_block_fn) ++ dev->tagger.query_block_fn = yaffs_tags_compat_query_block; ++ if(!dev->tagger.mark_bad_fn) ++ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; ++} +diff --git a/fs/yaffs2/yaffs_tagscompat.h b/fs/yaffs2/yaffs_tagscompat.h +new file mode 100644 +index 0000000..92d298a +--- /dev/null ++++ b/fs/yaffs2/yaffs_tagscompat.h +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSCOMPAT_H__ ++#define __YAFFS_TAGSCOMPAT_H__ ++ ++ ++#include "yaffs_guts.h" ++ ++#if 0 ++ ++ ++int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, const struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); ++int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number); ++ ++#endif ++ ++ ++void yaffs_tags_compat_install(struct yaffs_dev *dev); ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags); ++int yaffs_check_tags_ecc(struct yaffs_tags *tags); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_tagsmarshall.c b/fs/yaffs2/yaffs_tagsmarshall.c +new file mode 100644 +index 0000000..44a83b1 +--- /dev/null ++++ b/fs/yaffs2/yaffs_tagsmarshall.c +@@ -0,0 +1,199 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_packedtags2.h" ++ ++static int yaffs_tags_marshall_write(struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ const struct yaffs_ext_tags *tags) ++{ ++ struct yaffs_packed_tags2 pt; ++ int retval; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_tags_marshall_write chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ /* For yaffs2 writing there must be both data and tags. ++ * If we're using inband tags, then the tags are stuffed into ++ * the end of the data buffer. ++ */ ++ if (!data || !tags) ++ BUG(); ++ else if (dev->param.inband_tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *)(data + ++ dev-> ++ data_bytes_per_chunk); ++ yaffs_pack_tags2_tags_only(pt2tp, tags); ++ } else { ++ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); ++ } ++ ++ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ (dev->param.inband_tags) ? NULL : packed_tags_ptr, ++ (dev->param.inband_tags) ? 0 : packed_tags_size); ++ ++ return retval; ++} ++ ++static int yaffs_tags_marshall_read(struct yaffs_dev *dev, ++ int nand_chunk, u8 *data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = 0; ++ int local_data = 0; ++ u8 spare_buffer[100]; ++ enum yaffs_ecc_result ecc_result; ++ ++ struct yaffs_packed_tags2 pt; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_tags_marshall_read chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ if (dev->param.inband_tags) { ++ if (!data) { ++ local_data = 1; ++ data = yaffs_get_temp_buffer(dev); ++ } ++ } ++ ++ if (dev->param.inband_tags || (data && !tags)) ++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ NULL, 0, ++ &ecc_result); ++ else if (tags) ++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ spare_buffer, packed_tags_size, ++ &ecc_result); ++ else ++ BUG(); ++ ++ ++ if (dev->param.inband_tags) { ++ if (tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *) ++ &data[dev->data_bytes_per_chunk]; ++ yaffs_unpack_tags2_tags_only(tags, pt2tp); ++ } ++ } else if (tags) { ++ memcpy(packed_tags_ptr, spare_buffer, packed_tags_size); ++ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); ++ } ++ ++ if (local_data) ++ yaffs_release_temp_buffer(dev, data); ++ ++ if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) { ++ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) { ++ if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR) ++ tags->ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ } ++ ++ if (ecc_result < YAFFS_ECC_RESULT_UNFIXED) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d", ++ block_no); ++ ++ retval = dev->drv.drv_check_bad_fn(dev, block_no); ++ ++ if (retval== YAFFS_FAIL) { ++ yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); ++ ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ *seq_number = 0; ++ } else { ++ struct yaffs_ext_tags t; ++ ++ yaffs_tags_marshall_read(dev, ++ block_no * dev->param.chunks_per_block, ++ NULL, &t); ++ ++ if (t.chunk_used) { ++ *seq_number = t.seq_number; ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; ++ } else { ++ *seq_number = 0; ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "block query returns seq %d state %d", ++ *seq_number, *state); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ return dev->drv.drv_mark_bad_fn(dev, block_no); ++ ++} ++ ++ ++void yaffs_tags_marshall_install(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->tagger.write_chunk_tags_fn) ++ dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; ++ ++ if (!dev->tagger.read_chunk_tags_fn) ++ dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; ++ ++ if (!dev->tagger.query_block_fn) ++ dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; ++ ++ if (!dev->tagger.mark_bad_fn) ++ dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; ++ ++} +diff --git a/fs/yaffs2/yaffs_tagsmarshall.h b/fs/yaffs2/yaffs_tagsmarshall.h +new file mode 100644 +index 0000000..bf3e68a +--- /dev/null ++++ b/fs/yaffs2/yaffs_tagsmarshall.h +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSMARSHALL_H__ ++#define __YAFFS_TAGSMARSHALL_H__ ++ ++#include "yaffs_guts.h" ++void yaffs_tags_marshall_install(struct yaffs_dev *dev); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_trace.h b/fs/yaffs2/yaffs_trace.h +new file mode 100644 +index 0000000..fd26054 +--- /dev/null ++++ b/fs/yaffs2/yaffs_trace.h +@@ -0,0 +1,57 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YTRACE_H__ ++#define __YTRACE_H__ ++ ++extern unsigned int yaffs_trace_mask; ++extern unsigned int yaffs_wr_attempts; ++ ++/* ++ * Tracing flags. ++ * The flags masked in YAFFS_TRACE_ALWAYS are always traced. ++ */ ++ ++#define YAFFS_TRACE_OS 0x00000002 ++#define YAFFS_TRACE_ALLOCATE 0x00000004 ++#define YAFFS_TRACE_SCAN 0x00000008 ++#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 ++#define YAFFS_TRACE_ERASE 0x00000020 ++#define YAFFS_TRACE_GC 0x00000040 ++#define YAFFS_TRACE_WRITE 0x00000080 ++#define YAFFS_TRACE_TRACING 0x00000100 ++#define YAFFS_TRACE_DELETION 0x00000200 ++#define YAFFS_TRACE_BUFFERS 0x00000400 ++#define YAFFS_TRACE_NANDACCESS 0x00000800 ++#define YAFFS_TRACE_GC_DETAIL 0x00001000 ++#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 ++#define YAFFS_TRACE_MTD 0x00004000 ++#define YAFFS_TRACE_CHECKPOINT 0x00008000 ++ ++#define YAFFS_TRACE_VERIFY 0x00010000 ++#define YAFFS_TRACE_VERIFY_NAND 0x00020000 ++#define YAFFS_TRACE_VERIFY_FULL 0x00040000 ++#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 ++ ++#define YAFFS_TRACE_SYNC 0x00100000 ++#define YAFFS_TRACE_BACKGROUND 0x00200000 ++#define YAFFS_TRACE_LOCK 0x00400000 ++#define YAFFS_TRACE_MOUNT 0x00800000 ++ ++#define YAFFS_TRACE_ERROR 0x40000000 ++#define YAFFS_TRACE_BUG 0x80000000 ++#define YAFFS_TRACE_ALWAYS 0xf0000000 ++ ++#endif +diff --git a/fs/yaffs2/yaffs_verify.c b/fs/yaffs2/yaffs_verify.c +new file mode 100644 +index 0000000..e8f2f0a +--- /dev/null ++++ b/fs/yaffs2/yaffs_verify.c +@@ -0,0 +1,529 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_verify.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++ ++int yaffs_skip_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & ++ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_full_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_nand_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); ++} ++ ++static const char * const block_state_name[] = { ++ "Unknown", ++ "Needs scan", ++ "Scanning", ++ "Empty", ++ "Allocating", ++ "Full", ++ "Dirty", ++ "Checkpoint", ++ "Collecting", ++ "Dead" ++}; ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) ++{ ++ int actually_used; ++ int in_use; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Report illegal runtime states */ ++ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has undefined state %d", ++ n, bi->block_state); ++ ++ switch (bi->block_state) { ++ case YAFFS_BLOCK_STATE_UNKNOWN: ++ case YAFFS_BLOCK_STATE_SCANNING: ++ case YAFFS_BLOCK_STATE_NEEDS_SCAN: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has bad run-state %s", ++ n, block_state_name[bi->block_state]); ++ } ++ ++ /* Check pages in use and soft deletions are legal */ ++ ++ actually_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->pages_in_use < 0 || ++ bi->pages_in_use > dev->param.chunks_per_block || ++ bi->soft_del_pages < 0 || ++ bi->soft_del_pages > dev->param.chunks_per_block || ++ actually_used < 0 || actually_used > dev->param.chunks_per_block) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has illegal values pages_in_used %d soft_del_pages %d", ++ n, bi->pages_in_use, bi->soft_del_pages); ++ ++ /* Check chunk bitmap legal */ ++ in_use = yaffs_count_chunk_bits(dev, n); ++ if (in_use != bi->pages_in_use) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", ++ n, bi->pages_in_use, in_use); ++} ++ ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n) ++{ ++ yaffs_verify_blk(dev, bi, n); ++ ++ /* After collection the block should be in the erased state */ ++ ++ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && ++ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Block %d is in state %d after gc, should be erased", ++ n, bi->block_state); ++ } ++} ++ ++void yaffs_verify_blocks(struct yaffs_dev *dev) ++{ ++ int i; ++ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; ++ int illegal_states = 0; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ memset(state_count, 0, sizeof(state_count)); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ yaffs_verify_blk(dev, bi, i); ++ ++ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) ++ state_count[bi->block_state]++; ++ else ++ illegal_states++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%d blocks have illegal states", ++ illegal_states); ++ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many allocating blocks"); ++ ++ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%s %d blocks", ++ block_state_name[i], state_count[i]); ++ ++ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Checkpoint block count wrong dev %d count %d", ++ dev->blocks_in_checkpt, ++ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); ++ ++ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Erased block count wrong dev %d count %d", ++ dev->n_erased_blocks, ++ state_count[YAFFS_BLOCK_STATE_EMPTY]); ++ ++ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many collecting blocks %d (max is 1)", ++ state_count[YAFFS_BLOCK_STATE_COLLECTING]); ++} ++ ++/* ++ * Verify the object header. oh must be valid, but obj and tags may be NULL in ++ * which case those tests will not be performed. ++ */ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!(tags && obj && oh)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Verifying object header tags %p obj %p oh %p", ++ tags, obj, oh); ++ return; ++ } ++ ++ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || ++ oh->type > YAFFS_OBJECT_TYPE_MAX) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header type is illegal value 0x%x", ++ tags->obj_id, oh->type); ++ ++ if (tags->obj_id != obj->obj_id) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch obj_id %d", ++ tags->obj_id, obj->obj_id); ++ ++ /* ++ * Check that the object's parent ids match if parent_check requested. ++ * ++ * Tests do not apply to the root object. ++ */ ++ ++ if (parent_check && tags->obj_id > 1 && !obj->parent) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d obj->parent is NULL", ++ tags->obj_id, oh->parent_obj_id); ++ ++ if (parent_check && obj->parent && ++ oh->parent_obj_id != obj->parent->obj_id && ++ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d parent_obj_id %d", ++ tags->obj_id, oh->parent_obj_id, ++ obj->parent->obj_id); ++ ++ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is NULL", ++ obj->obj_id); ++ ++ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is 0xff", ++ obj->obj_id); ++} ++ ++void yaffs_verify_file(struct yaffs_obj *obj) ++{ ++ u32 x; ++ int required_depth; ++ int actual_depth; ++ int last_chunk; ++ u32 offset_in_chunk; ++ u32 the_chunk; ++ ++ u32 i; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ struct yaffs_tnode *tn; ++ u32 obj_id; ++ ++ if (!obj) ++ return; ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ dev = obj->my_dev; ++ obj_id = obj->obj_id; ++ ++ ++ /* Check file size is consistent with tnode depth */ ++ yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size, ++ &last_chunk, &offset_in_chunk); ++ last_chunk++; ++ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x > 0) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ actual_depth = obj->variant.file_variant.top_level; ++ ++ /* Check that the chunks in the tnode tree are all correct. ++ * We do this by scanning through the tnode tree and ++ * checking the tags for every chunk match. ++ */ ++ ++ if (yaffs_skip_nand_verification(dev)) ++ return; ++ ++ for (i = 1; i <= last_chunk; i++) { ++ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); ++ ++ if (!tn) ++ continue; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk > 0) { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ &tags); ++ if (tags.obj_id != obj_id || tags.chunk_id != i) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", ++ obj_id, i, the_chunk, ++ tags.obj_id, tags.chunk_id); ++ } ++ } ++} ++ ++void yaffs_verify_link(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify sane equivalent object */ ++} ++ ++void yaffs_verify_symlink(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify symlink string */ ++} ++ ++void yaffs_verify_special(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++} ++ ++void yaffs_verify_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ u32 chunk_min; ++ u32 chunk_max; ++ u32 chunk_id_ok; ++ u32 chunk_in_range; ++ u32 chunk_wrongly_deleted; ++ u32 chunk_valid; ++ ++ if (!obj) ++ return; ++ ++ if (obj->being_created) ++ return; ++ ++ dev = obj->my_dev; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Check sane object header chunk */ ++ ++ chunk_min = dev->internal_start_block * dev->param.chunks_per_block; ++ chunk_max = ++ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; ++ ++ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && ++ ((unsigned)(obj->hdr_chunk)) <= chunk_max); ++ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); ++ chunk_valid = chunk_in_range && ++ yaffs_check_chunk_bit(dev, ++ obj->hdr_chunk / dev->param.chunks_per_block, ++ obj->hdr_chunk % dev->param.chunks_per_block); ++ chunk_wrongly_deleted = chunk_in_range && !chunk_valid; ++ ++ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has chunk_id %d %s %s", ++ obj->obj_id, obj->hdr_chunk, ++ chunk_id_ok ? "" : ",out of range", ++ chunk_wrongly_deleted ? ",marked as deleted" : ""); ++ ++ if (chunk_valid && !yaffs_skip_nand_verification(dev)) { ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj_hdr *oh; ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); ++ ++ yaffs_verify_oh(obj, oh, &tags, 1); ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ ++ /* Verify it has a parent */ ++ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has parent pointer %p which does not look like an object", ++ obj->obj_id, obj->parent); ++ } ++ ++ /* Verify parent is a directory */ ++ if (obj->parent && ++ obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d's parent is not a directory (type %d)", ++ obj->obj_id, obj->parent->variant_type); ++ } ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ yaffs_verify_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ yaffs_verify_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ yaffs_verify_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ yaffs_verify_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ yaffs_verify_special(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has illegaltype %d", ++ obj->obj_id, obj->variant_type); ++ break; ++ } ++} ++ ++void yaffs_verify_objects(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ int i; ++ struct list_head *lh; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ yaffs_verify_obj(obj); ++ } ++ } ++} ++ ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ int count = 0; ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); ++ BUG(); ++ return; ++ } ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!obj->parent) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); ++ BUG(); ++ return; ++ } ++ ++ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); ++ BUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &obj->parent->variant.dir_variant.children) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_verify_obj(list_obj); ++ if (obj == list_obj) ++ count++; ++ } ++ ++ if (count != 1) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory %d times", ++ count); ++ BUG(); ++ } ++} ++ ++void yaffs_verify_dir(struct yaffs_obj *directory) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ ++ if (!directory) { ++ BUG(); ++ return; ++ } ++ ++ if (yaffs_skip_full_verification(directory->my_dev)) ++ return; ++ ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Directory has wrong type: %d", ++ directory->variant_type); ++ BUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &directory->variant.dir_variant.children) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (list_obj->parent != directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory list has wrong parent %p", ++ list_obj->parent); ++ BUG(); ++ } ++ yaffs_verify_obj_in_dir(list_obj); ++ } ++} ++ ++static int yaffs_free_verification_failures; ++ ++void yaffs_verify_free_chunks(struct yaffs_dev *dev) ++{ ++ int counted; ++ int difference; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ counted = yaffs_count_free_chunks(dev); ++ ++ difference = dev->n_free_chunks - counted; ++ ++ if (difference) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Freechunks verification failure %d %d %d", ++ dev->n_free_chunks, counted, difference); ++ yaffs_free_verification_failures++; ++ } ++} ++ ++int yaffs_verify_file_sane(struct yaffs_obj *in) ++{ ++ (void) in; ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_verify.h b/fs/yaffs2/yaffs_verify.h +new file mode 100644 +index 0000000..4f4af8d +--- /dev/null ++++ b/fs/yaffs2/yaffs_verify.h +@@ -0,0 +1,43 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_VERIFY_H__ ++#define __YAFFS_VERIFY_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, ++ int n); ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n); ++void yaffs_verify_blocks(struct yaffs_dev *dev); ++ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check); ++void yaffs_verify_file(struct yaffs_obj *obj); ++void yaffs_verify_link(struct yaffs_obj *obj); ++void yaffs_verify_symlink(struct yaffs_obj *obj); ++void yaffs_verify_special(struct yaffs_obj *obj); ++void yaffs_verify_obj(struct yaffs_obj *obj); ++void yaffs_verify_objects(struct yaffs_dev *dev); ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); ++void yaffs_verify_dir(struct yaffs_obj *directory); ++void yaffs_verify_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_verify_file_sane(struct yaffs_obj *obj); ++ ++int yaffs_skip_verification(struct yaffs_dev *dev); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_vfs.c b/fs/yaffs2/yaffs_vfs.c +new file mode 100644 +index 0000000..8b253ef +--- /dev/null ++++ b/fs/yaffs2/yaffs_vfs.c +@@ -0,0 +1,3664 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * Acknowledgements: ++ * Luc van OostenRyck for numerous patches. ++ * Nick Bane for numerous patches. ++ * Nick Bane for 2.5/2.6 integration. ++ * Andras Toth for mknod rdev issue. ++ * Michael Fischer for finding the problem with inode inconsistency. ++ * Some code bodily lifted from JFFS ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * ++ * This is the file system front-end to YAFFS that hooks it up to ++ * the VFS. ++ * ++ * Special notes: ++ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with ++ * this superblock ++ * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this ++ * superblock ++ * >> inode->u.generic_ip points to the associated struct yaffs_obj. ++ */ ++ ++/* ++ * There are two variants of the VFS glue code. This variant should compile ++ * for any version of Linux. ++ */ ++#include <linux/version.h> ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) ++#define YAFFS_COMPILE_BACKGROUND ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)) ++#define YAFFS_COMPILE_FREEZER ++#endif ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) ++#define YAFFS_COMPILE_EXPORTFS ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) ++#define YAFFS_USE_SETATTR_COPY ++#define YAFFS_USE_TRUNCATE_SETSIZE ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) ++#define YAFFS_HAS_EVICT_INODE ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) ++#define YAFFS_NEW_FOLLOW_LINK 1 ++#else ++#define YAFFS_NEW_FOLLOW_LINK 0 ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) ++#define YAFFS_HAS_WRITE_SUPER ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++#include <linux/config.h> ++#endif ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/fs.h> ++#include <linux/proc_fs.h> ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) ++#include <linux/smp_lock.h> ++#endif ++#include <linux/pagemap.h> ++#include <linux/mtd/mtd.h> ++#include <linux/interrupt.h> ++#include <linux/string.h> ++#include <linux/ctype.h> ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++#include <linux/namei.h> ++#endif ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++#include <linux/exportfs.h> ++#endif ++ ++#ifdef YAFFS_COMPILE_BACKGROUND ++#include <linux/kthread.h> ++#include <linux/delay.h> ++#endif ++#ifdef YAFFS_COMPILE_FREEZER ++#include <linux/freezer.h> ++#endif ++ ++#include <asm/div64.h> ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ ++#include <linux/statfs.h> ++ ++#define UnlockPage(p) unlock_page(p) ++#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) ++ ++/* FIXME: use sb->s_id instead ? */ ++#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) ++ ++#else ++ ++#include <linux/locks.h> ++#define BDEVNAME_SIZE 0 ++#define yaffs_devname(sb, buf) kdevname(sb->s_dev) ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) ++/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ ++#define __user ++#endif ++ ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) ++#define YPROC_ROOT (&proc_root) ++#else ++#define YPROC_ROOT NULL ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) ++#define Y_INIT_TIMER(a) init_timer(a) ++#else ++#define Y_INIT_TIMER(a) init_timer_on_stack(a) ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) ++#define YAFFS_USE_WRITE_BEGIN_END 1 ++#else ++#define YAFFS_USE_WRITE_BEGIN_END 0 ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) ++#define YAFFS_SUPER_HAS_DIRTY ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) ++#define set_nlink(inode, count) do { (inode)->i_nlink = (count); } while(0) ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) ++static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) ++{ ++ uint64_t result = partition_size; ++ do_div(result, block_size); ++ return (uint32_t) result; ++} ++#else ++#define YCALCBLOCKS(s, b) ((s)/(b)) ++#endif ++ ++#include <linux/uaccess.h> ++#include <linux/mtd/mtd.h> ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#include "yaffs_linux.h" ++ ++#include "yaffs_mtdif.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_getblockinfo.h" ++ ++unsigned int yaffs_trace_mask = ++ YAFFS_TRACE_BAD_BLOCKS | ++ YAFFS_TRACE_ALWAYS | ++ 0; ++ ++unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; ++unsigned int yaffs_auto_checkpoint = 1; ++unsigned int yaffs_gc_control = 1; ++unsigned int yaffs_bg_enable = 1; ++unsigned int yaffs_auto_select = 1; ++/* Module Parameters */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++module_param(yaffs_trace_mask, uint, 0644); ++module_param(yaffs_wr_attempts, uint, 0644); ++module_param(yaffs_auto_checkpoint, uint, 0644); ++module_param(yaffs_gc_control, uint, 0644); ++module_param(yaffs_bg_enable, uint, 0644); ++#else ++MODULE_PARM(yaffs_trace_mask, "i"); ++MODULE_PARM(yaffs_wr_attempts, "i"); ++MODULE_PARM(yaffs_auto_checkpoint, "i"); ++MODULE_PARM(yaffs_gc_control, "i"); ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) ++/* use iget and read_inode */ ++#define Y_IGET(sb, inum) iget((sb), (inum)) ++ ++#else ++/* Call local equivalent */ ++#define YAFFS_USE_OWN_IGET ++#define Y_IGET(sb, inum) yaffs_iget((sb), (inum)) ++ ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) ++#else ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip) ++#endif ++ ++#define yaffs_inode_to_obj(iptr) \ ++ ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) ++#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) ++#else ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->u.generic_sbp) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) ++#define Y_CLEAR_INODE(i) clear_inode(i) ++#else ++#define Y_CLEAR_INODE(i) end_writeback(i) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++#define YAFFS_USE_DIR_ITERATE ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0)) ++#define YAFFS_NEW_PROCFS ++#include <linux/seq_file.h> ++#endif ++ ++ ++#define update_dir_time(dir) do {\ ++ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ ++ } while (0) ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj); ++ ++ ++static void yaffs_gross_lock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); ++ mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); ++} ++ ++static void yaffs_gross_unlock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); ++ mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++} ++ ++ ++static int yaffs_readpage_nolock(struct file *f, struct page *pg) ++{ ++ /* Lifted from jffs2 */ ++ ++ struct yaffs_obj *obj; ++ unsigned char *pg_buf; ++ int ret; ++ loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readpage_nolock at %lld, size %08x", ++ (long long)pos, ++ (unsigned)PAGE_CACHE_SIZE); ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ BUG_ON(!PageLocked(pg)); ++#else ++ if (!PageLocked(pg)) ++ PAGE_BUG(pg); ++#endif ++ ++ pg_buf = kmap(pg); ++ /* FIXME: Can kmap fail? */ ++ ++ yaffs_gross_lock(dev); ++ ++ ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (ret >= 0) ++ ret = 0; ++ ++ if (ret) { ++ ClearPageUptodate(pg); ++ SetPageError(pg); ++ } else { ++ SetPageUptodate(pg); ++ ClearPageError(pg); ++ } ++ ++ flush_dcache_page(pg); ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); ++ return ret; ++} ++ ++static int yaffs_readpage_unlock(struct file *f, struct page *pg) ++{ ++ int ret = yaffs_readpage_nolock(f, pg); ++ UnlockPage(pg); ++ return ret; ++} ++ ++static int yaffs_readpage(struct file *f, struct page *pg) ++{ ++ int ret; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); ++ ret = yaffs_readpage_unlock(f, pg); ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); ++ return ret; ++} ++ ++ ++static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val) ++{ ++ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); ++ ++ if (lc) ++ lc->dirty = val; ++ ++# ifdef YAFFS_SUPER_HAS_DIRTY ++ { ++ struct super_block *sb = lc->super; ++ ++ if (sb) ++ sb->s_dirt = val; ++ } ++#endif ++ ++} ++ ++static void yaffs_set_super_dirty(struct yaffs_dev *dev) ++{ ++ yaffs_set_super_dirty_val(dev, 1); ++} ++ ++static void yaffs_clear_super_dirty(struct yaffs_dev *dev) ++{ ++ yaffs_set_super_dirty_val(dev, 0); ++} ++ ++static int yaffs_check_super_dirty(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); ++ ++ if (lc && lc->dirty) ++ return 1; ++ ++# ifdef YAFFS_SUPER_HAS_DIRTY ++ { ++ struct super_block *sb = lc->super; ++ ++ if (sb && sb->s_dirt) ++ return 1; ++ } ++#endif ++ return 0; ++ ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_writepage(struct page *page, struct writeback_control *wbc) ++#else ++static int yaffs_writepage(struct page *page) ++#endif ++{ ++ struct yaffs_dev *dev; ++ struct address_space *mapping = page->mapping; ++ struct inode *inode; ++ unsigned long end_index; ++ char *buffer; ++ struct yaffs_obj *obj; ++ int n_written = 0; ++ unsigned n_bytes; ++ loff_t i_size; ++ ++ if (!mapping) ++ BUG(); ++ inode = mapping->host; ++ if (!inode) ++ BUG(); ++ i_size = i_size_read(inode); ++ ++ end_index = i_size >> PAGE_CACHE_SHIFT; ++ ++ if (page->index < end_index) ++ n_bytes = PAGE_CACHE_SIZE; ++ else { ++ n_bytes = i_size & (PAGE_CACHE_SIZE - 1); ++ ++ if (page->index > end_index || !n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %lld, inode size = %lld!!", ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, ++ inode->i_size); ++ yaffs_trace(YAFFS_TRACE_OS, ++ " -> don't care!!"); ++ ++ zero_user_segment(page, 0, PAGE_CACHE_SIZE); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ return 0; ++ } ++ } ++ ++ if (n_bytes != PAGE_CACHE_SIZE) ++ zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); ++ ++ get_page(page); ++ ++ buffer = kmap(page); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %lld, size %08x", ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag0: obj = %lld, ino = %lld", ++ obj->variant.file_variant.file_size, inode->i_size); ++ ++ n_written = yaffs_wr_file(obj, buffer, ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0); ++ ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag1: obj = %lld, ino = %lld", ++ obj->variant.file_variant.file_size, inode->i_size); ++ ++ yaffs_gross_unlock(dev); ++ ++ kunmap(page); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ put_page(page); ++ ++ return (n_written == n_bytes) ? 0 : -ENOSPC; ++} ++ ++/* Space holding and freeing is done to ensure we have space available for write_begin/end */ ++/* For now we just assume few parallel writes and check against a small number. */ ++/* Todo: need to do this with a counter to handle parallel reads better */ ++ ++static ssize_t yaffs_hold_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ int n_free_chunks; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ n_free_chunks = yaffs_get_n_free_chunks(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ return (n_free_chunks > 20) ? 1 : 0; ++} ++ ++static void yaffs_release_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_gross_unlock(dev); ++} ++ ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++static int yaffs_write_begin(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ ++ struct page *pg = NULL; ++ pgoff_t index = pos >> PAGE_CACHE_SHIFT; ++ ++ int ret = 0; ++ int space_held = 0; ++ ++ /* Get a page */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) ++ pg = grab_cache_page_write_begin(mapping, index, flags); ++#else ++ pg = __grab_cache_page(mapping, index); ++#endif ++ ++ *pagep = pg; ++ if (!pg) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "start yaffs_write_begin index %d(%x) uptodate %d", ++ (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); ++ ++ /* Get fs space */ ++ space_held = yaffs_hold_space(filp); ++ ++ if (!space_held) { ++ ret = -ENOSPC; ++ goto out; ++ } ++ ++ /* Update page if required */ ++ ++ if (!Page_Uptodate(pg)) ++ ret = yaffs_readpage_nolock(filp, pg); ++ ++ if (ret) ++ goto out; ++ ++ /* Happy path return */ ++ yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); ++ ++ return 0; ++ ++out: ++ yaffs_trace(YAFFS_TRACE_OS, ++ "end yaffs_write_begin fail returning %d", ret); ++ if (space_held) ++ yaffs_release_space(filp); ++ if (pg) { ++ unlock_page(pg); ++ page_cache_release(pg); ++ } ++ return ret; ++} ++ ++#else ++ ++static int yaffs_prepare_write(struct file *f, struct page *pg, ++ unsigned offset, unsigned to) ++{ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write"); ++ ++ if (!Page_Uptodate(pg)) ++ return yaffs_readpage_nolock(f, pg); ++ return 0; ++} ++#endif ++ ++ ++static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, ++ loff_t * pos) ++{ ++ struct yaffs_obj *obj; ++ int n_written; ++ loff_t ipos; ++ struct inode *inode; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: hey obj is null!"); ++ return -EINVAL; ++ } ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ inode = f->f_dentry->d_inode; ++ ++ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) ++ ipos = inode->i_size; ++ else ++ ipos = *pos; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld", ++ (unsigned)n, (unsigned)n, obj->obj_id, ipos); ++ ++ n_written = yaffs_wr_file(obj, buf, ipos, n, 0); ++ ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: %d(%x) bytes written", ++ (unsigned)n, (unsigned)n); ++ ++ if (n_written > 0) { ++ ipos += n_written; ++ *pos = ipos; ++ if (ipos > inode->i_size) { ++ inode->i_size = ipos; ++ inode->i_blocks = (ipos + 511) >> 9; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write size updated to %lld bytes, %d blocks", ++ ipos, (int)(inode->i_blocks)); ++ } ++ ++ } ++ yaffs_gross_unlock(dev); ++ return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; ++} ++ ++ ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++static int yaffs_write_end(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *pg, void *fsdadata) ++{ ++ int ret = 0; ++ void *addr, *kva; ++ uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); ++ ++ kva = kmap(pg); ++ addr = kva + offset_into_page; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end addr %p pos %lld n_bytes %d", ++ addr, pos, copied); ++ ++ ret = yaffs_file_write(filp, addr, copied, &pos); ++ ++ if (ret != copied) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end not same size ret %d copied %d", ++ ret, copied); ++ SetPageError(pg); ++ } ++ ++ kunmap(pg); ++ ++ yaffs_release_space(filp); ++ unlock_page(pg); ++ page_cache_release(pg); ++ return ret; ++} ++#else ++ ++static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, ++ unsigned to) ++{ ++ void *addr, *kva; ++ ++ loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; ++ int n_bytes = to - offset; ++ int n_written; ++ ++ kva = kmap(pg); ++ addr = kva + offset; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write addr %p pos %lld n_bytes %d", ++ addr, pos, n_bytes); ++ ++ n_written = yaffs_file_write(f, addr, n_bytes, &pos); ++ ++ if (n_written != n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write not same size n_written %d n_bytes %d", ++ n_written, n_bytes); ++ SetPageError(pg); ++ } ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write returning %d", ++ n_written == n_bytes ? 0 : n_written); ++ ++ return n_written == n_bytes ? 0 : n_written; ++} ++#endif ++ ++static struct address_space_operations yaffs_file_address_operations = { ++ .readpage = yaffs_readpage, ++ .writepage = yaffs_writepage, ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++ .write_begin = yaffs_write_begin, ++ .write_end = yaffs_write_end, ++#else ++ .prepare_write = yaffs_prepare_write, ++ .commit_write = yaffs_commit_write, ++#endif ++}; ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_file_flush(struct file *file, fl_owner_t id) ++#else ++static int yaffs_file_flush(struct file *file) ++#endif ++{ ++ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); ++ ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_flush object %d (%s)", ++ obj->obj_id, ++ obj->dirty ? "dirty" : "clean"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_file(obj, 1, 0, 0); ++ ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) ++static int yaffs_sync_object(struct file *file, int datasync) ++#else ++static int yaffs_sync_object(struct file *file, struct dentry *dentry, ++ int datasync) ++#endif ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) ++ struct dentry *dentry = file->f_path.dentry; ++#endif ++ ++ obj = yaffs_dentry_to_obj(dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_object"); ++ yaffs_gross_lock(dev); ++ yaffs_flush_file(obj, 1, datasync, 0); ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) ++static const struct file_operations yaffs_file_operations = { ++ .read = new_sync_read, ++ .write = new_sync_write, ++ .read_iter = generic_file_read_iter, ++ .write_iter = generic_file_write_iter, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .splice_read = generic_file_splice_read, ++ .splice_write = iter_file_splice_write, ++ .llseek = generic_file_llseek, ++}; ++ ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .sendfile = generic_file_sendfile, ++}; ++ ++#else ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = generic_file_read, ++ .write = generic_file_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ .sendfile = generic_file_sendfile, ++#endif ++}; ++#endif ++ ++ ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) ++static void zero_user_segment(struct page *page, unsigned start, unsigned end) ++{ ++ void *kaddr = kmap_atomic(page, KM_USER0); ++ memset(kaddr + start, 0, end - start); ++ kunmap_atomic(kaddr, KM_USER0); ++ flush_dcache_page(page); ++} ++#endif ++ ++ ++static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) ++{ ++#ifdef YAFFS_USE_TRUNCATE_SETSIZE ++ truncate_setsize(inode, newsize); ++ return 0; ++#else ++ truncate_inode_pages(&inode->i_data, newsize); ++ return 0; ++#endif ++ ++} ++ ++ ++static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) ++{ ++#ifdef YAFFS_USE_SETATTR_COPY ++ setattr_copy(inode, attr); ++ return 0; ++#else ++ return inode_setattr(inode, attr); ++#endif ++ ++} ++ ++static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_setattr of object %d", ++ yaffs_inode_to_obj(inode)->obj_id); ++#if 0 ++ /* Fail if a requested resize >= 2GB */ ++ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) ++ error = -EINVAL; ++#endif ++ ++ if (error == 0) ++ error = inode_change_ok(inode, attr); ++ if (error == 0) { ++ int result; ++ if (!error) { ++ error = yaffs_vfs_setattr(inode, attr); ++ yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_vfs_setsize(inode, attr->ia_size); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ } ++ } ++ dev = yaffs_inode_to_obj(inode)->my_dev; ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "resize to %d(%x)", ++ (int)(attr->ia_size), ++ (int)(attr->ia_size)); ++ } ++ yaffs_gross_lock(dev); ++ result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); ++ if (result == YAFFS_OK) { ++ error = 0; ++ } else { ++ error = -EPERM; ++ } ++ yaffs_gross_unlock(dev); ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_set_xattrib(obj, name, value, size, flags); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, ++ void *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_getxattr \"%s\" from object %d", ++ name, obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_get_xattrib(obj, name, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_removexattr(struct dentry *dentry, const char *name) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_remove_xattrib(obj, name); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_list_xattrib(obj, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr done returning %d", error); ++ ++ return error; ++} ++ ++ ++static const struct inode_operations yaffs_file_inode_operations = { ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++ ++static int yaffs_readlink(struct dentry *dentry, char __user * buffer, ++ int buflen) ++{ ++ unsigned char *alias; ++ int ret; ++ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) ++ return -ENOMEM; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) ++ ret = vfs_readlink(dentry, buffer, buflen, alias); ++#else ++ ret = readlink_copy(buffer, buflen, alias); ++#endif ++ kfree(alias); ++ return ret; ++} ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ void *ret; ++#else ++static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ int ret ++#endif ++ unsigned char *alias; ++ int ret_int = 0; ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) { ++ ret_int = -ENOMEM; ++ goto out; ++ } ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++ nd_set_link(nd, alias); ++ ret = alias; ++out: ++ if (ret_int) ++ ret = ERR_PTR(ret_int); ++ return ret; ++#else ++ ret = vfs_follow_link(nd, alias); ++ kfree(alias); ++out: ++ if (ret_int) ++ ret = ret_int; ++ return ret; ++#endif ++} ++ ++ ++#ifdef YAFFS_HAS_PUT_INODE ++ ++/* For now put inode is just for debugging ++ * Put inode is called when the inode **structure** is put. ++ */ ++static void yaffs_put_inode(struct inode *inode) ++{ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_put_inode: ino %d, count %d"), ++ (int)inode->i_ino, atomic_read(&inode->i_count); ++ ++} ++#endif ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) ++{ ++ kfree(alias); ++} ++#endif ++ ++static const struct inode_operations yaffs_symlink_inode_operations = { ++ .readlink = yaffs_readlink, ++ .follow_link = yaffs_follow_link, ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++ .put_link = yaffs_put_link, ++#endif ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++#ifdef YAFFS_USE_OWN_IGET ++ ++static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) ++{ ++ struct inode *inode; ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); ++ ++ inode = iget_locked(sb, ino); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ yaffs_gross_unlock(dev); ++ ++ unlock_new_inode(inode); ++ return inode; ++} ++ ++#else ++ ++static void yaffs_read_inode(struct inode *inode) ++{ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_inode for %d", (int)inode->i_ino); ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++} ++ ++#endif ++ ++ ++ ++struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, ++ struct yaffs_obj *obj) ++{ ++ struct inode *inode; ++ ++ if (!sb) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL super_block!!"); ++ return NULL; ++ ++ } ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL object!!"); ++ return NULL; ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for object %d", obj->obj_id); ++ ++ inode = Y_IGET(sb, obj->obj_id); ++ if (IS_ERR(inode)) ++ return NULL; ++ ++ /* NB Side effect: iget calls back to yaffs_read_inode(). */ ++ /* iget also increments the inode's i_count */ ++ /* NB You can't be holding gross_lock or deadlock will happen! */ ++ ++ return inode; ++} ++ ++ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ++#define YCRED(x) x ++#else ++#define YCRED(x) (x->cred) ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) ++#define YPROC_uid(p) (YCRED(p)->fsuid) ++#define YPROC_gid(p) (YCRED(p)->fsgid) ++#define EXTRACT_gid(x) x ++#define EXTRACT_uid(x) x ++#define MAKE_gid(x) x ++#define MAKE_uid(x) x ++#else ++#define YPROC_uid(p) from_kuid(&init_user_ns, YCRED(p)->fsuid) ++#define YPROC_gid(p) from_kgid(&init_user_ns, YCRED(p)->fsgid) ++#define EXTRACT_gid(x) from_kgid(&init_user_ns, x) ++#define EXTRACT_uid(x) from_kuid(&init_user_ns, x) ++#define MAKE_gid(x) make_kgid(&init_user_ns, x) ++#define MAKE_uid(x) make_kuid(&init_user_ns, x) ++#endif ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, ++ dev_t rdev) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ dev_t rdev) ++#else ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ int rdev) ++#endif ++{ ++ struct inode *inode; ++ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_dev *dev; ++ ++ struct yaffs_obj *parent = yaffs_inode_to_obj(dir); ++ ++ int error = -ENOSPC; ++ uid_t uid = YPROC_uid(current); ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current); ++ ++ if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) ++ mode |= S_ISGID; ++ ++ if (parent) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: parent object %d type %d", ++ parent->obj_id, parent->variant_type); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: could not get parent object"); ++ return -EPERM; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: making oject for %s, mode %x dev %x", ++ dentry->d_name.name, mode, rdev); ++ ++ dev = parent->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ switch (mode & S_IFMT) { ++ default: ++ /* Special (socket, fifo, device...) */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, old_encode_dev(rdev)); ++#else ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, rdev); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); ++ obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, ++ gid); ++ break; ++ case S_IFDIR: /* directory */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); ++ obj = yaffs_create_dir(parent, dentry->d_name.name, mode, ++ uid, gid); ++ break; ++ case S_IFLNK: /* symlink */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); ++ obj = NULL; /* Do we ever get here? */ ++ break; ++ } ++ ++ /* Can not call yaffs_get_inode() with gross lock held */ ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod created object %d count = %d", ++ obj->obj_id, atomic_read(&inode->i_count)); ++ error = 0; ++ yaffs_fill_inode_from_obj(dir, parent); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); ++ error = -ENOMEM; ++ } ++ ++ return error; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++#else ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ int ret_val; ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir"); ++ ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); ++ return ret_val; ++} ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ bool dummy) ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ struct nameidata *n) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *n) ++#else ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_create"); ++ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ unsigned int dummy) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *n) ++#else ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) ++#endif ++{ ++ struct yaffs_obj *obj; ++ struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ ++ ++ struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s", ++ yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); ++ ++ obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); ++ ++ obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ ++ ++ /* Can't hold gross lock when calling yaffs_get_inode() */ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_lookup found %d", obj->obj_id); ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); ++ ++ } ++ ++/* added NCB for 2.5/6 compatability - forces add even if inode is ++ * NULL which creates dentry hash */ ++ d_add(dentry, inode); ++ ++ return NULL; ++} ++ ++/* ++ * Create a link... ++ */ ++static int yaffs_link(struct dentry *old_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ struct inode *inode = old_dentry->d_inode; ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *link = NULL; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ ++ link = ++ yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ obj); ++ ++ if (link) { ++ set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); ++ d_instantiate(dentry, old_dentry->d_inode); ++ atomic_inc(&old_dentry->d_inode->i_count); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_link link count %d i_count %d", ++ old_dentry->d_inode->i_nlink, ++ atomic_read(&old_dentry->d_inode->i_count)); ++ } ++ ++ yaffs_gross_unlock(dev); ++ ++ if (link) { ++ update_dir_time(dir); ++ return 0; ++ } ++ ++ return -EPERM; ++} ++ ++static int yaffs_symlink(struct inode *dir, struct dentry *dentry, ++ const char *symname) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ uid_t uid = YPROC_uid(current); ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); ++ ++ if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ return -ENAMETOOLONG; ++ ++ if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > ++ YAFFS_MAX_ALIAS_LENGTH) ++ return -ENAMETOOLONG; ++ ++ dev = yaffs_inode_to_obj(dir)->my_dev; ++ yaffs_gross_lock(dev); ++ obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ S_IFLNK | S_IRWXUGO, uid, gid, symname); ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ struct inode *inode; ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); ++ return 0; ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); ++ } ++ ++ return -ENOMEM; ++} ++ ++/* ++ * The VFS layer already does all the dentry stuff for rename. ++ * ++ * NB: POSIX says you can rename an object over an old object of the same name ++ */ ++static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ struct yaffs_dev *dev; ++ int ret_val = YAFFS_FAIL; ++ struct yaffs_obj *target; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); ++ dev = yaffs_inode_to_obj(old_dir)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ /* Check if the target is an existing directory that is not empty. */ ++ target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ ++ if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && ++ !list_empty(&target->variant.dir_variant.children)) { ++ ++ yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); ++ ++ ret_val = YAFFS_FAIL; ++ } else { ++ /* Now does unlinking internally using shadowing mechanism */ ++ yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); ++ ++ ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), ++ old_dentry->d_name.name, ++ yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ } ++ yaffs_gross_unlock(dev); ++ ++ if (ret_val == YAFFS_OK) { ++ if (target) ++ inode_dec_link_count(new_dentry->d_inode); ++ ++ update_dir_time(old_dir); ++ if (old_dir != new_dir) ++ update_dir_time(new_dir); ++ return 0; ++ } else { ++ return -ENOTEMPTY; ++ } ++} ++ ++ ++ ++ ++static int yaffs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int ret_val; ++ ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s", ++ (int)(dir->i_ino), dentry->d_name.name); ++ obj = yaffs_inode_to_obj(dir); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ ret_val = yaffs_unlinker(obj, dentry->d_name.name); ++ ++ if (ret_val == YAFFS_OK) { ++ inode_dec_link_count(dentry->d_inode); ++ dir->i_version++; ++ yaffs_gross_unlock(dev); ++ update_dir_time(dir); ++ return 0; ++ } ++ yaffs_gross_unlock(dev); ++ return -ENOTEMPTY; ++} ++ ++ ++ ++static const struct inode_operations yaffs_dir_inode_operations = { ++ .create = yaffs_create, ++ .lookup = yaffs_lookup, ++ .link = yaffs_link, ++ .unlink = yaffs_unlink, ++ .symlink = yaffs_symlink, ++ .mkdir = yaffs_mkdir, ++ .rmdir = yaffs_unlink, ++ .mknod = yaffs_mknod, ++ .rename = yaffs_rename, ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++/*-----------------------------------------------------------------*/ ++/* Directory search context allows us to unlock access to yaffs during ++ * filldir without causing problems with the directory being modified. ++ * This is similar to the tried and tested mechanism used in yaffs direct. ++ * ++ * A search context iterates along a doubly linked list of siblings in the ++ * directory. If the iterating object is deleted then this would corrupt ++ * the list iteration, likely causing a crash. The search context avoids ++ * this by using the remove_obj_fn to move the search context to the ++ * next object before the object is deleted. ++ * ++ * Many readdirs (and thus seach conexts) may be alive simulateously so ++ * each struct yaffs_dev has a list of these. ++ * ++ * A seach context lives for the duration of a readdir. ++ * ++ * All these functions must be called while yaffs is locked. ++ */ ++ ++struct yaffs_search_context { ++ struct yaffs_dev *dev; ++ struct yaffs_obj *dir_obj; ++ struct yaffs_obj *next_return; ++ struct list_head others; ++}; ++ ++/* ++ * yaffs_new_search() creates a new search context, initialises it and ++ * adds it to the device's search context list. ++ * ++ * Called at start of readdir. ++ */ ++static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) ++{ ++ struct yaffs_dev *dev = dir->my_dev; ++ struct yaffs_search_context *sc = ++ kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); ++ if (sc) { ++ sc->dir_obj = dir; ++ sc->dev = dev; ++ if (list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else ++ sc->next_return = ++ list_entry(dir->variant.dir_variant.children.next, ++ struct yaffs_obj, siblings); ++ INIT_LIST_HEAD(&sc->others); ++ list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); ++ } ++ return sc; ++} ++ ++/* ++ * yaffs_search_end() disposes of a search context and cleans up. ++ */ ++static void yaffs_search_end(struct yaffs_search_context *sc) ++{ ++ if (sc) { ++ list_del(&sc->others); ++ kfree(sc); ++ } ++} ++ ++/* ++ * yaffs_search_advance() moves a search context to the next object. ++ * Called when the search iterates or when an object removal causes ++ * the search context to be moved to the next object. ++ */ ++static void yaffs_search_advance(struct yaffs_search_context *sc) ++{ ++ if (!sc) ++ return; ++ ++ if (sc->next_return == NULL || ++ list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else { ++ struct list_head *next = sc->next_return->siblings.next; ++ ++ if (next == &sc->dir_obj->variant.dir_variant.children) ++ sc->next_return = NULL; /* end of list */ ++ else ++ sc->next_return = ++ list_entry(next, struct yaffs_obj, siblings); ++ } ++} ++ ++/* ++ * yaffs_remove_obj_callback() is called when an object is unlinked. ++ * We check open search contexts and advance any which are currently ++ * on the object being iterated. ++ */ ++static void yaffs_remove_obj_callback(struct yaffs_obj *obj) ++{ ++ ++ struct list_head *i; ++ struct yaffs_search_context *sc; ++ struct list_head *search_contexts = ++ &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); ++ ++ /* Iterate through the directory search contexts. ++ * If any are currently on the object being removed, then advance ++ * the search context to the next object to prevent a hanging pointer. ++ */ ++ list_for_each(i, search_contexts) { ++ sc = list_entry(i, struct yaffs_search_context, others); ++ if (sc->next_return == obj) ++ yaffs_search_advance(sc); ++ } ++ ++} ++ ++ ++/*-----------------------------------------------------------------*/ ++ ++#ifdef YAFFS_USE_DIR_ITERATE ++static int yaffs_iterate(struct file *f, struct dir_context *dc) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ unsigned long curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ if (!dir_emit_dots(f, dc)) ++ return 0; ++ ++ curoffs = 1; ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= dc->pos) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!dir_emit(dc, ++ name, ++ strlen(name), ++ this_inode, ++ this_type)) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ dc->pos++; ++ f->f_pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++ ++#else ++ ++static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ struct inode *inode = f->f_dentry->d_inode; ++ unsigned long offset, curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ offset = f->f_pos; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: starting at %d", (int)offset); ++ ++ if (offset == 0) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry . ino %d", ++ (int)inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ if (offset == 1) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry .. ino %d", ++ (int)f->f_dentry->d_parent->d_inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, "..", 2, offset, ++ f->f_dentry->d_parent->d_inode->i_ino, ++ DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ ++ curoffs = 1; ++ ++ /* If the directory has changed since the open or last call to ++ readdir, rewind to after the 2 canned entries. */ ++ if (f->f_version != inode->i_version) { ++ offset = 2; ++ f->f_pos = offset; ++ f->f_version = inode->i_version; ++ } ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= offset) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (filldir(dirent, ++ name, ++ strlen(name), ++ offset, this_inode, this_type) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ offset++; ++ f->f_pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++ ++#endif ++ ++static const struct file_operations yaffs_dir_operations = { ++ .read = generic_read_dir, ++#ifdef YAFFS_USE_DIR_ITERATE ++ .iterate = yaffs_iterate, ++#else ++ .readdir = yaffs_readdir, ++#endif ++ .fsync = yaffs_sync_object, ++ .llseek = generic_file_llseek, ++}; ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj) ++{ ++ if (inode && obj) { ++ ++ /* Check mode against the variant type and attempt to repair if broken. */ ++ u32 mode = obj->yst_mode; ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (!S_ISREG(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFREG; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ if (!S_ISLNK(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFLNK; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!S_ISDIR(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFDIR; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ default: ++ /* TODO? */ ++ break; ++ } ++ ++ inode->i_flags |= S_NOATIME; ++ ++ inode->i_ino = obj->obj_id; ++ inode->i_mode = obj->yst_mode; ++ inode->i_uid = MAKE_uid(obj->yst_uid); ++ inode->i_gid = MAKE_gid(obj->yst_gid); ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++ inode->i_blksize = inode->i_sb->s_blocksize; ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ ++ inode->i_rdev = old_decode_dev(obj->yst_rdev); ++ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); ++ inode->i_atime.tv_nsec = 0; ++ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; ++ inode->i_mtime.tv_nsec = 0; ++ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; ++ inode->i_ctime.tv_nsec = 0; ++#else ++ inode->i_rdev = obj->yst_rdev; ++ inode->i_atime = obj->yst_atime; ++ inode->i_mtime = obj->yst_mtime; ++ inode->i_ctime = obj->yst_ctime; ++#endif ++ inode->i_size = yaffs_get_obj_length(obj); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ ++ set_nlink(inode, yaffs_get_obj_link_count(obj)); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d", ++ inode->i_mode, obj->yst_uid, obj->yst_gid, ++ inode->i_size, atomic_read(&inode->i_count)); ++ ++ switch (obj->yst_mode & S_IFMT) { ++ default: /* fifo, device or socket */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ init_special_inode(inode, obj->yst_mode, ++ old_decode_dev(obj->yst_rdev)); ++#else ++ init_special_inode(inode, obj->yst_mode, ++ (dev_t) (obj->yst_rdev)); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ inode->i_op = &yaffs_file_inode_operations; ++ inode->i_fop = &yaffs_file_operations; ++ inode->i_mapping->a_ops = ++ &yaffs_file_address_operations; ++ break; ++ case S_IFDIR: /* directory */ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ break; ++ case S_IFLNK: /* symlink */ ++ inode->i_op = &yaffs_symlink_inode_operations; ++ break; ++ } ++ ++ yaffs_inode_to_obj_lv(inode) = obj; ++ ++ obj->my_inode = inode; ++ ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode invalid parameters"); ++ } ++ ++} ++ ++ ++ ++/* ++ * yaffs background thread functions . ++ * yaffs_bg_thread_fn() the thread function ++ * yaffs_bg_start() launches the background thread. ++ * yaffs_bg_stop() cleans up the background thread. ++ * ++ * NB: ++ * The thread should only run after the yaffs is initialised ++ * The thread should be stopped before yaffs is unmounted. ++ * The thread should not do any writing while the fs is in read only. ++ */ ++ ++static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) ++{ ++ unsigned erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned scattered = 0; /* Free chunks not in an erased block */ ++ ++ if (erased_chunks < dev->n_free_chunks) ++ scattered = (dev->n_free_chunks - erased_chunks); ++ ++ if (!context->bg_running) ++ return 0; ++ else if (scattered < (dev->param.chunks_per_block * 2)) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 2) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 4) ++ return 1; ++ else ++ return 2; ++} ++ ++#ifdef YAFFS_COMPILE_BACKGROUND ++ ++void yaffs_background_waker(unsigned long data) ++{ ++ wake_up_process((struct task_struct *)data); ++} ++ ++static int yaffs_bg_thread_fn(void *data) ++{ ++ struct yaffs_dev *dev = (struct yaffs_dev *)data; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned long now = jiffies; ++ unsigned long next_dir_update = now; ++ unsigned long next_gc = now; ++ unsigned long expires; ++ unsigned int urgency; ++ ++ int gc_result; ++ struct timer_list timer; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "yaffs_background starting for dev %p", (void *)dev); ++ ++#ifdef YAFFS_COMPILE_FREEZER ++ set_freezable(); ++#endif ++ while (context->bg_running) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); ++ ++ if (kthread_should_stop()) ++ break; ++ ++#ifdef YAFFS_COMPILE_FREEZER ++ if (try_to_freeze()) ++ continue; ++#endif ++ yaffs_gross_lock(dev); ++ ++ now = jiffies; ++ ++ if (time_after(now, next_dir_update) && yaffs_bg_enable) { ++ yaffs_update_dirty_dirs(dev); ++ next_dir_update = now + HZ; ++ } ++ ++ if (time_after(now, next_gc) && yaffs_bg_enable) { ++ if (!dev->is_checkpointed) { ++ urgency = yaffs_bg_gc_urgency(dev); ++ gc_result = yaffs_bg_gc(dev, urgency); ++ if (urgency > 1) ++ next_gc = now + HZ / 20 + 1; ++ else if (urgency > 0) ++ next_gc = now + HZ / 10 + 1; ++ else ++ next_gc = now + HZ * 2; ++ } else { ++ /* ++ * gc not running so set to next_dir_update ++ * to cut down on wake ups ++ */ ++ next_gc = next_dir_update; ++ } ++ } ++ yaffs_gross_unlock(dev); ++#if 1 ++ expires = next_dir_update; ++ if (time_before(next_gc, expires)) ++ expires = next_gc; ++ if (time_before(expires, now)) ++ expires = now + HZ; ++ ++ Y_INIT_TIMER(&timer); ++ timer.expires = expires + 1; ++ timer.data = (unsigned long)current; ++ timer.function = yaffs_background_waker; ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ add_timer(&timer); ++ schedule(); ++ del_timer_sync(&timer); ++#else ++ msleep(10); ++#endif ++ } ++ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ int retval = 0; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ ++ if (dev->read_only) ++ return -1; ++ ++ context->bg_running = 1; ++ ++ context->bg_thread = kthread_run(yaffs_bg_thread_fn, ++ (void *)dev, "yaffs-bg-%d", ++ context->mount_id); ++ ++ if (IS_ERR(context->bg_thread)) { ++ retval = PTR_ERR(context->bg_thread); ++ context->bg_thread = NULL; ++ context->bg_running = 0; ++ } ++ return retval; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); ++ ++ ctxt->bg_running = 0; ++ ++ if (ctxt->bg_thread) { ++ kthread_stop(ctxt->bg_thread); ++ ctxt->bg_thread = NULL; ++ } ++} ++#else ++static int yaffs_bg_thread_fn(void *data) ++{ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ return 0; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++} ++#endif ++ ++ ++static void yaffs_flush_inodes(struct super_block *sb) ++{ ++ struct inode *iptr; ++ struct yaffs_obj *obj; ++ ++ list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { ++ obj = yaffs_inode_to_obj(iptr); ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "flushing obj %d", ++ obj->obj_id); ++ yaffs_flush_file(obj, 1, 0, 0); ++ } ++ } ++} ++ ++static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ if (!dev) ++ return; ++ ++ yaffs_flush_inodes(sb); ++ yaffs_update_dirty_dirs(dev); ++ yaffs_flush_whole_cache(dev, 1); ++ if (do_checkpoint) ++ yaffs_checkpoint_save(dev); ++} ++ ++static LIST_HEAD(yaffs_context_list); ++struct mutex yaffs_context_lock; ++ ++static void yaffs_put_super(struct super_block *sb) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, ++ "yaffs_put_super"); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "Shutting down yaffs background thread"); ++ yaffs_bg_stop(dev); ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "yaffs background thread shut down"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_super(sb, 1); ++ ++ yaffs_deinitialise(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ mutex_lock(&yaffs_context_lock); ++ list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); ++ mutex_unlock(&yaffs_context_lock); ++ ++ if (yaffs_dev_to_lc(dev)->spare_buffer) { ++ kfree(yaffs_dev_to_lc(dev)->spare_buffer); ++ yaffs_dev_to_lc(dev)->spare_buffer = NULL; ++ } ++ ++ kfree(dev); ++ ++ yaffs_put_mtd_device(mtd); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, ++ "yaffs_put_super done"); ++} ++ ++ ++static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) ++{ ++ return yaffs_gc_control; ++} ++ ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++ ++static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, ++ uint32_t generation) ++{ ++ return Y_IGET(sb, ino); ++} ++ ++static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_parent(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++struct dentry *yaffs2_get_parent(struct dentry *dentry) ++{ ++ ++ struct super_block *sb = dentry->d_inode->i_sb; ++ struct dentry *parent = ERR_PTR(-ENOENT); ++ struct inode *inode; ++ unsigned long parent_ino; ++ struct yaffs_obj *d_obj; ++ struct yaffs_obj *parent_obj; ++ ++ d_obj = yaffs_inode_to_obj(dentry->d_inode); ++ ++ if (d_obj) { ++ parent_obj = d_obj->parent; ++ if (parent_obj) { ++ parent_ino = yaffs_get_obj_inode(parent_obj); ++ inode = Y_IGET(sb, parent_ino); ++ ++ if (IS_ERR(inode)) { ++ parent = ERR_CAST(inode); ++ } else { ++ parent = d_obtain_alias(inode); ++ if (!IS_ERR(parent)) { ++ parent = ERR_PTR(-ENOMEM); ++ iput(inode); ++ } ++ } ++ } ++ } ++ ++ return parent; ++} ++ ++/* Just declare a zero structure as a NULL value implies ++ * using the default functions of exportfs. ++ */ ++ ++static struct export_operations yaffs_export_ops = { ++ .fh_to_dentry = yaffs2_fh_to_dentry, ++ .fh_to_parent = yaffs2_fh_to_parent, ++ .get_parent = yaffs2_get_parent, ++}; ++ ++#endif ++ ++static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) ++{ ++ /* Clear the association between the inode and ++ * the struct yaffs_obj. ++ */ ++ obj->my_inode = NULL; ++ yaffs_inode_to_obj_lv(inode) = NULL; ++ ++ /* If the object freeing was deferred, then the real ++ * free happens now. ++ * This should fix the inode inconsistency problem. ++ */ ++ yaffs_handle_defered_free(obj); ++} ++ ++#ifdef YAFFS_HAS_EVICT_INODE ++/* yaffs_evict_inode combines into one operation what was previously done in ++ * yaffs_clear_inode() and yaffs_delete_inode() ++ * ++ */ ++static void yaffs_evict_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ int deleteme = 0; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_evict_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (!inode->i_nlink && !is_bad_inode(inode)) ++ deleteme = 1; ++ truncate_inode_pages(&inode->i_data, 0); ++ Y_CLEAR_INODE(inode); ++ ++ if (deleteme && obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++} ++#else ++ ++/* clear is called to tell the fs to release any per-inode data it holds. ++ * The object might still exist on disk and is just being thrown out of the cache ++ * or else the object has actually been deleted and we're being called via ++ * the chain ++ * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() ++ */ ++ ++static void yaffs_clear_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_clear_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++ ++} ++ ++/* delete is called when the link count is zero and the inode ++ * is put (ie. nobody wants to know about it anymore, time to ++ * delete the file). ++ * NB Must call clear_inode() ++ */ ++static void yaffs_delete_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_delete_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) ++ truncate_inode_pages(&inode->i_data, 0); ++#endif ++ clear_inode(inode); ++} ++#endif ++ ++ ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ struct super_block *sb = dentry->d_sb; ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++#else ++static int yaffs_statfs(struct super_block *sb, struct statfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++#endif ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); ++ ++ yaffs_gross_lock(dev); ++ ++ buf->f_type = YAFFS_MAGIC; ++ buf->f_bsize = sb->s_blocksize; ++ buf->f_namelen = 255; ++ ++ if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { ++ /* Do this if chunk size is not a power of 2 */ ++ ++ uint64_t bytes_in_dev; ++ uint64_t bytes_free; ++ ++ bytes_in_dev = ++ ((uint64_t) ++ ((dev->param.end_block - dev->param.start_block + ++ 1))) * ((uint64_t) (dev->param.chunks_per_block * ++ dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ ++ buf->f_blocks = bytes_in_dev; ++ ++ bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * ++ ((uint64_t) (dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_free, sb->s_blocksize); ++ ++ buf->f_bfree = bytes_free; ++ ++ } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { ++ ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ } else { ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ } ++ ++ buf->f_files = 0; ++ buf->f_ffree = 0; ++ buf->f_bavail = buf->f_bfree; ++ ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++ ++ ++static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) ++{ ++ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); ++ unsigned gc_urgent = yaffs_bg_gc_urgency(dev); ++ int do_checkpoint; ++ int dirty = yaffs_check_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_do_sync_fs: gc-urgency %d %s %s%s", ++ gc_urgent, ++ dirty ? "dirty" : "clean", ++ request_checkpoint ? "checkpoint requested" : "no checkpoint", ++ oneshot_checkpoint ? " one-shot" : ""); ++ ++ yaffs_gross_lock(dev); ++ do_checkpoint = ((request_checkpoint && !gc_urgent) || ++ oneshot_checkpoint) && !dev->is_checkpointed; ++ ++ if (dirty || do_checkpoint) { ++ yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); ++ yaffs_clear_super_dirty(dev); ++ if (oneshot_checkpoint) ++ yaffs_auto_checkpoint &= ~4; ++ } ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++ ++#ifdef YAFFS_HAS_WRITE_SUPER ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static void yaffs_write_super(struct super_block *sb) ++#else ++static int yaffs_write_super(struct super_block *sb) ++#endif ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_write_super %s", ++ request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) ++ return 0; ++#endif ++} ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_sync_fs(struct super_block *sb, int wait) ++#else ++static int yaffs_sync_fs(struct super_block *sb) ++#endif ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++ return 0; ++} ++ ++/* the function only is used to change dev->read_only when this file system ++ * is remounted. ++ */ ++static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) ++{ ++ int read_only = 0; ++ struct mtd_info *mtd; ++ struct yaffs_dev *dev = 0; ++ ++ /* Get the device */ ++ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); ++ if (!mtd) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device #%u doesn't appear to exist", ++ MINOR(sb->s_dev)); ++ return 1; ++ } ++ ++ /* Check it's NAND */ ++ if (mtd->type != MTD_NANDFLASH) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device is not NAND it's type %d", ++ mtd->type); ++ return 1; ++ } ++ ++ read_only = ((*flags & MS_RDONLY) != 0); ++ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { ++ read_only = 1; ++ printk(KERN_INFO ++ "yaffs: mtd is read only, setting superblock read only"); ++ *flags |= MS_RDONLY; ++ } ++ ++ dev = sb->s_fs_info; ++ dev->read_only = read_only; ++ ++ return 0; ++} ++ ++static const struct super_operations yaffs_super_ops = { ++ .statfs = yaffs_statfs, ++ ++#ifndef YAFFS_USE_OWN_IGET ++ .read_inode = yaffs_read_inode, ++#endif ++#ifdef YAFFS_HAS_PUT_INODE ++ .put_inode = yaffs_put_inode, ++#endif ++ .put_super = yaffs_put_super, ++#ifdef YAFFS_HAS_EVICT_INODE ++ .evict_inode = yaffs_evict_inode, ++#else ++ .delete_inode = yaffs_delete_inode, ++ .clear_inode = yaffs_clear_inode, ++#endif ++ .sync_fs = yaffs_sync_fs, ++#ifdef YAFFS_HAS_WRITE_SUPER ++ .write_super = yaffs_write_super, ++#endif ++ .remount_fs = yaffs_remount_fs, ++}; ++ ++struct yaffs_options { ++ int inband_tags; ++ int skip_checkpoint_read; ++ int skip_checkpoint_write; ++ int no_cache; ++ int tags_ecc_on; ++ int tags_ecc_overridden; ++ int lazy_loading_enabled; ++ int lazy_loading_overridden; ++ int empty_lost_and_found; ++ int empty_lost_and_found_overridden; ++ int disable_summary; ++}; ++ ++#define MAX_OPT_LEN 30 ++static int yaffs_parse_options(struct yaffs_options *options, ++ const char *options_str) ++{ ++ char cur_opt[MAX_OPT_LEN + 1]; ++ int p; ++ int error = 0; ++ ++ /* Parse through the options which is a comma seperated list */ ++ ++ while (options_str && *options_str && !error) { ++ memset(cur_opt, 0, MAX_OPT_LEN + 1); ++ p = 0; ++ ++ while (*options_str == ',') ++ options_str++; ++ ++ while (*options_str && *options_str != ',') { ++ if (p < MAX_OPT_LEN) { ++ cur_opt[p] = *options_str; ++ p++; ++ } ++ options_str++; ++ } ++ ++ if (!strcmp(cur_opt, "inband-tags")) { ++ options->inband_tags = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-off")) { ++ options->tags_ecc_on = 0; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-on")) { ++ options->tags_ecc_on = 1; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-off")) { ++ options->lazy_loading_enabled = 0; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-on")) { ++ options->lazy_loading_enabled = 1; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "disable-summary")) { ++ options->disable_summary = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { ++ options->empty_lost_and_found = 0; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { ++ options->empty_lost_and_found = 1; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "no-cache")) { ++ options->no_cache = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-read")) { ++ options->skip_checkpoint_read = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-write")) { ++ options->skip_checkpoint_write = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint")) { ++ options->skip_checkpoint_read = 1; ++ options->skip_checkpoint_write = 1; ++ } else { ++ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", ++ cur_opt); ++ error = 1; ++ } ++ } ++ ++ return error; ++} ++ ++ ++static struct dentry *yaffs_make_root(struct inode *inode) ++{ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) ++ struct dentry *root = d_alloc_root(inode); ++ ++ if (!root) ++ iput(inode); ++ ++ return root; ++#else ++ return d_make_root(inode); ++#endif ++} ++ ++ ++ ++ ++static struct super_block *yaffs_internal_read_super(int yaffs_version, ++ struct super_block *sb, ++ void *data, int silent) ++{ ++ int n_blocks; ++ struct inode *inode = NULL; ++ struct dentry *root; ++ struct yaffs_dev *dev = 0; ++ char devname_buf[BDEVNAME_SIZE + 1]; ++ struct mtd_info *mtd; ++ int err; ++ char *data_str = (char *)data; ++ struct yaffs_linux_context *context = NULL; ++ struct yaffs_param *param; ++ ++ int read_only = 0; ++ int inband_tags = 0; ++ ++ struct yaffs_options options; ++ ++ unsigned mount_id; ++ int found; ++ struct yaffs_linux_context *context_iterator; ++ struct list_head *l; ++ ++ if (!sb) { ++ printk(KERN_INFO "yaffs: sb is NULL\n"); ++ return NULL; ++ } ++ ++ sb->s_magic = YAFFS_MAGIC; ++ sb->s_op = &yaffs_super_ops; ++ sb->s_flags |= MS_NOATIME; ++ ++ read_only = ((sb->s_flags & MS_RDONLY) != 0); ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++ sb->s_export_op = &yaffs_export_ops; ++#endif ++ ++ if (!sb->s_dev) ++ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); ++ else if (!yaffs_devname(sb, devname_buf)) ++ printk(KERN_INFO "yaffs: devname is NULL\n"); ++ else ++ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", ++ sb->s_dev, ++ yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); ++ ++ if (!data_str) ++ data_str = ""; ++ ++ printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); ++ ++ memset(&options, 0, sizeof(options)); ++ ++ if (yaffs_parse_options(&options, data_str)) { ++ /* Option parsing failed */ ++ return NULL; ++ } ++ ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: Using yaffs%d", yaffs_version); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Attempting MTD mount of %u.%u,\"%s\"", ++ MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ yaffs_devname(sb, devname_buf)); ++ ++ /* Get the device */ ++ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); ++ if (IS_ERR(mtd)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: MTD device %u either not valid or unavailable", ++ MINOR(sb->s_dev)); ++ return NULL; ++ } ++ ++ if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); ++ yaffs_version = 2; ++ } ++ ++ /* Added NCB 26/5/2006 for completeness */ ++ if (yaffs_version == 2 && !options.inband_tags ++ && WRITE_SIZE(mtd) == 512) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); ++ yaffs_version = 1; ++ } ++ ++ if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || ++ options.inband_tags) ++ inband_tags = 1; ++ ++ if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) ++ return NULL; ++ ++ /* OK, so if we got here, we have an MTD that's NAND and looks ++ * like it has the right capabilities ++ * Set the struct yaffs_dev up for mtd ++ */ ++ ++ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { ++ read_only = 1; ++ printk(KERN_INFO ++ "yaffs: mtd is read only, setting superblock read only\n" ++ ); ++ sb->s_flags |= MS_RDONLY; ++ } ++ ++ dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); ++ context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); ++ ++ if (!dev || !context) { ++ kfree(dev); ++ kfree(context); ++ dev = NULL; ++ context = NULL; ++ ++ /* Deep shit could not allocate device structure */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: Failed trying to allocate struct yaffs_dev." ++ ); ++ return NULL; ++ } ++ memset(dev, 0, sizeof(struct yaffs_dev)); ++ param = &(dev->param); ++ ++ memset(context, 0, sizeof(struct yaffs_linux_context)); ++ dev->os_context = context; ++ INIT_LIST_HEAD(&(context->context_list)); ++ context->dev = dev; ++ context->super = sb; ++ ++ dev->read_only = read_only; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ sb->s_fs_info = dev; ++#else ++ sb->u.generic_sbp = dev; ++#endif ++ ++ ++ dev->driver_context = mtd; ++ param->name = mtd->name; ++ ++ /* Set up the memory size parameters.... */ ++ ++ ++ param->n_reserved_blocks = 5; ++ param->n_caches = (options.no_cache) ? 0 : 10; ++ param->inband_tags = inband_tags; ++ ++ param->enable_xattr = 1; ++ if (options.lazy_loading_overridden) ++ param->disable_lazy_load = !options.lazy_loading_enabled; ++ ++ param->defered_dir_update = 1; ++ ++ if (options.tags_ecc_overridden) ++ param->no_tags_ecc = !options.tags_ecc_on; ++ ++ param->empty_lost_n_found = 1; ++ param->refresh_period = 500; ++ param->disable_summary = options.disable_summary; ++ ++ ++#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING ++ param->disable_bad_block_marking = 1; ++#endif ++ if (options.empty_lost_and_found_overridden) ++ param->empty_lost_n_found = options.empty_lost_and_found; ++ ++ /* ... and the functions. */ ++ if (yaffs_version == 2) { ++ param->is_yaffs2 = 1; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++ param->total_bytes_per_chunk = mtd->writesize; ++ param->chunks_per_block = mtd->erasesize / mtd->writesize; ++#else ++ param->total_bytes_per_chunk = mtd->oobblock; ++ param->chunks_per_block = mtd->erasesize / mtd->oobblock; ++#endif ++ n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ } else { ++ param->is_yaffs2 = 0; ++ n_blocks = YCALCBLOCKS(mtd->size, ++ YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); ++ ++ param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; ++ param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; ++ } ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ ++ yaffs_mtd_drv_install(dev); ++ ++ param->sb_dirty_fn = yaffs_set_super_dirty; ++ param->gc_control_fn = yaffs_gc_control_callback; ++ ++ yaffs_dev_to_lc(dev)->super = sb; ++ ++ param->use_nand_ecc = 1; ++ ++ param->skip_checkpt_rd = options.skip_checkpoint_read; ++ param->skip_checkpt_wr = options.skip_checkpoint_write; ++ ++ mutex_lock(&yaffs_context_lock); ++ /* Get a mount id */ ++ found = 0; ++ for (mount_id = 0; !found; mount_id++) { ++ found = 1; ++ list_for_each(l, &yaffs_context_list) { ++ context_iterator = ++ list_entry(l, struct yaffs_linux_context, ++ context_list); ++ if (context_iterator->mount_id == mount_id) ++ found = 0; ++ } ++ } ++ context->mount_id = mount_id; ++ ++ list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), ++ &yaffs_context_list); ++ mutex_unlock(&yaffs_context_lock); ++ ++ /* Directory search handling... */ ++ INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); ++ param->remove_obj_fn = yaffs_remove_obj_callback; ++ ++ mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ ++ yaffs_gross_lock(dev); ++ ++ err = yaffs_guts_initialise(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: guts initialised %s", ++ (err == YAFFS_OK) ? "OK" : "FAILED"); ++ ++ if (err == YAFFS_OK) ++ yaffs_bg_start(dev); ++ ++ if (!context->bg_thread) ++ param->defered_dir_update = 0; ++ ++ sb->s_maxbytes = yaffs_max_file_size(dev); ++ ++ /* Release lock before yaffs_get_inode() */ ++ yaffs_gross_unlock(dev); ++ ++ /* Create root inode */ ++ if (err == YAFFS_OK) ++ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); ++ ++ if (!inode) ++ return NULL; ++ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); ++ ++ root = yaffs_make_root(inode); ++ ++ if (!root) ++ return NULL; ++ ++ sb->s_root = root; ++ if(!dev->is_checkpointed) ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); ++ return sb; ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd); ++} ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data, struct vfsmount *mnt) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs", ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ .mount = yaffs_mount, ++#else ++ .get_sb = yaffs_read_super, ++#endif ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs_read_super(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd); ++} ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, void *data, ++ struct vfsmount *mnt) ++{ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs2_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs2", ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ .mount = yaffs2_mount, ++#else ++ .get_sb = yaffs2_read_super, ++#endif ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs2_read_super(struct super_block *sb, ++ void *data, int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++static struct proc_dir_entry *my_proc_entry; ++ ++static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) ++{ ++ struct yaffs_param *param = &dev->param; ++ int bs[10]; ++ ++ yaffs_count_blocks_by_state(dev,bs); ++ ++ buf += sprintf(buf, "start_block.......... %d\n", param->start_block); ++ buf += sprintf(buf, "end_block............ %d\n", param->end_block); ++ buf += sprintf(buf, "total_bytes_per_chunk %d\n", ++ param->total_bytes_per_chunk); ++ buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc); ++ buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc); ++ buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2); ++ buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags); ++ buf += sprintf(buf, "empty_lost_n_found... %d\n", ++ param->empty_lost_n_found); ++ buf += sprintf(buf, "disable_lazy_load.... %d\n", ++ param->disable_lazy_load); ++ buf += sprintf(buf, "disable_bad_block_mrk %d\n", ++ param->disable_bad_block_marking); ++ buf += sprintf(buf, "refresh_period....... %d\n", ++ param->refresh_period); ++ buf += sprintf(buf, "n_caches............. %d\n", param->n_caches); ++ buf += sprintf(buf, "n_reserved_blocks.... %d\n", ++ param->n_reserved_blocks); ++ buf += sprintf(buf, "always_check_erased.. %d\n", ++ param->always_check_erased); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "block count by state\n"); ++ buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n", ++ bs[0], bs[1], bs[2], bs[3], bs[4]); ++ buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n", ++ bs[5], bs[6], bs[7], bs[8], bs[9]); ++ ++ return buf; ++} ++ ++static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) ++{ ++ buf += sprintf(buf, "max file size....... %lld\n", ++ (long long) yaffs_max_file_size(dev)); ++ buf += sprintf(buf, "data_bytes_per_chunk. %d\n", ++ dev->data_bytes_per_chunk); ++ buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits); ++ buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size); ++ buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks); ++ buf += sprintf(buf, "blocks_in_checkpt.... %d\n", ++ dev->blocks_in_checkpt); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes); ++ buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj); ++ buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes); ++ buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads); ++ buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures); ++ buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies); ++ buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs); ++ buf += sprintf(buf, "passive_gc_count..... %u\n", ++ dev->passive_gc_count); ++ buf += sprintf(buf, "oldest_dirty_gc_count %u\n", ++ dev->oldest_dirty_gc_count); ++ buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks); ++ buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs); ++ buf += sprintf(buf, "n_retried_writes..... %u\n", ++ dev->n_retried_writes); ++ buf += sprintf(buf, "n_retired_blocks..... %u\n", ++ dev->n_retired_blocks); ++ buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed); ++ buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed); ++ buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n", ++ dev->n_tags_ecc_fixed); ++ buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n", ++ dev->n_tags_ecc_unfixed); ++ buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits); ++ buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files); ++ buf += sprintf(buf, "n_unlinked_files..... %u\n", ++ dev->n_unlinked_files); ++ buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count); ++ buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions); ++ buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used); ++ buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used); ++ ++ return buf; ++} ++ ++static int yaffs_proc_read(char *page, ++ char **start, ++ off_t offset, int count, int *eof, void *data) ++{ ++ struct list_head *item; ++ char *buf = page; ++ int step = offset; ++ int n = 0; ++ ++ /* Get proc_file_read() to step 'offset' by one on each sucessive call. ++ * We use 'offset' (*ppos) to indicate where we are in dev_list. ++ * This also assumes the user has posted a read buffer large ++ * enough to hold the complete output; but that's life in /proc. ++ */ ++ ++ *(int *)start = 1; ++ ++ /* Print header first */ ++ if (step == 0) ++ buf += ++ sprintf(buf, ++ "Multi-version YAFFS built: \n"); ++ else if (step == 1) ++ buf += sprintf(buf, "\n"); ++ else { ++ step -= 2; ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (n < (step & ~1)) { ++ n += 2; ++ continue; ++ } ++ if ((step & 1) == 0) { ++ buf += ++ sprintf(buf, "\nDevice %d \"%s\"\n", n, ++ dev->param.name); ++ buf = yaffs_dump_dev_part0(buf, dev); ++ } else { ++ buf = yaffs_dump_dev_part1(buf, dev); ++ } ++ ++ break; ++ } ++ mutex_unlock(&yaffs_context_lock); ++ } ++ ++ return buf - page < count ? buf - page : count; ++} ++ ++/** ++ * Set the verbosity of the warnings and error messages. ++ * ++ * Note that the names can only be a..z or _ with the current code. ++ */ ++ ++static struct { ++ char *mask_name; ++ unsigned mask_bitfield; ++} mask_flags[] = { ++ {"allocate", YAFFS_TRACE_ALLOCATE}, ++ {"always", YAFFS_TRACE_ALWAYS}, ++ {"background", YAFFS_TRACE_BACKGROUND}, ++ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, ++ {"buffers", YAFFS_TRACE_BUFFERS}, ++ {"bug", YAFFS_TRACE_BUG}, ++ {"checkpt", YAFFS_TRACE_CHECKPOINT}, ++ {"deletion", YAFFS_TRACE_DELETION}, ++ {"erase", YAFFS_TRACE_ERASE}, ++ {"error", YAFFS_TRACE_ERROR}, ++ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, ++ {"gc", YAFFS_TRACE_GC}, ++ {"lock", YAFFS_TRACE_LOCK}, ++ {"mtd", YAFFS_TRACE_MTD}, ++ {"nandaccess", YAFFS_TRACE_NANDACCESS}, ++ {"os", YAFFS_TRACE_OS}, ++ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, ++ {"scan", YAFFS_TRACE_SCAN}, ++ {"mount", YAFFS_TRACE_MOUNT}, ++ {"tracing", YAFFS_TRACE_TRACING}, ++ {"sync", YAFFS_TRACE_SYNC}, ++ {"write", YAFFS_TRACE_WRITE}, ++ {"verify", YAFFS_TRACE_VERIFY}, ++ {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, ++ {"verify_full", YAFFS_TRACE_VERIFY_FULL}, ++ {"verify_all", YAFFS_TRACE_VERIFY_ALL}, ++ {"all", 0xffffffff}, ++ {"none", 0}, ++ {NULL, 0}, ++}; ++ ++#define MAX_MASK_NAME_LENGTH 40 ++static int yaffs_proc_write_trace_options(struct file *file, const char *buf, ++ unsigned long count) ++{ ++ unsigned rg = 0, mask_bitfield; ++ char *end; ++ char *mask_name; ++ const char *x; ++ char substring[MAX_MASK_NAME_LENGTH + 1]; ++ int i; ++ int done = 0; ++ int add, len = 0; ++ int pos = 0; ++ ++ rg = yaffs_trace_mask; ++ ++ while (!done && (pos < count)) { ++ done = 1; ++ while ((pos < count) && isspace(buf[pos])) ++ pos++; ++ ++ switch (buf[pos]) { ++ case '+': ++ case '-': ++ case '=': ++ add = buf[pos]; ++ pos++; ++ break; ++ ++ default: ++ add = ' '; ++ break; ++ } ++ mask_name = NULL; ++ ++ mask_bitfield = simple_strtoul(buf + pos, &end, 0); ++ ++ if (end > buf + pos) { ++ mask_name = "numeral"; ++ len = end - (buf + pos); ++ pos += len; ++ done = 0; ++ } else { ++ for (x = buf + pos, i = 0; ++ (*x == '_' || (*x >= 'a' && *x <= 'z')) && ++ i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) ++ substring[i] = *x; ++ substring[i] = '\0'; ++ ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ if (strcmp(substring, mask_flags[i].mask_name) ++ == 0) { ++ mask_name = mask_flags[i].mask_name; ++ mask_bitfield = ++ mask_flags[i].mask_bitfield; ++ done = 0; ++ break; ++ } ++ } ++ } ++ ++ if (mask_name != NULL) { ++ done = 0; ++ switch (add) { ++ case '-': ++ rg &= ~mask_bitfield; ++ break; ++ case '+': ++ rg |= mask_bitfield; ++ break; ++ case '=': ++ rg = mask_bitfield; ++ break; ++ default: ++ rg |= mask_bitfield; ++ break; ++ } ++ } ++ } ++ ++ yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; ++ ++ printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); ++ ++ if (rg & YAFFS_TRACE_ALWAYS) { ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ char flag; ++ flag = ((rg & mask_flags[i].mask_bitfield) == ++ mask_flags[i].mask_bitfield) ? '+' : '-'; ++ printk(KERN_DEBUG "%c%s\n", flag, ++ mask_flags[i].mask_name); ++ } ++ } ++ ++ return count; ++} ++ ++/* Debug strings are of the form: ++ * .bnnn print info on block n ++ * .cobjn,chunkn print nand chunk id for objn:chunkn ++ */ ++ ++static int yaffs_proc_debug_write(struct file *file, const char *buf, ++ unsigned long count) ++{ ++ ++ char str[100]; ++ char *p0; ++ char *p1; ++ long p1_val; ++ long p0_val; ++ char cmd; ++ struct list_head *item; ++ ++ memset(str, 0, sizeof(str)); ++ memcpy(str, buf, min((size_t)count, sizeof(str) -1)); ++ ++ cmd = str[1]; ++ ++ p0 = str + 2; ++ ++ p1 = p0; ++ ++ while (*p1 && *p1 != ',') { ++ p1++; ++ } ++ *p1 = '\0'; ++ p1++; ++ ++ p0_val = simple_strtol(p0, NULL, 0); ++ p1_val = simple_strtol(p1, NULL, 0); ++ ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (cmd == 'b') { ++ struct yaffs_block_info *bi; ++ ++ bi = yaffs_get_block_info(dev,p0_val); ++ ++ if(bi) { ++ printk("Block %d: state %d, retire %d, use %d, seq %d\n", ++ (int)p0_val, bi->block_state, ++ bi->needs_retiring, bi->pages_in_use, ++ bi->seq_number); ++ } ++ } else if (cmd == 'c') { ++ struct yaffs_obj *obj; ++ int nand_chunk; ++ ++ obj = yaffs_find_by_number(dev, p0_val); ++ if (!obj) ++ printk("No obj %d\n", (int)p0_val); ++ else { ++ if(p1_val == 0) ++ nand_chunk = obj->hdr_chunk; ++ else ++ nand_chunk = ++ yaffs_find_chunk_in_file(obj, ++ p1_val, NULL); ++ printk("Nand chunk for %d:%d is %d\n", ++ (int)p0_val, (int)p1_val, nand_chunk); ++ } ++ } ++ } ++ ++ mutex_unlock(&yaffs_context_lock); ++ ++ return count; ++} ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) ++static int yaffs_proc_write(struct file *file, const char *buf, ++ unsigned long count, void *ppos) ++#else ++static ssize_t yaffs_proc_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++#endif ++{ ++ if (buf[0] == '.') ++ return yaffs_proc_debug_write(file, buf, count); ++ return yaffs_proc_write_trace_options(file, buf, count); ++} ++ ++/* Stuff to handle installation of file systems */ ++struct file_system_to_install { ++ struct file_system_type *fst; ++ int installed; ++}; ++ ++static struct file_system_to_install fs_to_install[] = { ++ {&yaffs_fs_type, 0}, ++ {&yaffs2_fs_type, 0}, ++ {NULL, 0} ++}; ++ ++ ++#ifdef YAFFS_NEW_PROCFS ++static int yaffs_proc_show(struct seq_file *m, void *v) ++{ ++ /* FIXME: Unify in a better way? */ ++ char buffer[512]; ++ char *start; ++ int len; ++ ++ len = yaffs_proc_read(buffer, &start, 0, sizeof(buffer), NULL, NULL); ++ seq_puts(m, buffer); ++ return 0; ++} ++ ++static int yaffs_proc_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, yaffs_proc_show, NULL); ++} ++ ++static struct file_operations procfs_ops = { ++ .owner = THIS_MODULE, ++ .open = yaffs_proc_open, ++ .read = seq_read, ++ .write = yaffs_proc_write, ++}; ++ ++static int yaffs_procfs_init(void) ++{ ++ /* Install the proc_fs entries */ ++ my_proc_entry = proc_create("yaffs", ++ S_IRUGO | S_IFREG, ++ YPROC_ROOT, ++ &procfs_ops); ++ ++ if (my_proc_entry) { ++ return 0; ++ } else { ++ return -ENOMEM; ++ } ++} ++ ++#else ++ ++ ++static int yaffs_procfs_init(void) ++{ ++ /* Install the proc_fs entries */ ++ my_proc_entry = create_proc_entry("yaffs", ++ S_IRUGO | S_IFREG, YPROC_ROOT); ++ ++ if (my_proc_entry) { ++ my_proc_entry->write_proc = yaffs_proc_write; ++ my_proc_entry->read_proc = yaffs_proc_read; ++ my_proc_entry->data = NULL; ++ return 0; ++ } else { ++ return -ENOMEM; ++ } ++} ++ ++#endif ++ ++ ++static int __init init_yaffs_fs(void) ++{ ++ int error = 0; ++ struct file_system_to_install *fsinst; ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs built Installing."); ++ ++ mutex_init(&yaffs_context_lock); ++ ++ error = yaffs_procfs_init(); ++ if (error) ++ return error; ++ ++ /* Now add the file system entries */ ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst && !error) { ++ error = register_filesystem(fsinst->fst); ++ if (!error) ++ fsinst->installed = 1; ++ fsinst++; ++ } ++ ++ /* Any errors? uninstall */ ++ if (error) { ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++ } ++ ++ return error; ++} ++ ++static void __exit exit_yaffs_fs(void) ++{ ++ ++ struct file_system_to_install *fsinst; ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs built removing."); ++ ++ remove_proc_entry("yaffs", YPROC_ROOT); ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++} ++ ++module_init(init_yaffs_fs) ++module_exit(exit_yaffs_fs) ++ ++MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); ++MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); ++MODULE_LICENSE("GPL"); +diff --git a/fs/yaffs2/yaffs_yaffs1.c b/fs/yaffs2/yaffs_yaffs1.c +new file mode 100644 +index 0000000..d277e20 +--- /dev/null ++++ b/fs/yaffs2/yaffs_yaffs1.c +@@ -0,0 +1,422 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_yaffs1.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++#include "yaffs_attribs.h" ++ ++int yaffs1_scan(struct yaffs_dev *dev) ++{ ++ struct yaffs_ext_tags tags; ++ int blk; ++ int result; ++ int chunk; ++ int c; ++ int deleted; ++ enum yaffs_block_state state; ++ LIST_HEAD(hard_list); ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int alloc_failed = 0; ++ struct yaffs_shadow_fixer *shadow_fixers = NULL; ++ u8 *chunk_data; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs1_scan starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ chunk_data = yaffs_get_temp_buffer(dev); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, state, seq_number); ++ ++ if (state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } ++ bi++; ++ } ++ ++ /* For each block.... */ ++ for (blk = dev->internal_start_block; ++ !alloc_failed && blk <= dev->internal_end_block; blk++) { ++ ++ cond_resched(); ++ ++ bi = yaffs_get_block_info(dev, blk); ++ state = bi->block_state; ++ ++ deleted = 0; ++ ++ /* For each chunk in each block that needs scanning.... */ ++ for (c = 0; ++ !alloc_failed && c < dev->param.chunks_per_block && ++ state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { ++ /* Read the tags and decide what to do */ ++ chunk = blk * dev->param.chunks_per_block + c; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, ++ &tags); ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || ++ tags.is_deleted) { ++ /* YAFFS1 only... ++ * A deleted chunk ++ */ ++ deleted++; ++ dev->n_free_chunks++; ++ } else if (!tags.chunk_used) { ++ /* An unassigned chunk in the block ++ * This means that either the block is empty or ++ * this is the one being allocated from ++ */ ++ ++ if (c == 0) { ++ /* We're looking at the first chunk in ++ *the block so the block is unused */ ++ state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ /* this is the block being allocated */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, c); ++ state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = c; ++ dev->alloc_block_finder = blk; ++ ++ } ++ ++ dev->n_free_chunks += ++ (dev->param.chunks_per_block - c); ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ unsigned int endpos; ++ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ /* PutChunkIntoFile checks for a clash ++ * (two data chunks with the same chunk_id). ++ */ ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in) { ++ if (!yaffs_put_chunk_in_file ++ (in, tags.chunk_id, chunk, 1)) ++ alloc_failed = 1; ++ } ++ ++ endpos = ++ (tags.chunk_id - 1) * ++ dev->data_bytes_per_chunk + ++ tags.n_bytes; ++ if (in && ++ in->variant_type == ++ YAFFS_OBJECT_TYPE_FILE && ++ in->variant.file_variant.scanned_size < ++ endpos) { ++ in->variant.file_variant.scanned_size = ++ endpos; ++ if (!dev->param.use_header_file_size) { ++ in->variant. ++ file_variant.file_size = ++ in->variant. ++ file_variant.scanned_size; ++ } ++ ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Make the object ++ */ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ in = yaffs_find_by_number(dev, tags.obj_id); ++ if (in && in->variant_type != oh->type) { ++ /* This should not happen, but somehow ++ * Wev'e ended up with an obj_id that ++ * has been reused but not yet deleted, ++ * and worse still it has changed type. ++ * Delete the old object. ++ */ ++ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ oh->type); ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in && oh->shadows_obj > 0) { ++ ++ struct yaffs_shadow_fixer *fixer; ++ fixer = ++ kmalloc(sizeof ++ (struct yaffs_shadow_fixer), ++ GFP_NOFS); ++ if (fixer) { ++ fixer->next = shadow_fixers; ++ shadow_fixers = fixer; ++ fixer->obj_id = tags.obj_id; ++ fixer->shadowed_id = ++ oh->shadows_obj; ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Shadow fixer: %d shadows %d", ++ fixer->obj_id, ++ fixer->shadowed_id); ++ ++ } ++ ++ } ++ ++ if (in && in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate and need to ++ * resolve it. */ ++ ++ unsigned existing_serial = in->serial; ++ unsigned new_serial = ++ tags.serial_number; ++ ++ if (((existing_serial + 1) & 3) == ++ new_serial) { ++ /* Use new one - destroy the ++ * exisiting one */ ++ yaffs_chunk_del(dev, ++ in->hdr_chunk, ++ 1, __LINE__); ++ in->valid = 0; ++ } else { ++ /* Use existing - destroy ++ * this one. */ ++ yaffs_chunk_del(dev, chunk, 1, ++ __LINE__); ++ } ++ } ++ ++ if (in && !in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == ++ YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle ++ * with directory structure */ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ } else if (in && !in->valid) { ++ /* we need to load this info */ ++ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ in->dirty = 0; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ parent = ++ yaffs_find_or_create_by_number ++ (dev, oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ if (!parent) ++ alloc_failed = 1; ++ if (parent && parent->variant_type == ++ YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant. ++ children); ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, a problem.... ++ * We're trying to use a ++ * non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (dev->param. ++ use_header_file_size) ++ in->variant. ++ file_variant.file_size ++ = yaffs_oh_to_size(oh); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant. ++ hardlink_variant.equiv_id = ++ oh->equiv_id; ++ list_add(&in->hard_links, ++ &hard_list); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant. ++ alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant. ++ symlink_variant.alias) ++ alloc_failed = 1; ++ break; ++ } ++ } ++ } ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* If we got this far while scanning, ++ * then the block is fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ /* If the block was partially allocated then ++ * treat it as fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ bi->block_state = state; ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ yaffs_block_became_dirty(dev, blk); ++ } ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We should now have scanned all the objects, now it's time to add ++ * these hardlinks. ++ */ ++ ++ yaffs_link_fixup(dev, &hard_list); ++ ++ /* ++ * Fix up any shadowed objects. ++ * There should not be more than one of these. ++ */ ++ { ++ struct yaffs_shadow_fixer *fixer; ++ struct yaffs_obj *obj; ++ ++ while (shadow_fixers) { ++ fixer = shadow_fixers; ++ shadow_fixers = fixer->next; ++ /* Complete the rename transaction by deleting the ++ * shadowed object then setting the object header ++ to unshadowed. ++ */ ++ obj = yaffs_find_by_number(dev, fixer->shadowed_id); ++ if (obj) ++ yaffs_del_obj(obj); ++ ++ obj = yaffs_find_by_number(dev, fixer->obj_id); ++ ++ if (obj) ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ kfree(fixer); ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, chunk_data); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); ++ ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_yaffs1.h b/fs/yaffs2/yaffs_yaffs1.h +new file mode 100644 +index 0000000..97e2fdd +--- /dev/null ++++ b/fs/yaffs2/yaffs_yaffs1.h +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS1_H__ ++#define __YAFFS_YAFFS1_H__ ++ ++#include "yaffs_guts.h" ++int yaffs1_scan(struct yaffs_dev *dev); ++ ++#endif +diff --git a/fs/yaffs2/yaffs_yaffs2.c b/fs/yaffs2/yaffs_yaffs2.c +new file mode 100644 +index 0000000..9fb7c94 +--- /dev/null ++++ b/fs/yaffs2/yaffs_yaffs2.c +@@ -0,0 +1,1532 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_checkptrw.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_verify.h" ++#include "yaffs_attribs.h" ++#include "yaffs_summary.h" ++ ++/* ++ * Checkpoints are really no benefit on very small partitions. ++ * ++ * To save space on small partitions don't bother with checkpoints unless ++ * the partition is at least this big. ++ */ ++#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 ++#define YAFFS_SMALL_HOLE_THRESHOLD 4 ++ ++/* ++ * Oldest Dirty Sequence Number handling. ++ */ ++ ++/* yaffs_calc_oldest_dirty_seq() ++ * yaffs2_find_oldest_dirty_seq() ++ * Calculate the oldest dirty sequence number if we don't know it. ++ */ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ int i; ++ unsigned seq; ++ unsigned block_no = 0; ++ struct yaffs_block_info *b; ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ /* Find the oldest dirty sequence number. */ ++ seq = dev->seq_number + 1; ++ b = dev->block_info; ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ if (b->block_state == YAFFS_BLOCK_STATE_FULL && ++ (b->pages_in_use - b->soft_del_pages) < ++ dev->param.chunks_per_block && ++ b->seq_number < seq) { ++ seq = b->seq_number; ++ block_no = i; ++ } ++ b++; ++ } ++ ++ if (block_no) { ++ dev->oldest_dirty_seq = seq; ++ dev->oldest_dirty_block = block_no; ++ } ++} ++ ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->oldest_dirty_seq) ++ yaffs_calc_oldest_dirty_seq(dev); ++} ++ ++/* ++ * yaffs_clear_oldest_dirty_seq() ++ * Called when a block is erased or marked bad. (ie. when its seq_number ++ * becomes invalid). If the value matches the oldest then we clear ++ * dev->oldest_dirty_seq to force its recomputation. ++ */ ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!bi || bi->seq_number == dev->oldest_dirty_seq) { ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ } ++} ++ ++/* ++ * yaffs2_update_oldest_dirty_seq() ++ * Update the oldest dirty sequence number whenever we dirty a block. ++ * Only do this if the oldest_dirty_seq is actually being tracked. ++ */ ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (dev->oldest_dirty_seq) { ++ if (dev->oldest_dirty_seq > bi->seq_number) { ++ dev->oldest_dirty_seq = bi->seq_number; ++ dev->oldest_dirty_block = block_no; ++ } ++ } ++} ++ ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return 1; /* disqualification only applies to yaffs2. */ ++ ++ if (!bi->has_shrink_hdr) ++ return 1; /* can gc */ ++ ++ yaffs2_find_oldest_dirty_seq(dev); ++ ++ /* Can't do gc of this block if there are any blocks older than this ++ * one that have discarded pages. ++ */ ++ return (bi->seq_number <= dev->oldest_dirty_seq); ++} ++ ++/* ++ * yaffs2_find_refresh_block() ++ * periodically finds the oldest full block by sequence number for refreshing. ++ * Only for yaffs2. ++ */ ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) ++{ ++ u32 b; ++ u32 oldest = 0; ++ u32 oldest_seq = 0; ++ struct yaffs_block_info *bi; ++ ++ if (!dev->param.is_yaffs2) ++ return oldest; ++ ++ /* ++ * If refresh period < 10 then refreshing is disabled. ++ */ ++ if (dev->param.refresh_period < 10) ++ return oldest; ++ ++ /* ++ * Fix broken values. ++ */ ++ if (dev->refresh_skip > dev->param.refresh_period) ++ dev->refresh_skip = dev->param.refresh_period; ++ ++ if (dev->refresh_skip > 0) ++ return oldest; ++ ++ /* ++ * Refresh skip is now zero. ++ * We'll do a refresh this time around.... ++ * Update the refresh skip and find the oldest block. ++ */ ++ dev->refresh_skip = dev->param.refresh_period; ++ dev->refresh_count++; ++ bi = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ ++ if (oldest < 1 || bi->seq_number < oldest_seq) { ++ oldest = b; ++ oldest_seq = bi->seq_number; ++ } ++ } ++ bi++; ++ } ++ ++ if (oldest > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC refresh count %d selected block %d with seq_number %d", ++ dev->refresh_count, oldest, oldest_seq); ++ } ++ ++ return oldest; ++} ++ ++int yaffs2_checkpt_required(struct yaffs_dev *dev) ++{ ++ int nblocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ nblocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ return !dev->param.skip_checkpt_wr && ++ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); ++} ++ ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) ++{ ++ int retval; ++ int n_bytes = 0; ++ int n_blocks; ++ int dev_blocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { ++ /* Not a valid value so recalculate */ ++ dev_blocks = dev->param.end_block - dev->param.start_block + 1; ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(struct yaffs_checkpt_dev); ++ n_bytes += dev_blocks * sizeof(struct yaffs_block_info); ++ n_bytes += dev_blocks * dev->chunk_bit_stride; ++ n_bytes += ++ (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * ++ dev->n_obj; ++ n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(u32); /* checksum */ ++ ++ /* Round up and add 2 blocks to allow for some bad blocks, ++ * so add 3 */ ++ ++ n_blocks = ++ (n_bytes / ++ (dev->data_bytes_per_chunk * ++ dev->param.chunks_per_block)) + 3; ++ ++ dev->checkpoint_blocks_required = n_blocks; ++ } ++ ++ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; ++ if (retval < 0) ++ retval = 0; ++ return retval; ++} ++ ++/*--------------------- Checkpointing --------------------*/ ++ ++static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ ++ memset(&cp, 0, sizeof(cp)); ++ ++ cp.struct_type = sizeof(cp); ++ cp.magic = YAFFS_MAGIC; ++ cp.version = YAFFS_CHECKPOINT_VERSION; ++ cp.head = (head) ? 1 : 0; ++ ++ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ if (ok) ++ ok = (cp.struct_type == sizeof(cp)) && ++ (cp.magic == YAFFS_MAGIC) && ++ (cp.version == YAFFS_CHECKPOINT_VERSION) && ++ (cp.head == ((head) ? 1 : 0)); ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, ++ struct yaffs_dev *dev) ++{ ++ cp->n_erased_blocks = dev->n_erased_blocks; ++ cp->alloc_block = dev->alloc_block; ++ cp->alloc_page = dev->alloc_page; ++ cp->n_free_chunks = dev->n_free_chunks; ++ ++ cp->n_deleted_files = dev->n_deleted_files; ++ cp->n_unlinked_files = dev->n_unlinked_files; ++ cp->n_bg_deletions = dev->n_bg_deletions; ++ cp->seq_number = dev->seq_number; ++ ++} ++ ++static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, ++ struct yaffs_checkpt_dev *cp) ++{ ++ dev->n_erased_blocks = cp->n_erased_blocks; ++ dev->alloc_block = cp->alloc_block; ++ dev->alloc_page = cp->alloc_page; ++ dev->n_free_chunks = cp->n_free_chunks; ++ ++ dev->n_deleted_files = cp->n_deleted_files; ++ dev->n_unlinked_files = cp->n_unlinked_files; ++ dev->n_bg_deletions = cp->n_bg_deletions; ++ dev->seq_number = cp->seq_number; ++} ++ ++static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ int ok; ++ ++ /* Write device runtime values */ ++ yaffs2_dev_to_checkpt_dev(&cp, dev); ++ cp.struct_type = sizeof(cp); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ /* Write block info */ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); ++ if (!ok) ++ return 0; ++ ++ /* Write chunk bits */ ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = ++ (dev->internal_end_block - dev->internal_start_block + 1); ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ if (cp.struct_type != sizeof(cp)) ++ return 0; ++ ++ yaffs_checkpt_dev_to_dev(dev, &cp); ++ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); ++ ++ if (!ok) ++ return 0; ++ ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, ++ struct yaffs_obj *obj) ++{ ++ cp->obj_id = obj->obj_id; ++ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; ++ cp->hdr_chunk = obj->hdr_chunk; ++ cp->variant_type = obj->variant_type; ++ cp->deleted = obj->deleted; ++ cp->soft_del = obj->soft_del; ++ cp->unlinked = obj->unlinked; ++ cp->fake = obj->fake; ++ cp->rename_allowed = obj->rename_allowed; ++ cp->unlink_allowed = obj->unlink_allowed; ++ cp->serial = obj->serial; ++ cp->n_data_chunks = obj->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ cp->size_or_equiv_obj = obj->variant.file_variant.file_size; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; ++} ++ ++static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, ++ struct yaffs_checkpt_obj *cp) ++{ ++ struct yaffs_obj *parent; ++ ++ if (obj->variant_type != cp->variant_type) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Checkpoint read object %d type %d chunk %d does not match existing object type %d", ++ cp->obj_id, cp->variant_type, cp->hdr_chunk, ++ obj->variant_type); ++ return 0; ++ } ++ ++ obj->obj_id = cp->obj_id; ++ ++ if (cp->parent_id) ++ parent = yaffs_find_or_create_by_number(obj->my_dev, ++ cp->parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ else ++ parent = NULL; ++ ++ if (parent) { ++ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", ++ cp->obj_id, cp->parent_id, ++ cp->variant_type, cp->hdr_chunk, ++ parent->variant_type); ++ return 0; ++ } ++ yaffs_add_obj_to_dir(parent, obj); ++ } ++ ++ obj->hdr_chunk = cp->hdr_chunk; ++ obj->variant_type = cp->variant_type; ++ obj->deleted = cp->deleted; ++ obj->soft_del = cp->soft_del; ++ obj->unlinked = cp->unlinked; ++ obj->fake = cp->fake; ++ obj->rename_allowed = cp->rename_allowed; ++ obj->unlink_allowed = cp->unlink_allowed; ++ obj->serial = cp->serial; ++ obj->n_data_chunks = cp->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ obj->variant.file_variant.file_size = cp->size_or_equiv_obj; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; ++ ++ if (obj->hdr_chunk > 0) ++ obj->lazy_loaded = 1; ++ return 1; ++} ++ ++static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, ++ struct yaffs_tnode *tn, u32 level, ++ int chunk_offset) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ int ok = 1; ++ u32 base_offset; ++ ++ if (!tn) ++ return 1; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { ++ if (!tn->internal[i]) ++ continue; ++ ok = yaffs2_checkpt_tnode_worker(in, ++ tn->internal[i], ++ level - 1, ++ (chunk_offset << ++ YAFFS_TNODES_INTERNAL_BITS) + i); ++ } ++ return ok; ++ } ++ ++ /* Level 0 tnode */ ++ base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; ++ ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == ++ sizeof(base_offset)); ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ ++ return ok; ++} ++ ++static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 end_marker = ~0; ++ int ok = 1; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return ok; ++ ++ ok = yaffs2_checkpt_tnode_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant.file_variant. ++ top_level, 0); ++ if (ok) ++ ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, ++ sizeof(end_marker)) == sizeof(end_marker)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 base_chunk; ++ int ok = 1; ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; ++ struct yaffs_tnode *tn; ++ int nread = 0; ++ ++ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == ++ sizeof(base_chunk)); ++ ++ while (ok && (~base_chunk)) { ++ nread++; ++ /* Read level 0 tnode */ ++ ++ tn = yaffs_get_tnode(dev); ++ if (tn) ++ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ else ++ ok = 0; ++ ++ if (tn && ok) ++ ok = yaffs_add_find_tnode_0(dev, ++ file_stuct_ptr, ++ base_chunk, tn) ? 1 : 0; ++ ++ if (ok) ++ ok = (yaffs2_checkpt_rd ++ (dev, &base_chunk, ++ sizeof(base_chunk)) == sizeof(base_chunk)); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read tnodes %d records, last %d. ok %d", ++ nread, base_chunk, ok); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int i; ++ int ok = 1; ++ struct list_head *lh; ++ ++ /* Iterate through the objects in each hash entry, ++ * dumping them to the checkpointing stream. ++ */ ++ ++ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ if (!obj->defered_free) { ++ yaffs2_obj_checkpt_obj(&cp, obj); ++ cp.struct_type = sizeof(cp); ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", ++ cp.obj_id, cp.parent_id, ++ cp.variant_type, cp.hdr_chunk, obj); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, ++ sizeof(cp)) == sizeof(cp)); ++ ++ if (ok && ++ obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) ++ ok = yaffs2_wr_checkpt_tnodes(obj); ++ } ++ } ++ } ++ ++ /* Dump end of list */ ++ memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); ++ cp.struct_type = sizeof(cp); ++ ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int ok = 1; ++ int done = 0; ++ LIST_HEAD(hard_list); ++ ++ ++ while (ok && !done) { ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (cp.struct_type != sizeof(cp)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "struct size %d instead of %d ok %d", ++ cp.struct_type, (int)sizeof(cp), ok); ++ ok = 0; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read object %d parent %d type %d chunk %d ", ++ cp.obj_id, cp.parent_id, cp.variant_type, ++ cp.hdr_chunk); ++ ++ if (ok && cp.obj_id == ~0) { ++ done = 1; ++ } else if (ok) { ++ obj = ++ yaffs_find_or_create_by_number(dev, cp.obj_id, ++ cp.variant_type); ++ if (obj) { ++ ok = yaffs2_checkpt_obj_to_obj(obj, &cp); ++ if (!ok) ++ break; ++ if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) { ++ ok = yaffs2_rd_checkpt_tnodes(obj); ++ } else if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_HARDLINK) { ++ list_add(&obj->hard_links, &hard_list); ++ } ++ } else { ++ ok = 0; ++ } ++ } ++ } ++ ++ if (ok) ++ yaffs_link_fixup(dev, &hard_list); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum); ++ ++ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == ++ sizeof(checkpt_sum)); ++ ++ if (!ok) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum0; ++ u32 checkpt_sum1; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum0); ++ ++ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == ++ sizeof(checkpt_sum1)); ++ ++ if (!ok) ++ return 0; ++ ++ if (checkpt_sum0 != checkpt_sum1) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!yaffs2_checkpt_required(dev)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint write"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 1); ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint device"); ++ ok = yaffs2_wr_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint objects"); ++ ok = yaffs2_wr_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) ++ ok = yaffs2_wr_checkpt_sum(dev); ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return dev->is_checkpointed; ++} ++ ++static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!dev->param.is_yaffs2) ++ ok = 0; ++ ++ if (ok && dev->param.skip_checkpt_rd) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint read"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 0); /* open for read */ ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint device"); ++ ok = yaffs2_rd_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint objects"); ++ ok = yaffs2_rd_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) { ++ ok = yaffs2_rd_checkpt_sum(dev); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint checksum %d", ok); ++ } ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return ok ? 1 : 0; ++} ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) ++{ ++ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { ++ dev->is_checkpointed = 0; ++ yaffs2_checkpt_invalidate_stream(dev); ++ } ++ if (dev->param.sb_dirty_fn) ++ dev->param.sb_dirty_fn(dev); ++} ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "save entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ ++ if (!dev->is_checkpointed) { ++ yaffs2_checkpt_invalidate(dev); ++ yaffs2_wr_checkpt_data(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, ++ "save exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return dev->is_checkpointed; ++} ++ ++int yaffs2_checkpt_restore(struct yaffs_dev *dev) ++{ ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ retval = yaffs2_rd_checkpt_data(dev); ++ ++ if (dev->is_checkpointed) { ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return retval; ++} ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) ++{ ++ /* if new_size > old_file_size. ++ * We're going to be writing a hole. ++ * If the hole is small then write zeros otherwise write a start ++ * of hole marker. ++ */ ++ loff_t old_file_size; ++ loff_t increase; ++ int small_hole; ++ int result = YAFFS_OK; ++ struct yaffs_dev *dev = NULL; ++ u8 *local_buffer = NULL; ++ int small_increase_ok = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ dev = obj->my_dev; ++ ++ /* Bail out if not yaffs2 mode */ ++ if (!dev->param.is_yaffs2) ++ return YAFFS_OK; ++ ++ old_file_size = obj->variant.file_variant.file_size; ++ ++ if (new_size <= old_file_size) ++ return YAFFS_OK; ++ ++ increase = new_size - old_file_size; ++ ++ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && ++ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) ++ small_hole = 1; ++ else ++ small_hole = 0; ++ ++ if (small_hole) ++ local_buffer = yaffs_get_temp_buffer(dev); ++ ++ if (local_buffer) { ++ /* fill hole with zero bytes */ ++ loff_t pos = old_file_size; ++ int this_write; ++ int written; ++ memset(local_buffer, 0, dev->data_bytes_per_chunk); ++ small_increase_ok = 1; ++ ++ while (increase > 0 && small_increase_ok) { ++ this_write = increase; ++ if (this_write > dev->data_bytes_per_chunk) ++ this_write = dev->data_bytes_per_chunk; ++ written = ++ yaffs_do_file_wr(obj, local_buffer, pos, this_write, ++ 0); ++ if (written == this_write) { ++ pos += this_write; ++ increase -= this_write; ++ } else { ++ small_increase_ok = 0; ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ ++ /* If out of space then reverse any chunks we've added */ ++ if (!small_increase_ok) ++ yaffs_resize_file_down(obj, old_file_size); ++ } ++ ++ if (!small_increase_ok && ++ obj->parent && ++ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { ++ /* Write a hole start header with the old file size */ ++ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); ++ } ++ ++ return result; ++} ++ ++struct yaffs_block_index { ++ int seq; ++ int block; ++}; ++ ++static int yaffs2_ybicmp(const void *a, const void *b) ++{ ++ int aseq = ((struct yaffs_block_index *)a)->seq; ++ int bseq = ((struct yaffs_block_index *)b)->seq; ++ int ablock = ((struct yaffs_block_index *)a)->block; ++ int bblock = ((struct yaffs_block_index *)b)->block; ++ ++ if (aseq == bseq) ++ return ablock - bblock; ++ ++ return aseq - bseq; ++} ++ ++static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, ++ int blk, int chunk_in_block, ++ int *found_chunks, ++ u8 *chunk_data, ++ struct list_head *hard_list, ++ int summary_available) ++{ ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int equiv_id; ++ loff_t file_size; ++ int is_shrink; ++ int is_unlinked; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ int chunk = blk * dev->param.chunks_per_block + chunk_in_block; ++ struct yaffs_file_var *file_var; ++ struct yaffs_hardlink_var *hl_var; ++ struct yaffs_symlink_var *sl_var; ++ ++ if (summary_available) { ++ result = yaffs_summary_fetch(dev, &tags, chunk_in_block); ++ tags.seq_number = bi->seq_number; ++ } ++ ++ if (!summary_available || tags.obj_id == 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); ++ dev->tags_used++; ++ } else { ++ dev->summary_used++; ++ } ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (!tags.chunk_used) { ++ /* An unassigned chunk in the block. ++ * If there are used chunks after this one, then ++ * it is a chunk that was skipped due to failing ++ * the erased check. Just skip it so that it can ++ * be deleted. ++ * But, more typically, We get here when this is ++ * an unallocated chunk and his means that ++ * either the block is empty or this is the one ++ * being allocated from ++ */ ++ ++ if (*found_chunks) { ++ /* This is a chunk that was skipped due ++ * to failing the erased check */ ++ } else if (chunk_in_block == 0) { ++ /* We're looking at the first chunk in ++ * the block so the block is unused */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ if (dev->seq_number == bi->seq_number) { ++ /* Allocating from this block*/ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, chunk_in_block); ++ ++ bi->block_state = ++ YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = chunk_in_block; ++ dev->alloc_block_finder = blk; ++ } else { ++ /* This is a partially written block ++ * that is not the current ++ * allocation block. ++ */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Partially written block %d detected. gc will fix this.", ++ blk); ++ } ++ } ++ } ++ ++ dev->n_free_chunks++; ++ ++ } else if (tags.ecc_result == ++ YAFFS_ECC_RESULT_UNFIXED) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Unfixed ECC in chunk(%d:%d), chunk ignored", ++ blk, chunk_in_block); ++ dev->n_free_chunks++; ++ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || ++ tags.chunk_id > YAFFS_MAX_CHUNK_ID || ++ tags.obj_id == YAFFS_OBJECTID_SUMMARY || ++ (tags.chunk_id > 0 && ++ tags.n_bytes > dev->data_bytes_per_chunk) || ++ tags.seq_number != bi->seq_number) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", ++ blk, chunk_in_block, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ dev->n_free_chunks++; ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ loff_t endpos; ++ loff_t chunk_base = (tags.chunk_id - 1) * ++ dev->data_bytes_per_chunk; ++ ++ *found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ if (!in) ++ /* Out of memory */ ++ alloc_failed = 1; ++ ++ if (in && ++ in->variant_type == YAFFS_OBJECT_TYPE_FILE && ++ chunk_base < in->variant.file_variant.shrink_size) { ++ /* This has not been invalidated by ++ * a resize */ ++ if (!yaffs_put_chunk_in_file(in, tags.chunk_id, ++ chunk, -1)) ++ alloc_failed = 1; ++ ++ /* File size is calculated by looking at ++ * the data chunks if we have not ++ * seen an object header yet. ++ * Stop this practice once we find an ++ * object header. ++ */ ++ endpos = chunk_base + tags.n_bytes; ++ ++ if (!in->valid && ++ in->variant.file_variant.scanned_size < endpos) { ++ in->variant.file_variant. ++ scanned_size = endpos; ++ in->variant.file_variant. ++ file_size = endpos; ++ } ++ } else if (in) { ++ /* This chunk has been invalidated by a ++ * resize, or a past file deletion ++ * so delete the chunk*/ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Thus, we read in the object header and make ++ * the object ++ */ ++ *found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ ++ oh = NULL; ++ in = NULL; ++ ++ if (tags.extra_available) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ tags.extra_obj_type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ ++ if (!in || ++ (!in->valid && dev->param.disable_lazy_load) || ++ tags.extra_shadows || ++ (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { ++ ++ /* If we don't have valid info then we ++ * need to read the chunk ++ * TODO In future we can probably defer ++ * reading the chunk and living with ++ * invalid data until needed. ++ */ ++ ++ result = yaffs_rd_chunk_tags_nand(dev, ++ chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ if (dev->param.inband_tags) { ++ /* Fix up the header if they got ++ * corrupted by inband tags */ ++ oh->shadows_obj = ++ oh->inband_shadowed_obj_id; ++ oh->is_shrink = ++ oh->inband_is_shrink; ++ } ++ ++ if (!in) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, oh->type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ } ++ ++ if (!in) { ++ /* TODO Hoosterman we have a problem! */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Could not make object for object %d at chunk %d during scan", ++ tags.obj_id, chunk); ++ return YAFFS_FAIL; ++ } ++ ++ if (in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate that will be ++ * discarded, but we first have to suck ++ * out resize info if it is a file. ++ */ ++ if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && ++ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || ++ (tags.extra_available && ++ tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ )) { ++ loff_t this_size = (oh) ? ++ yaffs_oh_to_size(oh) : ++ tags.extra_file_size; ++ u32 parent_obj_id = (oh) ? ++ oh->parent_obj_id : ++ tags.extra_parent_id; ++ ++ is_shrink = (oh) ? ++ oh->is_shrink : ++ tags.extra_is_shrink; ++ ++ /* If it is deleted (unlinked ++ * at start also means deleted) ++ * we treat the file size as ++ * being zeroed at this point. ++ */ ++ if (parent_obj_id == YAFFS_OBJECTID_DELETED || ++ parent_obj_id == YAFFS_OBJECTID_UNLINKED) { ++ this_size = 0; ++ is_shrink = 1; ++ } ++ ++ if (is_shrink && ++ in->variant.file_variant.shrink_size > ++ this_size) ++ in->variant.file_variant.shrink_size = ++ this_size; ++ ++ if (is_shrink) ++ bi->has_shrink_hdr = 1; ++ } ++ /* Use existing - destroy this one. */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ } ++ ++ if (!in->valid && in->variant_type != ++ (oh ? oh->type : tags.extra_obj_type)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", ++ oh ? oh->type : tags.extra_obj_type, ++ in->variant_type, tags.obj_id, ++ chunk); ++ in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); ++ } ++ ++ if (!in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle ++ * with directory structure */ ++ in->valid = 1; ++ ++ if (oh) { ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->lazy_loaded = 0; ++ } else { ++ in->lazy_loaded = 1; ++ } ++ in->hdr_chunk = chunk; ++ ++ } else if (!in->valid) { ++ /* we need to load this info */ ++ in->valid = 1; ++ in->hdr_chunk = chunk; ++ if (oh) { ++ in->variant_type = oh->type; ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ ++ if (oh->shadows_obj > 0) ++ yaffs_handle_shadowed_obj(dev, ++ oh->shadows_obj, 1); ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ parent = yaffs_find_or_create_by_number(dev, ++ oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = yaffs_oh_to_size(oh); ++ is_shrink = oh->is_shrink; ++ equiv_id = oh->equiv_id; ++ } else { ++ in->variant_type = tags.extra_obj_type; ++ parent = yaffs_find_or_create_by_number(dev, ++ tags.extra_parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = tags.extra_file_size; ++ is_shrink = tags.extra_is_shrink; ++ equiv_id = tags.extra_equiv_id; ++ in->lazy_loaded = 1; ++ } ++ in->dirty = 0; ++ ++ if (!parent) ++ alloc_failed = 1; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ if (parent && ++ parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant.children); ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, another problem.... ++ * Trying to use a non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ yaffs_add_obj_to_dir(parent, in); ++ ++ is_unlinked = (parent == dev->del_dir) || ++ (parent == dev->unlinked_dir); ++ ++ if (is_shrink) ++ /* Mark the block */ ++ bi->has_shrink_hdr = 1; ++ ++ /* Note re hardlinks. ++ * Since we might scan a hardlink before its equivalent ++ * object is scanned we put them all in a list. ++ * After scanning is complete, we should have all the ++ * objects, so we run through this list and fix up all ++ * the chains. ++ */ ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ file_var = &in->variant.file_variant; ++ if (file_var->scanned_size < file_size) { ++ /* This covers the case where the file ++ * size is greater than the data held. ++ * This will happen if the file is ++ * resized to be larger than its ++ * current data extents. ++ */ ++ file_var->file_size = file_size; ++ file_var->scanned_size = file_size; ++ } ++ ++ if (file_var->shrink_size > file_size) ++ file_var->shrink_size = file_size; ++ ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ hl_var = &in->variant.hardlink_variant; ++ if (!is_unlinked) { ++ hl_var->equiv_id = equiv_id; ++ list_add(&in->hard_links, hard_list); ++ } ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ sl_var = &in->variant.symlink_variant; ++ if (oh) { ++ sl_var->alias = ++ yaffs_clone_str(oh->alias); ++ if (!sl_var->alias) ++ alloc_failed = 1; ++ } ++ break; ++ } ++ } ++ } ++ return alloc_failed ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++int yaffs2_scan_backwards(struct yaffs_dev *dev) ++{ ++ int blk; ++ int block_iter; ++ int start_iter; ++ int end_iter; ++ int n_to_scan = 0; ++ enum yaffs_block_state state; ++ int c; ++ LIST_HEAD(hard_list); ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ u8 *chunk_data; ++ int found_chunks; ++ int alloc_failed = 0; ++ struct yaffs_block_index *block_index = NULL; ++ int alt_block_index = 0; ++ int summary_available; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ block_index = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); ++ ++ if (!block_index) { ++ block_index = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_index)); ++ alt_block_index = 1; ++ } ++ ++ if (!block_index) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards() could not allocate block index!" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ chunk_data = yaffs_get_temp_buffer(dev); ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, bi->block_state, seq_number); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ dev->blocks_in_checkpt++; ++ ++ } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } else if (bi->block_state == ++ YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* Determine the highest sequence number */ ++ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && ++ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { ++ block_index[n_to_scan].seq = seq_number; ++ block_index[n_to_scan].block = blk; ++ n_to_scan++; ++ if (seq_number >= dev->seq_number) ++ dev->seq_number = seq_number; ++ } else { ++ /* TODO: Nasty sequence number! */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Block scanning block %d has bad sequence number %d", ++ blk, seq_number); ++ } ++ } ++ bi++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); ++ ++ cond_resched(); ++ ++ /* Sort the blocks by sequence number */ ++ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), ++ yaffs2_ybicmp, NULL); ++ ++ cond_resched(); ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "...done"); ++ ++ /* Now scan the blocks looking at the data. */ ++ start_iter = 0; ++ end_iter = n_to_scan - 1; ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); ++ ++ /* For each block.... backwards */ ++ for (block_iter = end_iter; ++ !alloc_failed && block_iter >= start_iter; ++ block_iter--) { ++ /* Cooperative multitasking! This loop can run for so ++ long that watchdog timers expire. */ ++ cond_resched(); ++ ++ /* get the block to scan in the correct order */ ++ blk = block_index[block_iter].block; ++ bi = yaffs_get_block_info(dev, blk); ++ ++ summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); ++ ++ /* For each chunk in each block that needs scanning.... */ ++ found_chunks = 0; ++ if (summary_available) ++ c = dev->chunks_per_summary - 1; ++ else ++ c = dev->param.chunks_per_block - 1; ++ ++ for (/* c is already initialised */; ++ !alloc_failed && c >= 0 && ++ (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); ++ c--) { ++ /* Scan backwards... ++ * Read the tags and decide what to do ++ */ ++ if (yaffs2_scan_chunk(dev, bi, blk, c, ++ &found_chunks, chunk_data, ++ &hard_list, summary_available) == ++ YAFFS_FAIL) ++ alloc_failed = 1; ++ } ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* If we got this far while scanning, then the block ++ * is fully allocated. */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ yaffs_block_became_dirty(dev, blk); ++ } ++ } ++ ++ yaffs_skip_rest_of_block(dev); ++ ++ if (alt_block_index) ++ vfree(block_index); ++ else ++ kfree(block_index); ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We have scanned all the objects, now it's time to add these ++ * hardlinks. ++ */ ++ yaffs_link_fixup(dev, &hard_list); ++ ++ yaffs_release_temp_buffer(dev, chunk_data); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); ++ ++ return YAFFS_OK; ++} +diff --git a/fs/yaffs2/yaffs_yaffs2.h b/fs/yaffs2/yaffs_yaffs2.h +new file mode 100644 +index 0000000..2363bfd +--- /dev/null ++++ b/fs/yaffs2/yaffs_yaffs2.h +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS2_H__ ++#define __YAFFS_YAFFS2_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi); ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); ++int yaffs2_checkpt_required(struct yaffs_dev *dev); ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); ++int yaffs2_checkpt_save(struct yaffs_dev *dev); ++int yaffs2_checkpt_restore(struct yaffs_dev *dev); ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); ++int yaffs2_scan_backwards(struct yaffs_dev *dev); ++ ++#endif +diff --git a/fs/yaffs2/yportenv.h b/fs/yaffs2/yportenv.h +new file mode 100644 +index 0000000..8975af3 +--- /dev/null ++++ b/fs/yaffs2/yportenv.h +@@ -0,0 +1,85 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning <charles@aleph1.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YPORTENV_H__ ++#define __YPORTENV_H__ ++ ++/* ++ * Define the MTD version in terms of Linux Kernel versions ++ * This allows yaffs to be used independantly of the kernel ++ * as well as with it. ++ */ ++ ++#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) ++ ++#ifdef YAFFS_OUT_OF_TREE ++#include "moduleconfig.h" ++#endif ++ ++#include <linux/version.h> ++#define MTD_VERSION_CODE LINUX_VERSION_CODE ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++#include <linux/config.h> ++#endif ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/mm.h> ++#include <linux/sched.h> ++#include <linux/string.h> ++#include <linux/slab.h> ++#include <linux/vmalloc.h> ++#include <linux/xattr.h> ++#include <linux/list.h> ++#include <linux/types.h> ++#include <linux/fs.h> ++#include <linux/stat.h> ++#include <linux/sort.h> ++#include <linux/bitops.h> ++ ++/* These type wrappings are used to support Unicode names in WinCE. */ ++#define YCHAR char ++#define YUCHAR unsigned char ++#define _Y(x) x ++ ++#define YAFFS_LOSTNFOUND_NAME "lost+found" ++#define YAFFS_LOSTNFOUND_PREFIX "obj" ++ ++ ++#define YAFFS_ROOT_MODE 0755 ++#define YAFFS_LOSTNFOUND_MODE 0700 ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++#define Y_CURRENT_TIME CURRENT_TIME.tv_sec ++#define Y_TIME_CONVERT(x) (x).tv_sec ++#else ++#define Y_CURRENT_TIME CURRENT_TIME ++#define Y_TIME_CONVERT(x) (x) ++#endif ++ ++#define compile_time_assertion(assertion) \ ++ ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) ++ ++ ++#define yaffs_printf(msk, fmt, ...) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) ++ ++#define yaffs_trace(msk, fmt, ...) do { \ ++ if (yaffs_trace_mask & (msk)) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ ++} while (0) ++ ++ ++#endif +diff --git a/include/asm-generic/seccomp.h b/include/asm-generic/seccomp.h +new file mode 100644 +index 0000000..9fa1f65 +--- /dev/null ++++ b/include/asm-generic/seccomp.h +@@ -0,0 +1,30 @@ ++/* ++ * include/asm-generic/seccomp.h ++ * ++ * Copyright (C) 2014 Linaro Limited ++ * Author: AKASHI Takahiro <takahiro.akashi@linaro.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef _ASM_GENERIC_SECCOMP_H ++#define _ASM_GENERIC_SECCOMP_H ++ ++#include <linux/unistd.h> ++ ++#if defined(CONFIG_COMPAT) && !defined(__NR_seccomp_read_32) ++#define __NR_seccomp_read_32 __NR_read ++#define __NR_seccomp_write_32 __NR_write ++#define __NR_seccomp_exit_32 __NR_exit ++#define __NR_seccomp_sigreturn_32 __NR_rt_sigreturn ++#endif /* CONFIG_COMPAT && ! already defined */ ++ ++#define __NR_seccomp_read __NR_read ++#define __NR_seccomp_write __NR_write ++#define __NR_seccomp_exit __NR_exit ++#ifndef __NR_seccomp_sigreturn ++#define __NR_seccomp_sigreturn __NR_rt_sigreturn ++#endif ++ ++#endif /* _ASM_GENERIC_SECCOMP_H */ +diff --git a/include/dt-bindings/clock/hi3516av200-clock.h b/include/dt-bindings/clock/hi3516av200-clock.h +new file mode 100644 +index 0000000..6389633 +--- /dev/null ++++ b/include/dt-bindings/clock/hi3516av200-clock.h +@@ -0,0 +1,106 @@ ++/* ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef __DTS_HI3516AV200_CLOCK_H ++#define __DTS_HI3516AV200_CLOCK_H ++ ++/* fixed rate */ ++#define HI3516AV200_FIXED_2376M 1 ++#define HI3516AV200_FIXED_1188M 2 ++#define HI3516AV200_FIXED_594M 3 ++#define HI3516AV200_FIXED_297M 4 ++#define HI3516AV200_FIXED_148P5M 5 ++#define HI3516AV200_FIXED_74P25M 6 ++#define HI3516AV200_FIXED_792M 7 ++#define HI3516AV200_FIXED_475M 8 ++#define HI3516AV200_FIXED_340M 9 ++#define HI3516AV200_FIXED_72M 10 ++#define HI3516AV200_FIXED_400M 11 ++#define HI3516AV200_FIXED_200M 12 ++#define HI3516AV200_FIXED_54M 13 ++#define HI3516AV200_FIXED_27M 14 ++#define HI3516AV200_FIXED_37P125M 15 ++#define HI3516AV200_FIXED_3000M 16 ++#define HI3516AV200_FIXED_1500M 17 ++#define HI3516AV200_FIXED_500M 18 ++#define HI3516AV200_FIXED_250M 19 ++#define HI3516AV200_FIXED_125M 20 ++#define HI3516AV200_FIXED_1000M 21 ++#define HI3516AV200_FIXED_600M 22 ++#define HI3516AV200_FIXED_750M 23 ++#define HI3516AV200_FIXED_150M 24 ++#define HI3516AV200_FIXED_75M 25 ++#define HI3516AV200_FIXED_300M 26 ++#define HI3516AV200_FIXED_60M 27 ++#define HI3516AV200_FIXED_214M 28 ++#define HI3516AV200_FIXED_107M 29 ++#define HI3516AV200_FIXED_100M 30 ++#define HI3516AV200_FIXED_50M 31 ++#define HI3516AV200_FIXED_25M 32 ++#define HI3516AV200_FIXED_24M 33 ++#define HI3516AV200_FIXED_3M 34 ++#define HI3516AV200_FIXED_198M 35 ++#define HI3516AV200_FIXED_396M 36 ++ ++/* mux clocks */ ++#define HI3516AV200_FMC_MUX 64 ++#define HI3516AV200_I2C_MUX 65 ++#define HI3516AV200_UART_MUX 66 ++#define HI3516AV200_SYSAXI_MUX 67 ++#define HI3516AV200_A17_MUX 68 ++#define HI3516AV200_MMC0_MUX 69 ++#define HI3516AV200_MMC1_MUX 70 ++#define HI3516AV200_MMC2_MUX 71 ++ ++/*fixed factor clocks*/ ++#define HI3516AV200_SYSAPB_CLK 97 ++#define HI3516AV200_MMC0_FAC_CLK 98 ++#define HI3516AV200_MMC1_FAC_CLK 99 ++#define HI3516AV200_MMC2_FAC_CLK 100 ++ ++/* gate clocks */ ++#define HI3516AV200_FMC_CLK 129 ++#define HI3516AV200_MMC0_CLK 130 ++#define HI3516AV200_MMC1_CLK 131 ++#define HI3516AV200_MMC2_CLK 132 ++#define HI3516AV200_UART0_CLK 153 ++#define HI3516AV200_UART1_CLK 154 ++#define HI3516AV200_UART2_CLK 155 ++#define HI3516AV200_UART3_CLK 156 ++#define HI3516AV200_UART4_CLK 157 ++#define HI3516AV200_SPI0_CLK 160 ++#define HI3516AV200_SPI1_CLK 161 ++#define HI3516AV200_SPI2_CLK 162 ++#define HI3516AV200_SPI3_CLK 163 ++ ++#define HI3516AV200_USB2_CTRL_UTMI0_REQ 183 ++#define HI3516AV200_USB2_PHY_PORT0_TREQ 184 ++#define HI3516AV200_USB2_PHY_REQ 185 ++#define HI3516AV200_USB2_HRST_REQ 186 ++#define HI3516AV200_USB2_CLK 187 ++#define HI3516AV200_USB3_CLK 188 ++ ++#define HI3516AV200_ETH_CLK 192 ++#define HI3516AV200_ETH_MACIF_CLK 193 ++ ++/* complex */ ++#define HI3516AV200_USB_CLK 195 ++#define HI3516AV200_APLL_CLK 196 ++ ++#define HI3516AV200_NR_CLKS 256 ++#define HI3516AV200_NR_RSTS 256 ++#endif /* __DTS_HI3516AV200_CLOCK_H */ +diff --git a/include/dt-bindings/clock/hi3516cv300-clock.h b/include/dt-bindings/clock/hi3516cv300-clock.h +new file mode 100644 +index 0000000..f37a59d +--- /dev/null ++++ b/include/dt-bindings/clock/hi3516cv300-clock.h +@@ -0,0 +1,113 @@ ++/* ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef __DTS_HI3516CV300_CLOCK_H ++#define __DTS_HI3516CV300_CLOCK_H ++ ++/* clk in hi3516cv300 CRG */ ++/* fixed rate clocks */ ++#define HI3516CV300_FIXED_3M 1 ++#define HI3516CV300_FIXED_6M 2 ++#define HI3516CV300_FIXED_12M 3 ++#define HI3516CV300_FIXED_15M 4 ++#define HI3516CV300_FIXED_24M 5 ++#define HI3516CV300_FIXED_25M 6 ++#define HI3516CV300_FIXED_27M 7 ++#define HI3516CV300_FIXED_37P125M 8 ++#define HI3516CV300_FIXED_44M 9 ++#define HI3516CV300_FIXED_49P5M 10 ++#define HI3516CV300_FIXED_50M 11 ++#define HI3516CV300_FIXED_54M 12 ++#define HI3516CV300_FIXED_74P25M 13 ++#define HI3516CV300_FIXED_75M 14 ++#define HI3516CV300_FIXED_83P3M 15 ++#define HI3516CV300_FIXED_99M 16 ++#define HI3516CV300_FIXED_100M 17 ++#define HI3516CV300_FIXED_125M 18 ++#define HI3516CV300_FIXED_148P5M 19 ++#define HI3516CV300_FIXED_150M 20 ++#define HI3516CV300_FIXED_166P6M 21 ++#define HI3516CV300_FIXED_198M 22 ++#define HI3516CV300_FIXED_200M 23 ++#define HI3516CV300_FIXED_250M 24 ++#define HI3516CV300_FIXED_297M 25 ++#define HI3516CV300_FIXED_300M 26 ++#define HI3516CV300_FIXED_396M 27 ++#define HI3516CV300_FIXED_400M 28 ++ ++/* mux clocks */ ++#define HI3516CV300_APB_CLK 30 ++#define HI3516CV300_UART_MUX 31 ++#define HI3516CV300_FMC_MUX 32 ++#define HI3516CV300_MMC0_MUX 33 ++#define HI3516CV300_MMC1_MUX 34 ++#define HI3516CV300_MMC2_MUX 35 ++#define HI3516CV300_MMC3_MUX 36 ++#define HI3516CV300_SENSOR_MUX 37 ++#define HI3516CV300_VIU_MUX 38 ++#define HI3516CV300_VEDU_MUX 39 ++#define HI3516CV300_VPSS_MUX 40 ++#define HI3516CV300_VGS_MUX 41 ++#define HI3516CV300_IVE_MUX 42 ++#define HI3516CV300_PWM_MUX 43 ++ ++/* div clocks */ ++#define HI3516CV300_ISP_DIV 45 ++ ++/* gate clocks */ ++#define HI3516CV300_UART0_CLK 50 ++#define HI3516CV300_UART1_CLK 51 ++#define HI3516CV300_UART2_CLK 52 ++#define HI3516CV300_SPI0_CLK 53 ++#define HI3516CV300_SPI1_CLK 54 ++#define HI3516CV300_FMC_CLK 55 ++#define HI3516CV300_MMC0_CLK 56 ++#define HI3516CV300_MMC1_CLK 57 ++#define HI3516CV300_MMC2_CLK 58 ++#define HI3516CV300_MMC3_CLK 59 ++#define HI3516CV300_ETH_CLK 60 ++#define HI3516CV300_ETH_MACIF_CLK 61 ++#define HI3516CV300_USB2_BUS_CLK 62 ++#define HI3516CV300_UTMI0_CLK 63 ++#define HI3516CV300_USB2_CLK 64 ++#define HI3516CV300_DMAC_CLK 65 ++#define HI3516CV300_SENSOR_CLK 66 ++#define HI3516CV300_MIPI_CLK 67 ++#define HI3516CV300_ISP_CLK 68 ++#define HI3516CV300_VIU_CLK 69 ++#define HI3516CV300_VEDU_CLK 70 ++#define HI3516CV300_VPSS_CLK 71 ++#define HI3516CV300_VGS_CLK 72 ++#define HI3516CV300_JPGE_CLK 73 ++#define HI3516CV300_IVE_CLK 74 ++#define HI3516CV300_AIAO_CLK 75 ++#define HI3516CV300_PWM_CLK 76 ++ ++#define HI3516CV300_CRG_NR_CLKS 80 ++#define HI3516CV300_CRG_NR_RSTS 0x120 ++ ++/* clk in hi3518ev200 CRG */ ++/* mux clocks */ ++#define HI3516CV300_TIME00_CLK 1 ++#define HI3516CV300_TIME01_CLK 2 ++#define HI3516CV300_TIME10_CLK 3 ++#define HI3516CV300_TIME11_CLK 4 ++ ++#define HI3516CV300_SYS_NR_CLKS 10 ++#define HI3516CV300_SYS_NR_RSTS 0x10 ++#endif /* __DTS_HI3516CV300_CLOCK_H */ +diff --git a/include/dt-bindings/clock/hi3519-clock.h b/include/dt-bindings/clock/hi3519-clock.h +new file mode 100644 +index 0000000..f18c94c +--- /dev/null ++++ b/include/dt-bindings/clock/hi3519-clock.h +@@ -0,0 +1,106 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef __DTS_HI3519_CLOCK_H ++#define __DTS_HI3519_CLOCK_H ++ ++/* fixed rate */ ++#define HI3519_FIXED_2376M 1 ++#define HI3519_FIXED_1188M 2 ++#define HI3519_FIXED_594M 3 ++#define HI3519_FIXED_297M 4 ++#define HI3519_FIXED_148P5M 5 ++#define HI3519_FIXED_74P25M 6 ++#define HI3519_FIXED_792M 7 ++#define HI3519_FIXED_475M 8 ++#define HI3519_FIXED_340M 9 ++#define HI3519_FIXED_72M 10 ++#define HI3519_FIXED_400M 11 ++#define HI3519_FIXED_200M 12 ++#define HI3519_FIXED_54M 13 ++#define HI3519_FIXED_27M 14 ++#define HI3519_FIXED_37P125M 15 ++#define HI3519_FIXED_3000M 16 ++#define HI3519_FIXED_1500M 17 ++#define HI3519_FIXED_500M 18 ++#define HI3519_FIXED_250M 19 ++#define HI3519_FIXED_125M 20 ++#define HI3519_FIXED_1000M 21 ++#define HI3519_FIXED_600M 22 ++#define HI3519_FIXED_750M 23 ++#define HI3519_FIXED_150M 24 ++#define HI3519_FIXED_75M 25 ++#define HI3519_FIXED_300M 26 ++#define HI3519_FIXED_60M 27 ++#define HI3519_FIXED_214M 28 ++#define HI3519_FIXED_107M 29 ++#define HI3519_FIXED_100M 30 ++#define HI3519_FIXED_50M 31 ++#define HI3519_FIXED_25M 32 ++#define HI3519_FIXED_24M 33 ++#define HI3519_FIXED_3M 34 ++#define HI3519_FIXED_198M 35 ++#define HI3519_FIXED_396M 36 ++ ++/* mux clocks */ ++#define HI3519_FMC_MUX 64 ++#define HI3519_I2C_MUX 65 ++#define HI3519_UART_MUX 66 ++#define HI3519_SYSAXI_MUX 67 ++#define HI3519_A17_MUX 68 ++#define HI3519_MMC0_MUX 69 ++#define HI3519_MMC1_MUX 70 ++#define HI3519_MMC2_MUX 71 ++ ++/*fixed factor clocks*/ ++#define HI3519_SYSAPB_CLK 97 ++#define HI3519_MMC0_FAC_CLK 98 ++#define HI3519_MMC1_FAC_CLK 99 ++#define HI3519_MMC2_FAC_CLK 100 ++ ++/* gate clocks */ ++#define HI3519_FMC_CLK 129 ++#define HI3519_MMC0_CLK 130 ++#define HI3519_MMC1_CLK 131 ++#define HI3519_MMC2_CLK 132 ++#define HI3519_UART0_CLK 153 ++#define HI3519_UART1_CLK 154 ++#define HI3519_UART2_CLK 155 ++#define HI3519_UART3_CLK 156 ++#define HI3519_UART4_CLK 157 ++#define HI3519_SPI0_CLK 160 ++#define HI3519_SPI1_CLK 161 ++#define HI3519_SPI2_CLK 162 ++#define HI3519_SPI3_CLK 163 ++ ++#define HI3519_USB2_CTRL_UTMI0_REQ 183 ++#define HI3519_USB2_PHY_PORT0_TREQ 184 ++#define HI3519_USB2_PHY_REQ 185 ++#define HI3519_USB2_HRST_REQ 186 ++#define HI3519_USB2_CLK 187 ++#define HI3519_USB3_CLK 188 ++ ++#define HI3519_ETH_CLK 192 ++#define HI3519_ETH_MACIF_CLK 193 ++ ++/* complex */ ++#define HI3519_USB_CLK 195 ++#define HI3519_APLL_CLK 196 ++ ++#define HI3519_NR_CLKS 256 ++#define HI3519_NR_RSTS 256 ++#endif /* __DTS_HI3519_CLOCK_H */ +diff --git a/include/dt-bindings/clock/hi3521d-clock.h b/include/dt-bindings/clock/hi3521d-clock.h +new file mode 100644 +index 0000000..50d3b64 +--- /dev/null ++++ b/include/dt-bindings/clock/hi3521d-clock.h +@@ -0,0 +1,86 @@ ++/* ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef __DTS_HI3521D_CLOCK_H ++#define __DTS_HI3521D_CLOCK_H ++ ++/* clk in hi3521D CRG */ ++/* fixed rate clocks */ ++#define HI3521D_FIXED_3M 1 ++#define HI3521D_FIXED_6M 2 ++#define HI3521D_FIXED_12M 3 ++#define HI3521D_FIXED_24M 4 ++#define HI3521D_FIXED_83P3M 5 ++#define HI3521D_FIXED_100M 6 ++#define HI3521D_FIXED_125M 7 ++#define HI3521D_FIXED_148P5M 8 ++#define HI3521D_FIXED_150M 9 ++#define HI3521D_FIXED_200M 10 ++#define HI3521D_FIXED_250M 11 ++#define HI3521D_FIXED_300M 12 ++#define HI3521D_FIXED_324M 13 ++#define HI3521D_FIXED_342M 14 ++#define HI3521D_FIXED_375M 15 ++#define HI3521D_FIXED_400M 16 ++#define HI3521D_FIXED_448M 17 ++#define HI3521D_FIXED_500M 18 ++#define HI3521D_FIXED_540M 19 ++#define HI3521D_FIXED_600M 20 ++#define HI3521D_FIXED_750M 21 ++#define HI3521D_FIXED_1500M 22 ++ ++/* mux clocks */ ++#define HI3521D_SYSAXI_CLK 25 ++#define HI3521D_FMC_MUX 26 ++#define HI3521D_UART_MUX 27 ++#define HI3521D_VIU_MUX 28 ++ ++/* gate clocks */ ++#define HI3521D_UART0_CLK 35 ++#define HI3521D_UART1_CLK 36 ++#define HI3521D_UART2_CLK 37 ++#define HI3521D_UART3_CLK 38 ++#define HI3521D_SPI0_CLK 39 ++#define HI3521D_FMC_CLK 40 ++#define HI3521D_ETH_CLK 41 ++#define HI3521D_ETH_MACIF_CLK 42 ++#define HI3521D_USB2_BUS_CLK 43 ++#define HI3521D_USB2_CLK 44 ++#define HI3521D_SATA_CLK 45 ++#define HI3521D_ETH_PUB_CLK 46 ++#define HI3521D_ETH_PHY_CLK 47 ++#define HI3521D_VIU_CLK 48 ++#define HI3521D_DMAC_CLK 49 ++ ++#define HI3521D_CRG_NR_CLKS 50 ++#define HI3521D_CRG_NR_RSTS 0x200 ++ ++/* clock in system control */ ++/* mux clocks */ ++#define HI3521D_TIME0_0_CLK 1 ++#define HI3521D_TIME0_1_CLK 2 ++#define HI3521D_TIME1_2_CLK 3 ++#define HI3521D_TIME1_3_CLK 4 ++#define HI3521D_TIME2_4_CLK 5 ++#define HI3521D_TIME2_5_CLK 6 ++#define HI3521D_TIME3_6_CLK 7 ++#define HI3521D_TIME3_7_CLK 8 ++ ++#define HI3521D_SYS_NR_CLKS 10 ++#define HI3521D_SYS_NR_RSTS 0x10 ++#endif /* __DTS_HI3521D_CLOCK_H */ +diff --git a/include/dt-bindings/clock/hi3531d-clock.h b/include/dt-bindings/clock/hi3531d-clock.h +new file mode 100644 +index 0000000..008e53a +--- /dev/null ++++ b/include/dt-bindings/clock/hi3531d-clock.h +@@ -0,0 +1,99 @@ ++/* ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef __DTS_HI3531D_CLOCK_H ++#define __DTS_HI3531D_CLOCK_H ++ ++/* clk in hi3531D CRG */ ++/* fixed rate clocks */ ++#define HI3531D_FIXED_3M 1 ++#define HI3531D_FIXED_6M 2 ++#define HI3531D_FIXED_12M 3 ++#define HI3531D_FIXED_15M 4 ++#define HI3531D_FIXED_24M 5 ++#define HI3531D_FIXED_44M 6 ++#define HI3531D_FIXED_49P5 7 ++#define HI3531D_FIXED_50M 8 ++#define HI3531D_FIXED_54M 9 ++#define HI3531D_FIXED_75M 10 ++#define HI3531D_FIXED_83P3M 11 ++#define HI3531D_FIXED_99M 12 ++#define HI3531D_FIXED_100M 13 ++#define HI3531D_FIXED_125M 14 ++#define HI3531D_FIXED_148P5M 15 ++#define HI3531D_FIXED_150M 16 ++#define HI3531D_FIXED_200M 17 ++#define HI3531D_FIXED_250M 18 ++#define HI3531D_FIXED_300M 19 ++#define HI3531D_FIXED_324M 20 ++#define HI3531D_FIXED_342M 21 ++#define HI3531D_FIXED_375M 22 ++#define HI3531D_FIXED_400M 23 ++#define HI3531D_FIXED_448M 24 ++#define HI3531D_FIXED_500M 25 ++#define HI3531D_FIXED_540M 26 ++#define HI3531D_FIXED_600M 27 ++#define HI3531D_FIXED_750M 28 ++#define HI3531D_FIXED_1500M 29 ++ ++/* mux clocks */ ++#define HI3531D_PERIAXI_CLK 30 ++#define HI3531D_SYSAXI_CLK 31 ++#define HI3531D_NFC_MUX 32 ++#define HI3531D_FMC_MUX 33 ++#define HI3531D_UART_MUX 34 ++#define HI3531D_VIU_MUX 58 ++ ++/* gate clocks */ ++#define HI3531D_UART0_CLK 35 ++#define HI3531D_UART1_CLK 36 ++#define HI3531D_UART2_CLK 37 ++#define HI3531D_UART3_CLK 38 ++#define HI3531D_UART4_CLK 39 ++#define HI3531D_SPI0_CLK 40 ++#define HI3531D_I2C0_CLK 41 ++#define HI3531D_I2C1_CLK 42 ++#define HI3531D_NFC_CLK 43 ++#define HI3531D_FMC_CLK 44 ++#define HI3531D_ETH_CLK 50 ++#define HI3531D_ETH_MACIF_CLK 51 ++#define HI3531D_USB2_BUS_CLK 52 ++#define HI3531D_UTMI0_CLK 53 ++#define HI3531D_USB2_CLK 54 ++#define HI3531D_DMAC_CLK 55 ++#define HI3531D_SATA_CLK 56 ++#define HI3531D_VIU_CLK 57 ++/* #define HI3531D_VIU_MUX 58 */ ++ ++#define HI3531D_CRG_NR_CLKS 60 ++#define HI3531D_CRG_NR_RSTS 0x250 ++ ++/* clock in system control */ ++/* mux clocks */ ++#define HI3531D_TIME0_0_CLK 1 ++#define HI3531D_TIME0_1_CLK 2 ++#define HI3531D_TIME1_2_CLK 3 ++#define HI3531D_TIME1_3_CLK 4 ++#define HI3531D_TIME2_4_CLK 5 ++#define HI3531D_TIME2_5_CLK 6 ++#define HI3531D_TIME3_6_CLK 7 ++#define HI3531D_TIME3_7_CLK 8 ++ ++#define HI3531D_SYS_NR_CLKS 10 ++#define HI3531D_SYS_NR_RSTS 0x10 ++#endif /* __DTS_HI3531D_CLOCK_H */ +diff --git a/include/dt-bindings/clock/hi3536c-clock.h b/include/dt-bindings/clock/hi3536c-clock.h +new file mode 100644 +index 0000000..e2b1c62 +--- /dev/null ++++ b/include/dt-bindings/clock/hi3536c-clock.h +@@ -0,0 +1,86 @@ ++/* ++ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ * ++ */ ++ ++#ifndef __DTS_HI3536C_CLOCK_H ++#define __DTS_HI3536C_CLOCK_H ++ ++/* clk in hi3536C CRG */ ++/* fixed rate clocks */ ++#define HI3536C_FIXED_3M 1 ++#define HI3536C_FIXED_6M 2 ++#define HI3536C_FIXED_12M 3 ++#define HI3536C_FIXED_24M 4 ++#define HI3536C_FIXED_83P3M 5 ++#define HI3536C_FIXED_100M 6 ++#define HI3536C_FIXED_125M 7 ++#define HI3536C_FIXED_148P5M 8 ++#define HI3536C_FIXED_150M 9 ++#define HI3536C_FIXED_200M 10 ++#define HI3536C_FIXED_250M 11 ++#define HI3536C_FIXED_300M 12 ++#define HI3536C_FIXED_324M 13 ++#define HI3536C_FIXED_342M 14 ++#define HI3536C_FIXED_375M 15 ++#define HI3536C_FIXED_400M 16 ++#define HI3536C_FIXED_448M 17 ++#define HI3536C_FIXED_500M 18 ++#define HI3536C_FIXED_540M 19 ++#define HI3536C_FIXED_600M 20 ++#define HI3536C_FIXED_750M 21 ++#define HI3536C_FIXED_1500M 22 ++ ++/* mux clocks */ ++#define HI3536C_SYSAXI_CLK 23 ++#define HI3536C_FMC_MUX 24 ++#define HI3536C_UART_MUX 25 ++ ++/* gate clocks */ ++#define HI3536C_UART0_CLK 26 ++#define HI3536C_UART1_CLK 27 ++#define HI3536C_UART2_CLK 28 ++#define HI3536C_SPI0_CLK 29 ++#define HI3536C_FMC_CLK 30 ++#define HI3536C_ETH_CLK 31 ++#define HI3536C_ETH_MACIF_CLK 32 ++#define HI3536C_USB2_BUS_CLK 33 ++#define HI3536C_USB2_CLK 34 ++#define HI3536C_SATA_CLK 35 ++#define HI3536C_ETH_PUB_CLK 36 ++#define HI3536C_ETH_PHY_CLK 37 ++#define HI3536C_ETH1_PHY_CLK 38 ++#define HI3536C_ETH1_CLK 39 ++#define HI3536C_ETH_MACIF1_CLK 40 ++#define HI3536C_DMAC_CLK 41 ++ ++#define HI3536C_CRG_NR_CLKS 50 ++#define HI3536C_CRG_NR_RSTS 0x200 ++ ++/* clock in system control */ ++/* mux clocks */ ++#define HI3536C_TIME0_0_CLK 1 ++#define HI3536C_TIME0_1_CLK 2 ++#define HI3536C_TIME1_2_CLK 3 ++#define HI3536C_TIME1_3_CLK 4 ++#define HI3536C_TIME2_4_CLK 5 ++#define HI3536C_TIME2_5_CLK 6 ++#define HI3536C_TIME3_6_CLK 7 ++#define HI3536C_TIME3_7_CLK 8 ++ ++#define HI3536C_SYS_NR_CLKS 10 ++#define HI3536C_SYS_NR_RSTS 0x10 ++#endif /* __DTS_HI3536C_CLOCK_H */ +diff --git a/include/dt-bindings/clock/hi3556-clock.h b/include/dt-bindings/clock/hi3556-clock.h +new file mode 100644 +index 0000000..f5103f8 +--- /dev/null ++++ b/include/dt-bindings/clock/hi3556-clock.h +@@ -0,0 +1,106 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef __DTS_HI3556_CLOCK_H ++#define __DTS_HI3556_CLOCK_H ++ ++/* fixed rate */ ++#define HI3556_FIXED_2376M 1 ++#define HI3556_FIXED_1188M 2 ++#define HI3556_FIXED_594M 3 ++#define HI3556_FIXED_297M 4 ++#define HI3556_FIXED_148P5M 5 ++#define HI3556_FIXED_74P25M 6 ++#define HI3556_FIXED_792M 7 ++#define HI3556_FIXED_475M 8 ++#define HI3556_FIXED_340M 9 ++#define HI3556_FIXED_72M 10 ++#define HI3556_FIXED_400M 11 ++#define HI3556_FIXED_200M 12 ++#define HI3556_FIXED_54M 13 ++#define HI3556_FIXED_27M 14 ++#define HI3556_FIXED_37P125M 15 ++#define HI3556_FIXED_3000M 16 ++#define HI3556_FIXED_1500M 17 ++#define HI3556_FIXED_500M 18 ++#define HI3556_FIXED_250M 19 ++#define HI3556_FIXED_125M 20 ++#define HI3556_FIXED_1000M 21 ++#define HI3556_FIXED_600M 22 ++#define HI3556_FIXED_750M 23 ++#define HI3556_FIXED_150M 24 ++#define HI3556_FIXED_75M 25 ++#define HI3556_FIXED_300M 26 ++#define HI3556_FIXED_60M 27 ++#define HI3556_FIXED_214M 28 ++#define HI3556_FIXED_107M 29 ++#define HI3556_FIXED_100M 30 ++#define HI3556_FIXED_50M 31 ++#define HI3556_FIXED_25M 32 ++#define HI3556_FIXED_24M 33 ++#define HI3556_FIXED_3M 34 ++#define HI3556_FIXED_198M 35 ++#define HI3556_FIXED_396M 36 ++ ++/* mux clocks */ ++#define HI3556_FMC_MUX 64 ++#define HI3556_I2C_MUX 65 ++#define HI3556_UART_MUX 66 ++#define HI3556_SYSAXI_MUX 67 ++#define HI3556_A17_MUX 68 ++#define HI3556_MMC0_MUX 69 ++#define HI3556_MMC1_MUX 70 ++#define HI3556_MMC2_MUX 71 ++ ++/*fixed factor clocks*/ ++#define HI3556_SYSAPB_CLK 97 ++#define HI3556_MMC0_FAC_CLK 98 ++#define HI3556_MMC1_FAC_CLK 99 ++#define HI3556_MMC2_FAC_CLK 100 ++ ++/* gate clocks */ ++#define HI3556_FMC_CLK 129 ++#define HI3556_MMC0_CLK 130 ++#define HI3556_MMC1_CLK 131 ++#define HI3556_MMC2_CLK 132 ++#define HI3556_UART0_CLK 153 ++#define HI3556_UART1_CLK 154 ++#define HI3556_UART2_CLK 155 ++#define HI3556_UART3_CLK 156 ++#define HI3556_UART4_CLK 157 ++#define HI3556_SPI0_CLK 160 ++#define HI3556_SPI1_CLK 161 ++#define HI3556_SPI2_CLK 162 ++#define HI3556_SPI3_CLK 163 ++ ++#define HI3556_USB2_CTRL_UTMI0_REQ 183 ++#define HI3556_USB2_PHY_PORT0_TREQ 184 ++#define HI3556_USB2_PHY_REQ 185 ++#define HI3556_USB2_HRST_REQ 186 ++#define HI3556_USB2_CLK 187 ++#define HI3556_USB3_CLK 188 ++ ++#define HI3556_ETH_CLK 192 ++#define HI3556_ETH_MACIF_CLK 193 ++ ++/* complex */ ++#define HI3556_USB_CLK 195 ++#define HI3556_APLL_CLK 196 ++ ++#define HI3556_NR_CLKS 256 ++#define HI3556_NR_RSTS 256 ++#endif /* __DTS_HI3556_CLOCK_H */ +diff --git a/include/dt-bindings/clock/hi3559-clock.h b/include/dt-bindings/clock/hi3559-clock.h +new file mode 100644 +index 0000000..661c15b +--- /dev/null ++++ b/include/dt-bindings/clock/hi3559-clock.h +@@ -0,0 +1,106 @@ ++/* ++ * Copyright (c) 2015 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++#ifndef __DTS_HI3559_CLOCK_H ++#define __DTS_HI3559_CLOCK_H ++ ++/* fixed rate */ ++#define HI3559_FIXED_2376M 1 ++#define HI3559_FIXED_1188M 2 ++#define HI3559_FIXED_594M 3 ++#define HI3559_FIXED_297M 4 ++#define HI3559_FIXED_148P5M 5 ++#define HI3559_FIXED_74P25M 6 ++#define HI3559_FIXED_792M 7 ++#define HI3559_FIXED_475M 8 ++#define HI3559_FIXED_340M 9 ++#define HI3559_FIXED_72M 10 ++#define HI3559_FIXED_400M 11 ++#define HI3559_FIXED_200M 12 ++#define HI3559_FIXED_54M 13 ++#define HI3559_FIXED_27M 14 ++#define HI3559_FIXED_37P125M 15 ++#define HI3559_FIXED_3000M 16 ++#define HI3559_FIXED_1500M 17 ++#define HI3559_FIXED_500M 18 ++#define HI3559_FIXED_250M 19 ++#define HI3559_FIXED_125M 20 ++#define HI3559_FIXED_1000M 21 ++#define HI3559_FIXED_600M 22 ++#define HI3559_FIXED_750M 23 ++#define HI3559_FIXED_150M 24 ++#define HI3559_FIXED_75M 25 ++#define HI3559_FIXED_300M 26 ++#define HI3559_FIXED_60M 27 ++#define HI3559_FIXED_214M 28 ++#define HI3559_FIXED_107M 29 ++#define HI3559_FIXED_100M 30 ++#define HI3559_FIXED_50M 31 ++#define HI3559_FIXED_25M 32 ++#define HI3559_FIXED_24M 33 ++#define HI3559_FIXED_3M 34 ++#define HI3559_FIXED_198M 35 ++#define HI3559_FIXED_396M 36 ++ ++/* mux clocks */ ++#define HI3559_FMC_MUX 64 ++#define HI3559_I2C_MUX 65 ++#define HI3559_UART_MUX 66 ++#define HI3559_SYSAXI_MUX 67 ++#define HI3559_A17_MUX 68 ++#define HI3559_MMC0_MUX 69 ++#define HI3559_MMC1_MUX 70 ++#define HI3559_MMC2_MUX 71 ++ ++/*fixed factor clocks*/ ++#define HI3559_SYSAPB_CLK 97 ++#define HI3559_MMC0_FAC_CLK 98 ++#define HI3559_MMC1_FAC_CLK 99 ++#define HI3559_MMC2_FAC_CLK 100 ++ ++/* gate clocks */ ++#define HI3559_FMC_CLK 129 ++#define HI3559_MMC0_CLK 130 ++#define HI3559_MMC1_CLK 131 ++#define HI3559_MMC2_CLK 132 ++#define HI3559_UART0_CLK 153 ++#define HI3559_UART1_CLK 154 ++#define HI3559_UART2_CLK 155 ++#define HI3559_UART3_CLK 156 ++#define HI3559_UART4_CLK 157 ++#define HI3559_SPI0_CLK 160 ++#define HI3559_SPI1_CLK 161 ++#define HI3559_SPI2_CLK 162 ++#define HI3559_SPI3_CLK 163 ++ ++#define HI3559_USB2_CTRL_UTMI0_REQ 183 ++#define HI3559_USB2_PHY_PORT0_TREQ 184 ++#define HI3559_USB2_PHY_REQ 185 ++#define HI3559_USB2_HRST_REQ 186 ++#define HI3559_USB2_CLK 187 ++#define HI3559_USB3_CLK 188 ++ ++#define HI3559_ETH_CLK 192 ++#define HI3559_ETH_MACIF_CLK 193 ++ ++/* complex */ ++#define HI3559_USB_CLK 195 ++#define HI3559_APLL_CLK 196 ++ ++#define HI3559_NR_CLKS 256 ++#define HI3559_NR_RSTS 256 ++#endif /* __DTS_HI3559_CLOCK_H */ +diff --git a/include/dt-bindings/thermal/thermal.h b/include/dt-bindings/thermal/thermal.h +index 59822a9..b5e6b00 100644 +--- a/include/dt-bindings/thermal/thermal.h ++++ b/include/dt-bindings/thermal/thermal.h +@@ -11,7 +11,7 @@ + #define _DT_BINDINGS_THERMAL_THERMAL_H + + /* On cooling devices upper and lower limits */ +-#define THERMAL_NO_LIMIT (-1UL) ++#define THERMAL_NO_LIMIT (~0) + + #endif + +diff --git a/include/linux/Kbuild b/include/linux/Kbuild +new file mode 100644 +index 0000000..a460889 +--- /dev/null ++++ b/include/linux/Kbuild +@@ -0,0 +1,2 @@ ++header-y += if_pppolac.h ++header-y += if_pppopns.h +diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h +index c324f57..d024bd9 100644 +--- a/include/linux/amba/bus.h ++++ b/include/linux/amba/bus.h +@@ -23,6 +23,7 @@ + + #define AMBA_NR_IRQS 9 + #define AMBA_CID 0xb105f00d ++#define CORESIGHT_CID 0xb105900d + + struct clk; + +diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h +index 8c98113..8bfd21c 100644 +--- a/include/linux/amba/mmci.h ++++ b/include/linux/amba/mmci.h +@@ -5,6 +5,15 @@ + #define AMBA_MMCI_H + + #include <linux/mmc/host.h> ++#include <linux/mmc/card.h> ++#include <linux/mmc/sdio_func.h> ++ ++struct embedded_sdio_data { ++ struct sdio_cis cis; ++ struct sdio_cccr cccr; ++ struct sdio_embedded_func *funcs; ++ int num_funcs; ++}; + + /** + * struct mmci_platform_data - platform configuration for the MMCI +@@ -31,6 +40,10 @@ struct mmci_platform_data { + int gpio_wp; + int gpio_cd; + bool cd_invert; ++ unsigned int status_irq; ++ struct embedded_sdio_data *embedded_sdio; ++ int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); ++ + }; + + #endif +diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h +index 10fe2a2..27e9ec8 100644 +--- a/include/linux/amba/pl08x.h ++++ b/include/linux/amba/pl08x.h +@@ -86,7 +86,7 @@ struct pl08x_channel_data { + * @mem_buses: buses which memory can be accessed from: PL08X_AHB1 | PL08X_AHB2 + */ + struct pl08x_platform_data { +- const struct pl08x_channel_data *slave_channels; ++ struct pl08x_channel_data *slave_channels; + unsigned int num_slave_channels; + struct pl08x_channel_data memcpy_channel; + int (*get_xfer_signal)(const struct pl08x_channel_data *); +diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h +new file mode 100644 +index 0000000..6f1fa17 +--- /dev/null ++++ b/include/linux/android_aid.h +@@ -0,0 +1,28 @@ ++/* include/linux/android_aid.h ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_ANDROID_AID_H ++#define _LINUX_ANDROID_AID_H ++ ++/* AIDs that the kernel treats differently */ ++#define AID_OBSOLETE_000 KGIDT_INIT(3001) /* was NET_BT_ADMIN */ ++#define AID_OBSOLETE_001 KGIDT_INIT(3002) /* was NET_BT */ ++#define AID_INET KGIDT_INIT(3003) ++#define AID_NET_RAW KGIDT_INIT(3004) ++#define AID_NET_ADMIN KGIDT_INIT(3005) ++#define AID_NET_BW_STATS KGIDT_INIT(3006) /* read bandwidth statistics */ ++#define AID_NET_BW_ACCT KGIDT_INIT(3007) /* change bandwidth statistics accounting */ ++ ++#endif +diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h +index aac0f9e..bb2b01a 100644 +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -1185,7 +1185,11 @@ extern int blk_verify_command(unsigned char *cmd, fmode_t has_write_perm); + enum blk_default_limits { + BLK_MAX_SEGMENTS = 128, + BLK_SAFE_MAX_SECTORS = 255, ++#if defined(CONFIG_ARCH_HI3559) || defined(CONFIG_ARCH_HI3556) ++ BLK_DEF_MAX_SECTORS = 8192, ++#else + BLK_DEF_MAX_SECTORS = 1024, ++#endif + BLK_MAX_SEGMENT_SIZE = 65536, + BLK_SEG_BOUNDARY_MASK = 0xFFFFFFFFUL, + }; +diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h +index 1d51968..cbc8db1 100644 +--- a/include/linux/cgroup.h ++++ b/include/linux/cgroup.h +@@ -615,6 +615,8 @@ struct cgroup_subsys { + void (*css_free)(struct cgroup_subsys_state *css); + void (*css_reset)(struct cgroup_subsys_state *css); + ++ int (*allow_attach)(struct cgroup_subsys_state *css, ++ struct cgroup_taskset *tset); + int (*can_attach)(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset); + void (*cancel_attach)(struct cgroup_subsys_state *css, +@@ -911,6 +913,17 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from); + struct cgroup_subsys_state *css_tryget_online_from_dir(struct dentry *dentry, + struct cgroup_subsys *ss); + ++/* ++ * Default Android check for whether the current process is allowed to move a ++ * task across cgroups, either because CAP_SYS_NICE is set or because the uid ++ * of the calling process is the same as the moved task or because we are ++ * running as root. ++ * Returns 0 if this is allowed, or -EACCES otherwise. ++ */ ++int subsys_cgroup_allow_attach(struct cgroup_subsys_state *css, ++ struct cgroup_taskset *tset); ++ ++ + #else /* !CONFIG_CGROUPS */ + + static inline int cgroup_init_early(void) { return 0; } +@@ -932,6 +945,11 @@ static inline int cgroup_attach_task_all(struct task_struct *from, + return 0; + } + ++static inline int subsys_cgroup_allow_attach(struct cgroup_subsys_state *css, ++ void *tset) ++{ ++ return -EINVAL; ++} + #endif /* !CONFIG_CGROUPS */ + + #endif /* _LINUX_CGROUP_H */ +diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h +index 0ca5f60..228a987 100644 +--- a/include/linux/clk-private.h ++++ b/include/linux/clk-private.h +@@ -13,7 +13,9 @@ + + #include <linux/clk-provider.h> + #include <linux/kref.h> ++#include <linux/ktime.h> + #include <linux/list.h> ++#include <linux/rbtree.h> + + /* + * WARNING: Do not include clk-private.h from any file that implements struct +@@ -28,6 +30,14 @@ + + struct module; + ++#ifdef CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING ++struct freq_stats { ++ ktime_t time_spent; ++ unsigned long rate; ++ struct rb_node node; ++}; ++#endif /*CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING*/ ++ + struct clk { + const char *name; + const struct clk_ops *ops; +@@ -53,6 +63,13 @@ struct clk { + unsigned int notifier_count; + #ifdef CONFIG_DEBUG_FS + struct dentry *dentry; ++#ifdef CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING ++ struct rb_root freq_stats_table; ++ struct freq_stats *current_freq_stats; ++ ktime_t default_freq_time; ++ ktime_t start_time; ++#endif /* CONFIG_COMMON_CLK_FREQ_STATS_ACCOUNTING*/ ++ + #endif + struct kref ref; + }; +diff --git a/include/linux/clock_cooling.h b/include/linux/clock_cooling.h +new file mode 100644 +index 0000000..4d1019d +--- /dev/null ++++ b/include/linux/clock_cooling.h +@@ -0,0 +1,65 @@ ++/* ++ * linux/include/linux/clock_cooling.h ++ * ++ * Copyright (C) 2014 Eduardo Valentin <edubezval@gmail.com> ++ * ++ * Copyright (C) 2013 Texas Instruments Inc. ++ * Contact: Eduardo Valentin <eduardo.valentin@ti.com> ++ * ++ * Highly based on cpu_cooling.c. ++ * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) ++ * Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org> ++ * ++ * 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; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#ifndef __CPU_COOLING_H__ ++#define __CPU_COOLING_H__ ++ ++#include <linux/of.h> ++#include <linux/thermal.h> ++#include <linux/cpumask.h> ++ ++#ifdef CONFIG_CLOCK_THERMAL ++/** ++ * clock_cooling_register - function to create clock cooling device. ++ * @dev: struct device pointer to the device used as clock cooling device. ++ * @clock_name: string containing the clock used as cooling mechanism. ++ */ ++struct thermal_cooling_device * ++clock_cooling_register(struct device *dev, const char *clock_name); ++ ++/** ++ * clock_cooling_unregister - function to remove clock cooling device. ++ * @cdev: thermal cooling device pointer. ++ */ ++void clock_cooling_unregister(struct thermal_cooling_device *cdev); ++ ++unsigned long clock_cooling_get_level(struct thermal_cooling_device *cdev, ++ unsigned long freq); ++#else /* !CONFIG_CLOCK_THERMAL */ ++static inline struct thermal_cooling_device * ++clock_cooling_register(struct device *dev, const char *clock_name) ++{ ++ return NULL; ++} ++static inline ++void clock_cooling_unregister(struct thermal_cooling_device *cdev) ++{ ++} ++static inline ++unsigned long clock_cooling_get_level(struct thermal_cooling_device *cdev, ++ unsigned long freq) ++{ ++ return THERMAL_CSTATE_INVALID; ++} ++#endif /* CONFIG_CLOCK_THERMAL */ ++ ++#endif /* __CPU_COOLING_H__ */ +diff --git a/include/linux/coresight.h b/include/linux/coresight.h +new file mode 100644 +index 0000000..44c1597 +--- /dev/null ++++ b/include/linux/coresight.h +@@ -0,0 +1,251 @@ ++/* Copyright (c) 2012, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _LINUX_CORESIGHT_H ++#define _LINUX_CORESIGHT_H ++ ++#include <linux/device.h> ++ ++/* Peripheral id registers (0xFD0-0xFEC) */ ++#define CORESIGHT_PERIPHIDR4 0xfd0 ++#define CORESIGHT_PERIPHIDR5 0xfd4 ++#define CORESIGHT_PERIPHIDR6 0xfd8 ++#define CORESIGHT_PERIPHIDR7 0xfdC ++#define CORESIGHT_PERIPHIDR0 0xfe0 ++#define CORESIGHT_PERIPHIDR1 0xfe4 ++#define CORESIGHT_PERIPHIDR2 0xfe8 ++#define CORESIGHT_PERIPHIDR3 0xfeC ++/* Component id registers (0xFF0-0xFFC) */ ++#define CORESIGHT_COMPIDR0 0xff0 ++#define CORESIGHT_COMPIDR1 0xff4 ++#define CORESIGHT_COMPIDR2 0xff8 ++#define CORESIGHT_COMPIDR3 0xffC ++ ++#define ETM_ARCH_V3_3 0x23 ++#define ETM_ARCH_V3_5 0x25 ++#define PFT_ARCH_V1_0 0x30 ++#define PFT_ARCH_V1_1 0x31 ++ ++#define CORESIGHT_UNLOCK 0xc5acce55 ++ ++extern struct bus_type coresight_bustype; ++ ++enum coresight_dev_type { ++ CORESIGHT_DEV_TYPE_NONE, ++ CORESIGHT_DEV_TYPE_SINK, ++ CORESIGHT_DEV_TYPE_LINK, ++ CORESIGHT_DEV_TYPE_LINKSINK, ++ CORESIGHT_DEV_TYPE_SOURCE, ++}; ++ ++enum coresight_dev_subtype_sink { ++ CORESIGHT_DEV_SUBTYPE_SINK_NONE, ++ CORESIGHT_DEV_SUBTYPE_SINK_PORT, ++ CORESIGHT_DEV_SUBTYPE_SINK_BUFFER, ++}; ++ ++enum coresight_dev_subtype_link { ++ CORESIGHT_DEV_SUBTYPE_LINK_NONE, ++ CORESIGHT_DEV_SUBTYPE_LINK_MERG, ++ CORESIGHT_DEV_SUBTYPE_LINK_SPLIT, ++ CORESIGHT_DEV_SUBTYPE_LINK_FIFO, ++}; ++ ++enum coresight_dev_subtype_source { ++ CORESIGHT_DEV_SUBTYPE_SOURCE_NONE, ++ CORESIGHT_DEV_SUBTYPE_SOURCE_PROC, ++ CORESIGHT_DEV_SUBTYPE_SOURCE_BUS, ++ CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE, ++}; ++ ++/** ++ * struct coresight_dev_subtype - further characterisation of a type ++ * @sink_subtype: type of sink this component is, as defined ++ by @coresight_dev_subtype_sink. ++ * @link_subtype: type of link this component is, as defined ++ by @coresight_dev_subtype_link. ++ * @source_subtype: type of source this component is, as defined ++ by @coresight_dev_subtype_source. ++ */ ++struct coresight_dev_subtype { ++ enum coresight_dev_subtype_sink sink_subtype; ++ enum coresight_dev_subtype_link link_subtype; ++ enum coresight_dev_subtype_source source_subtype; ++}; ++ ++/** ++ * struct coresight_platform_data - data harvested from the DT specification ++ * @cpu: the CPU a source belongs to. Only applicable for ETM/PTMs. ++ * @name: name of the component as shown under sysfs. ++ * @nr_inport: number of input ports for this component. ++ * @outports: list of remote enpoint port number. ++ * @child_names:name of all child components connected to this device. ++ * @child_ports:child component port number the current component is ++ connected to. ++ * @nr_outport: number of output ports for this component. ++ * @clk: The clock this component is associated to. ++ */ ++struct coresight_platform_data { ++ int cpu; ++ const char *name; ++ int nr_inport; ++ int *outports; ++ const char **child_names; ++ int *child_ports; ++ int nr_outport; ++ struct clk *clk; ++}; ++ ++/** ++ * struct coresight_desc - description of a component required from drivers ++ * @type: as defined by @coresight_dev_type. ++ * @subtype: as defined by @coresight_dev_subtype. ++ * @ops: generic operations for this component, as defined ++ by @coresight_ops. ++ * @pdata: platform data collected from DT. ++ * @dev: The device entity associated to this component. ++ * @groups :operations specific to this component. These will end up ++ in the component's sysfs sub-directory. ++ */ ++struct coresight_desc { ++ enum coresight_dev_type type; ++ struct coresight_dev_subtype subtype; ++ const struct coresight_ops *ops; ++ struct coresight_platform_data *pdata; ++ struct device *dev; ++ const struct attribute_group **groups; ++}; ++ ++/** ++ * struct coresight_connection - representation of a single connection ++ * @ref_count: keeping count a port' references. ++ * @outport: a connection's output port number. ++ * @chid_name: remote component's name. ++ * @child_port: remote component's port number @output is connected to. ++ * @child_dev: a @coresight_device representation of the component ++ connected to @outport. ++ */ ++struct coresight_connection { ++ int outport; ++ const char *child_name; ++ int child_port; ++ struct coresight_device *child_dev; ++}; ++ ++/** ++ * struct coresight_device - representation of a device as used by the framework ++ * @nr_inport: number of input port associated to this component. ++ * @nr_outport: number of output port associated to this component. ++ * @type: as defined by @coresight_dev_type. ++ * @subtype: as defined by @coresight_dev_subtype. ++ * @ops: generic operations for this component, as defined ++ by @coresight_ops. ++ * @dev: The device entity associated to this component. ++ * @refcnt: keep track of what is in use. ++ * @path_link: link of current component into the path being enabled. ++ * @orphan: true if the component has connections that haven't been linked. ++ * @enable: 'true' if component is currently part of an active path. ++ * @activated: 'true' only if a _sink_ has been activated. A sink can be ++ activated but not yet enabled. Enabling for a _sink_ ++ happens when a source has been selected for that it. ++ */ ++struct coresight_device { ++ struct coresight_connection *conns; ++ int nr_inport; ++ int nr_outport; ++ enum coresight_dev_type type; ++ struct coresight_dev_subtype subtype; ++ const struct coresight_ops *ops; ++ struct device dev; ++ atomic_t *refcnt; ++ struct list_head path_link; ++ bool orphan; ++ bool enable; /* true only if configured as part of a path */ ++ bool activated; /* true only if a sink is part of a path */ ++}; ++ ++#define to_coresight_device(d) container_of(d, struct coresight_device, dev) ++ ++#define source_ops(csdev) csdev->ops->source_ops ++#define sink_ops(csdev) csdev->ops->sink_ops ++#define link_ops(csdev) csdev->ops->link_ops ++ ++/** ++ * struct coresight_ops_sink - basic operations for a sink ++ * Operations available for sinks ++ * @enable: enables the sink. ++ * @disable: disables the sink. ++ */ ++struct coresight_ops_sink { ++ int (*enable)(struct coresight_device *csdev); ++ void (*disable)(struct coresight_device *csdev); ++}; ++ ++/** ++ * struct coresight_ops_link - basic operations for a link ++ * Operations available for links. ++ * @enable: enables flow between iport and oport. ++ * @disable: disables flow between iport and oport. ++ */ ++struct coresight_ops_link { ++ int (*enable)(struct coresight_device *csdev, int iport, int oport); ++ void (*disable)(struct coresight_device *csdev, int iport, int oport); ++}; ++ ++/** ++ * struct coresight_ops_source - basic operations for a source ++ * Operations available for sources. ++ * @trace_id: returns the value of the component's trace ID as known ++ to the HW. ++ * @enable: enables tracing from a source. ++ * @disable: disables tracing for a source. ++ */ ++struct coresight_ops_source { ++ int (*trace_id)(struct coresight_device *csdev); ++ int (*enable)(struct coresight_device *csdev); ++ void (*disable)(struct coresight_device *csdev); ++}; ++ ++struct coresight_ops { ++ const struct coresight_ops_sink *sink_ops; ++ const struct coresight_ops_link *link_ops; ++ const struct coresight_ops_source *source_ops; ++}; ++ ++#ifdef CONFIG_CORESIGHT ++extern struct coresight_device * ++coresight_register(struct coresight_desc *desc); ++extern void coresight_unregister(struct coresight_device *csdev); ++extern int coresight_enable(struct coresight_device *csdev); ++extern void coresight_disable(struct coresight_device *csdev); ++extern int coresight_timeout(void __iomem *addr, u32 offset, ++ int position, int value); ++#else ++static inline struct coresight_device * ++coresight_register(struct coresight_desc *desc) { return NULL; } ++static inline void coresight_unregister(struct coresight_device *csdev) {} ++static inline int ++coresight_enable(struct coresight_device *csdev) { return -ENOSYS; } ++static inline void coresight_disable(struct coresight_device *csdev) {} ++static inline int coresight_timeout(void __iomem *addr, u32 offset, ++ int position, int value) { return 1; } ++#endif ++ ++#ifdef CONFIG_OF ++extern struct coresight_platform_data *of_get_coresight_platform_data( ++ struct device *dev, struct device_node *node); ++#else ++static inline struct coresight_platform_data *of_get_coresight_platform_data( ++ struct device *dev, struct device_node *node) { return NULL; } ++#endif ++ ++#endif +diff --git a/include/linux/cpu.h b/include/linux/cpu.h +index b2d9a43..10d981a 100644 +--- a/include/linux/cpu.h ++++ b/include/linux/cpu.h +@@ -267,4 +267,11 @@ void arch_cpu_idle_enter(void); + void arch_cpu_idle_exit(void); + void arch_cpu_idle_dead(void); + ++#define IDLE_START 1 ++#define IDLE_END 2 ++ ++void idle_notifier_register(struct notifier_block *n); ++void idle_notifier_unregister(struct notifier_block *n); ++void idle_notifier_call_chain(unsigned long val); ++ + #endif /* _LINUX_CPU_H_ */ +diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h +index c303d38..8ac0331 100644 +--- a/include/linux/cpu_cooling.h ++++ b/include/linux/cpu_cooling.h +@@ -28,6 +28,9 @@ + #include <linux/thermal.h> + #include <linux/cpumask.h> + ++typedef int (*get_static_t)(cpumask_t *cpumask, int interval, ++ unsigned long voltage, u32 *power); ++ + #ifdef CONFIG_CPU_THERMAL + /** + * cpufreq_cooling_register - function to create cpufreq cooling device. +@@ -36,6 +39,10 @@ + struct thermal_cooling_device * + cpufreq_cooling_register(const struct cpumask *clip_cpus); + ++struct thermal_cooling_device * ++cpufreq_power_cooling_register(const struct cpumask *clip_cpus, ++ u32 capacitance, get_static_t plat_static_func); ++ + /** + * of_cpufreq_cooling_register - create cpufreq cooling device based on DT. + * @np: a valid struct device_node to the cooling device device tree node. +@@ -45,6 +52,12 @@ cpufreq_cooling_register(const struct cpumask *clip_cpus); + struct thermal_cooling_device * + of_cpufreq_cooling_register(struct device_node *np, + const struct cpumask *clip_cpus); ++ ++struct thermal_cooling_device * ++of_cpufreq_power_cooling_register(struct device_node *np, ++ const struct cpumask *clip_cpus, ++ u32 capacitance, ++ get_static_t plat_static_func); + #else + static inline struct thermal_cooling_device * + of_cpufreq_cooling_register(struct device_node *np, +@@ -52,6 +65,15 @@ of_cpufreq_cooling_register(struct device_node *np, + { + return NULL; + } ++ ++static inline struct thermal_cooling_device * ++of_cpufreq_power_cooling_register(struct device_node *np, ++ const struct cpumask *clip_cpus, ++ u32 capacitance, ++ get_static_t plat_static_func) ++{ ++ return NULL; ++} + #endif + + /** +@@ -68,11 +90,28 @@ cpufreq_cooling_register(const struct cpumask *clip_cpus) + return NULL; + } + static inline struct thermal_cooling_device * ++cpufreq_power_cooling_register(const struct cpumask *clip_cpus, ++ u32 capacitance, get_static_t plat_static_func) ++{ ++ return NULL; ++} ++ ++static inline struct thermal_cooling_device * + of_cpufreq_cooling_register(struct device_node *np, + const struct cpumask *clip_cpus) + { + return NULL; + } ++ ++static inline struct thermal_cooling_device * ++of_cpufreq_power_cooling_register(struct device_node *np, ++ const struct cpumask *clip_cpus, ++ u32 capacitance, ++ get_static_t plat_static_func) ++{ ++ return NULL; ++} ++ + static inline + void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) + { +diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h +index 503b085..56ea8a3 100644 +--- a/include/linux/cpufreq.h ++++ b/include/linux/cpufreq.h +@@ -18,6 +18,8 @@ + #include <linux/notifier.h> + #include <linux/spinlock.h> + #include <linux/sysfs.h> ++#include <asm/cputime.h> ++ + + /********************************************************************* + * CPUFREQ INTERFACE * +@@ -481,6 +483,9 @@ extern struct cpufreq_governor cpufreq_gov_ondemand; + #elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE) + extern struct cpufreq_governor cpufreq_gov_conservative; + #define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_conservative) ++#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE) ++extern struct cpufreq_governor cpufreq_gov_interactive; ++#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_interactive) + #endif + + /********************************************************************* +@@ -597,4 +602,11 @@ unsigned int cpufreq_generic_get(unsigned int cpu); + int cpufreq_generic_init(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, + unsigned int transition_latency); ++ ++/********************************************************************* ++ * CPUFREQ STATS * ++ *********************************************************************/ ++ ++void acct_update_power(struct task_struct *p, cputime_t cputime); ++ + #endif /* _LINUX_CPUFREQ_H */ +diff --git a/include/linux/dcache.h b/include/linux/dcache.h +index 1c2f1b8..af7410c 100644 +--- a/include/linux/dcache.h ++++ b/include/linux/dcache.h +@@ -462,10 +462,39 @@ static inline bool d_is_positive(const struct dentry *dentry) + return !d_is_negative(dentry); + } + ++/** ++ * d_really_is_positive - Determine if a dentry is really positive (ignoring fallthroughs) ++ * @dentry: The dentry in question ++ * ++ * Returns true if the dentry represents a name that maps to an inode ++ * (ie. ->d_inode is not NULL). The dentry might still represent a whiteout if ++ * that is represented on medium as a 0,0 chardev. ++ * ++ * Note! (1) This should be used *only* by a filesystem to examine its own ++ * dentries. It should not be used to look at some other filesystem's ++ * dentries. (2) It should also be used in combination with d_inode() to get ++ * the inode. ++ */ ++static inline bool d_really_is_positive(const struct dentry *dentry) ++{ ++ return dentry->d_inode != NULL; ++} + extern int sysctl_vfs_cache_pressure; + + static inline unsigned long vfs_pressure_ratio(unsigned long val) + { + return mult_frac(val, sysctl_vfs_cache_pressure, 100); + } ++ ++/** ++ * d_inode - Get the actual inode of this dentry ++ * @dentry: The dentry to query ++ * ++ * This is the helper normal filesystems should use to get at their own inodes ++ * in their own dentries and ignore the layering superimposed upon them. ++ */ ++static inline struct inode *d_inode(const struct dentry *dentry) ++{ ++ return dentry->d_inode; ++} + #endif /* __LINUX_DCACHE_H */ +diff --git a/include/linux/falloc.h b/include/linux/falloc.h +index 3159168..1f3b42c 100644 +--- a/include/linux/falloc.h ++++ b/include/linux/falloc.h +@@ -21,4 +21,9 @@ struct space_resv { + #define FS_IOC_RESVSP _IOW('X', 40, struct space_resv) + #define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv) + ++#define FALLOC_FL_SUPPORTED_MASK (FALLOC_FL_KEEP_SIZE | \ ++ FALLOC_FL_PUNCH_HOLE | \ ++ FALLOC_FL_COLLAPSE_RANGE | \ ++ FALLOC_FL_ZERO_RANGE | \ ++ FALLOC_FL_INSERT_RANGE) + #endif /* _FALLOC_H_ */ +diff --git a/include/linux/filter.h b/include/linux/filter.h +index ca95abd..c10500e 100644 +--- a/include/linux/filter.h ++++ b/include/linux/filter.h +@@ -358,7 +358,11 @@ static inline void bpf_prog_unlock_ro(struct bpf_prog *fp) + } + #endif /* CONFIG_DEBUG_SET_MODULE_RONX */ + +-int sk_filter(struct sock *sk, struct sk_buff *skb); ++int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap); ++static inline int sk_filter(struct sock *sk, struct sk_buff *skb) ++{ ++ return sk_filter_trim_cap(sk, skb, 1); ++} + + void bpf_prog_select_runtime(struct bpf_prog *fp); + void bpf_prog_free(struct bpf_prog *fp); +diff --git a/include/linux/fs.h b/include/linux/fs.h +index 84d6729..1260de3 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -402,6 +402,8 @@ struct address_space { + struct rb_root i_mmap; /* tree of private and shared mappings */ + struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */ + struct mutex i_mmap_mutex; /* protect tree, count, list */ ++ struct rw_semaphore i_mmap_rwsem; /* protect tree, count, list */ ++ + /* Protected by tree_lock together with the radix tree */ + unsigned long nrpages; /* number of total pages */ + unsigned long nrshadows; /* number of shadow entries */ +@@ -467,6 +469,26 @@ struct block_device { + + int mapping_tagged(struct address_space *mapping, int tag); + ++static inline void i_mmap_lock_write(struct address_space *mapping) ++{ ++ down_write(&mapping->i_mmap_rwsem); ++} ++ ++static inline void i_mmap_unlock_write(struct address_space *mapping) ++{ ++ up_write(&mapping->i_mmap_rwsem); ++} ++ ++static inline void i_mmap_lock_read(struct address_space *mapping) ++{ ++ down_read(&mapping->i_mmap_rwsem); ++} ++ ++static inline void i_mmap_unlock_read(struct address_space *mapping) ++{ ++ up_read(&mapping->i_mmap_rwsem); ++} ++ + /* + * Might pages of this file be mapped into userspace? + */ +@@ -657,6 +679,31 @@ enum inode_i_mutex_lock_class + I_MUTEX_PARENT2, + }; + ++static inline void inode_lock(struct inode *inode) ++{ ++ mutex_lock(&inode->i_mutex); ++} ++ ++static inline void inode_unlock(struct inode *inode) ++{ ++ mutex_unlock(&inode->i_mutex); ++} ++ ++static inline int inode_trylock(struct inode *inode) ++{ ++ return mutex_trylock(&inode->i_mutex); ++} ++ ++static inline int inode_is_locked(struct inode *inode) ++{ ++ return mutex_is_locked(&inode->i_mutex); ++} ++ ++static inline void inode_lock_nested(struct inode *inode, unsigned subclass) ++{ ++ mutex_lock_nested(&inode->i_mutex, subclass); ++} ++ + void lock_two_nondirectories(struct inode *, struct inode*); + void unlock_two_nondirectories(struct inode *, struct inode*); + +@@ -790,6 +837,9 @@ struct file { + } f_u; + struct path f_path; + #define f_dentry f_path.dentry ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++#define f_vfsmnt f_path.mnt ++#endif + struct inode *f_inode; /* cached value */ + const struct file_operations *f_op; + +@@ -1219,6 +1269,15 @@ struct super_block { + + struct list_head s_inodes; /* all inodes */ + struct hlist_bl_head s_anon; /* anonymous dentries for (nfs) exporting */ ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ ++ /* fix me */ ++/*#ifdef CONFIG_SMP ++ struct list_head __percpu *s_files; ++#else*/ ++ struct list_head s_files; ++/*#endif*/ ++#endif + struct list_head s_mounts; /* list of mounts; _not_ for fs use */ + struct block_device *s_bdev; + struct backing_dev_info *s_bdi; +@@ -2663,6 +2722,7 @@ extern int buffer_migrate_page(struct address_space *, + extern int inode_change_ok(const struct inode *, struct iattr *); + extern int inode_newsize_ok(const struct inode *, loff_t offset); + extern void setattr_copy(struct inode *inode, const struct iattr *attr); ++extern int setattr_killpriv(struct dentry *dentry, struct iattr *attr); + + extern int file_update_time(struct file *file); + +diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h +index 28672e8..cac1e80 100644 +--- a/include/linux/ftrace_event.h ++++ b/include/linux/ftrace_event.h +@@ -44,6 +44,10 @@ const char *ftrace_print_bitmask_seq(struct trace_seq *p, void *bitmask_ptr, + const char *ftrace_print_hex_seq(struct trace_seq *p, + const unsigned char *buf, int len); + ++const char *ftrace_print_array_seq(struct trace_seq *p, ++ const void *buf, int buf_len, ++ size_t el_size); ++ + struct trace_iterator; + struct trace_event; + +diff --git a/include/linux/gpio_event.h b/include/linux/gpio_event.h +new file mode 100644 +index 0000000..2613fc5 +--- /dev/null ++++ b/include/linux/gpio_event.h +@@ -0,0 +1,170 @@ ++/* include/linux/gpio_event.h ++ * ++ * Copyright (C) 2007 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_GPIO_EVENT_H ++#define _LINUX_GPIO_EVENT_H ++ ++#include <linux/input.h> ++ ++struct gpio_event_input_devs { ++ int count; ++ struct input_dev *dev[]; ++}; ++enum { ++ GPIO_EVENT_FUNC_UNINIT = 0x0, ++ GPIO_EVENT_FUNC_INIT = 0x1, ++ GPIO_EVENT_FUNC_SUSPEND = 0x2, ++ GPIO_EVENT_FUNC_RESUME = 0x3, ++}; ++struct gpio_event_info { ++ int (*func)(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, ++ void **data, int func); ++ int (*event)(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, ++ void **data, unsigned int dev, unsigned int type, ++ unsigned int code, int value); /* out events */ ++ bool no_suspend; ++}; ++ ++struct gpio_event_platform_data { ++ const char *name; ++ struct gpio_event_info **info; ++ size_t info_count; ++ int (*power)(const struct gpio_event_platform_data *pdata, bool on); ++ const char *names[]; /* If name is NULL, names contain a NULL */ ++ /* terminated list of input devices to create */ ++}; ++ ++#define GPIO_EVENT_DEV_NAME "gpio-event" ++ ++/* Key matrix */ ++ ++enum gpio_event_matrix_flags { ++ /* unset: drive active output low, set: drive active output high */ ++ GPIOKPF_ACTIVE_HIGH = 1U << 0, ++ GPIOKPF_DEBOUNCE = 1U << 1, ++ GPIOKPF_REMOVE_SOME_PHANTOM_KEYS = 1U << 2, ++ GPIOKPF_REMOVE_PHANTOM_KEYS = GPIOKPF_REMOVE_SOME_PHANTOM_KEYS | ++ GPIOKPF_DEBOUNCE, ++ GPIOKPF_DRIVE_INACTIVE = 1U << 3, ++ GPIOKPF_LEVEL_TRIGGERED_IRQ = 1U << 4, ++ GPIOKPF_PRINT_UNMAPPED_KEYS = 1U << 16, ++ GPIOKPF_PRINT_MAPPED_KEYS = 1U << 17, ++ GPIOKPF_PRINT_PHANTOM_KEYS = 1U << 18, ++}; ++ ++#define MATRIX_CODE_BITS (10) ++#define MATRIX_KEY_MASK ((1U << MATRIX_CODE_BITS) - 1) ++#define MATRIX_KEY(dev, code) \ ++ (((dev) << MATRIX_CODE_BITS) | (code & MATRIX_KEY_MASK)) ++ ++extern int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func); ++struct gpio_event_matrix_info { ++ /* initialize to gpio_event_matrix_func */ ++ struct gpio_event_info info; ++ /* size must be ninputs * noutputs */ ++ const unsigned short *keymap; ++ unsigned int *input_gpios; ++ unsigned int *output_gpios; ++ unsigned int ninputs; ++ unsigned int noutputs; ++ /* time to wait before reading inputs after driving each output */ ++ ktime_t settle_time; ++ /* time to wait before scanning the keypad a second time */ ++ ktime_t debounce_delay; ++ ktime_t poll_time; ++ unsigned flags; ++}; ++ ++/* Directly connected inputs and outputs */ ++ ++enum gpio_event_direct_flags { ++ GPIOEDF_ACTIVE_HIGH = 1U << 0, ++/* GPIOEDF_USE_DOWN_IRQ = 1U << 1, */ ++/* GPIOEDF_USE_IRQ = (1U << 2) | GPIOIDF_USE_DOWN_IRQ, */ ++ GPIOEDF_PRINT_KEYS = 1U << 8, ++ GPIOEDF_PRINT_KEY_DEBOUNCE = 1U << 9, ++ GPIOEDF_PRINT_KEY_UNSTABLE = 1U << 10, ++}; ++ ++struct gpio_event_direct_entry { ++ uint32_t gpio:16; ++ uint32_t code:10; ++ uint32_t dev:6; ++}; ++ ++/* inputs */ ++extern int gpio_event_input_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func); ++struct gpio_event_input_info { ++ /* initialize to gpio_event_input_func */ ++ struct gpio_event_info info; ++ ktime_t debounce_time; ++ ktime_t poll_time; ++ uint16_t flags; ++ uint16_t type; ++ const struct gpio_event_direct_entry *keymap; ++ size_t keymap_size; ++}; ++ ++/* outputs */ ++extern int gpio_event_output_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func); ++extern int gpio_event_output_event(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, ++ unsigned int dev, unsigned int type, ++ unsigned int code, int value); ++struct gpio_event_output_info { ++ /* initialize to gpio_event_output_func and gpio_event_output_event */ ++ struct gpio_event_info info; ++ uint16_t flags; ++ uint16_t type; ++ const struct gpio_event_direct_entry *keymap; ++ size_t keymap_size; ++}; ++ ++ ++/* axes */ ++ ++enum gpio_event_axis_flags { ++ GPIOEAF_PRINT_UNKNOWN_DIRECTION = 1U << 16, ++ GPIOEAF_PRINT_RAW = 1U << 17, ++ GPIOEAF_PRINT_EVENT = 1U << 18, ++}; ++ ++extern int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, ++ struct gpio_event_info *info, void **data, int func); ++struct gpio_event_axis_info { ++ /* initialize to gpio_event_axis_func */ ++ struct gpio_event_info info; ++ uint8_t count; /* number of gpios for this axis */ ++ uint8_t dev; /* device index when using multiple input devices */ ++ uint8_t type; /* EV_REL or EV_ABS */ ++ uint16_t code; ++ uint16_t decoded_size; ++ uint16_t (*map)(struct gpio_event_axis_info *info, uint16_t in); ++ uint32_t *gpio; ++ uint32_t flags; ++}; ++#define gpio_axis_2bit_gray_map gpio_axis_4bit_gray_map ++#define gpio_axis_3bit_gray_map gpio_axis_4bit_gray_map ++uint16_t gpio_axis_4bit_gray_map( ++ struct gpio_event_axis_info *info, uint16_t in); ++uint16_t gpio_axis_5bit_singletrack_map( ++ struct gpio_event_axis_info *info, uint16_t in); ++ ++#endif +diff --git a/include/linux/hid.h b/include/linux/hid.h +index 78ea9bf..1568080 100644 +--- a/include/linux/hid.h ++++ b/include/linux/hid.h +@@ -670,8 +670,8 @@ struct hid_driver { + int (*input_mapped)(struct hid_device *hdev, + struct hid_input *hidinput, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max); +- void (*input_configured)(struct hid_device *hdev, +- struct hid_input *hidinput); ++ int (*input_configured)(struct hid_device *hdev, ++ struct hid_input *hidinput); + void (*feature_mapping)(struct hid_device *hdev, + struct hid_field *field, + struct hid_usage *usage); +diff --git a/include/linux/hidmac.h b/include/linux/hidmac.h +new file mode 100644 +index 0000000..bf291be +--- /dev/null ++++ b/include/linux/hidmac.h +@@ -0,0 +1,135 @@ ++/****************************************************************************** ++ * COPYRIGHT (C) 2013 Hisilicon ++ * All rights reserved. ++ * *** ++ * Create 2013-08-23 ++ * ++ ******************************************************************************/ ++#ifndef __DMAC_H__ ++#define __DMAC_H__ ++ ++#define DMAC_ERROR_BASE 100 ++#define DMAC_CHN_SUCCESS (DMAC_ERROR_BASE+0x10) ++ ++#ifdef CONFIG_HI_DMAC ++extern int dma_driver_init(void); ++extern int dmac_channelclose(unsigned int channel); ++extern int dmac_channelstart(unsigned int u32channel); ++extern int dmac_channel_allocate(void *pisr); ++ ++extern int dmac_start_m2p(unsigned int channel, unsigned int pmemaddr, ++ unsigned int uwperipheralid, ++ unsigned int uwnumtransfers, ++ unsigned int next_lli_addr); ++extern int dmac_m2p_transfer(unsigned int memaddr, ++ unsigned int uwperipheralid, unsigned int length); ++extern int dmac_channel_free(unsigned int channel); ++extern int do_dma_m2p(unsigned int mem_addr, unsigned int peripheral_addr, ++ unsigned int length); ++extern int do_dma_p2m(unsigned int mem_addr, unsigned int peripheral_addr, ++ unsigned int length); ++extern int dmac_wait(int channel); ++extern int dmac_start_m2m(unsigned int channel, unsigned int psource, ++ unsigned int pdest, unsigned int uwnumtransfers); ++extern int dmac_m2m_transfer(unsigned int source, ++ unsigned int dest, unsigned int length); ++extern int dmac_register_isr(unsigned int channel, void *pisr); ++extern int free_dmalli_space(unsigned int *ppheadlli, unsigned int page_num); ++extern int dmac_start_llim2p(unsigned int channel, unsigned int *pfirst_lli, ++ unsigned int uwperipheralid); ++extern int dmac_buildllim2m(unsigned int *ppheadlli, unsigned int pdest, ++ unsigned int psource, ++ unsigned int totaltransfersize, ++ unsigned int uwnumtransfers); ++extern int dmac_start_llim2m(unsigned int channel, unsigned int *pfirst_lli); ++extern int allocate_dmalli_space(unsigned int *ppheadlli, ++ unsigned int page_num); ++ ++extern int do_dma_llim2m_isp(unsigned int *source, ++ unsigned int *dest, ++ unsigned int *length, ++ unsigned int num); ++ ++#else /* !CONFIG_HI_DMAC */ ++static inline int dma_driver_init(void) { return 0; } ++static inline int dmac_channelclose(unsigned int channel) { return 0; } ++static inline int dmac_channelstart(unsigned int u32channel) { return 0; } ++static inline int dmac_channel_allocate(void *pisr) { return 0; } ++ ++static inline int dmac_start_m2p(unsigned int channel, unsigned int pmemaddr, ++ unsigned int uwperipheralid, ++ unsigned int uwnumtransfers, ++ unsigned int next_lli_addr) ++{ return 0; } ++ ++static inline int dmac_m2p_transfer(unsigned int memaddr, ++ unsigned int uwperipheralid, unsigned int length) ++{ return 0; } ++ ++static inline int dmac_channel_free(unsigned int channel) { return 0; } ++ ++int do_dma_m2p(unsigned int mem_addr, unsigned int peripheral_addr, ++ unsigned int length) ++{ return 0; } ++ ++static inline int do_dma_p2m(unsigned int mem_addr, ++ unsigned int peripheral_addr, ++ unsigned int length) ++{ return 0; } ++ ++static inline int dmac_wait(int channel) { return 0; } ++ ++static inline int dmac_start_m2m(unsigned int channel, unsigned int psource, ++ unsigned int pdest, unsigned int uwnumtransfers) ++{ return 0; } ++ ++static inline int dmac_m2m_transfer(unsigned int source, ++ unsigned int dest, unsigned int length) ++{ return 0; } ++ ++static inline int dmac_register_isr(unsigned int channel, void *pisr) ++{ return 0; } ++ ++static inline int free_dmalli_space(unsigned int *ppheadlli, ++ unsigned int page_num) ++{ return 0; } ++ ++static inline int dmac_start_llim2p(unsigned int channel, ++ unsigned int *pfirst_lli, ++ unsigned int uwperipheralid) ++{ return 0; } ++ ++static inline int dmac_buildllim2m(unsigned int *ppheadlli, unsigned int pdest, ++ unsigned int psource, ++ unsigned int totaltransfersize, ++ unsigned int uwnumtransfers) ++{ return 0; } ++ ++static inline int dmac_start_llim2m(unsigned int channel, ++ unsigned int *pfirst_lli) ++{ return 0; } ++ ++static inline int allocate_dmalli_space(unsigned int *ppheadlli, ++ unsigned int page_num) ++{ return 0; } ++ ++static inline int do_dma_llim2m_isp(unsigned int *source, ++ unsigned int *dest, ++ unsigned int *length, ++ unsigned int num) ++{ return 0; } ++#endif /* CONFIG_HI_DMAC */ ++ ++/*structure for LLI*/ ++typedef struct dmac_lli { ++ /*source address*/ ++ unsigned int src_addr; ++ /*destination address*/ ++ unsigned int dst_addr; ++ /*pointer to next LLI*/ ++ unsigned int next_lli; ++ /*control word*/ ++ unsigned int lli_transfer_ctrl; ++} dmac_lli; ++ ++#endif +diff --git a/include/linux/i2c.h b/include/linux/i2c.h +index b556e0a..07e1ca8 100644 +--- a/include/linux/i2c.h ++++ b/include/linux/i2c.h +@@ -66,6 +66,29 @@ extern int i2c_master_recv(const struct i2c_client *client, char *buf, + */ + extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num); ++ ++#ifdef CONFIG_ARCH_HISI ++#if defined(CONFIG_HI_I2C) || defined(CONFIG_I2C_HISI_V110) ++extern int hi_i2c_dma_read(const struct i2c_client *client, ++ unsigned int dma_buf, unsigned int reg_addr, ++ unsigned int reg_addr_num, unsigned int length); ++ ++extern int hi_i2c_dma_write(const struct i2c_client *client, ++ unsigned int dma_buf, unsigned int reg_addr, ++ unsigned int reg_addr_num, unsigned int length); ++#endif ++ ++extern int hi_i2c_master_send(const struct i2c_client *client, const char *buf, ++ int count); ++ ++extern int hi_i2c_master_recv(const struct i2c_client *client, char *buf, ++ int count); ++ ++extern int hi_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, ++ int num); ++ ++#endif ++ + /* Unlocked flavor */ + extern int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num); +diff --git a/include/linux/if_pppolac.h b/include/linux/if_pppolac.h +new file mode 100644 +index 0000000..e40aa10 +--- /dev/null ++++ b/include/linux/if_pppolac.h +@@ -0,0 +1,23 @@ ++/* include/linux/if_pppolac.h ++ * ++ * Header for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661) ++ * ++ * Copyright (C) 2009 Google, Inc. ++ * Author: Chia-chi Yeh <chiachi@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __LINUX_IF_PPPOLAC_H ++#define __LINUX_IF_PPPOLAC_H ++ ++#include <uapi/linux/if_pppolac.h> ++ ++#endif /* __LINUX_IF_PPPOLAC_H */ +diff --git a/include/linux/if_pppopns.h b/include/linux/if_pppopns.h +new file mode 100644 +index 0000000..4ac621a +--- /dev/null ++++ b/include/linux/if_pppopns.h +@@ -0,0 +1,23 @@ ++/* include/linux/if_pppopns.h ++ * ++ * Header for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) ++ * ++ * Copyright (C) 2009 Google, Inc. ++ * Author: Chia-chi Yeh <chiachi@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __LINUX_IF_PPPOPNS_H ++#define __LINUX_IF_PPPOPNS_H ++ ++#include <uapi/linux/if_pppopns.h> ++ ++#endif /* __LINUX_IF_PPPOPNS_H */ +diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h +index aff7ad8..070ec7d 100644 +--- a/include/linux/if_pppox.h ++++ b/include/linux/if_pppox.h +@@ -41,6 +41,25 @@ struct pptp_opt { + u32 seq_sent, seq_recv; + int ppp_flags; + }; ++ ++struct pppolac_opt { ++ __u32 local; ++ __u32 remote; ++ __u32 recv_sequence; ++ __u32 xmit_sequence; ++ atomic_t sequencing; ++ int (*backlog_rcv)(struct sock *sk_udp, struct sk_buff *skb); ++}; ++ ++struct pppopns_opt { ++ __u16 local; ++ __u16 remote; ++ __u32 recv_sequence; ++ __u32 xmit_sequence; ++ void (*data_ready)(struct sock *sk_raw); ++ int (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb); ++}; ++ + #include <net/sock.h> + + struct pppox_sock { +@@ -51,6 +70,8 @@ struct pppox_sock { + union { + struct pppoe_opt pppoe; + struct pptp_opt pptp; ++ struct pppolac_opt lac; ++ struct pppopns_opt pns; + } proto; + __be16 num; + }; +diff --git a/include/linux/initramfs.h b/include/linux/initramfs.h +new file mode 100644 +index 0000000..fc7da63 +--- /dev/null ++++ b/include/linux/initramfs.h +@@ -0,0 +1,32 @@ ++/* ++ * include/linux/initramfs.h ++ * ++ * Copyright (C) 2015, Google ++ * Rom Lemarchand <romlem@android.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; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _LINUX_INITRAMFS_H ++#define _LINUX_INITRAMFS_H ++ ++#include <linux/kconfig.h> ++ ++#if IS_BUILTIN(CONFIG_BLK_DEV_INITRD) ++ ++int __init default_rootfs(void); ++ ++#endif ++ ++#endif /* _LINUX_INITRAMFS_H */ +diff --git a/include/linux/iopoll.h b/include/linux/iopoll.h +new file mode 100644 +index 0000000..f72b358 +--- /dev/null ++++ b/include/linux/iopoll.h +@@ -0,0 +1,144 @@ ++/* ++ * Copyright (c) 2012-2014 The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_IOPOLL_H ++#define _LINUX_IOPOLL_H ++ ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/hrtimer.h> ++#include <linux/delay.h> ++#include <linux/errno.h> ++#include <linux/io.h> ++ ++/** ++ * readx_poll_timeout - Periodically poll an address until a condition is met or a timeout occurs ++ * @op: accessor function (takes @addr as its only argument) ++ * @addr: Address to poll ++ * @val: Variable to read the value into ++ * @cond: Break condition (usually involving @val) ++ * @sleep_us: Maximum time to sleep between reads in us (0 ++ * tight-loops). Should be less than ~20ms since usleep_range ++ * is used (see Documentation/timers/timers-howto.txt). ++ * @timeout_us: Timeout in us, 0 means never timeout ++ * ++ * Returns 0 on success and -ETIMEDOUT upon a timeout. In either ++ * case, the last read value at @addr is stored in @val. Must not ++ * be called from atomic context if sleep_us or timeout_us are used. ++ * ++ * When available, you'll probably want to use one of the specialized ++ * macros defined below rather than this macro directly. ++ */ ++#define readx_poll_timeout(op, addr, val, cond, sleep_us, timeout_us) \ ++({ \ ++ ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \ ++ might_sleep_if(sleep_us); \ ++ for (;;) { \ ++ (val) = op(addr); \ ++ if (cond) \ ++ break; \ ++ if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \ ++ (val) = op(addr); \ ++ break; \ ++ } \ ++ if (sleep_us) \ ++ usleep_range((sleep_us >> 2) + 1, sleep_us); \ ++ } \ ++ (cond) ? 0 : -ETIMEDOUT; \ ++}) ++ ++/** ++ * readx_poll_timeout_atomic - Periodically poll an address until a condition is met or a timeout occurs ++ * @op: accessor function (takes @addr as its only argument) ++ * @addr: Address to poll ++ * @val: Variable to read the value into ++ * @cond: Break condition (usually involving @val) ++ * @delay_us: Time to udelay between reads in us (0 tight-loops). Should ++ * be less than ~10us since udelay is used (see ++ * Documentation/timers/timers-howto.txt). ++ * @timeout_us: Timeout in us, 0 means never timeout ++ * ++ * Returns 0 on success and -ETIMEDOUT upon a timeout. In either ++ * case, the last read value at @addr is stored in @val. ++ * ++ * When available, you'll probably want to use one of the specialized ++ * macros defined below rather than this macro directly. ++ */ ++#define readx_poll_timeout_atomic(op, addr, val, cond, delay_us, timeout_us) \ ++({ \ ++ ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \ ++ for (;;) { \ ++ (val) = op(addr); \ ++ if (cond) \ ++ break; \ ++ if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \ ++ (val) = op(addr); \ ++ break; \ ++ } \ ++ if (delay_us) \ ++ udelay(delay_us); \ ++ } \ ++ (cond) ? 0 : -ETIMEDOUT; \ ++}) ++ ++ ++#define readb_poll_timeout(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout(readb, addr, val, cond, delay_us, timeout_us) ++ ++#define readb_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout_atomic(readb, addr, val, cond, delay_us, timeout_us) ++ ++#define readw_poll_timeout(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout(readw, addr, val, cond, delay_us, timeout_us) ++ ++#define readw_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout_atomic(readw, addr, val, cond, delay_us, timeout_us) ++ ++#define readl_poll_timeout(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout(hi_readl, addr, val, cond, delay_us, timeout_us) ++ ++#define readl_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout_atomic(readl, addr, val, cond, delay_us, timeout_us) ++ ++#define readq_poll_timeout(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout(readq, addr, val, cond, delay_us, timeout_us) ++ ++#define readq_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout_atomic(readq, addr, val, cond, delay_us, timeout_us) ++ ++#define readb_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout(readb_relaxed, addr, val, cond, delay_us, timeout_us) ++ ++#define readb_relaxed_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout_atomic(readb_relaxed, addr, val, cond, delay_us, timeout_us) ++ ++#define readw_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout(readw_relaxed, addr, val, cond, delay_us, timeout_us) ++ ++#define readw_relaxed_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout_atomic(readw_relaxed, addr, val, cond, delay_us, timeout_us) ++ ++#define readl_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout(readl_relaxed, addr, val, cond, delay_us, timeout_us) ++ ++#define readl_relaxed_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout_atomic(readl_relaxed, addr, val, cond, delay_us, timeout_us) ++ ++#define readq_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout(readq_relaxed, addr, val, cond, delay_us, timeout_us) ++ ++#define readq_relaxed_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \ ++ readx_poll_timeout_atomic(readq_relaxed, addr, val, cond, delay_us, timeout_us) ++ ++#endif /* _LINUX_IOPOLL_H */ +diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h +index ff56053..ee05d8b 100644 +--- a/include/linux/ipv6.h ++++ b/include/linux/ipv6.h +@@ -37,11 +37,13 @@ struct ipv6_devconf { + __s32 accept_ra_rt_info_max_plen; + #endif + #endif ++ __s32 accept_ra_rt_table; + __s32 proxy_ndp; + __s32 accept_source_route; + __s32 accept_ra_from_local; + #ifdef CONFIG_IPV6_OPTIMISTIC_DAD + __s32 optimistic_dad; ++ __s32 use_optimistic; + #endif + #ifdef CONFIG_IPV6_MROUTE + __s32 mc_forwarding; +@@ -123,6 +125,12 @@ struct ipv6_mc_socklist; + struct ipv6_ac_socklist; + struct ipv6_fl_socklist; + ++struct inet6_cork { ++ struct ipv6_txoptions *opt; ++ u8 hop_limit; ++ u8 tclass; ++}; ++ + /** + * struct ipv6_pinfo - ipv6 private area + * +@@ -212,14 +220,10 @@ struct ipv6_pinfo { + struct ipv6_ac_socklist *ipv6_ac_list; + struct ipv6_fl_socklist __rcu *ipv6_fl_list; + +- struct ipv6_txoptions *opt; ++ struct ipv6_txoptions __rcu *opt; + struct sk_buff *pktoptions; + struct sk_buff *rxpmtu; +- struct { +- struct ipv6_txoptions *opt; +- u8 hop_limit; +- u8 tclass; +- } cork; ++ struct inet6_cork cork; + }; + + /* WARNING: don't change the layout of the members in {raw,udp,tcp}6_sock! */ +diff --git a/include/linux/kernel.h b/include/linux/kernel.h +index 3d770f5..54c20a4 100644 +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -103,6 +103,18 @@ + (((__x) - ((__d) / 2)) / (__d)); \ + } \ + ) ++/* ++ * Same as above but for u64 dividends. divisor must be a 32-bit ++ * number. ++ */ ++#define DIV_ROUND_CLOSEST_ULL(x, divisor)( \ ++{ \ ++ typeof(divisor) __d = divisor; \ ++ unsigned long long _tmp = (x) + (__d) / 2; \ ++ do_div(_tmp, __d); \ ++ _tmp; \ ++} \ ++) + + /* + * Multiplies an integer by a fraction, while avoiding unnecessary +@@ -814,4 +826,8 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { } + /* Other writable? Generally considered a bad idea. */ \ + BUILD_BUG_ON_ZERO((perms) & 2) + \ + (perms)) ++ ++/* To identify board information in panic logs, set this */ ++extern char *mach_panic_string; ++ + #endif +diff --git a/include/linux/keychord.h b/include/linux/keychord.h +new file mode 100644 +index 0000000..08cf540 +--- /dev/null ++++ b/include/linux/keychord.h +@@ -0,0 +1,23 @@ ++/* ++ * Key chord input driver ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++*/ ++ ++#ifndef __LINUX_KEYCHORD_H_ ++#define __LINUX_KEYCHORD_H_ ++ ++#include <uapi/linux/keychord.h> ++ ++#endif /* __LINUX_KEYCHORD_H_ */ +diff --git a/include/linux/keycombo.h b/include/linux/keycombo.h +new file mode 100644 +index 0000000..c6db262 +--- /dev/null ++++ b/include/linux/keycombo.h +@@ -0,0 +1,36 @@ ++/* ++ * include/linux/keycombo.h - platform data structure for keycombo driver ++ * ++ * Copyright (C) 2014 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_KEYCOMBO_H ++#define _LINUX_KEYCOMBO_H ++ ++#define KEYCOMBO_NAME "keycombo" ++ ++/* ++ * if key_down_fn and key_up_fn are both present, you are guaranteed that ++ * key_down_fn will return before key_up_fn is called, and that key_up_fn ++ * is called iff key_down_fn is called. ++ */ ++struct keycombo_platform_data { ++ void (*key_down_fn)(void *); ++ void (*key_up_fn)(void *); ++ void *priv; ++ int key_down_delay; /* Time in ms */ ++ int *keys_up; ++ int keys_down[]; /* 0 terminated */ ++}; ++ ++#endif /* _LINUX_KEYCOMBO_H */ +diff --git a/include/linux/keyreset.h b/include/linux/keyreset.h +new file mode 100644 +index 0000000..2e34afa +--- /dev/null ++++ b/include/linux/keyreset.h +@@ -0,0 +1,29 @@ ++/* ++ * include/linux/keyreset.h - platform data structure for resetkeys driver ++ * ++ * Copyright (C) 2014 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_KEYRESET_H ++#define _LINUX_KEYRESET_H ++ ++#define KEYRESET_NAME "keyreset" ++ ++struct keyreset_platform_data { ++ int (*reset_fn)(void); ++ int key_down_delay; ++ int *keys_up; ++ int keys_down[]; /* 0 terminated */ ++}; ++ ++#endif /* _LINUX_KEYRESET_H */ +diff --git a/include/linux/kmod.h b/include/linux/kmod.h +index 0555cc6..1632757 100644 +--- a/include/linux/kmod.h ++++ b/include/linux/kmod.h +@@ -73,6 +73,37 @@ extern struct subprocess_info * + call_usermodehelper_setup(char *path, char **argv, char **envp, gfp_t gfp_mask, + int (*init)(struct subprocess_info *info, struct cred *new), + void (*cleanup)(struct subprocess_info *), void *data); ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++int call_usermodehelper_exec_force(struct subprocess_info *info, int wait); ++#endif ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++static inline int ++call_usermodehelper_fns_force(char *path, char **argv, char **envp, ++ int wait, int (*init)(struct subprocess_info *info, ++ struct cred *new), void (*cleanup)(struct subprocess_info *), ++ void *data) ++{ ++ struct subprocess_info *info; ++ gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; ++ ++ info = call_usermodehelper_setup(path, argv, envp, gfp_mask, init, ++ cleanup, data); ++ ++ if (info == NULL) ++ return -ENOMEM; ++ ++ /*call_usermodehelper_setfns(info, init, cleanup, data);*/ ++ ++ return call_usermodehelper_exec_force(info, wait); ++} ++ ++static inline int ++call_usermodehelper_force(char *path, char **argv, char **envp, int wait) ++{ ++ return call_usermodehelper_fns_force(path, argv, envp, wait, ++ NULL, NULL, NULL); ++} ++#endif + + extern int + call_usermodehelper_exec(struct subprocess_info *info, int wait); +diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h +index 1cc89e9..ffb9c9d 100644 +--- a/include/linux/lsm_audit.h ++++ b/include/linux/lsm_audit.h +@@ -40,6 +40,11 @@ struct lsm_network_audit { + } fam; + }; + ++struct lsm_ioctlop_audit { ++ struct path path; ++ u16 cmd; ++}; ++ + /* Auxiliary data to use in generating the audit record. */ + struct common_audit_data { + char type; +@@ -53,6 +58,7 @@ struct common_audit_data { + #define LSM_AUDIT_DATA_KMOD 8 + #define LSM_AUDIT_DATA_INODE 9 + #define LSM_AUDIT_DATA_DENTRY 10 ++#define LSM_AUDIT_DATA_IOCTL_OP 11 + union { + struct path path; + struct dentry *dentry; +@@ -68,6 +74,7 @@ struct common_audit_data { + } key_struct; + #endif + char *kmod_name; ++ struct lsm_ioctlop_audit *op; + } u; + /* this union contains LSM specific data */ + union { +diff --git a/include/linux/mfd/hisi_fmc.h b/include/linux/mfd/hisi_fmc.h +new file mode 100644 +index 0000000..ac94cd8 +--- /dev/null ++++ b/include/linux/mfd/hisi_fmc.h +@@ -0,0 +1,503 @@ ++/* ++ * Header file for HiSilicon Flash Memory Controller Driver ++ * ++ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef __HISI_FMC_H ++#define __HISI_FMC_H ++ ++#include <linux/compiler.h> ++#include <linux/clk.h> ++#include <linux/mutex.h> ++#include <linux/bitops.h> ++ ++/*****************************************************************************/ ++#define _512B (512) ++#define _1K (1024) ++#define _2K (2048) ++#define _4K (4096) ++#define _8K (8192) ++#define _16K (16384) ++#define _32K (32768) ++#define _64K (0x10000UL) ++#define _128K (0x20000UL) ++#define _256K (0x40000UL) ++#define _512K (0x80000UL) ++#define _1M (0x100000UL) ++#define _2M (0x200000UL) ++#define _4M (0x400000UL) ++#define _8M (0x800000UL) ++#define _16M (0x1000000UL) ++#define _32M (0x2000000UL) ++#define _64M (0x4000000UL) ++#define _128M (0x8000000UL) ++#define _256M (0x10000000UL) ++#define _512M (0x20000000UL) ++#define _1G (0x40000000ULL) ++#define _2G (0x80000000ULL) ++#define _4G (0x100000000ULL) ++#define _8G (0x200000000ULL) ++#define _16G (0x400000000ULL) ++#define _64G (0x1000000000ULL) ++ ++/*****************************************************************************/ ++/* HIFMC REG MAP */ ++/*****************************************************************************/ ++#define FMC_CFG 0x00 ++#define FMC_CFG_SPI_NAND_SEL(_type) (((_size) & 0x3) << 11) ++#define SPI_NOR_ADDR_MODE BIT(10) ++#define FMC_CFG_OP_MODE_MASK BIT_MASK(0) ++#define FMC_CFG_OP_MODE_BOOT 0 ++#define FMC_CFG_OP_MODE_NORMAL 1 ++#define SPI_NOR_ADDR_MODE_3BYTES (0x0 << 10) ++#define SPI_NOR_ADDR_MODE_4BYTES (0x1 << 10) ++ ++#define FMC_CFG_BLOCK_SIZE(_size) (((_size) & 0x3) << 8) ++#define FMC_CFG_ECC_TYPE(_type) (((_type) & 0x7) << 5) ++#define FMC_CFG_PAGE_SIZE(_size) (((_size) & 0x3) << 3) ++#define FMC_CFG_FLASH_SEL(_type) (((_type) & 0x3) << 1) ++#define FMC_CFG_OP_MODE(_mode) ((_mode) & 0x1) ++ ++#define SPI_NAND_MFR_OTHER 0x0 ++#define SPI_NAND_MFR_WINBOND 0x1 ++#define SPI_NAND_MFR_ESMT 0x2 ++#define SPI_NAND_MFR_MICRON 0x3 ++ ++#define SPI_NAND_SEL_SHIFT 11 ++#define SPI_NAND_SEL_MASK (0x3 << SPI_NAND_SEL_SHIFT) ++ ++#define SPI_NOR_ADDR_MODE_3_BYTES 0x0 ++#define SPI_NOR_ADDR_MODE_4_BYTES 0x1 ++ ++#define SPI_NOR_ADDR_MODE_SHIFT 10 ++#define SPI_NOR_ADDR_MODE_MASK (0x1 << SPI_NOR_ADDR_MODE_SHIFT) ++ ++#define BLOCK_SIZE_64_PAGE 0x0 ++#define BLOCK_SIZE_128_PAGE 0x1 ++#define BLOCK_SIZE_256_PAGE 0x2 ++#define BLOCK_SIZE_512_PAGE 0x3 ++ ++#define BLOCK_SIZE_MASK (0x3 << 8) ++ ++#define ECC_TYPE_0BIT 0x0 ++#define ECC_TYPE_8BIT 0x1 ++#define ECC_TYPE_16BIT 0x2 ++#define ECC_TYPE_24BIT 0x3 ++#define ECC_TYPE_28BIT 0x4 ++#define ECC_TYPE_40BIT 0x5 ++#define ECC_TYPE_64BIT 0x6 ++ ++#define ECC_TYPE_SHIFT 5 ++#define ECC_TYPE_MASK (0x7 << ECC_TYPE_SHIFT) ++ ++#define PAGE_SIZE_2KB 0x0 ++#define PAGE_SIZE_4KB 0x1 ++#define PAGE_SIZE_8KB 0x2 ++#define PAGE_SIZE_16KB 0x3 ++ ++#define PAGE_SIZE_SHIFT 3 ++#define PAGE_SIZE_MASK (0x3 << PAGE_SIZE_SHIFT) ++ ++#define FLASH_TYPE_SPI_NOR 0x0 ++#define FLASH_TYPE_SPI_NAND 0x1 ++#define FLASH_TYPE_NAND 0x2 ++#define FLASH_TYPE_UNKNOWN 0x3 ++ ++#define FLASH_TYPE_SEL_MASK (0x3 << 1) ++#define GET_SPI_FLASH_TYPE(_reg) (((_reg) >> 1) & 0x3) ++ ++/*****************************************************************************/ ++#define FMC_GLOBAL_CFG 0x04 ++#define FMC_GLOBAL_CFG_WP_ENABLE BIT(6) ++#define FMC_GLOBAL_CFG_RANDOMIZER_EN (1 << 2) ++#define FLASH_TYPE_SEL_MASK (0x3 << 1) ++#define FMC_CFG_FLASH_SEL(_type) (((_type) & 0x3) << 1) ++ ++/*****************************************************************************/ ++#define FMC_SPI_TIMING_CFG 0x08 ++#define TIMING_CFG_TCSH(nr) (((nr) & 0xf) << 8) ++#define TIMING_CFG_TCSS(nr) (((nr) & 0xf) << 4) ++#define TIMING_CFG_TSHSL(nr) ((nr) & 0xf) ++ ++#define CS_HOLD_TIME 0x6 ++#define CS_SETUP_TIME 0x6 ++#define CS_DESELECT_TIME 0xf ++ ++/*****************************************************************************/ ++#define FMC_PND_PWIDTH_CFG 0x0c ++#define PWIDTH_CFG_RW_HCNT(_n) (((_n) & 0xf) << 8) ++#define PWIDTH_CFG_R_LCNT(_n) (((_n) & 0xf) << 4) ++#define PWIDTH_CFG_W_LCNT(_n) ((_n) & 0xf) ++ ++#define RW_H_WIDTH (0xa) ++#define R_L_WIDTH (0xa) ++#define W_L_WIDTH (0xa) ++ ++/*****************************************************************************/ ++#define FMC_INT 0x18 ++#define FMC_INT_AHB_OP BIT(7) ++#define FMC_INT_WR_LOCK BIT(6) ++#define FMC_INT_DMA_ERR BIT(5) ++#define FMC_INT_ERR_ALARM BIT(4) ++#define FMC_INT_ERR_INVALID BIT(3) ++#define FMC_INT_ERR_INVALID_MASK (0x8) ++#define FMC_INT_ERR_VALID BIT(2) ++#define FMC_INT_ERR_VALID_MASK (0x4) ++#define FMC_INT_OP_FAIL BIT(1) ++#define FMC_INT_OP_DONE BIT(0) ++ ++/*****************************************************************************/ ++#define FMC_INT_EN 0x1c ++#define FMC_INT_EN_AHB_OP BIT(7) ++#define FMC_INT_EN_WR_LOCK BIT(6) ++#define FMC_INT_EN_DMA_ERR BIT(5) ++#define FMC_INT_EN_ERR_ALARM BIT(4) ++#define FMC_INT_EN_ERR_INVALID BIT(3) ++#define FMC_INT_EN_ERR_VALID BIT(2) ++#define FMC_INT_EN_OP_FAIL BIT(1) ++#define FMC_INT_EN_OP_DONE BIT(0) ++ ++/*****************************************************************************/ ++#define FMC_INT_CLR 0x20 ++#define FMC_INT_CLR_AHB_OP BIT(7) ++#define FMC_INT_CLR_WR_LOCK BIT(6) ++#define FMC_INT_CLR_DMA_ERR BIT(5) ++#define FMC_INT_CLR_ERR_ALARM BIT(4) ++#define FMC_INT_CLR_ERR_INVALID BIT(3) ++#define FMC_INT_CLR_ERR_VALID BIT(2) ++#define FMC_INT_CLR_OP_FAIL BIT(1) ++#define FMC_INT_CLR_OP_DONE BIT(0) ++ ++#define FMC_INT_CLR_ALL 0xff ++ ++/*****************************************************************************/ ++#define FMC_CMD 0x24 ++#define FMC_CMD_CMD2(_cmd) (((_cmd) & 0xff) << 8) ++#define FMC_CMD_CMD1(_cmd) ((_cmd) & 0xff) ++ ++/*****************************************************************************/ ++#define FMC_ADDRH 0x28 ++#define FMC_ADDRH_SET(_addr) ((_addr) & 0xff) ++ ++/*****************************************************************************/ ++#define FMC_ADDRL 0x2c ++#define FMC_ADDRL_BLOCK_MASK(_page) ((_page) & 0xffffffc0) ++#define FMC_ADDRL_BLOCK_H_MASK(_page) (((_page) & 0xffff) << 16) ++#define FMC_ADDRL_BLOCK_L_MASK(_page) ((_page) & 0xffc0) ++ ++#define READ_ID_ADDR 0x00 ++#define PROTECT_ADDR 0xa0 ++#define FEATURE_ADDR 0xb0 ++#define STATUS_ADDR 0xc0 ++/*****************************************************************************/ ++#define FMC_OP_CFG 0x30 ++#define OP_CFG_FM_CS(_cs) ((_cs) << 11) ++#define OP_CFG_FORCE_CS_EN(_en) ((_en) << 10) ++#define OP_CFG_MEM_IF_TYPE(_type) (((_type) & 0x7) << 7) ++#define OP_CFG_ADDR_NUM(_addr) (((_addr) & 0x7) << 4) ++#define OP_CFG_DUMMY_NUM(_dummy) ((_dummy) & 0xf) ++ ++#define IF_TYPE_SHIFT 7 ++#define IF_TYPE_MASK (0x7 << IF_TYPE_SHIFT) ++ ++#define READ_ID_ADDR_NUM 1 ++#define FEATURES_OP_ADDR_NUM 1 ++#define STD_OP_ADDR_NUM 3 ++ ++/*****************************************************************************/ ++#define FMC_SPI_OP_ADDR 0x34 ++ ++/*****************************************************************************/ ++#define FMC_DATA_NUM 0x38 ++#define FMC_DATA_NUM_CNT(_n) ((_n) & 0x3fff) ++ ++#define SPI_NOR_SR_LEN 1 /* Status Register length */ ++#define SPI_NOR_CR_LEN 1 /* Config Register length */ ++#define FEATURES_DATA_LEN 1 ++#define READ_OOB_BB_LEN 1 ++ ++#define PROTECT_BRWD_MASK BIT(7) ++#define PROTECT_BP3_MASK BIT(6) ++#define PROTECT_BP2_MASK BIT(5) ++#define PROTECT_BP1_MASK BIT(4) ++#define PROTECT_BP0_MASK BIT(3) ++ ++#define ANY_BP_ENABLE(_val) ((PROTECT_BP3_MASK & _val) \ ++ || (PROTECT_BP2_MASK & _val) \ ++ || (PROTECT_BP1_MASK & _val) \ ++ || (PROTECT_BP0_MASK & _val)) ++ ++#define ALL_BP_MASK (PROTECT_BP3_MASK \ ++ | PROTECT_BP2_MASK \ ++ | PROTECT_BP1_MASK \ ++ | PROTECT_BP0_MASK) ++ ++#define FEATURE_ECC_ENABLE (1 << 4) ++#define FEATURE_QE_ENABLE (1 << 0) ++ ++/*****************************************************************************/ ++#define FMC_OP 0x3c ++#define FMC_OP_DUMMY_EN BIT(8) ++#define FMC_OP_CMD1_EN BIT(7) ++#define FMC_OP_ADDR_EN BIT(6) ++#define FMC_OP_WRITE_DATA_EN BIT(5) ++#define FMC_OP_CMD2_EN BIT(4) ++#define FMC_OP_WAIT_READY_EN BIT(3) ++#define FMC_OP_READ_DATA_EN BIT(2) ++#define FMC_OP_READ_STATUS_EN BIT(1) ++#define FMC_OP_REG_OP_START BIT(0) ++ ++/*****************************************************************************/ ++#define FMC_DMA_LEN 0x40 ++#define FMC_DMA_LEN_SET(_len) ((_len) & 0x0fffffff) ++ ++/*****************************************************************************/ ++#define FMC_DMA_AHB_CTRL 0x48 ++#define FMC_DMA_AHB_CTRL_DMA_PP_EN BIT(3) ++#define FMC_DMA_AHB_CTRL_BURST16_EN BIT(2) ++#define FMC_DMA_AHB_CTRL_BURST8_EN BIT(1) ++#define FMC_DMA_AHB_CTRL_BURST4_EN BIT(0) ++ ++#define ALL_BURST_ENABLE (FMC_DMA_AHB_CTRL_BURST16_EN \ ++ | FMC_DMA_AHB_CTRL_BURST8_EN \ ++ | FMC_DMA_AHB_CTRL_BURST4_EN) ++ ++#define FMC_DMA_ADDR_OFFSET 4096 ++ ++/*****************************************************************************/ ++#define FMC_DMA_SADDR_D0 0x4c ++#define HIFMC_DMA_MAX_LEN (4096) ++#define HIFMC_DMA_MASK (HIFMC_DMA_MAX_LEN - 1) ++ ++/*****************************************************************************/ ++#define FMC_DMA_SADDR_D1 0x50 ++ ++/*****************************************************************************/ ++#define FMC_DMA_SADDR_D2 0x54 ++ ++/*****************************************************************************/ ++#define FMC_DMA_SADDR_D3 0x58 ++ ++/*****************************************************************************/ ++#define FMC_DMA_SADDR_OOB 0x5c ++ ++/*****************************************************************************/ ++#define FMC_DMA_BLK_SADDR 0x60 ++#define FMC_DMA_BLK_SADDR_SET(_addr) ((_addr) & 0xffffff) ++ ++/*****************************************************************************/ ++#define FMC_DMA_BLK_LEN 0x64 ++#define FMC_DMA_BLK_LEN_SET(_len) ((_len) & 0xffff) ++ ++/*****************************************************************************/ ++#define FMC_OP_CTRL 0x68 ++#define OP_CTRL_RD_OPCODE(_code) (((_code) & 0xff) << 16) ++#define OP_CTRL_WR_OPCODE(_code) (((_code) & 0xff) << 8) ++#define OP_CTRL_RD_OP_SEL(_op) (((_op) & 0x3) << 4) ++#define OP_CTRL_DMA_OP(_type) ((_type) << 2) ++#define OP_CTRL_RW_OP(_op) ((_op) << 1) ++#define OP_CTRL_DMA_OP_READY BIT(0) ++ ++#define RD_OP_READ_ALL_PAGE 0x0 ++#define RD_OP_READ_OOB 0x1 ++#define RD_OP_BLOCK_READ 0x2 ++ ++#define RD_OP_SHIFT 4 ++#define RD_OP_MASK (0x3 << RD_OP_SHIFT) ++ ++#define OP_TYPE_DMA 0x0 ++#define OP_TYPE_REG 0x1 ++ ++#define FMC_OP_READ 0x0 ++#define FMC_OP_WRITE 0x1 ++#define RW_OP_READ 0x0 ++#define RW_OP_WRITE 0x1 ++ ++/*****************************************************************************/ ++#define FMC_OP_PARA 0x70 ++#define FMC_OP_PARA_RD_OOB_ONLY BIT(1) ++ ++/*****************************************************************************/ ++#define FMC_BOOT_SET 0x74 ++#define FMC_BOOT_SET_DEVICE_ECC_EN BIT(3) ++#define FMC_BOOT_SET_BOOT_QUAD_EN BIT(1) ++ ++/*****************************************************************************/ ++#define FMC_STATUS 0xac ++ ++/*****************************************************************************/ ++#ifndef FMC_VERSION ++#define FMC_VERSION 0xbc ++#endif ++ ++/* Hifmc IP version */ ++#ifndef HIFMC_VER_100 ++#define HIFMC_VER_100 (0x100) ++#endif ++ ++/*****************************************************************************/ ++/* DMA address align with 32 bytes. */ ++#define FMC_DMA_ALIGN 32 ++ ++#define FMC_CHIP_DELAY 25 ++/*****************************************************************************/ ++#define HIFMC_ECC_ERR_NUM0_BUF0 0xc0 ++#define GET_ECC_ERR_NUM(_i, _reg) (((_reg) >> ((_i) * 8)) & 0xff) ++ ++#define DISABLE 0 ++#define ENABLE 1 ++ ++/*****************************************************************************/ ++#define HIFMC_REG_ADDRESS_LEN 0x200 ++ ++/*****************************************************************************/ ++#define FMC_MAX_READY_WAIT_JIFFIES (HZ) ++ ++#define MAX_SPI_NOR_ID_LEN 8 ++#define MAX_NAND_ID_LEN 8 ++#define MAX_SPI_NAND_ID_LEN 3 ++ ++#define GET_OP 0 ++#define SET_OP 1 ++ ++#define STATUS_ECC_MASK (0x3 << 4) ++#define STATUS_P_FAIL_MASK (1 << 3) ++#define STATUS_E_FAIL_MASK (1 << 2) ++#define STATUS_WEL_MASK (1 << 1) ++#define STATUS_OIP_MASK (1 << 0) ++ ++/*****************************************************************************/ ++#define FMC_VERSION 0xbc ++ ++/* Hifmc IP version */ ++#define HIFMC_VER_100 (0x100) ++ ++/*****************************************************************************/ ++#define GET_PAGE_INDEX(host) \ ++ ((host->addr_value[0] >> 16) | (host->addr_value[1] << 16)) ++/*****************************************************************************/ ++#define HIFMC_MAX_CHIP_NUM 2 ++ ++extern unsigned char hifmc_cs_user[]; ++ ++/*****************************************************************************/ ++#define hifmc_readl(_host, _reg) \ ++ (hi_readl((char *)_host->regbase + (_reg))) ++ ++#define hifmc_readb( _addr) \ ++ (hi_readb((void __iomem *)(_addr))) ++ ++#define hifmc_readw( _addr) \ ++ (hi_readw((void __iomem *)(_addr))) ++ ++#define hifmc_writel(_host, _reg, _value) \ ++ (hi_writel((u_int)(_value), ((char *)_host->regbase + (_reg)))) ++ ++#define hifmc_writeb(_val, _addr) \ ++ (hi_writeb((u_int)(_val), ((char *)_addr))) ++ ++/*****************************************************************************/ ++#define FMC_WAIT_TIMEOUT 0x1000000 ++ ++#define FMC_CMD_WAIT_CPU_FINISH(_host) \ ++ do { \ ++ unsigned regval, timeout = FMC_WAIT_TIMEOUT * 2; \ ++ do { \ ++ regval = hifmc_readl((_host), FMC_OP); \ ++ --timeout; \ ++ } while ((regval & FMC_OP_REG_OP_START) && timeout); \ ++ if (!timeout) \ ++ pr_info("Error: Wait cmd cpu finish timeout!\n"); \ ++ } while (0) ++ ++/*****************************************************************************/ ++#define FMC_DMA_WAIT_INT_FINISH(_host) \ ++ do { \ ++ unsigned regval, timeout = FMC_WAIT_TIMEOUT; \ ++ do { \ ++ regval = hifmc_readl((_host), FMC_INT); \ ++ --timeout; \ ++ } while ((!(regval & FMC_INT_OP_DONE) && timeout)); \ ++ if (!timeout) \ ++ pr_info("Error: Wait dma int finish timeout!\n"); \ ++ } while (0) ++ ++/*****************************************************************************/ ++#define FMC_DMA_WAIT_CPU_FINISH(_host) \ ++ do { \ ++ unsigned regval, timeout = FMC_WAIT_TIMEOUT; \ ++ do { \ ++ regval = hifmc_readl((_host), FMC_OP_CTRL); \ ++ --timeout; \ ++ } while ((regval & OP_CTRL_DMA_OP_READY) && timeout); \ ++ if (!timeout) \ ++ pr_info("Error: Wait dma cpu finish timeout!\n"); \ ++ } while (0) ++ ++/*****************************************************************************/ ++#define BT_DBG 0 /* Boot init debug print */ ++#define ER_DBG 0 /* Erase debug print */ ++#define WR_DBG 0 /* Write debug print */ ++#define RD_DBG 0 /* Read debug print */ ++#define QE_DBG 0 /* Quad Enable debug print */ ++#define OP_DBG 0 /* OP command debug print */ ++#define DMA_DB 0 /* DMA read or write debug print */ ++#define AC_DBG 0 /* 3-4byte Address Cycle */ ++#define SR_DBG 0 /* Status Register debug print */ ++#define CR_DBG 0 /* Config Register debug print */ ++#define FT_DBG 0 /* Features debug print */ ++#define WE_DBG 0 /* Write Enable debug print */ ++#define BP_DBG 0 /* Block Protection debug print */ ++#define EC_DBG 0 /* enable/disable ecc0 and randomizer */ ++#define PM_DBG 0 /* power management debug */ ++ ++#define FMC_PR(_type, _fmt, arg...) \ ++ do { \ ++ if (_type) \ ++ DB_MSG(_fmt, ##arg) \ ++ } while (0) ++ ++#define DB_MSG(_fmt, arg...) \ ++ pr_info("%s(%d): " _fmt, __func__, __LINE__, ##arg); ++ ++#define DB_BUG(fmt, args...) \ ++ do { \ ++ pr_info("%s(%d): BUG: " fmt, __FILE__, __LINE__, ##args); \ ++ while (1) \ ++ ; \ ++ } while (0) ++ ++/*****************************************************************************/ ++enum hifmc_iftype { ++ IF_TYPE_STD, ++ IF_TYPE_DUAL, ++ IF_TYPE_DIO, ++ IF_TYPE_QUAD, ++ IF_TYPE_QIO, ++}; ++struct hisi_fmc { ++ void __iomem *regbase; ++ void __iomem *iobase; ++ struct clk *clk; ++ struct mutex lock; ++}; ++ ++struct hifmc_cmd_op { ++ unsigned char cs; ++ unsigned char cmd; ++ unsigned char l_cmd; ++ unsigned char addr_h; ++ unsigned int addr_l; ++ unsigned int data_no; ++ unsigned short option; ++ unsigned short op_cfg; ++}; ++ ++extern struct mutex fmc_switch_mutex; ++ ++#endif /*__HISI_FMC_H*/ +diff --git a/include/linux/mm.h b/include/linux/mm.h +index 86a977b..0628a5f 100644 +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -1087,6 +1087,7 @@ extern void pagefault_out_of_memory(void); + extern void show_free_areas(unsigned int flags); + extern bool skip_free_areas_node(unsigned int flags, int nid); + ++void shmem_set_file(struct vm_area_struct *vma, struct file *file); + int shmem_zero_setup(struct vm_area_struct *); + #ifdef CONFIG_SHMEM + bool shmem_mapping(struct address_space *mapping); +@@ -1352,6 +1353,11 @@ static inline void update_hiwater_vm(struct mm_struct *mm) + mm->hiwater_vm = mm->total_vm; + } + ++static inline void reset_mm_hiwater_rss(struct mm_struct *mm) ++{ ++ mm->hiwater_rss = get_mm_rss(mm); ++} ++ + static inline void setmax_mm_hiwater_rss(unsigned long *maxrss, + struct mm_struct *mm) + { +@@ -1739,6 +1745,9 @@ extern void zone_pcp_reset(struct zone *zone); + + /* page_alloc.c */ + extern int min_free_kbytes; ++#ifdef CONFIG_CMA_ADVANCE_SHARE ++extern int cma_watermark; ++#endif + + /* nommu.c */ + extern atomic_long_t mmap_pages_allocated; +@@ -1790,7 +1799,7 @@ extern int vma_adjust(struct vm_area_struct *vma, unsigned long start, + extern struct vm_area_struct *vma_merge(struct mm_struct *, + struct vm_area_struct *prev, unsigned long addr, unsigned long end, + unsigned long vm_flags, struct anon_vma *, struct file *, pgoff_t, +- struct mempolicy *); ++ struct mempolicy *, const char __user *); + extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *); + extern int split_vma(struct mm_struct *, + struct vm_area_struct *, unsigned long addr, int new_below); +@@ -2028,6 +2037,7 @@ static inline struct page *follow_page(struct vm_area_struct *vma, + #define FOLL_NUMA 0x200 /* force NUMA hinting page fault */ + #define FOLL_MIGRATION 0x400 /* wait for page to replace migration entry */ + #define FOLL_TRIED 0x800 /* a retry, previous pass started an IO */ ++#define FOLL_COW 0x4000 /* internal GUP flag */ + + typedef int (*pte_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr, + void *data); +diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h +index 6e0b286..6c44170 100644 +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -274,6 +274,10 @@ struct vm_area_struct { + * For areas with an address space and backing store, + * linkage into the address_space->i_mmap interval tree, or + * linkage of vma in the address_space->i_mmap_nonlinear list. ++ * ++ * For private anonymous mappings, a pointer to a null terminated string ++ * in the user process containing the name given to the vma, or NULL ++ * if unnamed. + */ + union { + struct { +@@ -281,6 +285,7 @@ struct vm_area_struct { + unsigned long rb_subtree_last; + } linear; + struct list_head nonlinear; ++ const char __user *anon_name; + } shared; + + /* +@@ -525,4 +530,13 @@ enum tlb_flush_reason { + NR_TLB_FLUSH_REASONS, + }; + ++/* Return the name for an anonymous mapping or NULL for a file-backed mapping */ ++static inline const char __user *vma_get_anon_name(struct vm_area_struct *vma) ++{ ++ if (vma->vm_file) ++ return NULL; ++ ++ return vma->shared.anon_name; ++} ++ + #endif /* _LINUX_MM_TYPES_H */ +diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h +index b0692d2..b21b868 100644 +--- a/include/linux/mmc/card.h ++++ b/include/linux/mmc/card.h +@@ -306,6 +306,7 @@ struct mmc_card { + struct dentry *debugfs_root; + struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ + unsigned int nr_parts; ++ u32 c_ssr[16]; + }; + + /* +diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h +index f206e29..23d66ba 100644 +--- a/include/linux/mmc/core.h ++++ b/include/linux/mmc/core.h +@@ -40,6 +40,9 @@ struct mmc_command { + #define MMC_RSP_SPI_B4 (1 << 9) /* four data bytes */ + #define MMC_RSP_SPI_BUSY (1 << 10) /* card may send busy */ + ++#define MMC_CMD_NON_BLOCKING (1 << 11) /* non-blocking cmd flag */ ++#define MMC_CMD_TYPE_RW (1 << 12) /* general R/W cmd type */ ++ + /* + * These are the native response types, and correspond to valid bit + * patterns of the above flags. One additional valid pattern +@@ -135,6 +138,7 @@ struct mmc_request { + struct completion completion; + void (*done)(struct mmc_request *);/* completion function */ + struct mmc_host *host; ++ unsigned int wr_pos; + }; + + struct mmc_card; +@@ -154,7 +158,10 @@ extern void mmc_start_bkops(struct mmc_card *card, bool from_exception); + extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool, + bool, bool); + extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); ++extern int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error); + extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); ++extern int mmc_send_dll_tuning(struct mmc_host *host); ++extern int __mmc_start_data_req(struct mmc_host *, struct mmc_request *); + + #define MMC_ERASE_ARG 0x00000000 + #define MMC_SECURE_ERASE_ARG 0x80000000 +@@ -184,6 +191,8 @@ extern int mmc_hw_reset(struct mmc_host *host); + extern int mmc_hw_reset_check(struct mmc_host *host); + extern int mmc_can_reset(struct mmc_card *card); + ++extern int mmc_sw_reset(struct mmc_host *host); ++ + extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *); + extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int); + +diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h +index df0c153..46d5465 100644 +--- a/include/linux/mmc/host.h ++++ b/include/linux/mmc/host.h +@@ -119,6 +119,7 @@ struct mmc_host_ops { + * or a negative errno value when something bad happened + */ + void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); ++ unsigned int (*get_rd)(struct mmc_host *host); + int (*get_ro)(struct mmc_host *host); + int (*get_cd)(struct mmc_host *host); + +@@ -139,6 +140,7 @@ struct mmc_host_ops { + int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios); + int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv); + void (*hw_reset)(struct mmc_host *host); ++ void (*sw_reset)(struct mmc_host *host); + void (*card_event)(struct mmc_host *host); + + /* +@@ -147,6 +149,7 @@ struct mmc_host_ops { + */ + int (*multi_io_quirk)(struct mmc_card *card, + unsigned int direction, int blk_size); ++ int (*card_info_save)(struct mmc_host *host); + }; + + struct mmc_card; +@@ -375,6 +378,17 @@ struct mmc_host { + int dsr_req; /* DSR value is valid */ + u32 dsr; /* optional driver stage (DSR) value */ + ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++ struct { ++ struct sdio_cis *cis; ++ struct sdio_cccr *cccr; ++ struct sdio_embedded_func *funcs; ++ int num_funcs; ++ } embedded_sdio_data; ++#endif ++#define MMC_HOST_OK (0<<0) ++#define MMC_HOST_ERR (1<<0) ++ u32 status; + unsigned long private[0] ____cacheline_aligned; + }; + +@@ -384,6 +398,14 @@ void mmc_remove_host(struct mmc_host *); + void mmc_free_host(struct mmc_host *); + int mmc_of_parse(struct mmc_host *host); + ++#ifdef CONFIG_MMC_EMBEDDED_SDIO ++extern void mmc_set_embedded_sdio_data(struct mmc_host *host, ++ struct sdio_cis *cis, ++ struct sdio_cccr *cccr, ++ struct sdio_embedded_func *funcs, ++ int num_funcs); ++#endif ++ + static inline void *mmc_priv(struct mmc_host *host) + { + return (void *)host->private; +diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h +index 1cd00b3..469577a 100644 +--- a/include/linux/mmc/mmc.h ++++ b/include/linux/mmc/mmc.h +@@ -24,6 +24,16 @@ + #ifndef LINUX_MMC_MMC_H + #define LINUX_MMC_MMC_H + ++#define MMC_DEBUG_LEVEL 5 ++ ++/* mmc block debug info */ ++#define mmc_trace(level, msg...) do { \ ++ if (level >= MMC_DEBUG_LEVEL) { \ ++ printk(msg); \ ++ printk("\n"); \ ++ } \ ++} while (0) ++ + /* Standard MMC commands (4.1) type argument response */ + /* class 1 */ + #define MMC_GO_IDLE_STATE 0 /* bc */ +@@ -53,11 +63,6 @@ + #define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */ + #define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */ + +-#define MMC_TUNING_BLK_PATTERN_4BIT_SIZE 64 +-#define MMC_TUNING_BLK_PATTERN_8BIT_SIZE 128 +-extern const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE]; +-extern const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE]; +- + /* class 3 */ + #define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ + +diff --git a/include/linux/mmc/pm.h b/include/linux/mmc/pm.h +index 4a13920..6e2d6a1 100644 +--- a/include/linux/mmc/pm.h ++++ b/include/linux/mmc/pm.h +@@ -26,5 +26,6 @@ typedef unsigned int mmc_pm_flag_t; + + #define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */ + #define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */ ++#define MMC_PM_IGNORE_PM_NOTIFY (1 << 2) /* ignore mmc pm notify */ + + #endif /* LINUX_MMC_PM_H */ +diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h +old mode 100644 +new mode 100755 +index 50f0bc9..dc680c4 +--- a/include/linux/mmc/sdio_func.h ++++ b/include/linux/mmc/sdio_func.h +@@ -23,6 +23,14 @@ struct sdio_func; + typedef void (sdio_irq_handler_t)(struct sdio_func *); + + /* ++ * Structure used to hold embedded SDIO device data from platform layer ++ */ ++struct sdio_embedded_func { ++ uint8_t f_class; ++ uint32_t f_maxblksize; ++}; ++ ++/* + * SDIO function CIS tuple (unknown to the core) + */ + struct sdio_func_tuple { +@@ -130,6 +138,8 @@ extern int sdio_release_irq(struct sdio_func *func); + extern unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz); + + extern u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret); ++extern u8 sdio_readb_ext(struct sdio_func *func, unsigned int addr, int *err_ret, ++ unsigned in); + extern u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret); + extern u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret); + +diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h +index ffe66e3..74d793f 100644 +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -374,6 +374,12 @@ struct zone { + unsigned long min_unmapped_pages; + unsigned long min_slab_pages; + #endif /* CONFIG_NUMA */ ++#ifdef CONFIG_CMA_ADVANCE_SHARE ++ int has_cma; ++ int nr_try_cma; ++ int nr_try_movable; ++ int cma_wmark; ++#endif + + /* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */ + unsigned long zone_start_pfn; +@@ -886,6 +892,10 @@ static inline int is_highmem(struct zone *zone) + struct ctl_table; + int min_free_kbytes_sysctl_handler(struct ctl_table *, int, + void __user *, size_t *, loff_t *); ++#ifdef CONFIG_CMA_ADVANCE_SHARE ++int cma_watermark_sysctl_handler(struct ctl_table *table, int write, ++ void __user *buffer, size_t *length, loff_t *ppos); ++#endif + extern int sysctl_lowmem_reserve_ratio[MAX_NR_ZONES-1]; + int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *, int, + void __user *, size_t *, loff_t *); +diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h +index 031ff3a..db0430d 100644 +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -382,7 +382,6 @@ extern void __put_mtd_device(struct mtd_info *mtd); + extern struct mtd_info *get_mtd_device_nm(const char *name); + extern void put_mtd_device(struct mtd_info *mtd); + +- + struct mtd_notifier { + void (*add)(struct mtd_info *mtd); + void (*remove)(struct mtd_info *mtd); +diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h +index e4d451e..08be617 100644 +--- a/include/linux/mtd/nand.h ++++ b/include/linux/mtd/nand.h +@@ -78,6 +78,7 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); + #define NAND_CMD_READOOB 0x50 + #define NAND_CMD_ERASE1 0x60 + #define NAND_CMD_STATUS 0x70 ++#define NAND_CMD_STATUS_MULTI 0x71 + #define NAND_CMD_SEQIN 0x80 + #define NAND_CMD_RNDIN 0x85 + #define NAND_CMD_READID 0x90 +@@ -85,6 +86,7 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); + #define NAND_CMD_PARAM 0xec + #define NAND_CMD_GET_FEATURES 0xee + #define NAND_CMD_SET_FEATURES 0xef ++#define NAND_CMD_SYNC_RESET 0xfc + #define NAND_CMD_RESET 0xff + + #define NAND_CMD_LOCK 0x2a +@@ -135,6 +137,8 @@ typedef enum { + * Option constants for bizarre disfunctionality and real + * features. + */ ++/* Chip can not auto increment pages */ ++#define NAND_NO_AUTOINCR 0x00000001 + /* Buswidth is 16 bit */ + #define NAND_BUSWIDTH_16 0x00000002 + /* Chip has cache program function */ +@@ -715,14 +719,20 @@ struct nand_chip { + #define NAND_MFR_FUJITSU 0x04 + #define NAND_MFR_NATIONAL 0x8f + #define NAND_MFR_RENESAS 0x07 +-#define NAND_MFR_STMICRO 0x20 ++#define NAND_MFR_ST_MICRO 0x20 + #define NAND_MFR_HYNIX 0xad + #define NAND_MFR_MICRON 0x2c + #define NAND_MFR_AMD 0x01 +-#define NAND_MFR_MACRONIX 0xc2 ++#define NAND_MFR_GD_ESMT 0xc8 + #define NAND_MFR_EON 0x92 ++#define NAND_MFR_WINBOND 0xef ++#define NAND_MFR_ATO 0x9b ++#define NAND_MFR_MXIC 0xc2 ++#define NAND_MFR_ALL_FLASH 0xc1 ++#define NAND_MFR_PARAGON 0xa1 + #define NAND_MFR_SANDISK 0x45 + #define NAND_MFR_INTEL 0x89 ++#define NAND_MFR_HEYANGTEK 0xc9 + + /* The maximum expected count of bytes in the NAND ID sequence */ + #define NAND_MAX_ID_LEN 8 +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index 046a0a2..e2d0a59 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -10,6 +10,61 @@ + #ifndef __LINUX_MTD_SPI_NOR_H + #define __LINUX_MTD_SPI_NOR_H + ++#include <linux/bitops.h> ++#include <linux/mtd/cfi.h> ++ ++/* ++ * Manufacturer IDs ++ * ++ * The first byte returned from the flash after sending opcode SPINOR_OP_RDID. ++ * Sometimes these are the same as CFI IDs, but sometimes they aren't. ++ */ ++#define SNOR_MFR_ATMEL CFI_MFR_ATMEL ++#define SNOR_MFR_INTEL CFI_MFR_INTEL ++#define SNOR_MFR_MICRON CFI_MFR_ST /* ST Micro <--> Micron */ ++#define SNOR_MFR_MACRONIX CFI_MFR_MACRONIX ++#define SNOR_MFR_SPANSION CFI_MFR_AMD ++#define SNOR_MFR_SST CFI_MFR_SST ++#define SNOR_MFR_EON CFI_MFR_EON ++#define SNOR_MFR_WINBOND 0xef ++#define SNOR_MFR_ESMT 0x8c ++#define SNOR_MFR_GD 0xc8 ++ ++/* Flash set the RESET# from */ ++#define SPI_NOR_SR_RST_MASK BIT(7) ++#define SPI_NOR_GET_RST(val) (((val) & SPI_NOR_SR_RST_MASK) >> 7) ++#define SPI_NOR_SET_RST(val) ((val) | SPI_NOR_SR_RST_MASK) ++ ++/* Flash block protect */ ++#ifdef CONFIG_HISI_SPI_BLOCK_PROTECT ++#define _2M (0x200000UL) ++#define _4M (0x400000UL) ++#define _8M (0x800000UL) ++#define _16M (0x1000000UL) ++#define _32M (0x2000000UL) ++ ++#define BP_NUM_3 3 ++#define BP_NUM_4 4 ++ ++#define DEBUG_SPI_NOR_BP 0 ++ ++#define SPI_NOR_SR_SRWD_SHIFT 7 ++#define SPI_NOR_SR_SRWD_MASK (1 << SPI_NOR_SR_SRWD_SHIFT) ++ ++#define SPI_NOR_SR_BP0_SHIFT 2 ++#define SPI_NOR_SR_BP_WIDTH_4 0xf ++#define SPI_NOR_SR_BP_MASK_4 (SPI_NOR_SR_BP_WIDTH_4 << SPI_NOR_SR_BP0_SHIFT) ++ ++#define SPI_NOR_SR_BP_WIDTH_3 0x7 ++#define SPI_NOR_SR_BP_MASK_3 (SPI_NOR_SR_BP_WIDTH_3 << SPI_NOR_SR_BP0_SHIFT) ++ ++#define SPI_NOR_SR_TB_SHIFT 3 ++#define SPI_NOR_SR_TB_MASK (1 << SPI_NOR_SR_TB_SHIFT) ++ ++#define LOCK_LEVEL_MAX(bp_num) (((0x01) << bp_num) - 1) ++ ++#endif /* CONFIG_SPI_BLOCK_PROTECT */ ++ + /* + * Note on opcode nomenclature: some opcodes have a format like + * SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number +@@ -21,27 +76,42 @@ + /* Flash opcodes. */ + #define SPINOR_OP_WREN 0x06 /* Write enable */ + #define SPINOR_OP_RDSR 0x05 /* Read status register */ ++#define SPINOR_OP_RDSR2 0x35 /* Read Status Register-2 */ ++#define SPINOR_OP_RDSR3 0x15 /* Read Status Register-3 */ + #define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */ ++#define SPINOR_OP_WRSR2 0x31 /* Write Status Register-2 1 byte*/ ++#define SPINOR_OP_WRSR3 0x11 /* Write Status Register-3 1 byte*/ + #define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */ + #define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ +-#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */ +-#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */ ++#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */ ++#define SPINOR_OP_READ_1_2_2 0xbb /* Read data bytes (Dual I/O SPI) */ ++#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad Output SPI) */ ++#define SPINOR_OP_READ_1_4_4 0xeb /* Read data bytes (Quad I/O SPI) */ + #define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ ++#define SPINOR_OP_PP_1_1_4 0x32 /* Quad page program */ ++#define SPINOR_OP_PP_1_4_4 0x38 /* Quad page program */ + #define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */ + #define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ + #define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */ + #define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */ + #define SPINOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */ + #define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */ ++#define SPINOR_OP_RDSFDP 0x5a /* Read SFDP */ + #define SPINOR_OP_RDCR 0x35 /* Read configuration register */ + #define SPINOR_OP_RDFSR 0x70 /* Read flag status register */ + + /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ +-#define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */ +-#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */ +-#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */ +-#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */ ++#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */ ++#define SPINOR_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */ ++#define SPINOR_OP_READ_1_1_2_4B 0x3c /* Read data bytes (Dual Output SPI) */ ++#define SPINOR_OP_READ_1_2_2_4B 0xbc /* Read data bytes (Dual I/O SPI) */ ++#define SPINOR_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */ ++#define SPINOR_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */ + #define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */ ++#define SPINOR_OP_PP_1_1_4_4B 0x34 /* Quad page program */ ++#define SPINOR_OP_PP_1_4_4_4B 0x3e /* Quad page program */ ++#define SPINOR_OP_BE_4K_4B 0x21 /* Erase 4KiB block */ ++#define SPINOR_OP_BE_32K_4B 0x5c /* Erase 32KiB block */ + #define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ + + /* Used for SST flashes only. */ +@@ -54,57 +124,141 @@ + #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ + + /* Used for Spansion flashes only. */ ++#define SPINOR_OP_BRRD 0x16 /* Bank register write */ + #define SPINOR_OP_BRWR 0x17 /* Bank register write */ + ++/* Used for GigaDevice flashes only. */ ++#define SPINOR_OP_WRCR 0x31 /* Config register write */ ++ ++/* Used for Micron flashes only. */ ++#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ ++#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */ ++ ++/* Software reset code */ ++#define SPINOR_ENABLE_RESET 0x66 /* Enable reset */ ++#define SPINOR_OP_RESET 0x99 /* Reset */ ++ + /* Status Register bits. */ +-#define SR_WIP 1 /* Write in progress */ +-#define SR_WEL 2 /* Write enable latch */ ++#define SR_WIP BIT(0) /* Write in progress */ ++#define SR_WEL BIT(1) /* Write enable latch */ + /* meaning of other SR_* bits may differ between vendors */ +-#define SR_BP0 4 /* Block protect 0 */ +-#define SR_BP1 8 /* Block protect 1 */ +-#define SR_BP2 0x10 /* Block protect 2 */ +-#define SR_SRWD 0x80 /* SR write protect */ ++#define SR_BP0 BIT(2) /* Block protect 0 */ ++#define SR_BP1 BIT(3) /* Block protect 1 */ ++#define SR_BP2 BIT(4) /* Block protect 2 */ ++#define SR_TB BIT(5) /* Top/Bottom protect */ ++#define SR_SRWD BIT(7) /* SR write protect */ ++ ++#define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */ + +-#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ ++/* Enhanced Volatile Configuration Register bits */ ++#define EVCR_DUAL_EN_MICRON BIT(6) /* Micron Dual I/O */ ++#define EVCR_QUAD_EN_MICRON BIT(7) /* Micron Quad I/O */ + + /* Flag Status Register bits */ +-#define FSR_READY 0x80 ++#define FSR_READY BIT(7) + + /* Configuration Register bits. */ +-#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ ++#define CR_QUAD_EN_SPAN BIT(1) /* Spansion Quad I/O */ + +-enum read_mode { +- SPI_NOR_NORMAL = 0, +- SPI_NOR_FAST, +- SPI_NOR_DUAL, +- SPI_NOR_QUAD, ++/* Supported modes */ ++enum spi_nor_mode_index { ++ /* Sorted by ascending priority order */ ++ SNOR_MIDX_SLOW = 0, ++ SNOR_MIDX_1_1_1, ++ SNOR_MIDX_1_1_2, ++ SNOR_MIDX_1_2_2, ++ SNOR_MIDX_1_1_4, ++ SNOR_MIDX_1_4_4, ++ ++ SNOR_MIDX_MAX + }; + +-/** +- * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer +- * @wren: command for "Write Enable", or 0x00 for not required +- * @cmd: command for operation +- * @cmd_pins: number of pins to send @cmd (1, 2, 4) +- * @addr: address for operation +- * @addr_pins: number of pins to send @addr (1, 2, 4) +- * @addr_width: number of address bytes +- * (3,4, or 0 for address not required) +- * @mode: mode data +- * @mode_pins: number of pins to send @mode (1, 2, 4) +- * @mode_cycles: number of mode cycles (0 for mode not required) +- * @dummy_cycles: number of dummy cycles (0 for dummy not required) +- */ +-struct spi_nor_xfer_cfg { +- u8 wren; +- u8 cmd; +- u8 cmd_pins; +- u32 addr; +- u8 addr_pins; +- u8 addr_width; +- u8 mode; +- u8 mode_pins; +- u8 mode_cycles; +- u8 dummy_cycles; ++#define SNOR_MODE_SLOW BIT(SNOR_MIDX_SLOW) ++#define SNOR_MODE_1_1_1 BIT(SNOR_MIDX_1_1_1) ++#define SNOR_MODE_1_1_2 BIT(SNOR_MIDX_1_1_2) ++#define SNOR_MODE_1_2_2 BIT(SNOR_MIDX_1_2_2) ++#define SNOR_MODE_1_1_4 BIT(SNOR_MIDX_1_1_4) ++#define SNOR_MODE_1_4_4 BIT(SNOR_MIDX_1_4_4) ++ ++struct spi_nor_modes { ++ u32 rd_modes; /* supported SPI modes for (Fast) Read */ ++ u32 wr_modes; /* supported SPI modes for Page Program */ ++}; ++ ++struct spi_nor_read_op { ++ u8 num_mode_clocks; ++ u8 num_wait_states; ++ u8 opcode; ++}; ++ ++#define SNOR_OP_READ(_num_mode_clocks, _num_wait_states, _opcode) \ ++ { \ ++ .num_mode_clocks = _num_mode_clocks, \ ++ .num_wait_states = _num_wait_states, \ ++ .opcode = _opcode, \ ++ } ++ ++struct spi_nor_erase_type { ++ u8 size; /* specifies 'N' so erase size = 2^N */ ++ u8 opcode; ++}; ++ ++#define SNOR_OP_ERASE(_size, _opcode) { .size = _size, .opcode = _opcode } ++#define SNOR_OP_ERASE_64K(_opcode) SNOR_OP_ERASE(0x10, _opcode) ++#define SNOR_OP_ERASE_32K(_opcode) SNOR_OP_ERASE(0x0f, _opcode) ++#define SNOR_OP_ERASE_4K(_opcode) SNOR_OP_ERASE(0x0c, _opcode) ++ ++struct spi_nor; ++ ++#define SNOR_MAX_ERASE_TYPES 4 ++ ++struct spi_nor_basic_flash_parameter { ++ /* Fast Read settings */ ++ u32 rd_modes; ++ struct spi_nor_read_op reads[SNOR_MIDX_MAX]; ++ ++ /* Page Program settings */ ++ u32 wr_modes; ++ u8 page_programs[SNOR_MIDX_MAX]; ++ ++ /* Sector Erase settings */ ++ struct spi_nor_erase_type erase_types[SNOR_MAX_ERASE_TYPES]; ++ ++ int (*enable_quad_io)(struct spi_nor *nor); ++}; ++ ++#define SNOR_PROTO_CODE_OFF 8 ++#define SNOR_PROTO_CODE_MASK GENMASK(11, 8) ++#define SNOR_PROTO_CODE_TO_PROTO(code) \ ++ (((code) << SNOR_PROTO_CODE_OFF) & SNOR_PROTO_CODE_MASK) ++#define SNOR_PROTO_CODE_FROM_PROTO(proto) \ ++ ((((u32)(proto)) & SNOR_PROTO_CODE_MASK) >> SNOR_PROTO_CODE_OFF) ++ ++#define SNOR_PROTO_ADDR_OFF 4 ++#define SNOR_PROTO_ADDR_MASK GENMASK(7, 4) ++#define SNOR_PROTO_ADDR_TO_PROTO(addr) \ ++ (((addr) << SNOR_PROTO_ADDR_OFF) & SNOR_PROTO_ADDR_MASK) ++#define SNOR_PROTO_ADDR_FROM_PROTO(proto) \ ++ ((((u32)(proto)) & SNOR_PROTO_ADDR_MASK) >> SNOR_PROTO_ADDR_OFF) ++ ++#define SNOR_PROTO_DATA_OFF 0 ++#define SNOR_PROTO_DATA_MASK GENMASK(3, 0) ++#define SNOR_PROTO_DATA_TO_PROTO(data) \ ++ (((data) << SNOR_PROTO_DATA_OFF) & SNOR_PROTO_DATA_MASK) ++#define SNOR_PROTO_DATA_FROM_PROTO(proto) \ ++ ((((u32)(proto)) & SNOR_PROTO_DATA_MASK) >> SNOR_PROTO_DATA_OFF) ++ ++#define SNOR_PROTO(code, addr, data) \ ++ (SNOR_PROTO_CODE_TO_PROTO(code) | \ ++ SNOR_PROTO_ADDR_TO_PROTO(addr) | \ ++ SNOR_PROTO_DATA_TO_PROTO(data)) ++ ++enum spi_nor_protocol { ++ SNOR_PROTO_1_1_1 = SNOR_PROTO(1, 1, 1), /* SPI */ ++ SNOR_PROTO_1_1_2 = SNOR_PROTO(1, 1, 2), /* Dual Output */ ++ SNOR_PROTO_1_2_2 = SNOR_PROTO(1, 2, 2), /* Dual IO */ ++ SNOR_PROTO_1_1_4 = SNOR_PROTO(1, 1, 4), /* Quad Output */ ++ SNOR_PROTO_1_4_4 = SNOR_PROTO(1, 4, 4), /* Quad IO */ + }; + + #define SPI_NOR_MAX_CMD_SIZE 8 +@@ -116,64 +270,71 @@ enum spi_nor_ops { + SPI_NOR_OPS_UNLOCK, + }; + ++enum spi_nor_option_flags { ++ SNOR_F_USE_FSR = BIT(0), ++ SNOR_F_HAS_SR_TB = BIT(1), ++}; ++ ++struct mtd_info; ++ + /** + * struct spi_nor - Structure for defining a the SPI NOR layer + * @mtd: point to a mtd_info structure + * @lock: the lock for the read/write/erase/lock/unlock operations + * @dev: point to a spi device, or a spi nor controller device. ++ * @flash_node: point to a device node describing this flash instance. + * @page_size: the page size of the SPI NOR + * @addr_width: number of address bytes + * @erase_opcode: the opcode for erasing a sector + * @read_opcode: the read opcode + * @read_dummy: the dummy needed by the read operation + * @program_opcode: the program opcode +- * @flash_read: the mode of the read + * @sst_write_second: used by the SST write operation +- * @cfg: used by the read_xfer/write_xfer ++ * @flags: flag options for the current SPI-NOR (SNOR_F_*) ++ * @erase_proto: the SPI protocol used by erase operations ++ * @read_proto: the SPI protocol used by read operations ++ * @write_proto: the SPI protocol used by write operations ++ * @reg_proto the SPI protocol used by read_reg/write_reg operations + * @cmd_buf: used by the write_reg + * @prepare: [OPTIONAL] do some preparations for the + * read/write/erase/lock/unlock operations + * @unprepare: [OPTIONAL] do some post work after the + * read/write/erase/lock/unlock operations +- * @read_xfer: [OPTIONAL] the read fundamental primitive +- * @write_xfer: [OPTIONAL] the writefundamental primitive + * @read_reg: [DRIVER-SPECIFIC] read out the register + * @write_reg: [DRIVER-SPECIFIC] write data to the register +- * @read_id: [REPLACEABLE] read out the ID data, and find +- * the proper spi_device_id +- * @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready + * @read: [DRIVER-SPECIFIC] read data from the SPI NOR + * @write: [DRIVER-SPECIFIC] write data to the SPI NOR + * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR +- * at the offset @offs ++ * at the offset @offs; if not provided by the driver, ++ * spi-nor will send the erase opcode via write_reg() ++ * @flash_lock: [FLASH-SPECIFIC] lock a region of the SPI NOR ++ * @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR ++ * @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is ++ * completely locked + * @priv: the private data + */ + struct spi_nor { +- struct mtd_info *mtd; ++ struct mtd_info mtd; + struct mutex lock; + struct device *dev; ++ struct device_node *flash_node; + u32 page_size; + u8 addr_width; + u8 erase_opcode; + u8 read_opcode; + u8 read_dummy; + u8 program_opcode; +- enum read_mode flash_read; ++ enum spi_nor_protocol erase_proto; ++ enum spi_nor_protocol read_proto; ++ enum spi_nor_protocol write_proto; + bool sst_write_second; +- struct spi_nor_xfer_cfg cfg; ++ u32 flags; + u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; + + int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); + void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); +- int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, +- u8 *buf, size_t len); +- int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, +- u8 *buf, size_t len); + int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); +- int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len, +- int write_enable); +- const struct spi_device_id *(*read_id)(struct spi_nor *nor); +- int (*wait_till_ready)(struct spi_nor *nor); ++ int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); + + int (*read)(struct spi_nor *nor, loff_t from, + size_t len, size_t *retlen, u_char *read_buf); +@@ -181,6 +342,16 @@ struct spi_nor { + size_t len, size_t *retlen, const u_char *write_buf); + int (*erase)(struct spi_nor *nor, loff_t offs); + ++ int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len); ++ int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); ++ int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); ++ ++#ifdef CONFIG_HISI_SPI_BLOCK_PROTECT ++ unsigned int end_addr; ++ unsigned int lock_level_max; ++ unsigned char level; ++#endif ++ u32 clkrate; + void *priv; + }; + +@@ -188,7 +359,7 @@ struct spi_nor { + * spi_nor_scan() - scan the SPI NOR + * @nor: the spi_nor structure + * @name: the chip type name +- * @mode: the read mode supported by the driver ++ * @modes: the SPI modes supported by the controller driver + * + * The drivers can use this fuction to scan the SPI NOR. + * In the scanning, it will try to get all the necessary information to +@@ -198,6 +369,12 @@ struct spi_nor { + * + * Return: 0 for success, others for failure. + */ +-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode); ++int spi_nor_scan(struct spi_nor *nor, const char *name, ++ struct spi_nor_modes *modes); ++void spi_nor_driver_shutdown(struct spi_nor *nor); ++#ifdef CONFIG_PM ++int spi_nor_suspend(struct spi_nor *nor, pm_message_t state); ++int spi_nor_resume(struct spi_nor *nor); ++#endif + + #endif +diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h +index c3fd34d..4d10fd1 100644 +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -1894,8 +1894,8 @@ struct napi_gro_cb { + /* Used in ipv6_gro_receive() and foo-over-udp */ + u16 proto; + +- /* Used in udp_gro_receive */ +- u8 udp_mark:1; ++ /* Used in tunnel GRO receive */ ++ u8 encap_mark:1; + + /* GRO checksum is valid */ + u8 csum_valid:1; +diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h +new file mode 100644 +index 0000000..ca60fbd +--- /dev/null ++++ b/include/linux/netfilter/xt_qtaguid.h +@@ -0,0 +1,13 @@ ++#ifndef _XT_QTAGUID_MATCH_H ++#define _XT_QTAGUID_MATCH_H ++ ++/* For now we just replace the xt_owner. ++ * FIXME: make iptables aware of qtaguid. */ ++#include <linux/netfilter/xt_owner.h> ++ ++#define XT_QTAGUID_UID XT_OWNER_UID ++#define XT_QTAGUID_GID XT_OWNER_GID ++#define XT_QTAGUID_SOCKET XT_OWNER_SOCKET ++#define xt_qtaguid_match_info xt_owner_match_info ++ ++#endif /* _XT_QTAGUID_MATCH_H */ +diff --git a/include/linux/netfilter/xt_quota2.h b/include/linux/netfilter/xt_quota2.h +new file mode 100644 +index 0000000..eadc690 +--- /dev/null ++++ b/include/linux/netfilter/xt_quota2.h +@@ -0,0 +1,25 @@ ++#ifndef _XT_QUOTA_H ++#define _XT_QUOTA_H ++ ++enum xt_quota_flags { ++ XT_QUOTA_INVERT = 1 << 0, ++ XT_QUOTA_GROW = 1 << 1, ++ XT_QUOTA_PACKET = 1 << 2, ++ XT_QUOTA_NO_CHANGE = 1 << 3, ++ XT_QUOTA_MASK = 0x0F, ++}; ++ ++struct xt_quota_counter; ++ ++struct xt_quota_mtinfo2 { ++ char name[15]; ++ u_int8_t flags; ++ ++ /* Comparison-invariant */ ++ aligned_u64 quota; ++ ++ /* Used internally by the kernel */ ++ struct xt_quota_counter *master __attribute__((aligned(8))); ++}; ++ ++#endif /* _XT_QUOTA_H */ +diff --git a/include/linux/nmi.h b/include/linux/nmi.h +index 9b2022a..eeb54ae 100644 +--- a/include/linux/nmi.h ++++ b/include/linux/nmi.h +@@ -14,8 +14,11 @@ + * may be used to reset the timeout - for code which intentionally + * disables interrupts for a long time. This call is stateless. + */ +-#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR) ++#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR_NMI) + #include <asm/nmi.h> ++#endif ++ ++#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR) + extern void touch_nmi_watchdog(void); + #else + static inline void touch_nmi_watchdog(void) +@@ -45,13 +48,23 @@ static inline bool watchdog_hardlockup_detector_is_enabled(void) + #ifdef arch_trigger_all_cpu_backtrace + static inline bool trigger_all_cpu_backtrace(void) + { ++ #if defined(CONFIG_ARM) ++ arch_trigger_all_cpu_backtrace(); ++ #else + arch_trigger_all_cpu_backtrace(true); ++ #endif + + return true; + } + static inline bool trigger_allbutself_cpu_backtrace(void) + { ++ #if defined(CONFIG_ARM) ++ arch_trigger_all_cpu_backtrace(); ++ #else + arch_trigger_all_cpu_backtrace(false); ++ #endif ++ ++ + return true; + } + #else +diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h +index 0ff360d..35feeca 100644 +--- a/include/linux/of_fdt.h ++++ b/include/linux/of_fdt.h +@@ -57,6 +57,27 @@ extern int of_flat_dt_match(unsigned long node, const char *const *matches); + extern unsigned long of_get_flat_dt_root(void); + extern int of_get_flat_dt_size(void); + ++/* ++ * early_init_dt_scan_chosen - scan the device tree for ramdisk and bootargs ++ * ++ * The boot arguments will be placed into the memory pointed to by @data. ++ * That memory should be COMMAND_LINE_SIZE big and initialized to be a valid ++ * (possibly empty) string. Logic for what will be in @data after this ++ * function finishes: ++ * ++ * - CONFIG_CMDLINE_FORCE=true ++ * CONFIG_CMDLINE ++ * - CONFIG_CMDLINE_EXTEND=true, @data is non-empty string ++ * @data + dt bootargs (even if dt bootargs are empty) ++ * - CONFIG_CMDLINE_EXTEND=true, @data is empty string ++ * CONFIG_CMDLINE + dt bootargs (even if dt bootargs are empty) ++ * - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=non-empty: ++ * dt bootargs ++ * - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=empty, @data is non-empty string ++ * @data is left unchanged ++ * - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=empty, @data is empty string ++ * CONFIG_CMDLINE (or "" if that's not defined) ++ */ + extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, + int depth, void *data); + extern int early_init_dt_scan_memory(unsigned long node, const char *uname, +diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h +index d449018..3c4d653 100644 +--- a/include/linux/of_mdio.h ++++ b/include/linux/of_mdio.h +@@ -19,6 +19,9 @@ extern struct phy_device *of_phy_connect(struct net_device *dev, + struct device_node *phy_np, + void (*hndlr)(struct net_device *), + u32 flags, phy_interface_t iface); ++extern struct phy_device * ++of_phy_get_and_connect(struct net_device *dev, struct device_node *np, ++ void (*hndlr)(struct net_device *)); + struct phy_device *of_phy_attach(struct net_device *dev, + struct device_node *phy_np, u32 flags, + phy_interface_t iface); +@@ -49,6 +52,13 @@ static inline struct phy_device *of_phy_connect(struct net_device *dev, + return NULL; + } + ++static inline struct phy_device * ++of_phy_get_and_connect(struct net_device *dev, struct device_node *np, ++ void (*hndlr)(struct net_device *)) ++{ ++ return NULL; ++} ++ + static inline struct phy_device *of_phy_attach(struct net_device *dev, + struct device_node *phy_np, + u32 flags, phy_interface_t iface) +diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h +index 893a0d0..c36f339 100644 +--- a/include/linux/perf_event.h ++++ b/include/linux/perf_event.h +@@ -455,11 +455,6 @@ struct perf_event { + #endif /* CONFIG_PERF_EVENTS */ + }; + +-enum perf_event_context_type { +- task_context, +- cpu_context, +-}; +- + /** + * struct perf_event_context - event context structure + * +@@ -467,7 +462,6 @@ enum perf_event_context_type { + */ + struct perf_event_context { + struct pmu *pmu; +- enum perf_event_context_type type; + /* + * Protect the states of the events in the list, + * nr_active, and the list: +diff --git a/include/linux/phy.h b/include/linux/phy.h +index d090cfc..bd33e4b 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -760,6 +760,10 @@ int phy_register_fixup_for_id(const char *bus_id, + int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, + int (*run)(struct phy_device *)); + ++int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask); ++int phy_unregister_fixup_for_id(const char *bus_id); ++int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask); ++ + int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable); + int phy_get_eee_err(struct phy_device *phydev); + int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data); +diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h +index 8cb6f81..5bf5d4c 100644 +--- a/include/linux/phy/phy.h ++++ b/include/linux/phy/phy.h +@@ -153,6 +153,8 @@ struct phy *devm_phy_get(struct device *dev, const char *string); + struct phy *devm_phy_optional_get(struct device *dev, const char *string); + struct phy *devm_of_phy_get(struct device *dev, struct device_node *np, + const char *con_id); ++struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np, ++ int index); + void phy_put(struct phy *phy); + void devm_phy_put(struct device *dev, struct phy *phy); + struct phy *of_phy_get(struct device_node *np, const char *con_id); +@@ -280,6 +282,13 @@ static inline struct phy *devm_of_phy_get(struct device *dev, + return ERR_PTR(-ENOSYS); + } + ++static inline struct phy *devm_of_phy_get_by_index(struct device *dev, ++ struct device_node *np, ++ int index) ++{ ++ return ERR_PTR(-ENOSYS); ++} ++ + static inline void phy_put(struct phy *phy) + { + } +diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h +index eb8b8ac..24f5470 100644 +--- a/include/linux/pipe_fs_i.h ++++ b/include/linux/pipe_fs_i.h +@@ -42,6 +42,7 @@ struct pipe_buffer { + * @fasync_readers: reader side fasync + * @fasync_writers: writer side fasync + * @bufs: the circular array of pipe buffers ++ * @user: the user who created this pipe + **/ + struct pipe_inode_info { + struct mutex mutex; +@@ -57,6 +58,7 @@ struct pipe_inode_info { + struct fasync_struct *fasync_readers; + struct fasync_struct *fasync_writers; + struct pipe_buffer *bufs; ++ struct user_struct *user; + }; + + /* +@@ -123,6 +125,8 @@ void pipe_unlock(struct pipe_inode_info *); + void pipe_double_lock(struct pipe_inode_info *, struct pipe_inode_info *); + + extern unsigned int pipe_max_size, pipe_min_size; ++extern unsigned long pipe_user_pages_hard; ++extern unsigned long pipe_user_pages_soft; + int pipe_proc_fn(struct ctl_table *, int, void __user *, size_t *, loff_t *); + + +diff --git a/include/linux/platform_data/ds2482.h b/include/linux/platform_data/ds2482.h +new file mode 100644 +index 0000000..5a6879e +--- /dev/null ++++ b/include/linux/platform_data/ds2482.h +@@ -0,0 +1,21 @@ ++/* ++ * Copyright (C) 2012 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __PLATFORM_DATA_DS2482__ ++#define __PLATFORM_DATA_DS2482__ ++ ++struct ds2482_platform_data { ++ int slpz_gpio; ++}; ++ ++#endif /* __PLATFORM_DATA_DS2482__ */ +diff --git a/include/linux/poison.h b/include/linux/poison.h +index 2110a81..253c9b4 100644 +--- a/include/linux/poison.h ++++ b/include/linux/poison.h +@@ -19,8 +19,8 @@ + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +-#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA) +-#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA) ++#define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA) ++#define LIST_POISON2 ((void *) 0x200 + POISON_POINTER_DELTA) + + /********** include/linux/timer.h **********/ + /* +diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h +index 3e96a6a..d1a8ad7 100644 +--- a/include/linux/posix_acl.h ++++ b/include/linux/posix_acl.h +@@ -95,6 +95,7 @@ extern int set_posix_acl(struct inode *, int, struct posix_acl *); + extern int posix_acl_chmod(struct inode *, umode_t); + extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **, + struct posix_acl **); ++extern int posix_acl_update_mode(struct inode *, umode_t *, struct posix_acl **); + + extern int simple_set_acl(struct inode *, struct posix_acl *, int); + extern int simple_acl_create(struct inode *, struct inode *); +diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h +index 096dbce..ec509ed 100644 +--- a/include/linux/power_supply.h ++++ b/include/linux/power_supply.h +@@ -17,6 +17,7 @@ + #include <linux/leds.h> + #include <linux/spinlock.h> + #include <linux/notifier.h> ++#include <linux/types.h> + + /* + * All voltages, currents, charges, energies, time and temperatures in uV, +@@ -147,6 +148,12 @@ enum power_supply_property { + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_CALIBRATE, ++ /* Local extensions */ ++ POWER_SUPPLY_PROP_USB_HC, ++ POWER_SUPPLY_PROP_USB_OTG, ++ POWER_SUPPLY_PROP_CHARGE_ENABLED, ++ /* Local extensions of type int64_t */ ++ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, + /* Properties of type `const char *' */ + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +@@ -171,6 +178,7 @@ enum power_supply_notifier_events { + union power_supply_propval { + int intval; + const char *strval; ++ int64_t int64val; + }; + + struct device; +diff --git a/include/linux/pstore.h b/include/linux/pstore.h +index ece0c6b..8884f6e 100644 +--- a/include/linux/pstore.h ++++ b/include/linux/pstore.h +@@ -39,6 +39,7 @@ enum pstore_type_id { + PSTORE_TYPE_PPC_RTAS = 4, + PSTORE_TYPE_PPC_OF = 5, + PSTORE_TYPE_PPC_COMMON = 6, ++ PSTORE_TYPE_PMSG = 7, + PSTORE_TYPE_UNKNOWN = 255 + }; + +diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h +index 4af3fdc..712757f 100644 +--- a/include/linux/pstore_ram.h ++++ b/include/linux/pstore_ram.h +@@ -68,6 +68,8 @@ void persistent_ram_free_old(struct persistent_ram_zone *prz); + ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz, + char *str, size_t len); + ++void ramoops_console_write_buf(const char *buf, size_t size); ++ + /* + * Ramoops platform data + * @mem_size memory size for ramoops +@@ -81,6 +83,7 @@ struct ramoops_platform_data { + unsigned long record_size; + unsigned long console_size; + unsigned long ftrace_size; ++ unsigned long pmsg_size; + int dump_oops; + struct persistent_ram_ecc_info ecc_info; + }; +diff --git a/include/linux/sched.h b/include/linux/sched.h +index 5e344bb..b1bf96c 100644 +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -758,6 +758,8 @@ struct user_struct { + unsigned long mq_bytes; /* How many bytes can be allocated to mqueue? */ + #endif + unsigned long locked_shm; /* How many pages of mlocked shm ? */ ++ unsigned long unix_inflight; /* How many files in flight in unix sockets */ ++ atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */ + + #ifdef CONFIG_KEYS + struct key *uid_keyring; /* UID specific keyring */ +@@ -1034,6 +1036,13 @@ extern void wake_up_if_idle(int cpu); + # define SD_INIT_NAME(type) + #endif + ++#ifdef CONFIG_SCHED_HMP ++struct hmp_domain { ++ struct cpumask cpus; ++ struct cpumask possible_cpus; ++ struct list_head hmp_domains; ++}; ++#endif /* CONFIG_SCHED_HMP */ + #else /* CONFIG_SMP */ + + struct sched_domain_attr; +@@ -1081,8 +1090,23 @@ struct sched_avg { + u64 last_runnable_update; + s64 decay_count; + unsigned long load_avg_contrib; ++ unsigned long load_avg_ratio; ++#ifdef CONFIG_SCHED_HMP ++ u64 hmp_last_up_migration; ++ u64 hmp_last_down_migration; ++#endif ++ u32 usage_avg_sum; + }; + ++#ifdef CONFIG_SCHED_HMP ++/* ++ * We want to avoid boosting any processes forked from init (PID 1) ++ * and kthreadd (assumed to be PID 2). ++ */ ++#define hmp_task_should_forkboost(task) \ ++ ((task->parent && task->parent->pid > 2)) ++#endif ++ + #ifdef CONFIG_SCHEDSTATS + struct sched_statistics { + u64 wait_start; +@@ -1367,6 +1391,7 @@ struct task_struct { + + cputime_t utime, stime, utimescaled, stimescaled; + cputime_t gtime; ++ unsigned long long cpu_power; + #ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE + struct cputime prev_cputime; + #endif +@@ -1895,6 +1920,9 @@ static inline cputime_t task_gtime(struct task_struct *t) + extern void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st); + extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st); + ++extern int task_free_register(struct notifier_block *n); ++extern int task_free_unregister(struct notifier_block *n); ++ + /* + * Per process flags + */ +diff --git a/include/linux/security.h b/include/linux/security.h +index ba96471..d3c02ee 100644 +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -1441,6 +1441,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) + struct security_operations { + char name[SECURITY_NAME_MAX + 1]; + ++ int (*binder_set_context_mgr) (struct task_struct *mgr); ++ int (*binder_transaction) (struct task_struct *from, struct task_struct *to); ++ int (*binder_transfer_binder) (struct task_struct *from, struct task_struct *to); ++ int (*binder_transfer_file) (struct task_struct *from, struct task_struct *to, struct file *file); ++ + int (*ptrace_access_check) (struct task_struct *child, unsigned int mode); + int (*ptrace_traceme) (struct task_struct *parent); + int (*capget) (struct task_struct *target, +@@ -1739,6 +1744,10 @@ extern void __init security_fixup_ops(struct security_operations *ops); + + + /* Security operations */ ++int security_binder_set_context_mgr(struct task_struct *mgr); ++int security_binder_transaction(struct task_struct *from, struct task_struct *to); ++int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to); ++int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file); + int security_ptrace_access_check(struct task_struct *child, unsigned int mode); + int security_ptrace_traceme(struct task_struct *parent); + int security_capget(struct task_struct *target, +@@ -1927,6 +1936,26 @@ static inline int security_init(void) + return 0; + } + ++static inline int security_binder_set_context_mgr(struct task_struct *mgr) ++{ ++ return 0; ++} ++ ++static inline int security_binder_transaction(struct task_struct *from, struct task_struct *to) ++{ ++ return 0; ++} ++ ++static inline int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to) ++{ ++ return 0; ++} ++ ++static inline int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) ++{ ++ return 0; ++} ++ + static inline int security_ptrace_access_check(struct task_struct *child, + unsigned int mode) + { +diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h +index 21c2e05..219b6a3 100644 +--- a/include/linux/serial_core.h ++++ b/include/linux/serial_core.h +@@ -66,6 +66,7 @@ struct uart_ops { + void (*set_ldisc)(struct uart_port *, int new); + void (*pm)(struct uart_port *, unsigned int state, + unsigned int oldstate); ++ void (*wake_peer)(struct uart_port *); + + /* + * Return a string describing the type of the port +diff --git a/include/linux/slab.h b/include/linux/slab.h +index c265bec..f7d4ab5 100644 +--- a/include/linux/slab.h ++++ b/include/linux/slab.h +@@ -86,6 +86,11 @@ + #else + # define SLAB_FAILSLAB 0x00000000UL + #endif ++#if defined(CONFIG_MEMCG) && !defined(CONFIG_SLOB) ++# define SLAB_ACCOUNT 0x04000000UL /* Account to memcg */ ++#else ++# define SLAB_ACCOUNT 0x00000000UL ++#endif + + /* The following flags affect the page allocator grouping pages by mobility */ + #define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* Objects are reclaimable */ +diff --git a/include/linux/suspend.h b/include/linux/suspend.h +index 3388c1b..28d140d 100644 +--- a/include/linux/suspend.h ++++ b/include/linux/suspend.h +@@ -321,6 +321,14 @@ static inline void __init register_nosave_region_late(unsigned long b, unsigned + { + __register_nosave_region(b, e, 1); + } ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++/*snapshot boot add*/ ++extern void *register_save_region(unsigned long start_pfn, unsigned long); ++extern void unregister_save_region(void *); ++extern void *register_nosave_region_runtime(unsigned long start_pfn, ++ unsigned long); ++extern void unregister_nosave_region_runtime(void *); ++#endif + extern int swsusp_page_is_forbidden(struct page *); + extern void swsusp_set_page_free(struct page *); + extern void swsusp_unset_page_free(struct page *); +@@ -338,7 +346,20 @@ static inline void register_nosave_region_late(unsigned long b, unsigned long e) + static inline int swsusp_page_is_forbidden(struct page *p) { return 0; } + static inline void swsusp_set_page_free(struct page *p) {} + static inline void swsusp_unset_page_free(struct page *p) {} +- ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++/*snapshot boot add*/ ++static inline void *register_save_region(unsigned long b, unsigned long e) ++{ ++ return NULL; ++} ++static inline void unregister_save_region(void *mem) {} ++static inline void *register_nosave_region_runtime(unsigned long b, ++ unsigned long e) ++{ ++ return NULL; ++} ++static inline void unregister_nosave_region_runtime(void *mem) {} ++#endif + static inline void hibernation_set_ops(const struct platform_hibernation_ops *ops) {} + static inline int hibernate(void) { return -ENOSYS; } + static inline bool system_entering_hibernation(void) { return false; } +@@ -352,7 +373,13 @@ static inline bool hibernation_available(void) { return false; } + #define PM_POST_SUSPEND 0x0004 /* Suspend finished */ + #define PM_RESTORE_PREPARE 0x0005 /* Going to restore a saved image */ + #define PM_POST_RESTORE 0x0006 /* Restore failed */ +- ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++/*snapshot boot add*/ ++#define PM_POST_FREEZE_PROCESS 0x0101 ++#define PM_THAW_PROCESS_PREPARE 0x0102 ++#define PM_POST_DEVICE_SUSPEND 0x0103 ++#define PM_RESUME_DEVICE_PREPARE 0x0104 ++#endif + extern struct mutex pm_mutex; + + #ifdef CONFIG_PM_SLEEP +@@ -379,6 +406,7 @@ extern bool pm_get_wakeup_count(unsigned int *count, bool block); + extern bool pm_save_wakeup_count(unsigned int count); + extern void pm_wakep_autosleep_enabled(bool set); + extern void pm_print_active_wakeup_sources(void); ++extern void pm_get_active_wakeup_sources(char *pending_sources, size_t max); + + static inline void lock_system_sleep(void) + { +diff --git a/include/linux/switch.h b/include/linux/switch.h +new file mode 100644 +index 0000000..3e4c748 +--- /dev/null ++++ b/include/linux/switch.h +@@ -0,0 +1,53 @@ ++/* ++ * Switch class driver ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++*/ ++ ++#ifndef __LINUX_SWITCH_H__ ++#define __LINUX_SWITCH_H__ ++ ++struct switch_dev { ++ const char *name; ++ struct device *dev; ++ int index; ++ int state; ++ ++ ssize_t (*print_name)(struct switch_dev *sdev, char *buf); ++ ssize_t (*print_state)(struct switch_dev *sdev, char *buf); ++}; ++ ++struct gpio_switch_platform_data { ++ const char *name; ++ unsigned gpio; ++ ++ /* if NULL, switch_dev.name will be printed */ ++ const char *name_on; ++ const char *name_off; ++ /* if NULL, "0" or "1" will be printed */ ++ const char *state_on; ++ const char *state_off; ++}; ++ ++extern int switch_dev_register(struct switch_dev *sdev); ++extern void switch_dev_unregister(struct switch_dev *sdev); ++ ++static inline int switch_get_state(struct switch_dev *sdev) ++{ ++ return sdev->state; ++} ++ ++extern void switch_set_state(struct switch_dev *sdev, int state); ++ ++#endif /* __LINUX_SWITCH_H__ */ +diff --git a/include/linux/thermal.h b/include/linux/thermal.h +index ef90838..037e9df 100644 +--- a/include/linux/thermal.h ++++ b/include/linux/thermal.h +@@ -29,16 +29,19 @@ + #include <linux/idr.h> + #include <linux/device.h> + #include <linux/workqueue.h> ++#include <uapi/linux/thermal.h> + + #define THERMAL_TRIPS_NONE -1 + #define THERMAL_MAX_TRIPS 12 +-#define THERMAL_NAME_LENGTH 20 + + /* invalid cooling state */ + #define THERMAL_CSTATE_INVALID -1UL + + /* No upper/lower limit requirement */ +-#define THERMAL_NO_LIMIT THERMAL_CSTATE_INVALID ++#define THERMAL_NO_LIMIT ((u32)~0) ++ ++/* Default weight of a bound cooling device */ ++#define THERMAL_WEIGHT_DEFAULT 0 + + /* Unit conversion macros */ + #define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \ +@@ -49,11 +52,6 @@ + #define MILLICELSIUS_TO_DECI_KELVIN_WITH_OFFSET(t, off) (((t) / 100) + (off)) + #define MILLICELSIUS_TO_DECI_KELVIN(t) MILLICELSIUS_TO_DECI_KELVIN_WITH_OFFSET(t, 2732) + +-/* Adding event notification support elements */ +-#define THERMAL_GENL_FAMILY_NAME "thermal_event" +-#define THERMAL_GENL_VERSION 0x01 +-#define THERMAL_GENL_MCAST_GROUP_NAME "thermal_mc_grp" +- + /* Default Thermal Governor */ + #if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE) + #define DEFAULT_THERMAL_GOVERNOR "step_wise" +@@ -61,10 +59,13 @@ + #define DEFAULT_THERMAL_GOVERNOR "fair_share" + #elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE) + #define DEFAULT_THERMAL_GOVERNOR "user_space" ++#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR) ++#define DEFAULT_THERMAL_GOVERNOR "power_allocator" + #endif + + struct thermal_zone_device; + struct thermal_cooling_device; ++struct thermal_instance; + + enum thermal_device_mode { + THERMAL_DEVICE_DISABLED = 0, +@@ -86,30 +87,6 @@ enum thermal_trend { + THERMAL_TREND_DROP_FULL, /* apply lowest cooling action */ + }; + +-/* Events supported by Thermal Netlink */ +-enum events { +- THERMAL_AUX0, +- THERMAL_AUX1, +- THERMAL_CRITICAL, +- THERMAL_DEV_FAULT, +-}; +- +-/* attributes of thermal_genl_family */ +-enum { +- THERMAL_GENL_ATTR_UNSPEC, +- THERMAL_GENL_ATTR_EVENT, +- __THERMAL_GENL_ATTR_MAX, +-}; +-#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) +- +-/* commands supported by the thermal_genl_family */ +-enum { +- THERMAL_GENL_CMD_UNSPEC, +- THERMAL_GENL_CMD_EVENT, +- __THERMAL_GENL_CMD_MAX, +-}; +-#define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) +- + struct thermal_zone_device_ops { + int (*bind) (struct thermal_zone_device *, + struct thermal_cooling_device *); +@@ -142,6 +119,12 @@ struct thermal_cooling_device_ops { + int (*get_max_state) (struct thermal_cooling_device *, unsigned long *); + int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *); + int (*set_cur_state) (struct thermal_cooling_device *, unsigned long); ++ int (*get_requested_power)(struct thermal_cooling_device *, ++ struct thermal_zone_device *, u32 *); ++ int (*state2power)(struct thermal_cooling_device *, ++ struct thermal_zone_device *, unsigned long, u32 *); ++ int (*power2state)(struct thermal_cooling_device *, ++ struct thermal_zone_device *, u32, unsigned long *); + }; + + struct thermal_cooling_device { +@@ -173,8 +156,7 @@ struct thermal_attr { + * @devdata: private pointer for device private data + * @trips: number of trip points the thermal zone supports + * @passive_delay: number of milliseconds to wait between polls when +- * performing passive cooling. Currenty only used by the +- * step-wise governor ++ * performing passive cooling. + * @polling_delay: number of milliseconds to wait between polls when + * checking whether trip points have been crossed (0 for + * interrupt driven systems) +@@ -184,13 +166,13 @@ struct thermal_attr { + * @last_temperature: previous temperature read + * @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION + * @passive: 1 if you've crossed a passive trip point, 0 otherwise. +- * Currenty only used by the step-wise governor. + * @forced_passive: If > 0, temperature at which to switch on all ACPI + * processor cooling devices. Currently only used by the + * step-wise governor. + * @ops: operations this &thermal_zone_device supports + * @tzp: thermal zone parameters + * @governor: pointer to the governor for this thermal zone ++ * @governor_data: private pointer for governor data + * @thermal_instances: list of &struct thermal_instance of this thermal zone + * @idr: &struct idr to generate unique id for this zone's cooling + * devices +@@ -215,8 +197,9 @@ struct thermal_zone_device { + int passive; + unsigned int forced_passive; + struct thermal_zone_device_ops *ops; +- const struct thermal_zone_params *tzp; ++ struct thermal_zone_params *tzp; + struct thermal_governor *governor; ++ void *governor_data; + struct list_head thermal_instances; + struct idr idr; + struct mutex lock; +@@ -227,12 +210,19 @@ struct thermal_zone_device { + /** + * struct thermal_governor - structure that holds thermal governor information + * @name: name of the governor ++ * @bind_to_tz: callback called when binding to a thermal zone. If it ++ * returns 0, the governor is bound to the thermal zone, ++ * otherwise it fails. ++ * @unbind_from_tz: callback called when a governor is unbound from a ++ * thermal zone. + * @throttle: callback called for every trip point even if temperature is + * below the trip point temperature + * @governor_list: node in thermal_governor_list (in thermal_core.c) + */ + struct thermal_governor { + char name[THERMAL_NAME_LENGTH]; ++ int (*bind_to_tz)(struct thermal_zone_device *tz); ++ void (*unbind_from_tz)(struct thermal_zone_device *tz); + int (*throttle)(struct thermal_zone_device *tz, int trip); + struct list_head governor_list; + }; +@@ -243,9 +233,12 @@ struct thermal_bind_params { + + /* + * This is a measure of 'how effectively these devices can +- * cool 'this' thermal zone. The shall be determined by platform +- * characterization. This is on a 'percentage' scale. +- * See Documentation/thermal/sysfs-api.txt for more information. ++ * cool 'this' thermal zone. It shall be determined by ++ * platform characterization. This value is relative to the ++ * rest of the weights so a cooling device whose weight is ++ * double that of another cooling device is twice as ++ * effective. See Documentation/thermal/sysfs-api.txt for more ++ * information. + */ + int weight; + +@@ -282,6 +275,44 @@ struct thermal_zone_params { + + int num_tbps; /* Number of tbp entries */ + struct thermal_bind_params *tbp; ++ ++ /* ++ * Sustainable power (heat) that this thermal zone can dissipate in ++ * mW ++ */ ++ u32 sustainable_power; ++ ++ /* ++ * Proportional parameter of the PID controller when ++ * overshooting (i.e., when temperature is below the target) ++ */ ++ s32 k_po; ++ ++ /* ++ * Proportional parameter of the PID controller when ++ * undershooting ++ */ ++ s32 k_pu; ++ ++ /* Integral parameter of the PID controller */ ++ s32 k_i; ++ ++ /* Derivative parameter of the PID controller */ ++ s32 k_d; ++ ++ /* threshold below which the error is no longer accumulated */ ++ s32 integral_cutoff; ++ ++ /* ++ * @slope: slope of a linear temperature adjustment curve. ++ * Used by thermal zone drivers. ++ */ ++ int slope; ++ /* ++ * @offset: offset of a linear temperature adjustment curve. ++ * Used by thermal zone drivers (default 0). ++ */ ++ int offset; + }; + + struct thermal_genl_event { +@@ -289,19 +320,49 @@ struct thermal_genl_event { + enum events event; + }; + ++/** ++ * struct thermal_zone_of_device_ops - scallbacks for handling DT based zones ++ * ++ * Mandatory: ++ * @get_temp: a pointer to a function that reads the sensor temperature. ++ * ++ * Optional: ++ * @get_trend: a pointer to a function that reads the sensor temperature trend. ++ * @set_emul_temp: a pointer to a function that sets sensor emulated ++ * temperature. ++ */ ++struct thermal_zone_of_device_ops { ++ int (*get_temp)(void *, long *); ++ int (*get_trend)(void *, long *); ++ int (*set_emul_temp)(void *, unsigned long); ++}; ++ ++/** ++ * struct thermal_trip - representation of a point in temperature domain ++ * @np: pointer to struct device_node that this trip point was created from ++ * @temperature: temperature value in miliCelsius ++ * @hysteresis: relative hysteresis in miliCelsius ++ * @type: trip point type ++ */ ++ ++struct thermal_trip { ++ struct device_node *np; ++ unsigned long int temperature; ++ unsigned long int hysteresis; ++ enum thermal_trip_type type; ++}; ++ + /* Function declarations */ + #ifdef CONFIG_THERMAL_OF + struct thermal_zone_device * +-thermal_zone_of_sensor_register(struct device *dev, int id, +- void *data, int (*get_temp)(void *, long *), +- int (*get_trend)(void *, long *)); ++thermal_zone_of_sensor_register(struct device *dev, int id, void *data, ++ const struct thermal_zone_of_device_ops *ops); + void thermal_zone_of_sensor_unregister(struct device *dev, + struct thermal_zone_device *tz); + #else + static inline struct thermal_zone_device * +-thermal_zone_of_sensor_register(struct device *dev, int id, +- void *data, int (*get_temp)(void *, long *), +- int (*get_trend)(void *, long *)) ++thermal_zone_of_sensor_register(struct device *dev, int id, void *data, ++ const struct thermal_zone_of_device_ops *ops) + { + return NULL; + } +@@ -313,14 +374,27 @@ void thermal_zone_of_sensor_unregister(struct device *dev, + } + + #endif ++ ++#if IS_ENABLED(CONFIG_THERMAL) ++static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev) ++{ ++ return cdev->ops->get_requested_power && cdev->ops->state2power && ++ cdev->ops->power2state; ++} ++ ++int power_actor_get_max_power(struct thermal_cooling_device *, ++ struct thermal_zone_device *tz, u32 *max_power); ++int power_actor_set_power(struct thermal_cooling_device *, ++ struct thermal_instance *, u32); + struct thermal_zone_device *thermal_zone_device_register(const char *, int, int, + void *, struct thermal_zone_device_ops *, +- const struct thermal_zone_params *, int, int); ++ struct thermal_zone_params *, int, int); + void thermal_zone_device_unregister(struct thermal_zone_device *); + + int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int, + struct thermal_cooling_device *, +- unsigned long, unsigned long); ++ unsigned long, unsigned long, ++ unsigned int); + int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int, + struct thermal_cooling_device *); + void thermal_zone_device_update(struct thermal_zone_device *); +@@ -339,8 +413,66 @@ struct thermal_instance *get_thermal_instance(struct thermal_zone_device *, + struct thermal_cooling_device *, int); + void thermal_cdev_update(struct thermal_cooling_device *); + void thermal_notify_framework(struct thermal_zone_device *, int); +- +-#ifdef CONFIG_NET ++#else ++static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev) ++{ return false; } ++static inline int power_actor_get_max_power(struct thermal_cooling_device *cdev, ++ struct thermal_zone_device *tz, u32 *max_power) ++{ return 0; } ++static inline int power_actor_set_power(struct thermal_cooling_device *cdev, ++ struct thermal_instance *tz, u32 power) ++{ return 0; } ++static inline struct thermal_zone_device *thermal_zone_device_register( ++ const char *type, int trips, int mask, void *devdata, ++ struct thermal_zone_device_ops *ops, ++ const struct thermal_zone_params *tzp, ++ int passive_delay, int polling_delay) ++{ return ERR_PTR(-ENODEV); } ++static inline void thermal_zone_device_unregister( ++ struct thermal_zone_device *tz) ++{ } ++static inline int thermal_zone_bind_cooling_device( ++ struct thermal_zone_device *tz, int trip, ++ struct thermal_cooling_device *cdev, ++ unsigned long upper, unsigned long lower) ++{ return -ENODEV; } ++static inline int thermal_zone_unbind_cooling_device( ++ struct thermal_zone_device *tz, int trip, ++ struct thermal_cooling_device *cdev) ++{ return -ENODEV; } ++static inline void thermal_zone_device_update(struct thermal_zone_device *tz) ++{ } ++static inline struct thermal_cooling_device * ++thermal_cooling_device_register(char *type, void *devdata, ++ const struct thermal_cooling_device_ops *ops) ++{ return ERR_PTR(-ENODEV); } ++static inline struct thermal_cooling_device * ++thermal_of_cooling_device_register(struct device_node *np, ++ char *type, void *devdata, const struct thermal_cooling_device_ops *ops) ++{ return ERR_PTR(-ENODEV); } ++static inline void thermal_cooling_device_unregister( ++ struct thermal_cooling_device *cdev) ++{ } ++static inline struct thermal_zone_device *thermal_zone_get_zone_by_name( ++ const char *name) ++{ return ERR_PTR(-ENODEV); } ++static inline int thermal_zone_get_temp( ++ struct thermal_zone_device *tz, unsigned long *temp) ++{ return -ENODEV; } ++static inline int get_tz_trend(struct thermal_zone_device *tz, int trip) ++{ return -ENODEV; } ++static inline struct thermal_instance * ++get_thermal_instance(struct thermal_zone_device *tz, ++ struct thermal_cooling_device *cdev, int trip) ++{ return ERR_PTR(-ENODEV); } ++static inline void thermal_cdev_update(struct thermal_cooling_device *cdev) ++{ } ++static inline void thermal_notify_framework(struct thermal_zone_device *tz, ++ int trip) ++{ } ++#endif /* CONFIG_THERMAL */ ++ ++#if defined(CONFIG_NET) && IS_ENABLED(CONFIG_THERMAL) + extern int thermal_generate_netlink_event(struct thermal_zone_device *tz, + enum events event); + #else +diff --git a/include/linux/uid_stat.h b/include/linux/uid_stat.h +new file mode 100644 +index 0000000..6bd6c4e +--- /dev/null ++++ b/include/linux/uid_stat.h +@@ -0,0 +1,29 @@ ++/* include/linux/uid_stat.h ++ * ++ * Copyright (C) 2008-2009 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __uid_stat_h ++#define __uid_stat_h ++ ++/* Contains definitions for resource tracking per uid. */ ++ ++#ifdef CONFIG_UID_STAT ++int uid_stat_tcp_snd(uid_t uid, int size); ++int uid_stat_tcp_rcv(uid_t uid, int size); ++#else ++#define uid_stat_tcp_snd(uid, size) do {} while (0); ++#define uid_stat_tcp_rcv(uid, size) do {} while (0); ++#endif ++ ++#endif /* _LINUX_UID_STAT_H */ +diff --git a/include/linux/uio.h b/include/linux/uio.h +index 9b15814..0a48eee 100644 +--- a/include/linux/uio.h ++++ b/include/linux/uio.h +@@ -96,6 +96,13 @@ static inline size_t iov_iter_count(struct iov_iter *i) + { + return i->count; + } ++/* ++ * Get one of READ or WRITE out of iter->type without any other flags OR'd in ++ * with it. ++ * ++ * The ?: is just for type safety. ++ */ ++#define iov_iter_rw(i) ((0 ? (struct iov_iter *)0 : (i))->type & RW_MASK) + + /* + * Cap the iov_iter by given limit; note that the second argument is +diff --git a/include/linux/usb/class-dual-role.h b/include/linux/usb/class-dual-role.h +new file mode 100644 +index 0000000..af42ed3 +--- /dev/null ++++ b/include/linux/usb/class-dual-role.h +@@ -0,0 +1,128 @@ ++#ifndef __LINUX_CLASS_DUAL_ROLE_H__ ++#define __LINUX_CLASS_DUAL_ROLE_H__ ++ ++#include <linux/workqueue.h> ++#include <linux/errno.h> ++#include <linux/types.h> ++ ++struct device; ++ ++enum dual_role_supported_modes { ++ DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP = 0, ++ DUAL_ROLE_SUPPORTED_MODES_DFP, ++ DUAL_ROLE_SUPPORTED_MODES_UFP, ++/*The following should be the last element*/ ++ DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL, ++}; ++ ++enum { ++ DUAL_ROLE_PROP_MODE_UFP = 0, ++ DUAL_ROLE_PROP_MODE_DFP, ++ DUAL_ROLE_PROP_MODE_NONE, ++/*The following should be the last element*/ ++ DUAL_ROLE_PROP_MODE_TOTAL, ++}; ++ ++enum { ++ DUAL_ROLE_PROP_PR_SRC = 0, ++ DUAL_ROLE_PROP_PR_SNK, ++ DUAL_ROLE_PROP_PR_NONE, ++/*The following should be the last element*/ ++ DUAL_ROLE_PROP_PR_TOTAL, ++ ++}; ++ ++enum { ++ DUAL_ROLE_PROP_DR_HOST = 0, ++ DUAL_ROLE_PROP_DR_DEVICE, ++ DUAL_ROLE_PROP_DR_NONE, ++/*The following should be the last element*/ ++ DUAL_ROLE_PROP_DR_TOTAL, ++}; ++ ++enum { ++ DUAL_ROLE_PROP_VCONN_SUPPLY_NO = 0, ++ DUAL_ROLE_PROP_VCONN_SUPPLY_YES, ++/*The following should be the last element*/ ++ DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL, ++}; ++ ++enum dual_role_property { ++ DUAL_ROLE_PROP_SUPPORTED_MODES = 0, ++ DUAL_ROLE_PROP_MODE, ++ DUAL_ROLE_PROP_PR, ++ DUAL_ROLE_PROP_DR, ++ DUAL_ROLE_PROP_VCONN_SUPPLY, ++}; ++ ++struct dual_role_phy_instance; ++ ++/* Description of typec port */ ++struct dual_role_phy_desc { ++ /* /sys/class/dual_role_usb/<name>/ */ ++ const char *name; ++ enum dual_role_supported_modes supported_modes; ++ enum dual_role_property *properties; ++ size_t num_properties; ++ ++ /* Callback for "cat /sys/class/dual_role_usb/<name>/<property>" */ ++ int (*get_property)(struct dual_role_phy_instance *dual_role, ++ enum dual_role_property prop, ++ unsigned int *val); ++ /* Callback for "echo <value> > ++ * /sys/class/dual_role_usb/<name>/<property>" */ ++ int (*set_property)(struct dual_role_phy_instance *dual_role, ++ enum dual_role_property prop, ++ const unsigned int *val); ++ /* Decides whether userspace can change a specific property */ ++ int (*property_is_writeable)(struct dual_role_phy_instance *dual_role, ++ enum dual_role_property prop); ++}; ++ ++struct dual_role_phy_instance { ++ const struct dual_role_phy_desc *desc; ++ ++ /* Driver private data */ ++ void *drv_data; ++ ++ struct device dev; ++ struct work_struct changed_work; ++}; ++ ++#if IS_ENABLED(CONFIG_DUAL_ROLE_USB_INTF) ++extern void dual_role_instance_changed(struct dual_role_phy_instance ++ *dual_role); ++extern struct dual_role_phy_instance *__must_check ++devm_dual_role_instance_register(struct device *parent, ++ const struct dual_role_phy_desc *desc); ++extern void devm_dual_role_instance_unregister(struct device *dev, ++ struct dual_role_phy_instance ++ *dual_role); ++extern int dual_role_get_property(struct dual_role_phy_instance *dual_role, ++ enum dual_role_property prop, ++ unsigned int *val); ++extern int dual_role_set_property(struct dual_role_phy_instance *dual_role, ++ enum dual_role_property prop, ++ const unsigned int *val); ++extern int dual_role_property_is_writeable(struct dual_role_phy_instance ++ *dual_role, ++ enum dual_role_property prop); ++extern void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role); ++#else /* CONFIG_DUAL_ROLE_USB_INTF */ ++static void dual_role_instance_changed(struct dual_role_phy_instance ++ *dual_role){} ++static struct dual_role_phy_instance *__must_check ++devm_dual_role_instance_register(struct device *parent, ++ const struct dual_role_phy_desc *desc) ++{ ++ return ERR_PTR(-ENOSYS); ++} ++static void devm_dual_role_instance_unregister(struct device *dev, ++ struct dual_role_phy_instance ++ *dual_role){} ++static void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role) ++{ ++ return ERR_PTR(-ENOSYS); ++} ++#endif /* CONFIG_DUAL_ROLE_USB_INTF */ ++#endif /* __LINUX_CLASS_DUAL_ROLE_H__ */ +diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h +index c330f5e..6380a0e 100644 +--- a/include/linux/usb/composite.h ++++ b/include/linux/usb/composite.h +@@ -562,6 +562,7 @@ struct usb_function_instance { + struct config_group group; + struct list_head cfs_list; + struct usb_function_driver *fd; ++ struct usb_function *f; + int (*set_inst_name)(struct usb_function_instance *inst, + const char *name); + void (*free_func_inst)(struct usb_function_instance *inst); +diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h +new file mode 100644 +index 0000000..ebe3c4d +--- /dev/null ++++ b/include/linux/usb/f_accessory.h +@@ -0,0 +1,23 @@ ++/* ++ * Gadget Function Driver for Android USB accessories ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __LINUX_USB_F_ACCESSORY_H ++#define __LINUX_USB_F_ACCESSORY_H ++ ++#include <uapi/linux/usb/f_accessory.h> ++ ++#endif /* __LINUX_USB_F_ACCESSORY_H */ +diff --git a/include/linux/usb/f_mtp.h b/include/linux/usb/f_mtp.h +new file mode 100644 +index 0000000..4e84177 +--- /dev/null ++++ b/include/linux/usb/f_mtp.h +@@ -0,0 +1,23 @@ ++/* ++ * Gadget Function Driver for MTP ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef __LINUX_USB_F_MTP_H ++#define __LINUX_USB_F_MTP_H ++ ++#include <uapi/linux/usb/f_mtp.h> ++ ++#endif /* __LINUX_USB_F_MTP_H */ +diff --git a/include/linux/wakelock.h b/include/linux/wakelock.h +new file mode 100644 +index 0000000..f4a698a +--- /dev/null ++++ b/include/linux/wakelock.h +@@ -0,0 +1,67 @@ ++/* include/linux/wakelock.h ++ * ++ * Copyright (C) 2007-2012 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _LINUX_WAKELOCK_H ++#define _LINUX_WAKELOCK_H ++ ++#include <linux/ktime.h> ++#include <linux/device.h> ++ ++/* A wake_lock prevents the system from entering suspend or other low power ++ * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock ++ * prevents a full system suspend. ++ */ ++ ++enum { ++ WAKE_LOCK_SUSPEND, /* Prevent suspend */ ++ WAKE_LOCK_TYPE_COUNT ++}; ++ ++struct wake_lock { ++ struct wakeup_source ws; ++}; ++ ++static inline void wake_lock_init(struct wake_lock *lock, int type, ++ const char *name) ++{ ++ wakeup_source_init(&lock->ws, name); ++} ++ ++static inline void wake_lock_destroy(struct wake_lock *lock) ++{ ++ wakeup_source_trash(&lock->ws); ++} ++ ++static inline void wake_lock(struct wake_lock *lock) ++{ ++ __pm_stay_awake(&lock->ws); ++} ++ ++static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) ++{ ++ __pm_wakeup_event(&lock->ws, jiffies_to_msecs(timeout)); ++} ++ ++static inline void wake_unlock(struct wake_lock *lock) ++{ ++ __pm_relax(&lock->ws); ++} ++ ++static inline int wake_lock_active(struct wake_lock *lock) ++{ ++ return lock->ws.active; ++} ++ ++#endif +diff --git a/include/linux/wakeup_reason.h b/include/linux/wakeup_reason.h +new file mode 100644 +index 0000000..ad8b769 +--- /dev/null ++++ b/include/linux/wakeup_reason.h +@@ -0,0 +1,27 @@ ++/* ++ * include/linux/wakeup_reason.h ++ * ++ * Logs the reason which caused the kernel to resume ++ * from the suspend mode. ++ * ++ * Copyright (C) 2014 Google, Inc. ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _LINUX_WAKEUP_REASON_H ++#define _LINUX_WAKEUP_REASON_H ++ ++#define MAX_SUSPEND_ABORT_LEN 256 ++ ++void log_wakeup_reason(int irq); ++void log_suspend_abort_reason(const char *fmt, ...); ++int check_wakeup_reason(int irq); ++ ++#endif /* _LINUX_WAKEUP_REASON_H */ +diff --git a/include/linux/wifi_tiwlan.h b/include/linux/wifi_tiwlan.h +new file mode 100644 +index 0000000..f07e067 +--- /dev/null ++++ b/include/linux/wifi_tiwlan.h +@@ -0,0 +1,27 @@ ++/* include/linux/wifi_tiwlan.h ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++#ifndef _LINUX_WIFI_TIWLAN_H_ ++#define _LINUX_WIFI_TIWLAN_H_ ++ ++#include <linux/wlan_plat.h> ++ ++#define WMPA_NUMBER_OF_SECTIONS 3 ++#define WMPA_NUMBER_OF_BUFFERS 160 ++#define WMPA_SECTION_HEADER 24 ++#define WMPA_SECTION_SIZE_0 (WMPA_NUMBER_OF_BUFFERS * 64) ++#define WMPA_SECTION_SIZE_1 (WMPA_NUMBER_OF_BUFFERS * 256) ++#define WMPA_SECTION_SIZE_2 (WMPA_NUMBER_OF_BUFFERS * 2048) ++ ++#endif +diff --git a/include/linux/wlan_plat.h b/include/linux/wlan_plat.h +new file mode 100644 +index 0000000..8e8b06f +--- /dev/null ++++ b/include/linux/wlan_plat.h +@@ -0,0 +1,30 @@ ++/* include/linux/wlan_plat.h ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++#ifndef _LINUX_WLAN_PLAT_H_ ++#define _LINUX_WLAN_PLAT_H_ ++ ++#define WLAN_PLAT_NODFS_FLAG 0x01 ++ ++struct wifi_platform_data { ++ int (*set_power)(int val); ++ int (*set_reset)(int val); ++ int (*set_carddetect)(int val); ++ void *(*mem_prealloc)(int section, unsigned long size); ++ int (*get_mac_addr)(unsigned char *buf); ++ int (*get_wake_irq)(void); ++ void *(*get_country_code)(char *ccode, u32 flags); ++}; ++ ++#endif +diff --git a/include/net/activity_stats.h b/include/net/activity_stats.h +new file mode 100644 +index 0000000..10e4c15 +--- /dev/null ++++ b/include/net/activity_stats.h +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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. ++ * ++ * Author: Mike Chan (mike@android.com) ++ */ ++ ++#ifndef __activity_stats_h ++#define __activity_stats_h ++ ++#ifdef CONFIG_NET_ACTIVITY_STATS ++void activity_stats_update(void); ++#else ++#define activity_stats_update(void) {} ++#endif ++ ++#endif /* _NET_ACTIVITY_STATS_H */ +diff --git a/include/net/addrconf.h b/include/net/addrconf.h +index d13573b..9828539 100644 +--- a/include/net/addrconf.h ++++ b/include/net/addrconf.h +@@ -193,6 +193,8 @@ static inline bool ipv6_is_mld(struct sk_buff *skb, int nexthdr, int offset) + void addrconf_prefix_rcv(struct net_device *dev, + u8 *opt, int len, bool sllao); + ++u32 addrconf_rt_table(const struct net_device *dev, u32 default_table); ++ + /* + * anycast prototypes (anycast.c) + */ +diff --git a/include/net/af_unix.h b/include/net/af_unix.h +index a175ba4..9cb6119 100644 +--- a/include/net/af_unix.h ++++ b/include/net/af_unix.h +@@ -6,8 +6,8 @@ + #include <linux/mutex.h> + #include <net/sock.h> + +-void unix_inflight(struct file *fp); +-void unix_notinflight(struct file *fp); ++void unix_inflight(struct user_struct *user, struct file *fp); ++void unix_notinflight(struct user_struct *user, struct file *fp); + void unix_gc(void); + void wait_for_unix_gc(void); + struct sock *unix_get_socket(struct file *filp); +@@ -63,6 +63,7 @@ struct unix_sock { + #define UNIX_GC_CANDIDATE 0 + #define UNIX_GC_MAYBE_CYCLE 1 + struct socket_wq peer_wq; ++ wait_queue_t peer_wake; + }; + #define unix_sk(__sk) ((struct unix_sock *)__sk) + +diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h +index e584de1..c10c06f 100644 +--- a/include/net/fib_rules.h ++++ b/include/net/fib_rules.h +@@ -28,6 +28,8 @@ struct fib_rule { + int suppress_prefixlen; + char iifname[IFNAMSIZ]; + char oifname[IFNAMSIZ]; ++ kuid_t uid_start; ++ kuid_t uid_end; + struct rcu_head rcu; + }; + +@@ -86,9 +88,13 @@ struct fib_rules_ops { + [FRA_FWMARK] = { .type = NLA_U32 }, \ + [FRA_FWMASK] = { .type = NLA_U32 }, \ + [FRA_TABLE] = { .type = NLA_U32 }, \ ++ [FRA_GOTO] = { .type = NLA_U32 }, \ ++ [FRA_UID_START] = { .type = NLA_U32 }, \ ++ [FRA_UID_END] = { .type = NLA_U32 }, \ + [FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32 }, \ + [FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \ + [FRA_GOTO] = { .type = NLA_U32 } ++ + + static inline void fib_rule_get(struct fib_rule *rule) + { +diff --git a/include/net/flow.h b/include/net/flow.h +index 8109a15..da7743e 100644 +--- a/include/net/flow.h ++++ b/include/net/flow.h +@@ -10,6 +10,7 @@ + #include <linux/socket.h> + #include <linux/in6.h> + #include <linux/atomic.h> ++#include <linux/uidgid.h> + + /* + * ifindex generation is per-net namespace, and loopback is +@@ -30,6 +31,7 @@ struct flowi_common { + #define FLOWI_FLAG_ANYSRC 0x01 + #define FLOWI_FLAG_KNOWN_NH 0x02 + __u32 flowic_secid; ++ kuid_t flowic_uid; + }; + + union flowi_uli { +@@ -66,6 +68,7 @@ struct flowi4 { + #define flowi4_proto __fl_common.flowic_proto + #define flowi4_flags __fl_common.flowic_flags + #define flowi4_secid __fl_common.flowic_secid ++#define flowi4_uid __fl_common.flowic_uid + + /* (saddr,daddr) must be grouped, same order as in IP header */ + __be32 saddr; +@@ -85,7 +88,8 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, + __u32 mark, __u8 tos, __u8 scope, + __u8 proto, __u8 flags, + __be32 daddr, __be32 saddr, +- __be16 dport, __be16 sport) ++ __be16 dport, __be16 sport, ++ kuid_t uid) + { + fl4->flowi4_oif = oif; + fl4->flowi4_iif = LOOPBACK_IFINDEX; +@@ -95,6 +99,7 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, + fl4->flowi4_proto = proto; + fl4->flowi4_flags = flags; + fl4->flowi4_secid = 0; ++ fl4->flowi4_uid = uid; + fl4->daddr = daddr; + fl4->saddr = saddr; + fl4->fl4_dport = dport; +@@ -122,6 +127,7 @@ struct flowi6 { + #define flowi6_proto __fl_common.flowic_proto + #define flowi6_flags __fl_common.flowic_flags + #define flowi6_secid __fl_common.flowic_secid ++#define flowi6_uid __fl_common.flowic_uid + struct in6_addr daddr; + struct in6_addr saddr; + __be32 flowlabel; +@@ -165,6 +171,7 @@ struct flowi { + #define flowi_proto u.__fl_common.flowic_proto + #define flowi_flags u.__fl_common.flowic_flags + #define flowi_secid u.__fl_common.flowic_secid ++#define flowi_uid u.__fl_common.flowic_uid + } __attribute__((__aligned__(BITS_PER_LONG/8))); + + static inline struct flowi *flowi4_to_flowi(struct flowi4 *fl4) +diff --git a/include/net/ip.h b/include/net/ip.h +index c0c26c3..3fce700 100644 +--- a/include/net/ip.h ++++ b/include/net/ip.h +@@ -172,6 +172,7 @@ struct ip_reply_arg { + /* -1 if not needed */ + int bound_dev_if; + u8 tos; ++ kuid_t uid; + }; + + #define IP_REPLY_ARG_NOSRCCHECK 1 +diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h +index eda131d..d513308 100644 +--- a/include/net/ip6_route.h ++++ b/include/net/ip6_route.h +@@ -108,7 +108,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, + const struct in6_addr *gwaddr); + + void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, int oif, +- u32 mark); ++ u32 mark, kuid_t uid); + void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu); + void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark); + void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, +diff --git a/include/net/ipv6.h b/include/net/ipv6.h +index 4292929..f804797 100644 +--- a/include/net/ipv6.h ++++ b/include/net/ipv6.h +@@ -207,6 +207,7 @@ extern rwlock_t ip6_ra_lock; + */ + + struct ipv6_txoptions { ++ atomic_t refcnt; + /* Length of this structure */ + int tot_len; + +@@ -219,7 +220,7 @@ struct ipv6_txoptions { + struct ipv6_opt_hdr *dst0opt; + struct ipv6_rt_hdr *srcrt; /* Routing Header */ + struct ipv6_opt_hdr *dst1opt; +- ++ struct rcu_head rcu; + /* Option buffer, as read by IPV6_PKTOPTIONS, starts here. */ + }; + +@@ -252,6 +253,24 @@ struct ipv6_fl_socklist { + struct rcu_head rcu; + }; + ++static inline struct ipv6_txoptions *txopt_get(const struct ipv6_pinfo *np) ++{ ++ struct ipv6_txoptions *opt; ++ ++ rcu_read_lock(); ++ opt = rcu_dereference(np->opt); ++ if (opt && !atomic_inc_not_zero(&opt->refcnt)) ++ opt = NULL; ++ rcu_read_unlock(); ++ return opt; ++} ++ ++static inline void txopt_put(struct ipv6_txoptions *opt) ++{ ++ if (opt && atomic_dec_and_test(&opt->refcnt)) ++ kfree_rcu(opt, rcu); ++} ++ + struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label); + struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space, + struct ip6_flowlabel *fl, +diff --git a/include/net/route.h b/include/net/route.h +index b17cf28..41288e3 100644 +--- a/include/net/route.h ++++ b/include/net/route.h +@@ -140,7 +140,7 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi + flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos, + RT_SCOPE_UNIVERSE, proto, + sk ? inet_sk_flowi_flags(sk) : 0, +- daddr, saddr, dport, sport); ++ daddr, saddr, dport, sport, sk ? sock_i_uid(sk) : GLOBAL_ROOT_UID); + if (sk) + security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); + return ip_route_output_flow(net, fl4, sk); +@@ -249,7 +249,8 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32 + flow_flags |= FLOWI_FLAG_ANYSRC; + + flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, +- protocol, flow_flags, dst, src, dport, sport); ++ protocol, flow_flags, dst, src, dport, sport, ++ sock_i_uid(sk)); + } + + static inline struct rtable *ip_route_connect(struct flowi4 *fl4, +diff --git a/include/net/scm.h b/include/net/scm.h +index 262532d..59fa93c 100644 +--- a/include/net/scm.h ++++ b/include/net/scm.h +@@ -21,6 +21,7 @@ struct scm_creds { + struct scm_fp_list { + short count; + short max; ++ struct user_struct *user; + struct file *fp[SCM_MAX_FD]; + }; + +diff --git a/include/net/sock.h b/include/net/sock.h +index 4406dbe..b27b1ed 100644 +--- a/include/net/sock.h ++++ b/include/net/sock.h +@@ -379,6 +379,7 @@ struct sock { + sk_no_check_rx : 1, + sk_userlocks : 4, + sk_protocol : 8, ++#define SK_PROTOCOL_MAX U8_MAX + sk_type : 16; + kmemcheck_bitfield_end(flags); + int sk_wmem_queued; +diff --git a/include/net/tcp.h b/include/net/tcp.h +index 4062b4f..25d497d 100644 +--- a/include/net/tcp.h ++++ b/include/net/tcp.h +@@ -276,6 +276,7 @@ extern int sysctl_tcp_challenge_ack_limit; + extern unsigned int sysctl_tcp_notsent_lowat; + extern int sysctl_tcp_min_tso_segs; + extern int sysctl_tcp_autocorking; ++extern int sysctl_tcp_default_init_rwnd; + + extern atomic_long_t tcp_memory_allocated; + extern struct percpu_counter tcp_sockets_allocated; +@@ -1070,6 +1071,7 @@ static inline void tcp_prequeue_init(struct tcp_sock *tp) + } + + bool tcp_prequeue(struct sock *sk, struct sk_buff *skb); ++int tcp_filter(struct sock *sk, struct sk_buff *skb); + + #undef STATE_TRACE + +@@ -1431,6 +1433,8 @@ static inline void tcp_check_send_head(struct sock *sk, struct sk_buff *skb_unli + { + if (sk->sk_send_head == skb_unlinked) + sk->sk_send_head = NULL; ++ if (tcp_sk(sk)->highest_sack == skb_unlinked) ++ tcp_sk(sk)->highest_sack = NULL; + } + + static inline void tcp_init_send_head(struct sock *sk) +@@ -1601,6 +1605,8 @@ static inline bool tcp_stream_memory_free(const struct sock *sk) + return notsent_bytes < tcp_notsent_lowat(tp); + } + ++extern int tcp_nuke_addr(struct net *net, struct sockaddr *addr); ++ + #ifdef CONFIG_PROC_FS + int tcp4_proc_init(void); + void tcp4_proc_exit(void); +diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h +new file mode 100644 +index 0000000..951e6ca +--- /dev/null ++++ b/include/trace/events/cpufreq_interactive.h +@@ -0,0 +1,112 @@ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM cpufreq_interactive ++ ++#if !defined(_TRACE_CPUFREQ_INTERACTIVE_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _TRACE_CPUFREQ_INTERACTIVE_H ++ ++#include <linux/tracepoint.h> ++ ++DECLARE_EVENT_CLASS(set, ++ TP_PROTO(u32 cpu_id, unsigned long targfreq, ++ unsigned long actualfreq), ++ TP_ARGS(cpu_id, targfreq, actualfreq), ++ ++ TP_STRUCT__entry( ++ __field( u32, cpu_id ) ++ __field(unsigned long, targfreq ) ++ __field(unsigned long, actualfreq ) ++ ), ++ ++ TP_fast_assign( ++ __entry->cpu_id = (u32) cpu_id; ++ __entry->targfreq = targfreq; ++ __entry->actualfreq = actualfreq; ++ ), ++ ++ TP_printk("cpu=%u targ=%lu actual=%lu", ++ __entry->cpu_id, __entry->targfreq, ++ __entry->actualfreq) ++); ++ ++DEFINE_EVENT(set, cpufreq_interactive_setspeed, ++ TP_PROTO(u32 cpu_id, unsigned long targfreq, ++ unsigned long actualfreq), ++ TP_ARGS(cpu_id, targfreq, actualfreq) ++); ++ ++DECLARE_EVENT_CLASS(loadeval, ++ TP_PROTO(unsigned long cpu_id, unsigned long load, ++ unsigned long curtarg, unsigned long curactual, ++ unsigned long newtarg), ++ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg), ++ ++ TP_STRUCT__entry( ++ __field(unsigned long, cpu_id ) ++ __field(unsigned long, load ) ++ __field(unsigned long, curtarg ) ++ __field(unsigned long, curactual ) ++ __field(unsigned long, newtarg ) ++ ), ++ ++ TP_fast_assign( ++ __entry->cpu_id = cpu_id; ++ __entry->load = load; ++ __entry->curtarg = curtarg; ++ __entry->curactual = curactual; ++ __entry->newtarg = newtarg; ++ ), ++ ++ TP_printk("cpu=%lu load=%lu cur=%lu actual=%lu targ=%lu", ++ __entry->cpu_id, __entry->load, __entry->curtarg, ++ __entry->curactual, __entry->newtarg) ++); ++ ++DEFINE_EVENT(loadeval, cpufreq_interactive_target, ++ TP_PROTO(unsigned long cpu_id, unsigned long load, ++ unsigned long curtarg, unsigned long curactual, ++ unsigned long newtarg), ++ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) ++); ++ ++DEFINE_EVENT(loadeval, cpufreq_interactive_already, ++ TP_PROTO(unsigned long cpu_id, unsigned long load, ++ unsigned long curtarg, unsigned long curactual, ++ unsigned long newtarg), ++ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) ++); ++ ++DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, ++ TP_PROTO(unsigned long cpu_id, unsigned long load, ++ unsigned long curtarg, unsigned long curactual, ++ unsigned long newtarg), ++ TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) ++); ++ ++TRACE_EVENT(cpufreq_interactive_boost, ++ TP_PROTO(const char *s), ++ TP_ARGS(s), ++ TP_STRUCT__entry( ++ __string(s, s) ++ ), ++ TP_fast_assign( ++ __assign_str(s, s); ++ ), ++ TP_printk("%s", __get_str(s)) ++); ++ ++TRACE_EVENT(cpufreq_interactive_unboost, ++ TP_PROTO(const char *s), ++ TP_ARGS(s), ++ TP_STRUCT__entry( ++ __string(s, s) ++ ), ++ TP_fast_assign( ++ __assign_str(s, s); ++ ), ++ TP_printk("%s", __get_str(s)) ++); ++ ++#endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ ++ ++/* This part must be outside protection */ ++#include <trace/define_trace.h> +diff --git a/include/trace/events/gpu.h b/include/trace/events/gpu.h +new file mode 100644 +index 0000000..7e15cdf +--- /dev/null ++++ b/include/trace/events/gpu.h +@@ -0,0 +1,143 @@ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM gpu ++ ++#if !defined(_TRACE_GPU_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _TRACE_GPU_H ++ ++#include <linux/tracepoint.h> ++#include <linux/time.h> ++ ++#define show_secs_from_ns(ns) \ ++ ({ \ ++ u64 t = ns + (NSEC_PER_USEC / 2); \ ++ do_div(t, NSEC_PER_SEC); \ ++ t; \ ++ }) ++ ++#define show_usecs_from_ns(ns) \ ++ ({ \ ++ u64 t = ns + (NSEC_PER_USEC / 2) ; \ ++ u32 rem; \ ++ do_div(t, NSEC_PER_USEC); \ ++ rem = do_div(t, USEC_PER_SEC); \ ++ }) ++ ++/* ++ * The gpu_sched_switch event indicates that a switch from one GPU context to ++ * another occurred on one of the GPU hardware blocks. ++ * ++ * The gpu_name argument identifies the GPU hardware block. Each independently ++ * scheduled GPU hardware block should have a different name. This may be used ++ * in different ways for different GPUs. For example, if a GPU includes ++ * multiple processing cores it may use names "GPU 0", "GPU 1", etc. If a GPU ++ * includes a separately scheduled 2D and 3D hardware block, it might use the ++ * names "2D" and "3D". ++ * ++ * The timestamp argument is the timestamp at which the switch occurred on the ++ * GPU. These timestamps are in units of nanoseconds and must use ++ * approximately the same time as sched_clock, though they need not come from ++ * any CPU clock. The timestamps for a single hardware block must be ++ * monotonically nondecreasing. This means that if a variable compensation ++ * offset is used to translate from some other clock to the sched_clock, then ++ * care must be taken when increasing that offset, and doing so may result in ++ * multiple events with the same timestamp. ++ * ++ * The next_ctx_id argument identifies the next context that was running on ++ * the GPU hardware block. A value of 0 indicates that the hardware block ++ * will be idle. ++ * ++ * The next_prio argument indicates the priority of the next context at the ++ * time of the event. The exact numeric values may mean different things for ++ * different GPUs, but they should follow the rule that lower values indicate a ++ * higher priority. ++ * ++ * The next_job_id argument identifies the batch of work that the GPU will be ++ * working on. This should correspond to a job_id that was previously traced ++ * as a gpu_job_enqueue event when the batch of work was created. ++ */ ++TRACE_EVENT(gpu_sched_switch, ++ ++ TP_PROTO(const char *gpu_name, u64 timestamp, ++ u32 next_ctx_id, s32 next_prio, u32 next_job_id), ++ ++ TP_ARGS(gpu_name, timestamp, next_ctx_id, next_prio, next_job_id), ++ ++ TP_STRUCT__entry( ++ __string( gpu_name, gpu_name ) ++ __field( u64, timestamp ) ++ __field( u32, next_ctx_id ) ++ __field( s32, next_prio ) ++ __field( u32, next_job_id ) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(gpu_name, gpu_name); ++ __entry->timestamp = timestamp; ++ __entry->next_ctx_id = next_ctx_id; ++ __entry->next_prio = next_prio; ++ __entry->next_job_id = next_job_id; ++ ), ++ ++ TP_printk("gpu_name=%s ts=%llu.%06lu next_ctx_id=%lu next_prio=%ld " ++ "next_job_id=%lu", ++ __get_str(gpu_name), ++ (unsigned long long)show_secs_from_ns(__entry->timestamp), ++ (unsigned long)show_usecs_from_ns(__entry->timestamp), ++ (unsigned long)__entry->next_ctx_id, ++ (long)__entry->next_prio, ++ (unsigned long)__entry->next_job_id) ++); ++ ++/* ++ * The gpu_job_enqueue event indicates that a batch of work has been queued up ++ * to be processed by the GPU. This event is not intended to indicate that ++ * the batch of work has been submitted to the GPU hardware, but rather that ++ * it has been submitted to the GPU kernel driver. ++ * ++ * This event should be traced on the thread that initiated the work being ++ * queued. For example, if a batch of work is submitted to the kernel by a ++ * userland thread, the event should be traced on that thread. ++ * ++ * The ctx_id field identifies the GPU context in which the batch of work ++ * being queued is to be run. ++ * ++ * The job_id field identifies the batch of work being queued within the given ++ * GPU context. The first batch of work submitted for a given GPU context ++ * should have a job_id of 0, and each subsequent batch of work should ++ * increment the job_id by 1. ++ * ++ * The type field identifies the type of the job being enqueued. The job ++ * types may be different for different GPU hardware. For example, a GPU may ++ * differentiate between "2D", "3D", and "compute" jobs. ++ */ ++TRACE_EVENT(gpu_job_enqueue, ++ ++ TP_PROTO(u32 ctx_id, u32 job_id, const char *type), ++ ++ TP_ARGS(ctx_id, job_id, type), ++ ++ TP_STRUCT__entry( ++ __field( u32, ctx_id ) ++ __field( u32, job_id ) ++ __string( type, type ) ++ ), ++ ++ TP_fast_assign( ++ __entry->ctx_id = ctx_id; ++ __entry->job_id = job_id; ++ __assign_str(type, type); ++ ), ++ ++ TP_printk("ctx_id=%lu job_id=%lu type=%s", ++ (unsigned long)__entry->ctx_id, ++ (unsigned long)__entry->job_id, ++ __get_str(type)) ++); ++ ++#undef show_secs_from_ns ++#undef show_usecs_from_ns ++ ++#endif /* _TRACE_GPU_H */ ++ ++/* This part must be outside protection */ ++#include <trace/define_trace.h> +diff --git a/include/trace/events/mmc.h b/include/trace/events/mmc.h +new file mode 100644 +index 0000000..82b368d +--- /dev/null ++++ b/include/trace/events/mmc.h +@@ -0,0 +1,91 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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. ++ * ++ */ ++ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM mmc ++ ++#if !defined(_TRACE_MMC_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _TRACE_MMC_H ++ ++#include <linux/tracepoint.h> ++#include <linux/mmc/mmc.h> ++#include <linux/mmc/core.h> ++ ++/* ++ * Unconditional logging of mmc block erase operations, ++ * including cmd, address, size ++ */ ++DECLARE_EVENT_CLASS(mmc_blk_erase_class, ++ TP_PROTO(unsigned int cmd, unsigned int addr, unsigned int size), ++ TP_ARGS(cmd, addr, size), ++ TP_STRUCT__entry( ++ __field(unsigned int, cmd) ++ __field(unsigned int, addr) ++ __field(unsigned int, size) ++ ), ++ TP_fast_assign( ++ __entry->cmd = cmd; ++ __entry->addr = addr; ++ __entry->size = size; ++ ), ++ TP_printk("cmd=%u,addr=0x%08x,size=0x%08x", ++ __entry->cmd, __entry->addr, __entry->size) ++); ++ ++DEFINE_EVENT(mmc_blk_erase_class, mmc_blk_erase_start, ++ TP_PROTO(unsigned int cmd, unsigned int addr, unsigned int size), ++ TP_ARGS(cmd, addr, size)); ++ ++DEFINE_EVENT(mmc_blk_erase_class, mmc_blk_erase_end, ++ TP_PROTO(unsigned int cmd, unsigned int addr, unsigned int size), ++ TP_ARGS(cmd, addr, size)); ++ ++/* ++ * Logging of start of read or write mmc block operation, ++ * including cmd, address, size ++ */ ++DECLARE_EVENT_CLASS(mmc_blk_rw_class, ++ TP_PROTO(unsigned int cmd, unsigned int addr, struct mmc_data *data), ++ TP_ARGS(cmd, addr, data), ++ TP_STRUCT__entry( ++ __field(unsigned int, cmd) ++ __field(unsigned int, addr) ++ __field(unsigned int, size) ++ ), ++ TP_fast_assign( ++ __entry->cmd = cmd; ++ __entry->addr = addr; ++ __entry->size = data->blocks; ++ ), ++ TP_printk("cmd=%u,addr=0x%08x,size=0x%08x", ++ __entry->cmd, __entry->addr, __entry->size) ++); ++ ++DEFINE_EVENT_CONDITION(mmc_blk_rw_class, mmc_blk_rw_start, ++ TP_PROTO(unsigned int cmd, unsigned int addr, struct mmc_data *data), ++ TP_ARGS(cmd, addr, data), ++ TP_CONDITION(((cmd == MMC_READ_MULTIPLE_BLOCK) || ++ (cmd == MMC_WRITE_MULTIPLE_BLOCK)) && ++ data)); ++ ++DEFINE_EVENT_CONDITION(mmc_blk_rw_class, mmc_blk_rw_end, ++ TP_PROTO(unsigned int cmd, unsigned int addr, struct mmc_data *data), ++ TP_ARGS(cmd, addr, data), ++ TP_CONDITION(((cmd == MMC_READ_MULTIPLE_BLOCK) || ++ (cmd == MMC_WRITE_MULTIPLE_BLOCK)) && ++ data)); ++#endif /* _TRACE_MMC_H */ ++ ++/* This part must be outside protection */ ++#include <trace/define_trace.h> +diff --git a/include/trace/events/power.h b/include/trace/events/power.h +index d19840b..2740212 100644 +--- a/include/trace/events/power.h ++++ b/include/trace/events/power.h +@@ -264,6 +264,25 @@ DEFINE_EVENT(clock, clock_set_rate, + TP_ARGS(name, state, cpu_id) + ); + ++TRACE_EVENT(clock_set_parent, ++ ++ TP_PROTO(const char *name, const char *parent_name), ++ ++ TP_ARGS(name, parent_name), ++ ++ TP_STRUCT__entry( ++ __string( name, name ) ++ __string( parent_name, parent_name ) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(name, name); ++ __assign_str(parent_name, parent_name); ++ ), ++ ++ TP_printk("%s parent=%s", __get_str(name), __get_str(parent_name)) ++); ++ + /* + * The power domain events are used for power domains transitions + */ +diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h +index a7d67bc..41221f8 100644 +--- a/include/trace/events/sched.h ++++ b/include/trace/events/sched.h +@@ -443,6 +443,156 @@ TRACE_EVENT(sched_process_hang, + ); + #endif /* CONFIG_DETECT_HUNG_TASK */ + ++ ++/* ++ * Tracepoint for showing tracked load contribution. ++ */ ++TRACE_EVENT(sched_task_load_contrib, ++ ++ TP_PROTO(struct task_struct *tsk, unsigned long load_contrib), ++ ++ TP_ARGS(tsk, load_contrib), ++ ++ TP_STRUCT__entry( ++ __array(char, comm, TASK_COMM_LEN) ++ __field(pid_t, pid) ++ __field(unsigned long, load_contrib) ++ ), ++ ++ TP_fast_assign( ++ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); ++ __entry->pid = tsk->pid; ++ __entry->load_contrib = load_contrib; ++ ), ++ ++ TP_printk("comm=%s pid=%d load_contrib=%lu", ++ __entry->comm, __entry->pid, ++ __entry->load_contrib) ++); ++ ++/* ++ * Tracepoint for showing tracked task runnable ratio [0..1023]. ++ */ ++TRACE_EVENT(sched_task_runnable_ratio, ++ ++ TP_PROTO(struct task_struct *tsk, unsigned long ratio), ++ ++ TP_ARGS(tsk, ratio), ++ ++ TP_STRUCT__entry( ++ __array(char, comm, TASK_COMM_LEN) ++ __field(pid_t, pid) ++ __field(unsigned long, ratio) ++ ), ++ ++ TP_fast_assign( ++ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); ++ __entry->pid = tsk->pid; ++ __entry->ratio = ratio; ++ ), ++ ++ TP_printk("comm=%s pid=%d ratio=%lu", ++ __entry->comm, __entry->pid, ++ __entry->ratio) ++); ++ ++/* ++ * Tracepoint for showing tracked rq runnable ratio [0..1023]. ++ */ ++TRACE_EVENT(sched_rq_runnable_ratio, ++ ++ TP_PROTO(int cpu, unsigned long ratio), ++ ++ TP_ARGS(cpu, ratio), ++ ++ TP_STRUCT__entry( ++ __field(int, cpu) ++ __field(unsigned long, ratio) ++ ), ++ ++ TP_fast_assign( ++ __entry->cpu = cpu; ++ __entry->ratio = ratio; ++ ), ++ ++ TP_printk("cpu=%d ratio=%lu", ++ __entry->cpu, ++ __entry->ratio) ++); ++ ++/* ++ * Tracepoint for showing tracked rq runnable load. ++ */ ++TRACE_EVENT(sched_rq_runnable_load, ++ ++ TP_PROTO(int cpu, u64 load), ++ ++ TP_ARGS(cpu, load), ++ ++ TP_STRUCT__entry( ++ __field(int, cpu) ++ __field(u64, load) ++ ), ++ ++ TP_fast_assign( ++ __entry->cpu = cpu; ++ __entry->load = load; ++ ), ++ ++ TP_printk("cpu=%d load=%llu", ++ __entry->cpu, ++ __entry->load) ++); ++ ++TRACE_EVENT(sched_rq_nr_running, ++ ++ TP_PROTO(int cpu, unsigned int nr_running, int nr_iowait), ++ ++ TP_ARGS(cpu, nr_running, nr_iowait), ++ ++ TP_STRUCT__entry( ++ __field(int, cpu) ++ __field(unsigned int, nr_running) ++ __field(int, nr_iowait) ++ ), ++ ++ TP_fast_assign( ++ __entry->cpu = cpu; ++ __entry->nr_running = nr_running; ++ __entry->nr_iowait = nr_iowait; ++ ), ++ ++ TP_printk("cpu=%d nr_running=%u nr_iowait=%d", ++ __entry->cpu, ++ __entry->nr_running, __entry->nr_iowait) ++); ++ ++/* ++ * Tracepoint for showing tracked task cpu usage ratio [0..1023]. ++ */ ++TRACE_EVENT(sched_task_usage_ratio, ++ ++ TP_PROTO(struct task_struct *tsk, unsigned long ratio), ++ ++ TP_ARGS(tsk, ratio), ++ ++ TP_STRUCT__entry( ++ __array(char, comm, TASK_COMM_LEN) ++ __field(pid_t, pid) ++ __field(unsigned long, ratio) ++ ), ++ ++ TP_fast_assign( ++ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); ++ __entry->pid = tsk->pid; ++ __entry->ratio = ratio; ++ ), ++ ++ TP_printk("comm=%s pid=%d ratio=%lu", ++ __entry->comm, __entry->pid, ++ __entry->ratio) ++); ++ + DECLARE_EVENT_CLASS(sched_move_task_template, + + TP_PROTO(struct task_struct *tsk, int src_cpu, int dst_cpu), +@@ -550,6 +700,132 @@ TRACE_EVENT(sched_wake_idle_without_ipi, + + TP_printk("cpu=%d", __entry->cpu) + ); ++ ++/* ++ * Tracepoint for HMP (CONFIG_SCHED_HMP) task migrations, ++ * marking the forced transition of runnable or running tasks. ++ */ ++TRACE_EVENT(sched_hmp_migrate_force_running, ++ ++ TP_PROTO(struct task_struct *tsk, int running), ++ ++ TP_ARGS(tsk, running), ++ ++ TP_STRUCT__entry( ++ __array(char, comm, TASK_COMM_LEN) ++ __field(int, running) ++ ), ++ ++ TP_fast_assign( ++ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); ++ __entry->running = running; ++ ), ++ ++ TP_printk("running=%d comm=%s", ++ __entry->running, __entry->comm) ++); ++ ++/* ++ * Tracepoint for HMP (CONFIG_SCHED_HMP) task migrations, ++ * marking the forced transition of runnable or running ++ * tasks when a task is about to go idle. ++ */ ++TRACE_EVENT(sched_hmp_migrate_idle_running, ++ ++ TP_PROTO(struct task_struct *tsk, int running), ++ ++ TP_ARGS(tsk, running), ++ ++ TP_STRUCT__entry( ++ __array(char, comm, TASK_COMM_LEN) ++ __field(int, running) ++ ), ++ ++ TP_fast_assign( ++ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); ++ __entry->running = running; ++ ), ++ ++ TP_printk("running=%d comm=%s", ++ __entry->running, __entry->comm) ++); ++ ++/* ++ * Tracepoint for HMP (CONFIG_SCHED_HMP) task migrations. ++ */ ++#define HMP_MIGRATE_WAKEUP 0 ++#define HMP_MIGRATE_FORCE 1 ++#define HMP_MIGRATE_OFFLOAD 2 ++#define HMP_MIGRATE_IDLE_PULL 3 ++TRACE_EVENT(sched_hmp_migrate, ++ ++ TP_PROTO(struct task_struct *tsk, int dest, int force), ++ ++ TP_ARGS(tsk, dest, force), ++ ++ TP_STRUCT__entry( ++ __array(char, comm, TASK_COMM_LEN) ++ __field(pid_t, pid) ++ __field(int, dest) ++ __field(int, force) ++ ), ++ ++ TP_fast_assign( ++ memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); ++ __entry->pid = tsk->pid; ++ __entry->dest = dest; ++ __entry->force = force; ++ ), ++ ++ TP_printk("comm=%s pid=%d dest=%d force=%d", ++ __entry->comm, __entry->pid, ++ __entry->dest, __entry->force) ++); ++ ++TRACE_EVENT(sched_hmp_offload_abort, ++ ++ TP_PROTO(int cpu, int data, char *label), ++ ++ TP_ARGS(cpu, data, label), ++ ++ TP_STRUCT__entry( ++ __array(char, label, 64) ++ __field(int, cpu) ++ __field(int, data) ++ ), ++ ++ TP_fast_assign( ++ strncpy(__entry->label, label, 64); ++ __entry->cpu = cpu; ++ __entry->data = data; ++ ), ++ ++ TP_printk("cpu=%d data=%d label=%63s", ++ __entry->cpu, __entry->data, ++ __entry->label) ++); ++ ++TRACE_EVENT(sched_hmp_offload_succeed, ++ ++ TP_PROTO(int cpu, int dest_cpu), ++ ++ TP_ARGS(cpu, dest_cpu), ++ ++ TP_STRUCT__entry( ++ __field(int, cpu) ++ __field(int, dest_cpu) ++ ), ++ ++ TP_fast_assign( ++ __entry->cpu = cpu; ++ __entry->dest_cpu = dest_cpu; ++ ), ++ ++ TP_printk("cpu=%d dest=%d", ++ __entry->cpu, ++ __entry->dest_cpu) ++); ++ + #endif /* _TRACE_SCHED_H */ + + /* This part must be outside protection */ +diff --git a/include/trace/events/thermal.h b/include/trace/events/thermal.h +index 0f4f95d..8b1f806 100644 +--- a/include/trace/events/thermal.h ++++ b/include/trace/events/thermal.h +@@ -77,6 +77,64 @@ TRACE_EVENT(thermal_zone_trip, + __entry->trip_type) + ); + ++TRACE_EVENT(thermal_power_cpu_get_power, ++ TP_PROTO(const struct cpumask *cpus, unsigned long freq, u32 *load, ++ size_t load_len, u32 dynamic_power, u32 static_power), ++ ++ TP_ARGS(cpus, freq, load, load_len, dynamic_power, static_power), ++ ++ TP_STRUCT__entry( ++ __bitmask(cpumask, num_possible_cpus()) ++ __field(unsigned long, freq ) ++ __dynamic_array(u32, load, load_len) ++ __field(size_t, load_len ) ++ __field(u32, dynamic_power ) ++ __field(u32, static_power ) ++ ), ++ ++ TP_fast_assign( ++ __assign_bitmask(cpumask, cpumask_bits(cpus), ++ num_possible_cpus()); ++ __entry->freq = freq; ++ memcpy(__get_dynamic_array(load), load, ++ load_len * sizeof(*load)); ++ __entry->load_len = load_len; ++ __entry->dynamic_power = dynamic_power; ++ __entry->static_power = static_power; ++ ), ++ ++ TP_printk("cpus=%s freq=%lu load={%s} dynamic_power=%d static_power=%d", ++ __get_bitmask(cpumask), __entry->freq, ++ __print_array(__get_dynamic_array(load), __entry->load_len, 4), ++ __entry->dynamic_power, __entry->static_power) ++); ++ ++TRACE_EVENT(thermal_power_cpu_limit, ++ TP_PROTO(const struct cpumask *cpus, unsigned int freq, ++ unsigned long cdev_state, u32 power), ++ ++ TP_ARGS(cpus, freq, cdev_state, power), ++ ++ TP_STRUCT__entry( ++ __bitmask(cpumask, num_possible_cpus()) ++ __field(unsigned int, freq ) ++ __field(unsigned long, cdev_state) ++ __field(u32, power ) ++ ), ++ ++ TP_fast_assign( ++ __assign_bitmask(cpumask, cpumask_bits(cpus), ++ num_possible_cpus()); ++ __entry->freq = freq; ++ __entry->cdev_state = cdev_state; ++ __entry->power = power; ++ ), ++ ++ TP_printk("cpus=%s freq=%u cdev_state=%lu power=%u", ++ __get_bitmask(cpumask), __entry->freq, __entry->cdev_state, ++ __entry->power) ++); ++ + #endif /* _TRACE_THERMAL_H */ + + /* This part must be outside protection */ +diff --git a/include/trace/events/thermal_power_allocator.h b/include/trace/events/thermal_power_allocator.h +new file mode 100644 +index 0000000..12e1321 +--- /dev/null ++++ b/include/trace/events/thermal_power_allocator.h +@@ -0,0 +1,87 @@ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM thermal_power_allocator ++ ++#if !defined(_TRACE_THERMAL_POWER_ALLOCATOR_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _TRACE_THERMAL_POWER_ALLOCATOR_H ++ ++#include <linux/tracepoint.h> ++ ++TRACE_EVENT(thermal_power_allocator, ++ TP_PROTO(struct thermal_zone_device *tz, u32 *req_power, ++ u32 total_req_power, u32 *granted_power, ++ u32 total_granted_power, size_t num_actors, ++ u32 power_range, u32 max_allocatable_power, ++ unsigned long current_temp, s32 delta_temp), ++ TP_ARGS(tz, req_power, total_req_power, granted_power, ++ total_granted_power, num_actors, power_range, ++ max_allocatable_power, current_temp, delta_temp), ++ TP_STRUCT__entry( ++ __field(int, tz_id ) ++ __dynamic_array(u32, req_power, num_actors ) ++ __field(u32, total_req_power ) ++ __dynamic_array(u32, granted_power, num_actors) ++ __field(u32, total_granted_power ) ++ __field(size_t, num_actors ) ++ __field(u32, power_range ) ++ __field(u32, max_allocatable_power ) ++ __field(unsigned long, current_temp ) ++ __field(s32, delta_temp ) ++ ), ++ TP_fast_assign( ++ __entry->tz_id = tz->id; ++ memcpy(__get_dynamic_array(req_power), req_power, ++ num_actors * sizeof(*req_power)); ++ __entry->total_req_power = total_req_power; ++ memcpy(__get_dynamic_array(granted_power), granted_power, ++ num_actors * sizeof(*granted_power)); ++ __entry->total_granted_power = total_granted_power; ++ __entry->num_actors = num_actors; ++ __entry->power_range = power_range; ++ __entry->max_allocatable_power = max_allocatable_power; ++ __entry->current_temp = current_temp; ++ __entry->delta_temp = delta_temp; ++ ), ++ ++ TP_printk("thermal_zone_id=%d req_power={%s} total_req_power=%u granted_power={%s} total_granted_power=%u power_range=%u max_allocatable_power=%u current_temperature=%lu delta_temperature=%d", ++ __entry->tz_id, ++ __print_array(__get_dynamic_array(req_power), ++ __entry->num_actors, 4), ++ __entry->total_req_power, ++ __print_array(__get_dynamic_array(granted_power), ++ __entry->num_actors, 4), ++ __entry->total_granted_power, __entry->power_range, ++ __entry->max_allocatable_power, __entry->current_temp, ++ __entry->delta_temp) ++); ++ ++TRACE_EVENT(thermal_power_allocator_pid, ++ TP_PROTO(struct thermal_zone_device *tz, s32 err, s32 err_integral, ++ s64 p, s64 i, s64 d, s32 output), ++ TP_ARGS(tz, err, err_integral, p, i, d, output), ++ TP_STRUCT__entry( ++ __field(int, tz_id ) ++ __field(s32, err ) ++ __field(s32, err_integral) ++ __field(s64, p ) ++ __field(s64, i ) ++ __field(s64, d ) ++ __field(s32, output ) ++ ), ++ TP_fast_assign( ++ __entry->tz_id = tz->id; ++ __entry->err = err; ++ __entry->err_integral = err_integral; ++ __entry->p = p; ++ __entry->i = i; ++ __entry->d = d; ++ __entry->output = output; ++ ), ++ ++ TP_printk("thermal_zone_id=%d err=%d err_integral=%d p=%lld i=%lld d=%lld output=%d", ++ __entry->tz_id, __entry->err, __entry->err_integral, ++ __entry->p, __entry->i, __entry->d, __entry->output) ++); ++#endif /* _TRACE_THERMAL_POWER_ALLOCATOR_H */ ++ ++/* This part must be outside protection */ ++#include <trace/define_trace.h> +diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h +index 26b4f2e..f5d9371 100644 +--- a/include/trace/ftrace.h ++++ b/include/trace/ftrace.h +@@ -263,6 +263,14 @@ + #undef __print_hex + #define __print_hex(buf, buf_len) ftrace_print_hex_seq(p, buf, buf_len) + ++#undef __print_array ++#define __print_array(array, count, el_size) \ ++ ({ \ ++ BUILD_BUG_ON(el_size != 1 && el_size != 2 && \ ++ el_size != 4 && el_size != 8); \ ++ ftrace_print_array_seq(p, array, count, el_size); \ ++ }) ++ + #undef DECLARE_EVENT_CLASS + #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ + static notrace enum print_line_t \ +@@ -676,6 +684,7 @@ static inline void ftrace_test_probe_##call(void) \ + #undef __get_dynamic_array_len + #undef __get_str + #undef __get_bitmask ++#undef __print_array + + #undef TP_printk + #define TP_printk(fmt, args...) "\"" fmt "\", " __stringify(args) +diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild +index 8523f9b..fe47395 100644 +--- a/include/uapi/linux/Kbuild ++++ b/include/uapi/linux/Kbuild +@@ -383,6 +383,7 @@ header-y += tcp.h + header-y += tcp_metrics.h + header-y += telephony.h + header-y += termios.h ++header-y += thermal.h + header-y += time.h + header-y += times.h + header-y += timex.h +diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h +index ea9bf25..71e1d0e 100644 +--- a/include/uapi/linux/elf.h ++++ b/include/uapi/linux/elf.h +@@ -397,6 +397,7 @@ typedef struct elf64_shdr { + #define NT_ARM_TLS 0x401 /* ARM TLS register */ + #define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ + #define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ ++#define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ + #define NT_METAG_CBUF 0x500 /* Metag catch buffer registers */ + #define NT_METAG_RPIPE 0x501 /* Metag read pipeline state */ + #define NT_METAG_TLS 0x502 /* Metag TLS pointer */ +diff --git a/include/uapi/linux/falloc.h b/include/uapi/linux/falloc.h +index d1197ae..3e445a7 100644 +--- a/include/uapi/linux/falloc.h ++++ b/include/uapi/linux/falloc.h +@@ -41,4 +41,21 @@ + */ + #define FALLOC_FL_ZERO_RANGE 0x10 + ++/* ++ * FALLOC_FL_INSERT_RANGE is use to insert space within the file size without ++ * overwriting any existing data. The contents of the file beyond offset are ++ * shifted towards right by len bytes to create a hole. As such, this ++ * operation will increase the size of the file by len bytes. ++ * ++ * Different filesystems may implement different limitations on the granularity ++ * of the operation. Most will limit operations to filesystem block size ++ * boundaries, but this boundary may be larger or smaller depending on ++ * the filesystem and/or the configuration of the filesystem or file. ++ * ++ * Attempting to insert space using this flag at OR beyond the end of ++ * the file is considered an illegal operation - just use ftruncate(2) or ++ * fallocate(2) with mode 0 for such type of operations. ++ */ ++#define FALLOC_FL_INSERT_RANGE 0x20 ++ + #endif /* _UAPI_FALLOC_H_ */ +diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h +index 2b82d7e..743e300 100644 +--- a/include/uapi/linux/fib_rules.h ++++ b/include/uapi/linux/fib_rules.h +@@ -49,6 +49,8 @@ enum { + FRA_TABLE, /* Extended table id */ + FRA_FWMASK, /* mask for netfilter mark */ + FRA_OIFNAME, ++ FRA_UID_START, /* UID range */ ++ FRA_UID_END, + __FRA_MAX + }; + +diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h +index 3735fa0..48e59f9 100644 +--- a/include/uapi/linux/fs.h ++++ b/include/uapi/linux/fs.h +@@ -158,6 +158,8 @@ struct inodes_stat_t { + #define FITHAW _IOWR('X', 120, int) /* Thaw */ + #define FITRIM _IOWR('X', 121, struct fstrim_range) /* Trim */ + ++#define FIDTRIM _IOWR('f', 128, struct fstrim_range) /* Deep discard trim */ ++ + #define FS_IOC_GETFLAGS _IOR('f', 1, long) + #define FS_IOC_SETFLAGS _IOW('f', 2, long) + #define FS_IOC_GETVERSION _IOR('v', 1, long) +diff --git a/include/uapi/linux/i2c-dev.h b/include/uapi/linux/i2c-dev.h +index 3f31155..f864909 100644 +--- a/include/uapi/linux/i2c-dev.h ++++ b/include/uapi/linux/i2c-dev.h +@@ -50,7 +50,9 @@ + + #define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */ + #define I2C_SMBUS 0x0720 /* SMBus transfer */ +- ++#define I2C_16BIT_REG 0x0709 /* 16BIT REG WIDTH */ ++#define I2C_16BIT_DATA 0x070a /* 16BIT DATA WIDTH */ ++#define I2C_DMA 0x070b /* DMA mode */ + + /* This is the structure as used in the I2C_SMBUS ioctl call */ + struct i2c_smbus_ioctl_data { +diff --git a/include/uapi/linux/i2c.h b/include/uapi/linux/i2c.h +index 0e949cb..c48d3d2 100644 +--- a/include/uapi/linux/i2c.h ++++ b/include/uapi/linux/i2c.h +@@ -76,6 +76,9 @@ struct i2c_msg { + #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ + #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ + #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ ++#define I2C_M_16BIT_REG 0x0002 /* indicate reg bit-width is 16bit */ ++#define I2C_M_16BIT_DATA 0x0008 /* indicate data bit-width is 16bit */ ++#define I2C_M_DMA 0x0004 /* indicate use dma mode */ + __u16 len; /* msg length */ + __u8 *buf; /* pointer to msg data */ + }; +diff --git a/include/uapi/linux/if_pppolac.h b/include/uapi/linux/if_pppolac.h +new file mode 100644 +index 0000000..b7eb815 +--- /dev/null ++++ b/include/uapi/linux/if_pppolac.h +@@ -0,0 +1,33 @@ ++/* include/uapi/linux/if_pppolac.h ++ * ++ * Header for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661) ++ * ++ * Copyright (C) 2009 Google, Inc. ++ * Author: Chia-chi Yeh <chiachi@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _UAPI_LINUX_IF_PPPOLAC_H ++#define _UAPI_LINUX_IF_PPPOLAC_H ++ ++#include <linux/socket.h> ++#include <linux/types.h> ++ ++struct sockaddr_pppolac { ++ sa_family_t sa_family; /* AF_PPPOX */ ++ unsigned int sa_protocol; /* PX_PROTO_OLAC */ ++ int udp_socket; ++ struct __attribute__((packed)) { ++ __u16 tunnel, session; ++ } local, remote; ++} __attribute__((packed)); ++ ++#endif /* _UAPI_LINUX_IF_PPPOLAC_H */ +diff --git a/include/uapi/linux/if_pppopns.h b/include/uapi/linux/if_pppopns.h +new file mode 100644 +index 0000000..a392b52 +--- /dev/null ++++ b/include/uapi/linux/if_pppopns.h +@@ -0,0 +1,32 @@ ++/* include/uapi/linux/if_pppopns.h ++ * ++ * Header for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) ++ * ++ * Copyright (C) 2009 Google, Inc. ++ * Author: Chia-chi Yeh <chiachi@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _UAPI_LINUX_IF_PPPOPNS_H ++#define _UAPI_LINUX_IF_PPPOPNS_H ++ ++#include <linux/socket.h> ++#include <linux/types.h> ++ ++struct sockaddr_pppopns { ++ sa_family_t sa_family; /* AF_PPPOX */ ++ unsigned int sa_protocol; /* PX_PROTO_OPNS */ ++ int tcp_socket; ++ __u16 local; ++ __u16 remote; ++} __attribute__((packed)); ++ ++#endif /* _UAPI_LINUX_IF_PPPOPNS_H */ +diff --git a/include/uapi/linux/if_pppox.h b/include/uapi/linux/if_pppox.h +index e128769..5861d45 100644 +--- a/include/uapi/linux/if_pppox.h ++++ b/include/uapi/linux/if_pppox.h +@@ -23,6 +23,8 @@ + #include <linux/socket.h> + #include <linux/if_ether.h> + #include <linux/if_pppol2tp.h> ++#include <linux/if_pppolac.h> ++#include <linux/if_pppopns.h> + + /* For user-space programs to pick up these definitions + * which they wouldn't get otherwise without defining __KERNEL__ +@@ -56,7 +58,9 @@ struct pptp_addr { + #define PX_PROTO_OE 0 /* Currently just PPPoE */ + #define PX_PROTO_OL2TP 1 /* Now L2TP also */ + #define PX_PROTO_PPTP 2 +-#define PX_MAX_PROTO 3 ++#define PX_PROTO_OLAC 3 ++#define PX_PROTO_OPNS 4 ++#define PX_MAX_PROTO 5 + + struct sockaddr_pppox { + __kernel_sa_family_t sa_family; /* address family, AF_PPPOX */ +diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h +index a1d7e93..cc75b7b 100644 +--- a/include/uapi/linux/input.h ++++ b/include/uapi/linux/input.h +@@ -152,7 +152,12 @@ struct input_keymap_entry { + #define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */ + + #define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ +-#define EVIOCREVOKE _IOW('E', 0x91, int) /* Revoke device access */ ++ ++/* HACK: disable conflicting EVIOCREVOKE until Android userspace stops using EVIOCSSUSPENDBLOCK */ ++/*#define EVIOCREVOKE _IOW('E', 0x91, int)*/ /* Revoke device access */ ++ ++#define EVIOCGSUSPENDBLOCK _IOR('E', 0x91, int) /* get suspend block enable */ ++#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int) /* set suspend block enable */ + + #define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */ + +diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h +index efa2666..187d9f6 100644 +--- a/include/uapi/linux/ipv6.h ++++ b/include/uapi/linux/ipv6.h +@@ -160,10 +160,12 @@ enum { + DEVCONF_ACCEPT_DAD, + DEVCONF_FORCE_TLLAO, + DEVCONF_NDISC_NOTIFY, ++ DEVCONF_ACCEPT_RA_RT_TABLE, + DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL, + DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL, + DEVCONF_SUPPRESS_FRAG_NDISC, + DEVCONF_ACCEPT_RA_FROM_LOCAL, ++ DEVCONF_USE_OPTIMISTIC, + DEVCONF_MAX + }; + +diff --git a/include/uapi/linux/keychord.h b/include/uapi/linux/keychord.h +new file mode 100644 +index 0000000..ea7cf4d +--- /dev/null ++++ b/include/uapi/linux/keychord.h +@@ -0,0 +1,52 @@ ++/* ++ * Key chord input driver ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++*/ ++ ++#ifndef _UAPI_LINUX_KEYCHORD_H_ ++#define _UAPI_LINUX_KEYCHORD_H_ ++ ++#include <linux/input.h> ++ ++#define KEYCHORD_VERSION 1 ++ ++/* ++ * One or more input_keychord structs are written to /dev/keychord ++ * at once to specify the list of keychords to monitor. ++ * Reading /dev/keychord returns the id of a keychord when the ++ * keychord combination is pressed. A keychord is signalled when ++ * all of the keys in the keycode list are in the pressed state. ++ * The order in which the keys are pressed does not matter. ++ * The keychord will not be signalled if keys not in the keycode ++ * list are pressed. ++ * Keychords will not be signalled on key release events. ++ */ ++struct input_keychord { ++ /* should be KEYCHORD_VERSION */ ++ __u16 version; ++ /* ++ * client specified ID, returned from read() ++ * when this keychord is pressed. ++ */ ++ __u16 id; ++ ++ /* number of keycodes in this keychord */ ++ __u16 count; ++ ++ /* variable length array of keycodes */ ++ __u16 keycodes[]; ++}; ++ ++#endif /* _UAPI_LINUX_KEYCHORD_H_ */ +diff --git a/include/uapi/linux/msdos_fs.h b/include/uapi/linux/msdos_fs.h +index e284ff9..335004b 100644 +--- a/include/uapi/linux/msdos_fs.h ++++ b/include/uapi/linux/msdos_fs.h +@@ -96,6 +96,22 @@ struct __fat_dirent { + char d_name[256]; /* We must not include limits.h! */ + }; + ++struct fat_direntall { ++ unsigned long d_ino; ++ unsigned long d_off; ++ unsigned char d_type; ++ u64 d_size; ++ char d_createtime[8]; ++ unsigned short d_reclen; ++ char d_name[1]; ++}; ++ ++struct fat_direntall_buf { ++ int d_count; ++ int d_usecount; ++ struct fat_direntall direntall; ++}; ++ + /* + * ioctl commands + */ +@@ -106,6 +122,7 @@ struct __fat_dirent { + #define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, __u32) + /*Android kernel has used 0x12, so we use 0x13*/ + #define FAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x13, __u32) ++#define VFAT_IOCTL_READDIR_ALL _IOR('r', 0x14, struct fat_direntall_buf) + + struct fat_boot_sector { + __u8 ignored[3]; /* Boot strap short or near jump */ +diff --git a/include/uapi/linux/netfilter/xt_IDLETIMER.h b/include/uapi/linux/netfilter/xt_IDLETIMER.h +index 208ae93..faaa28b 100644 +--- a/include/uapi/linux/netfilter/xt_IDLETIMER.h ++++ b/include/uapi/linux/netfilter/xt_IDLETIMER.h +@@ -4,6 +4,7 @@ + * Header file for Xtables timer target module. + * + * Copyright (C) 2004, 2010 Nokia Corporation ++ * + * Written by Timo Teras <ext-timo.teras@nokia.com> + * + * Converted to x_tables and forward-ported to 2.6.34 +@@ -32,12 +33,19 @@ + #include <linux/types.h> + + #define MAX_IDLETIMER_LABEL_SIZE 28 ++#define NLMSG_MAX_SIZE 64 ++ ++#define NL_EVENT_TYPE_INACTIVE 0 ++#define NL_EVENT_TYPE_ACTIVE 1 + + struct idletimer_tg_info { + __u32 timeout; + + char label[MAX_IDLETIMER_LABEL_SIZE]; + ++ /* Use netlink messages for notification in addition to sysfs */ ++ __u8 send_nl_msg; ++ + /* for kernel module internal use only */ + struct idletimer_tg *timer __attribute__((aligned(8))); + }; +diff --git a/include/uapi/linux/netfilter/xt_dscp.h b/include/uapi/linux/netfilter/xt_dscp.h +index 15f8932..648e0b3 100644 +--- a/include/uapi/linux/netfilter/xt_dscp.h ++++ b/include/uapi/linux/netfilter/xt_dscp.h +@@ -1,31 +1,26 @@ +-/* x_tables module for matching the IPv4/IPv6 DSCP field ++/* x_tables module for setting the IPv4/IPv6 DSCP field + * + * (C) 2002 Harald Welte <laforge@gnumonks.org> ++ * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh <mgm@paktronix.com> + * This software is distributed under GNU GPL v2, 1991 + * + * See RFC2474 for a description of the DSCP field within the IP Header. + * +- * xt_dscp.h,v 1.3 2002/08/05 19:00:21 laforge Exp ++ * xt_DSCP.h,v 1.7 2002/03/14 12:03:13 laforge Exp + */ +-#ifndef _XT_DSCP_H +-#define _XT_DSCP_H +- ++#ifndef _XT_DSCP_TARGET_H ++#define _XT_DSCP_TARGET_H ++#include <linux/netfilter/xt_dscp.h> + #include <linux/types.h> + +-#define XT_DSCP_MASK 0xfc /* 11111100 */ +-#define XT_DSCP_SHIFT 2 +-#define XT_DSCP_MAX 0x3f /* 00111111 */ +- +-/* match info */ +-struct xt_dscp_info { ++/* target info */ ++struct xt_DSCP_info { + __u8 dscp; +- __u8 invert; + }; + +-struct xt_tos_match_info { +- __u8 tos_mask; ++struct xt_tos_target_info { + __u8 tos_value; +- __u8 invert; ++ __u8 tos_mask; + }; + +-#endif /* _XT_DSCP_H */ ++#endif /* _XT_DSCP_TARGET_H */ +diff --git a/include/uapi/linux/netfilter/xt_rateest.h b/include/uapi/linux/netfilter/xt_rateest.h +index d40a619..6605e20 100644 +--- a/include/uapi/linux/netfilter/xt_rateest.h ++++ b/include/uapi/linux/netfilter/xt_rateest.h +@@ -1,37 +1,15 @@ +-#ifndef _XT_RATEEST_MATCH_H +-#define _XT_RATEEST_MATCH_H ++#ifndef _XT_RATEEST_TARGET_H ++#define _XT_RATEEST_TARGET_H + + #include <linux/types.h> + +-enum xt_rateest_match_flags { +- XT_RATEEST_MATCH_INVERT = 1<<0, +- XT_RATEEST_MATCH_ABS = 1<<1, +- XT_RATEEST_MATCH_REL = 1<<2, +- XT_RATEEST_MATCH_DELTA = 1<<3, +- XT_RATEEST_MATCH_BPS = 1<<4, +- XT_RATEEST_MATCH_PPS = 1<<5, +-}; +- +-enum xt_rateest_match_mode { +- XT_RATEEST_MATCH_NONE, +- XT_RATEEST_MATCH_EQ, +- XT_RATEEST_MATCH_LT, +- XT_RATEEST_MATCH_GT, +-}; +- +-struct xt_rateest_match_info { +- char name1[IFNAMSIZ]; +- char name2[IFNAMSIZ]; +- __u16 flags; +- __u16 mode; +- __u32 bps1; +- __u32 pps1; +- __u32 bps2; +- __u32 pps2; ++struct xt_rateest_target_info { ++ char name[IFNAMSIZ]; ++ __s8 interval; ++ __u8 ewma_log; + + /* Used internally by the kernel */ +- struct xt_rateest *est1 __attribute__((aligned(8))); +- struct xt_rateest *est2 __attribute__((aligned(8))); ++ struct xt_rateest *est __attribute__((aligned(8))); + }; + +-#endif /* _XT_RATEEST_MATCH_H */ ++#endif /* _XT_RATEEST_TARGET_H */ +diff --git a/include/uapi/linux/netfilter/xt_socket.h b/include/uapi/linux/netfilter/xt_socket.h +index 6315e2a..55076a3 100644 +--- a/include/uapi/linux/netfilter/xt_socket.h ++++ b/include/uapi/linux/netfilter/xt_socket.h +@@ -18,4 +18,9 @@ struct xt_socket_mtinfo2 { + }; + #define XT_SOCKET_FLAGS_V2 (XT_SOCKET_TRANSPARENT | XT_SOCKET_NOWILDCARD) + ++struct sock *xt_socket_get4_sk(const struct sk_buff *skb, ++ struct xt_action_param *par); ++struct sock *xt_socket_get6_sk(const struct sk_buff *skb, ++ struct xt_action_param *par); ++ + #endif /* _XT_SOCKET_H */ +diff --git a/include/uapi/linux/netfilter_ipv6/ip6t_hl.h b/include/uapi/linux/netfilter_ipv6/ip6t_hl.h +index 6e76dbc..ebd8ead 100644 +--- a/include/uapi/linux/netfilter_ipv6/ip6t_hl.h ++++ b/include/uapi/linux/netfilter_ipv6/ip6t_hl.h +@@ -1,6 +1,6 @@ +-/* ip6tables module for matching the Hop Limit value ++/* Hop Limit modification module for ip6tables + * Maciej Soltysiak <solt@dns.toxicfilms.tv> +- * Based on HW's ttl module */ ++ * Based on HW's TTL module */ + + #ifndef _IP6T_HL_H + #define _IP6T_HL_H +@@ -8,14 +8,14 @@ + #include <linux/types.h> + + enum { +- IP6T_HL_EQ = 0, /* equals */ +- IP6T_HL_NE, /* not equals */ +- IP6T_HL_LT, /* less than */ +- IP6T_HL_GT, /* greater than */ ++ IP6T_HL_SET = 0, ++ IP6T_HL_INC, ++ IP6T_HL_DEC + }; + ++#define IP6T_HL_MAXMODE IP6T_HL_DEC + +-struct ip6t_hl_info { ++struct ip6t_HL_info { + __u8 mode; + __u8 hop_limit; + }; +diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h +index 513df75..933ff2a 100644 +--- a/include/uapi/linux/prctl.h ++++ b/include/uapi/linux/prctl.h +@@ -179,4 +179,13 @@ struct prctl_mm_map { + #define PR_SET_THP_DISABLE 41 + #define PR_GET_THP_DISABLE 42 + ++/* Sets the timerslack for arbitrary threads ++ * arg2 slack value, 0 means "use default" ++ * arg3 pid of the thread whose timer slack needs to be set ++ */ ++#define PR_SET_TIMERSLACK_PID 43 ++ ++#define PR_SET_VMA 0x53564d41 ++# define PR_SET_VMA_ANON_NAME 0 ++ + #endif /* _LINUX_PRCTL_H */ +diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h +index eb0f1a5..01757b7 100644 +--- a/include/uapi/linux/rtnetlink.h ++++ b/include/uapi/linux/rtnetlink.h +@@ -297,6 +297,7 @@ enum rtattr_type_t { + RTA_TABLE, + RTA_MARK, + RTA_MFC_STATS, ++ RTA_UID, + __RTA_MAX + }; + +diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h +index e888b1a..623e9aa 100644 +--- a/include/uapi/linux/sockios.h ++++ b/include/uapi/linux/sockios.h +@@ -65,6 +65,7 @@ + #define SIOCDIFADDR 0x8936 /* delete PA address */ + #define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */ + #define SIOCGIFCOUNT 0x8938 /* get number of devices */ ++#define SIOCKILLADDR 0x8939 /* kill sockets with this local addr */ + + #define SIOCGIFBR 0x8940 /* Bridging support */ + #define SIOCSIFBR 0x8941 /* Set bridging options */ +diff --git a/include/uapi/linux/thermal.h b/include/uapi/linux/thermal.h +new file mode 100644 +index 0000000..ac55358 +--- /dev/null ++++ b/include/uapi/linux/thermal.h +@@ -0,0 +1,35 @@ ++#ifndef _UAPI_LINUX_THERMAL_H ++#define _UAPI_LINUX_THERMAL_H ++ ++#define THERMAL_NAME_LENGTH 20 ++ ++/* Adding event notification support elements */ ++#define THERMAL_GENL_FAMILY_NAME "thermal_event" ++#define THERMAL_GENL_VERSION 0x01 ++#define THERMAL_GENL_MCAST_GROUP_NAME "thermal_mc_grp" ++ ++/* Events supported by Thermal Netlink */ ++enum events { ++ THERMAL_AUX0, ++ THERMAL_AUX1, ++ THERMAL_CRITICAL, ++ THERMAL_DEV_FAULT, ++}; ++ ++/* attributes of thermal_genl_family */ ++enum { ++ THERMAL_GENL_ATTR_UNSPEC, ++ THERMAL_GENL_ATTR_EVENT, ++ __THERMAL_GENL_ATTR_MAX, ++}; ++#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) ++ ++/* commands supported by the thermal_genl_family */ ++enum { ++ THERMAL_GENL_CMD_UNSPEC, ++ THERMAL_GENL_CMD_EVENT, ++ __THERMAL_GENL_CMD_MAX, ++}; ++#define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) ++ ++#endif /* _UAPI_LINUX_THERMAL_H */ +diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h +index aa33fd1..400196c 100644 +--- a/include/uapi/linux/usb/ch9.h ++++ b/include/uapi/linux/usb/ch9.h +@@ -705,6 +705,7 @@ struct usb_interface_assoc_descriptor { + __u8 iFunction; + } __attribute__ ((packed)); + ++#define USB_DT_INTERFACE_ASSOCIATION_SIZE 8 + + /*-------------------------------------------------------------------------*/ + +diff --git a/include/uapi/linux/usb/f_accessory.h b/include/uapi/linux/usb/f_accessory.h +new file mode 100644 +index 0000000..0baeb7d +--- /dev/null ++++ b/include/uapi/linux/usb/f_accessory.h +@@ -0,0 +1,146 @@ ++/* ++ * Gadget Function Driver for Android USB accessories ++ * ++ * Copyright (C) 2011 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _UAPI_LINUX_USB_F_ACCESSORY_H ++#define _UAPI_LINUX_USB_F_ACCESSORY_H ++ ++/* Use Google Vendor ID when in accessory mode */ ++#define USB_ACCESSORY_VENDOR_ID 0x18D1 ++ ++ ++/* Product ID to use when in accessory mode */ ++#define USB_ACCESSORY_PRODUCT_ID 0x2D00 ++ ++/* Product ID to use when in accessory mode and adb is enabled */ ++#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 ++ ++/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */ ++#define ACCESSORY_STRING_MANUFACTURER 0 ++#define ACCESSORY_STRING_MODEL 1 ++#define ACCESSORY_STRING_DESCRIPTION 2 ++#define ACCESSORY_STRING_VERSION 3 ++#define ACCESSORY_STRING_URI 4 ++#define ACCESSORY_STRING_SERIAL 5 ++ ++/* Control request for retrieving device's protocol version ++ * ++ * requestType: USB_DIR_IN | USB_TYPE_VENDOR ++ * request: ACCESSORY_GET_PROTOCOL ++ * value: 0 ++ * index: 0 ++ * data version number (16 bits little endian) ++ * 1 for original accessory support ++ * 2 adds HID and device to host audio support ++ */ ++#define ACCESSORY_GET_PROTOCOL 51 ++ ++/* Control request for host to send a string to the device ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_SEND_STRING ++ * value: 0 ++ * index: string ID ++ * data zero terminated UTF8 string ++ * ++ * The device can later retrieve these strings via the ++ * ACCESSORY_GET_STRING_* ioctls ++ */ ++#define ACCESSORY_SEND_STRING 52 ++ ++/* Control request for starting device in accessory mode. ++ * The host sends this after setting all its strings to the device. ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_START ++ * value: 0 ++ * index: 0 ++ * data none ++ */ ++#define ACCESSORY_START 53 ++ ++/* Control request for registering a HID device. ++ * Upon registering, a unique ID is sent by the accessory in the ++ * value parameter. This ID will be used for future commands for ++ * the device ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_REGISTER_HID_DEVICE ++ * value: Accessory assigned ID for the HID device ++ * index: total length of the HID report descriptor ++ * data none ++ */ ++#define ACCESSORY_REGISTER_HID 54 ++ ++/* Control request for unregistering a HID device. ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_REGISTER_HID ++ * value: Accessory assigned ID for the HID device ++ * index: 0 ++ * data none ++ */ ++#define ACCESSORY_UNREGISTER_HID 55 ++ ++/* Control request for sending the HID report descriptor. ++ * If the HID descriptor is longer than the endpoint zero max packet size, ++ * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC ++ * commands. The data for the descriptor must be sent sequentially ++ * if multiple packets are needed. ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_SET_HID_REPORT_DESC ++ * value: Accessory assigned ID for the HID device ++ * index: offset of data in descriptor ++ * (needed when HID descriptor is too big for one packet) ++ * data the HID report descriptor ++ */ ++#define ACCESSORY_SET_HID_REPORT_DESC 56 ++ ++/* Control request for sending HID events. ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_SEND_HID_EVENT ++ * value: Accessory assigned ID for the HID device ++ * index: 0 ++ * data the HID report for the event ++ */ ++#define ACCESSORY_SEND_HID_EVENT 57 ++ ++/* Control request for setting the audio mode. ++ * ++ * requestType: USB_DIR_OUT | USB_TYPE_VENDOR ++ * request: ACCESSORY_SET_AUDIO_MODE ++ * value: 0 - no audio ++ * 1 - device to host, 44100 16-bit stereo PCM ++ * index: 0 ++ * data none ++ */ ++#define ACCESSORY_SET_AUDIO_MODE 58 ++ ++/* ioctls for retrieving strings set by the host */ ++#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) ++#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) ++#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) ++#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) ++#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) ++#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) ++/* returns 1 if there is a start request pending */ ++#define ACCESSORY_IS_START_REQUESTED _IO('M', 7) ++/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ ++#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) ++ ++#endif /* _UAPI_LINUX_USB_F_ACCESSORY_H */ +diff --git a/include/uapi/linux/usb/f_mtp.h b/include/uapi/linux/usb/f_mtp.h +new file mode 100644 +index 0000000..5032918 +--- /dev/null ++++ b/include/uapi/linux/usb/f_mtp.h +@@ -0,0 +1,61 @@ ++/* ++ * Gadget Function Driver for MTP ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * Author: Mike Lockwood <lockwood@android.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _UAPI_LINUX_USB_F_MTP_H ++#define _UAPI_LINUX_USB_F_MTP_H ++ ++#include <linux/ioctl.h> ++#include <linux/types.h> ++ ++struct mtp_file_range { ++ /* file descriptor for file to transfer */ ++ int fd; ++ /* offset in file for start of transfer */ ++ loff_t offset; ++ /* number of bytes to transfer */ ++ int64_t length; ++ /* MTP command ID for data header, ++ * used only for MTP_SEND_FILE_WITH_HEADER ++ */ ++ uint16_t command; ++ /* MTP transaction ID for data header, ++ * used only for MTP_SEND_FILE_WITH_HEADER ++ */ ++ uint32_t transaction_id; ++}; ++ ++struct mtp_event { ++ /* size of the event */ ++ size_t length; ++ /* event data to send */ ++ void *data; ++}; ++ ++/* Sends the specified file range to the host */ ++#define MTP_SEND_FILE _IOW('M', 0, struct mtp_file_range) ++/* Receives data from the host and writes it to a file. ++ * The file is created if it does not exist. ++ */ ++#define MTP_RECEIVE_FILE _IOW('M', 1, struct mtp_file_range) ++/* Sends an event to the host via the interrupt endpoint */ ++#define MTP_SEND_EVENT _IOW('M', 3, struct mtp_event) ++/* Sends the specified file range to the host, ++ * with a 12 byte MTP data packet header at the beginning. ++ */ ++#define MTP_SEND_FILE_WITH_HEADER _IOW('M', 4, struct mtp_file_range) ++ ++#endif /* _UAPI_LINUX_USB_F_MTP_H */ +diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h +index 3b3b95e..192e753 100644 +--- a/include/uapi/linux/usb/video.h ++++ b/include/uapi/linux/usb/video.h +@@ -53,6 +53,8 @@ + #define UVC_VS_FORMAT_FRAME_BASED 0x10 + #define UVC_VS_FRAME_FRAME_BASED 0x11 + #define UVC_VS_FORMAT_STREAM_BASED 0x12 ++#define UVC_VS_FORMAT_H264 0x13 ++#define UVC_VS_FRAME_H264 0x14 + + /* A.7. Video Class-Specific Endpoint Descriptor Subtypes */ + #define UVC_EP_UNDEFINED 0x00 +@@ -300,6 +302,7 @@ struct uvc_processing_unit_descriptor { + __u8 bControlSize; + __u8 bmControls[2]; + __u8 iProcessing; ++ __u8 bmVideoStandards; + } __attribute__((__packed__)); + + #define UVC_DT_PROCESSING_UNIT_SIZE(n) (9+(n)) +@@ -524,6 +527,39 @@ struct uvc_format_mjpeg { + } __attribute__((__packed__)); + + #define UVC_DT_FORMAT_MJPEG_SIZE 11 ++#define UVC_DT_FORMAT_H264_SIZE 11 ++ ++struct uvc_format_h264 { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bFormatIndex; ++ __u8 bNumFrameDescriptors; ++ __u8 bmFlags; ++ __u8 bDefaultFrameIndex; ++ __u8 bAspectRatioX; ++ __u8 bAspectRatioY; ++ __u8 bmInterfaceFlags; ++ __u8 bCopyProtect; ++} __attribute__((__packed__)); ++ ++struct uvc_format_h264_base { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDescriptorSubType; ++ __u8 bFormatIndex; ++ __u8 bNumFrameDescriptors; ++ __u8 guidFormat[16]; ++ __u8 bBitsPerPixel; ++ __u8 bDefaultFrameIndex; ++ __u8 bAspectRatioX; ++ __u8 bAspectRatioY; ++ __u8 bmInterfaceFlags; ++ __u8 bCopyProtect; ++ __u8 bVariableSize; ++} __attribute__((__packed__)); ++ ++ + + /* MJPEG Payload - 3.1.2. MJPEG Video Frame Descriptor */ + struct uvc_frame_mjpeg { +@@ -564,5 +600,47 @@ struct UVC_FRAME_MJPEG(n) { \ + __u32 dwFrameInterval[n]; \ + } __attribute__ ((packed)) + ++#define UVC_DT_FRAME_H264_SIZE(n) (26+4*(n)) ++ ++#define UVC_FRAME_H264(n) \ ++ uvc_frame_h264_##n ++ ++#define UVC_FRAME_H264_BASE(n) \ ++ uvc_frame_h264_base##n ++ ++#define DECLARE_UVC_FRAME_H264(n) \ ++struct UVC_FRAME_H264(n) { \ ++ __u8 bLength; \ ++ __u8 bDescriptorType; \ ++ __u8 bDescriptorSubType; \ ++ __u8 bFrameIndex; \ ++ __u8 bmCapabilities; \ ++ __u16 wWidth; \ ++ __u16 wHeight; \ ++ __u32 dwMinBitRate; \ ++ __u32 dwMaxBitRate; \ ++ __u32 dwMaxVideoFrameBufferSize; \ ++ __u32 dwDefaultFrameInterval; \ ++ __u8 bFrameIntervalType; \ ++ __u32 dwFrameInterval[n]; \ ++} __attribute__ ((packed)) ++ ++#define DECLARE_UVC_FRAME_H264_BASE(n) \ ++struct UVC_FRAME_H264_BASE(n) { \ ++ __u8 bLength; \ ++ __u8 bDescriptorType; \ ++ __u8 bDescriptorSubType; \ ++ __u8 bFrameIndex; \ ++ __u8 bmCapabilities; \ ++ __u16 wWidth; \ ++ __u16 wHeight; \ ++ __u32 dwMinBitRate; \ ++ __u32 dwMaxBitRate; \ ++ __u32 dwDefaultFrameInterval; \ ++ __u8 bFrameIntervalType; \ ++ __u32 dwBytesPerLine; \ ++ __u32 dwFrameInterval[n]; \ ++} __attribute__ ((packed)) ++ + #endif /* __LINUX_USB_VIDEO_H */ + +diff --git a/include/uapi/video/adf.h b/include/uapi/video/adf.h +new file mode 100644 +index 0000000..c5d2e62 +--- /dev/null ++++ b/include/uapi/video/adf.h +@@ -0,0 +1,321 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _UAPI_VIDEO_ADF_H_ ++#define _UAPI_VIDEO_ADF_H_ ++ ++#include <linux/ioctl.h> ++#include <linux/types.h> ++ ++#include <drm/drm_fourcc.h> ++#include <drm/drm_mode.h> ++ ++#define ADF_NAME_LEN 32 ++#define ADF_MAX_CUSTOM_DATA_SIZE 4096 ++ ++enum adf_interface_type { ++ ADF_INTF_DSI = 0, ++ ADF_INTF_eDP = 1, ++ ADF_INTF_DPI = 2, ++ ADF_INTF_VGA = 3, ++ ADF_INTF_DVI = 4, ++ ADF_INTF_HDMI = 5, ++ ADF_INTF_MEMORY = 6, ++ ADF_INTF_TYPE_DEVICE_CUSTOM = 128, ++ ADF_INTF_TYPE_MAX = (~(__u32)0), ++}; ++ ++#define ADF_INTF_FLAG_PRIMARY (1 << 0) ++#define ADF_INTF_FLAG_EXTERNAL (1 << 1) ++ ++enum adf_event_type { ++ ADF_EVENT_VSYNC = 0, ++ ADF_EVENT_HOTPLUG = 1, ++ ADF_EVENT_DEVICE_CUSTOM = 128, ++ ADF_EVENT_TYPE_MAX = 255, ++}; ++ ++/** ++ * struct adf_set_event - start or stop subscribing to ADF events ++ * ++ * @type: the type of event to (un)subscribe ++ * @enabled: subscribe or unsubscribe ++ * ++ * After subscribing to an event, userspace may poll() the ADF object's fd ++ * to wait for events or read() to consume the event's data. ++ * ++ * ADF reserves event types 0 to %ADF_EVENT_DEVICE_CUSTOM-1 for its own events. ++ * Devices may use event types %ADF_EVENT_DEVICE_CUSTOM to %ADF_EVENT_TYPE_MAX-1 ++ * for driver-private events. ++ */ ++struct adf_set_event { ++ __u8 type; ++ __u8 enabled; ++}; ++ ++/** ++ * struct adf_event - common header for ADF event data ++ * ++ * @type: event type ++ * @length: total size of event data, header inclusive ++ */ ++struct adf_event { ++ __u8 type; ++ __u32 length; ++}; ++ ++/** ++ * struct adf_vsync_event - ADF vsync event ++ * ++ * @base: event header (see &struct adf_event) ++ * @timestamp: time of vsync event, in nanoseconds ++ */ ++struct adf_vsync_event { ++ struct adf_event base; ++ __aligned_u64 timestamp; ++}; ++ ++/** ++ * struct adf_vsync_event - ADF display hotplug event ++ * ++ * @base: event header (see &struct adf_event) ++ * @connected: whether a display is now connected to the interface ++ */ ++struct adf_hotplug_event { ++ struct adf_event base; ++ __u8 connected; ++}; ++ ++#define ADF_MAX_PLANES 4 ++/** ++ * struct adf_buffer_config - description of buffer displayed by adf_post_config ++ * ++ * @overlay_engine: id of the target overlay engine ++ * @w: width of display region in pixels ++ * @h: height of display region in pixels ++ * @format: DRM-style fourcc, see drm_fourcc.h for standard formats ++ * @fd: dma_buf fd for each plane ++ * @offset: location of first pixel to scan out, in bytes ++ * @pitch: stride (i.e. length of a scanline including padding) in bytes ++ * @n_planes: number of planes in buffer ++ * @acquire_fence: sync_fence fd which will clear when the buffer is ++ * ready for display, or <0 if the buffer is already ready ++ */ ++struct adf_buffer_config { ++ __u32 overlay_engine; ++ ++ __u32 w; ++ __u32 h; ++ __u32 format; ++ ++ __s32 fd[ADF_MAX_PLANES]; ++ __u32 offset[ADF_MAX_PLANES]; ++ __u32 pitch[ADF_MAX_PLANES]; ++ __u8 n_planes; ++ ++ __s32 acquire_fence; ++}; ++#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config)) ++ ++/** ++ * struct adf_post_config - request to flip to a new set of buffers ++ * ++ * @n_interfaces: number of interfaces targeted by the flip (input) ++ * @interfaces: ids of interfaces targeted by the flip (input) ++ * @n_bufs: number of buffers displayed (input) ++ * @bufs: description of buffers displayed (input) ++ * @custom_data_size: size of driver-private data (input) ++ * @custom_data: driver-private data (input) ++ * @complete_fence: sync_fence fd which will clear when this ++ * configuration has left the screen (output) ++ */ ++struct adf_post_config { ++ size_t n_interfaces; ++ __u32 __user *interfaces; ++ ++ size_t n_bufs; ++ struct adf_buffer_config __user *bufs; ++ ++ size_t custom_data_size; ++ void __user *custom_data; ++ ++ __s32 complete_fence; ++}; ++#define ADF_MAX_INTERFACES (4096 / sizeof(__u32)) ++ ++/** ++ * struct adf_simple_buffer_allocate - request to allocate a "simple" buffer ++ * ++ * @w: width of buffer in pixels (input) ++ * @h: height of buffer in pixels (input) ++ * @format: DRM-style fourcc (input) ++ * ++ * @fd: dma_buf fd (output) ++ * @offset: location of first pixel, in bytes (output) ++ * @pitch: length of a scanline including padding, in bytes (output) ++ * ++ * Simple buffers are analogous to DRM's "dumb" buffers. They have a single ++ * plane of linear RGB data which can be allocated and scanned out without ++ * any driver-private ioctls or data. ++ * ++ * @format must be a standard RGB format defined in drm_fourcc.h. ++ * ++ * ADF clients must NOT assume that an interface can scan out a simple buffer ++ * allocated by a different ADF interface, even if the two interfaces belong to ++ * the same ADF device. ++ */ ++struct adf_simple_buffer_alloc { ++ __u16 w; ++ __u16 h; ++ __u32 format; ++ ++ __s32 fd; ++ __u32 offset; ++ __u32 pitch; ++}; ++ ++/** ++ * struct adf_simple_post_config - request to flip to a single buffer without ++ * driver-private data ++ * ++ * @buf: description of buffer displayed (input) ++ * @complete_fence: sync_fence fd which will clear when this buffer has left the ++ * screen (output) ++ */ ++struct adf_simple_post_config { ++ struct adf_buffer_config buf; ++ __s32 complete_fence; ++}; ++ ++/** ++ * struct adf_attachment_config - description of attachment between an overlay ++ * engine and an interface ++ * ++ * @overlay_engine: id of the overlay engine ++ * @interface: id of the interface ++ */ ++struct adf_attachment_config { ++ __u32 overlay_engine; ++ __u32 interface; ++}; ++ ++/** ++ * struct adf_device_data - describes a display device ++ * ++ * @name: display device's name ++ * @n_attachments: the number of current attachments ++ * @attachments: list of current attachments ++ * @n_allowed_attachments: the number of allowed attachments ++ * @allowed_attachments: list of allowed attachments ++ * @custom_data_size: size of driver-private data ++ * @custom_data: driver-private data ++ */ ++struct adf_device_data { ++ char name[ADF_NAME_LEN]; ++ ++ size_t n_attachments; ++ struct adf_attachment_config __user *attachments; ++ ++ size_t n_allowed_attachments; ++ struct adf_attachment_config __user *allowed_attachments; ++ ++ size_t custom_data_size; ++ void __user *custom_data; ++}; ++#define ADF_MAX_ATTACHMENTS (4096 / sizeof(struct adf_attachment_config)) ++ ++/** ++ * struct adf_device_data - describes a display interface ++ * ++ * @name: display interface's name ++ * @type: interface type (see enum @adf_interface_type) ++ * @id: which interface of type @type; ++ * e.g. interface DSI.1 -> @type=@ADF_INTF_TYPE_DSI, @id=1 ++ * @flags: informational flags (bitmask of %ADF_INTF_FLAG_* values) ++ * @dpms_state: DPMS state (one of @DRM_MODE_DPMS_* defined in drm_mode.h) ++ * @hotplug_detect: whether a display is plugged in ++ * @width_mm: screen width in millimeters, or 0 if unknown ++ * @height_mm: screen height in millimeters, or 0 if unknown ++ * @current_mode: current display mode ++ * @n_available_modes: the number of hardware display modes ++ * @available_modes: list of hardware display modes ++ * @custom_data_size: size of driver-private data ++ * @custom_data: driver-private data ++ */ ++struct adf_interface_data { ++ char name[ADF_NAME_LEN]; ++ ++ __u32 type; ++ __u32 id; ++ /* e.g. type=ADF_INTF_TYPE_DSI, id=1 => DSI.1 */ ++ __u32 flags; ++ ++ __u8 dpms_state; ++ __u8 hotplug_detect; ++ __u16 width_mm; ++ __u16 height_mm; ++ ++ struct drm_mode_modeinfo current_mode; ++ size_t n_available_modes; ++ struct drm_mode_modeinfo __user *available_modes; ++ ++ size_t custom_data_size; ++ void __user *custom_data; ++}; ++#define ADF_MAX_MODES (4096 / sizeof(struct drm_mode_modeinfo)) ++ ++/** ++ * struct adf_overlay_engine_data - describes an overlay engine ++ * ++ * @name: overlay engine's name ++ * @n_supported_formats: number of supported formats ++ * @supported_formats: list of supported formats ++ * @custom_data_size: size of driver-private data ++ * @custom_data: driver-private data ++ */ ++struct adf_overlay_engine_data { ++ char name[ADF_NAME_LEN]; ++ ++ size_t n_supported_formats; ++ __u32 __user *supported_formats; ++ ++ size_t custom_data_size; ++ void __user *custom_data; ++}; ++#define ADF_MAX_SUPPORTED_FORMATS (4096 / sizeof(__u32)) ++ ++#define ADF_IOCTL_TYPE 'D' ++#define ADF_IOCTL_NR_CUSTOM 128 ++ ++#define ADF_SET_EVENT _IOW(ADF_IOCTL_TYPE, 0, struct adf_set_event) ++#define ADF_BLANK _IOW(ADF_IOCTL_TYPE, 1, __u8) ++#define ADF_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config) ++#define ADF_SET_MODE _IOW(ADF_IOCTL_TYPE, 3, \ ++ struct drm_mode_modeinfo) ++#define ADF_GET_DEVICE_DATA _IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data) ++#define ADF_GET_INTERFACE_DATA _IOR(ADF_IOCTL_TYPE, 5, \ ++ struct adf_interface_data) ++#define ADF_GET_OVERLAY_ENGINE_DATA \ ++ _IOR(ADF_IOCTL_TYPE, 6, \ ++ struct adf_overlay_engine_data) ++#define ADF_SIMPLE_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 7, \ ++ struct adf_simple_post_config) ++#define ADF_SIMPLE_BUFFER_ALLOC _IOW(ADF_IOCTL_TYPE, 8, \ ++ struct adf_simple_buffer_alloc) ++#define ADF_ATTACH _IOW(ADF_IOCTL_TYPE, 9, \ ++ struct adf_attachment_config) ++#define ADF_DETACH _IOW(ADF_IOCTL_TYPE, 10, \ ++ struct adf_attachment_config) ++ ++#endif /* _UAPI_VIDEO_ADF_H_ */ +diff --git a/include/video/adf.h b/include/video/adf.h +new file mode 100644 +index 0000000..34f10e5 +--- /dev/null ++++ b/include/video/adf.h +@@ -0,0 +1,502 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _VIDEO_ADF_H ++#define _VIDEO_ADF_H ++ ++#include <linux/device.h> ++#include <linux/dma-buf.h> ++#include <linux/idr.h> ++#include <linux/kref.h> ++#include <linux/kthread.h> ++#include <linux/ktime.h> ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/scatterlist.h> ++#include <linux/sched.h> ++#include <linux/spinlock.h> ++#include <linux/wait.h> ++#include <linux/workqueue.h> ++#include <uapi/video/adf.h> ++#include "sync.h" ++ ++struct adf_obj; ++struct adf_obj_ops; ++struct adf_device; ++struct adf_device_ops; ++struct adf_interface; ++struct adf_interface_ops; ++struct adf_overlay_engine; ++struct adf_overlay_engine_ops; ++ ++/** ++ * struct adf_buffer - buffer displayed by adf_post ++ * ++ * @overlay_engine: target overlay engine ++ * @w: width of display region in pixels ++ * @h: height of display region in pixels ++ * @format: DRM-style fourcc, see drm_fourcc.h for standard formats ++ * @dma_bufs: dma_buf for each plane ++ * @offset: location of first pixel to scan out, in bytes ++ * @pitch: length of a scanline including padding, in bytes ++ * @n_planes: number of planes in buffer ++ * @acquire_fence: sync_fence which will clear when the buffer is ++ * ready for display ++ * ++ * &struct adf_buffer is the in-kernel counterpart to the userspace-facing ++ * &struct adf_buffer_config. ++ */ ++struct adf_buffer { ++ struct adf_overlay_engine *overlay_engine; ++ ++ u32 w; ++ u32 h; ++ u32 format; ++ ++ struct dma_buf *dma_bufs[ADF_MAX_PLANES]; ++ u32 offset[ADF_MAX_PLANES]; ++ u32 pitch[ADF_MAX_PLANES]; ++ u8 n_planes; ++ ++ struct sync_fence *acquire_fence; ++}; ++ ++/** ++ * struct adf_buffer_mapping - state for mapping a &struct adf_buffer into the ++ * display device ++ * ++ * @attachments: dma-buf attachment for each plane ++ * @sg_tables: SG tables for each plane ++ */ ++struct adf_buffer_mapping { ++ struct dma_buf_attachment *attachments[ADF_MAX_PLANES]; ++ struct sg_table *sg_tables[ADF_MAX_PLANES]; ++}; ++ ++/** ++ * struct adf_post - request to flip to a new set of buffers ++ * ++ * @n_bufs: number of buffers displayed ++ * @bufs: buffers displayed ++ * @mappings: in-device mapping state for each buffer ++ * @custom_data_size: size of driver-private data ++ * @custom_data: driver-private data ++ * ++ * &struct adf_post is the in-kernel counterpart to the userspace-facing ++ * &struct adf_post_config. ++ */ ++struct adf_post { ++ size_t n_bufs; ++ struct adf_buffer *bufs; ++ struct adf_buffer_mapping *mappings; ++ ++ size_t custom_data_size; ++ void *custom_data; ++}; ++ ++/** ++ * struct adf_attachment - description of attachment between an overlay engine ++ * and an interface ++ * ++ * @overlay_engine: the overlay engine ++ * @interface: the interface ++ * ++ * &struct adf_attachment is the in-kernel counterpart to the userspace-facing ++ * &struct adf_attachment_config. ++ */ ++struct adf_attachment { ++ struct adf_overlay_engine *overlay_engine; ++ struct adf_interface *interface; ++}; ++ ++struct adf_pending_post { ++ struct list_head head; ++ struct adf_post config; ++ void *state; ++}; ++ ++enum adf_obj_type { ++ ADF_OBJ_OVERLAY_ENGINE = 0, ++ ADF_OBJ_INTERFACE = 1, ++ ADF_OBJ_DEVICE = 2, ++}; ++ ++/** ++ * struct adf_obj_ops - common ADF object implementation ops ++ * ++ * @open: handle opening the object's device node ++ * @release: handle releasing an open file ++ * @ioctl: handle custom ioctls ++ * ++ * @supports_event: return whether the object supports generating events of type ++ * @type ++ * @set_event: enable or disable events of type @type ++ * @event_type_str: return a string representation of custom event @type ++ * (@type >= %ADF_EVENT_DEVICE_CUSTOM). ++ * ++ * @custom_data: copy up to %ADF_MAX_CUSTOM_DATA_SIZE bytes of driver-private ++ * data into @data (allocated by ADF) and return the number of copied bytes ++ * in @size. Return 0 on success or an error code (<0) on failure. ++ */ ++struct adf_obj_ops { ++ /* optional */ ++ int (*open)(struct adf_obj *obj, struct inode *inode, ++ struct file *file); ++ /* optional */ ++ void (*release)(struct adf_obj *obj, struct inode *inode, ++ struct file *file); ++ /* optional */ ++ long (*ioctl)(struct adf_obj *obj, unsigned int cmd, unsigned long arg); ++ ++ /* optional */ ++ bool (*supports_event)(struct adf_obj *obj, enum adf_event_type type); ++ /* required if supports_event is implemented */ ++ void (*set_event)(struct adf_obj *obj, enum adf_event_type type, ++ bool enabled); ++ /* optional */ ++ const char *(*event_type_str)(struct adf_obj *obj, ++ enum adf_event_type type); ++ ++ /* optional */ ++ int (*custom_data)(struct adf_obj *obj, void *data, size_t *size); ++}; ++ ++struct adf_obj { ++ enum adf_obj_type type; ++ char name[ADF_NAME_LEN]; ++ struct adf_device *parent; ++ ++ const struct adf_obj_ops *ops; ++ ++ struct device dev; ++ ++ struct spinlock file_lock; ++ struct list_head file_list; ++ ++ struct mutex event_lock; ++ struct rb_root event_refcount; ++ ++ int id; ++ int minor; ++}; ++ ++/** ++ * struct adf_device_quirks - common display device quirks ++ * ++ * @buffer_padding: whether the last scanline of a buffer extends to the ++ * buffer's pitch (@ADF_BUFFER_PADDED_TO_PITCH) or just to the visible ++ * width (@ADF_BUFFER_UNPADDED) ++ */ ++struct adf_device_quirks { ++ /* optional, defaults to ADF_BUFFER_PADDED_TO_PITCH */ ++ enum { ++ ADF_BUFFER_PADDED_TO_PITCH = 0, ++ ADF_BUFFER_UNPADDED = 1, ++ } buffer_padding; ++}; ++ ++/** ++ * struct adf_device_ops - display device implementation ops ++ * ++ * @owner: device's module ++ * @base: common operations (see &struct adf_obj_ops) ++ * @quirks: device's quirks (see &struct adf_device_quirks) ++ * ++ * @attach: attach overlay engine @eng to interface @intf. Return 0 on success ++ * or error code (<0) on failure. ++ * @detach: detach overlay engine @eng from interface @intf. Return 0 on ++ * success or error code (<0) on failure. ++ * ++ * @validate_custom_format: validate the number and size of planes ++ * in buffers with a custom format (i.e., not one of the @DRM_FORMAT_* ++ * types defined in drm/drm_fourcc.h). Return 0 if the buffer is valid or ++ * an error code (<0) otherwise. ++ * ++ * @validate: validate that the proposed configuration @cfg is legal. The ++ * driver may optionally allocate and return some driver-private state in ++ * @driver_state, which will be passed to the corresponding post(). The ++ * driver may NOT commit any changes to hardware. Return 0 if @cfg is ++ * valid or an error code (<0) otherwise. ++ * @complete_fence: create a hardware-backed sync fence to be signaled when ++ * @cfg is removed from the screen. If unimplemented, ADF automatically ++ * creates an sw_sync fence. Return the sync fence on success or a ++ * PTR_ERR() on failure. ++ * @post: flip @cfg onto the screen. Wait for the display to begin scanning out ++ * @cfg before returning. ++ * @advance_timeline: signal the sync fence for the last configuration to leave ++ * the display. If unimplemented, ADF automatically advances an sw_sync ++ * timeline. ++ * @state_free: free driver-private state allocated during validate() ++ */ ++struct adf_device_ops { ++ /* required */ ++ struct module *owner; ++ const struct adf_obj_ops base; ++ /* optional */ ++ const struct adf_device_quirks quirks; ++ ++ /* optional */ ++ int (*attach)(struct adf_device *dev, struct adf_overlay_engine *eng, ++ struct adf_interface *intf); ++ /* optional */ ++ int (*detach)(struct adf_device *dev, struct adf_overlay_engine *eng, ++ struct adf_interface *intf); ++ ++ /* required if any of the device's overlay engines supports at least one ++ custom format */ ++ int (*validate_custom_format)(struct adf_device *dev, ++ struct adf_buffer *buf); ++ ++ /* required */ ++ int (*validate)(struct adf_device *dev, struct adf_post *cfg, ++ void **driver_state); ++ /* optional */ ++ struct sync_fence *(*complete_fence)(struct adf_device *dev, ++ struct adf_post *cfg, void *driver_state); ++ /* required */ ++ void (*post)(struct adf_device *dev, struct adf_post *cfg, ++ void *driver_state); ++ /* required if complete_fence is implemented */ ++ void (*advance_timeline)(struct adf_device *dev, ++ struct adf_post *cfg, void *driver_state); ++ /* required if validate allocates driver state */ ++ void (*state_free)(struct adf_device *dev, void *driver_state); ++}; ++ ++struct adf_attachment_list { ++ struct adf_attachment attachment; ++ struct list_head head; ++}; ++ ++struct adf_device { ++ struct adf_obj base; ++ struct device *dev; ++ ++ const struct adf_device_ops *ops; ++ ++ struct mutex client_lock; ++ ++ struct idr interfaces; ++ size_t n_interfaces; ++ struct idr overlay_engines; ++ ++ struct list_head post_list; ++ struct mutex post_lock; ++ struct kthread_worker post_worker; ++ struct task_struct *post_thread; ++ struct kthread_work post_work; ++ ++ struct list_head attached; ++ size_t n_attached; ++ struct list_head attach_allowed; ++ size_t n_attach_allowed; ++ ++ struct adf_pending_post *onscreen; ++ ++ struct sw_sync_timeline *timeline; ++ int timeline_max; ++}; ++ ++/** ++ * struct adf_interface_ops - display interface implementation ops ++ * ++ * @base: common operations (see &struct adf_obj_ops) ++ * ++ * @blank: change the display's DPMS state. Return 0 on success or error ++ * code (<0) on failure. ++ * ++ * @alloc_simple_buffer: allocate a buffer with the specified @w, @h, and ++ * @format. @format will be a standard RGB format (i.e., ++ * adf_format_is_rgb(@format) == true). Return 0 on success or error code ++ * (<0) on failure. On success, return the buffer, offset, and pitch in ++ * @dma_buf, @offset, and @pitch respectively. ++ * @describe_simple_post: provide driver-private data needed to post a single ++ * buffer @buf. Copy up to ADF_MAX_CUSTOM_DATA_SIZE bytes into @data ++ * (allocated by ADF) and return the number of bytes in @size. Return 0 on ++ * success or error code (<0) on failure. ++ * ++ * @modeset: change the interface's mode. @mode is not necessarily part of the ++ * modelist passed to adf_hotplug_notify_connected(); the driver may ++ * accept or reject custom modes at its discretion. Return 0 on success or ++ * error code (<0) if the mode could not be set. ++ * ++ * @screen_size: copy the screen dimensions in millimeters into @width_mm ++ * and @height_mm. Return 0 on success or error code (<0) if the display ++ * dimensions are unknown. ++ * ++ * @type_str: return a string representation of custom @intf->type ++ * (@intf->type >= @ADF_INTF_TYPE_DEVICE_CUSTOM). ++ */ ++struct adf_interface_ops { ++ const struct adf_obj_ops base; ++ ++ /* optional */ ++ int (*blank)(struct adf_interface *intf, u8 state); ++ ++ /* optional */ ++ int (*alloc_simple_buffer)(struct adf_interface *intf, ++ u16 w, u16 h, u32 format, ++ struct dma_buf **dma_buf, u32 *offset, u32 *pitch); ++ /* optional */ ++ int (*describe_simple_post)(struct adf_interface *intf, ++ struct adf_buffer *fb, void *data, size_t *size); ++ ++ /* optional */ ++ int (*modeset)(struct adf_interface *intf, ++ struct drm_mode_modeinfo *mode); ++ ++ /* optional */ ++ int (*screen_size)(struct adf_interface *intf, u16 *width_mm, ++ u16 *height_mm); ++ ++ /* optional */ ++ const char *(*type_str)(struct adf_interface *intf); ++}; ++ ++struct adf_interface { ++ struct adf_obj base; ++ const struct adf_interface_ops *ops; ++ ++ struct drm_mode_modeinfo current_mode; ++ ++ enum adf_interface_type type; ++ u32 idx; ++ u32 flags; ++ ++ wait_queue_head_t vsync_wait; ++ ktime_t vsync_timestamp; ++ rwlock_t vsync_lock; ++ ++ u8 dpms_state; ++ ++ bool hotplug_detect; ++ struct drm_mode_modeinfo *modelist; ++ size_t n_modes; ++ rwlock_t hotplug_modelist_lock; ++}; ++ ++/** ++ * struct adf_interface_ops - overlay engine implementation ops ++ * ++ * @base: common operations (see &struct adf_obj_ops) ++ * ++ * @supported_formats: list of fourccs the overlay engine can scan out ++ * @n_supported_formats: length of supported_formats, up to ++ * ADF_MAX_SUPPORTED_FORMATS ++ */ ++struct adf_overlay_engine_ops { ++ const struct adf_obj_ops base; ++ ++ /* required */ ++ const u32 *supported_formats; ++ /* required */ ++ const size_t n_supported_formats; ++}; ++ ++struct adf_overlay_engine { ++ struct adf_obj base; ++ ++ const struct adf_overlay_engine_ops *ops; ++}; ++ ++#define adf_obj_to_device(ptr) \ ++ container_of((ptr), struct adf_device, base) ++ ++#define adf_obj_to_interface(ptr) \ ++ container_of((ptr), struct adf_interface, base) ++ ++#define adf_obj_to_overlay_engine(ptr) \ ++ container_of((ptr), struct adf_overlay_engine, base) ++ ++int __printf(4, 5) adf_device_init(struct adf_device *dev, ++ struct device *parent, const struct adf_device_ops *ops, ++ const char *fmt, ...); ++void adf_device_destroy(struct adf_device *dev); ++int __printf(7, 8) adf_interface_init(struct adf_interface *intf, ++ struct adf_device *dev, enum adf_interface_type type, u32 idx, ++ u32 flags, const struct adf_interface_ops *ops, const char *fmt, ++ ...); ++void adf_interface_destroy(struct adf_interface *intf); ++static inline struct adf_device *adf_interface_parent( ++ struct adf_interface *intf) ++{ ++ return intf->base.parent; ++} ++int __printf(4, 5) adf_overlay_engine_init(struct adf_overlay_engine *eng, ++ struct adf_device *dev, ++ const struct adf_overlay_engine_ops *ops, const char *fmt, ...); ++void adf_overlay_engine_destroy(struct adf_overlay_engine *eng); ++static inline struct adf_device *adf_overlay_engine_parent( ++ struct adf_overlay_engine *eng) ++{ ++ return eng->base.parent; ++} ++ ++int adf_attachment_allow(struct adf_device *dev, struct adf_overlay_engine *eng, ++ struct adf_interface *intf); ++ ++const char *adf_obj_type_str(enum adf_obj_type type); ++const char *adf_interface_type_str(struct adf_interface *intf); ++const char *adf_event_type_str(struct adf_obj *obj, enum adf_event_type type); ++ ++#define ADF_FORMAT_STR_SIZE 5 ++void adf_format_str(u32 format, char buf[ADF_FORMAT_STR_SIZE]); ++int adf_format_validate_yuv(struct adf_device *dev, struct adf_buffer *buf, ++ u8 num_planes, u8 hsub, u8 vsub, u8 cpp[]); ++/** ++ * adf_format_validate_rgb - validate the number and size of planes in buffers ++ * with a custom RGB format. ++ * ++ * @dev: ADF device performing the validation ++ * @buf: buffer to validate ++ * @cpp: expected bytes per pixel ++ * ++ * adf_format_validate_rgb() is intended to be called as a helper from @dev's ++ * validate_custom_format() op. @buf must have a single RGB plane. ++ * ++ * Returns 0 if @buf has a single plane with sufficient size, or -EINVAL ++ * otherwise. ++ */ ++static inline int adf_format_validate_rgb(struct adf_device *dev, ++ struct adf_buffer *buf, u8 cpp) ++{ ++ return adf_format_validate_yuv(dev, buf, 1, 1, 1, &cpp); ++} ++ ++int adf_event_get(struct adf_obj *obj, enum adf_event_type type); ++int adf_event_put(struct adf_obj *obj, enum adf_event_type type); ++int adf_event_notify(struct adf_obj *obj, struct adf_event *event); ++ ++static inline void adf_vsync_get(struct adf_interface *intf) ++{ ++ adf_event_get(&intf->base, ADF_EVENT_VSYNC); ++} ++ ++static inline void adf_vsync_put(struct adf_interface *intf) ++{ ++ adf_event_put(&intf->base, ADF_EVENT_VSYNC); ++} ++ ++int adf_vsync_wait(struct adf_interface *intf, long timeout); ++void adf_vsync_notify(struct adf_interface *intf, ktime_t timestamp); ++ ++int adf_hotplug_notify_connected(struct adf_interface *intf, ++ struct drm_mode_modeinfo *modelist, size_t n_modes); ++void adf_hotplug_notify_disconnected(struct adf_interface *intf); ++ ++void adf_modeinfo_set_name(struct drm_mode_modeinfo *mode); ++void adf_modeinfo_set_vrefresh(struct drm_mode_modeinfo *mode); ++ ++#endif /* _VIDEO_ADF_H */ +diff --git a/include/video/adf_client.h b/include/video/adf_client.h +new file mode 100644 +index 0000000..983f2b6 +--- /dev/null ++++ b/include/video/adf_client.h +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _VIDEO_ADF_CLIENT_H_ ++#define _VIDEO_ADF_CLIENT_H_ ++ ++#include <video/adf.h> ++ ++int adf_interface_blank(struct adf_interface *intf, u8 state); ++u8 adf_interface_dpms_state(struct adf_interface *intf); ++ ++void adf_interface_current_mode(struct adf_interface *intf, ++ struct drm_mode_modeinfo *mode); ++size_t adf_interface_modelist(struct adf_interface *intf, ++ struct drm_mode_modeinfo *modelist, size_t n_modes); ++int adf_interface_set_mode(struct adf_interface *intf, ++ struct drm_mode_modeinfo *mode); ++int adf_interface_get_screen_size(struct adf_interface *intf, u16 *width, ++ u16 *height); ++int adf_interface_simple_buffer_alloc(struct adf_interface *intf, u16 w, u16 h, ++ u32 format, struct dma_buf **dma_buf, u32 *offset, u32 *pitch); ++struct sync_fence *adf_interface_simple_post(struct adf_interface *intf, ++ struct adf_buffer *buf); ++ ++bool adf_overlay_engine_supports_format(struct adf_overlay_engine *eng, ++ u32 format); ++ ++size_t adf_device_attachments(struct adf_device *dev, ++ struct adf_attachment *attachments, size_t n_attachments); ++size_t adf_device_attachments_allowed(struct adf_device *dev, ++ struct adf_attachment *attachments, size_t n_attachments); ++bool adf_device_attached(struct adf_device *dev, struct adf_overlay_engine *eng, ++ struct adf_interface *intf); ++bool adf_device_attach_allowed(struct adf_device *dev, ++ struct adf_overlay_engine *eng, struct adf_interface *intf); ++int adf_device_attach(struct adf_device *dev, struct adf_overlay_engine *eng, ++ struct adf_interface *intf); ++int adf_device_detach(struct adf_device *dev, struct adf_overlay_engine *eng, ++ struct adf_interface *intf); ++ ++struct sync_fence *adf_device_post(struct adf_device *dev, ++ struct adf_interface **intfs, size_t n_intfs, ++ struct adf_buffer *bufs, size_t n_bufs, void *custom_data, ++ size_t custom_data_size); ++struct sync_fence *adf_device_post_nocopy(struct adf_device *dev, ++ struct adf_interface **intfs, size_t n_intfs, ++ struct adf_buffer *bufs, size_t n_bufs, void *custom_data, ++ size_t custom_data_size); ++ ++#endif /* _VIDEO_ADF_CLIENT_H_ */ +diff --git a/include/video/adf_fbdev.h b/include/video/adf_fbdev.h +new file mode 100644 +index 0000000..b722c6b +--- /dev/null ++++ b/include/video/adf_fbdev.h +@@ -0,0 +1,124 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _VIDEO_ADF_FBDEV_H_ ++#define _VIDEO_ADF_FBDEV_H_ ++ ++#include <linux/fb.h> ++#include <linux/mutex.h> ++#include <video/adf.h> ++ ++struct adf_fbdev { ++ struct adf_interface *intf; ++ struct adf_overlay_engine *eng; ++ struct fb_info *info; ++ u32 pseudo_palette[16]; ++ ++ unsigned int refcount; ++ struct mutex refcount_lock; ++ ++ struct dma_buf *dma_buf; ++ u32 offset; ++ u32 pitch; ++ void *vaddr; ++ u32 format; ++ ++ u16 default_xres_virtual; ++ u16 default_yres_virtual; ++ u32 default_format; ++}; ++ ++#if IS_ENABLED(CONFIG_ADF_FBDEV) ++void adf_modeinfo_to_fb_videomode(const struct drm_mode_modeinfo *mode, ++ struct fb_videomode *vmode); ++void adf_modeinfo_from_fb_videomode(const struct fb_videomode *vmode, ++ struct drm_mode_modeinfo *mode); ++ ++int adf_fbdev_init(struct adf_fbdev *fbdev, struct adf_interface *interface, ++ struct adf_overlay_engine *eng, ++ u16 xres_virtual, u16 yres_virtual, u32 format, ++ struct fb_ops *fbops, const char *fmt, ...); ++void adf_fbdev_destroy(struct adf_fbdev *fbdev); ++ ++int adf_fbdev_open(struct fb_info *info, int user); ++int adf_fbdev_release(struct fb_info *info, int user); ++int adf_fbdev_check_var(struct fb_var_screeninfo *var, struct fb_info *info); ++int adf_fbdev_set_par(struct fb_info *info); ++int adf_fbdev_blank(int blank, struct fb_info *info); ++int adf_fbdev_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); ++int adf_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma); ++#else ++static inline void adf_modeinfo_to_fb_videomode(const struct drm_mode_modeinfo *mode, ++ struct fb_videomode *vmode) ++{ ++ WARN_ONCE(1, "%s: CONFIG_ADF_FBDEV is disabled\n", __func__); ++} ++ ++static inline void adf_modeinfo_from_fb_videomode(const struct fb_videomode *vmode, ++ struct drm_mode_modeinfo *mode) ++{ ++ WARN_ONCE(1, "%s: CONFIG_ADF_FBDEV is disabled\n", __func__); ++} ++ ++static inline int adf_fbdev_init(struct adf_fbdev *fbdev, ++ struct adf_interface *interface, ++ struct adf_overlay_engine *eng, ++ u16 xres_virtual, u16 yres_virtual, u32 format, ++ struct fb_ops *fbops, const char *fmt, ...) ++{ ++ return -ENODEV; ++} ++ ++static inline void adf_fbdev_destroy(struct adf_fbdev *fbdev) { } ++ ++static inline int adf_fbdev_open(struct fb_info *info, int user) ++{ ++ return -ENODEV; ++} ++ ++static inline int adf_fbdev_release(struct fb_info *info, int user) ++{ ++ return -ENODEV; ++} ++ ++static inline int adf_fbdev_check_var(struct fb_var_screeninfo *var, ++ struct fb_info *info) ++{ ++ return -ENODEV; ++} ++ ++static inline int adf_fbdev_set_par(struct fb_info *info) ++{ ++ return -ENODEV; ++} ++ ++static inline int adf_fbdev_blank(int blank, struct fb_info *info) ++{ ++ return -ENODEV; ++} ++ ++static inline int adf_fbdev_pan_display(struct fb_var_screeninfo *var, ++ struct fb_info *info) ++{ ++ return -ENODEV; ++} ++ ++static inline int adf_fbdev_mmap(struct fb_info *info, ++ struct vm_area_struct *vma) ++{ ++ return -ENODEV; ++} ++#endif ++ ++#endif /* _VIDEO_ADF_FBDEV_H_ */ +diff --git a/include/video/adf_format.h b/include/video/adf_format.h +new file mode 100644 +index 0000000..e03182c +--- /dev/null ++++ b/include/video/adf_format.h +@@ -0,0 +1,26 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _VIDEO_ADF_FORMAT_H ++#define _VIDEO_ADF_FORMAT_H ++ ++bool adf_format_is_standard(u32 format); ++bool adf_format_is_rgb(u32 format); ++u8 adf_format_num_planes(u32 format); ++u8 adf_format_bpp(u32 format); ++u8 adf_format_plane_cpp(u32 format, int plane); ++u8 adf_format_horz_chroma_subsampling(u32 format); ++u8 adf_format_vert_chroma_subsampling(u32 format); ++ ++#endif /* _VIDEO_ADF_FORMAT_H */ +diff --git a/include/video/adf_memblock.h b/include/video/adf_memblock.h +new file mode 100644 +index 0000000..6256e0e +--- /dev/null ++++ b/include/video/adf_memblock.h +@@ -0,0 +1,20 @@ ++/* ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _VIDEO_ADF_MEMBLOCK_H_ ++#define _VIDEO_ADF_MEMBLOCK_H_ ++ ++struct dma_buf *adf_memblock_export(phys_addr_t base, size_t size, int flags); ++ ++#endif /* _VIDEO_ADF_MEMBLOCK_H_ */ +diff --git a/init/Kconfig b/init/Kconfig +index 2081a4d..3265800 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -199,6 +199,20 @@ config KERNEL_LZ4 + + endchoice + ++choice ++ prompt "Kernel decompress mode" ++ default SOFT_DECOMPRESS ++ depends on (ARCH_HI3559 || ARCH_HI3556) && HAVE_KERNEL_GZIP ++ help ++ select the decompress mode. ++config HW_DECOMPRESS ++ bool "hardware decompress" ++ help ++ select hardware decompress mode. ++config SOFT_DECOMPRESS ++ bool "soft decompress" ++endchoice ++ + config DEFAULT_HOSTNAME + string "Default hostname" + default "(none)" +diff --git a/init/Makefile b/init/Makefile +index 7bc47ee..692b91f 100644 +--- a/init/Makefile ++++ b/init/Makefile +@@ -3,11 +3,8 @@ + # + + obj-y := main.o version.o mounts.o +-ifneq ($(CONFIG_BLK_DEV_INITRD),y) + obj-y += noinitramfs.o +-else + obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o +-endif + obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o + + ifneq ($(CONFIG_ARCH_INIT_TASK),y) +diff --git a/init/initramfs.c b/init/initramfs.c +index ad1bd77..d39079e 100644 +--- a/init/initramfs.c ++++ b/init/initramfs.c +@@ -18,6 +18,7 @@ + #include <linux/dirent.h> + #include <linux/syscalls.h> + #include <linux/utime.h> ++#include <linux/initramfs.h> + + static ssize_t __init xwrite(int fd, const char *p, size_t count) + { +@@ -605,9 +606,25 @@ static void __init clean_rootfs(void) + } + #endif + ++static int __initdata do_skip_initramfs; ++ ++static int __init skip_initramfs_param(char *str) ++{ ++ if (*str) ++ return 0; ++ do_skip_initramfs = 1; ++ return 1; ++} ++__setup("skip_initramfs", skip_initramfs_param); ++ + static int __init populate_rootfs(void) + { +- char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size); ++ char *err; ++ ++ if (do_skip_initramfs) ++ return default_rootfs(); ++ ++ err = unpack_to_rootfs(__initramfs_start, __initramfs_size); + if (err) + panic("%s", err); /* Failed to decompress INTERNAL initramfs */ + if (initrd_start) { +diff --git a/init/noinitramfs.c b/init/noinitramfs.c +index 267739d..bcc8bcb 100644 +--- a/init/noinitramfs.c ++++ b/init/noinitramfs.c +@@ -21,11 +21,16 @@ + #include <linux/stat.h> + #include <linux/kdev_t.h> + #include <linux/syscalls.h> ++#include <linux/kconfig.h> ++#include <linux/initramfs.h> + + /* + * Create a simple rootfs that is similar to the default initramfs + */ +-static int __init default_rootfs(void) ++#if !IS_BUILTIN(CONFIG_BLK_DEV_INITRD) ++static ++#endif ++int __init default_rootfs(void) + { + int err; + +@@ -49,4 +54,6 @@ out: + printk(KERN_WARNING "Failed to create a rootfs\n"); + return err; + } ++#if !IS_BUILTIN(CONFIG_BLK_DEV_INITRD) + rootfs_initcall(default_rootfs); ++#endif +diff --git a/ipc/mqueue.c b/ipc/mqueue.c +index 4fcf39a..9c2d42c 100644 +--- a/ipc/mqueue.c ++++ b/ipc/mqueue.c +@@ -1244,8 +1244,10 @@ retry: + + timeo = MAX_SCHEDULE_TIMEOUT; + ret = netlink_attachskb(sock, nc, &timeo, NULL); +- if (ret == 1) ++ if (ret == 1) { ++ sock = NULL; + goto retry; ++ } + if (ret) { + sock = NULL; + nc = NULL; +diff --git a/kernel/cgroup.c b/kernel/cgroup.c +index 136ecea..30f1801 100644 +--- a/kernel/cgroup.c ++++ b/kernel/cgroup.c +@@ -2327,6 +2327,44 @@ static int cgroup_attach_task(struct cgroup *dst_cgrp, + return ret; + } + ++int subsys_cgroup_allow_attach(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) ++{ ++ const struct cred *cred = current_cred(), *tcred; ++ struct task_struct *task; ++ ++ if (capable(CAP_SYS_NICE)) ++ return 0; ++ ++ cgroup_taskset_for_each(task, tset) { ++ tcred = __task_cred(task); ++ ++ if (current != task && !uid_eq(cred->euid, tcred->uid) && ++ !uid_eq(cred->euid, tcred->suid)) ++ return -EACCES; ++ } ++ ++ return 0; ++} ++ ++static int cgroup_allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) ++{ ++ struct cgroup_subsys_state *css; ++ int i; ++ int ret; ++ ++ for_each_css(css, i, cgrp) { ++ if (css->ss->allow_attach) { ++ ret = css->ss->allow_attach(css, tset); ++ if (ret) ++ return ret; ++ } else { ++ return -EACCES; ++ } ++ } ++ ++ return 0; ++} ++ + /* + * Find the task_struct of the task to attach by vpid and pass it along to the + * function to attach either it or all tasks in its threadgroup. Will lock +@@ -2365,9 +2403,24 @@ retry_find_task: + if (!uid_eq(cred->euid, GLOBAL_ROOT_UID) && + !uid_eq(cred->euid, tcred->uid) && + !uid_eq(cred->euid, tcred->suid)) { +- rcu_read_unlock(); +- ret = -EACCES; +- goto out_unlock_cgroup; ++ /* ++ * if the default permission check fails, give each ++ * cgroup a chance to extend the permission check ++ */ ++ struct cgroup_taskset tset = { ++ .src_csets = LIST_HEAD_INIT(tset.src_csets), ++ .dst_csets = LIST_HEAD_INIT(tset.dst_csets), ++ .csets = &tset.src_csets, ++ }; ++ struct css_set *cset; ++ cset = task_css_set(tsk); ++ list_add(&cset->mg_node, &tset.src_csets); ++ ret = cgroup_allow_attach(cgrp, &tset); ++ list_del(&tset.src_csets); ++ if (ret) { ++ rcu_read_unlock(); ++ goto out_unlock_cgroup; ++ } + } + } else + tsk = current; +diff --git a/kernel/cpu.c b/kernel/cpu.c +index 90a3d01..2fb146b 100644 +--- a/kernel/cpu.c ++++ b/kernel/cpu.c +@@ -791,3 +791,23 @@ void init_cpu_online(const struct cpumask *src) + { + cpumask_copy(to_cpumask(cpu_online_bits), src); + } ++ ++static ATOMIC_NOTIFIER_HEAD(idle_notifier); ++ ++void idle_notifier_register(struct notifier_block *n) ++{ ++ atomic_notifier_chain_register(&idle_notifier, n); ++} ++EXPORT_SYMBOL_GPL(idle_notifier_register); ++ ++void idle_notifier_unregister(struct notifier_block *n) ++{ ++ atomic_notifier_chain_unregister(&idle_notifier, n); ++} ++EXPORT_SYMBOL_GPL(idle_notifier_unregister); ++ ++void idle_notifier_call_chain(unsigned long val) ++{ ++ atomic_notifier_call_chain(&idle_notifier, val, NULL); ++} ++EXPORT_SYMBOL_GPL(idle_notifier_call_chain); +diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c +index 1adf62b..af06122 100644 +--- a/kernel/debug/debug_core.c ++++ b/kernel/debug/debug_core.c +@@ -87,6 +87,10 @@ static int kgdb_use_con; + bool dbg_is_early = true; + /* Next cpu to become the master debug core */ + int dbg_switch_cpu; ++/* Flag for entering kdb when a panic occurs */ ++static bool break_on_panic = true; ++/* Flag for entering kdb when an exception occurs */ ++static bool break_on_exception = true; + + /* Use kdb or gdbserver mode */ + int dbg_kdb_mode = 1; +@@ -101,6 +105,8 @@ early_param("kgdbcon", opt_kgdb_con); + + module_param(kgdb_use_con, int, 0644); + module_param(kgdbreboot, int, 0644); ++module_param(break_on_panic, bool, 0644); ++module_param(break_on_exception, bool, 0644); + + /* + * Holds information about breakpoints in a kernel. These breakpoints are +@@ -690,6 +696,9 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs) + if (arch_kgdb_ops.enable_nmi) + arch_kgdb_ops.enable_nmi(0); + ++ if (unlikely(signo != SIGTRAP && !break_on_exception)) ++ return 1; ++ + memset(ks, 0, sizeof(struct kgdb_state)); + ks->cpu = raw_smp_processor_id(); + ks->ex_vector = evector; +@@ -821,6 +830,9 @@ static int kgdb_panic_event(struct notifier_block *self, + unsigned long val, + void *data) + { ++ if (!break_on_panic) ++ return NOTIFY_DONE; ++ + if (dbg_kdb_mode) + kdb_printf("PANIC: %s\n", (char *)data); + kgdb_breakpoint(); +diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c +index 7c70812..13a1e6b 100644 +--- a/kernel/debug/kdb/kdb_io.c ++++ b/kernel/debug/kdb/kdb_io.c +@@ -216,7 +216,7 @@ static char *kdb_read(char *buffer, size_t bufsize) + int i; + int diag, dtab_count; + int key; +- ++ static int last_crlf; + + diag = kdbgetintenv("DTABCOUNT", &dtab_count); + if (diag) +@@ -237,6 +237,9 @@ poll_again: + return buffer; + if (key != 9) + tab = 0; ++ if (key != 10 && key != 13) ++ last_crlf = 0; ++ + switch (key) { + case 8: /* backspace */ + if (cp > buffer) { +@@ -254,7 +257,12 @@ poll_again: + *cp = tmp; + } + break; +- case 13: /* enter */ ++ case 10: /* new line */ ++ case 13: /* carriage return */ ++ /* handle \n after \r */ ++ if (last_crlf && last_crlf != key) ++ break; ++ last_crlf = key; + *lastchar++ = '\n'; + *lastchar++ = '\0'; + if (!KDB_STATE(KGDB_TRANS)) { +diff --git a/kernel/events/core.c b/kernel/events/core.c +index cb86038..295c61b 100644 +--- a/kernel/events/core.c ++++ b/kernel/events/core.c +@@ -6749,7 +6749,6 @@ skip_type: + __perf_event_init_context(&cpuctx->ctx); + lockdep_set_class(&cpuctx->ctx.mutex, &cpuctx_mutex); + lockdep_set_class(&cpuctx->ctx.lock, &cpuctx_lock); +- cpuctx->ctx.type = cpu_context; + cpuctx->ctx.pmu = pmu; + + __perf_cpu_hrtimer_init(cpuctx, cpu); +@@ -7391,7 +7390,19 @@ SYSCALL_DEFINE5(perf_event_open, + * task or CPU context: + */ + if (move_group) { +- if (group_leader->ctx->type != ctx->type) ++ /* ++ * Make sure we're both on the same task, or both ++ * per-cpu events. ++ */ ++ if (group_leader->ctx->task != ctx->task) ++ goto err_context; ++ ++ /* ++ * Make sure we're both events for the same CPU; ++ * grouping events for different CPUs is broken; since ++ * you can never concurrently schedule them anyhow. ++ */ ++ if (group_leader->cpu != event->cpu) + goto err_context; + } else { + if (group_leader->ctx != ctx) +diff --git a/kernel/fork.c b/kernel/fork.c +index 9b7d746..d3a8141 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -201,6 +201,9 @@ struct kmem_cache *vm_area_cachep; + /* SLAB cache for mm_struct structures (tsk->mm) */ + static struct kmem_cache *mm_cachep; + ++/* Notifier list called when a task struct is freed */ ++static ATOMIC_NOTIFIER_HEAD(task_free_notifier); ++ + static void account_kernel_stack(struct thread_info *ti, int account) + { + struct zone *zone = page_zone(virt_to_page(ti)); +@@ -234,6 +237,18 @@ static inline void put_signal_struct(struct signal_struct *sig) + free_signal_struct(sig); + } + ++int task_free_register(struct notifier_block *n) ++{ ++ return atomic_notifier_chain_register(&task_free_notifier, n); ++} ++EXPORT_SYMBOL(task_free_register); ++ ++int task_free_unregister(struct notifier_block *n) ++{ ++ return atomic_notifier_chain_unregister(&task_free_notifier, n); ++} ++EXPORT_SYMBOL(task_free_unregister); ++ + void __put_task_struct(struct task_struct *tsk) + { + WARN_ON(!tsk->exit_state); +@@ -246,6 +261,7 @@ void __put_task_struct(struct task_struct *tsk) + delayacct_tsk_free(tsk); + put_signal_struct(tsk->signal); + ++ atomic_notifier_call_chain(&task_free_notifier, 0, tsk); + if (!profile_handoff_task(tsk)) + free_task(tsk); + } +@@ -735,7 +751,8 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) + + mm = get_task_mm(task); + if (mm && mm != current->mm && +- !ptrace_may_access(task, mode)) { ++ !ptrace_may_access(task, mode) && ++ !capable(CAP_SYS_RESOURCE)) { + mmput(mm); + mm = ERR_PTR(-EACCES); + } +@@ -1287,6 +1304,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, + + p->utime = p->stime = p->gtime = 0; + p->utimescaled = p->stimescaled = 0; ++ p->cpu_power = 0; + #ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE + p->prev_cputime.utime = p->prev_cputime.stime = 0; + #endif +diff --git a/kernel/kmod.c b/kernel/kmod.c +index 80f7a6d..784e4b1 100644 +--- a/kernel/kmod.c ++++ b/kernel/kmod.c +@@ -496,7 +496,22 @@ static void helper_unlock(void) + if (atomic_dec_and_test(&running_helpers)) + wake_up(&running_helpers_waitq); + } ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++static void helper_lock_force(void) ++{ ++ atomic_inc(&running_helpers); ++ smp_mb(); ++} ++ ++static void helper_unlock_force(void) ++{ ++ int tmp; + ++ tmp = atomic_dec_and_test(&running_helpers); ++ if (tmp && (usermodehelper_disabled == 0)) ++ wake_up(&running_helpers_waitq); ++} ++#endif + /** + * call_usermodehelper_setup - prepare to call a usermode helper + * @path: path to usermode executable +@@ -615,6 +630,47 @@ unlock: + } + EXPORT_SYMBOL(call_usermodehelper_exec); + ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++int call_usermodehelper_exec_force(struct subprocess_info *sub_info, int wait) ++{ ++ DECLARE_COMPLETION_ONSTACK(done); ++ int retval = 0; ++ ++ helper_lock_force(); ++ ++ if (sub_info->path[0] == '\0') ++ goto out; ++ ++ if (!khelper_wq) { ++ retval = -EBUSY; ++ goto out; ++ } ++ ++ sub_info->complete = &done; ++ sub_info->wait = wait; ++ ++ queue_work(khelper_wq, &sub_info->work); ++ if (wait == UMH_NO_WAIT) /* task has freed sub_info */ ++ goto unlock; ++ wait_for_completion(&done); ++ retval = sub_info->retval; ++ ++out: ++ call_usermodehelper_freeinfo(sub_info); ++unlock: ++ helper_unlock_force(); ++ ++ return retval; ++} ++#else ++int call_usermodehelper_exec_force(struct subprocess_info *sub_info, int wait) ++{ ++ return 0; ++} ++ ++#endif ++EXPORT_SYMBOL(call_usermodehelper_exec_force); ++ + /** + * call_usermodehelper() - prepare and start a usermode application + * @path: path to usermode executable +diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c +index dadbf88..4541951 100644 +--- a/kernel/locking/mutex.c ++++ b/kernel/locking/mutex.c +@@ -378,8 +378,14 @@ done: + * reschedule now, before we try-lock the mutex. This avoids getting + * scheduled out right after we obtained the mutex. + */ +- if (need_resched()) ++ if (need_resched()) { ++ /* ++ * We _should_ have TASK_RUNNING here, but just in case ++ * we do not, make it so, otherwise we might get stuck. ++ */ ++ __set_current_state(TASK_RUNNING); + schedule_preempt_disabled(); ++ } + + return false; + } +diff --git a/kernel/panic.c b/kernel/panic.c +index cf80672..8bba197 100644 +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -23,10 +23,14 @@ + #include <linux/sysrq.h> + #include <linux/init.h> + #include <linux/nmi.h> ++#include <linux/console.h> + + #define PANIC_TIMER_STEP 100 + #define PANIC_BLINK_SPD 18 + ++/* Machine specific panic information string */ ++char *mach_panic_string; ++ + int panic_on_oops = CONFIG_PANIC_ON_OOPS_VALUE; + static unsigned long tainted_mask; + static int pause_on_oops; +@@ -145,6 +149,15 @@ void panic(const char *fmt, ...) + + bust_spinlocks(0); + ++ /* ++ * We may have ended up stopping the CPU holding the lock (in ++ * smp_send_stop()) while still having some valuable data in the console ++ * buffer. Try to acquire the lock then release it regardless of the ++ * result. The release will also print the buffers out. ++ */ ++ console_trylock(); ++ console_unlock(); ++ + if (!panic_blink) + panic_blink = no_blink; + +@@ -396,6 +409,11 @@ late_initcall(init_oops_id); + void print_oops_end_marker(void) + { + init_oops_id(); ++ ++ if (mach_panic_string) ++ printk(KERN_WARNING "Board Information: %s\n", ++ mach_panic_string); ++ + pr_warn("---[ end trace %016llx ]---\n", (unsigned long long)oops_id); + } + +diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig +index c2ae461..13f2f07 100644 +--- a/kernel/power/Kconfig ++++ b/kernel/power/Kconfig +@@ -18,15 +18,76 @@ config SUSPEND_FREEZER + + Turning OFF this setting is NOT recommended! If in doubt, say Y. + ++config WAKELOCK ++ bool "Android's method of preventing suspend" ++ default y ++ ---help--- ++ This allows applications to prevent the CPU from suspending while ++ they need it. ++ ++ Say Y if you are running an android userspace. ++ + config HIBERNATE_CALLBACKS + bool + ++config HISI_SNAPSHOT_BOOT ++ bool "snapshot fastboot Support" ++ select HIBERNATION ++ help ++ Enable the suspend to disk (STD) functionality, which is usually ++ called "hibernation" in user interfaces. STD checkpoints the ++ system and powers it off; and restores that checkpoint on reboot. ++ ++ You can suspend your machine with 'echo disk > /sys/power/state' ++ after placing resume=/dev/swappartition on the kernel command line ++ in your bootloader's configuration file. ++ ++choice ++ prompt "image store type" ++ depends on HISI_SNAPSHOT_BOOT ++ default DEFAULT_DDR ++ help ++ Select the I/O scheduler which will be used by default for all ++ block devices. ++ ++ config DEFAULT_MTD ++ bool "use MTD partition" ++ help ++ Note there is currently not a way to specify ++ which device to save the suspended image to. ++ It will simply pick the first available swap ++ device. ++ ++ config DEFAULT_DDR ++ bool "use ddr memory" ++ help ++ Note there is currently not a way to specify ++ which device to save the suspended image to. ++ It will save imgae to ddr and then write to ++ mtdblock in uboot. ++ ++endchoice ++ ++ ++if DEFAULT_DDR ++config SNAPSHOT_BUF_START ++ hex "snapshot buffer start addr" ++ default "0x8a000000" ++ ++config SNAPSHOT_BUF_SIZE ++ hex "snapshot buffer size" ++ default "0x4000000" ++endif ++ ++ ++ + config HIBERNATION + bool "Hibernation (aka 'suspend to disk')" +- depends on SWAP && ARCH_HIBERNATION_POSSIBLE ++ depends on SWAP && ARCH_HIBERNATION_POSSIBLE || HISI_SNAPSHOT_BOOT + select HIBERNATE_CALLBACKS + select LZO_COMPRESS + select LZO_DECOMPRESS ++ select GZIP_COMPRESS + select CRC32 + ---help--- + Enable the suspend to disk (STD) functionality, which is usually +@@ -66,6 +127,14 @@ config HIBERNATION + + For more information take a look at <file:Documentation/power/swsusp.txt>. + ++config GZIP_COMPRESS ++ bool ++ depends on HISI_SNAPSHOT_BOOT && HIBERNATION ++ ---help--- ++ Allow the system to compress the hibernation image by gzip method, so that ++ upon the next boot, the u-boot can use the hardware method to decompress the ++ saved image. ++ + config ARCH_SAVE_PAGE_KEYS + bool + +@@ -83,7 +152,7 @@ config PM_STD_PARTITION + + The partition specified can be overridden by specifying: + +- resume=/dev/<other device> ++ hbcomp=/dev/<other device> + + which will set the resume partition to the device specified. + +@@ -309,3 +378,10 @@ config PM_GENERIC_DOMAINS_OF + config CPU_PM + bool + depends on SUSPEND || CPU_IDLE ++ ++config SUSPEND_TIME ++ bool "Log time spent in suspend" ++ ---help--- ++ Prints the time spent in suspend in the kernel log, and ++ keeps statistics on the time spent in suspend in ++ /sys/kernel/debug/suspend_time +diff --git a/kernel/power/Makefile b/kernel/power/Makefile +index 29472bf..d08db66 100644 +--- a/kernel/power/Makefile ++++ b/kernel/power/Makefile +@@ -8,8 +8,14 @@ obj-$(CONFIG_FREEZER) += process.o + obj-$(CONFIG_SUSPEND) += suspend.o + obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o + obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ +- block_io.o ++ block_io.o crc_check.o + obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o + obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o ++obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o + + obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o ++ ++obj-$(CONFIG_SUSPEND) += wakeup_reason.o ++ ++ccflags-$(CONFIG_HISI_SNAPSHOT_BOOT) += -DUSE_FIXED_SMALL_CHUNK ++obj-$(CONFIG_HISI_SNAPSHOT_BOOT) += compress.o compress_wrapper.o +diff --git a/kernel/power/compress.c b/kernel/power/compress.c +new file mode 100644 +index 0000000..48064be +--- /dev/null ++++ b/kernel/power/compress.c +@@ -0,0 +1,425 @@ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/fs.h> ++#include <linux/mount.h> ++#include <linux/genhd.h> ++#include <linux/lzo.h> ++#include <linux/vmalloc.h> ++#include <asm/io.h> ++#include <asm/cacheflush.h> ++#include <mach/io.h> ++#include <mach/platform.h> ++#include <linux/of_address.h> ++#include <linux/platform_device.h> ++ ++#include "power.h" ++#include "compress.h" ++ ++extern int ++find_bootmem(const char *name, unsigned int *ofs, unsigned int *sz); ++ ++#define BOOTLOADER_ADMA_SIDE_JOB ++ ++#define __ALIGN_UP(x,y) (((x)+(y)-1) & (~((y)-1))) ++#define PAGE_NR(x) (((x) + PAGE_SIZE - 1) / PAGE_SIZE) ++ ++#ifdef BOOTLOADER_ADMA_SIDE_JOB ++/* guessing ratio started with (5+5)M:2M */ ++#define COMP_SRC_SZ_DEFAULT (SZ_1M * 5U) ++#define COMP_DST_SZ_DEFAULT (SZ_1M * 2U) ++#else ++#define COMP_SRC_SZ_DEFAULT (SZ_1M * 9U) ++#define COMP_DST_SZ_DEFAULT (SZ_1M * 3U) ++#endif ++ ++ ++#ifdef CONFIG_DEFAULT_MTD ++ ++#define SNAPSHOT_BUF_START (PLAT_PHYS_OFFSET + (128<<20UL)) ++#define SNAPSHOT_BUF_SIZE (2<<20UL) ++ ++static int hbpart_read(struct compress_writer *writer, ++ sector_t page_i, sector_t page_nr, void *buf) ++{ ++ struct block_device *bdev_orig = hib_resume_bdev; ++ sector_t p; ++ int ret; ++ ++ bdev_orig = hib_resume_bdev; ++ hib_resume_bdev = writer->bdev; ++ for (p = 0; p < page_nr; p++, page_i++, buf += PAGE_SIZE) { ++ ret = hib_bio_read_page(page_i, writer->pagebuf, NULL); ++ if (ret < 0) { ++ hib_resume_bdev = bdev_orig; ++ return ret; ++ } ++ memcpy(buf, writer->pagebuf, PAGE_SIZE); ++ } ++ hib_resume_bdev = bdev_orig; ++ return (int)p; ++} ++ ++static int hbpart_write(struct compress_writer *writer, ++ sector_t page_i, sector_t page_nr, const void *buf) ++{ ++ struct block_device *bdev_orig = hib_resume_bdev; ++ sector_t p; ++ int ret; ++ ++ bdev_orig = hib_resume_bdev; ++ hib_resume_bdev = writer->bdev; ++ for (p = 0; p < page_nr; p++, page_i++, buf += PAGE_SIZE) { ++ memcpy(writer->pagebuf, buf, PAGE_SIZE); ++ ret = hib_bio_write_page(page_i, writer->pagebuf, NULL); ++ if (ret < 0) { ++ hib_resume_bdev = bdev_orig; ++ return ret; ++ } ++ } ++ hib_resume_bdev = bdev_orig; ++ return (int)p; ++} ++ ++static int susp_writer_finish(struct compress_writer *writer) ++{ ++ /*do nothing*/ ++ return 0; ++} ++ ++#else ++ ++#define SNAPSHOT_BUF_START (CONFIG_SNAPSHOT_BUF_START) ++#define SNAPSHOT_BUF_SIZE (CONFIG_SNAPSHOT_BUF_SIZE) ++ ++static void __iomem *g_reg_sysctrl_base = NULL; ++ ++static int hbpart_read(struct compress_writer *writer, ++ sector_t page_i, sector_t page_nr, void *buf) ++{ ++ sector_t p; ++ ++ sector_t total_pages; ++ ++ if (!writer) ++ return -EINVAL; ++ ++ total_pages = writer->image_buf_sz/PAGE_SIZE; ++ ++ if ((page_i + page_nr) > total_pages) ++ return -EINVAL; ++ ++ for (p = 0; p < page_nr; p++, page_i++, buf += PAGE_SIZE) ++ memcpy(buf, (writer->image + page_i*PAGE_SIZE), PAGE_SIZE); ++ ++ return (int)p; ++} ++ ++static int hbpart_write(struct compress_writer *writer, ++ sector_t page_i, sector_t page_nr, const void *buf) ++{ ++ sector_t p; ++ ++ sector_t total_pages; ++ ++ if (!writer) ++ return -EINVAL; ++ ++ total_pages = writer->image_buf_sz/PAGE_SIZE; ++ ++ if ((page_i + page_nr) > total_pages) { ++ printk(KERN_INFO "%s write data exceed!\n", __func__); ++ return -EINVAL; ++ } ++ ++ for (p = 0; p < page_nr; p++, page_i++, buf += PAGE_SIZE) ++ memcpy((writer->image + page_i*PAGE_SIZE), buf, PAGE_SIZE); ++ ++ flush_cache_all(); ++ ++ return (int)p; ++} ++ ++static int susp_writer_finish(struct compress_writer *writer) ++{ ++ unsigned long image_addr, image_sz; ++ ++ union sscomp_header *sscomp = &writer->sscomp; ++ const struct sscomp_block *block = &sscomp->b.blocks[0]; ++ int total_blocks = sscomp->info.meta_blocks + sscomp->info.data_blocks; ++ int total_org_size = 0; ++ int total_comp_size = 0; ++ int i; ++ ++#define SNAPSHOT_MAGIC 0x53414e50 /*'S''N''A''P'*/ ++ ++ if (!g_reg_sysctrl_base) { ++ pr_err("%s: g_reg_sysctrl_base is NULL, error!\n", __func__); ++ WARN_ON(!g_reg_sysctrl_base); ++ } ++ ++ writel(SNAPSHOT_MAGIC, g_reg_sysctrl_base + 0xa4); ++ ++ image_addr = SNAPSHOT_BUF_START + ++ ((unsigned long)writer->image - (unsigned long)writer); ++ ++ writel(image_addr, g_reg_sysctrl_base + 0xa8); ++ ++ for (i = 0; i < total_blocks; i++) { ++ unsigned int aligned_comp_size; ++ aligned_comp_size = ++ (block->comp_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); ++ total_org_size += block->org_size; ++ total_comp_size += aligned_comp_size; ++ printk(KERN_INFO "block[%2d]: %9d:%d(%d)\n", i, ++ block->org_size, block->comp_size, aligned_comp_size); ++ block++; ++ } ++ ++ printk(KERN_INFO "original: %9d bytes\n", total_org_size); ++ printk(KERN_INFO "compressed: %9d bytes\n", total_comp_size); ++ ++ image_sz = __ALIGN_UP(total_comp_size+sizeof(*sscomp), SZ_1M); ++ writel(image_sz, g_reg_sysctrl_base + 0xac); ++ ++ return 0; ++} ++ ++#endif ++ ++static int do_compress(struct compress_writer *writer, ++ struct sscomp_block *block, unsigned int *src_len) ++{ ++ void *src = (void*)writer->src; ++ void *dst = (void*)writer->dst; ++ size_t tmp_slen, tmp_dlen; ++ const unsigned int slen = *src_len; ++ int ret, nr_compressed_pages; ++ bool succeed = false; ++ struct compress_wrapper *compressor = writer->compressor; ++ void *work_mem = (void*)writer->work_mem; ++ ++ unsigned int sb_len = COMP_SRC_SZ_DEFAULT; ++ unsigned int db_len = COMP_DST_SZ_DEFAULT; ++ ++ do { ++ printk(KERN_DEBUG "[comp: guessing %u:%u]\n", sb_len, db_len); ++ tmp_slen = min_t(size_t, slen, sb_len); ++ ret = compressor->compress( ++ dst, &tmp_dlen, src, tmp_slen, 0, work_mem); ++ if (ret >= 0 && tmp_dlen <= db_len) { ++ succeed = true; ++ break; ++ } ++ sb_len -= SZ_1K * 512U; ++ db_len += SZ_1K * 512U; ++ } while (sb_len >= db_len); ++ ++ if (!succeed) ++ return -EFAULT; ++ ++ printk(KERN_INFO "[comp: %08u -> %08u]\n", tmp_slen, tmp_dlen); ++ ++ block->org_size = tmp_slen; ++ block->comp_size = tmp_dlen; ++ ++ nr_compressed_pages = (tmp_dlen + PAGE_SIZE - 1) / PAGE_SIZE; ++ ++ *src_len = slen - tmp_slen; ++ /* backup before verifying */ ++ if (*src_len) ++ memmove(dst + writer->dst_buf_sz - *src_len, ++ src + tmp_slen, *src_len); ++ ++ /* restore after verifying */ ++ if (*src_len) ++ memmove(src, dst + writer->dst_buf_sz - *src_len, *src_len); ++ ++ return nr_compressed_pages; ++} ++ ++void put_susp_compress_writer(struct compress_writer *writer) ++{ ++ if (!writer) ++ return; ++ ++ if (writer->pagebuf) ++ free_page((unsigned long)writer->pagebuf); ++ writer->pagebuf = NULL; ++ ++ if (writer->bdev) ++ blkdev_put(writer->bdev, FMODE_WRITE); ++ writer->bdev = NULL; ++ ++ iounmap(writer); ++} ++ ++struct compress_writer *get_susp_compress_writer(const char *path) ++{ ++ struct compress_writer *writer; ++ struct compress_wrapper *compressor; ++ struct block_device *bdev; ++ const char *d; ++ char spath[64], dname[BDEVNAME_SIZE]; ++ dev_t device; ++ int ret; ++ ++ BUILD_BUG_ON(sizeof(writer->sscomp.info) > sizeof(writer->sscomp.b.c)); ++ BUILD_BUG_ON(sizeof(writer->sscomp.b) > sizeof(writer->sscomp.c)); ++ BUILD_BUG_ON(sizeof(writer->sscomp) % PAGE_SIZE); ++ ++ compressor = hb_bdev_find_compressor_by_name(compress_method); ++ if (!compressor) { ++ printk(KERN_ERR "%s: could not found compressor %s\n", ++ __func__, compress_method); ++ return NULL; ++ } ++ ++ if (!path || path[0] == '\0') { ++ printk(KERN_ERR "%s: invalid backing device path.\n", __func__); ++ return NULL; ++ } ++ ++ strncpy(spath, path, sizeof(spath)); ++ if (strncmp(spath, "/dev/block/", 11) == 0) ++ strcpy(spath + 5, spath + 11); ++ device = name_to_dev_t(spath); ++ if (device == MKDEV(0, 0)) { ++ printk(KERN_ERR "%s: could not found %s.\n", __func__, path); ++ return NULL; ++ } ++ ++ bdev = bdget(device); ++ if (!bdev) { ++ printk(KERN_ERR "%s: could not ref %s.\n", __func__, path); ++ return NULL; ++ } ++ bdev = bdgrab(bdev); ++ bdput(bdev); ++ ++ ret = blkdev_get(bdev, FMODE_WRITE, NULL); ++ if (ret) { ++ printk(KERN_ERR "%s: could not get %s.\n", __func__, path); ++ bdput(bdev); ++ return NULL; ++ } ++ ++ if (strncmp(spath, "/dev/", 5) == 0) ++ strcpy(spath, spath + 5); ++ d = bdevname(bdev, dname); ++ if (strcmp(spath, d)) { ++ printk(KERN_ERR "%s: something wrong %s != %s.\n", __func__, ++ spath, d); ++ blkdev_put(bdev, FMODE_WRITE); ++ return NULL; ++ } ++ ++ ret = set_blocksize(bdev, PAGE_SIZE); ++ if (ret < 0) { ++ printk(KERN_ERR "%s: %s does not support PAGE_SIZE IO.\n", ++ __func__, path); ++ blkdev_put(bdev, FMODE_WRITE); ++ return NULL; ++ } ++ ++ /* do not use kmalloc/vmalloc/etc. */ ++ writer = ioremap_cache(SNAPSHOT_BUF_START, SNAPSHOT_BUF_SIZE); ++ if (!writer) { ++ blkdev_put(bdev, FMODE_WRITE); ++ return NULL; ++ } ++ ++ printk(KERN_INFO "ioremap phys:0x%x to 0x%lx\n", ++ SNAPSHOT_BUF_START , (unsigned long)writer); ++ ++ memset(writer, 0, sizeof(*writer)); ++ ++ writer->compressor = compressor; ++ ++ writer->work_mem = __ALIGN_UP( ++ (unsigned long)writer + sizeof(*writer), PAGE_SIZE); ++#ifdef USE_FIXED_SMALL_CHUNK ++ writer->src_buf_sz = SZ_1K * 512; ++ writer->dst_buf_sz = SZ_1K * 512; ++#else ++ /* it should be allocated as src:dst=10M:9M fixed. */ ++ writer->src_buf_sz = SZ_1M * 10U; ++ writer->dst_buf_sz = SZ_1M * 9U; ++#endif ++ ++ writer->src = __ALIGN_UP(writer->work_mem + 1U, SZ_1M); ++ writer->dst = writer->src + writer->src_buf_sz; ++ ++#ifdef CONFIG_DEFAULT_MTD ++ if (writer->dst + writer->dst_buf_sz > ++ (unsigned long)writer + SNAPSHOT_BUF_SIZE) { ++ printk(KERN_ERR "%s: not enough space\n", __func__); ++ put_susp_compress_writer(writer); ++ return NULL; ++ } ++ printk(KERN_INFO "PM: compress: using dev %s(%u:%u)\n", ++ d, MAJOR(device), MINOR(device)); ++#else ++ writer->image = (void *)__ALIGN_UP( ++ writer->dst + writer->dst_buf_sz + 1U, SZ_1M); ++ writer->image_buf_sz = SNAPSHOT_BUF_SIZE - (5<<20UL); ++ ++ if ((unsigned long)writer->image + writer->image_buf_sz > ++ (unsigned long)writer + SNAPSHOT_BUF_SIZE) { ++ printk(KERN_ERR "%s: not enough space\n", __func__); ++ put_susp_compress_writer(writer); ++ return NULL; ++ } ++ ++ printk(KERN_INFO "PM: compress: using ddr, image:0x%x, image_buf_size:%d\n", ++ (unsigned int)writer->image, writer->image_buf_sz); ++#endif ++ writer->bdev = bdev; ++ writer->device = device; ++ writer->compress = do_compress; ++ writer->write = hbpart_write; ++ writer->read = hbpart_read; ++ writer->finish = susp_writer_finish; ++ writer->pagebuf = (void*)get_zeroed_page(GFP_KERNEL); ++ ++ writer->sscomp.info.comp_type = compressor->type; ++ writer->sscomp.info.image_ofs = PAGE_NR(sizeof(writer->sscomp)); ++#ifndef USE_FIXED_SMALL_CHUNK ++ writer->sscomp.info.block_type = COMP_BLK_DYNAMIC; ++#else ++ writer->sscomp.info.decomp_block_size = writer->src_buf_sz; ++ writer->sscomp.info.block_type = COMP_BLK_FIXED; ++#endif ++ ++ return writer; ++} ++ ++static int hi_pm_hibernate_probe(struct platform_device *pdev) ++{ ++ g_reg_sysctrl_base = of_iomap(pdev->dev.of_node, 0); ++ if (WARN_ON(!g_reg_sysctrl_base)) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static const struct of_device_id hi_pm_hibernate_match_table[] = { ++ { .compatible = "hisilicon,hibvt-pm-hibernate" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, hi_pm_hibernate_match_table); ++ ++static struct platform_driver hi_pm_hibernate_driver = { ++ .probe = hi_pm_hibernate_probe, ++ .driver = { ++ .name = "hibvt-pm-hibernate", ++ .of_match_table = hi_pm_hibernate_match_table, ++ }, ++}; ++ ++static int __init hi_pm_hibernate_init(void) ++{ ++ return platform_driver_register(&hi_pm_hibernate_driver); ++} ++core_initcall(hi_pm_hibernate_init); ++ ++MODULE_LICENSE("GPL v2"); +diff --git a/kernel/power/compress.h b/kernel/power/compress.h +new file mode 100644 +index 0000000..3c8f8fa +--- /dev/null ++++ b/kernel/power/compress.h +@@ -0,0 +1,96 @@ ++#ifndef __KEREL_POWER_COMPRESS_H_ ++#define __KEREL_POWER_COMPRESS_H_ ++ ++struct sscomp_block { ++ unsigned int org_size; ++ unsigned int comp_size; ++}; ++ ++union sscomp_header { ++ struct { ++#define COMP_TYPE_LZF 1 ++#define COMP_TYPE_LZO 2 ++#define COMP_TYPE_LZMA 3 ++#define COMP_TYPE_BZIP2 4 ++#define COMP_TYPE_SNAPPY 5 ++#define COMP_TYPE_QUICKLZ 6 ++#define COMP_TYPE_GZIP 7 ++ int comp_type; ++ ++ int meta_blocks; ++ int meta_pages; ++ int data_blocks; ++ int data_pages; ++ unsigned int decomp_block_size; ++ int image_ofs; ++ ++ int crc32; ++ ++#define SSCOMP_MAGIC_4 "SSCOMP#4" ++ char magic[8]; ++ unsigned char sha1sum[20]; ++ ++#define COMP_BLK_LEGACY 0 ++#define COMP_BLK_FIXED 1 ++#define COMP_BLK_DYNAMIC 2 ++ int block_type; ++ ++ unsigned long ctl_func; ++ unsigned long ctl_data; ++ } info __attribute__((packed)); ++ struct { ++ unsigned char c[128]; ++#define MAX_COMP_BLOCKS ((4096-128)/sizeof(struct sscomp_block)) ++ struct sscomp_block blocks[MAX_COMP_BLOCKS]; ++ } b __attribute__((packed)); ++ unsigned char c[4096]; ++}; ++ ++struct compress_wrapper { ++ const char *name; ++ int type; ++ int (*compress)(unsigned char *dst, int *dstlen, ++ unsigned char *src, int srclen, int factor, void *extra); ++ int (*decompress)(unsigned char *dst, int *dstlen, ++ unsigned char *src, int srclen, void *extra); ++}; ++ ++struct compress_writer { ++ union sscomp_header sscomp; ++ ++ unsigned long work_mem; ++ unsigned long src; ++ unsigned long dst; ++ unsigned int src_buf_sz; ++ unsigned int dst_buf_sz; ++ unsigned int image_buf_sz; ++ ++ struct block_device *bdev; ++ dev_t device; ++ ++ void *image; ++ void *pagebuf; ++ ++ struct compress_wrapper *compressor; ++ ++ int (*compress)(struct compress_writer *, struct sscomp_block *, ++ unsigned int *); ++ int (*decompress)(struct compress_writer *, struct sscomp_block *, ++ unsigned int *); ++ int (*write)(struct compress_writer *, sector_t page_i, ++ sector_t page_nr, const void *); ++ int (*read)(struct compress_writer *, sector_t page_i, ++ sector_t page_nr, void *buf); ++ int (*finish)(struct compress_writer *); ++}; ++ ++struct compress_wrapper *hb_bdev_find_compressor_by_name(const char *name); ++ ++extern void put_susp_compress_writer(struct compress_writer *); ++extern struct compress_writer *get_susp_compress_writer(const char *); ++ ++extern int hb_bdev_sha1_init(void); ++extern int hb_bdev_sha1_update(const u8 *data, unsigned int len); ++extern int hb_bdev_sha1_final(u8 *out); ++ ++#endif /* __KEREL_POWER_COMPRESS_H_ */ +diff --git a/kernel/power/compress_wrapper.c b/kernel/power/compress_wrapper.c +new file mode 100644 +index 0000000..93fe8f7 +--- /dev/null ++++ b/kernel/power/compress_wrapper.c +@@ -0,0 +1,164 @@ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/err.h> ++#include <linux/lzo.h> ++ ++#include <crypto/hash.h> ++ ++#include "compress.h" ++ ++/* I do not want to concern about {k|v}malloc something. */ ++#include <crypto/sha.h> ++#include "../../crypto/internal.h" ++#include <crypto/internal/hash.h> ++#ifdef USE_SHA1 ++static union my_desc_s { ++ struct shash_desc desc; ++ struct { ++ unsigned char c[sizeof(struct shash_desc)]; ++ struct sha1_state sha1_state; ++ } p; ++} my_desc; ++static struct shash_alg *sha1_hash; ++ ++static void hb_bdev_find_hash_sha1(void) ++{ ++ struct crypto_alg *hash_alg; ++ hash_alg = crypto_find_alg("sha1", &crypto_ahash_type, ++ CRYPTO_ALG_TYPE_HASH, CRYPTO_ALG_TYPE_AHASH_MASK); ++ if (IS_ERR_OR_NULL(hash_alg)) { ++ pr_err("%s: could not found sha1.\n", __func__); ++ sha1_hash = NULL; ++ } ++ sha1_hash = container_of(hash_alg, struct shash_alg, base); ++} ++ ++int hb_bdev_sha1_init(void) ++{ ++ if (!sha1_hash) { ++ pr_err("%s: could not found sha1.\n", __func__); ++ return -1; ++ } ++ return sha1_hash->init(&my_desc.desc); ++} ++ ++int hb_bdev_sha1_update(const u8 *data, unsigned int len) ++{ ++ if (!sha1_hash) { ++ pr_err("%s: could not found sha1.\n", __func__); ++ return -1; ++ } ++ return sha1_hash->update(&my_desc.desc, data, len); ++} ++ ++int hb_bdev_sha1_final(u8 *out) ++{ ++ if (!sha1_hash) { ++ pr_err("%s: could not found sha1.\n", __func__); ++ return -1; ++ } ++ return sha1_hash->final(&my_desc.desc, out); ++} ++#else ++ ++static inline void hb_bdev_find_hash_sha1(void) {} ++ ++inline int hb_bdev_sha1_init(void){return 0; } ++ ++inline int hb_bdev_sha1_update(const u8 *data, unsigned int len) {return 0; } ++ ++inline int hb_bdev_sha1_final(u8 *out) {return 0; } ++ ++#endif ++ ++ ++#if defined(CONFIG_LZO_COMPRESS) && defined(CONFIG_LZO_DECOMPRESS) ++static int lzo_compress(unsigned char *dst, int *dstlen, ++ unsigned char *src, int srclen, int unused, void *wrkmem) ++{ ++ size_t tmp_slen, tmp_dlen; ++ int ret; ++ ++ tmp_slen = srclen; ++ tmp_dlen = *dstlen; ++ ret = lzo1x_1_compress(src, tmp_slen, dst, &tmp_dlen, wrkmem); ++ if (ret != LZO_E_OK) { ++ pr_err("lzo1x_compress(): failed %d\n", ret); ++ return -1; ++ } ++ *dstlen = tmp_dlen; ++ return 0; ++} ++ ++static int lzo_decompress(unsigned char *dst, int *dstlen, ++ unsigned char *src, int srclen, void *unused) ++{ ++ size_t tmp_slen, tmp_dlen; ++ int ret; ++ tmp_slen = srclen; ++ tmp_dlen = *dstlen; ++ ret = lzo1x_decompress_safe(src, tmp_slen, dst, &tmp_dlen); ++ if (ret != LZO_E_OK) { ++ pr_err("lzo1x_decompress(): failed %d\n", ret); ++ return -1; ++ } ++ *dstlen = tmp_dlen; ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_GZIP_COMPRESS ++extern int gzip_defalte(void *dst, unsigned int *dst_len, const void *src, unsigned int len); ++ ++static int gzip_compress(unsigned char *dst, int *dstlen, ++ unsigned char *src, int srclen, int unused, void *wrkmem) ++{ ++ return gzip_defalte(dst, dstlen, src, srclen); ++} ++ ++/*static int lzma_decompress(unsigned char *dst, int *dstlen, ++ unsigned char *src, int srclen, void *unused) ++{ ++ return Lzma_Decode(dst, dstlen, src, srclen); ++}*/ ++#endif ++ ++static struct compress_wrapper compressor[] = { ++#if defined(CONFIG_LZO_COMPRESS) && defined(CONFIG_LZO_DECOMPRESS) ++ { ++ .name = "lzo", ++ .type = COMP_TYPE_LZO, ++ .compress = lzo_compress, ++ .decompress = lzo_decompress, ++ }, ++#ifdef CONFIG_GZIP_COMPRESS ++ { ++ .name = "gzip", ++ .type = COMP_TYPE_GZIP, ++ .compress = gzip_compress, ++ /*.decompress = lzma_decompress,*/ ++ }, ++#endif ++ ++#else ++#error "there is no compressor" ++#endif ++}; ++ ++struct compress_wrapper *hb_bdev_find_compressor_by_name(const char *name) ++{ ++ int i; ++ struct compress_wrapper *compress_wrapper; ++ ++ printk("%s:%d, compressor name = %s\n", __func__, __LINE__, name); ++ ++ hb_bdev_find_hash_sha1(); ++ ++ for (i = 0; i < ARRAY_SIZE(compressor); i++) { ++ compress_wrapper = &compressor[i]; ++ if (strcmp(name, compress_wrapper->name) == 0) ++ return compress_wrapper; ++ } ++ ++ return NULL; ++} +diff --git a/kernel/power/crc_check.c b/kernel/power/crc_check.c +new file mode 100644 +index 0000000..8964c1c +--- /dev/null ++++ b/kernel/power/crc_check.c +@@ -0,0 +1,250 @@ ++/* ++ * This file is derived from crc32.c from the zlib-1.1.3 distribution ++ * by Jean-loup Gailly and Mark Adler. ++ */ ++ ++/* crc32.c -- compute the CRC-32 of a data stream ++ * Copyright (C) 1995-1998 Mark Adler ++ * For conditions of distribution and use, see copyright notice in zlib.h ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/string.h> ++#include <linux/version.h> ++#include <linux/init.h> ++ ++#define local static ++#define ZEXPORT /* empty */ ++ ++#define tole(x) cpu_to_le32(x) ++ ++#define __BYTE_ORDER __LITTLE_ENDIAN ++ ++#undef DYNAMIC_CRC_TABLE ++ ++#ifdef DYNAMIC_CRC_TABLE ++ ++local int crc_table_empty = 1; ++local uint32_t crc_table[256]; ++local void make_crc_table OF((void)); ++ ++/* ++ Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: ++ x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. ++ ++ Polynomials over GF(2) are represented in binary, one bit per coefficient, ++ with the lowest powers in the most significant bit. Then adding polynomials ++ is just exclusive-or, and multiplying a polynomial by x is a right shift by ++ one. If we call the above polynomial p, and represent a byte as the ++ polynomial q, also with the lowest power in the most significant bit (so the ++ byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, ++ where a mod b means the remainder after dividing a by b. ++ ++ This calculation is done using the shift-register method of multiplying and ++ taking the remainder. The register is initialized to zero, and for each ++ incoming bit, x^32 is added mod p to the register if the bit is a one (where ++ x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by ++ x (which is shifting right by one and adding x^32 mod p if the bit shifted ++ out is a one). We start with the highest power (least significant bit) of ++ q and repeat for all eight bits of q. ++ ++ The table is simply the CRC of all possible eight bit values. This is all ++ the information needed to generate CRC's on data a byte at a time for all ++ combinations of CRC register values and incoming bytes. ++*/ ++local void make_crc_table() ++{ ++ uint32_t c; ++ int n, k; ++ uLong poly; /* polynomial exclusive-or pattern */ ++ /* terms of polynomial defining this crc (except x^32): */ ++ static const Byte p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; ++ ++ /* make exclusive-or pattern from polynomial (0xedb88320L) */ ++ poly = 0L; ++ for (n = 0; n < sizeof(p)/sizeof(Byte); n++) ++ poly |= 1L << (31 - p[n]); ++ ++ for (n = 0; n < 256; n++) ++ { ++ c = (uLong)n; ++ for (k = 0; k < 8; k++) ++ c = c & 1 ? poly ^ (c >> 1) : c >> 1; ++ crc_table[n] = tole(c); ++ } ++ crc_table_empty = 0; ++} ++#else ++/* ======================================================================== ++ * Table of CRC-32's of all single-byte values (made by make_crc_table) ++ */ ++ ++local const uint32_t crc_table[256] = { ++tole(0x00000000L), tole(0x77073096L), tole(0xee0e612cL), tole(0x990951baL), ++tole(0x076dc419L), tole(0x706af48fL), tole(0xe963a535L), tole(0x9e6495a3L), ++tole(0x0edb8832L), tole(0x79dcb8a4L), tole(0xe0d5e91eL), tole(0x97d2d988L), ++tole(0x09b64c2bL), tole(0x7eb17cbdL), tole(0xe7b82d07L), tole(0x90bf1d91L), ++tole(0x1db71064L), tole(0x6ab020f2L), tole(0xf3b97148L), tole(0x84be41deL), ++tole(0x1adad47dL), tole(0x6ddde4ebL), tole(0xf4d4b551L), tole(0x83d385c7L), ++tole(0x136c9856L), tole(0x646ba8c0L), tole(0xfd62f97aL), tole(0x8a65c9ecL), ++tole(0x14015c4fL), tole(0x63066cd9L), tole(0xfa0f3d63L), tole(0x8d080df5L), ++tole(0x3b6e20c8L), tole(0x4c69105eL), tole(0xd56041e4L), tole(0xa2677172L), ++tole(0x3c03e4d1L), tole(0x4b04d447L), tole(0xd20d85fdL), tole(0xa50ab56bL), ++tole(0x35b5a8faL), tole(0x42b2986cL), tole(0xdbbbc9d6L), tole(0xacbcf940L), ++tole(0x32d86ce3L), tole(0x45df5c75L), tole(0xdcd60dcfL), tole(0xabd13d59L), ++tole(0x26d930acL), tole(0x51de003aL), tole(0xc8d75180L), tole(0xbfd06116L), ++tole(0x21b4f4b5L), tole(0x56b3c423L), tole(0xcfba9599L), tole(0xb8bda50fL), ++tole(0x2802b89eL), tole(0x5f058808L), tole(0xc60cd9b2L), tole(0xb10be924L), ++tole(0x2f6f7c87L), tole(0x58684c11L), tole(0xc1611dabL), tole(0xb6662d3dL), ++tole(0x76dc4190L), tole(0x01db7106L), tole(0x98d220bcL), tole(0xefd5102aL), ++tole(0x71b18589L), tole(0x06b6b51fL), tole(0x9fbfe4a5L), tole(0xe8b8d433L), ++tole(0x7807c9a2L), tole(0x0f00f934L), tole(0x9609a88eL), tole(0xe10e9818L), ++tole(0x7f6a0dbbL), tole(0x086d3d2dL), tole(0x91646c97L), tole(0xe6635c01L), ++tole(0x6b6b51f4L), tole(0x1c6c6162L), tole(0x856530d8L), tole(0xf262004eL), ++tole(0x6c0695edL), tole(0x1b01a57bL), tole(0x8208f4c1L), tole(0xf50fc457L), ++tole(0x65b0d9c6L), tole(0x12b7e950L), tole(0x8bbeb8eaL), tole(0xfcb9887cL), ++tole(0x62dd1ddfL), tole(0x15da2d49L), tole(0x8cd37cf3L), tole(0xfbd44c65L), ++tole(0x4db26158L), tole(0x3ab551ceL), tole(0xa3bc0074L), tole(0xd4bb30e2L), ++tole(0x4adfa541L), tole(0x3dd895d7L), tole(0xa4d1c46dL), tole(0xd3d6f4fbL), ++tole(0x4369e96aL), tole(0x346ed9fcL), tole(0xad678846L), tole(0xda60b8d0L), ++tole(0x44042d73L), tole(0x33031de5L), tole(0xaa0a4c5fL), tole(0xdd0d7cc9L), ++tole(0x5005713cL), tole(0x270241aaL), tole(0xbe0b1010L), tole(0xc90c2086L), ++tole(0x5768b525L), tole(0x206f85b3L), tole(0xb966d409L), tole(0xce61e49fL), ++tole(0x5edef90eL), tole(0x29d9c998L), tole(0xb0d09822L), tole(0xc7d7a8b4L), ++tole(0x59b33d17L), tole(0x2eb40d81L), tole(0xb7bd5c3bL), tole(0xc0ba6cadL), ++tole(0xedb88320L), tole(0x9abfb3b6L), tole(0x03b6e20cL), tole(0x74b1d29aL), ++tole(0xead54739L), tole(0x9dd277afL), tole(0x04db2615L), tole(0x73dc1683L), ++tole(0xe3630b12L), tole(0x94643b84L), tole(0x0d6d6a3eL), tole(0x7a6a5aa8L), ++tole(0xe40ecf0bL), tole(0x9309ff9dL), tole(0x0a00ae27L), tole(0x7d079eb1L), ++tole(0xf00f9344L), tole(0x8708a3d2L), tole(0x1e01f268L), tole(0x6906c2feL), ++tole(0xf762575dL), tole(0x806567cbL), tole(0x196c3671L), tole(0x6e6b06e7L), ++tole(0xfed41b76L), tole(0x89d32be0L), tole(0x10da7a5aL), tole(0x67dd4accL), ++tole(0xf9b9df6fL), tole(0x8ebeeff9L), tole(0x17b7be43L), tole(0x60b08ed5L), ++tole(0xd6d6a3e8L), tole(0xa1d1937eL), tole(0x38d8c2c4L), tole(0x4fdff252L), ++tole(0xd1bb67f1L), tole(0xa6bc5767L), tole(0x3fb506ddL), tole(0x48b2364bL), ++tole(0xd80d2bdaL), tole(0xaf0a1b4cL), tole(0x36034af6L), tole(0x41047a60L), ++tole(0xdf60efc3L), tole(0xa867df55L), tole(0x316e8eefL), tole(0x4669be79L), ++tole(0xcb61b38cL), tole(0xbc66831aL), tole(0x256fd2a0L), tole(0x5268e236L), ++tole(0xcc0c7795L), tole(0xbb0b4703L), tole(0x220216b9L), tole(0x5505262fL), ++tole(0xc5ba3bbeL), tole(0xb2bd0b28L), tole(0x2bb45a92L), tole(0x5cb36a04L), ++tole(0xc2d7ffa7L), tole(0xb5d0cf31L), tole(0x2cd99e8bL), tole(0x5bdeae1dL), ++tole(0x9b64c2b0L), tole(0xec63f226L), tole(0x756aa39cL), tole(0x026d930aL), ++tole(0x9c0906a9L), tole(0xeb0e363fL), tole(0x72076785L), tole(0x05005713L), ++tole(0x95bf4a82L), tole(0xe2b87a14L), tole(0x7bb12baeL), tole(0x0cb61b38L), ++tole(0x92d28e9bL), tole(0xe5d5be0dL), tole(0x7cdcefb7L), tole(0x0bdbdf21L), ++tole(0x86d3d2d4L), tole(0xf1d4e242L), tole(0x68ddb3f8L), tole(0x1fda836eL), ++tole(0x81be16cdL), tole(0xf6b9265bL), tole(0x6fb077e1L), tole(0x18b74777L), ++tole(0x88085ae6L), tole(0xff0f6a70L), tole(0x66063bcaL), tole(0x11010b5cL), ++tole(0x8f659effL), tole(0xf862ae69L), tole(0x616bffd3L), tole(0x166ccf45L), ++tole(0xa00ae278L), tole(0xd70dd2eeL), tole(0x4e048354L), tole(0x3903b3c2L), ++tole(0xa7672661L), tole(0xd06016f7L), tole(0x4969474dL), tole(0x3e6e77dbL), ++tole(0xaed16a4aL), tole(0xd9d65adcL), tole(0x40df0b66L), tole(0x37d83bf0L), ++tole(0xa9bcae53L), tole(0xdebb9ec5L), tole(0x47b2cf7fL), tole(0x30b5ffe9L), ++tole(0xbdbdf21cL), tole(0xcabac28aL), tole(0x53b39330L), tole(0x24b4a3a6L), ++tole(0xbad03605L), tole(0xcdd70693L), tole(0x54de5729L), tole(0x23d967bfL), ++tole(0xb3667a2eL), tole(0xc4614ab8L), tole(0x5d681b02L), tole(0x2a6f2b94L), ++tole(0xb40bbe37L), tole(0xc30c8ea1L), tole(0x5a05df1bL), tole(0x2d02ef8dL) ++}; ++#endif ++ ++#if 0 ++/* ========================================================================= ++ * This function can be used by asm versions of crc32() ++ */ ++const uint32_t * ZEXPORT get_crc_table() ++{ ++#ifdef DYNAMIC_CRC_TABLE ++ if (crc_table_empty) make_crc_table(); ++#endif ++ return (const uint32_t *)crc_table; ++} ++#endif ++ ++/* ========================================================================= */ ++ ++# if __BYTE_ORDER == __LITTLE_ENDIAN ++# define DO_CRC(x) crc = tab[(crc ^ (x)) & 255] ^ (crc >> 8) ++# else ++# define DO_CRC(x) crc = tab[((crc >> 24) ^ (x)) & 255] ^ (crc << 8) ++# endif ++ ++/* ========================================================================= */ ++ ++/* No ones complement version. JFFS2 (and other things ?) ++ * don't use ones compliment in their CRC calculations. ++ */ ++uint32_t ZEXPORT crc32_no_comp(uint32_t crc, const unsigned char *buf, uint32_t len) ++{ ++ const uint32_t *tab = crc_table; ++ const uint32_t *b =(const uint32_t *)buf; ++ size_t rem_len; ++#ifdef DYNAMIC_CRC_TABLE ++ if (crc_table_empty) ++ make_crc_table(); ++#endif ++ crc = cpu_to_le32(crc); ++ /* Align it */ ++ if (((long)b) & 3 && len) { ++ uint8_t *p = (uint8_t *)b; ++ do { ++ DO_CRC(*p++); ++ } while ((--len) && ((long)p)&3); ++ b = (uint32_t *)p; ++ } ++ ++ rem_len = len & 3; ++ len = len >> 2; ++ for (--b; len; --len) { ++ /* load data 32 bits wide, xor data 32 bits wide. */ ++ crc ^= *++b; /* use pre increment for speed */ ++ DO_CRC(0); ++ DO_CRC(0); ++ DO_CRC(0); ++ DO_CRC(0); ++ } ++ len = rem_len; ++ /* And the last few bytes */ ++ if (len) { ++ uint8_t *p = (uint8_t *)(b + 1) - 1; ++ do { ++ DO_CRC(*++p); /* use pre increment for speed */ ++ } while (--len); ++ } ++ ++ return le32_to_cpu(crc); ++} ++#undef DO_CRC ++ ++uint32_t ZEXPORT crc32_check(uint32_t crc, const unsigned char *p, uint32_t len) ++{ ++ return crc32_no_comp(crc ^ 0xffffffffL, p, len) ^ 0xffffffffL; ++} ++ ++/* ++ * Calculate the crc32 checksum triggering the watchdog every 'chunk_sz' bytes ++ * of input. ++ */ ++uint32_t ZEXPORT crc32_wd (uint32_t crc, ++ const unsigned char *buf, ++ uint32_t len, uint32_t chunk_sz) ++{ ++#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) ++ const unsigned char *end, *curr; ++ int chunk; ++ ++ curr = buf; ++ end = buf + len; ++ while (curr < end) { ++ chunk = end - curr; ++ if (chunk > chunk_sz) ++ chunk = chunk_sz; ++ crc = crc32_check (crc, curr, chunk); ++ curr += chunk; ++ /*WATCHDOG_RESET ();*/ ++ } ++#else ++ crc = crc32_check(crc, buf, len); ++#endif ++ ++ return crc; ++} +diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c +index 1f35a34..5016852 100644 +--- a/kernel/power/hibernate.c ++++ b/kernel/power/hibernate.c +@@ -41,7 +41,16 @@ static unsigned int resume_delay; + static char resume_file[256] = CONFIG_PM_STD_PARTITION; + dev_t swsusp_resume_device; + sector_t swsusp_resume_block; ++ ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++int noshrink; ++char hb_bdev_file[64] = CONFIG_PM_STD_PARTITION; ++//char compress_method[16] = "lzo"; ++char compress_method[16] = "gzip"; ++volatile unsigned long in_suspend; ++#else + __visible int in_suspend __nosavedata; ++#endif + + enum { + HIBERNATION_INVALID, +@@ -668,8 +677,11 @@ int hibernate(void) + printk(KERN_INFO "PM: Syncing filesystems ... "); + sys_sync(); + printk("done.\n"); +- ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ error = suspend_freeze_processes(); ++#else + error = freeze_processes(); ++#endif + if (error) + goto Exit; + +@@ -708,7 +720,11 @@ int hibernate(void) + free_basic_memory_bitmaps(); + Thaw: + unlock_device_hotplug(); ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ suspend_thaw_processes(); ++#else + thaw_processes(); ++#endif + + /* Don't bother checking whether freezer_test_done is true */ + freezer_test_done = false; +@@ -831,7 +847,11 @@ static int software_resume(void) + goto Close_Finish; + + pr_debug("PM: Preparing processes for restore.\n"); ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ error = suspend_freeze_processes(); ++#else + error = freeze_processes(); ++#endif + if (error) + goto Close_Finish; + +@@ -852,7 +872,11 @@ static int software_resume(void) + free_basic_memory_bitmaps(); + Thaw: + unlock_device_hotplug(); ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ suspend_thaw_processes(); ++#else + thaw_processes(); ++#endif + Finish: + pm_notifier_call_chain(PM_POST_RESTORE); + pm_restore_console(); +@@ -866,9 +890,9 @@ static int software_resume(void) + swsusp_close(FMODE_READ); + goto Finish; + } +- ++#ifndef CONFIG_HISI_SNAPSHOT_BOOT + late_initcall_sync(software_resume); +- ++#endif + + static const char * const hibernation_modes[] = { + [HIBERNATION_PLATFORM] = "platform", +@@ -1046,6 +1070,33 @@ static ssize_t image_size_store(struct kobject *kobj, struct kobj_attribute *att + + power_attr(image_size); + ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++static ssize_t compress_method_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%s\n", compress_method); ++} ++static ssize_t compress_method_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ return -EINVAL; ++} ++power_attr(compress_method); ++static ssize_t hb_bdev_file_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%s\n", hb_bdev_file); ++} ++static ssize_t hb_bdev_file_store(struct kobject *kobj, ++ struct kobj_attribute *attr, ++ const char *buf, size_t n) ++{ ++ return -EINVAL; ++} ++power_attr(hb_bdev_file); ++#endif ++ + static ssize_t reserved_size_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) + { +@@ -1073,6 +1124,10 @@ static struct attribute * g[] = { + &resume_attr.attr, + &image_size_attr.attr, + &reserved_size_attr.attr, ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ &compress_method_attr.attr, ++ &hb_bdev_file_attr.attr, ++#endif + NULL, + }; + +@@ -1158,6 +1213,16 @@ static int __init kaslr_nohibernate_setup(char *str) + return nohibernate_setup(str); + } + ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++static int __init hb_bdev_setup(char *str) ++{ ++ strncpy(hb_bdev_file, str, sizeof(hb_bdev_file) - 1); ++ return 1; ++} ++ ++__setup("hbcomp=", hb_bdev_setup); ++#endif ++ + __setup("noresume", noresume_setup); + __setup("resume_offset=", resume_offset_setup); + __setup("resume=", resume_setup); +diff --git a/kernel/power/power.h b/kernel/power/power.h +index 2df883a..0e802b9 100644 +--- a/kernel/power/power.h ++++ b/kernel/power/power.h +@@ -81,10 +81,21 @@ static struct kobj_attribute _name##_attr = { \ + extern unsigned long image_size; + /* Size of memory reserved for drivers (default SPARE_PAGES x PAGE_SIZE) */ + extern unsigned long reserved_size; ++#ifndef CONFIG_HISI_SNAPSHOT_BOOT + extern int in_suspend; ++#endif + extern dev_t swsusp_resume_device; + extern sector_t swsusp_resume_block; + ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++extern char hb_bdev_file[64]; ++extern char compress_method[16]; ++extern void *saved_processor_context; ++ ++extern volatile unsigned long in_suspend; ++extern int noshrink; ++extern int swsusp_check_storage_all(void); ++#endif + extern asmlinkage int swsusp_arch_suspend(void); + extern asmlinkage int swsusp_arch_resume(void); + +@@ -243,7 +254,7 @@ static inline int suspend_freeze_processes(void) + */ + if (error) + return error; +- ++#ifndef CONFIG_HISI_SNAPSHOT_BOOT + error = freeze_kernel_threads(); + /* + * freeze_kernel_threads() thaws only kernel threads upon freezing +@@ -252,11 +263,18 @@ static inline int suspend_freeze_processes(void) + if (error) + thaw_processes(); + ++#else ++ error = pm_notifier_call_chain(PM_POST_FREEZE_PROCESS); ++#endif ++ + return error; + } + + static inline void suspend_thaw_processes(void) + { ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ pm_notifier_call_chain(PM_THAW_PROCESS_PREPARE); ++#endif + thaw_processes(); + } + #else +diff --git a/kernel/power/process.c b/kernel/power/process.c +index 5a6ec86..7456dcb 100644 +--- a/kernel/power/process.c ++++ b/kernel/power/process.c +@@ -18,6 +18,7 @@ + #include <linux/workqueue.h> + #include <linux/kmod.h> + #include <trace/events/power.h> ++#include <linux/wakeup_reason.h> + + /* + * Timeout for stopping processes +@@ -35,6 +36,9 @@ static int try_to_freeze_tasks(bool user_only) + unsigned int elapsed_msecs; + bool wakeup = false; + int sleep_usecs = USEC_PER_MSEC; ++#ifdef CONFIG_PM_SLEEP ++ char suspend_abort[MAX_SUSPEND_ABORT_LEN]; ++#endif + + do_gettimeofday(&start); + +@@ -64,6 +68,11 @@ static int try_to_freeze_tasks(bool user_only) + break; + + if (pm_wakeup_pending()) { ++#ifdef CONFIG_PM_SLEEP ++ pm_get_active_wakeup_sources(suspend_abort, ++ MAX_SUSPEND_ABORT_LEN); ++ log_suspend_abort_reason(suspend_abort); ++#endif + wakeup = true; + break; + } +@@ -83,15 +92,17 @@ static int try_to_freeze_tasks(bool user_only) + do_div(elapsed_msecs64, NSEC_PER_MSEC); + elapsed_msecs = elapsed_msecs64; + +- if (todo) { ++ if (wakeup) { + printk("\n"); +- printk(KERN_ERR "Freezing of tasks %s after %d.%03d seconds " +- "(%d tasks refusing to freeze, wq_busy=%d):\n", +- wakeup ? "aborted" : "failed", ++ printk(KERN_ERR "Freezing of tasks aborted after %d.%03d seconds", ++ elapsed_msecs / 1000, elapsed_msecs % 1000); ++ } else if (todo) { ++ printk("\n"); ++ printk(KERN_ERR "Freezing of tasks failed after %d.%03d seconds" ++ " (%d tasks refusing to freeze, wq_busy=%d):\n", + elapsed_msecs / 1000, elapsed_msecs % 1000, + todo - wq_busy, wq_busy); + +- if (!wakeup) { + read_lock(&tasklist_lock); + for_each_process_thread(g, p) { + if (p != current && !freezer_should_skip(p) +@@ -99,7 +110,6 @@ static int try_to_freeze_tasks(bool user_only) + sched_show_task(p); + } + read_unlock(&tasklist_lock); +- } + } else { + printk("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000, + elapsed_msecs % 1000); +diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c +index 7325e35..7999fa0 100644 +--- a/kernel/power/snapshot.c ++++ b/kernel/power/snapshot.c +@@ -28,6 +28,9 @@ + #include <linux/list.h> + #include <linux/slab.h> + #include <linux/compiler.h> ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++#include <linux/writeback.h> ++#endif + + #include <asm/uaccess.h> + #include <asm/mmu_context.h> +@@ -733,6 +736,19 @@ static void memory_bm_clear_current(struct memory_bitmap *bm) + clear_bit(bit, bm->cur.node->data); + } + ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++static int mem_bm_clear_bit_check(struct memory_bitmap *bm, unsigned long pfn) ++{ ++ void *addr; ++ unsigned int bit; ++ int error; ++ ++ error = memory_bm_find_bit(bm, pfn, &addr, &bit); ++ if (!error) ++ clear_bit(bit, addr); ++ return error; ++} ++#endif + static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn) + { + void *addr; +@@ -828,6 +844,73 @@ struct nosave_region { + unsigned long start_pfn; + unsigned long end_pfn; + }; ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++/* used for pmem pfn save */ ++static LIST_HEAD(save_regions); ++static DEFINE_SPINLOCK(save_spin); ++ ++void *register_save_region(unsigned long start_pfn, unsigned long end_pfn) ++{ ++ struct nosave_region *region; ++ region = kmalloc(sizeof(*region), GFP_KERNEL); ++ BUG_ON(!region); ++ ++ region->start_pfn = start_pfn; ++ region->end_pfn = end_pfn; ++ ++ spin_lock(&save_spin); ++ list_add_tail(®ion->list, &save_regions); ++ spin_unlock(&save_spin); ++ ++ return region; ++} ++EXPORT_SYMBOL(register_save_region); ++ ++void unregister_save_region(void *mem) ++{ ++ struct nosave_region *region = mem; ++ ++ if (!region) ++ return; ++ ++ spin_lock(&save_spin); ++ list_del(®ion->list); ++ spin_unlock(&save_spin); ++ ++ kfree(mem); ++} ++EXPORT_SYMBOL(unregister_save_region); ++ ++static LIST_HEAD(nosave_regions_runtime); ++static DEFINE_SPINLOCK(nosave_spin); ++ ++void *register_nosave_region_runtime(unsigned long start_pfn, ++ unsigned long end_pfn) ++{ ++ struct nosave_region *region; ++ region = kmalloc(sizeof(struct nosave_region), GFP_KERNEL); ++ BUG_ON(!region); ++ region->start_pfn = start_pfn; ++ region->end_pfn = end_pfn; ++ spin_lock(&nosave_spin); ++ list_add_tail(®ion->list, &nosave_regions_runtime); ++ spin_unlock(&nosave_spin); ++ return region; ++} ++EXPORT_SYMBOL(register_nosave_region_runtime); ++ ++void unregister_nosave_region_runtime(void *mem) ++{ ++ struct nosave_region *region = mem; ++ if (!region) ++ return; ++ spin_lock(&nosave_spin); ++ list_del(®ion->list); ++ spin_unlock(&nosave_spin); ++ kfree(mem); ++} ++EXPORT_SYMBOL(unregister_nosave_region_runtime); ++#endif + + static LIST_HEAD(nosave_regions); + +@@ -940,8 +1023,11 @@ static void mark_nosave_pages(struct memory_bitmap *bm) + (unsigned long long) region->start_pfn << PAGE_SHIFT, + ((unsigned long long) region->end_pfn << PAGE_SHIFT) + - 1); +- ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ for (pfn = region->start_pfn; pfn <= region->end_pfn; pfn++) ++#else + for (pfn = region->start_pfn; pfn < region->end_pfn; pfn++) ++#endif + if (pfn_valid(pfn)) { + /* + * It is safe to ignore the result of +@@ -953,7 +1039,49 @@ static void mark_nosave_pages(struct memory_bitmap *bm) + } + } + } ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++/*used for pmem pages mark. maybe useless for us*/ ++static void mark_snapshot_pages_again(void) ++{ ++ struct nosave_region *region; ++ struct memory_bitmap *bm = forbidden_pages_map; ++ ++ if (list_empty(&save_regions)) ++ goto proc_nosave; ++ ++ spin_lock(&save_spin); ++ list_for_each_entry(region, &save_regions, list) { ++ unsigned long pfn; ++ ++ pr_info("PM: Marking save pages: %016lx - %016lx\n", ++ region->start_pfn << PAGE_SHIFT, ++ region->end_pfn << PAGE_SHIFT); ++ ++ for (pfn = region->start_pfn; pfn <= region->end_pfn; pfn++) ++ if (pfn_valid(pfn)) ++ mem_bm_clear_bit_check(bm, pfn); ++ } ++ spin_unlock(&save_spin); ++ ++proc_nosave: ++ if (list_empty(&nosave_regions_runtime)) ++ return; ++ ++ spin_lock(&nosave_spin); ++ list_for_each_entry(region, &nosave_regions_runtime, list) { ++ unsigned long pfn; ++ ++ pr_info("PM: Marking runtime nosave pages: %016lx - %016lx\n", ++ region->start_pfn << PAGE_SHIFT, ++ region->end_pfn << PAGE_SHIFT); + ++ for (pfn = region->start_pfn; pfn <= region->end_pfn; pfn++) ++ if (pfn_valid(pfn)) ++ mem_bm_set_bit_check(bm, pfn); ++ } ++ spin_unlock(&nosave_spin); ++} ++#endif + /** + * create_basic_memory_bitmaps - create bitmaps needed for marking page + * frames that should not be saved and free page frames. The pointers +@@ -1070,6 +1198,11 @@ static unsigned int count_free_highmem_pages(void) + return cnt; + } + ++#ifdef CONFIG_CMA ++#ifndef CONFIG_CMA_ADVANCE_SHARE ++extern int hicma_page_free(unsigned long pfn); ++#endif ++#endif + /** + * saveable_highmem_page - Determine whether a highmem page should be + * included in the suspend image. +@@ -1096,7 +1229,13 @@ static struct page *saveable_highmem_page(struct zone *zone, unsigned long pfn) + + if (page_is_guard(page)) + return NULL; ++#ifdef CONFIG_CMA ++#ifndef CONFIG_CMA_ADVANCE_SHARE ++ if (hicma_page_free(pfn)) ++ return NULL; + ++#endif ++#endif + return page; + } + +@@ -1161,7 +1300,13 @@ static struct page *saveable_page(struct zone *zone, unsigned long pfn) + + if (page_is_guard(page)) + return NULL; ++#ifdef CONFIG_CMA ++#ifndef CONFIG_CMA_ADVANCE_SHARE ++ if (hicma_page_free(pfn)) ++ return NULL; + ++#endif ++#endif + return page; + } + +@@ -1452,9 +1597,9 @@ static inline unsigned long preallocate_highmem_fraction(unsigned long nr_pages, + /** + * free_unnecessary_pages - Release preallocated pages not needed for the image + */ +-static void free_unnecessary_pages(void) ++static unsigned long free_unnecessary_pages(void) + { +- unsigned long save, to_free_normal, to_free_highmem; ++ unsigned long save, to_free_normal, to_free_highmem, free; + + save = count_data_pages(); + if (alloc_normal >= save) { +@@ -1476,6 +1621,8 @@ static void free_unnecessary_pages(void) + to_free_normal = 0; + } + ++ free = to_free_normal + to_free_highmem; ++ + memory_bm_position_reset(©_bm); + + while (to_free_normal > 0 || to_free_highmem > 0) { +@@ -1498,8 +1645,31 @@ static void free_unnecessary_pages(void) + swsusp_unset_page_free(page); + __free_page(page); + } +-} + ++ return free; ++} ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++/* ++ ** Kick the writeback threads then try to free up some ZONE_NORMAL memory. ++ **/ ++static void free_more_memory(void) ++{ ++ struct zone *zone; ++ int nid; ++ ++ wakeup_flusher_threads(1024, WB_REASON_FREE_MORE_MEM); ++ yield(); ++ ++ for_each_online_node(nid) { ++ (void)first_zones_zonelist(node_zonelist(nid, GFP_NOFS), ++ gfp_zone(GFP_NOFS), NULL, ++ &zone); ++ if (zone) ++ try_to_free_pages(node_zonelist(nid, GFP_NOFS), 0, ++ GFP_NOFS, NULL); ++ } ++} ++#endif + /** + * minimum_image_size - Estimate the minimum acceptable size of an image + * @saveable: Number of saveable pages in the system. +@@ -1559,7 +1729,15 @@ int hibernate_preallocate_memory(void) + unsigned long alloc, save_highmem, pages_highmem, avail_normal; + struct timeval start, stop; + int error; ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ bool compress; ++ unsigned long min_image_size, prev_image_size; + ++ /* this will try to free some pages in the normal zone */ ++ free_more_memory(); ++ ++ mark_snapshot_pages_again(); ++#endif + printk(KERN_INFO "PM: Preallocating image memory... "); + do_gettimeofday(&start); + +@@ -1573,7 +1751,52 @@ int hibernate_preallocate_memory(void) + + alloc_normal = 0; + alloc_highmem = 0; ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ compress = swsusp_check_storage_all() == 1 ? true : false; ++ ++ if (noshrink) ++ goto skip_reclaim; + ++ /* calculates the snapshot image size */ ++ prev_image_size = min_image_size = ++ global_page_state(NR_SLAB_RECLAIMABLE) ++ + global_page_state(NR_ACTIVE_ANON) ++ + global_page_state(NR_INACTIVE_ANON) ++ + global_page_state(NR_ACTIVE_FILE) ++ + global_page_state(NR_INACTIVE_FILE) ++ - global_page_state(NR_FILE_MAPPED); ++ ++ /* Count the number of saveable data pages. ++ * Does page reclamation till min_image_size becomes constant ++ */ ++ do { ++ save_highmem = count_highmem_pages(); ++ saveable = count_data_pages(); ++ saveable += save_highmem; ++ ++ shrink_all_memory(saveable); ++ ++ min_image_size = global_page_state(NR_SLAB_RECLAIMABLE) ++ + global_page_state(NR_ACTIVE_ANON) ++ + global_page_state(NR_INACTIVE_ANON) ++ + global_page_state(NR_ACTIVE_FILE) ++ + global_page_state(NR_INACTIVE_FILE) ++ - global_page_state(NR_FILE_MAPPED); ++ ++ if (prev_image_size == min_image_size) ++ break; ++ else ++ prev_image_size = min_image_size; ++ } while (saveable > min_image_size); ++ ++skip_reclaim: ++ /* ++ * Compute the total number of page frames we can use (count) and the ++ * number of pages needed for image metadata (size). ++ */ ++ save_highmem = count_highmem_pages(); ++ saveable = count_data_pages(); ++#else + /* Count the number of saveable data pages. */ + save_highmem = count_highmem_pages(); + saveable = count_data_pages(); +@@ -1582,6 +1805,7 @@ int hibernate_preallocate_memory(void) + * Compute the total number of page frames we can use (count) and the + * number of pages needed for image metadata (size). + */ ++#endif + count = saveable; + saveable += save_highmem; + highmem = save_highmem; +@@ -1604,6 +1828,12 @@ int hibernate_preallocate_memory(void) + max_size = (count - (size + PAGES_FOR_IO)) / 2 + - 2 * DIV_ROUND_UP(reserved_size, PAGE_SIZE); + /* Compute the desired number of image pages specified by image_size. */ ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ if (compress) ++ /* very rough : may accomplish 40% of original */ ++ size = DIV_ROUND_UP(image_size * 5 / 2, PAGE_SIZE); ++ else ++#endif + size = DIV_ROUND_UP(image_size, PAGE_SIZE); + if (size > max_size) + size = max_size; +@@ -1638,8 +1868,16 @@ int hibernate_preallocate_memory(void) + * NOTE: If this is not done, performance will be hurt badly in some + * test cases. + */ ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ /* REVISIT : fail-safe case. ++ * * is this needed ? do you really want this ? ++ * * it may try to free all of page-cache. ++ * */ ++ if (!noshrink) ++ shrink_all_memory(saveable); ++#else + shrink_all_memory(saveable - size); +- ++#endif + /* + * The number of saveable pages in memory was too high, so apply some + * pressure to decrease it. First, make room for the largest possible +@@ -1687,7 +1925,7 @@ int hibernate_preallocate_memory(void) + * pages in memory, but we have allocated more. Release the excessive + * ones now. + */ +- free_unnecessary_pages(); ++ pages -= free_unnecessary_pages(); + + out: + do_gettimeofday(&stop); +diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c +index c347e3c..a212348 100644 +--- a/kernel/power/suspend.c ++++ b/kernel/power/suspend.c +@@ -26,8 +26,10 @@ + #include <linux/suspend.h> + #include <linux/syscore_ops.h> + #include <linux/ftrace.h> ++#include <linux/rtc.h> + #include <trace/events/power.h> + #include <linux/compiler.h> ++#include <linux/wakeup_reason.h> + + #include "power.h" + +@@ -271,7 +273,8 @@ void __weak arch_suspend_enable_irqs(void) + */ + static int suspend_enter(suspend_state_t state, bool *wakeup) + { +- int error; ++ char suspend_abort[MAX_SUSPEND_ABORT_LEN]; ++ int error, last_dev; + + error = platform_suspend_prepare(state); + if (error) +@@ -279,7 +282,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) + + error = dpm_suspend_late(PMSG_SUSPEND); + if (error) { ++ last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; ++ last_dev %= REC_FAILED_NUM; + printk(KERN_ERR "PM: late suspend of devices failed\n"); ++ log_suspend_abort_reason("%s device failed to power down", ++ suspend_stats.failed_devs[last_dev]); + goto Platform_finish; + } + error = platform_suspend_prepare_late(state); +@@ -288,7 +295,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) + + error = dpm_suspend_noirq(PMSG_SUSPEND); + if (error) { ++ last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; ++ last_dev %= REC_FAILED_NUM; + printk(KERN_ERR "PM: noirq suspend of devices failed\n"); ++ log_suspend_abort_reason("noirq suspend of %s device failed", ++ suspend_stats.failed_devs[last_dev]); + goto Platform_early_resume; + } + error = platform_suspend_prepare_noirq(state); +@@ -312,8 +323,10 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) + } + + error = disable_nonboot_cpus(); +- if (error || suspend_test(TEST_CPUS)) ++ if (error || suspend_test(TEST_CPUS)) { ++ log_suspend_abort_reason("Disabling non-boot cpus failed"); + goto Enable_cpus; ++ } + + arch_suspend_disable_irqs(); + BUG_ON(!irqs_disabled()); +@@ -328,6 +341,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) + trace_suspend_resume(TPS("machine_suspend"), + state, false); + events_check_enabled = false; ++ } else if (*wakeup) { ++ pm_get_active_wakeup_sources(suspend_abort, ++ MAX_SUSPEND_ABORT_LEN); ++ log_suspend_abort_reason(suspend_abort); ++ error = -EBUSY; + } + syscore_resume(); + } +@@ -374,6 +392,7 @@ int suspend_devices_and_enter(suspend_state_t state) + error = dpm_suspend_start(PMSG_SUSPEND); + if (error) { + pr_err("PM: Some devices failed to suspend, or early wake event detected\n"); ++ log_suspend_abort_reason("Some devices failed to suspend, or early wake event detected"); + goto Recover_platform; + } + suspend_test_finish("suspend devices"); +@@ -472,6 +491,18 @@ static int enter_state(suspend_state_t state) + return error; + } + ++static void pm_suspend_marker(char *annotation) ++{ ++ struct timespec ts; ++ struct rtc_time tm; ++ ++ getnstimeofday(&ts); ++ rtc_time_to_tm(ts.tv_sec, &tm); ++ pr_info("PM: suspend %s %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n", ++ annotation, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ++ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); ++} ++ + /** + * pm_suspend - Externally visible function for suspending the system. + * @state: System sleep state to enter. +@@ -486,6 +517,7 @@ int pm_suspend(suspend_state_t state) + if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX) + return -EINVAL; + ++ pm_suspend_marker("entry"); + error = enter_state(state); + if (error) { + suspend_stats.fail++; +@@ -493,6 +525,7 @@ int pm_suspend(suspend_state_t state) + } else { + suspend_stats.success++; + } ++ pm_suspend_marker("exit"); + return error; + } + EXPORT_SYMBOL(pm_suspend); +diff --git a/kernel/power/suspend_time.c b/kernel/power/suspend_time.c +new file mode 100644 +index 0000000..d2a65da +--- /dev/null ++++ b/kernel/power/suspend_time.c +@@ -0,0 +1,111 @@ ++/* ++ * debugfs file to track time spent in suspend ++ * ++ * Copyright (c) 2011, Google, Inc. ++ * ++ * 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/debugfs.h> ++#include <linux/err.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/seq_file.h> ++#include <linux/syscore_ops.h> ++#include <linux/time.h> ++ ++static struct timespec suspend_time_before; ++static unsigned int time_in_suspend_bins[32]; ++ ++#ifdef CONFIG_DEBUG_FS ++static int suspend_time_debug_show(struct seq_file *s, void *data) ++{ ++ int bin; ++ seq_printf(s, "time (secs) count\n"); ++ seq_printf(s, "------------------\n"); ++ for (bin = 0; bin < 32; bin++) { ++ if (time_in_suspend_bins[bin] == 0) ++ continue; ++ seq_printf(s, "%4d - %4d %4u\n", ++ bin ? 1 << (bin - 1) : 0, 1 << bin, ++ time_in_suspend_bins[bin]); ++ } ++ return 0; ++} ++ ++static int suspend_time_debug_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, suspend_time_debug_show, NULL); ++} ++ ++static const struct file_operations suspend_time_debug_fops = { ++ .open = suspend_time_debug_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int __init suspend_time_debug_init(void) ++{ ++ struct dentry *d; ++ ++ d = debugfs_create_file("suspend_time", 0755, NULL, NULL, ++ &suspend_time_debug_fops); ++ if (!d) { ++ pr_err("Failed to create suspend_time debug file\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++late_initcall(suspend_time_debug_init); ++#endif ++ ++static int suspend_time_syscore_suspend(void) ++{ ++ read_persistent_clock(&suspend_time_before); ++ ++ return 0; ++} ++ ++static void suspend_time_syscore_resume(void) ++{ ++ struct timespec after; ++ ++ read_persistent_clock(&after); ++ ++ after = timespec_sub(after, suspend_time_before); ++ ++ time_in_suspend_bins[fls(after.tv_sec)]++; ++ ++ pr_info("Suspended for %lu.%03lu seconds\n", after.tv_sec, ++ after.tv_nsec / NSEC_PER_MSEC); ++} ++ ++static struct syscore_ops suspend_time_syscore_ops = { ++ .suspend = suspend_time_syscore_suspend, ++ .resume = suspend_time_syscore_resume, ++}; ++ ++static int suspend_time_syscore_init(void) ++{ ++ register_syscore_ops(&suspend_time_syscore_ops); ++ ++ return 0; ++} ++ ++static void suspend_time_syscore_exit(void) ++{ ++ unregister_syscore_ops(&suspend_time_syscore_ops); ++} ++module_init(suspend_time_syscore_init); ++module_exit(suspend_time_syscore_exit); +diff --git a/kernel/power/swap.c b/kernel/power/swap.c +index aaa3261..681acde 100644 +--- a/kernel/power/swap.c ++++ b/kernel/power/swap.c +@@ -30,7 +30,9 @@ + #include <linux/atomic.h> + #include <linux/kthread.h> + #include <linux/crc32.h> +- ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++#include <linux/mount.h> ++#endif + #include "power.h" + + #define HIBERNATE_SIG "S1SUSPEND" +@@ -216,7 +218,9 @@ struct block_device *hib_resume_bdev; + /* + * Saving part + */ +- ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++static int flush_swap_writer(struct swap_map_handle *handle); ++#endif + static int mark_swapfiles(struct swap_map_handle *handle, unsigned int flags) + { + int error; +@@ -230,8 +234,19 @@ static int mark_swapfiles(struct swap_map_handle *handle, unsigned int flags) + swsusp_header->flags = flags; + if (flags & SF_CRC32_MODE) + swsusp_header->crc32 = handle->crc32; ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ /*store the resume address in the swsusp_header */ ++ *(unsigned long *)((&swsusp_header->image) - 1) = ++ (unsigned long)swsusp_arch_resume; ++#endif + error = hib_bio_write_page(swsusp_resume_block, +- swsusp_header, NULL); ++ swsusp_header, NULL); ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ /* ++ * Make sure the mark has been writen to flash! ++ */ ++ flush_swap_writer(handle); ++#endif + } else { + printk(KERN_ERR "PM: Swap header not found!\n"); + error = -ENODEV; +@@ -266,6 +281,60 @@ static int swsusp_swap_check(void) + return res; + } + ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++static int swsusp_hb_bdev_check(void) ++{ ++ char spath[64]; ++ struct block_device *bdev = NULL; ++ dev_t device; ++ ++ if (hb_bdev_file[0] == 0) { ++ printk("%s:%d\n", __func__, __LINE__); ++ return -1; ++ } ++ ++ ++ printk("hb_bdev_file = %s\n", hb_bdev_file); ++ strncpy(spath, hb_bdev_file, sizeof(spath)); ++ ++ if (strncmp(spath, "/dev/block/", 11) == 0) ++ strcpy(spath + 5, spath + 11); ++ printk("spath = %s\n", spath); ++ ++ device = name_to_dev_t(spath); ++ if (device == MKDEV(0, 0)) { ++ printk("%s:%d\n", __func__, __LINE__); ++ return -1; ++ } ++ ++ bdev = bdget(device); ++ if (!bdev) { ++ printk("%s:%d\n", __func__, __LINE__); ++ return -1; ++ } ++ ++ bdput(bdev); ++ ++ return 0; ++} ++ ++int swsusp_check_storage_all(void) ++{ ++ int ret; ++ ++ ret = swsusp_hb_bdev_check(); ++ if (ret >= 0) ++ return 1; /* use hb_bdev device */ ++ ++ return -1; ++} ++#else ++int swsusp_check_storage_all(void) ++{ ++ return 0; /* use swap device always */ ++} ++#endif /* CONFIG_HISI_SNAPSHOT_BOOT */ ++ + /** + * write_page - Write one page to given swap location. + * @buf: Address we're writing. +@@ -800,6 +869,307 @@ static int enough_swap(unsigned int nr_pages, unsigned int flags) + return free_swap > required; + } + ++ ++ ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ ++#define PAGE_NR(x) (((x) + PAGE_SIZE - 1) / PAGE_SIZE) ++ ++#include "compress.h" ++ ++static int hb_bdev_write_header(struct compress_writer *writer, ++ union sscomp_header *sscomp) ++{ ++ int ret; ++ ++ ret = writer->write(writer, 0, PAGE_NR(sizeof(*sscomp)), sscomp); ++ if (ret != PAGE_NR(sizeof(*sscomp))) { ++ printk(KERN_ERR "PM: %s: write() failed\n", __func__); ++ return -EFAULT; ++ } ++ ++ return 0; ++} ++ ++static int hb_bdev_write_data(struct compress_writer *writer, ++ struct sscomp_block *block, ++ unsigned int *src_len, ++ sector_t *write_offs_page_i) ++{ ++ void *buf = (void *)writer->dst; ++ int ret; ++ sector_t page_nr; ++ ++ ret = writer->compress(writer, block, src_len); ++ if (ret <= 0) { ++ printk(KERN_ERR "PM: %s: compress() failed\n", __func__); ++ return -EFAULT; ++ } ++ ++ page_nr = ret; ++ ret = writer->write(writer, *write_offs_page_i, page_nr, buf); ++ if (ret != page_nr) { ++ printk(KERN_ERR "PM: %s: write() failed\n", __func__); ++ return -EFAULT; ++ } ++ ++ *write_offs_page_i += page_nr; ++ ++ return 0; ++} ++ ++static inline int hb_bdev_write_finish(struct compress_writer *writer) ++{ ++ return writer->finish(writer); ++} ++ ++static inline void __hb_bdev_unpack_pfn(void *mem) ++{ ++ unsigned long *pfn = mem; ++ while ((void *)pfn < mem + PAGE_SIZE) { ++ *pfn <<= PAGE_SHIFT; ++ pfn++; ++ } ++} ++ ++ ++extern unsigned int crc32_check( ++ unsigned int crc, const unsigned char *p, unsigned int len); ++ ++static int hb_bdev_save_image(struct compress_writer *writer, ++ struct snapshot_handle *snapshot) ++{ ++ union sscomp_header *sscomp = &writer->sscomp; ++ struct sscomp_block *block = &sscomp->b.blocks[0]; ++ unsigned int nr_pages; ++ unsigned int filled_src = 0; ++ sector_t write_offs_page_i; ++ int ret; ++ ++ write_offs_page_i = PAGE_NR(sizeof(*sscomp)); ++ ++ /* meta block(s) */ ++ nr_pages = sscomp->info.meta_pages; ++ while (nr_pages) { ++ ret = snapshot_read_next(snapshot); ++ if (ret <= 0) { ++ printk(KERN_ERR "PM: snapshot_read_next() failed\n"); ++ return -EFAULT; ++ } ++ ++ memcpy((void *)writer->src + filled_src, data_of(*snapshot), ++ PAGE_SIZE); ++ ++ __hb_bdev_unpack_pfn((void *)writer->src + filled_src); ++ ++#ifdef USE_SHA1 ++ hb_bdev_sha1_update((void *)writer->src + filled_src, ++ PAGE_SIZE); ++#else ++ sscomp->info.crc32 = crc32_check(sscomp->info.crc32, ++ (void *)writer->src + filled_src, PAGE_SIZE); ++#endif ++ filled_src += PAGE_SIZE; ++ if (filled_src >= writer->src_buf_sz) { ++ ret = hb_bdev_write_data(writer, block, &filled_src, ++ &write_offs_page_i); ++ if (ret) ++ return -EFAULT; ++ sscomp->info.meta_blocks++; ++ block++; ++ } ++ nr_pages--; ++ } ++ ++ while (filled_src > 0) { ++ ret = hb_bdev_write_data(writer, block, &filled_src, ++ &write_offs_page_i); ++ if (ret) ++ return -EFAULT; ++ sscomp->info.meta_blocks++; ++ block++; ++ } ++ ++ /* copy block(s) */ ++ nr_pages = sscomp->info.data_pages; ++ while (nr_pages) { ++ ret = snapshot_read_next(snapshot); ++ if (ret <= 0) { ++ printk(KERN_ERR "PM: snapshot_read_next() failed\n"); ++ return -EFAULT; ++ } ++ ++ memcpy((void *)writer->src + filled_src, data_of(*snapshot), ++ PAGE_SIZE); ++ ++#ifdef USE_SHA1 ++ hb_bdev_sha1_update((void *)writer->src + filled_src, ++ PAGE_SIZE); ++#else ++ sscomp->info.crc32 = crc32_check(sscomp->info.crc32, ++ (void *)writer->src + filled_src, PAGE_SIZE); ++#endif ++ filled_src += PAGE_SIZE; ++ if (filled_src >= writer->src_buf_sz) { ++ ret = hb_bdev_write_data(writer, block, &filled_src, ++ &write_offs_page_i); ++ if (ret) ++ return -EFAULT; ++ sscomp->info.data_blocks++; ++ block++; ++ } ++ nr_pages--; ++ } ++ ++ while (filled_src > 0) { ++ ret = hb_bdev_write_data(writer, block, &filled_src, ++ &write_offs_page_i); ++ if (ret) ++ return -EFAULT; ++ sscomp->info.data_blocks++; ++ block++; ++ } ++ ++ printk(KERN_INFO "PM: compressed: %u pages to %llu pages.\n", ++ sscomp->info.meta_pages + sscomp->info.data_pages, ++ (unsigned long long)write_offs_page_i ++ - PAGE_NR(sizeof(*sscomp))); ++ ++ printk(KERN_INFO "PM: written: %u meta blocks and %u data blocks\n", ++ sscomp->info.meta_blocks, sscomp->info.data_blocks); ++ ++ return 0; ++} ++ ++ ++static int swsusp_compress_and_write(unsigned int flags) ++{ ++ struct compress_writer *writer = get_susp_compress_writer(hb_bdev_file); ++ struct snapshot_handle snapshot; ++ struct swsusp_info *susp_info; ++ union sscomp_header *sscomp; ++ unsigned int meta_pages, copy_pages; ++ int error; ++ ++ if (!writer) { ++ printk(KERN_ERR "PM: Cannot get compress-writer\n"); ++ return -EFAULT; ++ } ++ ++ sscomp = &writer->sscomp; ++ ++ /* invalidate existing one */ ++ memset(writer->pagebuf, 0, PAGE_SIZE); ++ error = hb_bdev_write_header(writer, writer->pagebuf); ++ if (error) ++ goto out_finish; ++ ++ memset(&snapshot, 0, sizeof(struct snapshot_handle)); ++ error = snapshot_read_next(&snapshot); ++ if (error < PAGE_SIZE) { ++ if (error >= 0) ++ error = -EFAULT; ++ goto out_error; ++ } ++ ++ susp_info = (struct swsusp_info *)data_of(snapshot); ++ copy_pages = susp_info->image_pages; ++ meta_pages = susp_info->pages - susp_info->image_pages - 1; ++ if (!copy_pages && !meta_pages) { ++ error = -EINVAL; ++ goto out_error; ++ } ++ ++ sscomp->info.meta_pages = meta_pages; ++ sscomp->info.data_pages = copy_pages; ++ ++#ifdef USE_SHA1 ++ hb_bdev_sha1_init(); ++ strncpy(sscomp->info.sha1sum, "Not checked yet!", ++ sizeof(sscomp->info.sha1sum)); ++#else ++ sscomp->info.crc32 = 0; ++#endif ++ ++ error = hb_bdev_save_image(writer, &snapshot); ++ if (error) ++ goto out_finish; ++ ++#ifdef USE_SHA1 ++ hb_bdev_sha1_final(sscomp->info.sha1sum); ++#endif ++ ++ /* update */ ++ memcpy(sscomp->info.magic, SSCOMP_MAGIC_4, sizeof(sscomp->info.magic)); ++ sscomp->info.ctl_func = (unsigned long)swsusp_arch_resume; ++ sscomp->info.ctl_data = (unsigned long)saved_processor_context; ++ ++ error = hb_bdev_write_header(writer, sscomp); ++ if (error) ++ goto out_finish; ++ ++ error = hb_bdev_write_finish(writer); ++ ++#if 0 ++ /** for test**/ ++ int i; ++ mm_segment_t fs; ++ loff_t pos = 0; ++ struct file *fp_result; ++ struct file *fp_src; ++ fp_src = filp_open("/tmp/zlib_src.txt", ++ O_APPEND | O_CREAT, ++ 777); ++ if (IS_ERR(fp_src)) { ++ printk("fp_drc is err!\n"); ++ return -1; ++ } ++ ++ fp_result = filp_open("/tmp/zlib_deflate.gz", ++ O_APPEND | O_CREAT, ++ 777); ++ if (IS_ERR(fp_result)) { ++ printk("fp_result is err!\n"); ++ return -1; ++ } ++ ++ fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ printk("writer->image = 0x%x, writer->image_buf_sz = 0x%x, %s:%d\n", ++ writer->dst, writer->dst_buf_sz, __func__, __LINE__); ++ for (i = 0; i < 32; i++) { ++ printk("[0x%x]\n", *((int *)writer->dst + i)); ++ } ++ ++ pos = 0; ++ fp_result->f_op->write(fp_result, writer->dst, writer->dst_buf_sz, &pos); ++ ++ pos = 0; ++ fp_result->f_op->write(fp_src, writer->src, writer->src_buf_sz, &pos); ++ ++ printk("%s:%d\n", __func__, __LINE__); ++ set_fs(fs); ++ filp_close(fp_result, NULL); ++ filp_close(fp_src, NULL); ++ /** end test**/ ++#endif ++ ++out_finish: ++out_error: ++ put_susp_compress_writer(writer); ++ return error; ++} ++ ++#else ++ ++static inline swsusp_compress_and_write(unsigned int flags) ++{ ++ return -RESTARTSYS; ++} ++ ++#endif /* CONFIG_HISI_SNAPSHOT_BOOT */ ++ + /** + * swsusp_write - Write entire image and metadata. + * @flags: flags to pass to the "boot" kernel in the image header +@@ -818,6 +1188,12 @@ int swsusp_write(unsigned int flags) + unsigned long pages; + int error; + ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++ error = swsusp_check_storage_all(); ++ if (error == 1) ++ return swsusp_compress_and_write(flags); ++ ++#endif + pages = snapshot_get_image_size(); + error = get_swap_writer(&handle); + if (error) { +@@ -1435,10 +1811,13 @@ int swsusp_check(void) + goto put; + + if (!memcmp(HIBERNATE_SIG, swsusp_header->sig, 10)) { ++#ifndef CONFIG_HISI_SNAPSHOT_BOOT + memcpy(swsusp_header->sig, swsusp_header->orig_sig, 10); + /* Reset swap signature now */ + error = hib_bio_write_page(swsusp_resume_block, + swsusp_header, NULL); ++#endif ++ + } else { + error = -EINVAL; + } +diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c +new file mode 100644 +index 0000000..252611f +--- /dev/null ++++ b/kernel/power/wakeup_reason.c +@@ -0,0 +1,225 @@ ++/* ++ * kernel/power/wakeup_reason.c ++ * ++ * Logs the reasons which caused the kernel to resume from ++ * the suspend mode. ++ * ++ * Copyright (C) 2014 Google, Inc. ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/wakeup_reason.h> ++#include <linux/kernel.h> ++#include <linux/irq.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/kobject.h> ++#include <linux/sysfs.h> ++#include <linux/init.h> ++#include <linux/spinlock.h> ++#include <linux/notifier.h> ++#include <linux/suspend.h> ++ ++ ++#define MAX_WAKEUP_REASON_IRQS 32 ++static int irq_list[MAX_WAKEUP_REASON_IRQS]; ++static int irqcount; ++static bool suspend_abort; ++static char abort_reason[MAX_SUSPEND_ABORT_LEN]; ++static struct kobject *wakeup_reason; ++static DEFINE_SPINLOCK(resume_reason_lock); ++ ++static ktime_t last_monotime; /* monotonic time before last suspend */ ++static ktime_t curr_monotime; /* monotonic time after last suspend */ ++static ktime_t last_stime; /* monotonic boottime offset before last suspend */ ++static ktime_t curr_stime; /* monotonic boottime offset after last suspend */ ++ ++static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribute *attr, ++ char *buf) ++{ ++ int irq_no, buf_offset = 0; ++ struct irq_desc *desc; ++ spin_lock(&resume_reason_lock); ++ if (suspend_abort) { ++ buf_offset = sprintf(buf, "Abort: %s", abort_reason); ++ } else { ++ for (irq_no = 0; irq_no < irqcount; irq_no++) { ++ desc = irq_to_desc(irq_list[irq_no]); ++ if (desc && desc->action && desc->action->name) ++ buf_offset += sprintf(buf + buf_offset, "%d %s\n", ++ irq_list[irq_no], desc->action->name); ++ else ++ buf_offset += sprintf(buf + buf_offset, "%d\n", ++ irq_list[irq_no]); ++ } ++ } ++ spin_unlock(&resume_reason_lock); ++ return buf_offset; ++} ++ ++static ssize_t last_suspend_time_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ struct timespec sleep_time; ++ struct timespec total_time; ++ struct timespec suspend_resume_time; ++ ++ /* ++ * total_time is calculated from monotonic bootoffsets because ++ * unlike CLOCK_MONOTONIC it include the time spent in suspend state. ++ */ ++ total_time = ktime_to_timespec(ktime_sub(curr_stime, last_stime)); ++ ++ /* ++ * suspend_resume_time is calculated as monotonic (CLOCK_MONOTONIC) ++ * time interval before entering suspend and post suspend. ++ */ ++ suspend_resume_time = ktime_to_timespec(ktime_sub(curr_monotime, last_monotime)); ++ ++ /* sleep_time = total_time - suspend_resume_time */ ++ sleep_time = timespec_sub(total_time, suspend_resume_time); ++ ++ /* Export suspend_resume_time and sleep_time in pair here. */ ++ return sprintf(buf, "%lu.%09lu %lu.%09lu\n", ++ suspend_resume_time.tv_sec, suspend_resume_time.tv_nsec, ++ sleep_time.tv_sec, sleep_time.tv_nsec); ++} ++ ++static struct kobj_attribute resume_reason = __ATTR_RO(last_resume_reason); ++static struct kobj_attribute suspend_time = __ATTR_RO(last_suspend_time); ++ ++static struct attribute *attrs[] = { ++ &resume_reason.attr, ++ &suspend_time.attr, ++ NULL, ++}; ++static struct attribute_group attr_group = { ++ .attrs = attrs, ++}; ++ ++/* ++ * logs all the wake up reasons to the kernel ++ * stores the irqs to expose them to the userspace via sysfs ++ */ ++void log_wakeup_reason(int irq) ++{ ++ struct irq_desc *desc; ++ desc = irq_to_desc(irq); ++ if (desc && desc->action && desc->action->name) ++ printk(KERN_INFO "Resume caused by IRQ %d, %s\n", irq, ++ desc->action->name); ++ else ++ printk(KERN_INFO "Resume caused by IRQ %d\n", irq); ++ ++ spin_lock(&resume_reason_lock); ++ if (irqcount == MAX_WAKEUP_REASON_IRQS) { ++ spin_unlock(&resume_reason_lock); ++ printk(KERN_WARNING "Resume caused by more than %d IRQs\n", ++ MAX_WAKEUP_REASON_IRQS); ++ return; ++ } ++ ++ irq_list[irqcount++] = irq; ++ spin_unlock(&resume_reason_lock); ++} ++ ++int check_wakeup_reason(int irq) ++{ ++ int irq_no; ++ int ret = false; ++ ++ spin_lock(&resume_reason_lock); ++ for (irq_no = 0; irq_no < irqcount; irq_no++) ++ if (irq_list[irq_no] == irq) { ++ ret = true; ++ break; ++ } ++ spin_unlock(&resume_reason_lock); ++ return ret; ++} ++ ++void log_suspend_abort_reason(const char *fmt, ...) ++{ ++ va_list args; ++ ++ spin_lock(&resume_reason_lock); ++ ++ //Suspend abort reason has already been logged. ++ if (suspend_abort) { ++ spin_unlock(&resume_reason_lock); ++ return; ++ } ++ ++ suspend_abort = true; ++ va_start(args, fmt); ++ vsnprintf(abort_reason, MAX_SUSPEND_ABORT_LEN, fmt, args); ++ va_end(args); ++ spin_unlock(&resume_reason_lock); ++} ++ ++/* Detects a suspend and clears all the previous wake up reasons*/ ++static int wakeup_reason_pm_event(struct notifier_block *notifier, ++ unsigned long pm_event, void *unused) ++{ ++ switch (pm_event) { ++ case PM_SUSPEND_PREPARE: ++ spin_lock(&resume_reason_lock); ++ irqcount = 0; ++ suspend_abort = false; ++ spin_unlock(&resume_reason_lock); ++ /* monotonic time since boot */ ++ last_monotime = ktime_get(); ++ /* monotonic time since boot including the time spent in suspend */ ++ last_stime = ktime_get_boottime(); ++ break; ++ case PM_POST_SUSPEND: ++ /* monotonic time since boot */ ++ curr_monotime = ktime_get(); ++ /* monotonic time since boot including the time spent in suspend */ ++ curr_stime = ktime_get_boottime(); ++ break; ++ default: ++ break; ++ } ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block wakeup_reason_pm_notifier_block = { ++ .notifier_call = wakeup_reason_pm_event, ++}; ++ ++/* Initializes the sysfs parameter ++ * registers the pm_event notifier ++ */ ++int __init wakeup_reason_init(void) ++{ ++ int retval; ++ ++ retval = register_pm_notifier(&wakeup_reason_pm_notifier_block); ++ if (retval) ++ printk(KERN_WARNING "[%s] failed to register PM notifier %d\n", ++ __func__, retval); ++ ++ wakeup_reason = kobject_create_and_add("wakeup_reasons", kernel_kobj); ++ if (!wakeup_reason) { ++ printk(KERN_WARNING "[%s] failed to create a sysfs kobject\n", ++ __func__); ++ return 1; ++ } ++ retval = sysfs_create_group(wakeup_reason, &attr_group); ++ if (retval) { ++ kobject_put(wakeup_reason); ++ printk(KERN_WARNING "[%s] failed to create a sysfs group %d\n", ++ __func__, retval); ++ } ++ return 0; ++} ++ ++late_initcall(wakeup_reason_init); +diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c +index 3b9f01b..dcee13f 100644 +--- a/kernel/printk/printk.c ++++ b/kernel/printk/printk.c +@@ -2007,8 +2007,11 @@ int update_console_cmdline(char *name, int idx, char *name_new, int idx_new, cha + /* not found */ + return -1; + } +- ++#ifdef CONFIG_HISI_SNAPSHOT_BOOT ++bool console_suspend_enabled; ++#else + bool console_suspend_enabled = true; ++#endif + EXPORT_SYMBOL(console_suspend_enabled); + + static int __init console_suspend_disable(char *str) +diff --git a/kernel/resource.c b/kernel/resource.c +index 0bcebff..7528c3b 100644 +--- a/kernel/resource.c ++++ b/kernel/resource.c +@@ -162,7 +162,7 @@ static const struct file_operations proc_iomem_operations = { + static int __init ioresources_init(void) + { + proc_create("ioports", 0, NULL, &proc_ioports_operations); +- proc_create("iomem", 0, NULL, &proc_iomem_operations); ++ proc_create("iomem", S_IRUSR, NULL, &proc_iomem_operations); + return 0; + } + __initcall(ioresources_init); +diff --git a/kernel/sched/core.c b/kernel/sched/core.c +index 6810e57..a800ea1 100644 +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -1582,7 +1582,12 @@ void scheduler_ipi(void) + */ + preempt_fold_need_resched(); + +- if (llist_empty(&this_rq()->wake_list) && !got_nohz_idle_kick()) ++ if (llist_empty(&this_rq()->wake_list) ++ && !got_nohz_idle_kick() ++#ifdef CONFIG_SCHED_HMP ++ && !this_rq()->wake_for_idle_pull ++#endif ++ ) + return; + + /* +@@ -1608,6 +1613,11 @@ void scheduler_ipi(void) + this_rq()->idle_balance = 1; + raise_softirq_irqoff(SCHED_SOFTIRQ); + } ++#ifdef CONFIG_SCHED_HMP ++ else if (unlikely(this_rq()->wake_for_idle_pull)) ++ raise_softirq_irqoff(SCHED_SOFTIRQ); ++#endif ++ + irq_exit(); + } + +@@ -1617,6 +1627,7 @@ static void ttwu_queue_remote(struct task_struct *p, int cpu) + + if (llist_add(&p->wake_entry, &cpu_rq(cpu)->wake_list)) { + if (!set_nr_if_polling(rq->idle)) ++ + smp_send_reschedule(cpu); + else + trace_sched_wake_idle_without_ipi(cpu); +@@ -1816,6 +1827,7 @@ void __dl_clear_params(struct task_struct *p) + dl_se->dl_bw = 0; + } + ++extern unsigned int task_fork_on_bigcore; + /* + * Perform scheduler related setup for a newly forked process p. + * p is forked by current. +@@ -1833,6 +1845,29 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) + p->se.nr_migrations = 0; + p->se.vruntime = 0; + INIT_LIST_HEAD(&p->se.group_node); ++/* ++ * Load-tracking only depends on SMP, FAIR_GROUP_SCHED dependency below may be ++ * removed when useful for applications beyond shares distribution (e.g. ++ * load-balance). ++ */ ++#if defined(CONFIG_SMP) && defined(CONFIG_FAIR_GROUP_SCHED) ++ p->se.avg.runnable_avg_period = 0; ++ p->se.avg.runnable_avg_sum = 0; ++#ifdef CONFIG_SCHED_HMP ++ /* keep LOAD_AVG_MAX in sync with fair.c if load avg series is changed */ ++#define LOAD_AVG_MAX 47742 ++ p->se.avg.hmp_last_up_migration = 0; ++ p->se.avg.hmp_last_down_migration = 0; ++ if (hmp_task_should_forkboost(p) && task_fork_on_bigcore) { ++ p->se.avg.load_avg_ratio = 1023; ++ p->se.avg.load_avg_contrib = ++ (1023 * scale_load_down(p->se.load.weight)); ++ p->se.avg.runnable_avg_period = LOAD_AVG_MAX; ++ p->se.avg.runnable_avg_sum = LOAD_AVG_MAX; ++ p->se.avg.usage_avg_sum = LOAD_AVG_MAX; ++ } ++#endif ++#endif + + #ifdef CONFIG_SCHEDSTATS + memset(&p->se.statistics, 0, sizeof(p->se.statistics)); +@@ -6174,7 +6209,11 @@ sd_init(struct sched_domain_topology_level *tl, int cpu) + .wake_idx = 0, + .forkexec_idx = 0, + +- .flags = 1*SD_LOAD_BALANCE ++#ifdef CONFIG_SCHED_HMP ++ .flags = 0*SD_LOAD_BALANCE ++#else ++ .flags = 1*SD_LOAD_BALANCE ++#endif + | 1*SD_BALANCE_NEWIDLE + | 1*SD_BALANCE_EXEC + | 1*SD_BALANCE_FORK +@@ -7190,14 +7229,24 @@ static inline int preempt_count_equals(int preempt_offset) + return (nested == preempt_offset); + } + ++static int __might_sleep_init_called; ++int __init __might_sleep_init(void) ++{ ++ __might_sleep_init_called = 1; ++ return 0; ++} ++early_initcall(__might_sleep_init); ++ + void __might_sleep(const char *file, int line, int preempt_offset) + { + static unsigned long prev_jiffy; /* ratelimiting */ + + rcu_sleep_check(); /* WARN_ON_ONCE() by default, no rate limit reqd. */ + if ((preempt_count_equals(preempt_offset) && !irqs_disabled() && +- !is_idle_task(current)) || +- system_state != SYSTEM_RUNNING || oops_in_progress) ++ !is_idle_task(current)) || oops_in_progress) ++ return; ++ if (system_state != SYSTEM_RUNNING && ++ (!__might_sleep_init_called || system_state != SYSTEM_BOOTING)) + return; + if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy) + return; +@@ -8257,6 +8306,7 @@ struct cgroup_subsys cpu_cgrp_subsys = { + .fork = cpu_cgroup_fork, + .can_attach = cpu_cgroup_can_attach, + .attach = cpu_cgroup_attach, ++ .allow_attach = subsys_cgroup_allow_attach, + .exit = cpu_cgroup_exit, + .legacy_cftypes = cpu_files, + .early_init = 1, +diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c +index 8394b1e..b293203 100644 +--- a/kernel/sched/cputime.c ++++ b/kernel/sched/cputime.c +@@ -1,3 +1,4 @@ ++#include <linux/cpufreq.h> + #include <linux/export.h> + #include <linux/sched.h> + #include <linux/tsacct_kern.h> +@@ -149,6 +150,11 @@ void account_user_time(struct task_struct *p, cputime_t cputime, + + /* Account for user time used */ + acct_account_cputime(p); ++ ++#ifdef CONFIG_CPU_FREQ_STAT ++ /* Account power usage for user time */ ++ acct_update_power(p, cputime); ++#endif + } + + /* +@@ -199,6 +205,11 @@ void __account_system_time(struct task_struct *p, cputime_t cputime, + + /* Account for system time used */ + acct_account_cputime(p); ++ ++#ifdef CONFIG_CPU_FREQ_STAT ++ /* Account power usage for system time */ ++ acct_update_power(p, cputime); ++#endif + } + + /* +diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c +index ce33780..3c2d558 100644 +--- a/kernel/sched/debug.c ++++ b/kernel/sched/debug.c +@@ -95,6 +95,7 @@ static void print_cfs_group_stats(struct seq_file *m, int cpu, struct task_group + #ifdef CONFIG_SMP + P(se->avg.runnable_avg_sum); + P(se->avg.runnable_avg_period); ++ P(se->avg.usage_avg_sum); + P(se->avg.load_avg_contrib); + P(se->avg.decay_count); + #endif +@@ -223,6 +224,8 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) + atomic_long_read(&cfs_rq->tg->load_avg)); + SEQ_printf(m, " .%-30s: %d\n", "tg->runnable_avg", + atomic_read(&cfs_rq->tg->runnable_avg)); ++ SEQ_printf(m, " .%-30s: %d\n", "tg->usage_avg", ++ atomic_read(&cfs_rq->tg->usage_avg)); + #endif + #endif + #ifdef CONFIG_CFS_BANDWIDTH +@@ -566,6 +569,12 @@ void proc_sched_show_task(struct task_struct *p, struct seq_file *m) + PN(se.exec_start); + PN(se.vruntime); + PN(se.sum_exec_runtime); ++#if defined(CONFIG_SMP) && defined(CONFIG_FAIR_GROUP_SCHED) ++ P(se.avg.runnable_avg_sum); ++ P(se.avg.runnable_avg_period); ++ P(se.avg.load_avg_contrib); ++ P(se.avg.decay_count); ++#endif + + nr_switches = p->nvcsw + p->nivcsw; + +diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c +index 2246a36..b14be25 100644 +--- a/kernel/sched/fair.c ++++ b/kernel/sched/fair.c +@@ -32,9 +32,28 @@ + #include <linux/task_work.h> + + #include <trace/events/sched.h> ++#include <linux/sysfs.h> ++#include <linux/vmalloc.h> ++#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE ++/* Include cpufreq header to add a notifier so that cpu frequency ++ * scaling can track the current CPU frequency ++ */ ++#include <linux/cpufreq.h> ++#endif /* CONFIG_HMP_FREQUENCY_INVARIANT_SCALE */ ++#ifdef CONFIG_SCHED_HMP ++#include <linux/cpuidle.h> ++#endif + + #include "sched.h" + ++ ++/* Add apportunity to config how to placement the task ++ * when task fork. ++ * Default: Task fork on A7 ++ */ ++unsigned int task_fork_on_bigcore = 1; ++ ++ + /* + * Targeted preemption latency for CPU-bound tasks: + * (default: 6ms * (1 + ilog(ncpus)), units: nanoseconds) +@@ -2001,6 +2020,13 @@ void task_numa_work(struct callback_head *work) + if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) + continue; + ++ /* ++ * Skip inaccessible VMAs to avoid any confusion between ++ * PROT_NONE and NUMA hinting ptes ++ */ ++ if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) ++ continue; ++ + do { + start = max(start, vma->vm_start); + end = ALIGN(start + (pages << PAGE_SHIFT), HPAGE_SIZE); +@@ -2289,8 +2315,91 @@ static u32 __compute_runnable_contrib(u64 n) + return contrib + runnable_avg_yN_sum[n]; + } + +-/* +- * We can represent the historical contribution to runnable average as the ++#ifdef CONFIG_SCHED_HMP ++#define HMP_VARIABLE_SCALE_SHIFT 16ULL ++struct hmp_global_attr { ++ struct attribute attr; ++ ssize_t (*show)(struct kobject *kobj, ++ struct attribute *attr, char *buf); ++ ssize_t (*store)(struct kobject *a, struct attribute *b, ++ const char *c, size_t count); ++ int *value; ++ int (*to_sysfs)(int); ++ int (*from_sysfs)(int); ++ ssize_t (*to_sysfs_text)(char *buf, int buf_size); ++}; ++ ++#define HMP_DATA_SYSFS_MAX 8 ++ ++struct hmp_data_struct { ++#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE ++ int freqinvar_load_scale_enabled; ++#endif ++ int multiplier; /* used to scale the time delta */ ++ struct attribute_group attr_group; ++ struct attribute *attributes[HMP_DATA_SYSFS_MAX + 1]; ++ struct hmp_global_attr attr[HMP_DATA_SYSFS_MAX]; ++} hmp_data; ++ ++static u64 hmp_variable_scale_convert(u64 delta); ++#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE ++/* Frequency-Invariant Load Modification: ++ * Loads are calculated as in PJT's patch however we also scale the current ++ * contribution in line with the frequency of the CPU that the task was ++ * executed on. ++ * In this version, we use a simple linear scale derived from the maximum ++ * frequency reported by CPUFreq. As an example: ++ * ++ * Consider that we ran a task for 100% of the previous interval. ++ * ++ * Our CPU was under asynchronous frequency control through one of the ++ * CPUFreq governors. ++ * ++ * The CPUFreq governor reports that it is able to scale the CPU between ++ * 500MHz and 1GHz. ++ * ++ * During the period, the CPU was running at 1GHz. ++ * ++ * In this case, our load contribution for that period is calculated as ++ * 1 * (number_of_active_microseconds) ++ * ++ * This results in our task being able to accumulate maximum load as normal. ++ * ++ * ++ * Consider now that our CPU was executing at 500MHz. ++ * ++ * We now scale the load contribution such that it is calculated as ++ * 0.5 * (number_of_active_microseconds) ++ * ++ * Our task can only record 50% maximum load during this period. ++ * ++ * This represents the task consuming 50% of the CPU's *possible* compute ++ * capacity. However the task did consume 100% of the CPU's *available* ++ * compute capacity which is the value seen by the CPUFreq governor and ++ * user-side CPU Utilization tools. ++ * ++ * Restricting tracked load to be scaled by the CPU's frequency accurately ++ * represents the consumption of possible compute capacity and allows the ++ * HMP migration's simple threshold migration strategy to interact more ++ * predictably with CPUFreq's asynchronous compute capacity changes. ++ */ ++#define SCHED_FREQSCALE_SHIFT 10 ++struct cpufreq_extents { ++ u32 curr_scale; ++ u32 min; ++ u32 max; ++ u32 flags; ++}; ++/* Flag set when the governor in use only allows one frequency. ++ * Disables scaling. ++ */ ++#define SCHED_LOAD_FREQINVAR_SINGLEFREQ 0x01 ++ ++static struct cpufreq_extents freq_scale[CONFIG_NR_CPUS]; ++#endif /* CONFIG_HMP_FREQUENCY_INVARIANT_SCALE */ ++#endif /* CONFIG_SCHED_HMP */ ++ ++/* We can represent the historical contribution to runnable average as the + * coefficients of a geometric series. To do this we sub-divide our runnable + * history into segments of approximately 1ms (1024us); label the segment that + * occurred N-ms ago p_N, with p_0 corresponding to the current period, e.g. +@@ -2319,13 +2428,24 @@ static u32 __compute_runnable_contrib(u64 n) + */ + static __always_inline int __update_entity_runnable_avg(u64 now, + struct sched_avg *sa, +- int runnable) ++ int runnable, ++ int running, ++ int cpu) + { + u64 delta, periods; + u32 runnable_contrib; + int delta_w, decayed = 0; ++#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE ++ u64 scaled_delta; ++ u32 scaled_runnable_contrib; ++ int scaled_delta_w; ++ u32 curr_scale = 1024; ++#endif /* CONFIG_HMP_FREQUENCY_INVARIANT_SCALE */ + + delta = now - sa->last_runnable_update; ++#ifdef CONFIG_SCHED_HMP ++ delta = hmp_variable_scale_convert(delta); ++#endif + /* + * This should only happen when time goes backwards, which it + * unfortunately does during sched clock init when we swap over to TSC. +@@ -2344,6 +2464,12 @@ static __always_inline int __update_entity_runnable_avg(u64 now, + return 0; + sa->last_runnable_update = now; + ++#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE ++ /* retrieve scale factor for load */ ++ if (hmp_data.freqinvar_load_scale_enabled) ++ curr_scale = freq_scale[cpu].curr_scale; ++#endif /* CONFIG_HMP_FREQUENCY_INVARIANT_SCALE */ ++ + /* delta_w is the amount already accumulated against our next period */ + delta_w = sa->runnable_avg_period % 1024; + if (delta + delta_w >= 1024) { +@@ -2356,8 +2482,20 @@ static __always_inline int __update_entity_runnable_avg(u64 now, + * period and accrue it. + */ + delta_w = 1024 - delta_w; ++ /* scale runnable time if necessary */ ++#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE ++ scaled_delta_w = (delta_w * curr_scale) ++ >> SCHED_FREQSCALE_SHIFT; ++ if (runnable) ++ sa->runnable_avg_sum += scaled_delta_w; ++ if (running) ++ sa->usage_avg_sum += scaled_delta_w; ++#else + if (runnable) + sa->runnable_avg_sum += delta_w; ++ if (running) ++ sa->usage_avg_sum += delta_w; ++#endif /* #ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE */ + sa->runnable_avg_period += delta_w; + + delta -= delta_w; +@@ -2365,22 +2503,49 @@ static __always_inline int __update_entity_runnable_avg(u64 now, + /* Figure out how many additional periods this update spans */ + periods = delta / 1024; + delta %= 1024; +- ++ /* decay the load we have accumulated so far */ + sa->runnable_avg_sum = decay_load(sa->runnable_avg_sum, + periods + 1); + sa->runnable_avg_period = decay_load(sa->runnable_avg_period, + periods + 1); +- ++ sa->usage_avg_sum = decay_load(sa->usage_avg_sum, periods + 1); ++ /* add the contribution from this period */ + /* Efficiently calculate \sum (1..n_period) 1024*y^i */ + runnable_contrib = __compute_runnable_contrib(periods); ++ /* Apply load scaling if necessary. ++ * Note that multiplying the whole series is same as ++ * multiplying all terms ++ */ ++#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE ++ scaled_runnable_contrib = (runnable_contrib * curr_scale) ++ >> SCHED_FREQSCALE_SHIFT; ++ if (runnable) ++ sa->runnable_avg_sum += scaled_runnable_contrib; ++ if (running) ++ sa->usage_avg_sum += scaled_runnable_contrib; ++#else + if (runnable) + sa->runnable_avg_sum += runnable_contrib; ++ if (running) ++ sa->usage_avg_sum += runnable_contrib; ++#endif /* CONFIG_HMP_FREQUENCY_INVARIANT_SCALE */ + sa->runnable_avg_period += runnable_contrib; + } + + /* Remainder of delta accrued against u_0` */ ++ /* scale if necessary */ ++#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE ++ scaled_delta = ((delta * curr_scale) >> SCHED_FREQSCALE_SHIFT); ++ if (runnable) ++ sa->runnable_avg_sum += scaled_delta; ++ if (running) ++ sa->usage_avg_sum += scaled_delta; ++#else + if (runnable) + sa->runnable_avg_sum += delta; ++ if (running) ++ sa->usage_avg_sum += delta; ++#endif /* CONFIG_HMP_FREQUENCY_INVARIANT_SCALE */ + sa->runnable_avg_period += delta; + + return decayed; +@@ -2393,12 +2558,10 @@ static inline u64 __synchronize_entity_decay(struct sched_entity *se) + u64 decays = atomic64_read(&cfs_rq->decay_counter); + + decays -= se->avg.decay_count; +- if (!decays) +- return 0; +- +- se->avg.load_avg_contrib = decay_load(se->avg.load_avg_contrib, decays); ++ if (decays) ++ se->avg.load_avg_contrib = ++ decay_load(se->avg.load_avg_contrib, decays); + se->avg.decay_count = 0; +- + return decays; + } + +@@ -2429,16 +2592,28 @@ static inline void __update_tg_runnable_avg(struct sched_avg *sa, + struct cfs_rq *cfs_rq) + { + struct task_group *tg = cfs_rq->tg; +- long contrib; ++ long contrib, usage_contrib; + + /* The fraction of a cpu used by this cfs_rq */ +- contrib = div_u64((u64)sa->runnable_avg_sum << NICE_0_SHIFT, ++ contrib = div_u64(sa->runnable_avg_sum << NICE_0_SHIFT, + sa->runnable_avg_period + 1); + contrib -= cfs_rq->tg_runnable_contrib; + +- if (abs(contrib) > cfs_rq->tg_runnable_contrib / 64) { ++ usage_contrib = div_u64(sa->usage_avg_sum << NICE_0_SHIFT, ++ sa->runnable_avg_period + 1); ++ usage_contrib -= cfs_rq->tg_usage_contrib; ++ ++ /* ++ * contrib/usage at this point represent deltas, only update if they ++ * are substantive. ++ */ ++ if ((abs(contrib) > cfs_rq->tg_runnable_contrib / 64) || ++ (abs(usage_contrib) > cfs_rq->tg_usage_contrib / 64)) { + atomic_add(contrib, &tg->runnable_avg); + cfs_rq->tg_runnable_contrib += contrib; ++ ++ atomic_add(usage_contrib, &tg->usage_avg); ++ cfs_rq->tg_usage_contrib += usage_contrib; + } + } + +@@ -2486,8 +2661,18 @@ static inline void __update_group_entity_contrib(struct sched_entity *se) + + static inline void update_rq_runnable_avg(struct rq *rq, int runnable) + { +- __update_entity_runnable_avg(rq_clock_task(rq), &rq->avg, runnable); ++ int cpu = -1; /* not used in normal case */ ++ ++#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE ++ cpu = rq->cpu; ++#endif ++ __update_entity_runnable_avg(rq_clock_task(rq), &rq->avg, runnable, ++ runnable, cpu); + __update_tg_runnable_avg(&rq->avg, &rq->cfs); ++ trace_sched_rq_runnable_ratio(cpu_of(rq), rq->avg.load_avg_ratio); ++ trace_sched_rq_runnable_load(cpu_of(rq), rq->cfs.runnable_load_avg); ++ trace_sched_rq_nr_running(cpu_of(rq), rq->nr_running, ++ rq->nr_iowait.counter); + } + #else /* CONFIG_FAIR_GROUP_SCHED */ + static inline void __update_cfs_rq_tg_load_contrib(struct cfs_rq *cfs_rq, +@@ -2506,12 +2691,19 @@ static inline void __update_task_entity_contrib(struct sched_entity *se) + contrib = se->avg.runnable_avg_sum * scale_load_down(se->load.weight); + contrib /= (se->avg.runnable_avg_period + 1); + se->avg.load_avg_contrib = scale_load(contrib); ++ trace_sched_task_load_contrib(task_of(se), se->avg.load_avg_contrib); ++ contrib = se->avg.runnable_avg_sum * scale_load_down(NICE_0_LOAD); ++ contrib /= (se->avg.runnable_avg_period + 1); ++ se->avg.load_avg_ratio = scale_load(contrib); ++ trace_sched_task_runnable_ratio(task_of(se), se->avg.load_avg_ratio); + } + + /* Compute the current contribution to load_avg by se, return any delta */ +-static long __update_entity_load_avg_contrib(struct sched_entity *se) ++static long __update_entity_load_avg_contrib(struct sched_entity *se, ++ long *ratio) + { + long old_contrib = se->avg.load_avg_contrib; ++ long old_ratio = se->avg.load_avg_ratio; + + if (entity_is_task(se)) { + __update_task_entity_contrib(se); +@@ -2520,6 +2712,8 @@ static long __update_entity_load_avg_contrib(struct sched_entity *se) + __update_group_entity_contrib(se); + } + ++ if (ratio) ++ *ratio = se->avg.load_avg_ratio - old_ratio; + return se->avg.load_avg_contrib - old_contrib; + } + +@@ -2539,9 +2733,13 @@ static inline void update_entity_load_avg(struct sched_entity *se, + int update_cfs_rq) + { + struct cfs_rq *cfs_rq = cfs_rq_of(se); +- long contrib_delta; ++ long contrib_delta, ratio_delta; + u64 now; ++ int cpu = -1; /* not used in normal case */ + ++#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE ++ cpu = cfs_rq->rq->cpu; ++#endif + /* + * For a group entity we need to use their owned cfs_rq_clock_task() in + * case they are the parent of a throttled hierarchy. +@@ -2551,18 +2749,21 @@ static inline void update_entity_load_avg(struct sched_entity *se, + else + now = cfs_rq_clock_task(group_cfs_rq(se)); + +- if (!__update_entity_runnable_avg(now, &se->avg, se->on_rq)) ++ if (!__update_entity_runnable_avg(now, &se->avg, se->on_rq, ++ cfs_rq->curr == se, cpu)) + return; + +- contrib_delta = __update_entity_load_avg_contrib(se); ++ contrib_delta = __update_entity_load_avg_contrib(se, &ratio_delta); + + if (!update_cfs_rq) + return; + +- if (se->on_rq) ++ if (se->on_rq) { + cfs_rq->runnable_load_avg += contrib_delta; +- else ++ rq_of(cfs_rq)->avg.load_avg_ratio += ratio_delta; ++ } else { + subtract_blocked_load_contrib(cfs_rq, -contrib_delta); ++ } + } + + /* +@@ -2637,6 +2838,8 @@ static inline void enqueue_entity_load_avg(struct cfs_rq *cfs_rq, + } + + cfs_rq->runnable_load_avg += se->avg.load_avg_contrib; ++ rq_of(cfs_rq)->avg.load_avg_ratio += se->avg.load_avg_ratio; ++ + /* we force update consideration on load-balancer moves */ + update_cfs_rq_blocked_load(cfs_rq, !wakeup); + } +@@ -2655,6 +2858,8 @@ static inline void dequeue_entity_load_avg(struct cfs_rq *cfs_rq, + update_cfs_rq_blocked_load(cfs_rq, !sleep); + + cfs_rq->runnable_load_avg -= se->avg.load_avg_contrib; ++ rq_of(cfs_rq)->avg.load_avg_ratio -= se->avg.load_avg_ratio; ++ + if (sleep) { + cfs_rq->blocked_load_avg += se->avg.load_avg_contrib; + se->avg.decay_count = atomic64_read(&cfs_rq->decay_counter); +@@ -2992,6 +3197,7 @@ set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) + */ + update_stats_wait_end(cfs_rq, se); + __dequeue_entity(cfs_rq, se); ++ update_entity_load_avg(se, 1); + } + + update_stats_curr_start(cfs_rq, se); +@@ -3331,6 +3537,7 @@ static inline int throttled_hierarchy(struct cfs_rq *cfs_rq) + return cfs_bandwidth_used() && cfs_rq->throttle_count; + } + ++ + /* + * Ensure that neither of the group entities corresponding to src_cpu or + * dest_cpu are members of a throttled hierarchy when performing group +@@ -3959,7 +4166,6 @@ static inline void hrtick_update(struct rq *rq) + { + } + #endif +- + /* + * The enqueue_task method is called before nr_running is + * increased. Here we update the fair scheduling stats and +@@ -4167,6 +4373,16 @@ static void task_waking_fair(struct task_struct *p) + } + + #ifdef CONFIG_FAIR_GROUP_SCHED ++ ++#ifdef CONFIG_NO_HZ_COMMON ++static int nohz_test_cpu(int cpu); ++#else ++static inline int nohz_test_cpu(int cpu) ++{ ++ return 0; ++} ++#endif ++ + /* + * effective_load() calculates the load change as seen from the root_task_group + * +@@ -4528,2529 +4744,4147 @@ done: + return target; + } + ++#ifdef CONFIG_SCHED_HMP + /* +- * select_task_rq_fair: Select target runqueue for the waking task in domains +- * that have the 'sd_flag' flag set. In practice, this is SD_BALANCE_WAKE, +- * SD_BALANCE_FORK, or SD_BALANCE_EXEC. ++ * Heterogenous multiprocessor (HMP) optimizations + * +- * Balances load by selecting the idlest cpu in the idlest group, or under +- * certain conditions an idle sibling cpu if the domain has SD_WAKE_AFFINE set. ++ * The cpu types are distinguished using a list of hmp_domains ++ * which each represent one cpu type using a cpumask. ++ * The list is assumed ordered by compute capacity with the ++ * fastest domain first. ++ */ ++DEFINE_PER_CPU(struct hmp_domain *, hmp_cpu_domain); ++static const int hmp_max_tasks = 5; ++ ++extern void __init arch_get_hmp_domains(struct list_head *hmp_domains_list); ++ ++#ifdef CONFIG_CPU_IDLE ++/* ++ * hmp_idle_pull: + * +- * Returns the target cpu number. ++ * In this version we have stopped using forced up migrations when we ++ * detect that a task running on a little CPU should be moved to a bigger ++ * CPU. In most cases, the bigger CPU is in a deep sleep state and a forced ++ * migration means we stop the task immediately but need to wait for the ++ * target CPU to wake up before we can restart the task which is being ++ *+ moved. Instead, we now wake a big CPU with an IPI and ask it to pull ++ *+ a task when ready. This allows the task to continue executing on its ++ *+ current CPU, reducing the amount of time that the task is stalled for. + * +- * preempt must be disabled. ++ * keepalive timers: ++ * ++ * The keepalive timer is used as a way to keep a CPU engaged in an ++ * idle pull operation out of idle while waiting for the source ++ * CPU to stop and move the task. Ideally this would not be necessary ++ * and we could impose a temporary zero-latency requirement on the ++ * current CPU, but in the current QoS framework this will result in ++ * all CPUs in the system being unable to enter idle states which is ++ * not desirable. The timer does not perform any work when it expires. + */ +-static int +-select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags) ++struct hmp_keepalive { ++ bool init; ++ ktime_t delay; /* if zero, no need for timer */ ++ struct hrtimer timer; ++}; ++DEFINE_PER_CPU(struct hmp_keepalive, hmp_cpu_keepalive); ++ ++/* setup per-cpu keepalive timers */ ++static enum hrtimer_restart hmp_cpu_keepalive_notify(struct hrtimer *hrtimer) + { +- struct sched_domain *tmp, *affine_sd = NULL, *sd = NULL; +- int cpu = smp_processor_id(); +- int new_cpu = cpu; +- int want_affine = 0; +- int sync = wake_flags & WF_SYNC; ++ return HRTIMER_NORESTART; ++} + +- if (p->nr_cpus_allowed == 1) +- return prev_cpu; ++/* ++ * Work out if any of the idle states have an exit latency too high for us. ++ * ns_delay is passed in containing the max we are willing to tolerate. ++ * If there are none, set ns_delay to zero. ++ * If there are any, set ns_delay to ++ * ('target_residency of state with shortest too-big latency' - 1) * 1000. ++ */ ++static void hmp_keepalive_delay(int cpu, unsigned int *ns_delay) ++{ ++ struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu); ++ struct cpuidle_driver *drv; ++ ++ drv = cpuidle_get_cpu_driver(dev); ++ if (drv) { ++ unsigned int us_delay = UINT_MAX; ++ unsigned int us_max_delay = *ns_delay / 1000; ++ int idx; ++ /* if cpuidle states are guaranteed to be sorted we ++ * could stop at the first match. ++ */ ++ for (idx = 0; idx < drv->state_count; idx++) { ++ if (drv->states[idx].exit_latency > us_max_delay && ++ drv->states[idx].target_residency < us_delay) { ++ us_delay = drv->states[idx].target_residency; ++ } ++ } ++ if (us_delay == UINT_MAX) ++ *ns_delay = 0; /* no timer required */ ++ else ++ *ns_delay = 1000 * (us_delay - 1); ++ } ++} + +- if (sd_flag & SD_BALANCE_WAKE) +- want_affine = cpumask_test_cpu(cpu, tsk_cpus_allowed(p)); ++static void hmp_cpu_keepalive_trigger(void) ++{ ++ int cpu = smp_processor_id(); ++ struct hmp_keepalive *keepalive = &per_cpu(hmp_cpu_keepalive, cpu); + +- rcu_read_lock(); +- for_each_domain(cpu, tmp) { +- if (!(tmp->flags & SD_LOAD_BALANCE)) +- continue; ++ if (!keepalive->init) { ++ unsigned int ns_delay = 100000; /* tolerate 100usec delay */ + +- /* +- * If both cpu and prev_cpu are part of this domain, +- * cpu is a valid SD_WAKE_AFFINE target. +- */ +- if (want_affine && (tmp->flags & SD_WAKE_AFFINE) && +- cpumask_test_cpu(prev_cpu, sched_domain_span(tmp))) { +- affine_sd = tmp; +- break; +- } ++ hrtimer_init(&keepalive->timer, ++ CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); ++ keepalive->timer.function = hmp_cpu_keepalive_notify; + +- if (tmp->flags & sd_flag) +- sd = tmp; ++ hmp_keepalive_delay(cpu, &ns_delay); ++ keepalive->delay = ns_to_ktime(ns_delay); ++ keepalive->init = true; + } ++ if (ktime_to_ns(keepalive->delay)) ++ hrtimer_start(&keepalive->timer, ++ keepalive->delay, HRTIMER_MODE_REL_PINNED); ++} + +- if (affine_sd && cpu != prev_cpu && wake_affine(affine_sd, p, sync)) +- prev_cpu = cpu; ++static void hmp_cpu_keepalive_cancel(int cpu) ++{ ++ struct hmp_keepalive *keepalive = &per_cpu(hmp_cpu_keepalive, cpu); + +- if (sd_flag & SD_BALANCE_WAKE) { +- new_cpu = select_idle_sibling(p, prev_cpu); +- goto unlock; +- } ++ if (keepalive->init) ++ hrtimer_cancel(&keepalive->timer); ++} ++#else /* !CONFIG_CPU_IDLE */ ++static void hmp_cpu_keepalive_trigger(void) ++{ ++} + +- while (sd) { +- struct sched_group *group; +- int weight; ++static void hmp_cpu_keepalive_cancel(int cpu) ++{ ++} ++#endif + +- if (!(sd->flags & sd_flag)) { +- sd = sd->child; +- continue; +- } ++/* Setup hmp_domains */ ++static int __init hmp_cpu_mask_setup(void) ++{ ++ char buf[64]; ++ struct hmp_domain *domain; ++ struct list_head *pos; ++ int dc, cpu; + +- group = find_idlest_group(sd, p, cpu, sd_flag); +- if (!group) { +- sd = sd->child; +- continue; +- } ++ pr_debug("Initializing HMP scheduler:\n"); + +- new_cpu = find_idlest_cpu(group, p, cpu); +- if (new_cpu == -1 || new_cpu == cpu) { +- /* Now try balancing at a lower domain level of cpu */ +- sd = sd->child; +- continue; +- } ++ /* Initialize hmp_domains using platform code */ ++ arch_get_hmp_domains(&hmp_domains); ++ if (list_empty(&hmp_domains)) { ++ pr_debug("HMP domain list is empty!\n"); ++ return 0; ++ } + +- /* Now try balancing at a lower domain level of new_cpu */ +- cpu = new_cpu; +- weight = sd->span_weight; +- sd = NULL; +- for_each_domain(cpu, tmp) { +- if (weight <= tmp->span_weight) +- break; +- if (tmp->flags & sd_flag) +- sd = tmp; ++ /* Print hmp_domains */ ++ dc = 0; ++ list_for_each(pos, &hmp_domains) { ++ domain = list_entry(pos, struct hmp_domain, hmp_domains); ++ cpulist_scnprintf(buf, 64, &domain->possible_cpus); ++ pr_debug(" HMP domain %d: %s\n", dc, buf); ++ ++ for_each_cpu_mask(cpu, domain->possible_cpus) { ++ per_cpu(hmp_cpu_domain, cpu) = domain; + } +- /* while loop will break here if sd == NULL */ ++ dc++; + } +-unlock: +- rcu_read_unlock(); + +- return new_cpu; ++ return 1; + } + +-/* +- * Called immediately before a task is migrated to a new cpu; task_cpu(p) and +- * cfs_rq_of(p) references at time of call are still valid and identify the +- * previous cpu. However, the caller only guarantees p->pi_lock is held; no +- * other assumptions, including the state of rq->lock, should be made. +- */ +-static void +-migrate_task_rq_fair(struct task_struct *p, int next_cpu) ++static struct hmp_domain *hmp_get_hmp_domain_for_cpu(int cpu) + { +- struct sched_entity *se = &p->se; +- struct cfs_rq *cfs_rq = cfs_rq_of(se); ++ struct hmp_domain *domain; ++ struct list_head *pos; + +- /* +- * Load tracking: accumulate removed load so that it can be processed +- * when we next update owning cfs_rq under rq->lock. Tasks contribute +- * to blocked load iff they have a positive decay-count. It can never +- * be negative here since on-rq tasks have decay-count == 0. +- */ +- if (se->avg.decay_count) { +- se->avg.decay_count = -__synchronize_entity_decay(se); +- atomic_long_add(se->avg.load_avg_contrib, +- &cfs_rq->removed_load); ++ list_for_each(pos, &hmp_domains) { ++ domain = list_entry(pos, struct hmp_domain, hmp_domains); ++ if (cpumask_test_cpu(cpu, &domain->possible_cpus)) ++ return domain; + } +- +- /* We have migrated, no longer consider this task hot */ +- se->exec_start = 0; ++ return NULL; + } +-#endif /* CONFIG_SMP */ + +-static unsigned long +-wakeup_gran(struct sched_entity *curr, struct sched_entity *se) ++static void hmp_online_cpu(int cpu) + { +- unsigned long gran = sysctl_sched_wakeup_granularity; ++ struct hmp_domain *domain = hmp_get_hmp_domain_for_cpu(cpu); + +- /* +- * Since its curr running now, convert the gran from real-time +- * to virtual-time in his units. +- * +- * By using 'se' instead of 'curr' we penalize light tasks, so +- * they get preempted easier. That is, if 'se' < 'curr' then +- * the resulting gran will be larger, therefore penalizing the +- * lighter, if otoh 'se' > 'curr' then the resulting gran will +- * be smaller, again penalizing the lighter task. +- * +- * This is especially important for buddies when the leftmost +- * task is higher priority than the buddy. +- */ +- return calc_delta_fair(gran, se); ++ if (domain) ++ cpumask_set_cpu(cpu, &domain->cpus); + } + +-/* +- * Should 'se' preempt 'curr'. +- * +- * |s1 +- * |s2 +- * |s3 +- * g +- * |<--->|c +- * +- * w(c, s1) = -1 +- * w(c, s2) = 0 +- * w(c, s3) = 1 +- * +- */ +-static int +-wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se) ++static void hmp_offline_cpu(int cpu) + { +- s64 gran, vdiff = curr->vruntime - se->vruntime; +- +- if (vdiff <= 0) +- return -1; ++ struct hmp_domain *domain = hmp_get_hmp_domain_for_cpu(cpu); + +- gran = wakeup_gran(curr, se); +- if (vdiff > gran) +- return 1; ++ if (domain) ++ cpumask_clear_cpu(cpu, &domain->cpus); + +- return 0; ++ hmp_cpu_keepalive_cancel(cpu); ++} ++/* ++ * Needed to determine heaviest tasks etc. ++ */ ++static inline unsigned int hmp_cpu_is_fastest(int cpu); ++static inline unsigned int hmp_cpu_is_slowest(int cpu); ++static inline struct hmp_domain *hmp_slower_domain(int cpu); ++static inline struct hmp_domain *hmp_faster_domain(int cpu); ++ ++/* must hold runqueue lock for queue se is currently on */ ++static struct sched_entity *hmp_get_heaviest_task( ++ struct sched_entity *se, int target_cpu) ++{ ++ int num_tasks = hmp_max_tasks; ++ struct sched_entity *max_se = se; ++ unsigned long int max_ratio = se->avg.load_avg_ratio; ++ const struct cpumask *hmp_target_mask = NULL; ++ struct hmp_domain *hmp; ++ ++ if (hmp_cpu_is_fastest(cpu_of(se->cfs_rq->rq))) ++ return max_se; ++ ++ hmp = hmp_faster_domain(cpu_of(se->cfs_rq->rq)); ++ hmp_target_mask = &hmp->cpus; ++ if (target_cpu >= 0) { ++ /* idle_balance gets run on a CPU while ++ * it is in the middle of being hotplugged ++ * out. Bail early in that case. ++ */ ++ if (!cpumask_test_cpu(target_cpu, hmp_target_mask)) ++ return NULL; ++ hmp_target_mask = cpumask_of(target_cpu); ++ } ++ /* The currently running task is not on the runqueue */ ++ se = __pick_first_entity(cfs_rq_of(se)); ++ ++ while (num_tasks && se) { ++ if (entity_is_task(se) && ++ se->avg.load_avg_ratio > max_ratio && ++ cpumask_intersects(hmp_target_mask, ++ tsk_cpus_allowed(task_of(se)))) { ++ max_se = se; ++ max_ratio = se->avg.load_avg_ratio; ++ } ++ se = __pick_next_entity(se); ++ num_tasks--; ++ } ++ return max_se; + } + +-static void set_last_buddy(struct sched_entity *se) ++static struct sched_entity *hmp_get_lightest_task( ++ struct sched_entity *se, int migrate_down) + { +- if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE)) +- return; ++ int num_tasks = hmp_max_tasks; ++ struct sched_entity *min_se = se; ++ unsigned long int min_ratio = se->avg.load_avg_ratio; ++ const struct cpumask *hmp_target_mask = NULL; + +- for_each_sched_entity(se) +- cfs_rq_of(se)->last = se; +-} ++ if (migrate_down) { ++ struct hmp_domain *hmp; + +-static void set_next_buddy(struct sched_entity *se) +-{ +- if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE)) +- return; ++ if (hmp_cpu_is_slowest(cpu_of(se->cfs_rq->rq))) ++ return min_se; ++ hmp = hmp_slower_domain(cpu_of(se->cfs_rq->rq)); ++ hmp_target_mask = &hmp->cpus; ++ } ++ /* The currently running task is not on the runqueue */ ++ se = __pick_first_entity(cfs_rq_of(se)); + +- for_each_sched_entity(se) +- cfs_rq_of(se)->next = se; ++ while (num_tasks && se) { ++ if (entity_is_task(se) && ++ (se->avg.load_avg_ratio < min_ratio && ++ hmp_target_mask && ++ cpumask_intersects(hmp_target_mask, ++ tsk_cpus_allowed(task_of(se))))) { ++ min_se = se; ++ min_ratio = se->avg.load_avg_ratio; ++ } ++ se = __pick_next_entity(se); ++ num_tasks--; ++ } ++ return min_se; + } + +-static void set_skip_buddy(struct sched_entity *se) +-{ +- for_each_sched_entity(se) +- cfs_rq_of(se)->skip = se; +-} ++/* ++ * Migration thresholds should be in the range [0..1023] ++ * hmp_up_threshold: min. load required for migrating tasks to a faster cpu ++ * hmp_down_threshold: max. load allowed for tasks migrating to a slower cpu ++ * ++ * hmp_up_prio: Only up migrate task with high priority (<hmp_up_prio) ++ * hmp_next_up_threshold: Delay before next up migration (1024 ~= 1 ms) ++ * hmp_next_down_threshold: Delay before next down migration (1024 ~= 1 ms) ++ * ++ * Small Task Packing: ++ * We can choose to fill the littlest CPUs in an HMP system rather than ++ * the typical spreading mechanic. This behavior is controllable using ++ * two variables. ++ * hmp_packing_enabled: runtime control over pack/spread ++ * hmp_full_threshold: Consider a CPU with this much unweighted load full ++ */ ++unsigned int hmp_up_threshold = 700; ++unsigned int hmp_down_threshold = 512; ++#ifdef CONFIG_SCHED_HMP_PRIO_FILTER ++unsigned int hmp_up_prio = NICE_TO_PRIO(CONFIG_SCHED_HMP_PRIO_FILTER_VAL); ++#endif ++unsigned int hmp_next_up_threshold = 4096; ++unsigned int hmp_next_down_threshold = 4096; + ++#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING + /* +- * Preempt the current task with a newly woken task if needed: ++ * Set the default packing threshold to try to keep little ++ * CPUs at no more than 80% of their maximum frequency if only ++ * packing a small number of small tasks. Bigger tasks will ++ * raise frequency as normal. ++ * In order to pack a task onto a CPU, the sum of the ++ * unweighted runnable_avg load of existing tasks plus the ++ * load of the new task must be less than hmp_full_threshold. ++ * ++ * This works in conjunction with frequency-invariant load ++ * and DVFS governors. Since most DVFS governors aim for 80% ++ * utilisation, we arrive at (0.8*0.8*(max_load=1024))=655 ++ * and use a value slightly lower to give a little headroom ++ * in the decision. ++ * Note that the most efficient frequency is different for ++ * each system so /sys/kernel/hmp/packing_limit should be ++ * configured at runtime for any given platform to achieve ++ * optimal energy usage. Some systems may not benefit from ++ * packing, so this feature can also be disabled at runtime ++ * with /sys/kernel/hmp/packing_enable + */ +-static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_flags) +-{ +- struct task_struct *curr = rq->curr; +- struct sched_entity *se = &curr->se, *pse = &p->se; +- struct cfs_rq *cfs_rq = task_cfs_rq(curr); +- int scale = cfs_rq->nr_running >= sched_nr_latency; +- int next_buddy_marked = 0; ++unsigned int hmp_packing_enabled = 1; ++unsigned int hmp_full_threshold = 650; ++#endif + +- if (unlikely(se == pse)) +- return; ++static unsigned int hmp_up_migration(int cpu, int *target_cpu, ++ struct sched_entity *se); ++static unsigned int hmp_down_migration(int cpu, struct sched_entity *se); ++static inline unsigned int hmp_domain_min_load(struct hmp_domain *hmpd, ++ int *min_cpu, ++ struct cpumask *affinity); + +- /* +- * This is possible from callers such as attach_tasks(), in which we +- * unconditionally check_prempt_curr() after an enqueue (which may have +- * lead to a throttle). This both saves work and prevents false +- * next-buddy nomination below. +- */ +- if (unlikely(throttled_hierarchy(cfs_rq_of(pse)))) +- return; ++static inline struct hmp_domain *hmp_smallest_domain(void) ++{ ++ return list_entry(hmp_domains.prev, struct hmp_domain, hmp_domains); ++} + +- if (sched_feat(NEXT_BUDDY) && scale && !(wake_flags & WF_FORK)) { +- set_next_buddy(pse); +- next_buddy_marked = 1; +- } ++/* Check if cpu is in fastest hmp_domain */ ++static inline unsigned int hmp_cpu_is_fastest(int cpu) ++{ ++ struct list_head *pos; + +- /* +- * We can come here with TIF_NEED_RESCHED already set from new task +- * wake up path. +- * +- * Note: this also catches the edge-case of curr being in a throttled +- * group (e.g. via set_curr_task), since update_curr() (in the +- * enqueue of curr) will have resulted in resched being set. This +- * prevents us from potentially nominating it as a false LAST_BUDDY +- * below. +- */ +- if (test_tsk_need_resched(curr)) +- return; ++ pos = &hmp_cpu_domain(cpu)->hmp_domains; ++ return pos == hmp_domains.next; ++} + +- /* Idle tasks are by definition preempted by non-idle tasks. */ +- if (unlikely(curr->policy == SCHED_IDLE) && +- likely(p->policy != SCHED_IDLE)) +- goto preempt; ++/* Check if cpu is in slowest hmp_domain */ ++static inline unsigned int hmp_cpu_is_slowest(int cpu) ++{ ++ struct list_head *pos; + +- /* +- * Batch and idle tasks do not preempt non-idle tasks (their preemption +- * is driven by the tick): +- */ +- if (unlikely(p->policy != SCHED_NORMAL) || !sched_feat(WAKEUP_PREEMPTION)) +- return; ++ pos = &hmp_cpu_domain(cpu)->hmp_domains; ++ return list_is_last(pos, &hmp_domains); ++} + +- find_matching_se(&se, &pse); +- update_curr(cfs_rq_of(se)); +- BUG_ON(!pse); +- if (wakeup_preempt_entity(se, pse) == 1) { +- /* +- * Bias pick_next to pick the sched entity that is +- * triggering this preemption. +- */ +- if (!next_buddy_marked) +- set_next_buddy(pse); +- goto preempt; +- } ++/* Next (slower) hmp_domain relative to cpu */ ++static inline struct hmp_domain *hmp_slower_domain(int cpu) ++{ ++ struct list_head *pos; + +- return; ++ pos = &hmp_cpu_domain(cpu)->hmp_domains; ++ return list_entry(pos->next, struct hmp_domain, hmp_domains); ++} + +-preempt: +- resched_curr(rq); +- /* +- * Only set the backward buddy when the current task is still +- * on the rq. This can happen when a wakeup gets interleaved +- * with schedule on the ->pre_schedule() or idle_balance() +- * point, either of which can * drop the rq lock. +- * +- * Also, during early boot the idle thread is in the fair class, +- * for obvious reasons its a bad idea to schedule back to it. +- */ +- if (unlikely(!se->on_rq || curr == rq->idle)) +- return; ++/* Previous (faster) hmp_domain relative to cpu */ ++static inline struct hmp_domain *hmp_faster_domain(int cpu) ++{ ++ struct list_head *pos; + +- if (sched_feat(LAST_BUDDY) && scale && entity_is_task(se)) +- set_last_buddy(se); ++ pos = &hmp_cpu_domain(cpu)->hmp_domains; ++ return list_entry(pos->prev, struct hmp_domain, hmp_domains); + } + +-static struct task_struct * +-pick_next_task_fair(struct rq *rq, struct task_struct *prev) ++/* ++ * Selects a cpu in previous (faster) hmp_domain ++ */ ++static inline unsigned int hmp_select_faster_cpu(struct task_struct *tsk, ++ int cpu) + { +- struct cfs_rq *cfs_rq = &rq->cfs; +- struct sched_entity *se; +- struct task_struct *p; +- int new_tasks; ++ int lowest_cpu = NR_CPUS; ++ __always_unused int lowest_ratio; ++ struct hmp_domain *hmp; + +-again: +-#ifdef CONFIG_FAIR_GROUP_SCHED +- if (!cfs_rq->nr_running) +- goto idle; ++ if (hmp_cpu_is_fastest(cpu)) ++ hmp = hmp_cpu_domain(cpu); ++ else ++ hmp = hmp_faster_domain(cpu); + +- if (prev->sched_class != &fair_sched_class) +- goto simple; ++ lowest_ratio = hmp_domain_min_load(hmp, &lowest_cpu, ++ tsk_cpus_allowed(tsk)); + +- /* +- * Because of the set_next_buddy() in dequeue_task_fair() it is rather +- * likely that a next task is from the same cgroup as the current. +- * +- * Therefore attempt to avoid putting and setting the entire cgroup +- * hierarchy, only change the part that actually changes. +- */ ++ return lowest_cpu; ++} + +- do { +- struct sched_entity *curr = cfs_rq->curr; ++/* ++ * Selects a cpu in next (slower) hmp_domain ++ * Note that cpumask_any_and() returns the first cpu in the cpumask ++ */ ++static inline unsigned int hmp_select_slower_cpu(struct task_struct *tsk, ++ int cpu) ++{ ++ int lowest_cpu = NR_CPUS; ++ struct hmp_domain *hmp; ++ __always_unused int lowest_ratio; + +- /* +- * Since we got here without doing put_prev_entity() we also +- * have to consider cfs_rq->curr. If it is still a runnable +- * entity, update_curr() will update its vruntime, otherwise +- * forget we've ever seen it. +- */ +- if (curr && curr->on_rq) +- update_curr(cfs_rq); +- else +- curr = NULL; ++ if (hmp_cpu_is_slowest(cpu)) ++ hmp = hmp_cpu_domain(cpu); ++ else ++ hmp = hmp_slower_domain(cpu); + +- /* +- * This call to check_cfs_rq_runtime() will do the throttle and +- * dequeue its entity in the parent(s). Therefore the 'simple' +- * nr_running test will indeed be correct. +- */ +- if (unlikely(check_cfs_rq_runtime(cfs_rq))) +- goto simple; ++ lowest_ratio = hmp_domain_min_load(hmp, &lowest_cpu, ++ tsk_cpus_allowed(tsk)); + +- se = pick_next_entity(cfs_rq, curr); +- cfs_rq = group_cfs_rq(se); +- } while (cfs_rq); ++ return lowest_cpu; ++} ++#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING ++/* ++ * Select the 'best' candidate little CPU to wake up on. ++ * Implements a packing strategy which examines CPU in ++ * logical CPU order, and selects the first which will ++ * be loaded less than hmp_full_threshold according to ++ * the sum of the tracked load of the runqueue and the task. ++ */ ++static inline unsigned int hmp_best_little_cpu(struct task_struct *tsk, ++ int cpu) { ++ int tmp_cpu; ++ unsigned long estimated_load; ++ struct hmp_domain *hmp; ++ struct sched_avg *avg; ++ struct cpumask allowed_hmp_cpus; ++ ++ if (!hmp_packing_enabled || ++ tsk->se.avg.load_avg_ratio > ((NICE_0_LOAD * 90)/100)) ++ return hmp_select_slower_cpu(tsk, cpu); ++ ++ if (hmp_cpu_is_slowest(cpu)) ++ hmp = hmp_cpu_domain(cpu); ++ else ++ hmp = hmp_slower_domain(cpu); ++ ++ /* respect affinity */ ++ cpumask_and(&allowed_hmp_cpus, &hmp->cpus, ++ tsk_cpus_allowed(tsk)); ++ ++ for_each_cpu_mask(tmp_cpu, allowed_hmp_cpus) { ++ avg = &cpu_rq(tmp_cpu)->avg; ++ /* estimate new rq load if we add this task */ ++ estimated_load = avg->load_avg_ratio + ++ tsk->se.avg.load_avg_ratio; ++ if (estimated_load <= hmp_full_threshold) { ++ cpu = tmp_cpu; ++ break; ++ } ++ } ++ /* if no match was found, the task uses the initial value */ ++ return cpu; ++} ++#endif + +- p = task_of(se); ++static inline void hmp_next_up_delay(struct sched_entity *se, int cpu) ++{ ++ /* hack - always use clock from first online CPU */ ++ u64 now = cpu_rq(cpumask_first(cpu_online_mask))->clock_task; + +- /* +- * Since we haven't yet done put_prev_entity and if the selected task +- * is a different task than we started out with, try and touch the +- * least amount of cfs_rqs. +- */ +- if (prev != p) { +- struct sched_entity *pse = &prev->se; ++ se->avg.hmp_last_up_migration = now; ++ se->avg.hmp_last_down_migration = 0; ++ cpu_rq(cpu)->avg.hmp_last_up_migration = now; ++ cpu_rq(cpu)->avg.hmp_last_down_migration = 0; ++} + +- while (!(cfs_rq = is_same_group(se, pse))) { +- int se_depth = se->depth; +- int pse_depth = pse->depth; ++static inline void hmp_next_down_delay(struct sched_entity *se, int cpu) ++{ ++ /* hack - always use clock from first online CPU */ ++ u64 now = cpu_rq(cpumask_first(cpu_online_mask))->clock_task; + +- if (se_depth <= pse_depth) { +- put_prev_entity(cfs_rq_of(pse), pse); +- pse = parent_entity(pse); +- } +- if (se_depth >= pse_depth) { +- set_next_entity(cfs_rq_of(se), se); +- se = parent_entity(se); +- } +- } ++ se->avg.hmp_last_down_migration = now; ++ se->avg.hmp_last_up_migration = 0; ++ cpu_rq(cpu)->avg.hmp_last_down_migration = now; ++ cpu_rq(cpu)->avg.hmp_last_up_migration = 0; ++} + +- put_prev_entity(cfs_rq, pse); +- set_next_entity(cfs_rq, se); +- } ++/* ++ * Heterogenous multiprocessor (HMP) optimizations ++ * ++ * These functions allow to change the growing speed of the load_avg_ratio ++ * by default it goes from 0 to 0.5 in LOAD_AVG_PERIOD = 32ms ++ * This can now be changed with /sys/kernel/hmp/load_avg_period_ms. ++ * ++ * These functions also allow to change the up and down threshold of HMP ++ * using /sys/kernel/hmp/{up,down}_threshold. ++ * Both must be between 0 and 1023. The threshold that is compared ++ * to the load_avg_ratio is up_threshold/1024 and down_threshold/1024. ++ * ++ * For instance, if load_avg_period = 64 and up_threshold = 512, an idle ++ * task with a load of 0 will reach the threshold after 64ms of busy loop. ++ * ++ * Changing load_avg_periods_ms has the same effect than changing the ++ * default scaling factor Y=1002/1024 in the load_avg_ratio computation to ++ * (1002/1024.0)^(LOAD_AVG_PERIOD/load_avg_period_ms), but the last one ++ * could trigger overflows. ++ * For instance, with Y = 1023/1024 in __update_task_entity_contrib() ++ * "contrib = se->avg.runnable_avg_sum * scale_load_down(se->load.weight);" ++ * could be overflowed for a weight > 2^12 even is the load_avg_contrib ++ * should still be a 32bits result. This would not happen by multiplicating ++ * delta time by 1/22 and setting load_avg_period_ms = 706. ++ */ + +- if (hrtick_enabled(rq)) +- hrtick_start_fair(rq, p); ++/* ++ * By scaling the delta time it end-up increasing or decrease the ++ * growing speed of the per entity load_avg_ratio ++ * The scale factor hmp_data.multiplier is a fixed point ++ * number: (32-HMP_VARIABLE_SCALE_SHIFT).HMP_VARIABLE_SCALE_SHIFT ++ */ ++static inline u64 hmp_variable_scale_convert(u64 delta) ++{ ++#ifdef CONFIG_HMP_VARIABLE_SCALE ++ u64 high = delta >> 32ULL; ++ u64 low = delta & 0xffffffffULL; + +- return p; +-simple: +- cfs_rq = &rq->cfs; ++ low *= hmp_data.multiplier; ++ high *= hmp_data.multiplier; ++ return (low >> HMP_VARIABLE_SCALE_SHIFT) ++ + (high << (32ULL - HMP_VARIABLE_SCALE_SHIFT)); ++#else ++ return delta; + #endif ++} + +- if (!cfs_rq->nr_running) +- goto idle; ++static ssize_t hmp_show(struct kobject *kobj, ++ struct attribute *attr, char *buf) ++{ ++ struct hmp_global_attr *hmp_attr = ++ container_of(attr, struct hmp_global_attr, attr); ++ int temp; + +- put_prev_task(rq, prev); ++ if (hmp_attr->to_sysfs_text != NULL) ++ return hmp_attr->to_sysfs_text(buf, PAGE_SIZE); + +- do { +- se = pick_next_entity(cfs_rq, NULL); +- set_next_entity(cfs_rq, se); +- cfs_rq = group_cfs_rq(se); +- } while (cfs_rq); ++ temp = *(hmp_attr->value); ++ if (hmp_attr->to_sysfs != NULL) ++ temp = hmp_attr->to_sysfs(temp); + +- p = task_of(se); ++ return (ssize_t)sprintf(buf, "%d\n", temp); ++} + +- if (hrtick_enabled(rq)) +- hrtick_start_fair(rq, p); ++static ssize_t hmp_store(struct kobject *a, struct attribute *attr, ++ const char *buf, size_t count) ++{ ++ int temp; ++ ssize_t ret = count; ++ struct hmp_global_attr *hmp_attr = ++ container_of(attr, struct hmp_global_attr, attr); ++ char *str = vmalloc(count + 1); + +- return p; ++ if (str == NULL) ++ return -ENOMEM; ++ memcpy(str, buf, count); ++ str[count] = 0; ++ if (sscanf(str, "%d", &temp) < 1) ++ ret = -EINVAL; ++ else { ++ if (hmp_attr->from_sysfs != NULL) ++ temp = hmp_attr->from_sysfs(temp); ++ if (temp < 0) ++ ret = -EINVAL; ++ else ++ *(hmp_attr->value) = temp; ++ } ++ vfree(str); ++ return ret; ++} + +-idle: +- new_tasks = idle_balance(rq); +- /* +- * Because idle_balance() releases (and re-acquires) rq->lock, it is +- * possible for any higher priority task to appear. In that case we +- * must re-start the pick_next_entity() loop. +- */ +- if (new_tasks < 0) +- return RETRY_TASK; ++static ssize_t hmp_print_domains(char *outbuf, int outbufsize) ++{ ++ char buf[64]; ++ const char nospace[] = "%s", space[] = " %s"; ++ const char *fmt = nospace; ++ struct hmp_domain *domain; ++ struct list_head *pos; ++ int outpos = 0; + +- if (new_tasks > 0) +- goto again; ++ list_for_each(pos, &hmp_domains) { ++ domain = list_entry(pos, struct hmp_domain, hmp_domains); ++ if (cpumask_scnprintf(buf, 64, &domain->possible_cpus)) { ++ outpos += sprintf(outbuf+outpos, fmt, buf); ++ fmt = space; ++ } ++ } ++ strcat(outbuf, "\n"); ++ return outpos+1; ++} + +- return NULL; ++#ifdef CONFIG_HMP_VARIABLE_SCALE ++static int hmp_period_tofrom_sysfs(int value) ++{ ++ return (LOAD_AVG_PERIOD << HMP_VARIABLE_SCALE_SHIFT) / value; + } ++#endif + +-/* +- * Account for a descheduled task: +- */ +-static void put_prev_task_fair(struct rq *rq, struct task_struct *prev) ++/* max value for threshold is 1024 */ ++static int hmp_theshold_from_sysfs(int value) + { +- struct sched_entity *se = &prev->se; +- struct cfs_rq *cfs_rq; ++ if (value > 1024) ++ return -1; ++ return value; ++} + +- for_each_sched_entity(se) { +- cfs_rq = cfs_rq_of(se); +- put_prev_entity(cfs_rq, se); +- } ++/* toggle control is only 0,1 off/on */ ++static int hmp_toggle_from_sysfs(int value) ++{ ++ if (value < 0 || value > 1) ++ return -1; ++ return value; + } + +-/* +- * sched_yield() is very simple +- * +- * The magic of dealing with the ->skip buddy is in pick_next_entity. +- */ +-static void yield_task_fair(struct rq *rq) ++#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING ++/* packing value must be non-negative */ ++static int hmp_packing_from_sysfs(int value) + { +- struct task_struct *curr = rq->curr; +- struct cfs_rq *cfs_rq = task_cfs_rq(curr); +- struct sched_entity *se = &curr->se; +- +- /* +- * Are we the only task in the tree? +- */ +- if (unlikely(rq->nr_running == 1)) +- return; ++ if (value < 0) ++ return -1; ++ return value; ++} ++#endif + +- clear_buddies(cfs_rq, se); ++static void hmp_attr_add( ++ const char *name, ++ int *value, ++ int (*to_sysfs)(int), ++ int (*from_sysfs)(int), ++ ssize_t (*to_sysfs_text)(char *, int), ++ umode_t mode) ++{ ++ int i = 0; + +- if (curr->policy != SCHED_BATCH) { +- update_rq_clock(rq); +- /* +- * Update run-time statistics of the 'current'. +- */ +- update_curr(cfs_rq); +- /* +- * Tell update_rq_clock() that we've just updated, +- * so we don't do microscopic update in schedule() +- * and double the fastpath cost. +- */ +- rq->skip_clock_update = 1; ++ while (hmp_data.attributes[i] != NULL) { ++ i++; ++ if (i >= HMP_DATA_SYSFS_MAX) ++ return; + } + +- set_skip_buddy(se); ++ if (mode) ++ hmp_data.attr[i].attr.mode = mode; ++ else ++ hmp_data.attr[i].attr.mode = 0644; ++ hmp_data.attr[i].show = hmp_show; ++ hmp_data.attr[i].store = hmp_store; ++ hmp_data.attr[i].attr.name = name; ++ hmp_data.attr[i].value = value; ++ hmp_data.attr[i].to_sysfs = to_sysfs; ++ hmp_data.attr[i].from_sysfs = from_sysfs; ++ hmp_data.attr[i].to_sysfs_text = to_sysfs_text; ++ hmp_data.attributes[i] = &hmp_data.attr[i].attr; ++ hmp_data.attributes[i + 1] = NULL; ++} ++ ++static int hmp_attr_init(void) ++{ ++ int ret; ++ ++ memset(&hmp_data, 0, sizeof(hmp_data)); ++ hmp_attr_add("hmp_domains", ++ NULL, ++ NULL, ++ NULL, ++ hmp_print_domains, ++ 0444); ++ hmp_attr_add("task_fork_on_bigcore", ++ &task_fork_on_bigcore, ++ NULL, ++ hmp_toggle_from_sysfs, ++ NULL, ++ 0); ++ hmp_attr_add("up_threshold", ++ &hmp_up_threshold, ++ NULL, ++ hmp_theshold_from_sysfs, ++ NULL, ++ 0); ++ hmp_attr_add("down_threshold", ++ &hmp_down_threshold, ++ NULL, ++ hmp_theshold_from_sysfs, ++ NULL, ++ 0); ++#ifdef CONFIG_HMP_VARIABLE_SCALE ++ /* by default load_avg_period_ms == LOAD_AVG_PERIOD ++ * meaning no change ++ */ ++ hmp_data.multiplier = hmp_period_tofrom_sysfs(LOAD_AVG_PERIOD); ++ hmp_attr_add("load_avg_period_ms", ++ &hmp_data.multiplier, ++ hmp_period_tofrom_sysfs, ++ hmp_period_tofrom_sysfs, ++ NULL, ++ 0); ++#endif ++#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE ++ /* default frequency-invariant scaling ON */ ++ hmp_data.freqinvar_load_scale_enabled = 1; ++ hmp_attr_add("frequency_invariant_load_scale", ++ &hmp_data.freqinvar_load_scale_enabled, ++ NULL, ++ hmp_toggle_from_sysfs, ++ NULL, ++ 0); ++#endif ++#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING ++ hmp_attr_add("packing_enable", ++ &hmp_packing_enabled, ++ NULL, ++ hmp_toggle_from_sysfs, ++ NULL, ++ 0); ++ hmp_attr_add("packing_limit", ++ &hmp_full_threshold, ++ NULL, ++ hmp_packing_from_sysfs, ++ NULL, ++ 0); ++#endif ++ hmp_data.attr_group.name = "hmp"; ++ hmp_data.attr_group.attrs = hmp_data.attributes; ++ ret = sysfs_create_group(kernel_kobj, ++ &hmp_data.attr_group); ++ return 0; + } +- +-static bool yield_to_task_fair(struct rq *rq, struct task_struct *p, bool preempt) ++late_initcall(hmp_attr_init); ++/* ++ * return the load of the lowest-loaded CPU in a given HMP domain ++ * min_cpu optionally points to an int to receive the CPU. ++ * affinity optionally points to a cpumask containing the ++ * CPUs to be considered. note: ++ * + min_cpu = NR_CPUS only if no CPUs are in the set of ++ * affinity && hmp_domain cpus ++ * + min_cpu will always otherwise equal one of the CPUs in ++ * the hmp domain ++ * + when more than one CPU has the same load, the one which ++ * is least-recently-disturbed by an HMP migration will be ++ * selected ++ * + if all CPUs are equally loaded or idle and the times are ++ * all the same, the first in the set will be used ++ * + if affinity is not set, cpu_online_mask is used ++ */ ++static inline unsigned int hmp_domain_min_load(struct hmp_domain *hmpd, ++ int *min_cpu, ++ struct cpumask *affinity) + { +- struct sched_entity *se = &p->se; +- +- /* throttled hierarchies are not runnable */ +- if (!se->on_rq || throttled_hierarchy(cfs_rq_of(se))) +- return false; ++ int cpu; ++ int min_cpu_runnable_temp = NR_CPUS; ++ u64 min_target_last_migration = ULLONG_MAX; ++ u64 curr_last_migration; ++ unsigned long min_runnable_load = INT_MAX; ++ unsigned long contrib; ++ struct sched_avg *avg; ++ struct cpumask temp_cpumask; ++ /* ++ * only look at CPUs allowed if specified, ++ * otherwise look at all online CPUs in the ++ * right HMP domain ++ */ ++ cpumask_and(&temp_cpumask, &hmpd->cpus, ++ affinity ? affinity : cpu_online_mask); ++ ++ for_each_cpu_mask(cpu, temp_cpumask) { ++ avg = &cpu_rq(cpu)->avg; ++ /* used for both up and down migration */ ++ curr_last_migration = avg->hmp_last_up_migration ? ++ avg->hmp_last_up_migration \ ++ : avg->hmp_last_down_migration; ++ ++ contrib = avg->load_avg_ratio; ++ /* ++ * Consider a runqueue completely busy if there is any load ++ * on it. Definitely not the best for overall fairness, but ++ * does well in typical Android use cases. ++ */ ++ if (contrib) ++ contrib = 1023; + +- /* Tell the scheduler that we'd really like pse to run next. */ +- set_next_buddy(se); ++ if ((contrib < min_runnable_load) || ++ (contrib == min_runnable_load && ++ curr_last_migration < min_target_last_migration)) { ++ /* ++ * if the load is the same target the CPU with ++ * the longest time since a migration. ++ * This is to spread migration load between ++ * members of a domain more evenly when the ++ * domain is fully loaded ++ */ ++ min_runnable_load = contrib; ++ min_cpu_runnable_temp = cpu; ++ min_target_last_migration = curr_last_migration; ++ } ++ } + +- yield_task_fair(rq); ++ if (min_cpu) ++ *min_cpu = min_cpu_runnable_temp; + +- return true; ++ return min_runnable_load; + } + +-#ifdef CONFIG_SMP +-/************************************************** +- * Fair scheduling class load-balancing methods. +- * +- * BASICS +- * +- * The purpose of load-balancing is to achieve the same basic fairness the +- * per-cpu scheduler provides, namely provide a proportional amount of compute +- * time to each task. This is expressed in the following equation: +- * +- * W_i,n/P_i == W_j,n/P_j for all i,j (1) +- * +- * Where W_i,n is the n-th weight average for cpu i. The instantaneous weight +- * W_i,0 is defined as: +- * +- * W_i,0 = \Sum_j w_i,j (2) +- * +- * Where w_i,j is the weight of the j-th runnable task on cpu i. This weight +- * is derived from the nice value as per prio_to_weight[]. +- * +- * The weight average is an exponential decay average of the instantaneous +- * weight: +- * +- * W'_i,n = (2^n - 1) / 2^n * W_i,n + 1 / 2^n * W_i,0 (3) +- * +- * C_i is the compute capacity of cpu i, typically it is the +- * fraction of 'recent' time available for SCHED_OTHER task execution. But it +- * can also include other factors [XXX]. +- * +- * To achieve this balance we define a measure of imbalance which follows +- * directly from (1): +- * +- * imb_i,j = max{ avg(W/C), W_i/C_i } - min{ avg(W/C), W_j/C_j } (4) +- * +- * We them move tasks around to minimize the imbalance. In the continuous +- * function space it is obvious this converges, in the discrete case we get +- * a few fun cases generally called infeasible weight scenarios. +- * +- * [XXX expand on: +- * - infeasible weights; +- * - local vs global optima in the discrete case. ] +- * +- * +- * SCHED DOMAINS +- * +- * In order to solve the imbalance equation (4), and avoid the obvious O(n^2) +- * for all i,j solution, we create a tree of cpus that follows the hardware +- * topology where each level pairs two lower groups (or better). This results +- * in O(log n) layers. Furthermore we reduce the number of cpus going up the +- * tree to only the first of the previous level and we decrease the frequency +- * of load-balance at each level inv. proportional to the number of cpus in +- * the groups. +- * +- * This yields: +- * +- * log_2 n 1 n +- * \Sum { --- * --- * 2^i } = O(n) (5) +- * i = 0 2^i 2^i +- * `- size of each group +- * | | `- number of cpus doing load-balance +- * | `- freq +- * `- sum over all levels +- * +- * Coupled with a limit on how many tasks we can migrate every balance pass, +- * this makes (5) the runtime complexity of the balancer. +- * +- * An important property here is that each CPU is still (indirectly) connected +- * to every other cpu in at most O(log n) steps: +- * +- * The adjacency matrix of the resulting graph is given by: +- * +- * log_2 n +- * A_i,j = \Union (i % 2^k == 0) && i / 2^(k+1) == j / 2^(k+1) (6) +- * k = 0 +- * +- * And you'll find that: +- * +- * A^(log_2 n)_i,j != 0 for all i,j (7) +- * +- * Showing there's indeed a path between every cpu in at most O(log n) steps. +- * The task movement gives a factor of O(m), giving a convergence complexity +- * of: +- * +- * O(nm log n), n := nr_cpus, m := nr_tasks (8) +- * +- * +- * WORK CONSERVING +- * +- * In order to avoid CPUs going idle while there's still work to do, new idle +- * balancing is more aggressive and has the newly idle cpu iterate up the domain +- * tree itself instead of relying on other CPUs to bring it work. +- * +- * This adds some complexity to both (5) and (8) but it reduces the total idle +- * time. +- * +- * [XXX more?] +- * +- * +- * CGROUPS +- * +- * Cgroups make a horror show out of (2), instead of a simple sum we get: +- * +- * s_k,i +- * W_i,0 = \Sum_j \Prod_k w_k * ----- (9) +- * S_k +- * +- * Where +- * +- * s_k,i = \Sum_j w_i,j,k and S_k = \Sum_i s_k,i (10) +- * +- * w_i,j,k is the weight of the j-th runnable task in the k-th cgroup on cpu i. +- * +- * The big problem is S_k, its a global sum needed to compute a local (W_i) +- * property. +- * +- * [XXX write more on how we solve this.. _after_ merging pjt's patches that +- * rewrite all of this once again.] +- */ ++/* ++ * Calculate the task starvation ++ * This is the ratio of actually running time vs. runnable time. ++ * If the two are equal the task is getting the cpu time it needs or ++ * it is alone on the cpu and the cpu is fully utilized. ++ */ ++static inline unsigned int hmp_task_starvation(struct sched_entity *se) ++{ ++ u32 starvation; + +-static unsigned long __read_mostly max_load_balance_interval = HZ/10; ++ starvation = se->avg.usage_avg_sum * scale_load_down(NICE_0_LOAD); ++ starvation /= (se->avg.runnable_avg_sum + 1); + +-enum fbq_type { regular, remote, all }; ++ return scale_load(starvation); ++} + +-#define LBF_ALL_PINNED 0x01 +-#define LBF_NEED_BREAK 0x02 +-#define LBF_DST_PINNED 0x04 +-#define LBF_SOME_PINNED 0x08 ++static inline unsigned int hmp_offload_down(int cpu, struct sched_entity *se) ++{ ++ int min_usage; ++ int dest_cpu = NR_CPUS; + +-struct lb_env { +- struct sched_domain *sd; ++ if (hmp_cpu_is_slowest(cpu)) ++ return NR_CPUS; + +- struct rq *src_rq; +- int src_cpu; ++ /* Is there an idle CPU in the current domain */ ++ min_usage = hmp_domain_min_load(hmp_cpu_domain(cpu), NULL, NULL); ++ if (min_usage == 0) { ++ trace_sched_hmp_offload_abort(cpu, min_usage, "load"); ++ return NR_CPUS; ++ } + +- int dst_cpu; +- struct rq *dst_rq; ++ /* Is the task alone on the cpu? */ ++ if (cpu_rq(cpu)->cfs.h_nr_running < 2) { ++ trace_sched_hmp_offload_abort(cpu, ++ cpu_rq(cpu)->cfs.h_nr_running, "nr_running"); ++ return NR_CPUS; ++ } + +- struct cpumask *dst_grpmask; +- int new_dst_cpu; +- enum cpu_idle_type idle; +- long imbalance; +- /* The set of CPUs under consideration for load-balancing */ +- struct cpumask *cpus; ++ /* Is the task actually starving? */ ++ /* >=25% ratio running/runnable = starving */ ++ if (hmp_task_starvation(se) > 768) { ++ trace_sched_hmp_offload_abort(cpu, hmp_task_starvation(se), ++ "starvation"); ++ return NR_CPUS; ++ } + +- unsigned int flags; ++ /* Does the slower domain have any idle CPUs? */ ++ min_usage = hmp_domain_min_load(hmp_slower_domain(cpu), &dest_cpu, ++ tsk_cpus_allowed(task_of(se))); + +- unsigned int loop; +- unsigned int loop_break; +- unsigned int loop_max; ++ if (min_usage == 0) { ++ trace_sched_hmp_offload_succeed(cpu, dest_cpu); ++ return dest_cpu; ++ } else ++ trace_sched_hmp_offload_abort(cpu, min_usage, "slowdomain"); ++ return NR_CPUS; ++} ++#endif /* CONFIG_SCHED_HMP */ + +- enum fbq_type fbq_type; +- struct list_head tasks; +-}; + + /* +- * Is this task likely cache-hot: ++ * select_task_rq_fair: Select target runqueue for the waking task in domains ++ * that have the 'sd_flag' flag set. In practice, this is SD_BALANCE_WAKE, ++ * SD_BALANCE_FORK, or SD_BALANCE_EXEC. ++ * ++ * Balances load by selecting the idlest cpu in the idlest group, or under ++ * certain conditions an idle sibling cpu if the domain has SD_WAKE_AFFINE set. ++ * ++ * Returns the target cpu number. ++ * ++ * preempt must be disabled. + */ +-static int task_hot(struct task_struct *p, struct lb_env *env) ++static int ++select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags) + { +- s64 delta; +- +- lockdep_assert_held(&env->src_rq->lock); ++ struct sched_domain *tmp, *affine_sd = NULL, *sd = NULL; ++ int cpu = smp_processor_id(); ++ int new_cpu = cpu; ++ int want_affine = 0; ++ int sync = wake_flags & WF_SYNC; + +- if (p->sched_class != &fair_sched_class) +- return 0; ++ if (p->nr_cpus_allowed == 1) ++ return prev_cpu; + +- if (unlikely(p->policy == SCHED_IDLE)) +- return 0; ++ if (sd_flag & SD_BALANCE_WAKE) ++ want_affine = cpumask_test_cpu(cpu, tsk_cpus_allowed(p)); + +- /* +- * Buddy candidates are cache hot: +- */ +- if (sched_feat(CACHE_HOT_BUDDY) && env->dst_rq->nr_running && +- (&p->se == cfs_rq_of(&p->se)->next || +- &p->se == cfs_rq_of(&p->se)->last)) +- return 1; ++ rcu_read_lock(); ++ for_each_domain(cpu, tmp) { ++ if (!(tmp->flags & SD_LOAD_BALANCE)) ++ continue; + +- if (sysctl_sched_migration_cost == -1) +- return 1; +- if (sysctl_sched_migration_cost == 0) +- return 0; ++ /* ++ * If both cpu and prev_cpu are part of this domain, ++ * cpu is a valid SD_WAKE_AFFINE target. ++ */ ++ if (want_affine && (tmp->flags & SD_WAKE_AFFINE) && ++ cpumask_test_cpu(prev_cpu, sched_domain_span(tmp))) { ++ affine_sd = tmp; ++ break; ++ } + +- delta = rq_clock_task(env->src_rq) - p->se.exec_start; ++ if (tmp->flags & sd_flag) ++ sd = tmp; ++ } + +- return delta < (s64)sysctl_sched_migration_cost; +-} ++ if (affine_sd && cpu != prev_cpu && wake_affine(affine_sd, p, sync)) ++ prev_cpu = cpu; + +-#ifdef CONFIG_NUMA_BALANCING +-/* Returns true if the destination node has incurred more faults */ +-static bool migrate_improves_locality(struct task_struct *p, struct lb_env *env) +-{ +- struct numa_group *numa_group = rcu_dereference(p->numa_group); +- int src_nid, dst_nid; ++#ifdef CONFIG_SCHED_HMP ++ /* always put non-kernel forking tasks on a big domain */ ++ if (unlikely(sd_flag & SD_BALANCE_FORK) ++ && hmp_task_should_forkboost(p)) { ++ new_cpu = hmp_select_faster_cpu(p, prev_cpu); ++ if (new_cpu != NR_CPUS) { ++ hmp_next_up_delay(&p->se, new_cpu); ++ return new_cpu; ++ } ++ /* failed to perform HMP fork balance, use normal balance */ ++ new_cpu = cpu; ++ } ++#endif + +- if (!sched_feat(NUMA_FAVOUR_HIGHER) || !p->numa_faults_memory || +- !(env->sd->flags & SD_NUMA)) { +- return false; ++ if (sd_flag & SD_BALANCE_WAKE) { ++ new_cpu = select_idle_sibling(p, prev_cpu); ++ goto unlock; + } + +- src_nid = cpu_to_node(env->src_cpu); +- dst_nid = cpu_to_node(env->dst_cpu); ++ while (sd) { ++ struct sched_group *group; ++ int weight; + +- if (src_nid == dst_nid) +- return false; ++ if (!(sd->flags & sd_flag)) { ++ sd = sd->child; ++ continue; ++ } + +- if (numa_group) { +- /* Task is already in the group's interleave set. */ +- if (node_isset(src_nid, numa_group->active_nodes)) +- return false; ++ group = find_idlest_group(sd, p, cpu, sd_flag); ++ if (!group) { ++ sd = sd->child; ++ continue; ++ } + +- /* Task is moving into the group's interleave set. */ +- if (node_isset(dst_nid, numa_group->active_nodes)) +- return true; ++ new_cpu = find_idlest_cpu(group, p, cpu); ++ if (new_cpu == -1 || new_cpu == cpu) { ++ /* Now try balancing at a lower domain level of cpu */ ++ sd = sd->child; ++ continue; ++ } + +- return group_faults(p, dst_nid) > group_faults(p, src_nid); ++ /* Now try balancing at a lower domain level of new_cpu */ ++ cpu = new_cpu; ++ weight = sd->span_weight; ++ sd = NULL; ++ for_each_domain(cpu, tmp) { ++ if (weight <= tmp->span_weight) ++ break; ++ if (tmp->flags & sd_flag) ++ sd = tmp; ++ } ++ /* while loop will break here if sd == NULL */ + } ++unlock: ++ rcu_read_unlock(); + +- /* Encourage migration to the preferred node. */ +- if (dst_nid == p->numa_preferred_nid) +- return true; ++#ifdef CONFIG_SCHED_HMP ++ prev_cpu = task_cpu(p); + +- return task_faults(p, dst_nid) > task_faults(p, src_nid); +-} ++ if (hmp_up_migration(prev_cpu, &new_cpu, &p->se)) { ++ hmp_next_up_delay(&p->se, new_cpu); ++ trace_sched_hmp_migrate(p, new_cpu, HMP_MIGRATE_WAKEUP); ++ return new_cpu; ++ } ++ if (hmp_down_migration(prev_cpu, &p->se)) { ++#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING ++ new_cpu = hmp_best_little_cpu(p, prev_cpu); ++#else ++ new_cpu = hmp_select_slower_cpu(p, prev_cpu); ++#endif ++ /* ++ * we might have no suitable CPU ++ * in which case new_cpu == NR_CPUS ++ */ ++ if (new_cpu < NR_CPUS && new_cpu != prev_cpu) { ++ hmp_next_down_delay(&p->se, new_cpu); ++ trace_sched_hmp_migrate(p, new_cpu, HMP_MIGRATE_WAKEUP); ++ return new_cpu; ++ } ++ } ++ /* Make sure that the task stays in its previous hmp domain */ ++ if (!cpumask_test_cpu(new_cpu, &hmp_cpu_domain(prev_cpu)->cpus)) ++ return prev_cpu; ++#endif + ++ return new_cpu; ++} + +-static bool migrate_degrades_locality(struct task_struct *p, struct lb_env *env) ++/* ++ * Called immediately before a task is migrated to a new cpu; task_cpu(p) and ++ * cfs_rq_of(p) references at time of call are still valid and identify the ++ * previous cpu. However, the caller only guarantees p->pi_lock is held; no ++ * other assumptions, including the state of rq->lock, should be made. ++ */ ++static void ++migrate_task_rq_fair(struct task_struct *p, int next_cpu) + { +- struct numa_group *numa_group = rcu_dereference(p->numa_group); +- int src_nid, dst_nid; ++ struct sched_entity *se = &p->se; ++ struct cfs_rq *cfs_rq = cfs_rq_of(se); + +- if (!sched_feat(NUMA) || !sched_feat(NUMA_RESIST_LOWER)) +- return false; ++ /* ++ * Load tracking: accumulate removed load so that it can be processed ++ * when we next update owning cfs_rq under rq->lock. Tasks contribute ++ * to blocked load iff they have a positive decay-count. It can never ++ * be negative here since on-rq tasks have decay-count == 0. ++ */ ++ if (se->avg.decay_count) { ++ /* ++ * If we migrate a sleeping task away from a CPU ++ * which has the tick stopped, then both the clock_task ++ * and decay_counter will be out of date for that CPU ++ * and we will not decay load correctly. ++ */ ++ if (!se->on_rq && nohz_test_cpu(task_cpu(p))) { ++ struct rq *rq = cpu_rq(task_cpu(p)); ++ unsigned long flags; ++ /* ++ * Current CPU cannot be holding rq->lock in this ++ * circumstance, but another might be. We must hold ++ * rq->lock before we go poking around in its clocks ++ */ ++ raw_spin_lock_irqsave(&rq->lock, flags); ++ update_rq_clock(rq); ++ update_cfs_rq_blocked_load(cfs_rq, 0); ++ raw_spin_unlock_irqrestore(&rq->lock, flags); ++ } ++ se->avg.decay_count = -__synchronize_entity_decay(se); ++ atomic_long_add(se->avg.load_avg_contrib, ++ &cfs_rq->removed_load); ++ } + +- if (!p->numa_faults_memory || !(env->sd->flags & SD_NUMA)) +- return false; ++ /* We have migrated, no longer consider this task hot */ ++ se->exec_start = 0; ++} ++#endif /* CONFIG_SMP */ + +- src_nid = cpu_to_node(env->src_cpu); +- dst_nid = cpu_to_node(env->dst_cpu); ++static unsigned long ++wakeup_gran(struct sched_entity *curr, struct sched_entity *se) ++{ ++ unsigned long gran = sysctl_sched_wakeup_granularity; + +- if (src_nid == dst_nid) +- return false; ++ /* ++ * Since its curr running now, convert the gran from real-time ++ * to virtual-time in his units. ++ * ++ * By using 'se' instead of 'curr' we penalize light tasks, so ++ * they get preempted easier. That is, if 'se' < 'curr' then ++ * the resulting gran will be larger, therefore penalizing the ++ * lighter, if otoh 'se' > 'curr' then the resulting gran will ++ * be smaller, again penalizing the lighter task. ++ * ++ * This is especially important for buddies when the leftmost ++ * task is higher priority than the buddy. ++ */ ++ return calc_delta_fair(gran, se); ++} + +- if (numa_group) { +- /* Task is moving within/into the group's interleave set. */ +- if (node_isset(dst_nid, numa_group->active_nodes)) +- return false; ++/* ++ * Should 'se' preempt 'curr'. ++ * ++ * |s1 ++ * |s2 ++ * |s3 ++ * g ++ * |<--->|c ++ * ++ * w(c, s1) = -1 ++ * w(c, s2) = 0 ++ * w(c, s3) = 1 ++ * ++ */ ++static int ++wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se) ++{ ++ s64 gran, vdiff = curr->vruntime - se->vruntime; + +- /* Task is moving out of the group's interleave set. */ +- if (node_isset(src_nid, numa_group->active_nodes)) +- return true; ++ if (vdiff <= 0) ++ return -1; + +- return group_faults(p, dst_nid) < group_faults(p, src_nid); +- } ++ gran = wakeup_gran(curr, se); ++ if (vdiff > gran) ++ return 1; + +- /* Migrating away from the preferred node is always bad. */ +- if (src_nid == p->numa_preferred_nid) +- return true; ++ return 0; ++} + +- return task_faults(p, dst_nid) < task_faults(p, src_nid); ++static void set_last_buddy(struct sched_entity *se) ++{ ++ if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE)) ++ return; ++ ++ for_each_sched_entity(se) ++ cfs_rq_of(se)->last = se; + } + +-#else +-static inline bool migrate_improves_locality(struct task_struct *p, +- struct lb_env *env) ++static void set_next_buddy(struct sched_entity *se) + { +- return false; ++ if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE)) ++ return; ++ ++ for_each_sched_entity(se) ++ cfs_rq_of(se)->next = se; + } + +-static inline bool migrate_degrades_locality(struct task_struct *p, +- struct lb_env *env) ++static void set_skip_buddy(struct sched_entity *se) + { +- return false; ++ for_each_sched_entity(se) ++ cfs_rq_of(se)->skip = se; + } +-#endif + + /* +- * can_migrate_task - may task p from runqueue rq be migrated to this_cpu? ++ * Preempt the current task with a newly woken task if needed: + */ +-static +-int can_migrate_task(struct task_struct *p, struct lb_env *env) ++static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_flags) + { +- int tsk_cache_hot = 0; ++ struct task_struct *curr = rq->curr; ++ struct sched_entity *se = &curr->se, *pse = &p->se; ++ struct cfs_rq *cfs_rq = task_cfs_rq(curr); ++ int scale = cfs_rq->nr_running >= sched_nr_latency; ++ int next_buddy_marked = 0; + +- lockdep_assert_held(&env->src_rq->lock); ++ if (unlikely(se == pse)) ++ return; + + /* +- * We do not migrate tasks that are: +- * 1) throttled_lb_pair, or +- * 2) cannot be migrated to this CPU due to cpus_allowed, or +- * 3) running (obviously), or +- * 4) are cache-hot on their current CPU. ++ * This is possible from callers such as attach_tasks(), in which we ++ * unconditionally check_prempt_curr() after an enqueue (which may have ++ * lead to a throttle). This both saves work and prevents false ++ * next-buddy nomination below. + */ +- if (throttled_lb_pair(task_group(p), env->src_cpu, env->dst_cpu)) +- return 0; +- +- if (!cpumask_test_cpu(env->dst_cpu, tsk_cpus_allowed(p))) { +- int cpu; +- +- schedstat_inc(p, se.statistics.nr_failed_migrations_affine); ++ if (unlikely(throttled_hierarchy(cfs_rq_of(pse)))) ++ return; + +- env->flags |= LBF_SOME_PINNED; ++ if (sched_feat(NEXT_BUDDY) && scale && !(wake_flags & WF_FORK)) { ++ set_next_buddy(pse); ++ next_buddy_marked = 1; ++ } + +- /* +- * Remember if this task can be migrated to any other cpu in +- * our sched_group. We may want to revisit it if we couldn't +- * meet load balance goals by pulling other tasks on src_cpu. +- * +- * Also avoid computing new_dst_cpu if we have already computed +- * one in current iteration. +- */ +- if (!env->dst_grpmask || (env->flags & LBF_DST_PINNED)) +- return 0; +- +- /* Prevent to re-select dst_cpu via env's cpus */ +- for_each_cpu_and(cpu, env->dst_grpmask, env->cpus) { +- if (cpumask_test_cpu(cpu, tsk_cpus_allowed(p))) { +- env->flags |= LBF_DST_PINNED; +- env->new_dst_cpu = cpu; +- break; +- } +- } +- +- return 0; +- } +- +- /* Record that we found atleast one task that could run on dst_cpu */ +- env->flags &= ~LBF_ALL_PINNED; ++ /* ++ * We can come here with TIF_NEED_RESCHED already set from new task ++ * wake up path. ++ * ++ * Note: this also catches the edge-case of curr being in a throttled ++ * group (e.g. via set_curr_task), since update_curr() (in the ++ * enqueue of curr) will have resulted in resched being set. This ++ * prevents us from potentially nominating it as a false LAST_BUDDY ++ * below. ++ */ ++ if (test_tsk_need_resched(curr)) ++ return; + +- if (task_running(env->src_rq, p)) { +- schedstat_inc(p, se.statistics.nr_failed_migrations_running); +- return 0; +- } ++ /* Idle tasks are by definition preempted by non-idle tasks. */ ++ if (unlikely(curr->policy == SCHED_IDLE) && ++ likely(p->policy != SCHED_IDLE)) ++ goto preempt; + + /* +- * Aggressive migration if: +- * 1) destination numa is preferred +- * 2) task is cache cold, or +- * 3) too many balance attempts have failed. ++ * Batch and idle tasks do not preempt non-idle tasks (their preemption ++ * is driven by the tick): + */ +- tsk_cache_hot = task_hot(p, env); +- if (!tsk_cache_hot) +- tsk_cache_hot = migrate_degrades_locality(p, env); ++ if (unlikely(p->policy != SCHED_NORMAL) || !sched_feat(WAKEUP_PREEMPTION)) ++ return; + +- if (migrate_improves_locality(p, env) || !tsk_cache_hot || +- env->sd->nr_balance_failed > env->sd->cache_nice_tries) { +- if (tsk_cache_hot) { +- schedstat_inc(env->sd, lb_hot_gained[env->idle]); +- schedstat_inc(p, se.statistics.nr_forced_migrations); +- } +- return 1; ++ find_matching_se(&se, &pse); ++ update_curr(cfs_rq_of(se)); ++ BUG_ON(!pse); ++ if (wakeup_preempt_entity(se, pse) == 1) { ++ /* ++ * Bias pick_next to pick the sched entity that is ++ * triggering this preemption. ++ */ ++ if (!next_buddy_marked) ++ set_next_buddy(pse); ++ goto preempt; + } + +- schedstat_inc(p, se.statistics.nr_failed_migrations_hot); +- return 0; +-} ++ return; + +-/* +- * detach_task() -- detach the task for the migration specified in env +- */ +-static void detach_task(struct task_struct *p, struct lb_env *env) +-{ +- lockdep_assert_held(&env->src_rq->lock); ++preempt: ++ resched_curr(rq); ++ /* ++ * Only set the backward buddy when the current task is still ++ * on the rq. This can happen when a wakeup gets interleaved ++ * with schedule on the ->pre_schedule() or idle_balance() ++ * point, either of which can * drop the rq lock. ++ * ++ * Also, during early boot the idle thread is in the fair class, ++ * for obvious reasons its a bad idea to schedule back to it. ++ */ ++ if (unlikely(!se->on_rq || curr == rq->idle)) ++ return; + +- deactivate_task(env->src_rq, p, 0); +- p->on_rq = TASK_ON_RQ_MIGRATING; +- set_task_cpu(p, env->dst_cpu); ++ if (sched_feat(LAST_BUDDY) && scale && entity_is_task(se)) ++ set_last_buddy(se); + } + +-/* +- * detach_one_task() -- tries to dequeue exactly one task from env->src_rq, as +- * part of active balancing operations within "domain". +- * +- * Returns a task if successful and NULL otherwise. +- */ +-static struct task_struct *detach_one_task(struct lb_env *env) ++static struct task_struct * ++pick_next_task_fair(struct rq *rq, struct task_struct *prev) + { +- struct task_struct *p, *n; ++ struct cfs_rq *cfs_rq = &rq->cfs; ++ struct sched_entity *se; ++ struct task_struct *p; ++ int new_tasks; + +- lockdep_assert_held(&env->src_rq->lock); ++again: ++#ifdef CONFIG_FAIR_GROUP_SCHED ++ if (!cfs_rq->nr_running) ++ goto idle; + +- list_for_each_entry_safe(p, n, &env->src_rq->cfs_tasks, se.group_node) { +- if (!can_migrate_task(p, env)) +- continue; ++ if (prev->sched_class != &fair_sched_class) ++ goto simple; + +- detach_task(p, env); ++ /* ++ * Because of the set_next_buddy() in dequeue_task_fair() it is rather ++ * likely that a next task is from the same cgroup as the current. ++ * ++ * Therefore attempt to avoid putting and setting the entire cgroup ++ * hierarchy, only change the part that actually changes. ++ */ ++ ++ do { ++ struct sched_entity *curr = cfs_rq->curr; + + /* +- * Right now, this is only the second place where +- * lb_gained[env->idle] is updated (other is detach_tasks) +- * so we can safely collect stats here rather than +- * inside detach_tasks(). ++ * Since we got here without doing put_prev_entity() we also ++ * have to consider cfs_rq->curr. If it is still a runnable ++ * entity, update_curr() will update its vruntime, otherwise ++ * forget we've ever seen it. + */ +- schedstat_inc(env->sd, lb_gained[env->idle]); +- return p; +- } +- return NULL; +-} +- +-static const unsigned int sched_nr_migrate_break = 32; ++ if (curr && curr->on_rq) ++ update_curr(cfs_rq); ++ else ++ curr = NULL; + +-/* +- * detach_tasks() -- tries to detach up to imbalance weighted load from +- * busiest_rq, as part of a balancing operation within domain "sd". +- * +- * Returns number of detached tasks if successful and 0 otherwise. +- */ +-static int detach_tasks(struct lb_env *env) +-{ +- struct list_head *tasks = &env->src_rq->cfs_tasks; +- struct task_struct *p; +- unsigned long load; +- int detached = 0; ++ /* ++ * This call to check_cfs_rq_runtime() will do the throttle and ++ * dequeue its entity in the parent(s). Therefore the 'simple' ++ * nr_running test will indeed be correct. ++ */ ++ if (unlikely(check_cfs_rq_runtime(cfs_rq))) ++ goto simple; + +- lockdep_assert_held(&env->src_rq->lock); ++ se = pick_next_entity(cfs_rq, curr); ++ cfs_rq = group_cfs_rq(se); ++ } while (cfs_rq); + +- if (env->imbalance <= 0) +- return 0; ++ p = task_of(se); + +- while (!list_empty(tasks)) { +- p = list_first_entry(tasks, struct task_struct, se.group_node); ++ /* ++ * Since we haven't yet done put_prev_entity and if the selected task ++ * is a different task than we started out with, try and touch the ++ * least amount of cfs_rqs. ++ */ ++ if (prev != p) { ++ struct sched_entity *pse = &prev->se; + +- env->loop++; +- /* We've more or less seen every task there is, call it quits */ +- if (env->loop > env->loop_max) +- break; ++ while (!(cfs_rq = is_same_group(se, pse))) { ++ int se_depth = se->depth; ++ int pse_depth = pse->depth; + +- /* take a breather every nr_migrate tasks */ +- if (env->loop > env->loop_break) { +- env->loop_break += sched_nr_migrate_break; +- env->flags |= LBF_NEED_BREAK; +- break; ++ if (se_depth <= pse_depth) { ++ put_prev_entity(cfs_rq_of(pse), pse); ++ pse = parent_entity(pse); ++ } ++ if (se_depth >= pse_depth) { ++ set_next_entity(cfs_rq_of(se), se); ++ se = parent_entity(se); ++ } + } + +- if (!can_migrate_task(p, env)) +- goto next; ++ put_prev_entity(cfs_rq, pse); ++ set_next_entity(cfs_rq, se); ++ } + +- load = task_h_load(p); ++ if (hrtick_enabled(rq)) ++ hrtick_start_fair(rq, p); + +- if (sched_feat(LB_MIN) && load < 16 && !env->sd->nr_balance_failed) +- goto next; ++ return p; ++simple: ++ cfs_rq = &rq->cfs; ++#endif + +- if ((load / 2) > env->imbalance) +- goto next; ++ if (!cfs_rq->nr_running) ++ goto idle; + +- detach_task(p, env); +- list_add(&p->se.group_node, &env->tasks); ++ put_prev_task(rq, prev); + +- detached++; +- env->imbalance -= load; ++ do { ++ se = pick_next_entity(cfs_rq, NULL); ++ set_next_entity(cfs_rq, se); ++ cfs_rq = group_cfs_rq(se); ++ } while (cfs_rq); + +-#ifdef CONFIG_PREEMPT +- /* +- * NEWIDLE balancing is a source of latency, so preemptible +- * kernels will stop after the first task is detached to minimize +- * the critical section. +- */ +- if (env->idle == CPU_NEWLY_IDLE) +- break; +-#endif ++ p = task_of(se); + +- /* +- * We only want to steal up to the prescribed amount of +- * weighted load. +- */ +- if (env->imbalance <= 0) +- break; ++ if (hrtick_enabled(rq)) ++ hrtick_start_fair(rq, p); + +- continue; +-next: +- list_move_tail(&p->se.group_node, tasks); +- } ++ return p; + ++idle: ++ new_tasks = idle_balance(rq); + /* +- * Right now, this is one of only two places we collect this stat +- * so we can safely collect detach_one_task() stats here rather +- * than inside detach_one_task(). ++ * Because idle_balance() releases (and re-acquires) rq->lock, it is ++ * possible for any higher priority task to appear. In that case we ++ * must re-start the pick_next_entity() loop. + */ +- schedstat_add(env->sd, lb_gained[env->idle], detached); +- +- return detached; +-} +- +-/* +- * attach_task() -- attach the task detached by detach_task() to its new rq. +- */ +-static void attach_task(struct rq *rq, struct task_struct *p) +-{ +- lockdep_assert_held(&rq->lock); ++ if (new_tasks < 0) ++ return RETRY_TASK; + +- BUG_ON(task_rq(p) != rq); +- p->on_rq = TASK_ON_RQ_QUEUED; +- activate_task(rq, p, 0); +- check_preempt_curr(rq, p, 0); +-} ++ if (new_tasks > 0) ++ goto again; + +-/* +- * attach_one_task() -- attaches the task returned from detach_one_task() to +- * its new rq. +- */ +-static void attach_one_task(struct rq *rq, struct task_struct *p) +-{ +- raw_spin_lock(&rq->lock); +- attach_task(rq, p); +- raw_spin_unlock(&rq->lock); ++ return NULL; + } + + /* +- * attach_tasks() -- attaches all tasks detached by detach_tasks() to their +- * new rq. ++ * Account for a descheduled task: + */ +-static void attach_tasks(struct lb_env *env) ++static void put_prev_task_fair(struct rq *rq, struct task_struct *prev) + { +- struct list_head *tasks = &env->tasks; +- struct task_struct *p; +- +- raw_spin_lock(&env->dst_rq->lock); +- +- while (!list_empty(tasks)) { +- p = list_first_entry(tasks, struct task_struct, se.group_node); +- list_del_init(&p->se.group_node); ++ struct sched_entity *se = &prev->se; ++ struct cfs_rq *cfs_rq; + +- attach_task(env->dst_rq, p); ++ for_each_sched_entity(se) { ++ cfs_rq = cfs_rq_of(se); ++ put_prev_entity(cfs_rq, se); + } +- +- raw_spin_unlock(&env->dst_rq->lock); + } + +-#ifdef CONFIG_FAIR_GROUP_SCHED + /* +- * update tg->load_weight by folding this cpu's load_avg ++ * sched_yield() is very simple ++ * ++ * The magic of dealing with the ->skip buddy is in pick_next_entity. + */ +-static void __update_blocked_averages_cpu(struct task_group *tg, int cpu) ++static void yield_task_fair(struct rq *rq) + { +- struct sched_entity *se = tg->se[cpu]; +- struct cfs_rq *cfs_rq = tg->cfs_rq[cpu]; ++ struct task_struct *curr = rq->curr; ++ struct cfs_rq *cfs_rq = task_cfs_rq(curr); ++ struct sched_entity *se = &curr->se; + +- /* throttled entities do not contribute to load */ +- if (throttled_hierarchy(cfs_rq)) ++ /* ++ * Are we the only task in the tree? ++ */ ++ if (unlikely(rq->nr_running == 1)) + return; + +- update_cfs_rq_blocked_load(cfs_rq, 1); ++ clear_buddies(cfs_rq, se); + +- if (se) { +- update_entity_load_avg(se, 1); ++ if (curr->policy != SCHED_BATCH) { ++ update_rq_clock(rq); + /* +- * We pivot on our runnable average having decayed to zero for +- * list removal. This generally implies that all our children +- * have also been removed (modulo rounding error or bandwidth +- * control); however, such cases are rare and we can fix these +- * at enqueue. +- * +- * TODO: fix up out-of-order children on enqueue. ++ * Update run-time statistics of the 'current'. + */ +- if (!se->avg.runnable_avg_sum && !cfs_rq->nr_running) +- list_del_leaf_cfs_rq(cfs_rq); +- } else { +- struct rq *rq = rq_of(cfs_rq); +- update_rq_runnable_avg(rq, rq->nr_running); +- } +-} +- +-static void update_blocked_averages(int cpu) +-{ +- struct rq *rq = cpu_rq(cpu); +- struct cfs_rq *cfs_rq; +- unsigned long flags; +- +- raw_spin_lock_irqsave(&rq->lock, flags); +- update_rq_clock(rq); +- /* +- * Iterates the task_group tree in a bottom up fashion, see +- * list_add_leaf_cfs_rq() for details. +- */ +- for_each_leaf_cfs_rq(rq, cfs_rq) { ++ update_curr(cfs_rq); + /* +- * Note: We may want to consider periodically releasing +- * rq->lock about these updates so that creating many task +- * groups does not result in continually extending hold time. ++ * Tell update_rq_clock() that we've just updated, ++ * so we don't do microscopic update in schedule() ++ * and double the fastpath cost. + */ +- __update_blocked_averages_cpu(cfs_rq->tg, rq->cpu); +- } +- +- raw_spin_unlock_irqrestore(&rq->lock, flags); +-} +- +-/* +- * Compute the hierarchical load factor for cfs_rq and all its ascendants. +- * This needs to be done in a top-down fashion because the load of a child +- * group is a fraction of its parents load. +- */ +-static void update_cfs_rq_h_load(struct cfs_rq *cfs_rq) +-{ +- struct rq *rq = rq_of(cfs_rq); +- struct sched_entity *se = cfs_rq->tg->se[cpu_of(rq)]; +- unsigned long now = jiffies; +- unsigned long load; +- +- if (cfs_rq->last_h_load_update == now) +- return; +- +- cfs_rq->h_load_next = NULL; +- for_each_sched_entity(se) { +- cfs_rq = cfs_rq_of(se); +- cfs_rq->h_load_next = se; +- if (cfs_rq->last_h_load_update == now) +- break; +- } +- +- if (!se) { +- cfs_rq->h_load = cfs_rq->runnable_load_avg; +- cfs_rq->last_h_load_update = now; +- } +- +- while ((se = cfs_rq->h_load_next) != NULL) { +- load = cfs_rq->h_load; +- load = div64_ul(load * se->avg.load_avg_contrib, +- cfs_rq->runnable_load_avg + 1); +- cfs_rq = group_cfs_rq(se); +- cfs_rq->h_load = load; +- cfs_rq->last_h_load_update = now; ++ rq->skip_clock_update = 1; + } +-} +- +-static unsigned long task_h_load(struct task_struct *p) +-{ +- struct cfs_rq *cfs_rq = task_cfs_rq(p); + +- update_cfs_rq_h_load(cfs_rq); +- return div64_ul(p->se.avg.load_avg_contrib * cfs_rq->h_load, +- cfs_rq->runnable_load_avg + 1); +-} +-#else +-static inline void update_blocked_averages(int cpu) +-{ ++ set_skip_buddy(se); + } + +-static unsigned long task_h_load(struct task_struct *p) ++static bool yield_to_task_fair(struct rq *rq, struct task_struct *p, bool preempt) + { +- return p->se.avg.load_avg_contrib; +-} +-#endif +- +-/********** Helpers for find_busiest_group ************************/ +- +-enum group_type { +- group_other = 0, +- group_imbalanced, +- group_overloaded, +-}; ++ struct sched_entity *se = &p->se; + +-/* +- * sg_lb_stats - stats of a sched_group required for load_balancing +- */ +-struct sg_lb_stats { +- unsigned long avg_load; /*Avg load across the CPUs of the group */ +- unsigned long group_load; /* Total load over the CPUs of the group */ +- unsigned long sum_weighted_load; /* Weighted load of group's tasks */ +- unsigned long load_per_task; +- unsigned long group_capacity; +- unsigned int sum_nr_running; /* Nr tasks running in the group */ +- unsigned int group_capacity_factor; +- unsigned int idle_cpus; +- unsigned int group_weight; +- enum group_type group_type; +- int group_has_free_capacity; +-#ifdef CONFIG_NUMA_BALANCING +- unsigned int nr_numa_running; +- unsigned int nr_preferred_running; +-#endif +-}; ++ /* throttled hierarchies are not runnable */ ++ if (!se->on_rq || throttled_hierarchy(cfs_rq_of(se))) ++ return false; + +-/* +- * sd_lb_stats - Structure to store the statistics of a sched_domain +- * during load balancing. +- */ +-struct sd_lb_stats { +- struct sched_group *busiest; /* Busiest group in this sd */ +- struct sched_group *local; /* Local group in this sd */ +- unsigned long total_load; /* Total load of all groups in sd */ +- unsigned long total_capacity; /* Total capacity of all groups in sd */ +- unsigned long avg_load; /* Average load across all groups in sd */ ++ /* Tell the scheduler that we'd really like pse to run next. */ ++ set_next_buddy(se); + +- struct sg_lb_stats busiest_stat;/* Statistics of the busiest group */ +- struct sg_lb_stats local_stat; /* Statistics of the local group */ +-}; ++ yield_task_fair(rq); + +-static inline void init_sd_lb_stats(struct sd_lb_stats *sds) +-{ +- /* +- * Skimp on the clearing to avoid duplicate work. We can avoid clearing +- * local_stat because update_sg_lb_stats() does a full clear/assignment. +- * We must however clear busiest_stat::avg_load because +- * update_sd_pick_busiest() reads this before assignment. +- */ +- *sds = (struct sd_lb_stats){ +- .busiest = NULL, +- .local = NULL, +- .total_load = 0UL, +- .total_capacity = 0UL, +- .busiest_stat = { +- .avg_load = 0UL, +- .sum_nr_running = 0, +- .group_type = group_other, +- }, +- }; ++ return true; + } + +-/** +- * get_sd_load_idx - Obtain the load index for a given sched domain. +- * @sd: The sched_domain whose load_idx is to be obtained. +- * @idle: The idle status of the CPU for whose sd load_idx is obtained. ++#ifdef CONFIG_SMP ++/************************************************** ++ * Fair scheduling class load-balancing methods. + * +- * Return: The load index. +- */ +-static inline int get_sd_load_idx(struct sched_domain *sd, +- enum cpu_idle_type idle) +-{ +- int load_idx; ++ * BASICS ++ * ++ * The purpose of load-balancing is to achieve the same basic fairness the ++ * per-cpu scheduler provides, namely provide a proportional amount of compute ++ * time to each task. This is expressed in the following equation: ++ * ++ * W_i,n/P_i == W_j,n/P_j for all i,j (1) ++ * ++ * Where W_i,n is the n-th weight average for cpu i. The instantaneous weight ++ * W_i,0 is defined as: ++ * ++ * W_i,0 = \Sum_j w_i,j (2) ++ * ++ * Where w_i,j is the weight of the j-th runnable task on cpu i. This weight ++ * is derived from the nice value as per prio_to_weight[]. ++ * ++ * The weight average is an exponential decay average of the instantaneous ++ * weight: ++ * ++ * W'_i,n = (2^n - 1) / 2^n * W_i,n + 1 / 2^n * W_i,0 (3) ++ * ++ * C_i is the compute capacity of cpu i, typically it is the ++ * fraction of 'recent' time available for SCHED_OTHER task execution. But it ++ * can also include other factors [XXX]. ++ * ++ * To achieve this balance we define a measure of imbalance which follows ++ * directly from (1): ++ * ++ * imb_i,j = max{ avg(W/C), W_i/C_i } - min{ avg(W/C), W_j/C_j } (4) ++ * ++ * We them move tasks around to minimize the imbalance. In the continuous ++ * function space it is obvious this converges, in the discrete case we get ++ * a few fun cases generally called infeasible weight scenarios. ++ * ++ * [XXX expand on: ++ * - infeasible weights; ++ * - local vs global optima in the discrete case. ] ++ * ++ * ++ * SCHED DOMAINS ++ * ++ * In order to solve the imbalance equation (4), and avoid the obvious O(n^2) ++ * for all i,j solution, we create a tree of cpus that follows the hardware ++ * topology where each level pairs two lower groups (or better). This results ++ * in O(log n) layers. Furthermore we reduce the number of cpus going up the ++ * tree to only the first of the previous level and we decrease the frequency ++ * of load-balance at each level inv. proportional to the number of cpus in ++ * the groups. ++ * ++ * This yields: ++ * ++ * log_2 n 1 n ++ * \Sum { --- * --- * 2^i } = O(n) (5) ++ * i = 0 2^i 2^i ++ * `- size of each group ++ * | | `- number of cpus doing load-balance ++ * | `- freq ++ * `- sum over all levels ++ * ++ * Coupled with a limit on how many tasks we can migrate every balance pass, ++ * this makes (5) the runtime complexity of the balancer. ++ * ++ * An important property here is that each CPU is still (indirectly) connected ++ * to every other cpu in at most O(log n) steps: ++ * ++ * The adjacency matrix of the resulting graph is given by: ++ * ++ * log_2 n ++ * A_i,j = \Union (i % 2^k == 0) && i / 2^(k+1) == j / 2^(k+1) (6) ++ * k = 0 ++ * ++ * And you'll find that: ++ * ++ * A^(log_2 n)_i,j != 0 for all i,j (7) ++ * ++ * Showing there's indeed a path between every cpu in at most O(log n) steps. ++ * The task movement gives a factor of O(m), giving a convergence complexity ++ * of: ++ * ++ * O(nm log n), n := nr_cpus, m := nr_tasks (8) ++ * ++ * ++ * WORK CONSERVING ++ * ++ * In order to avoid CPUs going idle while there's still work to do, new idle ++ * balancing is more aggressive and has the newly idle cpu iterate up the domain ++ * tree itself instead of relying on other CPUs to bring it work. ++ * ++ * This adds some complexity to both (5) and (8) but it reduces the total idle ++ * time. ++ * ++ * [XXX more?] ++ * ++ * ++ * CGROUPS ++ * ++ * Cgroups make a horror show out of (2), instead of a simple sum we get: ++ * ++ * s_k,i ++ * W_i,0 = \Sum_j \Prod_k w_k * ----- (9) ++ * S_k ++ * ++ * Where ++ * ++ * s_k,i = \Sum_j w_i,j,k and S_k = \Sum_i s_k,i (10) ++ * ++ * w_i,j,k is the weight of the j-th runnable task in the k-th cgroup on cpu i. ++ * ++ * The big problem is S_k, its a global sum needed to compute a local (W_i) ++ * property. ++ * ++ * [XXX write more on how we solve this.. _after_ merging pjt's patches that ++ * rewrite all of this once again.] ++ */ + +- switch (idle) { +- case CPU_NOT_IDLE: +- load_idx = sd->busy_idx; +- break; ++static unsigned long __read_mostly max_load_balance_interval = HZ/10; + +- case CPU_NEWLY_IDLE: +- load_idx = sd->newidle_idx; +- break; +- default: +- load_idx = sd->idle_idx; +- break; +- } ++enum fbq_type { regular, remote, all }; + +- return load_idx; +-} ++#define LBF_ALL_PINNED 0x01 ++#define LBF_NEED_BREAK 0x02 ++#define LBF_DST_PINNED 0x04 ++#define LBF_SOME_PINNED 0x08 + +-static unsigned long default_scale_capacity(struct sched_domain *sd, int cpu) +-{ +- return SCHED_CAPACITY_SCALE; +-} ++struct lb_env { ++ struct sched_domain *sd; + +-unsigned long __weak arch_scale_freq_capacity(struct sched_domain *sd, int cpu) +-{ +- return default_scale_capacity(sd, cpu); +-} ++ struct rq *src_rq; ++ int src_cpu; + +-static unsigned long default_scale_cpu_capacity(struct sched_domain *sd, int cpu) +-{ +- if ((sd->flags & SD_SHARE_CPUCAPACITY) && (sd->span_weight > 1)) +- return sd->smt_gain / sd->span_weight; ++ int dst_cpu; ++ struct rq *dst_rq; + +- return SCHED_CAPACITY_SCALE; +-} ++ struct cpumask *dst_grpmask; ++ int new_dst_cpu; ++ enum cpu_idle_type idle; ++ long imbalance; ++ /* The set of CPUs under consideration for load-balancing */ ++ struct cpumask *cpus; + +-unsigned long __weak arch_scale_cpu_capacity(struct sched_domain *sd, int cpu) +-{ +- return default_scale_cpu_capacity(sd, cpu); +-} ++ unsigned int flags; + +-static unsigned long scale_rt_capacity(int cpu) ++ unsigned int loop; ++ unsigned int loop_break; ++ unsigned int loop_max; ++ ++ enum fbq_type fbq_type; ++ struct list_head tasks; ++}; ++ ++/* ++ * Is this task likely cache-hot: ++ */ ++static int task_hot(struct task_struct *p, struct lb_env *env) + { +- struct rq *rq = cpu_rq(cpu); +- u64 total, available, age_stamp, avg; + s64 delta; + +- /* +- * Since we're reading these variables without serialization make sure +- * we read them once before doing sanity checks on them. +- */ +- age_stamp = ACCESS_ONCE(rq->age_stamp); +- avg = ACCESS_ONCE(rq->rt_avg); ++ lockdep_assert_held(&env->src_rq->lock); + +- delta = rq_clock(rq) - age_stamp; +- if (unlikely(delta < 0)) +- delta = 0; ++ if (p->sched_class != &fair_sched_class) ++ return 0; + +- total = sched_avg_period() + delta; ++ if (unlikely(p->policy == SCHED_IDLE)) ++ return 0; + +- if (unlikely(total < avg)) { +- /* Ensures that capacity won't end up being negative */ +- available = 0; +- } else { +- available = total - avg; +- } ++ /* ++ * Buddy candidates are cache hot: ++ */ ++ if (sched_feat(CACHE_HOT_BUDDY) && env->dst_rq->nr_running && ++ (&p->se == cfs_rq_of(&p->se)->next || ++ &p->se == cfs_rq_of(&p->se)->last)) ++ return 1; + +- if (unlikely((s64)total < SCHED_CAPACITY_SCALE)) +- total = SCHED_CAPACITY_SCALE; ++ if (sysctl_sched_migration_cost == -1) ++ return 1; ++ if (sysctl_sched_migration_cost == 0) ++ return 0; + +- total >>= SCHED_CAPACITY_SHIFT; ++ delta = rq_clock_task(env->src_rq) - p->se.exec_start; + +- return div_u64(available, total); ++ return delta < (s64)sysctl_sched_migration_cost; + } + +-static void update_cpu_capacity(struct sched_domain *sd, int cpu) ++#ifdef CONFIG_NUMA_BALANCING ++/* Returns true if the destination node has incurred more faults */ ++static bool migrate_improves_locality(struct task_struct *p, struct lb_env *env) + { +- unsigned long capacity = SCHED_CAPACITY_SCALE; +- struct sched_group *sdg = sd->groups; +- +- if (sched_feat(ARCH_CAPACITY)) +- capacity *= arch_scale_cpu_capacity(sd, cpu); +- else +- capacity *= default_scale_cpu_capacity(sd, cpu); ++ struct numa_group *numa_group = rcu_dereference(p->numa_group); ++ int src_nid, dst_nid; + +- capacity >>= SCHED_CAPACITY_SHIFT; ++ if (!sched_feat(NUMA_FAVOUR_HIGHER) || !p->numa_faults_memory || ++ !(env->sd->flags & SD_NUMA)) { ++ return false; ++ } + +- sdg->sgc->capacity_orig = capacity; ++ src_nid = cpu_to_node(env->src_cpu); ++ dst_nid = cpu_to_node(env->dst_cpu); + +- if (sched_feat(ARCH_CAPACITY)) +- capacity *= arch_scale_freq_capacity(sd, cpu); +- else +- capacity *= default_scale_capacity(sd, cpu); ++ if (src_nid == dst_nid) ++ return false; + +- capacity >>= SCHED_CAPACITY_SHIFT; ++ if (numa_group) { ++ /* Task is already in the group's interleave set. */ ++ if (node_isset(src_nid, numa_group->active_nodes)) ++ return false; + +- capacity *= scale_rt_capacity(cpu); +- capacity >>= SCHED_CAPACITY_SHIFT; ++ /* Task is moving into the group's interleave set. */ ++ if (node_isset(dst_nid, numa_group->active_nodes)) ++ return true; + +- if (!capacity) +- capacity = 1; ++ return group_faults(p, dst_nid) > group_faults(p, src_nid); ++ } + +- cpu_rq(cpu)->cpu_capacity = capacity; +- sdg->sgc->capacity = capacity; ++ /* Encourage migration to the preferred node. */ ++ if (dst_nid == p->numa_preferred_nid) ++ return true; ++ ++ return task_faults(p, dst_nid) > task_faults(p, src_nid); + } + +-void update_group_capacity(struct sched_domain *sd, int cpu) +-{ +- struct sched_domain *child = sd->child; +- struct sched_group *group, *sdg = sd->groups; +- unsigned long capacity, capacity_orig; +- unsigned long interval; + +- interval = msecs_to_jiffies(sd->balance_interval); +- interval = clamp(interval, 1UL, max_load_balance_interval); +- sdg->sgc->next_update = jiffies + interval; ++static bool migrate_degrades_locality(struct task_struct *p, struct lb_env *env) ++{ ++ struct numa_group *numa_group = rcu_dereference(p->numa_group); ++ int src_nid, dst_nid; + +- if (!child) { +- update_cpu_capacity(sd, cpu); +- return; +- } ++ if (!sched_feat(NUMA) || !sched_feat(NUMA_RESIST_LOWER)) ++ return false; + +- capacity_orig = capacity = 0; ++ if (!p->numa_faults_memory || !(env->sd->flags & SD_NUMA)) ++ return false; + +- if (child->flags & SD_OVERLAP) { +- /* +- * SD_OVERLAP domains cannot assume that child groups +- * span the current group. +- */ ++ src_nid = cpu_to_node(env->src_cpu); ++ dst_nid = cpu_to_node(env->dst_cpu); + +- for_each_cpu(cpu, sched_group_cpus(sdg)) { +- struct sched_group_capacity *sgc; +- struct rq *rq = cpu_rq(cpu); ++ if (src_nid == dst_nid) ++ return false; + +- /* +- * build_sched_domains() -> init_sched_groups_capacity() +- * gets here before we've attached the domains to the +- * runqueues. +- * +- * Use capacity_of(), which is set irrespective of domains +- * in update_cpu_capacity(). +- * +- * This avoids capacity/capacity_orig from being 0 and +- * causing divide-by-zero issues on boot. +- * +- * Runtime updates will correct capacity_orig. +- */ +- if (unlikely(!rq->sd)) { +- capacity_orig += capacity_of(cpu); +- capacity += capacity_of(cpu); +- continue; +- } ++ if (numa_group) { ++ /* Task is moving within/into the group's interleave set. */ ++ if (node_isset(dst_nid, numa_group->active_nodes)) ++ return false; + +- sgc = rq->sd->groups->sgc; +- capacity_orig += sgc->capacity_orig; +- capacity += sgc->capacity; +- } +- } else { +- /* +- * !SD_OVERLAP domains can assume that child groups +- * span the current group. +- */ ++ /* Task is moving out of the group's interleave set. */ ++ if (node_isset(src_nid, numa_group->active_nodes)) ++ return true; + +- group = child->groups; +- do { +- capacity_orig += group->sgc->capacity_orig; +- capacity += group->sgc->capacity; +- group = group->next; +- } while (group != child->groups); ++ return group_faults(p, dst_nid) < group_faults(p, src_nid); + } + +- sdg->sgc->capacity_orig = capacity_orig; +- sdg->sgc->capacity = capacity; ++ /* Migrating away from the preferred node is always bad. */ ++ if (src_nid == p->numa_preferred_nid) ++ return true; ++ ++ return task_faults(p, dst_nid) < task_faults(p, src_nid); ++} ++ ++#else ++static inline bool migrate_improves_locality(struct task_struct *p, ++ struct lb_env *env) ++{ ++ return false; + } + ++static inline bool migrate_degrades_locality(struct task_struct *p, ++ struct lb_env *env) ++{ ++ return false; ++} ++#endif ++ + /* +- * Try and fix up capacity for tiny siblings, this is needed when +- * things like SD_ASYM_PACKING need f_b_g to select another sibling +- * which on its own isn't powerful enough. +- * +- * See update_sd_pick_busiest() and check_asym_packing(). ++ * can_migrate_task - may task p from runqueue rq be migrated to this_cpu? + */ +-static inline int +-fix_small_capacity(struct sched_domain *sd, struct sched_group *group) ++static ++int can_migrate_task(struct task_struct *p, struct lb_env *env) + { ++ int tsk_cache_hot = 0; ++ ++ lockdep_assert_held(&env->src_rq->lock); ++ + /* +- * Only siblings can have significantly less than SCHED_CAPACITY_SCALE ++ * We do not migrate tasks that are: ++ * 1) throttled_lb_pair, or ++ * 2) cannot be migrated to this CPU due to cpus_allowed, or ++ * 3) running (obviously), or ++ * 4) are cache-hot on their current CPU. + */ +- if (!(sd->flags & SD_SHARE_CPUCAPACITY)) ++ if (throttled_lb_pair(task_group(p), env->src_cpu, env->dst_cpu)) ++ return 0; ++ ++ if (!cpumask_test_cpu(env->dst_cpu, tsk_cpus_allowed(p))) { ++ int cpu; ++ ++ schedstat_inc(p, se.statistics.nr_failed_migrations_affine); ++ ++ env->flags |= LBF_SOME_PINNED; ++ ++ /* ++ * Remember if this task can be migrated to any other cpu in ++ * our sched_group. We may want to revisit it if we couldn't ++ * meet load balance goals by pulling other tasks on src_cpu. ++ * ++ * Also avoid computing new_dst_cpu if we have already computed ++ * one in current iteration. ++ */ ++ if (!env->dst_grpmask || (env->flags & LBF_DST_PINNED)) ++ return 0; ++ ++ /* Prevent to re-select dst_cpu via env's cpus */ ++ for_each_cpu_and(cpu, env->dst_grpmask, env->cpus) { ++ if (cpumask_test_cpu(cpu, tsk_cpus_allowed(p))) { ++ env->flags |= LBF_DST_PINNED; ++ env->new_dst_cpu = cpu; ++ break; ++ } ++ } ++ ++ return 0; ++ } ++ ++ /* Record that we found atleast one task that could run on dst_cpu */ ++ env->flags &= ~LBF_ALL_PINNED; ++ ++ if (task_running(env->src_rq, p)) { ++ schedstat_inc(p, se.statistics.nr_failed_migrations_running); + return 0; ++ } + + /* +- * If ~90% of the cpu_capacity is still there, we're good. ++ * Aggressive migration if: ++ * 1) destination numa is preferred ++ * 2) task is cache cold, or ++ * 3) too many balance attempts have failed. + */ +- if (group->sgc->capacity * 32 > group->sgc->capacity_orig * 29) ++ tsk_cache_hot = task_hot(p, env); ++ if (!tsk_cache_hot) ++ tsk_cache_hot = migrate_degrades_locality(p, env); ++ ++ if (migrate_improves_locality(p, env) || !tsk_cache_hot || ++ env->sd->nr_balance_failed > env->sd->cache_nice_tries) { ++ if (tsk_cache_hot) { ++ schedstat_inc(env->sd, lb_hot_gained[env->idle]); ++ schedstat_inc(p, se.statistics.nr_forced_migrations); ++ } + return 1; ++ } + ++ schedstat_inc(p, se.statistics.nr_failed_migrations_hot); + return 0; + } +- ++#ifdef CONFIG_SCHED_HMP ++static unsigned int hmp_idle_pull(int this_cpu); ++#endif + /* +- * Group imbalance indicates (and tries to solve) the problem where balancing +- * groups is inadequate due to tsk_cpus_allowed() constraints. +- * +- * Imagine a situation of two groups of 4 cpus each and 4 tasks each with a +- * cpumask covering 1 cpu of the first group and 3 cpus of the second group. +- * Something like: +- * +- * { 0 1 2 3 } { 4 5 6 7 } +- * * * * * +- * +- * If we were to balance group-wise we'd place two tasks in the first group and +- * two tasks in the second group. Clearly this is undesired as it will overload +- * cpu 3 and leave one of the cpus in the second group unused. +- * +- * The current solution to this issue is detecting the skew in the first group +- * by noticing the lower domain failed to reach balance and had difficulty +- * moving tasks due to affinity constraints. +- * +- * When this is so detected; this group becomes a candidate for busiest; see +- * update_sd_pick_busiest(). And calculate_imbalance() and +- * find_busiest_group() avoid some of the usual balance conditions to allow it +- * to create an effective group imbalance. +- * +- * This is a somewhat tricky proposition since the next run might not find the +- * group imbalance and decide the groups need to be balanced again. A most +- * subtle and fragile situation. ++ * detach_task() -- detach the task for the migration specified in env + */ +- +-static inline int sg_imbalanced(struct sched_group *group) ++static void detach_task(struct task_struct *p, struct lb_env *env) + { +- return group->sgc->imbalance; ++ lockdep_assert_held(&env->src_rq->lock); ++ ++ deactivate_task(env->src_rq, p, 0); ++ p->on_rq = TASK_ON_RQ_MIGRATING; ++ set_task_cpu(p, env->dst_cpu); + } + + /* +- * Compute the group capacity factor. ++ * detach_one_task() -- tries to dequeue exactly one task from env->src_rq, as ++ * part of active balancing operations within "domain". + * +- * Avoid the issue where N*frac(smt_capacity) >= 1 creates 'phantom' cores by +- * first dividing out the smt factor and computing the actual number of cores +- * and limit unit capacity with that. ++ * Returns a task if successful and NULL otherwise. + */ +-static inline int sg_capacity_factor(struct lb_env *env, struct sched_group *group) ++static struct task_struct *detach_one_task(struct lb_env *env) + { +- unsigned int capacity_factor, smt, cpus; +- unsigned int capacity, capacity_orig; ++ struct task_struct *p, *n; + +- capacity = group->sgc->capacity; +- capacity_orig = group->sgc->capacity_orig; +- cpus = group->group_weight; ++ lockdep_assert_held(&env->src_rq->lock); + +- /* smt := ceil(cpus / capacity), assumes: 1 < smt_capacity < 2 */ +- smt = DIV_ROUND_UP(SCHED_CAPACITY_SCALE * cpus, capacity_orig); +- capacity_factor = cpus / smt; /* cores */ ++ list_for_each_entry_safe(p, n, &env->src_rq->cfs_tasks, se.group_node) { ++ if (!can_migrate_task(p, env)) ++ continue; + +- capacity_factor = min_t(unsigned, +- capacity_factor, DIV_ROUND_CLOSEST(capacity, SCHED_CAPACITY_SCALE)); +- if (!capacity_factor) +- capacity_factor = fix_small_capacity(env->sd, group); ++ detach_task(p, env); + +- return capacity_factor; +-} +- +-static enum group_type +-group_classify(struct sched_group *group, struct sg_lb_stats *sgs) +-{ +- if (sgs->sum_nr_running > sgs->group_capacity_factor) +- return group_overloaded; +- +- if (sg_imbalanced(group)) +- return group_imbalanced; +- +- return group_other; ++ /* ++ * Right now, this is only the second place where ++ * lb_gained[env->idle] is updated (other is detach_tasks) ++ * so we can safely collect stats here rather than ++ * inside detach_tasks(). ++ */ ++ schedstat_inc(env->sd, lb_gained[env->idle]); ++ return p; ++ } ++ return NULL; + } + +-/** +- * update_sg_lb_stats - Update sched_group's statistics for load balancing. +- * @env: The load balancing environment. +- * @group: sched_group whose statistics are to be updated. +- * @load_idx: Load index of sched_domain of this_cpu for load calc. +- * @local_group: Does group contain this_cpu. +- * @sgs: variable to hold the statistics for this group. +- * @overload: Indicate more than one runnable task for any CPU. ++static const unsigned int sched_nr_migrate_break = 32; ++ ++/* ++ * detach_tasks() -- tries to detach up to imbalance weighted load from ++ * busiest_rq, as part of a balancing operation within domain "sd". ++ * ++ * Returns number of detached tasks if successful and 0 otherwise. + */ +-static inline void update_sg_lb_stats(struct lb_env *env, +- struct sched_group *group, int load_idx, +- int local_group, struct sg_lb_stats *sgs, +- bool *overload) ++static int detach_tasks(struct lb_env *env) + { ++ struct list_head *tasks = &env->src_rq->cfs_tasks; ++ struct task_struct *p; + unsigned long load; +- int i; +- +- memset(sgs, 0, sizeof(*sgs)); ++ int detached = 0; + +- for_each_cpu_and(i, sched_group_cpus(group), env->cpus) { +- struct rq *rq = cpu_rq(i); ++ lockdep_assert_held(&env->src_rq->lock); + +- /* Bias balancing toward cpus of our domain */ +- if (local_group) +- load = target_load(i, load_idx); +- else +- load = source_load(i, load_idx); ++ if (env->imbalance <= 0) ++ return 0; + +- sgs->group_load += load; +- sgs->sum_nr_running += rq->cfs.h_nr_running; ++ while (!list_empty(tasks)) { ++ p = list_first_entry(tasks, struct task_struct, se.group_node); + +- if (rq->nr_running > 1) +- *overload = true; ++ env->loop++; ++ /* We've more or less seen every task there is, call it quits */ ++ if (env->loop > env->loop_max) ++ break; + +-#ifdef CONFIG_NUMA_BALANCING +- sgs->nr_numa_running += rq->nr_numa_running; +- sgs->nr_preferred_running += rq->nr_preferred_running; +-#endif +- sgs->sum_weighted_load += weighted_cpuload(i); +- if (idle_cpu(i)) +- sgs->idle_cpus++; +- } ++ /* take a breather every nr_migrate tasks */ ++ if (env->loop > env->loop_break) { ++ env->loop_break += sched_nr_migrate_break; ++ env->flags |= LBF_NEED_BREAK; ++ break; ++ } + +- /* Adjust by relative CPU capacity of the group */ +- sgs->group_capacity = group->sgc->capacity; +- sgs->avg_load = (sgs->group_load*SCHED_CAPACITY_SCALE) / sgs->group_capacity; ++ if (!can_migrate_task(p, env)) ++ goto next; + +- if (sgs->sum_nr_running) +- sgs->load_per_task = sgs->sum_weighted_load / sgs->sum_nr_running; ++ load = task_h_load(p); + +- sgs->group_weight = group->group_weight; +- sgs->group_capacity_factor = sg_capacity_factor(env, group); +- sgs->group_type = group_classify(group, sgs); ++ if (sched_feat(LB_MIN) && load < 16 && !env->sd->nr_balance_failed) ++ goto next; + +- if (sgs->group_capacity_factor > sgs->sum_nr_running) +- sgs->group_has_free_capacity = 1; +-} ++ if ((load / 2) > env->imbalance) ++ goto next; + +-/** +- * update_sd_pick_busiest - return 1 on busiest group +- * @env: The load balancing environment. +- * @sds: sched_domain statistics +- * @sg: sched_group candidate to be checked for being the busiest +- * @sgs: sched_group statistics +- * +- * Determine if @sg is a busier group than the previously selected +- * busiest group. +- * +- * Return: %true if @sg is a busier group than the previously selected +- * busiest group. %false otherwise. +- */ +-static bool update_sd_pick_busiest(struct lb_env *env, +- struct sd_lb_stats *sds, +- struct sched_group *sg, +- struct sg_lb_stats *sgs) +-{ +- struct sg_lb_stats *busiest = &sds->busiest_stat; ++ detach_task(p, env); ++ list_add(&p->se.group_node, &env->tasks); + +- if (sgs->group_type > busiest->group_type) +- return true; ++ detached++; ++ env->imbalance -= load; + +- if (sgs->group_type < busiest->group_type) +- return false; ++#ifdef CONFIG_PREEMPT ++ /* ++ * NEWIDLE balancing is a source of latency, so preemptible ++ * kernels will stop after the first task is detached to minimize ++ * the critical section. ++ */ ++ if (env->idle == CPU_NEWLY_IDLE) ++ break; ++#endif + +- if (sgs->avg_load <= busiest->avg_load) +- return false; ++ /* ++ * We only want to steal up to the prescribed amount of ++ * weighted load. ++ */ ++ if (env->imbalance <= 0) ++ break; + +- /* This is the busiest node in its class. */ +- if (!(env->sd->flags & SD_ASYM_PACKING)) +- return true; ++ continue; ++next: ++ list_move_tail(&p->se.group_node, tasks); ++ } + + /* +- * ASYM_PACKING needs to move all the work to the lowest +- * numbered CPUs in the group, therefore mark all groups +- * higher than ourself as busy. ++ * Right now, this is one of only two places we collect this stat ++ * so we can safely collect detach_one_task() stats here rather ++ * than inside detach_one_task(). + */ +- if (sgs->sum_nr_running && env->dst_cpu < group_first_cpu(sg)) { +- if (!sds->busiest) +- return true; +- +- if (group_first_cpu(sds->busiest) > group_first_cpu(sg)) +- return true; +- } ++ schedstat_add(env->sd, lb_gained[env->idle], detached); + +- return false; ++ return detached; + } + +-#ifdef CONFIG_NUMA_BALANCING +-static inline enum fbq_type fbq_classify_group(struct sg_lb_stats *sgs) ++/* ++ * attach_task() -- attach the task detached by detach_task() to its new rq. ++ */ ++static void attach_task(struct rq *rq, struct task_struct *p) + { +- if (sgs->sum_nr_running > sgs->nr_numa_running) +- return regular; +- if (sgs->sum_nr_running > sgs->nr_preferred_running) +- return remote; +- return all; +-} ++ lockdep_assert_held(&rq->lock); + +-static inline enum fbq_type fbq_classify_rq(struct rq *rq) +-{ +- if (rq->nr_running > rq->nr_numa_running) +- return regular; +- if (rq->nr_running > rq->nr_preferred_running) +- return remote; +- return all; ++ BUG_ON(task_rq(p) != rq); ++ p->on_rq = TASK_ON_RQ_QUEUED; ++ activate_task(rq, p, 0); ++ check_preempt_curr(rq, p, 0); + } +-#else +-static inline enum fbq_type fbq_classify_group(struct sg_lb_stats *sgs) ++ ++/* ++ * attach_one_task() -- attaches the task returned from detach_one_task() to ++ * its new rq. ++ */ ++static void attach_one_task(struct rq *rq, struct task_struct *p) + { +- return all; ++ raw_spin_lock(&rq->lock); ++ attach_task(rq, p); ++ raw_spin_unlock(&rq->lock); + } + +-static inline enum fbq_type fbq_classify_rq(struct rq *rq) ++/* ++ * attach_tasks() -- attaches all tasks detached by detach_tasks() to their ++ * new rq. ++ */ ++static void attach_tasks(struct lb_env *env) + { +- return regular; ++ struct list_head *tasks = &env->tasks; ++ struct task_struct *p; ++ ++ raw_spin_lock(&env->dst_rq->lock); ++ ++ while (!list_empty(tasks)) { ++ p = list_first_entry(tasks, struct task_struct, se.group_node); ++ list_del_init(&p->se.group_node); ++ ++ attach_task(env->dst_rq, p); ++ } ++ ++ raw_spin_unlock(&env->dst_rq->lock); + } +-#endif /* CONFIG_NUMA_BALANCING */ + +-/** +- * update_sd_lb_stats - Update sched_domain's statistics for load balancing. +- * @env: The load balancing environment. +- * @sds: variable to hold the statistics for this sched_domain. ++#ifdef CONFIG_FAIR_GROUP_SCHED ++/* ++ * update tg->load_weight by folding this cpu's load_avg + */ +-static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sds) ++static void __update_blocked_averages_cpu(struct task_group *tg, int cpu) + { +- struct sched_domain *child = env->sd->child; +- struct sched_group *sg = env->sd->groups; +- struct sg_lb_stats tmp_sgs; +- int load_idx, prefer_sibling = 0; +- bool overload = false; ++ struct sched_entity *se = tg->se[cpu]; ++ struct cfs_rq *cfs_rq = tg->cfs_rq[cpu]; ++ ++ /* throttled entities do not contribute to load */ ++ if (throttled_hierarchy(cfs_rq)) ++ return; ++ ++ update_cfs_rq_blocked_load(cfs_rq, 1); ++ ++ if (se) { ++ update_entity_load_avg(se, 1); ++ /* ++ * We pivot on our runnable average having decayed to zero for ++ * list removal. This generally implies that all our children ++ * have also been removed (modulo rounding error or bandwidth ++ * control); however, such cases are rare and we can fix these ++ * at enqueue. ++ * ++ * TODO: fix up out-of-order children on enqueue. ++ */ ++ if (!se->avg.runnable_avg_sum && !cfs_rq->nr_running) ++ list_del_leaf_cfs_rq(cfs_rq); ++ } else { ++ struct rq *rq = rq_of(cfs_rq); ++ update_rq_runnable_avg(rq, rq->nr_running); ++ } ++} ++ ++static void update_blocked_averages(int cpu) ++{ ++ struct rq *rq = cpu_rq(cpu); ++ struct cfs_rq *cfs_rq; ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&rq->lock, flags); ++ update_rq_clock(rq); ++ /* ++ * Iterates the task_group tree in a bottom up fashion, see ++ * list_add_leaf_cfs_rq() for details. ++ */ ++ for_each_leaf_cfs_rq(rq, cfs_rq) { ++ /* ++ * Note: We may want to consider periodically releasing ++ * rq->lock about these updates so that creating many task ++ * groups does not result in continually extending hold time. ++ */ ++ __update_blocked_averages_cpu(cfs_rq->tg, rq->cpu); ++ } ++ ++ raw_spin_unlock_irqrestore(&rq->lock, flags); ++} ++ ++/* ++ * Compute the hierarchical load factor for cfs_rq and all its ascendants. ++ * This needs to be done in a top-down fashion because the load of a child ++ * group is a fraction of its parents load. ++ */ ++static void update_cfs_rq_h_load(struct cfs_rq *cfs_rq) ++{ ++ struct rq *rq = rq_of(cfs_rq); ++ struct sched_entity *se = cfs_rq->tg->se[cpu_of(rq)]; ++ unsigned long now = jiffies; ++ unsigned long load; ++ ++ if (cfs_rq->last_h_load_update == now) ++ return; ++ ++ cfs_rq->h_load_next = NULL; ++ for_each_sched_entity(se) { ++ cfs_rq = cfs_rq_of(se); ++ cfs_rq->h_load_next = se; ++ if (cfs_rq->last_h_load_update == now) ++ break; ++ } ++ ++ if (!se) { ++ cfs_rq->h_load = cfs_rq->runnable_load_avg; ++ cfs_rq->last_h_load_update = now; ++ } ++ ++ while ((se = cfs_rq->h_load_next) != NULL) { ++ load = cfs_rq->h_load; ++ load = div64_ul(load * se->avg.load_avg_contrib, ++ cfs_rq->runnable_load_avg + 1); ++ cfs_rq = group_cfs_rq(se); ++ cfs_rq->h_load = load; ++ cfs_rq->last_h_load_update = now; ++ } ++} ++ ++static unsigned long task_h_load(struct task_struct *p) ++{ ++ struct cfs_rq *cfs_rq = task_cfs_rq(p); ++ ++ update_cfs_rq_h_load(cfs_rq); ++ return div64_ul(p->se.avg.load_avg_contrib * cfs_rq->h_load, ++ cfs_rq->runnable_load_avg + 1); ++} ++#else ++static inline void update_blocked_averages(int cpu) ++{ ++} ++ ++static unsigned long task_h_load(struct task_struct *p) ++{ ++ return p->se.avg.load_avg_contrib; ++} ++#endif ++ ++/********** Helpers for find_busiest_group ************************/ ++ ++enum group_type { ++ group_other = 0, ++ group_imbalanced, ++ group_overloaded, ++}; ++ ++/* ++ * sg_lb_stats - stats of a sched_group required for load_balancing ++ */ ++struct sg_lb_stats { ++ unsigned long avg_load; /*Avg load across the CPUs of the group */ ++ unsigned long group_load; /* Total load over the CPUs of the group */ ++ unsigned long sum_weighted_load; /* Weighted load of group's tasks */ ++ unsigned long load_per_task; ++ unsigned long group_capacity; ++ unsigned int sum_nr_running; /* Nr tasks running in the group */ ++ unsigned int group_capacity_factor; ++ unsigned int idle_cpus; ++ unsigned int group_weight; ++ enum group_type group_type; ++ int group_has_free_capacity; ++#ifdef CONFIG_NUMA_BALANCING ++ unsigned int nr_numa_running; ++ unsigned int nr_preferred_running; ++#endif ++}; ++ ++/* ++ * sd_lb_stats - Structure to store the statistics of a sched_domain ++ * during load balancing. ++ */ ++struct sd_lb_stats { ++ struct sched_group *busiest; /* Busiest group in this sd */ ++ struct sched_group *local; /* Local group in this sd */ ++ unsigned long total_load; /* Total load of all groups in sd */ ++ unsigned long total_capacity; /* Total capacity of all groups in sd */ ++ unsigned long avg_load; /* Average load across all groups in sd */ ++ ++ struct sg_lb_stats busiest_stat;/* Statistics of the busiest group */ ++ struct sg_lb_stats local_stat; /* Statistics of the local group */ ++}; ++ ++static inline void init_sd_lb_stats(struct sd_lb_stats *sds) ++{ ++ /* ++ * Skimp on the clearing to avoid duplicate work. We can avoid clearing ++ * local_stat because update_sg_lb_stats() does a full clear/assignment. ++ * We must however clear busiest_stat::avg_load because ++ * update_sd_pick_busiest() reads this before assignment. ++ */ ++ *sds = (struct sd_lb_stats){ ++ .busiest = NULL, ++ .local = NULL, ++ .total_load = 0UL, ++ .total_capacity = 0UL, ++ .busiest_stat = { ++ .avg_load = 0UL, ++ .sum_nr_running = 0, ++ .group_type = group_other, ++ }, ++ }; ++} ++ ++/** ++ * get_sd_load_idx - Obtain the load index for a given sched domain. ++ * @sd: The sched_domain whose load_idx is to be obtained. ++ * @idle: The idle status of the CPU for whose sd load_idx is obtained. ++ * ++ * Return: The load index. ++ */ ++static inline int get_sd_load_idx(struct sched_domain *sd, ++ enum cpu_idle_type idle) ++{ ++ int load_idx; ++ ++ switch (idle) { ++ case CPU_NOT_IDLE: ++ load_idx = sd->busy_idx; ++ break; ++ ++ case CPU_NEWLY_IDLE: ++ load_idx = sd->newidle_idx; ++ break; ++ default: ++ load_idx = sd->idle_idx; ++ break; ++ } ++ ++ return load_idx; ++} ++ ++static unsigned long default_scale_capacity(struct sched_domain *sd, int cpu) ++{ ++ return SCHED_CAPACITY_SCALE; ++} ++ ++unsigned long __weak arch_scale_freq_capacity(struct sched_domain *sd, int cpu) ++{ ++ return default_scale_capacity(sd, cpu); ++} ++ ++static unsigned long default_scale_cpu_capacity(struct sched_domain *sd, int cpu) ++{ ++ if ((sd->flags & SD_SHARE_CPUCAPACITY) && (sd->span_weight > 1)) ++ return sd->smt_gain / sd->span_weight; ++ ++ return SCHED_CAPACITY_SCALE; ++} ++ ++unsigned long __weak arch_scale_cpu_capacity(struct sched_domain *sd, int cpu) ++{ ++ return default_scale_cpu_capacity(sd, cpu); ++} ++ ++static unsigned long scale_rt_capacity(int cpu) ++{ ++ struct rq *rq = cpu_rq(cpu); ++ u64 total, available, age_stamp, avg; ++ s64 delta; ++ ++ /* ++ * Since we're reading these variables without serialization make sure ++ * we read them once before doing sanity checks on them. ++ */ ++ age_stamp = ACCESS_ONCE(rq->age_stamp); ++ avg = ACCESS_ONCE(rq->rt_avg); ++ ++ delta = rq_clock(rq) - age_stamp; ++ if (unlikely(delta < 0)) ++ delta = 0; ++ ++ total = sched_avg_period() + delta; ++ ++ if (unlikely(total < avg)) { ++ /* Ensures that capacity won't end up being negative */ ++ available = 0; ++ } else { ++ available = total - avg; ++ } ++ ++ if (unlikely((s64)total < SCHED_CAPACITY_SCALE)) ++ total = SCHED_CAPACITY_SCALE; ++ ++ total >>= SCHED_CAPACITY_SHIFT; ++ ++ return div_u64(available, total); ++} ++ ++static void update_cpu_capacity(struct sched_domain *sd, int cpu) ++{ ++ unsigned long capacity = SCHED_CAPACITY_SCALE; ++ struct sched_group *sdg = sd->groups; ++ ++ if (sched_feat(ARCH_CAPACITY)) ++ capacity *= arch_scale_cpu_capacity(sd, cpu); ++ else ++ capacity *= default_scale_cpu_capacity(sd, cpu); ++ ++ capacity >>= SCHED_CAPACITY_SHIFT; ++ ++ sdg->sgc->capacity_orig = capacity; ++ ++ if (sched_feat(ARCH_CAPACITY)) ++ capacity *= arch_scale_freq_capacity(sd, cpu); ++ else ++ capacity *= default_scale_capacity(sd, cpu); ++ ++ capacity >>= SCHED_CAPACITY_SHIFT; ++ ++ capacity *= scale_rt_capacity(cpu); ++ capacity >>= SCHED_CAPACITY_SHIFT; ++ ++ if (!capacity) ++ capacity = 1; ++ ++ cpu_rq(cpu)->cpu_capacity = capacity; ++ sdg->sgc->capacity = capacity; ++} ++ ++void update_group_capacity(struct sched_domain *sd, int cpu) ++{ ++ struct sched_domain *child = sd->child; ++ struct sched_group *group, *sdg = sd->groups; ++ unsigned long capacity, capacity_orig; ++ unsigned long interval; ++ ++ interval = msecs_to_jiffies(sd->balance_interval); ++ interval = clamp(interval, 1UL, max_load_balance_interval); ++ sdg->sgc->next_update = jiffies + interval; ++ ++ if (!child) { ++ update_cpu_capacity(sd, cpu); ++ return; ++ } ++ ++ capacity_orig = capacity = 0; ++ ++ if (child->flags & SD_OVERLAP) { ++ /* ++ * SD_OVERLAP domains cannot assume that child groups ++ * span the current group. ++ */ ++ ++ for_each_cpu(cpu, sched_group_cpus(sdg)) { ++ struct sched_group_capacity *sgc; ++ struct rq *rq = cpu_rq(cpu); ++ ++ /* ++ * build_sched_domains() -> init_sched_groups_capacity() ++ * gets here before we've attached the domains to the ++ * runqueues. ++ * ++ * Use capacity_of(), which is set irrespective of domains ++ * in update_cpu_capacity(). ++ * ++ * This avoids capacity/capacity_orig from being 0 and ++ * causing divide-by-zero issues on boot. ++ * ++ * Runtime updates will correct capacity_orig. ++ */ ++ if (unlikely(!rq->sd)) { ++ capacity_orig += capacity_of(cpu); ++ capacity += capacity_of(cpu); ++ continue; ++ } ++ ++ sgc = rq->sd->groups->sgc; ++ capacity_orig += sgc->capacity_orig; ++ capacity += sgc->capacity; ++ } ++ } else { ++ /* ++ * !SD_OVERLAP domains can assume that child groups ++ * span the current group. ++ */ ++ ++ group = child->groups; ++ do { ++ capacity_orig += group->sgc->capacity_orig; ++ capacity += group->sgc->capacity; ++ group = group->next; ++ } while (group != child->groups); ++ } ++ ++ sdg->sgc->capacity_orig = capacity_orig; ++ sdg->sgc->capacity = capacity; ++} ++ ++/* ++ * Try and fix up capacity for tiny siblings, this is needed when ++ * things like SD_ASYM_PACKING need f_b_g to select another sibling ++ * which on its own isn't powerful enough. ++ * ++ * See update_sd_pick_busiest() and check_asym_packing(). ++ */ ++static inline int ++fix_small_capacity(struct sched_domain *sd, struct sched_group *group) ++{ ++ /* ++ * Only siblings can have significantly less than SCHED_CAPACITY_SCALE ++ */ ++ if (!(sd->flags & SD_SHARE_CPUCAPACITY)) ++ return 0; ++ ++ /* ++ * If ~90% of the cpu_capacity is still there, we're good. ++ */ ++ if (group->sgc->capacity * 32 > group->sgc->capacity_orig * 29) ++ return 1; ++ ++ return 0; ++} ++ ++/* ++ * Group imbalance indicates (and tries to solve) the problem where balancing ++ * groups is inadequate due to tsk_cpus_allowed() constraints. ++ * ++ * Imagine a situation of two groups of 4 cpus each and 4 tasks each with a ++ * cpumask covering 1 cpu of the first group and 3 cpus of the second group. ++ * Something like: ++ * ++ * { 0 1 2 3 } { 4 5 6 7 } ++ * * * * * ++ * ++ * If we were to balance group-wise we'd place two tasks in the first group and ++ * two tasks in the second group. Clearly this is undesired as it will overload ++ * cpu 3 and leave one of the cpus in the second group unused. ++ * ++ * The current solution to this issue is detecting the skew in the first group ++ * by noticing the lower domain failed to reach balance and had difficulty ++ * moving tasks due to affinity constraints. ++ * ++ * When this is so detected; this group becomes a candidate for busiest; see ++ * update_sd_pick_busiest(). And calculate_imbalance() and ++ * find_busiest_group() avoid some of the usual balance conditions to allow it ++ * to create an effective group imbalance. ++ * ++ * This is a somewhat tricky proposition since the next run might not find the ++ * group imbalance and decide the groups need to be balanced again. A most ++ * subtle and fragile situation. ++ */ ++ ++static inline int sg_imbalanced(struct sched_group *group) ++{ ++ return group->sgc->imbalance; ++} ++ ++/* ++ * Compute the group capacity factor. ++ * ++ * Avoid the issue where N*frac(smt_capacity) >= 1 creates 'phantom' cores by ++ * first dividing out the smt factor and computing the actual number of cores ++ * and limit unit capacity with that. ++ */ ++static inline int sg_capacity_factor(struct lb_env *env, struct sched_group *group) ++{ ++ unsigned int capacity_factor, smt, cpus; ++ unsigned int capacity, capacity_orig; ++ ++ capacity = group->sgc->capacity; ++ capacity_orig = group->sgc->capacity_orig; ++ cpus = group->group_weight; ++ ++ /* smt := ceil(cpus / capacity), assumes: 1 < smt_capacity < 2 */ ++ smt = DIV_ROUND_UP(SCHED_CAPACITY_SCALE * cpus, capacity_orig); ++ capacity_factor = cpus / smt; /* cores */ ++ ++ capacity_factor = min_t(unsigned, ++ capacity_factor, DIV_ROUND_CLOSEST(capacity, SCHED_CAPACITY_SCALE)); ++ if (!capacity_factor) ++ capacity_factor = fix_small_capacity(env->sd, group); ++ ++ return capacity_factor; ++} ++ ++static enum group_type ++group_classify(struct sched_group *group, struct sg_lb_stats *sgs) ++{ ++ if (sgs->sum_nr_running > sgs->group_capacity_factor) ++ return group_overloaded; ++ ++ if (sg_imbalanced(group)) ++ return group_imbalanced; ++ ++ return group_other; ++} ++ ++/** ++ * update_sg_lb_stats - Update sched_group's statistics for load balancing. ++ * @env: The load balancing environment. ++ * @group: sched_group whose statistics are to be updated. ++ * @load_idx: Load index of sched_domain of this_cpu for load calc. ++ * @local_group: Does group contain this_cpu. ++ * @sgs: variable to hold the statistics for this group. ++ * @overload: Indicate more than one runnable task for any CPU. ++ */ ++static inline void update_sg_lb_stats(struct lb_env *env, ++ struct sched_group *group, int load_idx, ++ int local_group, struct sg_lb_stats *sgs, ++ bool *overload) ++{ ++ unsigned long load; ++ int i; ++ ++ memset(sgs, 0, sizeof(*sgs)); ++ ++ for_each_cpu_and(i, sched_group_cpus(group), env->cpus) { ++ struct rq *rq = cpu_rq(i); ++ ++ /* Bias balancing toward cpus of our domain */ ++ if (local_group) ++ load = target_load(i, load_idx); ++ else ++ load = source_load(i, load_idx); ++ ++ sgs->group_load += load; ++ sgs->sum_nr_running += rq->cfs.h_nr_running; ++ ++ if (rq->nr_running > 1) ++ *overload = true; ++ ++#ifdef CONFIG_NUMA_BALANCING ++ sgs->nr_numa_running += rq->nr_numa_running; ++ sgs->nr_preferred_running += rq->nr_preferred_running; ++#endif ++ sgs->sum_weighted_load += weighted_cpuload(i); ++ if (idle_cpu(i)) ++ sgs->idle_cpus++; ++ } ++ ++ /* Adjust by relative CPU capacity of the group */ ++ sgs->group_capacity = group->sgc->capacity; ++ sgs->avg_load = (sgs->group_load*SCHED_CAPACITY_SCALE) / sgs->group_capacity; ++ ++ if (sgs->sum_nr_running) ++ sgs->load_per_task = sgs->sum_weighted_load / sgs->sum_nr_running; ++ ++ sgs->group_weight = group->group_weight; ++ sgs->group_capacity_factor = sg_capacity_factor(env, group); ++ sgs->group_type = group_classify(group, sgs); ++ ++ if (sgs->group_capacity_factor > sgs->sum_nr_running) ++ sgs->group_has_free_capacity = 1; ++} ++ ++/** ++ * update_sd_pick_busiest - return 1 on busiest group ++ * @env: The load balancing environment. ++ * @sds: sched_domain statistics ++ * @sg: sched_group candidate to be checked for being the busiest ++ * @sgs: sched_group statistics ++ * ++ * Determine if @sg is a busier group than the previously selected ++ * busiest group. ++ * ++ * Return: %true if @sg is a busier group than the previously selected ++ * busiest group. %false otherwise. ++ */ ++static bool update_sd_pick_busiest(struct lb_env *env, ++ struct sd_lb_stats *sds, ++ struct sched_group *sg, ++ struct sg_lb_stats *sgs) ++{ ++ struct sg_lb_stats *busiest = &sds->busiest_stat; ++ ++ if (sgs->group_type > busiest->group_type) ++ return true; ++ ++ if (sgs->group_type < busiest->group_type) ++ return false; ++ ++ if (sgs->avg_load <= busiest->avg_load) ++ return false; ++ ++ /* This is the busiest node in its class. */ ++ if (!(env->sd->flags & SD_ASYM_PACKING)) ++ return true; ++ ++ /* ++ * ASYM_PACKING needs to move all the work to the lowest ++ * numbered CPUs in the group, therefore mark all groups ++ * higher than ourself as busy. ++ */ ++ if (sgs->sum_nr_running && env->dst_cpu < group_first_cpu(sg)) { ++ if (!sds->busiest) ++ return true; ++ ++ if (group_first_cpu(sds->busiest) > group_first_cpu(sg)) ++ return true; ++ } ++ ++ return false; ++} ++ ++#ifdef CONFIG_NUMA_BALANCING ++static inline enum fbq_type fbq_classify_group(struct sg_lb_stats *sgs) ++{ ++ if (sgs->sum_nr_running > sgs->nr_numa_running) ++ return regular; ++ if (sgs->sum_nr_running > sgs->nr_preferred_running) ++ return remote; ++ return all; ++} ++ ++static inline enum fbq_type fbq_classify_rq(struct rq *rq) ++{ ++ if (rq->nr_running > rq->nr_numa_running) ++ return regular; ++ if (rq->nr_running > rq->nr_preferred_running) ++ return remote; ++ return all; ++} ++#else ++static inline enum fbq_type fbq_classify_group(struct sg_lb_stats *sgs) ++{ ++ return all; ++} ++ ++static inline enum fbq_type fbq_classify_rq(struct rq *rq) ++{ ++ return regular; ++} ++#endif /* CONFIG_NUMA_BALANCING */ ++ ++/** ++ * update_sd_lb_stats - Update sched_domain's statistics for load balancing. ++ * @env: The load balancing environment. ++ * @sds: variable to hold the statistics for this sched_domain. ++ */ ++static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sds) ++{ ++ struct sched_domain *child = env->sd->child; ++ struct sched_group *sg = env->sd->groups; ++ struct sg_lb_stats tmp_sgs; ++ int load_idx, prefer_sibling = 0; ++ bool overload = false; ++ ++ if (child && child->flags & SD_PREFER_SIBLING) ++ prefer_sibling = 1; ++ ++ load_idx = get_sd_load_idx(env->sd, env->idle); ++ ++ do { ++ struct sg_lb_stats *sgs = &tmp_sgs; ++ int local_group; ++ ++ local_group = cpumask_test_cpu(env->dst_cpu, sched_group_cpus(sg)); ++ if (local_group) { ++ sds->local = sg; ++ sgs = &sds->local_stat; ++ ++ if (env->idle != CPU_NEWLY_IDLE || ++ time_after_eq(jiffies, sg->sgc->next_update)) ++ update_group_capacity(env->sd, env->dst_cpu); ++ } ++ ++ update_sg_lb_stats(env, sg, load_idx, local_group, sgs, ++ &overload); ++ ++ if (local_group) ++ goto next_group; ++ ++ /* ++ * In case the child domain prefers tasks go to siblings ++ * first, lower the sg capacity factor to one so that we'll try ++ * and move all the excess tasks away. We lower the capacity ++ * of a group only if the local group has the capacity to fit ++ * these excess tasks, i.e. nr_running < group_capacity_factor. The ++ * extra check prevents the case where you always pull from the ++ * heaviest group when it is already under-utilized (possible ++ * with a large weight task outweighs the tasks on the system). ++ */ ++ if (prefer_sibling && sds->local && ++ sds->local_stat.group_has_free_capacity) ++ sgs->group_capacity_factor = min(sgs->group_capacity_factor, 1U); ++ ++ if (update_sd_pick_busiest(env, sds, sg, sgs)) { ++ sds->busiest = sg; ++ sds->busiest_stat = *sgs; ++ } ++ ++next_group: ++ /* Now, start updating sd_lb_stats */ ++ sds->total_load += sgs->group_load; ++ sds->total_capacity += sgs->group_capacity; ++ ++ sg = sg->next; ++ } while (sg != env->sd->groups); ++ ++ if (env->sd->flags & SD_NUMA) ++ env->fbq_type = fbq_classify_group(&sds->busiest_stat); ++ ++ if (!env->sd->parent) { ++ /* update overload indicator if we are at root domain */ ++ if (env->dst_rq->rd->overload != overload) ++ env->dst_rq->rd->overload = overload; ++ } ++ ++} ++ ++/** ++ * check_asym_packing - Check to see if the group is packed into the ++ * sched doman. ++ * ++ * This is primarily intended to used at the sibling level. Some ++ * cores like POWER7 prefer to use lower numbered SMT threads. In the ++ * case of POWER7, it can move to lower SMT modes only when higher ++ * threads are idle. When in lower SMT modes, the threads will ++ * perform better since they share less core resources. Hence when we ++ * have idle threads, we want them to be the higher ones. ++ * ++ * This packing function is run on idle threads. It checks to see if ++ * the busiest CPU in this domain (core in the P7 case) has a higher ++ * CPU number than the packing function is being run on. Here we are ++ * assuming lower CPU number will be equivalent to lower a SMT thread ++ * number. ++ * ++ * Return: 1 when packing is required and a task should be moved to ++ * this CPU. The amount of the imbalance is returned in *imbalance. ++ * ++ * @env: The load balancing environment. ++ * @sds: Statistics of the sched_domain which is to be packed ++ */ ++static int check_asym_packing(struct lb_env *env, struct sd_lb_stats *sds) ++{ ++ int busiest_cpu; ++ ++ if (!(env->sd->flags & SD_ASYM_PACKING)) ++ return 0; ++ ++ if (!sds->busiest) ++ return 0; ++ ++ busiest_cpu = group_first_cpu(sds->busiest); ++ if (env->dst_cpu > busiest_cpu) ++ return 0; ++ ++ env->imbalance = DIV_ROUND_CLOSEST( ++ sds->busiest_stat.avg_load * sds->busiest_stat.group_capacity, ++ SCHED_CAPACITY_SCALE); ++ ++ return 1; ++} ++ ++/** ++ * fix_small_imbalance - Calculate the minor imbalance that exists ++ * amongst the groups of a sched_domain, during ++ * load balancing. ++ * @env: The load balancing environment. ++ * @sds: Statistics of the sched_domain whose imbalance is to be calculated. ++ */ ++static inline ++void fix_small_imbalance(struct lb_env *env, struct sd_lb_stats *sds) ++{ ++ unsigned long tmp, capa_now = 0, capa_move = 0; ++ unsigned int imbn = 2; ++ unsigned long scaled_busy_load_per_task; ++ struct sg_lb_stats *local, *busiest; ++ ++ local = &sds->local_stat; ++ busiest = &sds->busiest_stat; ++ ++ if (!local->sum_nr_running) ++ local->load_per_task = cpu_avg_load_per_task(env->dst_cpu); ++ else if (busiest->load_per_task > local->load_per_task) ++ imbn = 1; ++ ++ scaled_busy_load_per_task = ++ (busiest->load_per_task * SCHED_CAPACITY_SCALE) / ++ busiest->group_capacity; ++ ++ if (busiest->avg_load + scaled_busy_load_per_task >= ++ local->avg_load + (scaled_busy_load_per_task * imbn)) { ++ env->imbalance = busiest->load_per_task; ++ return; ++ } ++ ++ /* ++ * OK, we don't have enough imbalance to justify moving tasks, ++ * however we may be able to increase total CPU capacity used by ++ * moving them. ++ */ ++ ++ capa_now += busiest->group_capacity * ++ min(busiest->load_per_task, busiest->avg_load); ++ capa_now += local->group_capacity * ++ min(local->load_per_task, local->avg_load); ++ capa_now /= SCHED_CAPACITY_SCALE; ++ ++ /* Amount of load we'd subtract */ ++ if (busiest->avg_load > scaled_busy_load_per_task) { ++ capa_move += busiest->group_capacity * ++ min(busiest->load_per_task, ++ busiest->avg_load - scaled_busy_load_per_task); ++ } ++ ++ /* Amount of load we'd add */ ++ if (busiest->avg_load * busiest->group_capacity < ++ busiest->load_per_task * SCHED_CAPACITY_SCALE) { ++ tmp = (busiest->avg_load * busiest->group_capacity) / ++ local->group_capacity; ++ } else { ++ tmp = (busiest->load_per_task * SCHED_CAPACITY_SCALE) / ++ local->group_capacity; ++ } ++ capa_move += local->group_capacity * ++ min(local->load_per_task, local->avg_load + tmp); ++ capa_move /= SCHED_CAPACITY_SCALE; ++ ++ /* Move if we gain throughput */ ++ if (capa_move > capa_now) ++ env->imbalance = busiest->load_per_task; ++} ++ ++/** ++ * calculate_imbalance - Calculate the amount of imbalance present within the ++ * groups of a given sched_domain during load balance. ++ * @env: load balance environment ++ * @sds: statistics of the sched_domain whose imbalance is to be calculated. ++ */ ++static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *sds) ++{ ++ unsigned long max_pull, load_above_capacity = ~0UL; ++ struct sg_lb_stats *local, *busiest; ++ ++ local = &sds->local_stat; ++ busiest = &sds->busiest_stat; ++ ++ if (busiest->group_type == group_imbalanced) { ++ /* ++ * In the group_imb case we cannot rely on group-wide averages ++ * to ensure cpu-load equilibrium, look at wider averages. XXX ++ */ ++ busiest->load_per_task = ++ min(busiest->load_per_task, sds->avg_load); ++ } ++ ++ /* ++ * In the presence of smp nice balancing, certain scenarios can have ++ * max load less than avg load(as we skip the groups at or below ++ * its cpu_capacity, while calculating max_load..) ++ */ ++ if (busiest->avg_load <= sds->avg_load || ++ local->avg_load >= sds->avg_load) { ++ env->imbalance = 0; ++ return fix_small_imbalance(env, sds); ++ } ++ ++ /* ++ * If there aren't any idle cpus, avoid creating some. ++ */ ++ if (busiest->group_type == group_overloaded && ++ local->group_type == group_overloaded) { ++ load_above_capacity = ++ (busiest->sum_nr_running - busiest->group_capacity_factor); ++ ++ load_above_capacity *= (SCHED_LOAD_SCALE * SCHED_CAPACITY_SCALE); ++ load_above_capacity /= busiest->group_capacity; ++ } ++ ++ /* ++ * We're trying to get all the cpus to the average_load, so we don't ++ * want to push ourselves above the average load, nor do we wish to ++ * reduce the max loaded cpu below the average load. At the same time, ++ * we also don't want to reduce the group load below the group capacity ++ * (so that we can implement power-savings policies etc). Thus we look ++ * for the minimum possible imbalance. ++ */ ++ max_pull = min(busiest->avg_load - sds->avg_load, load_above_capacity); ++ ++ /* How much load to actually move to equalise the imbalance */ ++ env->imbalance = min( ++ max_pull * busiest->group_capacity, ++ (sds->avg_load - local->avg_load) * local->group_capacity ++ ) / SCHED_CAPACITY_SCALE; ++ ++ /* ++ * if *imbalance is less than the average load per runnable task ++ * there is no guarantee that any tasks will be moved so we'll have ++ * a think about bumping its value to force at least one task to be ++ * moved ++ */ ++ if (env->imbalance < busiest->load_per_task) ++ return fix_small_imbalance(env, sds); ++} ++ ++/******* find_busiest_group() helpers end here *********************/ ++ ++/** ++ * find_busiest_group - Returns the busiest group within the sched_domain ++ * if there is an imbalance. If there isn't an imbalance, and ++ * the user has opted for power-savings, it returns a group whose ++ * CPUs can be put to idle by rebalancing those tasks elsewhere, if ++ * such a group exists. ++ * ++ * Also calculates the amount of weighted load which should be moved ++ * to restore balance. ++ * ++ * @env: The load balancing environment. ++ * ++ * Return: - The busiest group if imbalance exists. ++ * - If no imbalance and user has opted for power-savings balance, ++ * return the least loaded group whose CPUs can be ++ * put to idle by rebalancing its tasks onto our group. ++ */ ++static struct sched_group *find_busiest_group(struct lb_env *env) ++{ ++ struct sg_lb_stats *local, *busiest; ++ struct sd_lb_stats sds; ++ ++ init_sd_lb_stats(&sds); ++ ++ /* ++ * Compute the various statistics relavent for load balancing at ++ * this level. ++ */ ++ update_sd_lb_stats(env, &sds); ++ local = &sds.local_stat; ++ busiest = &sds.busiest_stat; ++ ++ if ((env->idle == CPU_IDLE || env->idle == CPU_NEWLY_IDLE) && ++ check_asym_packing(env, &sds)) ++ return sds.busiest; ++ ++ /* There is no busy sibling group to pull tasks from */ ++ if (!sds.busiest || busiest->sum_nr_running == 0) ++ goto out_balanced; ++ ++ sds.avg_load = (SCHED_CAPACITY_SCALE * sds.total_load) ++ / sds.total_capacity; ++ ++ /* ++ * If the busiest group is imbalanced the below checks don't ++ * work because they assume all things are equal, which typically ++ * isn't true due to cpus_allowed constraints and the like. ++ */ ++ if (busiest->group_type == group_imbalanced) ++ goto force_balance; ++ ++ /* SD_BALANCE_NEWIDLE trumps SMP nice when underutilized */ ++ if (env->idle == CPU_NEWLY_IDLE && local->group_has_free_capacity && ++ !busiest->group_has_free_capacity) ++ goto force_balance; ++ ++ /* ++ * If the local group is busier than the selected busiest group ++ * don't try and pull any tasks. ++ */ ++ if (local->avg_load >= busiest->avg_load) ++ goto out_balanced; ++ ++ /* ++ * Don't pull any tasks if this group is already above the domain ++ * average load. ++ */ ++ if (local->avg_load >= sds.avg_load) ++ goto out_balanced; ++ ++ if (env->idle == CPU_IDLE) { ++ /* ++ * This cpu is idle. If the busiest group is not overloaded ++ * and there is no imbalance between this and busiest group ++ * wrt idle cpus, it is balanced. The imbalance becomes ++ * significant if the diff is greater than 1 otherwise we ++ * might end up to just move the imbalance on another group ++ */ ++ if ((busiest->group_type != group_overloaded) && ++ (local->idle_cpus <= (busiest->idle_cpus + 1))) ++ goto out_balanced; ++ } else { ++ /* ++ * In the CPU_NEWLY_IDLE, CPU_NOT_IDLE cases, use ++ * imbalance_pct to be conservative. ++ */ ++ if (100 * busiest->avg_load <= ++ env->sd->imbalance_pct * local->avg_load) ++ goto out_balanced; ++ } ++ ++force_balance: ++ /* Looks like there is an imbalance. Compute it */ ++ calculate_imbalance(env, &sds); ++ return sds.busiest; ++ ++out_balanced: ++ env->imbalance = 0; ++ return NULL; ++} ++ ++/* ++ * find_busiest_queue - find the busiest runqueue among the cpus in group. ++ */ ++static struct rq *find_busiest_queue(struct lb_env *env, ++ struct sched_group *group) ++{ ++ struct rq *busiest = NULL, *rq; ++ unsigned long busiest_load = 0, busiest_capacity = 1; ++ int i; ++ ++ for_each_cpu_and(i, sched_group_cpus(group), env->cpus) { ++ unsigned long capacity, capacity_factor, wl; ++ enum fbq_type rt; ++ ++ rq = cpu_rq(i); ++ rt = fbq_classify_rq(rq); ++ ++ /* ++ * We classify groups/runqueues into three groups: ++ * - regular: there are !numa tasks ++ * - remote: there are numa tasks that run on the 'wrong' node ++ * - all: there is no distinction ++ * ++ * In order to avoid migrating ideally placed numa tasks, ++ * ignore those when there's better options. ++ * ++ * If we ignore the actual busiest queue to migrate another ++ * task, the next balance pass can still reduce the busiest ++ * queue by moving tasks around inside the node. ++ * ++ * If we cannot move enough load due to this classification ++ * the next pass will adjust the group classification and ++ * allow migration of more tasks. ++ * ++ * Both cases only affect the total convergence complexity. ++ */ ++ if (rt > env->fbq_type) ++ continue; ++ ++ capacity = capacity_of(i); ++ capacity_factor = DIV_ROUND_CLOSEST(capacity, SCHED_CAPACITY_SCALE); ++ if (!capacity_factor) ++ capacity_factor = fix_small_capacity(env->sd, group); ++ ++ wl = weighted_cpuload(i); ++ ++ /* ++ * When comparing with imbalance, use weighted_cpuload() ++ * which is not scaled with the cpu capacity. ++ */ ++ if (capacity_factor && rq->nr_running == 1 && wl > env->imbalance) ++ continue; ++ ++ /* ++ * For the load comparisons with the other cpu's, consider ++ * the weighted_cpuload() scaled with the cpu capacity, so ++ * that the load can be moved away from the cpu that is ++ * potentially running at a lower capacity. ++ * ++ * Thus we're looking for max(wl_i / capacity_i), crosswise ++ * multiplication to rid ourselves of the division works out ++ * to: wl_i * capacity_j > wl_j * capacity_i; where j is ++ * our previous maximum. ++ */ ++ if (wl * busiest_capacity > busiest_load * capacity) { ++ busiest_load = wl; ++ busiest_capacity = capacity; ++ busiest = rq; ++ } ++ } ++ ++ return busiest; ++} ++ ++/* ++ * Max backoff if we encounter pinned tasks. Pretty arbitrary value, but ++ * so long as it is large enough. ++ */ ++#define MAX_PINNED_INTERVAL 512 ++ ++/* Working cpumask for load_balance and load_balance_newidle. */ ++DEFINE_PER_CPU(cpumask_var_t, load_balance_mask); ++ ++static int need_active_balance(struct lb_env *env) ++{ ++ struct sched_domain *sd = env->sd; ++ ++ if (env->idle == CPU_NEWLY_IDLE) { ++ ++ /* ++ * ASYM_PACKING needs to force migrate tasks from busy but ++ * higher numbered CPUs in order to pack all tasks in the ++ * lowest numbered CPUs. ++ */ ++ if ((sd->flags & SD_ASYM_PACKING) && env->src_cpu > env->dst_cpu) ++ return 1; ++ } ++ ++ return unlikely(sd->nr_balance_failed > sd->cache_nice_tries+2); ++} ++ ++static int active_load_balance_cpu_stop(void *data); ++ ++static int should_we_balance(struct lb_env *env) ++{ ++ struct sched_group *sg = env->sd->groups; ++ struct cpumask *sg_cpus, *sg_mask; ++ int cpu, balance_cpu = -1; ++ ++ /* ++ * In the newly idle case, we will allow all the cpu's ++ * to do the newly idle load balance. ++ */ ++ if (env->idle == CPU_NEWLY_IDLE) ++ return 1; ++ ++ sg_cpus = sched_group_cpus(sg); ++ sg_mask = sched_group_mask(sg); ++ /* Try to find first idle cpu */ ++ for_each_cpu_and(cpu, sg_cpus, env->cpus) { ++ if (!cpumask_test_cpu(cpu, sg_mask) || !idle_cpu(cpu)) ++ continue; ++ ++ balance_cpu = cpu; ++ break; ++ } ++ ++ if (balance_cpu == -1) ++ balance_cpu = group_balance_cpu(sg); ++ ++ /* ++ * First idle cpu or the first cpu(busiest) in this sched group ++ * is eligible for doing load balancing at this and above domains. ++ */ ++ return balance_cpu == env->dst_cpu; ++} ++ ++/* ++ * Check this_cpu to ensure it is balanced within domain. Attempt to move ++ * tasks if there is an imbalance. ++ */ ++static int load_balance(int this_cpu, struct rq *this_rq, ++ struct sched_domain *sd, enum cpu_idle_type idle, ++ int *continue_balancing) ++{ ++ int ld_moved, cur_ld_moved, active_balance = 0; ++ struct sched_domain *sd_parent = sd->parent; ++ struct sched_group *group; ++ struct rq *busiest; ++ unsigned long flags; ++ struct cpumask *cpus = this_cpu_cpumask_var_ptr(load_balance_mask); ++ ++ struct lb_env env = { ++ .sd = sd, ++ .dst_cpu = this_cpu, ++ .dst_rq = this_rq, ++ .dst_grpmask = sched_group_cpus(sd->groups), ++ .idle = idle, ++ .loop_break = sched_nr_migrate_break, ++ .cpus = cpus, ++ .fbq_type = all, ++ .tasks = LIST_HEAD_INIT(env.tasks), ++ }; ++ ++ /* ++ * For NEWLY_IDLE load_balancing, we don't need to consider ++ * other cpus in our group ++ */ ++ if (idle == CPU_NEWLY_IDLE) ++ env.dst_grpmask = NULL; ++ ++ cpumask_copy(cpus, cpu_active_mask); ++ ++ schedstat_inc(sd, lb_count[idle]); ++ ++redo: ++ if (!should_we_balance(&env)) { ++ *continue_balancing = 0; ++ goto out_balanced; ++ } ++ ++ group = find_busiest_group(&env); ++ if (!group) { ++ schedstat_inc(sd, lb_nobusyg[idle]); ++ goto out_balanced; ++ } ++ ++ busiest = find_busiest_queue(&env, group); ++ if (!busiest) { ++ schedstat_inc(sd, lb_nobusyq[idle]); ++ goto out_balanced; ++ } ++ ++ BUG_ON(busiest == env.dst_rq); ++ ++ schedstat_add(sd, lb_imbalance[idle], env.imbalance); ++ ++ ld_moved = 0; ++ if (busiest->nr_running > 1) { ++ /* ++ * Attempt to move tasks. If find_busiest_group has found ++ * an imbalance but busiest->nr_running <= 1, the group is ++ * still unbalanced. ld_moved simply stays zero, so it is ++ * correctly treated as an imbalance. ++ */ ++ env.flags |= LBF_ALL_PINNED; ++ env.src_cpu = busiest->cpu; ++ env.src_rq = busiest; ++ env.loop_max = min(sysctl_sched_nr_migrate, busiest->nr_running); ++ ++more_balance: ++ raw_spin_lock_irqsave(&busiest->lock, flags); ++ ++ /* ++ * cur_ld_moved - load moved in current iteration ++ * ld_moved - cumulative load moved across iterations ++ */ ++ cur_ld_moved = detach_tasks(&env); ++ ++ /* ++ * We've detached some tasks from busiest_rq. Every ++ * task is masked "TASK_ON_RQ_MIGRATING", so we can safely ++ * unlock busiest->lock, and we are able to be sure ++ * that nobody can manipulate the tasks in parallel. ++ * See task_rq_lock() family for the details. ++ */ ++ ++ raw_spin_unlock(&busiest->lock); ++ ++ if (cur_ld_moved) { ++ attach_tasks(&env); ++ ld_moved += cur_ld_moved; ++ } ++ ++ local_irq_restore(flags); ++ ++ if (env.flags & LBF_NEED_BREAK) { ++ env.flags &= ~LBF_NEED_BREAK; ++ goto more_balance; ++ } ++ ++ /* ++ * Revisit (affine) tasks on src_cpu that couldn't be moved to ++ * us and move them to an alternate dst_cpu in our sched_group ++ * where they can run. The upper limit on how many times we ++ * iterate on same src_cpu is dependent on number of cpus in our ++ * sched_group. ++ * ++ * This changes load balance semantics a bit on who can move ++ * load to a given_cpu. In addition to the given_cpu itself ++ * (or a ilb_cpu acting on its behalf where given_cpu is ++ * nohz-idle), we now have balance_cpu in a position to move ++ * load to given_cpu. In rare situations, this may cause ++ * conflicts (balance_cpu and given_cpu/ilb_cpu deciding ++ * _independently_ and at _same_ time to move some load to ++ * given_cpu) causing exceess load to be moved to given_cpu. ++ * This however should not happen so much in practice and ++ * moreover subsequent load balance cycles should correct the ++ * excess load moved. ++ */ ++ if ((env.flags & LBF_DST_PINNED) && env.imbalance > 0) { ++ ++ /* Prevent to re-select dst_cpu via env's cpus */ ++ cpumask_clear_cpu(env.dst_cpu, env.cpus); ++ ++ env.dst_rq = cpu_rq(env.new_dst_cpu); ++ env.dst_cpu = env.new_dst_cpu; ++ env.flags &= ~LBF_DST_PINNED; ++ env.loop = 0; ++ env.loop_break = sched_nr_migrate_break; ++ ++ /* ++ * Go back to "more_balance" rather than "redo" since we ++ * need to continue with same src_cpu. ++ */ ++ goto more_balance; ++ } ++ ++ /* ++ * We failed to reach balance because of affinity. ++ */ ++ if (sd_parent) { ++ int *group_imbalance = &sd_parent->groups->sgc->imbalance; ++ ++ if ((env.flags & LBF_SOME_PINNED) && env.imbalance > 0) ++ *group_imbalance = 1; ++ } ++ ++ /* All tasks on this runqueue were pinned by CPU affinity */ ++ if (unlikely(env.flags & LBF_ALL_PINNED)) { ++ cpumask_clear_cpu(cpu_of(busiest), cpus); ++ if (!cpumask_empty(cpus)) { ++ env.loop = 0; ++ env.loop_break = sched_nr_migrate_break; ++ goto redo; ++ } ++ goto out_all_pinned; ++ } ++ } ++ ++ if (!ld_moved) { ++ schedstat_inc(sd, lb_failed[idle]); ++ /* ++ * Increment the failure counter only on periodic balance. ++ * We do not want newidle balance, which can be very ++ * frequent, pollute the failure counter causing ++ * excessive cache_hot migrations and active balances. ++ */ ++ if (idle != CPU_NEWLY_IDLE) ++ sd->nr_balance_failed++; + +- if (child && child->flags & SD_PREFER_SIBLING) +- prefer_sibling = 1; ++ if (need_active_balance(&env)) { ++ raw_spin_lock_irqsave(&busiest->lock, flags); + +- load_idx = get_sd_load_idx(env->sd, env->idle); ++ /* don't kick the active_load_balance_cpu_stop, ++ * if the curr task on busiest cpu can't be ++ * moved to this_cpu ++ */ ++ if (!cpumask_test_cpu(this_cpu, ++ tsk_cpus_allowed(busiest->curr))) { ++ raw_spin_unlock_irqrestore(&busiest->lock, ++ flags); ++ env.flags |= LBF_ALL_PINNED; ++ goto out_one_pinned; ++ } + +- do { +- struct sg_lb_stats *sgs = &tmp_sgs; +- int local_group; ++ /* ++ * ->active_balance synchronizes accesses to ++ * ->active_balance_work. Once set, it's cleared ++ * only after active load balance is finished. ++ */ ++ if (!busiest->active_balance) { ++ busiest->active_balance = 1; ++ busiest->push_cpu = this_cpu; ++ active_balance = 1; ++ } ++ raw_spin_unlock_irqrestore(&busiest->lock, flags); + +- local_group = cpumask_test_cpu(env->dst_cpu, sched_group_cpus(sg)); +- if (local_group) { +- sds->local = sg; +- sgs = &sds->local_stat; ++ if (active_balance) { ++ stop_one_cpu_nowait(cpu_of(busiest), ++ active_load_balance_cpu_stop, busiest, ++ &busiest->active_balance_work); ++ } + +- if (env->idle != CPU_NEWLY_IDLE || +- time_after_eq(jiffies, sg->sgc->next_update)) +- update_group_capacity(env->sd, env->dst_cpu); ++ /* ++ * We've kicked active balancing, reset the failure ++ * counter. ++ */ ++ sd->nr_balance_failed = sd->cache_nice_tries+1; + } ++ } else ++ sd->nr_balance_failed = 0; + +- update_sg_lb_stats(env, sg, load_idx, local_group, sgs, +- &overload); +- +- if (local_group) +- goto next_group; +- ++ if (likely(!active_balance)) { ++ /* We were unbalanced, so reset the balancing interval */ ++ sd->balance_interval = sd->min_interval; ++ } else { + /* +- * In case the child domain prefers tasks go to siblings +- * first, lower the sg capacity factor to one so that we'll try +- * and move all the excess tasks away. We lower the capacity +- * of a group only if the local group has the capacity to fit +- * these excess tasks, i.e. nr_running < group_capacity_factor. The +- * extra check prevents the case where you always pull from the +- * heaviest group when it is already under-utilized (possible +- * with a large weight task outweighs the tasks on the system). ++ * If we've begun active balancing, start to back off. This ++ * case may not be covered by the all_pinned logic if there ++ * is only 1 task on the busy runqueue (because we don't call ++ * detach_tasks). + */ +- if (prefer_sibling && sds->local && +- sds->local_stat.group_has_free_capacity) +- sgs->group_capacity_factor = min(sgs->group_capacity_factor, 1U); ++ if (sd->balance_interval < sd->max_interval) ++ sd->balance_interval *= 2; ++ } + +- if (update_sd_pick_busiest(env, sds, sg, sgs)) { +- sds->busiest = sg; +- sds->busiest_stat = *sgs; +- } ++ goto out; + +-next_group: +- /* Now, start updating sd_lb_stats */ +- sds->total_load += sgs->group_load; +- sds->total_capacity += sgs->group_capacity; ++out_balanced: ++ /* ++ * We reach balance although we may have faced some affinity ++ * constraints. Clear the imbalance flag if it was set. ++ */ ++ if (sd_parent) { ++ int *group_imbalance = &sd_parent->groups->sgc->imbalance; + +- sg = sg->next; +- } while (sg != env->sd->groups); ++ if (*group_imbalance) ++ *group_imbalance = 0; ++ } + +- if (env->sd->flags & SD_NUMA) +- env->fbq_type = fbq_classify_group(&sds->busiest_stat); ++out_all_pinned: ++ /* ++ * We reach balance because all tasks are pinned at this level so ++ * we can't migrate them. Let the imbalance flag set so parent level ++ * can try to migrate them. ++ */ ++ schedstat_inc(sd, lb_balanced[idle]); + +- if (!env->sd->parent) { +- /* update overload indicator if we are at root domain */ +- if (env->dst_rq->rd->overload != overload) +- env->dst_rq->rd->overload = overload; +- } ++ sd->nr_balance_failed = 0; ++ ++out_one_pinned: ++ /* tune up the balancing interval */ ++ if (((env.flags & LBF_ALL_PINNED) && ++ sd->balance_interval < MAX_PINNED_INTERVAL) || ++ (sd->balance_interval < sd->max_interval)) ++ sd->balance_interval *= 2; + ++ ld_moved = 0; ++out: ++ return ld_moved; + } + +-/** +- * check_asym_packing - Check to see if the group is packed into the +- * sched doman. +- * +- * This is primarily intended to used at the sibling level. Some +- * cores like POWER7 prefer to use lower numbered SMT threads. In the +- * case of POWER7, it can move to lower SMT modes only when higher +- * threads are idle. When in lower SMT modes, the threads will +- * perform better since they share less core resources. Hence when we +- * have idle threads, we want them to be the higher ones. +- * +- * This packing function is run on idle threads. It checks to see if +- * the busiest CPU in this domain (core in the P7 case) has a higher +- * CPU number than the packing function is being run on. Here we are +- * assuming lower CPU number will be equivalent to lower a SMT thread +- * number. +- * +- * Return: 1 when packing is required and a task should be moved to +- * this CPU. The amount of the imbalance is returned in *imbalance. +- * +- * @env: The load balancing environment. +- * @sds: Statistics of the sched_domain which is to be packed +- */ +-static int check_asym_packing(struct lb_env *env, struct sd_lb_stats *sds) ++static inline unsigned long ++get_sd_balance_interval(struct sched_domain *sd, int cpu_busy) + { +- int busiest_cpu; ++ unsigned long interval = sd->balance_interval; + +- if (!(env->sd->flags & SD_ASYM_PACKING)) +- return 0; ++ if (cpu_busy) ++ interval *= sd->busy_factor; + +- if (!sds->busiest) +- return 0; ++ /* scale ms to jiffies */ ++ interval = msecs_to_jiffies(interval); ++ interval = clamp(interval, 1UL, max_load_balance_interval); + +- busiest_cpu = group_first_cpu(sds->busiest); +- if (env->dst_cpu > busiest_cpu) +- return 0; ++ return interval; ++} + +- env->imbalance = DIV_ROUND_CLOSEST( +- sds->busiest_stat.avg_load * sds->busiest_stat.group_capacity, +- SCHED_CAPACITY_SCALE); ++static inline void ++update_next_balance(struct sched_domain *sd, int cpu_busy, unsigned long *next_balance) ++{ ++ unsigned long interval, next; + +- return 1; ++ interval = get_sd_balance_interval(sd, cpu_busy); ++ next = sd->last_balance + interval; ++ ++ if (time_after(*next_balance, next)) ++ *next_balance = next; + } + +-/** +- * fix_small_imbalance - Calculate the minor imbalance that exists +- * amongst the groups of a sched_domain, during +- * load balancing. +- * @env: The load balancing environment. +- * @sds: Statistics of the sched_domain whose imbalance is to be calculated. ++/* ++ * idle_balance is called by schedule() if this_cpu is about to become ++ * idle. Attempts to pull tasks from other CPUs. + */ +-static inline +-void fix_small_imbalance(struct lb_env *env, struct sd_lb_stats *sds) ++static int idle_balance(struct rq *this_rq) + { +- unsigned long tmp, capa_now = 0, capa_move = 0; +- unsigned int imbn = 2; +- unsigned long scaled_busy_load_per_task; +- struct sg_lb_stats *local, *busiest; ++ unsigned long next_balance = jiffies + HZ; ++ int this_cpu = this_rq->cpu; ++ struct sched_domain *sd; ++ int pulled_task = 0; ++ u64 curr_cost = 0; + +- local = &sds->local_stat; +- busiest = &sds->busiest_stat; ++ idle_enter_fair(this_rq); + +- if (!local->sum_nr_running) +- local->load_per_task = cpu_avg_load_per_task(env->dst_cpu); +- else if (busiest->load_per_task > local->load_per_task) +- imbn = 1; ++ /* ++ * We must set idle_stamp _before_ calling idle_balance(), such that we ++ * measure the duration of idle_balance() as idle time. ++ */ ++ this_rq->idle_stamp = rq_clock(this_rq); + +- scaled_busy_load_per_task = +- (busiest->load_per_task * SCHED_CAPACITY_SCALE) / +- busiest->group_capacity; ++ if (this_rq->avg_idle < sysctl_sched_migration_cost || ++ !this_rq->rd->overload) { ++ rcu_read_lock(); ++ sd = rcu_dereference_check_sched_domain(this_rq->sd); ++ if (sd) ++ update_next_balance(sd, 0, &next_balance); ++ rcu_read_unlock(); + +- if (busiest->avg_load + scaled_busy_load_per_task >= +- local->avg_load + (scaled_busy_load_per_task * imbn)) { +- env->imbalance = busiest->load_per_task; +- return; ++ goto out; + } + + /* +- * OK, we don't have enough imbalance to justify moving tasks, +- * however we may be able to increase total CPU capacity used by +- * moving them. ++ * Drop the rq->lock, but keep IRQ/preempt disabled. + */ ++ raw_spin_unlock(&this_rq->lock); + +- capa_now += busiest->group_capacity * +- min(busiest->load_per_task, busiest->avg_load); +- capa_now += local->group_capacity * +- min(local->load_per_task, local->avg_load); +- capa_now /= SCHED_CAPACITY_SCALE; ++ update_blocked_averages(this_cpu); ++ rcu_read_lock(); ++ for_each_domain(this_cpu, sd) { ++ int continue_balancing = 1; ++ u64 t0, domain_cost; + +- /* Amount of load we'd subtract */ +- if (busiest->avg_load > scaled_busy_load_per_task) { +- capa_move += busiest->group_capacity * +- min(busiest->load_per_task, +- busiest->avg_load - scaled_busy_load_per_task); +- } ++ if (!(sd->flags & SD_LOAD_BALANCE)) ++ continue; ++ ++ if (this_rq->avg_idle < curr_cost + sd->max_newidle_lb_cost) { ++ update_next_balance(sd, 0, &next_balance); ++ break; ++ } ++ ++ if (sd->flags & SD_BALANCE_NEWIDLE) { ++ t0 = sched_clock_cpu(this_cpu); + +- /* Amount of load we'd add */ +- if (busiest->avg_load * busiest->group_capacity < +- busiest->load_per_task * SCHED_CAPACITY_SCALE) { +- tmp = (busiest->avg_load * busiest->group_capacity) / +- local->group_capacity; +- } else { +- tmp = (busiest->load_per_task * SCHED_CAPACITY_SCALE) / +- local->group_capacity; +- } +- capa_move += local->group_capacity * +- min(local->load_per_task, local->avg_load + tmp); +- capa_move /= SCHED_CAPACITY_SCALE; ++ pulled_task = load_balance(this_cpu, this_rq, ++ sd, CPU_NEWLY_IDLE, ++ &continue_balancing); + +- /* Move if we gain throughput */ +- if (capa_move > capa_now) +- env->imbalance = busiest->load_per_task; +-} ++ domain_cost = sched_clock_cpu(this_cpu) - t0; ++ if (domain_cost > sd->max_newidle_lb_cost) ++ sd->max_newidle_lb_cost = domain_cost; + +-/** +- * calculate_imbalance - Calculate the amount of imbalance present within the +- * groups of a given sched_domain during load balance. +- * @env: load balance environment +- * @sds: statistics of the sched_domain whose imbalance is to be calculated. +- */ +-static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *sds) +-{ +- unsigned long max_pull, load_above_capacity = ~0UL; +- struct sg_lb_stats *local, *busiest; ++ curr_cost += domain_cost; ++ } + +- local = &sds->local_stat; +- busiest = &sds->busiest_stat; ++ update_next_balance(sd, 0, &next_balance); + +- if (busiest->group_type == group_imbalanced) { + /* +- * In the group_imb case we cannot rely on group-wide averages +- * to ensure cpu-load equilibrium, look at wider averages. XXX ++ * Stop searching for tasks to pull if there are ++ * now runnable tasks on this rq. + */ +- busiest->load_per_task = +- min(busiest->load_per_task, sds->avg_load); ++ if (pulled_task || this_rq->nr_running > 0) ++ break; + } ++ rcu_read_unlock(); ++#ifdef CONFIG_SCHED_HMP ++ if (!pulled_task) ++ pulled_task = hmp_idle_pull(this_cpu); ++#endif ++ raw_spin_lock(&this_rq->lock); + +- /* +- * In the presence of smp nice balancing, certain scenarios can have +- * max load less than avg load(as we skip the groups at or below +- * its cpu_capacity, while calculating max_load..) +- */ +- if (busiest->avg_load <= sds->avg_load || +- local->avg_load >= sds->avg_load) { +- env->imbalance = 0; +- return fix_small_imbalance(env, sds); +- } ++ if (curr_cost > this_rq->max_idle_balance_cost) ++ this_rq->max_idle_balance_cost = curr_cost; + + /* +- * If there aren't any idle cpus, avoid creating some. ++ * While browsing the domains, we released the rq lock, a task could ++ * have been enqueued in the meantime. Since we're not going idle, ++ * pretend we pulled a task. + */ +- if (busiest->group_type == group_overloaded && +- local->group_type == group_overloaded) { +- load_above_capacity = +- (busiest->sum_nr_running - busiest->group_capacity_factor); ++ if (this_rq->cfs.h_nr_running && !pulled_task) ++ pulled_task = 1; + +- load_above_capacity *= (SCHED_LOAD_SCALE * SCHED_CAPACITY_SCALE); +- load_above_capacity /= busiest->group_capacity; +- } ++out: ++ /* Move the next balance forward */ ++ if (time_after(this_rq->next_balance, next_balance)) ++ this_rq->next_balance = next_balance; + +- /* +- * We're trying to get all the cpus to the average_load, so we don't +- * want to push ourselves above the average load, nor do we wish to +- * reduce the max loaded cpu below the average load. At the same time, +- * we also don't want to reduce the group load below the group capacity +- * (so that we can implement power-savings policies etc). Thus we look +- * for the minimum possible imbalance. +- */ +- max_pull = min(busiest->avg_load - sds->avg_load, load_above_capacity); ++ /* Is there a task of a high priority class? */ ++ if (this_rq->nr_running != this_rq->cfs.h_nr_running) ++ pulled_task = -1; + +- /* How much load to actually move to equalise the imbalance */ +- env->imbalance = min( +- max_pull * busiest->group_capacity, +- (sds->avg_load - local->avg_load) * local->group_capacity +- ) / SCHED_CAPACITY_SCALE; ++ if (pulled_task) { ++ idle_exit_fair(this_rq); ++ this_rq->idle_stamp = 0; ++ } + +- /* +- * if *imbalance is less than the average load per runnable task +- * there is no guarantee that any tasks will be moved so we'll have +- * a think about bumping its value to force at least one task to be +- * moved +- */ +- if (env->imbalance < busiest->load_per_task) +- return fix_small_imbalance(env, sds); ++ return pulled_task; + } + +-/******* find_busiest_group() helpers end here *********************/ +- +-/** +- * find_busiest_group - Returns the busiest group within the sched_domain +- * if there is an imbalance. If there isn't an imbalance, and +- * the user has opted for power-savings, it returns a group whose +- * CPUs can be put to idle by rebalancing those tasks elsewhere, if +- * such a group exists. +- * +- * Also calculates the amount of weighted load which should be moved +- * to restore balance. +- * +- * @env: The load balancing environment. +- * +- * Return: - The busiest group if imbalance exists. +- * - If no imbalance and user has opted for power-savings balance, +- * return the least loaded group whose CPUs can be +- * put to idle by rebalancing its tasks onto our group. ++/* ++ * active_load_balance_cpu_stop is run by cpu stopper. It pushes ++ * running tasks off the busiest CPU onto idle CPUs. It requires at ++ * least 1 task to be running on each physical CPU where possible, and ++ * avoids physical / logical imbalances. + */ +-static struct sched_group *find_busiest_group(struct lb_env *env) ++static int active_load_balance_cpu_stop(void *data) + { +- struct sg_lb_stats *local, *busiest; +- struct sd_lb_stats sds; ++ struct rq *busiest_rq = data; ++ int busiest_cpu = cpu_of(busiest_rq); ++ int target_cpu = busiest_rq->push_cpu; ++ struct rq *target_rq = cpu_rq(target_cpu); ++ struct sched_domain *sd; ++ struct task_struct *p = NULL; + +- init_sd_lb_stats(&sds); ++ raw_spin_lock_irq(&busiest_rq->lock); ++ ++ /* make sure the requested cpu hasn't gone down in the meantime */ ++ if (unlikely(busiest_cpu != smp_processor_id() || ++ !busiest_rq->active_balance)) ++ goto out_unlock; ++ ++ /* Is there any task to move? */ ++ if (busiest_rq->nr_running <= 1) ++ goto out_unlock; + + /* +- * Compute the various statistics relavent for load balancing at +- * this level. ++ * This condition is "impossible", if it occurs ++ * we need to fix it. Originally reported by ++ * Bjorn Helgaas on a 128-cpu setup. + */ +- update_sd_lb_stats(env, &sds); +- local = &sds.local_stat; +- busiest = &sds.busiest_stat; ++ BUG_ON(busiest_rq == target_rq); + +- if ((env->idle == CPU_IDLE || env->idle == CPU_NEWLY_IDLE) && +- check_asym_packing(env, &sds)) +- return sds.busiest; ++ /* Search for an sd spanning us and the target CPU. */ ++ rcu_read_lock(); ++ for_each_domain(target_cpu, sd) { ++ if ((sd->flags & SD_LOAD_BALANCE) && ++ cpumask_test_cpu(busiest_cpu, sched_domain_span(sd))) ++ break; ++ } + +- /* There is no busy sibling group to pull tasks from */ +- if (!sds.busiest || busiest->sum_nr_running == 0) +- goto out_balanced; ++ if (likely(sd)) { ++ struct lb_env env = { ++ .sd = sd, ++ .dst_cpu = target_cpu, ++ .dst_rq = target_rq, ++ .src_cpu = busiest_rq->cpu, ++ .src_rq = busiest_rq, ++ .idle = CPU_IDLE, ++ }; + +- sds.avg_load = (SCHED_CAPACITY_SCALE * sds.total_load) +- / sds.total_capacity; ++ schedstat_inc(sd, alb_count); + +- /* +- * If the busiest group is imbalanced the below checks don't +- * work because they assume all things are equal, which typically +- * isn't true due to cpus_allowed constraints and the like. +- */ +- if (busiest->group_type == group_imbalanced) +- goto force_balance; ++ p = detach_one_task(&env); ++ if (p) ++ schedstat_inc(sd, alb_pushed); ++ else ++ schedstat_inc(sd, alb_failed); ++ } ++ rcu_read_unlock(); ++out_unlock: ++ busiest_rq->active_balance = 0; ++ raw_spin_unlock(&busiest_rq->lock); + +- /* SD_BALANCE_NEWIDLE trumps SMP nice when underutilized */ +- if (env->idle == CPU_NEWLY_IDLE && local->group_has_free_capacity && +- !busiest->group_has_free_capacity) +- goto force_balance; ++ if (p) ++ attach_one_task(target_rq, p); + +- /* +- * If the local group is busier than the selected busiest group +- * don't try and pull any tasks. +- */ +- if (local->avg_load >= busiest->avg_load) +- goto out_balanced; ++ local_irq_enable(); + +- /* +- * Don't pull any tasks if this group is already above the domain +- * average load. +- */ +- if (local->avg_load >= sds.avg_load) +- goto out_balanced; ++ return 0; ++} + +- if (env->idle == CPU_IDLE) { +- /* +- * This cpu is idle. If the busiest group is not overloaded +- * and there is no imbalance between this and busiest group +- * wrt idle cpus, it is balanced. The imbalance becomes +- * significant if the diff is greater than 1 otherwise we +- * might end up to just move the imbalance on another group +- */ +- if ((busiest->group_type != group_overloaded) && +- (local->idle_cpus <= (busiest->idle_cpus + 1))) +- goto out_balanced; +- } else { +- /* +- * In the CPU_NEWLY_IDLE, CPU_NOT_IDLE cases, use +- * imbalance_pct to be conservative. +- */ +- if (100 * busiest->avg_load <= +- env->sd->imbalance_pct * local->avg_load) +- goto out_balanced; +- } ++static inline int on_null_domain(struct rq *rq) ++{ ++ return unlikely(!rcu_dereference_sched(rq->sd)); ++} + +-force_balance: +- /* Looks like there is an imbalance. Compute it */ +- calculate_imbalance(env, &sds); +- return sds.busiest; ++#ifdef CONFIG_NO_HZ_COMMON ++/* ++ * idle load balancing details ++ * - When one of the busy CPUs notice that there may be an idle rebalancing ++ * needed, they will kick the idle load balancer, which then does idle ++ * load balancing for all the idle CPUs. ++ */ ++static struct { ++ cpumask_var_t idle_cpus_mask; ++ atomic_t nr_cpus; ++ unsigned long next_balance; /* in jiffy units */ ++} nohz ____cacheline_aligned; + +-out_balanced: +- env->imbalance = 0; +- return NULL; ++/* ++ * nohz_test_cpu used when load tracking is enabled. FAIR_GROUP_SCHED ++ * dependency below may be removed when load tracking guards are ++ * removed. ++ */ ++#ifdef CONFIG_FAIR_GROUP_SCHED ++static int nohz_test_cpu(int cpu) ++{ ++ return cpumask_test_cpu(cpu, nohz.idle_cpus_mask); + } ++#endif + ++#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING + /* +- * find_busiest_queue - find the busiest runqueue among the cpus in group. ++ * Decide if the tasks on the busy CPUs in the ++ * littlest domain would benefit from an idle balance + */ +-static struct rq *find_busiest_queue(struct lb_env *env, +- struct sched_group *group) ++static int hmp_packing_ilb_needed(int cpu, int ilb_needed) + { +- struct rq *busiest = NULL, *rq; +- unsigned long busiest_load = 0, busiest_capacity = 1; +- int i; +- +- for_each_cpu_and(i, sched_group_cpus(group), env->cpus) { +- unsigned long capacity, capacity_factor, wl; +- enum fbq_type rt; +- +- rq = cpu_rq(i); +- rt = fbq_classify_rq(rq); +- +- /* +- * We classify groups/runqueues into three groups: +- * - regular: there are !numa tasks +- * - remote: there are numa tasks that run on the 'wrong' node +- * - all: there is no distinction +- * +- * In order to avoid migrating ideally placed numa tasks, +- * ignore those when there's better options. +- * +- * If we ignore the actual busiest queue to migrate another +- * task, the next balance pass can still reduce the busiest +- * queue by moving tasks around inside the node. +- * +- * If we cannot move enough load due to this classification +- * the next pass will adjust the group classification and +- * allow migration of more tasks. +- * +- * Both cases only affect the total convergence complexity. +- */ +- if (rt > env->fbq_type) +- continue; +- +- capacity = capacity_of(i); +- capacity_factor = DIV_ROUND_CLOSEST(capacity, SCHED_CAPACITY_SCALE); +- if (!capacity_factor) +- capacity_factor = fix_small_capacity(env->sd, group); ++ struct hmp_domain *hmp; ++ /* allow previous decision on non-slowest domain */ ++ if (!hmp_cpu_is_slowest(cpu)) ++ return ilb_needed; + +- wl = weighted_cpuload(i); ++ /* if disabled, use normal ILB behaviour */ ++ if (!hmp_packing_enabled) ++ return ilb_needed; + +- /* +- * When comparing with imbalance, use weighted_cpuload() +- * which is not scaled with the cpu capacity. +- */ +- if (capacity_factor && rq->nr_running == 1 && wl > env->imbalance) +- continue; ++ hmp = hmp_cpu_domain(cpu); ++ for_each_cpu_and(cpu, &hmp->cpus, nohz.idle_cpus_mask) { ++ /* only idle balance if a CPU is loaded over threshold */ ++ if (cpu_rq(cpu)->avg.load_avg_ratio > hmp_full_threshold) ++ return 1; ++ } ++ return 0; ++} ++#endif ++DEFINE_PER_CPU(cpumask_var_t, ilb_tmpmask); + +- /* +- * For the load comparisons with the other cpu's, consider +- * the weighted_cpuload() scaled with the cpu capacity, so +- * that the load can be moved away from the cpu that is +- * potentially running at a lower capacity. +- * +- * Thus we're looking for max(wl_i / capacity_i), crosswise +- * multiplication to rid ourselves of the division works out +- * to: wl_i * capacity_j > wl_j * capacity_i; where j is +- * our previous maximum. +- */ +- if (wl * busiest_capacity > busiest_load * capacity) { +- busiest_load = wl; +- busiest_capacity = capacity; +- busiest = rq; ++static inline int find_new_ilb(void) ++{ ++ int ilb = cpumask_first(nohz.idle_cpus_mask); ++#ifdef CONFIG_SCHED_HMP ++ int ilb_needed = 0; ++ int call_cpu = smp_processor_id(); ++ int cpu; ++ struct cpumask *tmp = per_cpu(ilb_tmpmask, smp_processor_id()); ++ ++ /* restrict nohz balancing to occur in the same hmp domain */ ++ ilb = cpumask_first_and(nohz.idle_cpus_mask, ++ &((struct hmp_domain *)hmp_cpu_domain(call_cpu))->cpus); ++ ++ /* check to see if it's necessary within this domain */ ++ cpumask_andnot(tmp, ++ &((struct hmp_domain *)hmp_cpu_domain(call_cpu))->cpus, ++ nohz.idle_cpus_mask); ++ for_each_cpu(cpu, tmp) { ++ if (cpu_rq(cpu)->nr_running > 1) { ++ ilb_needed = 1; ++ break; + } + } + +- return busiest; ++#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING ++ if (ilb < nr_cpu_ids) ++ ilb_needed = hmp_packing_ilb_needed(ilb, ilb_needed); ++#endif ++ ++ if (ilb_needed && ilb < nr_cpu_ids && idle_cpu(ilb)) ++ return ilb; ++#else ++ if (ilb < nr_cpu_ids && idle_cpu(ilb)) ++ return ilb; ++#endif ++ return nr_cpu_ids; + } + + /* +- * Max backoff if we encounter pinned tasks. Pretty arbitrary value, but +- * so long as it is large enough. ++ * Kick a CPU to do the nohz balancing, if it is time for it. We pick the ++ * nohz_load_balancer CPU (if there is one) otherwise fallback to any idle ++ * CPU (if there is one). + */ +-#define MAX_PINNED_INTERVAL 512 ++static void nohz_balancer_kick(void) ++{ ++ int ilb_cpu; + +-/* Working cpumask for load_balance and load_balance_newidle. */ +-DEFINE_PER_CPU(cpumask_var_t, load_balance_mask); ++ nohz.next_balance++; + +-static int need_active_balance(struct lb_env *env) +-{ +- struct sched_domain *sd = env->sd; ++ ilb_cpu = find_new_ilb(); + +- if (env->idle == CPU_NEWLY_IDLE) { ++ if (ilb_cpu >= nr_cpu_ids) ++ return; ++ ++ if (test_and_set_bit(NOHZ_BALANCE_KICK, nohz_flags(ilb_cpu))) ++ return; ++ /* ++ * Use smp_send_reschedule() instead of resched_cpu(). ++ * This way we generate a sched IPI on the target cpu which ++ * is idle. And the softirq performing nohz idle load balance ++ * will be run before returning from the IPI. ++ */ ++ smp_send_reschedule(ilb_cpu); ++ return; ++} + ++static inline void nohz_balance_exit_idle(int cpu) ++{ ++ if (unlikely(test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))) { + /* +- * ASYM_PACKING needs to force migrate tasks from busy but +- * higher numbered CPUs in order to pack all tasks in the +- * lowest numbered CPUs. ++ * Completely isolated CPUs don't ever set, so we must test. + */ +- if ((sd->flags & SD_ASYM_PACKING) && env->src_cpu > env->dst_cpu) +- return 1; ++ if (likely(cpumask_test_cpu(cpu, nohz.idle_cpus_mask))) { ++ cpumask_clear_cpu(cpu, nohz.idle_cpus_mask); ++ atomic_dec(&nohz.nr_cpus); ++ } ++ clear_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)); + } +- +- return unlikely(sd->nr_balance_failed > sd->cache_nice_tries+2); + } + +-static int active_load_balance_cpu_stop(void *data); +- +-static int should_we_balance(struct lb_env *env) ++static inline void set_cpu_sd_state_busy(void) + { +- struct sched_group *sg = env->sd->groups; +- struct cpumask *sg_cpus, *sg_mask; +- int cpu, balance_cpu = -1; ++ struct sched_domain *sd; ++ int cpu = smp_processor_id(); + +- /* +- * In the newly idle case, we will allow all the cpu's +- * to do the newly idle load balance. +- */ +- if (env->idle == CPU_NEWLY_IDLE) +- return 1; ++ rcu_read_lock(); ++ sd = rcu_dereference(per_cpu(sd_busy, cpu)); + +- sg_cpus = sched_group_cpus(sg); +- sg_mask = sched_group_mask(sg); +- /* Try to find first idle cpu */ +- for_each_cpu_and(cpu, sg_cpus, env->cpus) { +- if (!cpumask_test_cpu(cpu, sg_mask) || !idle_cpu(cpu)) +- continue; ++ if (!sd || !sd->nohz_idle) ++ goto unlock; ++ sd->nohz_idle = 0; + +- balance_cpu = cpu; +- break; +- } ++ atomic_inc(&sd->groups->sgc->nr_busy_cpus); ++unlock: ++ rcu_read_unlock(); ++} + +- if (balance_cpu == -1) +- balance_cpu = group_balance_cpu(sg); ++void set_cpu_sd_state_idle(void) ++{ ++ struct sched_domain *sd; ++ int cpu = smp_processor_id(); + +- /* +- * First idle cpu or the first cpu(busiest) in this sched group +- * is eligible for doing load balancing at this and above domains. +- */ +- return balance_cpu == env->dst_cpu; ++ rcu_read_lock(); ++ sd = rcu_dereference(per_cpu(sd_busy, cpu)); ++ ++ if (!sd || sd->nohz_idle) ++ goto unlock; ++ sd->nohz_idle = 1; ++ ++ atomic_dec(&sd->groups->sgc->nr_busy_cpus); ++unlock: ++ rcu_read_unlock(); + } + + /* +- * Check this_cpu to ensure it is balanced within domain. Attempt to move +- * tasks if there is an imbalance. ++ * This routine will record that the cpu is going idle with tick stopped. ++ * This info will be used in performing idle load balancing in the future. + */ +-static int load_balance(int this_cpu, struct rq *this_rq, +- struct sched_domain *sd, enum cpu_idle_type idle, +- int *continue_balancing) ++void nohz_balance_enter_idle(int cpu) + { +- int ld_moved, cur_ld_moved, active_balance = 0; +- struct sched_domain *sd_parent = sd->parent; +- struct sched_group *group; +- struct rq *busiest; +- unsigned long flags; +- struct cpumask *cpus = this_cpu_cpumask_var_ptr(load_balance_mask); +- +- struct lb_env env = { +- .sd = sd, +- .dst_cpu = this_cpu, +- .dst_rq = this_rq, +- .dst_grpmask = sched_group_cpus(sd->groups), +- .idle = idle, +- .loop_break = sched_nr_migrate_break, +- .cpus = cpus, +- .fbq_type = all, +- .tasks = LIST_HEAD_INIT(env.tasks), +- }; +- + /* +- * For NEWLY_IDLE load_balancing, we don't need to consider +- * other cpus in our group ++ * If this cpu is going down, then nothing needs to be done. + */ +- if (idle == CPU_NEWLY_IDLE) +- env.dst_grpmask = NULL; +- +- cpumask_copy(cpus, cpu_active_mask); ++ if (!cpu_active(cpu)) ++ return; + +- schedstat_inc(sd, lb_count[idle]); ++ if (test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu))) ++ return; + +-redo: +- if (!should_we_balance(&env)) { +- *continue_balancing = 0; +- goto out_balanced; +- } ++ /* ++ * If we're a completely isolated CPU, we don't play. ++ */ ++ if (on_null_domain(cpu_rq(cpu))) ++ return; + +- group = find_busiest_group(&env); +- if (!group) { +- schedstat_inc(sd, lb_nobusyg[idle]); +- goto out_balanced; +- } ++ cpumask_set_cpu(cpu, nohz.idle_cpus_mask); ++ atomic_inc(&nohz.nr_cpus); ++ set_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)); ++} + +- busiest = find_busiest_queue(&env, group); +- if (!busiest) { +- schedstat_inc(sd, lb_nobusyq[idle]); +- goto out_balanced; ++static int sched_ilb_notifier(struct notifier_block *nfb, ++ unsigned long action, void *hcpu) ++{ ++ switch (action & ~CPU_TASKS_FROZEN) { ++ case CPU_DYING: ++ nohz_balance_exit_idle(smp_processor_id()); ++ return NOTIFY_OK; ++ default: ++ return NOTIFY_DONE; + } ++} ++#endif + +- BUG_ON(busiest == env.dst_rq); +- +- schedstat_add(sd, lb_imbalance[idle], env.imbalance); ++static DEFINE_SPINLOCK(balancing); + +- ld_moved = 0; +- if (busiest->nr_running > 1) { +- /* +- * Attempt to move tasks. If find_busiest_group has found +- * an imbalance but busiest->nr_running <= 1, the group is +- * still unbalanced. ld_moved simply stays zero, so it is +- * correctly treated as an imbalance. +- */ +- env.flags |= LBF_ALL_PINNED; +- env.src_cpu = busiest->cpu; +- env.src_rq = busiest; +- env.loop_max = min(sysctl_sched_nr_migrate, busiest->nr_running); ++/* ++ * Scale the max load_balance interval with the number of CPUs in the system. ++ * This trades load-balance latency on larger machines for less cross talk. ++ */ ++void update_max_interval(void) ++{ ++ max_load_balance_interval = HZ*num_online_cpus()/10; ++} + +-more_balance: +- raw_spin_lock_irqsave(&busiest->lock, flags); ++/* ++ * It checks each scheduling domain to see if it is due to be balanced, ++ * and initiates a balancing operation if so. ++ * ++ * Balancing parameters are set up in init_sched_domains. ++ */ ++static void rebalance_domains(struct rq *rq, enum cpu_idle_type idle) ++{ ++ int continue_balancing = 1; ++ int cpu = rq->cpu; ++ unsigned long interval; ++ struct sched_domain *sd; ++ /* Earliest time when we have to do rebalance again */ ++ unsigned long next_balance = jiffies + 60*HZ; ++ int update_next_balance = 0; ++ int need_serialize, need_decay = 0; ++ u64 max_cost = 0; + +- /* +- * cur_ld_moved - load moved in current iteration +- * ld_moved - cumulative load moved across iterations +- */ +- cur_ld_moved = detach_tasks(&env); ++ update_blocked_averages(cpu); + ++ rcu_read_lock(); ++ for_each_domain(cpu, sd) { + /* +- * We've detached some tasks from busiest_rq. Every +- * task is masked "TASK_ON_RQ_MIGRATING", so we can safely +- * unlock busiest->lock, and we are able to be sure +- * that nobody can manipulate the tasks in parallel. +- * See task_rq_lock() family for the details. ++ * Decay the newidle max times here because this is a regular ++ * visit to all the domains. Decay ~1% per second. + */ +- +- raw_spin_unlock(&busiest->lock); +- +- if (cur_ld_moved) { +- attach_tasks(&env); +- ld_moved += cur_ld_moved; ++ if (time_after(jiffies, sd->next_decay_max_lb_cost)) { ++ sd->max_newidle_lb_cost = ++ (sd->max_newidle_lb_cost * 253) / 256; ++ sd->next_decay_max_lb_cost = jiffies + HZ; ++ need_decay = 1; + } ++ max_cost += sd->max_newidle_lb_cost; + +- local_irq_restore(flags); +- +- if (env.flags & LBF_NEED_BREAK) { +- env.flags &= ~LBF_NEED_BREAK; +- goto more_balance; +- } ++ if (!(sd->flags & SD_LOAD_BALANCE)) ++ continue; + + /* +- * Revisit (affine) tasks on src_cpu that couldn't be moved to +- * us and move them to an alternate dst_cpu in our sched_group +- * where they can run. The upper limit on how many times we +- * iterate on same src_cpu is dependent on number of cpus in our +- * sched_group. +- * +- * This changes load balance semantics a bit on who can move +- * load to a given_cpu. In addition to the given_cpu itself +- * (or a ilb_cpu acting on its behalf where given_cpu is +- * nohz-idle), we now have balance_cpu in a position to move +- * load to given_cpu. In rare situations, this may cause +- * conflicts (balance_cpu and given_cpu/ilb_cpu deciding +- * _independently_ and at _same_ time to move some load to +- * given_cpu) causing exceess load to be moved to given_cpu. +- * This however should not happen so much in practice and +- * moreover subsequent load balance cycles should correct the +- * excess load moved. ++ * Stop the load balance at this level. There is another ++ * CPU in our sched group which is doing load balancing more ++ * actively. + */ +- if ((env.flags & LBF_DST_PINNED) && env.imbalance > 0) { +- +- /* Prevent to re-select dst_cpu via env's cpus */ +- cpumask_clear_cpu(env.dst_cpu, env.cpus); +- +- env.dst_rq = cpu_rq(env.new_dst_cpu); +- env.dst_cpu = env.new_dst_cpu; +- env.flags &= ~LBF_DST_PINNED; +- env.loop = 0; +- env.loop_break = sched_nr_migrate_break; +- +- /* +- * Go back to "more_balance" rather than "redo" since we +- * need to continue with same src_cpu. +- */ +- goto more_balance; ++ if (!continue_balancing) { ++ if (need_decay) ++ continue; ++ break; + } + +- /* +- * We failed to reach balance because of affinity. +- */ +- if (sd_parent) { +- int *group_imbalance = &sd_parent->groups->sgc->imbalance; ++ interval = get_sd_balance_interval(sd, idle != CPU_IDLE); + +- if ((env.flags & LBF_SOME_PINNED) && env.imbalance > 0) +- *group_imbalance = 1; ++ need_serialize = sd->flags & SD_SERIALIZE; ++ if (need_serialize) { ++ if (!spin_trylock(&balancing)) ++ goto out; + } + +- /* All tasks on this runqueue were pinned by CPU affinity */ +- if (unlikely(env.flags & LBF_ALL_PINNED)) { +- cpumask_clear_cpu(cpu_of(busiest), cpus); +- if (!cpumask_empty(cpus)) { +- env.loop = 0; +- env.loop_break = sched_nr_migrate_break; +- goto redo; ++ if (time_after_eq(jiffies, sd->last_balance + interval)) { ++ if (load_balance(cpu, rq, sd, idle, &continue_balancing)) { ++ /* ++ * The LBF_DST_PINNED logic could have changed ++ * env->dst_cpu, so we can't know our idle ++ * state even if we migrated tasks. Update it. ++ */ ++ idle = idle_cpu(cpu) ? CPU_IDLE : CPU_NOT_IDLE; + } +- goto out_all_pinned; ++ sd->last_balance = jiffies; ++ interval = get_sd_balance_interval(sd, idle != CPU_IDLE); ++ } ++ if (need_serialize) ++ spin_unlock(&balancing); ++out: ++ if (time_after(next_balance, sd->last_balance + interval)) { ++ next_balance = sd->last_balance + interval; ++ update_next_balance = 1; + } + } +- +- if (!ld_moved) { +- schedstat_inc(sd, lb_failed[idle]); ++ if (need_decay) { + /* +- * Increment the failure counter only on periodic balance. +- * We do not want newidle balance, which can be very +- * frequent, pollute the failure counter causing +- * excessive cache_hot migrations and active balances. ++ * Ensure the rq-wide value also decays but keep it at a ++ * reasonable floor to avoid funnies with rq->avg_idle. + */ +- if (idle != CPU_NEWLY_IDLE) +- sd->nr_balance_failed++; ++ rq->max_idle_balance_cost = ++ max((u64)sysctl_sched_migration_cost, max_cost); ++ } ++ rcu_read_unlock(); + +- if (need_active_balance(&env)) { +- raw_spin_lock_irqsave(&busiest->lock, flags); ++ /* ++ * next_balance will be updated only when there is a need. ++ * When the cpu is attached to null domain for ex, it will not be ++ * updated. ++ */ ++ if (likely(update_next_balance)) ++ rq->next_balance = next_balance; ++} + +- /* don't kick the active_load_balance_cpu_stop, +- * if the curr task on busiest cpu can't be +- * moved to this_cpu +- */ +- if (!cpumask_test_cpu(this_cpu, +- tsk_cpus_allowed(busiest->curr))) { +- raw_spin_unlock_irqrestore(&busiest->lock, +- flags); +- env.flags |= LBF_ALL_PINNED; +- goto out_one_pinned; +- } ++#ifdef CONFIG_NO_HZ_COMMON ++/* ++ * In CONFIG_NO_HZ_COMMON case, the idle balance kickee will do the ++ * rebalancing for all the cpus for whom scheduler ticks are stopped. ++ */ ++static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle) ++{ ++ int this_cpu = this_rq->cpu; ++ struct rq *rq; ++ int balance_cpu; + +- /* +- * ->active_balance synchronizes accesses to +- * ->active_balance_work. Once set, it's cleared +- * only after active load balance is finished. +- */ +- if (!busiest->active_balance) { +- busiest->active_balance = 1; +- busiest->push_cpu = this_cpu; +- active_balance = 1; +- } +- raw_spin_unlock_irqrestore(&busiest->lock, flags); ++ if (idle != CPU_IDLE || ++ !test_bit(NOHZ_BALANCE_KICK, nohz_flags(this_cpu))) ++ goto end; + +- if (active_balance) { +- stop_one_cpu_nowait(cpu_of(busiest), +- active_load_balance_cpu_stop, busiest, +- &busiest->active_balance_work); +- } ++ for_each_cpu(balance_cpu, nohz.idle_cpus_mask) { ++ if (balance_cpu == this_cpu || !idle_cpu(balance_cpu)) ++ continue; + +- /* +- * We've kicked active balancing, reset the failure +- * counter. +- */ +- sd->nr_balance_failed = sd->cache_nice_tries+1; +- } +- } else +- sd->nr_balance_failed = 0; ++ /* ++ * If this cpu gets work to do, stop the load balancing ++ * work being done for other cpus. Next load ++ * balancing owner will pick it up. ++ */ ++ if (need_resched()) ++ break; ++ ++ rq = cpu_rq(balance_cpu); + +- if (likely(!active_balance)) { +- /* We were unbalanced, so reset the balancing interval */ +- sd->balance_interval = sd->min_interval; +- } else { + /* +- * If we've begun active balancing, start to back off. This +- * case may not be covered by the all_pinned logic if there +- * is only 1 task on the busy runqueue (because we don't call +- * detach_tasks). ++ * If time for next balance is due, ++ * do the balance. + */ +- if (sd->balance_interval < sd->max_interval) +- sd->balance_interval *= 2; ++ if (time_after_eq(jiffies, rq->next_balance)) { ++ raw_spin_lock_irq(&rq->lock); ++ update_rq_clock(rq); ++ update_idle_cpu_load(rq); ++ raw_spin_unlock_irq(&rq->lock); ++ rebalance_domains(rq, CPU_IDLE); ++ } ++ ++ if (time_after(this_rq->next_balance, rq->next_balance)) ++ this_rq->next_balance = rq->next_balance; + } ++ nohz.next_balance = this_rq->next_balance; ++end: ++ clear_bit(NOHZ_BALANCE_KICK, nohz_flags(this_cpu)); ++} + +- goto out; ++/* ++ * Current heuristic for kicking the idle load balancer in the presence ++ * of an idle cpu is the system. ++ * - This rq has more than one task. ++ * - At any scheduler domain level, this cpu's scheduler group has multiple ++ * busy cpu's exceeding the group's capacity. ++ * - For SD_ASYM_PACKING, if the lower numbered cpu's in the scheduler ++ * domain span are idle. ++ */ ++static inline int nohz_kick_needed(struct rq *rq) ++{ ++ unsigned long now = jiffies; ++ struct sched_domain *sd; ++ struct sched_group_capacity *sgc; ++ int nr_busy, cpu = rq->cpu; ++ ++ if (unlikely(rq->idle_balance)) ++ return 0; ++ ++ /* ++ * We may be recently in ticked or tickless idle mode. At the first ++ * busy tick after returning from idle, we will update the busy stats. ++ */ ++ set_cpu_sd_state_busy(); ++ nohz_balance_exit_idle(cpu); + +-out_balanced: + /* +- * We reach balance although we may have faced some affinity +- * constraints. Clear the imbalance flag if it was set. ++ * None are in tickless mode and hence no need for NOHZ idle load ++ * balancing. + */ +- if (sd_parent) { +- int *group_imbalance = &sd_parent->groups->sgc->imbalance; ++ if (likely(!atomic_read(&nohz.nr_cpus))) ++ return 0; + +- if (*group_imbalance) +- *group_imbalance = 0; +- } ++ if (time_before(now, nohz.next_balance)) ++ return 0; + +-out_all_pinned: ++#ifdef CONFIG_SCHED_HMP + /* +- * We reach balance because all tasks are pinned at this level so +- * we can't migrate them. Let the imbalance flag set so parent level +- * can try to migrate them. ++ * Bail out if there are no nohz CPUs in our ++ * HMP domain, since we will move tasks between ++ * domains through wakeup and force balancing ++ * as necessary based upon task load. + */ +- schedstat_inc(sd, lb_balanced[idle]); ++ if (cpumask_first_and(nohz.idle_cpus_mask, ++ &((struct hmp_domain *)hmp_cpu_domain(cpu))->cpus) >= nr_cpu_ids) ++ return 0; ++#endif + +- sd->nr_balance_failed = 0; ++ if (rq->nr_running >= 2) ++ goto need_kick; + +-out_one_pinned: +- /* tune up the balancing interval */ +- if (((env.flags & LBF_ALL_PINNED) && +- sd->balance_interval < MAX_PINNED_INTERVAL) || +- (sd->balance_interval < sd->max_interval)) +- sd->balance_interval *= 2; ++ rcu_read_lock(); ++ sd = rcu_dereference(per_cpu(sd_busy, cpu)); + +- ld_moved = 0; +-out: +- return ld_moved; ++ if (sd) { ++ sgc = sd->groups->sgc; ++ nr_busy = atomic_read(&sgc->nr_busy_cpus); ++ ++ if (nr_busy > 1) ++ goto need_kick_unlock; ++ } ++ ++ sd = rcu_dereference(per_cpu(sd_asym, cpu)); ++ ++ if (sd && (cpumask_first_and(nohz.idle_cpus_mask, ++ sched_domain_span(sd)) < cpu)) ++ goto need_kick_unlock; ++ ++ rcu_read_unlock(); ++ return 0; ++ ++need_kick_unlock: ++ rcu_read_unlock(); ++need_kick: ++ return 1; + } ++#else ++static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle) { } ++#endif + +-static inline unsigned long +-get_sd_balance_interval(struct sched_domain *sd, int cpu_busy) ++#ifdef CONFIG_SCHED_HMP ++static unsigned int hmp_task_eligible_for_up_migration(struct sched_entity *se) + { +- unsigned long interval = sd->balance_interval; ++ /* below hmp_up_threshold, never eligible */ ++ if (se->avg.load_avg_ratio < hmp_up_threshold) ++ return 0; ++ return 1; ++} + +- if (cpu_busy) +- interval *= sd->busy_factor; ++/* Check if task should migrate to a faster cpu */ ++static unsigned int hmp_up_migration(int cpu, ++ int *target_cpu, struct sched_entity *se) ++{ ++ struct task_struct *p = task_of(se); ++ int temp_target_cpu; ++ u64 now; + +- /* scale ms to jiffies */ +- interval = msecs_to_jiffies(interval); +- interval = clamp(interval, 1UL, max_load_balance_interval); ++ if (hmp_cpu_is_fastest(cpu)) ++ return 0; + +- return interval; ++#ifdef CONFIG_SCHED_HMP_PRIO_FILTER ++ /* Filter by task priority */ ++ if (p->prio >= hmp_up_prio) ++ return 0; ++#endif ++ if (!hmp_task_eligible_for_up_migration(se)) ++ return 0; ++ ++ /* Let the task load settle before doing another up migration */ ++ /* hack - always use clock from first online CPU */ ++ now = cpu_rq(cpumask_first(cpu_online_mask))->clock_task; ++ if (((now - se->avg.hmp_last_up_migration) >> 10) ++ < hmp_next_up_threshold) ++ return 0; ++ ++ /* hmp_domain_min_load only returns 0 for an ++ * idle CPU or 1023 for any partly-busy one. ++ * Be explicit about requirement for an idle CPU. ++ */ ++ if (hmp_domain_min_load(hmp_faster_domain(cpu), &temp_target_cpu, ++ tsk_cpus_allowed(p)) == 0 ++ && temp_target_cpu != NR_CPUS) { ++ if (target_cpu) ++ *target_cpu = temp_target_cpu; ++ return 1; ++ } ++ return 0; + } + +-static inline void +-update_next_balance(struct sched_domain *sd, int cpu_busy, unsigned long *next_balance) ++/* Check if task should migrate to a slower cpu */ ++static unsigned int hmp_down_migration(int cpu, struct sched_entity *se) + { +- unsigned long interval, next; ++ struct task_struct *p = task_of(se); ++ u64 now; + +- interval = get_sd_balance_interval(sd, cpu_busy); +- next = sd->last_balance + interval; ++ if (hmp_cpu_is_slowest(cpu)) { ++#ifdef CONFIG_SCHED_HMP_LITTLE_PACKING ++ if (hmp_packing_enabled) ++ return 1; ++ else ++#endif ++ return 0; ++ } + +- if (time_after(*next_balance, next)) +- *next_balance = next; ++#ifdef CONFIG_SCHED_HMP_PRIO_FILTER ++ /* Filter by task priority */ ++ if ((p->prio >= hmp_up_prio) && ++ cpumask_intersects(&hmp_slower_domain(cpu)->cpus, ++ tsk_cpus_allowed(p))) { ++ return 1; ++ } ++#endif ++ ++ /* Let the task load settle before doing another down migration */ ++ /* hack - always use clock from first online CPU */ ++ now = cpu_rq(cpumask_first(cpu_online_mask))->clock_task; ++ if (((now - se->avg.hmp_last_down_migration) >> 10) ++ < hmp_next_down_threshold) ++ return 0; ++ ++ if (cpumask_intersects(&hmp_slower_domain(cpu)->cpus, ++ tsk_cpus_allowed(p)) ++ && se->avg.load_avg_ratio < hmp_down_threshold) { ++ return 1; ++ } ++ return 0; + } + + /* +- * idle_balance is called by schedule() if this_cpu is about to become +- * idle. Attempts to pull tasks from other CPUs. ++ * hmp_can_migrate_task - may task p from runqueue rq be migrated to this_cpu? ++ * Ideally this function should be merged with can_migrate_task() to avoid ++ * redundant code. + */ +-static int idle_balance(struct rq *this_rq) ++static int hmp_can_migrate_task(struct task_struct *p, struct lb_env *env) + { +- unsigned long next_balance = jiffies + HZ; +- int this_cpu = this_rq->cpu; +- struct sched_domain *sd; +- int pulled_task = 0; +- u64 curr_cost = 0; +- +- idle_enter_fair(this_rq); ++ int tsk_cache_hot = 0; + + /* +- * We must set idle_stamp _before_ calling idle_balance(), such that we +- * measure the duration of idle_balance() as idle time. ++ * We do not migrate tasks that are: ++ * 1) running (obviously), or ++ * 2) cannot be migrated to this CPU due to cpus_allowed + */ +- this_rq->idle_stamp = rq_clock(this_rq); +- +- if (this_rq->avg_idle < sysctl_sched_migration_cost || +- !this_rq->rd->overload) { +- rcu_read_lock(); +- sd = rcu_dereference_check_sched_domain(this_rq->sd); +- if (sd) +- update_next_balance(sd, 0, &next_balance); +- rcu_read_unlock(); ++ if (!cpumask_test_cpu(env->dst_cpu, tsk_cpus_allowed(p))) { ++ schedstat_inc(p, se.statistics.nr_failed_migrations_affine); ++ return 0; ++ } ++ env->flags &= ~LBF_ALL_PINNED; + +- goto out; ++ if (task_running(env->src_rq, p)) { ++ schedstat_inc(p, se.statistics.nr_failed_migrations_running); ++ return 0; + } + + /* +- * Drop the rq->lock, but keep IRQ/preempt disabled. ++ * Aggressive migration if: ++ * 1) task is cache cold, or ++ * 2) too many balance attempts have failed. + */ +- raw_spin_unlock(&this_rq->lock); +- +- update_blocked_averages(this_cpu); +- rcu_read_lock(); +- for_each_domain(this_cpu, sd) { +- int continue_balancing = 1; +- u64 t0, domain_cost; +- +- if (!(sd->flags & SD_LOAD_BALANCE)) +- continue; +- +- if (this_rq->avg_idle < curr_cost + sd->max_newidle_lb_cost) { +- update_next_balance(sd, 0, &next_balance); +- break; +- } +- +- if (sd->flags & SD_BALANCE_NEWIDLE) { +- t0 = sched_clock_cpu(this_cpu); +- +- pulled_task = load_balance(this_cpu, this_rq, +- sd, CPU_NEWLY_IDLE, +- &continue_balancing); +- +- domain_cost = sched_clock_cpu(this_cpu) - t0; +- if (domain_cost > sd->max_newidle_lb_cost) +- sd->max_newidle_lb_cost = domain_cost; + +- curr_cost += domain_cost; ++ tsk_cache_hot = task_hot(p, env); ++ if (!tsk_cache_hot || ++ env->sd->nr_balance_failed > env->sd->cache_nice_tries) { ++#ifdef CONFIG_SCHEDSTATS ++ if (tsk_cache_hot) { ++ schedstat_inc(env->sd, lb_hot_gained[env->idle]); ++ schedstat_inc(p, se.statistics.nr_forced_migrations); + } +- +- update_next_balance(sd, 0, &next_balance); +- +- /* +- * Stop searching for tasks to pull if there are +- * now runnable tasks on this rq. +- */ +- if (pulled_task || this_rq->nr_running > 0) +- break; ++#endif ++ return 1; + } +- rcu_read_unlock(); +- +- raw_spin_lock(&this_rq->lock); + +- if (curr_cost > this_rq->max_idle_balance_cost) +- this_rq->max_idle_balance_cost = curr_cost; ++ return 1; ++} + +- /* +- * While browsing the domains, we released the rq lock, a task could +- * have been enqueued in the meantime. Since we're not going idle, +- * pretend we pulled a task. +- */ +- if (this_rq->cfs.h_nr_running && !pulled_task) +- pulled_task = 1; ++/* ++ * move_task - move a task from one runqueue to another runqueue. ++ * Both runqueues must be locked. ++ */ ++static void move_task(struct task_struct *p, struct lb_env *env) ++{ ++ deactivate_task(env->src_rq, p, 0); ++ set_task_cpu(p, env->dst_cpu); ++ activate_task(env->dst_rq, p, 0); ++ check_preempt_curr(env->dst_rq, p, 0); ++} + +-out: +- /* Move the next balance forward */ +- if (time_after(this_rq->next_balance, next_balance)) +- this_rq->next_balance = next_balance; ++/* ++ * move_specific_task tries to move a specific task. ++ * Returns 1 if successful and 0 otherwise. ++ * Called with both runqueues locked. ++ */ ++static int move_specific_task(struct lb_env *env, struct task_struct *pm) ++{ ++ struct task_struct *p, *n; + +- /* Is there a task of a high priority class? */ +- if (this_rq->nr_running != this_rq->cfs.h_nr_running) +- pulled_task = -1; ++ list_for_each_entry_safe(p, n, &env->src_rq->cfs_tasks, se.group_node) { ++ if (throttled_lb_pair(task_group(p), env->src_rq->cpu, ++ env->dst_cpu)) ++ continue; + +- if (pulled_task) { +- idle_exit_fair(this_rq); +- this_rq->idle_stamp = 0; +- } ++ if (!hmp_can_migrate_task(p, env)) ++ continue; ++ /* Check if we found the right task */ ++ if (p != pm) ++ continue; + +- return pulled_task; ++ move_task(p, env); ++ /* ++ * Right now, this is only the third place move_task() ++ * is called, so we can safely collect move_task() ++ * stats here rather than inside move_task(). ++ */ ++ schedstat_inc(env->sd, lb_gained[env->idle]); ++ return 1; ++ } ++ return 0; + } + + /* +- * active_load_balance_cpu_stop is run by cpu stopper. It pushes +- * running tasks off the busiest CPU onto idle CPUs. It requires at +- * least 1 task to be running on each physical CPU where possible, and +- * avoids physical / logical imbalances. ++ * hmp_active_task_migration_cpu_stop is run by cpu stopper and used to ++ * migrate a specific task from one runqueue to another. ++ * hmp_force_up_migration uses this to push a currently running task ++ * off a runqueue. ++ * Based on active_load_balance_stop_cpu and can potentially be merged. + */ +-static int active_load_balance_cpu_stop(void *data) ++static int hmp_active_task_migration_cpu_stop(void *data) + { + struct rq *busiest_rq = data; ++ struct task_struct *p = busiest_rq->migrate_task; + int busiest_cpu = cpu_of(busiest_rq); + int target_cpu = busiest_rq->push_cpu; + struct rq *target_rq = cpu_rq(target_cpu); + struct sched_domain *sd; +- struct task_struct *p = NULL; + + raw_spin_lock_irq(&busiest_rq->lock); +- + /* make sure the requested cpu hasn't gone down in the meantime */ + if (unlikely(busiest_cpu != smp_processor_id() || +- !busiest_rq->active_balance)) ++ !busiest_rq->active_balance)) { + goto out_unlock; +- ++ } + /* Is there any task to move? */ + if (busiest_rq->nr_running <= 1) + goto out_unlock; +- ++ /* Task has migrated meanwhile, abort forced migration */ ++ if (task_rq(p) != busiest_rq) ++ goto out_unlock; + /* + * This condition is "impossible", if it occurs + * we need to fix it. Originally reported by +@@ -7058,12 +8892,14 @@ static int active_load_balance_cpu_stop(void *data) + */ + BUG_ON(busiest_rq == target_rq); + ++ /* move a task from busiest_rq to target_rq */ ++ double_lock_balance(busiest_rq, target_rq); ++ + /* Search for an sd spanning us and the target CPU. */ + rcu_read_lock(); + for_each_domain(target_cpu, sd) { +- if ((sd->flags & SD_LOAD_BALANCE) && +- cpumask_test_cpu(busiest_cpu, sched_domain_span(sd))) +- break; ++ if (cpumask_test_cpu(busiest_cpu, sched_domain_span(sd))) ++ break; + } + + if (likely(sd)) { +@@ -7078,390 +8914,354 @@ static int active_load_balance_cpu_stop(void *data) + + schedstat_inc(sd, alb_count); + +- p = detach_one_task(&env); +- if (p) ++ if (move_specific_task(&env, p)) + schedstat_inc(sd, alb_pushed); + else + schedstat_inc(sd, alb_failed); + } + rcu_read_unlock(); ++ double_unlock_balance(busiest_rq, target_rq); + out_unlock: ++ put_task_struct(p); + busiest_rq->active_balance = 0; +- raw_spin_unlock(&busiest_rq->lock); +- +- if (p) +- attach_one_task(target_rq, p); +- +- local_irq_enable(); +- ++ raw_spin_unlock_irq(&busiest_rq->lock); + return 0; + } + +-static inline int on_null_domain(struct rq *rq) +-{ +- return unlikely(!rcu_dereference_sched(rq->sd)); +-} +- +-#ifdef CONFIG_NO_HZ_COMMON +-/* +- * idle load balancing details +- * - When one of the busy CPUs notice that there may be an idle rebalancing +- * needed, they will kick the idle load balancer, which then does idle +- * load balancing for all the idle CPUs. +- */ +-static struct { +- cpumask_var_t idle_cpus_mask; +- atomic_t nr_cpus; +- unsigned long next_balance; /* in jiffy units */ +-} nohz ____cacheline_aligned; +- +-static inline int find_new_ilb(void) +-{ +- int ilb = cpumask_first(nohz.idle_cpus_mask); +- +- if (ilb < nr_cpu_ids && idle_cpu(ilb)) +- return ilb; +- +- return nr_cpu_ids; +-} +- + /* +- * Kick a CPU to do the nohz balancing, if it is time for it. We pick the +- * nohz_load_balancer CPU (if there is one) otherwise fallback to any idle +- * CPU (if there is one). ++ * hmp_idle_pull_cpu_stop is run by cpu stopper and used to ++ * migrate a specific task from one runqueue to another. ++ * hmp_idle_pull uses this to push a currently running task ++ * off a runqueue to a faster CPU. ++ * Locking is slightly different than usual. ++ * Based on active_load_balance_stop_cpu and can potentially be merged. + */ +-static void nohz_balancer_kick(void) +-{ +- int ilb_cpu; +- +- nohz.next_balance++; +- +- ilb_cpu = find_new_ilb(); +- +- if (ilb_cpu >= nr_cpu_ids) +- return; +- +- if (test_and_set_bit(NOHZ_BALANCE_KICK, nohz_flags(ilb_cpu))) +- return; +- /* +- * Use smp_send_reschedule() instead of resched_cpu(). +- * This way we generate a sched IPI on the target cpu which +- * is idle. And the softirq performing nohz idle load balance +- * will be run before returning from the IPI. +- */ +- smp_send_reschedule(ilb_cpu); +- return; +-} +- +-static inline void nohz_balance_exit_idle(int cpu) +-{ +- if (unlikely(test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))) { +- /* +- * Completely isolated CPUs don't ever set, so we must test. +- */ +- if (likely(cpumask_test_cpu(cpu, nohz.idle_cpus_mask))) { +- cpumask_clear_cpu(cpu, nohz.idle_cpus_mask); +- atomic_dec(&nohz.nr_cpus); +- } +- clear_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)); +- } +-} +- +-static inline void set_cpu_sd_state_busy(void) +-{ +- struct sched_domain *sd; +- int cpu = smp_processor_id(); +- +- rcu_read_lock(); +- sd = rcu_dereference(per_cpu(sd_busy, cpu)); +- +- if (!sd || !sd->nohz_idle) +- goto unlock; +- sd->nohz_idle = 0; +- +- atomic_inc(&sd->groups->sgc->nr_busy_cpus); +-unlock: +- rcu_read_unlock(); +-} +- +-void set_cpu_sd_state_idle(void) ++static int hmp_idle_pull_cpu_stop(void *data) + { ++ struct rq *busiest_rq = data; ++ struct task_struct *p = busiest_rq->migrate_task; ++ int busiest_cpu = cpu_of(busiest_rq); ++ int target_cpu = busiest_rq->push_cpu; ++ struct rq *target_rq = cpu_rq(target_cpu); + struct sched_domain *sd; +- int cpu = smp_processor_id(); +- +- rcu_read_lock(); +- sd = rcu_dereference(per_cpu(sd_busy, cpu)); + +- if (!sd || sd->nohz_idle) +- goto unlock; +- sd->nohz_idle = 1; ++ raw_spin_lock_irq(&busiest_rq->lock); + +- atomic_dec(&sd->groups->sgc->nr_busy_cpus); +-unlock: +- rcu_read_unlock(); +-} ++ /* make sure the requested cpu hasn't gone down in the meantime */ ++ if (unlikely(busiest_cpu != smp_processor_id() || ++ !busiest_rq->active_balance)) ++ goto out_unlock; + +-/* +- * This routine will record that the cpu is going idle with tick stopped. +- * This info will be used in performing idle load balancing in the future. +- */ +-void nohz_balance_enter_idle(int cpu) +-{ +- /* +- * If this cpu is going down, then nothing needs to be done. +- */ +- if (!cpu_active(cpu)) +- return; ++ /* Is there any task to move? */ ++ if (busiest_rq->nr_running <= 1) ++ goto out_unlock; + +- if (test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu))) +- return; ++ /* Task has migrated meanwhile, abort forced migration */ ++ if (task_rq(p) != busiest_rq) ++ goto out_unlock; + + /* +- * If we're a completely isolated CPU, we don't play. ++ * This condition is "impossible", if it occurs ++ * we need to fix it. Originally reported by ++ * Bjorn Helgaas on a 128-cpu setup. + */ +- if (on_null_domain(cpu_rq(cpu))) +- return; ++ BUG_ON(busiest_rq == target_rq); + +- cpumask_set_cpu(cpu, nohz.idle_cpus_mask); +- atomic_inc(&nohz.nr_cpus); +- set_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)); +-} ++ /* move a task from busiest_rq to target_rq */ ++ double_lock_balance(busiest_rq, target_rq); + +-static int sched_ilb_notifier(struct notifier_block *nfb, +- unsigned long action, void *hcpu) +-{ +- switch (action & ~CPU_TASKS_FROZEN) { +- case CPU_DYING: +- nohz_balance_exit_idle(smp_processor_id()); +- return NOTIFY_OK; +- default: +- return NOTIFY_DONE; ++ /* Search for an sd spanning us and the target CPU. */ ++ rcu_read_lock(); ++ for_each_domain(target_cpu, sd) { ++ if (cpumask_test_cpu(busiest_cpu, sched_domain_span(sd))) ++ break; + } +-} +-#endif ++ if (likely(sd)) { ++ struct lb_env env = { ++ .sd = sd, ++ .dst_cpu = target_cpu, ++ .dst_rq = target_rq, ++ .src_cpu = busiest_rq->cpu, ++ .src_rq = busiest_rq, ++ .idle = CPU_IDLE, ++ }; + +-static DEFINE_SPINLOCK(balancing); ++ schedstat_inc(sd, alb_count); + +-/* +- * Scale the max load_balance interval with the number of CPUs in the system. +- * This trades load-balance latency on larger machines for less cross talk. +- */ +-void update_max_interval(void) +-{ +- max_load_balance_interval = HZ*num_online_cpus()/10; ++ if (move_specific_task(&env, p)) ++ schedstat_inc(sd, alb_pushed); ++ else ++ schedstat_inc(sd, alb_failed); ++ } ++ rcu_read_unlock(); ++ double_unlock_balance(busiest_rq, target_rq); ++out_unlock: ++ put_task_struct(p); ++ busiest_rq->active_balance = 0; ++ raw_spin_unlock_irq(&busiest_rq->lock); ++ return 0; + } + + /* +- * It checks each scheduling domain to see if it is due to be balanced, +- * and initiates a balancing operation if so. ++ * Move task in a runnable state to another CPU. + * +- * Balancing parameters are set up in init_sched_domains. ++ * Tailored on 'active_load_balance_stop_cpu' with slight ++ * modification to locking and pre-transfer checks. Note ++ * rq->lock must be held before calling. + */ +-static void rebalance_domains(struct rq *rq, enum cpu_idle_type idle) ++static void hmp_migrate_runnable_task(struct rq *rq) + { +- int continue_balancing = 1; +- int cpu = rq->cpu; +- unsigned long interval; + struct sched_domain *sd; +- /* Earliest time when we have to do rebalance again */ +- unsigned long next_balance = jiffies + 60*HZ; +- int update_next_balance = 0; +- int need_serialize, need_decay = 0; +- u64 max_cost = 0; +- +- update_blocked_averages(cpu); +- +- rcu_read_lock(); +- for_each_domain(cpu, sd) { +- /* +- * Decay the newidle max times here because this is a regular +- * visit to all the domains. Decay ~1% per second. +- */ +- if (time_after(jiffies, sd->next_decay_max_lb_cost)) { +- sd->max_newidle_lb_cost = +- (sd->max_newidle_lb_cost * 253) / 256; +- sd->next_decay_max_lb_cost = jiffies + HZ; +- need_decay = 1; +- } +- max_cost += sd->max_newidle_lb_cost; ++ int src_cpu = cpu_of(rq); ++ struct rq *src_rq = rq; ++ int dst_cpu = rq->push_cpu; ++ struct rq *dst_rq = cpu_rq(dst_cpu); ++ struct task_struct *p = rq->migrate_task; ++ /* ++ * One last check to make sure nobody else is playing ++ * with the source rq. ++ */ ++ if (src_rq->active_balance) ++ goto out; + +- if (!(sd->flags & SD_LOAD_BALANCE)) +- continue; ++ if (src_rq->nr_running <= 1) ++ goto out; + +- /* +- * Stop the load balance at this level. There is another +- * CPU in our sched group which is doing load balancing more +- * actively. +- */ +- if (!continue_balancing) { +- if (need_decay) +- continue; ++ if (task_rq(p) != src_rq) ++ goto out; ++ /* ++ * Not sure if this applies here but one can never ++ * be too cautious ++ */ ++ BUG_ON(src_rq == dst_rq); ++ ++ double_lock_balance(src_rq, dst_rq); ++ ++ rcu_read_lock(); ++ for_each_domain(dst_cpu, sd) { ++ if (cpumask_test_cpu(src_cpu, sched_domain_span(sd))) + break; +- } ++ } + +- interval = get_sd_balance_interval(sd, idle != CPU_IDLE); ++ if (likely(sd)) { ++ struct lb_env env = { ++ .sd = sd, ++ .dst_cpu = dst_cpu, ++ .dst_rq = dst_rq, ++ .src_cpu = src_cpu, ++ .src_rq = src_rq, ++ .idle = CPU_IDLE, ++ }; + +- need_serialize = sd->flags & SD_SERIALIZE; +- if (need_serialize) { +- if (!spin_trylock(&balancing)) +- goto out; +- } ++ schedstat_inc(sd, alb_count); + +- if (time_after_eq(jiffies, sd->last_balance + interval)) { +- if (load_balance(cpu, rq, sd, idle, &continue_balancing)) { +- /* +- * The LBF_DST_PINNED logic could have changed +- * env->dst_cpu, so we can't know our idle +- * state even if we migrated tasks. Update it. +- */ +- idle = idle_cpu(cpu) ? CPU_IDLE : CPU_NOT_IDLE; +- } +- sd->last_balance = jiffies; +- interval = get_sd_balance_interval(sd, idle != CPU_IDLE); +- } +- if (need_serialize) +- spin_unlock(&balancing); +-out: +- if (time_after(next_balance, sd->last_balance + interval)) { +- next_balance = sd->last_balance + interval; +- update_next_balance = 1; +- } +- } +- if (need_decay) { +- /* +- * Ensure the rq-wide value also decays but keep it at a +- * reasonable floor to avoid funnies with rq->avg_idle. +- */ +- rq->max_idle_balance_cost = +- max((u64)sysctl_sched_migration_cost, max_cost); ++ if (move_specific_task(&env, p)) ++ schedstat_inc(sd, alb_pushed); ++ else ++ schedstat_inc(sd, alb_failed); + } +- rcu_read_unlock(); + +- /* +- * next_balance will be updated only when there is a need. +- * When the cpu is attached to null domain for ex, it will not be +- * updated. +- */ +- if (likely(update_next_balance)) +- rq->next_balance = next_balance; ++ rcu_read_unlock(); ++ double_unlock_balance(src_rq, dst_rq); ++out: ++ put_task_struct(p); + } + +-#ifdef CONFIG_NO_HZ_COMMON ++static DEFINE_SPINLOCK(hmp_force_migration); ++ + /* +- * In CONFIG_NO_HZ_COMMON case, the idle balance kickee will do the +- * rebalancing for all the cpus for whom scheduler ticks are stopped. ++ * hmp_force_up_migration checks runqueues for tasks that need to ++ * be actively migrated to a faster cpu. + */ +-static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle) ++static void hmp_force_up_migration(int this_cpu) + { +- int this_cpu = this_rq->cpu; +- struct rq *rq; +- int balance_cpu; +- +- if (idle != CPU_IDLE || +- !test_bit(NOHZ_BALANCE_KICK, nohz_flags(this_cpu))) +- goto end; ++ int cpu, target_cpu; ++ struct sched_entity *curr, *orig; ++ struct rq *target; ++ unsigned long flags; ++ unsigned int force, got_target; ++ struct task_struct *p; + +- for_each_cpu(balance_cpu, nohz.idle_cpus_mask) { +- if (balance_cpu == this_cpu || !idle_cpu(balance_cpu)) ++ if (!spin_trylock(&hmp_force_migration)) ++ return; ++ for_each_online_cpu(cpu) { ++ force = 0; ++ got_target = 0; ++ target = cpu_rq(cpu); ++ raw_spin_lock_irqsave(&target->lock, flags); ++ curr = target->cfs.curr; ++ if (!curr || target->active_balance) { ++ raw_spin_unlock_irqrestore(&target->lock, flags); + continue; ++ } ++ if (!entity_is_task(curr)) { ++ struct cfs_rq *cfs_rq; + ++ cfs_rq = group_cfs_rq(curr); ++ while (cfs_rq) { ++ curr = cfs_rq->curr; ++ cfs_rq = group_cfs_rq(curr); ++ } ++ } ++ orig = curr; ++ curr = hmp_get_heaviest_task(curr, -1); ++ if (!curr) { ++ raw_spin_unlock_irqrestore(&target->lock, flags); ++ continue; ++ } ++ p = task_of(curr); ++ if (hmp_up_migration(cpu, &target_cpu, curr)) { ++ cpu_rq(target_cpu)->wake_for_idle_pull = 1; ++ raw_spin_unlock_irqrestore(&target->lock, flags); ++ spin_unlock(&hmp_force_migration); ++ smp_send_reschedule(target_cpu); ++ return; ++ } ++ if (!got_target) { ++ /* ++ * For now we just check the currently running task. ++ * Selecting the lightest task for offloading will ++ * require extensive book keeping. ++ */ ++ curr = hmp_get_lightest_task(orig, 1); ++ p = task_of(curr); ++ target->push_cpu = hmp_offload_down(cpu, curr); ++ if (target->push_cpu < NR_CPUS) { ++ get_task_struct(p); ++ target->migrate_task = p; ++ got_target = 1; ++ trace_sched_hmp_migrate(p, target->push_cpu, ++ HMP_MIGRATE_OFFLOAD); ++ hmp_next_down_delay(&p->se, target->push_cpu); ++ } ++ } + /* +- * If this cpu gets work to do, stop the load balancing +- * work being done for other cpus. Next load +- * balancing owner will pick it up. +- */ +- if (need_resched()) +- break; +- +- rq = cpu_rq(balance_cpu); +- +- /* +- * If time for next balance is due, +- * do the balance. ++ * We have a target with no active_balance. If the task ++ * is not currently running move it, otherwise let the ++ * CPU stopper take care of it. + */ +- if (time_after_eq(jiffies, rq->next_balance)) { +- raw_spin_lock_irq(&rq->lock); +- update_rq_clock(rq); +- update_idle_cpu_load(rq); +- raw_spin_unlock_irq(&rq->lock); +- rebalance_domains(rq, CPU_IDLE); ++ if (got_target) { ++ if (!task_running(target, p)) { ++ trace_sched_hmp_migrate_force_running(p, 0); ++ hmp_migrate_runnable_task(target); ++ } else { ++ target->active_balance = 1; ++ force = 1; ++ } + } + +- if (time_after(this_rq->next_balance, rq->next_balance)) +- this_rq->next_balance = rq->next_balance; ++ raw_spin_unlock_irqrestore(&target->lock, flags); ++ ++ if (force) ++ stop_one_cpu_nowait(cpu_of(target), ++ hmp_active_task_migration_cpu_stop, ++ target, &target->active_balance_work); + } +- nohz.next_balance = this_rq->next_balance; +-end: +- clear_bit(NOHZ_BALANCE_KICK, nohz_flags(this_cpu)); ++ spin_unlock(&hmp_force_migration); + } +- + /* +- * Current heuristic for kicking the idle load balancer in the presence +- * of an idle cpu is the system. +- * - This rq has more than one task. +- * - At any scheduler domain level, this cpu's scheduler group has multiple +- * busy cpu's exceeding the group's capacity. +- * - For SD_ASYM_PACKING, if the lower numbered cpu's in the scheduler +- * domain span are idle. ++ * hmp_idle_pull looks at little domain runqueues to see ++ * if a task should be pulled. ++ * ++ * Reuses hmp_force_migration spinlock. ++ * + */ +-static inline int nohz_kick_needed(struct rq *rq) ++static unsigned int hmp_idle_pull(int this_cpu) + { +- unsigned long now = jiffies; +- struct sched_domain *sd; +- struct sched_group_capacity *sgc; +- int nr_busy, cpu = rq->cpu; +- +- if (unlikely(rq->idle_balance)) +- return 0; +- +- /* +- * We may be recently in ticked or tickless idle mode. At the first +- * busy tick after returning from idle, we will update the busy stats. +- */ +- set_cpu_sd_state_busy(); +- nohz_balance_exit_idle(cpu); ++ int cpu; ++ struct sched_entity *curr, *orig; ++ struct hmp_domain *hmp_domain = NULL; ++ struct rq *target = NULL, *rq; ++ unsigned long flags, ratio = 0; ++ unsigned int force = 0; ++ struct task_struct *p = NULL; + +- /* +- * None are in tickless mode and hence no need for NOHZ idle load +- * balancing. +- */ +- if (likely(!atomic_read(&nohz.nr_cpus))) ++ if (!hmp_cpu_is_slowest(this_cpu)) ++ hmp_domain = hmp_slower_domain(this_cpu); ++ if (!hmp_domain) + return 0; + +- if (time_before(now, nohz.next_balance)) ++ if (!spin_trylock(&hmp_force_migration)) + return 0; + +- if (rq->nr_running >= 2) +- goto need_kick; +- +- rcu_read_lock(); +- sd = rcu_dereference(per_cpu(sd_busy, cpu)); +- +- if (sd) { +- sgc = sd->groups->sgc; +- nr_busy = atomic_read(&sgc->nr_busy_cpus); +- +- if (nr_busy > 1) +- goto need_kick_unlock; ++ /* first select a task */ ++ for_each_cpu(cpu, &hmp_domain->cpus) { ++ rq = cpu_rq(cpu); ++ raw_spin_lock_irqsave(&rq->lock, flags); ++ curr = rq->cfs.curr; ++ if (!curr) { ++ raw_spin_unlock_irqrestore(&rq->lock, flags); ++ continue; ++ } ++ if (!entity_is_task(curr)) { ++ struct cfs_rq *cfs_rq; ++ ++ cfs_rq = group_cfs_rq(curr); ++ while (cfs_rq) { ++ curr = cfs_rq->curr; ++ if (!entity_is_task(curr)) ++ cfs_rq = group_cfs_rq(curr); ++ else ++ cfs_rq = NULL; ++ } ++ } ++ orig = curr; ++ curr = hmp_get_heaviest_task(curr, this_cpu); ++ /* check if heaviest eligible task on this ++ * CPU is heavier than previous task ++ */ ++ if (curr && hmp_task_eligible_for_up_migration(curr) && ++ curr->avg.load_avg_ratio > ratio && ++ cpumask_test_cpu(this_cpu, ++ tsk_cpus_allowed(task_of(curr)))) { ++ p = task_of(curr); ++ target = rq; ++ ratio = curr->avg.load_avg_ratio; ++ } ++ raw_spin_unlock_irqrestore(&rq->lock, flags); + } + +- sd = rcu_dereference(per_cpu(sd_asym, cpu)); +- +- if (sd && (cpumask_first_and(nohz.idle_cpus_mask, +- sched_domain_span(sd)) < cpu)) +- goto need_kick_unlock; ++ if (!p) ++ goto done; + +- rcu_read_unlock(); +- return 0; ++ /* now we have a candidate */ ++ raw_spin_lock_irqsave(&target->lock, flags); ++ if (!target->active_balance && task_rq(p) == target) { ++ get_task_struct(p); ++ target->push_cpu = this_cpu; ++ target->migrate_task = p; ++ trace_sched_hmp_migrate(p, target->push_cpu, ++ HMP_MIGRATE_IDLE_PULL); ++ hmp_next_up_delay(&p->se, target->push_cpu); ++ /* ++ * if the task isn't running move it right away. ++ * Otherwise setup the active_balance mechanic and let ++ * the CPU stopper do its job. ++ */ ++ if (!task_running(target, p)) { ++ trace_sched_hmp_migrate_idle_running(p, 0); ++ hmp_migrate_runnable_task(target); ++ } else { ++ target->active_balance = 1; ++ force = 1; ++ } ++ } ++ raw_spin_unlock_irqrestore(&target->lock, flags); + +-need_kick_unlock: +- rcu_read_unlock(); +-need_kick: +- return 1; ++ if (force) { ++ /* start timer to keep us awake */ ++ hmp_cpu_keepalive_trigger(); ++ stop_one_cpu_nowait(cpu_of(target), ++ hmp_idle_pull_cpu_stop, ++ target, &target->active_balance_work); ++ } ++done: ++ spin_unlock(&hmp_force_migration); ++ return force; + } + #else +-static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle) { } +-#endif ++static void hmp_force_up_migration(int this_cpu) { } ++#endif /* CONFIG_SCHED_HMP */ + + /* + * run_rebalance_domains is triggered when needed from the scheduler tick. +@@ -7472,8 +9272,23 @@ static void run_rebalance_domains(struct softirq_action *h) + struct rq *this_rq = this_rq(); + enum cpu_idle_type idle = this_rq->idle_balance ? + CPU_IDLE : CPU_NOT_IDLE; ++ int this_cpu = smp_processor_id(); + + rebalance_domains(this_rq, idle); ++#ifdef CONFIG_SCHED_HMP ++ /* shortcut for hmp idle pull wakeups */ ++ if (unlikely(this_rq->wake_for_idle_pull)) { ++ this_rq->wake_for_idle_pull = 0; ++ if (hmp_idle_pull(this_cpu)) { ++ /* break out unless running nohz idle as well */ ++ if (idle != CPU_IDLE) ++ return; ++ } ++ } ++#endif ++ ++ hmp_force_up_migration(this_cpu); ++ + + /* + * If this cpu has a pending nohz_balance_kick, then do the +@@ -7502,6 +9317,9 @@ void trigger_load_balance(struct rq *rq) + + static void rq_online_fair(struct rq *rq) + { ++#ifdef CONFIG_SCHED_HMP ++ hmp_online_cpu(rq->cpu); ++#endif + update_sysctl(); + + update_runtime_enabled(rq); +@@ -7509,6 +9327,9 @@ static void rq_online_fair(struct rq *rq) + + static void rq_offline_fair(struct rq *rq) + { ++#ifdef CONFIG_SCHED_HMP ++ hmp_offline_cpu(rq->cpu); ++#endif + update_sysctl(); + + /* Ensure any throttled groups are reachable by pick_next_task */ +@@ -7992,6 +9813,139 @@ __init void init_sched_fair_class(void) + zalloc_cpumask_var(&nohz.idle_cpus_mask, GFP_NOWAIT); + cpu_notifier(sched_ilb_notifier, 0); + #endif ++ ++#ifdef CONFIG_SCHED_HMP ++ hmp_cpu_mask_setup(); ++#endif + #endif /* SMP */ + + } ++ ++#ifdef CONFIG_HMP_FREQUENCY_INVARIANT_SCALE ++static u32 cpufreq_calc_scale(u32 min, u32 max, u32 curr) ++{ ++ u32 result = curr / max; ++ return result; ++} ++ ++/* Called when the CPU Frequency is changed. ++ * Once for each CPU. ++ */ ++static int cpufreq_callback(struct notifier_block *nb, ++ unsigned long val, void *data) ++{ ++ struct cpufreq_freqs *freq = data; ++ int cpu = freq->cpu; ++ struct cpufreq_extents *extents; ++ ++ if (freq->flags & CPUFREQ_CONST_LOOPS) ++ return NOTIFY_OK; ++ ++ if (val != CPUFREQ_POSTCHANGE) ++ return NOTIFY_OK; ++ ++ /* if dynamic load scale is disabled, set the load scale to 1.0 */ ++ if (!hmp_data.freqinvar_load_scale_enabled) { ++ freq_scale[cpu].curr_scale = 1024; ++ return NOTIFY_OK; ++ } ++ ++ extents = &freq_scale[cpu]; ++ if (extents->flags & SCHED_LOAD_FREQINVAR_SINGLEFREQ) { ++ /* If our governor was recognised as a single-freq governor, ++ * use 1.0 ++ */ ++ extents->curr_scale = 1024; ++ } else { ++ extents->curr_scale = cpufreq_calc_scale(extents->min, ++ extents->max, freq->new); ++ } ++ ++ return NOTIFY_OK; ++} ++ ++/* Called when the CPUFreq governor is changed. ++ * Only called for the CPUs which are actually changed by the ++ * userspace. ++ */ ++static int cpufreq_policy_callback(struct notifier_block *nb, ++ unsigned long event, void *data) ++{ ++ struct cpufreq_policy *policy = data; ++ struct cpufreq_extents *extents; ++ int cpu, singleFreq = 0; ++ static const char performance_governor[] = "performance"; ++ static const char powersave_governor[] = "powersave"; ++ ++ if (event == CPUFREQ_START) ++ return 0; ++ ++ if (event != CPUFREQ_INCOMPATIBLE) ++ return 0; ++ ++ /* CPUFreq governors do not accurately report the range of ++ * CPU Frequencies they will choose from. ++ * We recognise performance and powersave governors as ++ * single-frequency only. ++ */ ++ if (!strncmp(policy->governor->name, performance_governor, ++ strlen(performance_governor)) || ++ !strncmp(policy->governor->name, powersave_governor, ++ strlen(powersave_governor))) ++ singleFreq = 1; ++ ++ /* Make sure that all CPUs impacted by this policy are ++ * updated since we will only get a notification when the ++ * user explicitly changes the policy on a CPU. ++ */ ++ for_each_cpu(cpu, policy->cpus) { ++ extents = &freq_scale[cpu]; ++ extents->max = policy->max >> SCHED_FREQSCALE_SHIFT; ++ extents->min = policy->min >> SCHED_FREQSCALE_SHIFT; ++ if (!hmp_data.freqinvar_load_scale_enabled) { ++ extents->curr_scale = 1024; ++ } else if (singleFreq) { ++ extents->flags |= SCHED_LOAD_FREQINVAR_SINGLEFREQ; ++ extents->curr_scale = 1024; ++ } else { ++ extents->flags &= ~SCHED_LOAD_FREQINVAR_SINGLEFREQ; ++ extents->curr_scale = cpufreq_calc_scale(extents->min, ++ extents->max, policy->cur); ++ } ++ } ++ ++ return 0; ++} ++ ++static struct notifier_block cpufreq_notifier = { ++ .notifier_call = cpufreq_callback, ++}; ++static struct notifier_block cpufreq_policy_notifier = { ++ .notifier_call = cpufreq_policy_callback, ++}; ++ ++static int __init register_sched_cpufreq_notifier(void) ++{ ++ int ret = 0; ++ ++ /* init safe defaults since there are no policies at registration */ ++ for (ret = 0; ret < CONFIG_NR_CPUS; ret++) { ++ /* safe defaults */ ++ freq_scale[ret].max = 1024; ++ freq_scale[ret].min = 1024; ++ freq_scale[ret].curr_scale = 1024; ++ } ++ ++ pr_info("sched: registering cpufreq notifiers for scale-invariant loads\n"); ++ ret = cpufreq_register_notifier(&cpufreq_policy_notifier, ++ CPUFREQ_POLICY_NOTIFIER); ++ ++ if (ret != -EINVAL) ++ ret = cpufreq_register_notifier(&cpufreq_notifier, ++ CPUFREQ_TRANSITION_NOTIFIER); ++ ++ return ret; ++} ++ ++core_initcall(register_sched_cpufreq_notifier); ++#endif /* CONFIG_HMP_FREQUENCY_INVARIANT_SCALE */ +diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h +index 2df8ef0..6455961 100644 +--- a/kernel/sched/sched.h ++++ b/kernel/sched/sched.h +@@ -218,7 +218,7 @@ struct task_group { + + #ifdef CONFIG_SMP + atomic_long_t load_avg; +- atomic_t runnable_avg; ++ atomic_t runnable_avg, usage_avg; + #endif + #endif + +@@ -351,7 +351,7 @@ struct cfs_rq { + + #ifdef CONFIG_FAIR_GROUP_SCHED + /* Required to track per-cpu representation of a task_group */ +- u32 tg_runnable_contrib; ++ u32 tg_runnable_contrib, tg_usage_contrib; + unsigned long tg_load_contrib; + + /* +@@ -507,6 +507,12 @@ struct root_domain { + + extern struct root_domain def_root_domain; + ++#ifdef CONFIG_SCHED_HMP ++static LIST_HEAD(hmp_domains); ++DECLARE_PER_CPU(struct hmp_domain *, hmp_cpu_domain); ++#define hmp_cpu_domain(cpu) (per_cpu(hmp_cpu_domain, (cpu))) ++#endif /* CONFIG_SCHED_HMP */ ++ + #endif /* CONFIG_SMP */ + + /* +@@ -586,6 +592,10 @@ struct rq { + int active_balance; + int push_cpu; + struct cpu_stop_work active_balance_work; ++#ifdef CONFIG_SCHED_HMP ++ struct task_struct *migrate_task; ++ int wake_for_idle_pull; ++#endif + /* cpu of this runqueue: */ + int cpu; + int online; +diff --git a/kernel/sys.c b/kernel/sys.c +index 1eaa2f0..38a2099 100644 +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -41,6 +41,9 @@ + #include <linux/syscore_ops.h> + #include <linux/version.h> + #include <linux/ctype.h> ++#include <linux/mm.h> ++#include <linux/mempolicy.h> ++#include <linux/sched.h> + + #include <linux/compat.h> + #include <linux/syscalls.h> +@@ -2025,10 +2028,158 @@ static int prctl_get_tid_address(struct task_struct *me, int __user **tid_addr) + } + #endif + ++#ifdef CONFIG_MMU ++static int prctl_update_vma_anon_name(struct vm_area_struct *vma, ++ struct vm_area_struct **prev, ++ unsigned long start, unsigned long end, ++ const char __user *name_addr) ++{ ++ struct mm_struct * mm = vma->vm_mm; ++ int error = 0; ++ pgoff_t pgoff; ++ ++ if (name_addr == vma_get_anon_name(vma)) { ++ *prev = vma; ++ goto out; ++ } ++ ++ pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); ++ *prev = vma_merge(mm, *prev, start, end, vma->vm_flags, vma->anon_vma, ++ vma->vm_file, pgoff, vma_policy(vma), ++ name_addr); ++ if (*prev) { ++ vma = *prev; ++ goto success; ++ } ++ ++ *prev = vma; ++ ++ if (start != vma->vm_start) { ++ error = split_vma(mm, vma, start, 1); ++ if (error) ++ goto out; ++ } ++ ++ if (end != vma->vm_end) { ++ error = split_vma(mm, vma, end, 0); ++ if (error) ++ goto out; ++ } ++ ++success: ++ if (!vma->vm_file) ++ vma->shared.anon_name = name_addr; ++ ++out: ++ if (error == -ENOMEM) ++ error = -EAGAIN; ++ return error; ++} ++ ++static int prctl_set_vma_anon_name(unsigned long start, unsigned long end, ++ unsigned long arg) ++{ ++ unsigned long tmp; ++ struct vm_area_struct * vma, *prev; ++ int unmapped_error = 0; ++ int error = -EINVAL; ++ ++ /* ++ * If the interval [start,end) covers some unmapped address ++ * ranges, just ignore them, but return -ENOMEM at the end. ++ * - this matches the handling in madvise. ++ */ ++ vma = find_vma_prev(current->mm, start, &prev); ++ if (vma && start > vma->vm_start) ++ prev = vma; ++ ++ for (;;) { ++ /* Still start < end. */ ++ error = -ENOMEM; ++ if (!vma) ++ return error; ++ ++ /* Here start < (end|vma->vm_end). */ ++ if (start < vma->vm_start) { ++ unmapped_error = -ENOMEM; ++ start = vma->vm_start; ++ if (start >= end) ++ return error; ++ } ++ ++ /* Here vma->vm_start <= start < (end|vma->vm_end) */ ++ tmp = vma->vm_end; ++ if (end < tmp) ++ tmp = end; ++ ++ /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */ ++ error = prctl_update_vma_anon_name(vma, &prev, start, tmp, ++ (const char __user *)arg); ++ if (error) ++ return error; ++ start = tmp; ++ if (prev && start < prev->vm_end) ++ start = prev->vm_end; ++ error = unmapped_error; ++ if (start >= end) ++ return error; ++ if (prev) ++ vma = prev->vm_next; ++ else /* madvise_remove dropped mmap_sem */ ++ vma = find_vma(current->mm, start); ++ } ++} ++ ++static int prctl_set_vma(unsigned long opt, unsigned long start, ++ unsigned long len_in, unsigned long arg) ++{ ++ struct mm_struct *mm = current->mm; ++ int error; ++ unsigned long len; ++ unsigned long end; ++ ++ if (start & ~PAGE_MASK) ++ return -EINVAL; ++ len = (len_in + ~PAGE_MASK) & PAGE_MASK; ++ ++ /* Check to see whether len was rounded up from small -ve to zero */ ++ if (len_in && !len) ++ return -EINVAL; ++ ++ end = start + len; ++ if (end < start) ++ return -EINVAL; ++ ++ if (end == start) ++ return 0; ++ ++ down_write(&mm->mmap_sem); ++ ++ switch (opt) { ++ case PR_SET_VMA_ANON_NAME: ++ error = prctl_set_vma_anon_name(start, end, arg); ++ break; ++ default: ++ error = -EINVAL; ++ } ++ ++ up_write(&mm->mmap_sem); ++ ++ return error; ++} ++#else /* CONFIG_MMU */ ++static int prctl_set_vma(unsigned long opt, unsigned long start, ++ unsigned long len_in, unsigned long arg) ++{ ++ return -EINVAL; ++} ++#endif ++ + SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, + unsigned long, arg4, unsigned long, arg5) + { + struct task_struct *me = current; ++ struct task_struct *tsk; + unsigned char comm[sizeof(me->comm)]; + long error; + +@@ -2171,6 +2322,26 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, + case PR_GET_TID_ADDRESS: + error = prctl_get_tid_address(me, (int __user **)arg2); + break; ++ case PR_SET_TIMERSLACK_PID: ++ if (task_pid_vnr(current) != (pid_t)arg3 && ++ !capable(CAP_SYS_NICE)) ++ return -EPERM; ++ rcu_read_lock(); ++ tsk = find_task_by_vpid((pid_t)arg3); ++ if (tsk == NULL) { ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ get_task_struct(tsk); ++ rcu_read_unlock(); ++ if (arg2 <= 0) ++ tsk->timer_slack_ns = ++ tsk->default_timer_slack_ns; ++ else ++ tsk->timer_slack_ns = arg2; ++ put_task_struct(tsk); ++ error = 0; ++ break; + case PR_SET_CHILD_SUBREAPER: + me->signal->is_child_subreaper = !!arg2; + break; +@@ -2203,6 +2374,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, + me->mm->def_flags &= ~VM_NOHUGEPAGE; + up_write(&me->mm->mmap_sem); + break; ++ case PR_SET_VMA: ++ error = prctl_set_vma(arg2, arg3, arg4, arg5); ++ break; + default: + error = -EINVAL; + break; +diff --git a/kernel/sysctl.c b/kernel/sysctl.c +index cd0e835..5ef8871 100644 +--- a/kernel/sysctl.c ++++ b/kernel/sysctl.c +@@ -104,6 +104,8 @@ extern char core_pattern[]; + extern unsigned int core_pipe_limit; + #endif + extern int pid_max; ++extern int extra_free_kbytes; ++extern int min_free_order_shift; + extern int pid_max_min, pid_max_max; + extern int percpu_pagelist_fraction; + extern int compat_log; +@@ -1308,6 +1310,21 @@ static struct ctl_table vm_table[] = { + .extra1 = &zero, + }, + { ++ .procname = "extra_free_kbytes", ++ .data = &extra_free_kbytes, ++ .maxlen = sizeof(extra_free_kbytes), ++ .mode = 0644, ++ .proc_handler = min_free_kbytes_sysctl_handler, ++ .extra1 = &zero, ++ }, ++ { ++ .procname = "min_free_order_shift", ++ .data = &min_free_order_shift, ++ .maxlen = sizeof(min_free_order_shift), ++ .mode = 0644, ++ .proc_handler = &proc_dointvec ++ }, ++ { + .procname = "percpu_pagelist_fraction", + .data = &percpu_pagelist_fraction, + .maxlen = sizeof(percpu_pagelist_fraction), +@@ -1483,6 +1500,15 @@ static struct ctl_table vm_table[] = { + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + }, ++#ifdef CONFIG_CMA_ADVANCE_SHARE ++ { ++ .procname = "cma_watermark", ++ .data = &cma_watermark, ++ .maxlen = sizeof(cma_watermark), ++ .mode = 0644, ++ .proc_handler = cma_watermark_sysctl_handler, ++ }, ++#endif + { } + }; + +@@ -1656,6 +1682,20 @@ static struct ctl_table fs_table[] = { + .proc_handler = &pipe_proc_fn, + .extra1 = &pipe_min_size, + }, ++ { ++ .procname = "pipe-user-pages-hard", ++ .data = &pipe_user_pages_hard, ++ .maxlen = sizeof(pipe_user_pages_hard), ++ .mode = 0644, ++ .proc_handler = proc_doulongvec_minmax, ++ }, ++ { ++ .procname = "pipe-user-pages-soft", ++ .data = &pipe_user_pages_soft, ++ .maxlen = sizeof(pipe_user_pages_soft), ++ .mode = 0644, ++ .proc_handler = proc_doulongvec_minmax, ++ }, + { } + }; + +diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig +index a5da09c..37e1fb8 100644 +--- a/kernel/trace/Kconfig ++++ b/kernel/trace/Kconfig +@@ -77,6 +77,9 @@ config EVENT_TRACING + select CONTEXT_SWITCH_TRACER + bool + ++config GPU_TRACEPOINTS ++ bool ++ + config CONTEXT_SWITCH_TRACER + bool + +diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile +index 67d6369..5da3cfb 100644 +--- a/kernel/trace/Makefile ++++ b/kernel/trace/Makefile +@@ -63,6 +63,7 @@ obj-$(CONFIG_KGDB_KDB) += trace_kdb.o + endif + obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o + obj-$(CONFIG_UPROBE_EVENT) += trace_uprobe.o ++obj-$(CONFIG_GPU_TRACEPOINTS) += gpu-traces.o + + obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o + +diff --git a/kernel/trace/gpu-traces.c b/kernel/trace/gpu-traces.c +new file mode 100644 +index 0000000..a4b3f00 +--- /dev/null ++++ b/kernel/trace/gpu-traces.c +@@ -0,0 +1,23 @@ ++/* ++ * GPU tracepoints ++ * ++ * Copyright (C) 2013 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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> ++ ++#define CREATE_TRACE_POINTS ++#include <trace/events/gpu.h> ++ ++EXPORT_TRACEPOINT_SYMBOL(gpu_sched_switch); ++EXPORT_TRACEPOINT_SYMBOL(gpu_job_enqueue); +diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c +index 72c7134..637f572 100644 +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -812,6 +812,7 @@ static const char *trace_options[] = { + "irq-info", + "markers", + "function-trace", ++ "print-tgid", + NULL + }; + +@@ -1279,6 +1280,7 @@ void tracing_reset_all_online_cpus(void) + + #define SAVED_CMDLINES_DEFAULT 128 + #define NO_CMDLINE_MAP UINT_MAX ++static unsigned saved_tgids[SAVED_CMDLINES_DEFAULT]; + static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED; + struct saved_cmdlines_buffer { + unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1]; +@@ -1517,7 +1519,7 @@ static int trace_save_cmdline(struct task_struct *tsk) + } + + set_cmdline(idx, tsk->comm); +- ++ saved_tgids[idx] = tsk->tgid; + arch_spin_unlock(&trace_cmdline_lock); + + return 1; +@@ -1560,6 +1562,25 @@ void trace_find_cmdline(int pid, char comm[]) + preempt_enable(); + } + ++int trace_find_tgid(int pid) ++{ ++ unsigned map; ++ int tgid; ++ ++ preempt_disable(); ++ arch_spin_lock(&trace_cmdline_lock); ++ map = savedcmd->map_pid_to_cmdline[pid]; ++ if (map != NO_CMDLINE_MAP) ++ tgid = saved_tgids[map]; ++ else ++ tgid = -1; ++ ++ arch_spin_unlock(&trace_cmdline_lock); ++ preempt_enable(); ++ ++ return tgid; ++} ++ + void tracing_record_cmdline(struct task_struct *tsk) + { + if (atomic_read(&trace_record_cmdline_disabled) || !tracing_is_on()) +@@ -2537,6 +2558,13 @@ static void print_func_help_header(struct trace_buffer *buf, struct seq_file *m) + seq_puts(m, "# | | | | |\n"); + } + ++static void print_func_help_header_tgid(struct trace_buffer *buf, struct seq_file *m) ++{ ++ print_event_info(buf, m); ++ seq_puts(m, "# TASK-PID TGID CPU# TIMESTAMP FUNCTION\n"); ++ seq_puts(m, "# | | | | | |\n"); ++} ++ + static void print_func_help_header_irq(struct trace_buffer *buf, struct seq_file *m) + { + print_event_info(buf, m); +@@ -2549,6 +2577,18 @@ static void print_func_help_header_irq(struct trace_buffer *buf, struct seq_file + seq_puts(m, "# | | | |||| | |\n"); + } + ++static void print_func_help_header_irq_tgid(struct trace_buffer *buf, struct seq_file *m) ++{ ++ print_event_info(buf, m); ++ seq_puts(m, "# _-----=> irqs-off\n"); ++ seq_puts(m, "# / _----=> need-resched\n"); ++ seq_puts(m, "# | / _---=> hardirq/softirq\n"); ++ seq_puts(m, "# || / _--=> preempt-depth\n"); ++ seq_puts(m, "# ||| / delay\n"); ++ seq_puts(m, "# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION\n"); ++ seq_puts(m, "# | | | | |||| | |\n"); ++} ++ + void + print_trace_header(struct seq_file *m, struct trace_iterator *iter) + { +@@ -2849,9 +2889,15 @@ void trace_default_header(struct seq_file *m) + } else { + if (!(trace_flags & TRACE_ITER_VERBOSE)) { + if (trace_flags & TRACE_ITER_IRQ_INFO) +- print_func_help_header_irq(iter->trace_buffer, m); ++ if (trace_flags & TRACE_ITER_TGID) ++ print_func_help_header_irq_tgid(iter->trace_buffer, m); ++ else ++ print_func_help_header_irq(iter->trace_buffer, m); + else +- print_func_help_header(iter->trace_buffer, m); ++ if (trace_flags & TRACE_ITER_TGID) ++ print_func_help_header_tgid(iter->trace_buffer, m); ++ else ++ print_func_help_header(iter->trace_buffer, m); + } + } + } +@@ -3897,6 +3943,50 @@ static const struct file_operations tracing_saved_cmdlines_size_fops = { + }; + + static ssize_t ++tracing_saved_tgids_read(struct file *file, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ char *file_buf; ++ char *buf; ++ int len = 0; ++ int pid; ++ int i; ++ ++ file_buf = kmalloc(SAVED_CMDLINES_DEFAULT*(16+1+16), GFP_KERNEL); ++ if (!file_buf) ++ return -ENOMEM; ++ ++ buf = file_buf; ++ ++ for (i = 0; i < SAVED_CMDLINES_DEFAULT; i++) { ++ int tgid; ++ int r; ++ ++ pid = savedcmd->map_cmdline_to_pid[i]; ++ if (pid == -1 || pid == NO_CMDLINE_MAP) ++ continue; ++ ++ tgid = trace_find_tgid(pid); ++ r = sprintf(buf, "%d %d\n", pid, tgid); ++ buf += r; ++ len += r; ++ } ++ ++ len = simple_read_from_buffer(ubuf, cnt, ppos, ++ file_buf, len); ++ ++ kfree(file_buf); ++ ++ return len; ++} ++ ++static const struct file_operations tracing_saved_tgids_fops = { ++ .open = tracing_open_generic, ++ .read = tracing_saved_tgids_read, ++ .llseek = generic_file_llseek, ++}; ++ ++static ssize_t + tracing_set_trace_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) + { +@@ -6520,6 +6610,9 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer) + trace_create_file("trace_marker", 0220, d_tracer, + tr, &tracing_mark_fops); + ++ trace_create_file("saved_tgids", 0444, d_tracer, ++ tr, &tracing_saved_tgids_fops); ++ + trace_create_file("trace_clock", 0644, d_tracer, tr, + &trace_clock_fops); + +diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h +index 5642436..0d2f587 100644 +--- a/kernel/trace/trace.h ++++ b/kernel/trace/trace.h +@@ -659,6 +659,7 @@ static inline void __trace_stack(struct trace_array *tr, unsigned long flags, + extern cycle_t ftrace_now(int cpu); + + extern void trace_find_cmdline(int pid, char comm[]); ++extern int trace_find_tgid(int pid); + + #ifdef CONFIG_DYNAMIC_FTRACE + extern unsigned long ftrace_update_tot_cnt; +@@ -939,6 +940,7 @@ enum trace_iterator_flags { + TRACE_ITER_IRQ_INFO = 0x800000, + TRACE_ITER_MARKERS = 0x1000000, + TRACE_ITER_FUNCTION = 0x2000000, ++ TRACE_ITER_TGID = 0x4000000, + }; + + /* +diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c +index 2964333..70c2f41 100644 +--- a/kernel/trace/trace_functions_graph.c ++++ b/kernel/trace/trace_functions_graph.c +@@ -65,6 +65,9 @@ struct fgraph_data { + + #define TRACE_GRAPH_INDENT 2 + ++/* Flag options */ ++#define TRACE_GRAPH_PRINT_FLAT 0x80 ++ + static unsigned int max_depth; + + static struct tracer_opt trace_opts[] = { +@@ -84,6 +87,8 @@ static struct tracer_opt trace_opts[] = { + { TRACER_OPT(funcgraph-irqs, TRACE_GRAPH_PRINT_IRQS) }, + /* Display function name after trailing } */ + { TRACER_OPT(funcgraph-tail, TRACE_GRAPH_PRINT_TAIL) }, ++ /* Use standard trace formatting rather than hierarchical */ ++ { TRACER_OPT(funcgraph-flat, TRACE_GRAPH_PRINT_FLAT) }, + { } /* Empty entry */ + }; + +@@ -1314,6 +1319,9 @@ print_graph_function_flags(struct trace_iterator *iter, u32 flags) + int cpu = iter->cpu; + int ret; + ++ if (flags & TRACE_GRAPH_PRINT_FLAT) ++ return TRACE_TYPE_UNHANDLED; ++ + if (data && per_cpu_ptr(data->cpu_data, cpu)->ignore) { + per_cpu_ptr(data->cpu_data, cpu)->ignore = 0; + return TRACE_TYPE_HANDLED; +@@ -1371,13 +1379,6 @@ print_graph_function(struct trace_iterator *iter) + return print_graph_function_flags(iter, tracer_flags.val); + } + +-static enum print_line_t +-print_graph_function_event(struct trace_iterator *iter, int flags, +- struct trace_event *event) +-{ +- return print_graph_function(iter); +-} +- + static void print_lat_header(struct seq_file *s, u32 flags) + { + static const char spaces[] = " " /* 16 spaces */ +@@ -1444,6 +1445,11 @@ void print_graph_headers_flags(struct seq_file *s, u32 flags) + { + struct trace_iterator *iter = s->private; + ++ if (flags & TRACE_GRAPH_PRINT_FLAT) { ++ trace_default_header(s); ++ return; ++ } ++ + if (!(trace_flags & TRACE_ITER_CONTEXT_INFO)) + return; + +@@ -1519,19 +1525,6 @@ func_graph_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set) + return 0; + } + +-static struct trace_event_functions graph_functions = { +- .trace = print_graph_function_event, +-}; +- +-static struct trace_event graph_trace_entry_event = { +- .type = TRACE_GRAPH_ENT, +- .funcs = &graph_functions, +-}; +- +-static struct trace_event graph_trace_ret_event = { +- .type = TRACE_GRAPH_RET, +- .funcs = &graph_functions +-}; + + static struct tracer graph_trace __tracer_data = { + .name = "function_graph", +@@ -1608,16 +1601,6 @@ static __init int init_graph_trace(void) + { + max_bytes_for_cpu = snprintf(NULL, 0, "%d", nr_cpu_ids - 1); + +- if (!register_ftrace_event(&graph_trace_entry_event)) { +- pr_warning("Warning: could not register graph trace events\n"); +- return 1; +- } +- +- if (!register_ftrace_event(&graph_trace_ret_event)) { +- pr_warning("Warning: could not register graph trace events\n"); +- return 1; +- } +- + return register_tracer(&graph_trace); + } + +diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c +index c6977d5..a2b8202 100644 +--- a/kernel/trace/trace_output.c ++++ b/kernel/trace/trace_output.c +@@ -186,6 +186,50 @@ ftrace_print_hex_seq(struct trace_seq *p, const unsigned char *buf, int buf_len) + } + EXPORT_SYMBOL(ftrace_print_hex_seq); + ++const char * ++ftrace_print_array_seq(struct trace_seq *p, const void *buf, int buf_len, ++ size_t el_size) ++{ ++ const char *ret = trace_seq_buffer_ptr(p); ++ const char *prefix = ""; ++ void *ptr = (void *)buf; ++ ++ trace_seq_putc(p, '{'); ++ ++ while (ptr < buf + buf_len) { ++ switch (el_size) { ++ case 1: ++ trace_seq_printf(p, "%s0x%x", prefix, ++ *(u8 *)ptr); ++ break; ++ case 2: ++ trace_seq_printf(p, "%s0x%x", prefix, ++ *(u16 *)ptr); ++ break; ++ case 4: ++ trace_seq_printf(p, "%s0x%x", prefix, ++ *(u32 *)ptr); ++ break; ++ case 8: ++ trace_seq_printf(p, "%s0x%llx", prefix, ++ *(u64 *)ptr); ++ break; ++ default: ++ trace_seq_printf(p, "BAD SIZE:%zu 0x%x", el_size, ++ *(u8 *)ptr); ++ el_size = 1; ++ } ++ prefix = ","; ++ ptr += el_size; ++ } ++ ++ trace_seq_putc(p, '}'); ++ trace_seq_putc(p, 0); ++ ++ return ret; ++} ++EXPORT_SYMBOL(ftrace_print_array_seq); ++ + int ftrace_raw_output_prep(struct trace_iterator *iter, + struct trace_event *trace_event) + { +@@ -521,11 +565,25 @@ int trace_print_context(struct trace_iterator *iter) + unsigned long secs, usec_rem; + char comm[TASK_COMM_LEN]; + int ret; ++ int tgid; + + trace_find_cmdline(entry->pid, comm); + +- ret = trace_seq_printf(s, "%16s-%-5d [%03d] ", +- comm, entry->pid, iter->cpu); ++ ret = trace_seq_printf(s, "%16s-%-5d ", comm, entry->pid); ++ if (!ret) ++ return 0; ++ ++ if (trace_flags & TRACE_ITER_TGID) { ++ tgid = trace_find_tgid(entry->pid); ++ if (tgid < 0) ++ ret = trace_seq_puts(s, "(-----) "); ++ else ++ ret = trace_seq_printf(s, "(%5d) ", tgid); ++ if (!ret) ++ return 0; ++ } ++ ++ ret = trace_seq_printf(s, "[%03d] ", iter->cpu); + if (!ret) + return 0; + +@@ -854,6 +912,168 @@ static struct trace_event trace_fn_event = { + .funcs = &trace_fn_funcs, + }; + ++/* TRACE_GRAPH_ENT */ ++static enum print_line_t trace_graph_ent_trace(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct trace_seq *s = &iter->seq; ++ struct ftrace_graph_ent_entry *field; ++ ++ trace_assign_type(field, iter->ent); ++ ++ if (!trace_seq_puts(s, "graph_ent: func=")) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ if (!seq_print_ip_sym(s, field->graph_ent.func, flags)) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ if (!trace_seq_puts(s, "\n")) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t trace_graph_ent_raw(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct ftrace_graph_ent_entry *field; ++ ++ trace_assign_type(field, iter->ent); ++ ++ if (!trace_seq_printf(&iter->seq, "%lx %d\n", ++ field->graph_ent.func, ++ field->graph_ent.depth)) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t trace_graph_ent_hex(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct ftrace_graph_ent_entry *field; ++ struct trace_seq *s = &iter->seq; ++ ++ trace_assign_type(field, iter->ent); ++ ++ SEQ_PUT_HEX_FIELD_RET(s, field->graph_ent.func); ++ SEQ_PUT_HEX_FIELD_RET(s, field->graph_ent.depth); ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t trace_graph_ent_bin(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct ftrace_graph_ent_entry *field; ++ struct trace_seq *s = &iter->seq; ++ ++ trace_assign_type(field, iter->ent); ++ ++ SEQ_PUT_FIELD_RET(s, field->graph_ent.func); ++ SEQ_PUT_FIELD_RET(s, field->graph_ent.depth); ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static struct trace_event_functions trace_graph_ent_funcs = { ++ .trace = trace_graph_ent_trace, ++ .raw = trace_graph_ent_raw, ++ .hex = trace_graph_ent_hex, ++ .binary = trace_graph_ent_bin, ++}; ++ ++static struct trace_event trace_graph_ent_event = { ++ .type = TRACE_GRAPH_ENT, ++ .funcs = &trace_graph_ent_funcs, ++}; ++ ++/* TRACE_GRAPH_RET */ ++static enum print_line_t trace_graph_ret_trace(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct trace_seq *s = &iter->seq; ++ struct trace_entry *entry = iter->ent; ++ struct ftrace_graph_ret_entry *field; ++ ++ trace_assign_type(field, entry); ++ ++ if (!trace_seq_puts(s, "graph_ret: func=")) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ if (!seq_print_ip_sym(s, field->ret.func, flags)) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ if (!trace_seq_puts(s, "\n")) ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t trace_graph_ret_raw(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct ftrace_graph_ret_entry *field; ++ ++ trace_assign_type(field, iter->ent); ++ ++ if (!trace_seq_printf(&iter->seq, "%lx %lld %lld %ld %d\n", ++ field->ret.func, ++ field->ret.calltime, ++ field->ret.rettime, ++ field->ret.overrun, ++ field->ret.depth)); ++ return TRACE_TYPE_PARTIAL_LINE; ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t trace_graph_ret_hex(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct ftrace_graph_ret_entry *field; ++ struct trace_seq *s = &iter->seq; ++ ++ trace_assign_type(field, iter->ent); ++ ++ SEQ_PUT_HEX_FIELD_RET(s, field->ret.func); ++ SEQ_PUT_HEX_FIELD_RET(s, field->ret.calltime); ++ SEQ_PUT_HEX_FIELD_RET(s, field->ret.rettime); ++ SEQ_PUT_HEX_FIELD_RET(s, field->ret.overrun); ++ SEQ_PUT_HEX_FIELD_RET(s, field->ret.depth); ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static enum print_line_t trace_graph_ret_bin(struct trace_iterator *iter, int flags, ++ struct trace_event *event) ++{ ++ struct ftrace_graph_ret_entry *field; ++ struct trace_seq *s = &iter->seq; ++ ++ trace_assign_type(field, iter->ent); ++ ++ SEQ_PUT_FIELD_RET(s, field->ret.func); ++ SEQ_PUT_FIELD_RET(s, field->ret.calltime); ++ SEQ_PUT_FIELD_RET(s, field->ret.rettime); ++ SEQ_PUT_FIELD_RET(s, field->ret.overrun); ++ SEQ_PUT_FIELD_RET(s, field->ret.depth); ++ ++ return TRACE_TYPE_HANDLED; ++} ++ ++static struct trace_event_functions trace_graph_ret_funcs = { ++ .trace = trace_graph_ret_trace, ++ .raw = trace_graph_ret_raw, ++ .hex = trace_graph_ret_hex, ++ .binary = trace_graph_ret_bin, ++}; ++ ++static struct trace_event trace_graph_ret_event = { ++ .type = TRACE_GRAPH_RET, ++ .funcs = &trace_graph_ret_funcs, ++}; ++ + /* TRACE_CTX an TRACE_WAKE */ + static enum print_line_t trace_ctxwake_print(struct trace_iterator *iter, + char *delim) +@@ -1244,6 +1464,8 @@ static struct trace_event trace_print_event = { + + static struct trace_event *events[] __initdata = { + &trace_fn_event, ++ &trace_graph_ent_event, ++ &trace_graph_ret_event, + &trace_ctx_event, + &trace_wake_event, + &trace_stack_event, +diff --git a/kernel/watchdog.c b/kernel/watchdog.c +index 70bf118..7eccf9b 100644 +--- a/kernel/watchdog.c ++++ b/kernel/watchdog.c +@@ -47,6 +47,11 @@ static DEFINE_PER_CPU(struct task_struct *, softlockup_task_ptr_saved); + static DEFINE_PER_CPU(bool, hard_watchdog_warn); + static DEFINE_PER_CPU(bool, watchdog_nmi_touch); + static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved); ++#endif ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU ++static cpumask_t __read_mostly watchdog_cpus; ++#endif ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI + static DEFINE_PER_CPU(struct perf_event *, watchdog_ev); + #endif + static unsigned long soft_lockup_nmi_warn; +@@ -221,7 +226,7 @@ void touch_softlockup_watchdog_sync(void) + __this_cpu_write(watchdog_touch_ts, 0); + } + +-#ifdef CONFIG_HARDLOCKUP_DETECTOR ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI + /* watchdog detector functions */ + static int is_hardlockup(void) + { +@@ -235,6 +240,76 @@ static int is_hardlockup(void) + } + #endif + ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU ++static unsigned int watchdog_next_cpu(unsigned int cpu) ++{ ++ cpumask_t cpus = watchdog_cpus; ++ unsigned int next_cpu; ++ ++ next_cpu = cpumask_next(cpu, &cpus); ++ if (next_cpu >= nr_cpu_ids) ++ next_cpu = cpumask_first(&cpus); ++ ++ if (next_cpu == cpu) ++ return nr_cpu_ids; ++ ++ return next_cpu; ++} ++ ++static int is_hardlockup_other_cpu(unsigned int cpu) ++{ ++ unsigned long hrint = per_cpu(hrtimer_interrupts, cpu); ++ ++ if (per_cpu(hrtimer_interrupts_saved, cpu) == hrint) ++ return 1; ++ ++ per_cpu(hrtimer_interrupts_saved, cpu) = hrint; ++ return 0; ++} ++ ++static void watchdog_check_hardlockup_other_cpu(void) ++{ ++ unsigned int next_cpu; ++ ++ /* ++ * Test for hardlockups every 3 samples. The sample period is ++ * watchdog_thresh * 2 / 5, so 3 samples gets us back to slightly over ++ * watchdog_thresh (over by 20%). ++ */ ++ if (__this_cpu_read(hrtimer_interrupts) % 3 != 0) ++ return; ++ ++ /* check for a hardlockup on the next cpu */ ++ next_cpu = watchdog_next_cpu(smp_processor_id()); ++ if (next_cpu >= nr_cpu_ids) ++ return; ++ ++ smp_rmb(); ++ ++ if (per_cpu(watchdog_nmi_touch, next_cpu) == true) { ++ per_cpu(watchdog_nmi_touch, next_cpu) = false; ++ return; ++ } ++ ++ if (is_hardlockup_other_cpu(next_cpu)) { ++ /* only warn once */ ++ if (per_cpu(hard_watchdog_warn, next_cpu) == true) ++ return; ++ ++ if (hardlockup_panic) ++ panic("Watchdog detected hard LOCKUP on cpu %u", next_cpu); ++ else ++ WARN(1, "Watchdog detected hard LOCKUP on cpu %u", next_cpu); ++ ++ per_cpu(hard_watchdog_warn, next_cpu) = true; ++ } else { ++ per_cpu(hard_watchdog_warn, next_cpu) = false; ++ } ++} ++#else ++static inline void watchdog_check_hardlockup_other_cpu(void) { return; } ++#endif ++ + static int is_softlockup(unsigned long touch_ts) + { + unsigned long now = get_timestamp(); +@@ -246,7 +321,7 @@ static int is_softlockup(unsigned long touch_ts) + return 0; + } + +-#ifdef CONFIG_HARDLOCKUP_DETECTOR ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI + + static struct perf_event_attr wd_hw_attr = { + .type = PERF_TYPE_HARDWARE, +@@ -296,7 +371,7 @@ static void watchdog_overflow_callback(struct perf_event *event, + __this_cpu_write(hard_watchdog_warn, false); + return; + } +-#endif /* CONFIG_HARDLOCKUP_DETECTOR */ ++#endif /* CONFIG_HARDLOCKUP_DETECTOR_NMI */ + + static void watchdog_interrupt_count(void) + { +@@ -317,6 +392,9 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) + /* kick the hardlockup detector */ + watchdog_interrupt_count(); + ++ /* test for hardlockups on the next cpu */ ++ watchdog_check_hardlockup_other_cpu(); ++ + /* kick the softlockup detector */ + wake_up_process(__this_cpu_read(softlockup_watchdog)); + +@@ -479,7 +557,7 @@ static void watchdog(unsigned int cpu) + __touch_watchdog(); + } + +-#ifdef CONFIG_HARDLOCKUP_DETECTOR ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI + /* + * People like the simple clean cpu node info on boot. + * Reduce the watchdog noise by only printing messages +@@ -568,9 +646,44 @@ static void watchdog_nmi_disable(unsigned int cpu) + } + } + #else ++#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU ++static int watchdog_nmi_enable(unsigned int cpu) ++{ ++ /* ++ * The new cpu will be marked online before the first hrtimer interrupt ++ * runs on it. If another cpu tests for a hardlockup on the new cpu ++ * before it has run its first hrtimer, it will get a false positive. ++ * Touch the watchdog on the new cpu to delay the first check for at ++ * least 3 sampling periods to guarantee one hrtimer has run on the new ++ * cpu. ++ */ ++ per_cpu(watchdog_nmi_touch, cpu) = true; ++ smp_wmb(); ++ cpumask_set_cpu(cpu, &watchdog_cpus); ++ return 0; ++} ++ ++static void watchdog_nmi_disable(unsigned int cpu) ++{ ++ unsigned int next_cpu = watchdog_next_cpu(cpu); ++ ++ /* ++ * Offlining this cpu will cause the cpu before this one to start ++ * checking the one after this one. If this cpu just finished checking ++ * the next cpu and updating hrtimer_interrupts_saved, and then the ++ * previous cpu checks it within one sample period, it will trigger a ++ * false positive. Touch the watchdog on the next cpu to prevent it. ++ */ ++ if (next_cpu < nr_cpu_ids) ++ per_cpu(watchdog_nmi_touch, next_cpu) = true; ++ smp_wmb(); ++ cpumask_clear_cpu(cpu, &watchdog_cpus); ++} ++#else + static int watchdog_nmi_enable(unsigned int cpu) { return 0; } + static void watchdog_nmi_disable(unsigned int cpu) { return; } +-#endif /* CONFIG_HARDLOCKUP_DETECTOR */ ++#endif /* CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU */ ++#endif /* CONFIG_HARDLOCKUP_DETECTOR_NMI */ + + static struct smp_hotplug_thread watchdog_threads = { + .store = &softlockup_watchdog, +diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug +index 4e35a5d..95e5931 100644 +--- a/lib/Kconfig.debug ++++ b/lib/Kconfig.debug +@@ -668,15 +668,27 @@ config LOCKUP_DETECTOR + The overhead should be minimal. A periodic hrtimer runs to + generate interrupts and kick the watchdog task every 4 seconds. + An NMI is generated every 10 seconds or so to check for hardlockups. ++ If NMIs are not available on the platform, every 12 seconds the ++ hrtimer interrupt on one cpu will be used to check for hardlockups ++ on the next cpu. + + The frequency of hrtimer and NMI events and the soft and hard lockup + thresholds can be controlled through the sysctl watchdog_thresh. + +-config HARDLOCKUP_DETECTOR ++config HARDLOCKUP_DETECTOR_NMI + def_bool y + depends on LOCKUP_DETECTOR && !HAVE_NMI_WATCHDOG + depends on PERF_EVENTS && HAVE_PERF_EVENTS_NMI + ++config HARDLOCKUP_DETECTOR_OTHER_CPU ++ def_bool y ++ depends on LOCKUP_DETECTOR && SMP ++ depends on !HARDLOCKUP_DETECTOR_NMI && !HAVE_NMI_WATCHDOG ++ ++config HARDLOCKUP_DETECTOR ++ def_bool y ++ depends on HARDLOCKUP_DETECTOR_NMI || HARDLOCKUP_DETECTOR_OTHER_CPU ++ + config BOOTPARAM_HARDLOCKUP_PANIC + bool "Panic (Reboot) On Hard Lockups" + depends on HARDLOCKUP_DETECTOR +diff --git a/lib/Makefile b/lib/Makefile +index 0211d2b..3d49766 100644 +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -80,6 +80,7 @@ obj-$(CONFIG_REED_SOLOMON) += reed_solomon/ + obj-$(CONFIG_BCH) += bch.o + obj-$(CONFIG_LZO_COMPRESS) += lzo/ + obj-$(CONFIG_LZO_DECOMPRESS) += lzo/ ++obj-$(CONFIG_GZIP_COMPRESS) += gzip/ + obj-$(CONFIG_LZ4_COMPRESS) += lz4/ + obj-$(CONFIG_LZ4HC_COMPRESS) += lz4/ + obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/ +diff --git a/lib/gzip/Makefile b/lib/gzip/Makefile +new file mode 100644 +index 0000000..0d8bc8b +--- /dev/null ++++ b/lib/gzip/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_GZIP_COMPRESS) += gzip_rm.o ++gzip_rm-y := util.o deflate.o zip.o bits.o trees.o gzip.o +diff --git a/lib/gzip/bits.c b/lib/gzip/bits.c +new file mode 100644 +index 0000000..e3a37d6 +--- /dev/null ++++ b/lib/gzip/bits.c +@@ -0,0 +1,164 @@ ++/* bits.c -- output variable-length bit strings ++* Copyright (C) 1992-1993 Jean-loup Gailly ++* This is free software; you can redistribute it and/or modify it under the ++* terms of the GNU General Public License, see the file COPYING. ++*/ ++ ++/* ++* PURPOSE ++* ++* Output variable-length bit strings. Compression can be done ++* to a file or to memory. (The latter is not supported in this version.) ++* ++* DISCUSSION ++* ++* The PKZIP "deflate" file format interprets compressed file data ++* as a sequence of bits. Multi-bit strings in the file may cross ++* byte boundaries without restriction. ++* ++* The first bit of each byte is the low-order bit. ++* ++* The routines in this file allow a variable-length bit value to ++* be output right-to-left (useful for literal values). For ++* left-to-right output (useful for code strings from the tree routines), ++* the bits must have been reversed first with bi_reverse(). ++* ++* For in-memory compression, the compressed bit stream goes directly ++* into the requested output buffer. The input data is read in blocks ++* by the mem_read() function. The buffer is limited to 64K on 16 bit ++* machines. ++* ++* INTERFACE ++* ++* void bi_init (FILE *zipfile) ++* Initialize the bit string routines. ++* ++* void send_bits (int value, int length) ++* Write out a bit string, taking the source bits right to ++* left. ++* ++* int bi_reverse (int value, int length) ++* Reverse the bits of a bit string, taking the source bits left to ++* right and emitting them right to left. ++* ++* void bi_windup (void) ++* Write out any remaining bits in an incomplete byte. ++* ++* void copy_block(char *buf, unsigned len, int header) ++* Copy a stored block to the zip file, storing first the length and ++* its one's complement if requested. ++* ++*/ ++ ++#include "gzip.h" ++ ++/* =========================================================================== ++* Local data used by the "bit string" routines. ++*/ ++ ++local unsigned short bi_buf = 0; ++/* Output buffer. bits are inserted starting at the bottom (least significant ++* bits). ++*/ ++ ++#define Buf_size (8 * 2*sizeof(char)) ++/* Number of bits used within bi_buf. (bi_buf might be implemented on ++* more than 16 bits on some systems.) ++*/ ++ ++local int bi_valid = 0; ++/* Number of valid bits in bi_buf. All bits above the last valid bit ++* are always zero. ++*/ ++ ++int (* read_buf )( char * buf, unsigned size ); ++/* Current input function. Set to mem_read for in-memory compression */ ++ ++/* =========================================================================== ++* Initialize the bit string routines. ++*/ ++void bi_init ( void ) ++{ ++ bi_buf = 0; ++ bi_valid = 0; ++ ++ /* Set the defaults for file compression. They are set by memcompress ++ * for in-memory compression. ++ */ ++ read_buf = mem_read; ++} ++ ++/* =========================================================================== ++* Send a value on a given number of bits. ++* IN assertion: length <= 16 and value fits in length bits. ++*/ ++void send_bits( value, length ) ++int value; /* value to send */ ++int length; /* number of bits */ ++{ ++ /* If not enough room in bi_buf, use (valid) bits from bi_buf and ++ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) ++ * unused bits in value. ++ */ ++ if ( bi_valid > ( int )Buf_size - length ) { ++ bi_buf |= ( value << bi_valid ); ++ put_short( bi_buf ); ++ bi_buf = ( ush )value >> ( Buf_size - bi_valid ); ++ bi_valid += length - Buf_size; ++ } else { ++ bi_buf |= value << bi_valid; ++ bi_valid += length; ++ } ++} ++ ++/* =========================================================================== ++* Reverse the first len bits of a code, using straightforward code (a faster ++* method would use a table) ++* IN assertion: 1 <= len <= 15 ++*/ ++unsigned bi_reverse( unsigned code, int len ) ++{ ++ register unsigned res = 0; ++ do ++ { ++ res |= code & 1; ++ code >>= 1, res <<= 1; ++ } while (-- len > 0 ); ++ return res >> 1; ++} ++ ++/* =========================================================================== ++* Write out any remaining bits in an incomplete byte. ++*/ ++void bi_windup() ++{ ++ if ( bi_valid > 8 )/* �Ƿ����short���� */ ++ { ++ put_short( bi_buf ); ++ } ++ else if ( bi_valid > 0 ) ++ { ++ put_byte( bi_buf ); ++ } ++ bi_buf = 0; ++ bi_valid = 0; ++} ++ ++/* =========================================================================== ++* Copy a stored block to the zip file, storing first the length and its ++* one's complement if requested. ++*/ ++void copy_block( char * buf, unsigned len, int header ) ++{ ++ bi_windup(); /* align on byte boundary */ ++ ++ if ( header ) ++ { ++ put_short(( ush )len ); ++ put_short(( ush )~ len ); ++ } ++ while ( len --) ++ { ++ put_byte(* buf ++); ++ } ++} +diff --git a/lib/gzip/deflate.c b/lib/gzip/deflate.c +new file mode 100644 +index 0000000..a074cd7 +--- /dev/null ++++ b/lib/gzip/deflate.c +@@ -0,0 +1,553 @@ ++/* deflate.c -- compress data using the deflation algorithm ++* Copyright (C) 1992-1993 Jean-loup Gailly ++* This is free software; you can redistribute it and/or modify it under the ++* terms of the GNU General Public License, see the file COPYING. ++*/ ++ ++/* ++* PURPOSE ++* ++* Identify new text as repetitions of old text within a fixed- ++* length sliding window trailing behind the new text. ++* ++* DISCUSSION ++* ++* The "deflation" process depends on being able to identify portions ++* of the input text which are identical to earlier input (within a ++* sliding window trailing behind the input currently being processed). ++* ++* The most straightforward technique turns out to be the fastest for ++* most input files: try all possible matches and select the longest. ++* The key feature of this algorithm is that insertions into the string ++* dictionary are very simple and thus fast, and deletions are avoided ++* completely. Insertions are performed at each input character, whereas ++* string matches are performed only when the previous match ends. So it ++* is preferable to spend more time in matches to allow very fast string ++* insertions and avoid deletions. The matching algorithm for small ++* strings is inspired from that of Rabin & Karp. A brute force approach ++* is used to find longer strings when a small match has been found. ++* A similar algorithm is used in comic (by Jan-Mark Wams) and freeze ++* (by Leonid Broukhis). ++* A previous version of this file used a more sophisticated algorithm ++* (by Fiala and Greene) which is guaranteed to run in linear amortized ++* time, but has a larger average cost, uses more memory and is patented. ++* However the F&G algorithm may be faster for some highly redundant ++* files if the parameter max_chain_length (described below) is too large. ++* ++* ACKNOWLEDGEMENTS ++* ++* The idea of lazy evaluation of matches is due to Jan-Mark Wams, and ++* I found it in 'freeze' written by Leonid Broukhis. ++* Thanks to many info-zippers for bug reports and testing. ++* ++* REFERENCES ++* ++* APPNOTE.TXT documentation file in PKZIP 1.93a distribution. ++* ++* A description of the Rabin and Karp algorithm is given in the book ++* "Algorithms" by R. Sedgewick, Addison-Wesley, p252. ++* ++* Fiala,E.R., and Greene,D.H. ++* Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 ++* ++* INTERFACE ++* ++* void lm_init (int pack_level, ush *flags) ++* Initialize the "longest match" routines for a new file ++* ++* ulg deflate (void) ++* Processes a new input file and return its compressed length. Sets ++* the compressed length, crc, deflate flags and internal file ++* attributes. ++*/ ++ ++#include <linux/unistd.h> ++#include "gzip.h" ++#include "lzw.h" /* just for consistency checking */ ++ ++/* =========================================================================== ++* Configuration parameters ++*/ ++ ++/* Compile with MEDIUM_MEM to reduce the memory requirements or ++* with SMALL_MEM to use as little memory as possible. Use BIG_MEM if the ++* entire input file can be held in memory (not possible on 16 bit systems). ++* Warning: defining these symbols affects HASH_BITS (see below) and thus ++* affects the compression ratio. The compressed output ++* is still correct, and might even be smaller in some cases. ++*/ ++ ++#ifndef HASH_BITS ++#define HASH_BITS 15 /* hash */ ++/* For portability to 16 bit machines, do not use values above 15. */ ++#endif ++ ++/* To save space (see unlzw.c), we overlay prev+head with tab_prefix and ++* window with tab_suffix. Check that we can do this: ++*/ ++#if (WSIZE<<1) > (1<<BITS) /* �����ж� */ ++//error: cannot overlay window with tab_suffix and prev with tab_prefix0 ++#endif ++#if HASH_BITS > BITS-1 ++//error: cannot overlay head with tab_prefix1 ++#endif ++ ++#define HASH_SIZE (unsigned)(1<<HASH_BITS) ++#define HASH_MASK (HASH_SIZE-1) ++#define WMASK (WSIZE-1) ++ /* HASH_SIZE and WSIZE must be powers of two */ ++ /* Tail of hash chains */ ++#define NIL 0 ++ ++ /* speed options for the general purpose bit flag */ ++#define FAST 4 ++ /* speed options for the general purpose bit flag */ ++#define SLOW 2 ++ ++#ifndef TOO_FAR ++#define TOO_FAR 4096 /* TOO FAR */ ++#endif ++ /* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ ++ ++ /* =========================================================================== ++ * Local data used by the "longest match" routines. ++ */ ++ ++ typedef ush Pos; ++ typedef unsigned IPos; ++ /* A Pos is an index in the character window. We use short instead of int to ++ * save space in the various tables. IPos is used only for parameter passing. ++ */ ++ ++ /* DECLARE(uch, window, 2L*WSIZE); */ ++ /* Sliding window. Input bytes are read into the second half of the window, ++ * and move to the first half later to keep a dictionary of at least WSIZE ++ * bytes. With this organization, matches are limited to a distance of ++ * WSIZE-MAX_MATCH bytes, but this ensures that IO is always ++ * performed with a length multiple of the block size. Also, it limits ++ * the window size to 64K, which is quite useful on MSDOS. ++ * To do: limit the window size to WSIZE+BSZ if SMALL_MEM (the code would ++ * be less efficient). ++ */ ++ ++ /* DECLARE(Pos, prev, WSIZE); */ ++ /* Link to older string with same hash index. To limit the size of this ++ * array to 64K, this link is maintained only for the last 32K strings. ++ * An index in this array is thus a window index modulo 32K. ++ */ ++ ++ /* DECLARE(Pos, head, 1<<HASH_BITS); */ ++ /* Heads of the hash chains or NIL. */ ++ ++ ulg window_size = ( ulg )2 * WSIZE; ++ /* window size, 2*WSIZE except for MMAP or BIG_MEM, where it is the ++ * input file length plus MIN_LOOKAHEAD. ++ */ ++ ++ int block_start = 0; ++ /* window position at the beginning of the current output block. Gets ++ * negative when the window is moved backwards. ++ */ ++ ++ local unsigned ins_h = 0; /* hash index of string to be inserted */ ++ ++#define H_SHIFT ((HASH_BITS+MIN_MATCH-1)/MIN_MATCH) ++ /* Number of bits by which ins_h and del_h must be shifted at each ++ * input step. It must be such that after MIN_MATCH steps, the oldest ++ * byte no longer takes part in the hash key, that is: ++ * H_SHIFT * MIN_MATCH >= HASH_BITS ++ */ ++ ++ unsigned int near prev_length = 0; ++ /* Length of the best match at previous step. Matches not greater than this ++ * are discarded. This is used in the lazy match evaluation. ++ */ ++ ++ unsigned near strstart = 0; /* start of string to insert */ ++ unsigned near match_start = 0; /* start of matching string */ ++ local int eofile = 0; /* flag set at end of input file */ ++ local unsigned lookahead = 0; /* number of valid bytes ahead in window */ ++ ++ unsigned near max_chain_length = 0; ++ /* To speed up deflation, hash chains are never searched beyond this length. ++ * A higher limit improves compression ratio but degrades the speed. ++ */ ++ ++ local unsigned int max_lazy_match = 0; ++ /* Attempt to find a better match only when the current match is strictly ++ * smaller than this value. This mechanism is used only for compression ++ * levels >= 4. ++ */ ++#define max_insert_length max_lazy_match ++ /* Insert new strings in the hash table only if the match length ++ * is not greater than this length. This saves time but degrades compression. ++ * max_insert_length is used only for compression levels <= 3. ++ */ ++ /* compression level (1..9) */ ++ ++ unsigned near good_match = 0; ++ /* Use a faster search when the previous match is longer than this */ ++ ++ /* Values for max_lazy_match, good_match and max_chain_length, depending on ++ * the desired pack level (0..9). The values given below have been tuned to ++ * exclude worst case performance for pathological files. Better values may be ++ * found for specific files. ++ */ ++ ++ typedef struct config { ++ ush good_length; /* reduce lazy search above this match length */ ++ ush max_lazy; /* do not perform lazy search above this match length */ ++ ush nice_length; /* quit search above this match length */ ++ ush max_chain; ++ } config; ++ ++ int near nice_match = 0; /* Stop searching when current match exceeds this */ ++ ++ /* good lazy nice chain */ ++ local config configuration_table[10] = { ++ { 0, 0, 0, 0}, /* store only *//* 0 */ ++ { 4, 4, 8, 4}, /* maximum speed, no lazy matches */ /* 1 */ ++ { 4, 5, 16, 8}, /* 2 */ ++ { 4, 6, 32, 32}, /* 3 */ ++ ++ { 4, 4, 16, 16}, /* lazy matches *//* 4 */ ++ { 8, 16, 32, 32},/* 5 */ ++ { 8, 16, 128, 128},/* 6 */ ++ { 8, 32, 128, 256},/* 7 */ ++ { 32, 128, 258, 1024},/* 8 */ ++ { 32, 258, 258, 4096}}; /* maximum compression *//* 9 */ ++ ++ /* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 ++ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different ++ * meaning. ++ */ ++ ++#define EQUAL 0 ++ /* result of memcmp for equal strings */ ++ ++ /* =========================================================================== ++ * Prototypes for local functions. ++ */ ++ local void fill_window( void ); ++ ++ int longest_match( IPos cur_match ); ++ ++ /* =========================================================================== ++ * Update a hash value with the given input byte ++ * IN assertion: all calls to to UPDATE_HASH are made with consecutive ++ * input characters, so that a running hash key can be computed from the ++ * previous key instead of complete recalculation each time. ++ */ ++#define UPDATE_HASH(h,c) (h = (((h)<<H_SHIFT) ^ (c)) & HASH_MASK) ++ ++ /* =========================================================================== ++ * Insert string s in the dictionary and set match_head to the previous head ++ * of the hash chain (the most recent string with same hash key). Return ++ * the previous length of the hash chain. ++ * IN assertion: all calls to to INSERT_STRING are made with consecutive ++ * input characters and the first MIN_MATCH bytes of s are valid ++ * (except for the last MIN_MATCH-1 bytes of the input file). ++ */ ++#define INSERT_STRING(s, match_head) \ ++ (UPDATE_HASH(ins_h, window[(s) + MIN_MATCH-1]), \ ++ prev[(s) & WMASK] = match_head = head[ins_h], \ ++ head[ins_h] = (s)) ++ ++ /* =========================================================================== ++ * Initialize the "longest match" routines for a new file ++ */ ++ void lm_init ( int pack_level, ush * flags ) ++ { ++ register unsigned j; ++ ++ /* Initialize the hash table. */ ++ memzero(( char *)head, HASH_SIZE * sizeof(* head )); ++ /* prev will be initialized on the fly */ ++ ++ /* Set the default configuration parameters: ++ */ ++ max_lazy_match = configuration_table[pack_level].max_lazy; ++ good_match = configuration_table[pack_level].good_length; ++ ++ nice_match = configuration_table[pack_level].nice_length; ++ ++ max_chain_length = configuration_table[pack_level].max_chain; ++ /* ??? reduce max_chain_length for binary files */ ++ ++ strstart = 0; ++ block_start = 0L; ++ ++ lookahead = read_buf(( char *)window, ++ sizeof( int ) <= 2 ? ( unsigned )WSIZE : 2 * WSIZE ); /* �������� */ ++ ++ if ( lookahead == 0 || lookahead == ( unsigned )- 1 ) { ++ eofile = 1, lookahead = 0; ++ return; ++ } ++ eofile = 0; ++ /* Make sure that we always have enough lookahead. This is important ++ * if input comes from a device such as a tty. ++ */ ++ while ( lookahead < MIN_LOOKAHEAD && ! eofile ) fill_window(); ++ ++ ins_h = 0; ++ for ( j = 0; j < MIN_MATCH - 1; j ++) UPDATE_HASH( ins_h, window[j] ); ++ /* If lookahead < MIN_MATCH, ins_h is garbage, but this is ++ * not important since only literal bytes will be emitted. ++ */ ++ } ++ ++ /* =========================================================================== ++ * Set match_start to the longest match starting at the given string and ++ * return its length. Matches shorter or equal to prev_length are discarded, ++ * in which case the result is equal to prev_length and match_start is ++ * garbage. ++ * IN assertions: cur_match is the head of the hash chain for the current ++ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 ++ */ ++ /* For MSDOS, OS/2 and 386 Unix, an optimized version is in match.asm or ++ * match.s. The code is functionally equivalent, so you can use the C version ++ * if desired. ++ */ ++ int longest_match( IPos cur_match ) ++ { ++ unsigned chain_length = max_chain_length; /* max hash chain length */ ++ register uch * scan = window + strstart; /* current string */ ++ register uch * match; /* matched string */ ++ register int len; /* length of current match */ ++ int best_len = prev_length; /* best match length so far */ ++ IPos limit = strstart > ( IPos )MAX_DIST ? strstart - ( IPos )MAX_DIST : NIL; ++ /* Stop when cur_match becomes <= limit. To simplify the code, ++ * we prevent matches with the string of window index 0. ++ */ ++ ++ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. ++ * It is easy to get rid of this optimization if necessary. ++ */ ++ register uch * strend = window + strstart + MAX_MATCH; ++ register uch scan_end1 = scan[best_len - 1]; ++ register uch scan_end = scan[best_len]; ++ ++ if ( prev_length >= good_match ) { ++ /* Do not waste too much time if we already have a good match: */ ++ chain_length >>= 2; ++ } ++ ++ do ++ { ++ Assert( cur_match < strstart, "no future" ); ++ match = window + cur_match; ++ ++ /* Skip to next match if the match length cannot increase ++ * or if the match length is less than 2: ++ */ ++ ++ if ( match[best_len] != scan_end || ++ match[best_len - 1] != scan_end1 || ++ * match != * scan || ++ *++ match != scan[1] ) continue; ++ ++ /* The check at best_len-1 can be removed because it will be made ++ * again later. (This heuristic is not always a win.) ++ * It is not necessary to compare scan[2] and match[2] since they ++ * are always equal when the other bytes match, given that ++ * the hash keys are equal and that HASH_BITS >= 8. ++ */ ++ scan += 2, match ++; ++ ++ /* We check for insufficient lookahead only every 8th comparison; ++ * the 256th check will be made at strstart+258. ++ */ ++ do { ++ } while (*++ scan == *++ match && *++ scan == *++ match && ++ *++ scan == *++ match && *++ scan == *++ match && ++ *++ scan == *++ match && *++ scan == *++ match && ++ *++ scan == *++ match && *++ scan == *++ match && ++ scan < strend ); ++ ++ len = MAX_MATCH - ( int )( strend - scan ); ++ scan = strend - MAX_MATCH; ++ ++ if ( len > best_len ) { ++ match_start = cur_match; ++ best_len = len; ++ if ( len >= nice_match ) break; ++ scan_end1 = scan[best_len - 1]; ++ scan_end = scan[best_len]; ++ } ++ } while (( cur_match = prev[cur_match & WMASK] ) > limit ++ && -- chain_length != 0 ); ++ ++ return best_len; ++ } ++ ++#define check_match(start, match, length) ++ ++ /* =========================================================================== ++ * Fill the window when the lookahead becomes insufficient. ++ * Updates strstart and lookahead, and sets eofile if end of input file. ++ * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 ++ * OUT assertions: at least one byte has been read, or eofile is set; ++ * file reads are performed for at least two bytes (required for the ++ * translate_eol option). ++ */ ++ local void fill_window() ++ { ++ register unsigned n, m; ++ unsigned more = ( unsigned )( window_size - ( ulg )lookahead - ( ulg )strstart ); ++ /* Amount of free space at the end of the window. */ ++ ++ /* If the window is almost full and there is insufficient lookahead, ++ * move the upper half to the lower one to make room in the upper half. ++ */ ++ if ( more == ( unsigned )- 1 ) { ++ /* Very unlikely, but possible on 16 bit machine if strstart == 0 ++ * and lookahead == 1 (input done one byte at time) ++ */ ++ more --; ++ } else if ( strstart >= WSIZE + MAX_DIST ) { ++ /* By the IN assertion, the window is not empty so we can't confuse ++ * more == 0 with more == 64K on a 16 bit machine. ++ */ ++ Assert( window_size == ( ulg )2 * WSIZE, "no sliding with BIG_MEM" ); ++ ++ memcpy(( char *)window, ( char *)window + WSIZE, ( unsigned )WSIZE ); ++ match_start -= WSIZE; ++ strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */ ++ ++ block_start -= ( int ) WSIZE; ++ ++ for ( n = 0; n < HASH_SIZE; n ++) { ++ m = head[n]; ++ head[n] = ( Pos )( m >= WSIZE ? m - WSIZE : NIL ); ++ } ++ for ( n = 0; n < WSIZE; n ++) { ++ m = prev[n]; ++ prev[n] = ( Pos )( m >= WSIZE ? m - WSIZE : NIL ); ++ /* If n is not on any hash chain, prev[n] is garbage but ++ * its value will never be used. ++ */ ++ } ++ more += WSIZE; ++ } ++ /* At this point, more >= 2 */ ++ if (! eofile ) { ++ n = read_buf(( char *)window + strstart + lookahead, more ); ++ if ( n == 0 || n == ( unsigned )- 1 ) { ++ eofile = 1; ++ } else { ++ lookahead += n; ++ } ++ } ++ } ++ ++ /* =========================================================================== ++ * Flush the current block, with given end-of-file flag. ++ * IN assertion: strstart is set to the end of the current match. ++ */ ++#define FLUSH_BLOCK(eof) \ ++ flush_block(block_start >= 0L ? (char*)&window[(unsigned)block_start] : \ ++ (char*)NULL, (long)strstart - block_start, (eof)) ++ ++ /* =========================================================================== ++ * Same as above, but achieves better compression. We use a lazy ++ * evaluation for matches: a match is finally adopted only if there is ++ * no better match at the next window position. ++ */ ++ ulg deflate() ++ { ++ IPos hash_head; /* head of hash chain */ ++ IPos prev_match; /* previous match */ ++ int flush; /* set if current block must be flushed */ ++ int match_available = 0; /* set if previous match exists */ ++ register unsigned match_length = MIN_MATCH - 1; /* length of best match */ ++ ++ /* Process the input block. */ ++ while ( lookahead != 0 ) { ++ /* Insert the string window[strstart .. strstart+2] in the ++ * dictionary, and set hash_head to the head of the hash chain: ++ */ ++ INSERT_STRING( strstart, hash_head ); ++ ++ /* Find the longest match, discarding those <= prev_length. ++ */ ++ prev_length = match_length, prev_match = match_start; ++ match_length = MIN_MATCH - 1; ++ ++ if ( hash_head != NIL && prev_length < max_lazy_match && ++ strstart - hash_head <= MAX_DIST ) { ++ /* To simplify the code, we prevent matches with the string ++ * of window index 0 (in particular we have to avoid a match ++ * of the string with itself at the start of the input file). ++ */ ++ match_length = longest_match ( hash_head ); ++ /* longest_match() sets match_start */ ++ if ( match_length > lookahead ) match_length = lookahead; ++ ++ /* Ignore a length 3 match if it is too distant: */ ++ if ( match_length == MIN_MATCH && strstart - match_start > TOO_FAR ){ ++ /* If prev_match is also MIN_MATCH, match_start is garbage ++ * but we will ignore the current match anyway. ++ */ ++ match_length --; ++ } ++ } ++ /* If there was a match at the previous step and the current ++ * match is not better, output the previous match: ++ */ ++ if ( prev_length >= MIN_MATCH && match_length <= prev_length ) { ++ ++ check_match( strstart - 1, prev_match, prev_length ); ++ ++ flush = ct_tally( strstart - 1 - prev_match, prev_length - MIN_MATCH ); ++ ++ /* Insert in hash table all strings up to the end of the match. ++ * strstart-1 and strstart are already inserted. ++ */ ++ lookahead -= prev_length - 1; ++ prev_length -= 2; /* -2 */ ++ do { ++ strstart ++; ++ INSERT_STRING( strstart, hash_head ); ++ /* strstart never exceeds WSIZE-MAX_MATCH, so there are ++ * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH ++ * these bytes are garbage, but it does not matter since the ++ * next lookahead bytes will always be emitted as literals. ++ */ ++ } while (-- prev_length != 0 ); ++ match_available = 0; ++ match_length = MIN_MATCH - 1; ++ strstart ++; ++ if ( flush ) FLUSH_BLOCK( 0 ), block_start = strstart; ++ } else if ( match_available ) { ++ /* If there was no match at the previous position, output a ++ * single literal. If there was a match but the current match ++ * is longer, truncate the previous match to a single literal. ++ */ ++ Tracevv(( stderr, "%c", window[strstart - 1] )); ++ if ( ct_tally ( 0, window[strstart - 1] )) { ++ FLUSH_BLOCK( 0 ), block_start = strstart; ++ } ++ strstart ++; ++ lookahead --; ++ } else { ++ /* There is no previous match to compare with, wait for ++ * the next step to decide. ++ */ ++ match_available = 1; ++ strstart ++; ++ lookahead --; ++ } ++ Assert ( strstart <= isize && lookahead <= isize, "a bit too far" ); ++ ++ /* Make sure that we always have enough lookahead, except ++ * at the end of the input file. We need MAX_MATCH bytes ++ * for the next match, plus MIN_MATCH bytes to insert the ++ * string following the next match. ++ */ ++ while ( lookahead < MIN_LOOKAHEAD && ! eofile ) fill_window(); ++ } ++ if ( match_available ) ct_tally ( 0, window[strstart - 1] ); ++ ++ return FLUSH_BLOCK( 1 ); /* eof */ ++ } +diff --git a/lib/gzip/gzip.c b/lib/gzip/gzip.c +new file mode 100644 +index 0000000..f229585 +--- /dev/null ++++ b/lib/gzip/gzip.c +@@ -0,0 +1,156 @@ ++/* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface ++* Copyright (C) 1992-1993 Jean-loup Gailly ++* The unzip code was written and put in the public domain by Mark Adler. ++* Portions of the lzw code are derived from the public domain 'compress' ++* written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies, ++* Ken Turkowski, Dave Mack and Peter Jannesen. ++* ++* See the license_msg below and the file COPYING for the software license. ++* See the file algorithm.doc for the compression algorithms and file formats. ++*/ ++ ++/* Compress files with zip algorithm and 'compress' interface. ++* See usage() and help() functions below for all options. ++* Outputs: ++* file.gz: compressed file with same mode, owner, and utimes ++* or stdout with -c option or if stdin used as input. ++* If the output file name had to be truncated, the original name is kept ++* in the compressed file. ++* On MSDOS, file.tmp -> file.tmz. On VMS, file.tmp -> file.tmp-gz. ++* ++* Using gz on MSDOS would create too many file name conflicts. For ++* example, foo.txt -> foo.tgz (.tgz must be reserved as shorthand for ++* tar.gz). Similarly, foo.dir and foo.doc would both be mapped to foo.dgz. ++* I also considered 12345678.txt -> 12345txt.gz but this truncates the name ++* too heavily. There is no ideal solution given the MSDOS 8+3 limitation. ++* ++* For the meaning of all compilation flags, see comments in Makefile.in. ++*/ ++//#include <stdlib.h> ++#include "zipmem.h" ++#include "gzip.h" ++#include "lzw.h" ++ ++/* global buffers */ ++ ++DECLARE( uch, inbuf, INBUFSIZ + INBUF_EXTRA ); ++DECLARE( uch, outbuf, OUTBUFSIZ + OUTBUF_EXTRA ); ++DECLARE( ush, d_buf, DIST_BUFSIZE ); ++DECLARE( uch, window, 2L * WSIZE ); /* ���� */ ++DECLARE( ush, tab_prefix, 1L << BITS ); ++ ++/* local variables */ ++int quiet = 0; /* be very quiet (-q) */ ++int maxbits = BITS; /* max bits per code for LZW */ ++int method = DEFLATED;/* compression method */ ++int level = 6; /* compression level */ ++int exit_code = 0; /* program exit code */ ++int time_stamp; /* original time stamp (modification time) */ ++ ++int bytes_in = 0; /* number of input bytes */ ++int bytes_out = 0; /* number of output bytes */ ++int total_in = 0; /* input bytes for all files */ ++int total_out = 0; /* output bytes for all files */ ++unsigned insize = 0; /* valid bytes in inbuf */ ++unsigned inptr = 0; /* index of next byte to be processed in inbuf */ ++unsigned outcnt = 0; /* bytes in output buffer */ ++ ++//local int get_method(void); ++ ++int (* work )(void) = zip; /* function to call */ ++ ++/* ======================================================================== ++* Compress ++*/ ++int zipmem( char * mem_inptr, int mem_insize, char * mem_outptr ) ++{ ++ time_stamp = 0; ++ ALLOC( uch, inbuf, INBUFSIZ + INBUF_EXTRA ); ++ ALLOC( uch, outbuf, OUTBUFSIZ + OUTBUF_EXTRA ); ++ ALLOC( ush, d_buf, DIST_BUFSIZE ); ++ ALLOC( uch, window, 2L * WSIZE ); /* ���� */ ++ ALLOC( ush, tab_prefix, 1L << BITS ); ++ clear_bufs(); /* clear input and output buffers */ ++ zip_mem_outptr = mem_outptr; /* ������� */ ++ zip_mem_outlen = 0; /* ��������С */ ++ zip_mem_inptr = mem_inptr; /* ����ָ�� */ ++ zip_mem_insize = mem_insize; /* ���뻺�泤�� */ ++ zip_mem_inpos = 0; /* ���뻺�浱ǰλ�� */ ++ zip(); ++ FREE( inbuf ); ++ FREE( outbuf ); ++ FREE( d_buf ); ++ FREE( window ); ++ FREE( tab_prefix ); ++ return zip_mem_outlen; ++} ++ ++#if 0 ++/* ======================================================================== ++* deCompress ++*/ ++int unzipmem( char * mem_inptr, int mem_insize, char * mem_outptr ) ++{ ++ time_stamp = 0; ++ ALLOC( uch, inbuf, INBUFSIZ + INBUF_EXTRA ); ++ ALLOC( uch, outbuf, OUTBUFSIZ + OUTBUF_EXTRA ); ++ ALLOC( ush, d_buf, DIST_BUFSIZE ); ++ ALLOC( uch, window, 2L * WSIZE );/* ���� */ ++ ALLOC( ush, tab_prefix, 1L << BITS ); ++ clear_bufs(); /* clear input and output buffers */ ++ zip_mem_outptr = mem_outptr; /* ������� */ ++ zip_mem_outlen = 0; /* ��������С */ ++ /* ��ѹ�� */ ++ unzip_mem_inptr = mem_inptr; /* ����ָ�� */ ++ unzip_mem_insize = mem_insize; /* ���뻺�泤�� */ ++ unzip_mem_inpos = 0; /* ���뻺�浱ǰλ�� */ ++ get_method(); ++ unzip(); ++ FREE( inbuf ); ++ FREE( outbuf ); ++ FREE( d_buf ); ++ FREE( window ); ++ FREE( tab_prefix ); ++ return zip_mem_outlen; ++} ++ ++/* ======================================================================== ++* Check the magic number of the input file and update ofname if an ++* original name was given and to_stdout is not set. ++* Return the compression method, -1 for error, -2 for warning. ++* Set inptr to the offset of the next byte to be processed. ++* Updates time_stamp if there is one and --no-time is not used. ++* This function may be called repeatedly for an input file consisting ++* of several contiguous gzip'ed members. ++* IN assertions: there is at least one remaining compressed member. ++* If the member is a zip file, it must be the only one. ++*/ ++local int get_method() ++{ ++ /* If --force and --stdout, zcat == cat, so do not complain about ++ * premature end of file: use try_byte instead of get_byte. ++ */ ++ ( char )get_byte(); ++ ( char )get_byte(); ++ method = - 1; /* unknown yet */ ++ /* assume multiple members in gzip file except for record oriented I/O */ ++ ++ method = ( int )get_byte(); ++ work = unzip; ++ ( uch )get_byte(); ++ ( ulg )get_byte(); ++ (( ulg )get_byte()); ++ (( ulg )get_byte()); ++ (( ulg )get_byte()); ++ ( void )get_byte(); /* Ignore extra flags for the moment */ ++ ( void )get_byte(); /* Ignore OS type for the moment */ ++ ++ return method; ++} ++#endif ++ ++int gzip_defalte(void *dst, unsigned int *dst_len, const void *src, unsigned int len) ++{ ++ *dst_len = zipmem((char *)src, len, dst); ++ return 0; ++} +diff --git a/lib/gzip/gzip.h b/lib/gzip/gzip.h +new file mode 100644 +index 0000000..2e4a58e +--- /dev/null ++++ b/lib/gzip/gzip.h +@@ -0,0 +1,238 @@ ++/* gzip.h -- common declarations for all gzip modules ++* Copyright (C) 1992-1993 Jean-loup Gailly. ++* This is free software; you can redistribute it and/or modify it under the ++* terms of the GNU General Public License, see the file COPYING. ++*/ ++ ++/* I don't like nested includes, but the string and io functions are used ++* too often ++*/ ++#include <linux/unistd.h> ++#include <linux/string.h> ++#include <linux/vmalloc.h> ++ ++//#define (args) args ++ ++typedef char * voidp; ++ ++#define memzero(s, n) memset ((voidp)(s), 0, (n)) ++ ++#ifndef RETSIGTYPE ++#define RETSIGTYPE void ++#endif ++ ++#define local //static ++ ++typedef unsigned char uch; ++typedef unsigned short ush; ++typedef unsigned int ulg; ++ ++/* Return codes from gzip */ ++ ++/* Compression methods (see algorithm.doc) */ ++#define STORED 0 /* stored */ ++#define COMPRESSED 1 /* compressed */ ++#define PACKED 2 /* packed */ ++#define LZHED 3 /* lzwed */ ++/* methods 4 to 7 reserved */ ++#define DEFLATED 8 /* ȱʡ */ ++#define MAX_METHODS 9 /* ���ѹ�� */ ++extern int method; /* compression method */ ++ ++/* To save memory for 16 bit systems, some arrays are overlaid between ++* the various modules: ++* deflate: prev+head window d_buf l_buf outbuf ++* unlzw: tab_prefix tab_suffix stack inbuf outbuf ++* inflate: window inbuf ++* unpack: window inbuf prefix_len ++* unlzh: left+right window c_table inbuf c_len ++* For compression, input is done in window[]. For decompression, output ++* is done in window except for unlzw. ++*/ ++#define INBUFSIZ 0x2000 /* input buffer size */ ++#define INBUF_EXTRA 64 /* required by unlzw() */ ++#define OUTBUFSIZ 8192 /* output buffer size */ ++ ++#define OUTBUF_EXTRA 2048 /* required by unlzw() */ ++#define DIST_BUFSIZE 0x2000 /* buffer for distances, see trees.c */ ++ ++#define near ++ ++#define EXTERN(type, array) extern type * near array ++#define DECLARE(type, array, size) type * near array ++ ++#define fcalloc(items,size) vmalloc((size_t)(items)*(size_t)(size)) ++#define fcfree(ptr) vfree(ptr) ++#define ALLOC(type, array, size) { \ ++ array = (type*)fcalloc((size_t)(((size)+1L)/2), 2*sizeof(type));/* malloc */ \ ++ if (array == NULL) error("insufficient memory"); \ ++} ++#define FREE(array) {if (array != NULL) fcfree(array), array=NULL;} ++ ++EXTERN( uch, inbuf ); /* input buffer */ ++EXTERN( uch, outbuf ); /* output buffer */ ++EXTERN( ush, d_buf ); /* buffer for distances, see trees.c */ ++EXTERN( uch, window ); /* Sliding window and suffix table (unlzw) */ ++#define tab_suffix window ++#define tab_prefix prev /* hash link (see deflate.c) */ ++#define head (prev+WSIZE) /* hash head (see deflate.c) */ ++EXTERN( ush, tab_prefix ); /* prefix code (see unlzw.c) */ ++ ++extern unsigned insize; /* valid bytes in inbuf */ ++extern unsigned inptr; /* index of next byte to be processed in inbuf */ ++extern unsigned outcnt; /* bytes in output buffer */ ++ ++extern int bytes_in; /* number of input bytes */ ++extern int bytes_out; /* number of output bytes */ ++extern int header_bytes;/* number of bytes in gzip header */ ++ ++#define isize bytes_in ++/* for compatibility with old zip sources (to be cleaned) */ ++ ++extern int time_stamp; /* original time stamp (modification time) */ ++ ++#define PACK_MAGIC "\037\036" /* Magic header for packed files */ ++#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */ ++#define OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */ ++#define LZH_MAGIC "\037\240" /* Magic header for SCO LZH Compress files*/ ++#define PKZIP_MAGIC "\120\113\003\004" /* Magic header for pkzip files */ ++ ++/* gzip flag byte */ ++#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ ++#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ ++#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ ++#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ ++#define COMMENT 0x10 /* bit 4 set: file comment present */ ++#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ ++#define RESERVED 0xC0 /* bit 6,7: reserved */ ++ ++/* internal file attribute */ ++#define UNKNOWN 0xffff /* δ֪ */ ++#define BINARY 0 /* binary */ ++#define ASCII 1 /* ascII */ ++ ++#ifndef WSIZE ++#ifdef CONFIG_ARCH_HI3516CV300 ++#define WSIZE 0x2000 /* widow size for hi3516cv300 is 8k*/ ++#else ++#define WSIZE 0x8000 /* window size--must be a power of two, and */ ++ /* at least 32K for zip's deflate method */ ++#endif ++#endif ++ ++#define MIN_MATCH 3 /* min match */ ++#define MAX_MATCH 258 /* max match*/ ++/* The minimum and maximum match lengths */ ++ ++#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) ++/* Minimum amount of lookahead, except at the end of the input file. ++* See deflate.c for comments about the MIN_MATCH+1. ++*/ ++ ++#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) ++/* In order to simplify the code, particularly on 16 bit machines, match ++* distances are limited to MAX_DIST instead of WSIZE. ++*/ ++ ++extern int exit_code; /* program exit code */ ++extern int quiet; /* be quiet (-q) */ ++extern int level; /* compression level */ ++ ++#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) ++#define try_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) ++ ++/* put_byte is used for the compressed output, put_ubyte for the ++* uncompressed output. However unlzw() uses window for its ++* suffix table instead of its output buffer, so it does not use put_ubyte ++* (to be cleaned up). ++*/ ++#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\ ++ flush_outbuf();} ++#define put_ubyte(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\ ++ flush_window();} ++ ++/* Output a 16 bit value, lsb first */ ++#define put_short(w) \ ++{ if (outcnt < OUTBUFSIZ-2) { /* compare */\ ++ outbuf[outcnt++] = (uch) ((w) & 0xff);/* ��λ */ \ ++ outbuf[outcnt++] = (uch) ((ush)(w) >> 8);/* ��λ */ \ ++} else { \ ++ put_byte((uch)((w) & 0xff)); /* ��λ */\ ++ put_byte((uch)((ush)(w) >> 8));/* ��λ */ \ ++} \ ++} ++ ++/* Output a 32 bit value to the bit stream, lsb first */ ++#define put_long(n) { \ ++ put_short((n) & 0xffff);/* ��λ */ \ ++ put_short(((ulg)(n)) >> 16); /* ��λ */\ ++} ++ ++#define seekable() 0 /* force sequential output */ ++#define translate_eol 0 /* no option -a yet */ ++ ++/* Macros for getting two-byte and four-byte header values */ ++#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8)) ++/* Macros for getting two-byte and four-byte header values */ ++#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16)) ++ ++/* Diagnostic functions */ ++#define Assert(cond,msg) ++#define Trace(x) ++#define Tracev(x) ++#define Tracevv(x) ++#define Tracec(c,x) ++#define Tracecv(c,x) ++ ++/*#define WARN(msg) {if (!quiet) fprintf msg ; \ ++ if (exit_code == OK) exit_code = WARNING;}*/ ++ ++extern char * zip_mem_inptr; ++extern int zip_mem_insize; ++extern int zip_mem_inpos; ++ ++extern char * unzip_mem_inptr ; ++extern int unzip_mem_insize ; ++extern int unzip_mem_inpos; ++ ++extern char * zip_mem_outptr; ++extern int zip_mem_outlen ; ++ ++/* in zip.c: */ ++extern int zip ( void ); ++extern int mem_read ( char * buf, unsigned size ); ++/* in unzip.c */ ++extern int unzip ( void ); ++ ++/* in deflate.c */ ++void lm_init ( int pack_level, ush * flags ); ++ulg deflate ( void ); ++ ++/* in trees.c */ ++void ct_init ( ush * attr, int * method ); ++int ct_tally ( int dist, int lc ); ++ulg flush_block ( char * buf, ulg stored_len, int eof ); ++ ++/* in bits.c */ ++void bi_init(void); ++void send_bits ( int value, int length ); ++unsigned bi_reverse ( unsigned value, int length ); ++void bi_windup ( void ); ++void copy_block ( char * buf, unsigned len, int header ); ++extern int (* read_buf ) ( char * buf, unsigned size ); ++ ++/* in util.c: */ ++extern ulg updcrc ( uch * s, unsigned n ); ++extern void clear_bufs ( void ); ++extern int fill_inbuf ( void ); ++extern void flush_outbuf ( void ); ++extern void flush_window ( void ); ++extern void write_buf ( voidp buf, unsigned cnt ); ++extern void error ( char * m ); ++extern void warn ( char * a, char * b ); ++extern void read_error ( void ); ++extern void write_error ( void ); ++extern voidp xmalloc ( unsigned int size ); ++ ++/* in inflate.c */ ++extern int inflate ( void ); +diff --git a/lib/gzip/inflate.c b/lib/gzip/inflate.c +new file mode 100644 +index 0000000..228c2bb +--- /dev/null ++++ b/lib/gzip/inflate.c +@@ -0,0 +1,869 @@ ++/* inflate.c -- Not copyrighted 1992 by Mark Adler ++version c10p1, 10 January 1993 */ ++ ++/* You can do whatever you like with this source file, though I would ++prefer that if you modify it and redistribute it that you include ++comments to that effect with your name and the date. Thank you. ++[The history has been moved to the file ChangeLog.] ++*/ ++ ++/* ++Inflate deflated (PKZIP's method 8 compressed) data. The compression ++method searches for as much of the current string of bytes (up to a ++length of 258) in the previous 32K bytes. If it doesn't find any ++matches (of at least length 3), it codes the next byte. Otherwise, it ++codes the length of the matched string and its distance backwards from ++the current position. There is a single Huffman code that codes both ++single bytes (called "literals") and match lengths. A second Huffman ++code codes the distance information, which follows a length code. Each ++length or distance code actually represents a base value and a number ++of "extra" (sometimes zero) bits to get to add to the base value. At ++the end of each deflated block is a special end-of-block (EOB) literal/ ++length code. The decoding process is basically: get a literal/length ++code; if EOB then done; if a literal, emit the decoded byte; if a ++length then get the distance and emit the referred-to bytes from the ++sliding window of previously emitted data. ++ ++There are (currently) three kinds of inflate blocks: stored, fixed, and ++dynamic. The compressor deals with some chunk of data at a time, and ++decides which method to use on a chunk-by-chunk basis. A chunk might ++typically be 32K or 64K. If the chunk is uncompressible, then the ++"stored" method is used. In this case, the bytes are simply stored as ++is, eight bits per byte, with none of the above coding. The bytes are ++preceded by a count, since there is no longer an EOB code. ++ ++If the data is compressible, then either the fixed or dynamic methods ++are used. In the dynamic method, the compressed data is preceded by ++an encoding of the literal/length and distance Huffman codes that are ++to be used to decode this block. The representation is itself Huffman ++coded, and so is preceded by a description of that code. These code ++descriptions take up a little space, and so for small blocks, there is ++a predefined set of codes, called the fixed codes. The fixed method is ++used if the block codes up smaller that way (usually for quite small ++chunks), otherwise the dynamic method is used. In the latter case, the ++codes are customized to the probabilities in the current block, and so ++can code it much better than the pre-determined fixed codes. ++ ++The Huffman codes themselves are decoded using a mutli-level table ++lookup, in order to maximize the speed of decoding plus the speed of ++building the decoding tables. See the comments below that precede the ++lbits and dbits tuning parameters. ++*/ ++ ++/* ++Notes beyond the 1.93a appnote.txt: ++ ++1. Distance pointers never point before the beginning of the output ++stream. ++2. Distance pointers can point back across blocks, up to 32k away. ++3. There is an implied maximum of 7 bits for the bit length table and ++15 bits for the actual data. ++4. If only one code exists, then it is encoded using one bit. (Zero ++would be more efficient, but perhaps a little confusing.) If two ++codes exist, they are coded using one bit each (0 and 1). ++5. There is no way of sending zero distance codes--a dummy must be ++sent if there are none. (History: a pre 2.0 version of PKZIP would ++store blocks with no distance codes, but this was discovered to be ++too harsh a criterion.) Valid only for 1.93a. 2.04c does allow ++zero distance codes, which is sent as one code of zero bits in ++length. ++6. There are up to 286 literal/length codes. Code 256 represents the ++end-of-block. Note however that the static length tree defines ++288 codes just to fill out the Huffman codes. Codes 286 and 287 ++cannot be used though, since there is no length base or extra bits ++defined for them. Similarly, there are up to 30 distance codes. ++However, static trees define 32 codes (all 5 bits) to fill out the ++Huffman codes, but the last two had better not show up in the data. ++7. Unzip can check dynamic Huffman blocks for complete code sets. ++The exception is that a single code would not be complete (see #4). ++8. The five bits following the block type is really the number of ++literal codes sent minus 257. ++9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits ++(1+6+6). Therefore, to output three times the length, you output ++three codes (1+1+1), whereas to output four times the same length, ++you only need two codes (1+3). Hmm. ++10. In the tree reconstruction algorithm, Code = Code + Increment ++only if BitLength(i) is not zero. (Pretty obvious.) ++11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) ++12. Note: length code 284 can represent 227-258, but length code 285 ++really is 258. The last length deserves its own, short code ++since it gets used a lot in very redundant files. The length ++258 is special since 258 - 3 (the min match length) is 255. ++13. The literal/length and distance code bit lengths are read as a ++single stream of lengths. It is possible (and advantageous) for ++a repeat code (16, 17, or 18) to go across the boundary between ++the two sets of lengths. ++*/ ++//#include <stdlib.h> ++#include "gzip.h" ++#define slide window ++ ++/* Huffman code lookup table entry--this entry is four bytes for machines ++that have 16-bit pointers (e.g. PC's in the small or medium model). ++Valid extra bits are 0..13. e == 15 is EOB (end of block), e == 16 ++means that v is a literal, 16 < e < 32 means that v is a pointer to ++the next table, which codes e - 16 bits, and lastly e == 99 indicates ++an unused code. If a code with e == 99 is looked up, this implies an ++error in the data. */ ++struct huft { ++ uch e; /* number of extra bits or operation */ ++ uch b; /* number of bits in this code or subcode */ ++ union { ++ ush n; /* literal, length base, or distance base */ ++ struct huft * t; /* pointer to next level of table */ ++ } v; ++}; ++ ++/* Function prototypes */ ++int huft_build OF(( unsigned *, unsigned, unsigned, ush *, ush *, ++struct huft **, int *)); ++int huft_free OF(( struct huft *)); ++int inflate_codes OF(( struct huft *, struct huft *, int, int )); ++int inflate_stored OF(( void )); ++int inflate_fixed OF(( void )); ++int inflate_dynamic OF(( void )); ++int inflate_block OF(( int *)); ++int inflate OF(( void )); ++ ++/* The inflate algorithm uses a sliding 32K byte window on the uncompressed ++stream to find repeated byte strings. This is implemented here as a ++circular buffer. The index is updated simply by incrementing and then ++and'ing with 0x7fff (32K-1). */ ++/* It is left to other modules to supply the 32K area. It is assumed ++to be usable as if it were declared "uch slide[32768];" or as just ++"uch *slide;" and then malloc'ed in the latter case. The definition ++must be in unzip.h, included above. */ ++/* unsigned wp; current position in slide */ ++#define wp outcnt ++#define flush_output(w) (wp=(w),flush_window()) ++ ++/* Tables for deflate from PKZIP's appnote.txt. */ ++static unsigned border[] = { ++ /* Order of the bit length code lengths */ ++ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ++}; ++static ush cplens[] = { ++ /* Copy lengths for literal codes 257..285 */ ++ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, ++ /* Copy lengths for literal codes 257..285 */ ++ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 ++}; ++/* note: see note #13 above about the 258 in this list. */ ++static ush cplext[] = { ++ /* Extra bits for literal codes 257..285 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, ++ /* Extra bits for literal codes 257..285 */ ++ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99 ++}; /* 99==invalid */ ++static ush cpdist[] = { ++ /* Copy offsets for distance codes 0..29 */ ++ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, ++ /* Copy offsets for distance codes 0..29 */ ++ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, ++ /* Copy offsets for distance codes 0..29 */ ++ 8193, 12289, 16385, 24577 ++}; ++static ush cpdext[] = { /* Extra bits for distance codes */ ++ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,/* Extra bits for distance codes */ ++ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,/* Extra bits for distance codes */ ++ 12, 12, 13, 13 /* Extra bits for distance codes */ ++}; ++ ++/* Macros for inflate() bit peeking and grabbing. ++The usage is: ++ ++NEEDBITS(j) ++x = b & mask_bits[j]; ++DUMPBITS(j) ++ ++where NEEDBITS makes sure that b has at least j bits in it, and ++DUMPBITS removes the bits from b. The macros use the variable k ++for the number of bits in b. Normally, b and k are register ++variables for speed, and are initialized at the beginning of a ++routine that uses these macros from a global bit buffer and count. ++ ++If we assume that EOB will be the longest code, then we will never ++ask for bits with NEEDBITS that are beyond the end of the stream. ++So, NEEDBITS should not read any more bytes than are needed to ++meet the request. Then no bytes need to be "returned" to the buffer ++at the end of the last block. ++ ++However, this assumption is not true for fixed blocks--the EOB code ++is 7 bits, but the other literal/length codes can be 8 or 9 bits. ++(The EOB code is shorter than other codes because fixed blocks are ++generally short. So, while a block always has an EOB, many other ++literal/length codes have a significantly lower probability of ++showing up at all.) However, by making the first table have a ++lookup of seven bits, the EOB code will be found in that first ++lookup, and so will not require that too many bits be pulled from ++the stream. ++*/ ++ ++ulg bb = 0; /* bit buffer */ ++unsigned bk = 0; /* bits in bit buffer */ ++ ++ush mask_bits[] = { ++ /* bits in mask bit buffer */ ++ 0x0000, ++ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,/* bits in mask bit buffer */ ++ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff /* bits in mask bit buffer */ ++}; ++ ++#define NEXTBYTE() (uch)get_byte() ++ ++#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<<k;k+=8;}} ++#define DUMPBITS(n) {b>>=(n);k-=(n);} ++ ++/* ++Huffman code decoding is performed using a multi-level table lookup. ++The fastest way to decode is to simply build a lookup table whose ++size is determined by the longest code. However, the time it takes ++to build this table can also be a factor if the data being decoded ++is not very long. The most common codes are necessarily the ++shortest codes, so those codes dominate the decoding time, and hence ++the speed. The idea is you can have a shorter table that decodes the ++shorter, more probable codes, and then point to subsidiary tables for ++the longer codes. The time it costs to decode the longer codes is ++then traded against the time it takes to make longer tables. ++ ++This results of this trade are in the variables lbits and dbits ++below. lbits is the number of bits the first level table for literal/ ++length codes can decode in one step, and dbits is the same thing for ++the distance codes. Subsequent tables are also less than or equal to ++those sizes. These values may be adjusted either when all of the ++codes are shorter than that, in which case the longest code length in ++bits is used, or when the shortest code is *longer* than the requested ++table size, in which case the length of the shortest code in bits is ++used. ++ ++There are two different values for the two tables, since they code a ++different number of possibilities each. The literal/length table ++codes 286 possible values, or in a flat code, a little over eight ++bits. The distance table codes 30 possible values, or a little less ++than five bits, flat. The optimum values for speed end up being ++about one bit more than those, so lbits is 8+1 and dbits is 5+1. ++The optimum values may differ though from machine to machine, and ++possibly even between compilers. Your mileage may vary. ++*/ ++ ++int lbits = 9; /* bits in base literal/length lookup table */ ++int dbits = 6; /* bits in base distance lookup table */ ++ ++/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ ++#define BMAX 16 /* maximum bit length of any code (16 for explode) */ ++#define N_MAX 288 /* maximum number of codes in any set */ ++ ++unsigned hufts; /* track memory usage */ ++ ++int huft_build( unsigned * b, unsigned n, unsigned s, ush * d, ush * e, struct huft ** t, int * m ) ++/* Given a list of code lengths and a maximum table size, make a set of ++tables to decode that set of codes. Return zero on success, one if ++the given code set is incomplete (the tables are still built in this ++case), two if the input is invalid (all zero length codes or an ++oversubscribed set of lengths), and three if not enough memory. */ ++{ ++ unsigned a; /* counter for codes of length k */ ++ unsigned c[BMAX + 1]; /* bit length count table */ ++ unsigned f; /* i repeats in table every f entries */ ++ int g; /* maximum code length */ ++ int h; /* table level */ ++ register unsigned i; /* counter, current code */ ++ register unsigned j; /* counter */ ++ register int k; /* number of bits in current code */ ++ int l; /* bits per table (returned in m) */ ++ register unsigned * p; /* pointer into c[], b[], or v[] */ ++ register struct huft * q; /* points to current table */ ++ struct huft r; /* table entry for structure assignment */ ++ struct huft * u[BMAX]; /* table stack */ ++ unsigned v[N_MAX]; /* values in order of bit length */ ++ register int w; /* bits before this table == (l * h) */ ++ unsigned x[BMAX + 1]; /* bit offsets, then code stack */ ++ unsigned * xp; /* pointer into x */ ++ int y; /* number of dummy codes added */ ++ unsigned z; /* number of entries in current table */ ++ ++ /* Generate counts for each bit length */ ++ memzero( c, sizeof( c )); ++ p = b; i = n; ++ do { ++ Tracecv(* p, ( stderr, ( n - i >= ' ' && n - i <= '~' ? "%c %d\n" : "0x%x %d\n" ), ++ n - i, * p )); ++ c[ * p] ++; /* assume all entries <= BMAX */ ++ p ++; /* Can't combine with above line (Solaris bug) */ ++ } while (-- i ); ++ if ( c[0] == n ) /* null input--all zero length codes */ ++ { ++ * t = ( struct huft *)NULL; ++ * m = 0; ++ return 0; ++ } ++ ++ /* Find minimum and maximum length, bound *m by those */ ++ l = * m; ++ for ( j = 1; j <= BMAX; j ++) ++ if ( c[j] ) ++ break; ++ k = j; /* minimum code length */ ++ if (( unsigned )l < j ) ++ l = j; ++ for ( i = BMAX; i; i --) ++ if ( c[i] ) ++ break; ++ g = i; /* maximum code length */ ++ if (( unsigned )l > i ) ++ l = i; ++ * m = l; ++ ++ /* Adjust last length count to fill out codes, if needed */ ++ for ( y = 1 << j; j < i; j ++, y <<= 1 ) ++ if (( y -= c[j] ) < 0 ) ++ return 2; /* bad input: more codes than bits */ ++ if (( y -= c[i] ) < 0 ) ++ return 2;/* comment */ ++ c[i] += y; ++ ++ /* Generate starting offsets into the value table for each length */ ++ x[1] = j = 0; ++ p = c + 1; xp = x + 2; /* comment */ ++ while (-- i ) { /* note that i == g from above */ ++ * xp ++ = ( j += * p ++); ++ } ++ ++ /* Make a table of values in order of bit lengths */ ++ p = b; i = 0; ++ do { ++ if (( j = * p ++) != 0 ) ++ v[x[j] ++ ] = i; ++ } while (++ i < n ); ++ ++ /* Generate the Huffman codes and for each, make the table entries */ ++ x[0] = i = 0; /* first Huffman code is zero */ ++ p = v; /* grab values in bit order */ ++ h = - 1; /* no tables yet--level -1 */ ++ w = - l; /* bits decoded == (l * h) */ ++ u[0] = ( struct huft *)NULL; /* just to keep compilers happy */ ++ q = ( struct huft *)NULL; /* ditto */ ++ z = 0; /* ditto */ ++ ++ /* go through the bit lengths (k already is bits in shortest code) */ ++ for (; k <= g; k ++) ++ { ++ a = c[k]; ++ while ( a --) ++ { ++ /* here i is the Huffman code of length k bits for value *p */ ++ /* make tables up to required level */ ++ while ( k > w + l ) ++ { ++ h ++; ++ w += l; /* previous table always l bits */ ++ ++ /* compute minimum size table less than or equal to l bits */ ++ z = ( z = g - w ) > ( unsigned )l ? l : z; /* upper limit on table size */ ++ if (( f = 1 << ( j = k - w )) > a + 1 ) /* try a k-w bit table */ ++ { /* too few codes for k-w bit table */ ++ f -= a + 1; /* deduct codes from patterns left */ ++ xp = c + k; ++ while (++ j < z ) /* try smaller tables up to z bits */ ++ { ++ if (( f <<= 1 ) <= *++ xp ) ++ break; /* enough codes to use up j bits */ ++ f -= * xp; /* else deduct codes from patterns */ ++ } ++ } ++ z = 1 << j; /* table entries for j-bit table */ ++ ++ /* allocate and link in new table */ ++ if (( q = ( struct huft *)malloc(( z + 1 )* sizeof( struct huft ))) == ++ ( struct huft *)NULL ) ++ { ++ if ( h ) ++ huft_free( u[0] ); ++ return 3; /* not enough memory */ ++ } ++ hufts += z + 1; /* track memory usage */ ++ * t = q + 1; /* link to list for huft_free() */ ++ *( t = &( q->v.t )) = ( struct huft *)NULL; ++ u[h] = ++ q; /* table starts after link */ ++ ++ /* connect to last table, if there is one */ ++ if ( h ) ++ { ++ x[h] = i; /* save pattern for backing up */ ++ r.b = ( uch )l; /* bits to dump before this table */ ++ r.e = ( uch )( 16 + j ); /* bits in this table */ ++ r.v.t = q; /* pointer to this table */ ++ j = i >> ( w - l ); /* (get around Turbo C bug) */ ++ u[h - 1][j] = r; /* connect to last table */ ++ } ++ } ++ ++ /* set up table entry in r */ ++ r.b = ( uch )( k - w ); ++ if ( p >= v + n ) ++ r.e = 99; /* out of values--invalid code */ ++ else if (* p < s ) ++ { ++ r.e = ( uch )(* p < 256 ? 16 : 15 ); /* 256 is end-of-block code */ ++ r.v.n = ( ush )(* p ); /* simple code is just the value */ ++ p ++; /* one compiler does not like *p++ */ ++ } ++ else ++ { ++ r.e = ( uch )e[ * p - s]; /* non-simple--look up in lists */ ++ r.v.n = d[ * p ++ - s]; ++ } ++ ++ /* fill code-like entries with r */ ++ f = 1 << ( k - w ); ++ for ( j = i >> w; j < z; j += f ) ++ q[j] = r; ++ ++ /* backwards increment the k-bit code i */ ++ for ( j = 1 << ( k - 1 ); i & j; j >>= 1 ) ++ i ^= j; ++ i ^= j; ++ ++ /* backup over finished tables */ ++ while (( i & (( 1 << w ) - 1 )) != x[h] ) ++ { ++ h --; /* don't need to update q */ ++ w -= l; ++ } ++ } ++ } ++ ++ /* Return true (1) if we were given an incomplete table */ ++ return y != 0 && g != 1; ++} ++ ++int huft_free( struct huft * t ) ++/* Free the malloc'ed tables built by huft_build(), which makes a linked ++list of the tables it made, with the links in a dummy first entry of ++each table. */ ++{ ++ register struct huft * p, * q; ++ ++ /* Go through linked list, freeing from the malloced (t[-1]) address. */ ++ p = t; ++ while ( p != ( struct huft *)NULL ) ++ { ++ q = (-- p )->v.t; ++ free(( char *)p ); ++ p = q; ++ } ++ return 0; ++} ++ ++int inflate_codes( struct huft * tl, struct huft * td, int bl, int bd ) ++/* inflate (decompress) the codes in a deflated (compressed) block. ++Return an error code or zero if it all goes ok. */ ++{ ++ register unsigned e; /* table entry flag/number of extra bits */ ++ unsigned n, d; /* length and index for copy */ ++ unsigned w; /* current window position */ ++ struct huft * t; /* pointer to table entry */ ++ unsigned ml, md; /* masks for bl and bd bits */ ++ register ulg b; /* bit buffer */ ++ register unsigned k; /* number of bits in bit buffer */ ++ ++ /* make local copies of globals */ ++ b = bb; /* initialize bit buffer */ ++ k = bk; ++ w = wp; /* initialize window position */ ++ ++ /* inflate the coded data */ ++ ml = mask_bits[bl]; /* precompute masks for speed */ ++ md = mask_bits[bd]; ++ for (;;) /* do until end of block */ ++ { ++ NEEDBITS(( unsigned )bl ); ++ if (( e = ( t = tl + (( unsigned )b & ml ))->e ) > 16 ) /* bits */ ++ do { ++ if ( e == 99 )/* comment */ ++ return 1; ++ DUMPBITS( t->b ); ++ e -= 16; /* bits */ ++ NEEDBITS( e ); ++ } while (( e = ( t = t->v.t + (( unsigned )b & mask_bits[e] ))->e ) > 16 ); /* judge */ ++ DUMPBITS( t->b ); ++ if ( e == 16 ) /* then it's a literal */ ++ { ++ slide[w ++ ] = ( uch )t->v.n; ++ Tracevv(( stderr, "%c", slide[w - 1] )); ++ if ( w == WSIZE ) ++ { ++ flush_output( w ); ++ w = 0; ++ } ++ } ++ else /* it's an EOB or a length */ ++ { ++ /* exit if end of block */ ++ if ( e == 15 ) ++ break; ++ ++ /* get length of block to copy */ ++ NEEDBITS( e ); ++ n = t->v.n + (( unsigned )b & mask_bits[e] ); ++ DUMPBITS( e ); ++ ++ /* decode distance of block to copy */ ++ NEEDBITS(( unsigned )bd ) ++ if (( e = ( t = td + (( unsigned )b & md ))->e ) > 16 ) ++ do { ++ if ( e == 99 )/* comment */ ++ return 1; ++ DUMPBITS( t->b ); ++ e -= 16; /* bits */ ++ NEEDBITS( e ); ++ } while (( e = ( t = t->v.t + (( unsigned )b & mask_bits[e] ))->e ) > 16 ); /* bits */ ++ DUMPBITS( t->b ); ++ NEEDBITS( e ) ++ d = w - t->v.n - (( unsigned )b & mask_bits[e] ); ++ DUMPBITS( e ) ++ Tracevv(( stderr, "\\[%d,%d]", w - d, n )); ++ ++ /* do the copy */ ++ do { ++ n -= ( e = ( e = WSIZE - (( d &= WSIZE - 1 ) > w ? d : w )) > n ? n : e ); ++#if !defined(NOMEMCPY) && !defined(DEBUG) ++ if ( w - d >= e ) /* (this test assumes unsigned comparison) */ ++ { ++ memcpy( slide + w, slide + d, e ); ++ w += e; ++ d += e; ++ } ++ else /* do it slow to avoid memcpy() overlap */ ++#endif /* !NOMEMCPY */ ++ do { ++ slide[w ++ ] = slide[d ++ ]; ++ Tracevv(( stderr, "%c", slide[w - 1] )); ++ } while (-- e ); ++ if ( w == WSIZE ) ++ { ++ flush_output( w ); ++ w = 0; ++ } ++ } while ( n ); ++ } ++ } ++ ++ /* restore the globals from the locals */ ++ wp = w; /* restore global window pointer */ ++ bb = b; /* restore global bit buffer */ ++ bk = k; ++ ++ /* done */ ++ return 0; ++} ++ ++int inflate_stored() ++/* "decompress" an inflated type 0 (stored) block. */ ++{ ++ unsigned n; /* number of bytes in block */ ++ unsigned w; /* current window position */ ++ register ulg b; /* bit buffer */ ++ register unsigned k; /* number of bits in bit buffer */ ++ ++ /* make local copies of globals */ ++ b = bb; /* initialize bit buffer */ ++ k = bk; ++ w = wp; /* initialize window position */ ++ ++ /* go to byte boundary */ ++ n = k & 7; ++ DUMPBITS( n ); ++ ++ /* get the length and its complement */ ++ NEEDBITS( 16 ); ++ n = (( unsigned )b & 0xffff );/* n */ ++ DUMPBITS( 16 ); /* dump bits */ ++ NEEDBITS( 16 ); /* dump bits */ ++ if ( n != ( unsigned )((~ b ) & 0xffff )) /* n */ ++ return 1; /* error in compressed data */ ++ DUMPBITS( 16 ); /* dump bits */ ++ ++ /* read and output the compressed data */ ++ while ( n --) ++ { ++ NEEDBITS( 8 ); /* bits */ ++ slide[w ++ ] = ( uch )b; ++ if ( w == WSIZE ) ++ { ++ flush_output( w ); ++ w = 0; ++ } ++ DUMPBITS( 8 ); /* bits */ ++ } ++ ++ /* restore the globals from the locals */ ++ wp = w; /* restore global window pointer */ ++ bb = b; /* restore global bit buffer */ ++ bk = k; ++ return 0; ++} ++ ++int inflate_fixed() ++/* decompress an inflated type 1 (fixed Huffman codes) block. We should ++either replace this with a custom decoder, or at least precompute the ++Huffman tables. */ ++{ ++ int i; /* temporary variable */ ++ struct huft * tl; /* literal/length code table */ ++ struct huft * td; /* distance code table */ ++ int bl; /* lookup bits for tl */ ++ int bd; /* lookup bits for td */ ++ unsigned l[288]; /* length list for huft_build */ ++ ++ /* set up literal table */ ++ for ( i = 0; i < 144; i ++) ++ l[i] = 8; /* 8 */ ++ /* set up literal table */ ++ for (; i < 256; i ++) ++ l[i] = 9; /* 9 */ ++ /* set up literal table */ ++ for (; i < 280; i ++) ++ l[i] = 7; /* 7 */ ++ /* set up literal table */ ++ for (; i < 288; i ++) /* make a complete, but wrong code set */ ++ l[i] = 8; /* 8 */ ++ bl = 7; /* bl */ ++ if (( i = huft_build( l, 288, 257, cplens, cplext, & tl, & bl )) != 0 )/* comment */ ++ return i; ++ ++ /* set up distance table */ ++ for ( i = 0; i < 30; i ++) /* make an incomplete code set */ ++ l[i] = 5; /* 5 */ ++ bd = 5; /* 5 */ ++ /* huffman build tree */ ++ if (( i = huft_build( l, 30, 0, cpdist, cpdext, & td, & bd )) > 1 ) ++ { ++ huft_free( tl ); ++ return i; ++ } ++ ++ /* decompress until an end-of-block code */ ++ if ( inflate_codes( tl, td, bl, bd )) ++ return 1; ++ ++ /* free the decoding tables, return */ ++ huft_free( tl ); ++ huft_free( td ); ++ return 0; ++} ++ ++int inflate_dynamic() ++/* decompress an inflated type 2 (dynamic Huffman codes) block. */ ++{ ++ int i; /* temporary variables */ ++ unsigned j; ++ unsigned l; /* last length */ ++ unsigned m; /* mask for bit lengths table */ ++ unsigned n; /* number of lengths to get */ ++ struct huft * tl; /* literal/length code table */ ++ struct huft * td; /* distance code table */ ++ int bl; /* lookup bits for tl */ ++ int bd; /* lookup bits for td */ ++ unsigned nb; /* number of bit length codes */ ++ unsigned nl; /* number of literal/length codes */ ++ unsigned nd; /* number of distance codes */ ++ unsigned ll[286 + 30]; /* literal/length and distance code lengths */ ++ register ulg b; /* bit buffer */ ++ register unsigned k; /* number of bits in bit buffer */ ++ ++ /* make local bit buffer */ ++ b = bb; ++ k = bk; ++ ++ /* read in table lengths */ ++ NEEDBITS( 5 ); /* bits */ ++ nl = 257 + (( unsigned )b & 0x1f ); /* number of literal/length codes */ ++ DUMPBITS( 5 ); /* bits */ ++ NEEDBITS( 5 ); /* bits */ ++ nd = 1 + (( unsigned )b & 0x1f ); /* number of distance codes */ ++ DUMPBITS( 5 ); /* bits */ ++ NEEDBITS( 4 ); /* bits */ ++ nb = 4 + (( unsigned )b & 0xf ); /* number of bit length codes */ ++ DUMPBITS( 4 ); /* bits */ ++ ++ if ( nl > 286 || nd > 30 ) /* judge */ ++ return 1; /* bad lengths */ ++ ++ /* read in bit-length-code lengths */ ++ for ( j = 0; j < nb; j ++) ++ { ++ NEEDBITS( 3 ); /* bits */ ++ ll[border[j]] = ( unsigned )b & 7; /* bits */ ++ DUMPBITS( 3 ); /* bits */ ++ } ++ for (; j < 19; j ++) /* bits */ ++ ll[border[j]] = 0; /* bits */ ++ ++ /* build decoding table for trees--single level, 7 bit lookup */ ++ bl = 7; ++ if (( i = huft_build( ll, 19, 19, NULL, NULL, & tl, & bl )) != 0 ) /* bits */ ++ { ++ if ( i == 1 ) ++ huft_free( tl ); ++ return i; /* incomplete code set */ ++ } ++ ++ /* read in literal and distance code lengths */ ++ n = nl + nd; ++ m = mask_bits[bl]; ++ i = l = 0; ++ while (( unsigned )i < n ) ++ { ++ NEEDBITS(( unsigned )bl ); ++ j = ( td = tl + (( unsigned )b & m ))->b; ++ DUMPBITS( j ); ++ j = td->v.n; ++ if ( j < 16 ) /* length of code in bits (0..15) */ ++ ll[i ++ ] = l = j; /* save last length in l */ ++ else if ( j == 16 ) /* repeat last length 3 to 6 times */ ++ { ++ NEEDBITS( 2 ); /* bits */ ++ j = 3 + (( unsigned )b & 3 );/* comment */ ++ DUMPBITS( 2 ); /* bits */ ++ if (( unsigned )i + j > n ) ++ return 1; ++ while ( j --) ++ ll[i ++ ] = l; ++ } ++ else if ( j == 17 ) /* 3 to 10 zero length codes */ ++ { ++ NEEDBITS( 3 ); /* bits */ ++ j = 3 + (( unsigned )b & 7 );/* comment */ ++ DUMPBITS( 3 ); /* bits */ ++ if (( unsigned )i + j > n ) ++ return 1; ++ while ( j --) ++ ll[i ++ ] = 0; ++ l = 0; ++ } ++ else /* j == 18: 11 to 138 zero length codes */ ++ { ++ NEEDBITS( 7 ); /* bits */ ++ j = 11 + (( unsigned )b & 0x7f ); /* j */ ++ DUMPBITS( 7 ); /* bits */ ++ if (( unsigned )i + j > n ) ++ return 1; ++ while ( j --) ++ ll[i ++ ] = 0; ++ l = 0; ++ } ++ } ++ ++ /* free decoding table for trees */ ++ huft_free( tl ); ++ ++ /* restore the global bit buffer */ ++ bb = b; ++ bk = k; ++ ++ /* build the decoding tables for literal/length and distance codes */ ++ bl = lbits; ++ if (( i = huft_build( ll, nl, 257, cplens, cplext, & tl, & bl )) != 0 )/* comment */ ++ { ++ if ( i == 1 ) { ++ fprintf( stderr, " incomplete literal tree\n" ); ++ huft_free( tl ); ++ } ++ return i; /* incomplete code set */ ++ } ++ bd = dbits; ++ if (( i = huft_build( ll + nl, nd, 0, cpdist, cpdext, & td, & bd )) != 0 ) ++ { ++ if ( i == 1 ) ++ { ++ fprintf( stderr, " incomplete distance tree\n" ); ++ huft_free( td ); ++ } ++ huft_free( tl ); ++ return i; /* incomplete code set */ ++ } ++ ++ /* decompress until an end-of-block code */ ++ if ( inflate_codes( tl, td, bl, bd )) ++ return 1; ++ ++ /* free the decoding tables, return */ ++ huft_free( tl ); ++ huft_free( td ); ++ return 0; ++} ++ ++int inflate_block( int * e ) ++/* decompress an inflated block */ ++{ ++ unsigned t; /* block type */ ++ register ulg b; /* bit buffer */ ++ register unsigned k; /* number of bits in bit buffer */ ++ ++ /* make local bit buffer */ ++ b = bb; ++ k = bk; ++ ++ /* read in last block bit */ ++ NEEDBITS( 1 ); /* bits */ ++ * e = ( int )b & 1; ++ DUMPBITS( 1 ); /* bits */ ++ ++ /* read in block type */ ++ NEEDBITS( 2 ); /* bits */ ++ t = ( unsigned )b & 3;/* comment */ ++ DUMPBITS( 2 ); /* bits */ ++ ++ /* restore the global bit buffer */ ++ bb = b; ++ bk = k; ++ ++ /* inflate that block type */ ++ if ( t == 2 ) ++ return inflate_dynamic(); ++ if ( t == 0 ) ++ return inflate_stored(); ++ if ( t == 1 ) ++ return inflate_fixed(); ++ ++ /* bad block type */ ++ return 2; ++} ++ ++int inflate() ++/* decompress an inflated entry */ ++{ ++ int e; /* last block flag */ ++ int r; /* result code */ ++ unsigned h; /* maximum struct huft's malloc'ed */ ++ ++ /* initialize window, bit buffer */ ++ wp = 0; ++ bk = 0; ++ bb = 0; ++ ++ /* decompress until the last block */ ++ h = 0; ++ do { ++ hufts = 0; ++ if (( r = inflate_block(& e )) != 0 ) ++ return r; ++ if ( hufts > h ) ++ h = hufts; ++ } while (! e ); ++ ++ /* Undo too much lookahead. The next read will be byte aligned so we ++ * can discard unused bits in the last meaningful byte. ++ */ ++ while ( bk >= 8 ) { ++ bk -= 8;/* comment */ ++ inptr --; ++ } ++ ++ /* flush out slide */ ++ flush_output( wp ); ++ ++ /* return success */ ++ return 0; ++} +diff --git a/lib/gzip/lzw.h b/lib/gzip/lzw.h +new file mode 100644 +index 0000000..d1b35a8 +--- /dev/null ++++ b/lib/gzip/lzw.h +@@ -0,0 +1,32 @@ ++/* lzw.h -- define the lzw functions. ++* Copyright (C) 1992-1993 Jean-loup Gailly. ++* This is free software; you can redistribute it and/or modify it under the ++* terms of the GNU General Public License, see the file COPYING. ++*/ ++#if !defined( OF ) && defined( lint ) ++#include "gzip.h" ++#endif ++#ifndef BITS ++#define BITS 16 /* bits len */ ++#endif ++#define INIT_BITS 9 /* Initial number of bits per code */ ++#define LZW_MAGIC "\037\235" /* Magic header for lzw files, 1F 9D */ ++//#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */ ++/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free. ++* It's a pity that old uncompress does not check bit 0x20. That makes ++* extension of the format actually undesirable because old compress ++* would just crash on the new format instead of giving a meaningful ++* error message. It does check the number of bits, but it's more ++* helpful to say "unsupported format, get a new version" than ++* "can only handle 16 bits". ++*/ ++#define BLOCK_MODE 0x80 /* Block compression: if table is full and compression rate is dropping, ++* clear the dictionary. ++*/ ++#define LZW_RESERVED 0x60 /* reserved bits */ ++#define CLEAR 256 /* flush the dictionary */ ++#define FIRST ( CLEAR + 1 ) /* first free entry */ ++extern int maxbits; /* max bits per code for LZW */ ++extern int block_mode; /* block compress mode -C compatible with 2.0 */ ++extern int lzw ( int in, int out ); ++extern int unlzw ( int in, int out ); +diff --git a/lib/gzip/trees.c b/lib/gzip/trees.c +new file mode 100644 +index 0000000..235a4e6 +--- /dev/null ++++ b/lib/gzip/trees.c +@@ -0,0 +1,1006 @@ ++/* trees.c -- output deflated data using Huffman coding ++* Copyright (C) 1992-1993 Jean-loup Gailly ++* This is free software; you can redistribute it and/or modify it under the ++* terms of the GNU General Public License, see the file COPYING. ++*/ ++ ++/* ++* PURPOSE ++* ++* Encode various sets of source values using variable-length ++* binary code trees. ++* ++* DISCUSSION ++* ++* The PKZIP "deflation" process uses several Huffman trees. The more ++* common source values are represented by shorter bit sequences. ++* ++* Each code tree is stored in the ZIP file in a compressed form ++* which is itself a Huffman encoding of the lengths of ++* all the code strings (in ascending order by source values). ++* The actual code strings are reconstructed from the lengths in ++* the UNZIP process, as described in the "application note" ++* (APPNOTE.TXT) distributed as part of PKWARE's PKZIP program. ++* ++* REFERENCES ++* ++* Lynch, Thomas J. ++* Data Compression: Techniques and Applications, pp. 53-55. ++* Lifetime Learning Publications, 1985. ISBN 0-534-03418-7. ++* ++* Storer, James A. ++* Data Compression: Methods and Theory, pp. 49-50. ++* Computer Science Press, 1988. ISBN 0-7167-8156-5. ++* ++* Sedgewick, R. ++* Algorithms, p290. ++* Addison-Wesley, 1983. ISBN 0-201-06672-6. ++* ++* INTERFACE ++* ++* void ct_init (ush *attr, int *methodp) ++* Allocate the match buffer, initialize the various tables and save ++* the location of the internal file attribute (ascii/binary) and ++* method (DEFLATE/STORE) ++* ++* void ct_tally (int dist, int lc); ++* Save the match info and tally the frequency counts. ++* ++* long flush_block (char *buf, ulg stored_len, int eof) ++* Determine the best encoding for the current block: dynamic trees, ++* static trees or store, and output the encoded block to the zip ++* file. Returns the total compressed length for the file so far. ++* ++*/ ++ ++#include "gzip.h" ++ ++/* =========================================================================== ++* Constants ++*/ ++/* All codes must not exceed MAX_BITS bits */ ++#define MAX_BITS 15 ++ ++/* Bit length codes must not exceed MAX_BL_BITS bits */ ++#define MAX_BL_BITS 7 ++ ++/* number of length codes, not counting the special END_BLOCK code */ ++#define LENGTH_CODES 29 ++ ++/* number of literal bytes 0..255 */ ++#define LITERALS 256 ++ ++/* end of block literal code */ ++#define END_BLOCK 256 ++ ++/* number of Literal or Length codes, including the END_BLOCK code */ ++#define L_CODES (LITERALS+1+LENGTH_CODES) ++ ++/* number of distance codes */ ++#define D_CODES 30 ++ ++/* number of codes used to transfer the bit lengths */ ++#define BL_CODES 19 ++ ++local int near extra_lbits[LENGTH_CODES] ++/* extra bits for each length code */ ++= {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; ++ ++local int near extra_dbits[D_CODES] ++/* extra bits for each distance code */ ++= {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; ++ ++local int near extra_blbits[BL_CODES] ++/* extra bits for each bit length code */ ++= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7}; ++ ++#define STORED_BLOCK 0 ++/* The three kinds of block type */ ++#define STATIC_TREES 1 ++/* The three kinds of block type */ ++#define DYN_TREES 2 ++ ++#define LIT_BUFSIZE 0x2000 ++#ifndef DIST_BUFSIZE ++#define DIST_BUFSIZE LIT_BUFSIZE ++#endif ++/* Sizes of match buffers for literals/lengths and distances. There are ++* 4 reasons for limiting LIT_BUFSIZE to 64K: ++* - frequencies can be kept in 16 bit counters ++* - if compression is not successful for the first block, all input data is ++* still in the window so we can still emit a stored block even when input ++* comes from standard input. (This can also be done for all blocks if ++* LIT_BUFSIZE is not greater than 32K.) ++* - if compression is not successful for a file smaller than 64K, we can ++* even emit a stored file instead of a stored block (saving 5 bytes). ++* - creating new Huffman trees less frequently may not provide fast ++* adaptation to changes in the input data statistics. (Take for ++* example a binary file with poorly compressible code followed by ++* a highly compressible string table.) Smaller buffer sizes give ++* fast adaptation but have of course the overhead of transmitting trees ++* more frequently. ++* - I can't count above 4 ++* The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save ++* memory at the expense of compression). Some optimizations would be possible ++* if we rely on DIST_BUFSIZE == LIT_BUFSIZE. ++*/ ++#if LIT_BUFSIZE > INBUFSIZ ++error cannot overlay l_buf and inbuf ++#endif ++/* repeat previous bit length 3-6 times (2 bits of repeat count) */ ++#define REP_3_6 16 ++ ++/* repeat a zero length 3-10 times (3 bits of repeat count) */ ++#define REPZ_3_10 17 ++ ++/* repeat a zero length 11-138 times (7 bits of repeat count) */ ++#define REPZ_11_138 18 ++ ++/* =========================================================================== ++* Local data ++*/ ++ ++/* Data structure describing a single value and its code string. */ ++typedef struct ct_data { ++ union { ++ ush freq; /* frequency count */ ++ ush code; /* bit string */ ++ } fc; ++ union { ++ ush dad; /* father node in Huffman tree */ ++ ush len; /* length of bit string */ ++ } dl; ++} ct_data; ++ ++#define Freq fc.freq ++#define Code fc.code ++#define Dad dl.dad ++#define Len dl.len ++ ++/* maximum heap size */ ++#define HEAP_SIZE (2*L_CODES+1) ++ ++local ct_data near dyn_ltree[HEAP_SIZE]; /* literal and length tree */ ++local ct_data near dyn_dtree[2 * D_CODES + 1]; /* distance tree */ ++ ++/* The static literal tree. Since the bit lengths are imposed, there is no ++* need for the L_CODES extra codes used during heap construction. However ++* The codes 286 and 287 are needed to build a canonical tree (see ct_init ++* below). ++*/ ++local ct_data near static_ltree[L_CODES + 2]; ++ ++local ct_data near static_dtree[D_CODES]; ++/* The static distance tree. (Actually a trivial tree since all codes use ++* 5 bits.) ++*/ ++ ++/* Huffman tree for the bit lengths */ ++local ct_data near bl_tree[2 * BL_CODES + 1]; ++ ++typedef struct tree_desc { ++ ct_data near * dyn_tree; /* the dynamic tree */ ++ ct_data near * static_tree; /* corresponding static tree or NULL */ ++ int near * extra_bits; /* extra bits for each code or NULL */ ++ int extra_base; /* base index for extra_bits */ ++ int elems; /* max number of elements in the tree */ ++ int max_length; /* max bit length for the codes */ ++ int max_code; /* largest code with non zero frequency */ ++} tree_desc; ++ ++local tree_desc near l_desc = { ++ dyn_ltree, static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS, 0 ++}; ++ ++local tree_desc near d_desc = { ++ dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0 ++}; ++ ++local tree_desc near bl_desc = { ++ bl_tree, ( ct_data near *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS, 0 ++}; ++ ++/* number of codes at each bit length for an optimal tree */ ++local ush near bl_count[MAX_BITS + 1]; ++ ++/* The lengths of the bit length codes are sent in order of decreasing ++* probability, to avoid transmitting the lengths for unused bit length codes. ++*/ ++local uch near bl_order[BL_CODES] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; ++ ++local int near heap[2 * L_CODES + 1]; /* heap used to build the Huffman trees */ ++local int heap_len = 0; /* number of elements in the heap */ ++local int heap_max = 0; /* element of largest frequency */ ++/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. ++* The same heap array is used to build all trees. ++*/ ++ ++/* Depth of each subtree used as tie breaker for trees of equal frequency */ ++local uch near depth[2 * L_CODES + 1]; ++ ++/* length code for each normalized match length (0 == MIN_MATCH) */ ++local uch length_code[MAX_MATCH - MIN_MATCH + 1]; ++ ++/* distance codes. The first 256 values correspond to the distances ++* 3 .. 258, the last 256 values correspond to the top 8 bits of ++* the 15 bit distances. ++*/ ++local uch dist_code[512]; ++ ++/* First normalized length for each code (0 = MIN_MATCH) */ ++local int near base_length[LENGTH_CODES]; ++ ++/* First normalized distance for each code (0 = distance of 1) */ ++local int near base_dist[D_CODES]; ++ ++/* DECLARE(uch, l_buf, LIT_BUFSIZE); buffer for literals or lengths */ ++#define l_buf inbuf ++ ++/* flag_buf is a bit array distinguishing literals from lengths in ++* l_buf, thus indicating the presence or absence of a distance. ++*/ ++local uch near flag_buf[( LIT_BUFSIZE / 8 )]; ++ ++local unsigned last_lit = 0; /* running index in l_buf */ ++local unsigned last_dist = 0; /* running index in d_buf */ ++local unsigned last_flags = 0; /* running index in flag_buf */ ++local uch trees_flags = 0; /* current flags not yet saved in flag_buf */ ++local uch flag_bit = 0; /* current bit used in flags */ ++/* bits are filled in flags starting at bit 0 (least significant). ++* Note: these flags are overkill in the current code since we don't ++* take advantage of DIST_BUFSIZE == LIT_BUFSIZE. ++*/ ++ ++local ulg opt_len = 0; /* bit length of current block with optimal trees */ ++local ulg static_len = 0; /* bit length of current block with static trees */ ++ ++local ulg compressed_len = 0; /* total bit length of compressed file */ ++ ++local ulg input_len = 0; /* total byte length of input file */ ++/* input_len is for debugging only since we can get it by other means. */ ++ ++ush * file_type; /* pointer to UNKNOWN, BINARY or ASCII */ ++int * file_method; /* pointer to DEFLATE or STORE */ ++ ++#ifdef DEBUG ++extern ulg bits_sent; /* bit length of the compressed data */ ++extern int isize; /* byte length of input file */ ++#endif ++ ++extern int block_start; /* window offset of current block */ ++extern unsigned near strstart; /* window offset of current string */ ++ ++/* =========================================================================== ++* Local (static) routines in this file. ++*/ ++ ++local void init_block( void ); ++local void pqdownheap( ct_data near * tree, int k ); ++local void gen_bitlen( tree_desc near * desc ); ++local void gen_codes( ct_data near * tree, int max_code ); ++local void build_tree( tree_desc near * desc ); ++local void scan_tree( ct_data near * tree, int max_code ); ++local void send_tree( ct_data near * tree, int max_code ); ++local int build_bl_tree( void ); ++local void send_all_trees( int lcodes, int dcodes, int blcodes ); ++local void compress_block( ct_data near * ltree, ct_data near * dtree ); ++local void set_file_type( void ); ++ ++#ifndef DEBUG ++#define send_code(c, tree) send_bits(tree[c].Code, tree[c].Len) ++/* Send a code of the given tree. c and tree must not have side effects */ ++ ++#else /* DEBUG */ ++#define send_code(c, tree) \ ++{ if (verbose>1) fprintf(stderr,"\ncd %3d ",(c)); \ ++ send_bits(tree[c].Code, tree[c].Len); } ++#endif ++ ++/* Mapping from a distance to a distance code. dist is the distance - 1 and ++* must not have side effects. dist_code[256] and dist_code[257] are never ++* used. ++*/ ++#define d_code(dist) ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)]) ++ ++#define MAX(a,b) (a >= b ? a : b) ++/* the arguments must not have side effects */ ++ ++/* =========================================================================== ++* Allocate the match buffer, initialize the various tables and save the ++* location of the internal file attribute (ascii/binary) and method ++* (DEFLATE/STORE). ++*/ ++void ct_init( ush * attr, int * methodp ) ++{ ++ int n; /* iterates over tree elements */ ++ int bits; /* bit counter */ ++ int length; /* length value */ ++ int code; /* code value */ ++ int dist; /* distance index */ ++ ++ file_type = attr; ++ file_method = methodp; ++ compressed_len = input_len = 0L; ++ ++ if ( static_dtree[0].Len != 0 ) return; /* ct_init already called */ ++ ++ /* Initialize the mapping length (0..255) -> length code (0..28) */ ++ length = 0; ++ for ( code = 0; code < LENGTH_CODES - 1; code ++) { ++ base_length[code] = length; ++ for ( n = 0; n < ( 1 << extra_lbits[code] ); n ++) { ++ length_code[length ++ ] = ( uch )code; ++ } ++ } ++ /* Note that the length 255 (match length 258) can be represented ++ * in two different ways: code 284 + 5 bits or code 285, so we ++ * overwrite length_code[255] to use the best encoding: ++ */ ++ length_code[length - 1] = ( uch )code; ++ ++ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ ++ dist = 0; ++ for ( code = 0 ; code < 16; code ++) { ++ base_dist[code] = dist; ++ for ( n = 0; n < ( 1 << extra_dbits[code] ); n ++) { ++ dist_code[dist ++ ] = ( uch )code; ++ } ++ } ++ dist >>= 7; /* from now on, all distances are divided by 128 */ ++ for ( ; code < D_CODES; code ++) { ++ base_dist[code] = dist << 7;/* base dist */ ++ for ( n = 0; n < ( 1 <<( extra_dbits[code] - 7 )); n ++) { ++ dist_code[256 + dist ++ ] = ( uch )code; /* dist code */ ++ } ++ } ++ ++ /* Construct the codes of the static literal tree */ ++ for ( bits = 0; bits <= MAX_BITS; bits ++) bl_count[bits] = 0; ++ n = 0; ++ while ( n <= 143 ) static_ltree[n ++ ].Len = 8, bl_count[8] ++; /* ��ʼ�� */ ++ while ( n <= 255 ) static_ltree[n ++ ].Len = 9, bl_count[9] ++; /* ��ʼ�� */ ++ while ( n <= 279 ) static_ltree[n ++ ].Len = 7, bl_count[7] ++; /* ��ʼ�� */ ++ while ( n <= 287 ) static_ltree[n ++ ].Len = 8, bl_count[8] ++; /* ��ʼ�� */ ++ /* Codes 286 and 287 do not exist, but we must include them in the ++ * tree construction to get a canonical Huffman tree (longest code ++ * all ones) ++ */ ++ gen_codes(( ct_data near *)static_ltree, L_CODES + 1 ); ++ ++ /* The static distance tree is trivial: */ ++ for ( n = 0; n < D_CODES; n ++) { ++ static_dtree[n].Len = 5; /* ��ʼ�� */ ++ static_dtree[n].Code = bi_reverse( n, 5 ); /* ��ʼ�� */ ++ } ++ ++ /* Initialize the first block of the first file: */ ++ init_block(); ++} ++ ++/* =========================================================================== ++* Initialize a new block. ++*/ ++local void init_block() ++{ ++ int n; /* iterates over tree elements */ ++ ++ /* Initialize the trees. */ ++ for ( n = 0; n < L_CODES; n ++) dyn_ltree[n].Freq = 0; ++ for ( n = 0; n < D_CODES; n ++) dyn_dtree[n].Freq = 0; ++ for ( n = 0; n < BL_CODES; n ++) bl_tree[n].Freq = 0; ++ ++ dyn_ltree[END_BLOCK].Freq = 1; ++ opt_len = static_len = 0L; ++ last_lit = last_dist = last_flags = 0; ++ trees_flags = 0; flag_bit = 1; ++} ++ ++#define SMALLEST 1 ++/* Index within the heap array of least frequent node in the Huffman tree */ ++ ++/* =========================================================================== ++* Remove the smallest element from the heap and recreate the heap with ++* one less element. Updates heap and heap_len. ++*/ ++#define pqremove(tree, top) \ ++{\ ++ top = heap[SMALLEST]; \ ++ heap[SMALLEST] = heap[heap_len--]; \ ++ pqdownheap(tree, SMALLEST); \ ++} ++ ++/* =========================================================================== ++* Compares to subtrees, using the tree depth as tie breaker when ++* the subtrees have equal frequency. This minimizes the worst case length. ++*/ ++#define smaller(tree, n, m) \ ++ (tree[n].Freq < tree[m].Freq || \ ++ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) ++ ++/* =========================================================================== ++* Restore the heap property by moving down the tree starting at node k, ++* exchanging a node with the smallest of its two sons if necessary, stopping ++* when the heap property is re-established (each father smaller than its ++* two sons). ++*/ ++local void pqdownheap( ct_data near * tree, int k ) ++{ ++ int v = heap[k]; ++ int j = k << 1; /* left son of k */ ++ while ( j <= heap_len ) { ++ /* Set j to the smallest of the two sons: */ ++ if ( j < heap_len && smaller( tree, heap[j + 1], heap[j] )) j ++; ++ ++ /* Exit if v is smaller than both sons */ ++ if ( smaller( tree, v, heap[j] )) break; ++ ++ /* Exchange v with the smallest son */ ++ heap[k] = heap[j]; k = j; ++ ++ /* And continue down the tree, setting j to the left son of k */ ++ j <<= 1; ++ } ++ heap[k] = v; ++} ++ ++/* =========================================================================== ++* Compute the optimal bit lengths for a tree and update the total bit length ++* for the current block. ++* IN assertion: the fields freq and dad are set, heap[heap_max] and ++* above are the tree nodes sorted by increasing frequency. ++* OUT assertions: the field len is set to the optimal bit length, the ++* array bl_count contains the frequencies for each bit length. ++* The length opt_len is updated; static_len is also updated if stree is ++* not null. ++*/ ++local void gen_bitlen( tree_desc near * desc ) ++{ ++ ct_data near * tree = desc->dyn_tree; ++ int near * extra = desc->extra_bits; ++ int base = desc->extra_base; ++ int max_code = desc->max_code; ++ int max_length = desc->max_length; ++ ct_data near * stree = desc->static_tree; ++ int h; /* heap index */ ++ int n, m; /* iterate over the tree elements */ ++ int bits; /* bit length */ ++ int xbits; /* extra bits */ ++ ush f; /* frequency */ ++ int overflow = 0; /* number of elements with bit length too large */ ++ ++ for ( bits = 0; bits <= MAX_BITS; bits ++) bl_count[bits] = 0; ++ ++ /* In a first pass, compute the optimal bit lengths (which may ++ * overflow in the case of the bit length tree). ++ */ ++ tree[heap[heap_max]].Len = 0; /* root of the heap */ ++ ++ for ( h = heap_max + 1; h < HEAP_SIZE; h ++) { ++ n = heap[h]; ++ bits = tree[tree[n].Dad].Len + 1; ++ if ( bits > max_length ) bits = max_length, overflow ++; ++ tree[n].Len = ( ush )bits; ++ /* We overwrite tree[n].Dad which is no longer needed */ ++ ++ if ( n > max_code ) continue; /* not a leaf node */ ++ ++ bl_count[bits] ++; ++ xbits = 0; ++ if ( n >= base ) xbits = extra[n - base]; ++ f = tree[n].Freq; ++ opt_len += ( ulg )f * ( bits + xbits ); ++ if ( stree ) static_len += ( ulg )f * ( stree[n].Len + xbits ); ++ } ++ if ( overflow == 0 ) return; ++ ++ Trace(( stderr, "\nbit length overflow\n" )); ++ /* This happens for example on obj2 and pic of the Calgary corpus */ ++ ++ /* Find the first bit length which could increase: */ ++ do { ++ bits = max_length - 1; ++ while ( bl_count[bits] == 0 ) bits --; ++ bl_count[bits] --; /* move one leaf down the tree */ ++ bl_count[bits + 1] += 2; /* move one overflow item as its brother */ ++ bl_count[max_length] --; ++ /* The brother of the overflow item also moves one step up, ++ * but this does not affect bl_count[max_length] ++ */ ++ overflow -= 2; ++ } while ( overflow > 0 ); ++ ++ /* Now recompute all bit lengths, scanning in increasing frequency. ++ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all ++ * lengths instead of fixing only the wrong ones. This idea is taken ++ * from 'ar' written by Haruhiko Okumura.) ++ */ ++ for ( bits = max_length; bits != 0; bits --) { ++ n = bl_count[bits]; ++ while ( n != 0 ) { ++ m = heap[ -- h]; ++ if ( m > max_code ) continue; ++ if ( tree[m].Len != ( unsigned ) bits ) { ++ Trace(( stderr, "code %d bits %d->%d\n", m, tree[m].Len, bits )); ++ opt_len += (( int )bits -( int )tree[m].Len )*( int )tree[m].Freq; ++ tree[m].Len = ( ush )bits; ++ } ++ n --; ++ } ++ } ++} ++ ++/* =========================================================================== ++* Generate the codes for a given tree and bit counts (which need not be ++* optimal). ++* IN assertion: the array bl_count contains the bit length statistics for ++* the given tree and the field len is set for all tree elements. ++* OUT assertion: the field code is set for all tree elements of non ++* zero code length. ++*/ ++local void gen_codes ( ct_data near * tree, int max_code ) ++{ ++ ush next_code[MAX_BITS + 1]; /* next code value for each bit length */ ++ ush code = 0; /* running code value */ ++ int bits; /* bit index */ ++ int n; /* code index */ ++ ++ /* The distribution counts are first used to generate the code values ++ * without bit reversal. ++ */ ++ for ( bits = 1; bits <= MAX_BITS; bits ++) { ++ next_code[bits] = code = ( code + bl_count[bits - 1] ) << 1; ++ } ++ /* Check that the bit counts in bl_count are consistent. The last code ++ * must be all ones. ++ */ ++ Assert ( code + bl_count[MAX_BITS] - 1 == ( 1 << MAX_BITS )- 1, ++ "inconsistent bit counts" ); ++ Tracev(( stderr, "\ngen_codes: max_code %d ", max_code )); ++ ++ for ( n = 0; n <= max_code; n ++) { ++ int len = tree[n].Len; ++ if ( len == 0 ) continue; ++ /* Now reverse the bits */ ++ tree[n].Code = bi_reverse( next_code[len] ++, len ); ++ ++ Tracec( tree != static_ltree, ( stderr, "\nn %3d %c l %2d c %4x (%x) ", ++ n, ( isgraph( n ) ? n : ' ' ), len, tree[n].Code, next_code[len] - 1 )); ++ } ++} ++ ++/* =========================================================================== ++* Construct one Huffman tree and assigns the code bit strings and lengths. ++* Update the total bit length for the current block. ++* IN assertion: the field freq is set for all tree elements. ++* OUT assertions: the fields len and code are set to the optimal bit length ++* and corresponding code. The length opt_len is updated; static_len is ++* also updated if stree is not null. The field max_code is set. ++*/ ++local void build_tree( tree_desc near * desc ) ++{ ++ ct_data near * tree = desc->dyn_tree; ++ ct_data near * stree = desc->static_tree; ++ int elems = desc->elems; ++ int n, m; /* iterate over heap elements */ ++ int max_code = - 1; /* largest code with non zero frequency */ ++ int node = elems; /* next internal node of the tree */ ++ ++ /* Construct the initial heap, with least frequent element in ++ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. ++ * heap[0] is not used. ++ */ ++ heap_len = 0, heap_max = HEAP_SIZE; ++ ++ for ( n = 0; n < elems; n ++) { ++ if ( tree[n].Freq != 0 ) { ++ heap[ ++ heap_len] = max_code = n; ++ depth[n] = 0; ++ } else { ++ tree[n].Len = 0; ++ } ++ } ++ ++ /* The pkzip format requires that at least one distance code exists, ++ * and that at least one bit should be sent even if there is only one ++ * possible code. So to avoid special checks later on we force at least ++ * two codes of non zero frequency. ++ */ ++ while ( heap_len < 2 ) { ++ int new = heap[ ++ heap_len] = ( max_code < 2 ? ++ max_code : 0 ); /* ��ʼ�� */ ++ tree[new].Freq = 1; ++ depth[new] = 0; ++ opt_len --; if ( stree ) static_len -= stree[new].Len; ++ /* new is 0 or 1 so it does not have extra bits */ ++ } ++ desc->max_code = max_code; ++ ++ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, ++ * establish sub-heaps of increasing lengths: ++ */ ++ for ( n = heap_len / 2; n >= 1; n --) pqdownheap( tree, n ); ++ ++ /* Construct the Huffman tree by repeatedly combining the least two ++ * frequent nodes. ++ */ ++ do { ++ pqremove( tree, n ); /* n = node of least frequency */ ++ m = heap[SMALLEST]; /* m = node of next least frequency */ ++ ++ heap[ -- heap_max] = n; /* keep the nodes sorted by frequency */ ++ heap[ -- heap_max] = m; ++ ++ /* Create a new node father of n and m */ ++ tree[node].Freq = tree[n].Freq + tree[m].Freq; ++ depth[node] = ( uch ) ( MAX( depth[n], depth[m] ) + 1 ); ++ tree[n].Dad = tree[m].Dad = ( ush )node; ++#ifdef DUMP_BL_TREE ++ if ( tree == bl_tree ) { ++ 1 fprintf( stderr, "\nnode %d(%d), sons %d(%d) %d(%d)", ++ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq ); ++ } ++#endif ++ /* and insert the new node in the heap */ ++ heap[SMALLEST] = node ++; ++ pqdownheap( tree, SMALLEST ); ++ } while ( heap_len >= 2 );/* ������ж� */ ++ ++ heap[ -- heap_max] = heap[SMALLEST]; ++ ++ /* At this point, the fields freq and dad are set. We can now ++ * generate the bit lengths. ++ */ ++ gen_bitlen(( tree_desc near *)desc ); ++ ++ /* The field len is now set, we can generate the bit codes */ ++ gen_codes (( ct_data near *)tree, max_code ); ++} ++ ++/* =========================================================================== ++* Scan a literal or distance tree to determine the frequencies of the codes ++* in the bit length tree. Updates opt_len to take into account the repeat ++* counts. (The contribution of the bit length codes will be added later ++* during the construction of bl_tree.) ++*/ ++local void scan_tree ( ct_data near * tree, int max_code ) ++{ ++ int n; /* iterates over all tree elements */ ++ int prevlen = - 1; /* last emitted length */ ++ int curlen; /* length of current code */ ++ int nextlen = tree[0].Len; /* length of next code */ ++ int count = 0; /* repeat count of the current code */ ++ int max_count = 7; /* max repeat count */ ++ int min_count = 4; /* min repeat count */ ++ ++ if ( nextlen == 0 ) max_count = 138, min_count = 3; /* ��ʼ�� */ ++ tree[max_code + 1].Len = ( ush )0xffff; /* guard */ ++ ++ for ( n = 0; n <= max_code; n ++) { ++ curlen = nextlen; nextlen = tree[n + 1].Len; ++ if (++ count < max_count && curlen == nextlen ) { ++ continue; ++ } else if ( count < min_count ) { ++ bl_tree[curlen].Freq += count; ++ } else if ( curlen != 0 ) { ++ if ( curlen != prevlen ) bl_tree[curlen].Freq ++; ++ bl_tree[REP_3_6].Freq ++; ++ } else if ( count <= 10 ) { /* ���ȱȽ� */ ++ bl_tree[REPZ_3_10].Freq ++; ++ } else { ++ bl_tree[REPZ_11_138].Freq ++; ++ } ++ count = 0; prevlen = curlen; ++ if ( nextlen == 0 ) { ++ max_count = 138, min_count = 3; /* ��ʼ�� */ ++ } else if ( curlen == nextlen ) { ++ max_count = 6, min_count = 3; /* ��ʼ�� */ ++ } else { ++ max_count = 7, min_count = 4; /* ��ʼ�� */ ++ } ++ } ++} ++ ++/* =========================================================================== ++* Send a literal or distance tree in compressed form, using the codes in ++* bl_tree. ++*/ ++local void send_tree ( ct_data near * tree, int max_code ) ++{ ++ int n; /* iterates over all tree elements */ ++ int prevlen = - 1; /* last emitted length */ ++ int curlen; /* length of current code */ ++ int nextlen = tree[0].Len; /* length of next code */ ++ int count = 0; /* repeat count of the current code */ ++ int max_count = 7; /* max repeat count */ ++ int min_count = 4; /* min repeat count */ ++ ++ /* tree[max_code+1].Len = -1; */ /* guard already set */ ++ if ( nextlen == 0 ) max_count = 138, min_count = 3; ++ ++ for ( n = 0; n <= max_code; n ++) { ++ curlen = nextlen; nextlen = tree[n + 1].Len; ++ if (++ count < max_count && curlen == nextlen ) { ++ continue; ++ } else if ( count < min_count ) { ++ do { send_code( curlen, bl_tree ); } while (-- count != 0 ); ++ } else if ( curlen != 0 ) { ++ if ( curlen != prevlen ) { ++ send_code( curlen, bl_tree ); count --; ++ } ++ send_code( REP_3_6, bl_tree ); send_bits( count - 3, 2 ); /* ���͵����� */ ++ } else if ( count <= 10 ) { /* ���ݳ��� */ ++ send_code( REPZ_3_10, bl_tree ); send_bits( count - 3, 3 ); /* ���͵����� */ ++ } else { ++ send_code( REPZ_11_138, bl_tree ); send_bits( count - 11, 7 ); /* ���͵����� */ ++ } ++ count = 0; prevlen = curlen; ++ if ( nextlen == 0 ) { ++ max_count = 138, min_count = 3; /* ��ʼ�� */ ++ } else if ( curlen == nextlen ) { ++ max_count = 6, min_count = 3; /* ��ʼ�� */ ++ } else { ++ max_count = 7, min_count = 4; /* ��ʼ�� */ ++ } ++ } ++} ++ ++/* =========================================================================== ++* Construct the Huffman tree for the bit lengths and return the index in ++* bl_order of the last bit length code to send. ++*/ ++local int build_bl_tree() ++{ ++ int max_blindex; /* index of last bit length code of non zero freq */ ++ ++ /* Determine the bit length frequencies for literal and distance trees */ ++ scan_tree(( ct_data near *)dyn_ltree, l_desc.max_code ); ++ scan_tree(( ct_data near *)dyn_dtree, d_desc.max_code ); ++ ++ /* Build the bit length tree: */ ++ build_tree(( tree_desc near *)(& bl_desc )); ++ /* opt_len now includes the length of the tree representations, except ++ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. ++ */ ++ ++ /* Determine the number of bit length codes to send. The pkzip format ++ * requires that at least 4 bit length codes be sent. (appnote.txt says ++ * 3 but the actual value used is 4.) ++ */ ++ for ( max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex --) { ++ if ( bl_tree[bl_order[max_blindex]].Len != 0 ) break; ++ } ++ /* Update opt_len to include the bit length tree and counts */ ++ opt_len += 3 *( max_blindex + 1 ) + 5 + 5 + 4; /* ��ʼ�� */ ++ Tracev(( stderr, "\ndyn trees: dyn %ld, stat %ld", opt_len, static_len )); ++ ++ return max_blindex; ++} ++ ++/* =========================================================================== ++* Send the header for a block using dynamic Huffman trees: the counts, the ++* lengths of the bit length codes, the literal tree and the distance tree. ++* IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. ++*/ ++local void send_all_trees( int lcodes, int dcodes, int blcodes ) ++{ ++ int rank; /* index in bl_order */ ++ ++ Assert ( lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, ++ "too many codes" ); ++ Tracev(( stderr, "\nbl counts: " )); ++ send_bits( lcodes - 257, 5 ); /* not +255 as stated in appnote.txt */ ++ send_bits( dcodes - 1, 5 ); /* д�뻺�� */ ++ send_bits( blcodes - 4, 4 ); /* not -3 as stated in appnote.txt */ ++ for ( rank = 0; rank < blcodes; rank ++) { ++ Tracev(( stderr, "\nbl code %2d ", bl_order[rank] )); ++ send_bits( bl_tree[bl_order[rank]].Len, 3 ); /* д�뻺�� */ ++ } ++ Tracev(( stderr, "\nbl tree: sent %ld", bits_sent )); ++ ++ send_tree(( ct_data near *)dyn_ltree, lcodes - 1 ); /* send the literal tree */ ++ Tracev(( stderr, "\nlit tree: sent %ld", bits_sent )); ++ ++ send_tree(( ct_data near *)dyn_dtree, dcodes - 1 ); /* send the distance tree */ ++ Tracev(( stderr, "\ndist tree: sent %ld", bits_sent )); ++} ++ ++/* =========================================================================== ++* Determine the best encoding for the current block: dynamic trees, static ++* trees or store, and output the encoded block to the zip file. This function ++* returns the total compressed length for the file so far. ++*/ ++ulg flush_block( char * buf, ulg stored_len, int eof ) ++{ ++ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ ++ int max_blindex; /* index of last bit length code of non zero freq */ ++ ++ flag_buf[last_flags] = trees_flags; /* Save the flags for the last 8 items */ ++ ++ /* Check if the file is ascii or binary */ ++ if (* file_type == ( ush )UNKNOWN ) set_file_type(); ++ ++ /* Construct the literal and distance trees */ ++ build_tree(( tree_desc near *)(& l_desc )); ++ Tracev(( stderr, "\nlit data: dyn %ld, stat %ld", opt_len, static_len )); ++ ++ build_tree(( tree_desc near *)(& d_desc )); ++ Tracev(( stderr, "\ndist data: dyn %ld, stat %ld", opt_len, static_len )); ++ /* At this point, opt_len and static_len are the total bit lengths of ++ * the compressed block data, excluding the tree representations. ++ */ ++ ++ /* Build the bit length tree for the above two trees, and get the index ++ * in bl_order of the last bit length code to send. ++ */ ++ max_blindex = build_bl_tree(); ++ ++ /* Determine the best encoding. Compute first the block length in bytes */ ++ opt_lenb = ( opt_len + 3 + 7 )>> 3; /* ��ʼ�� */ ++ static_lenb = ( static_len + 3 + 7 )>> 3; /* ��ʼ�� */ ++ input_len += stored_len; /* for debugging only */ ++ ++ Trace(( stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", ++ opt_lenb, opt_len, static_lenb, static_len, stored_len, ++ last_lit, last_dist )); ++ ++ if ( static_lenb <= opt_lenb ) opt_lenb = static_lenb; ++ ++ /* If compression failed and this is the first and last block, ++ * and if the zip file can be seeked (to rewrite the local header), ++ * the whole file is transformed into a stored file: ++ */ ++ if ( stored_len <= opt_lenb && eof && compressed_len == 0L && seekable()) { ++ /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */ ++ if ( buf == ( char *)0 ) error ( "block vanished" ); ++ ++ copy_block( buf, ( unsigned )stored_len, 0 ); /* without header */ ++ compressed_len = stored_len << 3; /* ѹ������ */ ++ * file_method = STORED; ++ } else if ( stored_len + 4 <= opt_lenb && buf != ( char *)0 ) { /* 4: two words for the lengths */ ++ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. ++ * Otherwise we can't have processed more than WSIZE input bytes since ++ * the last block flush, because compression would have been ++ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to ++ * transform a block into a stored block. ++ */ ++ send_bits(( STORED_BLOCK << 1 )+ eof, 3 ); /* send block type */ ++ compressed_len = ( compressed_len + 3 + 7 ) & ~ 7L; /* ѹ������ */ ++ compressed_len += ( stored_len + 4 ) << 3; /* ѹ������ */ ++ ++ copy_block( buf, ( unsigned )stored_len, 1 ); /* with header */ ++ } else if ( static_lenb == opt_lenb ) { ++ send_bits(( STATIC_TREES << 1 )+ eof, 3 ); /* ���͵����� */ ++ compress_block(( ct_data near *)static_ltree, ( ct_data near *)static_dtree ); ++ compressed_len += 3 + static_len;/* ��ѹ�� */ ++ } else { ++ send_bits(( DYN_TREES << 1 )+ eof, 3 ); /* ���͵����� */ ++ send_all_trees( l_desc.max_code + 1, d_desc.max_code + 1, max_blindex + 1 ); ++ compress_block(( ct_data near *)dyn_ltree, ( ct_data near *)dyn_dtree ); ++ compressed_len += 3 + opt_len; /* ѹ������ */ ++ } ++ Assert ( compressed_len == bits_sent, "bad compressed size" ); ++ init_block(); ++ ++ if ( eof ) { ++ Assert ( input_len == isize, "bad input size" ); ++ bi_windup(); ++ compressed_len += 7; /* align on byte boundary */ ++ } ++ ++ return compressed_len >> 3; /* ѹ������ */ ++} ++ ++/* =========================================================================== ++* Save the match info and tally the frequency counts. Return true if ++* the current block must be flushed. ++*/ ++int ct_tally ( int dist, int lc ) ++{ ++ l_buf[last_lit ++ ] = ( uch )lc; ++ if ( dist == 0 ) { ++ /* lc is the unmatched char */ ++ dyn_ltree[lc].Freq ++; ++ } else { ++ /* Here, lc is the match length - MIN_MATCH */ ++ dist --; /* dist = match distance - 1 */ ++ Assert(( ush )dist < ( ush )MAX_DIST && ++ ( ush )lc <= ( ush )( MAX_MATCH - MIN_MATCH ) && ++ ( ush )d_code( dist ) < ( ush )D_CODES, "ct_tally: bad match" ); ++ ++ dyn_ltree[length_code[lc] + LITERALS + 1].Freq ++; ++ dyn_dtree[d_code( dist )].Freq ++; ++ ++ d_buf[last_dist ++ ] = ( ush )dist; ++ trees_flags |= flag_bit; ++ } ++ flag_bit <<= 1; ++ ++ /* Output the flags if they fill a byte: */ ++ if (( last_lit & 7 ) == 0 ) { ++ flag_buf[last_flags ++ ] = trees_flags; ++ trees_flags = 0, flag_bit = 1; ++ } ++ /* Try to guess if it is profitable to stop the current block here */ ++ if ( level > 2 && ( last_lit & 0xfff ) == 0 ) { ++ /* Compute an upper bound for the compressed length */ ++ ulg out_length = ( ulg )last_lit * 8L; ++ ulg in_length = ( ulg )strstart - block_start; ++ int dcode; ++ for ( dcode = 0; dcode < D_CODES; dcode ++) { ++ out_length += ( ulg )dyn_dtree[dcode].Freq *( 5L + extra_dbits[dcode] );/* ��ѯ */ ++ } ++ out_length >>= 3; /* ������� */ ++ if ( last_dist < last_lit / 2 && out_length < in_length / 2 ) return 1;/* �����ж� */ ++ } ++ return ( last_lit == LIT_BUFSIZE - 1 || last_dist == DIST_BUFSIZE ); ++ /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K ++ * on 16 bit machines and because stored blocks are restricted to ++ * 64K-1 bytes. ++ */ ++} ++ ++/* =========================================================================== ++* Send the block data compressed using the given Huffman trees ++*/ ++local void compress_block( ct_data near * ltree, ct_data near * dtree ) ++{ ++ unsigned dist; /* distance of matched string */ ++ int lc; /* match length or unmatched char (if dist == 0) */ ++ unsigned lx = 0; /* running index in l_buf */ ++ unsigned dx = 0; /* running index in d_buf */ ++ unsigned fx = 0; /* running index in flag_buf */ ++ uch flag = 0; /* current flags */ ++ unsigned code; /* the code to send */ ++ int extra; /* number of extra bits to send */ ++ ++ if ( last_lit != 0 ) do { ++ if (( lx & 7 ) == 0 ) flag = flag_buf[fx ++ ]; /* ��־ */ ++ lc = l_buf[lx ++ ]; ++ if (( flag & 1 ) == 0 ) { ++ send_code( lc, ltree ); /* send a literal byte */ ++ Tracecv( isgraph( lc ), ( stderr, " '%c' ", lc )); ++ } else { ++ /* Here, lc is the match length - MIN_MATCH */ ++ code = length_code[lc]; ++ send_code( code + LITERALS + 1, ltree ); /* send the length code */ ++ extra = extra_lbits[code]; ++ if ( extra != 0 ) { ++ lc -= base_length[code]; ++ send_bits( lc, extra ); /* send the extra length bits */ ++ } ++ dist = d_buf[dx ++ ]; ++ /* Here, dist is the match distance - 1 */ ++ code = d_code( dist ); ++ Assert ( code < D_CODES, "bad d_code" ); ++ ++ send_code( code, dtree ); /* send the distance code */ ++ extra = extra_dbits[code]; ++ if ( extra != 0 ) { ++ dist -= base_dist[code]; ++ send_bits( dist, extra ); /* send the extra distance bits */ ++ } ++ } /* literal or match pair ? */ ++ flag >>= 1; ++ } while ( lx < last_lit ); ++ ++ send_code( END_BLOCK, ltree ); ++} ++ ++/* =========================================================================== ++* Set the file type to ASCII or BINARY, using a crude approximation: ++* binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. ++* IN assertion: the fields freq of dyn_ltree are set and the total of all ++* frequencies does not exceed 64K (to fit in an int on 16 bit machines). ++*/ ++local void set_file_type() ++{ ++ int n = 0; ++ unsigned ascii_freq = 0; ++ unsigned bin_freq = 0; ++ while ( n < 7 ) bin_freq += dyn_ltree[n ++ ].Freq; /* ��ʼ�� */ ++ while ( n < 128 ) ascii_freq += dyn_ltree[n ++ ].Freq; /* ��ʼ�� */ ++ while ( n < LITERALS ) bin_freq += dyn_ltree[n ++ ].Freq; ++ * file_type = bin_freq > ( ascii_freq >> 2 ) ? BINARY : ASCII; /* ������ò¿ª·ï¿½Ê½ */ ++ if (* file_type == BINARY && translate_eol ) { ++ warn( "-l used on binary file", "" ); ++ } ++} +diff --git a/lib/gzip/unzip.c b/lib/gzip/unzip.c +new file mode 100644 +index 0000000..5f9ea66 +--- /dev/null ++++ b/lib/gzip/unzip.c +@@ -0,0 +1,50 @@ ++/* unzip.c -- decompress files in gzip or pkzip format. ++* Copyright (C) 1992-1993 Jean-loup Gailly ++* This is free software; you can redistribute it and/or modify it under the ++* terms of the GNU General Public License, see the file COPYING. ++* ++* The code in this file is derived from the file funzip.c written ++* and put in the public domain by Mark Adler. ++*/ ++ ++#include "gzip.h" ++ ++/* =========================================================================== ++* Unzip in to out. This routine works on both gzip and pkzip files. ++* ++* IN assertions: the buffer inbuf contains already the beginning of ++* the compressed data, from offsets inptr to insize-1 included. ++* The magic header has already been checked. The output buffer is cleared. ++*/ ++ ++int unzip( void ) ++{ ++ ulg orig_crc = 0; /* original crc */ ++ ulg orig_len = 0; /* original uncompressed length */ ++ int n; uch buf[8]; /* extended local header */ ++ ++ updcrc( NULL, 0 ); /* initialize crc */ ++ ++ /* Decompress */ ++ inflate(); ++ ++ for ( n = 0; n < 8; n ++) /* Get the crc and original length */ ++ { ++ buf[n] = ( uch )get_byte(); /* may cause an error if EOF */ ++ } ++ ++ orig_crc = LG( buf ); ++ orig_len = LG( buf + 4 ); /* Validate decompression */ ++ ++ if ( orig_crc != updcrc( outbuf, 0 )) ++ { ++ error( "invalid compressed data--crc error" ); ++ } ++ ++ if ( orig_len != ( ulg )bytes_out ) ++ { ++ error( "invalid compressed data--length error" ); ++ } ++ ++ return 0; ++} +diff --git a/lib/gzip/util.c b/lib/gzip/util.c +new file mode 100644 +index 0000000..04395dc +--- /dev/null ++++ b/lib/gzip/util.c +@@ -0,0 +1,256 @@ ++/* util.c -- utility functions for gzip support ++* Copyright (C) 1992-1993 Jean-loup Gailly ++* This is free software; you can redistribute it and/or modify it under the ++* terms of the GNU General Public License, see the file COPYING. ++*/ ++ ++//#include <stdlib.h> ++#include "gzip.h" ++ ++extern ulg crc_32_tab[]; /* crc table, defined below */ ++ ++/* =========================================================================== ++* Run a set of bytes through the crc shift register. If s is a NULL ++* pointer, then initialize the crc shift register contents instead. ++* Return the current crc in either case. ++*/ ++ulg updcrc( uch * s, unsigned n ) ++{ ++ register ulg c; /* temporary variable */ ++ ++ static ulg crc = ( ulg )0xffffffffL; /* shift register contents */ ++ ++ if ( s == NULL ) { ++ c = 0xffffffffL; /* ��ʼ��crc */ ++ } else { ++ c = crc; ++ if ( n ) do { ++ c = crc_32_tab[(( int )c ^ (* s ++)) & 0xff] ^ ( c >> 8 ); /* ����crc */ ++ } while (-- n ); ++ } ++ crc = c; ++ return c ^ 0xffffffffL; /* (instead of ~c for 64-bit machines) */ ++} ++ ++/* =========================================================================== ++* Clear input and output buffers ++*/ ++void clear_bufs() ++{ ++ outcnt = 0; ++ insize = inptr = 0; ++ bytes_in = bytes_out = 0L; ++} ++ ++char * unzip_mem_inptr = 0; ++int unzip_mem_insize = 0; ++int unzip_mem_inpos = 0; ++ ++/* =========================================================================== ++* Fill the input buffer. This is called only when the buffer is empty. ++*/ ++int fill_inbuf( void ) ++{ ++ int len; ++#undef min ++#define min(a,b) (((int)(a) < (int)(b)) ? (a) : (b)) ++ /* Read as much as possible */ ++ insize = 0; ++ ++ do { ++ len = min( unzip_mem_insize - unzip_mem_inpos, INBUFSIZ - insize ); ++ if( len > 0 ) ++ { ++ memcpy(( char *)inbuf + insize, & unzip_mem_inptr[unzip_mem_inpos], len ); ++ insize += len; ++ unzip_mem_inpos += len; ++ } ++ else ++ break; ++ } while ( insize < INBUFSIZ ); ++ ++ if ( insize == 0 ) { ++ read_error(); ++ } ++ bytes_in += ( ulg )insize; ++ inptr = 1; ++ return inbuf[0]; ++} ++ ++/* =========================================================================== ++* Write the output buffer outbuf[0..outcnt-1] and update bytes_out. ++* (used for the compressed data only) ++*/ ++void flush_outbuf() ++{ ++ if ( outcnt == 0 ) return; ++ ++ write_buf(( char *)outbuf, outcnt ); ++ bytes_out += ( ulg )outcnt; ++ outcnt = 0; ++} ++ ++/* =========================================================================== ++* Write the output window window[0..outcnt-1] and update crc and bytes_out. ++* (Used for the decompressed data only.) ++*/ ++void flush_window() ++{ ++ if ( outcnt == 0 ) return; ++ updcrc( window, outcnt ); ++ write_buf(( char *)window, outcnt ); ++ bytes_out += ( ulg )outcnt; ++ outcnt = 0; ++} ++ ++/* =========================================================================== ++* Does the same as write(), but also handles partial pipe writes and checks ++* for error return. ++*/ ++char * zip_mem_outptr; ++int zip_mem_outlen = 0; ++void write_buf( voidp buf, unsigned cnt ) ++{ ++ memcpy(& zip_mem_outptr[zip_mem_outlen], buf, cnt ); ++ zip_mem_outlen += cnt; ++} ++ ++/* ======================================================================== ++* Error handlers. ++*/ ++void error( char * m ) ++{ ++} ++ ++void warn( char * a, char * b ) ++{ ++} ++ ++void read_error() ++{ ++} ++ ++void write_error() ++{ ++} ++ ++/* ======================================================================== ++* Semi-safe malloc -- never returns NULL. ++*/ ++voidp xmalloc ( unsigned size ) ++{ ++ voidp cp = ( voidp )vmalloc ( size ); ++ ++ if ( cp == NULL ) error( "out of memory" ); ++ return cp; ++} ++ ++/* ======================================================================== ++* Table of CRC-32's of all single-byte values (made by makecrc.c) ++*/ ++ulg crc_32_tab[] = { ++ /* Table of CRC-32's of all single-byte values */ ++ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, ++ /* Table of CRC-32's of all single-byte values */ ++ 0x2d02ef8dL ++}; +diff --git a/lib/gzip/zip.c b/lib/gzip/zip.c +new file mode 100644 +index 0000000..e923bf8 +--- /dev/null ++++ b/lib/gzip/zip.c +@@ -0,0 +1,57 @@ ++/* zip.c -- compress files to the gzip or pkzip format ++* Copyright (C) 1992-1993 Jean-loup Gailly ++* This is free software; you can redistribute it and/or modify it under the ++* terms of the GNU General Public License, see the file COPYING. ++*/ ++#include "gzip.h" ++local ulg crc = 0; /* crc on uncompressed file data */ ++/* =========================================================================== ++* Deflate in to out. ++* IN assertions: the input and output buffers are cleared. ++* The variables time_stamp and save_orig_name are initialized. ++*/ ++int zip( void ) ++{ ++ uch flags = 0; /* general purpose bit flags */ ++ ush attr = 0; /* ascii/binary flag */ ++ ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */ ++ outcnt = 0; /* Write the header to the gzip file. See algorithm.doc for the format */ ++ method = DEFLATED; ++ put_byte( GZIP_MAGIC[0] ); /* magic header */ ++ put_byte( GZIP_MAGIC[1] ); ++ put_byte( DEFLATED ); /* compression method */ ++ put_byte( flags ); /* general flags */ ++ put_long( time_stamp ); /* Write deflated file to zip file */ ++ crc = updcrc( 0, 0 ); ++ bi_init(); ++ ct_init(& attr, & method ); ++ lm_init( level, & deflate_flags ); ++ put_byte(( uch )deflate_flags ); /* extra flags */ ++ put_byte( 0 ); /* OS identifier */ ++ ( void )deflate(); /* Write the crc and uncompressed size */ ++ put_long( crc ); ++ put_long( isize ); ++ flush_outbuf(); ++ ++ return 0; ++} ++ ++char * zip_mem_inptr = NULL; ++int zip_mem_insize = 0; ++int zip_mem_inpos = 0; ++int mem_read( char * buf, unsigned size ) ++{ ++ int len; ++#define min_1( a, b ) ((( int )( a ) < ( int )( b )) ? ( a ) : ( b )) ++ len = min_1( zip_mem_insize - zip_mem_inpos, size ); ++ if( len > 0 ) ++ { ++ memcpy( buf, & zip_mem_inptr[zip_mem_inpos], len ); ++ crc = updcrc(( uch *)buf, len ); ++ isize += ( ulg )len; ++ zip_mem_inpos += len; ++ } ++ else ++ len = - 1; ++ return ( int )len; ++} +diff --git a/lib/gzip/zipmem.h b/lib/gzip/zipmem.h +new file mode 100644 +index 0000000..345f892 +--- /dev/null ++++ b/lib/gzip/zipmem.h +@@ -0,0 +1,9 @@ ++#ifndef _ZIP_MEM_H_ ++#define _ZIP_MEM_H_ ++ ++ ++int zipmem( char * mem_inptr, int mem_insize, char * mem_outptr ); ++ ++int unzipmem( char * mem_inptr, int mem_insize, char * mem_outptr ); ++ ++#endif +diff --git a/linaro/configs/arndale.conf b/linaro/configs/arndale.conf +new file mode 100644 +index 0000000..109052f +--- /dev/null ++++ b/linaro/configs/arndale.conf +@@ -0,0 +1,66 @@ ++CONFIG_KALLSYMS_ALL=y ++CONFIG_PARTITION_ADVANCED=y ++CONFIG_BSD_DISKLABEL=y ++CONFIG_SOLARIS_X86_PARTITION=y ++CONFIG_ARCH_EXYNOS=y ++CONFIG_S3C_LOWLEVEL_UART_PORT=2 ++CONFIG_ARCH_EXYNOS5=y ++# CONFIG_EXYNOS_ATAGS is not set ++CONFIG_MACH_EXYNOS4_DT=y ++CONFIG_VMSPLIT_2G=y ++CONFIG_NR_CPUS=2 ++CONFIG_HIGHMEM=y ++# CONFIG_COMPACTION is not set ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y ++CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init= mem=256M" ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_VFP=y ++CONFIG_NEON=y ++CONFIG_PM_RUNTIME=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_SD=y ++CONFIG_CHR_DEV_SG=y ++CONFIG_ATA=y ++CONFIG_SATA_AHCI_PLATFORM=y ++CONFIG_SATA_EXYNOS=y ++CONFIG_AX88796=y ++CONFIG_AX88796_93CX6=y ++CONFIG_INPUT_EVDEV=y ++CONFIG_KEYBOARD_GPIO=y ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_SAMSUNG=y ++CONFIG_SERIAL_SAMSUNG_CONSOLE=y ++CONFIG_HW_RANDOM=y ++CONFIG_I2C=y ++CONFIG_I2C_S3C2410=y ++CONFIG_THERMAL=y ++CONFIG_CPU_THERMAL=y ++CONFIG_EXYNOS_THERMAL=y ++CONFIG_MFD_SEC_CORE=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++CONFIG_REGULATOR_S5M8767=y ++CONFIG_DRM=y ++CONFIG_DRM_LOAD_EDID_FIRMWARE=y ++CONFIG_DRM_EXYNOS=y ++CONFIG_DRM_EXYNOS_DMABUF=y ++CONFIG_DRM_EXYNOS_HDMI=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_LOGO=y ++CONFIG_MMC=y ++CONFIG_MMC_UNSAFE_RESUME=y ++CONFIG_MMC_DW=y ++CONFIG_MMC_DW_IDMAC=y ++CONFIG_MMC_DW_EXYNOS=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_DRV_S3C=y ++CONFIG_DEBUG_KERNEL=y ++CONFIG_DETECT_HUNG_TASK=y ++CONFIG_DEBUG_RT_MUTEXES=y ++CONFIG_DEBUG_SPINLOCK=y ++CONFIG_DEBUG_INFO=y ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++CONFIG_DEBUG_USER=y ++CONFIG_TUN=y +diff --git a/linaro/configs/big-LITTLE-IKS.conf b/linaro/configs/big-LITTLE-IKS.conf +new file mode 100644 +index 0000000..b067fde +--- /dev/null ++++ b/linaro/configs/big-LITTLE-IKS.conf +@@ -0,0 +1,5 @@ ++CONFIG_BIG_LITTLE=y ++CONFIG_BL_SWITCHER=y ++CONFIG_ARM_DT_BL_CPUFREQ=y ++CONFIG_ARM_VEXPRESS_BL_CPUFREQ=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y +diff --git a/linaro/configs/bigendian.conf b/linaro/configs/bigendian.conf +new file mode 100644 +index 0000000..6a10202 +--- /dev/null ++++ b/linaro/configs/bigendian.conf +@@ -0,0 +1,4 @@ ++CONFIG_CPU_BIG_ENDIAN=y ++CONFIG_CPU_ENDIAN_BE8=y ++# CONFIG_VIRTUALIZATION is not set ++# CONFIG_MMC_DW_IDMAC is not set +diff --git a/linaro/configs/distribution.conf b/linaro/configs/distribution.conf +new file mode 100644 +index 0000000..729b9b8 +--- /dev/null ++++ b/linaro/configs/distribution.conf +@@ -0,0 +1,49 @@ ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_CGROUPS=y ++# CONFIG_COMPAT_BRK is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 ++CONFIG_SECCOMP=y ++CONFIG_CC_STACKPROTECTOR=y ++CONFIG_SYN_COOKIES=y ++CONFIG_IPV6=y ++CONFIG_NETLABEL=y ++CONFIG_BRIDGE_NETFILTER=y ++CONFIG_NF_CONNTRACK=m ++CONFIG_NETFILTER_XT_CONNMARK=m ++CONFIG_NETFILTER_XT_MARK=m ++CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m ++CONFIG_NF_CONNTRACK_IPV4=m ++CONFIG_NF_NAT_IPV4=m ++CONFIG_IP_NF_IPTABLES=m ++CONFIG_IP_NF_FILTER=m ++CONFIG_IP_NF_MANGLE=m ++CONFIG_NF_CONNTRACK_IPV6=m ++CONFIG_NF_NAT_IPV6=m ++CONFIG_IP6_NF_IPTABLES=m ++CONFIG_IP6_NF_FILTER=m ++CONFIG_IP6_NF_MANGLE=m ++CONFIG_BRIDGE_NF_EBTABLES=m ++CONFIG_BRIDGE_EBT_MARK_T=m ++CONFIG_BRIDGE=m ++CONFIG_TUN=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_SIZE=65536 ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_UINPUT=y ++# CONFIG_DEVKMEM is not set ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_AUTOFS4_FS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_STRICT_DEVMEM=y ++CONFIG_SECURITY=y ++CONFIG_LSM_MMAP_MIN_ADDR=0 ++CONFIG_SECURITY_SELINUX=y ++CONFIG_SECURITY_SMACK=y ++CONFIG_SECURITY_APPARMOR=y ++CONFIG_DEFAULT_SECURITY_APPARMOR=y ++CONFIG_HUGETLBFS=y ++CONFIG_HUGETLB_PAGE=y ++CONFIG_TRANSPARENT_HUGEPAGE=y ++CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y +diff --git a/linaro/configs/kvm-guest.conf b/linaro/configs/kvm-guest.conf +new file mode 100644 +index 0000000..00e84a3 +--- /dev/null ++++ b/linaro/configs/kvm-guest.conf +@@ -0,0 +1,11 @@ ++CONFIG_BALLOON_COMPACTION=y ++CONFIG_VIRTIO_BLK=y ++CONFIG_VIRTIO_NET=y ++CONFIG_HVC_DRIVER=y ++CONFIG_VIRTIO_CONSOLE=y ++CONFIG_VIRTIO=y ++CONFIG_VIRTIO_BALLOON=y ++CONFIG_VIRTIO_MMIO=y ++CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y ++CONFIG_VIRTUALIZATION=y ++# CONFIG_THUMB2_KERNEL is not set +diff --git a/linaro/configs/kvm-host.conf b/linaro/configs/kvm-host.conf +new file mode 100644 +index 0000000..21a40e0 +--- /dev/null ++++ b/linaro/configs/kvm-host.conf +@@ -0,0 +1,11 @@ ++CONFIG_VIRTUALIZATION=y ++CONFIG_ARM_LPAE=y ++CONFIG_ARM_VIRT_EXT=y ++CONFIG_HAVE_KVM_IRQCHIP=y ++CONFIG_KVM_ARM_HOST=y ++CONFIG_KVM_ARM_MAX_VCPUS=4 ++CONFIG_KVM_ARM_TIMER=y ++CONFIG_KVM_ARM_VGIC=y ++CONFIG_KVM_MMIO=y ++CONFIG_KVM=y ++CONFIG_BLK_DEV_NBD=m +diff --git a/linaro/configs/linaro-base.conf b/linaro/configs/linaro-base.conf +new file mode 100644 +index 0000000..0a1d7bc +--- /dev/null ++++ b/linaro/configs/linaro-base.conf +@@ -0,0 +1,129 @@ ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++CONFIG_LOG_BUF_SHIFT=16 ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_EMBEDDED=y ++CONFIG_HOTPLUG=y ++CONFIG_PERF_EVENTS=y ++CONFIG_SLAB=y ++CONFIG_PROFILING=y ++CONFIG_OPROFILE=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_SMP=y ++CONFIG_SCHED_MC=y ++CONFIG_SCHED_SMT=y ++CONFIG_THUMB2_KERNEL=y ++CONFIG_AEABI=y ++# CONFIG_OABI_COMPAT is not set ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_IDLE=y ++CONFIG_BINFMT_MISC=y ++CONFIG_MD=y ++CONFIG_BLK_DEV_DM=y ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_XFRM_USER=y ++CONFIG_NET_KEY=y ++CONFIG_NET_KEY_MIGRATE=y ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++CONFIG_IP_PNP_RARP=y ++# CONFIG_INET_LRO is not set ++CONFIG_NETFILTER=y ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_CONNECTOR=y ++CONFIG_MTD=y ++CONFIG_MTD_CMDLINE_PARTS=y ++CONFIG_MTD_BLOCK=y ++CONFIG_MTD_OOPS=y ++CONFIG_MTD_CFI=y ++CONFIG_MTD_CFI_INTELEXT=y ++CONFIG_MTD_NAND=y ++CONFIG_NETDEVICES=y ++CONFIG_EXT2_FS=y ++CONFIG_EXT3_FS=y ++CONFIG_EXT4_FS=y ++CONFIG_BTRFS_FS=y ++CONFIG_QUOTA=y ++CONFIG_QFMT_V2=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_TMPFS=y ++CONFIG_ECRYPT_FS=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_SUMMARY=y ++CONFIG_JFFS2_FS_XATTR=y ++CONFIG_JFFS2_COMPRESSION_OPTIONS=y ++CONFIG_JFFS2_LZO=y ++CONFIG_JFFS2_RUBIN=y ++CONFIG_CRAMFS=y ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++# CONFIG_NFS_V2 is not set ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_ROOT_NFS=y ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_ISO8859_1=y ++CONFIG_PRINTK_TIME=y ++CONFIG_MAGIC_SYSRQ=y ++CONFIG_DEBUG_FS=y ++CONFIG_SCHEDSTATS=y ++CONFIG_TIMER_STATS=y ++CONFIG_KEYS=y ++CONFIG_CRYPTO_MICHAEL_MIC=y ++CONFIG_CRC_CCITT=y ++CONFIG_CRC_T10DIF=y ++CONFIG_CRC_ITU_T=y ++CONFIG_CRC7=y ++CONFIG_HW_PERF_EVENTS=y ++CONFIG_FUNCTION_TRACER=y ++CONFIG_ENABLE_DEFAULT_TRACERS=y ++CONFIG_PROC_DEVICETREE=y ++CONFIG_JUMP_LABEL=y ++CONFIG_STRICT_DEVMEM=y ++CONFIG_KGDB=y ++CONFIG_KGDB_TESTS=y ++CONFIG_OF_IDLE_STATES=y ++CONFIG_FTRACE=y ++CONFIG_FUNCTION_TRACER=y ++CONFIG_FTRACE_SYSCALLS=y ++CONFIG_STACK_TRACER=y ++CONFIG_FUNCTION_PROFILER=y ++CONFIG_MAILBOX=y ++CONFIG_AUDIT=y ++CONFIG_NF_CONNTRACK_SECMARK=y ++CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y ++CONFIG_NETFILTER_XT_TARGET_SECMARK=y ++CONFIG_IP_NF_SECURITY=y ++CONFIG_SECURITY=y ++CONFIG_SECURITY_NETWORK=y ++CONFIG_LSM_MMAP_MIN_ADDR=4096 ++CONFIG_SECURITY_SELINUX=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_DEBUG_INFO=y ++CONFIG_FANOTIFY=y ++CONFIG_RCU_TORTURE_TEST=m ++CONFIG_RCU_TORTURE_TEST_RUNNABLE=n ++CONFIG_CORESIGHT=y ++CONFIG_CORESIGHT_LINKS_AND_SINKS=y ++CONFIG_CORESIGHT_SOURCE_ETM=y ++ ++# enable ZRAM features ++CONFIG_ZPOOL=y ++CONFIG_ZSMALLOC=y ++CONFIG_ZRAM=y ++CONFIG_ZRAM_LZ4_COMPRESS=y +diff --git a/linaro/configs/omap4.conf b/linaro/configs/omap4.conf +new file mode 100644 +index 0000000..d0a2b80 +--- /dev/null ++++ b/linaro/configs/omap4.conf +@@ -0,0 +1,196 @@ ++CONFIG_EXPERT=y ++CONFIG_KPROBES=y ++CONFIG_MODULE_FORCE_LOAD=y ++CONFIG_MODULE_FORCE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++# CONFIG_BLK_DEV_BSG is not set ++CONFIG_PARTITION_ADVANCED=y ++CONFIG_GPIO_PCA953X=y ++CONFIG_OMAP_RESET_CLOCKS=y ++CONFIG_OMAP_MUX_DEBUG=y ++CONFIG_ARCH_OMAP3=y ++CONFIG_ARCH_OMAP4=y ++CONFIG_ARCH_OMAP2PLUS=y ++CONFIG_SOC_OMAP5=y ++# CONFIG_ARCH_OMAP2 is not set ++CONFIG_ARCH_VEXPRESS_CA9X4=y ++CONFIG_ARM_THUMBEE=y ++CONFIG_ARM_ERRATA_411920=y ++CONFIG_NR_CPUS=2 ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 ++CONFIG_CMDLINE="root=/dev/mmcblk0p2 rootwait console=ttyO2,115200" ++CONFIG_KEXEC=y ++CONFIG_PM_DEBUG=y ++CONFIG_CAN=m ++CONFIG_CAN_C_CAN=m ++CONFIG_CAN_C_CAN_PLATFORM=m ++CONFIG_BT=m ++CONFIG_BT_HCIUART=m ++CONFIG_BT_HCIUART_H4=y ++CONFIG_BT_HCIUART_BCSP=y ++CONFIG_BT_HCIUART_LL=y ++CONFIG_BT_HCIBCM203X=m ++CONFIG_BT_HCIBPA10X=m ++CONFIG_CFG80211=m ++CONFIG_MAC80211=m ++CONFIG_MAC80211_RC_PID=y ++CONFIG_MAC80211_RC_DEFAULT_PID=y ++CONFIG_CMA=y ++CONFIG_MTD_NAND_OMAP2=y ++CONFIG_MTD_ONENAND=y ++CONFIG_MTD_ONENAND_VERIFY_WRITE=y ++CONFIG_MTD_ONENAND_OMAP2=y ++CONFIG_MTD_UBI=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_RAM_SIZE=16384 ++CONFIG_SENSORS_TSL2550=m ++CONFIG_SENSORS_LIS3_I2C=m ++CONFIG_SCSI=y ++CONFIG_BLK_DEV_SD=y ++CONFIG_SCSI_MULTI_LUN=y ++CONFIG_SCSI_SCAN_ASYNC=y ++CONFIG_KS8851=y ++CONFIG_KS8851_MLL=y ++CONFIG_SMC91X=y ++CONFIG_SMSC911X=y ++CONFIG_TI_CPSW=y ++CONFIG_SMSC_PHY=y ++CONFIG_USB_USBNET=y ++CONFIG_USB_NET_SMSC95XX=y ++CONFIG_USB_ALI_M5632=y ++CONFIG_USB_AN2720=y ++CONFIG_USB_EPSON2888=y ++CONFIG_USB_KC2190=y ++CONFIG_LIBERTAS=m ++CONFIG_LIBERTAS_USB=m ++CONFIG_LIBERTAS_SDIO=m ++CONFIG_LIBERTAS_DEBUG=y ++CONFIG_INPUT_JOYDEV=y ++CONFIG_INPUT_EVDEV=y ++CONFIG_KEYBOARD_GPIO=y ++CONFIG_KEYBOARD_MATRIX=m ++CONFIG_KEYBOARD_TWL4030=y ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_ADS7846=y ++CONFIG_INPUT_TWL4030_PWRBUTTON=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_LEGACY_PTYS is not set ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=32 ++CONFIG_SERIAL_8250_EXTENDED=y ++CONFIG_SERIAL_8250_MANY_PORTS=y ++CONFIG_SERIAL_8250_SHARE_IRQ=y ++CONFIG_SERIAL_8250_DETECT_IRQ=y ++CONFIG_SERIAL_8250_RSA=y ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++CONFIG_SERIAL_OMAP=y ++CONFIG_SERIAL_OMAP_CONSOLE=y ++CONFIG_HW_RANDOM=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_SPI=y ++CONFIG_SPI_OMAP24XX=y ++CONFIG_PINCTRL_SINGLE=y ++CONFIG_DEBUG_GPIO=y ++CONFIG_GPIO_SYSFS=y ++CONFIG_GPIO_TWL4030=y ++CONFIG_W1=y ++CONFIG_SENSORS_LM75=m ++CONFIG_WATCHDOG=y ++CONFIG_OMAP_WATCHDOG=y ++CONFIG_TWL4030_WATCHDOG=y ++CONFIG_MFD_TPS65217=y ++CONFIG_MFD_TPS65910=y ++CONFIG_TWL6040_CORE=y ++CONFIG_REGULATOR_TPS65023=y ++CONFIG_REGULATOR_TPS6507X=y ++CONFIG_REGULATOR_TPS65217=y ++CONFIG_REGULATOR_TPS65910=y ++CONFIG_REGULATOR_TWL4030=y ++CONFIG_FB=y ++CONFIG_FIRMWARE_EDID=y ++CONFIG_FB_MODE_HELPERS=y ++CONFIG_FB_TILEBLITTING=y ++CONFIG_OMAP2_DSS=m ++CONFIG_OMAP2_DSS_RFBI=y ++CONFIG_OMAP2_DSS_SDI=y ++CONFIG_OMAP2_DSS_DSI=y ++CONFIG_FB_OMAP2=m ++CONFIG_PANEL_GENERIC_DPI=m ++CONFIG_PANEL_TFP410=m ++CONFIG_PANEL_SHARP_LS037V7DW01=m ++CONFIG_PANEL_NEC_NL8048HL11_01B=m ++CONFIG_PANEL_TAAL=m ++CONFIG_PANEL_TPO_TD043MTEA1=m ++CONFIG_PANEL_ACX565AKM=m ++CONFIG_BACKLIGHT_LCD_SUPPORT=y ++CONFIG_LCD_CLASS_DEVICE=y ++CONFIG_LCD_PLATFORM=y ++CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y ++CONFIG_FONTS=y ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++CONFIG_LOGO=y ++CONFIG_SOUND=m ++CONFIG_SND=m ++CONFIG_SND_VERBOSE_PRINTK=y ++CONFIG_SND_DEBUG=y ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_SOC=m ++CONFIG_SND_OMAP_SOC=m ++CONFIG_SND_OMAP_SOC_OMAP_TWL4030=m ++CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040=m ++CONFIG_SND_OMAP_SOC_OMAP3_PANDORA=m ++CONFIG_USB=y ++CONFIG_USB_DEBUG=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_MON=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_WDM=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_TEST=y ++CONFIG_USB_PHY=y ++CONFIG_NOP_USB_XCEIV=y ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_DEBUG=y ++CONFIG_USB_GADGET_DEBUG_FILES=y ++CONFIG_USB_GADGET_DEBUG_FS=y ++CONFIG_USB_ZERO=m ++CONFIG_MMC=y ++CONFIG_MMC_UNSAFE_RESUME=y ++CONFIG_SDIO_UART=y ++CONFIG_MMC_ARMMMCI=y ++CONFIG_MMC_OMAP=y ++CONFIG_MMC_OMAP_HS=y ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_TRIGGERS=y ++CONFIG_LEDS_TRIGGER_TIMER=y ++CONFIG_LEDS_TRIGGER_ONESHOT=y ++CONFIG_LEDS_TRIGGER_HEARTBEAT=y ++CONFIG_LEDS_TRIGGER_BACKLIGHT=y ++CONFIG_LEDS_TRIGGER_CPU=y ++CONFIG_LEDS_TRIGGER_GPIO=y ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_DRV_TWL92330=y ++CONFIG_RTC_DRV_TWL4030=y ++CONFIG_RTC_DRV_OMAP=y ++CONFIG_DMADEVICES=y ++CONFIG_DMA_OMAP=y ++# CONFIG_EXT3_FS_XATTR is not set ++CONFIG_UBIFS_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_ROOT_NFS=y ++# CONFIG_DEBUG_BUGVERBOSE is not set ++CONFIG_DEBUG_INFO=y ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++CONFIG_LIBCRC32C=y ++# CONFIG_CPU_FREQ is not set +diff --git a/linaro/configs/preempt-rt.conf b/linaro/configs/preempt-rt.conf +new file mode 100644 +index 0000000..7c6594f +--- /dev/null ++++ b/linaro/configs/preempt-rt.conf +@@ -0,0 +1,3 @@ ++CONFIG_PREEMPT=y ++CONFIG_PREEMPT_RT_FULL=y ++# CONFIG_CPU_FREQ is not set +diff --git a/linaro/configs/vexpress64.conf b/linaro/configs/vexpress64.conf +new file mode 100644 +index 0000000..cb5d016 +--- /dev/null ++++ b/linaro/configs/vexpress64.conf +@@ -0,0 +1,56 @@ ++CONFIG_ARCH_VEXPRESS=y ++CONFIG_SMP=y ++CONFIG_NR_CPUS=8 ++CONFIG_CMDLINE="console=ttyAMA0" ++CONFIG_COMPAT=y ++CONFIG_SMC91X=y ++CONFIG_INPUT_EVDEV=y ++CONFIG_SERIO_AMBAKMI=y ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_SERIO_I8042 is not set ++CONFIG_FB=y ++CONFIG_FB_ARMCLCD=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++# CONFIG_VGA_CONSOLE is not set ++CONFIG_LOGO=y ++# CONFIG_LOGO_LINUX_MONO is not set ++# CONFIG_LOGO_LINUX_VGA16 is not set ++CONFIG_MMC=y ++CONFIG_MMC_ARMMMCI=y ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_DRV_PL031=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_ROOT_NFS=y ++CONFIG_VIRTIO=y ++CONFIG_VIRTIO_BLK=y ++CONFIG_VIRTIO_MMIO=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_FIXED_VOLTAGE=y ++CONFIG_CMA=y ++CONFIG_DMA_CMA=y ++CONFIG_COMMON_CLK_SCPI=y ++CONFIG_SMSC911X=y ++CONFIG_I2C=y ++CONFIG_ARM_MHU_MBOX=y ++CONFIG_ARM_SCPI_PROTOCOL=y ++CONFIG_USB_HIDDEV=y ++CONFIG_SCSI=y ++CONFIG_BLK_DEV_SD=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB=y ++CONFIG_USB_ULPI=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_EHCI_HCD_SYNOPSYS=y ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_PHY=y ++CONFIG_USB_ISP1301=y ++CONFIG_PM_OPP=y ++CONFIG_GENERIC_CPUFREQ_CPU0=y ++CONFIG_ARM_BIG_LITTLE_CPUFREQ=y ++CONFIG_ARM_DT_BL_CPUFREQ=y ++CONFIG_ARM64_CPUIDLE=y ++CONFIG_ARM64_CRYPTO=y +diff --git a/mm/cma.c b/mm/cma.c +index 8e9ec13..cc14aea 100644 +--- a/mm/cma.c ++++ b/mm/cma.c +@@ -46,6 +46,9 @@ static struct cma cma_areas[MAX_CMA_AREAS]; + static unsigned cma_area_count; + static DEFINE_MUTEX(cma_mutex); + ++#define BITMAP_SIZE 16384 ++static char bitmap_buf[BITMAP_SIZE]; ++ + phys_addr_t cma_get_base(struct cma *cma) + { + return PFN_PHYS(cma->base_pfn); +@@ -89,15 +92,18 @@ static void cma_clear_bitmap(struct cma *cma, unsigned long pfn, int count) + static int __init cma_activate_area(struct cma *cma) + { + int bitmap_size = BITS_TO_LONGS(cma_bitmap_maxno(cma)) * sizeof(long); ++#ifdef CONFIG_CMA_MEM_SHARED + unsigned long base_pfn = cma->base_pfn, pfn = base_pfn; + unsigned i = cma->count >> pageblock_order; + struct zone *zone; ++#endif + + cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + + if (!cma->bitmap) + return -ENOMEM; + ++#ifdef CONFIG_CMA_MEM_SHARED + WARN_ON_ONCE(!pfn_valid(pfn)); + zone = page_zone(pfn_to_page(pfn)); + +@@ -118,14 +124,16 @@ static int __init cma_activate_area(struct cma *cma) + } + init_cma_reserved_pageblock(pfn_to_page(base_pfn)); + } while (--i); +- ++#endif + mutex_init(&cma->lock); + return 0; + ++#ifdef CONFIG_CMA_MEM_SHARED + err: + kfree(cma->bitmap); + cma->count = 0; + return -EINVAL; ++#endif + } + + static int __init cma_init_reserved_areas(void) +@@ -329,6 +337,95 @@ err: + return ret; + } + ++int hicma_saveable_page(unsigned long pfn) ++{ ++ int i; ++ int bitmap_no; ++ ++ for (i = 0; i < cma_area_count; i++) { ++ if ((pfn >= cma_areas[i].base_pfn) && ++ (pfn < (cma_areas[i].base_pfn + cma_areas[i].count))) { ++ bitmap_no = (pfn - cma_areas[i].base_pfn) >> cma_areas[i].order_per_bit; ++ if (test_bit(bitmap_no, cma_areas[i].bitmap)) { ++ return 1; ++ } ++ } ++ } ++ return 0; ++} ++ ++int hicma_page_free(unsigned long pfn) ++{ ++ int i; ++ int bitmap_no; ++ ++ for (i = 0; i < cma_area_count; i++) { ++ if ((pfn >= cma_areas[i].base_pfn) && ++ (pfn < (cma_areas[i].base_pfn + cma_areas[i].count))) { ++ bitmap_no = (pfn - cma_areas[i].base_pfn) >> cma_areas[i].order_per_bit; ++ if (test_bit(bitmap_no, cma_areas[i].bitmap)) { ++ return 0; ++ } else { ++ return 1; ++ } ++ } ++ } ++ return 0; ++} ++ ++EXPORT_SYMBOL(hicma_page_free); ++/* ++ ** bitmap_find_next_zero_area_best - find a contiguous aligned zero area closest ++ ** to nr ++ ** @map: The address to base the search on ++ ** @size: The bitmap size in bits ++ ** @start: The bitnumber to start searching at ++ ** @nr: The number of zeroed bits we're looking for ++ ** @align_mask: Alignment mask for zero area ++ ** ++ ** The @align_mask should be one less than a power of 2; the effect is that ++ ** the bit offset of all zero areas this function finds is multiples of that ++ ** power of 2. A @align_mask of 0 means no alignment is required. ++ **/ ++unsigned long bitmap_find_next_zero_area_best(unsigned long *map, ++ unsigned long size, ++ unsigned long start, ++ unsigned int nr, ++ unsigned long align_mask) ++{ ++ unsigned long index, end, i; ++ unsigned long best = size + 1, len = -1; ++ ++again: ++ index = find_next_zero_bit(map, size, start); ++ ++ /* Align allocation */ ++ index = __ALIGN_MASK(index, align_mask); ++ end = index + nr; ++ if (end > size) ++ goto out; ++ i = find_next_bit(map, size, index); ++ if (i < end) { ++ start = i + 1; ++ goto again; ++ } ++ ++ /* more suitable than last */ ++ if ((i - index) < len) { ++ len = i - index; ++ best = index; ++ } ++ ++ /* traverse all the bitmap */ ++ if (i < size) { ++ start = i + 1; ++ goto again; ++ } ++ ++out: ++ return best; ++} ++ + /** + * cma_alloc() - allocate pages from contiguous area + * @cma: Contiguous memory region for which the allocation is performed. +@@ -342,8 +439,17 @@ struct page *cma_alloc(struct cma *cma, int count, unsigned int align) + { + unsigned long mask, pfn, start = 0; + unsigned long bitmap_maxno, bitmap_no, bitmap_count; ++#ifdef CONFIG_CMA_MEM_SHARED + struct page *page = NULL; ++#endif + int ret; ++ int len; ++ ++ /* ++ * force align to 4KBtytes start, to minimize cma fragmentation ++ * but needs to fix later. ++ */ ++ align = 0; + + if (!cma || !cma->count) + return NULL; +@@ -360,11 +466,19 @@ struct page *cma_alloc(struct cma *cma, int count, unsigned int align) + + for (;;) { + mutex_lock(&cma->lock); ++#ifdef CONFIG_CMA_MEM_SHARED + bitmap_no = bitmap_find_next_zero_area(cma->bitmap, + bitmap_maxno, start, bitmap_count, mask); ++#else ++ bitmap_no = bitmap_find_next_zero_area_best(cma->bitmap, ++ bitmap_maxno, start, bitmap_count, mask); ++#endif + if (bitmap_no >= bitmap_maxno) { + mutex_unlock(&cma->lock); +- break; ++#ifndef CONFIG_CMA_MEM_SHARED ++ ret = -ENOMEM; ++#endif ++ goto error; + } + bitmap_set(cma->bitmap, bitmap_no, bitmap_count); + /* +@@ -375,6 +489,7 @@ struct page *cma_alloc(struct cma *cma, int count, unsigned int align) + mutex_unlock(&cma->lock); + + pfn = cma->base_pfn + (bitmap_no << cma->order_per_bit); ++#ifdef CONFIG_CMA_MEM_SHARED + mutex_lock(&cma_mutex); + ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA); + mutex_unlock(&cma_mutex); +@@ -385,16 +500,36 @@ struct page *cma_alloc(struct cma *cma, int count, unsigned int align) + + cma_clear_bitmap(cma, pfn, count); + if (ret != -EBUSY) +- break; +- ++ goto error; ++#else ++ break; ++#endif + pr_debug("%s(): memory range at %p is busy, retrying\n", + __func__, pfn_to_page(pfn)); + /* try again with a bit different memory target */ + start = bitmap_no + mask + 1; + } +- ++#ifdef CONFIG_CMA_MEM_SHARED + pr_debug("%s(): returned %p\n", __func__, page); + return page; ++#else ++ pr_debug("%s(): returned %p\n", __func__, pfn_to_page(pfn)); ++ return pfn_to_page(pfn); ++#endif ++error: ++ if (ret != -EINTR) { ++ memset(bitmap_buf, 0, sizeof(bitmap_buf)); ++ len = bitmap_scnlistprintf(bitmap_buf, 16384 - 2, cma->bitmap, ++ cma->count); ++ bitmap_buf[len++] = '\n'; ++ bitmap_buf[len] = '\0'; ++ pr_warn("cma area total:%lu pages\n", cma->count); ++ pr_warn("alloc %d failed: %d\n", count, ret); ++ pr_warn("bitmap: %s\n", bitmap_buf); ++ } else { ++ pr_warn("Interrupted system call!\n"); ++ } ++ return NULL; + } + + /** +@@ -423,7 +558,11 @@ bool cma_release(struct cma *cma, struct page *pages, int count) + + VM_BUG_ON(pfn + count > cma->base_pfn + cma->count); + ++#ifdef CONFIG_CMA_MEM_SHARED ++ mutex_lock(&cma_mutex); + free_contig_range(pfn, count); ++ mutex_unlock(&cma_mutex); ++#endif + cma_clear_bitmap(cma, pfn, count); + + return true; +diff --git a/mm/gup.c b/mm/gup.c +index 377a5a7..bef4bb0 100644 +--- a/mm/gup.c ++++ b/mm/gup.c +@@ -32,6 +32,16 @@ static struct page *no_page_table(struct vm_area_struct *vma, + return NULL; + } + ++/* ++ * FOLL_FORCE can write to even unwritable pte's, but only ++ * after we've gone through a COW cycle and they are dirty. ++ */ ++static inline bool can_follow_write_pte(pte_t pte, unsigned int flags) ++{ ++ return pte_write(pte) || ++ ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte)); ++} ++ + static struct page *follow_page_pte(struct vm_area_struct *vma, + unsigned long address, pmd_t *pmd, unsigned int flags) + { +@@ -66,7 +76,7 @@ retry: + } + if ((flags & FOLL_NUMA) && pte_numa(pte)) + goto no_page; +- if ((flags & FOLL_WRITE) && !pte_write(pte)) { ++ if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags)) { + pte_unmap_unlock(ptep, ptl); + return NULL; + } +@@ -315,7 +325,7 @@ static int faultin_page(struct task_struct *tsk, struct vm_area_struct *vma, + * reCOWed by userspace write). + */ + if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE)) +- *flags &= ~FOLL_WRITE; ++ *flags |= FOLL_COW; + return 0; + } + +diff --git a/mm/hugetlb.c b/mm/hugetlb.c +index da8fa4e..77c8d03 100644 +--- a/mm/hugetlb.c ++++ b/mm/hugetlb.c +@@ -855,6 +855,31 @@ struct hstate *size_to_hstate(unsigned long size) + return NULL; + } + ++/* ++ * Test to determine whether the hugepage is "active/in-use" (i.e. being linked ++ * to hstate->hugepage_activelist.) ++ * ++ * This function can be called for tail pages, but never returns true for them. ++ */ ++bool page_huge_active(struct page *page) ++{ ++ VM_BUG_ON_PAGE(!PageHuge(page), page); ++ return PageHead(page) && PagePrivate(&page[1]); ++} ++ ++/* never called for tail page */ ++static void set_page_huge_active(struct page *page) ++{ ++ VM_BUG_ON_PAGE(!PageHeadHuge(page), page); ++ SetPagePrivate(&page[1]); ++} ++ ++static void clear_page_huge_active(struct page *page) ++{ ++ VM_BUG_ON_PAGE(!PageHeadHuge(page), page); ++ ClearPagePrivate(&page[1]); ++} ++ + void free_huge_page(struct page *page) + { + /* +@@ -875,6 +900,7 @@ void free_huge_page(struct page *page) + ClearPagePrivate(page); + + spin_lock(&hugetlb_lock); ++ clear_page_huge_active(page); + hugetlb_cgroup_uncharge_page(hstate_index(h), + pages_per_huge_page(h), page); + if (restore_reserve) +@@ -2780,6 +2806,14 @@ static void unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma, + continue; + + /* ++ * Shared VMAs have their own reserves and do not affect ++ * MAP_PRIVATE accounting but it is possible that a shared ++ * VMA is using the same page so check and skip such VMAs. ++ */ ++ if (iter_vma->vm_flags & VM_MAYSHARE) ++ continue; ++ ++ /* + * Unmap the page from other VMAs without their own reserves. + * They get marked to be SIGKILLed if they fault in these + * areas. This is because a future no-page fault on this VMA +@@ -2884,6 +2918,7 @@ retry_avoidcopy: + copy_user_huge_page(new_page, old_page, address, vma, + pages_per_huge_page(h)); + __SetPageUptodate(new_page); ++ set_page_huge_active(new_page); + + mmun_start = address & huge_page_mask(h); + mmun_end = mmun_start + huge_page_size(h); +@@ -2995,6 +3030,7 @@ retry: + } + clear_huge_page(page, address, pages_per_huge_page(h)); + __SetPageUptodate(page); ++ set_page_huge_active(page); + + if (vma->vm_flags & VM_MAYSHARE) { + int err; +@@ -3799,19 +3835,26 @@ int dequeue_hwpoisoned_huge_page(struct page *hpage) + + bool isolate_huge_page(struct page *page, struct list_head *list) + { ++ bool ret = true; ++ + VM_BUG_ON_PAGE(!PageHead(page), page); +- if (!get_page_unless_zero(page)) +- return false; + spin_lock(&hugetlb_lock); ++ if (!page_huge_active(page) || !get_page_unless_zero(page)) { ++ ret = false; ++ goto unlock; ++ } ++ clear_page_huge_active(page); + list_move_tail(&page->lru, list); ++unlock: + spin_unlock(&hugetlb_lock); +- return true; ++ return ret; + } + + void putback_active_hugepage(struct page *page) + { + VM_BUG_ON_PAGE(!PageHead(page), page); + spin_lock(&hugetlb_lock); ++ set_page_huge_active(page); + list_move_tail(&page->lru, &(page_hstate(page))->hugepage_activelist); + spin_unlock(&hugetlb_lock); + put_page(page); +diff --git a/mm/init-mm.c b/mm/init-mm.c +index a56a851..e1567c3 100644 +--- a/mm/init-mm.c ++++ b/mm/init-mm.c +@@ -23,3 +23,4 @@ struct mm_struct init_mm = { + .mmlist = LIST_HEAD_INIT(init_mm.mmlist), + INIT_MM_CONTEXT(init_mm) + }; ++EXPORT_SYMBOL(init_mm); +diff --git a/mm/madvise.c b/mm/madvise.c +index 0938b30..dd7d354 100644 +--- a/mm/madvise.c ++++ b/mm/madvise.c +@@ -102,7 +102,8 @@ static long madvise_behavior(struct vm_area_struct *vma, + + pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); + *prev = vma_merge(mm, *prev, start, end, new_flags, vma->anon_vma, +- vma->vm_file, pgoff, vma_policy(vma)); ++ vma->vm_file, pgoff, vma_policy(vma), ++ vma_get_anon_name(vma)); + if (*prev) { + vma = *prev; + goto success; +diff --git a/mm/memcontrol.c b/mm/memcontrol.c +index d72bdc3..ee15ae6 100644 +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -6001,6 +6001,12 @@ static int mem_cgroup_can_attach(struct cgroup_subsys_state *css, + return ret; + } + ++static int mem_cgroup_allow_attach(struct cgroup_subsys_state *css, ++ struct cgroup_taskset *tset) ++{ ++ return subsys_cgroup_allow_attach(css, tset); ++} ++ + static void mem_cgroup_cancel_attach(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset) + { +@@ -6169,6 +6175,11 @@ static int mem_cgroup_can_attach(struct cgroup_subsys_state *css, + { + return 0; + } ++static int mem_cgroup_allow_attach(struct cgroup_subsys_state *css, ++ struct cgroup_taskset *tset) ++{ ++ return 0; ++} + static void mem_cgroup_cancel_attach(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset) + { +@@ -6204,6 +6215,7 @@ struct cgroup_subsys memory_cgrp_subsys = { + .can_attach = mem_cgroup_can_attach, + .cancel_attach = mem_cgroup_cancel_attach, + .attach = mem_cgroup_move_task, ++ .allow_attach = mem_cgroup_allow_attach, + .bind = mem_cgroup_bind, + .legacy_cftypes = mem_cgroup_files, + .early_init = 0, +diff --git a/mm/memory-failure.c b/mm/memory-failure.c +index 22f047f..1b69b9c 100644 +--- a/mm/memory-failure.c ++++ b/mm/memory-failure.c +@@ -1552,8 +1552,18 @@ static int soft_offline_huge_page(struct page *page, int flags) + } + unlock_page(hpage); + +- /* Keep page count to indicate a given hugepage is isolated. */ +- list_move(&hpage->lru, &pagelist); ++ ret = isolate_huge_page(hpage, &pagelist); ++ if (ret) { ++ /* ++ * get_any_page() and isolate_huge_page() takes a refcount each, ++ * so need to drop one here. ++ */ ++ put_page(hpage); ++ } else { ++ pr_info("soft offline: %#lx hugepage failed to isolate\n", pfn); ++ return -EBUSY; ++ } ++ + ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL, + MIGRATE_SYNC, MR_MEMORY_FAILURE); + if (ret) { +diff --git a/mm/memory.c b/mm/memory.c +index 90fb265..df30feb 100644 +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -2629,6 +2629,10 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, + + pte_unmap(page_table); + ++ /* File mapping without ->vm_ops ? */ ++ if (vma->vm_flags & VM_SHARED) ++ return VM_FAULT_SIGBUS; ++ + /* Check if we need to add a guard page to the stack */ + if (check_stack_guard_page(vma, address) < 0) + return VM_FAULT_SIGSEGV; +@@ -3033,6 +3037,10 @@ static int do_linear_fault(struct mm_struct *mm, struct vm_area_struct *vma, + - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; + + pte_unmap(page_table); ++ ++ /* The VMA was not fully populated on mmap() or missing VM_DONTEXPAND */ ++ if (!vma->vm_ops->fault) ++ return VM_FAULT_SIGBUS; + if (!(flags & FAULT_FLAG_WRITE)) + return do_read_fault(mm, vma, address, pmd, pgoff, flags, + orig_pte); +@@ -3198,13 +3206,12 @@ static int handle_pte_fault(struct mm_struct *mm, + entry = ACCESS_ONCE(*pte); + if (!pte_present(entry)) { + if (pte_none(entry)) { +- if (vma->vm_ops) { +- if (likely(vma->vm_ops->fault)) +- return do_linear_fault(mm, vma, address, +- pte, pmd, flags, entry); +- } +- return do_anonymous_page(mm, vma, address, +- pte, pmd, flags); ++ if (vma->vm_ops) ++ return do_linear_fault(mm, vma, address, pte, pmd, ++ flags, entry); ++ ++ return do_anonymous_page(mm, vma, address, pte, pmd, ++ flags); + } + if (pte_file(entry)) + return do_nonlinear_fault(mm, vma, address, +diff --git a/mm/mempolicy.c b/mm/mempolicy.c +index 39198cb..30d683e 100644 +--- a/mm/mempolicy.c ++++ b/mm/mempolicy.c +@@ -770,7 +770,7 @@ static int mbind_range(struct mm_struct *mm, unsigned long start, + ((vmstart - vma->vm_start) >> PAGE_SHIFT); + prev = vma_merge(mm, prev, vmstart, vmend, vma->vm_flags, + vma->anon_vma, vma->vm_file, pgoff, +- new_pol); ++ new_pol, vma_get_anon_name(vma)); + if (prev) { + vma = prev; + next = vma->vm_next; +diff --git a/mm/migrate.c b/mm/migrate.c +index cd4fd10..d858da3 100644 +--- a/mm/migrate.c ++++ b/mm/migrate.c +@@ -39,6 +39,7 @@ + #include <linux/mmu_notifier.h> + + #include <asm/tlbflush.h> ++#include <linux/ptrace.h> + + #define CREATE_TRACE_POINTS + #include <trace/events/migrate.h> +@@ -1490,14 +1491,9 @@ SYSCALL_DEFINE6(move_pages, pid_t, pid, unsigned long, nr_pages, + + /* + * Check if this process has the right to modify the specified +- * process. The right exists if the process has administrative +- * capabilities, superuser privileges or the same +- * userid as the target process. ++ * process. Use the regular "ptrace_may_access()" checks. + */ +- tcred = __task_cred(task); +- if (!uid_eq(cred->euid, tcred->suid) && !uid_eq(cred->euid, tcred->uid) && +- !uid_eq(cred->uid, tcred->suid) && !uid_eq(cred->uid, tcred->uid) && +- !capable(CAP_SYS_NICE)) { ++ if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS)) { + rcu_read_unlock(); + err = -EPERM; + goto out; +diff --git a/mm/mlock.c b/mm/mlock.c +index 73cf098..1d0d239 100644 +--- a/mm/mlock.c ++++ b/mm/mlock.c +@@ -566,7 +566,8 @@ static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev, + + pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); + *prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma, +- vma->vm_file, pgoff, vma_policy(vma)); ++ vma->vm_file, pgoff, vma_policy(vma), ++ vma_get_anon_name(vma)); + if (*prev) { + vma = *prev; + goto success; +diff --git a/mm/mmap.c b/mm/mmap.c +index f88b4f9..f73ff5b 100644 +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -928,7 +928,8 @@ again: remove_next = 1 + (end > next->vm_end); + * per-vma resources, so we don't attempt to merge those. + */ + static inline int is_mergeable_vma(struct vm_area_struct *vma, +- struct file *file, unsigned long vm_flags) ++ struct file *file, unsigned long vm_flags, ++ const char __user *anon_name) + { + /* + * VM_SOFTDIRTY should not prevent from VMA merging, if we +@@ -944,6 +945,8 @@ static inline int is_mergeable_vma(struct vm_area_struct *vma, + return 0; + if (vma->vm_ops && vma->vm_ops->close) + return 0; ++ if (vma_get_anon_name(vma) != anon_name) ++ return 0; + return 1; + } + +@@ -974,9 +977,10 @@ static inline int is_mergeable_anon_vma(struct anon_vma *anon_vma1, + */ + static int + can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags, +- struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) ++ struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff, ++ const char __user *anon_name) + { +- if (is_mergeable_vma(vma, file, vm_flags) && ++ if (is_mergeable_vma(vma, file, vm_flags, anon_name) && + is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { + if (vma->vm_pgoff == vm_pgoff) + return 1; +@@ -993,9 +997,10 @@ can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags, + */ + static int + can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, +- struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) ++ struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff, ++ const char __user *anon_name) + { +- if (is_mergeable_vma(vma, file, vm_flags) && ++ if (is_mergeable_vma(vma, file, vm_flags, anon_name) && + is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { + pgoff_t vm_pglen; + vm_pglen = vma_pages(vma); +@@ -1006,9 +1011,9 @@ can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, + } + + /* +- * Given a mapping request (addr,end,vm_flags,file,pgoff), figure out +- * whether that can be merged with its predecessor or its successor. +- * Or both (it neatly fills a hole). ++ * Given a mapping request (addr,end,vm_flags,file,pgoff,anon_name), ++ * figure out whether that can be merged with its predecessor or its ++ * successor. Or both (it neatly fills a hole). + * + * In most cases - when called for mmap, brk or mremap - [addr,end) is + * certain not to be mapped by the time vma_merge is called; but when +@@ -1037,8 +1042,9 @@ can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, + struct vm_area_struct *vma_merge(struct mm_struct *mm, + struct vm_area_struct *prev, unsigned long addr, + unsigned long end, unsigned long vm_flags, +- struct anon_vma *anon_vma, struct file *file, +- pgoff_t pgoff, struct mempolicy *policy) ++ struct anon_vma *anon_vma, struct file *file, ++ pgoff_t pgoff, struct mempolicy *policy, ++ const char __user *anon_name) + { + pgoff_t pglen = (end - addr) >> PAGE_SHIFT; + struct vm_area_struct *area, *next; +@@ -1063,16 +1069,16 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, + * Can it merge with the predecessor? + */ + if (prev && prev->vm_end == addr && +- mpol_equal(vma_policy(prev), policy) && +- can_vma_merge_after(prev, vm_flags, +- anon_vma, file, pgoff)) { ++ mpol_equal(vma_policy(prev), policy) && ++ can_vma_merge_after(prev, vm_flags, anon_vma, ++ file, pgoff, anon_name)) { + /* + * OK, it can. Can we now merge in the successor as well? + */ + if (next && end == next->vm_start && + mpol_equal(policy, vma_policy(next)) && +- can_vma_merge_before(next, vm_flags, +- anon_vma, file, pgoff+pglen) && ++ can_vma_merge_before(next, vm_flags, anon_vma, ++ file, pgoff+pglen, anon_name) && + is_mergeable_anon_vma(prev->anon_vma, + next->anon_vma, NULL)) { + /* cases 1, 6 */ +@@ -1091,9 +1097,9 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, + * Can this new request be merged in front of next? + */ + if (next && end == next->vm_start && +- mpol_equal(policy, vma_policy(next)) && +- can_vma_merge_before(next, vm_flags, +- anon_vma, file, pgoff+pglen)) { ++ mpol_equal(policy, vma_policy(next)) && ++ can_vma_merge_before(next, vm_flags, anon_vma, ++ file, pgoff+pglen, anon_name)) { + if (prev && addr < prev->vm_end) /* case 4 */ + err = vma_adjust(prev, prev->vm_start, + addr, prev->vm_pgoff, NULL); +@@ -1580,7 +1586,8 @@ munmap_back: + /* + * Can we just expand an old mapping? + */ +- vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, NULL); ++ vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, ++ NULL, NULL); + if (vma) + goto out; + +@@ -2694,7 +2701,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len) + + /* Can we just expand an old private anonymous mapping? */ + vma = vma_merge(mm, prev, addr, addr + len, flags, +- NULL, NULL, pgoff, NULL); ++ NULL, NULL, pgoff, NULL, NULL); + if (vma) + goto out; + +@@ -2853,7 +2860,8 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, + if (find_vma_links(mm, addr, addr + len, &prev, &rb_link, &rb_parent)) + return NULL; /* should never get here */ + new_vma = vma_merge(mm, prev, addr, addr + len, vma->vm_flags, +- vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma)); ++ vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), ++ vma_get_anon_name(vma)); + if (new_vma) { + /* + * Source vma may have been merged into new_vma +diff --git a/mm/mprotect.c b/mm/mprotect.c +index ace9345..5911e60 100644 +--- a/mm/mprotect.c ++++ b/mm/mprotect.c +@@ -289,7 +289,8 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, + */ + pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); + *pprev = vma_merge(mm, *pprev, start, end, newflags, +- vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma)); ++ vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), ++ vma_get_anon_name(vma)); + if (*pprev) { + vma = *pprev; + goto success; +diff --git a/mm/page_alloc.c b/mm/page_alloc.c +index c32cb64..87732e5 100644 +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -204,8 +204,28 @@ static char * const zone_names[MAX_NR_ZONES] = { + "Movable", + }; + ++/* ++ * Try to keep at least this much lowmem free. Do not allow normal ++ * allocations below this point, only high priority ones. Automatically ++ * tuned according to the amount of memory in the system. ++ */ + int min_free_kbytes = 1024; + int user_min_free_kbytes = -1; ++int min_free_order_shift = 1; ++#ifdef CONFIG_CMA_ADVANCE_SHARE ++/* ++ * Try to set water mark of cma when cma is used by system. ++ */ ++int cma_watermark; ++static bool is_cma_wmark_set = false; ++#endif ++ ++/* ++ * Extra memory for the system to try freeing. Used to temporarily ++ * free memory, to make space for new workloads. Anyone can allocate ++ * down to the min watermarks controlled by min_free_kbytes above. ++ */ ++int extra_free_kbytes = 0; + + static unsigned long __meminitdata nr_kernel_pages; + static unsigned long __meminitdata nr_all_pages; +@@ -801,6 +821,18 @@ void __init __free_pages_bootmem(struct page *page, unsigned int order) + } + + #ifdef CONFIG_CMA ++ ++#ifdef CONFIG_CMA_ADVANCE_SHARE ++void __init init_alloc_ratio_counter(struct zone *zone) ++{ ++ if (zone->has_cma) ++ return; ++ zone->has_cma = 1; ++ zone->nr_try_movable = 0; ++ zone->nr_try_cma = 0; ++} ++#endif /* CONFIG_CMA_ADVANCE_SHARE ++ */ + /* Free whole pageblock and set its migration type to MIGRATE_CMA. */ + void __init init_cma_reserved_pageblock(struct page *page) + { +@@ -828,6 +860,9 @@ void __init init_cma_reserved_pageblock(struct page *page) + } + + adjust_managed_page_count(page, pageblock_nr_pages); ++#ifdef CONFIG_CMA_ADVANCE_SHARE ++ init_alloc_ratio_counter(page_zone(page)); ++#endif /* CONFIG_CMA_ADVANCE_SHARE */ + } + #endif + +@@ -1169,6 +1204,105 @@ __rmqueue_fallback(struct zone *zone, unsigned int order, int start_migratetype) + return NULL; + } + ++#ifdef CONFIG_CMA_ADVANCE_SHARE ++static struct page *__rmqueue_cma(struct zone *zone, unsigned int order, ++ int migratetype) ++{ ++#ifdef CONFIG_CMA_MEM_SHARED ++ long free , free_cma , free_wmark; ++ struct page *page; ++ long cma_multiplex; ++ long cma_wm; ++ ++ if (migratetype != MIGRATE_MOVABLE || !zone->has_cma) ++ return NULL; ++ ++ if (is_cma_wmark_set) { ++ cma_wm = cma_watermark >> (PAGE_SHIFT - 10); ++ if (cma_wm < (low_wmark_pages(zone))) { ++ /* ++ * The parameter is illegal and set default value: high_wmark_pages(zone). ++ * The parameter should be larger than high water mark of the zone! ++ */ ++ cma_watermark = (high_wmark_pages(zone)) << (PAGE_SHIFT - 10); ++ cma_wm = cma_watermark >> (PAGE_SHIFT - 10); ++ is_cma_wmark_set = false; ++ } ++ } else { ++ cma_watermark = (high_wmark_pages(zone)) << (PAGE_SHIFT - 10); ++ cma_wm = cma_watermark >> (PAGE_SHIFT - 10); ++ } ++ ++ zone->cma_wmark = cma_watermark; ++ cma_multiplex = cma_wm - low_wmark_pages(zone); ++ ++ /* Reset ratio counter */ ++ free_cma = zone_page_state(zone, NR_FREE_CMA_PAGES); ++ ++ free = zone_page_state(zone, NR_FREE_PAGES); ++ free_wmark = free - free_cma - low_wmark_pages(zone); ++ ++ /* ++ * No cma free pages or there is more sys mem which means there is more ++ * mem then cma_watermark,so recharge only movable allocation ++ */ ++ if ((free_cma <= 0) || (free_wmark - cma_multiplex > 0)) { ++ zone->nr_try_movable = 1; ++ zone->nr_try_cma = 0; ++ goto alloc_movable; ++ } ++ ++ /* ++ *free_wmark is below than 0, and it means that normal pages ++ *are under the pressure, so we recharge only cma allocation. ++ */ ++ if (free_wmark <= 0) { ++ zone->nr_try_cma = 1; ++ zone->nr_try_movable = 0; ++ goto alloc_cma; ++ } ++ ++ /* ++ * when free sys mem between high_wmark_pages(zone) and low_wmark_pages(zone), ++ * sys mem and cma mem could be allocated by turns ++ */ ++ if (zone->nr_try_movable) ++ goto alloc_movable; ++ ++ if (zone->nr_try_cma) ++ goto alloc_cma; ++ ++ if (free_wmark > free_cma) { ++ zone->nr_try_movable = ++ (free_wmark * pageblock_nr_pages) / free_cma; ++ zone->nr_try_cma = pageblock_nr_pages; ++ } else { ++ zone->nr_try_movable = pageblock_nr_pages; ++ zone->nr_try_cma = free_cma * pageblock_nr_pages / free_wmark; ++ } ++ ++ /* Reset complete, start on movable first */ ++alloc_movable: ++ zone->nr_try_movable--; ++ return NULL; ++ ++alloc_cma: ++ if (zone->nr_try_cma) { ++ /* Okay. Now, we can try to allocate the page from cma region */ ++ zone->nr_try_cma--; ++ page = __rmqueue_smallest(zone, order, MIGRATE_CMA); ++ ++ /* CMA pages can vanish through CMA allocation */ ++ if (unlikely(!page && order == 0)) ++ zone->nr_try_cma = 0; ++ ++ return page; ++ } ++#endif ++ return NULL; ++} ++#endif /* CONFIG_CMA_ADVANCE_SHARE */ ++ + /* + * Do the hard work of removing an element from the buddy allocator. + * Call me with the zone->lock already held. +@@ -1176,10 +1310,16 @@ __rmqueue_fallback(struct zone *zone, unsigned int order, int start_migratetype) + static struct page *__rmqueue(struct zone *zone, unsigned int order, + int migratetype) + { +- struct page *page; ++ struct page *page = NULL; ++ ++#ifdef CONFIG_CMA_ADVANCE_SHARE ++ if (IS_ENABLED(CONFIG_CMA)) ++ page = __rmqueue_cma(zone, order, migratetype); ++#endif /* CONFIG_CMA_ADVANCE_SHARE */ + + retry_reserve: +- page = __rmqueue_smallest(zone, order, migratetype); ++ if (!page) ++ page = __rmqueue_smallest(zone, order, migratetype); + + if (unlikely(!page) && migratetype != MIGRATE_RESERVE) { + page = __rmqueue_fallback(zone, order, migratetype); +@@ -1408,7 +1548,8 @@ void free_hot_cold_page(struct page *page, bool cold) + * excessively into the page allocator + */ + if (migratetype >= MIGRATE_PCPTYPES) { +- if (unlikely(is_migrate_isolate(migratetype))) { ++ if (unlikely(is_migrate_isolate(migratetype)) ++ || is_migrate_cma(migratetype)) { + free_one_page(zone, page, pfn, 0, migratetype); + goto out; + } +@@ -1726,7 +1867,7 @@ static bool __zone_watermark_ok(struct zone *z, unsigned int order, + free_pages -= z->free_area[o].nr_free << o; + + /* Require fewer higher order pages to be free */ +- min >>= 1; ++ min >>= min_free_order_shift; + + if (free_pages <= min) + return false; +@@ -3228,7 +3369,11 @@ void show_free_areas(unsigned int filter) + + for_each_populated_zone(zone) { + int i; +- ++#ifdef CONFIG_CMA_ADVANCE_SHARE ++ unsigned long cma_wm = zone->cma_wmark; ++#else ++ unsigned long cma_wm = 0; ++#endif + if (skip_free_areas_node(filter, zone_to_nid(zone))) + continue; + show_node(zone); +@@ -3261,6 +3406,7 @@ void show_free_areas(unsigned int filter) + " writeback_tmp:%lukB" + " pages_scanned:%lu" + " all_unreclaimable? %s" ++ " cma_watermark:%lukB" + "\n", + zone->name, + K(zone_page_state(zone, NR_FREE_PAGES)), +@@ -3291,7 +3437,8 @@ void show_free_areas(unsigned int filter) + K(zone_page_state(zone, NR_FREE_CMA_PAGES)), + K(zone_page_state(zone, NR_WRITEBACK_TEMP)), + K(zone_page_state(zone, NR_PAGES_SCANNED)), +- (!zone_reclaimable(zone) ? "yes" : "no") ++ (!zone_reclaimable(zone) ? "yes" : "no"), ++ cma_wm + ); + printk("lowmem_reserve[]:"); + for (i = 0; i < MAX_NR_ZONES; i++) +@@ -4894,6 +5041,10 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat, + zone_seqlock_init(zone); + zone->zone_pgdat = pgdat; + zone_pcp_init(zone); ++#ifdef CONFIG_CMA_ADVANCE_SHARE ++ if (IS_ENABLED(CONFIG_CMA)) ++ zone->has_cma = 0; ++#endif + + /* For bootup, initialized properly in watermark setup */ + mod_zone_page_state(zone, NR_ALLOC_BATCH, zone->managed_pages); +@@ -5652,6 +5803,7 @@ static void setup_per_zone_lowmem_reserve(void) + static void __setup_per_zone_wmarks(void) + { + unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10); ++ unsigned long pages_low = extra_free_kbytes >> (PAGE_SHIFT - 10); + unsigned long lowmem_pages = 0; + struct zone *zone; + unsigned long flags; +@@ -5663,11 +5815,14 @@ static void __setup_per_zone_wmarks(void) + } + + for_each_zone(zone) { +- u64 tmp; ++ u64 min, low; + + spin_lock_irqsave(&zone->lock, flags); +- tmp = (u64)pages_min * zone->managed_pages; +- do_div(tmp, lowmem_pages); ++ min = (u64)pages_min * zone->managed_pages; ++ do_div(min, lowmem_pages); ++ low = (u64)pages_low * zone->managed_pages; ++ do_div(low, vm_total_pages); ++ + if (is_highmem(zone)) { + /* + * __GFP_HIGH and PF_MEMALLOC allocations usually don't +@@ -5688,11 +5843,13 @@ static void __setup_per_zone_wmarks(void) + * If it's a lowmem zone, reserve a number of pages + * proportionate to the zone's size. + */ +- zone->watermark[WMARK_MIN] = tmp; ++ zone->watermark[WMARK_MIN] = min; + } + +- zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + (tmp >> 2); +- zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + (tmp >> 1); ++ zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + ++ low + (min >> 2); ++ zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + ++ low + (min >> 1); + + __mod_zone_page_state(zone, NR_ALLOC_BATCH, + high_wmark_pages(zone) - low_wmark_pages(zone) - +@@ -5816,7 +5973,7 @@ module_init(init_per_zone_wmark_min) + /* + * min_free_kbytes_sysctl_handler - just a wrapper around proc_dointvec() so + * that we can call two helper functions whenever min_free_kbytes +- * changes. ++ * or extra_free_kbytes changes. + */ + int min_free_kbytes_sysctl_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *length, loff_t *ppos) +@@ -5834,6 +5991,17 @@ int min_free_kbytes_sysctl_handler(struct ctl_table *table, int write, + return 0; + } + ++#ifdef CONFIG_CMA_ADVANCE_SHARE ++int cma_watermark_sysctl_handler(struct ctl_table *table, int write, ++ void __user *buffer, size_t *length, loff_t *ppos) ++{ ++ proc_dointvec(table, write, buffer, length, ppos); ++ if (write) ++ is_cma_wmark_set = true; ++ return 0; ++} ++#endif /* CONFIG_CMA_ADVANCE_SHARE */ ++ + #ifdef CONFIG_NUMA + int sysctl_min_unmapped_ratio_sysctl_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *length, loff_t *ppos) +@@ -6397,7 +6565,7 @@ int alloc_contig_range(unsigned long start, unsigned long end, + + /* Make sure the range is really isolated. */ + if (test_pages_isolated(outer_start, end, false)) { +- pr_info("%s: [%lx, %lx) PFNs busy\n", ++ pr_warn_once("%s: [%lx, %lx) PFNs busy\n", + __func__, outer_start, end); + ret = -EBUSY; + goto done; +diff --git a/mm/percpu.c b/mm/percpu.c +index 88bb6c9..fe72f81 100644 +--- a/mm/percpu.c ++++ b/mm/percpu.c +@@ -110,7 +110,7 @@ struct pcpu_chunk { + int map_used; /* # of map entries used before the sentry */ + int map_alloc; /* # of map entries allocated */ + int *map; /* allocation map */ +- struct work_struct map_extend_work;/* async ->map[] extension */ ++ struct list_head map_extend_list;/* on pcpu_map_extend_chunks */ + + void *data; /* chunk data */ + int first_free; /* no free below this */ +@@ -160,10 +160,13 @@ static struct pcpu_chunk *pcpu_reserved_chunk; + static int pcpu_reserved_chunk_limit; + + static DEFINE_SPINLOCK(pcpu_lock); /* all internal data structures */ +-static DEFINE_MUTEX(pcpu_alloc_mutex); /* chunk create/destroy, [de]pop */ ++static DEFINE_MUTEX(pcpu_alloc_mutex); /* chunk create/destroy, [de]pop, map ext */ + + static struct list_head *pcpu_slot __read_mostly; /* chunk list slots */ + ++/* chunks which need their map areas extended, protected by pcpu_lock */ ++static LIST_HEAD(pcpu_map_extend_chunks); ++ + /* + * The number of empty populated pages, protected by pcpu_lock. The + * reserved chunk doesn't contribute to the count. +@@ -401,9 +404,13 @@ static int pcpu_need_to_extend(struct pcpu_chunk *chunk, bool is_atomic) + margin = 3; + + if (chunk->map_alloc < +- chunk->map_used + PCPU_ATOMIC_MAP_MARGIN_LOW && +- pcpu_async_enabled) +- schedule_work(&chunk->map_extend_work); ++ chunk->map_used + PCPU_ATOMIC_MAP_MARGIN_LOW) { ++ if (list_empty(&chunk->map_extend_list)) { ++ list_add_tail(&chunk->map_extend_list, ++ &pcpu_map_extend_chunks); ++ pcpu_schedule_balance_work(); ++ } ++ } + } else { + margin = PCPU_ATOMIC_MAP_MARGIN_HIGH; + } +@@ -437,6 +444,8 @@ static int pcpu_extend_area_map(struct pcpu_chunk *chunk, int new_alloc) + size_t old_size = 0, new_size = new_alloc * sizeof(new[0]); + unsigned long flags; + ++ lockdep_assert_held(&pcpu_alloc_mutex); ++ + new = pcpu_mem_zalloc(new_size); + if (!new) + return -ENOMEM; +@@ -469,20 +478,6 @@ out_unlock: + return 0; + } + +-static void pcpu_map_extend_workfn(struct work_struct *work) +-{ +- struct pcpu_chunk *chunk = container_of(work, struct pcpu_chunk, +- map_extend_work); +- int new_alloc; +- +- spin_lock_irq(&pcpu_lock); +- new_alloc = pcpu_need_to_extend(chunk, false); +- spin_unlock_irq(&pcpu_lock); +- +- if (new_alloc) +- pcpu_extend_area_map(chunk, new_alloc); +-} +- + /** + * pcpu_fit_in_area - try to fit the requested allocation in a candidate area + * @chunk: chunk the candidate area belongs to +@@ -742,7 +737,7 @@ static struct pcpu_chunk *pcpu_alloc_chunk(void) + chunk->map_used = 1; + + INIT_LIST_HEAD(&chunk->list); +- INIT_WORK(&chunk->map_extend_work, pcpu_map_extend_workfn); ++ INIT_LIST_HEAD(&chunk->map_extend_list); + chunk->free_size = pcpu_unit_size; + chunk->contig_hint = pcpu_unit_size; + +@@ -897,6 +892,9 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved, + return NULL; + } + ++ if (!is_atomic) ++ mutex_lock(&pcpu_alloc_mutex); ++ + spin_lock_irqsave(&pcpu_lock, flags); + + /* serve reserved allocations from the reserved chunk if available */ +@@ -974,7 +972,6 @@ restart: + if (list_empty(&pcpu_slot[pcpu_nr_slots - 1])) { + chunk = pcpu_create_chunk(); + if (!chunk) { +- mutex_unlock(&pcpu_alloc_mutex); + err = "failed to allocate new chunk"; + goto fail; + } +@@ -985,7 +982,6 @@ restart: + spin_lock_irqsave(&pcpu_lock, flags); + } + +- mutex_unlock(&pcpu_alloc_mutex); + goto restart; + + area_found: +@@ -995,8 +991,6 @@ area_found: + if (!is_atomic) { + int page_start, page_end, rs, re; + +- mutex_lock(&pcpu_alloc_mutex); +- + page_start = PFN_DOWN(off); + page_end = PFN_UP(off + size); + +@@ -1007,7 +1001,6 @@ area_found: + + spin_lock_irqsave(&pcpu_lock, flags); + if (ret) { +- mutex_unlock(&pcpu_alloc_mutex); + pcpu_free_area(chunk, off, &occ_pages); + err = "failed to populate"; + goto fail_unlock; +@@ -1047,6 +1040,8 @@ fail: + /* see the flag handling in pcpu_blance_workfn() */ + pcpu_atomic_alloc_failed = true; + pcpu_schedule_balance_work(); ++ } else { ++ mutex_unlock(&pcpu_alloc_mutex); + } + return NULL; + } +@@ -1131,6 +1126,7 @@ static void pcpu_balance_workfn(struct work_struct *work) + if (chunk == list_first_entry(free_head, struct pcpu_chunk, list)) + continue; + ++ list_del_init(&chunk->map_extend_list); + list_move(&chunk->list, &to_free); + } + +@@ -1148,6 +1144,25 @@ static void pcpu_balance_workfn(struct work_struct *work) + pcpu_destroy_chunk(chunk); + } + ++ /* service chunks which requested async area map extension */ ++ do { ++ int new_alloc = 0; ++ ++ spin_lock_irq(&pcpu_lock); ++ ++ chunk = list_first_entry_or_null(&pcpu_map_extend_chunks, ++ struct pcpu_chunk, map_extend_list); ++ if (chunk) { ++ list_del_init(&chunk->map_extend_list); ++ new_alloc = pcpu_need_to_extend(chunk, false); ++ } ++ ++ spin_unlock_irq(&pcpu_lock); ++ ++ if (new_alloc) ++ pcpu_extend_area_map(chunk, new_alloc); ++ } while (chunk); ++ + /* + * Ensure there are certain number of free populated pages for + * atomic allocs. Fill up from the most packed so that atomic +@@ -1648,7 +1663,7 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, + */ + schunk = memblock_virt_alloc(pcpu_chunk_struct_size, 0); + INIT_LIST_HEAD(&schunk->list); +- INIT_WORK(&schunk->map_extend_work, pcpu_map_extend_workfn); ++ INIT_LIST_HEAD(&schunk->map_extend_list); + schunk->base_addr = base_addr; + schunk->map = smap; + schunk->map_alloc = ARRAY_SIZE(smap); +@@ -1678,7 +1693,7 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, + if (dyn_size) { + dchunk = memblock_virt_alloc(pcpu_chunk_struct_size, 0); + INIT_LIST_HEAD(&dchunk->list); +- INIT_WORK(&dchunk->map_extend_work, pcpu_map_extend_workfn); ++ INIT_LIST_HEAD(&dchunk->map_extend_list); + dchunk->base_addr = base_addr; + dchunk->map = dmap; + dchunk->map_alloc = ARRAY_SIZE(dmap); +diff --git a/mm/shmem.c b/mm/shmem.c +index 0b4ba55..5830599 100644 +--- a/mm/shmem.c ++++ b/mm/shmem.c +@@ -557,6 +557,10 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr) + if (error) + return error; + ++ error = setattr_killpriv(dentry, attr); ++ if (error) ++ return error; ++ + if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) { + loff_t oldsize = inode->i_size; + loff_t newsize = attr->ia_size; +@@ -3404,6 +3408,14 @@ struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags + } + EXPORT_SYMBOL_GPL(shmem_file_setup); + ++void shmem_set_file(struct vm_area_struct *vma, struct file *file) ++{ ++ if (vma->vm_file) ++ fput(vma->vm_file); ++ vma->vm_file = file; ++ vma->vm_ops = &shmem_vm_ops; ++} ++ + /** + * shmem_zero_setup - setup a shared anonymous mapping + * @vma: the vma to be mmapped is prepared by do_mmap_pgoff +@@ -3417,10 +3429,7 @@ int shmem_zero_setup(struct vm_area_struct *vma) + if (IS_ERR(file)) + return PTR_ERR(file); + +- if (vma->vm_file) +- fput(vma->vm_file); +- vma->vm_file = file; +- vma->vm_ops = &shmem_vm_ops; ++ shmem_set_file(vma, file); + return 0; + } + +diff --git a/mm/vmscan.c b/mm/vmscan.c +index e3b0a54..e9d4059 100644 +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -46,6 +46,7 @@ + #include <linux/oom.h> + #include <linux/prefetch.h> + #include <linux/printk.h> ++#include <linux/debugfs.h> + + #include <asm/tlbflush.h> + #include <asm/div64.h> +@@ -186,6 +187,39 @@ static unsigned long get_lru_size(struct lruvec *lruvec, enum lru_list lru) + return zone_page_state(lruvec_zone(lruvec), NR_LRU_BASE + lru); + } + ++struct dentry *debug_file; ++ ++static int debug_shrinker_show(struct seq_file *s, void *unused) ++{ ++ struct shrinker *shrinker; ++ struct shrink_control sc; ++ ++ sc.gfp_mask = -1; ++ sc.nr_to_scan = 0; ++ ++ down_read(&shrinker_rwsem); ++ list_for_each_entry(shrinker, &shrinker_list, list) { ++ int num_objs; ++ ++ num_objs = shrinker->count_objects(shrinker, &sc); ++ seq_printf(s, "%pf %d\n", shrinker->scan_objects, num_objs); ++ } ++ up_read(&shrinker_rwsem); ++ return 0; ++} ++ ++static int debug_shrinker_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, debug_shrinker_show, inode->i_private); ++} ++ ++static const struct file_operations debug_shrinker_fops = { ++ .open = debug_shrinker_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ + /* + * Add a shrinker callback to be called from the vm. + */ +@@ -215,6 +249,15 @@ int register_shrinker(struct shrinker *shrinker) + } + EXPORT_SYMBOL(register_shrinker); + ++static int __init add_shrinker_debug(void) ++{ ++ debugfs_create_file("shrinker", 0644, NULL, NULL, ++ &debug_shrinker_fops); ++ return 0; ++} ++ ++late_initcall(add_shrinker_debug); ++ + /* + * Remove one + */ +@@ -926,7 +969,7 @@ static unsigned long shrink_page_list(struct list_head *page_list, + + /* Case 2 above */ + } else if (global_reclaim(sc) || +- !PageReclaim(page) || !(sc->gfp_mask & __GFP_IO)) { ++ !PageReclaim(page) || !may_enter_fs) { + /* + * This is slightly racy - end_page_writeback() + * might have just cleared PageReclaim, then +@@ -1115,7 +1158,7 @@ cull_mlocked: + if (PageSwapCache(page)) + try_to_free_swap(page); + unlock_page(page); +- putback_lru_page(page); ++ list_add(&page->lru, &ret_pages); + continue; + + activate_locked: +diff --git a/net/Kconfig b/net/Kconfig +index 99815b5..15a2d76 100644 +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -83,6 +83,20 @@ source "net/netlabel/Kconfig" + + endif # if INET + ++config ANDROID_PARANOID_NETWORK ++ bool "Only allow certain groups to create sockets" ++ default ANDROID ++ help ++ none ++ ++config NET_ACTIVITY_STATS ++ bool "Network activity statistics tracking" ++ default y ++ help ++ Network activity statistics are useful for tracking wireless ++ modem activity on 2G, 3G, 4G wireless networks. Counts number of ++ transmissions and groups them in specified time buckets. ++ + config NETWORK_SECMARK + bool "Security Marking" + help +@@ -230,7 +244,7 @@ source "net/mpls/Kconfig" + source "net/hsr/Kconfig" + + config RPS +- boolean ++ boolean "RPS" + depends on SMP && SYSFS + default y + +diff --git a/net/Makefile b/net/Makefile +index 7ed1970..550c112 100644 +--- a/net/Makefile ++++ b/net/Makefile +@@ -73,3 +73,4 @@ obj-$(CONFIG_OPENVSWITCH) += openvswitch/ + obj-$(CONFIG_VSOCKETS) += vmw_vsock/ + obj-$(CONFIG_NET_MPLS_GSO) += mpls/ + obj-$(CONFIG_HSR) += hsr/ ++obj-$(CONFIG_NET_ACTIVITY_STATS) += activity_stats.o +diff --git a/net/activity_stats.c b/net/activity_stats.c +new file mode 100644 +index 0000000..4609ce2 +--- /dev/null ++++ b/net/activity_stats.c +@@ -0,0 +1,119 @@ ++/* net/activity_stats.c ++ * ++ * Copyright (C) 2010 Google, Inc. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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. ++ * ++ * Author: Mike Chan (mike@android.com) ++ */ ++ ++#include <linux/proc_fs.h> ++#include <linux/seq_file.h> ++#include <linux/suspend.h> ++#include <net/net_namespace.h> ++ ++/* ++ * Track transmission rates in buckets (power of 2). ++ * 1,2,4,8...512 seconds. ++ * ++ * Buckets represent the count of network transmissions at least ++ * N seconds apart, where N is 1 << bucket index. ++ */ ++#define BUCKET_MAX 10 ++ ++/* Track network activity frequency */ ++static unsigned long activity_stats[BUCKET_MAX]; ++static ktime_t last_transmit; ++static ktime_t suspend_time; ++static DEFINE_SPINLOCK(activity_lock); ++ ++void activity_stats_update(void) ++{ ++ int i; ++ unsigned long flags; ++ ktime_t now; ++ s64 delta; ++ ++ spin_lock_irqsave(&activity_lock, flags); ++ now = ktime_get(); ++ delta = ktime_to_ns(ktime_sub(now, last_transmit)); ++ ++ for (i = BUCKET_MAX - 1; i >= 0; i--) { ++ /* ++ * Check if the time delta between network activity is within the ++ * minimum bucket range. ++ */ ++ if (delta < (1000000000ULL << i)) ++ continue; ++ ++ activity_stats[i]++; ++ last_transmit = now; ++ break; ++ } ++ spin_unlock_irqrestore(&activity_lock, flags); ++} ++ ++static int activity_stats_show(struct seq_file *m, void *v) ++{ ++ int i; ++ int ret; ++ ++ seq_printf(m, "Min Bucket(sec) Count\n"); ++ ++ for (i = 0; i < BUCKET_MAX; i++) { ++ ret = seq_printf(m, "%15d %lu\n", 1 << i, activity_stats[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int activity_stats_notifier(struct notifier_block *nb, ++ unsigned long event, void *dummy) ++{ ++ switch (event) { ++ case PM_SUSPEND_PREPARE: ++ suspend_time = ktime_get_real(); ++ break; ++ ++ case PM_POST_SUSPEND: ++ suspend_time = ktime_sub(ktime_get_real(), suspend_time); ++ last_transmit = ktime_sub(last_transmit, suspend_time); ++ } ++ ++ return 0; ++} ++ ++static int activity_stats_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, activity_stats_show, PDE_DATA(inode)); ++} ++ ++static const struct file_operations activity_stats_fops = { ++ .open = activity_stats_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++ ++static struct notifier_block activity_stats_notifier_block = { ++ .notifier_call = activity_stats_notifier, ++}; ++ ++static int __init activity_stats_init(void) ++{ ++ proc_create("activity", S_IRUGO, ++ init_net.proc_net_stat, &activity_stats_fops); ++ return register_pm_notifier(&activity_stats_notifier_block); ++} ++ ++subsys_initcall(activity_stats_init); ++ +diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c +index c35c3f4..1428c3f 100644 +--- a/net/ax25/af_ax25.c ++++ b/net/ax25/af_ax25.c +@@ -806,6 +806,9 @@ static int ax25_create(struct net *net, struct socket *sock, int protocol, + struct sock *sk; + ax25_cb *ax25; + ++ if (protocol < 0 || protocol > SK_PROTOCOL_MAX) ++ return -EINVAL; ++ + if (!net_eq(net, &init_net)) + return -EAFNOSUPPORT; + +diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c +index 339c74a..6ad3468 100644 +--- a/net/bluetooth/af_bluetooth.c ++++ b/net/bluetooth/af_bluetooth.c +@@ -104,11 +104,40 @@ void bt_sock_unregister(int proto) + } + EXPORT_SYMBOL(bt_sock_unregister); + ++#ifdef CONFIG_PARANOID_NETWORK ++static inline int current_has_bt_admin(void) ++{ ++ return !current_euid(); ++} ++ ++static inline int current_has_bt(void) ++{ ++ return current_has_bt_admin(); ++} ++# else ++static inline int current_has_bt_admin(void) ++{ ++ return 1; ++} ++ ++static inline int current_has_bt(void) ++{ ++ return 1; ++} ++#endif ++ + static int bt_sock_create(struct net *net, struct socket *sock, int proto, + int kern) + { + int err; + ++ if (proto == BTPROTO_RFCOMM || proto == BTPROTO_SCO || ++ proto == BTPROTO_L2CAP) { ++ if (!current_has_bt()) ++ return -EPERM; ++ } else if (!current_has_bt_admin()) ++ return -EPERM; ++ + if (net != &init_net) + return -EAFNOSUPPORT; + +diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c +index ffd379d..b8919d0 100644 +--- a/net/bridge/br_device.c ++++ b/net/bridge/br_device.c +@@ -44,16 +44,17 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) + } + #endif + +- u64_stats_update_begin(&brstats->syncp); +- brstats->tx_packets++; +- brstats->tx_bytes += skb->len; +- u64_stats_update_end(&brstats->syncp); +- + BR_INPUT_SKB_CB(skb)->brdev = dev; + + skb_reset_mac_header(skb); + skb_pull(skb, ETH_HLEN); + ++ u64_stats_update_begin(&brstats->syncp); ++ brstats->tx_packets++; ++ /* Exclude ETH_HLEN from byte stats for consistency with Rx chain */ ++ brstats->tx_bytes += skb->len; ++ u64_stats_update_end(&brstats->syncp); ++ + if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid)) + goto out; + +diff --git a/net/core/dev.c b/net/core/dev.c +index fb96258..ecc1d4b 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -4019,7 +4019,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff + NAPI_GRO_CB(skb)->same_flow = 0; + NAPI_GRO_CB(skb)->flush = 0; + NAPI_GRO_CB(skb)->free = 0; +- NAPI_GRO_CB(skb)->udp_mark = 0; ++ NAPI_GRO_CB(skb)->encap_mark = 0; + + /* Setup for GRO checksum validation */ + switch (skb->ip_summed) { +diff --git a/net/core/ethtool.c b/net/core/ethtool.c +index 06dfb29..e61a94f 100644 +--- a/net/core/ethtool.c ++++ b/net/core/ethtool.c +@@ -900,11 +900,13 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr) + + static int ethtool_get_wol(struct net_device *dev, char __user *useraddr) + { +- struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; ++ struct ethtool_wolinfo wol;; + + if (!dev->ethtool_ops->get_wol) + return -EOPNOTSUPP; + ++ memset(&wol, 0, sizeof(struct ethtool_wolinfo)); ++ wol.cmd = ETHTOOL_GWOL; + dev->ethtool_ops->get_wol(dev, &wol); + + if (copy_to_user(useraddr, &wol, sizeof(wol))) +diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c +index 185c341..5cbcdfd 100644 +--- a/net/core/fib_rules.c ++++ b/net/core/fib_rules.c +@@ -31,6 +31,8 @@ int fib_default_rule_add(struct fib_rules_ops *ops, + r->pref = pref; + r->table = table; + r->flags = flags; ++ r->uid_start = INVALID_UID; ++ r->uid_end = INVALID_UID; + r->fr_net = hold_net(ops->fro_net); + + r->suppress_prefixlen = -1; +@@ -182,6 +184,23 @@ void fib_rules_unregister(struct fib_rules_ops *ops) + } + EXPORT_SYMBOL_GPL(fib_rules_unregister); + ++static inline kuid_t fib_nl_uid(struct nlattr *nla) ++{ ++ return make_kuid(current_user_ns(), nla_get_u32(nla)); ++} ++ ++static int nla_put_uid(struct sk_buff *skb, int idx, kuid_t uid) ++{ ++ return nla_put_u32(skb, idx, from_kuid_munged(current_user_ns(), uid)); ++} ++ ++static int fib_uid_range_match(struct flowi *fl, struct fib_rule *rule) ++{ ++ return (!uid_valid(rule->uid_start) && !uid_valid(rule->uid_end)) || ++ (uid_gte(fl->flowi_uid, rule->uid_start) && ++ uid_lte(fl->flowi_uid, rule->uid_end)); ++} ++ + static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, + struct flowi *fl, int flags) + { +@@ -196,6 +215,9 @@ static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, + if ((rule->mark ^ fl->flowi_mark) & rule->mark_mask) + goto out; + ++ if (!fib_uid_range_match(fl, rule)) ++ goto out; ++ + ret = ops->match(rule, fl, flags); + out: + return (rule->flags & FIB_RULE_INVERT) ? !ret : ret; +@@ -378,6 +400,19 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh) + } else if (rule->action == FR_ACT_GOTO) + goto errout_free; + ++ /* UID start and end must either both be valid or both unspecified. */ ++ rule->uid_start = rule->uid_end = INVALID_UID; ++ if (tb[FRA_UID_START] || tb[FRA_UID_END]) { ++ if (tb[FRA_UID_START] && tb[FRA_UID_END]) { ++ rule->uid_start = fib_nl_uid(tb[FRA_UID_START]); ++ rule->uid_end = fib_nl_uid(tb[FRA_UID_END]); ++ } ++ if (!uid_valid(rule->uid_start) || ++ !uid_valid(rule->uid_end) || ++ !uid_lte(rule->uid_start, rule->uid_end)) ++ goto errout_free; ++ } ++ + err = ops->configure(rule, skb, frh, tb); + if (err < 0) + goto errout_free; +@@ -484,6 +519,14 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh) + (rule->mark_mask != nla_get_u32(tb[FRA_FWMASK]))) + continue; + ++ if (tb[FRA_UID_START] && ++ !uid_eq(rule->uid_start, fib_nl_uid(tb[FRA_UID_START]))) ++ continue; ++ ++ if (tb[FRA_UID_END] && ++ !uid_eq(rule->uid_end, fib_nl_uid(tb[FRA_UID_END]))) ++ continue; ++ + if (!ops->compare(rule, frh, tb)) + continue; + +@@ -542,7 +585,9 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + + nla_total_size(4) /* FRA_SUPPRESS_PREFIXLEN */ + + nla_total_size(4) /* FRA_SUPPRESS_IFGROUP */ + + nla_total_size(4) /* FRA_FWMARK */ +- + nla_total_size(4); /* FRA_FWMASK */ ++ + nla_total_size(4) /* FRA_FWMASK */ ++ + nla_total_size(4) /* FRA_UID_START */ ++ + nla_total_size(4); /* FRA_UID_END */ + + if (ops->nlmsg_payload) + payload += ops->nlmsg_payload(rule); +@@ -598,7 +643,11 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, + ((rule->mark_mask || rule->mark) && + nla_put_u32(skb, FRA_FWMASK, rule->mark_mask)) || + (rule->target && +- nla_put_u32(skb, FRA_GOTO, rule->target))) ++ nla_put_u32(skb, FRA_GOTO, rule->target)) || ++ (uid_valid(rule->uid_start) && ++ nla_put_uid(skb, FRA_UID_START, rule->uid_start)) || ++ (uid_valid(rule->uid_end) && ++ nla_put_uid(skb, FRA_UID_END, rule->uid_end))) + goto nla_put_failure; + + if (rule->suppress_ifgroup != -1) { +diff --git a/net/core/filter.c b/net/core/filter.c +index 647b122..14b0959 100644 +--- a/net/core/filter.c ++++ b/net/core/filter.c +@@ -46,9 +46,10 @@ + #include <linux/if_vlan.h> + + /** +- * sk_filter - run a packet through a socket filter ++ * sk_filter_trim_cap - run a packet through a socket filter + * @sk: sock associated with &sk_buff + * @skb: buffer to filter ++ * @cap: limit on how short the eBPF program may trim the packet + * + * Run the filter code and then cut skb->data to correct size returned by + * SK_RUN_FILTER. If pkt_len is 0 we toss packet. If skb->len is smaller +@@ -57,7 +58,7 @@ + * be accepted or -EPERM if the packet should be tossed. + * + */ +-int sk_filter(struct sock *sk, struct sk_buff *skb) ++int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap) + { + int err; + struct sk_filter *filter; +@@ -78,14 +79,13 @@ int sk_filter(struct sock *sk, struct sk_buff *skb) + filter = rcu_dereference(sk->sk_filter); + if (filter) { + unsigned int pkt_len = SK_RUN_FILTER(filter, skb); +- +- err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM; ++ err = pkt_len ? pskb_trim(skb, max(cap, pkt_len)) : -EPERM; + } + rcu_read_unlock(); + + return err; + } +-EXPORT_SYMBOL(sk_filter); ++EXPORT_SYMBOL(sk_filter_trim_cap); + + static u64 __skb_get_pay_offset(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) + { +diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c +index c522f7a..8bc4e3b 100644 +--- a/net/core/rtnetlink.c ++++ b/net/core/rtnetlink.c +@@ -1018,14 +1018,16 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, + goto nla_put_failure; + + if (1) { +- struct rtnl_link_ifmap map = { +- .mem_start = dev->mem_start, +- .mem_end = dev->mem_end, +- .base_addr = dev->base_addr, +- .irq = dev->irq, +- .dma = dev->dma, +- .port = dev->if_port, +- }; ++ struct rtnl_link_ifmap map; ++ ++ memset(&map, 0, sizeof(map)); ++ map.mem_start = dev->mem_start; ++ map.mem_end = dev->mem_end; ++ map.base_addr = dev->base_addr; ++ map.irq = dev->irq; ++ map.dma = dev->dma; ++ map.port = dev->if_port; ++ + if (nla_put(skb, IFLA_MAP, sizeof(map), &map)) + goto nla_put_failure; + } +diff --git a/net/core/scm.c b/net/core/scm.c +index b442e7e..47a5515 100644 +--- a/net/core/scm.c ++++ b/net/core/scm.c +@@ -87,6 +87,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp) + *fplp = fpl; + fpl->count = 0; + fpl->max = SCM_MAX_FD; ++ fpl->user = NULL; + } + fpp = &fpl->fp[fpl->count]; + +@@ -107,6 +108,10 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp) + *fpp++ = file; + fpl->count++; + } ++ ++ if (!fpl->user) ++ fpl->user = get_uid(current_user()); ++ + return num; + } + +@@ -119,6 +124,7 @@ void __scm_destroy(struct scm_cookie *scm) + scm->fp = NULL; + for (i=fpl->count-1; i>=0; i--) + fput(fpl->fp[i]); ++ free_uid(fpl->user); + kfree(fpl); + } + } +@@ -335,6 +341,7 @@ struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl) + for (i = 0; i < fpl->count; i++) + get_file(fpl->fp[i]); + new_fpl->max = new_fpl->count; ++ new_fpl->user = get_uid(fpl->user); + } + return new_fpl; + } +diff --git a/net/core/sock.c b/net/core/sock.c +index 1e5130d..ef07173 100644 +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -735,7 +735,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname, + val = min_t(u32, val, sysctl_wmem_max); + set_sndbuf: + sk->sk_userlocks |= SOCK_SNDBUF_LOCK; +- sk->sk_sndbuf = max_t(u32, val * 2, SOCK_MIN_SNDBUF); ++ sk->sk_sndbuf = max_t(int, val * 2, SOCK_MIN_SNDBUF); + /* Wake up sending tasks if we upped the value. */ + sk->sk_write_space(sk); + break; +@@ -771,7 +771,7 @@ set_rcvbuf: + * returning the value we actually used in getsockopt + * is the most desirable behavior. + */ +- sk->sk_rcvbuf = max_t(u32, val * 2, SOCK_MIN_RCVBUF); ++ sk->sk_rcvbuf = max_t(int, val * 2, SOCK_MIN_RCVBUF); + break; + + case SO_RCVBUFFORCE: +diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c +index e731c96..8725b91 100644 +--- a/net/core/sysctl_net_core.c ++++ b/net/core/sysctl_net_core.c +@@ -23,7 +23,6 @@ + #include <net/pkt_sched.h> + + static int zero = 0; +-static int one = 1; + static int ushort_max = USHRT_MAX; + static int min_sndbuf = SOCK_MIN_SNDBUF; + static int min_rcvbuf = SOCK_MIN_RCVBUF; +diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c +index 6bcaa33..568f9b3 100644 +--- a/net/dccp/ipv6.c ++++ b/net/dccp/ipv6.c +@@ -238,7 +238,9 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req) + security_req_classify_flow(req, flowi6_to_flowi(&fl6)); + + +- final_p = fl6_update_dst(&fl6, np->opt, &final); ++ rcu_read_lock(); ++ final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final); ++ rcu_read_unlock(); + + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); + if (IS_ERR(dst)) { +@@ -255,7 +257,10 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req) + &ireq->ir_v6_loc_addr, + &ireq->ir_v6_rmt_addr); + fl6.daddr = ireq->ir_v6_rmt_addr; +- err = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass); ++ rcu_read_lock(); ++ err = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt), ++ np->tclass); ++ rcu_read_unlock(); + err = net_xmit_eval(err); + } + +@@ -450,6 +455,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, + { + struct inet_request_sock *ireq = inet_rsk(req); + struct ipv6_pinfo *newnp, *np = inet6_sk(sk); ++ struct ipv6_txoptions *opt; + struct inet_sock *newinet; + struct dccp6_sock *newdp6; + struct sock *newsk; +@@ -479,6 +485,9 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, + newsk->sk_backlog_rcv = dccp_v4_do_rcv; + newnp->pktoptions = NULL; + newnp->opt = NULL; ++ newnp->ipv6_mc_list = NULL; ++ newnp->ipv6_ac_list = NULL; ++ newnp->ipv6_fl_list = NULL; + newnp->mcast_oif = inet6_iif(skb); + newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; + +@@ -554,6 +563,9 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, + /* Clone RX bits */ + newnp->rxopt.all = np->rxopt.all; + ++ newnp->ipv6_mc_list = NULL; ++ newnp->ipv6_ac_list = NULL; ++ newnp->ipv6_fl_list = NULL; + /* Clone pktoptions received with SYN */ + newnp->pktoptions = NULL; + if (ireq->pktopts != NULL) { +@@ -573,13 +585,15 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, + * Yes, keeping reference count would be much more clever, but we make + * one more one thing there: reattach optmem to newsk. + */ +- if (np->opt != NULL) +- newnp->opt = ipv6_dup_options(newsk, np->opt); +- ++ opt = rcu_dereference(np->opt); ++ if (opt) { ++ opt = ipv6_dup_options(newsk, opt); ++ RCU_INIT_POINTER(newnp->opt, opt); ++ } + inet_csk(newsk)->icsk_ext_hdr_len = 0; +- if (newnp->opt != NULL) +- inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen + +- newnp->opt->opt_flen); ++ if (opt) ++ inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen + ++ opt->opt_flen; + + dccp_sync_mss(newsk, dst_mtu(dst)); + +@@ -832,6 +846,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, + struct ipv6_pinfo *np = inet6_sk(sk); + struct dccp_sock *dp = dccp_sk(sk); + struct in6_addr *saddr = NULL, *final_p, final; ++ struct ipv6_txoptions *opt; + struct flowi6 fl6; + struct dst_entry *dst; + int addr_type; +@@ -933,7 +948,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, + fl6.fl6_sport = inet->inet_sport; + security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); + +- final_p = fl6_update_dst(&fl6, np->opt, &final); ++ opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); ++ final_p = fl6_update_dst(&fl6, opt, &final); + + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); + if (IS_ERR(dst)) { +@@ -953,9 +969,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, + __ip6_dst_store(sk, dst, NULL, NULL); + + icsk->icsk_ext_hdr_len = 0; +- if (np->opt != NULL) +- icsk->icsk_ext_hdr_len = (np->opt->opt_flen + +- np->opt->opt_nflen); ++ if (opt) ++ icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen; + + inet->inet_dport = usin->sin6_port; + +diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c +index 25733d5..2aeeb4f 100644 +--- a/net/decnet/af_decnet.c ++++ b/net/decnet/af_decnet.c +@@ -678,6 +678,9 @@ static int dn_create(struct net *net, struct socket *sock, int protocol, + { + struct sock *sk; + ++ if (protocol < 0 || protocol > SK_PROTOCOL_MAX) ++ return -EINVAL; ++ + if (!net_eq(net, &init_net)) + return -EAFNOSUPPORT; + +diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile +index 518c04e..591745d 100644 +--- a/net/ipv4/Makefile ++++ b/net/ipv4/Makefile +@@ -15,6 +15,7 @@ obj-y := route.o inetpeer.o protocol.o \ + + obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o + obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o ++obj-$(CONFIG_SYSFS) += sysfs_net_ipv4.o + obj-$(CONFIG_PROC_FS) += proc.o + obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o + obj-$(CONFIG_IP_MROUTE) += ipmr.o +diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c +index 9a17357..20a113a 100644 +--- a/net/ipv4/af_inet.c ++++ b/net/ipv4/af_inet.c +@@ -119,6 +119,19 @@ + #include <linux/mroute.h> + #endif + ++#ifdef CONFIG_ANDROID_PARANOID_NETWORK ++#include <linux/android_aid.h> ++ ++static inline int current_has_network(void) ++{ ++ return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); ++} ++#else ++static inline int current_has_network(void) ++{ ++ return 1; ++} ++#endif + + /* The inetsw table contains everything that inet_create needs to + * build a new socket. +@@ -259,6 +272,12 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, + int try_loading_module = 0; + int err; + ++ if (protocol < 0 || protocol >= IPPROTO_MAX) ++ return -EINVAL; ++ ++ if (!current_has_network()) ++ return -EACCES; ++ + sock->state = SS_UNCONNECTED; + + /* Look for the requested type/protocol pair. */ +@@ -307,8 +326,7 @@ lookup_protocol: + } + + err = -EPERM; +- if (sock->type == SOCK_RAW && !kern && +- !ns_capable(net->user_ns, CAP_NET_RAW)) ++ if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) + goto out_rcu_unlock; + + sock->ops = answer->ops; +@@ -870,6 +888,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) + case SIOCSIFPFLAGS: + case SIOCGIFPFLAGS: + case SIOCSIFFLAGS: ++ case SIOCKILLADDR: + err = devinet_ioctl(net, cmd, (void __user *)arg); + break; + default: +@@ -1388,6 +1407,19 @@ out: + return pp; + } + ++static struct sk_buff **ipip_gro_receive(struct sk_buff **head, ++ struct sk_buff *skb) ++{ ++ if (NAPI_GRO_CB(skb)->encap_mark) { ++ NAPI_GRO_CB(skb)->flush = 1; ++ return NULL; ++ } ++ ++ NAPI_GRO_CB(skb)->encap_mark = 1; ++ ++ return inet_gro_receive(head, skb); ++} ++ + int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) + { + if (sk->sk_family == AF_INET) +@@ -1646,7 +1678,7 @@ static struct packet_offload ip_packet_offload __read_mostly = { + static const struct net_offload ipip_offload = { + .callbacks = { + .gso_segment = inet_gso_segment, +- .gro_receive = inet_gro_receive, ++ .gro_receive = ipip_gro_receive, + .gro_complete = inet_gro_complete, + }, + }; +diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c +index 214882e..ac854e3 100644 +--- a/net/ipv4/devinet.c ++++ b/net/ipv4/devinet.c +@@ -59,6 +59,7 @@ + + #include <net/arp.h> + #include <net/ip.h> ++#include <net/tcp.h> + #include <net/route.h> + #include <net/ip_fib.h> + #include <net/rtnetlink.h> +@@ -334,6 +335,9 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, + + ASSERT_RTNL(); + ++ if (in_dev->dead) ++ goto no_promotions; ++ + /* 1. Deleting primary ifaddr forces deletion all secondaries + * unless alias promotion is set + **/ +@@ -380,6 +384,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, + fib_del_ifaddr(ifa, ifa1); + } + ++no_promotions: + /* 2. Unlink it */ + + *ifap = ifa1->ifa_next; +@@ -934,6 +939,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) + case SIOCSIFBRDADDR: /* Set the broadcast address */ + case SIOCSIFDSTADDR: /* Set the destination address */ + case SIOCSIFNETMASK: /* Set the netmask for the interface */ ++ case SIOCKILLADDR: /* Nuke all sockets on this address */ + ret = -EPERM; + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) + goto out; +@@ -985,7 +991,8 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) + } + + ret = -EADDRNOTAVAIL; +- if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) ++ if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS ++ && cmd != SIOCKILLADDR) + goto done; + + switch (cmd) { +@@ -1112,6 +1119,9 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) + inet_insert_ifa(ifa); + } + break; ++ case SIOCKILLADDR: /* Nuke all connections on this address */ ++ ret = tcp_nuke_addr(net, (struct sockaddr *) sin); ++ break; + } + done: + rtnl_unlock(); +diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c +index 23104a3..4a84e85 100644 +--- a/net/ipv4/fib_frontend.c ++++ b/net/ipv4/fib_frontend.c +@@ -533,6 +533,7 @@ const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = { + [RTA_METRICS] = { .type = NLA_NESTED }, + [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, + [RTA_FLOW] = { .type = NLA_U32 }, ++ [RTA_UID] = { .type = NLA_U32 }, + }; + + static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, +@@ -814,6 +815,9 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim) + subnet = 1; + } + ++ if (in_dev->dead) ++ goto no_promotions; ++ + /* Deletion is more complicated than add. + * We should take care of not to delete too much :-) + * +@@ -889,6 +893,7 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim) + } + } + ++no_promotions: + if (!(ok & BRD_OK)) + fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim); + if (subnet && ifa->ifa_prefixlen < 31) { +diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c +index 51973dd..3c84285 100644 +--- a/net/ipv4/gre_offload.c ++++ b/net/ipv4/gre_offload.c +@@ -127,6 +127,11 @@ static struct sk_buff **gre_gro_receive(struct sk_buff **head, + struct packet_offload *ptype; + __be16 type; + ++ if (NAPI_GRO_CB(skb)->encap_mark) ++ goto out; ++ ++ NAPI_GRO_CB(skb)->encap_mark = 1; ++ + off = skb_gro_offset(skb); + hlen = off + sizeof(*greh); + greh = skb_gro_header_fast(skb, off); +diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c +index 14d02ea..59270b7 100644 +--- a/net/ipv4/inet_connection_sock.c ++++ b/net/ipv4/inet_connection_sock.c +@@ -410,7 +410,8 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, + sk->sk_protocol, + flags, + (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr, +- ireq->ir_loc_addr, ireq->ir_rmt_port, inet_sk(sk)->inet_sport); ++ ireq->ir_loc_addr, ireq->ir_rmt_port, inet_sk(sk)->inet_sport, ++ sock_i_uid(sk)); + security_req_classify_flow(req, flowi4_to_flowi(fl4)); + rt = ip_route_output_flow(net, fl4, sk); + if (IS_ERR(rt)) +@@ -446,7 +447,8 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk, + RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, + sk->sk_protocol, inet_sk_flowi_flags(sk), + (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr, +- ireq->ir_loc_addr, ireq->ir_rmt_port, inet_sk(sk)->inet_sport); ++ ireq->ir_loc_addr, ireq->ir_rmt_port, inet_sk(sk)->inet_sport, ++ sock_i_uid(sk)); + security_req_classify_flow(req, flowi4_to_flowi(fl4)); + rt = ip_route_output_flow(net, fl4, sk); + if (IS_ERR(rt)) +@@ -677,6 +679,8 @@ struct sock *inet_csk_clone_lock(const struct sock *sk, + inet_sk(newsk)->inet_sport = htons(inet_rsk(req)->ir_num); + newsk->sk_write_space = sk_stream_write_space; + ++ inet_sk(newsk)->mc_list = NULL; ++ + newsk->sk_mark = inet_rsk(req)->ir_mark; + + newicsk->icsk_retransmits = 0; +diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c +index 357c2a9..a02b2d8 100644 +--- a/net/ipv4/ip_output.c ++++ b/net/ipv4/ip_output.c +@@ -888,10 +888,12 @@ static int __ip_append_data(struct sock *sk, + csummode = CHECKSUM_PARTIAL; + + cork->length += length; +- if (((length > mtu) || (skb && skb_is_gso(skb))) && ++ if ((skb && skb_is_gso(skb)) || ++ (((length + (skb ? skb->len : fragheaderlen)) > mtu) && ++ (skb_queue_len(queue) <= 1) && + (sk->sk_protocol == IPPROTO_UDP) && + (rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len && +- (sk->sk_type == SOCK_DGRAM)) { ++ (sk->sk_type == SOCK_DGRAM))) { + err = ip_ufo_append_data(sk, queue, getfrag, from, length, + hh_len, fragheaderlen, transhdrlen, + maxfraglen, flags); +@@ -1207,6 +1209,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, + + cork->length += size; + if ((size + skb->len > mtu) && ++ (skb_queue_len(&sk->sk_write_queue) == 1) && + (sk->sk_protocol == IPPROTO_UDP) && + (rt->dst.dev->features & NETIF_F_UFO)) { + skb_shinfo(skb)->gso_size = mtu - fragheaderlen; +@@ -1544,7 +1547,8 @@ void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb, + RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol, + ip_reply_arg_flowi_flags(arg), + daddr, saddr, +- tcp_hdr(skb)->source, tcp_hdr(skb)->dest); ++ tcp_hdr(skb)->source, tcp_hdr(skb)->dest, ++ arg->uid); + security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); + rt = ip_route_output_key(net, &fl4); + if (IS_ERR(rt)) +diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c +index 046fce0..8839b55 100644 +--- a/net/ipv4/ip_sockglue.c ++++ b/net/ipv4/ip_sockglue.c +@@ -1066,7 +1066,14 @@ void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb) + pktinfo->ipi_ifindex = 0; + pktinfo->ipi_spec_dst.s_addr = 0; + } +- skb_dst_drop(skb); ++ /* We need to keep the dst for __ip_options_echo() ++ * We could restrict the test to opt.ts_needtime || opt.srr, ++ * but the following is good enough as IP options are not often used. ++ */ ++ if (unlikely(IPCB(skb)->opt.optlen)) ++ skb_dst_force(skb); ++ else ++ skb_dst_drop(skb); + } + + int ip_setsockopt(struct sock *sk, int level, +diff --git a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c +index c6eb421..ea91058 100644 +--- a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c ++++ b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c +@@ -108,10 +108,18 @@ static int masq_inet_event(struct notifier_block *this, + unsigned long event, + void *ptr) + { +- struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev; ++ struct in_device *idev = ((struct in_ifaddr *)ptr)->ifa_dev; + struct netdev_notifier_info info; + +- netdev_notifier_info_init(&info, dev); ++ /* The masq_dev_notifier will catch the case of the device going ++ * down. So if the inetdev is dead and being destroyed we have ++ * no work to do. Otherwise this is an individual address removal ++ * and we have to perform the flush. ++ */ ++ if (idev->dead) ++ return NOTIFY_DONE; ++ ++ netdev_notifier_info_init(&info, idev->dev); + return masq_device_event(this, event, &info); + } + +diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c +index 64f4edb..ecda491 100644 +--- a/net/ipv4/ping.c ++++ b/net/ipv4/ping.c +@@ -154,17 +154,18 @@ void ping_hash(struct sock *sk) + void ping_unhash(struct sock *sk) + { + struct inet_sock *isk = inet_sk(sk); ++ + pr_debug("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num); ++ write_lock_bh(&ping_table.lock); + if (sk_hashed(sk)) { +- write_lock_bh(&ping_table.lock); + hlist_nulls_del(&sk->sk_nulls_node); + sk_nulls_node_init(&sk->sk_nulls_node); + sock_put(sk); + isk->inet_num = 0; + isk->inet_sport = 0; + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); +- write_unlock_bh(&ping_table.lock); + } ++ write_unlock_bh(&ping_table.lock); + } + EXPORT_SYMBOL_GPL(ping_unhash); + +@@ -789,7 +790,8 @@ static int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m + + flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, + RT_SCOPE_UNIVERSE, sk->sk_protocol, +- inet_sk_flowi_flags(sk), faddr, saddr, 0, 0); ++ inet_sk_flowi_flags(sk), faddr, saddr, 0, 0, ++ sock_i_uid(sk)); + + security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); + rt = ip_route_output_flow(net, &fl4, sk); +diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c +index 739db31..6167b55 100644 +--- a/net/ipv4/raw.c ++++ b/net/ipv4/raw.c +@@ -582,7 +582,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, + inet_sk_flowi_flags(sk) | + (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0), +- daddr, saddr, 0, 0); ++ daddr, saddr, 0, 0, ++ sock_i_uid(sk)); + + if (!inet->hdrincl) { + err = raw_probe_proto_opt(&fl4, msg); +diff --git a/net/ipv4/route.c b/net/ipv4/route.c +index b7ac498..f5174c8 100644 +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -499,7 +499,7 @@ void __ip_select_ident(struct iphdr *iph, int segs) + } + EXPORT_SYMBOL(__ip_select_ident); + +-static void __build_flow_key(struct flowi4 *fl4, const struct sock *sk, ++static void __build_flow_key(struct flowi4 *fl4, struct sock *sk, + const struct iphdr *iph, + int oif, u8 tos, + u8 prot, u32 mark, int flow_flags) +@@ -515,11 +515,12 @@ static void __build_flow_key(struct flowi4 *fl4, const struct sock *sk, + flowi4_init_output(fl4, oif, mark, tos, + RT_SCOPE_UNIVERSE, prot, + flow_flags, +- iph->daddr, iph->saddr, 0, 0); ++ iph->daddr, iph->saddr, 0, 0, ++ sk ? sock_i_uid(sk) : GLOBAL_ROOT_UID); + } + + static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, +- const struct sock *sk) ++ struct sock *sk) + { + const struct iphdr *iph = ip_hdr(skb); + int oif = skb->dev->ifindex; +@@ -530,7 +531,7 @@ static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, + __build_flow_key(fl4, sk, iph, oif, tos, prot, mark, 0); + } + +-static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) ++static void build_sk_flow_key(struct flowi4 *fl4, struct sock *sk) + { + const struct inet_sock *inet = inet_sk(sk); + const struct ip_options_rcu *inet_opt; +@@ -544,11 +545,12 @@ static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) + RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, + inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, + inet_sk_flowi_flags(sk), +- daddr, inet->inet_saddr, 0, 0); ++ daddr, inet->inet_saddr, 0, 0, ++ sock_i_uid(sk)); + rcu_read_unlock(); + } + +-static void ip_rt_build_flow_key(struct flowi4 *fl4, const struct sock *sk, ++static void ip_rt_build_flow_key(struct flowi4 *fl4, struct sock *sk, + const struct sk_buff *skb) + { + if (skb) +@@ -2353,6 +2355,11 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, + nla_put_u32(skb, RTA_MARK, fl4->flowi4_mark)) + goto nla_put_failure; + ++ if (!uid_eq(fl4->flowi4_uid, INVALID_UID) && ++ nla_put_u32(skb, RTA_UID, ++ from_kuid_munged(current_user_ns(), fl4->flowi4_uid))) ++ goto nla_put_failure; ++ + error = rt->dst.error; + + if (rt_is_input_route(rt)) { +@@ -2402,6 +2409,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) + int err; + int mark; + struct sk_buff *skb; ++ kuid_t uid; + + err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy); + if (err < 0) +@@ -2429,6 +2437,10 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) + dst = tb[RTA_DST] ? nla_get_be32(tb[RTA_DST]) : 0; + iif = tb[RTA_IIF] ? nla_get_u32(tb[RTA_IIF]) : 0; + mark = tb[RTA_MARK] ? nla_get_u32(tb[RTA_MARK]) : 0; ++ if (tb[RTA_UID]) ++ uid = make_kuid(current_user_ns(), nla_get_u32(tb[RTA_UID])); ++ else ++ uid = (iif ? INVALID_UID : current_uid()); + + memset(&fl4, 0, sizeof(fl4)); + fl4.daddr = dst; +@@ -2436,6 +2448,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) + fl4.flowi4_tos = rtm->rtm_tos; + fl4.flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0; + fl4.flowi4_mark = mark; ++ fl4.flowi4_uid = uid; + + if (iif) { + struct net_device *dev; +diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c +index 32b98d0..088b6ab 100644 +--- a/net/ipv4/syncookies.c ++++ b/net/ipv4/syncookies.c +@@ -336,8 +336,9 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) + flowi4_init_output(&fl4, sk->sk_bound_dev_if, ireq->ir_mark, + RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP, + inet_sk_flowi_flags(sk), +- opt->srr ? opt->faddr : ireq->ir_rmt_addr, +- ireq->ir_loc_addr, th->source, th->dest); ++ (opt && opt->srr) ? opt->faddr : ireq->ir_rmt_addr, ++ ireq->ir_loc_addr, th->source, th->dest, ++ sock_i_uid(sk)); + security_req_classify_flow(req, flowi4_to_flowi(&fl4)); + rt = ip_route_output_key(sock_net(sk), &fl4); + if (IS_ERR(rt)) { +diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c +index b3c53c8..6aae73f 100644 +--- a/net/ipv4/sysctl_net_ipv4.c ++++ b/net/ipv4/sysctl_net_ipv4.c +@@ -145,6 +145,21 @@ static int ipv4_ping_group_range(struct ctl_table *table, int write, + return ret; + } + ++/* Validate changes from /proc interface. */ ++static int proc_tcp_default_init_rwnd(struct ctl_table *ctl, int write, ++ void __user *buffer, ++ size_t *lenp, loff_t *ppos) ++{ ++ int old_value = *(int *)ctl->data; ++ int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); ++ int new_value = *(int *)ctl->data; ++ ++ if (write && ret == 0 && (new_value < 3 || new_value > 100)) ++ *(int *)ctl->data = old_value; ++ ++ return ret; ++} ++ + static int proc_tcp_congestion_control(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) + { +@@ -722,6 +737,13 @@ static struct ctl_table ipv4_table[] = { + .extra2 = &one, + }, + { ++ .procname = "tcp_default_init_rwnd", ++ .data = &sysctl_tcp_default_init_rwnd, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = proc_tcp_default_init_rwnd ++ }, ++ { + .procname = "icmp_msgs_per_sec", + .data = &sysctl_icmp_msgs_per_sec, + .maxlen = sizeof(int), +diff --git a/net/ipv4/sysfs_net_ipv4.c b/net/ipv4/sysfs_net_ipv4.c +new file mode 100644 +index 0000000..0cbbf10 +--- /dev/null ++++ b/net/ipv4/sysfs_net_ipv4.c +@@ -0,0 +1,88 @@ ++/* ++ * net/ipv4/sysfs_net_ipv4.c ++ * ++ * sysfs-based networking knobs (so we can, unlike with sysctl, control perms) ++ * ++ * Copyright (C) 2008 Google, Inc. ++ * ++ * Robert Love <rlove@google.com> ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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/kobject.h> ++#include <linux/string.h> ++#include <linux/sysfs.h> ++#include <linux/init.h> ++#include <net/tcp.h> ++ ++#define CREATE_IPV4_FILE(_name, _var) \ ++static ssize_t _name##_show(struct kobject *kobj, \ ++ struct kobj_attribute *attr, char *buf) \ ++{ \ ++ return sprintf(buf, "%d\n", _var); \ ++} \ ++static ssize_t _name##_store(struct kobject *kobj, \ ++ struct kobj_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ int val, ret; \ ++ ret = sscanf(buf, "%d", &val); \ ++ if (ret != 1) \ ++ return -EINVAL; \ ++ if (val < 0) \ ++ return -EINVAL; \ ++ _var = val; \ ++ return count; \ ++} \ ++static struct kobj_attribute _name##_attr = \ ++ __ATTR(_name, 0644, _name##_show, _name##_store) ++ ++CREATE_IPV4_FILE(tcp_wmem_min, sysctl_tcp_wmem[0]); ++CREATE_IPV4_FILE(tcp_wmem_def, sysctl_tcp_wmem[1]); ++CREATE_IPV4_FILE(tcp_wmem_max, sysctl_tcp_wmem[2]); ++ ++CREATE_IPV4_FILE(tcp_rmem_min, sysctl_tcp_rmem[0]); ++CREATE_IPV4_FILE(tcp_rmem_def, sysctl_tcp_rmem[1]); ++CREATE_IPV4_FILE(tcp_rmem_max, sysctl_tcp_rmem[2]); ++ ++static struct attribute *ipv4_attrs[] = { ++ &tcp_wmem_min_attr.attr, ++ &tcp_wmem_def_attr.attr, ++ &tcp_wmem_max_attr.attr, ++ &tcp_rmem_min_attr.attr, ++ &tcp_rmem_def_attr.attr, ++ &tcp_rmem_max_attr.attr, ++ NULL ++}; ++ ++static struct attribute_group ipv4_attr_group = { ++ .attrs = ipv4_attrs, ++}; ++ ++static __init int sysfs_ipv4_init(void) ++{ ++ struct kobject *ipv4_kobject; ++ int ret; ++ ++ ipv4_kobject = kobject_create_and_add("ipv4", kernel_kobj); ++ if (!ipv4_kobject) ++ return -ENOMEM; ++ ++ ret = sysfs_create_group(ipv4_kobject, &ipv4_attr_group); ++ if (ret) { ++ kobject_put(ipv4_kobject); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++subsys_initcall(sysfs_ipv4_init); +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index 32b25cc..5b1f544 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -268,12 +268,16 @@ + #include <linux/crypto.h> + #include <linux/time.h> + #include <linux/slab.h> ++#include <linux/uid_stat.h> + + #include <net/icmp.h> + #include <net/inet_common.h> + #include <net/tcp.h> + #include <net/xfrm.h> + #include <net/ip.h> ++#include <net/ip6_route.h> ++#include <net/ipv6.h> ++#include <net/transp_v6.h> + #include <net/sock.h> + + #include <asm/uaccess.h> +@@ -775,6 +779,12 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, + ret = -EAGAIN; + break; + } ++ /* if __tcp_splice_read() got nothing while we have ++ * an skb in receive queue, we do not want to loop. ++ * This might happen with URG data. ++ */ ++ if (!skb_queue_empty(&sk->sk_receive_queue)) ++ break; + sk_wait_data(sk, &timeo); + if (signal_pending(current)) { + ret = sock_intr_errno(timeo); +@@ -1298,6 +1308,10 @@ out: + tcp_push(sk, flags, mss_now, tp->nonagle, size_goal); + out_nopush: + release_sock(sk); ++ ++ if (copied + copied_syn) ++ uid_stat_tcp_snd(from_kuid(&init_user_ns, current_uid()), ++ copied + copied_syn); + return copied + copied_syn; + + do_fault: +@@ -1569,6 +1583,8 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, + if (copied > 0) { + tcp_recv_skb(sk, seq, &offset); + tcp_cleanup_rbuf(sk, copied); ++ uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), ++ copied); + } + return copied; + } +@@ -1898,6 +1914,10 @@ skip_copy: + tcp_cleanup_rbuf(sk, copied); + + release_sock(sk); ++ ++ if (copied > 0) ++ uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), ++ copied); + return copied; + + out: +@@ -1906,6 +1926,9 @@ out: + + recv_urg: + err = tcp_recv_urg(sk, msg, len, flags); ++ if (err > 0) ++ uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), ++ err); + goto out; + + recv_sndq: +@@ -2256,6 +2279,10 @@ int tcp_disconnect(struct sock *sk, int flags) + tcp_set_ca_state(sk, TCP_CA_Open); + tcp_clear_retrans(tp); + inet_csk_delack_init(sk); ++ /* Initialize rcv_mss to TCP_MIN_MSS to avoid division by 0 ++ * issue in __tcp_select_window() ++ */ ++ icsk->icsk_ack.rcv_mss = TCP_MIN_MSS; + tcp_init_send_head(sk); + memset(&tp->rx_opt, 0, sizeof(tp->rx_opt)); + __sk_dst_reset(sk); +@@ -3128,3 +3155,117 @@ void __init tcp_init(void) + BUG_ON(tcp_register_congestion_control(&tcp_reno) != 0); + tcp_tasklet_init(); + } ++ ++static int tcp_is_local(struct net *net, __be32 addr) { ++ struct rtable *rt; ++ struct flowi4 fl4 = { .daddr = addr }; ++ rt = ip_route_output_key(net, &fl4); ++ if (IS_ERR_OR_NULL(rt)) ++ return 0; ++ return rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK); ++} ++ ++#if defined(CONFIG_IPV6) ++static int tcp_is_local6(struct net *net, struct in6_addr *addr) { ++ struct rt6_info *rt6 = rt6_lookup(net, addr, addr, 0, 0); ++ return rt6 && rt6->dst.dev && (rt6->dst.dev->flags & IFF_LOOPBACK); ++} ++#endif ++ ++/* ++ * tcp_nuke_addr - destroy all sockets on the given local address ++ * if local address is the unspecified address (0.0.0.0 or ::), destroy all ++ * sockets with local addresses that are not configured. ++ */ ++int tcp_nuke_addr(struct net *net, struct sockaddr *addr) ++{ ++ int family = addr->sa_family; ++ unsigned int bucket; ++ ++ struct in_addr *in = NULL; ++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++ struct in6_addr *in6 = NULL; ++#endif ++ if (family == AF_INET) { ++ in = &((struct sockaddr_in *)addr)->sin_addr; ++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) ++ } else if (family == AF_INET6) { ++ in6 = &((struct sockaddr_in6 *)addr)->sin6_addr; ++#endif ++ } else { ++ return -EAFNOSUPPORT; ++ } ++ ++ for (bucket = 0; bucket <= tcp_hashinfo.ehash_mask; bucket++) { ++ struct hlist_nulls_node *node; ++ struct sock *sk; ++ spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket); ++ ++restart: ++ spin_lock_bh(lock); ++ sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) { ++ struct inet_sock *inet = inet_sk(sk); ++ ++ if (sk->sk_state == TCP_TIME_WAIT) { ++ /* ++ * Sockets that are in TIME_WAIT state are ++ * instances of lightweight inet_timewait_sock, ++ * we should simply skip them (or we'll try to ++ * access non-existing fields and crash). ++ */ ++ continue; ++ } ++ ++ if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT) ++ continue; ++ ++ if (sock_flag(sk, SOCK_DEAD)) ++ continue; ++ ++ if (family == AF_INET) { ++ __be32 s4 = inet->inet_rcv_saddr; ++ if (s4 == LOOPBACK4_IPV6) ++ continue; ++ ++ if (in->s_addr != s4 && ++ !(in->s_addr == INADDR_ANY && ++ !tcp_is_local(net, s4))) ++ continue; ++ } ++ ++#if defined(CONFIG_IPV6) ++ if (family == AF_INET6) { ++ struct in6_addr *s6; ++ ++ s6 = &sk->sk_v6_rcv_saddr; ++ if (ipv6_addr_type(s6) == IPV6_ADDR_MAPPED) ++ continue; ++ ++ if (!ipv6_addr_equal(in6, s6) && ++ !(ipv6_addr_equal(in6, &in6addr_any) && ++ !tcp_is_local6(net, s6))) ++ continue; ++ } ++#endif ++ ++ sock_hold(sk); ++ spin_unlock_bh(lock); ++ ++ local_bh_disable(); ++ bh_lock_sock(sk); ++ sk->sk_err = ETIMEDOUT; ++ sk->sk_error_report(sk); ++ ++ tcp_done(sk); ++ bh_unlock_sock(sk); ++ local_bh_enable(); ++ sock_put(sk); ++ ++ goto restart; ++ } ++ spin_unlock_bh(lock); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(tcp_nuke_addr); +diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c +index 6f46cde..7503171 100644 +--- a/net/ipv4/tcp_input.c ++++ b/net/ipv4/tcp_input.c +@@ -88,7 +88,7 @@ int sysctl_tcp_adv_win_scale __read_mostly = 1; + EXPORT_SYMBOL(sysctl_tcp_adv_win_scale); + + /* rfc5961 challenge ack rate limiting */ +-int sysctl_tcp_challenge_ack_limit = 100; ++int sysctl_tcp_challenge_ack_limit = 1000; + + int sysctl_tcp_stdurg __read_mostly; + int sysctl_tcp_rfc1337 __read_mostly; +@@ -99,6 +99,7 @@ int sysctl_tcp_thin_dupack __read_mostly; + + int sysctl_tcp_moderate_rcvbuf __read_mostly = 1; + int sysctl_tcp_early_retrans __read_mostly = 3; ++int sysctl_tcp_default_init_rwnd __read_mostly = TCP_INIT_CWND * 2; + + #define FLAG_DATA 0x01 /* Incoming frame contained data. */ + #define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ +@@ -2515,6 +2516,9 @@ static void tcp_cwnd_reduction(struct sock *sk, const int prior_unsacked, + int newly_acked_sacked = prior_unsacked - + (tp->packets_out - tp->sacked_out); + ++ if (newly_acked_sacked <= 0 || WARN_ON_ONCE(!tp->prior_cwnd)) ++ return; ++ + tp->prr_delivered += newly_acked_sacked; + if (tcp_packets_in_flight(tp) > tp->snd_ssthresh) { + u64 dividend = (u64)tp->snd_ssthresh * tp->prr_delivered + +@@ -3324,13 +3328,18 @@ static void tcp_send_challenge_ack(struct sock *sk) + /* unprotected vars, we dont care of overwrites */ + static u32 challenge_timestamp; + static unsigned int challenge_count; +- u32 now = jiffies / HZ; ++ u32 count, now = jiffies / HZ; + + if (now != challenge_timestamp) { ++ u32 half = (sysctl_tcp_challenge_ack_limit + 1) >> 1; ++ + challenge_timestamp = now; +- challenge_count = 0; ++ WRITE_ONCE(challenge_count, half + ++ prandom_u32_max(sysctl_tcp_challenge_ack_limit)); + } +- if (++challenge_count <= sysctl_tcp_challenge_ack_limit) { ++ count = READ_ONCE(challenge_count); ++ if (count > 0) { ++ WRITE_ONCE(challenge_count, count - 1); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK); + tcp_send_ack(sk); + } +diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c +index a5fdfe9..f7f2387 100644 +--- a/net/ipv4/tcp_ipv4.c ++++ b/net/ipv4/tcp_ipv4.c +@@ -1580,6 +1580,21 @@ bool tcp_prequeue(struct sock *sk, struct sk_buff *skb) + } + EXPORT_SYMBOL(tcp_prequeue); + ++int tcp_filter(struct sock *sk, struct sk_buff *skb) ++{ ++ struct tcphdr *th = (struct tcphdr *)skb->data; ++ unsigned int eaten = skb->len; ++ int err; ++ ++ err = sk_filter_trim_cap(sk, skb, th->doff * 4); ++ if (!err) { ++ eaten -= skb->len; ++ TCP_SKB_CB(skb)->end_seq -= eaten; ++ } ++ return err; ++} ++EXPORT_SYMBOL(tcp_filter); ++ + /* + * From tcp_input.c + */ +@@ -1663,8 +1678,10 @@ process: + + nf_reset(skb); + +- if (sk_filter(sk, skb)) ++ if (tcp_filter(sk, skb)) + goto discard_and_relse; ++ th = (const struct tcphdr *)skb->data; ++ iph = ip_hdr(skb); + + sk_mark_napi_id(sk, skb); + skb->dev = NULL; +diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c +index dc9f925..c07062b 100644 +--- a/net/ipv4/tcp_output.c ++++ b/net/ipv4/tcp_output.c +@@ -197,7 +197,7 @@ u32 tcp_default_init_rwnd(u32 mss) + * (RFC 3517, Section 4, NextSeg() rule (2)). Further place a + * limit when mss is larger than 1460. + */ +- u32 init_rwnd = TCP_INIT_CWND * 2; ++ u32 init_rwnd = sysctl_tcp_default_init_rwnd; + + if (mss > 1460) + init_rwnd = max((1460 * init_rwnd) / mss, 2U); +diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c +index c5e3194..68ed1ab 100644 +--- a/net/ipv4/udp.c ++++ b/net/ipv4/udp.c +@@ -804,7 +804,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4) + if (is_udplite) /* UDP-Lite */ + csum = udplite_csum(skb); + +- else if (sk->sk_no_check_tx) { /* UDP csum disabled */ ++ else if (sk->sk_no_check_tx && !skb_is_gso(skb)) { /* UDP csum off */ + + skb->ip_summed = CHECKSUM_NONE; + goto send; +@@ -1007,7 +1007,8 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, + RT_SCOPE_UNIVERSE, sk->sk_protocol, + inet_sk_flowi_flags(sk), +- faddr, saddr, dport, inet->inet_sport); ++ faddr, saddr, dport, inet->inet_sport, ++ sock_i_uid(sk)); + + security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); + rt = ip_route_output_flow(net, fl4, sk); +@@ -1252,6 +1253,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + int peeked, off = 0; + int err; + int is_udplite = IS_UDPLITE(sk); ++ bool checksum_valid = false; + bool slow; + + if (flags & MSG_ERRQUEUE) +@@ -1277,11 +1279,12 @@ try_again: + */ + + if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) { +- if (udp_lib_checksum_complete(skb)) ++ checksum_valid = !udp_lib_checksum_complete(skb); ++ if (!checksum_valid) + goto csum_copy_err; + } + +- if (skb_csum_unnecessary(skb)) ++ if (checksum_valid || skb_csum_unnecessary(skb)) + err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), + msg->msg_iov, copied); + else { +diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c +index 6480cea..e6d05ae 100644 +--- a/net/ipv4/udp_offload.c ++++ b/net/ipv4/udp_offload.c +@@ -266,14 +266,14 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb, + unsigned int off = skb_gro_offset(skb); + int flush = 1; + +- if (NAPI_GRO_CB(skb)->udp_mark || ++ if (NAPI_GRO_CB(skb)->encap_mark || + (skb->ip_summed != CHECKSUM_PARTIAL && + NAPI_GRO_CB(skb)->csum_cnt == 0 && + !NAPI_GRO_CB(skb)->csum_valid)) + goto out; + +- /* mark that this skb passed once through the udp gro layer */ +- NAPI_GRO_CB(skb)->udp_mark = 1; ++ /* mark that this skb passed once through the tunnel gro layer */ ++ NAPI_GRO_CB(skb)->encap_mark = 1; + + rcu_read_lock(); + uo_priv = rcu_dereference(udp_offload_base); +diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c +index 17a0258..0bd85d0 100644 +--- a/net/ipv6/addrconf.c ++++ b/net/ipv6/addrconf.c +@@ -196,6 +196,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { + .accept_ra_rt_info_max_plen = 0, + #endif + #endif ++ .accept_ra_rt_table = 0, + .proxy_ndp = 0, + .accept_source_route = 0, /* we do not accept RH0 by default. */ + .disable_ipv6 = 0, +@@ -233,6 +234,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { + .accept_ra_rt_info_max_plen = 0, + #endif + #endif ++ .accept_ra_rt_table = 0, + .proxy_ndp = 0, + .accept_source_route = 0, /* we do not accept RH0 by default. */ + .disable_ipv6 = 0, +@@ -1170,6 +1172,9 @@ enum { + IPV6_SADDR_RULE_PRIVACY, + IPV6_SADDR_RULE_ORCHID, + IPV6_SADDR_RULE_PREFIX, ++#ifdef CONFIG_IPV6_OPTIMISTIC_DAD ++ IPV6_SADDR_RULE_NOT_OPTIMISTIC, ++#endif + IPV6_SADDR_RULE_MAX + }; + +@@ -1197,6 +1202,15 @@ static inline int ipv6_saddr_preferred(int type) + return 0; + } + ++static inline bool ipv6_use_optimistic_addr(struct inet6_dev *idev) ++{ ++#ifdef CONFIG_IPV6_OPTIMISTIC_DAD ++ return idev && idev->cnf.optimistic_dad && idev->cnf.use_optimistic; ++#else ++ return false; ++#endif ++} ++ + static int ipv6_get_saddr_eval(struct net *net, + struct ipv6_saddr_score *score, + struct ipv6_saddr_dst *dst, +@@ -1257,10 +1271,16 @@ static int ipv6_get_saddr_eval(struct net *net, + score->scopedist = ret; + break; + case IPV6_SADDR_RULE_PREFERRED: ++ { + /* Rule 3: Avoid deprecated and optimistic addresses */ ++ u8 avoid = IFA_F_DEPRECATED; ++ ++ if (!ipv6_use_optimistic_addr(score->ifa->idev)) ++ avoid |= IFA_F_OPTIMISTIC; + ret = ipv6_saddr_preferred(score->addr_type) || +- !(score->ifa->flags & (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)); ++ !(score->ifa->flags & avoid); + break; ++ } + #ifdef CONFIG_IPV6_MIP6 + case IPV6_SADDR_RULE_HOA: + { +@@ -1306,6 +1326,14 @@ static int ipv6_get_saddr_eval(struct net *net, + ret = score->ifa->prefix_len; + score->matchlen = ret; + break; ++#ifdef CONFIG_IPV6_OPTIMISTIC_DAD ++ case IPV6_SADDR_RULE_NOT_OPTIMISTIC: ++ /* Optimistic addresses still have lower precedence than other ++ * preferred addresses. ++ */ ++ ret = !(score->ifa->flags & IFA_F_OPTIMISTIC); ++ break; ++#endif + default: + ret = 0; + } +@@ -1503,7 +1531,9 @@ int ipv6_chk_addr(struct net *net, const struct in6_addr *addr, + if (!net_eq(dev_net(ifp->idev->dev), net)) + continue; + if (ipv6_addr_equal(&ifp->addr, addr) && +- !(ifp->flags&IFA_F_TENTATIVE) && ++ (!(ifp->flags&IFA_F_TENTATIVE) || ++ (ipv6_use_optimistic_addr(ifp->idev) && ++ ifp->flags&IFA_F_OPTIMISTIC)) && + (dev == NULL || ifp->idev->dev == dev || + !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))) { + rcu_read_unlock_bh(); +@@ -1966,6 +1996,31 @@ static void __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmp + __ipv6_regen_rndid(idev); + } + ++u32 addrconf_rt_table(const struct net_device *dev, u32 default_table) { ++ /* Determines into what table to put autoconf PIO/RIO/default routes ++ * learned on this device. ++ * ++ * - If 0, use the same table for every device. This puts routes into ++ * one of RT_TABLE_{PREFIX,INFO,DFLT} depending on the type of route ++ * (but note that these three are currently all equal to ++ * RT6_TABLE_MAIN). ++ * - If > 0, use the specified table. ++ * - If < 0, put routes into table dev->ifindex + (-rt_table). ++ */ ++ struct inet6_dev *idev = in6_dev_get(dev); ++ u32 table; ++ int sysctl = idev->cnf.accept_ra_rt_table; ++ if (sysctl == 0) { ++ table = default_table; ++ } else if (sysctl > 0) { ++ table = (u32) sysctl; ++ } else { ++ table = (unsigned) dev->ifindex + (-sysctl); ++ } ++ in6_dev_put(idev); ++ return table; ++} ++ + /* + * Add prefix route. + */ +@@ -1975,7 +2030,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev, + unsigned long expires, u32 flags) + { + struct fib6_config cfg = { +- .fc_table = RT6_TABLE_PREFIX, ++ .fc_table = addrconf_rt_table(dev, RT6_TABLE_PREFIX), + .fc_metric = IP6_RT_PRIO_ADDRCONF, + .fc_ifindex = dev->ifindex, + .fc_expires = expires, +@@ -2009,7 +2064,8 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, + struct rt6_info *rt = NULL; + struct fib6_table *table; + +- table = fib6_get_table(dev_net(dev), RT6_TABLE_PREFIX); ++ table = fib6_get_table(dev_net(dev), ++ addrconf_rt_table(dev, RT6_TABLE_PREFIX)); + if (table == NULL) + return NULL; + +@@ -3222,8 +3278,15 @@ static void addrconf_dad_begin(struct inet6_ifaddr *ifp) + * Optimistic nodes can start receiving + * Frames right away + */ +- if (ifp->flags & IFA_F_OPTIMISTIC) ++ if (ifp->flags & IFA_F_OPTIMISTIC) { + ip6_ins_rt(ifp->rt); ++ if (ipv6_use_optimistic_addr(idev)) { ++ /* Because optimistic nodes can use this address, ++ * notify listeners. If DAD fails, RTM_DELADDR is sent. ++ */ ++ ipv6_ifa_notify(RTM_NEWADDR, ifp); ++ } ++ } + + addrconf_dad_kick(ifp); + out: +@@ -4326,10 +4389,12 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, + array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen; + #endif + #endif ++ array[DEVCONF_ACCEPT_RA_RT_TABLE] = cnf->accept_ra_rt_table; + array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp; + array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route; + #ifdef CONFIG_IPV6_OPTIMISTIC_DAD + array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad; ++ array[DEVCONF_USE_OPTIMISTIC] = cnf->use_optimistic; + #endif + #ifdef CONFIG_IPV6_MROUTE + array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding; +@@ -4843,6 +4908,21 @@ int addrconf_sysctl_forward(struct ctl_table *ctl, int write, + return ret; + } + ++static ++int addrconf_sysctl_mtu(struct ctl_table *ctl, int write, ++ void __user *buffer, size_t *lenp, loff_t *ppos) ++{ ++ struct inet6_dev *idev = ctl->extra1; ++ int min_mtu = IPV6_MIN_MTU; ++ struct ctl_table lctl; ++ ++ lctl = *ctl; ++ lctl.extra1 = &min_mtu; ++ lctl.extra2 = idev ? &idev->dev->mtu : NULL; ++ ++ return proc_dointvec_minmax(&lctl, write, buffer, lenp, ppos); ++} ++ + static void dev_disable_change(struct inet6_dev *idev) + { + struct netdev_notifier_info info; +@@ -4994,7 +5074,7 @@ static struct addrconf_sysctl_table + .data = &ipv6_devconf.mtu6, + .maxlen = sizeof(int), + .mode = 0644, +- .proc_handler = proc_dointvec, ++ .proc_handler = addrconf_sysctl_mtu, + }, + { + .procname = "accept_ra", +@@ -5150,6 +5230,13 @@ static struct addrconf_sysctl_table + #endif + #endif + { ++ .procname = "accept_ra_rt_table", ++ .data = &ipv6_devconf.accept_ra_rt_table, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = proc_dointvec, ++ }, ++ { + .procname = "proxy_ndp", + .data = &ipv6_devconf.proxy_ndp, + .maxlen = sizeof(int), +@@ -5172,6 +5259,14 @@ static struct addrconf_sysctl_table + .proc_handler = proc_dointvec, + + }, ++ { ++ .procname = "use_optimistic", ++ .data = &ipv6_devconf.use_optimistic, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = proc_dointvec, ++ ++ }, + #endif + #ifdef CONFIG_IPV6_MROUTE + { +diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c +index e8c4400..5615dde 100644 +--- a/net/ipv6/af_inet6.c ++++ b/net/ipv6/af_inet6.c +@@ -64,6 +64,20 @@ + #include <asm/uaccess.h> + #include <linux/mroute6.h> + ++#ifdef CONFIG_ANDROID_PARANOID_NETWORK ++#include <linux/android_aid.h> ++ ++static inline int current_has_network(void) ++{ ++ return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); ++} ++#else ++static inline int current_has_network(void) ++{ ++ return 1; ++} ++#endif ++ + MODULE_AUTHOR("Cast of dozens"); + MODULE_DESCRIPTION("IPv6 protocol stack for Linux"); + MODULE_LICENSE("GPL"); +@@ -109,6 +123,12 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, + int try_loading_module = 0; + int err; + ++ if (protocol < 0 || protocol >= IPPROTO_MAX) ++ return -EINVAL; ++ ++ if (!current_has_network()) ++ return -EACCES; ++ + /* Look for the requested type/protocol pair. */ + lookup_protocol: + err = -ESOCKTNOSUPPORT; +@@ -155,8 +175,7 @@ lookup_protocol: + } + + err = -EPERM; +- if (sock->type == SOCK_RAW && !kern && +- !ns_capable(net->user_ns, CAP_NET_RAW)) ++ if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) + goto out_rcu_unlock; + + sock->ops = answer->ops; +@@ -425,9 +444,11 @@ void inet6_destroy_sock(struct sock *sk) + + /* Free tx options */ + +- opt = xchg(&np->opt, NULL); +- if (opt != NULL) +- sock_kfree_s(sk, opt, opt->tot_len); ++ opt = xchg((__force struct ipv6_txoptions **)&np->opt, NULL); ++ if (opt) { ++ atomic_sub(opt->tot_len, &sk->sk_omem_alloc); ++ txopt_put(opt); ++ } + } + EXPORT_SYMBOL_GPL(inet6_destroy_sock); + +@@ -471,6 +492,21 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr, + } + EXPORT_SYMBOL(inet6_getname); + ++int inet6_killaddr_ioctl(struct net *net, void __user *arg) { ++ struct in6_ifreq ireq; ++ struct sockaddr_in6 sin6; ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EACCES; ++ ++ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) ++ return -EFAULT; ++ ++ sin6.sin6_family = AF_INET6; ++ sin6.sin6_addr = ireq.ifr6_addr; ++ return tcp_nuke_addr(net, (struct sockaddr *) &sin6); ++} ++ + int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) + { + struct sock *sk = sock->sk; +@@ -494,6 +530,8 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) + return addrconf_del_ifaddr(net, (void __user *) arg); + case SIOCSIFDSTADDR: + return addrconf_set_dstaddr(net, (void __user *) arg); ++ case SIOCKILLADDR: ++ return inet6_killaddr_ioctl(net, (void __user *) arg); + default: + if (!sk->sk_prot->ioctl) + return -ENOIOCTLCMD; +@@ -654,9 +692,13 @@ int inet6_sk_rebuild_header(struct sock *sk) + fl6.flowi6_mark = sk->sk_mark; + fl6.fl6_dport = inet->inet_dport; + fl6.fl6_sport = inet->inet_sport; ++ fl6.flowi6_uid = sock_i_uid(sk); + security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); + +- final_p = fl6_update_dst(&fl6, np->opt, &final); ++ rcu_read_lock(); ++ final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), ++ &final); ++ rcu_read_unlock(); + + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); + if (IS_ERR(dst)) { +diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c +index 6d16eb0..bb93d3b 100644 +--- a/net/ipv6/ah6.c ++++ b/net/ipv6/ah6.c +@@ -662,7 +662,7 @@ static int ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + if (type == NDISC_REDIRECT) + ip6_redirect(skb, net, skb->dev->ifindex, 0); + else +- ip6_update_pmtu(skb, net, info, 0, 0); ++ ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); + xfrm_state_put(x); + + return 0; +diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c +index 11e3945..7626716 100644 +--- a/net/ipv6/datagram.c ++++ b/net/ipv6/datagram.c +@@ -161,14 +161,17 @@ ipv4_connected: + fl6.flowi6_mark = sk->sk_mark; + fl6.fl6_dport = inet->inet_dport; + fl6.fl6_sport = inet->inet_sport; ++ fl6.flowi6_uid = sock_i_uid(sk); + + if (!fl6.flowi6_oif && (addr_type&IPV6_ADDR_MULTICAST)) + fl6.flowi6_oif = np->mcast_oif; + + security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); + +- opt = flowlabel ? flowlabel->opt : np->opt; ++ rcu_read_lock(); ++ opt = flowlabel ? flowlabel->opt : rcu_dereference(np->opt); + final_p = fl6_update_dst(&fl6, opt, &final); ++ rcu_read_unlock(); + + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); + err = 0; +diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c +index 83fc3a3..0958354 100644 +--- a/net/ipv6/esp6.c ++++ b/net/ipv6/esp6.c +@@ -441,7 +441,7 @@ static int esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + if (type == NDISC_REDIRECT) + ip6_redirect(skb, net, skb->dev->ifindex, 0); + else +- ip6_update_pmtu(skb, net, info, 0, 0); ++ ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); + xfrm_state_put(x); + + return 0; +diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c +index bfde361..4f08a0f 100644 +--- a/net/ipv6/exthdrs.c ++++ b/net/ipv6/exthdrs.c +@@ -727,6 +727,7 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) + *((char **)&opt2->dst1opt) += dif; + if (opt2->srcrt) + *((char **)&opt2->srcrt) += dif; ++ atomic_set(&opt2->refcnt, 1); + } + return opt2; + } +@@ -790,7 +791,7 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, + return ERR_PTR(-ENOBUFS); + + memset(opt2, 0, tot_len); +- ++ atomic_set(&opt2->refcnt, 1); + opt2->tot_len = tot_len; + p = (char *)(opt2 + 1); + +diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c +index 8af3eb5..958a187 100644 +--- a/net/ipv6/exthdrs_core.c ++++ b/net/ipv6/exthdrs_core.c +@@ -166,15 +166,15 @@ EXPORT_SYMBOL_GPL(ipv6_find_tlv); + * to explore inner IPv6 header, eg. ICMPv6 error messages. + * + * If target header is found, its offset is set in *offset and return protocol +- * number. Otherwise, return -1. ++ * number. Otherwise, return -ENOENT or -EBADMSG. + * + * If the first fragment doesn't contain the final protocol header or + * NEXTHDR_NONE it is considered invalid. + * + * Note that non-1st fragment is special case that "the protocol number + * of last header" is "next header" field in Fragment header. In this case, +- * *offset is meaningless and fragment offset is stored in *fragoff if fragoff +- * isn't NULL. ++ * *offset is meaningless. If fragoff is not NULL, the fragment offset is ++ * stored in *fragoff; if it is NULL, return -EINVAL. + * + * if flags is not NULL and it's a fragment, then the frag flag + * IP6_FH_F_FRAG will be set. If it's an AH header, the +@@ -253,9 +253,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, + if (target < 0 && + ((!ipv6_ext_hdr(hp->nexthdr)) || + hp->nexthdr == NEXTHDR_NONE)) { +- if (fragoff) ++ if (fragoff) { + *fragoff = _frag_off; +- return hp->nexthdr; ++ return hp->nexthdr; ++ } else { ++ return -EINVAL; ++ } + } + return -ENOENT; + } +diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c +index 97ae700..070d591 100644 +--- a/net/ipv6/icmp.c ++++ b/net/ipv6/icmp.c +@@ -91,7 +91,7 @@ static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + struct net *net = dev_net(skb->dev); + + if (type == ICMPV6_PKT_TOOBIG) +- ip6_update_pmtu(skb, net, info, 0, 0); ++ ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); + else if (type == NDISC_REDIRECT) + ip6_redirect(skb, net, skb->dev->ifindex, 0); + +diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c +index 29b3220..ca655c9 100644 +--- a/net/ipv6/inet6_connection_sock.c ++++ b/net/ipv6/inet6_connection_sock.c +@@ -77,12 +77,15 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, + memset(fl6, 0, sizeof(*fl6)); + fl6->flowi6_proto = IPPROTO_TCP; + fl6->daddr = ireq->ir_v6_rmt_addr; +- final_p = fl6_update_dst(fl6, np->opt, &final); ++ rcu_read_lock(); ++ final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); ++ rcu_read_unlock(); + fl6->saddr = ireq->ir_v6_loc_addr; + fl6->flowi6_oif = ireq->ir_iif; + fl6->flowi6_mark = ireq->ir_mark; + fl6->fl6_dport = ireq->ir_rmt_port; + fl6->fl6_sport = htons(ireq->ir_num); ++ fl6->flowi6_uid = sock_i_uid(sk); + security_req_classify_flow(req, flowi6_to_flowi(fl6)); + + dst = ip6_dst_lookup_flow(sk, fl6, final_p); +@@ -206,9 +209,12 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk, + fl6->flowi6_mark = sk->sk_mark; + fl6->fl6_sport = inet->inet_sport; + fl6->fl6_dport = inet->inet_dport; ++ fl6->flowi6_uid = sock_i_uid(sk); + security_sk_classify_flow(sk, flowi6_to_flowi(fl6)); + +- final_p = fl6_update_dst(fl6, np->opt, &final); ++ rcu_read_lock(); ++ final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); ++ rcu_read_unlock(); + + dst = __inet6_csk_dst_check(sk, np->dst_cookie); + if (!dst) { +@@ -241,7 +247,8 @@ int inet6_csk_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl_unused + /* Restore final destination back after routing done */ + fl6.daddr = sk->sk_v6_daddr; + +- res = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass); ++ res = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt), ++ np->tclass); + rcu_read_unlock(); + return res; + } +diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c +index 01e12d0..bb39292 100644 +--- a/net/ipv6/ip6_offload.c ++++ b/net/ipv6/ip6_offload.c +@@ -122,6 +122,8 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, + + if (udpfrag) { + unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); ++ if (unfrag_ip6hlen < 0) ++ return ERR_PTR(unfrag_ip6hlen); + fptr = (struct frag_hdr *)((u8 *)ipv6h + unfrag_ip6hlen); + fptr->frag_off = htons(offset); + if (skb->next != NULL) +@@ -258,6 +260,19 @@ out: + return pp; + } + ++static struct sk_buff **sit_gro_receive(struct sk_buff **head, ++ struct sk_buff *skb) ++{ ++ if (NAPI_GRO_CB(skb)->encap_mark) { ++ NAPI_GRO_CB(skb)->flush = 1; ++ return NULL; ++ } ++ ++ NAPI_GRO_CB(skb)->encap_mark = 1; ++ ++ return ipv6_gro_receive(head, skb); ++} ++ + static int ipv6_gro_complete(struct sk_buff *skb, int nhoff) + { + const struct net_offload *ops; +@@ -292,7 +307,7 @@ static struct packet_offload ipv6_packet_offload __read_mostly = { + static const struct net_offload sit_offload = { + .callbacks = { + .gso_segment = ipv6_gso_segment, +- .gro_receive = ipv6_gro_receive, ++ .gro_receive = sit_gro_receive, + .gro_complete = ipv6_gro_complete, + }, + }; +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index 7b5cb00..3a48040 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -567,6 +567,10 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) + struct net *net = dev_net(skb_dst(skb)->dev); + + hlen = ip6_find_1stfragopt(skb, &prevhdr); ++ if (hlen < 0) { ++ err = hlen; ++ goto fail; ++ } + nexthdr = *prevhdr; + + mtu = ip6_skb_dst_mtu(skb); +@@ -1042,6 +1046,7 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, + EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow); + + static inline int ip6_ufo_append_data(struct sock *sk, ++ struct sk_buff_head *queue, + int getfrag(void *from, char *to, int offset, int len, + int odd, struct sk_buff *skb), + void *from, int length, int hh_len, int fragheaderlen, +@@ -1057,7 +1062,8 @@ static inline int ip6_ufo_append_data(struct sock *sk, + * device, so create one single skb packet containing complete + * udp datagram + */ +- if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) { ++ skb = skb_peek_tail(queue); ++ if (skb == NULL) { + skb = sock_alloc_send_skb(sk, + hh_len + fragheaderlen + transhdrlen + 20, + (flags & MSG_DONTWAIT), &err); +@@ -1079,7 +1085,7 @@ static inline int ip6_ufo_append_data(struct sock *sk, + skb->protocol = htons(ETH_P_IPV6); + skb->csum = 0; + +- __skb_queue_tail(&sk->sk_write_queue, skb); ++ __skb_queue_tail(queue, skb); + } else if (skb_is_gso(skb)) { + goto append; + } +@@ -1135,99 +1141,107 @@ static void ip6_append_data_mtu(unsigned int *mtu, + } + } + +-int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, +- int offset, int len, int odd, struct sk_buff *skb), +- void *from, int length, int transhdrlen, +- int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6, +- struct rt6_info *rt, unsigned int flags, int dontfrag) ++static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork, ++ struct inet6_cork *v6_cork, ++ int hlimit, int tclass, struct ipv6_txoptions *opt, ++ struct rt6_info *rt, struct flowi6 *fl6) + { +- struct inet_sock *inet = inet_sk(sk); + struct ipv6_pinfo *np = inet6_sk(sk); +- struct inet_cork *cork; ++ unsigned int mtu; ++ ++ /* ++ * setup for corking ++ */ ++ if (opt) { ++ if (WARN_ON(v6_cork->opt)) ++ return -EINVAL; ++ ++ v6_cork->opt = kzalloc(opt->tot_len, sk->sk_allocation); ++ if (unlikely(v6_cork->opt == NULL)) ++ return -ENOBUFS; ++ ++ v6_cork->opt->tot_len = opt->tot_len; ++ v6_cork->opt->opt_flen = opt->opt_flen; ++ v6_cork->opt->opt_nflen = opt->opt_nflen; ++ ++ v6_cork->opt->dst0opt = ip6_opt_dup(opt->dst0opt, ++ sk->sk_allocation); ++ if (opt->dst0opt && !v6_cork->opt->dst0opt) ++ return -ENOBUFS; ++ ++ v6_cork->opt->dst1opt = ip6_opt_dup(opt->dst1opt, ++ sk->sk_allocation); ++ if (opt->dst1opt && !v6_cork->opt->dst1opt) ++ return -ENOBUFS; ++ ++ v6_cork->opt->hopopt = ip6_opt_dup(opt->hopopt, ++ sk->sk_allocation); ++ if (opt->hopopt && !v6_cork->opt->hopopt) ++ return -ENOBUFS; ++ ++ v6_cork->opt->srcrt = ip6_rthdr_dup(opt->srcrt, ++ sk->sk_allocation); ++ if (opt->srcrt && !v6_cork->opt->srcrt) ++ return -ENOBUFS; ++ ++ /* need source address above miyazawa*/ ++ } ++ dst_hold(&rt->dst); ++ cork->base.dst = &rt->dst; ++ cork->fl.u.ip6 = *fl6; ++ v6_cork->hop_limit = hlimit; ++ v6_cork->tclass = tclass; ++ if (rt->dst.flags & DST_XFRM_TUNNEL) ++ mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? ++ rt->dst.dev->mtu : dst_mtu(&rt->dst); ++ else ++ mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? ++ rt->dst.dev->mtu : dst_mtu(rt->dst.path); ++ if (np->frag_size < mtu) { ++ if (np->frag_size) ++ mtu = np->frag_size; ++ } ++ cork->base.fragsize = mtu; ++ if (dst_allfrag(rt->dst.path)) ++ cork->base.flags |= IPCORK_ALLFRAG; ++ cork->base.length = 0; ++ ++ return 0; ++} ++ ++static int __ip6_append_data(struct sock *sk, ++ struct flowi6 *fl6, ++ struct sk_buff_head *queue, ++ struct inet_cork *cork, ++ struct inet6_cork *v6_cork, ++ struct page_frag *pfrag, ++ int getfrag(void *from, char *to, int offset, ++ int len, int odd, struct sk_buff *skb), ++ void *from, int length, int transhdrlen, ++ unsigned int flags, int dontfrag) ++{ + struct sk_buff *skb, *skb_prev = NULL; + unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu; +- int exthdrlen; +- int dst_exthdrlen; ++ int exthdrlen = 0; ++ int dst_exthdrlen = 0; + int hh_len; + int copy; + int err; + int offset = 0; + __u8 tx_flags = 0; + u32 tskey = 0; +- +- if (flags&MSG_PROBE) +- return 0; +- cork = &inet->cork.base; +- if (skb_queue_empty(&sk->sk_write_queue)) { +- /* +- * setup for corking +- */ +- if (opt) { +- if (WARN_ON(np->cork.opt)) +- return -EINVAL; +- +- np->cork.opt = kzalloc(opt->tot_len, sk->sk_allocation); +- if (unlikely(np->cork.opt == NULL)) +- return -ENOBUFS; +- +- np->cork.opt->tot_len = opt->tot_len; +- np->cork.opt->opt_flen = opt->opt_flen; +- np->cork.opt->opt_nflen = opt->opt_nflen; +- +- np->cork.opt->dst0opt = ip6_opt_dup(opt->dst0opt, +- sk->sk_allocation); +- if (opt->dst0opt && !np->cork.opt->dst0opt) +- return -ENOBUFS; +- +- np->cork.opt->dst1opt = ip6_opt_dup(opt->dst1opt, +- sk->sk_allocation); +- if (opt->dst1opt && !np->cork.opt->dst1opt) +- return -ENOBUFS; +- +- np->cork.opt->hopopt = ip6_opt_dup(opt->hopopt, +- sk->sk_allocation); +- if (opt->hopopt && !np->cork.opt->hopopt) +- return -ENOBUFS; +- +- np->cork.opt->srcrt = ip6_rthdr_dup(opt->srcrt, +- sk->sk_allocation); +- if (opt->srcrt && !np->cork.opt->srcrt) +- return -ENOBUFS; +- +- /* need source address above miyazawa*/ +- } +- dst_hold(&rt->dst); +- cork->dst = &rt->dst; +- inet->cork.fl.u.ip6 = *fl6; +- np->cork.hop_limit = hlimit; +- np->cork.tclass = tclass; +- if (rt->dst.flags & DST_XFRM_TUNNEL) +- mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? +- rt->dst.dev->mtu : dst_mtu(&rt->dst); +- else +- mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? +- rt->dst.dev->mtu : dst_mtu(rt->dst.path); +- if (np->frag_size < mtu) { +- if (np->frag_size) +- mtu = np->frag_size; +- } +- cork->fragsize = mtu; +- if (dst_allfrag(rt->dst.path)) +- cork->flags |= IPCORK_ALLFRAG; +- cork->length = 0; +- exthdrlen = (opt ? opt->opt_flen : 0); +- length += exthdrlen; +- transhdrlen += exthdrlen; ++ struct rt6_info *rt = (struct rt6_info *)cork->dst; ++ struct ipv6_txoptions *opt = v6_cork->opt; ++ int csummode = CHECKSUM_NONE; ++ unsigned int maxnonfragsize, headersize; ++ ++ skb = skb_peek_tail(queue); ++ if (!skb) { ++ exthdrlen = opt ? opt->opt_flen : 0; + dst_exthdrlen = rt->dst.header_len - rt->rt6i_nfheader_len; +- } else { +- rt = (struct rt6_info *)cork->dst; +- fl6 = &inet->cork.fl.u.ip6; +- opt = np->cork.opt; +- transhdrlen = 0; +- exthdrlen = 0; +- dst_exthdrlen = 0; +- mtu = cork->fragsize; + } ++ ++ mtu = cork->fragsize; + orig_mtu = mtu; + + hh_len = LL_RESERVED_SPACE(rt->dst.dev); +@@ -1237,38 +1251,43 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, + maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - + sizeof(struct frag_hdr); + +- if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) { +- unsigned int maxnonfragsize, headersize; +- +- headersize = sizeof(struct ipv6hdr) + +- (opt ? opt->opt_flen + opt->opt_nflen : 0) + +- (dst_allfrag(&rt->dst) ? +- sizeof(struct frag_hdr) : 0) + +- rt->rt6i_nfheader_len; +- +- if (ip6_sk_ignore_df(sk)) +- maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN; +- else +- maxnonfragsize = mtu; ++ headersize = sizeof(struct ipv6hdr) + ++ (opt ? opt->opt_flen + opt->opt_nflen : 0) + ++ (dst_allfrag(&rt->dst) ? ++ sizeof(struct frag_hdr) : 0) + ++ rt->rt6i_nfheader_len; ++ ++ if (cork->length + length > mtu - headersize && dontfrag && ++ (sk->sk_protocol == IPPROTO_UDP || ++ sk->sk_protocol == IPPROTO_RAW)) { ++ ipv6_local_rxpmtu(sk, fl6, mtu - headersize + ++ sizeof(struct ipv6hdr)); ++ goto emsgsize; ++ } + +- /* dontfrag active */ +- if ((cork->length + length > mtu - headersize) && dontfrag && +- (sk->sk_protocol == IPPROTO_UDP || +- sk->sk_protocol == IPPROTO_RAW)) { +- ipv6_local_rxpmtu(sk, fl6, mtu - headersize + +- sizeof(struct ipv6hdr)); +- goto emsgsize; +- } ++ if (ip6_sk_ignore_df(sk)) ++ maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN; ++ else ++ maxnonfragsize = mtu; + +- if (cork->length + length > maxnonfragsize - headersize) { ++ if (cork->length + length > maxnonfragsize - headersize) { + emsgsize: +- ipv6_local_error(sk, EMSGSIZE, fl6, +- mtu - headersize + +- sizeof(struct ipv6hdr)); +- return -EMSGSIZE; +- } ++ ipv6_local_error(sk, EMSGSIZE, fl6, ++ mtu - headersize + ++ sizeof(struct ipv6hdr)); ++ return -EMSGSIZE; + } + ++ /* CHECKSUM_PARTIAL only with no extension headers and when ++ * we are not going to fragment ++ */ ++ if (transhdrlen && sk->sk_protocol == IPPROTO_UDP && ++ headersize == sizeof(struct ipv6hdr) && ++ length < mtu - headersize && ++ !(flags & MSG_MORE) && ++ rt->dst.dev->features & NETIF_F_V6_CSUM) ++ csummode = CHECKSUM_PARTIAL; ++ + if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_RAW) { + sock_tx_timestamp(sk, &tx_flags); + if (tx_flags & SKBTX_ANY_SW_TSTAMP && +@@ -1292,14 +1311,14 @@ emsgsize: + * --yoshfuji + */ + +- skb = skb_peek_tail(&sk->sk_write_queue); + cork->length += length; +- if (((length > mtu) || +- (skb && skb_is_gso(skb))) && ++ if ((skb && skb_is_gso(skb)) || ++ (((length + (skb ? skb->len : headersize)) > mtu) && ++ (skb_queue_len(queue) <= 1) && + (sk->sk_protocol == IPPROTO_UDP) && +- (rt->dst.dev->features & NETIF_F_UFO) && +- (sk->sk_type == SOCK_DGRAM)) { +- err = ip6_ufo_append_data(sk, getfrag, from, length, ++ (rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len && ++ (sk->sk_type == SOCK_DGRAM))) { ++ err = ip6_ufo_append_data(sk, queue, getfrag, from, length, + hh_len, fragheaderlen, + transhdrlen, mtu, flags, rt); + if (err) +@@ -1370,6 +1389,11 @@ alloc_new_skb: + */ + alloclen += sizeof(struct frag_hdr); + ++ copy = datalen - transhdrlen - fraggap; ++ if (copy < 0) { ++ err = -EINVAL; ++ goto error; ++ } + if (transhdrlen) { + skb = sock_alloc_send_skb(sk, + alloclen + hh_len, +@@ -1390,7 +1414,7 @@ alloc_new_skb: + * Fill in the control structures + */ + skb->protocol = htons(ETH_P_IPV6); +- skb->ip_summed = CHECKSUM_NONE; ++ skb->ip_summed = csummode; + skb->csum = 0; + /* reserve for fragmentation and ipsec header */ + skb_reserve(skb, hh_len + sizeof(struct frag_hdr) + +@@ -1419,13 +1443,9 @@ alloc_new_skb: + data += fraggap; + pskb_trim_unique(skb_prev, maxfraglen); + } +- copy = datalen - transhdrlen - fraggap; +- +- if (copy < 0) { +- err = -EINVAL; +- kfree_skb(skb); +- goto error; +- } else if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) { ++ if (copy > 0 && ++ getfrag(from, data + transhdrlen, offset, ++ copy, fraggap, skb) < 0) { + err = -EFAULT; + kfree_skb(skb); + goto error; +@@ -1440,7 +1460,7 @@ alloc_new_skb: + /* + * Put the packet on the pending queue + */ +- __skb_queue_tail(&sk->sk_write_queue, skb); ++ __skb_queue_tail(queue, skb); + continue; + } + +@@ -1459,7 +1479,6 @@ alloc_new_skb: + } + } else { + int i = skb_shinfo(skb)->nr_frags; +- struct page_frag *pfrag = sk_page_frag(sk); + + err = -ENOMEM; + if (!sk_page_frag_refill(sk, pfrag)) +@@ -1502,25 +1521,62 @@ error: + IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); + return err; + } ++ ++int ip6_append_data(struct sock *sk, ++ int getfrag(void *from, char *to, int offset, int len, ++ int odd, struct sk_buff *skb), ++ void *from, int length, int transhdrlen, int hlimit, ++ int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6, ++ struct rt6_info *rt, unsigned int flags, int dontfrag) ++{ ++ struct inet_sock *inet = inet_sk(sk); ++ struct ipv6_pinfo *np = inet6_sk(sk); ++ int exthdrlen; ++ int err; ++ ++ if (flags&MSG_PROBE) ++ return 0; ++ if (skb_queue_empty(&sk->sk_write_queue)) { ++ /* ++ * setup for corking ++ */ ++ err = ip6_setup_cork(sk, &inet->cork, &np->cork, hlimit, ++ tclass, opt, rt, fl6); ++ if (err) ++ return err; ++ ++ exthdrlen = (opt ? opt->opt_flen : 0); ++ length += exthdrlen; ++ transhdrlen += exthdrlen; ++ } else { ++ fl6 = &inet->cork.fl.u.ip6; ++ transhdrlen = 0; ++ } ++ ++ return __ip6_append_data(sk, fl6, &sk->sk_write_queue, &inet->cork.base, ++ &np->cork, sk_page_frag(sk), getfrag, ++ from, length, transhdrlen, flags, dontfrag); ++} + EXPORT_SYMBOL_GPL(ip6_append_data); + +-static void ip6_cork_release(struct inet_sock *inet, struct ipv6_pinfo *np) ++static void ip6_cork_release(struct inet_cork_full *cork, ++ struct inet6_cork *v6_cork) + { +- if (np->cork.opt) { +- kfree(np->cork.opt->dst0opt); +- kfree(np->cork.opt->dst1opt); +- kfree(np->cork.opt->hopopt); +- kfree(np->cork.opt->srcrt); +- kfree(np->cork.opt); +- np->cork.opt = NULL; ++ if (v6_cork->opt) { ++ kfree(v6_cork->opt->dst0opt); ++ kfree(v6_cork->opt->dst1opt); ++ kfree(v6_cork->opt->hopopt); ++ kfree(v6_cork->opt->srcrt); ++ kfree(v6_cork->opt); ++ v6_cork->opt = NULL; + } + +- if (inet->cork.base.dst) { +- dst_release(inet->cork.base.dst); +- inet->cork.base.dst = NULL; +- inet->cork.base.flags &= ~IPCORK_ALLFRAG; ++ if (cork->base.dst) { ++ dst_release(cork->base.dst); ++ cork->base.dst = NULL; ++ cork->base.flags &= ~IPCORK_ALLFRAG; + } +- memset(&inet->cork.fl, 0, sizeof(inet->cork.fl)); ++ memset(&cork->fl, 0, sizeof(cork->fl)); + } + + int ip6_push_pending_frames(struct sock *sk) +@@ -1599,7 +1655,7 @@ int ip6_push_pending_frames(struct sock *sk) + } + + out: +- ip6_cork_release(inet, np); ++ ip6_cork_release(&inet->cork, &np->cork); + return err; + error: + IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); +@@ -1607,17 +1663,23 @@ error: + } + EXPORT_SYMBOL_GPL(ip6_push_pending_frames); + +-void ip6_flush_pending_frames(struct sock *sk) ++static void __ip6_flush_pending_frames(struct sock *sk, ++ struct sk_buff_head *queue) + { + struct sk_buff *skb; + +- while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) { ++ while ((skb = __skb_dequeue_tail(queue)) != NULL) { + if (skb_dst(skb)) + IP6_INC_STATS(sock_net(sk), ip6_dst_idev(skb_dst(skb)), + IPSTATS_MIB_OUTDISCARDS); + kfree_skb(skb); + } + +- ip6_cork_release(inet_sk(sk), inet6_sk(sk)); ++ ip6_cork_release(&inet_sk(sk)->cork, &inet6_sk(sk)->cork); ++} ++ ++void ip6_flush_pending_frames(struct sock *sk) ++{ ++ __ip6_flush_pending_frames(sk, &sk->sk_write_queue); + } + EXPORT_SYMBOL_GPL(ip6_flush_pending_frames); +diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c +index 2459b7b..8d12faa 100644 +--- a/net/ipv6/ip6_vti.c ++++ b/net/ipv6/ip6_vti.c +@@ -555,7 +555,7 @@ static int vti6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + if (type == NDISC_REDIRECT) + ip6_redirect(skb, net, skb->dev->ifindex, 0); + else +- ip6_update_pmtu(skb, net, info, 0, 0); ++ ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); + xfrm_state_put(x); + + return 0; +diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c +index 1b9316e..b247bac 100644 +--- a/net/ipv6/ipcomp6.c ++++ b/net/ipv6/ipcomp6.c +@@ -76,7 +76,7 @@ static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + if (type == NDISC_REDIRECT) + ip6_redirect(skb, net, skb->dev->ifindex, 0); + else +- ip6_update_pmtu(skb, net, info, 0, 0); ++ ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); + xfrm_state_put(x); + + return 0; +diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c +index e1a9583..7f42cd7 100644 +--- a/net/ipv6/ipv6_sockglue.c ++++ b/net/ipv6/ipv6_sockglue.c +@@ -104,16 +104,18 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk, + { + if (inet_sk(sk)->is_icsk) { + if (opt && +- !((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) && +- inet_sk(sk)->inet_daddr != LOOPBACK4_IPV6) { ++ !((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) && ++ inet_sk(sk)->inet_daddr != LOOPBACK4_IPV6) { + struct inet_connection_sock *icsk = inet_csk(sk); + icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen; + icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); + } +- opt = xchg(&inet6_sk(sk)->opt, opt); ++ opt = xchg((__force struct ipv6_txoptions **)&inet6_sk(sk)->opt, ++ opt); + } else { + spin_lock(&sk->sk_dst_lock); +- opt = xchg(&inet6_sk(sk)->opt, opt); ++ opt = xchg((__force struct ipv6_txoptions **)&inet6_sk(sk)->opt, ++ opt); + spin_unlock(&sk->sk_dst_lock); + } + sk_dst_reset(sk); +@@ -213,9 +215,12 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, + sk->sk_socket->ops = &inet_dgram_ops; + sk->sk_family = PF_INET; + } +- opt = xchg(&np->opt, NULL); +- if (opt) +- sock_kfree_s(sk, opt, opt->tot_len); ++ opt = xchg((__force struct ipv6_txoptions **)&np->opt, ++ NULL); ++ if (opt) { ++ atomic_sub(opt->tot_len, &sk->sk_omem_alloc); ++ txopt_put(opt); ++ } + pktopt = xchg(&np->pktoptions, NULL); + kfree_skb(pktopt); + +@@ -385,7 +390,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, + if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW)) + break; + +- opt = ipv6_renew_options(sk, np->opt, optname, ++ opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); ++ opt = ipv6_renew_options(sk, opt, optname, + (struct ipv6_opt_hdr __user *)optval, + optlen); + if (IS_ERR(opt)) { +@@ -414,8 +420,10 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, + retv = 0; + opt = ipv6_update_options(sk, opt); + sticky_done: +- if (opt) +- sock_kfree_s(sk, opt, opt->tot_len); ++ if (opt) { ++ atomic_sub(opt->tot_len, &sk->sk_omem_alloc); ++ txopt_put(opt); ++ } + break; + } + +@@ -468,6 +476,7 @@ sticky_done: + break; + + memset(opt, 0, sizeof(*opt)); ++ atomic_set(&opt->refcnt, 1); + opt->tot_len = sizeof(*opt) + optlen; + retv = -EFAULT; + if (copy_from_user(opt+1, optval, optlen)) +@@ -484,8 +493,10 @@ update: + retv = 0; + opt = ipv6_update_options(sk, opt); + done: +- if (opt) +- sock_kfree_s(sk, opt, opt->tot_len); ++ if (opt) { ++ atomic_sub(opt->tot_len, &sk->sk_omem_alloc); ++ txopt_put(opt); ++ } + break; + } + case IPV6_UNICAST_HOPS: +@@ -1092,10 +1103,11 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, + case IPV6_RTHDR: + case IPV6_DSTOPTS: + { ++ struct ipv6_txoptions *opt; + + lock_sock(sk); +- len = ipv6_getsockopt_sticky(sk, np->opt, +- optname, optval, len); ++ opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); ++ len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len); + release_sock(sk); + /* check if ipv6_getsockopt_sticky() returns err code */ + if (len < 0) +diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c +index 97f41a3..c471baa 100644 +--- a/net/ipv6/output_core.c ++++ b/net/ipv6/output_core.c +@@ -44,15 +44,15 @@ EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident); + + int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) + { +- u16 offset = sizeof(struct ipv6hdr); +- struct ipv6_opt_hdr *exthdr = +- (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1); ++ unsigned int offset = sizeof(struct ipv6hdr); + unsigned int packet_len = skb_tail_pointer(skb) - + skb_network_header(skb); + int found_rhdr = 0; + *nexthdr = &ipv6_hdr(skb)->nexthdr; + +- while (offset + 1 <= packet_len) { ++ while (offset <= packet_len) { ++ struct ipv6_opt_hdr *exthdr; ++ unsigned int len; + + switch (**nexthdr) { + +@@ -73,13 +73,19 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) + return offset; + } + +- offset += ipv6_optlen(exthdr); +- *nexthdr = &exthdr->nexthdr; ++ if (offset + sizeof(struct ipv6_opt_hdr) > packet_len) ++ return -EINVAL; ++ + exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) + + offset); ++ len = ipv6_optlen(exthdr); ++ if (len + offset >= IPV6_MAXPLEN) ++ return -EINVAL; ++ offset += len; ++ *nexthdr = &exthdr->nexthdr; + } + +- return offset; ++ return -EINVAL; + } + EXPORT_SYMBOL(ip6_find_1stfragopt); + +diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c +index 2d452a3..75aa8c1 100644 +--- a/net/ipv6/ping.c ++++ b/net/ipv6/ping.c +@@ -136,6 +136,7 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + fl6.saddr = np->saddr; + fl6.daddr = *daddr; + fl6.flowi6_mark = sk->sk_mark; ++ fl6.flowi6_uid = sock_i_uid(sk); + fl6.fl6_icmp_type = user_icmph.icmp6_type; + fl6.fl6_icmp_code = user_icmph.icmp6_code; + security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); +diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c +index 896af88..bc3459d 100644 +--- a/net/ipv6/raw.c ++++ b/net/ipv6/raw.c +@@ -735,6 +735,7 @@ static int rawv6_probe_proto_opt(struct flowi6 *fl6, struct msghdr *msg) + static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t len) + { ++ struct ipv6_txoptions *opt_to_free = NULL; + struct ipv6_txoptions opt_space; + DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); + struct in6_addr *daddr, *final_p, final; +@@ -768,6 +769,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, + memset(&fl6, 0, sizeof(fl6)); + + fl6.flowi6_mark = sk->sk_mark; ++ fl6.flowi6_uid = sock_i_uid(sk); + + if (sin6) { + if (addr_len < SIN6_LEN_RFC2133) +@@ -840,8 +842,10 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, + if (!(opt->opt_nflen|opt->opt_flen)) + opt = NULL; + } +- if (opt == NULL) +- opt = np->opt; ++ if (!opt) { ++ opt = txopt_get(np); ++ opt_to_free = opt; ++ } + if (flowlabel) + opt = fl6_merge_options(&opt_space, flowlabel, opt); + opt = ipv6_fixup_options(&opt_space, opt); +@@ -902,6 +906,7 @@ done: + dst_release(dst); + out: + fl6_sock_release(flowlabel); ++ txopt_put(opt_to_free); + return err < 0 ? err : len; + do_confirm: + dst_confirm(dst); +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index 2d9aca5..babc601 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -95,13 +95,12 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, + static int rt6_score_route(struct rt6_info *rt, int oif, int strict); + + #ifdef CONFIG_IPV6_ROUTE_INFO +-static struct rt6_info *rt6_add_route_info(struct net *net, ++static struct rt6_info *rt6_add_route_info(struct net_device *dev, + const struct in6_addr *prefix, int prefixlen, +- const struct in6_addr *gwaddr, int ifindex, +- unsigned int pref); +-static struct rt6_info *rt6_get_route_info(struct net *net, ++ const struct in6_addr *gwaddr, unsigned int pref); ++static struct rt6_info *rt6_get_route_info(struct net_device *dev, + const struct in6_addr *prefix, int prefixlen, +- const struct in6_addr *gwaddr, int ifindex); ++ const struct in6_addr *gwaddr); + #endif + + static void rt6_bind_peer(struct rt6_info *rt, int create) +@@ -700,7 +699,6 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) + int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, + const struct in6_addr *gwaddr) + { +- struct net *net = dev_net(dev); + struct route_info *rinfo = (struct route_info *) opt; + struct in6_addr prefix_buf, *prefix; + unsigned int pref; +@@ -745,8 +743,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, + if (rinfo->prefix_len == 0) + rt = rt6_get_dflt_router(gwaddr, dev); + else +- rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, +- gwaddr, dev->ifindex); ++ rt = rt6_get_route_info(dev, prefix, rinfo->prefix_len, gwaddr); + + if (rt && !lifetime) { + ip6_del_rt(rt); +@@ -754,8 +751,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, + } + + if (!rt && lifetime) +- rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, +- pref); ++ rt = rt6_add_route_info(dev, prefix, rinfo->prefix_len, gwaddr, pref); + else if (rt) + rt->rt6i_flags = RTF_ROUTEINFO | + (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); +@@ -1159,7 +1155,7 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, + } + + void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, +- int oif, u32 mark) ++ int oif, u32 mark, kuid_t uid) + { + const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; + struct dst_entry *dst; +@@ -1171,6 +1167,7 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, + fl6.daddr = iph->daddr; + fl6.saddr = iph->saddr; + fl6.flowlabel = ip6_flowinfo(iph); ++ fl6.flowi6_uid = uid; + + dst = ip6_route_output(net, NULL, &fl6); + if (!dst->error) +@@ -1182,7 +1179,7 @@ EXPORT_SYMBOL_GPL(ip6_update_pmtu); + void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) + { + ip6_update_pmtu(skb, sock_net(sk), mtu, +- sk->sk_bound_dev_if, sk->sk_mark); ++ sk->sk_bound_dev_if, sk->sk_mark, sock_i_uid(sk)); + } + EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); + +@@ -1902,15 +1899,16 @@ static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, + } + + #ifdef CONFIG_IPV6_ROUTE_INFO +-static struct rt6_info *rt6_get_route_info(struct net *net, ++static struct rt6_info *rt6_get_route_info(struct net_device *dev, + const struct in6_addr *prefix, int prefixlen, +- const struct in6_addr *gwaddr, int ifindex) ++ const struct in6_addr *gwaddr) + { + struct fib6_node *fn; + struct rt6_info *rt = NULL; + struct fib6_table *table; + +- table = fib6_get_table(net, RT6_TABLE_INFO); ++ table = fib6_get_table(dev_net(dev), ++ addrconf_rt_table(dev, RT6_TABLE_INFO)); + if (!table) + return NULL; + +@@ -1920,7 +1918,7 @@ static struct rt6_info *rt6_get_route_info(struct net *net, + goto out; + + for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { +- if (rt->dst.dev->ifindex != ifindex) ++ if (rt->dst.dev->ifindex != dev->ifindex) + continue; + if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) + continue; +@@ -1934,21 +1932,20 @@ out: + return rt; + } + +-static struct rt6_info *rt6_add_route_info(struct net *net, ++static struct rt6_info *rt6_add_route_info(struct net_device *dev, + const struct in6_addr *prefix, int prefixlen, +- const struct in6_addr *gwaddr, int ifindex, +- unsigned int pref) ++ const struct in6_addr *gwaddr, unsigned int pref) + { + struct fib6_config cfg = { +- .fc_table = RT6_TABLE_INFO, ++ .fc_table = addrconf_rt_table(dev, RT6_TABLE_INFO), + .fc_metric = IP6_RT_PRIO_USER, +- .fc_ifindex = ifindex, ++ .fc_ifindex = dev->ifindex, + .fc_dst_len = prefixlen, + .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | + RTF_UP | RTF_PREF(pref), + .fc_nlinfo.portid = 0, + .fc_nlinfo.nlh = NULL, +- .fc_nlinfo.nl_net = net, ++ .fc_nlinfo.nl_net = dev_net(dev), + }; + + cfg.fc_dst = *prefix; +@@ -1960,7 +1957,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net, + + ip6_route_add(&cfg); + +- return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); ++ return rt6_get_route_info(dev, prefix, prefixlen, gwaddr); + } + #endif + +@@ -1969,7 +1966,8 @@ struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_dev + struct rt6_info *rt; + struct fib6_table *table; + +- table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); ++ table = fib6_get_table(dev_net(dev), ++ addrconf_rt_table(dev, RT6_TABLE_MAIN)); + if (!table) + return NULL; + +@@ -1991,7 +1989,7 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, + unsigned int pref) + { + struct fib6_config cfg = { +- .fc_table = RT6_TABLE_DFLT, ++ .fc_table = addrconf_rt_table(dev, RT6_TABLE_DFLT), + .fc_metric = IP6_RT_PRIO_USER, + .fc_ifindex = dev->ifindex, + .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | +@@ -2008,28 +2006,17 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, + return rt6_get_dflt_router(gwaddr, dev); + } + +-void rt6_purge_dflt_routers(struct net *net) +-{ +- struct rt6_info *rt; +- struct fib6_table *table; + +- /* NOTE: Keep consistent with rt6_get_dflt_router */ +- table = fib6_get_table(net, RT6_TABLE_DFLT); +- if (!table) +- return; ++int rt6_addrconf_purge(struct rt6_info *rt, void *arg) { ++ if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && ++ (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) ++ return -1; ++ return 0; ++} + +-restart: +- read_lock_bh(&table->tb6_lock); +- for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { +- if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && +- (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { +- dst_hold(&rt->dst); +- read_unlock_bh(&table->tb6_lock); +- ip6_del_rt(rt); +- goto restart; +- } +- } +- read_unlock_bh(&table->tb6_lock); ++void rt6_purge_dflt_routers(struct net *net) ++{ ++ fib6_clean_all(net, rt6_addrconf_purge, NULL); + } + + static void rtmsg_to_fib6_config(struct net *net, +@@ -2335,6 +2322,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { + [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_METRICS] = { .type = NLA_NESTED }, + [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, ++ [RTA_UID] = { .type = NLA_U32 }, + }; + + static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, +@@ -2724,6 +2712,11 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) + if (tb[RTA_MARK]) + fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); + ++ if (tb[RTA_UID]) ++ fl6.flowi6_uid = make_kuid(current_user_ns(), ++ nla_get_u32(tb[RTA_UID])); ++ else ++ fl6.flowi6_uid = iif ? INVALID_UID : current_uid(); + if (iif) { + struct net_device *dev; + int flags = 0; +diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c +index 2f25cb6..b52a246 100644 +--- a/net/ipv6/syncookies.c ++++ b/net/ipv6/syncookies.c +@@ -241,12 +241,13 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) + memset(&fl6, 0, sizeof(fl6)); + fl6.flowi6_proto = IPPROTO_TCP; + fl6.daddr = ireq->ir_v6_rmt_addr; +- final_p = fl6_update_dst(&fl6, np->opt, &final); ++ final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final); + fl6.saddr = ireq->ir_v6_loc_addr; + fl6.flowi6_oif = sk->sk_bound_dev_if; + fl6.flowi6_mark = ireq->ir_mark; + fl6.fl6_dport = ireq->ir_rmt_port; + fl6.fl6_sport = inet_sk(sk)->inet_sport; ++ fl6.flowi6_uid = sock_i_uid(sk); + security_req_classify_flow(req, flowi6_to_flowi(&fl6)); + + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); +diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c +index a3f9f11..b8b9ad2 100644 +--- a/net/ipv6/tcp_ipv6.c ++++ b/net/ipv6/tcp_ipv6.c +@@ -134,6 +134,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, + struct ipv6_pinfo *np = inet6_sk(sk); + struct tcp_sock *tp = tcp_sk(sk); + struct in6_addr *saddr = NULL, *final_p, final; ++ struct ipv6_txoptions *opt; + struct rt6_info *rt; + struct flowi6 fl6; + struct dst_entry *dst; +@@ -252,8 +253,10 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, + fl6.flowi6_mark = sk->sk_mark; + fl6.fl6_dport = usin->sin6_port; + fl6.fl6_sport = inet->inet_sport; ++ fl6.flowi6_uid = sock_i_uid(sk); + +- final_p = fl6_update_dst(&fl6, np->opt, &final); ++ opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); ++ final_p = fl6_update_dst(&fl6, opt, &final); + + security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); + +@@ -282,9 +285,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, + tcp_fetch_timewait_stamp(sk, dst); + + icsk->icsk_ext_hdr_len = 0; +- if (np->opt) +- icsk->icsk_ext_hdr_len = (np->opt->opt_flen + +- np->opt->opt_nflen); ++ if (opt) ++ icsk->icsk_ext_hdr_len = opt->opt_flen + ++ opt->opt_nflen; + + tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); + +@@ -501,7 +504,8 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, + fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts)); + + skb_set_queue_mapping(skb, queue_mapping); +- err = ip6_xmit(sk, skb, fl6, np->opt, np->tclass); ++ err = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt), ++ np->tclass); + err = net_xmit_eval(err); + } + +@@ -1052,6 +1056,7 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, + struct inet_request_sock *ireq; + struct ipv6_pinfo *newnp, *np = inet6_sk(sk); + struct tcp6_sock *newtcp6sk; ++ struct ipv6_txoptions *opt; + struct inet_sock *newinet; + struct tcp_sock *newtp; + struct sock *newsk; +@@ -1091,6 +1096,7 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, + newtp->af_specific = &tcp_sock_ipv6_mapped_specific; + #endif + ++ newnp->ipv6_mc_list = NULL; + newnp->ipv6_ac_list = NULL; + newnp->ipv6_fl_list = NULL; + newnp->pktoptions = NULL; +@@ -1162,6 +1168,7 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, + First: no IPv4 options. + */ + newinet->inet_opt = NULL; ++ newnp->ipv6_mc_list = NULL; + newnp->ipv6_ac_list = NULL; + newnp->ipv6_fl_list = NULL; + +@@ -1191,13 +1198,15 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, + but we make one more one thing there: reattach optmem + to newsk. + */ +- if (np->opt) +- newnp->opt = ipv6_dup_options(newsk, np->opt); +- ++ opt = rcu_dereference(np->opt); ++ if (opt) { ++ opt = ipv6_dup_options(newsk, opt); ++ RCU_INIT_POINTER(newnp->opt, opt); ++ } + inet_csk(newsk)->icsk_ext_hdr_len = 0; +- if (newnp->opt) +- inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen + +- newnp->opt->opt_flen); ++ if (opt) ++ inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen + ++ opt->opt_flen; + + tcp_sync_mss(newsk, dst_mtu(dst)); + newtp->advmss = dst_metric_advmss(dst); +@@ -1268,7 +1277,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) + if (skb->protocol == htons(ETH_P_IP)) + return tcp_v4_do_rcv(sk, skb); + +- if (sk_filter(sk, skb)) ++ if (tcp_filter(sk, skb)) + goto discard; + + /* +@@ -1472,8 +1481,10 @@ process: + goto discard_and_relse; + #endif + +- if (sk_filter(sk, skb)) ++ if (tcp_filter(sk, skb)) + goto discard_and_relse; ++ th = (const struct tcphdr *)skb->data; ++ hdr = ipv6_hdr(skb); + + sk_mark_napi_id(sk, skb); + skb->dev = NULL; +diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c +index dd530f0..3e2ff3d 100644 +--- a/net/ipv6/udp.c ++++ b/net/ipv6/udp.c +@@ -388,6 +388,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, + int peeked, off = 0; + int err; + int is_udplite = IS_UDPLITE(sk); ++ bool checksum_valid = false; + int is_udp4; + bool slow; + +@@ -419,11 +420,12 @@ try_again: + */ + + if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) { +- if (udp_lib_checksum_complete(skb)) ++ checksum_valid = !udp_lib_checksum_complete(skb); ++ if (!checksum_valid) + goto csum_copy_err; + } + +- if (skb_csum_unnecessary(skb)) ++ if (checksum_valid || skb_csum_unnecessary(skb)) + err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), + msg->msg_iov, copied); + else { +@@ -1082,6 +1084,7 @@ int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, + DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); + struct in6_addr *daddr, *final_p, final; + struct ipv6_txoptions *opt = NULL; ++ struct ipv6_txoptions *opt_to_free = NULL; + struct ip6_flowlabel *flowlabel = NULL; + struct flowi6 fl6; + struct dst_entry *dst; +@@ -1213,6 +1216,7 @@ do_udp_sendmsg: + fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; + + fl6.flowi6_mark = sk->sk_mark; ++ fl6.flowi6_uid = sock_i_uid(sk); + + if (msg->msg_controllen) { + opt = &opt_space; +@@ -1234,8 +1238,10 @@ do_udp_sendmsg: + opt = NULL; + connected = 0; + } +- if (opt == NULL) +- opt = np->opt; ++ if (!opt) { ++ opt = txopt_get(np); ++ opt_to_free = opt; ++ } + if (flowlabel) + opt = fl6_merge_options(&opt_space, flowlabel, opt); + opt = ipv6_fixup_options(&opt_space, opt); +@@ -1329,6 +1335,7 @@ do_append_data: + out: + dst_release(dst); + fl6_sock_release(flowlabel); ++ txopt_put(opt_to_free); + if (!err) + return len; + /* +diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c +index 6b8f543..b032886 100644 +--- a/net/ipv6/udp_offload.c ++++ b/net/ipv6/udp_offload.c +@@ -94,6 +94,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, + * bytes to insert fragment header. + */ + unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); ++ if (unfrag_ip6hlen < 0) ++ return ERR_PTR(unfrag_ip6hlen); + nexthdr = *prevhdr; + *prevhdr = NEXTHDR_FRAGMENT; + unfrag_len = (skb_network_header(skb) - skb_mac_header(skb)) + +diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c +index 3f3a6cb..b3f6ec0 100644 +--- a/net/irda/af_irda.c ++++ b/net/irda/af_irda.c +@@ -1100,6 +1100,9 @@ static int irda_create(struct net *net, struct socket *sock, int protocol, + + IRDA_DEBUG(2, "%s()\n", __func__); + ++ if (protocol < 0 || protocol > SK_PROTOCOL_MAX) ++ return -EINVAL; ++ + if (net != &init_net) + return -EAFNOSUPPORT; + +diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c +index 0edb263..3865882 100644 +--- a/net/l2tp/l2tp_ip6.c ++++ b/net/l2tp/l2tp_ip6.c +@@ -487,6 +487,7 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk, + DECLARE_SOCKADDR(struct sockaddr_l2tpip6 *, lsa, msg->msg_name); + struct in6_addr *daddr, *final_p, final; + struct ipv6_pinfo *np = inet6_sk(sk); ++ struct ipv6_txoptions *opt_to_free = NULL; + struct ipv6_txoptions *opt = NULL; + struct ip6_flowlabel *flowlabel = NULL; + struct dst_entry *dst = NULL; +@@ -576,8 +577,10 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk, + opt = NULL; + } + +- if (opt == NULL) +- opt = np->opt; ++ if (!opt) { ++ opt = txopt_get(np); ++ opt_to_free = opt; ++ } + if (flowlabel) + opt = fl6_merge_options(&opt_space, flowlabel, opt); + opt = ipv6_fixup_options(&opt_space, opt); +@@ -632,6 +635,7 @@ done: + dst_release(dst); + out: + fl6_sock_release(flowlabel); ++ txopt_put(opt_to_free); + + return err < 0 ? err : len; + +diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig +index ae5096a..6b099d1 100644 +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -1255,6 +1255,8 @@ config NETFILTER_XT_MATCH_OWNER + based on who created the socket: the user or group. It is also + possible to check whether a socket actually exists. + ++ Conflicts with '"quota, tag, uid" match' ++ + config NETFILTER_XT_MATCH_POLICY + tristate 'IPsec "policy" match support' + depends on XFRM +@@ -1288,6 +1290,22 @@ config NETFILTER_XT_MATCH_PKTTYPE + + To compile it as a module, choose M here. If unsure, say N. + ++config NETFILTER_XT_MATCH_QTAGUID ++ bool '"quota, tag, owner" match and stats support' ++ depends on NETFILTER_XT_MATCH_SOCKET ++ depends on NETFILTER_XT_MATCH_OWNER=n ++ help ++ This option replaces the `owner' match. In addition to matching ++ on uid, it keeps stats based on a tag assigned to a socket. ++ The full tag is comprised of a UID and an accounting tag. ++ The tags are assignable to sockets from user space (e.g. a download ++ manager can assign the socket to another UID for accounting). ++ Stats and control are done via /proc/net/xt_qtaguid/. ++ It replaces owner as it takes the same arguments, but should ++ really be recognized by the iptables tool. ++ ++ If unsure, say `N'. ++ + config NETFILTER_XT_MATCH_QUOTA + tristate '"quota" match support' + depends on NETFILTER_ADVANCED +@@ -1298,6 +1316,30 @@ config NETFILTER_XT_MATCH_QUOTA + If you want to compile it as a module, say M here and read + <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. + ++config NETFILTER_XT_MATCH_QUOTA2 ++ tristate '"quota2" match support' ++ depends on NETFILTER_ADVANCED ++ help ++ This option adds a `quota2' match, which allows to match on a ++ byte counter correctly and not per CPU. ++ It allows naming the quotas. ++ This is based on http://xtables-addons.git.sourceforge.net ++ ++ If you want to compile it as a module, say M here and read ++ <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. ++ ++config NETFILTER_XT_MATCH_QUOTA2_LOG ++ bool '"quota2" Netfilter LOG support' ++ depends on NETFILTER_XT_MATCH_QUOTA2 ++ depends on IP_NF_TARGET_ULOG=n # not yes, not module, just no ++ default n ++ help ++ This option allows `quota2' to log ONCE when a quota limit ++ is passed. It logs via NETLINK using the NETLINK_NFLOG family. ++ It logs similarly to how ipt_ULOG would without data. ++ ++ If unsure, say `N'. ++ + config NETFILTER_XT_MATCH_RATEEST + tristate '"rateest" match support' + depends on NETFILTER_ADVANCED +diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile +index a9571be..c07bfc8 100644 +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -155,7 +155,9 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_CGROUP) += xt_cgroup.o + obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o + obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o + obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o ++obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid_print.o xt_qtaguid.o + obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o ++obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA2) += xt_quota2.o + obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o + obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o + obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o +diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c +index f407ebc..0975c99 100644 +--- a/net/netfilter/xt_IDLETIMER.c ++++ b/net/netfilter/xt_IDLETIMER.c +@@ -5,6 +5,7 @@ + * After timer expires a kevent will be sent. + * + * Copyright (C) 2004, 2010 Nokia Corporation ++ * + * Written by Timo Teras <ext-timo.teras@nokia.com> + * + * Converted to x_tables and reworked for upstream inclusion +@@ -38,8 +39,16 @@ + #include <linux/netfilter/xt_IDLETIMER.h> + #include <linux/kdev_t.h> + #include <linux/kobject.h> ++#include <linux/skbuff.h> + #include <linux/workqueue.h> + #include <linux/sysfs.h> ++#include <linux/rtc.h> ++#include <linux/time.h> ++#include <linux/math64.h> ++#include <linux/suspend.h> ++#include <linux/notifier.h> ++#include <net/net_namespace.h> ++#include <net/sock.h> + + struct idletimer_tg_attr { + struct attribute attr; +@@ -55,14 +64,110 @@ struct idletimer_tg { + struct kobject *kobj; + struct idletimer_tg_attr attr; + ++ struct timespec delayed_timer_trigger; ++ struct timespec last_modified_timer; ++ struct timespec last_suspend_time; ++ struct notifier_block pm_nb; ++ ++ int timeout; + unsigned int refcnt; ++ bool work_pending; ++ bool send_nl_msg; ++ bool active; ++ uid_t uid; + }; + + static LIST_HEAD(idletimer_tg_list); + static DEFINE_MUTEX(list_mutex); ++static DEFINE_SPINLOCK(timestamp_lock); + + static struct kobject *idletimer_tg_kobj; + ++static bool check_for_delayed_trigger(struct idletimer_tg *timer, ++ struct timespec *ts) ++{ ++ bool state; ++ struct timespec temp; ++ spin_lock_bh(×tamp_lock); ++ timer->work_pending = false; ++ if ((ts->tv_sec - timer->last_modified_timer.tv_sec) > timer->timeout || ++ timer->delayed_timer_trigger.tv_sec != 0) { ++ state = false; ++ temp.tv_sec = timer->timeout; ++ temp.tv_nsec = 0; ++ if (timer->delayed_timer_trigger.tv_sec != 0) { ++ temp = timespec_add(timer->delayed_timer_trigger, temp); ++ ts->tv_sec = temp.tv_sec; ++ ts->tv_nsec = temp.tv_nsec; ++ timer->delayed_timer_trigger.tv_sec = 0; ++ timer->work_pending = true; ++ schedule_work(&timer->work); ++ } else { ++ temp = timespec_add(timer->last_modified_timer, temp); ++ ts->tv_sec = temp.tv_sec; ++ ts->tv_nsec = temp.tv_nsec; ++ } ++ } else { ++ state = timer->active; ++ } ++ spin_unlock_bh(×tamp_lock); ++ return state; ++} ++ ++static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) ++{ ++ char iface_msg[NLMSG_MAX_SIZE]; ++ char state_msg[NLMSG_MAX_SIZE]; ++ char timestamp_msg[NLMSG_MAX_SIZE]; ++ char uid_msg[NLMSG_MAX_SIZE]; ++ char *envp[] = { iface_msg, state_msg, timestamp_msg, uid_msg, NULL }; ++ int res; ++ struct timespec ts; ++ uint64_t time_ns; ++ bool state; ++ ++ res = snprintf(iface_msg, NLMSG_MAX_SIZE, "INTERFACE=%s", ++ iface); ++ if (NLMSG_MAX_SIZE <= res) { ++ pr_err("message too long (%d)", res); ++ return; ++ } ++ ++ get_monotonic_boottime(&ts); ++ state = check_for_delayed_trigger(timer, &ts); ++ res = snprintf(state_msg, NLMSG_MAX_SIZE, "STATE=%s", ++ state ? "active" : "inactive"); ++ ++ if (NLMSG_MAX_SIZE <= res) { ++ pr_err("message too long (%d)", res); ++ return; ++ } ++ ++ if (state) { ++ res = snprintf(uid_msg, NLMSG_MAX_SIZE, "UID=%u", timer->uid); ++ if (NLMSG_MAX_SIZE <= res) ++ pr_err("message too long (%d)", res); ++ } else { ++ res = snprintf(uid_msg, NLMSG_MAX_SIZE, "UID="); ++ if (NLMSG_MAX_SIZE <= res) ++ pr_err("message too long (%d)", res); ++ } ++ ++ time_ns = timespec_to_ns(&ts); ++ res = snprintf(timestamp_msg, NLMSG_MAX_SIZE, "TIME_NS=%llu", time_ns); ++ if (NLMSG_MAX_SIZE <= res) { ++ timestamp_msg[0] = '\0'; ++ pr_err("message too long (%d)", res); ++ } ++ ++ pr_debug("putting nlmsg: <%s> <%s> <%s> <%s>\n", iface_msg, state_msg, ++ timestamp_msg, uid_msg); ++ kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); ++ return; ++ ++ ++} ++ + static + struct idletimer_tg *__idletimer_tg_find_by_label(const char *label) + { +@@ -83,6 +188,7 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, + { + struct idletimer_tg *timer; + unsigned long expires = 0; ++ unsigned long now = jiffies; + + mutex_lock(&list_mutex); + +@@ -92,11 +198,15 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, + + mutex_unlock(&list_mutex); + +- if (time_after(expires, jiffies)) ++ if (time_after(expires, now)) + return sprintf(buf, "%u\n", +- jiffies_to_msecs(expires - jiffies) / 1000); ++ jiffies_to_msecs(expires - now) / 1000); + +- return sprintf(buf, "0\n"); ++ if (timer->send_nl_msg) ++ return sprintf(buf, "0 %d\n", ++ jiffies_to_msecs(now - expires) / 1000); ++ else ++ return sprintf(buf, "0\n"); + } + + static void idletimer_tg_work(struct work_struct *work) +@@ -105,6 +215,9 @@ static void idletimer_tg_work(struct work_struct *work) + work); + + sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name); ++ ++ if (timer->send_nl_msg) ++ notify_netlink_uevent(timer->attr.attr.name, timer); + } + + static void idletimer_tg_expired(unsigned long data) +@@ -112,8 +225,55 @@ static void idletimer_tg_expired(unsigned long data) + struct idletimer_tg *timer = (struct idletimer_tg *) data; + + pr_debug("timer %s expired\n", timer->attr.attr.name); +- ++ spin_lock_bh(×tamp_lock); ++ timer->active = false; ++ timer->work_pending = true; + schedule_work(&timer->work); ++ spin_unlock_bh(×tamp_lock); ++} ++ ++static int idletimer_resume(struct notifier_block *notifier, ++ unsigned long pm_event, void *unused) ++{ ++ struct timespec ts; ++ unsigned long time_diff, now = jiffies; ++ struct idletimer_tg *timer = container_of(notifier, ++ struct idletimer_tg, pm_nb); ++ if (!timer) ++ return NOTIFY_DONE; ++ switch (pm_event) { ++ case PM_SUSPEND_PREPARE: ++ get_monotonic_boottime(&timer->last_suspend_time); ++ break; ++ case PM_POST_SUSPEND: ++ spin_lock_bh(×tamp_lock); ++ if (!timer->active) { ++ spin_unlock_bh(×tamp_lock); ++ break; ++ } ++ /* since jiffies are not updated when suspended now represents ++ * the time it would have suspended */ ++ if (time_after(timer->timer.expires, now)) { ++ get_monotonic_boottime(&ts); ++ ts = timespec_sub(ts, timer->last_suspend_time); ++ time_diff = timespec_to_jiffies(&ts); ++ if (timer->timer.expires > (time_diff + now)) { ++ mod_timer_pending(&timer->timer, ++ (timer->timer.expires - time_diff)); ++ } else { ++ del_timer(&timer->timer); ++ timer->timer.expires = 0; ++ timer->active = false; ++ timer->work_pending = true; ++ schedule_work(&timer->work); ++ } ++ } ++ spin_unlock_bh(×tamp_lock); ++ break; ++ default: ++ break; ++ } ++ return NOTIFY_DONE; + } + + static int idletimer_tg_create(struct idletimer_tg_info *info) +@@ -126,6 +286,7 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) + goto out; + } + ++ sysfs_attr_init(&info->timer->attr.attr); + info->timer->attr.attr.name = kstrdup(info->label, GFP_KERNEL); + if (!info->timer->attr.attr.name) { + ret = -ENOMEM; +@@ -145,6 +306,21 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) + setup_timer(&info->timer->timer, idletimer_tg_expired, + (unsigned long) info->timer); + info->timer->refcnt = 1; ++ info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true; ++ info->timer->active = true; ++ info->timer->timeout = info->timeout; ++ ++ info->timer->delayed_timer_trigger.tv_sec = 0; ++ info->timer->delayed_timer_trigger.tv_nsec = 0; ++ info->timer->work_pending = false; ++ info->timer->uid = 0; ++ get_monotonic_boottime(&info->timer->last_modified_timer); ++ ++ info->timer->pm_nb.notifier_call = idletimer_resume; ++ ret = register_pm_notifier(&info->timer->pm_nb); ++ if (ret) ++ printk(KERN_WARNING "[%s] Failed to register pm notifier %d\n", ++ __func__, ret); + + mod_timer(&info->timer->timer, + msecs_to_jiffies(info->timeout * 1000) + jiffies); +@@ -161,6 +337,42 @@ out: + return ret; + } + ++static void reset_timer(const struct idletimer_tg_info *info, ++ struct sk_buff *skb) ++{ ++ unsigned long now = jiffies; ++ struct idletimer_tg *timer = info->timer; ++ bool timer_prev; ++ ++ spin_lock_bh(×tamp_lock); ++ timer_prev = timer->active; ++ timer->active = true; ++ /* timer_prev is used to guard overflow problem in time_before*/ ++ if (!timer_prev || time_before(timer->timer.expires, now)) { ++ pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", ++ timer->timer.expires, now); ++ ++ /* Stores the uid resposible for waking up the radio */ ++ if (skb && (skb->sk)) { ++ timer->uid = from_kuid_munged(current_user_ns(), ++ sock_i_uid(skb->sk)); ++ } ++ ++ /* checks if there is a pending inactive notification*/ ++ if (timer->work_pending) ++ timer->delayed_timer_trigger = timer->last_modified_timer; ++ else { ++ timer->work_pending = true; ++ schedule_work(&timer->work); ++ } ++ } ++ ++ get_monotonic_boottime(&timer->last_modified_timer); ++ mod_timer(&timer->timer, ++ msecs_to_jiffies(info->timeout * 1000) + now); ++ spin_unlock_bh(×tamp_lock); ++} ++ + /* + * The actual xt_tables plugin. + */ +@@ -168,15 +380,23 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, + const struct xt_action_param *par) + { + const struct idletimer_tg_info *info = par->targinfo; ++ unsigned long now = jiffies; + + pr_debug("resetting timer %s, timeout period %u\n", + info->label, info->timeout); + + BUG_ON(!info->timer); + +- mod_timer(&info->timer->timer, +- msecs_to_jiffies(info->timeout * 1000) + jiffies); ++ info->timer->active = true; + ++ if (time_before(info->timer->timer.expires, now)) { ++ schedule_work(&info->timer->work); ++ pr_debug("Starting timer %s (Expired, Jiffies): %lu, %lu\n", ++ info->label, info->timer->timer.expires, now); ++ } ++ ++ /* TODO: Avoid modifying timers on each packet */ ++ reset_timer(info, skb); + return XT_CONTINUE; + } + +@@ -185,7 +405,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) + struct idletimer_tg_info *info = par->targinfo; + int ret; + +- pr_debug("checkentry targinfo%s\n", info->label); ++ pr_debug("checkentry targinfo %s\n", info->label); + + if (info->timeout == 0) { + pr_debug("timeout value is zero\n"); +@@ -204,9 +424,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) + info->timer = __idletimer_tg_find_by_label(info->label); + if (info->timer) { + info->timer->refcnt++; +- mod_timer(&info->timer->timer, +- msecs_to_jiffies(info->timeout * 1000) + jiffies); +- ++ reset_timer(info, NULL); + pr_debug("increased refcnt of timer %s to %u\n", + info->label, info->timer->refcnt); + } else { +@@ -219,6 +437,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) + } + + mutex_unlock(&list_mutex); ++ + return 0; + } + +@@ -236,11 +455,12 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) + list_del(&info->timer->entry); + del_timer_sync(&info->timer->timer); + sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr); ++ unregister_pm_notifier(&info->timer->pm_nb); + kfree(info->timer->attr.attr.name); + kfree(info->timer); + } else { + pr_debug("decreased refcnt of timer %s to %u\n", +- info->label, info->timer->refcnt); ++ info->label, info->timer->refcnt); + } + + mutex_unlock(&list_mutex); +@@ -248,6 +468,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) + + static struct xt_target idletimer_tg __read_mostly = { + .name = "IDLETIMER", ++ .revision = 1, + .family = NFPROTO_UNSPEC, + .target = idletimer_tg_target, + .targetsize = sizeof(struct idletimer_tg_info), +@@ -313,3 +534,4 @@ MODULE_DESCRIPTION("Xtables: idle time monitor"); + MODULE_LICENSE("GPL v2"); + MODULE_ALIAS("ipt_IDLETIMER"); + MODULE_ALIAS("ip6t_IDLETIMER"); ++MODULE_ALIAS("arpt_IDLETIMER"); +diff --git a/net/netfilter/xt_hl.c b/net/netfilter/xt_hl.c +index 0039511..1535e87 100644 +--- a/net/netfilter/xt_hl.c ++++ b/net/netfilter/xt_hl.c +@@ -1,96 +1,169 @@ + /* +- * IP tables module for matching the value of the TTL +- * (C) 2000,2001 by Harald Welte <laforge@netfilter.org> ++ * TTL modification target for IP tables ++ * (C) 2000,2005 by Harald Welte <laforge@netfilter.org> + * +- * Hop Limit matching module +- * (C) 2001-2002 Maciej Soltysiak <solt@dns.toxicfilms.tv> ++ * Hop Limit modification target for ip6tables ++ * Maciej Soltysiak <solt@dns.toxicfilms.tv> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +- +-#include <linux/ip.h> +-#include <linux/ipv6.h> ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> + #include <linux/skbuff.h> ++#include <linux/ip.h> ++#include <linux/ipv6.h> ++#include <net/checksum.h> + + #include <linux/netfilter/x_tables.h> +-#include <linux/netfilter_ipv4/ipt_ttl.h> +-#include <linux/netfilter_ipv6/ip6t_hl.h> ++#include <linux/netfilter_ipv4/ipt_TTL.h> ++#include <linux/netfilter_ipv6/ip6t_HL.h> + ++MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); + MODULE_AUTHOR("Maciej Soltysiak <solt@dns.toxicfilms.tv>"); +-MODULE_DESCRIPTION("Xtables: Hoplimit/TTL field match"); ++MODULE_DESCRIPTION("Xtables: Hoplimit/TTL Limit field modification target"); + MODULE_LICENSE("GPL"); +-MODULE_ALIAS("ipt_ttl"); +-MODULE_ALIAS("ip6t_hl"); + +-static bool ttl_mt(const struct sk_buff *skb, struct xt_action_param *par) ++static unsigned int ++ttl_tg(struct sk_buff *skb, const struct xt_action_param *par) + { +- const struct ipt_ttl_info *info = par->matchinfo; +- const u8 ttl = ip_hdr(skb)->ttl; ++ struct iphdr *iph; ++ const struct ipt_TTL_info *info = par->targinfo; ++ int new_ttl; ++ ++ if (!skb_make_writable(skb, skb->len)) ++ return NF_DROP; ++ ++ iph = ip_hdr(skb); + + switch (info->mode) { +- case IPT_TTL_EQ: +- return ttl == info->ttl; +- case IPT_TTL_NE: +- return ttl != info->ttl; +- case IPT_TTL_LT: +- return ttl < info->ttl; +- case IPT_TTL_GT: +- return ttl > info->ttl; ++ case IPT_TTL_SET: ++ new_ttl = info->ttl; ++ break; ++ case IPT_TTL_INC: ++ new_ttl = iph->ttl + info->ttl; ++ if (new_ttl > 255) ++ new_ttl = 255; ++ break; ++ case IPT_TTL_DEC: ++ new_ttl = iph->ttl - info->ttl; ++ if (new_ttl < 0) ++ new_ttl = 0; ++ break; ++ default: ++ new_ttl = iph->ttl; ++ break; ++ } ++ ++ if (new_ttl != iph->ttl) { ++ csum_replace2(&iph->check, htons(iph->ttl << 8), ++ htons(new_ttl << 8)); ++ iph->ttl = new_ttl; + } + +- return false; ++ return XT_CONTINUE; + } + +-static bool hl_mt6(const struct sk_buff *skb, struct xt_action_param *par) ++static unsigned int ++hl_tg6(struct sk_buff *skb, const struct xt_action_param *par) + { +- const struct ip6t_hl_info *info = par->matchinfo; +- const struct ipv6hdr *ip6h = ipv6_hdr(skb); ++ struct ipv6hdr *ip6h; ++ const struct ip6t_HL_info *info = par->targinfo; ++ int new_hl; ++ ++ if (!skb_make_writable(skb, skb->len)) ++ return NF_DROP; ++ ++ ip6h = ipv6_hdr(skb); + + switch (info->mode) { +- case IP6T_HL_EQ: +- return ip6h->hop_limit == info->hop_limit; +- case IP6T_HL_NE: +- return ip6h->hop_limit != info->hop_limit; +- case IP6T_HL_LT: +- return ip6h->hop_limit < info->hop_limit; +- case IP6T_HL_GT: +- return ip6h->hop_limit > info->hop_limit; ++ case IP6T_HL_SET: ++ new_hl = info->hop_limit; ++ break; ++ case IP6T_HL_INC: ++ new_hl = ip6h->hop_limit + info->hop_limit; ++ if (new_hl > 255) ++ new_hl = 255; ++ break; ++ case IP6T_HL_DEC: ++ new_hl = ip6h->hop_limit - info->hop_limit; ++ if (new_hl < 0) ++ new_hl = 0; ++ break; ++ default: ++ new_hl = ip6h->hop_limit; ++ break; + } + +- return false; ++ ip6h->hop_limit = new_hl; ++ ++ return XT_CONTINUE; ++} ++ ++static int ttl_tg_check(const struct xt_tgchk_param *par) ++{ ++ const struct ipt_TTL_info *info = par->targinfo; ++ ++ if (info->mode > IPT_TTL_MAXMODE) { ++ pr_info("TTL: invalid or unknown mode %u\n", info->mode); ++ return -EINVAL; ++ } ++ if (info->mode != IPT_TTL_SET && info->ttl == 0) ++ return -EINVAL; ++ return 0; ++} ++ ++static int hl_tg6_check(const struct xt_tgchk_param *par) ++{ ++ const struct ip6t_HL_info *info = par->targinfo; ++ ++ if (info->mode > IP6T_HL_MAXMODE) { ++ pr_info("invalid or unknown mode %u\n", info->mode); ++ return -EINVAL; ++ } ++ if (info->mode != IP6T_HL_SET && info->hop_limit == 0) { ++ pr_info("increment/decrement does not " ++ "make sense with value 0\n"); ++ return -EINVAL; ++ } ++ return 0; + } + +-static struct xt_match hl_mt_reg[] __read_mostly = { ++static struct xt_target hl_tg_reg[] __read_mostly = { + { +- .name = "ttl", ++ .name = "TTL", + .revision = 0, + .family = NFPROTO_IPV4, +- .match = ttl_mt, +- .matchsize = sizeof(struct ipt_ttl_info), ++ .target = ttl_tg, ++ .targetsize = sizeof(struct ipt_TTL_info), ++ .table = "mangle", ++ .checkentry = ttl_tg_check, + .me = THIS_MODULE, + }, + { +- .name = "hl", ++ .name = "HL", + .revision = 0, + .family = NFPROTO_IPV6, +- .match = hl_mt6, +- .matchsize = sizeof(struct ip6t_hl_info), ++ .target = hl_tg6, ++ .targetsize = sizeof(struct ip6t_HL_info), ++ .table = "mangle", ++ .checkentry = hl_tg6_check, + .me = THIS_MODULE, + }, + }; + +-static int __init hl_mt_init(void) ++static int __init hl_tg_init(void) + { +- return xt_register_matches(hl_mt_reg, ARRAY_SIZE(hl_mt_reg)); ++ return xt_register_targets(hl_tg_reg, ARRAY_SIZE(hl_tg_reg)); + } + +-static void __exit hl_mt_exit(void) ++static void __exit hl_tg_exit(void) + { +- xt_unregister_matches(hl_mt_reg, ARRAY_SIZE(hl_mt_reg)); ++ xt_unregister_targets(hl_tg_reg, ARRAY_SIZE(hl_tg_reg)); + } + +-module_init(hl_mt_init); +-module_exit(hl_mt_exit); ++module_init(hl_tg_init); ++module_exit(hl_tg_exit); ++MODULE_ALIAS("ipt_TTL"); ++MODULE_ALIAS("ip6t_HL"); +diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c +new file mode 100644 +index 0000000..87603f6 +--- /dev/null ++++ b/net/netfilter/xt_qtaguid.c +@@ -0,0 +1,3033 @@ ++/* ++ * Kernel iptables module to track stats for packets based on user tags. ++ * ++ * (C) 2011 Google, Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * There are run-time debug flags enabled via the debug_mask module param, or ++ * via the DEFAULT_DEBUG_MASK. See xt_qtaguid_internal.h. ++ */ ++#define DEBUG ++ ++#include <linux/file.h> ++#include <linux/inetdevice.h> ++#include <linux/module.h> ++#include <linux/netfilter/x_tables.h> ++#include <linux/netfilter/xt_qtaguid.h> ++#include <linux/ratelimit.h> ++#include <linux/seq_file.h> ++#include <linux/skbuff.h> ++#include <linux/workqueue.h> ++#include <net/addrconf.h> ++#include <net/sock.h> ++#include <net/tcp.h> ++#include <net/udp.h> ++ ++#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) ++#include <linux/netfilter_ipv6/ip6_tables.h> ++#endif ++ ++#include <linux/netfilter/xt_socket.h> ++#include "xt_qtaguid_internal.h" ++#include "xt_qtaguid_print.h" ++#include "../../fs/proc/internal.h" ++ ++/* ++ * We only use the xt_socket funcs within a similar context to avoid unexpected ++ * return values. ++ */ ++#define XT_SOCKET_SUPPORTED_HOOKS \ ++ ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) ++ ++ ++static const char *module_procdirname = "xt_qtaguid"; ++static struct proc_dir_entry *xt_qtaguid_procdir; ++ ++static unsigned int proc_iface_perms = S_IRUGO; ++module_param_named(iface_perms, proc_iface_perms, uint, S_IRUGO | S_IWUSR); ++ ++static struct proc_dir_entry *xt_qtaguid_stats_file; ++static unsigned int proc_stats_perms = S_IRUGO; ++module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR); ++ ++static struct proc_dir_entry *xt_qtaguid_ctrl_file; ++ ++/* Everybody can write. But proc_ctrl_write_limited is true by default which ++ * limits what can be controlled. See the can_*() functions. ++ */ ++static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO; ++module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR); ++ ++/* Limited by default, so the gid of the ctrl and stats proc entries ++ * will limit what can be done. See the can_*() functions. ++ */ ++static bool proc_stats_readall_limited = true; ++static bool proc_ctrl_write_limited = true; ++ ++module_param_named(stats_readall_limited, proc_stats_readall_limited, bool, ++ S_IRUGO | S_IWUSR); ++module_param_named(ctrl_write_limited, proc_ctrl_write_limited, bool, ++ S_IRUGO | S_IWUSR); ++ ++/* ++ * Limit the number of active tags (via socket tags) for a given UID. ++ * Multiple processes could share the UID. ++ */ ++static int max_sock_tags = DEFAULT_MAX_SOCK_TAGS; ++module_param(max_sock_tags, int, S_IRUGO | S_IWUSR); ++ ++/* ++ * After the kernel has initiallized this module, it is still possible ++ * to make it passive. ++ * Setting passive to Y: ++ * - the iface stats handling will not act on notifications. ++ * - iptables matches will never match. ++ * - ctrl commands silently succeed. ++ * - stats are always empty. ++ * This is mostly usefull when a bug is suspected. ++ */ ++static bool module_passive; ++module_param_named(passive, module_passive, bool, S_IRUGO | S_IWUSR); ++ ++/* ++ * Control how qtaguid data is tracked per proc/uid. ++ * Setting tag_tracking_passive to Y: ++ * - don't create proc specific structs to track tags ++ * - don't check that active tag stats exceed some limits. ++ * - don't clean up socket tags on process exits. ++ * This is mostly usefull when a bug is suspected. ++ */ ++static bool qtu_proc_handling_passive; ++module_param_named(tag_tracking_passive, qtu_proc_handling_passive, bool, ++ S_IRUGO | S_IWUSR); ++ ++#define QTU_DEV_NAME "xt_qtaguid" ++ ++uint qtaguid_debug_mask = DEFAULT_DEBUG_MASK; ++module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR); ++ ++/*---------------------------------------------------------------------------*/ ++static const char *iface_stat_procdirname = "iface_stat"; ++static struct proc_dir_entry *iface_stat_procdir; ++/* ++ * The iface_stat_all* will go away once userspace gets use to the new fields ++ * that have a format line. ++ */ ++static const char *iface_stat_all_procfilename = "iface_stat_all"; ++static struct proc_dir_entry *iface_stat_all_procfile; ++static const char *iface_stat_fmt_procfilename = "iface_stat_fmt"; ++static struct proc_dir_entry *iface_stat_fmt_procfile; ++ ++ ++static LIST_HEAD(iface_stat_list); ++static DEFINE_SPINLOCK(iface_stat_list_lock); ++ ++static struct rb_root sock_tag_tree = RB_ROOT; ++static DEFINE_SPINLOCK(sock_tag_list_lock); ++ ++static struct rb_root tag_counter_set_tree = RB_ROOT; ++static DEFINE_SPINLOCK(tag_counter_set_list_lock); ++ ++static struct rb_root uid_tag_data_tree = RB_ROOT; ++static DEFINE_SPINLOCK(uid_tag_data_tree_lock); ++ ++static struct rb_root proc_qtu_data_tree = RB_ROOT; ++/* No proc_qtu_data_tree_lock; use uid_tag_data_tree_lock */ ++ ++static struct qtaguid_event_counts qtu_events; ++/*----------------------------------------------*/ ++static bool can_manipulate_uids(void) ++{ ++ /* root pwnd */ ++ return in_egroup_p(xt_qtaguid_ctrl_file->gid) ++ || unlikely(!from_kuid(&init_user_ns, current_fsuid())) || unlikely(!proc_ctrl_write_limited) ++ || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid)); ++} ++ ++static bool can_impersonate_uid(kuid_t uid) ++{ ++ return uid_eq(uid, current_fsuid()) || can_manipulate_uids(); ++} ++ ++static bool can_read_other_uid_stats(kuid_t uid) ++{ ++ /* root pwnd */ ++ return in_egroup_p(xt_qtaguid_stats_file->gid) ++ || unlikely(!from_kuid(&init_user_ns, current_fsuid())) || uid_eq(uid, current_fsuid()) ++ || unlikely(!proc_stats_readall_limited) ++ || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid)); ++} ++ ++static inline void dc_add_byte_packets(struct data_counters *counters, int set, ++ enum ifs_tx_rx direction, ++ enum ifs_proto ifs_proto, ++ int bytes, ++ int packets) ++{ ++ counters->bpc[set][direction][ifs_proto].bytes += bytes; ++ counters->bpc[set][direction][ifs_proto].packets += packets; ++} ++ ++static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) ++{ ++ struct rb_node *node = root->rb_node; ++ ++ while (node) { ++ struct tag_node *data = rb_entry(node, struct tag_node, node); ++ int result; ++ RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " ++ " node=%p data=%p\n", tag, node, data); ++ result = tag_compare(tag, data->tag); ++ RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " ++ " data.tag=0x%llx (uid=%u) res=%d\n", ++ tag, data->tag, get_uid_from_tag(data->tag), result); ++ if (result < 0) ++ node = node->rb_left; ++ else if (result > 0) ++ node = node->rb_right; ++ else ++ return data; ++ } ++ return NULL; ++} ++ ++static void tag_node_tree_insert(struct tag_node *data, struct rb_root *root) ++{ ++ struct rb_node **new = &(root->rb_node), *parent = NULL; ++ ++ /* Figure out where to put new node */ ++ while (*new) { ++ struct tag_node *this = rb_entry(*new, struct tag_node, ++ node); ++ int result = tag_compare(data->tag, this->tag); ++ RB_DEBUG("qtaguid: %s(): tag=0x%llx" ++ " (uid=%u)\n", __func__, ++ this->tag, ++ get_uid_from_tag(this->tag)); ++ parent = *new; ++ if (result < 0) ++ new = &((*new)->rb_left); ++ else if (result > 0) ++ new = &((*new)->rb_right); ++ else ++ BUG(); ++ } ++ ++ /* Add new node and rebalance tree. */ ++ rb_link_node(&data->node, parent, new); ++ rb_insert_color(&data->node, root); ++} ++ ++static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) ++{ ++ tag_node_tree_insert(&data->tn, root); ++} ++ ++static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) ++{ ++ struct tag_node *node = tag_node_tree_search(root, tag); ++ if (!node) ++ return NULL; ++ return rb_entry(&node->node, struct tag_stat, tn.node); ++} ++ ++static void tag_counter_set_tree_insert(struct tag_counter_set *data, ++ struct rb_root *root) ++{ ++ tag_node_tree_insert(&data->tn, root); ++} ++ ++static struct tag_counter_set *tag_counter_set_tree_search(struct rb_root *root, ++ tag_t tag) ++{ ++ struct tag_node *node = tag_node_tree_search(root, tag); ++ if (!node) ++ return NULL; ++ return rb_entry(&node->node, struct tag_counter_set, tn.node); ++ ++} ++ ++static void tag_ref_tree_insert(struct tag_ref *data, struct rb_root *root) ++{ ++ tag_node_tree_insert(&data->tn, root); ++} ++ ++static struct tag_ref *tag_ref_tree_search(struct rb_root *root, tag_t tag) ++{ ++ struct tag_node *node = tag_node_tree_search(root, tag); ++ if (!node) ++ return NULL; ++ return rb_entry(&node->node, struct tag_ref, tn.node); ++} ++ ++static struct sock_tag *sock_tag_tree_search(struct rb_root *root, ++ const struct sock *sk) ++{ ++ struct rb_node *node = root->rb_node; ++ ++ while (node) { ++ struct sock_tag *data = rb_entry(node, struct sock_tag, ++ sock_node); ++ if (sk < data->sk) ++ node = node->rb_left; ++ else if (sk > data->sk) ++ node = node->rb_right; ++ else ++ return data; ++ } ++ return NULL; ++} ++ ++static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) ++{ ++ struct rb_node **new = &(root->rb_node), *parent = NULL; ++ ++ /* Figure out where to put new node */ ++ while (*new) { ++ struct sock_tag *this = rb_entry(*new, struct sock_tag, ++ sock_node); ++ parent = *new; ++ if (data->sk < this->sk) ++ new = &((*new)->rb_left); ++ else if (data->sk > this->sk) ++ new = &((*new)->rb_right); ++ else ++ BUG(); ++ } ++ ++ /* Add new node and rebalance tree. */ ++ rb_link_node(&data->sock_node, parent, new); ++ rb_insert_color(&data->sock_node, root); ++} ++ ++static void sock_tag_tree_erase(struct rb_root *st_to_free_tree) ++{ ++ struct rb_node *node; ++ struct sock_tag *st_entry; ++ ++ node = rb_first(st_to_free_tree); ++ while (node) { ++ st_entry = rb_entry(node, struct sock_tag, sock_node); ++ node = rb_next(node); ++ CT_DEBUG("qtaguid: %s(): " ++ "erase st: sk=%p tag=0x%llx (uid=%u)\n", __func__, ++ st_entry->sk, ++ st_entry->tag, ++ get_uid_from_tag(st_entry->tag)); ++ rb_erase(&st_entry->sock_node, st_to_free_tree); ++ sockfd_put(st_entry->socket); ++ kfree(st_entry); ++ } ++} ++ ++static struct proc_qtu_data *proc_qtu_data_tree_search(struct rb_root *root, ++ const pid_t pid) ++{ ++ struct rb_node *node = root->rb_node; ++ ++ while (node) { ++ struct proc_qtu_data *data = rb_entry(node, ++ struct proc_qtu_data, ++ node); ++ if (pid < data->pid) ++ node = node->rb_left; ++ else if (pid > data->pid) ++ node = node->rb_right; ++ else ++ return data; ++ } ++ return NULL; ++} ++ ++static void proc_qtu_data_tree_insert(struct proc_qtu_data *data, ++ struct rb_root *root) ++{ ++ struct rb_node **new = &(root->rb_node), *parent = NULL; ++ ++ /* Figure out where to put new node */ ++ while (*new) { ++ struct proc_qtu_data *this = rb_entry(*new, ++ struct proc_qtu_data, ++ node); ++ parent = *new; ++ if (data->pid < this->pid) ++ new = &((*new)->rb_left); ++ else if (data->pid > this->pid) ++ new = &((*new)->rb_right); ++ else ++ BUG(); ++ } ++ ++ /* Add new node and rebalance tree. */ ++ rb_link_node(&data->node, parent, new); ++ rb_insert_color(&data->node, root); ++} ++ ++static void uid_tag_data_tree_insert(struct uid_tag_data *data, ++ struct rb_root *root) ++{ ++ struct rb_node **new = &(root->rb_node), *parent = NULL; ++ ++ /* Figure out where to put new node */ ++ while (*new) { ++ struct uid_tag_data *this = rb_entry(*new, ++ struct uid_tag_data, ++ node); ++ parent = *new; ++ if (data->uid < this->uid) ++ new = &((*new)->rb_left); ++ else if (data->uid > this->uid) ++ new = &((*new)->rb_right); ++ else ++ BUG(); ++ } ++ ++ /* Add new node and rebalance tree. */ ++ rb_link_node(&data->node, parent, new); ++ rb_insert_color(&data->node, root); ++} ++ ++static struct uid_tag_data *uid_tag_data_tree_search(struct rb_root *root, ++ uid_t uid) ++{ ++ struct rb_node *node = root->rb_node; ++ ++ while (node) { ++ struct uid_tag_data *data = rb_entry(node, ++ struct uid_tag_data, ++ node); ++ if (uid < data->uid) ++ node = node->rb_left; ++ else if (uid > data->uid) ++ node = node->rb_right; ++ else ++ return data; ++ } ++ return NULL; ++} ++ ++/* ++ * Allocates a new uid_tag_data struct if needed. ++ * Returns a pointer to the found or allocated uid_tag_data. ++ * Returns a PTR_ERR on failures, and lock is not held. ++ * If found is not NULL: ++ * sets *found to true if not allocated. ++ * sets *found to false if allocated. ++ */ ++struct uid_tag_data *get_uid_data(uid_t uid, bool *found_res) ++{ ++ struct uid_tag_data *utd_entry; ++ ++ /* Look for top level uid_tag_data for the UID */ ++ utd_entry = uid_tag_data_tree_search(&uid_tag_data_tree, uid); ++ DR_DEBUG("qtaguid: get_uid_data(%u) utd=%p\n", uid, utd_entry); ++ ++ if (found_res) ++ *found_res = utd_entry; ++ if (utd_entry) ++ return utd_entry; ++ ++ utd_entry = kzalloc(sizeof(*utd_entry), GFP_ATOMIC); ++ if (!utd_entry) { ++ pr_err("qtaguid: get_uid_data(%u): " ++ "tag data alloc failed\n", uid); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ utd_entry->uid = uid; ++ utd_entry->tag_ref_tree = RB_ROOT; ++ uid_tag_data_tree_insert(utd_entry, &uid_tag_data_tree); ++ DR_DEBUG("qtaguid: get_uid_data(%u) new utd=%p\n", uid, utd_entry); ++ return utd_entry; ++} ++ ++/* Never returns NULL. Either PTR_ERR or a valid ptr. */ ++static struct tag_ref *new_tag_ref(tag_t new_tag, ++ struct uid_tag_data *utd_entry) ++{ ++ struct tag_ref *tr_entry; ++ int res; ++ ++ if (utd_entry->num_active_tags + 1 > max_sock_tags) { ++ pr_info("qtaguid: new_tag_ref(0x%llx): " ++ "tag ref alloc quota exceeded. max=%d\n", ++ new_tag, max_sock_tags); ++ res = -EMFILE; ++ goto err_res; ++ ++ } ++ ++ tr_entry = kzalloc(sizeof(*tr_entry), GFP_ATOMIC); ++ if (!tr_entry) { ++ pr_err("qtaguid: new_tag_ref(0x%llx): " ++ "tag ref alloc failed\n", ++ new_tag); ++ res = -ENOMEM; ++ goto err_res; ++ } ++ tr_entry->tn.tag = new_tag; ++ /* tr_entry->num_sock_tags handled by caller */ ++ utd_entry->num_active_tags++; ++ tag_ref_tree_insert(tr_entry, &utd_entry->tag_ref_tree); ++ DR_DEBUG("qtaguid: new_tag_ref(0x%llx): " ++ " inserted new tag ref %p\n", ++ new_tag, tr_entry); ++ return tr_entry; ++ ++err_res: ++ return ERR_PTR(res); ++} ++ ++static struct tag_ref *lookup_tag_ref(tag_t full_tag, ++ struct uid_tag_data **utd_res) ++{ ++ struct uid_tag_data *utd_entry; ++ struct tag_ref *tr_entry; ++ bool found_utd; ++ uid_t uid = get_uid_from_tag(full_tag); ++ ++ DR_DEBUG("qtaguid: lookup_tag_ref(tag=0x%llx (uid=%u))\n", ++ full_tag, uid); ++ ++ utd_entry = get_uid_data(uid, &found_utd); ++ if (IS_ERR_OR_NULL(utd_entry)) { ++ if (utd_res) ++ *utd_res = utd_entry; ++ return NULL; ++ } ++ ++ tr_entry = tag_ref_tree_search(&utd_entry->tag_ref_tree, full_tag); ++ if (utd_res) ++ *utd_res = utd_entry; ++ DR_DEBUG("qtaguid: lookup_tag_ref(0x%llx) utd_entry=%p tr_entry=%p\n", ++ full_tag, utd_entry, tr_entry); ++ return tr_entry; ++} ++ ++/* Never returns NULL. Either PTR_ERR or a valid ptr. */ ++static struct tag_ref *get_tag_ref(tag_t full_tag, ++ struct uid_tag_data **utd_res) ++{ ++ struct uid_tag_data *utd_entry; ++ struct tag_ref *tr_entry; ++ ++ DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n", ++ full_tag); ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ tr_entry = lookup_tag_ref(full_tag, &utd_entry); ++ BUG_ON(IS_ERR_OR_NULL(utd_entry)); ++ if (!tr_entry) ++ tr_entry = new_tag_ref(full_tag, utd_entry); ++ ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ if (utd_res) ++ *utd_res = utd_entry; ++ DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n", ++ full_tag, utd_entry, tr_entry); ++ return tr_entry; ++} ++ ++/* Checks and maybe frees the UID Tag Data entry */ ++static void put_utd_entry(struct uid_tag_data *utd_entry) ++{ ++ /* Are we done with the UID tag data entry? */ ++ if (RB_EMPTY_ROOT(&utd_entry->tag_ref_tree) && ++ !utd_entry->num_pqd) { ++ DR_DEBUG("qtaguid: %s(): " ++ "erase utd_entry=%p uid=%u " ++ "by pid=%u tgid=%u uid=%u\n", __func__, ++ utd_entry, utd_entry->uid, ++ current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); ++ BUG_ON(utd_entry->num_active_tags); ++ rb_erase(&utd_entry->node, &uid_tag_data_tree); ++ kfree(utd_entry); ++ } else { ++ DR_DEBUG("qtaguid: %s(): " ++ "utd_entry=%p still has %d tags %d proc_qtu_data\n", ++ __func__, utd_entry, utd_entry->num_active_tags, ++ utd_entry->num_pqd); ++ BUG_ON(!(utd_entry->num_active_tags || ++ utd_entry->num_pqd)); ++ } ++} ++ ++/* ++ * If no sock_tags are using this tag_ref, ++ * decrements refcount of utd_entry, removes tr_entry ++ * from utd_entry->tag_ref_tree and frees. ++ */ ++static void free_tag_ref_from_utd_entry(struct tag_ref *tr_entry, ++ struct uid_tag_data *utd_entry) ++{ ++ DR_DEBUG("qtaguid: %s(): %p tag=0x%llx (uid=%u)\n", __func__, ++ tr_entry, tr_entry->tn.tag, ++ get_uid_from_tag(tr_entry->tn.tag)); ++ if (!tr_entry->num_sock_tags) { ++ BUG_ON(!utd_entry->num_active_tags); ++ utd_entry->num_active_tags--; ++ rb_erase(&tr_entry->tn.node, &utd_entry->tag_ref_tree); ++ DR_DEBUG("qtaguid: %s(): erased %p\n", __func__, tr_entry); ++ kfree(tr_entry); ++ } ++} ++ ++static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry) ++{ ++ struct rb_node *node; ++ struct tag_ref *tr_entry; ++ tag_t acct_tag; ++ ++ DR_DEBUG("qtaguid: %s(tag=0x%llx (uid=%u))\n", __func__, ++ full_tag, get_uid_from_tag(full_tag)); ++ acct_tag = get_atag_from_tag(full_tag); ++ node = rb_first(&utd_entry->tag_ref_tree); ++ while (node) { ++ tr_entry = rb_entry(node, struct tag_ref, tn.node); ++ node = rb_next(node); ++ if (!acct_tag || tr_entry->tn.tag == full_tag) ++ free_tag_ref_from_utd_entry(tr_entry, utd_entry); ++ } ++} ++ ++static ssize_t read_proc_u64(struct file *file, char __user *buf, ++ size_t size, loff_t *ppos) ++{ ++ uint64_t *valuep = PDE_DATA(file_inode(file)); ++ char tmp[24]; ++ size_t tmp_size; ++ ++ tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", *valuep); ++ return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); ++} ++ ++static ssize_t read_proc_bool(struct file *file, char __user *buf, ++ size_t size, loff_t *ppos) ++{ ++ bool *valuep = PDE_DATA(file_inode(file)); ++ char tmp[24]; ++ size_t tmp_size; ++ ++ tmp_size = scnprintf(tmp, sizeof(tmp), "%u\n", *valuep); ++ return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); ++} ++ ++static int get_active_counter_set(tag_t tag) ++{ ++ int active_set = 0; ++ struct tag_counter_set *tcs; ++ ++ MT_DEBUG("qtaguid: get_active_counter_set(tag=0x%llx)" ++ " (uid=%u)\n", ++ tag, get_uid_from_tag(tag)); ++ /* For now we only handle UID tags for active sets */ ++ tag = get_utag_from_tag(tag); ++ spin_lock_bh(&tag_counter_set_list_lock); ++ tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); ++ if (tcs) ++ active_set = tcs->active_set; ++ spin_unlock_bh(&tag_counter_set_list_lock); ++ return active_set; ++} ++ ++/* ++ * Find the entry for tracking the specified interface. ++ * Caller must hold iface_stat_list_lock ++ */ ++static struct iface_stat *get_iface_entry(const char *ifname) ++{ ++ struct iface_stat *iface_entry; ++ ++ /* Find the entry for tracking the specified tag within the interface */ ++ if (ifname == NULL) { ++ pr_info("qtaguid: iface_stat: get() NULL device name\n"); ++ return NULL; ++ } ++ ++ /* Iterate over interfaces */ ++ list_for_each_entry(iface_entry, &iface_stat_list, list) { ++ if (!strcmp(ifname, iface_entry->ifname)) ++ goto done; ++ } ++ iface_entry = NULL; ++done: ++ return iface_entry; ++} ++ ++/* This is for fmt2 only */ ++static void pp_iface_stat_header(struct seq_file *m) ++{ ++ seq_puts(m, ++ "ifname " ++ "total_skb_rx_bytes total_skb_rx_packets " ++ "total_skb_tx_bytes total_skb_tx_packets " ++ "rx_tcp_bytes rx_tcp_packets " ++ "rx_udp_bytes rx_udp_packets " ++ "rx_other_bytes rx_other_packets " ++ "tx_tcp_bytes tx_tcp_packets " ++ "tx_udp_bytes tx_udp_packets " ++ "tx_other_bytes tx_other_packets\n" ++ ); ++} ++ ++static void pp_iface_stat_line(struct seq_file *m, ++ struct iface_stat *iface_entry) ++{ ++ struct data_counters *cnts; ++ int cnt_set = 0; /* We only use one set for the device */ ++ cnts = &iface_entry->totals_via_skb; ++ seq_printf(m, "%s %llu %llu %llu %llu %llu %llu %llu %llu " ++ "%llu %llu %llu %llu %llu %llu %llu %llu\n", ++ iface_entry->ifname, ++ dc_sum_bytes(cnts, cnt_set, IFS_RX), ++ dc_sum_packets(cnts, cnt_set, IFS_RX), ++ dc_sum_bytes(cnts, cnt_set, IFS_TX), ++ dc_sum_packets(cnts, cnt_set, IFS_TX), ++ cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, ++ cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, ++ cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, ++ cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, ++ cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, ++ cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, ++ cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, ++ cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, ++ cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, ++ cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, ++ cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, ++ cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); ++} ++ ++struct proc_iface_stat_fmt_info { ++ int fmt; ++}; ++ ++static void *iface_stat_fmt_proc_start(struct seq_file *m, loff_t *pos) ++{ ++ struct proc_iface_stat_fmt_info *p = m->private; ++ loff_t n = *pos; ++ ++ /* ++ * This lock will prevent iface_stat_update() from changing active, ++ * and in turn prevent an interface from unregistering itself. ++ */ ++ spin_lock_bh(&iface_stat_list_lock); ++ ++ if (unlikely(module_passive)) ++ return NULL; ++ ++ if (!n && p->fmt == 2) ++ pp_iface_stat_header(m); ++ ++ return seq_list_start(&iface_stat_list, n); ++} ++ ++static void *iface_stat_fmt_proc_next(struct seq_file *m, void *p, loff_t *pos) ++{ ++ return seq_list_next(p, &iface_stat_list, pos); ++} ++ ++static void iface_stat_fmt_proc_stop(struct seq_file *m, void *p) ++{ ++ spin_unlock_bh(&iface_stat_list_lock); ++} ++ ++static int iface_stat_fmt_proc_show(struct seq_file *m, void *v) ++{ ++ struct proc_iface_stat_fmt_info *p = m->private; ++ struct iface_stat *iface_entry; ++ struct rtnl_link_stats64 dev_stats, *stats; ++ struct rtnl_link_stats64 no_dev_stats = {0}; ++ ++ ++ CT_DEBUG("qtaguid:proc iface_stat_fmt pid=%u tgid=%u uid=%u\n", ++ current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); ++ ++ iface_entry = list_entry(v, struct iface_stat, list); ++ ++ if (iface_entry->active) { ++ stats = dev_get_stats(iface_entry->net_dev, ++ &dev_stats); ++ } else { ++ stats = &no_dev_stats; ++ } ++ /* ++ * If the meaning of the data changes, then update the fmtX ++ * string. ++ */ ++ if (p->fmt == 1) { ++ seq_printf(m, "%s %d %llu %llu %llu %llu %llu %llu %llu %llu\n", ++ iface_entry->ifname, ++ iface_entry->active, ++ iface_entry->totals_via_dev[IFS_RX].bytes, ++ iface_entry->totals_via_dev[IFS_RX].packets, ++ iface_entry->totals_via_dev[IFS_TX].bytes, ++ iface_entry->totals_via_dev[IFS_TX].packets, ++ stats->rx_bytes, stats->rx_packets, ++ stats->tx_bytes, stats->tx_packets ++ ); ++ } else { ++ pp_iface_stat_line(m, iface_entry); ++ } ++ return 0; ++} ++ ++static const struct file_operations read_u64_fops = { ++ .read = read_proc_u64, ++ .llseek = default_llseek, ++}; ++ ++static const struct file_operations read_bool_fops = { ++ .read = read_proc_bool, ++ .llseek = default_llseek, ++}; ++ ++static void iface_create_proc_worker(struct work_struct *work) ++{ ++ struct proc_dir_entry *proc_entry; ++ struct iface_stat_work *isw = container_of(work, struct iface_stat_work, ++ iface_work); ++ struct iface_stat *new_iface = isw->iface_entry; ++ ++ /* iface_entries are not deleted, so safe to manipulate. */ ++ proc_entry = proc_mkdir(new_iface->ifname, iface_stat_procdir); ++ if (IS_ERR_OR_NULL(proc_entry)) { ++ pr_err("qtaguid: iface_stat: create_proc(): alloc failed.\n"); ++ kfree(isw); ++ return; ++ } ++ ++ new_iface->proc_ptr = proc_entry; ++ ++ proc_create_data("tx_bytes", proc_iface_perms, proc_entry, ++ &read_u64_fops, ++ &new_iface->totals_via_dev[IFS_TX].bytes); ++ proc_create_data("rx_bytes", proc_iface_perms, proc_entry, ++ &read_u64_fops, ++ &new_iface->totals_via_dev[IFS_RX].bytes); ++ proc_create_data("tx_packets", proc_iface_perms, proc_entry, ++ &read_u64_fops, ++ &new_iface->totals_via_dev[IFS_TX].packets); ++ proc_create_data("rx_packets", proc_iface_perms, proc_entry, ++ &read_u64_fops, ++ &new_iface->totals_via_dev[IFS_RX].packets); ++ proc_create_data("active", proc_iface_perms, proc_entry, ++ &read_bool_fops, &new_iface->active); ++ ++ IF_DEBUG("qtaguid: iface_stat: create_proc(): done " ++ "entry=%p dev=%s\n", new_iface, new_iface->ifname); ++ kfree(isw); ++} ++ ++/* ++ * Will set the entry's active state, and ++ * update the net_dev accordingly also. ++ */ ++static void _iface_stat_set_active(struct iface_stat *entry, ++ struct net_device *net_dev, ++ bool activate) ++{ ++ if (activate) { ++ entry->net_dev = net_dev; ++ entry->active = true; ++ IF_DEBUG("qtaguid: %s(%s): " ++ "enable tracking. rfcnt=%d\n", __func__, ++ entry->ifname, ++ __this_cpu_read(*net_dev->pcpu_refcnt)); ++ } else { ++ entry->active = false; ++ entry->net_dev = NULL; ++ IF_DEBUG("qtaguid: %s(%s): " ++ "disable tracking. rfcnt=%d\n", __func__, ++ entry->ifname, ++ __this_cpu_read(*net_dev->pcpu_refcnt)); ++ ++ } ++} ++ ++/* Caller must hold iface_stat_list_lock */ ++static struct iface_stat *iface_alloc(struct net_device *net_dev) ++{ ++ struct iface_stat *new_iface; ++ struct iface_stat_work *isw; ++ ++ new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC); ++ if (new_iface == NULL) { ++ pr_err("qtaguid: iface_stat: create(%s): " ++ "iface_stat alloc failed\n", net_dev->name); ++ return NULL; ++ } ++ new_iface->ifname = kstrdup(net_dev->name, GFP_ATOMIC); ++ if (new_iface->ifname == NULL) { ++ pr_err("qtaguid: iface_stat: create(%s): " ++ "ifname alloc failed\n", net_dev->name); ++ kfree(new_iface); ++ return NULL; ++ } ++ spin_lock_init(&new_iface->tag_stat_list_lock); ++ new_iface->tag_stat_tree = RB_ROOT; ++ _iface_stat_set_active(new_iface, net_dev, true); ++ ++ /* ++ * ipv6 notifier chains are atomic :( ++ * No create_proc_read_entry() for you! ++ */ ++ isw = kmalloc(sizeof(*isw), GFP_ATOMIC); ++ if (!isw) { ++ pr_err("qtaguid: iface_stat: create(%s): " ++ "work alloc failed\n", new_iface->ifname); ++ _iface_stat_set_active(new_iface, net_dev, false); ++ kfree(new_iface->ifname); ++ kfree(new_iface); ++ return NULL; ++ } ++ isw->iface_entry = new_iface; ++ INIT_WORK(&isw->iface_work, iface_create_proc_worker); ++ schedule_work(&isw->iface_work); ++ list_add(&new_iface->list, &iface_stat_list); ++ return new_iface; ++} ++ ++static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, ++ struct iface_stat *iface) ++{ ++ struct rtnl_link_stats64 dev_stats, *stats; ++ bool stats_rewound; ++ ++ stats = dev_get_stats(net_dev, &dev_stats); ++ /* No empty packets */ ++ stats_rewound = ++ (stats->rx_bytes < iface->last_known[IFS_RX].bytes) ++ || (stats->tx_bytes < iface->last_known[IFS_TX].bytes); ++ ++ IF_DEBUG("qtaguid: %s(%s): iface=%p netdev=%p " ++ "bytes rx/tx=%llu/%llu " ++ "active=%d last_known=%d " ++ "stats_rewound=%d\n", __func__, ++ net_dev ? net_dev->name : "?", ++ iface, net_dev, ++ stats->rx_bytes, stats->tx_bytes, ++ iface->active, iface->last_known_valid, stats_rewound); ++ ++ if (iface->active && iface->last_known_valid && stats_rewound) { ++ pr_warn_once("qtaguid: iface_stat: %s(%s): " ++ "iface reset its stats unexpectedly\n", __func__, ++ net_dev->name); ++ ++ iface->totals_via_dev[IFS_TX].bytes += ++ iface->last_known[IFS_TX].bytes; ++ iface->totals_via_dev[IFS_TX].packets += ++ iface->last_known[IFS_TX].packets; ++ iface->totals_via_dev[IFS_RX].bytes += ++ iface->last_known[IFS_RX].bytes; ++ iface->totals_via_dev[IFS_RX].packets += ++ iface->last_known[IFS_RX].packets; ++ iface->last_known_valid = false; ++ IF_DEBUG("qtaguid: %s(%s): iface=%p " ++ "used last known bytes rx/tx=%llu/%llu\n", __func__, ++ iface->ifname, iface, iface->last_known[IFS_RX].bytes, ++ iface->last_known[IFS_TX].bytes); ++ } ++} ++ ++/* ++ * Create a new entry for tracking the specified interface. ++ * Do nothing if the entry already exists. ++ * Called when an interface is configured with a valid IP address. ++ */ ++static void iface_stat_create(struct net_device *net_dev, ++ struct in_ifaddr *ifa) ++{ ++ struct in_device *in_dev = NULL; ++ const char *ifname; ++ struct iface_stat *entry; ++ __be32 ipaddr = 0; ++ struct iface_stat *new_iface; ++ ++ IF_DEBUG("qtaguid: iface_stat: create(%s): ifa=%p netdev=%p\n", ++ net_dev ? net_dev->name : "?", ++ ifa, net_dev); ++ if (!net_dev) { ++ pr_err("qtaguid: iface_stat: create(): no net dev\n"); ++ return; ++ } ++ ++ ifname = net_dev->name; ++ if (!ifa) { ++ in_dev = in_dev_get(net_dev); ++ if (!in_dev) { ++ pr_err("qtaguid: iface_stat: create(%s): no inet dev\n", ++ ifname); ++ return; ++ } ++ IF_DEBUG("qtaguid: iface_stat: create(%s): in_dev=%p\n", ++ ifname, in_dev); ++ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { ++ IF_DEBUG("qtaguid: iface_stat: create(%s): " ++ "ifa=%p ifa_label=%s\n", ++ ifname, ifa, ++ ifa->ifa_label ? ifa->ifa_label : "(null)"); ++ if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) ++ break; ++ } ++ } ++ ++ if (!ifa) { ++ IF_DEBUG("qtaguid: iface_stat: create(%s): no matching IP\n", ++ ifname); ++ goto done_put; ++ } ++ ipaddr = ifa->ifa_local; ++ ++ spin_lock_bh(&iface_stat_list_lock); ++ entry = get_iface_entry(ifname); ++ if (entry != NULL) { ++ IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", ++ ifname, entry); ++ iface_check_stats_reset_and_adjust(net_dev, entry); ++ _iface_stat_set_active(entry, net_dev, true); ++ IF_DEBUG("qtaguid: %s(%s): " ++ "tracking now %d on ip=%pI4\n", __func__, ++ entry->ifname, true, &ipaddr); ++ goto done_unlock_put; ++ } ++ ++ new_iface = iface_alloc(net_dev); ++ IF_DEBUG("qtaguid: iface_stat: create(%s): done " ++ "entry=%p ip=%pI4\n", ifname, new_iface, &ipaddr); ++done_unlock_put: ++ spin_unlock_bh(&iface_stat_list_lock); ++done_put: ++ if (in_dev) ++ in_dev_put(in_dev); ++} ++ ++static void iface_stat_create_ipv6(struct net_device *net_dev, ++ struct inet6_ifaddr *ifa) ++{ ++ struct in_device *in_dev; ++ const char *ifname; ++ struct iface_stat *entry; ++ struct iface_stat *new_iface; ++ int addr_type; ++ ++ IF_DEBUG("qtaguid: iface_stat: create6(): ifa=%p netdev=%p->name=%s\n", ++ ifa, net_dev, net_dev ? net_dev->name : ""); ++ if (!net_dev) { ++ pr_err("qtaguid: iface_stat: create6(): no net dev!\n"); ++ return; ++ } ++ ifname = net_dev->name; ++ ++ in_dev = in_dev_get(net_dev); ++ if (!in_dev) { ++ pr_err("qtaguid: iface_stat: create6(%s): no inet dev\n", ++ ifname); ++ return; ++ } ++ ++ IF_DEBUG("qtaguid: iface_stat: create6(%s): in_dev=%p\n", ++ ifname, in_dev); ++ ++ if (!ifa) { ++ IF_DEBUG("qtaguid: iface_stat: create6(%s): no matching IP\n", ++ ifname); ++ goto done_put; ++ } ++ addr_type = ipv6_addr_type(&ifa->addr); ++ ++ spin_lock_bh(&iface_stat_list_lock); ++ entry = get_iface_entry(ifname); ++ if (entry != NULL) { ++ IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, ++ ifname, entry); ++ iface_check_stats_reset_and_adjust(net_dev, entry); ++ _iface_stat_set_active(entry, net_dev, true); ++ IF_DEBUG("qtaguid: %s(%s): " ++ "tracking now %d on ip=%pI6c\n", __func__, ++ entry->ifname, true, &ifa->addr); ++ goto done_unlock_put; ++ } ++ ++ new_iface = iface_alloc(net_dev); ++ IF_DEBUG("qtaguid: iface_stat: create6(%s): done " ++ "entry=%p ip=%pI6c\n", ifname, new_iface, &ifa->addr); ++ ++done_unlock_put: ++ spin_unlock_bh(&iface_stat_list_lock); ++done_put: ++ in_dev_put(in_dev); ++} ++ ++static struct sock_tag *get_sock_stat_nl(const struct sock *sk) ++{ ++ MT_DEBUG("qtaguid: get_sock_stat_nl(sk=%p)\n", sk); ++ return sock_tag_tree_search(&sock_tag_tree, sk); ++} ++ ++static struct sock_tag *get_sock_stat(const struct sock *sk) ++{ ++ struct sock_tag *sock_tag_entry; ++ MT_DEBUG("qtaguid: get_sock_stat(sk=%p)\n", sk); ++ if (!sk) ++ return NULL; ++ spin_lock_bh(&sock_tag_list_lock); ++ sock_tag_entry = get_sock_stat_nl(sk); ++ spin_unlock_bh(&sock_tag_list_lock); ++ return sock_tag_entry; ++} ++ ++static int ipx_proto(const struct sk_buff *skb, ++ struct xt_action_param *par) ++{ ++ int thoff = 0, tproto; ++ ++ switch (par->family) { ++ case NFPROTO_IPV6: ++ tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); ++ if (tproto < 0) ++ MT_DEBUG("%s(): transport header not found in ipv6" ++ " skb=%p\n", __func__, skb); ++ break; ++ case NFPROTO_IPV4: ++ tproto = ip_hdr(skb)->protocol; ++ break; ++ default: ++ tproto = IPPROTO_RAW; ++ } ++ return tproto; ++} ++ ++static void ++data_counters_update(struct data_counters *dc, int set, ++ enum ifs_tx_rx direction, int proto, int bytes) ++{ ++ switch (proto) { ++ case IPPROTO_TCP: ++ dc_add_byte_packets(dc, set, direction, IFS_TCP, bytes, 1); ++ break; ++ case IPPROTO_UDP: ++ dc_add_byte_packets(dc, set, direction, IFS_UDP, bytes, 1); ++ break; ++ case IPPROTO_IP: ++ default: ++ dc_add_byte_packets(dc, set, direction, IFS_PROTO_OTHER, bytes, ++ 1); ++ break; ++ } ++} ++ ++/* ++ * Update stats for the specified interface. Do nothing if the entry ++ * does not exist (when a device was never configured with an IP address). ++ * Called when an device is being unregistered. ++ */ ++static void iface_stat_update(struct net_device *net_dev, bool stash_only) ++{ ++ struct rtnl_link_stats64 dev_stats, *stats; ++ struct iface_stat *entry; ++ ++ stats = dev_get_stats(net_dev, &dev_stats); ++ spin_lock_bh(&iface_stat_list_lock); ++ entry = get_iface_entry(net_dev->name); ++ if (entry == NULL) { ++ IF_DEBUG("qtaguid: iface_stat: update(%s): not tracked\n", ++ net_dev->name); ++ spin_unlock_bh(&iface_stat_list_lock); ++ return; ++ } ++ ++ IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, ++ net_dev->name, entry); ++ if (!entry->active) { ++ IF_DEBUG("qtaguid: %s(%s): already disabled\n", __func__, ++ net_dev->name); ++ spin_unlock_bh(&iface_stat_list_lock); ++ return; ++ } ++ ++ if (stash_only) { ++ entry->last_known[IFS_TX].bytes = stats->tx_bytes; ++ entry->last_known[IFS_TX].packets = stats->tx_packets; ++ entry->last_known[IFS_RX].bytes = stats->rx_bytes; ++ entry->last_known[IFS_RX].packets = stats->rx_packets; ++ entry->last_known_valid = true; ++ IF_DEBUG("qtaguid: %s(%s): " ++ "dev stats stashed rx/tx=%llu/%llu\n", __func__, ++ net_dev->name, stats->rx_bytes, stats->tx_bytes); ++ spin_unlock_bh(&iface_stat_list_lock); ++ return; ++ } ++ entry->totals_via_dev[IFS_TX].bytes += stats->tx_bytes; ++ entry->totals_via_dev[IFS_TX].packets += stats->tx_packets; ++ entry->totals_via_dev[IFS_RX].bytes += stats->rx_bytes; ++ entry->totals_via_dev[IFS_RX].packets += stats->rx_packets; ++ /* We don't need the last_known[] anymore */ ++ entry->last_known_valid = false; ++ _iface_stat_set_active(entry, net_dev, false); ++ IF_DEBUG("qtaguid: %s(%s): " ++ "disable tracking. rx/tx=%llu/%llu\n", __func__, ++ net_dev->name, stats->rx_bytes, stats->tx_bytes); ++ spin_unlock_bh(&iface_stat_list_lock); ++} ++ ++/* ++ * Update stats for the specified interface from the skb. ++ * Do nothing if the entry ++ * does not exist (when a device was never configured with an IP address). ++ * Called on each sk. ++ */ ++static void iface_stat_update_from_skb(const struct sk_buff *skb, ++ struct xt_action_param *par) ++{ ++ struct iface_stat *entry; ++ const struct net_device *el_dev; ++ enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; ++ int bytes = skb->len; ++ int proto; ++ ++ if (!skb->dev) { ++ MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); ++ el_dev = par->in ? : par->out; ++ } else { ++ const struct net_device *other_dev; ++ el_dev = skb->dev; ++ other_dev = par->in ? : par->out; ++ if (el_dev != other_dev) { ++ MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " ++ "par->(in/out)=%p %s\n", ++ par->hooknum, el_dev, el_dev->name, other_dev, ++ other_dev->name); ++ } ++ } ++ ++ if (unlikely(!el_dev)) { ++ pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n", ++ par->hooknum, __func__); ++ BUG(); ++ } else if (unlikely(!el_dev->name)) { ++ pr_err_ratelimited("qtaguid[%d]: %s(): no dev->name?!!\n", ++ par->hooknum, __func__); ++ BUG(); ++ } else { ++ proto = ipx_proto(skb, par); ++ MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", ++ par->hooknum, el_dev->name, el_dev->type, ++ par->family, proto); ++ } ++ ++ spin_lock_bh(&iface_stat_list_lock); ++ entry = get_iface_entry(el_dev->name); ++ if (entry == NULL) { ++ IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n", ++ __func__, el_dev->name); ++ spin_unlock_bh(&iface_stat_list_lock); ++ return; ++ } ++ ++ IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, ++ el_dev->name, entry); ++ ++ data_counters_update(&entry->totals_via_skb, 0, direction, proto, ++ bytes); ++ spin_unlock_bh(&iface_stat_list_lock); ++} ++ ++static void tag_stat_update(struct tag_stat *tag_entry, ++ enum ifs_tx_rx direction, int proto, int bytes) ++{ ++ int active_set; ++ active_set = get_active_counter_set(tag_entry->tn.tag); ++ MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%u) set=%d " ++ "dir=%d proto=%d bytes=%d)\n", ++ tag_entry->tn.tag, get_uid_from_tag(tag_entry->tn.tag), ++ active_set, direction, proto, bytes); ++ data_counters_update(&tag_entry->counters, active_set, direction, ++ proto, bytes); ++ if (tag_entry->parent_counters) ++ data_counters_update(tag_entry->parent_counters, active_set, ++ direction, proto, bytes); ++} ++ ++/* ++ * Create a new entry for tracking the specified {acct_tag,uid_tag} within ++ * the interface. ++ * iface_entry->tag_stat_list_lock should be held. ++ */ ++static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, ++ tag_t tag) ++{ ++ struct tag_stat *new_tag_stat_entry = NULL; ++ IF_DEBUG("qtaguid: iface_stat: %s(): ife=%p tag=0x%llx" ++ " (uid=%u)\n", __func__, ++ iface_entry, tag, get_uid_from_tag(tag)); ++ new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); ++ if (!new_tag_stat_entry) { ++ pr_err("qtaguid: iface_stat: tag stat alloc failed\n"); ++ goto done; ++ } ++ new_tag_stat_entry->tn.tag = tag; ++ tag_stat_tree_insert(new_tag_stat_entry, &iface_entry->tag_stat_tree); ++done: ++ return new_tag_stat_entry; ++} ++ ++static void if_tag_stat_update(const char *ifname, uid_t uid, ++ const struct sock *sk, enum ifs_tx_rx direction, ++ int proto, int bytes) ++{ ++ struct tag_stat *tag_stat_entry; ++ tag_t tag, acct_tag; ++ tag_t uid_tag; ++ struct data_counters *uid_tag_counters; ++ struct sock_tag *sock_tag_entry; ++ struct iface_stat *iface_entry; ++ struct tag_stat *new_tag_stat = NULL; ++ MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " ++ "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", ++ ifname, uid, sk, direction, proto, bytes); ++ ++ ++ iface_entry = get_iface_entry(ifname); ++ if (!iface_entry) { ++ pr_err_ratelimited("qtaguid: iface_stat: stat_update() " ++ "%s not found\n", ifname); ++ return; ++ } ++ /* It is ok to process data when an iface_entry is inactive */ ++ ++ MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n", ++ ifname, iface_entry); ++ ++ /* ++ * Look for a tagged sock. ++ * It will have an acct_uid. ++ */ ++ sock_tag_entry = get_sock_stat(sk); ++ if (sock_tag_entry) { ++ tag = sock_tag_entry->tag; ++ acct_tag = get_atag_from_tag(tag); ++ uid_tag = get_utag_from_tag(tag); ++ } else { ++ acct_tag = make_atag_from_value(0); ++ tag = combine_atag_with_uid(acct_tag, uid); ++ uid_tag = make_tag_from_uid(uid); ++ } ++ MT_DEBUG("qtaguid: iface_stat: stat_update(): " ++ " looking for tag=0x%llx (uid=%u) in ife=%p\n", ++ tag, get_uid_from_tag(tag), iface_entry); ++ /* Loop over tag list under this interface for {acct_tag,uid_tag} */ ++ spin_lock_bh(&iface_entry->tag_stat_list_lock); ++ ++ tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, ++ tag); ++ if (tag_stat_entry) { ++ /* ++ * Updating the {acct_tag, uid_tag} entry handles both stats: ++ * {0, uid_tag} will also get updated. ++ */ ++ tag_stat_update(tag_stat_entry, direction, proto, bytes); ++ spin_unlock_bh(&iface_entry->tag_stat_list_lock); ++ return; ++ } ++ ++ /* Loop over tag list under this interface for {0,uid_tag} */ ++ tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, ++ uid_tag); ++ if (!tag_stat_entry) { ++ /* Here: the base uid_tag did not exist */ ++ /* ++ * No parent counters. So ++ * - No {0, uid_tag} stats and no {acc_tag, uid_tag} stats. ++ */ ++ new_tag_stat = create_if_tag_stat(iface_entry, uid_tag); ++ if (!new_tag_stat) ++ goto unlock; ++ uid_tag_counters = &new_tag_stat->counters; ++ } else { ++ uid_tag_counters = &tag_stat_entry->counters; ++ } ++ ++ if (acct_tag) { ++ /* Create the child {acct_tag, uid_tag} and hook up parent. */ ++ new_tag_stat = create_if_tag_stat(iface_entry, tag); ++ if (!new_tag_stat) ++ goto unlock; ++ new_tag_stat->parent_counters = uid_tag_counters; ++ } else { ++ /* ++ * For new_tag_stat to be still NULL here would require: ++ * {0, uid_tag} exists ++ * and {acct_tag, uid_tag} doesn't exist ++ * AND acct_tag == 0. ++ * Impossible. This reassures us that new_tag_stat ++ * below will always be assigned. ++ */ ++ BUG_ON(!new_tag_stat); ++ } ++ tag_stat_update(new_tag_stat, direction, proto, bytes); ++unlock: ++ spin_unlock_bh(&iface_entry->tag_stat_list_lock); ++} ++ ++static int iface_netdev_event_handler(struct notifier_block *nb, ++ unsigned long event, void *ptr) { ++ struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ ++ if (unlikely(module_passive)) ++ return NOTIFY_DONE; ++ ++ IF_DEBUG("qtaguid: iface_stat: netdev_event(): " ++ "ev=0x%lx/%s netdev=%p->name=%s\n", ++ event, netdev_evt_str(event), dev, dev ? dev->name : ""); ++ ++ switch (event) { ++ case NETDEV_UP: ++ iface_stat_create(dev, NULL); ++ atomic64_inc(&qtu_events.iface_events); ++ break; ++ case NETDEV_DOWN: ++ case NETDEV_UNREGISTER: ++ iface_stat_update(dev, event == NETDEV_DOWN); ++ atomic64_inc(&qtu_events.iface_events); ++ break; ++ } ++ return NOTIFY_DONE; ++} ++ ++static int iface_inet6addr_event_handler(struct notifier_block *nb, ++ unsigned long event, void *ptr) ++{ ++ struct inet6_ifaddr *ifa = ptr; ++ struct net_device *dev; ++ ++ if (unlikely(module_passive)) ++ return NOTIFY_DONE; ++ ++ IF_DEBUG("qtaguid: iface_stat: inet6addr_event(): " ++ "ev=0x%lx/%s ifa=%p\n", ++ event, netdev_evt_str(event), ifa); ++ ++ switch (event) { ++ case NETDEV_UP: ++ BUG_ON(!ifa || !ifa->idev); ++ dev = (struct net_device *)ifa->idev->dev; ++ iface_stat_create_ipv6(dev, ifa); ++ atomic64_inc(&qtu_events.iface_events); ++ break; ++ case NETDEV_DOWN: ++ case NETDEV_UNREGISTER: ++ BUG_ON(!ifa || !ifa->idev); ++ dev = (struct net_device *)ifa->idev->dev; ++ iface_stat_update(dev, event == NETDEV_DOWN); ++ atomic64_inc(&qtu_events.iface_events); ++ break; ++ } ++ return NOTIFY_DONE; ++} ++ ++static int iface_inetaddr_event_handler(struct notifier_block *nb, ++ unsigned long event, void *ptr) ++{ ++ struct in_ifaddr *ifa = ptr; ++ struct net_device *dev; ++ ++ if (unlikely(module_passive)) ++ return NOTIFY_DONE; ++ ++ IF_DEBUG("qtaguid: iface_stat: inetaddr_event(): " ++ "ev=0x%lx/%s ifa=%p\n", ++ event, netdev_evt_str(event), ifa); ++ ++ switch (event) { ++ case NETDEV_UP: ++ BUG_ON(!ifa || !ifa->ifa_dev); ++ dev = ifa->ifa_dev->dev; ++ iface_stat_create(dev, ifa); ++ atomic64_inc(&qtu_events.iface_events); ++ break; ++ case NETDEV_DOWN: ++ case NETDEV_UNREGISTER: ++ BUG_ON(!ifa || !ifa->ifa_dev); ++ dev = ifa->ifa_dev->dev; ++ iface_stat_update(dev, event == NETDEV_DOWN); ++ atomic64_inc(&qtu_events.iface_events); ++ break; ++ } ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block iface_netdev_notifier_blk = { ++ .notifier_call = iface_netdev_event_handler, ++}; ++ ++static struct notifier_block iface_inetaddr_notifier_blk = { ++ .notifier_call = iface_inetaddr_event_handler, ++}; ++ ++static struct notifier_block iface_inet6addr_notifier_blk = { ++ .notifier_call = iface_inet6addr_event_handler, ++}; ++ ++static const struct seq_operations iface_stat_fmt_proc_seq_ops = { ++ .start = iface_stat_fmt_proc_start, ++ .next = iface_stat_fmt_proc_next, ++ .stop = iface_stat_fmt_proc_stop, ++ .show = iface_stat_fmt_proc_show, ++}; ++ ++static int proc_iface_stat_fmt_open(struct inode *inode, struct file *file) ++{ ++ struct proc_iface_stat_fmt_info *s; ++ ++ s = __seq_open_private(file, &iface_stat_fmt_proc_seq_ops, ++ sizeof(struct proc_iface_stat_fmt_info)); ++ if (!s) ++ return -ENOMEM; ++ ++ s->fmt = (uintptr_t)PDE_DATA(inode); ++ return 0; ++} ++ ++static const struct file_operations proc_iface_stat_fmt_fops = { ++ .open = proc_iface_stat_fmt_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release_private, ++}; ++ ++static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) ++{ ++ int err; ++ ++ iface_stat_procdir = proc_mkdir(iface_stat_procdirname, parent_procdir); ++ if (!iface_stat_procdir) { ++ pr_err("qtaguid: iface_stat: init failed to create proc entry\n"); ++ err = -1; ++ goto err; ++ } ++ ++ iface_stat_all_procfile = proc_create_data(iface_stat_all_procfilename, ++ proc_iface_perms, ++ parent_procdir, ++ &proc_iface_stat_fmt_fops, ++ (void *)1 /* fmt1 */); ++ if (!iface_stat_all_procfile) { ++ pr_err("qtaguid: iface_stat: init " ++ " failed to create stat_old proc entry\n"); ++ err = -1; ++ goto err_zap_entry; ++ } ++ ++ iface_stat_fmt_procfile = proc_create_data(iface_stat_fmt_procfilename, ++ proc_iface_perms, ++ parent_procdir, ++ &proc_iface_stat_fmt_fops, ++ (void *)2 /* fmt2 */); ++ if (!iface_stat_fmt_procfile) { ++ pr_err("qtaguid: iface_stat: init " ++ " failed to create stat_all proc entry\n"); ++ err = -1; ++ goto err_zap_all_stats_entry; ++ } ++ ++ ++ err = register_netdevice_notifier(&iface_netdev_notifier_blk); ++ if (err) { ++ pr_err("qtaguid: iface_stat: init " ++ "failed to register dev event handler\n"); ++ goto err_zap_all_stats_entries; ++ } ++ err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); ++ if (err) { ++ pr_err("qtaguid: iface_stat: init " ++ "failed to register ipv4 dev event handler\n"); ++ goto err_unreg_nd; ++ } ++ ++ err = register_inet6addr_notifier(&iface_inet6addr_notifier_blk); ++ if (err) { ++ pr_err("qtaguid: iface_stat: init " ++ "failed to register ipv6 dev event handler\n"); ++ goto err_unreg_ip4_addr; ++ } ++ return 0; ++ ++err_unreg_ip4_addr: ++ unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); ++err_unreg_nd: ++ unregister_netdevice_notifier(&iface_netdev_notifier_blk); ++err_zap_all_stats_entries: ++ remove_proc_entry(iface_stat_fmt_procfilename, parent_procdir); ++err_zap_all_stats_entry: ++ remove_proc_entry(iface_stat_all_procfilename, parent_procdir); ++err_zap_entry: ++ remove_proc_entry(iface_stat_procdirname, parent_procdir); ++err: ++ return err; ++} ++ ++static struct sock *qtaguid_find_sk(const struct sk_buff *skb, ++ struct xt_action_param *par) ++{ ++ struct sock *sk; ++ unsigned int hook_mask = (1 << par->hooknum); ++ ++ MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, ++ par->hooknum, par->family); ++ ++ /* ++ * Let's not abuse the the xt_socket_get*_sk(), or else it will ++ * return garbage SKs. ++ */ ++ if (!(hook_mask & XT_SOCKET_SUPPORTED_HOOKS)) ++ return NULL; ++ ++ switch (par->family) { ++ case NFPROTO_IPV6: ++ sk = xt_socket_get6_sk(skb, par); ++ break; ++ case NFPROTO_IPV4: ++ sk = xt_socket_get4_sk(skb, par); ++ break; ++ default: ++ return NULL; ++ } ++ ++ if (sk) { ++ MT_DEBUG("qtaguid: %p->sk_proto=%u " ++ "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); ++ /* ++ * When in TCP_TIME_WAIT the sk is not a "struct sock" but ++ * "struct inet_timewait_sock" which is missing fields. ++ */ ++ if (sk->sk_state == TCP_TIME_WAIT) { ++ if (sk != skb->sk) ++ sock_gen_put(sk); ++ sk = NULL; ++ } ++ } ++ return sk; ++} ++ ++static void account_for_uid(const struct sk_buff *skb, ++ const struct sock *alternate_sk, uid_t uid, ++ struct xt_action_param *par) ++{ ++ const struct net_device *el_dev; ++ ++ if (!skb->dev) { ++ MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); ++ el_dev = par->in ? : par->out; ++ } else { ++ const struct net_device *other_dev; ++ el_dev = skb->dev; ++ other_dev = par->in ? : par->out; ++ if (el_dev != other_dev) { ++ MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " ++ "par->(in/out)=%p %s\n", ++ par->hooknum, el_dev, el_dev->name, other_dev, ++ other_dev->name); ++ } ++ } ++ ++ if (unlikely(!el_dev)) { ++ pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum); ++ } else if (unlikely(!el_dev->name)) { ++ pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); ++ } else { ++ int proto = ipx_proto(skb, par); ++ MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", ++ par->hooknum, el_dev->name, el_dev->type, ++ par->family, proto); ++ ++ if_tag_stat_update(el_dev->name, uid, ++ skb->sk ? skb->sk : alternate_sk, ++ par->in ? IFS_RX : IFS_TX, ++ proto, skb->len); ++ } ++} ++ ++static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ const struct xt_qtaguid_match_info *info = par->matchinfo; ++ const struct file *filp; ++ bool got_sock = false; ++ struct sock *sk; ++ kuid_t sock_uid; ++ bool res; ++ bool set_sk_callback_lock = false; ++ ++ if (unlikely(module_passive)) ++ return (info->match ^ info->invert) == 0; ++ ++ MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", ++ par->hooknum, skb, par->in, par->out, par->family); ++ ++ atomic64_inc(&qtu_events.match_calls); ++ if (skb == NULL) { ++ res = (info->match ^ info->invert) == 0; ++ goto ret_res; ++ } ++ ++ switch (par->hooknum) { ++ case NF_INET_PRE_ROUTING: ++ case NF_INET_POST_ROUTING: ++ atomic64_inc(&qtu_events.match_calls_prepost); ++ iface_stat_update_from_skb(skb, par); ++ /* ++ * We are done in pre/post. The skb will get processed ++ * further alter. ++ */ ++ res = (info->match ^ info->invert); ++ goto ret_res; ++ break; ++ /* default: Fall through and do UID releated work */ ++ } ++ ++ sk = skb->sk; ++ /* ++ * When in TCP_TIME_WAIT the sk is not a "struct sock" but ++ * "struct inet_timewait_sock" which is missing fields. ++ * So we ignore it. ++ */ ++ if (sk && sk->sk_state == TCP_TIME_WAIT) ++ sk = NULL; ++ if (sk == NULL) { ++ /* ++ * A missing sk->sk_socket happens when packets are in-flight ++ * and the matching socket is already closed and gone. ++ */ ++ sk = qtaguid_find_sk(skb, par); ++ /* ++ * If we got the socket from the find_sk(), we will need to put ++ * it back, as nf_tproxy_get_sock_v4() got it. ++ */ ++ got_sock = sk; ++ if (sk) ++ atomic64_inc(&qtu_events.match_found_sk_in_ct); ++ else ++ atomic64_inc(&qtu_events.match_found_no_sk_in_ct); ++ } else { ++ atomic64_inc(&qtu_events.match_found_sk); ++ } ++ MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", ++ par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); ++ if (sk != NULL) { ++ set_sk_callback_lock = true; ++ read_lock_bh(&sk->sk_callback_lock); ++ MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", ++ par->hooknum, sk, sk->sk_socket, ++ sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); ++ filp = sk->sk_socket ? sk->sk_socket->file : NULL; ++ MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", ++ par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1); ++ } ++ ++ if (sk == NULL || sk->sk_socket == NULL) { ++ /* ++ * Here, the qtaguid_find_sk() using connection tracking ++ * couldn't find the owner, so for now we just count them ++ * against the system. ++ */ ++ /* ++ * TODO: unhack how to force just accounting. ++ * For now we only do iface stats when the uid-owner is not ++ * requested. ++ */ ++ if (!(info->match & XT_QTAGUID_UID)) ++ account_for_uid(skb, sk, 0, par); ++ MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", ++ par->hooknum, ++ sk ? sk->sk_socket : NULL); ++ res = (info->match ^ info->invert) == 0; ++ atomic64_inc(&qtu_events.match_no_sk); ++ goto put_sock_ret_res; ++ } else if (info->match & info->invert & XT_QTAGUID_SOCKET) { ++ res = false; ++ goto put_sock_ret_res; ++ } ++ filp = sk->sk_socket->file; ++ if (filp == NULL) { ++ MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); ++ account_for_uid(skb, sk, 0, par); ++ res = ((info->match ^ info->invert) & ++ (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; ++ atomic64_inc(&qtu_events.match_no_sk_file); ++ goto put_sock_ret_res; ++ } ++ sock_uid = filp->f_cred->fsuid; ++ /* ++ * TODO: unhack how to force just accounting. ++ * For now we only do iface stats when the uid-owner is not requested ++ */ ++ if (!(info->match & XT_QTAGUID_UID)) ++ account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par); ++ ++ /* ++ * The following two tests fail the match when: ++ * id not in range AND no inverted condition requested ++ * or id in range AND inverted condition requested ++ * Thus (!a && b) || (a && !b) == a ^ b ++ */ ++ if (info->match & XT_QTAGUID_UID) { ++ kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min); ++ kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max); ++ ++ if ((uid_gte(filp->f_cred->fsuid, uid_min) && ++ uid_lte(filp->f_cred->fsuid, uid_max)) ^ ++ !(info->invert & XT_QTAGUID_UID)) { ++ MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", ++ par->hooknum); ++ res = false; ++ goto put_sock_ret_res; ++ } ++ } ++ if (info->match & XT_QTAGUID_GID) { ++ kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min); ++ kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max); ++ ++ if ((gid_gte(filp->f_cred->fsgid, gid_min) && ++ gid_lte(filp->f_cred->fsgid, gid_max)) ^ ++ !(info->invert & XT_QTAGUID_GID)) { ++ MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", ++ par->hooknum); ++ res = false; ++ goto put_sock_ret_res; ++ } ++ } ++ MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum); ++ res = true; ++ ++put_sock_ret_res: ++ if (got_sock) ++ sock_gen_put(sk); ++ if (set_sk_callback_lock) ++ read_unlock_bh(&sk->sk_callback_lock); ++ret_res: ++ MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); ++ return res; ++} ++ ++#ifdef DDEBUG ++/* This function is not in xt_qtaguid_print.c because of locks visibility */ ++static void prdebug_full_state(int indent_level, const char *fmt, ...) ++{ ++ va_list args; ++ char *fmt_buff; ++ char *buff; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ fmt_buff = kasprintf(GFP_ATOMIC, ++ "qtaguid: %s(): %s {\n", __func__, fmt); ++ BUG_ON(!fmt_buff); ++ va_start(args, fmt); ++ buff = kvasprintf(GFP_ATOMIC, ++ fmt_buff, args); ++ BUG_ON(!buff); ++ pr_debug("%s", buff); ++ kfree(fmt_buff); ++ kfree(buff); ++ va_end(args); ++ ++ spin_lock_bh(&sock_tag_list_lock); ++ prdebug_sock_tag_tree(indent_level, &sock_tag_tree); ++ spin_unlock_bh(&sock_tag_list_lock); ++ ++ spin_lock_bh(&sock_tag_list_lock); ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); ++ prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ spin_unlock_bh(&sock_tag_list_lock); ++ ++ spin_lock_bh(&iface_stat_list_lock); ++ prdebug_iface_stat_list(indent_level, &iface_stat_list); ++ spin_unlock_bh(&iface_stat_list_lock); ++ ++ pr_debug("qtaguid: %s(): }\n", __func__); ++} ++#else ++static void prdebug_full_state(int indent_level, const char *fmt, ...) {} ++#endif ++ ++struct proc_ctrl_print_info { ++ struct sock *sk; /* socket found by reading to sk_pos */ ++ loff_t sk_pos; ++}; ++ ++static void *qtaguid_ctrl_proc_next(struct seq_file *m, void *v, loff_t *pos) ++{ ++ struct proc_ctrl_print_info *pcpi = m->private; ++ struct sock_tag *sock_tag_entry = v; ++ struct rb_node *node; ++ ++ (*pos)++; ++ ++ if (!v || v == SEQ_START_TOKEN) ++ return NULL; ++ ++ node = rb_next(&sock_tag_entry->sock_node); ++ if (!node) { ++ pcpi->sk = NULL; ++ sock_tag_entry = SEQ_START_TOKEN; ++ } else { ++ sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); ++ pcpi->sk = sock_tag_entry->sk; ++ } ++ pcpi->sk_pos = *pos; ++ return sock_tag_entry; ++} ++ ++static void *qtaguid_ctrl_proc_start(struct seq_file *m, loff_t *pos) ++{ ++ struct proc_ctrl_print_info *pcpi = m->private; ++ struct sock_tag *sock_tag_entry; ++ struct rb_node *node; ++ ++ spin_lock_bh(&sock_tag_list_lock); ++ ++ if (unlikely(module_passive)) ++ return NULL; ++ ++ if (*pos == 0) { ++ pcpi->sk_pos = 0; ++ node = rb_first(&sock_tag_tree); ++ if (!node) { ++ pcpi->sk = NULL; ++ return SEQ_START_TOKEN; ++ } ++ sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); ++ pcpi->sk = sock_tag_entry->sk; ++ } else { ++ sock_tag_entry = (pcpi->sk ? get_sock_stat_nl(pcpi->sk) : ++ NULL) ?: SEQ_START_TOKEN; ++ if (*pos != pcpi->sk_pos) { ++ /* seq_read skipped a next call */ ++ *pos = pcpi->sk_pos; ++ return qtaguid_ctrl_proc_next(m, sock_tag_entry, pos); ++ } ++ } ++ return sock_tag_entry; ++} ++ ++static void qtaguid_ctrl_proc_stop(struct seq_file *m, void *v) ++{ ++ spin_unlock_bh(&sock_tag_list_lock); ++} ++ ++/* ++ * Procfs reader to get all active socket tags using style "1)" as described in ++ * fs/proc/generic.c ++ */ ++static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) ++{ ++ struct sock_tag *sock_tag_entry = v; ++ uid_t uid; ++ long f_count; ++ ++ CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n", ++ current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); ++ ++ if (sock_tag_entry != SEQ_START_TOKEN) { ++ uid = get_uid_from_tag(sock_tag_entry->tag); ++ CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) " ++ "pid=%u\n", ++ sock_tag_entry->sk, ++ sock_tag_entry->tag, ++ uid, ++ sock_tag_entry->pid ++ ); ++ f_count = atomic_long_read( ++ &sock_tag_entry->socket->file->f_count); ++ seq_printf(m, "sock=%p tag=0x%llx (uid=%u) pid=%u " ++ "f_count=%lu\n", ++ sock_tag_entry->sk, ++ sock_tag_entry->tag, uid, ++ sock_tag_entry->pid, f_count); ++ } else { ++ seq_printf(m, "events: sockets_tagged=%llu " ++ "sockets_untagged=%llu " ++ "counter_set_changes=%llu " ++ "delete_cmds=%llu " ++ "iface_events=%llu " ++ "match_calls=%llu " ++ "match_calls_prepost=%llu " ++ "match_found_sk=%llu " ++ "match_found_sk_in_ct=%llu " ++ "match_found_no_sk_in_ct=%llu " ++ "match_no_sk=%llu " ++ "match_no_sk_file=%llu\n", ++ (u64)atomic64_read(&qtu_events.sockets_tagged), ++ (u64)atomic64_read(&qtu_events.sockets_untagged), ++ (u64)atomic64_read(&qtu_events.counter_set_changes), ++ (u64)atomic64_read(&qtu_events.delete_cmds), ++ (u64)atomic64_read(&qtu_events.iface_events), ++ (u64)atomic64_read(&qtu_events.match_calls), ++ (u64)atomic64_read(&qtu_events.match_calls_prepost), ++ (u64)atomic64_read(&qtu_events.match_found_sk), ++ (u64)atomic64_read(&qtu_events.match_found_sk_in_ct), ++ (u64)atomic64_read(&qtu_events.match_found_no_sk_in_ct), ++ (u64)atomic64_read(&qtu_events.match_no_sk), ++ (u64)atomic64_read(&qtu_events.match_no_sk_file)); ++ ++ /* Count the following as part of the last item_index */ ++ prdebug_full_state(0, "proc ctrl"); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Delete socket tags, and stat tags associated with a given ++ * accouting tag and uid. ++ */ ++static int ctrl_cmd_delete(const char *input) ++{ ++ char cmd; ++ int uid_int; ++ kuid_t uid; ++ uid_t entry_uid; ++ tag_t acct_tag; ++ tag_t tag; ++ int res, argc; ++ struct iface_stat *iface_entry; ++ struct rb_node *node; ++ struct sock_tag *st_entry; ++ struct rb_root st_to_free_tree = RB_ROOT; ++ struct tag_stat *ts_entry; ++ struct tag_counter_set *tcs_entry; ++ struct tag_ref *tr_entry; ++ struct uid_tag_data *utd_entry; ++ ++ argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid_int); ++ uid = make_kuid(&init_user_ns, uid_int); ++ CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " ++ "user_tag=0x%llx uid=%u\n", input, argc, cmd, ++ acct_tag, uid_int); ++ if (argc < 2) { ++ res = -EINVAL; ++ goto err; ++ } ++ if (!valid_atag(acct_tag)) { ++ pr_info("qtaguid: ctrl_delete(%s): invalid tag\n", input); ++ res = -EINVAL; ++ goto err; ++ } ++ if (argc < 3) { ++ uid = current_fsuid(); ++ uid_int = from_kuid(&init_user_ns, uid); ++ } else if (!can_impersonate_uid(uid)) { ++ pr_info("qtaguid: ctrl_delete(%s): " ++ "insufficient priv from pid=%u tgid=%u uid=%u\n", ++ input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); ++ res = -EPERM; ++ goto err; ++ } ++ ++ tag = combine_atag_with_uid(acct_tag, uid_int); ++ CT_DEBUG("qtaguid: ctrl_delete(%s): " ++ "looking for tag=0x%llx (uid=%u)\n", ++ input, tag, uid_int); ++ ++ /* Delete socket tags */ ++ spin_lock_bh(&sock_tag_list_lock); ++ node = rb_first(&sock_tag_tree); ++ while (node) { ++ st_entry = rb_entry(node, struct sock_tag, sock_node); ++ entry_uid = get_uid_from_tag(st_entry->tag); ++ node = rb_next(node); ++ if (entry_uid != uid_int) ++ continue; ++ ++ CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n", ++ input, st_entry->tag, entry_uid); ++ ++ if (!acct_tag || st_entry->tag == tag) { ++ rb_erase(&st_entry->sock_node, &sock_tag_tree); ++ /* Can't sockfd_put() within spinlock, do it later. */ ++ sock_tag_tree_insert(st_entry, &st_to_free_tree); ++ tr_entry = lookup_tag_ref(st_entry->tag, NULL); ++ BUG_ON(tr_entry->num_sock_tags <= 0); ++ tr_entry->num_sock_tags--; ++ /* ++ * TODO: remove if, and start failing. ++ * This is a hack to work around the fact that in some ++ * places we have "if (IS_ERR_OR_NULL(pqd_entry))" ++ * and are trying to work around apps ++ * that didn't open the /dev/xt_qtaguid. ++ */ ++ if (st_entry->list.next && st_entry->list.prev) ++ list_del(&st_entry->list); ++ } ++ } ++ spin_unlock_bh(&sock_tag_list_lock); ++ ++ sock_tag_tree_erase(&st_to_free_tree); ++ ++ /* Delete tag counter-sets */ ++ spin_lock_bh(&tag_counter_set_list_lock); ++ /* Counter sets are only on the uid tag, not full tag */ ++ tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); ++ if (tcs_entry) { ++ CT_DEBUG("qtaguid: ctrl_delete(%s): " ++ "erase tcs: tag=0x%llx (uid=%u) set=%d\n", ++ input, ++ tcs_entry->tn.tag, ++ get_uid_from_tag(tcs_entry->tn.tag), ++ tcs_entry->active_set); ++ rb_erase(&tcs_entry->tn.node, &tag_counter_set_tree); ++ kfree(tcs_entry); ++ } ++ spin_unlock_bh(&tag_counter_set_list_lock); ++ ++ /* ++ * If acct_tag is 0, then all entries belonging to uid are ++ * erased. ++ */ ++ spin_lock_bh(&iface_stat_list_lock); ++ list_for_each_entry(iface_entry, &iface_stat_list, list) { ++ spin_lock_bh(&iface_entry->tag_stat_list_lock); ++ node = rb_first(&iface_entry->tag_stat_tree); ++ while (node) { ++ ts_entry = rb_entry(node, struct tag_stat, tn.node); ++ entry_uid = get_uid_from_tag(ts_entry->tn.tag); ++ node = rb_next(node); ++ ++ CT_DEBUG("qtaguid: ctrl_delete(%s): " ++ "ts tag=0x%llx (uid=%u)\n", ++ input, ts_entry->tn.tag, entry_uid); ++ ++ if (entry_uid != uid_int) ++ continue; ++ if (!acct_tag || ts_entry->tn.tag == tag) { ++ CT_DEBUG("qtaguid: ctrl_delete(%s): " ++ "erase ts: %s 0x%llx %u\n", ++ input, iface_entry->ifname, ++ get_atag_from_tag(ts_entry->tn.tag), ++ entry_uid); ++ rb_erase(&ts_entry->tn.node, ++ &iface_entry->tag_stat_tree); ++ kfree(ts_entry); ++ } ++ } ++ spin_unlock_bh(&iface_entry->tag_stat_list_lock); ++ } ++ spin_unlock_bh(&iface_stat_list_lock); ++ ++ /* Cleanup the uid_tag_data */ ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ node = rb_first(&uid_tag_data_tree); ++ while (node) { ++ utd_entry = rb_entry(node, struct uid_tag_data, node); ++ entry_uid = utd_entry->uid; ++ node = rb_next(node); ++ ++ CT_DEBUG("qtaguid: ctrl_delete(%s): " ++ "utd uid=%u\n", ++ input, entry_uid); ++ ++ if (entry_uid != uid_int) ++ continue; ++ /* ++ * Go over the tag_refs, and those that don't have ++ * sock_tags using them are freed. ++ */ ++ put_tag_ref_tree(tag, utd_entry); ++ put_utd_entry(utd_entry); ++ } ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ ++ atomic64_inc(&qtu_events.delete_cmds); ++ res = 0; ++ ++err: ++ return res; ++} ++ ++static int ctrl_cmd_counter_set(const char *input) ++{ ++ char cmd; ++ uid_t uid = 0; ++ tag_t tag; ++ int res, argc; ++ struct tag_counter_set *tcs; ++ int counter_set; ++ ++ argc = sscanf(input, "%c %d %u", &cmd, &counter_set, &uid); ++ CT_DEBUG("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c " ++ "set=%d uid=%u\n", input, argc, cmd, ++ counter_set, uid); ++ if (argc != 3) { ++ res = -EINVAL; ++ goto err; ++ } ++ if (counter_set < 0 || counter_set >= IFS_MAX_COUNTER_SETS) { ++ pr_info("qtaguid: ctrl_counterset(%s): invalid counter_set range\n", ++ input); ++ res = -EINVAL; ++ goto err; ++ } ++ if (!can_manipulate_uids()) { ++ pr_info("qtaguid: ctrl_counterset(%s): " ++ "insufficient priv from pid=%u tgid=%u uid=%u\n", ++ input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); ++ res = -EPERM; ++ goto err; ++ } ++ ++ tag = make_tag_from_uid(uid); ++ spin_lock_bh(&tag_counter_set_list_lock); ++ tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); ++ if (!tcs) { ++ tcs = kzalloc(sizeof(*tcs), GFP_ATOMIC); ++ if (!tcs) { ++ spin_unlock_bh(&tag_counter_set_list_lock); ++ pr_err("qtaguid: ctrl_counterset(%s): " ++ "failed to alloc counter set\n", ++ input); ++ res = -ENOMEM; ++ goto err; ++ } ++ tcs->tn.tag = tag; ++ tag_counter_set_tree_insert(tcs, &tag_counter_set_tree); ++ CT_DEBUG("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx " ++ "(uid=%u) set=%d\n", ++ input, tag, get_uid_from_tag(tag), counter_set); ++ } ++ tcs->active_set = counter_set; ++ spin_unlock_bh(&tag_counter_set_list_lock); ++ atomic64_inc(&qtu_events.counter_set_changes); ++ res = 0; ++ ++err: ++ return res; ++} ++ ++static int ctrl_cmd_tag(const char *input) ++{ ++ char cmd; ++ int sock_fd = 0; ++ kuid_t uid; ++ unsigned int uid_int = 0; ++ tag_t acct_tag = make_atag_from_value(0); ++ tag_t full_tag; ++ struct socket *el_socket; ++ int res, argc; ++ struct sock_tag *sock_tag_entry; ++ struct tag_ref *tag_ref_entry; ++ struct uid_tag_data *uid_tag_data_entry; ++ struct proc_qtu_data *pqd_entry; ++ ++ /* Unassigned args will get defaulted later. */ ++ argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid_int); ++ uid = make_kuid(&init_user_ns, uid_int); ++ CT_DEBUG("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " ++ "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, ++ acct_tag, uid_int); ++ if (argc < 2) { ++ res = -EINVAL; ++ goto err; ++ } ++ el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ ++ if (!el_socket) { ++ pr_info("qtaguid: ctrl_tag(%s): failed to lookup" ++ " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", ++ input, sock_fd, res, current->pid, current->tgid, ++ from_kuid(&init_user_ns, current_fsuid())); ++ goto err; ++ } ++ CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", ++ input, atomic_long_read(&el_socket->file->f_count), ++ el_socket->sk); ++ if (argc < 3) { ++ acct_tag = make_atag_from_value(0); ++ } else if (!valid_atag(acct_tag)) { ++ pr_info("qtaguid: ctrl_tag(%s): invalid tag\n", input); ++ res = -EINVAL; ++ goto err_put; ++ } ++ CT_DEBUG("qtaguid: ctrl_tag(%s): " ++ "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " ++ "ctrl.gid=%u in_group()=%d in_egroup()=%d\n", ++ input, current->pid, current->tgid, ++ from_kuid(&init_user_ns, current_uid()), ++ from_kuid(&init_user_ns, current_euid()), ++ from_kuid(&init_user_ns, current_fsuid()), ++ from_kgid(&init_user_ns, xt_qtaguid_ctrl_file->gid), ++ in_group_p(xt_qtaguid_ctrl_file->gid), ++ in_egroup_p(xt_qtaguid_ctrl_file->gid)); ++ if (argc < 4) { ++ uid = current_fsuid(); ++ uid_int = from_kuid(&init_user_ns, uid); ++ } else if (!can_impersonate_uid(uid)) { ++ pr_info("qtaguid: ctrl_tag(%s): " ++ "insufficient priv from pid=%u tgid=%u uid=%u\n", ++ input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); ++ res = -EPERM; ++ goto err_put; ++ } ++ full_tag = combine_atag_with_uid(acct_tag, uid_int); ++ ++ spin_lock_bh(&sock_tag_list_lock); ++ sock_tag_entry = get_sock_stat_nl(el_socket->sk); ++ tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry); ++ if (IS_ERR(tag_ref_entry)) { ++ res = PTR_ERR(tag_ref_entry); ++ spin_unlock_bh(&sock_tag_list_lock); ++ goto err_put; ++ } ++ tag_ref_entry->num_sock_tags++; ++ if (sock_tag_entry) { ++ struct tag_ref *prev_tag_ref_entry; ++ ++ CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p " ++ "st@%p ...->f_count=%ld\n", ++ input, el_socket->sk, sock_tag_entry, ++ atomic_long_read(&el_socket->file->f_count)); ++ /* ++ * This is a re-tagging, so release the sock_fd that was ++ * locked at the time of the 1st tagging. ++ * There is still the ref from this call's sockfd_lookup() so ++ * it can be done within the spinlock. ++ */ ++ sockfd_put(sock_tag_entry->socket); ++ prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, ++ &uid_tag_data_entry); ++ BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry)); ++ BUG_ON(prev_tag_ref_entry->num_sock_tags <= 0); ++ prev_tag_ref_entry->num_sock_tags--; ++ sock_tag_entry->tag = full_tag; ++ } else { ++ CT_DEBUG("qtaguid: ctrl_tag(%s): newtag for sk=%p\n", ++ input, el_socket->sk); ++ sock_tag_entry = kzalloc(sizeof(*sock_tag_entry), ++ GFP_ATOMIC); ++ if (!sock_tag_entry) { ++ pr_err("qtaguid: ctrl_tag(%s): " ++ "socket tag alloc failed\n", ++ input); ++ spin_unlock_bh(&sock_tag_list_lock); ++ res = -ENOMEM; ++ goto err_tag_unref_put; ++ } ++ sock_tag_entry->sk = el_socket->sk; ++ sock_tag_entry->socket = el_socket; ++ sock_tag_entry->pid = current->tgid; ++ sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int); ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ pqd_entry = proc_qtu_data_tree_search( ++ &proc_qtu_data_tree, current->tgid); ++ /* ++ * TODO: remove if, and start failing. ++ * At first, we want to catch user-space code that is not ++ * opening the /dev/xt_qtaguid. ++ */ ++ if (IS_ERR_OR_NULL(pqd_entry)) ++ pr_warn_once( ++ "qtaguid: %s(): " ++ "User space forgot to open /dev/xt_qtaguid? " ++ "pid=%u tgid=%u uid=%u\n", __func__, ++ current->pid, current->tgid, ++ from_kuid(&init_user_ns, current_fsuid())); ++ else ++ list_add(&sock_tag_entry->list, ++ &pqd_entry->sock_tag_list); ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ ++ sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); ++ atomic64_inc(&qtu_events.sockets_tagged); ++ } ++ spin_unlock_bh(&sock_tag_list_lock); ++ /* We keep the ref to the socket (file) until it is untagged */ ++ CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n", ++ input, sock_tag_entry, ++ atomic_long_read(&el_socket->file->f_count)); ++ return 0; ++ ++err_tag_unref_put: ++ BUG_ON(tag_ref_entry->num_sock_tags <= 0); ++ tag_ref_entry->num_sock_tags--; ++ free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); ++err_put: ++ CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n", ++ input, atomic_long_read(&el_socket->file->f_count) - 1); ++ /* Release the sock_fd that was grabbed by sockfd_lookup(). */ ++ sockfd_put(el_socket); ++ return res; ++ ++err: ++ CT_DEBUG("qtaguid: ctrl_tag(%s): done.\n", input); ++ return res; ++} ++ ++static int ctrl_cmd_untag(const char *input) ++{ ++ char cmd; ++ int sock_fd = 0; ++ struct socket *el_socket; ++ int res, argc; ++ struct sock_tag *sock_tag_entry; ++ struct tag_ref *tag_ref_entry; ++ struct uid_tag_data *utd_entry; ++ struct proc_qtu_data *pqd_entry; ++ ++ argc = sscanf(input, "%c %d", &cmd, &sock_fd); ++ CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", ++ input, argc, cmd, sock_fd); ++ if (argc < 2) { ++ res = -EINVAL; ++ goto err; ++ } ++ el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ ++ if (!el_socket) { ++ pr_info("qtaguid: ctrl_untag(%s): failed to lookup" ++ " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", ++ input, sock_fd, res, current->pid, current->tgid, ++ from_kuid(&init_user_ns, current_fsuid())); ++ goto err; ++ } ++ CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", ++ input, atomic_long_read(&el_socket->file->f_count), ++ el_socket->sk); ++ spin_lock_bh(&sock_tag_list_lock); ++ sock_tag_entry = get_sock_stat_nl(el_socket->sk); ++ if (!sock_tag_entry) { ++ spin_unlock_bh(&sock_tag_list_lock); ++ res = -EINVAL; ++ goto err_put; ++ } ++ /* ++ * The socket already belongs to the current process ++ * so it can do whatever it wants to it. ++ */ ++ rb_erase(&sock_tag_entry->sock_node, &sock_tag_tree); ++ ++ tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, &utd_entry); ++ BUG_ON(!tag_ref_entry); ++ BUG_ON(tag_ref_entry->num_sock_tags <= 0); ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ pqd_entry = proc_qtu_data_tree_search( ++ &proc_qtu_data_tree, current->tgid); ++ /* ++ * TODO: remove if, and start failing. ++ * At first, we want to catch user-space code that is not ++ * opening the /dev/xt_qtaguid. ++ */ ++ if (IS_ERR_OR_NULL(pqd_entry)) ++ pr_warn_once("qtaguid: %s(): " ++ "User space forgot to open /dev/xt_qtaguid? " ++ "pid=%u tgid=%u uid=%u\n", __func__, ++ current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); ++ else ++ list_del(&sock_tag_entry->list); ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ /* ++ * We don't free tag_ref from the utd_entry here, ++ * only during a cmd_delete(). ++ */ ++ tag_ref_entry->num_sock_tags--; ++ spin_unlock_bh(&sock_tag_list_lock); ++ /* ++ * Release the sock_fd that was grabbed at tag time, ++ * and once more for the sockfd_lookup() here. ++ */ ++ sockfd_put(sock_tag_entry->socket); ++ CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n", ++ input, sock_tag_entry, ++ atomic_long_read(&el_socket->file->f_count) - 1); ++ sockfd_put(el_socket); ++ ++ kfree(sock_tag_entry); ++ atomic64_inc(&qtu_events.sockets_untagged); ++ ++ return 0; ++ ++err_put: ++ CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n", ++ input, atomic_long_read(&el_socket->file->f_count) - 1); ++ /* Release the sock_fd that was grabbed by sockfd_lookup(). */ ++ sockfd_put(el_socket); ++ return res; ++ ++err: ++ CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input); ++ return res; ++} ++ ++static ssize_t qtaguid_ctrl_parse(const char *input, size_t count) ++{ ++ char cmd; ++ ssize_t res; ++ ++ CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", ++ input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); ++ ++ cmd = input[0]; ++ /* Collect params for commands */ ++ switch (cmd) { ++ case 'd': ++ res = ctrl_cmd_delete(input); ++ break; ++ ++ case 's': ++ res = ctrl_cmd_counter_set(input); ++ break; ++ ++ case 't': ++ res = ctrl_cmd_tag(input); ++ break; ++ ++ case 'u': ++ res = ctrl_cmd_untag(input); ++ break; ++ ++ default: ++ res = -EINVAL; ++ goto err; ++ } ++ if (!res) ++ res = count; ++err: ++ CT_DEBUG("qtaguid: ctrl(%s): res=%zd\n", input, res); ++ return res; ++} ++ ++#define MAX_QTAGUID_CTRL_INPUT_LEN 255 ++static ssize_t qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, ++ size_t count, loff_t *offp) ++{ ++ char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN]; ++ ++ if (unlikely(module_passive)) ++ return count; ++ ++ if (count >= MAX_QTAGUID_CTRL_INPUT_LEN) ++ return -EINVAL; ++ ++ if (copy_from_user(input_buf, buffer, count)) ++ return -EFAULT; ++ ++ input_buf[count] = '\0'; ++ return qtaguid_ctrl_parse(input_buf, count); ++} ++ ++struct proc_print_info { ++ struct iface_stat *iface_entry; ++ int item_index; ++ tag_t tag; /* tag found by reading to tag_pos */ ++ off_t tag_pos; ++ int tag_item_index; ++}; ++ ++static void pp_stats_header(struct seq_file *m) ++{ ++ seq_puts(m, ++ "idx iface acct_tag_hex uid_tag_int cnt_set " ++ "rx_bytes rx_packets " ++ "tx_bytes tx_packets " ++ "rx_tcp_bytes rx_tcp_packets " ++ "rx_udp_bytes rx_udp_packets " ++ "rx_other_bytes rx_other_packets " ++ "tx_tcp_bytes tx_tcp_packets " ++ "tx_udp_bytes tx_udp_packets " ++ "tx_other_bytes tx_other_packets\n"); ++} ++ ++static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, ++ int cnt_set) ++{ ++ int ret; ++ struct data_counters *cnts; ++ tag_t tag = ts_entry->tn.tag; ++ uid_t stat_uid = get_uid_from_tag(tag); ++ struct proc_print_info *ppi = m->private; ++ /* Detailed tags are not available to everybody */ ++ if (get_atag_from_tag(tag) && !can_read_other_uid_stats( ++ make_kuid(&init_user_ns,stat_uid))) { ++ CT_DEBUG("qtaguid: stats line: " ++ "%s 0x%llx %u: insufficient priv " ++ "from pid=%u tgid=%u uid=%u stats.gid=%u\n", ++ ppi->iface_entry->ifname, ++ get_atag_from_tag(tag), stat_uid, ++ current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()), ++ from_kgid(&init_user_ns,xt_qtaguid_stats_file->gid)); ++ return 0; ++ } ++ ppi->item_index++; ++ cnts = &ts_entry->counters; ++ ret = seq_printf(m, "%d %s 0x%llx %u %u " ++ "%llu %llu " ++ "%llu %llu " ++ "%llu %llu " ++ "%llu %llu " ++ "%llu %llu " ++ "%llu %llu " ++ "%llu %llu " ++ "%llu %llu\n", ++ ppi->item_index, ++ ppi->iface_entry->ifname, ++ get_atag_from_tag(tag), ++ stat_uid, ++ cnt_set, ++ dc_sum_bytes(cnts, cnt_set, IFS_RX), ++ dc_sum_packets(cnts, cnt_set, IFS_RX), ++ dc_sum_bytes(cnts, cnt_set, IFS_TX), ++ dc_sum_packets(cnts, cnt_set, IFS_TX), ++ cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, ++ cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, ++ cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, ++ cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, ++ cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, ++ cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, ++ cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, ++ cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, ++ cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, ++ cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, ++ cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, ++ cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); ++ return ret ?: 1; ++} ++ ++static bool pp_sets(struct seq_file *m, struct tag_stat *ts_entry) ++{ ++ int ret; ++ int counter_set; ++ for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS; ++ counter_set++) { ++ ret = pp_stats_line(m, ts_entry, counter_set); ++ if (ret < 0) ++ return false; ++ } ++ return true; ++} ++ ++static int qtaguid_stats_proc_iface_stat_ptr_valid(struct iface_stat *ptr) ++{ ++ struct iface_stat *iface_entry; ++ ++ if (!ptr) ++ return false; ++ ++ list_for_each_entry(iface_entry, &iface_stat_list, list) ++ if (iface_entry == ptr) ++ return true; ++ return false; ++} ++ ++static void qtaguid_stats_proc_next_iface_entry(struct proc_print_info *ppi) ++{ ++ spin_unlock_bh(&ppi->iface_entry->tag_stat_list_lock); ++ list_for_each_entry_continue(ppi->iface_entry, &iface_stat_list, list) { ++ spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock); ++ return; ++ } ++ ppi->iface_entry = NULL; ++} ++ ++static void *qtaguid_stats_proc_next(struct seq_file *m, void *v, loff_t *pos) ++{ ++ struct proc_print_info *ppi = m->private; ++ struct tag_stat *ts_entry; ++ struct rb_node *node; ++ ++ if (!v) { ++ pr_err("qtaguid: %s(): unexpected v: NULL\n", __func__); ++ return NULL; ++ } ++ ++ (*pos)++; ++ ++ if (!ppi->iface_entry || unlikely(module_passive)) ++ return NULL; ++ ++ if (v == SEQ_START_TOKEN) ++ node = rb_first(&ppi->iface_entry->tag_stat_tree); ++ else ++ node = rb_next(&((struct tag_stat *)v)->tn.node); ++ ++ while (!node) { ++ qtaguid_stats_proc_next_iface_entry(ppi); ++ if (!ppi->iface_entry) ++ return NULL; ++ node = rb_first(&ppi->iface_entry->tag_stat_tree); ++ } ++ ++ ts_entry = rb_entry(node, struct tag_stat, tn.node); ++ ppi->tag = ts_entry->tn.tag; ++ ppi->tag_pos = *pos; ++ ppi->tag_item_index = ppi->item_index; ++ return ts_entry; ++} ++ ++static void *qtaguid_stats_proc_start(struct seq_file *m, loff_t *pos) ++{ ++ struct proc_print_info *ppi = m->private; ++ struct tag_stat *ts_entry = NULL; ++ ++ spin_lock_bh(&iface_stat_list_lock); ++ ++ if (*pos == 0) { ++ ppi->item_index = 1; ++ ppi->tag_pos = 0; ++ if (list_empty(&iface_stat_list)) { ++ ppi->iface_entry = NULL; ++ } else { ++ ppi->iface_entry = list_first_entry(&iface_stat_list, ++ struct iface_stat, ++ list); ++ spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock); ++ } ++ return SEQ_START_TOKEN; ++ } ++ if (!qtaguid_stats_proc_iface_stat_ptr_valid(ppi->iface_entry)) { ++ if (ppi->iface_entry) { ++ pr_err("qtaguid: %s(): iface_entry %p not found\n", ++ __func__, ppi->iface_entry); ++ ppi->iface_entry = NULL; ++ } ++ return NULL; ++ } ++ ++ spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock); ++ ++ if (!ppi->tag_pos) { ++ /* seq_read skipped first next call */ ++ ts_entry = SEQ_START_TOKEN; ++ } else { ++ ts_entry = tag_stat_tree_search( ++ &ppi->iface_entry->tag_stat_tree, ppi->tag); ++ if (!ts_entry) { ++ pr_info("qtaguid: %s(): tag_stat.tag 0x%llx not found. Abort.\n", ++ __func__, ppi->tag); ++ return NULL; ++ } ++ } ++ ++ if (*pos == ppi->tag_pos) { /* normal resume */ ++ ppi->item_index = ppi->tag_item_index; ++ } else { ++ /* seq_read skipped a next call */ ++ *pos = ppi->tag_pos; ++ ts_entry = qtaguid_stats_proc_next(m, ts_entry, pos); ++ } ++ ++ return ts_entry; ++} ++ ++static void qtaguid_stats_proc_stop(struct seq_file *m, void *v) ++{ ++ struct proc_print_info *ppi = m->private; ++ if (ppi->iface_entry) ++ spin_unlock_bh(&ppi->iface_entry->tag_stat_list_lock); ++ spin_unlock_bh(&iface_stat_list_lock); ++} ++ ++/* ++ * Procfs reader to get all tag stats using style "1)" as described in ++ * fs/proc/generic.c ++ * Groups all protocols tx/rx bytes. ++ */ ++static int qtaguid_stats_proc_show(struct seq_file *m, void *v) ++{ ++ struct tag_stat *ts_entry = v; ++ ++ if (v == SEQ_START_TOKEN) ++ pp_stats_header(m); ++ else ++ pp_sets(m, ts_entry); ++ ++ return 0; ++} ++ ++/*------------------------------------------*/ ++static int qtudev_open(struct inode *inode, struct file *file) ++{ ++ struct uid_tag_data *utd_entry; ++ struct proc_qtu_data *pqd_entry; ++ struct proc_qtu_data *new_pqd_entry; ++ int res; ++ bool utd_entry_found; ++ ++ if (unlikely(qtu_proc_handling_passive)) ++ return 0; ++ ++ DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n", ++ current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); ++ ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ ++ /* Look for existing uid data, or alloc one. */ ++ utd_entry = get_uid_data(from_kuid(&init_user_ns, current_fsuid()), &utd_entry_found); ++ if (IS_ERR_OR_NULL(utd_entry)) { ++ res = PTR_ERR(utd_entry); ++ goto err_unlock; ++ } ++ ++ /* Look for existing PID based proc_data */ ++ pqd_entry = proc_qtu_data_tree_search(&proc_qtu_data_tree, ++ current->tgid); ++ if (pqd_entry) { ++ pr_err("qtaguid: qtudev_open(): %u/%u %u " ++ "%s already opened\n", ++ current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()), ++ QTU_DEV_NAME); ++ res = -EBUSY; ++ goto err_unlock_free_utd; ++ } ++ ++ new_pqd_entry = kzalloc(sizeof(*new_pqd_entry), GFP_ATOMIC); ++ if (!new_pqd_entry) { ++ pr_err("qtaguid: qtudev_open(): %u/%u %u: " ++ "proc data alloc failed\n", ++ current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); ++ res = -ENOMEM; ++ goto err_unlock_free_utd; ++ } ++ new_pqd_entry->pid = current->tgid; ++ INIT_LIST_HEAD(&new_pqd_entry->sock_tag_list); ++ new_pqd_entry->parent_tag_data = utd_entry; ++ utd_entry->num_pqd++; ++ ++ proc_qtu_data_tree_insert(new_pqd_entry, ++ &proc_qtu_data_tree); ++ ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n", ++ from_kuid(&init_user_ns, current_fsuid()), new_pqd_entry); ++ file->private_data = new_pqd_entry; ++ return 0; ++ ++err_unlock_free_utd: ++ if (!utd_entry_found) { ++ rb_erase(&utd_entry->node, &uid_tag_data_tree); ++ kfree(utd_entry); ++ } ++err_unlock: ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ return res; ++} ++ ++static int qtudev_release(struct inode *inode, struct file *file) ++{ ++ struct proc_qtu_data *pqd_entry = file->private_data; ++ struct uid_tag_data *utd_entry = pqd_entry->parent_tag_data; ++ struct sock_tag *st_entry; ++ struct rb_root st_to_free_tree = RB_ROOT; ++ struct list_head *entry, *next; ++ struct tag_ref *tr; ++ ++ if (unlikely(qtu_proc_handling_passive)) ++ return 0; ++ ++ /* ++ * Do not trust the current->pid, it might just be a kworker cleaning ++ * up after a dead proc. ++ */ ++ DR_DEBUG("qtaguid: qtudev_release(): " ++ "pid=%u tgid=%u uid=%u " ++ "pqd_entry=%p->pid=%u utd_entry=%p->active_tags=%d\n", ++ current->pid, current->tgid, pqd_entry->parent_tag_data->uid, ++ pqd_entry, pqd_entry->pid, utd_entry, ++ utd_entry->num_active_tags); ++ ++ spin_lock_bh(&sock_tag_list_lock); ++ spin_lock_bh(&uid_tag_data_tree_lock); ++ ++ list_for_each_safe(entry, next, &pqd_entry->sock_tag_list) { ++ st_entry = list_entry(entry, struct sock_tag, list); ++ DR_DEBUG("qtaguid: %s(): " ++ "erase sock_tag=%p->sk=%p pid=%u tgid=%u uid=%u\n", ++ __func__, ++ st_entry, st_entry->sk, ++ current->pid, current->tgid, ++ pqd_entry->parent_tag_data->uid); ++ ++ utd_entry = uid_tag_data_tree_search( ++ &uid_tag_data_tree, ++ get_uid_from_tag(st_entry->tag)); ++ BUG_ON(IS_ERR_OR_NULL(utd_entry)); ++ DR_DEBUG("qtaguid: %s(): " ++ "looking for tag=0x%llx in utd_entry=%p\n", __func__, ++ st_entry->tag, utd_entry); ++ tr = tag_ref_tree_search(&utd_entry->tag_ref_tree, ++ st_entry->tag); ++ BUG_ON(!tr); ++ BUG_ON(tr->num_sock_tags <= 0); ++ tr->num_sock_tags--; ++ free_tag_ref_from_utd_entry(tr, utd_entry); ++ ++ rb_erase(&st_entry->sock_node, &sock_tag_tree); ++ list_del(&st_entry->list); ++ /* Can't sockfd_put() within spinlock, do it later. */ ++ sock_tag_tree_insert(st_entry, &st_to_free_tree); ++ ++ /* ++ * Try to free the utd_entry if no other proc_qtu_data is ++ * using it (num_pqd is 0) and it doesn't have active tags ++ * (num_active_tags is 0). ++ */ ++ put_utd_entry(utd_entry); ++ } ++ ++ rb_erase(&pqd_entry->node, &proc_qtu_data_tree); ++ BUG_ON(pqd_entry->parent_tag_data->num_pqd < 1); ++ pqd_entry->parent_tag_data->num_pqd--; ++ put_utd_entry(pqd_entry->parent_tag_data); ++ kfree(pqd_entry); ++ file->private_data = NULL; ++ ++ spin_unlock_bh(&uid_tag_data_tree_lock); ++ spin_unlock_bh(&sock_tag_list_lock); ++ ++ ++ sock_tag_tree_erase(&st_to_free_tree); ++ ++ prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__, ++ current->pid, current->tgid); ++ return 0; ++} ++ ++/*------------------------------------------*/ ++static const struct file_operations qtudev_fops = { ++ .owner = THIS_MODULE, ++ .open = qtudev_open, ++ .release = qtudev_release, ++}; ++ ++static struct miscdevice qtu_device = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = QTU_DEV_NAME, ++ .fops = &qtudev_fops, ++ /* How sad it doesn't allow for defaults: .mode = S_IRUGO | S_IWUSR */ ++}; ++ ++static const struct seq_operations proc_qtaguid_ctrl_seqops = { ++ .start = qtaguid_ctrl_proc_start, ++ .next = qtaguid_ctrl_proc_next, ++ .stop = qtaguid_ctrl_proc_stop, ++ .show = qtaguid_ctrl_proc_show, ++}; ++ ++static int proc_qtaguid_ctrl_open(struct inode *inode, struct file *file) ++{ ++ return seq_open_private(file, &proc_qtaguid_ctrl_seqops, ++ sizeof(struct proc_ctrl_print_info)); ++} ++ ++static const struct file_operations proc_qtaguid_ctrl_fops = { ++ .open = proc_qtaguid_ctrl_open, ++ .read = seq_read, ++ .write = qtaguid_ctrl_proc_write, ++ .llseek = seq_lseek, ++ .release = seq_release_private, ++}; ++ ++static const struct seq_operations proc_qtaguid_stats_seqops = { ++ .start = qtaguid_stats_proc_start, ++ .next = qtaguid_stats_proc_next, ++ .stop = qtaguid_stats_proc_stop, ++ .show = qtaguid_stats_proc_show, ++}; ++ ++static int proc_qtaguid_stats_open(struct inode *inode, struct file *file) ++{ ++ return seq_open_private(file, &proc_qtaguid_stats_seqops, ++ sizeof(struct proc_print_info)); ++} ++ ++static const struct file_operations proc_qtaguid_stats_fops = { ++ .open = proc_qtaguid_stats_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release_private, ++}; ++ ++/*------------------------------------------*/ ++static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) ++{ ++ int ret; ++ *res_procdir = proc_mkdir(module_procdirname, init_net.proc_net); ++ if (!*res_procdir) { ++ pr_err("qtaguid: failed to create proc/.../xt_qtaguid\n"); ++ ret = -ENOMEM; ++ goto no_dir; ++ } ++ ++ xt_qtaguid_ctrl_file = proc_create_data("ctrl", proc_ctrl_perms, ++ *res_procdir, ++ &proc_qtaguid_ctrl_fops, ++ NULL); ++ if (!xt_qtaguid_ctrl_file) { ++ pr_err("qtaguid: failed to create xt_qtaguid/ctrl " ++ " file\n"); ++ ret = -ENOMEM; ++ goto no_ctrl_entry; ++ } ++ ++ xt_qtaguid_stats_file = proc_create_data("stats", proc_stats_perms, ++ *res_procdir, ++ &proc_qtaguid_stats_fops, ++ NULL); ++ if (!xt_qtaguid_stats_file) { ++ pr_err("qtaguid: failed to create xt_qtaguid/stats " ++ "file\n"); ++ ret = -ENOMEM; ++ goto no_stats_entry; ++ } ++ /* ++ * TODO: add support counter hacking ++ * xt_qtaguid_stats_file->write_proc = qtaguid_stats_proc_write; ++ */ ++ return 0; ++ ++no_stats_entry: ++ remove_proc_entry("ctrl", *res_procdir); ++no_ctrl_entry: ++ remove_proc_entry("xt_qtaguid", NULL); ++no_dir: ++ return ret; ++} ++ ++static struct xt_match qtaguid_mt_reg __read_mostly = { ++ /* ++ * This module masquerades as the "owner" module so that iptables ++ * tools can deal with it. ++ */ ++ .name = "owner", ++ .revision = 1, ++ .family = NFPROTO_UNSPEC, ++ .match = qtaguid_mt, ++ .matchsize = sizeof(struct xt_qtaguid_match_info), ++ .me = THIS_MODULE, ++}; ++ ++static int __init qtaguid_mt_init(void) ++{ ++ if (qtaguid_proc_register(&xt_qtaguid_procdir) ++ || iface_stat_init(xt_qtaguid_procdir) ++ || xt_register_match(&qtaguid_mt_reg) ++ || misc_register(&qtu_device)) ++ return -1; ++ return 0; ++} ++ ++/* ++ * TODO: allow unloading of the module. ++ * For now stats are permanent. ++ * Kconfig forces'y/n' and never an 'm'. ++ */ ++ ++module_init(qtaguid_mt_init); ++MODULE_AUTHOR("jpa <jpa@google.com>"); ++MODULE_DESCRIPTION("Xtables: socket owner+tag matching and associated stats"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_owner"); ++MODULE_ALIAS("ip6t_owner"); ++MODULE_ALIAS("ipt_qtaguid"); ++MODULE_ALIAS("ip6t_qtaguid"); +diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h +new file mode 100644 +index 0000000..6dc14a9 +--- /dev/null ++++ b/net/netfilter/xt_qtaguid_internal.h +@@ -0,0 +1,352 @@ ++/* ++ * Kernel iptables module to track stats for packets based on user tags. ++ * ++ * (C) 2011 Google, Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef __XT_QTAGUID_INTERNAL_H__ ++#define __XT_QTAGUID_INTERNAL_H__ ++ ++#include <linux/types.h> ++#include <linux/rbtree.h> ++#include <linux/spinlock_types.h> ++#include <linux/workqueue.h> ++ ++/* Iface handling */ ++#define IDEBUG_MASK (1<<0) ++/* Iptable Matching. Per packet. */ ++#define MDEBUG_MASK (1<<1) ++/* Red-black tree handling. Per packet. */ ++#define RDEBUG_MASK (1<<2) ++/* procfs ctrl/stats handling */ ++#define CDEBUG_MASK (1<<3) ++/* dev and resource tracking */ ++#define DDEBUG_MASK (1<<4) ++ ++/* E.g (IDEBUG_MASK | CDEBUG_MASK | DDEBUG_MASK) */ ++#define DEFAULT_DEBUG_MASK 0 ++ ++/* ++ * (Un)Define these *DEBUG to compile out/in the pr_debug calls. ++ * All undef: text size ~ 0x3030; all def: ~ 0x4404. ++ */ ++#define IDEBUG ++#define MDEBUG ++#define RDEBUG ++#define CDEBUG ++#define DDEBUG ++ ++#define MSK_DEBUG(mask, ...) do { \ ++ if (unlikely(qtaguid_debug_mask & (mask))) \ ++ pr_debug(__VA_ARGS__); \ ++ } while (0) ++#ifdef IDEBUG ++#define IF_DEBUG(...) MSK_DEBUG(IDEBUG_MASK, __VA_ARGS__) ++#else ++#define IF_DEBUG(...) no_printk(__VA_ARGS__) ++#endif ++#ifdef MDEBUG ++#define MT_DEBUG(...) MSK_DEBUG(MDEBUG_MASK, __VA_ARGS__) ++#else ++#define MT_DEBUG(...) no_printk(__VA_ARGS__) ++#endif ++#ifdef RDEBUG ++#define RB_DEBUG(...) MSK_DEBUG(RDEBUG_MASK, __VA_ARGS__) ++#else ++#define RB_DEBUG(...) no_printk(__VA_ARGS__) ++#endif ++#ifdef CDEBUG ++#define CT_DEBUG(...) MSK_DEBUG(CDEBUG_MASK, __VA_ARGS__) ++#else ++#define CT_DEBUG(...) no_printk(__VA_ARGS__) ++#endif ++#ifdef DDEBUG ++#define DR_DEBUG(...) MSK_DEBUG(DDEBUG_MASK, __VA_ARGS__) ++#else ++#define DR_DEBUG(...) no_printk(__VA_ARGS__) ++#endif ++ ++extern uint qtaguid_debug_mask; ++ ++/*---------------------------------------------------------------------------*/ ++/* ++ * Tags: ++ * ++ * They represent what the data usage counters will be tracked against. ++ * By default a tag is just based on the UID. ++ * The UID is used as the base for policing, and can not be ignored. ++ * So a tag will always at least represent a UID (uid_tag). ++ * ++ * A tag can be augmented with an "accounting tag" which is associated ++ * with a UID. ++ * User space can set the acct_tag portion of the tag which is then used ++ * with sockets: all data belonging to that socket will be counted against the ++ * tag. The policing is then based on the tag's uid_tag portion, ++ * and stats are collected for the acct_tag portion separately. ++ * ++ * There could be ++ * a: {acct_tag=1, uid_tag=10003} ++ * b: {acct_tag=2, uid_tag=10003} ++ * c: {acct_tag=3, uid_tag=10003} ++ * d: {acct_tag=0, uid_tag=10003} ++ * a, b, and c represent tags associated with specific sockets. ++ * d is for the totals for that uid, including all untagged traffic. ++ * Typically d is used with policing/quota rules. ++ * ++ * We want tag_t big enough to distinguish uid_t and acct_tag. ++ * It might become a struct if needed. ++ * Nothing should be using it as an int. ++ */ ++typedef uint64_t tag_t; /* Only used via accessors */ ++ ++#define TAG_UID_MASK 0xFFFFFFFFULL ++#define TAG_ACCT_MASK (~0xFFFFFFFFULL) ++ ++static inline int tag_compare(tag_t t1, tag_t t2) ++{ ++ return t1 < t2 ? -1 : t1 == t2 ? 0 : 1; ++} ++ ++static inline tag_t combine_atag_with_uid(tag_t acct_tag, uid_t uid) ++{ ++ return acct_tag | uid; ++} ++static inline tag_t make_tag_from_uid(uid_t uid) ++{ ++ return uid; ++} ++static inline uid_t get_uid_from_tag(tag_t tag) ++{ ++ return tag & TAG_UID_MASK; ++} ++static inline tag_t get_utag_from_tag(tag_t tag) ++{ ++ return tag & TAG_UID_MASK; ++} ++static inline tag_t get_atag_from_tag(tag_t tag) ++{ ++ return tag & TAG_ACCT_MASK; ++} ++ ++static inline bool valid_atag(tag_t tag) ++{ ++ return !(tag & TAG_UID_MASK); ++} ++static inline tag_t make_atag_from_value(uint32_t value) ++{ ++ return (uint64_t)value << 32; ++} ++/*---------------------------------------------------------------------------*/ ++ ++/* ++ * Maximum number of socket tags that a UID is allowed to have active. ++ * Multiple processes belonging to the same UID contribute towards this limit. ++ * Special UIDs that can impersonate a UID also contribute (e.g. download ++ * manager, ...) ++ */ ++#define DEFAULT_MAX_SOCK_TAGS 1024 ++ ++/* ++ * For now we only track 2 sets of counters. ++ * The default set is 0. ++ * Userspace can activate another set for a given uid being tracked. ++ */ ++#define IFS_MAX_COUNTER_SETS 2 ++ ++enum ifs_tx_rx { ++ IFS_TX, ++ IFS_RX, ++ IFS_MAX_DIRECTIONS ++}; ++ ++/* For now, TCP, UDP, the rest */ ++enum ifs_proto { ++ IFS_TCP, ++ IFS_UDP, ++ IFS_PROTO_OTHER, ++ IFS_MAX_PROTOS ++}; ++ ++struct byte_packet_counters { ++ uint64_t bytes; ++ uint64_t packets; ++}; ++ ++struct data_counters { ++ struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; ++}; ++ ++static inline uint64_t dc_sum_bytes(struct data_counters *counters, ++ int set, ++ enum ifs_tx_rx direction) ++{ ++ return counters->bpc[set][direction][IFS_TCP].bytes ++ + counters->bpc[set][direction][IFS_UDP].bytes ++ + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; ++} ++ ++static inline uint64_t dc_sum_packets(struct data_counters *counters, ++ int set, ++ enum ifs_tx_rx direction) ++{ ++ return counters->bpc[set][direction][IFS_TCP].packets ++ + counters->bpc[set][direction][IFS_UDP].packets ++ + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; ++} ++ ++ ++/* Generic X based nodes used as a base for rb_tree ops */ ++struct tag_node { ++ struct rb_node node; ++ tag_t tag; ++}; ++ ++struct tag_stat { ++ struct tag_node tn; ++ struct data_counters counters; ++ /* ++ * If this tag is acct_tag based, we need to count against the ++ * matching parent uid_tag. ++ */ ++ struct data_counters *parent_counters; ++}; ++ ++struct iface_stat { ++ struct list_head list; /* in iface_stat_list */ ++ char *ifname; ++ bool active; ++ /* net_dev is only valid for active iface_stat */ ++ struct net_device *net_dev; ++ ++ struct byte_packet_counters totals_via_dev[IFS_MAX_DIRECTIONS]; ++ struct data_counters totals_via_skb; ++ /* ++ * We keep the last_known, because some devices reset their counters ++ * just before NETDEV_UP, while some will reset just before ++ * NETDEV_REGISTER (which is more normal). ++ * So now, if the device didn't do a NETDEV_UNREGISTER and we see ++ * its current dev stats smaller that what was previously known, we ++ * assume an UNREGISTER and just use the last_known. ++ */ ++ struct byte_packet_counters last_known[IFS_MAX_DIRECTIONS]; ++ /* last_known is usable when last_known_valid is true */ ++ bool last_known_valid; ++ ++ struct proc_dir_entry *proc_ptr; ++ ++ struct rb_root tag_stat_tree; ++ spinlock_t tag_stat_list_lock; ++}; ++ ++/* This is needed to create proc_dir_entries from atomic context. */ ++struct iface_stat_work { ++ struct work_struct iface_work; ++ struct iface_stat *iface_entry; ++}; ++ ++/* ++ * Track tag that this socket is transferring data for, and not necessarily ++ * the uid that owns the socket. ++ * This is the tag against which tag_stat.counters will be billed. ++ * These structs need to be looked up by sock and pid. ++ */ ++struct sock_tag { ++ struct rb_node sock_node; ++ struct sock *sk; /* Only used as a number, never dereferenced */ ++ /* The socket is needed for sockfd_put() */ ++ struct socket *socket; ++ /* Used to associate with a given pid */ ++ struct list_head list; /* in proc_qtu_data.sock_tag_list */ ++ pid_t pid; ++ ++ tag_t tag; ++}; ++ ++struct qtaguid_event_counts { ++ /* Various successful events */ ++ atomic64_t sockets_tagged; ++ atomic64_t sockets_untagged; ++ atomic64_t counter_set_changes; ++ atomic64_t delete_cmds; ++ atomic64_t iface_events; /* Number of NETDEV_* events handled */ ++ ++ atomic64_t match_calls; /* Number of times iptables called mt */ ++ /* Number of times iptables called mt from pre or post routing hooks */ ++ atomic64_t match_calls_prepost; ++ /* ++ * match_found_sk_*: numbers related to the netfilter matching ++ * function finding a sock for the sk_buff. ++ * Total skbs processed is sum(match_found*). ++ */ ++ atomic64_t match_found_sk; /* An sk was already in the sk_buff. */ ++ /* The connection tracker had or didn't have the sk. */ ++ atomic64_t match_found_sk_in_ct; ++ atomic64_t match_found_no_sk_in_ct; ++ /* ++ * No sk could be found. No apparent owner. Could happen with ++ * unsolicited traffic. ++ */ ++ atomic64_t match_no_sk; ++ /* ++ * The file ptr in the sk_socket wasn't there. ++ * This might happen for traffic while the socket is being closed. ++ */ ++ atomic64_t match_no_sk_file; ++}; ++ ++/* Track the set active_set for the given tag. */ ++struct tag_counter_set { ++ struct tag_node tn; ++ int active_set; ++}; ++ ++/*----------------------------------------------*/ ++/* ++ * The qtu uid data is used to track resources that are created directly or ++ * indirectly by processes (uid tracked). ++ * It is shared by the processes with the same uid. ++ * Some of the resource will be counted to prevent further rogue allocations, ++ * some will need freeing once the owner process (uid) exits. ++ */ ++struct uid_tag_data { ++ struct rb_node node; ++ uid_t uid; ++ ++ /* ++ * For the uid, how many accounting tags have been set. ++ */ ++ int num_active_tags; ++ /* Track the number of proc_qtu_data that reference it */ ++ int num_pqd; ++ struct rb_root tag_ref_tree; ++ /* No tag_node_tree_lock; use uid_tag_data_tree_lock */ ++}; ++ ++struct tag_ref { ++ struct tag_node tn; ++ ++ /* ++ * This tracks the number of active sockets that have a tag on them ++ * which matches this tag_ref.tn.tag. ++ * A tag ref can live on after the sockets are untagged. ++ * A tag ref can only be removed during a tag delete command. ++ */ ++ int num_sock_tags; ++}; ++ ++struct proc_qtu_data { ++ struct rb_node node; ++ pid_t pid; ++ ++ struct uid_tag_data *parent_tag_data; ++ ++ /* Tracks the sock_tags that need freeing upon this proc's death */ ++ struct list_head sock_tag_list; ++ /* No spinlock_t sock_tag_list_lock; use the global one. */ ++}; ++ ++/*----------------------------------------------*/ ++#endif /* ifndef __XT_QTAGUID_INTERNAL_H__ */ +diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c +new file mode 100644 +index 0000000..f6a00a3 +--- /dev/null ++++ b/net/netfilter/xt_qtaguid_print.c +@@ -0,0 +1,566 @@ ++/* ++ * Pretty printing Support for iptables xt_qtaguid module. ++ * ++ * (C) 2011 Google, Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * Most of the functions in this file just waste time if DEBUG is not defined. ++ * The matching xt_qtaguid_print.h will static inline empty funcs if the needed ++ * debug flags ore not defined. ++ * Those funcs that fail to allocate memory will panic as there is no need to ++ * hobble allong just pretending to do the requested work. ++ */ ++ ++#define DEBUG ++ ++#include <linux/fs.h> ++#include <linux/gfp.h> ++#include <linux/net.h> ++#include <linux/rbtree.h> ++#include <linux/slab.h> ++#include <linux/spinlock_types.h> ++ ++ ++#include "xt_qtaguid_internal.h" ++#include "xt_qtaguid_print.h" ++ ++#ifdef DDEBUG ++ ++static void _bug_on_err_or_null(void *ptr) ++{ ++ if (IS_ERR_OR_NULL(ptr)) { ++ pr_err("qtaguid: kmalloc failed\n"); ++ BUG(); ++ } ++} ++ ++char *pp_tag_t(tag_t *tag) ++{ ++ char *res; ++ ++ if (!tag) ++ res = kasprintf(GFP_ATOMIC, "tag_t@null{}"); ++ else ++ res = kasprintf(GFP_ATOMIC, ++ "tag_t@%p{tag=0x%llx, uid=%u}", ++ tag, *tag, get_uid_from_tag(*tag)); ++ _bug_on_err_or_null(res); ++ return res; ++} ++ ++char *pp_data_counters(struct data_counters *dc, bool showValues) ++{ ++ char *res; ++ ++ if (!dc) ++ res = kasprintf(GFP_ATOMIC, "data_counters@null{}"); ++ else if (showValues) ++ res = kasprintf( ++ GFP_ATOMIC, "data_counters@%p{" ++ "set0{" ++ "rx{" ++ "tcp{b=%llu, p=%llu}, " ++ "udp{b=%llu, p=%llu}," ++ "other{b=%llu, p=%llu}}, " ++ "tx{" ++ "tcp{b=%llu, p=%llu}, " ++ "udp{b=%llu, p=%llu}," ++ "other{b=%llu, p=%llu}}}, " ++ "set1{" ++ "rx{" ++ "tcp{b=%llu, p=%llu}, " ++ "udp{b=%llu, p=%llu}," ++ "other{b=%llu, p=%llu}}, " ++ "tx{" ++ "tcp{b=%llu, p=%llu}, " ++ "udp{b=%llu, p=%llu}," ++ "other{b=%llu, p=%llu}}}}", ++ dc, ++ dc->bpc[0][IFS_RX][IFS_TCP].bytes, ++ dc->bpc[0][IFS_RX][IFS_TCP].packets, ++ dc->bpc[0][IFS_RX][IFS_UDP].bytes, ++ dc->bpc[0][IFS_RX][IFS_UDP].packets, ++ dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].bytes, ++ dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].packets, ++ dc->bpc[0][IFS_TX][IFS_TCP].bytes, ++ dc->bpc[0][IFS_TX][IFS_TCP].packets, ++ dc->bpc[0][IFS_TX][IFS_UDP].bytes, ++ dc->bpc[0][IFS_TX][IFS_UDP].packets, ++ dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].bytes, ++ dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].packets, ++ dc->bpc[1][IFS_RX][IFS_TCP].bytes, ++ dc->bpc[1][IFS_RX][IFS_TCP].packets, ++ dc->bpc[1][IFS_RX][IFS_UDP].bytes, ++ dc->bpc[1][IFS_RX][IFS_UDP].packets, ++ dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].bytes, ++ dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].packets, ++ dc->bpc[1][IFS_TX][IFS_TCP].bytes, ++ dc->bpc[1][IFS_TX][IFS_TCP].packets, ++ dc->bpc[1][IFS_TX][IFS_UDP].bytes, ++ dc->bpc[1][IFS_TX][IFS_UDP].packets, ++ dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].bytes, ++ dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].packets); ++ else ++ res = kasprintf(GFP_ATOMIC, "data_counters@%p{...}", dc); ++ _bug_on_err_or_null(res); ++ return res; ++} ++ ++char *pp_tag_node(struct tag_node *tn) ++{ ++ char *tag_str; ++ char *res; ++ ++ if (!tn) { ++ res = kasprintf(GFP_ATOMIC, "tag_node@null{}"); ++ _bug_on_err_or_null(res); ++ return res; ++ } ++ tag_str = pp_tag_t(&tn->tag); ++ res = kasprintf(GFP_ATOMIC, ++ "tag_node@%p{tag=%s}", ++ tn, tag_str); ++ _bug_on_err_or_null(res); ++ kfree(tag_str); ++ return res; ++} ++ ++char *pp_tag_ref(struct tag_ref *tr) ++{ ++ char *tn_str; ++ char *res; ++ ++ if (!tr) { ++ res = kasprintf(GFP_ATOMIC, "tag_ref@null{}"); ++ _bug_on_err_or_null(res); ++ return res; ++ } ++ tn_str = pp_tag_node(&tr->tn); ++ res = kasprintf(GFP_ATOMIC, ++ "tag_ref@%p{%s, num_sock_tags=%d}", ++ tr, tn_str, tr->num_sock_tags); ++ _bug_on_err_or_null(res); ++ kfree(tn_str); ++ return res; ++} ++ ++char *pp_tag_stat(struct tag_stat *ts) ++{ ++ char *tn_str; ++ char *counters_str; ++ char *parent_counters_str; ++ char *res; ++ ++ if (!ts) { ++ res = kasprintf(GFP_ATOMIC, "tag_stat@null{}"); ++ _bug_on_err_or_null(res); ++ return res; ++ } ++ tn_str = pp_tag_node(&ts->tn); ++ counters_str = pp_data_counters(&ts->counters, true); ++ parent_counters_str = pp_data_counters(ts->parent_counters, false); ++ res = kasprintf(GFP_ATOMIC, ++ "tag_stat@%p{%s, counters=%s, parent_counters=%s}", ++ ts, tn_str, counters_str, parent_counters_str); ++ _bug_on_err_or_null(res); ++ kfree(tn_str); ++ kfree(counters_str); ++ kfree(parent_counters_str); ++ return res; ++} ++ ++char *pp_iface_stat(struct iface_stat *is) ++{ ++ char *res; ++ if (!is) { ++ res = kasprintf(GFP_ATOMIC, "iface_stat@null{}"); ++ } else { ++ struct data_counters *cnts = &is->totals_via_skb; ++ res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" ++ "list=list_head{...}, " ++ "ifname=%s, " ++ "total_dev={rx={bytes=%llu, " ++ "packets=%llu}, " ++ "tx={bytes=%llu, " ++ "packets=%llu}}, " ++ "total_skb={rx={bytes=%llu, " ++ "packets=%llu}, " ++ "tx={bytes=%llu, " ++ "packets=%llu}}, " ++ "last_known_valid=%d, " ++ "last_known={rx={bytes=%llu, " ++ "packets=%llu}, " ++ "tx={bytes=%llu, " ++ "packets=%llu}}, " ++ "active=%d, " ++ "net_dev=%p, " ++ "proc_ptr=%p, " ++ "tag_stat_tree=rb_root{...}}", ++ is, ++ is->ifname, ++ is->totals_via_dev[IFS_RX].bytes, ++ is->totals_via_dev[IFS_RX].packets, ++ is->totals_via_dev[IFS_TX].bytes, ++ is->totals_via_dev[IFS_TX].packets, ++ dc_sum_bytes(cnts, 0, IFS_RX), ++ dc_sum_packets(cnts, 0, IFS_RX), ++ dc_sum_bytes(cnts, 0, IFS_TX), ++ dc_sum_packets(cnts, 0, IFS_TX), ++ is->last_known_valid, ++ is->last_known[IFS_RX].bytes, ++ is->last_known[IFS_RX].packets, ++ is->last_known[IFS_TX].bytes, ++ is->last_known[IFS_TX].packets, ++ is->active, ++ is->net_dev, ++ is->proc_ptr); ++ } ++ _bug_on_err_or_null(res); ++ return res; ++} ++ ++char *pp_sock_tag(struct sock_tag *st) ++{ ++ char *tag_str; ++ char *res; ++ ++ if (!st) { ++ res = kasprintf(GFP_ATOMIC, "sock_tag@null{}"); ++ _bug_on_err_or_null(res); ++ return res; ++ } ++ tag_str = pp_tag_t(&st->tag); ++ res = kasprintf(GFP_ATOMIC, "sock_tag@%p{" ++ "sock_node=rb_node{...}, " ++ "sk=%p socket=%p (f_count=%lu), list=list_head{...}, " ++ "pid=%u, tag=%s}", ++ st, st->sk, st->socket, atomic_long_read( ++ &st->socket->file->f_count), ++ st->pid, tag_str); ++ _bug_on_err_or_null(res); ++ kfree(tag_str); ++ return res; ++} ++ ++char *pp_uid_tag_data(struct uid_tag_data *utd) ++{ ++ char *res; ++ ++ if (!utd) ++ res = kasprintf(GFP_ATOMIC, "uid_tag_data@null{}"); ++ else ++ res = kasprintf(GFP_ATOMIC, "uid_tag_data@%p{" ++ "uid=%u, num_active_acct_tags=%d, " ++ "num_pqd=%d, " ++ "tag_node_tree=rb_root{...}, " ++ "proc_qtu_data_tree=rb_root{...}}", ++ utd, utd->uid, ++ utd->num_active_tags, utd->num_pqd); ++ _bug_on_err_or_null(res); ++ return res; ++} ++ ++char *pp_proc_qtu_data(struct proc_qtu_data *pqd) ++{ ++ char *parent_tag_data_str; ++ char *res; ++ ++ if (!pqd) { ++ res = kasprintf(GFP_ATOMIC, "proc_qtu_data@null{}"); ++ _bug_on_err_or_null(res); ++ return res; ++ } ++ parent_tag_data_str = pp_uid_tag_data(pqd->parent_tag_data); ++ res = kasprintf(GFP_ATOMIC, "proc_qtu_data@%p{" ++ "node=rb_node{...}, pid=%u, " ++ "parent_tag_data=%s, " ++ "sock_tag_list=list_head{...}}", ++ pqd, pqd->pid, parent_tag_data_str ++ ); ++ _bug_on_err_or_null(res); ++ kfree(parent_tag_data_str); ++ return res; ++} ++ ++/*------------------------------------------*/ ++void prdebug_sock_tag_tree(int indent_level, ++ struct rb_root *sock_tag_tree) ++{ ++ struct rb_node *node; ++ struct sock_tag *sock_tag_entry; ++ char *str; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (RB_EMPTY_ROOT(sock_tag_tree)) { ++ str = "sock_tag_tree=rb_root{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "sock_tag_tree=rb_root{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ for (node = rb_first(sock_tag_tree); ++ node; ++ node = rb_next(node)) { ++ sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); ++ str = pp_sock_tag(sock_tag_entry); ++ pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); ++ kfree(str); ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++void prdebug_sock_tag_list(int indent_level, ++ struct list_head *sock_tag_list) ++{ ++ struct sock_tag *sock_tag_entry; ++ char *str; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (list_empty(sock_tag_list)) { ++ str = "sock_tag_list=list_head{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "sock_tag_list=list_head{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ list_for_each_entry(sock_tag_entry, sock_tag_list, list) { ++ str = pp_sock_tag(sock_tag_entry); ++ pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); ++ kfree(str); ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++void prdebug_proc_qtu_data_tree(int indent_level, ++ struct rb_root *proc_qtu_data_tree) ++{ ++ char *str; ++ struct rb_node *node; ++ struct proc_qtu_data *proc_qtu_data_entry; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (RB_EMPTY_ROOT(proc_qtu_data_tree)) { ++ str = "proc_qtu_data_tree=rb_root{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "proc_qtu_data_tree=rb_root{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ for (node = rb_first(proc_qtu_data_tree); ++ node; ++ node = rb_next(node)) { ++ proc_qtu_data_entry = rb_entry(node, ++ struct proc_qtu_data, ++ node); ++ str = pp_proc_qtu_data(proc_qtu_data_entry); ++ pr_debug("%*d: %s,\n", indent_level*2, indent_level, ++ str); ++ kfree(str); ++ indent_level++; ++ prdebug_sock_tag_list(indent_level, ++ &proc_qtu_data_entry->sock_tag_list); ++ indent_level--; ++ ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) ++{ ++ char *str; ++ struct rb_node *node; ++ struct tag_ref *tag_ref_entry; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (RB_EMPTY_ROOT(tag_ref_tree)) { ++ str = "tag_ref_tree{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "tag_ref_tree{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ for (node = rb_first(tag_ref_tree); ++ node; ++ node = rb_next(node)) { ++ tag_ref_entry = rb_entry(node, ++ struct tag_ref, ++ tn.node); ++ str = pp_tag_ref(tag_ref_entry); ++ pr_debug("%*d: %s,\n", indent_level*2, indent_level, ++ str); ++ kfree(str); ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++void prdebug_uid_tag_data_tree(int indent_level, ++ struct rb_root *uid_tag_data_tree) ++{ ++ char *str; ++ struct rb_node *node; ++ struct uid_tag_data *uid_tag_data_entry; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (RB_EMPTY_ROOT(uid_tag_data_tree)) { ++ str = "uid_tag_data_tree=rb_root{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "uid_tag_data_tree=rb_root{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ for (node = rb_first(uid_tag_data_tree); ++ node; ++ node = rb_next(node)) { ++ uid_tag_data_entry = rb_entry(node, struct uid_tag_data, ++ node); ++ str = pp_uid_tag_data(uid_tag_data_entry); ++ pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); ++ kfree(str); ++ if (!RB_EMPTY_ROOT(&uid_tag_data_entry->tag_ref_tree)) { ++ indent_level++; ++ prdebug_tag_ref_tree(indent_level, ++ &uid_tag_data_entry->tag_ref_tree); ++ indent_level--; ++ } ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++void prdebug_tag_stat_tree(int indent_level, ++ struct rb_root *tag_stat_tree) ++{ ++ char *str; ++ struct rb_node *node; ++ struct tag_stat *ts_entry; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (RB_EMPTY_ROOT(tag_stat_tree)) { ++ str = "tag_stat_tree{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "tag_stat_tree{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ for (node = rb_first(tag_stat_tree); ++ node; ++ node = rb_next(node)) { ++ ts_entry = rb_entry(node, struct tag_stat, tn.node); ++ str = pp_tag_stat(ts_entry); ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, ++ str); ++ kfree(str); ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++void prdebug_iface_stat_list(int indent_level, ++ struct list_head *iface_stat_list) ++{ ++ char *str; ++ struct iface_stat *iface_entry; ++ ++ if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) ++ return; ++ ++ if (list_empty(iface_stat_list)) { ++ str = "iface_stat_list=list_head{}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ return; ++ } ++ ++ str = "iface_stat_list=list_head{"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ indent_level++; ++ list_for_each_entry(iface_entry, iface_stat_list, list) { ++ str = pp_iface_stat(iface_entry); ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++ kfree(str); ++ ++ spin_lock_bh(&iface_entry->tag_stat_list_lock); ++ if (!RB_EMPTY_ROOT(&iface_entry->tag_stat_tree)) { ++ indent_level++; ++ prdebug_tag_stat_tree(indent_level, ++ &iface_entry->tag_stat_tree); ++ indent_level--; ++ } ++ spin_unlock_bh(&iface_entry->tag_stat_list_lock); ++ } ++ indent_level--; ++ str = "}"; ++ pr_debug("%*d: %s\n", indent_level*2, indent_level, str); ++} ++ ++#endif /* ifdef DDEBUG */ ++/*------------------------------------------*/ ++static const char * const netdev_event_strings[] = { ++ "netdev_unknown", ++ "NETDEV_UP", ++ "NETDEV_DOWN", ++ "NETDEV_REBOOT", ++ "NETDEV_CHANGE", ++ "NETDEV_REGISTER", ++ "NETDEV_UNREGISTER", ++ "NETDEV_CHANGEMTU", ++ "NETDEV_CHANGEADDR", ++ "NETDEV_GOING_DOWN", ++ "NETDEV_CHANGENAME", ++ "NETDEV_FEAT_CHANGE", ++ "NETDEV_BONDING_FAILOVER", ++ "NETDEV_PRE_UP", ++ "NETDEV_PRE_TYPE_CHANGE", ++ "NETDEV_POST_TYPE_CHANGE", ++ "NETDEV_POST_INIT", ++ "NETDEV_UNREGISTER_BATCH", ++ "NETDEV_RELEASE", ++ "NETDEV_NOTIFY_PEERS", ++ "NETDEV_JOIN", ++}; ++ ++const char *netdev_evt_str(int netdev_event) ++{ ++ if (netdev_event < 0 ++ || netdev_event >= ARRAY_SIZE(netdev_event_strings)) ++ return "bad event num"; ++ return netdev_event_strings[netdev_event]; ++} +diff --git a/net/netfilter/xt_qtaguid_print.h b/net/netfilter/xt_qtaguid_print.h +new file mode 100644 +index 0000000..b63871a +--- /dev/null ++++ b/net/netfilter/xt_qtaguid_print.h +@@ -0,0 +1,120 @@ ++/* ++ * Pretty printing Support for iptables xt_qtaguid module. ++ * ++ * (C) 2011 Google, Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef __XT_QTAGUID_PRINT_H__ ++#define __XT_QTAGUID_PRINT_H__ ++ ++#include "xt_qtaguid_internal.h" ++ ++#ifdef DDEBUG ++ ++char *pp_tag_t(tag_t *tag); ++char *pp_data_counters(struct data_counters *dc, bool showValues); ++char *pp_tag_node(struct tag_node *tn); ++char *pp_tag_ref(struct tag_ref *tr); ++char *pp_tag_stat(struct tag_stat *ts); ++char *pp_iface_stat(struct iface_stat *is); ++char *pp_sock_tag(struct sock_tag *st); ++char *pp_uid_tag_data(struct uid_tag_data *qtd); ++char *pp_proc_qtu_data(struct proc_qtu_data *pqd); ++ ++/*------------------------------------------*/ ++void prdebug_sock_tag_list(int indent_level, ++ struct list_head *sock_tag_list); ++void prdebug_sock_tag_tree(int indent_level, ++ struct rb_root *sock_tag_tree); ++void prdebug_proc_qtu_data_tree(int indent_level, ++ struct rb_root *proc_qtu_data_tree); ++void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree); ++void prdebug_uid_tag_data_tree(int indent_level, ++ struct rb_root *uid_tag_data_tree); ++void prdebug_tag_stat_tree(int indent_level, ++ struct rb_root *tag_stat_tree); ++void prdebug_iface_stat_list(int indent_level, ++ struct list_head *iface_stat_list); ++ ++#else ++ ++/*------------------------------------------*/ ++static inline char *pp_tag_t(tag_t *tag) ++{ ++ return NULL; ++} ++static inline char *pp_data_counters(struct data_counters *dc, bool showValues) ++{ ++ return NULL; ++} ++static inline char *pp_tag_node(struct tag_node *tn) ++{ ++ return NULL; ++} ++static inline char *pp_tag_ref(struct tag_ref *tr) ++{ ++ return NULL; ++} ++static inline char *pp_tag_stat(struct tag_stat *ts) ++{ ++ return NULL; ++} ++static inline char *pp_iface_stat(struct iface_stat *is) ++{ ++ return NULL; ++} ++static inline char *pp_sock_tag(struct sock_tag *st) ++{ ++ return NULL; ++} ++static inline char *pp_uid_tag_data(struct uid_tag_data *qtd) ++{ ++ return NULL; ++} ++static inline char *pp_proc_qtu_data(struct proc_qtu_data *pqd) ++{ ++ return NULL; ++} ++ ++/*------------------------------------------*/ ++static inline ++void prdebug_sock_tag_list(int indent_level, ++ struct list_head *sock_tag_list) ++{ ++} ++static inline ++void prdebug_sock_tag_tree(int indent_level, ++ struct rb_root *sock_tag_tree) ++{ ++} ++static inline ++void prdebug_proc_qtu_data_tree(int indent_level, ++ struct rb_root *proc_qtu_data_tree) ++{ ++} ++static inline ++void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) ++{ ++} ++static inline ++void prdebug_uid_tag_data_tree(int indent_level, ++ struct rb_root *uid_tag_data_tree) ++{ ++} ++static inline ++void prdebug_tag_stat_tree(int indent_level, ++ struct rb_root *tag_stat_tree) ++{ ++} ++static inline ++void prdebug_iface_stat_list(int indent_level, ++ struct list_head *iface_stat_list) ++{ ++} ++#endif ++/*------------------------------------------*/ ++const char *netdev_evt_str(int netdev_event); ++#endif /* ifndef __XT_QTAGUID_PRINT_H__ */ +diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c +new file mode 100644 +index 0000000..99592ae +--- /dev/null ++++ b/net/netfilter/xt_quota2.c +@@ -0,0 +1,382 @@ ++/* ++ * xt_quota2 - enhanced xt_quota that can count upwards and in packets ++ * as a minimal accounting match. ++ * by Jan Engelhardt <jengelh@medozas.de>, 2008 ++ * ++ * Originally based on xt_quota.c: ++ * netfilter module to enforce network quotas ++ * Sam Johnston <samj@samj.net> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License; either ++ * version 2 of the License, as published by the Free Software Foundation. ++ */ ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/proc_fs.h> ++#include <linux/skbuff.h> ++#include <linux/spinlock.h> ++#include <asm/atomic.h> ++#include <net/netlink.h> ++ ++#include <linux/netfilter/x_tables.h> ++#include <linux/netfilter/xt_quota2.h> ++#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG ++#include <linux/netfilter_ipv4/ipt_ULOG.h> ++#endif ++ ++/** ++ * @lock: lock to protect quota writers from each other ++ */ ++struct xt_quota_counter { ++ u_int64_t quota; ++ spinlock_t lock; ++ struct list_head list; ++ atomic_t ref; ++ char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)]; ++ struct proc_dir_entry *procfs_entry; ++}; ++ ++#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG ++/* Harald's favorite number +1 :D From ipt_ULOG.C */ ++static int qlog_nl_event = 112; ++module_param_named(event_num, qlog_nl_event, uint, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(event_num, ++ "Event number for NETLINK_NFLOG message. 0 disables log." ++ "111 is what ipt_ULOG uses."); ++static struct sock *nflognl; ++#endif ++ ++static LIST_HEAD(counter_list); ++static DEFINE_SPINLOCK(counter_list_lock); ++ ++static struct proc_dir_entry *proc_xt_quota; ++static unsigned int quota_list_perms = S_IRUGO | S_IWUSR; ++static kuid_t quota_list_uid = KUIDT_INIT(0); ++static kgid_t quota_list_gid = KGIDT_INIT(0); ++module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); ++ ++#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG ++static void quota2_log(unsigned int hooknum, ++ const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const char *prefix) ++{ ++ ulog_packet_msg_t *pm; ++ struct sk_buff *log_skb; ++ size_t size; ++ struct nlmsghdr *nlh; ++ ++ if (!qlog_nl_event) ++ return; ++ ++ size = NLMSG_SPACE(sizeof(*pm)); ++ size = max(size, (size_t)NLMSG_GOODSIZE); ++ log_skb = alloc_skb(size, GFP_ATOMIC); ++ if (!log_skb) { ++ pr_err("xt_quota2: cannot alloc skb for logging\n"); ++ return; ++ } ++ ++ nlh = nlmsg_put(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event, ++ sizeof(*pm), 0); ++ if (!nlh) { ++ pr_err("xt_quota2: nlmsg_put failed\n"); ++ kfree_skb(log_skb); ++ return; ++ } ++ pm = nlmsg_data(nlh); ++ if (skb->tstamp.tv64 == 0) ++ __net_timestamp((struct sk_buff *)skb); ++ pm->data_len = 0; ++ pm->hook = hooknum; ++ if (prefix != NULL) ++ strlcpy(pm->prefix, prefix, sizeof(pm->prefix)); ++ else ++ *(pm->prefix) = '\0'; ++ if (in) ++ strlcpy(pm->indev_name, in->name, sizeof(pm->indev_name)); ++ else ++ pm->indev_name[0] = '\0'; ++ ++ if (out) ++ strlcpy(pm->outdev_name, out->name, sizeof(pm->outdev_name)); ++ else ++ pm->outdev_name[0] = '\0'; ++ ++ NETLINK_CB(log_skb).dst_group = 1; ++ pr_debug("throwing 1 packets to netlink group 1\n"); ++ netlink_broadcast(nflognl, log_skb, 0, 1, GFP_ATOMIC); ++} ++#else ++static void quota2_log(unsigned int hooknum, ++ const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const char *prefix) ++{ ++} ++#endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */ ++ ++static ssize_t quota_proc_read(struct file *file, char __user *buf, ++ size_t size, loff_t *ppos) ++{ ++ struct xt_quota_counter *e = PDE_DATA(file_inode(file)); ++ char tmp[24]; ++ size_t tmp_size; ++ ++ spin_lock_bh(&e->lock); ++ tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", e->quota); ++ spin_unlock_bh(&e->lock); ++ return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); ++} ++ ++static ssize_t quota_proc_write(struct file *file, const char __user *input, ++ size_t size, loff_t *ppos) ++{ ++ struct xt_quota_counter *e = PDE_DATA(file_inode(file)); ++ char buf[sizeof("18446744073709551616")]; ++ ++ if (size > sizeof(buf)) ++ size = sizeof(buf); ++ if (copy_from_user(buf, input, size) != 0) ++ return -EFAULT; ++ buf[sizeof(buf)-1] = '\0'; ++ ++ spin_lock_bh(&e->lock); ++ e->quota = simple_strtoull(buf, NULL, 0); ++ spin_unlock_bh(&e->lock); ++ return size; ++} ++ ++static const struct file_operations q2_counter_fops = { ++ .read = quota_proc_read, ++ .write = quota_proc_write, ++ .llseek = default_llseek, ++}; ++ ++static struct xt_quota_counter * ++q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) ++{ ++ struct xt_quota_counter *e; ++ unsigned int size; ++ ++ /* Do not need all the procfs things for anonymous counters. */ ++ size = anon ? offsetof(typeof(*e), list) : sizeof(*e); ++ e = kmalloc(size, GFP_KERNEL); ++ if (e == NULL) ++ return NULL; ++ ++ e->quota = q->quota; ++ spin_lock_init(&e->lock); ++ if (!anon) { ++ INIT_LIST_HEAD(&e->list); ++ atomic_set(&e->ref, 1); ++ strlcpy(e->name, q->name, sizeof(e->name)); ++ } ++ return e; ++} ++ ++/** ++ * q2_get_counter - get ref to counter or create new ++ * @name: name of counter ++ */ ++static struct xt_quota_counter * ++q2_get_counter(const struct xt_quota_mtinfo2 *q) ++{ ++ struct proc_dir_entry *p; ++ struct xt_quota_counter *e = NULL; ++ struct xt_quota_counter *new_e; ++ ++ if (*q->name == '\0') ++ return q2_new_counter(q, true); ++ ++ /* No need to hold a lock while getting a new counter */ ++ new_e = q2_new_counter(q, false); ++ if (new_e == NULL) ++ goto out; ++ ++ spin_lock_bh(&counter_list_lock); ++ list_for_each_entry(e, &counter_list, list) ++ if (strcmp(e->name, q->name) == 0) { ++ atomic_inc(&e->ref); ++ spin_unlock_bh(&counter_list_lock); ++ kfree(new_e); ++ pr_debug("xt_quota2: old counter name=%s", e->name); ++ return e; ++ } ++ e = new_e; ++ pr_debug("xt_quota2: new_counter name=%s", e->name); ++ list_add_tail(&e->list, &counter_list); ++ /* The entry having a refcount of 1 is not directly destructible. ++ * This func has not yet returned the new entry, thus iptables ++ * has not references for destroying this entry. ++ * For another rule to try to destroy it, it would 1st need for this ++ * func* to be re-invoked, acquire a new ref for the same named quota. ++ * Nobody will access the e->procfs_entry either. ++ * So release the lock. */ ++ spin_unlock_bh(&counter_list_lock); ++ ++ /* create_proc_entry() is not spin_lock happy */ ++ p = e->procfs_entry = proc_create_data(e->name, quota_list_perms, ++ proc_xt_quota, &q2_counter_fops, e); ++ ++ if (IS_ERR_OR_NULL(p)) { ++ spin_lock_bh(&counter_list_lock); ++ list_del(&e->list); ++ spin_unlock_bh(&counter_list_lock); ++ goto out; ++ } ++ proc_set_user(p, quota_list_uid, quota_list_gid); ++ return e; ++ ++ out: ++ kfree(e); ++ return NULL; ++} ++ ++static int quota_mt2_check(const struct xt_mtchk_param *par) ++{ ++ struct xt_quota_mtinfo2 *q = par->matchinfo; ++ ++ pr_debug("xt_quota2: check() flags=0x%04x", q->flags); ++ ++ if (q->flags & ~XT_QUOTA_MASK) ++ return -EINVAL; ++ ++ q->name[sizeof(q->name)-1] = '\0'; ++ if (*q->name == '.' || strchr(q->name, '/') != NULL) { ++ printk(KERN_ERR "xt_quota.3: illegal name\n"); ++ return -EINVAL; ++ } ++ ++ q->master = q2_get_counter(q); ++ if (q->master == NULL) { ++ printk(KERN_ERR "xt_quota.3: memory alloc failure\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void quota_mt2_destroy(const struct xt_mtdtor_param *par) ++{ ++ struct xt_quota_mtinfo2 *q = par->matchinfo; ++ struct xt_quota_counter *e = q->master; ++ ++ if (*q->name == '\0') { ++ kfree(e); ++ return; ++ } ++ ++ spin_lock_bh(&counter_list_lock); ++ if (!atomic_dec_and_test(&e->ref)) { ++ spin_unlock_bh(&counter_list_lock); ++ return; ++ } ++ ++ list_del(&e->list); ++ remove_proc_entry(e->name, proc_xt_quota); ++ spin_unlock_bh(&counter_list_lock); ++ kfree(e); ++} ++ ++static bool ++quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ struct xt_quota_mtinfo2 *q = (void *)par->matchinfo; ++ struct xt_quota_counter *e = q->master; ++ bool ret = q->flags & XT_QUOTA_INVERT; ++ ++ spin_lock_bh(&e->lock); ++ if (q->flags & XT_QUOTA_GROW) { ++ /* ++ * While no_change is pointless in "grow" mode, we will ++ * implement it here simply to have a consistent behavior. ++ */ ++ if (!(q->flags & XT_QUOTA_NO_CHANGE)) { ++ e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; ++ } ++ ret = true; ++ } else { ++ if (e->quota >= skb->len) { ++ if (!(q->flags & XT_QUOTA_NO_CHANGE)) ++ e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; ++ ret = !ret; ++ } else { ++ /* We are transitioning, log that fact. */ ++ if (e->quota) { ++ quota2_log(par->hooknum, ++ skb, ++ par->in, ++ par->out, ++ q->name); ++ } ++ /* we do not allow even small packets from now on */ ++ e->quota = 0; ++ } ++ } ++ spin_unlock_bh(&e->lock); ++ return ret; ++} ++ ++static struct xt_match quota_mt2_reg[] __read_mostly = { ++ { ++ .name = "quota2", ++ .revision = 3, ++ .family = NFPROTO_IPV4, ++ .checkentry = quota_mt2_check, ++ .match = quota_mt2, ++ .destroy = quota_mt2_destroy, ++ .matchsize = sizeof(struct xt_quota_mtinfo2), ++ .me = THIS_MODULE, ++ }, ++ { ++ .name = "quota2", ++ .revision = 3, ++ .family = NFPROTO_IPV6, ++ .checkentry = quota_mt2_check, ++ .match = quota_mt2, ++ .destroy = quota_mt2_destroy, ++ .matchsize = sizeof(struct xt_quota_mtinfo2), ++ .me = THIS_MODULE, ++ }, ++}; ++ ++static int __init quota_mt2_init(void) ++{ ++ int ret; ++ pr_debug("xt_quota2: init()"); ++ ++#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG ++ nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, NULL); ++ if (!nflognl) ++ return -ENOMEM; ++#endif ++ ++ proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net); ++ if (proc_xt_quota == NULL) ++ return -EACCES; ++ ++ ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); ++ if (ret < 0) ++ remove_proc_entry("xt_quota", init_net.proc_net); ++ pr_debug("xt_quota2: init() %d", ret); ++ return ret; ++} ++ ++static void __exit quota_mt2_exit(void) ++{ ++ xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); ++ remove_proc_entry("xt_quota", init_net.proc_net); ++} ++ ++module_init(quota_mt2_init); ++module_exit(quota_mt2_exit); ++MODULE_DESCRIPTION("Xtables: countdown quota match; up counter"); ++MODULE_AUTHOR("Sam Johnston <samj@samj.net>"); ++MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_quota2"); ++MODULE_ALIAS("ip6t_quota2"); +diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c +index 7720b03..604df6f 100644 +--- a/net/netfilter/xt_rateest.c ++++ b/net/netfilter/xt_rateest.c +@@ -8,150 +8,187 @@ + #include <linux/module.h> + #include <linux/skbuff.h> + #include <linux/gen_stats.h> ++#include <linux/jhash.h> ++#include <linux/rtnetlink.h> ++#include <linux/random.h> ++#include <linux/slab.h> ++#include <net/gen_stats.h> ++#include <net/netlink.h> + + #include <linux/netfilter/x_tables.h> +-#include <linux/netfilter/xt_rateest.h> ++#include <linux/netfilter/xt_RATEEST.h> + #include <net/netfilter/xt_rateest.h> + ++static DEFINE_MUTEX(xt_rateest_mutex); + +-static bool +-xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par) ++#define RATEEST_HSIZE 16 ++static struct hlist_head rateest_hash[RATEEST_HSIZE] __read_mostly; ++static unsigned int jhash_rnd __read_mostly; ++static bool rnd_inited __read_mostly; ++ ++static unsigned int xt_rateest_hash(const char *name) + { +- const struct xt_rateest_match_info *info = par->matchinfo; +- struct gnet_stats_rate_est64 *r; +- u_int32_t bps1, bps2, pps1, pps2; +- bool ret = true; +- +- spin_lock_bh(&info->est1->lock); +- r = &info->est1->rstats; +- if (info->flags & XT_RATEEST_MATCH_DELTA) { +- bps1 = info->bps1 >= r->bps ? info->bps1 - r->bps : 0; +- pps1 = info->pps1 >= r->pps ? info->pps1 - r->pps : 0; +- } else { +- bps1 = r->bps; +- pps1 = r->pps; +- } +- spin_unlock_bh(&info->est1->lock); +- +- if (info->flags & XT_RATEEST_MATCH_ABS) { +- bps2 = info->bps2; +- pps2 = info->pps2; +- } else { +- spin_lock_bh(&info->est2->lock); +- r = &info->est2->rstats; +- if (info->flags & XT_RATEEST_MATCH_DELTA) { +- bps2 = info->bps2 >= r->bps ? info->bps2 - r->bps : 0; +- pps2 = info->pps2 >= r->pps ? info->pps2 - r->pps : 0; +- } else { +- bps2 = r->bps; +- pps2 = r->pps; ++ return jhash(name, FIELD_SIZEOF(struct xt_rateest, name), jhash_rnd) & ++ (RATEEST_HSIZE - 1); ++} ++ ++static void xt_rateest_hash_insert(struct xt_rateest *est) ++{ ++ unsigned int h; ++ ++ h = xt_rateest_hash(est->name); ++ hlist_add_head(&est->list, &rateest_hash[h]); ++} ++ ++struct xt_rateest *xt_rateest_lookup(const char *name) ++{ ++ struct xt_rateest *est; ++ unsigned int h; ++ ++ h = xt_rateest_hash(name); ++ mutex_lock(&xt_rateest_mutex); ++ hlist_for_each_entry(est, &rateest_hash[h], list) { ++ if (strcmp(est->name, name) == 0) { ++ est->refcnt++; ++ mutex_unlock(&xt_rateest_mutex); ++ return est; + } +- spin_unlock_bh(&info->est2->lock); + } ++ mutex_unlock(&xt_rateest_mutex); ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(xt_rateest_lookup); + +- switch (info->mode) { +- case XT_RATEEST_MATCH_LT: +- if (info->flags & XT_RATEEST_MATCH_BPS) +- ret &= bps1 < bps2; +- if (info->flags & XT_RATEEST_MATCH_PPS) +- ret &= pps1 < pps2; +- break; +- case XT_RATEEST_MATCH_GT: +- if (info->flags & XT_RATEEST_MATCH_BPS) +- ret &= bps1 > bps2; +- if (info->flags & XT_RATEEST_MATCH_PPS) +- ret &= pps1 > pps2; +- break; +- case XT_RATEEST_MATCH_EQ: +- if (info->flags & XT_RATEEST_MATCH_BPS) +- ret &= bps1 == bps2; +- if (info->flags & XT_RATEEST_MATCH_PPS) +- ret &= pps1 == pps2; +- break; ++void xt_rateest_put(struct xt_rateest *est) ++{ ++ mutex_lock(&xt_rateest_mutex); ++ if (--est->refcnt == 0) { ++ hlist_del(&est->list); ++ gen_kill_estimator(&est->bstats, &est->rstats); ++ /* ++ * gen_estimator est_timer() might access est->lock or bstats, ++ * wait a RCU grace period before freeing 'est' ++ */ ++ kfree_rcu(est, rcu); + } +- +- ret ^= info->flags & XT_RATEEST_MATCH_INVERT ? true : false; +- return ret; ++ mutex_unlock(&xt_rateest_mutex); + } ++EXPORT_SYMBOL_GPL(xt_rateest_put); + +-static int xt_rateest_mt_checkentry(const struct xt_mtchk_param *par) ++static unsigned int ++xt_rateest_tg(struct sk_buff *skb, const struct xt_action_param *par) + { +- struct xt_rateest_match_info *info = par->matchinfo; +- struct xt_rateest *est1, *est2; +- int ret = -EINVAL; ++ const struct xt_rateest_target_info *info = par->targinfo; ++ struct gnet_stats_basic_packed *stats = &info->est->bstats; + +- if (hweight32(info->flags & (XT_RATEEST_MATCH_ABS | +- XT_RATEEST_MATCH_REL)) != 1) +- goto err1; ++ spin_lock_bh(&info->est->lock); ++ stats->bytes += skb->len; ++ stats->packets++; ++ spin_unlock_bh(&info->est->lock); + +- if (!(info->flags & (XT_RATEEST_MATCH_BPS | XT_RATEEST_MATCH_PPS))) +- goto err1; ++ return XT_CONTINUE; ++} + +- switch (info->mode) { +- case XT_RATEEST_MATCH_EQ: +- case XT_RATEEST_MATCH_LT: +- case XT_RATEEST_MATCH_GT: +- break; +- default: +- goto err1; ++static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par) ++{ ++ struct xt_rateest_target_info *info = par->targinfo; ++ struct xt_rateest *est; ++ struct { ++ struct nlattr opt; ++ struct gnet_estimator est; ++ } cfg; ++ int ret; ++ ++ if (unlikely(!rnd_inited)) { ++ get_random_bytes(&jhash_rnd, sizeof(jhash_rnd)); ++ rnd_inited = true; ++ } ++ ++ est = xt_rateest_lookup(info->name); ++ if (est) { ++ /* ++ * If estimator parameters are specified, they must match the ++ * existing estimator. ++ */ ++ if ((!info->interval && !info->ewma_log) || ++ (info->interval != est->params.interval || ++ info->ewma_log != est->params.ewma_log)) { ++ xt_rateest_put(est); ++ return -EINVAL; ++ } ++ info->est = est; ++ return 0; + } + +- ret = -ENOENT; +- est1 = xt_rateest_lookup(info->name1); +- if (!est1) ++ ret = -ENOMEM; ++ est = kzalloc(sizeof(*est), GFP_KERNEL); ++ if (!est) + goto err1; + +- est2 = NULL; +- if (info->flags & XT_RATEEST_MATCH_REL) { +- est2 = xt_rateest_lookup(info->name2); +- if (!est2) +- goto err2; +- } ++ strlcpy(est->name, info->name, sizeof(est->name)); ++ spin_lock_init(&est->lock); ++ est->refcnt = 1; ++ est->params.interval = info->interval; ++ est->params.ewma_log = info->ewma_log; ++ ++ cfg.opt.nla_len = nla_attr_size(sizeof(cfg.est)); ++ cfg.opt.nla_type = TCA_STATS_RATE_EST; ++ cfg.est.interval = info->interval; ++ cfg.est.ewma_log = info->ewma_log; ++ ++ ret = gen_new_estimator(&est->bstats, NULL, &est->rstats, ++ &est->lock, &cfg.opt); ++ if (ret < 0) ++ goto err2; + +- info->est1 = est1; +- info->est2 = est2; ++ info->est = est; ++ xt_rateest_hash_insert(est); + return 0; + + err2: +- xt_rateest_put(est1); ++ kfree(est); + err1: + return ret; + } + +-static void xt_rateest_mt_destroy(const struct xt_mtdtor_param *par) ++static void xt_rateest_tg_destroy(const struct xt_tgdtor_param *par) + { +- struct xt_rateest_match_info *info = par->matchinfo; ++ struct xt_rateest_target_info *info = par->targinfo; + +- xt_rateest_put(info->est1); +- if (info->est2) +- xt_rateest_put(info->est2); ++ xt_rateest_put(info->est); + } + +-static struct xt_match xt_rateest_mt_reg __read_mostly = { +- .name = "rateest", ++static struct xt_target xt_rateest_tg_reg __read_mostly = { ++ .name = "RATEEST", + .revision = 0, + .family = NFPROTO_UNSPEC, +- .match = xt_rateest_mt, +- .checkentry = xt_rateest_mt_checkentry, +- .destroy = xt_rateest_mt_destroy, +- .matchsize = sizeof(struct xt_rateest_match_info), ++ .target = xt_rateest_tg, ++ .checkentry = xt_rateest_tg_checkentry, ++ .destroy = xt_rateest_tg_destroy, ++ .targetsize = sizeof(struct xt_rateest_target_info), + .me = THIS_MODULE, + }; + +-static int __init xt_rateest_mt_init(void) ++static int __init xt_rateest_tg_init(void) + { +- return xt_register_match(&xt_rateest_mt_reg); ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(rateest_hash); i++) ++ INIT_HLIST_HEAD(&rateest_hash[i]); ++ ++ return xt_register_target(&xt_rateest_tg_reg); + } + +-static void __exit xt_rateest_mt_fini(void) ++static void __exit xt_rateest_tg_fini(void) + { +- xt_unregister_match(&xt_rateest_mt_reg); ++ xt_unregister_target(&xt_rateest_tg_reg); + } + ++ + MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); + MODULE_LICENSE("GPL"); +-MODULE_DESCRIPTION("xtables rate estimator match"); +-MODULE_ALIAS("ipt_rateest"); +-MODULE_ALIAS("ip6t_rateest"); +-module_init(xt_rateest_mt_init); +-module_exit(xt_rateest_mt_fini); ++MODULE_DESCRIPTION("Xtables: packet rate estimator"); ++MODULE_ALIAS("ipt_RATEEST"); ++MODULE_ALIAS("ip6t_RATEEST"); ++module_init(xt_rateest_tg_init); ++module_exit(xt_rateest_tg_fini); +diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c +index 1ba6793..a3b25b2 100644 +--- a/net/netfilter/xt_socket.c ++++ b/net/netfilter/xt_socket.c +@@ -129,9 +129,10 @@ xt_socket_get_sock_v4(struct net *net, const u8 protocol, + return NULL; + } + +-static bool +-socket_match(const struct sk_buff *skb, struct xt_action_param *par, +- const struct xt_socket_mtinfo1 *info) ++ ++ ++struct sock* ++xt_socket_get4_sk(const struct sk_buff *skb, struct xt_action_param *par) + { + const struct iphdr *iph = ip_hdr(skb); + struct udphdr _hdr, *hp = NULL; +@@ -148,7 +149,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, + hp = skb_header_pointer(skb, ip_hdrlen(skb), + sizeof(_hdr), &_hdr); + if (hp == NULL) +- return false; ++ return NULL; + + protocol = iph->protocol; + saddr = iph->saddr; +@@ -159,9 +160,9 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, + } else if (iph->protocol == IPPROTO_ICMP) { + if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr, + &sport, &dport)) +- return false; ++ return NULL; + } else { +- return false; ++ return NULL; + } + + #ifdef XT_SOCKET_HAVE_CONNTRACK +@@ -183,10 +184,30 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, + } + #endif + +- if (!sk) ++ if (sk) ++ atomic_inc(&sk->sk_refcnt); ++ else + sk = xt_socket_get_sock_v4(dev_net(skb->dev), protocol, + saddr, daddr, sport, dport, + par->in); ++ ++ pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n", ++ protocol, &saddr, ntohs(sport), ++ &daddr, ntohs(dport), ++ &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); ++ ++ return sk; ++} ++EXPORT_SYMBOL(xt_socket_get4_sk); ++ ++static bool ++socket_match(const struct sk_buff *skb, struct xt_action_param *par, ++ const struct xt_socket_mtinfo1 *info) ++{ ++ struct sock *sk; ++ ++ sk = xt_socket_get4_sk(skb, par); ++ + if (sk) { + bool wildcard; + bool transparent = true; +@@ -206,18 +227,12 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, + (sk->sk_state == TCP_TIME_WAIT && + inet_twsk(sk)->tw_transparent)); + +- if (sk != skb->sk) +- sock_gen_put(sk); ++ sock_gen_put(sk); + + if (wildcard || !transparent) + sk = NULL; + } + +- pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n", +- protocol, &saddr, ntohs(sport), +- &daddr, ntohs(dport), +- &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); +- + return (sk != NULL); + } + +@@ -312,8 +327,8 @@ xt_socket_get_sock_v6(struct net *net, const u8 protocol, + return NULL; + } + +-static bool +-socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) ++struct sock* ++xt_socket_get6_sk(const struct sk_buff *skb, struct xt_action_param *par) + { + struct ipv6hdr *iph = ipv6_hdr(skb); + struct udphdr _hdr, *hp = NULL; +@@ -321,7 +336,6 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) + struct in6_addr *daddr = NULL, *saddr = NULL; + __be16 uninitialized_var(dport), uninitialized_var(sport); + int thoff = 0, uninitialized_var(tproto); +- const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; + + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); + if (tproto < 0) { +@@ -333,7 +347,7 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) + hp = skb_header_pointer(skb, thoff, + sizeof(_hdr), &_hdr); + if (hp == NULL) +- return false; ++ return NULL; + + saddr = &iph->saddr; + sport = hp->source; +@@ -343,15 +357,37 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) + } else if (tproto == IPPROTO_ICMPV6) { + if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr, + &sport, &dport)) +- return false; ++ return NULL; + } else { +- return false; ++ return NULL; + } + +- if (!sk) ++ if (sk) ++ atomic_inc(&sk->sk_refcnt); ++ else + sk = xt_socket_get_sock_v6(dev_net(skb->dev), tproto, + saddr, daddr, sport, dport, + par->in); ++ ++ pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu " ++ "(orig %pI6:%hu) sock %p\n", ++ tproto, saddr, ntohs(sport), ++ daddr, ntohs(dport), ++ &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); ++ return sk; ++} ++EXPORT_SYMBOL(xt_socket_get6_sk); ++ ++static bool ++socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) ++{ ++ struct sock *sk; ++ const struct xt_socket_mtinfo1 *info; ++ ++ info = (struct xt_socket_mtinfo1 *) par->matchinfo; ++ ++ sk = xt_socket_get6_sk(skb, par); ++ + if (sk) { + bool wildcard; + bool transparent = true; +@@ -378,12 +414,6 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) + sk = NULL; + } + +- pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu " +- "(orig %pI6:%hu) sock %p\n", +- tproto, saddr, ntohs(sport), +- daddr, ntohs(dport), +- &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); +- + return (sk != NULL); + } + #endif +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index c82b2e3..84d9476 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -2599,6 +2599,7 @@ static int netlink_dump(struct sock *sk) + struct netlink_callback *cb; + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh; ++ struct module *module; + int len, err = -ENOBUFS; + int alloc_size; + +@@ -2668,9 +2669,11 @@ static int netlink_dump(struct sock *sk) + cb->done(cb); + + nlk->cb_running = false; ++ module = cb->module; ++ skb = cb->skb; + mutex_unlock(nlk->cb_mutex); +- module_put(cb->module); +- consume_skb(cb->skb); ++ module_put(module); ++ consume_skb(skb); + return 0; + + errout_skb: +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index 5dcfe05..9ef5ba6 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -3261,19 +3261,25 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv + + if (optlen != sizeof(val)) + return -EINVAL; +- if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) +- return -EBUSY; + if (copy_from_user(&val, optval, sizeof(val))) + return -EFAULT; + switch (val) { + case TPACKET_V1: + case TPACKET_V2: + case TPACKET_V3: +- po->tp_version = val; +- return 0; ++ break; + default: + return -EINVAL; + } ++ lock_sock(sk); ++ if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) { ++ ret = -EBUSY; ++ } else { ++ po->tp_version = val; ++ ret = 0; ++ } ++ release_sock(sk); ++ return ret; + } + case PACKET_RESERVE: + { +@@ -3281,12 +3287,19 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv + + if (optlen != sizeof(val)) + return -EINVAL; +- if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) +- return -EBUSY; + if (copy_from_user(&val, optval, sizeof(val))) + return -EFAULT; +- po->tp_reserve = val; +- return 0; ++ if (val > INT_MAX) ++ return -EINVAL; ++ lock_sock(sk); ++ if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) { ++ ret = -EBUSY; ++ } else { ++ po->tp_reserve = val; ++ ret = 0; ++ } ++ release_sock(sk); ++ return ret; + } + case PACKET_LOSS: + { +@@ -3736,6 +3749,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, + /* Added to avoid minimal code churn */ + struct tpacket_req *req = &req_u->req; + ++ lock_sock(sk); + /* Opening a Tx-ring is NOT supported in TPACKET_V3 */ + if (!closing && tx_ring && (po->tp_version > TPACKET_V2)) { + WARN(1, "Tx-ring is not supported.\n"); +@@ -3777,8 +3791,8 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, + if (unlikely(req->tp_block_size & (PAGE_SIZE - 1))) + goto out; + if (po->tp_version >= TPACKET_V3 && +- (int)(req->tp_block_size - +- BLK_PLUS_PRIV(req_u->req3.tp_sizeof_priv)) <= 0) ++ req->tp_block_size <= ++ BLK_PLUS_PRIV((u64)req_u->req3.tp_sizeof_priv)) + goto out; + if (unlikely(req->tp_frame_size < po->tp_hdrlen + + po->tp_reserve)) +@@ -3817,8 +3831,6 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, + goto out; + } + +- lock_sock(sk); +- + /* Detach socket from network */ + spin_lock(&po->bind_lock); + was_running = po->running; +@@ -3866,11 +3878,11 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, + if (!tx_ring) + prb_shutdown_retire_blk_timer(po, tx_ring, rb_queue); + } +- release_sock(sk); + + if (pg_vec) + free_pg_vec(pg_vec, order, req->tp_block_nr); + out: ++ release_sock(sk); + return err; + } + +diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig +index 4c10e7e..77aa1df 100644 +--- a/net/rfkill/Kconfig ++++ b/net/rfkill/Kconfig +@@ -10,6 +10,11 @@ menuconfig RFKILL + To compile this driver as a module, choose M here: the + module will be called rfkill. + ++config RFKILL_PM ++ bool "Power off on suspend" ++ depends on RFKILL && PM ++ default y ++ + # LED trigger support + config RFKILL_LEDS + bool +diff --git a/net/rfkill/core.c b/net/rfkill/core.c +index fa7cd79..c22df40 100644 +--- a/net/rfkill/core.c ++++ b/net/rfkill/core.c +@@ -782,6 +782,7 @@ void rfkill_pause_polling(struct rfkill *rfkill) + } + EXPORT_SYMBOL(rfkill_pause_polling); + ++#ifdef CONFIG_RFKILL_PM + void rfkill_resume_polling(struct rfkill *rfkill) + { + BUG_ON(!rfkill); +@@ -817,14 +818,17 @@ static int rfkill_resume(struct device *dev) + + return 0; + } ++#endif + + static struct class rfkill_class = { + .name = "rfkill", + .dev_release = rfkill_release, + .dev_groups = rfkill_dev_groups, + .dev_uevent = rfkill_dev_uevent, ++#ifdef CONFIG_RFKILL_PM + .suspend = rfkill_suspend, + .resume = rfkill_resume, ++#endif + }; + + bool rfkill_blocked(struct rfkill *rfkill) +diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c +index 79c4abc..0a63947 100644 +--- a/net/rose/rose_in.c ++++ b/net/rose/rose_in.c +@@ -164,7 +164,8 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety + rose_frames_acked(sk, nr); + if (ns == rose->vr) { + rose_start_idletimer(sk); +- if (sock_queue_rcv_skb(sk, skb) == 0) { ++ if (sk_filter_trim_cap(sk, skb, ROSE_MIN_LEN) == 0 && ++ __sock_queue_rcv_skb(sk, skb) == 0) { + rose->vr = (rose->vr + 1) % ROSE_MODULUS; + queued = 1; + } else { +diff --git a/net/socket.c b/net/socket.c +index 02fc7c8..7f61789 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -2410,31 +2410,31 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, + break; + } + +-out_put: +- fput_light(sock->file, fput_needed); +- + if (err == 0) +- return datagrams; ++ goto out_put; + +- if (datagrams != 0) { ++ if (datagrams == 0) { ++ datagrams = err; ++ goto out_put; ++ } ++ ++ /* ++ * We may return less entries than requested (vlen) if the ++ * sock is non block and there aren't enough datagrams... ++ */ ++ if (err != -EAGAIN) { + /* +- * We may return less entries than requested (vlen) if the +- * sock is non block and there aren't enough datagrams... ++ * ... or if recvmsg returns an error after we ++ * received some datagrams, where we record the ++ * error to return on the next call or if the ++ * app asks about it using getsockopt(SO_ERROR). + */ +- if (err != -EAGAIN) { +- /* +- * ... or if recvmsg returns an error after we +- * received some datagrams, where we record the +- * error to return on the next call or if the +- * app asks about it using getsockopt(SO_ERROR). +- */ +- sock->sk->sk_err = -err; +- } +- +- return datagrams; ++ sock->sk->sk_err = -err; + } ++out_put: ++ fput_light(sock->file, fput_needed); + +- return err; ++ return datagrams; + } + + SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg, +diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c +index 8232118..2852bb7 100644 +--- a/net/unix/af_unix.c ++++ b/net/unix/af_unix.c +@@ -316,6 +316,118 @@ found: + return s; + } + ++/* Support code for asymmetrically connected dgram sockets ++ * ++ * If a datagram socket is connected to a socket not itself connected ++ * to the first socket (eg, /dev/log), clients may only enqueue more ++ * messages if the present receive queue of the server socket is not ++ * "too large". This means there's a second writeability condition ++ * poll and sendmsg need to test. The dgram recv code will do a wake ++ * up on the peer_wait wait queue of a socket upon reception of a ++ * datagram which needs to be propagated to sleeping would-be writers ++ * since these might not have sent anything so far. This can't be ++ * accomplished via poll_wait because the lifetime of the server ++ * socket might be less than that of its clients if these break their ++ * association with it or if the server socket is closed while clients ++ * are still connected to it and there's no way to inform "a polling ++ * implementation" that it should let go of a certain wait queue ++ * ++ * In order to propagate a wake up, a wait_queue_t of the client ++ * socket is enqueued on the peer_wait queue of the server socket ++ * whose wake function does a wake_up on the ordinary client socket ++ * wait queue. This connection is established whenever a write (or ++ * poll for write) hit the flow control condition and broken when the ++ * association to the server socket is dissolved or after a wake up ++ * was relayed. ++ */ ++ ++static int unix_dgram_peer_wake_relay(wait_queue_t *q, unsigned mode, int flags, ++ void *key) ++{ ++ struct unix_sock *u; ++ wait_queue_head_t *u_sleep; ++ ++ u = container_of(q, struct unix_sock, peer_wake); ++ ++ __remove_wait_queue(&unix_sk(u->peer_wake.private)->peer_wait, ++ q); ++ u->peer_wake.private = NULL; ++ ++ /* relaying can only happen while the wq still exists */ ++ u_sleep = sk_sleep(&u->sk); ++ if (u_sleep) ++ wake_up_interruptible_poll(u_sleep, key); ++ ++ return 0; ++} ++ ++static int unix_dgram_peer_wake_connect(struct sock *sk, struct sock *other) ++{ ++ struct unix_sock *u, *u_other; ++ int rc; ++ ++ u = unix_sk(sk); ++ u_other = unix_sk(other); ++ rc = 0; ++ spin_lock(&u_other->peer_wait.lock); ++ ++ if (!u->peer_wake.private) { ++ u->peer_wake.private = other; ++ __add_wait_queue(&u_other->peer_wait, &u->peer_wake); ++ ++ rc = 1; ++ } ++ ++ spin_unlock(&u_other->peer_wait.lock); ++ return rc; ++} ++ ++static void unix_dgram_peer_wake_disconnect(struct sock *sk, ++ struct sock *other) ++{ ++ struct unix_sock *u, *u_other; ++ ++ u = unix_sk(sk); ++ u_other = unix_sk(other); ++ spin_lock(&u_other->peer_wait.lock); ++ ++ if (u->peer_wake.private == other) { ++ __remove_wait_queue(&u_other->peer_wait, &u->peer_wake); ++ u->peer_wake.private = NULL; ++ } ++ ++ spin_unlock(&u_other->peer_wait.lock); ++} ++ ++static void unix_dgram_peer_wake_disconnect_wakeup(struct sock *sk, ++ struct sock *other) ++{ ++ unix_dgram_peer_wake_disconnect(sk, other); ++ wake_up_interruptible_poll(sk_sleep(sk), ++ POLLOUT | ++ POLLWRNORM | ++ POLLWRBAND); ++} ++ ++/* preconditions: ++ * - unix_peer(sk) == other ++ * - association is stable ++ */ ++static int unix_dgram_peer_wake_me(struct sock *sk, struct sock *other) ++{ ++ int connected; ++ ++ connected = unix_dgram_peer_wake_connect(sk, other); ++ ++ if (unix_recvq_full(other)) ++ return 1; ++ ++ if (connected) ++ unix_dgram_peer_wake_disconnect(sk, other); ++ ++ return 0; ++} ++ + static inline int unix_writable(struct sock *sk) + { + return (atomic_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf; +@@ -420,6 +532,8 @@ static void unix_release_sock(struct sock *sk, int embrion) + skpair->sk_state_change(skpair); + sk_wake_async(skpair, SOCK_WAKE_WAITD, POLL_HUP); + } ++ ++ unix_dgram_peer_wake_disconnect(sk, skpair); + sock_put(skpair); /* It may now die */ + unix_peer(sk) = NULL; + } +@@ -653,6 +767,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock) + INIT_LIST_HEAD(&u->link); + mutex_init(&u->readlock); /* single task reading lock */ + init_waitqueue_head(&u->peer_wait); ++ init_waitqueue_func_entry(&u->peer_wake, unix_dgram_peer_wake_relay); + unix_insert_socket(unix_sockets_unbound(sk), sk); + out: + if (sk == NULL) +@@ -1020,6 +1135,8 @@ restart: + if (unix_peer(sk)) { + struct sock *old_peer = unix_peer(sk); + unix_peer(sk) = other; ++ unix_dgram_peer_wake_disconnect_wakeup(sk, old_peer); ++ + unix_state_double_unlock(sk, other); + + if (other != old_peer) +@@ -1352,7 +1469,7 @@ static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb) + UNIXCB(skb).fp = NULL; + + for (i = scm->fp->count-1; i >= 0; i--) +- unix_notinflight(scm->fp->fp[i]); ++ unix_notinflight(scm->fp->user, scm->fp->fp[i]); + } + + static void unix_destruct_scm(struct sk_buff *skb) +@@ -1369,6 +1486,21 @@ static void unix_destruct_scm(struct sk_buff *skb) + sock_wfree(skb); + } + ++/* ++ * The "user->unix_inflight" variable is protected by the garbage ++ * collection lock, and we just read it locklessly here. If you go ++ * over the limit, there might be a tiny race in actually noticing ++ * it across threads. Tough. ++ */ ++static inline bool too_many_unix_fds(struct task_struct *p) ++{ ++ struct user_struct *user = current_user(); ++ ++ if (unlikely(user->unix_inflight > task_rlimit(p, RLIMIT_NOFILE))) ++ return !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN); ++ return false; ++} ++ + #define MAX_RECURSION_LEVEL 4 + + static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) +@@ -1377,6 +1509,9 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) + unsigned char max_level = 0; + int unix_sock_count = 0; + ++ if (too_many_unix_fds(current)) ++ return -ETOOMANYREFS; ++ + for (i = scm->fp->count - 1; i >= 0; i--) { + struct sock *sk = unix_get_socket(scm->fp->fp[i]); + +@@ -1398,10 +1533,8 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) + if (!UNIXCB(skb).fp) + return -ENOMEM; + +- if (unix_sock_count) { +- for (i = scm->fp->count - 1; i >= 0; i--) +- unix_inflight(scm->fp->fp[i]); +- } ++ for (i = scm->fp->count - 1; i >= 0; i--) ++ unix_inflight(scm->fp->user, scm->fp->fp[i]); + return max_level; + } + +@@ -1459,6 +1592,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, + struct scm_cookie tmp_scm; + int max_level; + int data_len = 0; ++ int sk_locked; + + if (NULL == siocb->scm) + siocb->scm = &tmp_scm; +@@ -1540,12 +1674,14 @@ restart: + goto out_free; + } + ++ sk_locked = 0; + unix_state_lock(other); ++restart_locked: + err = -EPERM; + if (!unix_may_send(sk, other)) + goto out_unlock; + +- if (sock_flag(other, SOCK_DEAD)) { ++ if (unlikely(sock_flag(other, SOCK_DEAD))) { + /* + * Check with 1003.1g - what should + * datagram error +@@ -1553,10 +1689,14 @@ restart: + unix_state_unlock(other); + sock_put(other); + ++ if (!sk_locked) ++ unix_state_lock(sk); ++ + err = 0; +- unix_state_lock(sk); + if (unix_peer(sk) == other) { + unix_peer(sk) = NULL; ++ unix_dgram_peer_wake_disconnect_wakeup(sk, other); ++ + unix_state_unlock(sk); + + unix_dgram_disconnected(sk, other); +@@ -1582,21 +1722,43 @@ restart: + goto out_unlock; + } + +- if (unix_peer(other) != sk && unix_recvq_full(other)) { +- if (!timeo) { +- err = -EAGAIN; +- goto out_unlock; ++ /* other == sk && unix_peer(other) != sk if ++ * - unix_peer(sk) == NULL, destination address bound to sk ++ * - unix_peer(sk) == sk by time of get but disconnected before lock ++ */ ++ if (other != sk && ++ unlikely(unix_peer(other) != sk && unix_recvq_full(other))) { ++ if (timeo) { ++ timeo = unix_wait_for_peer(other, timeo); ++ ++ err = sock_intr_errno(timeo); ++ if (signal_pending(current)) ++ goto out_free; ++ ++ goto restart; + } + +- timeo = unix_wait_for_peer(other, timeo); ++ if (!sk_locked) { ++ unix_state_unlock(other); ++ unix_state_double_lock(sk, other); ++ } + +- err = sock_intr_errno(timeo); +- if (signal_pending(current)) +- goto out_free; ++ if (unix_peer(sk) != other || ++ unix_dgram_peer_wake_me(sk, other)) { ++ err = -EAGAIN; ++ sk_locked = 1; ++ goto out_unlock; ++ } + +- goto restart; ++ if (!sk_locked) { ++ sk_locked = 1; ++ goto restart_locked; ++ } + } + ++ if (unlikely(sk_locked)) ++ unix_state_unlock(sk); ++ + if (sock_flag(other, SOCK_RCVTSTAMP)) + __net_timestamp(skb); + maybe_add_creds(skb, sock, other); +@@ -1610,6 +1772,8 @@ restart: + return len; + + out_unlock: ++ if (sk_locked) ++ unix_state_unlock(sk); + unix_state_unlock(other); + out_free: + kfree_skb(skb); +@@ -2255,14 +2419,16 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock, + return mask; + + writable = unix_writable(sk); +- other = unix_peer_get(sk); +- if (other) { +- if (unix_peer(other) != sk) { +- sock_poll_wait(file, &unix_sk(other)->peer_wait, wait); +- if (unix_recvq_full(other)) +- writable = 0; +- } +- sock_put(other); ++ if (writable) { ++ unix_state_lock(sk); ++ ++ other = unix_peer(sk); ++ if (other && unix_peer(other) != sk && ++ unix_recvq_full(other) && ++ unix_dgram_peer_wake_me(sk, other)) ++ writable = 0; ++ ++ unix_state_unlock(sk); + } + + if (writable) +diff --git a/net/unix/garbage.c b/net/unix/garbage.c +index 99f7012..a5385af 100644 +--- a/net/unix/garbage.c ++++ b/net/unix/garbage.c +@@ -122,12 +122,15 @@ struct sock *unix_get_socket(struct file *filp) + * descriptor if it is for an AF_UNIX socket. + */ + +-void unix_inflight(struct file *fp) ++void unix_inflight(struct user_struct *user, struct file *fp) + { + struct sock *s = unix_get_socket(fp); ++ ++ spin_lock(&unix_gc_lock); ++ + if (s) { + struct unix_sock *u = unix_sk(s); +- spin_lock(&unix_gc_lock); ++ + if (atomic_long_inc_return(&u->inflight) == 1) { + BUG_ON(!list_empty(&u->link)); + list_add_tail(&u->link, &gc_inflight_list); +@@ -135,22 +138,27 @@ void unix_inflight(struct file *fp) + BUG_ON(list_empty(&u->link)); + } + unix_tot_inflight++; +- spin_unlock(&unix_gc_lock); + } ++ user->unix_inflight++; ++ spin_unlock(&unix_gc_lock); + } + +-void unix_notinflight(struct file *fp) ++void unix_notinflight(struct user_struct *user, struct file *fp) + { + struct sock *s = unix_get_socket(fp); ++ ++ spin_lock(&unix_gc_lock); ++ + if (s) { + struct unix_sock *u = unix_sk(s); +- spin_lock(&unix_gc_lock); ++ + BUG_ON(list_empty(&u->link)); + if (atomic_long_dec_and_test(&u->inflight)) + list_del_init(&u->link); + unix_tot_inflight--; +- spin_unlock(&unix_gc_lock); + } ++ user->unix_inflight--; ++ spin_unlock(&unix_gc_lock); + } + + static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *), +diff --git a/net/wireless/scan.c b/net/wireless/scan.c +index bda39f1..9e62e9c 100644 +--- a/net/wireless/scan.c ++++ b/net/wireless/scan.c +@@ -56,7 +56,7 @@ + * also linked into the probe response struct. + */ + +-#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) ++#define IEEE80211_SCAN_RESULT_EXPIRE (7 * HZ) + + static void bss_free(struct cfg80211_internal_bss *bss) + { +diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include +index 65e7b08..5374b1b 100644 +--- a/scripts/Kbuild.include ++++ b/scripts/Kbuild.include +@@ -179,6 +179,12 @@ build := -f $(srctree)/scripts/Makefile.build obj + # $(Q)$(MAKE) $(modbuiltin)=dir + modbuiltin := -f $(srctree)/scripts/Makefile.modbuiltin obj + ++### ++# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.dtbinst obj= ++# Usage: ++# $(Q)$(MAKE) $(dtbinst)=dir ++dtbinst := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.dtbinst obj ++ + # Prefix -I with $(srctree) if it is not an absolute path. + # skip if -I has no parameter + addtree = $(if $(patsubst -I%,%,$(1)), \ +diff --git a/scripts/Makefile.dtbinst b/scripts/Makefile.dtbinst +new file mode 100644 +index 0000000..909ed7a +--- /dev/null ++++ b/scripts/Makefile.dtbinst +@@ -0,0 +1,51 @@ ++# ========================================================================== ++# Installing dtb files ++# ++# Installs all dtb files listed in $(dtb-y) either in the ++# INSTALL_DTBS_PATH directory or the default location: ++# ++# $INSTALL_PATH/dtbs/$KERNELRELEASE ++# ++# Traverse through subdirectories listed in $(dts-dirs). ++# ========================================================================== ++ ++src := $(obj) ++ ++PHONY := __dtbs_install ++__dtbs_install: ++ ++export dtbinst-root ?= $(obj) ++ ++include include/config/auto.conf ++include scripts/Kbuild.include ++include $(srctree)/$(obj)/Makefile ++ ++PHONY += __dtbs_install_prep ++__dtbs_install_prep: ++ifeq ("$(dtbinst-root)", "$(obj)") ++ $(Q)if [ -d $(INSTALL_DTBS_PATH).old ]; then rm -rf $(INSTALL_DTBS_PATH).old; fi ++ $(Q)if [ -d $(INSTALL_DTBS_PATH) ]; then mv $(INSTALL_DTBS_PATH) $(INSTALL_DTBS_PATH).old; fi ++ $(Q)mkdir -p $(INSTALL_DTBS_PATH) ++endif ++ ++dtbinst-files := $(dtb-y) ++dtbinst-dirs := $(dts-dirs) ++ ++# Helper targets for Installing DTBs into the boot directory ++quiet_cmd_dtb_install = INSTALL $< ++ cmd_dtb_install = mkdir -p $(2); cp $< $(2) ++ ++install-dir = $(patsubst $(dtbinst-root)%,$(INSTALL_DTBS_PATH)%,$(obj)) ++ ++$(dtbinst-files) $(dtbinst-dirs): | __dtbs_install_prep ++ ++$(dtbinst-files): %.dtb: $(obj)/%.dtb ++ $(call cmd,dtb_install,$(install-dir)) ++ ++$(dtbinst-dirs): ++ $(Q)$(MAKE) $(dtbinst)=$(obj)/$@ ++ ++PHONY += $(dtbinst-files) $(dtbinst-dirs) ++__dtbs_install: $(dtbinst-files) $(dtbinst-dirs) ++ ++.PHONY: $(PHONY) +diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib +index 54be19a..bb65833 100644 +--- a/scripts/Makefile.lib ++++ b/scripts/Makefile.lib +@@ -283,17 +283,11 @@ $(obj)/%.dtb: $(src)/%.dts FORCE + + dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) + +-# Helper targets for Installing DTBs into the boot directory +-quiet_cmd_dtb_install = INSTALL $< +- cmd_dtb_install = cp $< $(2) +- +-_dtbinst_pre_: +- $(Q)if [ -d $(INSTALL_DTBS_PATH).old ]; then rm -rf $(INSTALL_DTBS_PATH).old; fi +- $(Q)if [ -d $(INSTALL_DTBS_PATH) ]; then mv $(INSTALL_DTBS_PATH) $(INSTALL_DTBS_PATH).old; fi +- $(Q)mkdir -p $(INSTALL_DTBS_PATH) +- +-%.dtb_dtbinst_: $(obj)/%.dtb _dtbinst_pre_ +- $(call cmd,dtb_install,$(INSTALL_DTBS_PATH)) ++# cat ++# --------------------------------------------------------------------------- ++# Concatentate multiple files together ++quiet_cmd_cat = CAT $@ ++cmd_cat = (cat $(filter-out FORCE,$^) > $@) || (rm -f $@; false) + + # Bzip2 + # --------------------------------------------------------------------------- +diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst +index e48a4e9..9b7b280 100644 +--- a/scripts/Makefile.modinst ++++ b/scripts/Makefile.modinst +@@ -29,7 +29,7 @@ quiet_cmd_modules_install = INSTALL $@ + INSTALL_MOD_DIR ?= extra + ext-mod-dir = $(INSTALL_MOD_DIR)$(subst $(patsubst %/,%,$(KBUILD_EXTMOD)),,$(@D)) + +-modinst_dir = $(if $(KBUILD_EXTMOD),$(ext-mod-dir),kernel/$(@D)) ++modinst_dir ?= $(if $(KBUILD_EXTMOD),$(ext-mod-dir),kernel/$(@D)) + + $(modules): + $(call cmd,modules_install,$(MODLIB)/$(modinst_dir)) +diff --git a/scripts/setlocalversion b/scripts/setlocalversion +index 63d91e2..186cb46 100755 +--- a/scripts/setlocalversion ++++ b/scripts/setlocalversion +@@ -1,4 +1,4 @@ +-#!/bin/sh ++#/bin/sh + # + # This scripts adds local version information from the version + # control systems git, mercurial (hg) and subversion (svn). +@@ -153,6 +153,7 @@ if test ! "$srctree" -ef .; then + res="$res$(collect_files "$srctree"/localversion*)" + fi + ++LOCALVERSION= + # CONFIG_LOCALVERSION and LOCALVERSION (if set) + res="${res}${CONFIG_LOCALVERSION}${LOCALVERSION}" + +diff --git a/security/capability.c b/security/capability.c +index d68c57a..ef951b0 100644 +--- a/security/capability.c ++++ b/security/capability.c +@@ -12,6 +12,26 @@ + + #include <linux/security.h> + ++static int cap_binder_set_context_mgr(struct task_struct *mgr) ++{ ++ return 0; ++} ++ ++static int cap_binder_transaction(struct task_struct *from, struct task_struct *to) ++{ ++ return 0; ++} ++ ++static int cap_binder_transfer_binder(struct task_struct *from, struct task_struct *to) ++{ ++ return 0; ++} ++ ++static int cap_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) ++{ ++ return 0; ++} ++ + static int cap_syslog(int type) + { + return 0; +@@ -930,6 +950,10 @@ static void cap_audit_rule_free(void *lsmrule) + + void __init security_fixup_ops(struct security_operations *ops) + { ++ set_to_cap_if_null(ops, binder_set_context_mgr); ++ set_to_cap_if_null(ops, binder_transaction); ++ set_to_cap_if_null(ops, binder_transfer_binder); ++ set_to_cap_if_null(ops, binder_transfer_file); + set_to_cap_if_null(ops, ptrace_access_check); + set_to_cap_if_null(ops, ptrace_traceme); + set_to_cap_if_null(ops, capget); +diff --git a/security/commoncap.c b/security/commoncap.c +index bab0611..7ae2519 100644 +--- a/security/commoncap.c ++++ b/security/commoncap.c +@@ -31,6 +31,10 @@ + #include <linux/binfmts.h> + #include <linux/personality.h> + ++#ifdef CONFIG_ANDROID_PARANOID_NETWORK ++#include <linux/android_aid.h> ++#endif ++ + /* + * If a non-root user executes a setuid-root binary in + * !secure(SECURE_NOROOT) mode, then we raise capabilities. +@@ -78,6 +82,13 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, + { + struct user_namespace *ns = targ_ns; + ++#ifdef CONFIG_ANDROID_PARANOID_NETWORK ++ if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) ++ return 0; ++ if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) ++ return 0; ++#endif ++ + /* See if cred has the capability in the target user namespace + * by examining the target user namespace and all of the target + * user namespace's parents. +diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c +index bd536cb..db91639 100644 +--- a/security/keys/process_keys.c ++++ b/security/keys/process_keys.c +@@ -794,6 +794,7 @@ long join_session_keyring(const char *name) + ret = PTR_ERR(keyring); + goto error2; + } else if (keyring == new->session_keyring) { ++ key_put(keyring); + ret = 0; + goto error2; + } +diff --git a/security/lsm_audit.c b/security/lsm_audit.c +index 69fdf3b..7147c17 100644 +--- a/security/lsm_audit.c ++++ b/security/lsm_audit.c +@@ -245,6 +245,21 @@ static void dump_common_audit_data(struct audit_buffer *ab, + } + break; + } ++ case LSM_AUDIT_DATA_IOCTL_OP: { ++ struct inode *inode; ++ ++ audit_log_d_path(ab, " path=", &a->u.op->path); ++ ++ inode = a->u.op->path.dentry->d_inode; ++ if (inode) { ++ audit_log_format(ab, " dev="); ++ audit_log_untrustedstring(ab, inode->i_sb->s_id); ++ audit_log_format(ab, " ino=%lu", inode->i_ino); ++ } ++ ++ audit_log_format(ab, " ioctlcmd=%hx", a->u.op->cmd); ++ break; ++ } + case LSM_AUDIT_DATA_DENTRY: { + struct inode *inode; + +diff --git a/security/security.c b/security/security.c +index 18b35c6..6ad484c 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -135,6 +135,26 @@ int __init register_security(struct security_operations *ops) + + /* Security operations */ + ++int security_binder_set_context_mgr(struct task_struct *mgr) ++{ ++ return security_ops->binder_set_context_mgr(mgr); ++} ++ ++int security_binder_transaction(struct task_struct *from, struct task_struct *to) ++{ ++ return security_ops->binder_transaction(from, to); ++} ++ ++int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to) ++{ ++ return security_ops->binder_transfer_binder(from, to); ++} ++ ++int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) ++{ ++ return security_ops->binder_transfer_file(from, to, file); ++} ++ + int security_ptrace_access_check(struct task_struct *child, unsigned int mode) + { + #ifdef CONFIG_SECURITY_YAMA_STACKED +diff --git a/security/selinux/avc.c b/security/selinux/avc.c +index a18f1fa..21a3415 100644 +--- a/security/selinux/avc.c ++++ b/security/selinux/avc.c +@@ -22,6 +22,7 @@ + #include <linux/init.h> + #include <linux/skbuff.h> + #include <linux/percpu.h> ++#include <linux/list.h> + #include <net/sock.h> + #include <linux/un.h> + #include <net/af_unix.h> +@@ -48,6 +49,7 @@ struct avc_entry { + u32 tsid; + u16 tclass; + struct av_decision avd; ++ struct avc_operation_node *ops_node; + }; + + struct avc_node { +@@ -64,6 +66,16 @@ struct avc_cache { + u32 latest_notif; /* latest revocation notification */ + }; + ++struct avc_operation_decision_node { ++ struct operation_decision od; ++ struct list_head od_list; ++}; ++ ++struct avc_operation_node { ++ struct operation ops; ++ struct list_head od_head; /* list of operation_decision_node */ ++}; ++ + struct avc_callback_node { + int (*callback) (u32 event); + u32 events; +@@ -80,6 +92,9 @@ DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 }; + static struct avc_cache avc_cache; + static struct avc_callback_node *avc_callbacks; + static struct kmem_cache *avc_node_cachep; ++static struct kmem_cache *avc_operation_decision_node_cachep; ++static struct kmem_cache *avc_operation_node_cachep; ++static struct kmem_cache *avc_operation_perm_cachep; + + static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) + { +@@ -171,6 +186,16 @@ void __init avc_init(void) + + avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), + 0, SLAB_PANIC, NULL); ++ avc_operation_node_cachep = kmem_cache_create("avc_operation_node", ++ sizeof(struct avc_operation_node), ++ 0, SLAB_PANIC, NULL); ++ avc_operation_decision_node_cachep = kmem_cache_create( ++ "avc_operation_decision_node", ++ sizeof(struct avc_operation_decision_node), ++ 0, SLAB_PANIC, NULL); ++ avc_operation_perm_cachep = kmem_cache_create("avc_operation_perm", ++ sizeof(struct operation_perm), ++ 0, SLAB_PANIC, NULL); + + audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n"); + } +@@ -205,9 +230,271 @@ int avc_get_hash_stats(char *page) + slots_used, AVC_CACHE_SLOTS, max_chain_len); + } + ++/* ++ * using a linked list for operation_decision lookup because the list is ++ * always small. i.e. less than 5, typically 1 ++ */ ++static struct operation_decision *avc_operation_lookup(u8 type, ++ struct avc_operation_node *ops_node) ++{ ++ struct avc_operation_decision_node *od_node; ++ struct operation_decision *od = NULL; ++ ++ list_for_each_entry(od_node, &ops_node->od_head, od_list) { ++ if (od_node->od.type != type) ++ continue; ++ od = &od_node->od; ++ break; ++ } ++ return od; ++} ++ ++static inline unsigned int avc_operation_has_perm(struct operation_decision *od, ++ u16 cmd, u8 specified) ++{ ++ unsigned int rc = 0; ++ u8 num = cmd & 0xff; ++ ++ if ((specified == OPERATION_ALLOWED) && ++ (od->specified & OPERATION_ALLOWED)) ++ rc = security_operation_test(od->allowed->perms, num); ++ else if ((specified == OPERATION_AUDITALLOW) && ++ (od->specified & OPERATION_AUDITALLOW)) ++ rc = security_operation_test(od->auditallow->perms, num); ++ else if ((specified == OPERATION_DONTAUDIT) && ++ (od->specified & OPERATION_DONTAUDIT)) ++ rc = security_operation_test(od->dontaudit->perms, num); ++ return rc; ++} ++ ++static void avc_operation_allow_perm(struct avc_operation_node *node, u16 cmd) ++{ ++ struct operation_decision *od; ++ u8 type; ++ u8 num; ++ ++ type = cmd >> 8; ++ num = cmd & 0xff; ++ security_operation_set(node->ops.type, type); ++ od = avc_operation_lookup(type, node); ++ if (od && od->allowed) ++ security_operation_set(od->allowed->perms, num); ++} ++ ++static void avc_operation_decision_free( ++ struct avc_operation_decision_node *od_node) ++{ ++ struct operation_decision *od; ++ ++ od = &od_node->od; ++ if (od->allowed) ++ kmem_cache_free(avc_operation_perm_cachep, od->allowed); ++ if (od->auditallow) ++ kmem_cache_free(avc_operation_perm_cachep, od->auditallow); ++ if (od->dontaudit) ++ kmem_cache_free(avc_operation_perm_cachep, od->dontaudit); ++ kmem_cache_free(avc_operation_decision_node_cachep, od_node); ++} ++ ++static void avc_operation_free(struct avc_operation_node *ops_node) ++{ ++ struct avc_operation_decision_node *od_node, *tmp; ++ ++ if (!ops_node) ++ return; ++ ++ list_for_each_entry_safe(od_node, tmp, &ops_node->od_head, od_list) { ++ list_del(&od_node->od_list); ++ avc_operation_decision_free(od_node); ++ } ++ kmem_cache_free(avc_operation_node_cachep, ops_node); ++} ++ ++static void avc_copy_operation_decision(struct operation_decision *dest, ++ struct operation_decision *src) ++{ ++ dest->type = src->type; ++ dest->specified = src->specified; ++ if (dest->specified & OPERATION_ALLOWED) ++ memcpy(dest->allowed->perms, src->allowed->perms, ++ sizeof(src->allowed->perms)); ++ if (dest->specified & OPERATION_AUDITALLOW) ++ memcpy(dest->auditallow->perms, src->auditallow->perms, ++ sizeof(src->auditallow->perms)); ++ if (dest->specified & OPERATION_DONTAUDIT) ++ memcpy(dest->dontaudit->perms, src->dontaudit->perms, ++ sizeof(src->dontaudit->perms)); ++} ++ ++/* ++ * similar to avc_copy_operation_decision, but only copy decision ++ * information relevant to this command ++ */ ++static inline void avc_quick_copy_operation_decision(u16 cmd, ++ struct operation_decision *dest, ++ struct operation_decision *src) ++{ ++ /* ++ * compute index of the u32 of the 256 bits (8 u32s) that contain this ++ * command permission ++ */ ++ u8 i = (0xff & cmd) >> 5; ++ ++ dest->specified = src->specified; ++ if (dest->specified & OPERATION_ALLOWED) ++ dest->allowed->perms[i] = src->allowed->perms[i]; ++ if (dest->specified & OPERATION_AUDITALLOW) ++ dest->auditallow->perms[i] = src->auditallow->perms[i]; ++ if (dest->specified & OPERATION_DONTAUDIT) ++ dest->dontaudit->perms[i] = src->dontaudit->perms[i]; ++} ++ ++static struct avc_operation_decision_node ++ *avc_operation_decision_alloc(u8 specified) ++{ ++ struct avc_operation_decision_node *node; ++ struct operation_decision *od; ++ ++ node = kmem_cache_zalloc(avc_operation_decision_node_cachep, ++ GFP_ATOMIC | __GFP_NOMEMALLOC); ++ if (!node) ++ return NULL; ++ ++ od = &node->od; ++ if (specified & OPERATION_ALLOWED) { ++ od->allowed = kmem_cache_zalloc(avc_operation_perm_cachep, ++ GFP_ATOMIC | __GFP_NOMEMALLOC); ++ if (!od->allowed) ++ goto error; ++ } ++ if (specified & OPERATION_AUDITALLOW) { ++ od->auditallow = kmem_cache_zalloc(avc_operation_perm_cachep, ++ GFP_ATOMIC | __GFP_NOMEMALLOC); ++ if (!od->auditallow) ++ goto error; ++ } ++ if (specified & OPERATION_DONTAUDIT) { ++ od->dontaudit = kmem_cache_zalloc(avc_operation_perm_cachep, ++ GFP_ATOMIC | __GFP_NOMEMALLOC); ++ if (!od->dontaudit) ++ goto error; ++ } ++ return node; ++error: ++ avc_operation_decision_free(node); ++ return NULL; ++} ++ ++static int avc_add_operation(struct avc_node *node, ++ struct operation_decision *od) ++{ ++ struct avc_operation_decision_node *dest_od; ++ ++ node->ae.ops_node->ops.len++; ++ dest_od = avc_operation_decision_alloc(od->specified); ++ if (!dest_od) ++ return -ENOMEM; ++ avc_copy_operation_decision(&dest_od->od, od); ++ list_add(&dest_od->od_list, &node->ae.ops_node->od_head); ++ return 0; ++} ++ ++static struct avc_operation_node *avc_operation_alloc(void) ++{ ++ struct avc_operation_node *ops; ++ ++ ops = kmem_cache_zalloc(avc_operation_node_cachep, ++ GFP_ATOMIC|__GFP_NOMEMALLOC); ++ if (!ops) ++ return ops; ++ INIT_LIST_HEAD(&ops->od_head); ++ return ops; ++} ++ ++static int avc_operation_populate(struct avc_node *node, ++ struct avc_operation_node *src) ++{ ++ struct avc_operation_node *dest; ++ struct avc_operation_decision_node *dest_od; ++ struct avc_operation_decision_node *src_od; ++ ++ if (src->ops.len == 0) ++ return 0; ++ dest = avc_operation_alloc(); ++ if (!dest) ++ return -ENOMEM; ++ ++ memcpy(dest->ops.type, &src->ops.type, sizeof(dest->ops.type)); ++ dest->ops.len = src->ops.len; ++ ++ /* for each source od allocate a destination od and copy */ ++ list_for_each_entry(src_od, &src->od_head, od_list) { ++ dest_od = avc_operation_decision_alloc(src_od->od.specified); ++ if (!dest_od) ++ goto error; ++ avc_copy_operation_decision(&dest_od->od, &src_od->od); ++ list_add(&dest_od->od_list, &dest->od_head); ++ } ++ node->ae.ops_node = dest; ++ return 0; ++error: ++ avc_operation_free(dest); ++ return -ENOMEM; ++ ++} ++ ++static inline u32 avc_operation_audit_required(u32 requested, ++ struct av_decision *avd, ++ struct operation_decision *od, ++ u16 cmd, ++ int result, ++ u32 *deniedp) ++{ ++ u32 denied, audited; ++ ++ denied = requested & ~avd->allowed; ++ if (unlikely(denied)) { ++ audited = denied & avd->auditdeny; ++ if (audited && od) { ++ if (avc_operation_has_perm(od, cmd, ++ OPERATION_DONTAUDIT)) ++ audited &= ~requested; ++ } ++ } else if (result) { ++ audited = denied = requested; ++ } else { ++ audited = requested & avd->auditallow; ++ if (audited && od) { ++ if (!avc_operation_has_perm(od, cmd, ++ OPERATION_AUDITALLOW)) ++ audited &= ~requested; ++ } ++ } ++ ++ *deniedp = denied; ++ return audited; ++} ++ ++static inline int avc_operation_audit(u32 ssid, u32 tsid, u16 tclass, ++ u32 requested, struct av_decision *avd, ++ struct operation_decision *od, ++ u16 cmd, int result, ++ struct common_audit_data *ad) ++{ ++ u32 audited, denied; ++ ++ audited = avc_operation_audit_required( ++ requested, avd, od, cmd, result, &denied); ++ if (likely(!audited)) ++ return 0; ++ return slow_avc_audit(ssid, tsid, tclass, requested, ++ audited, denied, result, ad, 0); ++} ++ + static void avc_node_free(struct rcu_head *rhead) + { + struct avc_node *node = container_of(rhead, struct avc_node, rhead); ++ avc_operation_free(node->ae.ops_node); + kmem_cache_free(avc_node_cachep, node); + avc_cache_stats_incr(frees); + } +@@ -221,6 +508,7 @@ static void avc_node_delete(struct avc_node *node) + + static void avc_node_kill(struct avc_node *node) + { ++ avc_operation_free(node->ae.ops_node); + kmem_cache_free(avc_node_cachep, node); + avc_cache_stats_incr(frees); + atomic_dec(&avc_cache.active_nodes); +@@ -367,6 +655,7 @@ static int avc_latest_notif_update(int seqno, int is_insert) + * @tsid: target security identifier + * @tclass: target security class + * @avd: resulting av decision ++ * @ops: resulting operation decisions + * + * Insert an AVC entry for the SID pair + * (@ssid, @tsid) and class @tclass. +@@ -378,7 +667,9 @@ static int avc_latest_notif_update(int seqno, int is_insert) + * the access vectors into a cache entry, returns + * avc_node inserted. Otherwise, this function returns NULL. + */ +-static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd) ++static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, ++ struct av_decision *avd, ++ struct avc_operation_node *ops_node) + { + struct avc_node *pos, *node = NULL; + int hvalue; +@@ -391,10 +682,15 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_dec + if (node) { + struct hlist_head *head; + spinlock_t *lock; ++ int rc = 0; + + hvalue = avc_hash(ssid, tsid, tclass); + avc_node_populate(node, ssid, tsid, tclass, avd); +- ++ rc = avc_operation_populate(node, ops_node); ++ if (rc) { ++ kmem_cache_free(avc_node_cachep, node); ++ return NULL; ++ } + head = &avc_cache.slots[hvalue]; + lock = &avc_cache.slots_lock[hvalue]; + +@@ -528,14 +824,17 @@ static inline int avc_sidcmp(u32 x, u32 y) + * @perms : Permission mask bits + * @ssid,@tsid,@tclass : identifier of an AVC entry + * @seqno : sequence number when decision was made ++ * @od: operation_decision to be added to the node + * + * if a valid AVC entry doesn't exist,this function returns -ENOENT. + * if kmalloc() called internal returns NULL, this function returns -ENOMEM. + * otherwise, this function updates the AVC entry. The original AVC-entry object + * will release later by RCU. + */ +-static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, +- u32 seqno) ++static int avc_update_node(u32 event, u32 perms, u16 cmd, u32 ssid, u32 tsid, ++ u16 tclass, u32 seqno, ++ struct operation_decision *od, ++ u32 flags) + { + int hvalue, rc = 0; + unsigned long flag; +@@ -579,9 +878,19 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, + + avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd); + ++ if (orig->ae.ops_node) { ++ rc = avc_operation_populate(node, orig->ae.ops_node); ++ if (rc) { ++ kmem_cache_free(avc_node_cachep, node); ++ goto out_unlock; ++ } ++ } ++ + switch (event) { + case AVC_CALLBACK_GRANT: + node->ae.avd.allowed |= perms; ++ if (node->ae.ops_node && (flags & AVC_OPERATION_CMD)) ++ avc_operation_allow_perm(node->ae.ops_node, cmd); + break; + case AVC_CALLBACK_TRY_REVOKE: + case AVC_CALLBACK_REVOKE: +@@ -599,6 +908,9 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, + case AVC_CALLBACK_AUDITDENY_DISABLE: + node->ae.avd.auditdeny &= ~perms; + break; ++ case AVC_CALLBACK_ADD_OPERATION: ++ avc_add_operation(node, od); ++ break; + } + avc_node_replace(node, orig); + out_unlock: +@@ -670,18 +982,20 @@ int avc_ss_reset(u32 seqno) + * results in a bigger stack frame. + */ + static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid, +- u16 tclass, struct av_decision *avd) ++ u16 tclass, struct av_decision *avd, ++ struct avc_operation_node *ops_node) + { + rcu_read_unlock(); +- security_compute_av(ssid, tsid, tclass, avd); ++ INIT_LIST_HEAD(&ops_node->od_head); ++ security_compute_av(ssid, tsid, tclass, avd, &ops_node->ops); + rcu_read_lock(); +- return avc_insert(ssid, tsid, tclass, avd); ++ return avc_insert(ssid, tsid, tclass, avd, ops_node); + } + + static noinline int avc_denied(u32 ssid, u32 tsid, +- u16 tclass, u32 requested, +- unsigned flags, +- struct av_decision *avd) ++ u16 tclass, u32 requested, ++ u16 cmd, unsigned flags, ++ struct av_decision *avd) + { + if (flags & AVC_STRICT) + return -EACCES; +@@ -689,11 +1003,92 @@ static noinline int avc_denied(u32 ssid, u32 tsid, + if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE)) + return -EACCES; + +- avc_update_node(AVC_CALLBACK_GRANT, requested, ssid, +- tsid, tclass, avd->seqno); ++ avc_update_node(AVC_CALLBACK_GRANT, requested, cmd, ssid, ++ tsid, tclass, avd->seqno, NULL, flags); + return 0; + } + ++/* ++ * ioctl commands are comprised of four fields, direction, size, type, and ++ * number. The avc operation logic filters based on two of them: ++ * ++ * type: or code, typically unique to each driver ++ * number: or function ++ * ++ * For example, 0x89 is a socket type, and number 0x27 is the get hardware ++ * address function. ++ */ ++int avc_has_operation(u32 ssid, u32 tsid, u16 tclass, u32 requested, ++ u16 cmd, struct common_audit_data *ad) ++{ ++ struct avc_node *node; ++ struct av_decision avd; ++ u32 denied; ++ struct operation_decision *od = NULL; ++ struct operation_decision od_local; ++ struct operation_perm allowed; ++ struct operation_perm auditallow; ++ struct operation_perm dontaudit; ++ struct avc_operation_node local_ops_node; ++ struct avc_operation_node *ops_node; ++ u8 type = cmd >> 8; ++ int rc = 0, rc2; ++ ++ ops_node = &local_ops_node; ++ BUG_ON(!requested); ++ ++ rcu_read_lock(); ++ ++ node = avc_lookup(ssid, tsid, tclass); ++ if (unlikely(!node)) { ++ node = avc_compute_av(ssid, tsid, tclass, &avd, ops_node); ++ } else { ++ memcpy(&avd, &node->ae.avd, sizeof(avd)); ++ ops_node = node->ae.ops_node; ++ } ++ /* if operations are not defined, only consider av_decision */ ++ if (!ops_node || !ops_node->ops.len) ++ goto decision; ++ ++ od_local.allowed = &allowed; ++ od_local.auditallow = &auditallow; ++ od_local.dontaudit = &dontaudit; ++ ++ /* lookup operation decision */ ++ od = avc_operation_lookup(type, ops_node); ++ if (unlikely(!od)) { ++ /* Compute operation decision if type is flagged */ ++ if (!security_operation_test(ops_node->ops.type, type)) { ++ avd.allowed &= ~requested; ++ goto decision; ++ } ++ rcu_read_unlock(); ++ security_compute_operation(ssid, tsid, tclass, type, &od_local); ++ rcu_read_lock(); ++ avc_update_node(AVC_CALLBACK_ADD_OPERATION, requested, cmd, ++ ssid, tsid, tclass, avd.seqno, &od_local, 0); ++ } else { ++ avc_quick_copy_operation_decision(cmd, &od_local, od); ++ } ++ od = &od_local; ++ ++ if (!avc_operation_has_perm(od, cmd, OPERATION_ALLOWED)) ++ avd.allowed &= ~requested; ++ ++decision: ++ denied = requested & ~(avd.allowed); ++ if (unlikely(denied)) ++ rc = avc_denied(ssid, tsid, tclass, requested, cmd, ++ AVC_OPERATION_CMD, &avd); ++ ++ rcu_read_unlock(); ++ ++ rc2 = avc_operation_audit(ssid, tsid, tclass, requested, ++ &avd, od, cmd, rc, ad); ++ if (rc2) ++ return rc2; ++ return rc; ++} + + /** + * avc_has_perm_noaudit - Check permissions but perform no auditing. +@@ -721,6 +1116,7 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, + struct av_decision *avd) + { + struct avc_node *node; ++ struct avc_operation_node ops_node; + int rc = 0; + u32 denied; + +@@ -729,16 +1125,14 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, + rcu_read_lock(); + + node = avc_lookup(ssid, tsid, tclass); +- if (unlikely(!node)) { +- node = avc_compute_av(ssid, tsid, tclass, avd); +- } else { ++ if (unlikely(!node)) ++ node = avc_compute_av(ssid, tsid, tclass, avd, &ops_node); ++ else + memcpy(avd, &node->ae.avd, sizeof(*avd)); +- avd = &node->ae.avd; +- } + + denied = requested & ~(avd->allowed); + if (unlikely(denied)) +- rc = avc_denied(ssid, tsid, tclass, requested, flags, avd); ++ rc = avc_denied(ssid, tsid, tclass, requested, 0, flags, avd); + + rcu_read_unlock(); + return rc; +diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c +index b1e455b..cdce494 100644 +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -403,24 +403,15 @@ static int selinux_is_sblabel_mnt(struct super_block *sb) + { + struct superblock_security_struct *sbsec = sb->s_security; + +- if (sbsec->behavior == SECURITY_FS_USE_XATTR || +- sbsec->behavior == SECURITY_FS_USE_TRANS || +- sbsec->behavior == SECURITY_FS_USE_TASK || +- sbsec->behavior == SECURITY_FS_USE_NATIVE) +- return 1; +- +- /* Special handling for sysfs. Is genfs but also has setxattr handler*/ +- if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0) +- return 1; +- +- /* +- * Special handling for rootfs. Is genfs but supports +- * setting SELinux context on in-core inodes. +- */ +- if (strncmp(sb->s_type->name, "rootfs", sizeof("rootfs")) == 0) +- return 1; +- +- return 0; ++ return sbsec->behavior == SECURITY_FS_USE_XATTR || ++ sbsec->behavior == SECURITY_FS_USE_TRANS || ++ sbsec->behavior == SECURITY_FS_USE_TASK || ++ sbsec->behavior == SECURITY_FS_USE_NATIVE || ++ /* Special handling. Genfs but also in-core setxattr handler */ ++ !strcmp(sb->s_type->name, "sysfs") || ++ !strcmp(sb->s_type->name, "pstore") || ++ !strcmp(sb->s_type->name, "debugfs") || ++ !strcmp(sb->s_type->name, "rootfs"); + } + + static int sb_finish_set_opts(struct super_block *sb) +@@ -741,7 +732,12 @@ static int selinux_set_mnt_opts(struct super_block *sb, + } + + if (strcmp(sb->s_type->name, "proc") == 0) +- sbsec->flags |= SE_SBPROC; ++ sbsec->flags |= SE_SBPROC | SE_SBGENFS; ++ ++ if (!strcmp(sb->s_type->name, "debugfs") || ++ !strcmp(sb->s_type->name, "sysfs") || ++ !strcmp(sb->s_type->name, "pstore")) ++ sbsec->flags |= SE_SBGENFS; + + if (!sbsec->behavior) { + /* +@@ -1237,12 +1233,13 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc + return SECCLASS_SOCKET; + } + +-#ifdef CONFIG_PROC_FS +-static int selinux_proc_get_sid(struct dentry *dentry, +- u16 tclass, +- u32 *sid) ++static int selinux_genfs_get_sid(struct dentry *dentry, ++ u16 tclass, ++ u16 flags, ++ u32 *sid) + { + int rc; ++ struct super_block *sb = dentry->d_inode->i_sb; + char *buffer, *path; + + buffer = (char *)__get_free_page(GFP_KERNEL); +@@ -1253,26 +1250,20 @@ static int selinux_proc_get_sid(struct dentry *dentry, + if (IS_ERR(path)) + rc = PTR_ERR(path); + else { +- /* each process gets a /proc/PID/ entry. Strip off the +- * PID part to get a valid selinux labeling. +- * e.g. /proc/1/net/rpc/nfs -> /net/rpc/nfs */ +- while (path[1] >= '0' && path[1] <= '9') { +- path[1] = '/'; +- path++; ++ if (flags & SE_SBPROC) { ++ /* each process gets a /proc/PID/ entry. Strip off the ++ * PID part to get a valid selinux labeling. ++ * e.g. /proc/1/net/rpc/nfs -> /net/rpc/nfs */ ++ while (path[1] >= '0' && path[1] <= '9') { ++ path[1] = '/'; ++ path++; ++ } + } +- rc = security_genfs_sid("proc", path, tclass, sid); ++ rc = security_genfs_sid(sb->s_type->name, path, tclass, sid); + } + free_page((unsigned long)buffer); + return rc; + } +-#else +-static int selinux_proc_get_sid(struct dentry *dentry, +- u16 tclass, +- u32 *sid) +-{ +- return -EINVAL; +-} +-#endif + + /* The inode's security attributes must be initialized before first use. */ + static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry) +@@ -1429,7 +1420,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent + /* Default to the fs superblock SID. */ + isec->sid = sbsec->sid; + +- if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) { ++ if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) { + /* We must have a dentry to determine the label on + * procfs inodes */ + if (opt_dentry) +@@ -1452,7 +1443,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent + if (!dentry) + goto out_unlock; + isec->sclass = inode_mode_to_security_class(inode->i_mode); +- rc = selinux_proc_get_sid(dentry, isec->sclass, &sid); ++ rc = selinux_genfs_get_sid(dentry, isec->sclass, ++ sbsec->flags, &sid); + dput(dentry); + if (rc) + goto out_unlock; +@@ -1936,6 +1928,65 @@ static inline u32 open_file_to_av(struct file *file) + + /* Hook functions begin here. */ + ++static int selinux_binder_set_context_mgr(struct task_struct *mgr) ++{ ++ u32 mysid = current_sid(); ++ u32 mgrsid = task_sid(mgr); ++ ++ return avc_has_perm(mysid, mgrsid, SECCLASS_BINDER, BINDER__SET_CONTEXT_MGR, NULL); ++} ++ ++static int selinux_binder_transaction(struct task_struct *from, struct task_struct *to) ++{ ++ u32 mysid = current_sid(); ++ u32 fromsid = task_sid(from); ++ u32 tosid = task_sid(to); ++ int rc; ++ ++ if (mysid != fromsid) { ++ rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER, BINDER__IMPERSONATE, NULL); ++ if (rc) ++ return rc; ++ } ++ ++ return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__CALL, NULL); ++} ++ ++static int selinux_binder_transfer_binder(struct task_struct *from, struct task_struct *to) ++{ ++ u32 fromsid = task_sid(from); ++ u32 tosid = task_sid(to); ++ return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER, NULL); ++} ++ ++static int selinux_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) ++{ ++ u32 sid = task_sid(to); ++ struct file_security_struct *fsec = file->f_security; ++ struct inode *inode = file->f_path.dentry->d_inode; ++ struct inode_security_struct *isec = inode->i_security; ++ struct common_audit_data ad; ++ int rc; ++ ++ ad.type = LSM_AUDIT_DATA_PATH; ++ ad.u.path = file->f_path; ++ ++ if (sid != fsec->sid) { ++ rc = avc_has_perm(sid, fsec->sid, ++ SECCLASS_FD, ++ FD__USE, ++ &ad); ++ if (rc) ++ return rc; ++ } ++ ++ if (unlikely(IS_PRIVATE(inode))) ++ return 0; ++ ++ return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file), ++ &ad); ++} ++ + static int selinux_ptrace_access_check(struct task_struct *child, + unsigned int mode) + { +@@ -3187,6 +3238,44 @@ static void selinux_file_free_security(struct file *file) + file_free_security(file); + } + ++/* ++ * Check whether a task has the ioctl permission and cmd ++ * operation to an inode. ++ */ ++int ioctl_has_perm(const struct cred *cred, struct file *file, ++ u32 requested, u16 cmd) ++{ ++ struct common_audit_data ad; ++ struct file_security_struct *fsec = file->f_security; ++ struct inode *inode = file_inode(file); ++ struct inode_security_struct *isec = inode->i_security; ++ struct lsm_ioctlop_audit ioctl; ++ u32 ssid = cred_sid(cred); ++ int rc; ++ ++ ad.type = LSM_AUDIT_DATA_IOCTL_OP; ++ ad.u.op = &ioctl; ++ ad.u.op->cmd = cmd; ++ ad.u.op->path = file->f_path; ++ ++ if (ssid != fsec->sid) { ++ rc = avc_has_perm(ssid, fsec->sid, ++ SECCLASS_FD, ++ FD__USE, ++ &ad); ++ if (rc) ++ goto out; ++ } ++ ++ if (unlikely(IS_PRIVATE(inode))) ++ return 0; ++ ++ rc = avc_has_operation(ssid, isec->sid, isec->sclass, ++ requested, cmd, &ad); ++out: ++ return rc; ++} ++ + static int selinux_file_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) + { +@@ -3229,7 +3318,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd, + * to the file's ioctl() function. + */ + default: +- error = file_has_perm(cred, file, FILE__IOCTL); ++ error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd); + } + return error; + } +@@ -5813,6 +5902,11 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) + static struct security_operations selinux_ops = { + .name = "selinux", + ++ .binder_set_context_mgr = selinux_binder_set_context_mgr, ++ .binder_transaction = selinux_binder_transaction, ++ .binder_transfer_binder = selinux_binder_transfer_binder, ++ .binder_transfer_file = selinux_binder_transfer_file, ++ + .ptrace_access_check = selinux_ptrace_access_check, + .ptrace_traceme = selinux_ptrace_traceme, + .capget = selinux_capget, +diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h +index ddf8eec..3165d4e 100644 +--- a/security/selinux/include/avc.h ++++ b/security/selinux/include/avc.h +@@ -142,6 +142,7 @@ static inline int avc_audit(u32 ssid, u32 tsid, + } + + #define AVC_STRICT 1 /* Ignore permissive mode. */ ++#define AVC_OPERATION_CMD 2 /* ignore command when updating operations */ + int avc_has_perm_noaudit(u32 ssid, u32 tsid, + u16 tclass, u32 requested, + unsigned flags, +@@ -151,6 +152,9 @@ int avc_has_perm(u32 ssid, u32 tsid, + u16 tclass, u32 requested, + struct common_audit_data *auditdata); + ++int avc_has_operation(u32 ssid, u32 tsid, u16 tclass, u32 requested, ++ u16 cmd, struct common_audit_data *ad); ++ + u32 avc_policy_seqno(void); + + #define AVC_CALLBACK_GRANT 1 +@@ -161,6 +165,7 @@ u32 avc_policy_seqno(void); + #define AVC_CALLBACK_AUDITALLOW_DISABLE 32 + #define AVC_CALLBACK_AUDITDENY_ENABLE 64 + #define AVC_CALLBACK_AUDITDENY_DISABLE 128 ++#define AVC_CALLBACK_ADD_OPERATION 256 + + int avc_add_callback(int (*callback)(u32 event), u32 events); + +diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h +index be491a7..bdc9325 100644 +--- a/security/selinux/include/classmap.h ++++ b/security/selinux/include/classmap.h +@@ -151,5 +151,6 @@ struct security_class_mapping secclass_map[] = { + { "kernel_service", { "use_as_override", "create_files_as", NULL } }, + { "tun_socket", + { COMMON_SOCK_PERMS, "attach_queue", NULL } }, ++ { "binder", { "impersonate", "call", "set_context_mgr", "transfer", NULL } }, + { NULL } + }; +diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h +index d1e0b23..8d50683 100644 +--- a/security/selinux/include/security.h ++++ b/security/selinux/include/security.h +@@ -35,13 +35,14 @@ + #define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27 + #define POLICYDB_VERSION_DEFAULT_TYPE 28 + #define POLICYDB_VERSION_CONSTRAINT_NAMES 29 ++#define POLICYDB_VERSION_IOCTL_OPERATIONS 30 + + /* Range of policy versions we understand*/ + #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE + #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX + #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE + #else +-#define POLICYDB_VERSION_MAX POLICYDB_VERSION_CONSTRAINT_NAMES ++#define POLICYDB_VERSION_MAX POLICYDB_VERSION_IOCTL_OPERATIONS + #endif + + /* Mask for just the mount related flags */ +@@ -56,6 +57,7 @@ + /* Non-mount related flags */ + #define SE_SBINITIALIZED 0x0100 + #define SE_SBPROC 0x0200 ++#define SE_SBGENFS 0x0400 + + #define CONTEXT_STR "context=" + #define FSCONTEXT_STR "fscontext=" +@@ -108,11 +110,40 @@ struct av_decision { + u32 flags; + }; + ++#define security_operation_set(perms, x) (perms[x >> 5] |= 1 << (x & 0x1f)) ++#define security_operation_test(perms, x) (1 & (perms[x >> 5] >> (x & 0x1f))) ++ ++struct operation_perm { ++ u32 perms[8]; ++}; ++ ++struct operation_decision { ++ u8 type; ++ u8 specified; ++ struct operation_perm *allowed; ++ struct operation_perm *auditallow; ++ struct operation_perm *dontaudit; ++}; ++ ++#define OPERATION_ALLOWED 1 ++#define OPERATION_AUDITALLOW 2 ++#define OPERATION_DONTAUDIT 4 ++#define OPERATION_ALL (OPERATION_ALLOWED | OPERATION_AUDITALLOW |\ ++ OPERATION_DONTAUDIT) ++struct operation { ++ u16 len; /* length of operation decision chain */ ++ u32 type[8]; /* 256 types */ ++}; ++ + /* definitions of av_decision.flags */ + #define AVD_FLAGS_PERMISSIVE 0x0001 + + void security_compute_av(u32 ssid, u32 tsid, +- u16 tclass, struct av_decision *avd); ++ u16 tclass, struct av_decision *avd, ++ struct operation *ops); ++ ++void security_compute_operation(u32 ssid, u32 tsid, u16 tclass, ++ u8 type, struct operation_decision *od); + + void security_compute_av_user(u32 ssid, u32 tsid, + u16 tclass, struct av_decision *avd); +diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c +index a3dd9fa..dd7466c 100644 +--- a/security/selinux/ss/avtab.c ++++ b/security/selinux/ss/avtab.c +@@ -24,6 +24,7 @@ + #include "policydb.h" + + static struct kmem_cache *avtab_node_cachep; ++static struct kmem_cache *avtab_operation_cachep; + + static inline int avtab_hash(struct avtab_key *keyp, u16 mask) + { +@@ -37,11 +38,24 @@ avtab_insert_node(struct avtab *h, int hvalue, + struct avtab_key *key, struct avtab_datum *datum) + { + struct avtab_node *newnode; ++ struct avtab_operation *ops; + newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL); + if (newnode == NULL) + return NULL; + newnode->key = *key; +- newnode->datum = *datum; ++ ++ if (key->specified & AVTAB_OP) { ++ ops = kmem_cache_zalloc(avtab_operation_cachep, GFP_KERNEL); ++ if (ops == NULL) { ++ kmem_cache_free(avtab_node_cachep, newnode); ++ return NULL; ++ } ++ *ops = *(datum->u.ops); ++ newnode->datum.u.ops = ops; ++ } else { ++ newnode->datum.u.data = datum->u.data; ++ } ++ + if (prev) { + newnode->next = prev->next; + prev->next = newnode; +@@ -70,8 +84,11 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat + if (key->source_type == cur->key.source_type && + key->target_type == cur->key.target_type && + key->target_class == cur->key.target_class && +- (specified & cur->key.specified)) ++ (specified & cur->key.specified)) { ++ if (specified & AVTAB_OPNUM) ++ break; + return -EEXIST; ++ } + if (key->source_type < cur->key.source_type) + break; + if (key->source_type == cur->key.source_type && +@@ -232,6 +249,9 @@ void avtab_destroy(struct avtab *h) + while (cur) { + temp = cur; + cur = cur->next; ++ if (temp->key.specified & AVTAB_OP) ++ kmem_cache_free(avtab_operation_cachep, ++ temp->datum.u.ops); + kmem_cache_free(avtab_node_cachep, temp); + } + h->htable[i] = NULL; +@@ -320,7 +340,13 @@ static uint16_t spec_order[] = { + AVTAB_AUDITALLOW, + AVTAB_TRANSITION, + AVTAB_CHANGE, +- AVTAB_MEMBER ++ AVTAB_MEMBER, ++ AVTAB_OPNUM_ALLOWED, ++ AVTAB_OPNUM_AUDITALLOW, ++ AVTAB_OPNUM_DONTAUDIT, ++ AVTAB_OPTYPE_ALLOWED, ++ AVTAB_OPTYPE_AUDITALLOW, ++ AVTAB_OPTYPE_DONTAUDIT + }; + + int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, +@@ -330,10 +356,11 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, + { + __le16 buf16[4]; + u16 enabled; +- __le32 buf32[7]; + u32 items, items2, val, vers = pol->policyvers; + struct avtab_key key; + struct avtab_datum datum; ++ struct avtab_operation ops; ++ __le32 buf32[ARRAY_SIZE(ops.op.perms)]; + int i, rc; + unsigned set; + +@@ -390,11 +417,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, + printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); + return -EINVAL; + } ++ if (val & AVTAB_OP) { ++ printk(KERN_ERR "SELinux: avtab: entry has operations\n"); ++ return -EINVAL; ++ } + + for (i = 0; i < ARRAY_SIZE(spec_order); i++) { + if (val & spec_order[i]) { + key.specified = spec_order[i] | enabled; +- datum.data = le32_to_cpu(buf32[items++]); ++ datum.u.data = le32_to_cpu(buf32[items++]); + rc = insertf(a, &key, &datum, p); + if (rc) + return rc; +@@ -413,7 +444,6 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, + printk(KERN_ERR "SELinux: avtab: truncated entry\n"); + return rc; + } +- + items = 0; + key.source_type = le16_to_cpu(buf16[items++]); + key.target_type = le16_to_cpu(buf16[items++]); +@@ -437,14 +467,32 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, + return -EINVAL; + } + +- rc = next_entry(buf32, fp, sizeof(u32)); +- if (rc) { +- printk(KERN_ERR "SELinux: avtab: truncated entry\n"); +- return rc; ++ if ((vers < POLICYDB_VERSION_IOCTL_OPERATIONS) ++ || !(key.specified & AVTAB_OP)) { ++ rc = next_entry(buf32, fp, sizeof(u32)); ++ if (rc) { ++ printk(KERN_ERR "SELinux: avtab: truncated entry\n"); ++ return rc; ++ } ++ datum.u.data = le32_to_cpu(*buf32); ++ } else { ++ memset(&ops, 0, sizeof(struct avtab_operation)); ++ rc = next_entry(&ops.type, fp, sizeof(u8)); ++ if (rc) { ++ printk(KERN_ERR "SELinux: avtab: truncated entry\n"); ++ return rc; ++ } ++ rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(ops.op.perms)); ++ if (rc) { ++ printk(KERN_ERR "SELinux: avtab: truncated entry\n"); ++ return rc; ++ } ++ for (i = 0; i < ARRAY_SIZE(ops.op.perms); i++) ++ ops.op.perms[i] = le32_to_cpu(buf32[i]); ++ datum.u.ops = &ops; + } +- datum.data = le32_to_cpu(*buf32); + if ((key.specified & AVTAB_TYPE) && +- !policydb_type_isvalid(pol, datum.data)) { ++ !policydb_type_isvalid(pol, datum.u.data)) { + printk(KERN_ERR "SELinux: avtab: invalid type\n"); + return -EINVAL; + } +@@ -504,8 +552,9 @@ bad: + int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) + { + __le16 buf16[4]; +- __le32 buf32[1]; ++ __le32 buf32[ARRAY_SIZE(cur->datum.u.ops->op.perms)]; + int rc; ++ unsigned int i; + + buf16[0] = cpu_to_le16(cur->key.source_type); + buf16[1] = cpu_to_le16(cur->key.target_type); +@@ -514,8 +563,19 @@ int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) + rc = put_entry(buf16, sizeof(u16), 4, fp); + if (rc) + return rc; +- buf32[0] = cpu_to_le32(cur->datum.data); +- rc = put_entry(buf32, sizeof(u32), 1, fp); ++ ++ if (cur->key.specified & AVTAB_OP) { ++ rc = put_entry(&cur->datum.u.ops->type, sizeof(u8), 1, fp); ++ if (rc) ++ return rc; ++ for (i = 0; i < ARRAY_SIZE(cur->datum.u.ops->op.perms); i++) ++ buf32[i] = cpu_to_le32(cur->datum.u.ops->op.perms[i]); ++ rc = put_entry(buf32, sizeof(u32), ++ ARRAY_SIZE(cur->datum.u.ops->op.perms), fp); ++ } else { ++ buf32[0] = cpu_to_le32(cur->datum.u.data); ++ rc = put_entry(buf32, sizeof(u32), 1, fp); ++ } + if (rc) + return rc; + return 0; +@@ -548,9 +608,13 @@ void avtab_cache_init(void) + avtab_node_cachep = kmem_cache_create("avtab_node", + sizeof(struct avtab_node), + 0, SLAB_PANIC, NULL); ++ avtab_operation_cachep = kmem_cache_create("avtab_operation", ++ sizeof(struct avtab_operation), ++ 0, SLAB_PANIC, NULL); + } + + void avtab_cache_destroy(void) + { + kmem_cache_destroy(avtab_node_cachep); ++ kmem_cache_destroy(avtab_operation_cachep); + } +diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h +index 63ce2f9..97acd6f 100644 +--- a/security/selinux/ss/avtab.h ++++ b/security/selinux/ss/avtab.h +@@ -23,6 +23,8 @@ + #ifndef _SS_AVTAB_H_ + #define _SS_AVTAB_H_ + ++#include "security.h" ++ + struct avtab_key { + u16 source_type; /* source type */ + u16 target_type; /* target type */ +@@ -35,13 +37,34 @@ struct avtab_key { + #define AVTAB_MEMBER 0x0020 + #define AVTAB_CHANGE 0x0040 + #define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE) ++#define AVTAB_OPNUM_ALLOWED 0x0100 ++#define AVTAB_OPNUM_AUDITALLOW 0x0200 ++#define AVTAB_OPNUM_DONTAUDIT 0x0400 ++#define AVTAB_OPNUM (AVTAB_OPNUM_ALLOWED | \ ++ AVTAB_OPNUM_AUDITALLOW | \ ++ AVTAB_OPNUM_DONTAUDIT) ++#define AVTAB_OPTYPE_ALLOWED 0x1000 ++#define AVTAB_OPTYPE_AUDITALLOW 0x2000 ++#define AVTAB_OPTYPE_DONTAUDIT 0x4000 ++#define AVTAB_OPTYPE (AVTAB_OPTYPE_ALLOWED | \ ++ AVTAB_OPTYPE_AUDITALLOW | \ ++ AVTAB_OPTYPE_DONTAUDIT) ++#define AVTAB_OP (AVTAB_OPNUM | AVTAB_OPTYPE) + #define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */ + #define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */ + u16 specified; /* what field is specified */ + }; + ++struct avtab_operation { ++ u8 type; ++ struct operation_perm op; ++}; ++ + struct avtab_datum { +- u32 data; /* access vector or type value */ ++ union { ++ u32 data; /* access vector or type value */ ++ struct avtab_operation *ops; /* ioctl operations */ ++ } u; + }; + + struct avtab_node { +diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c +index 62c6773..c4cd20a 100644 +--- a/security/selinux/ss/conditional.c ++++ b/security/selinux/ss/conditional.c +@@ -15,6 +15,7 @@ + + #include "security.h" + #include "conditional.h" ++#include "services.h" + + /* + * cond_evaluate_expr evaluates a conditional expr +@@ -612,21 +613,39 @@ int cond_write_list(struct policydb *p, struct cond_node *list, void *fp) + + return 0; + } ++ ++void cond_compute_operation(struct avtab *ctab, struct avtab_key *key, ++ struct operation_decision *od) ++{ ++ struct avtab_node *node; ++ ++ if (!ctab || !key || !od) ++ return; ++ ++ for (node = avtab_search_node(ctab, key); node; ++ node = avtab_search_node_next(node, key->specified)) { ++ if (node->key.specified & AVTAB_ENABLED) ++ services_compute_operation_num(od, node); ++ } ++ return; ++ ++} + /* Determine whether additional permissions are granted by the conditional + * av table, and if so, add them to the result + */ +-void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd) ++void cond_compute_av(struct avtab *ctab, struct avtab_key *key, ++ struct av_decision *avd, struct operation *ops) + { + struct avtab_node *node; + +- if (!ctab || !key || !avd) ++ if (!ctab || !key || !avd || !ops) + return; + + for (node = avtab_search_node(ctab, key); node; + node = avtab_search_node_next(node, key->specified)) { + if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) == + (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED))) +- avd->allowed |= node->datum.data; ++ avd->allowed |= node->datum.u.data; + if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) == + (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) + /* Since a '0' in an auditdeny mask represents a +@@ -634,10 +653,13 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decisi + * the '&' operand to ensure that all '0's in the mask + * are retained (much unlike the allow and auditallow cases). + */ +- avd->auditdeny &= node->datum.data; ++ avd->auditdeny &= node->datum.u.data; + if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) == + (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) +- avd->auditallow |= node->datum.data; ++ avd->auditallow |= node->datum.u.data; ++ if ((node->key.specified & AVTAB_ENABLED) && ++ (node->key.specified & AVTAB_OP)) ++ services_compute_operation_type(ops, node); + } + return; + } +diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h +index 4d1f874..80ee2bb 100644 +--- a/security/selinux/ss/conditional.h ++++ b/security/selinux/ss/conditional.h +@@ -73,8 +73,10 @@ int cond_read_list(struct policydb *p, void *fp); + int cond_write_bool(void *key, void *datum, void *ptr); + int cond_write_list(struct policydb *p, struct cond_node *list, void *fp); + +-void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd); +- ++void cond_compute_av(struct avtab *ctab, struct avtab_key *key, ++ struct av_decision *avd, struct operation *ops); ++void cond_compute_operation(struct avtab *ctab, struct avtab_key *key, ++ struct operation_decision *od); + int evaluate_cond_node(struct policydb *p, struct cond_node *node); + + #endif /* _CONDITIONAL_H_ */ +diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c +index bc2a586..1c7daf2 100644 +--- a/security/selinux/ss/policydb.c ++++ b/security/selinux/ss/policydb.c +@@ -148,6 +148,11 @@ static struct policydb_compat_info policydb_compat[] = { + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, ++ { ++ .version = POLICYDB_VERSION_IOCTL_OPERATIONS, ++ .sym_num = SYM_NUM, ++ .ocon_num = OCON_NUM, ++ }, + }; + + static struct policydb_compat_info *policydb_lookup_compat(int version) +diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c +index a1d3944..4eba19f 100644 +--- a/security/selinux/ss/services.c ++++ b/security/selinux/ss/services.c +@@ -93,9 +93,10 @@ static int context_struct_to_string(struct context *context, char **scontext, + u32 *scontext_len); + + static void context_struct_compute_av(struct context *scontext, +- struct context *tcontext, +- u16 tclass, +- struct av_decision *avd); ++ struct context *tcontext, ++ u16 tclass, ++ struct av_decision *avd, ++ struct operation *ops); + + struct selinux_mapping { + u16 value; /* policy value */ +@@ -565,7 +566,8 @@ static void type_attribute_bounds_av(struct context *scontext, + context_struct_compute_av(&lo_scontext, + tcontext, + tclass, +- &lo_avd); ++ &lo_avd, ++ NULL); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; +@@ -580,7 +582,8 @@ static void type_attribute_bounds_av(struct context *scontext, + context_struct_compute_av(scontext, + &lo_tcontext, + tclass, +- &lo_avd); ++ &lo_avd, ++ NULL); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; +@@ -596,7 +599,8 @@ static void type_attribute_bounds_av(struct context *scontext, + context_struct_compute_av(&lo_scontext, + &lo_tcontext, + tclass, +- &lo_avd); ++ &lo_avd, ++ NULL); + if ((lo_avd.allowed & avd->allowed) == avd->allowed) + return; /* no masked permission */ + masked = ~lo_avd.allowed & avd->allowed; +@@ -612,14 +616,39 @@ static void type_attribute_bounds_av(struct context *scontext, + } + } + ++/* flag ioctl types that have operation permissions */ ++void services_compute_operation_type( ++ struct operation *ops, ++ struct avtab_node *node) ++{ ++ u8 type; ++ unsigned int i; ++ ++ if (node->key.specified & AVTAB_OPTYPE) { ++ /* if allowing one or more complete types */ ++ for (i = 0; i < ARRAY_SIZE(ops->type); i++) ++ ops->type[i] |= node->datum.u.ops->op.perms[i]; ++ } else { ++ /* if allowing operations within a type */ ++ type = node->datum.u.ops->type; ++ security_operation_set(ops->type, type); ++ } ++ ++ /* If no ioctl commands are allowed, ignore auditallow and auditdeny */ ++ if (node->key.specified & AVTAB_OPTYPE_ALLOWED || ++ node->key.specified & AVTAB_OPNUM_ALLOWED) ++ ops->len = 1; ++} ++ + /* +- * Compute access vectors based on a context structure pair for +- * the permissions in a particular class. ++ * Compute access vectors and operations ranges based on a context ++ * structure pair for the permissions in a particular class. + */ + static void context_struct_compute_av(struct context *scontext, +- struct context *tcontext, +- u16 tclass, +- struct av_decision *avd) ++ struct context *tcontext, ++ u16 tclass, ++ struct av_decision *avd, ++ struct operation *ops) + { + struct constraint_node *constraint; + struct role_allow *ra; +@@ -633,6 +662,10 @@ static void context_struct_compute_av(struct context *scontext, + avd->allowed = 0; + avd->auditallow = 0; + avd->auditdeny = 0xffffffff; ++ if (ops) { ++ memset(&ops->type, 0, sizeof(ops->type)); ++ ops->len = 0; ++ } + + if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { + if (printk_ratelimit()) +@@ -647,7 +680,7 @@ static void context_struct_compute_av(struct context *scontext, + * this permission check, then use it. + */ + avkey.target_class = tclass; +- avkey.specified = AVTAB_AV; ++ avkey.specified = AVTAB_AV | AVTAB_OP; + sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1); + BUG_ON(!sattr); + tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1); +@@ -660,15 +693,17 @@ static void context_struct_compute_av(struct context *scontext, + node; + node = avtab_search_node_next(node, avkey.specified)) { + if (node->key.specified == AVTAB_ALLOWED) +- avd->allowed |= node->datum.data; ++ avd->allowed |= node->datum.u.data; + else if (node->key.specified == AVTAB_AUDITALLOW) +- avd->auditallow |= node->datum.data; ++ avd->auditallow |= node->datum.u.data; + else if (node->key.specified == AVTAB_AUDITDENY) +- avd->auditdeny &= node->datum.data; ++ avd->auditdeny &= node->datum.u.data; ++ else if (ops && (node->key.specified & AVTAB_OP)) ++ services_compute_operation_type(ops, node); + } + + /* Check conditional av table for additional permissions */ +- cond_compute_av(&policydb.te_cond_avtab, &avkey, avd); ++ cond_compute_av(&policydb.te_cond_avtab, &avkey, avd, ops); + + } + } +@@ -899,13 +934,138 @@ static void avd_init(struct av_decision *avd) + avd->flags = 0; + } + ++void services_compute_operation_num(struct operation_decision *od, ++ struct avtab_node *node) ++{ ++ unsigned int i; + ++ if (node->key.specified & AVTAB_OPNUM) { ++ if (od->type != node->datum.u.ops->type) ++ return; ++ } else { ++ if (!security_operation_test(node->datum.u.ops->op.perms, ++ od->type)) ++ return; ++ } ++ ++ if (node->key.specified == AVTAB_OPTYPE_ALLOWED) { ++ od->specified |= OPERATION_ALLOWED; ++ memset(od->allowed->perms, 0xff, ++ sizeof(od->allowed->perms)); ++ } else if (node->key.specified == AVTAB_OPTYPE_AUDITALLOW) { ++ od->specified |= OPERATION_AUDITALLOW; ++ memset(od->auditallow->perms, 0xff, ++ sizeof(od->auditallow->perms)); ++ } else if (node->key.specified == AVTAB_OPTYPE_DONTAUDIT) { ++ od->specified |= OPERATION_DONTAUDIT; ++ memset(od->dontaudit->perms, 0xff, ++ sizeof(od->dontaudit->perms)); ++ } else if (node->key.specified == AVTAB_OPNUM_ALLOWED) { ++ od->specified |= OPERATION_ALLOWED; ++ for (i = 0; i < ARRAY_SIZE(od->allowed->perms); i++) ++ od->allowed->perms[i] |= ++ node->datum.u.ops->op.perms[i]; ++ } else if (node->key.specified == AVTAB_OPNUM_AUDITALLOW) { ++ od->specified |= OPERATION_AUDITALLOW; ++ for (i = 0; i < ARRAY_SIZE(od->auditallow->perms); i++) ++ od->auditallow->perms[i] |= ++ node->datum.u.ops->op.perms[i]; ++ } else if (node->key.specified == AVTAB_OPNUM_DONTAUDIT) { ++ od->specified |= OPERATION_DONTAUDIT; ++ for (i = 0; i < ARRAY_SIZE(od->dontaudit->perms); i++) ++ od->dontaudit->perms[i] |= ++ node->datum.u.ops->op.perms[i]; ++ } else { ++ BUG(); ++ } ++} ++ ++void security_compute_operation(u32 ssid, ++ u32 tsid, ++ u16 orig_tclass, ++ u8 type, ++ struct operation_decision *od) ++{ ++ u16 tclass; ++ struct context *scontext, *tcontext; ++ struct avtab_key avkey; ++ struct avtab_node *node; ++ struct ebitmap *sattr, *tattr; ++ struct ebitmap_node *snode, *tnode; ++ unsigned int i, j; ++ ++ od->type = type; ++ od->specified = 0; ++ memset(od->allowed->perms, 0, sizeof(od->allowed->perms)); ++ memset(od->auditallow->perms, 0, sizeof(od->auditallow->perms)); ++ memset(od->dontaudit->perms, 0, sizeof(od->dontaudit->perms)); ++ ++ read_lock(&policy_rwlock); ++ if (!ss_initialized) ++ goto allow; ++ ++ scontext = sidtab_search(&sidtab, ssid); ++ if (!scontext) { ++ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", ++ __func__, ssid); ++ goto out; ++ } ++ ++ tcontext = sidtab_search(&sidtab, tsid); ++ if (!tcontext) { ++ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", ++ __func__, tsid); ++ goto out; ++ } ++ ++ tclass = unmap_class(orig_tclass); ++ if (unlikely(orig_tclass && !tclass)) { ++ if (policydb.allow_unknown) ++ goto allow; ++ goto out; ++ } ++ ++ ++ if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { ++ pr_warn_ratelimited("SELinux: Invalid class %hu\n", tclass); ++ goto out; ++ } ++ ++ avkey.target_class = tclass; ++ avkey.specified = AVTAB_OP; ++ sattr = flex_array_get(policydb.type_attr_map_array, ++ scontext->type - 1); ++ BUG_ON(!sattr); ++ tattr = flex_array_get(policydb.type_attr_map_array, ++ tcontext->type - 1); ++ BUG_ON(!tattr); ++ ebitmap_for_each_positive_bit(sattr, snode, i) { ++ ebitmap_for_each_positive_bit(tattr, tnode, j) { ++ avkey.source_type = i + 1; ++ avkey.target_type = j + 1; ++ for (node = avtab_search_node(&policydb.te_avtab, &avkey); ++ node; ++ node = avtab_search_node_next(node, avkey.specified)) ++ services_compute_operation_num(od, node); ++ ++ cond_compute_operation(&policydb.te_cond_avtab, ++ &avkey, od); ++ } ++ } ++out: ++ read_unlock(&policy_rwlock); ++ return; ++allow: ++ memset(od->allowed->perms, 0xff, sizeof(od->allowed->perms)); ++ goto out; ++} + /** + * security_compute_av - Compute access vector decisions. + * @ssid: source security identifier + * @tsid: target security identifier + * @tclass: target security class + * @avd: access vector decisions ++ * @od: operation decisions + * + * Compute a set of access vector decisions based on the + * SID pair (@ssid, @tsid) for the permissions in @tclass. +@@ -913,13 +1073,15 @@ static void avd_init(struct av_decision *avd) + void security_compute_av(u32 ssid, + u32 tsid, + u16 orig_tclass, +- struct av_decision *avd) ++ struct av_decision *avd, ++ struct operation *ops) + { + u16 tclass; + struct context *scontext = NULL, *tcontext = NULL; + + read_lock(&policy_rwlock); + avd_init(avd); ++ ops->len = 0; + if (!ss_initialized) + goto allow; + +@@ -947,7 +1109,7 @@ void security_compute_av(u32 ssid, + goto allow; + goto out; + } +- context_struct_compute_av(scontext, tcontext, tclass, avd); ++ context_struct_compute_av(scontext, tcontext, tclass, avd, ops); + map_decision(orig_tclass, avd, policydb.allow_unknown); + out: + read_unlock(&policy_rwlock); +@@ -993,7 +1155,7 @@ void security_compute_av_user(u32 ssid, + goto out; + } + +- context_struct_compute_av(scontext, tcontext, tclass, avd); ++ context_struct_compute_av(scontext, tcontext, tclass, avd, NULL); + out: + read_unlock(&policy_rwlock); + return; +@@ -1515,7 +1677,7 @@ static int security_compute_sid(u32 ssid, + + if (avdatum) { + /* Use the type from the type transition/member/change rule. */ +- newcontext.type = avdatum->data; ++ newcontext.type = avdatum->u.data; + } + + /* if we have a objname this is a file trans check so check those rules */ +diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h +index e8d907e..5697574 100644 +--- a/security/selinux/ss/services.h ++++ b/security/selinux/ss/services.h +@@ -11,5 +11,11 @@ + + extern struct policydb policydb; + ++void services_compute_operation_type(struct operation *ops, ++ struct avtab_node *node); ++ ++void services_compute_operation_num(struct operation_decision *od, ++ struct avtab_node *node); ++ + #endif /* _SS_SERVICES_H_ */ + diff --git a/br-ext-chip-hisilicon/board/unknown_unknown_hi3536cv100_unknown/config b/br-ext-chip-hisilicon/board/unknown_unknown_hi3536cv100_unknown/config new file mode 100644 index 00000000..ff46642c --- /dev/null +++ b/br-ext-chip-hisilicon/board/unknown_unknown_hi3536cv100_unknown/config @@ -0,0 +1,10 @@ +VENDOR=unknown +MODEL=unknown +FAMILY=hi3536cv100 +CHIP=hi3536cv100 +RAM_SIZE=1024M +RAM_LINUX_SIZE=1024M +RAM_MPP_SIZE=0M +ROM_SIZE=? +CMOS=unknown +UBOOT_SIZE=256K diff --git a/br-ext-chip-hisilicon/board/unknown_unknown_hi3536cv100_unknown/kernel/.gitkeep b/br-ext-chip-hisilicon/board/unknown_unknown_hi3536cv100_unknown/kernel/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/br-ext-chip-hisilicon/configs/unknown_unknown_hi3536cv100_openipc_defconfig b/br-ext-chip-hisilicon/configs/unknown_unknown_hi3536cv100_openipc_defconfig new file mode 100644 index 00000000..7e77942c --- /dev/null +++ b/br-ext-chip-hisilicon/configs/unknown_unknown_hi3536cv100_openipc_defconfig @@ -0,0 +1,95 @@ +# Architecture +BR2_arm=y +BR2_cortex_a7=y +BR2_ARM_EABI=y +BR2_ARM_FPU_NEON_VFPV4=y +BR2_ARM_INSTRUCTIONS_THUMB2=y +BR2_KERNEL_HEADERS_VERSION=y +BR2_DEFAULT_KERNEL_VERSION="3.18.20" +BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_3_18=y + +# Toolchain +BR2_PER_PACKAGE_DIRECTORIES=y +BR2_GCC_VERSION_7_X=y +# BR2_TOOLCHAIN_USES_UCLIBC is not set +# BR2_TOOLCHAIN_BUILDROOT_UCLIBC is not set +# BR2_TOOLCHAIN_BUILDROOT_LIBC="uclibc" +BR2_TOOLCHAIN_USES_MUSL=y +BR2_TOOLCHAIN_BUILDROOT_MUSL=y +BR2_TOOLCHAIN_BUILDROOT_LIBC="musl" +# BR2_TOOLCHAIN_BUILDROOT_CXX is not set +BR2_TOOLCHAIN_BUILDROOT_LOCALE=y +BR2_TOOLCHAIN_BUILDROOT_USE_SSP=y + +# Kernel +BR2_LINUX_KERNEL=y +BR2_LINUX_KERNEL_CUSTOM_VERSION=y +BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="3.18.20" +BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y +BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL_HISILICON_PATH)/board/hi3536cv100/kernel/hi3536cv100.generic.config" +BR2_LINUX_KERNEL_UIMAGE=y +BR2_LINUX_KERNEL_UIMAGE_LOADADDR="0x80008000" +BR2_LINUX_KERNEL_XZ=y +BR2_LINUX_KERNEL_EXT_HISI_PATCHER=y +BR2_LINUX_KERNEL_EXT_HISI_PATCHER_LIST="$(BR2_EXTERNAL_HISILICON_PATH)/board/hi3536cv100/kernel/patches/ $(BR2_EXTERNAL_HISILICON_PATH)/board/hi3536cv100/kernel/overlay" + +# Filesystem +# BR2_TARGET_TZ_INFO is not set +BR2_TARGET_ROOTFS_CPIO=y +BR2_TARGET_ROOTFS_SQUASHFS=y +BR2_TARGET_ROOTFS_SQUASHFS4_XZ=y +BR2_ROOTFS_OVERLAY="$(TOPDIR)/../general/overlay" +BR2_ROOTFS_POST_BUILD_SCRIPT="$(TOPDIR)/../scripts/executing_commands_for_$(BR2_TOOLCHAIN_BUILDROOT_LIBC).sh" + +# OpenIPC configuration +BR2_TOOLCHAIN_BUILDROOT_VENDOR="openipc" +BR2_TARGET_GENERIC_ISSUE="Welcome to OpenIPC v2.1" +BR2_TARGET_GENERIC_HOSTNAME="openipc-hi3536cv100" +BR2_GLOBAL_PATCH_DIR="$(TOPDIR)/../general/package/all-patches" + +# OpenIPC packages +BR2_PACKAGE_BUSYBOX_CONFIG="$(TOPDIR)/../general/package/busybox/busybox.config" +BR2_PACKAGE_DROPBEAR=y +# BR2_PACKAGE_FDK_AAC_OPENIPC is not set +BR2_PACKAGE_FWPRINTENV_OPENIPC=y +BR2_PACKAGE_HASERL=y +BR2_PACKAGE_HISI_GPIO=y +BR2_PACKAGE_IPCTOOL=y +BR2_PACKAGE_JSON_C=y +BR2_PACKAGE_LAME_OPENIPC=y +BR2_PACKAGE_LIBCURL_OPENIPC=y +BR2_PACKAGE_LIBCURL_OPENIPC_CURL=y +# BR2_PACKAGE_LIBCURL_OPENIPC_VERBOSE is not set +# BR2_PACKAGE_LIBCURL_OPENIPC_PROXY_SUPPORT is not set +# BR2_PACKAGE_LIBCURL_OPENIPC_COOKIES_SUPPORT is not set +# BR2_PACKAGE_LIBCURL_OPENIPC_EXTRA_PROTOCOLS_FEATURES is not set +BR2_PACKAGE_LIBCURL_OPENIPC_MBEDTLS=y +BR2_PACKAGE_LIBEVENT_OPENIPC=y +BR2_PACKAGE_LIBEVENT_OPENIPC_REMOVE_PYSCRIPT=y +BR2_PACKAGE_LIBOGG_OPENIPC=y +BR2_PACKAGE_LIBYAML=y +BR2_PACKAGE_MBEDTLS_OPENIPC=y +# BR2_PACKAGE_MBEDTLS_OPENIPC_PROGRAMS is not set +# BR2_PACKAGE_MBEDTLS_OPENIPC_COMPRESSION is not set +BR2_PACKAGE_MICROBE_WEB=y +# BR2_PACKAGE_MINI_SNMPD is not set +BR2_PACKAGE_OPUS_OPENIPC=y +BR2_PACKAGE_OPUS_OPENIPC_FIXED_POINT=y +# BR2_PACKAGE_SSHPASS is not set +BR2_PACKAGE_UACME_OPENIPC=y +BR2_PACKAGE_VTUND_OPENIPC=y +BR2_PACKAGE_YAML_CLI=y + +# WiFi +BR2_PACKAGE_WIRELESS_TOOLS=y +BR2_PACKAGE_WPA_SUPPLICANT=y +BR2_PACKAGE_WPA_SUPPLICANT_CLI=y +BR2_PACKAGE_WPA_SUPPLICANT_NL80211=y +BR2_PACKAGE_WPA_SUPPLICANT_PASSPHRASE=y +BR2_PACKAGE_LINUX_FIRMWARE=y +BR2_PACKAGE_LINUX_FIRMWARE_MEDIATEK_MT7601U=y +# BR2_PACKAGE_RTL8188EU is not set + +# WIREGUARD +BR2_PACKAGE_WIREGUARD_LINUX_COMPAT=y +BR2_PACKAGE_WIREGUARD_TOOLS=y diff --git a/building.sh b/building.sh index 6a4e09f8..5cccbbe9 100755 --- a/building.sh +++ b/building.sh @@ -165,6 +165,11 @@ hi3518ev300() { fresh && make PLATFORM=hisilicon BOARD=unknown_unknown_${soc}_openipc all && rename } +hi3536cv100() { + soc="hi3536cv100" + fresh && make PLATFORM=hisilicon BOARD=unknown_unknown_${soc}_openipc all && rename +} + hi3536dv100() { soc="hi3536dv100" fresh && make PLATFORM=hisilicon BOARD=unknown_unknown_${soc}_openipc all && rename